summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorFWoltermann@gmail.com <FWoltermann@gmail.com@076cb2c4-205e-83fd-5cf3-1be9aa105544>2011-12-08 14:53:40 +0000
committerFWoltermann@gmail.com <FWoltermann@gmail.com@076cb2c4-205e-83fd-5cf3-1be9aa105544>2011-12-08 14:53:40 +0000
commite33e19d0587146859d48a134ec9fd94e7b7ba5cd (patch)
tree69d048c8801858d2756ab3a487090a7a1b74bf14
downloadstarshatter-e33e19d0587146859d48a134ec9fd94e7b7ba5cd.zip
starshatter-e33e19d0587146859d48a134ec9fd94e7b7ba5cd.tar.gz
starshatter-e33e19d0587146859d48a134ec9fd94e7b7ba5cd.tar.bz2
Initial upload
-rw-r--r--Datafile/Archive.cpp460
-rw-r--r--Datafile/Archive.h83
-rw-r--r--Datafile/DataFile.dsp112
-rw-r--r--Datafile/DataFile.dsw29
-rw-r--r--Datafile/DataFile.mak231
-rw-r--r--Datafile/DataFile.mdpbin0 -> 34816 bytes
-rw-r--r--Datafile/Main.cpp453
-rw-r--r--Doc/3DS File Format.txt1557
-rw-r--r--Doc/AI.TXT1248
-rw-r--r--Doc/DESIGNER.TXT173
-rw-r--r--Doc/Engine.txt106
-rw-r--r--Doc/Eschaton-Atmosphere.txt9
-rw-r--r--Doc/ExceptionHandler.cpp.txt446
-rw-r--r--Doc/GAMEPLAY.TXT124
-rw-r--r--Doc/Manual.docbin0 -> 75264 bytes
-rw-r--r--Doc/Manual2.docbin0 -> 25600 bytes
-rw-r--r--Doc/NEARMAP.TXT117
-rw-r--r--Doc/OUTLINE.TXT47
-rw-r--r--Doc/PAVLOV.TXT59
-rw-r--r--Doc/PLAYER.DOCbin0 -> 2856960 bytes
-rw-r--r--Doc/PLAYER2.DOCbin0 -> 648704 bytes
-rw-r--r--Doc/Starshatter Campaign.txt998
-rw-r--r--Doc/Starshatter Q&A.docbin0 -> 32768 bytes
-rw-r--r--Doc/Starshatter Script - Endgames.docbin0 -> 29696 bytes
-rw-r--r--Doc/Starshatter Script - Fleet Admiral Evars.docbin0 -> 32768 bytes
-rw-r--r--Doc/Starshatter Script - Kash Anlon.docbin0 -> 26112 bytes
-rw-r--r--Doc/Starshatter Script - Promotions.docbin0 -> 23552 bytes
-rw-r--r--Doc/Starshatter Script - Sara Hunter.docbin0 -> 38912 bytes
-rw-r--r--Doc/Starshatter Script - Vice Admiral Caldott.docbin0 -> 44032 bytes
-rw-r--r--Doc/Starshatter System Map.xlsbin0 -> 29696 bytes
-rw-r--r--Doc/Starshatter Text.docbin0 -> 179712 bytes
-rw-r--r--Doc/TASK.TXT380
-rw-r--r--Doc/Voice Over Pickup Script.docbin0 -> 28672 bytes
-rw-r--r--Doc/Weapons Rules.xlsbin0 -> 16384 bytes
-rw-r--r--Doc/Word Stems.xlsbin0 -> 17408 bytes
-rw-r--r--Doc/backstory.docbin0 -> 22528 bytes
-rw-r--r--Doc/dynamic campaigns.txt701
-rw-r--r--Doc/landscape.txt88
-rw-r--r--Doc/old manual.docbin0 -> 1250053 bytes
-rw-r--r--Doc/space sim market crash.txt32
-rw-r--r--Doc/squadron (wc) info.txt96
-rw-r--r--FoundationEx/ArrayList.cpp724
-rw-r--r--FoundationEx/ArrayList.h181
-rw-r--r--FoundationEx/Dictionary.h101
-rw-r--r--FoundationEx/Dictionary.inl260
-rw-r--r--FoundationEx/List.h107
-rw-r--r--FoundationEx/List.inl445
-rw-r--r--FoundationEx/MemDebug.cpp212
-rw-r--r--FoundationEx/MemDebug.h91
-rw-r--r--FoundationEx/Text.cpp751
-rw-r--r--FoundationEx/Text.h193
-rw-r--r--FoundationEx/ThreadSync.h57
-rw-r--r--Magic2/AlphaInverse.cpp4100
-rw-r--r--Magic2/AlphaPalette.cpp260
-rw-r--r--Magic2/Command.cpp52
-rw-r--r--Magic2/Command.h55
-rw-r--r--Magic2/Editor.cpp412
-rw-r--r--Magic2/Editor.h84
-rw-r--r--Magic2/Grid.cpp346
-rw-r--r--Magic2/Grid.h81
-rw-r--r--Magic2/GridProps.cpp126
-rw-r--r--Magic2/GridProps.h69
-rw-r--r--Magic2/M3DS.cpp263
-rw-r--r--Magic2/M3DS.h329
-rw-r--r--Magic2/Magic.cpp202
-rw-r--r--Magic2/Magic.dsp608
-rw-r--r--Magic2/Magic.dsw29
-rw-r--r--Magic2/Magic.h63
-rw-r--r--Magic2/Magic.rc1065
-rw-r--r--Magic2/Magic.vcxproj277
-rw-r--r--Magic2/Magic.vcxproj.filters403
-rw-r--r--Magic2/MagicDoc.cpp615
-rw-r--r--Magic2/MagicDoc.h120
-rw-r--r--Magic2/MagicView.cpp1437
-rw-r--r--Magic2/MagicView.h201
-rw-r--r--Magic2/MainFrm.cpp152
-rw-r--r--Magic2/MainFrm.h62
-rw-r--r--Magic2/MaterialDialog.cpp693
-rw-r--r--Magic2/MaterialDialog.h117
-rw-r--r--Magic2/ModelFile3DS.cpp283
-rw-r--r--Magic2/ModelFile3DS.h37
-rw-r--r--Magic2/ModelFileMAG.cpp824
-rw-r--r--Magic2/ModelFileMAG.h39
-rw-r--r--Magic2/ModelFileOBJ.cpp783
-rw-r--r--Magic2/ModelFileOBJ.h37
-rw-r--r--Magic2/ModelView.cpp417
-rw-r--r--Magic2/ModelView.h64
-rw-r--r--Magic2/Primitives.cpp52
-rw-r--r--Magic2/Primitives.h55
-rw-r--r--Magic2/Res/Magic.icobin0 -> 1078 bytes
-rw-r--r--Magic2/Res/Magic.rc213
-rw-r--r--Magic2/Res/MagicDoc.icobin0 -> 1078 bytes
-rw-r--r--Magic2/Res/Orig Toolbar.bmpbin0 -> 1078 bytes
-rw-r--r--Magic2/Res/Toolbar.bmpbin0 -> 2518 bytes
-rw-r--r--Magic2/Selection.cpp171
-rw-r--r--Magic2/Selection.h74
-rw-r--r--Magic2/Selector.cpp390
-rw-r--r--Magic2/Selector.h76
-rw-r--r--Magic2/StdAfx.cpp8
-rw-r--r--Magic2/StdAfx.h39
-rw-r--r--Magic2/SurfacePropertiesDialog.cpp93
-rw-r--r--Magic2/SurfacePropertiesDialog.h73
-rw-r--r--Magic2/TextureMapDialog.cpp185
-rw-r--r--Magic2/TextureMapDialog.h85
-rw-r--r--Magic2/Thumbnail.cpp57
-rw-r--r--Magic2/Thumbnail.h31
-rw-r--r--Magic2/UVMapView.cpp414
-rw-r--r--Magic2/UVMapView.h82
-rw-r--r--Magic2/l3ds.cpp1795
-rw-r--r--Magic2/l3ds.h457
-rw-r--r--Magic2/resource.h233
-rw-r--r--NetEx/HttpClient.cpp68
-rw-r--r--NetEx/HttpClient.h43
-rw-r--r--NetEx/HttpServer.cpp1003
-rw-r--r--NetEx/HttpServer.h217
-rw-r--r--NetEx/HttpServlet.cpp223
-rw-r--r--NetEx/HttpServlet.h85
-rw-r--r--NetEx/HttpServletExec.cpp233
-rw-r--r--NetEx/HttpServletExec.h57
-rw-r--r--NetEx/NetAddr.cpp107
-rw-r--r--NetEx/NetAddr.h58
-rw-r--r--NetEx/NetClient.cpp116
-rw-r--r--NetEx/NetClient.h103
-rw-r--r--NetEx/NetEx.dsp204
-rw-r--r--NetEx/NetEx.dsw29
-rw-r--r--NetEx/NetEx.vcxproj153
-rw-r--r--NetEx/NetEx.vcxproj.filters101
-rw-r--r--NetEx/NetGram.cpp111
-rw-r--r--NetEx/NetGram.h79
-rw-r--r--NetEx/NetHost.cpp103
-rw-r--r--NetEx/NetHost.h50
-rw-r--r--NetEx/NetLayer.cpp93
-rw-r--r--NetEx/NetLayer.h45
-rw-r--r--NetEx/NetLink.cpp487
-rw-r--r--NetEx/NetLink.h113
-rw-r--r--NetEx/NetMsg.cpp89
-rw-r--r--NetEx/NetMsg.h81
-rw-r--r--NetEx/NetPeer.cpp441
-rw-r--r--NetEx/NetPeer.h98
-rw-r--r--NetEx/NetServer.cpp260
-rw-r--r--NetEx/NetServer.h65
-rw-r--r--NetEx/NetSock.cpp342
-rw-r--r--NetEx/NetSock.h73
-rw-r--r--Opcode/Demo/IceCharacter.dllbin0 -> 45056 bytes
-rw-r--r--Opcode/Demo/IceCore.dllbin0 -> 98304 bytes
-rw-r--r--Opcode/Demo/IceDX7Renderer.dllbin0 -> 65536 bytes
-rw-r--r--Opcode/Demo/IceImageWork.dllbin0 -> 40960 bytes
-rw-r--r--Opcode/Demo/IceMaths.dllbin0 -> 118784 bytes
-rw-r--r--Opcode/Demo/IceRenderManager.dllbin0 -> 729088 bytes
-rw-r--r--Opcode/Demo/IceRenderer.dllbin0 -> 151552 bytes
-rw-r--r--Opcode/Demo/IceTerrain.dllbin0 -> 45056 bytes
-rw-r--r--Opcode/Demo/Meshmerizer.dllbin0 -> 282624 bytes
-rw-r--r--Opcode/Demo/Opcode.dllbin0 -> 69632 bytes
-rw-r--r--Opcode/Demo/Opcode.exebin0 -> 57344 bytes
-rw-r--r--Opcode/Demo/Rapid.dllbin0 -> 40960 bytes
-rw-r--r--Opcode/Demo/Readme.txt142
-rw-r--r--Opcode/Demo/Test scenes/Opcode00_KnotKnot.zcbbin0 -> 66042 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode01_VenusRoom.zcbbin0 -> 243721 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode02_TorusTorus.zcbbin0 -> 12617 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode03_TeapotTeapot.zcbbin0 -> 66074 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode04_SphereCylinder.zcbbin0 -> 15327 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode05_KnotKnot2.zcbbin0 -> 36591 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode06_VenusVenus.zcbbin0 -> 59373 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode07_VenusTorus.zcbbin0 -> 56544 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode08_BoxBox.zcbbin0 -> 7563 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode09_Stairs.zcbbin0 -> 46842 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode10_Explora.zcbbin0 -> 203926 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode11_Gears.zcbbin0 -> 36038 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode12_Irion.zcbbin0 -> 10988 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode13_Pen.zcbbin0 -> 6555 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode14_Shad.zcbbin0 -> 95924 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode15_Chevy.zcbbin0 -> 471484 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode16_Char.zcbbin0 -> 446017 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode17_PCP_Big.zcbbin0 -> 22559 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode17_PCP_Small.zcbbin0 -> 17014 bytes
-rw-r--r--Opcode/Demo/Test scenes/Opcode18_Skewed.zcbbin0 -> 899 bytes
-rw-r--r--Opcode/Demo/Test scenes/ice.log192
-rw-r--r--Opcode/Demo/ZCollide.dllbin0 -> 73728 bytes
-rw-r--r--Opcode/Demo/ZLib.dllbin0 -> 57344 bytes
-rw-r--r--Opcode/Demo/ice.ini28
-rw-r--r--Opcode/Demo/libbz2.dllbin0 -> 57344 bytes
-rw-r--r--Opcode/Doc/OpcodeUserManual.pdfbin0 -> 39653 bytes
-rw-r--r--Opcode/Ice/IceAABB.cpp405
-rw-r--r--Opcode/Ice/IceAABB.h505
-rw-r--r--Opcode/Ice/IceAxes.h54
-rw-r--r--Opcode/Ice/IceBoundingSphere.h142
-rw-r--r--Opcode/Ice/IceContainer.cpp345
-rw-r--r--Opcode/Ice/IceContainer.h212
-rw-r--r--Opcode/Ice/IceFPU.h317
-rw-r--r--Opcode/Ice/IceHPoint.cpp70
-rw-r--r--Opcode/Ice/IceHPoint.h157
-rw-r--r--Opcode/Ice/IceIndexedTriangle.cpp548
-rw-r--r--Opcode/Ice/IceIndexedTriangle.h68
-rw-r--r--Opcode/Ice/IceLSS.h75
-rw-r--r--Opcode/Ice/IceMatrix3x3.cpp48
-rw-r--r--Opcode/Ice/IceMatrix3x3.h496
-rw-r--r--Opcode/Ice/IceMatrix4x4.cpp135
-rw-r--r--Opcode/Ice/IceMatrix4x4.h455
-rw-r--r--Opcode/Ice/IceMemoryMacros.h105
-rw-r--r--Opcode/Ice/IceOBB.cpp323
-rw-r--r--Opcode/Ice/IceOBB.h177
-rw-r--r--Opcode/Ice/IcePairs.h45
-rw-r--r--Opcode/Ice/IcePlane.cpp45
-rw-r--r--Opcode/Ice/IcePlane.h113
-rw-r--r--Opcode/Ice/IcePoint.cpp193
-rw-r--r--Opcode/Ice/IcePoint.h528
-rw-r--r--Opcode/Ice/IcePreprocessor.h128
-rw-r--r--Opcode/Ice/IceRandom.cpp35
-rw-r--r--Opcode/Ice/IceRandom.h42
-rw-r--r--Opcode/Ice/IceRay.cpp84
-rw-r--r--Opcode/Ice/IceRay.h98
-rw-r--r--Opcode/Ice/IceRevisitedRadix.cpp520
-rw-r--r--Opcode/Ice/IceRevisitedRadix.h65
-rw-r--r--Opcode/Ice/IceSegment.cpp57
-rw-r--r--Opcode/Ice/IceSegment.h55
-rw-r--r--Opcode/Ice/IceTriangle.cpp286
-rw-r--r--Opcode/Ice/IceTriangle.h68
-rw-r--r--Opcode/Ice/IceTrilist.h61
-rw-r--r--Opcode/Ice/IceTypes.h157
-rw-r--r--Opcode/Ice/IceUtils.cpp39
-rw-r--r--Opcode/Ice/IceUtils.h256
-rw-r--r--Opcode/OPC_AABBCollider.cpp696
-rw-r--r--Opcode/OPC_AABBCollider.h97
-rw-r--r--Opcode/OPC_AABBTree.cpp573
-rw-r--r--Opcode/OPC_AABBTree.h137
-rw-r--r--Opcode/OPC_BaseModel.cpp138
-rw-r--r--Opcode/OPC_BaseModel.h175
-rw-r--r--Opcode/OPC_BoxBoxOverlap.h122
-rw-r--r--Opcode/OPC_BoxPruning.cpp367
-rw-r--r--Opcode/OPC_BoxPruning.h31
-rw-r--r--Opcode/OPC_Collider.cpp54
-rw-r--r--Opcode/OPC_Collider.h176
-rw-r--r--Opcode/OPC_Common.cpp48
-rw-r--r--Opcode/OPC_Common.h101
-rw-r--r--Opcode/OPC_HybridModel.cpp466
-rw-r--r--Opcode/OPC_HybridModel.h106
-rw-r--r--Opcode/OPC_IceHook.h70
-rw-r--r--Opcode/OPC_LSSAABBOverlap.h523
-rw-r--r--Opcode/OPC_LSSCollider.cpp725
-rw-r--r--Opcode/OPC_LSSCollider.h99
-rw-r--r--Opcode/OPC_LSSTriOverlap.h679
-rw-r--r--Opcode/OPC_MeshInterface.cpp299
-rw-r--r--Opcode/OPC_MeshInterface.h182
-rw-r--r--Opcode/OPC_Model.cpp222
-rw-r--r--Opcode/OPC_Model.h65
-rw-r--r--Opcode/OPC_OBBCollider.cpp767
-rw-r--r--Opcode/OPC_OBBCollider.h142
-rw-r--r--Opcode/OPC_OptimizedTree.cpp782
-rw-r--r--Opcode/OPC_OptimizedTree.h206
-rw-r--r--Opcode/OPC_Picking.cpp182
-rw-r--r--Opcode/OPC_Picking.h45
-rw-r--r--Opcode/OPC_PlanesAABBOverlap.h50
-rw-r--r--Opcode/OPC_PlanesCollider.cpp653
-rw-r--r--Opcode/OPC_PlanesCollider.h121
-rw-r--r--Opcode/OPC_PlanesTriOverlap.h40
-rw-r--r--Opcode/OPC_RayAABBOverlap.h63
-rw-r--r--Opcode/OPC_RayCollider.cpp762
-rw-r--r--Opcode/OPC_RayCollider.h225
-rw-r--r--Opcode/OPC_RayTriOverlap.h89
-rw-r--r--Opcode/OPC_Settings.h49
-rw-r--r--Opcode/OPC_SphereAABBOverlap.h128
-rw-r--r--Opcode/OPC_SphereCollider.cpp726
-rw-r--r--Opcode/OPC_SphereCollider.h96
-rw-r--r--Opcode/OPC_SphereTriOverlap.h187
-rw-r--r--Opcode/OPC_SweepAndPrune.cpp664
-rw-r--r--Opcode/OPC_SweepAndPrune.h86
-rw-r--r--Opcode/OPC_TreeBuilders.cpp255
-rw-r--r--Opcode/OPC_TreeBuilders.h173
-rw-r--r--Opcode/OPC_TreeCollider.cpp943
-rw-r--r--Opcode/OPC_TreeCollider.h244
-rw-r--r--Opcode/OPC_TriBoxOverlap.h339
-rw-r--r--Opcode/OPC_TriTriOverlap.h279
-rw-r--r--Opcode/OPC_VolumeCollider.cpp103
-rw-r--r--Opcode/OPC_VolumeCollider.h138
-rw-r--r--Opcode/Opcode.cpp65
-rw-r--r--Opcode/Opcode.dsp517
-rw-r--r--Opcode/Opcode.dsw41
-rw-r--r--Opcode/Opcode.h89
-rw-r--r--Opcode/Opcode.ncbbin0 -> 525312 bytes
-rw-r--r--Opcode/Opcode.optbin0 -> 75776 bytes
-rw-r--r--Opcode/Opcode.plg299
-rw-r--r--Opcode/Opcode.vcxproj367
-rw-r--r--Opcode/Opcode.vcxproj.filters327
-rw-r--r--Opcode/OpcodeLib/Ice/IceAABB.cpp405
-rw-r--r--Opcode/OpcodeLib/Ice/IceAABB.h505
-rw-r--r--Opcode/OpcodeLib/Ice/IceAxes.h54
-rw-r--r--Opcode/OpcodeLib/Ice/IceBoundingSphere.h142
-rw-r--r--Opcode/OpcodeLib/Ice/IceContainer.cpp345
-rw-r--r--Opcode/OpcodeLib/Ice/IceContainer.h212
-rw-r--r--Opcode/OpcodeLib/Ice/IceFPU.h317
-rw-r--r--Opcode/OpcodeLib/Ice/IceHPoint.cpp70
-rw-r--r--Opcode/OpcodeLib/Ice/IceHPoint.h157
-rw-r--r--Opcode/OpcodeLib/Ice/IceIndexedTriangle.cpp548
-rw-r--r--Opcode/OpcodeLib/Ice/IceIndexedTriangle.h68
-rw-r--r--Opcode/OpcodeLib/Ice/IceLSS.h75
-rw-r--r--Opcode/OpcodeLib/Ice/IceMatrix3x3.cpp48
-rw-r--r--Opcode/OpcodeLib/Ice/IceMatrix3x3.h496
-rw-r--r--Opcode/OpcodeLib/Ice/IceMatrix4x4.cpp135
-rw-r--r--Opcode/OpcodeLib/Ice/IceMatrix4x4.h455
-rw-r--r--Opcode/OpcodeLib/Ice/IceMemoryMacros.h105
-rw-r--r--Opcode/OpcodeLib/Ice/IceOBB.cpp323
-rw-r--r--Opcode/OpcodeLib/Ice/IceOBB.h177
-rw-r--r--Opcode/OpcodeLib/Ice/IcePairs.h45
-rw-r--r--Opcode/OpcodeLib/Ice/IcePlane.cpp45
-rw-r--r--Opcode/OpcodeLib/Ice/IcePlane.h113
-rw-r--r--Opcode/OpcodeLib/Ice/IcePoint.cpp193
-rw-r--r--Opcode/OpcodeLib/Ice/IcePoint.h528
-rw-r--r--Opcode/OpcodeLib/Ice/IcePreprocessor.h128
-rw-r--r--Opcode/OpcodeLib/Ice/IceRandom.cpp35
-rw-r--r--Opcode/OpcodeLib/Ice/IceRandom.h42
-rw-r--r--Opcode/OpcodeLib/Ice/IceRay.cpp84
-rw-r--r--Opcode/OpcodeLib/Ice/IceRay.h98
-rw-r--r--Opcode/OpcodeLib/Ice/IceRevisitedRadix.cpp520
-rw-r--r--Opcode/OpcodeLib/Ice/IceRevisitedRadix.h65
-rw-r--r--Opcode/OpcodeLib/Ice/IceSegment.cpp57
-rw-r--r--Opcode/OpcodeLib/Ice/IceSegment.h55
-rw-r--r--Opcode/OpcodeLib/Ice/IceTriangle.cpp286
-rw-r--r--Opcode/OpcodeLib/Ice/IceTriangle.h68
-rw-r--r--Opcode/OpcodeLib/Ice/IceTrilist.h61
-rw-r--r--Opcode/OpcodeLib/Ice/IceTypes.h157
-rw-r--r--Opcode/OpcodeLib/Ice/IceUtils.cpp39
-rw-r--r--Opcode/OpcodeLib/Ice/IceUtils.h256
-rw-r--r--Opcode/OpcodeLib/OPC_AABBCollider.cpp696
-rw-r--r--Opcode/OpcodeLib/OPC_AABBCollider.h97
-rw-r--r--Opcode/OpcodeLib/OPC_AABBTree.cpp573
-rw-r--r--Opcode/OpcodeLib/OPC_AABBTree.h137
-rw-r--r--Opcode/OpcodeLib/OPC_BaseModel.cpp138
-rw-r--r--Opcode/OpcodeLib/OPC_BaseModel.h175
-rw-r--r--Opcode/OpcodeLib/OPC_BoxBoxOverlap.h122
-rw-r--r--Opcode/OpcodeLib/OPC_Collider.cpp54
-rw-r--r--Opcode/OpcodeLib/OPC_Collider.h176
-rw-r--r--Opcode/OpcodeLib/OPC_Common.cpp48
-rw-r--r--Opcode/OpcodeLib/OPC_Common.h101
-rw-r--r--Opcode/OpcodeLib/OPC_HybridModel.cpp466
-rw-r--r--Opcode/OpcodeLib/OPC_HybridModel.h106
-rw-r--r--Opcode/OpcodeLib/OPC_IceHook.h70
-rw-r--r--Opcode/OpcodeLib/OPC_MeshInterface.cpp299
-rw-r--r--Opcode/OpcodeLib/OPC_MeshInterface.h182
-rw-r--r--Opcode/OpcodeLib/OPC_Model.cpp222
-rw-r--r--Opcode/OpcodeLib/OPC_Model.h65
-rw-r--r--Opcode/OpcodeLib/OPC_OBBCollider.cpp767
-rw-r--r--Opcode/OpcodeLib/OPC_OBBCollider.h142
-rw-r--r--Opcode/OpcodeLib/OPC_OptimizedTree.cpp782
-rw-r--r--Opcode/OpcodeLib/OPC_OptimizedTree.h206
-rw-r--r--Opcode/OpcodeLib/OPC_RayAABBOverlap.h63
-rw-r--r--Opcode/OpcodeLib/OPC_RayCollider.cpp762
-rw-r--r--Opcode/OpcodeLib/OPC_RayCollider.h225
-rw-r--r--Opcode/OpcodeLib/OPC_RayTriOverlap.h89
-rw-r--r--Opcode/OpcodeLib/OPC_Settings.h49
-rw-r--r--Opcode/OpcodeLib/OPC_TreeBuilders.cpp255
-rw-r--r--Opcode/OpcodeLib/OPC_TreeBuilders.h173
-rw-r--r--Opcode/OpcodeLib/OPC_TreeCollider.cpp943
-rw-r--r--Opcode/OpcodeLib/OPC_TreeCollider.h244
-rw-r--r--Opcode/OpcodeLib/OPC_TriBoxOverlap.h339
-rw-r--r--Opcode/OpcodeLib/OPC_TriTriOverlap.h279
-rw-r--r--Opcode/OpcodeLib/OPC_VolumeCollider.cpp103
-rw-r--r--Opcode/OpcodeLib/OPC_VolumeCollider.h138
-rw-r--r--Opcode/OpcodeLib/Opcode.cpp65
-rw-r--r--Opcode/OpcodeLib/Opcode.h64
-rw-r--r--Opcode/OpcodeLib/OpcodeLib.dsp405
-rw-r--r--Opcode/OpcodeLib/OpcodeLib.plg259
-rw-r--r--Opcode/OpcodeLib/Readme.txt24
-rw-r--r--Opcode/OpcodeLib/StdAfx.cpp10
-rw-r--r--Opcode/OpcodeLib/StdAfx.h24
-rw-r--r--Opcode/ReadMe.txt171
-rw-r--r--Opcode/StdAfx.cpp10
-rw-r--r--Opcode/StdAfx.h24
-rw-r--r--Opcode/TemporalCoherence.txt32
-rw-r--r--Parser/Parser.cpp309
-rw-r--r--Parser/Parser.h46
-rw-r--r--Parser/Reader.cpp114
-rw-r--r--Parser/Reader.h68
-rw-r--r--Parser/Term.cpp122
-rw-r--r--Parser/Term.h172
-rw-r--r--Parser/Token.cpp546
-rw-r--r--Parser/Token.h146
-rw-r--r--Parser/stdafx.h1
-rw-r--r--Stars45/Asteroid.cpp116
-rw-r--r--Stars45/Asteroid.h35
-rw-r--r--Stars45/AudDlg.cpp201
-rw-r--r--Stars45/AudDlg.h79
-rw-r--r--Stars45/AudioConfig.cpp382
-rw-r--r--Stars45/AudioConfig.h71
-rw-r--r--Stars45/Authorization.cpp202
-rw-r--r--Stars45/Authorization.h30
-rw-r--r--Stars45/AwardDlg.cpp152
-rw-r--r--Stars45/AwardDlg.h59
-rw-r--r--Stars45/AwardShowDlg.cpp159
-rw-r--r--Stars45/AwardShowDlg.h63
-rw-r--r--Stars45/BaseScreen.h83
-rw-r--r--Stars45/Callsign.cpp102
-rw-r--r--Stars45/Callsign.h29
-rw-r--r--Stars45/CameraDirector.cpp1196
-rw-r--r--Stars45/CameraDirector.h156
-rw-r--r--Stars45/Campaign.cpp2347
-rw-r--r--Stars45/Campaign.h282
-rw-r--r--Stars45/CampaignMissionFighter.cpp2152
-rw-r--r--Stars45/CampaignMissionFighter.h121
-rw-r--r--Stars45/CampaignMissionRequest.cpp39
-rw-r--r--Stars45/CampaignMissionRequest.h84
-rw-r--r--Stars45/CampaignMissionStarship.cpp1405
-rw-r--r--Stars45/CampaignMissionStarship.h107
-rw-r--r--Stars45/CampaignPlan.h58
-rw-r--r--Stars45/CampaignPlanAssignment.cpp158
-rw-r--r--Stars45/CampaignPlanAssignment.h50
-rw-r--r--Stars45/CampaignPlanEvent.cpp1315
-rw-r--r--Stars45/CampaignPlanEvent.h72
-rw-r--r--Stars45/CampaignPlanMission.cpp374
-rw-r--r--Stars45/CampaignPlanMission.h58
-rw-r--r--Stars45/CampaignPlanMovement.cpp168
-rw-r--r--Stars45/CampaignPlanMovement.h44
-rw-r--r--Stars45/CampaignPlanStrategic.cpp483
-rw-r--r--Stars45/CampaignPlanStrategic.h59
-rw-r--r--Stars45/CampaignSaveGame.cpp729
-rw-r--r--Stars45/CampaignSaveGame.h69
-rw-r--r--Stars45/CampaignSituationReport.cpp416
-rw-r--r--Stars45/CampaignSituationReport.h59
-rw-r--r--Stars45/CarrierAI.cpp400
-rw-r--r--Stars45/CarrierAI.h64
-rw-r--r--Stars45/CmdDlg.cpp186
-rw-r--r--Stars45/CmdDlg.h83
-rw-r--r--Stars45/CmdForceDlg.cpp718
-rw-r--r--Stars45/CmdForceDlg.h69
-rw-r--r--Stars45/CmdIntelDlg.cpp393
-rw-r--r--Stars45/CmdIntelDlg.h76
-rw-r--r--Stars45/CmdMissionsDlg.cpp325
-rw-r--r--Stars45/CmdMissionsDlg.h65
-rw-r--r--Stars45/CmdMsgDlg.cpp92
-rw-r--r--Stars45/CmdMsgDlg.h54
-rw-r--r--Stars45/CmdOrdersDlg.cpp139
-rw-r--r--Stars45/CmdOrdersDlg.h56
-rw-r--r--Stars45/CmdTheaterDlg.cpp216
-rw-r--r--Stars45/CmdTheaterDlg.h62
-rw-r--r--Stars45/CmdTitleDlg.cpp78
-rw-r--r--Stars45/CmdTitleDlg.h56
-rw-r--r--Stars45/CmpCompleteDlg.cpp108
-rw-r--r--Stars45/CmpCompleteDlg.h50
-rw-r--r--Stars45/CmpFileDlg.cpp191
-rw-r--r--Stars45/CmpFileDlg.h64
-rw-r--r--Stars45/CmpLoadDlg.cpp118
-rw-r--r--Stars45/CmpLoadDlg.h46
-rw-r--r--Stars45/CmpSceneDlg.cpp188
-rw-r--r--Stars45/CmpSceneDlg.h68
-rw-r--r--Stars45/CmpSelectDlg.cpp623
-rw-r--r--Stars45/CmpSelectDlg.h91
-rw-r--r--Stars45/CmpnScreen.cpp647
-rw-r--r--Stars45/CmpnScreen.h137
-rw-r--r--Stars45/CombatAction.cpp352
-rw-r--r--Stars45/CombatAction.h205
-rw-r--r--Stars45/CombatAssignment.cpp68
-rw-r--r--Stars45/CombatAssignment.h57
-rw-r--r--Stars45/CombatEvent.cpp153
-rw-r--r--Stars45/CombatEvent.h122
-rw-r--r--Stars45/CombatGroup.cpp1589
-rw-r--r--Stars45/CombatGroup.h231
-rw-r--r--Stars45/CombatRoster.cpp93
-rw-r--r--Stars45/CombatRoster.h48
-rw-r--r--Stars45/CombatUnit.cpp402
-rw-r--r--Stars45/CombatUnit.h134
-rw-r--r--Stars45/CombatZone.cpp282
-rw-r--r--Stars45/CombatZone.h103
-rw-r--r--Stars45/Combatant.cpp172
-rw-r--r--Stars45/Combatant.h73
-rw-r--r--Stars45/Component.cpp177
-rw-r--r--Stars45/Component.h100
-rw-r--r--Stars45/Computer.cpp93
-rw-r--r--Stars45/Computer.h41
-rw-r--r--Stars45/ConfirmDlg.cpp150
-rw-r--r--Stars45/ConfirmDlg.h64
-rw-r--r--Stars45/Contact.cpp365
-rw-r--r--Stars45/Contact.h91
-rw-r--r--Stars45/CtlDlg.cpp461
-rw-r--r--Stars45/CtlDlg.h119
-rw-r--r--Stars45/DebriefDlg.cpp375
-rw-r--r--Stars45/DebriefDlg.h81
-rw-r--r--Stars45/Debris.cpp220
-rw-r--r--Stars45/Debris.h43
-rw-r--r--Stars45/DetailSet.cpp228
-rw-r--r--Stars45/DetailSet.h73
-rw-r--r--Stars45/DisplayView.cpp239
-rw-r--r--Stars45/DisplayView.h71
-rw-r--r--Stars45/Drive.cpp497
-rw-r--r--Stars45/Drive.h93
-rw-r--r--Stars45/DriveSprite.cpp143
-rw-r--r--Stars45/DriveSprite.h47
-rw-r--r--Stars45/Drone.cpp196
-rw-r--r--Stars45/Drone.h69
-rw-r--r--Stars45/DropShipAI.cpp122
-rw-r--r--Stars45/DropShipAI.h47
-rw-r--r--Stars45/Element.cpp667
-rw-r--r--Stars45/Element.h172
-rw-r--r--Stars45/EngDlg.cpp1043
-rw-r--r--Stars45/EngDlg.h106
-rw-r--r--Stars45/ExceptionHandler.cpp429
-rw-r--r--Stars45/ExitDlg.cpp152
-rw-r--r--Stars45/ExitDlg.h60
-rw-r--r--Stars45/Explosion.cpp611
-rw-r--r--Stars45/Explosion.h84
-rw-r--r--Stars45/Farcaster.cpp288
-rw-r--r--Stars45/Farcaster.h92
-rw-r--r--Stars45/FighterAI.cpp1831
-rw-r--r--Stars45/FighterAI.h85
-rw-r--r--Stars45/FighterTacticalAI.cpp562
-rw-r--r--Stars45/FighterTacticalAI.h56
-rw-r--r--Stars45/FirstTimeDlg.cpp147
-rw-r--r--Stars45/FirstTimeDlg.h56
-rw-r--r--Stars45/FlightComp.cpp305
-rw-r--r--Stars45/FlightComp.h63
-rw-r--r--Stars45/FlightDeck.cpp1186
-rw-r--r--Stars45/FlightDeck.h195
-rw-r--r--Stars45/FlightPlanner.cpp372
-rw-r--r--Stars45/FlightPlanner.h51
-rw-r--r--Stars45/FltDlg.cpp1084
-rw-r--r--Stars45/FltDlg.h81
-rw-r--r--Stars45/Galaxy.cpp283
-rw-r--r--Stars45/Galaxy.h74
-rw-r--r--Stars45/GameScreen.cpp1254
-rw-r--r--Stars45/GameScreen.h192
-rw-r--r--Stars45/Grid.cpp92
-rw-r--r--Stars45/Grid.h50
-rw-r--r--Stars45/GroundAI.cpp191
-rw-r--r--Stars45/GroundAI.h61
-rw-r--r--Stars45/HUDSounds.cpp118
-rw-r--r--Stars45/HUDSounds.h46
-rw-r--r--Stars45/HUDView.cpp4364
-rw-r--r--Stars45/HUDView.h218
-rw-r--r--Stars45/Hangar.cpp933
-rw-r--r--Stars45/Hangar.h127
-rw-r--r--Stars45/HardPoint.cpp104
-rw-r--r--Stars45/HardPoint.h82
-rw-r--r--Stars45/Hoop.cpp156
-rw-r--r--Stars45/Hoop.h45
-rw-r--r--Stars45/Instruction.cpp569
-rw-r--r--Stars45/Instruction.h166
-rw-r--r--Stars45/Intel.cpp46
-rw-r--r--Stars45/Intel.h37
-rw-r--r--Stars45/JoyDlg.cpp230
-rw-r--r--Stars45/JoyDlg.h56
-rw-r--r--Stars45/KeyDlg.cpp199
-rw-r--r--Stars45/KeyDlg.h65
-rw-r--r--Stars45/KeyMap.cpp824
-rw-r--r--Stars45/KeyMap.h181
-rw-r--r--Stars45/LandingGear.cpp278
-rw-r--r--Stars45/LandingGear.h66
-rw-r--r--Stars45/LoadDlg.cpp77
-rw-r--r--Stars45/LoadDlg.h41
-rw-r--r--Stars45/LoadScreen.cpp163
-rw-r--r--Stars45/LoadScreen.h64
-rw-r--r--Stars45/Main.cpp186
-rw-r--r--Stars45/MapView.cpp3478
-rw-r--r--Stars45/MapView.h223
-rw-r--r--Stars45/MenuDlg.cpp276
-rw-r--r--Stars45/MenuDlg.h86
-rw-r--r--Stars45/MenuScreen.cpp1076
-rw-r--r--Stars45/MenuScreen.h199
-rw-r--r--Stars45/MenuView.cpp333
-rw-r--r--Stars45/MenuView.h77
-rw-r--r--Stars45/Mfd.cpp1403
-rw-r--r--Stars45/Mfd.h104
-rw-r--r--Stars45/Mission.cpp2160
-rw-r--r--Stars45/Mission.h426
-rw-r--r--Stars45/MissionEvent.cpp874
-rw-r--r--Stars45/MissionEvent.h167
-rw-r--r--Stars45/MissionTemplate.cpp846
-rw-r--r--Stars45/MissionTemplate.h117
-rw-r--r--Stars45/ModConfig.cpp381
-rw-r--r--Stars45/ModConfig.h75
-rw-r--r--Stars45/ModDlg.cpp350
-rw-r--r--Stars45/ModDlg.h94
-rw-r--r--Stars45/ModInfo.cpp292
-rw-r--r--Stars45/ModInfo.h122
-rw-r--r--Stars45/ModInfoDlg.cpp115
-rw-r--r--Stars45/ModInfoDlg.h65
-rw-r--r--Stars45/MsnDlg.cpp303
-rw-r--r--Stars45/MsnDlg.h73
-rw-r--r--Stars45/MsnEditDlg.cpp896
-rw-r--r--Stars45/MsnEditDlg.h116
-rw-r--r--Stars45/MsnEditNavDlg.cpp300
-rw-r--r--Stars45/MsnEditNavDlg.h78
-rw-r--r--Stars45/MsnElemDlg.cpp805
-rw-r--r--Stars45/MsnElemDlg.h99
-rw-r--r--Stars45/MsnEventDlg.cpp416
-rw-r--r--Stars45/MsnEventDlg.h86
-rw-r--r--Stars45/MsnNavDlg.cpp104
-rw-r--r--Stars45/MsnNavDlg.h54
-rw-r--r--Stars45/MsnObjDlg.cpp316
-rw-r--r--Stars45/MsnObjDlg.h68
-rw-r--r--Stars45/MsnPkgDlg.cpp336
-rw-r--r--Stars45/MsnPkgDlg.h66
-rw-r--r--Stars45/MsnSelectDlg.cpp499
-rw-r--r--Stars45/MsnSelectDlg.h81
-rw-r--r--Stars45/MsnWepDlg.cpp517
-rw-r--r--Stars45/MsnWepDlg.h86
-rw-r--r--Stars45/MusicDirector.cpp520
-rw-r--r--Stars45/MusicDirector.h114
-rw-r--r--Stars45/MusicTrack.cpp273
-rw-r--r--Stars45/MusicTrack.h77
-rw-r--r--Stars45/NPClient.h190
-rw-r--r--Stars45/NPClientWraps.cpp257
-rw-r--r--Stars45/NPClientWraps.h33
-rw-r--r--Stars45/NavAI.cpp622
-rw-r--r--Stars45/NavAI.h74
-rw-r--r--Stars45/NavDlg.cpp1126
-rw-r--r--Stars45/NavDlg.h119
-rw-r--r--Stars45/NavLight.cpp188
-rw-r--r--Stars45/NavLight.h67
-rw-r--r--Stars45/NavSystem.cpp113
-rw-r--r--Stars45/NavSystem.h55
-rw-r--r--Stars45/NetAddrDlg.cpp160
-rw-r--r--Stars45/NetAddrDlg.h60
-rw-r--r--Stars45/NetAdminChat.cpp122
-rw-r--r--Stars45/NetAdminChat.h33
-rw-r--r--Stars45/NetAdminServer.cpp887
-rw-r--r--Stars45/NetAdminServer.h91
-rw-r--r--Stars45/NetAuth.cpp216
-rw-r--r--Stars45/NetAuth.h51
-rw-r--r--Stars45/NetBrokerClient.cpp239
-rw-r--r--Stars45/NetBrokerClient.h43
-rw-r--r--Stars45/NetChat.cpp53
-rw-r--r--Stars45/NetChat.h52
-rw-r--r--Stars45/NetClientConfig.cpp332
-rw-r--r--Stars45/NetClientConfig.h73
-rw-r--r--Stars45/NetClientDlg.cpp431
-rw-r--r--Stars45/NetClientDlg.h80
-rw-r--r--Stars45/NetData.cpp1453
-rw-r--r--Stars45/NetData.h966
-rw-r--r--Stars45/NetFileServlet.cpp118
-rw-r--r--Stars45/NetFileServlet.h50
-rw-r--r--Stars45/NetGame.cpp330
-rw-r--r--Stars45/NetGame.h127
-rw-r--r--Stars45/NetGameClient.cpp1069
-rw-r--r--Stars45/NetGameClient.h87
-rw-r--r--Stars45/NetGameServer.cpp1199
-rw-r--r--Stars45/NetGameServer.h90
-rw-r--r--Stars45/NetLobby.cpp630
-rw-r--r--Stars45/NetLobby.h289
-rw-r--r--Stars45/NetLobbyClient.cpp809
-rw-r--r--Stars45/NetLobbyClient.h104
-rw-r--r--Stars45/NetLobbyDlg.cpp475
-rw-r--r--Stars45/NetLobbyDlg.h89
-rw-r--r--Stars45/NetLobbyServer.cpp1359
-rw-r--r--Stars45/NetLobbyServer.h119
-rw-r--r--Stars45/NetPacket.cpp356
-rw-r--r--Stars45/NetPacket.h74
-rw-r--r--Stars45/NetPassDlg.cpp143
-rw-r--r--Stars45/NetPassDlg.h58
-rw-r--r--Stars45/NetPlayer.cpp464
-rw-r--r--Stars45/NetPlayer.h98
-rw-r--r--Stars45/NetServerConfig.cpp463
-rw-r--r--Stars45/NetServerConfig.h99
-rw-r--r--Stars45/NetServerDlg.cpp179
-rw-r--r--Stars45/NetServerDlg.h64
-rw-r--r--Stars45/NetUnitDlg.cpp641
-rw-r--r--Stars45/NetUnitDlg.h93
-rw-r--r--Stars45/NetUser.cpp117
-rw-r--r--Stars45/NetUser.h112
-rw-r--r--Stars45/NetUtil.cpp424
-rw-r--r--Stars45/NetUtil.h56
-rw-r--r--Stars45/OptDlg.cpp322
-rw-r--r--Stars45/OptDlg.h87
-rw-r--r--Stars45/PlanScreen.cpp395
-rw-r--r--Stars45/PlanScreen.h108
-rw-r--r--Stars45/Player.cpp1552
-rw-r--r--Stars45/Player.h187
-rw-r--r--Stars45/PlayerDlg.cpp389
-rw-r--r--Stars45/PlayerDlg.h86
-rw-r--r--Stars45/Power.cpp300
-rw-r--r--Stars45/Power.h62
-rw-r--r--Stars45/QuantumDrive.cpp246
-rw-r--r--Stars45/QuantumDrive.h73
-rw-r--r--Stars45/QuantumFlash.cpp175
-rw-r--r--Stars45/QuantumFlash.h60
-rw-r--r--Stars45/QuantumView.cpp318
-rw-r--r--Stars45/QuantumView.h73
-rw-r--r--Stars45/QuitView.cpp302
-rw-r--r--Stars45/QuitView.h64
-rw-r--r--Stars45/RLoc.cpp88
-rw-r--r--Stars45/RLoc.h71
-rw-r--r--Stars45/RadioHandler.cpp561
-rw-r--r--Stars45/RadioHandler.h53
-rw-r--r--Stars45/RadioMessage.cpp165
-rw-r--r--Stars45/RadioMessage.h155
-rw-r--r--Stars45/RadioTraffic.cpp391
-rw-r--r--Stars45/RadioTraffic.h65
-rw-r--r--Stars45/RadioView.cpp640
-rw-r--r--Stars45/RadioView.h87
-rw-r--r--Stars45/RadioVox.cpp249
-rw-r--r--Stars45/RadioVox.h59
-rw-r--r--Stars45/SeekerAI.cpp257
-rw-r--r--Stars45/SeekerAI.h73
-rw-r--r--Stars45/Sensor.cpp850
-rw-r--r--Stars45/Sensor.h86
-rw-r--r--Stars45/Shield.cpp259
-rw-r--r--Stars45/Shield.h77
-rw-r--r--Stars45/ShieldRep.cpp250
-rw-r--r--Stars45/ShieldRep.h48
-rw-r--r--Stars45/Ship.cpp5305
-rw-r--r--Stars45/Ship.h583
-rw-r--r--Stars45/ShipAI.cpp1358
-rw-r--r--Stars45/ShipAI.h153
-rw-r--r--Stars45/ShipCtrl.cpp341
-rw-r--r--Stars45/ShipCtrl.h59
-rw-r--r--Stars45/ShipDesign.cpp3700
-rw-r--r--Stars45/ShipDesign.h292
-rw-r--r--Stars45/ShipKiller.cpp263
-rw-r--r--Stars45/ShipKiller.h55
-rw-r--r--Stars45/ShipSolid.cpp96
-rw-r--r--Stars45/ShipSolid.h52
-rw-r--r--Stars45/Shot.cpp626
-rw-r--r--Stars45/Shot.h127
-rw-r--r--Stars45/Sim.cpp3810
-rw-r--r--Stars45/Sim.h330
-rw-r--r--Stars45/SimEvent.cpp262
-rw-r--r--Stars45/SimEvent.h163
-rw-r--r--Stars45/SimObject.cpp150
-rw-r--r--Stars45/SimObject.h99
-rw-r--r--Stars45/Sky.cpp726
-rw-r--r--Stars45/Sky.h105
-rw-r--r--Stars45/StarServer.cpp545
-rw-r--r--Stars45/StarServer.h76
-rw-r--r--Stars45/StarSystem.cpp1974
-rw-r--r--Stars45/StarSystem.h322
-rw-r--r--Stars45/Stars.dsp1585
-rw-r--r--Stars45/Stars.dsw29
-rw-r--r--Stars45/Stars.icobin0 -> 2998 bytes
-rw-r--r--Stars45/Stars.rc99
-rw-r--r--Stars45/Stars.vcxproj522
-rw-r--r--Stars45/Stars.vcxproj.filters1123
-rw-r--r--Stars45/Starshatter.cpp2903
-rw-r--r--Stars45/Starshatter.h225
-rw-r--r--Stars45/StarshipAI.cpp822
-rw-r--r--Stars45/StarshipAI.h62
-rw-r--r--Stars45/StarshipTacticalAI.cpp276
-rw-r--r--Stars45/StarshipTacticalAI.h47
-rw-r--r--Stars45/SteerAI.cpp453
-rw-r--r--Stars45/SteerAI.h125
-rw-r--r--Stars45/System.cpp399
-rw-r--r--Stars45/System.h174
-rw-r--r--Stars45/SystemDesign.cpp168
-rw-r--r--Stars45/SystemDesign.h54
-rw-r--r--Stars45/TacRefDlg.cpp689
-rw-r--r--Stars45/TacRefDlg.h101
-rw-r--r--Stars45/TacticalAI.cpp958
-rw-r--r--Stars45/TacticalAI.h91
-rw-r--r--Stars45/TacticalView.cpp1495
-rw-r--r--Stars45/TacticalView.h120
-rw-r--r--Stars45/Terrain.cpp561
-rw-r--r--Stars45/Terrain.h119
-rw-r--r--Stars45/TerrainApron.cpp336
-rw-r--r--Stars45/TerrainApron.h71
-rw-r--r--Stars45/TerrainClouds.cpp269
-rw-r--r--Stars45/TerrainClouds.h64
-rw-r--r--Stars45/TerrainHaze.cpp90
-rw-r--r--Stars45/TerrainHaze.h46
-rw-r--r--Stars45/TerrainLayer.h63
-rw-r--r--Stars45/TerrainPatch.cpp1113
-rw-r--r--Stars45/TerrainPatch.h94
-rw-r--r--Stars45/TerrainRegion.cpp266
-rw-r--r--Stars45/TerrainRegion.h126
-rw-r--r--Stars45/Thruster.cpp595
-rw-r--r--Stars45/Thruster.h97
-rw-r--r--Stars45/TrackIR.cpp247
-rw-r--r--Stars45/TrackIR.h53
-rw-r--r--Stars45/Trail.cpp200
-rw-r--r--Stars45/Trail.h60
-rw-r--r--Stars45/VidDlg.cpp516
-rw-r--r--Stars45/VidDlg.h106
-rw-r--r--Stars45/Weapon.cpp1184
-rw-r--r--Stars45/Weapon.h204
-rw-r--r--Stars45/WeaponDesign.cpp726
-rw-r--r--Stars45/WeaponDesign.h187
-rw-r--r--Stars45/WeaponGroup.cpp348
-rw-r--r--Stars45/WeaponGroup.h106
-rw-r--r--Stars45/Weather.cpp178
-rw-r--r--Stars45/Weather.h67
-rw-r--r--Stars45/WepView.cpp531
-rw-r--r--Stars45/WepView.h88
-rw-r--r--Stars45/resource.h18
-rw-r--r--Starshatter/Starshatter.sln235
-rw-r--r--libpng/libpng.vcxproj679
-rw-r--r--libpng/libpng.vcxproj.filters83
-rw-r--r--libpng/libpng.vcxproj.user3
-rw-r--r--libpng/png.c2362
-rw-r--r--libpng/png.h2309
-rw-r--r--libpng/pngconf.h649
-rw-r--r--libpng/pngdebug.h157
-rw-r--r--libpng/pngerror.c447
-rw-r--r--libpng/pngget.c1032
-rw-r--r--libpng/pnginfo.h270
-rw-r--r--libpng/pnglibconf.h181
-rw-r--r--libpng/pngmem.c658
-rw-r--r--libpng/pngpread.c1854
-rw-r--r--libpng/pngpriv.h1246
-rw-r--r--libpng/pngread.c1473
-rw-r--r--libpng/pngrio.c176
-rw-r--r--libpng/pngrtran.c4303
-rw-r--r--libpng/pngrutil.c3632
-rw-r--r--libpng/pngset.c1225
-rw-r--r--libpng/pngstruct.h308
-rw-r--r--libpng/pngtrans.c674
-rw-r--r--libpng/pngwio.c254
-rw-r--r--libpng/pngwrite.c1605
-rw-r--r--libpng/pngwtran.c631
-rw-r--r--libpng/pngwutil.c2948
-rw-r--r--nGenEx/ActiveWindow.cpp1001
-rw-r--r--nGenEx/ActiveWindow.h325
-rw-r--r--nGenEx/Archive.cpp587
-rw-r--r--nGenEx/Archive.h90
-rw-r--r--nGenEx/AviFile.cpp178
-rw-r--r--nGenEx/AviFile.h63
-rw-r--r--nGenEx/Bitmap.cpp1618
-rw-r--r--nGenEx/Bitmap.h124
-rw-r--r--nGenEx/Bmp.cpp251
-rw-r--r--nGenEx/Bmp.h76
-rw-r--r--nGenEx/Bolt.cpp176
-rw-r--r--nGenEx/Bolt.h65
-rw-r--r--nGenEx/Button.cpp687
-rw-r--r--nGenEx/Button.h121
-rw-r--r--nGenEx/Camera.cpp212
-rw-r--r--nGenEx/Camera.h61
-rw-r--r--nGenEx/CameraView.cpp805
-rw-r--r--nGenEx/CameraView.h114
-rw-r--r--nGenEx/Color.cpp682
-rw-r--r--nGenEx/Color.h295
-rw-r--r--nGenEx/ComboBox.cpp434
-rw-r--r--nGenEx/ComboBox.h105
-rw-r--r--nGenEx/ComboList.cpp434
-rw-r--r--nGenEx/ComboList.h92
-rw-r--r--nGenEx/ContentBundle.cpp137
-rw-r--r--nGenEx/ContentBundle.h48
-rw-r--r--nGenEx/D3DXImage.cpp222
-rw-r--r--nGenEx/D3DXImage.h43
-rw-r--r--nGenEx/DataLoader.cpp1013
-rw-r--r--nGenEx/DataLoader.h86
-rw-r--r--nGenEx/Director.h44
-rw-r--r--nGenEx/EditBox.cpp452
-rw-r--r--nGenEx/EditBox.h92
-rw-r--r--nGenEx/Encrypt.cpp171
-rw-r--r--nGenEx/Encrypt.h38
-rw-r--r--nGenEx/EventDispatch.cpp275
-rw-r--r--nGenEx/EventDispatch.h62
-rw-r--r--nGenEx/EventTarget.h59
-rw-r--r--nGenEx/FadeView.cpp145
-rw-r--r--nGenEx/FadeView.h56
-rw-r--r--nGenEx/Fix.cpp24
-rw-r--r--nGenEx/Fix.h180
-rw-r--r--nGenEx/Font.cpp1232
-rw-r--r--nGenEx/Font.h143
-rw-r--r--nGenEx/FontMgr.cpp58
-rw-r--r--nGenEx/FontMgr.h50
-rw-r--r--nGenEx/FormDef.cpp1269
-rw-r--r--nGenEx/FormDef.h332
-rw-r--r--nGenEx/FormWindow.cpp809
-rw-r--r--nGenEx/FormWindow.h77
-rw-r--r--nGenEx/FormatUtil.cpp337
-rw-r--r--nGenEx/FormatUtil.h47
-rw-r--r--nGenEx/Game.cpp1574
-rw-r--r--nGenEx/Game.h220
-rw-r--r--nGenEx/Geometry.cpp698
-rw-r--r--nGenEx/Geometry.h302
-rw-r--r--nGenEx/Graphic.cpp170
-rw-r--r--nGenEx/Graphic.h139
-rw-r--r--nGenEx/IA3D.H128
-rw-r--r--nGenEx/ImageBox.cpp298
-rw-r--r--nGenEx/ImageBox.h71
-rw-r--r--nGenEx/ImgView.cpp79
-rw-r--r--nGenEx/ImgView.h51
-rw-r--r--nGenEx/Joystick.cpp914
-rw-r--r--nGenEx/Joystick.h82
-rw-r--r--nGenEx/Keyboard.cpp217
-rw-r--r--nGenEx/Keyboard.h74
-rw-r--r--nGenEx/Layout.cpp246
-rw-r--r--nGenEx/Layout.h66
-rw-r--r--nGenEx/Light.cpp72
-rw-r--r--nGenEx/Light.h96
-rw-r--r--nGenEx/ListBox.cpp1333
-rw-r--r--nGenEx/ListBox.h170
-rw-r--r--nGenEx/Locale.cpp237
-rw-r--r--nGenEx/Locale.h53
-rw-r--r--nGenEx/MCIWave.cpp155
-rw-r--r--nGenEx/MCIWave.h25
-rw-r--r--nGenEx/MachineInfo.cpp795
-rw-r--r--nGenEx/MachineInfo.h42
-rw-r--r--nGenEx/Menu.cpp143
-rw-r--r--nGenEx/Menu.h124
-rw-r--r--nGenEx/MotionController.h231
-rw-r--r--nGenEx/Mouse.cpp192
-rw-r--r--nGenEx/Mouse.h75
-rw-r--r--nGenEx/MouseController.cpp247
-rw-r--r--nGenEx/MouseController.h70
-rw-r--r--nGenEx/MultiController.cpp130
-rw-r--r--nGenEx/MultiController.h68
-rw-r--r--nGenEx/PCX.CPP516
-rw-r--r--nGenEx/ParseUtil.cpp481
-rw-r--r--nGenEx/ParseUtil.h52
-rw-r--r--nGenEx/Particles.cpp264
-rw-r--r--nGenEx/Particles.h86
-rw-r--r--nGenEx/Pcx.h68
-rw-r--r--nGenEx/Physical.cpp775
-rw-r--r--nGenEx/Physical.h205
-rw-r--r--nGenEx/PngImage.cpp246
-rw-r--r--nGenEx/PngImage.h44
-rw-r--r--nGenEx/Polygon.cpp745
-rw-r--r--nGenEx/Polygon.h159
-rw-r--r--nGenEx/Projector.cpp470
-rw-r--r--nGenEx/Projector.h106
-rw-r--r--nGenEx/Random.cpp148
-rw-r--r--nGenEx/Random.h35
-rw-r--r--nGenEx/Res.cpp28
-rw-r--r--nGenEx/Res.h37
-rw-r--r--nGenEx/Resource.h21
-rw-r--r--nGenEx/RichTextBox.cpp454
-rw-r--r--nGenEx/RichTextBox.h65
-rw-r--r--nGenEx/Scene.cpp261
-rw-r--r--nGenEx/Scene.h76
-rw-r--r--nGenEx/Screen.cpp161
-rw-r--r--nGenEx/Screen.h65
-rw-r--r--nGenEx/ScrollWindow.cpp632
-rw-r--r--nGenEx/ScrollWindow.h132
-rw-r--r--nGenEx/Sha1.cpp589
-rw-r--r--nGenEx/Sha1.h89
-rw-r--r--nGenEx/Shadow.cpp176
-rw-r--r--nGenEx/Shadow.h70
-rw-r--r--nGenEx/Skin.cpp175
-rw-r--r--nGenEx/Skin.h92
-rw-r--r--nGenEx/Slider.cpp557
-rw-r--r--nGenEx/Slider.h107
-rw-r--r--nGenEx/Solid.cpp2481
-rw-r--r--nGenEx/Solid.h313
-rw-r--r--nGenEx/Sound.cpp284
-rw-r--r--nGenEx/Sound.h150
-rw-r--r--nGenEx/SoundCard.cpp103
-rw-r--r--nGenEx/SoundCard.h76
-rw-r--r--nGenEx/SoundD3D.cpp1295
-rw-r--r--nGenEx/SoundD3D.h162
-rw-r--r--nGenEx/Sprite.cpp380
-rw-r--r--nGenEx/Sprite.h90
-rw-r--r--nGenEx/TexCubeDX9.cpp120
-rw-r--r--nGenEx/TexCubeDX9.h49
-rw-r--r--nGenEx/TexDX9.cpp418
-rw-r--r--nGenEx/TexDX9.h79
-rw-r--r--nGenEx/TimeSnap.h26
-rw-r--r--nGenEx/Types.h66
-rw-r--r--nGenEx/Universe.h32
-rw-r--r--nGenEx/Video.cpp65
-rw-r--r--nGenEx/Video.h242
-rw-r--r--nGenEx/VideoDX9.cpp3675
-rw-r--r--nGenEx/VideoDX9.h195
-rw-r--r--nGenEx/VideoDX9Enum.cpp1057
-rw-r--r--nGenEx/VideoDX9Enum.h185
-rw-r--r--nGenEx/VideoDX9VertexBuffer.cpp281
-rw-r--r--nGenEx/VideoDX9VertexBuffer.h79
-rw-r--r--nGenEx/VideoFactory.cpp71
-rw-r--r--nGenEx/VideoFactory.h42
-rw-r--r--nGenEx/VideoSettings.cpp268
-rw-r--r--nGenEx/VideoSettings.h131
-rw-r--r--nGenEx/View.h52
-rw-r--r--nGenEx/Water.cpp295
-rw-r--r--nGenEx/Water.h54
-rw-r--r--nGenEx/Wave.h52
-rw-r--r--nGenEx/WebBrowser.cpp133
-rw-r--r--nGenEx/WebBrowser.h46
-rw-r--r--nGenEx/Window.cpp936
-rw-r--r--nGenEx/Window.h104
-rw-r--r--nGenEx/nGenEx.dsp800
-rw-r--r--nGenEx/nGenEx.dsw29
-rw-r--r--nGenEx/nGenEx.vcxproj310
-rw-r--r--nGenEx/nGenEx.vcxproj.filters550
-rw-r--r--nGenEx/nGenEx.vcxproj.user3
-rw-r--r--oggvorbis/include/ogg/config_types.h11
-rw-r--r--oggvorbis/include/ogg/ogg.h202
-rw-r--r--oggvorbis/include/ogg/os_types.h106
-rw-r--r--oggvorbis/include/theora/theora.h539
-rw-r--r--oggvorbis/include/vorbis/codec.h240
-rw-r--r--oggvorbis/include/vorbis/vorbisenc.h93
-rw-r--r--oggvorbis/include/vorbis/vorbisfile.h143
-rw-r--r--oggvorbis/lib/ogg_static.libbin0 -> 29444 bytes
-rw-r--r--oggvorbis/lib/ogg_static_d.libbin0 -> 64190 bytes
-rw-r--r--oggvorbis/lib/theora_static.libbin0 -> 260528 bytes
-rw-r--r--oggvorbis/lib/theora_static_d.libbin0 -> 619478 bytes
-rw-r--r--oggvorbis/lib/vorbis_static.libbin0 -> 235482 bytes
-rw-r--r--oggvorbis/lib/vorbis_static_d.libbin0 -> 448152 bytes
-rw-r--r--oggvorbis/lib/vorbisfile_static.libbin0 -> 35106 bytes
-rw-r--r--oggvorbis/lib/vorbisfile_static_d.libbin0 -> 78478 bytes
-rw-r--r--zlib/ChangeLog764
-rw-r--r--zlib/FAQ337
-rw-r--r--zlib/INDEX51
-rw-r--r--zlib/Makefile154
-rw-r--r--zlib/Makefile.in154
-rw-r--r--zlib/README126
-rw-r--r--zlib/Zlib.dsp188
-rw-r--r--zlib/Zlib.dsw29
-rw-r--r--zlib/Zlib.ncbbin0 -> 99328 bytes
-rw-r--r--zlib/Zlib.optbin0 -> 70656 bytes
-rw-r--r--zlib/Zlib.plg68
-rw-r--r--zlib/Zlib.vcxproj147
-rw-r--r--zlib/Zlib.vcxproj.filters89
-rw-r--r--zlib/adler32.c74
-rw-r--r--zlib/algorithm.txt209
-rw-r--r--zlib/compress.c79
-rw-r--r--zlib/configure443
-rw-r--r--zlib/crc32.c333
-rw-r--r--zlib/crc32.h441
-rw-r--r--zlib/deflate.c1502
-rw-r--r--zlib/deflate.h325
-rw-r--r--zlib/example.c567
-rw-r--r--zlib/gzio.c1009
-rw-r--r--zlib/infback.c622
-rw-r--r--zlib/inffast.c305
-rw-r--r--zlib/inffast.h11
-rw-r--r--zlib/inffixed.h94
-rw-r--r--zlib/inflate.c1274
-rw-r--r--zlib/inflate.h117
-rw-r--r--zlib/inftrees.c328
-rw-r--r--zlib/inftrees.h55
-rw-r--r--zlib/minigzip.c322
-rw-r--r--zlib/trees.c1215
-rw-r--r--zlib/trees.h128
-rw-r--r--zlib/uncompr.c61
-rw-r--r--zlib/zconf.h326
-rw-r--r--zlib/zconf.in.h326
-rw-r--r--zlib/zlib.3159
-rw-r--r--zlib/zlib.h1200
-rw-r--r--zlib/zutil.c319
-rw-r--r--zlib/zutil.h263
1025 files changed, 312587 insertions, 0 deletions
diff --git a/Datafile/Archive.cpp b/Datafile/Archive.cpp
new file mode 100644
index 0000000..d9f9088
--- /dev/null
+++ b/Datafile/Archive.cpp
@@ -0,0 +1,460 @@
+/* Project nGen
+ John DiCamillo Software Consulting
+ Copyright © 1997. All Rights Reserved.
+
+ SUBSYSTEM: DataFile.exe
+ FILE: Archive.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <io.h>
+#include <stdio.h>
+#include <math.h>
+#include <time.h>
+#include <windows.h>
+#include <windowsx.h>
+
+#include "zlib.h"
+#include <mmsystem.h>
+#include "Archive.h"
+
+int verbose = 1;
+int err;
+
+#define CHECK_ERR(err, msg) { \
+ if (err != Z_OK) { \
+ fprintf(stderr, "%s error: %d\n", msg, err); \
+ exit(1); \
+ } \
+}
+
+// +--------------------------------------------------------------------+
+
+DataArchive::DataArchive(const char* name)
+{
+ ZeroMemory(this, sizeof(DataArchive));
+
+ if (name)
+ LoadDatafile(name);
+}
+
+DataArchive::~DataArchive()
+{
+ delete [] block_map;
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::WriteEntry(int index, BYTE* buf)
+{
+ int f = _open(datafile, _O_RDWR|_O_CREAT|_O_BINARY, _S_IREAD|_S_IWRITE);
+
+ if (f != -1) {
+ header.dir_size_comp = DirBlocks() * BLOCK_SIZE;
+ dirbuf = new BYTE[header.dir_size_comp];
+
+ err = compress(dirbuf, &header.dir_size_comp,
+ (BYTE*) directory, header.nfiles * sizeof(DataEntry));
+ CHECK_ERR(err, "compress");
+
+ header.dir_blocks = Blocks(header.dir_size_comp) * BLOCK_SIZE;
+
+ _lseek(f, 0, SEEK_SET);
+ _write(f, &header, sizeof(DataHeader));
+ _lseek(f, sizeof(DataHeader) + header.dir_offset, SEEK_SET);
+ _write(f, dirbuf, header.dir_blocks);
+
+ delete [] dirbuf;
+
+ if (buf && directory[index].size_comp) {
+ _lseek(f, sizeof(DataHeader) + directory[index].offset, SEEK_SET);
+ _write(f, buf, directory[index].size_comp);
+ }
+ _close(f);
+ }
+ else
+ perror("WriteEntry");
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD DataArchive::Blocks(DWORD raw_size)
+{
+ int full_blocks = raw_size / BLOCK_SIZE;
+ int part_blocks = (raw_size % BLOCK_SIZE) > 0;
+
+ return full_blocks + part_blocks;
+}
+
+DWORD DataArchive::DirBlocks()
+{
+ DWORD result = Blocks(header.nfiles * sizeof(DataEntry));
+ if (result == 0) result = 1;
+ return result;
+}
+
+DWORD DataArchive::FileBlocks(int index)
+{
+ return Blocks(directory[index].size_comp);
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::CreateBlockMap()
+{
+ delete [] block_map;
+ block_map = 0;
+
+ if (header.nfiles == 0) return;
+
+ DWORD i,j;
+ DWORD dir_usage = header.dir_offset + DirBlocks() * BLOCK_SIZE;
+ DWORD max_usage = dir_usage;
+
+ for (i = 0; i < header.nfiles; i++) {
+ DWORD last_block = directory[i].offset + FileBlocks(i) * BLOCK_SIZE;
+ if (last_block > max_usage)
+ max_usage = last_block;
+ }
+
+ nblocks = max_usage/BLOCK_SIZE;
+ block_map = new DWORD[nblocks];
+ ZeroMemory(block_map, nblocks*sizeof(DWORD));
+
+ DWORD first_block = header.dir_offset/BLOCK_SIZE +
+ (header.dir_offset%BLOCK_SIZE > 0);
+
+ for (j = 0; j < DirBlocks(); j++)
+ block_map[first_block+j] = 1;
+
+ for (i = 0; i < header.nfiles; i++) {
+ DWORD first_block = directory[i].offset/BLOCK_SIZE +
+ (directory[i].offset%BLOCK_SIZE > 0);
+
+ for (j = 0; j < FileBlocks(i); j++)
+ block_map[first_block+j] = i+2;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int DataArchive::FindDataBlocks(int need)
+{
+ if ((int) (nblocks)-need > 0) {
+ DWORD start;
+ int i;
+
+ for (start = 0; start < nblocks-need; start++) {
+ for (i = 0; block_map[start+i] == 0 && i < need; i++);
+
+ if (i == need) return start*BLOCK_SIZE;
+
+ start += i;
+ }
+ }
+
+ return nblocks*BLOCK_SIZE;
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::LoadDatafile(const char* name)
+{
+ strncpy(datafile, name, NAMELEN-1);
+ header.nfiles = 0;
+
+ FILE* f = fopen(datafile, "rb");
+ if (f) {
+ fread(&header, sizeof(DataHeader), 1, f);
+
+ if (header.version != VERSION) {
+ printf("ERROR: datafile '%s' invalid version '%d'\n",
+ datafile, header.version);
+ fclose(f);
+ exit(-2);
+ }
+
+ DWORD len = DirBlocks() * BLOCK_SIZE;
+
+ dirbuf = new BYTE[len];
+ fseek(f, sizeof(DataHeader) + header.dir_offset, SEEK_SET);
+ fread(dirbuf, header.dir_size_comp, 1, f);
+
+ int err = uncompress((BYTE*) directory, &len,
+ dirbuf, header.dir_size_comp);
+ if (err != Z_OK)
+ ZeroMemory(directory, sizeof(directory));
+
+ delete [] dirbuf;
+ CreateBlockMap();
+ }
+ else {
+ printf("Creating Archive '%s'...\n", datafile);
+
+ header.version = VERSION;
+ header.nfiles = 0;
+ header.dir_blocks = 0;
+ header.dir_size_comp = 0;
+ header.dir_offset = 0;
+
+ nblocks = DirBlocks();
+
+ delete [] block_map;
+ block_map = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int DataArchive::FindEntry(const char* req_name)
+{
+ int entry = -1;
+
+ for (DWORD i = 0; i < header.nfiles; i++)
+ if (!stricmp(directory[i].name, req_name))
+ return i;
+
+ return entry;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE* DataArchive::CompressEntry(int i)
+{
+ char* name = directory[i].name;
+
+ FILE* f = fopen(name, "rb");
+
+ if (f) {
+ fseek(f, 0, SEEK_END);
+ DWORD len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ BYTE* buf = new BYTE[len];
+
+ fread(buf, len, 1, f);
+ fclose(f);
+
+ directory[i].size_orig = len;
+
+ directory[i].size_comp = (int) (len * 1.1);
+ BYTE* cbuf = new BYTE[directory[i].size_comp];
+
+ err = compress(cbuf, &directory[i].size_comp, buf, len);
+ CHECK_ERR(err, "compress");
+
+ delete [] buf;
+ return cbuf;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int DataArchive::ExpandEntry(int i, BYTE*& buf)
+{
+ DWORD len = 0;
+
+ FILE* f = fopen(datafile, "rb");
+
+ if (f) {
+ DWORD clen = directory[i].size_comp;
+ BYTE* cbuf = new BYTE[clen];
+
+ fseek(f, sizeof(DataHeader) + directory[i].offset, SEEK_SET);
+ fread(cbuf, clen, 1, f);
+
+ len = directory[i].size_orig;
+ buf = new BYTE[len];
+
+ int err = uncompress(buf, &len, cbuf, clen);
+ if (err != Z_OK) {
+ delete [] buf;
+ buf = 0;
+ }
+
+ delete [] cbuf;
+ fclose(f);
+ }
+
+ return len;
+}
+
+// +--------------------------------------------------------------------+
+
+int DataArchive::InsertEntry(const char* name)
+{
+ if (!name) return -1;
+
+ DWORD len = strlen(name);
+
+ for (int i = 0; i < MAX_FILES; i++) {
+ if (directory[i].size_orig == 0) {
+ strncpy(directory[i].name, name, NAMELEN);
+ directory[i].name[NAMELEN-1] = '\0';
+ directory[i].size_orig = 1;
+
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::RemoveEntry(int index)
+{
+ ZeroMemory(&directory[index], sizeof(DataEntry));
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::Insert(const char* name)
+{
+ DWORD old_blocks = 0, old_offset = 0, new_blocks = 0;
+ DWORD old_dir_blocks = 0, old_dir_offset = 0, new_dir_blocks = 0;
+ int added = 0;
+
+ int index = FindEntry(name);
+
+ if (index < 0) {
+ old_dir_blocks = DirBlocks();
+ old_dir_offset = header.dir_offset;
+
+ index = InsertEntry(name);
+
+ if (index >= (int) header.nfiles) {
+ header.nfiles = index+1;
+ added = 1;
+ }
+
+ new_dir_blocks = DirBlocks();
+
+ if (new_dir_blocks > old_dir_blocks) {
+ header.dir_offset = FindDataBlocks(new_dir_blocks);
+ CreateBlockMap();
+ }
+ }
+ else {
+ old_blocks = FileBlocks(index);
+ old_offset = directory[index].offset;
+ }
+
+ if (index >= 0) {
+ DataEntry& e = directory[index];
+
+ if (verbose) printf(" Inserting: %-48s ", e.name);
+
+ BYTE* buf = CompressEntry(index);
+
+ if (!buf) {
+ // this is (almost) unrecoverable,
+ // so we quit before screwing things up:
+ printf("ERROR: Could not compress %d:%s\n", index, directory[index].name);
+ exit(1);
+ }
+
+ new_blocks = FileBlocks(index);
+
+ // the file is new, or got bigger,
+ // need to find room for the data:
+ if (new_blocks > old_blocks) {
+ directory[index].offset = FindDataBlocks(new_blocks);
+ CreateBlockMap();
+ }
+
+ WriteEntry(index, buf);
+ delete [] buf;
+
+ if (verbose) {
+ int ratio = (int) (100.0 * (double) e.size_comp / (double) e.size_orig);
+ printf("%9d => %9d (%2d%%)\n", e.size_orig, e.size_comp, ratio);
+ }
+ }
+ else if (added)
+ header.nfiles--;
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::Extract(const char* name)
+{
+ int index = FindEntry(name);
+
+ if (index < 0) {
+ printf("Could not extract '%s', not found\n", name);
+ return;
+ }
+
+ BYTE* buf;
+ ExpandEntry(index, buf);
+
+ FILE* f = fopen(directory[index].name, "wb");
+ if (f) {
+ fwrite(buf, directory[index].size_orig, 1, f);
+ fclose(f);
+ }
+ else
+ printf("Could not extract '%s', could not open file for writing\n", name);
+
+ delete [] buf;
+
+ if (verbose) printf(" Extracted: %s\n", name);
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::Remove(const char* name)
+{
+ int index = FindEntry(name);
+
+ if (index < 0) {
+ printf("Could not remove '%s', not found\n", name);
+ return;
+ }
+
+ RemoveEntry(index);
+ WriteEntry(index, 0);
+
+ if (verbose) printf(" Removed: %s\n", name);
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::List()
+{
+ int total_orig = 0;
+ int total_comp = 0;
+
+ printf("DATAFILE: %s\n", datafile);
+ printf("Files: %d\n", header.nfiles);
+ printf("\n");
+ printf("Index Orig Size Comp Size Ratio Name\n");
+ printf("----- --------- --------- ----- ----------------\n");
+
+ for (DWORD i = 0; i < header.nfiles; i++) {
+ DataEntry& e = directory[i];
+ int ratio = (int) (100.0 * (double) e.size_comp / (double) e.size_orig);
+
+ printf("%5d %9d %9d %2d%% %s\n", i+1, e.size_orig, e.size_comp, ratio, e.name);
+
+ total_orig += e.size_orig;
+ total_comp += e.size_comp;
+ }
+
+ int total_ratio = (int) (100.0 * (double) total_comp / (double) total_orig);
+
+ printf("----- --------- --------- -----\n");
+ printf("TOTAL %9d %9d %2d%%\n\n", total_orig, total_comp, total_ratio);
+}
+
+
+// +--------------------------------------------------------------------+
+
diff --git a/Datafile/Archive.h b/Datafile/Archive.h
new file mode 100644
index 0000000..f3ec099
--- /dev/null
+++ b/Datafile/Archive.h
@@ -0,0 +1,83 @@
+/* Project nGen
+ John DiCamillo Software Consulting
+ Copyright © 1997. All Rights Reserved.
+
+ SUBSYSTEM: DataFile.exe
+ FILE: Archive.hpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef ARCHIVE_HPP
+#define ARCHIVE_HPP
+
+// +------------------------------------------------------------------+
+
+#define VERSION 0x0010
+#define BLOCK_SIZE 1024
+#define MAX_FILES 4096
+#define FILE_BLOCK 1024
+#define NAMELEN 64
+
+// +------------------------------------------------------------------+
+
+struct DataHeader
+{
+ DWORD version;
+ DWORD nfiles;
+ DWORD dir_blocks;
+ DWORD dir_size_comp;
+ DWORD dir_offset;
+};
+
+struct DataEntry
+{
+ char name[NAMELEN];
+ DWORD size_orig;
+ DWORD size_comp;
+ DWORD offset;
+};
+
+class DataArchive
+{
+public:
+ // ctor:
+ DataArchive(const char* name = 0);
+ ~DataArchive();
+
+ // operations:
+ void LoadDatafile(const char* name);
+ void Insert(const char* name);
+ void Extract(const char* name);
+ void Remove(const char* name);
+ void List();
+
+ void WriteEntry(int index, BYTE* buf);
+ int FindEntry(const char* req_name);
+ int ExpandEntry(int index, BYTE*& buf);
+ BYTE* CompressEntry(int index);
+ int InsertEntry(const char* name);
+ void RemoveEntry(int index);
+ DWORD Blocks(DWORD raw_size);
+ DWORD DirBlocks();
+ DWORD FileBlocks(int index);
+ int FindDataBlocks(int blocks_needed);
+ void CreateBlockMap();
+
+ DWORD NumFiles() { return header.nfiles; }
+ DataEntry* GetFile(int i) { if (i>=0 && i<(int)header.nfiles) return &directory[i]; return 0; }
+
+private:
+ // persistent data members:
+ DataHeader header;
+ DataEntry directory[MAX_FILES];
+ BYTE* dirbuf;
+
+ // transient members:
+ char datafile[NAMELEN];
+
+ DWORD* block_map;
+ DWORD nblocks;
+};
+
+#endif
diff --git a/Datafile/DataFile.dsp b/Datafile/DataFile.dsp
new file mode 100644
index 0000000..66afdf9
--- /dev/null
+++ b/Datafile/DataFile.dsp
@@ -0,0 +1,112 @@
+# Microsoft Developer Studio Project File - Name="DataFile" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=DataFile - Win32 Release
+!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 "DataFile.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 "DataFile.mak" CFG="DataFile - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "DataFile - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "DataFile - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "DataFile - 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" /YX /c
+# ADD CPP /nologo /W3 /GX /O2 /I "..\zlib" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /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 /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 /nologo /subsystem:console /machine:I386
+
+!ELSEIF "$(CFG)" == "DataFile - 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" /YX /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\zlib" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /FD /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 /nologo /subsystem:console /debug /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 /nologo /subsystem:console /debug /machine:I386
+
+!ENDIF
+
+# Begin Target
+
+# Name "DataFile - Win32 Release"
+# Name "DataFile - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=.\Archive.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\main.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=\USERS\MILO\GameDev\zlib\Release\zlib.lib
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\Archive.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/Datafile/DataFile.dsw b/Datafile/DataFile.dsw
new file mode 100644
index 0000000..8a0f764
--- /dev/null
+++ b/Datafile/DataFile.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "DataFile"=.\DataFile.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Datafile/DataFile.mak b/Datafile/DataFile.mak
new file mode 100644
index 0000000..765d647
--- /dev/null
+++ b/Datafile/DataFile.mak
@@ -0,0 +1,231 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=DataFile - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to DataFile - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "DataFile - Win32 Release" && "$(CFG)" !=\
+ "DataFile - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "DataFile.mak" CFG="DataFile - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "DataFile - Win32 Release" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE "DataFile - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "DataFile - Win32 Debug"
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "DataFile - 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 ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\DataFile.exe"
+
+CLEAN :
+ -@erase "$(INTDIR)\Archive.obj"
+ -@erase "$(INTDIR)\main.obj"
+ -@erase "$(OUTDIR)\DataFile.exe"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /W3 /GX /O2 /I "../zlib" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /ML /W3 /GX /O2 /I "../zlib" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/DataFile.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/DataFile.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem: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 /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=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 /incremental:no\
+ /pdb:"$(OUTDIR)/DataFile.pdb" /machine:I386 /out:"$(OUTDIR)/DataFile.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\Archive.obj" \
+ "$(INTDIR)\main.obj" \
+ "..\zlib\Release\zlib.lib"
+
+"$(OUTDIR)\DataFile.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "DataFile - 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 ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\DataFile.exe"
+
+CLEAN :
+ -@erase "$(INTDIR)\Archive.obj"
+ -@erase "$(INTDIR)\main.obj"
+ -@erase "$(INTDIR)\vc40.idb"
+ -@erase "$(INTDIR)\vc40.pdb"
+ -@erase "$(OUTDIR)\DataFile.exe"
+ -@erase "$(OUTDIR)\DataFile.ilk"
+ -@erase "$(OUTDIR)\DataFile.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "../zlib" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MLd /W3 /Gm /GX /Zi /Od /I "../zlib" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/DataFile.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/"\
+ /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/DataFile.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /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 /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=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 /incremental:yes\
+ /pdb:"$(OUTDIR)/DataFile.pdb" /debug /machine:I386\
+ /out:"$(OUTDIR)/DataFile.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\Archive.obj" \
+ "$(INTDIR)\main.obj" \
+ "..\zlib\Release\zlib.lib"
+
+"$(OUTDIR)\DataFile.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+################################################################################
+# Begin Target
+
+# Name "DataFile - Win32 Release"
+# Name "DataFile - Win32 Debug"
+
+!IF "$(CFG)" == "DataFile - Win32 Release"
+
+!ELSEIF "$(CFG)" == "DataFile - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\main.cpp
+DEP_CPP_MAIN_=\
+ ".\Archive.hpp"\
+
+
+"$(INTDIR)\main.obj" : $(SOURCE) $(DEP_CPP_MAIN_) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\USERS\MILO\GameDev\zlib\Release\zlib.lib
+
+!IF "$(CFG)" == "DataFile - Win32 Release"
+
+!ELSEIF "$(CFG)" == "DataFile - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\Archive.cpp
+DEP_CPP_ARCHI=\
+ "..\zlib\zconf.h"\
+ ".\../zlib\zlib.h"\
+ ".\Archive.hpp"\
+ {$(INCLUDE)}"\sys\stat.h"\
+ {$(INCLUDE)}"\sys\types.h"\
+
+
+"$(INTDIR)\Archive.obj" : $(SOURCE) $(DEP_CPP_ARCHI) "$(INTDIR)"
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/Datafile/DataFile.mdp b/Datafile/DataFile.mdp
new file mode 100644
index 0000000..25c0627
--- /dev/null
+++ b/Datafile/DataFile.mdp
Binary files differ
diff --git a/Datafile/Main.cpp b/Datafile/Main.cpp
new file mode 100644
index 0000000..dd4370b
--- /dev/null
+++ b/Datafile/Main.cpp
@@ -0,0 +1,453 @@
+/* Project nGen
+ John DiCamillo Software Consulting
+ Copyright © 1997. All Rights Reserved.
+
+ SUBSYSTEM: DataFile.exe
+ FILE: main.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <time.h>
+#include <windows.h>
+#include <windowsx.h>
+#include "Archive.h"
+
+//#define MOD_MAKER 1
+
+// +------------------------------------------------------------------+
+
+void insertFile(DataArchive& a, const char* sPath, WIN32_FIND_DATA* find)
+{
+ char sFile[256];
+ char sFlat[256];
+ DWORD find_attrib_forbidden =
+ FILE_ATTRIBUTE_DIRECTORY |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_OFFLINE;
+
+ if (sPath && *sPath)
+ sprintf(sFile, "%s/%s", sPath, find->cFileName);
+ else
+ strcpy(sFile, find->cFileName);
+
+ if (find->dwFileAttributes & find_attrib_forbidden) {
+ printf(" Skipping: %-48s \n", sFile);
+ return;
+ }
+
+ int n = strlen(sFile);
+
+ if (n >= NAMELEN) {
+ printf(" Skipping: %-48s (NAME TOO LONG!)\n", sFile);
+ return;
+ }
+
+ for (int i = 0; i < n; i++)
+ sFlat[i] = tolower(sFile[i]);
+
+ if (strstr(sFlat, ".exe")) {
+ printf(" Skipping: %-48s (executable file)\n", sFile);
+ }
+ else if (strstr(sFlat, ".cmd")) {
+ printf(" Skipping: %-48s (executable file)\n", sFile);
+ }
+ else if (strstr(sFlat, ".bat")) {
+ printf(" Skipping: %-48s (executable file)\n", sFile);
+ }
+ else if (strstr(sFlat, ".bin")) {
+ printf(" Skipping: %-48s (unknown file)\n", sFile);
+ }
+ else if (strstr(sFlat, ".db")) {
+ printf(" Skipping: %-48s (unknown file)\n", sFile);
+ }
+ else if (strstr(sFlat, ".dat")) {
+ printf(" Skipping: %-48s (data file)\n", sFile);
+ }
+ else if (strstr(sFlat, ".zip")) {
+ printf(" Skipping: %-48s (zip file)\n", sFile);
+ }
+ else if (strstr(sFlat, ".arc")) {
+ printf(" Skipping: %-48s (archive file)\n", sFile);
+ }
+ else if (strstr(sFlat, ".psd")) {
+ printf(" Skipping: %-48s (PSD file)\n", sFile);
+ }
+ else {
+ a.Insert(sFile);
+ }
+}
+
+// +------------------------------------------------------------------+
+
+void ins(DataArchive& a, int argc, char* argv[])
+{
+ char sPath[256];
+ char* pDirSep = 0;
+
+ for (int i = 0; i < argc; i++) {
+ if (strchr(argv[i], '*')) {
+ strcpy(sPath, argv[i]);
+
+ if ((pDirSep = strrchr(sPath, '\\')) != 0)
+ *pDirSep = 0;
+
+ else if ((pDirSep = strrchr(sPath, '/')) != 0)
+ *pDirSep = 0;
+
+ else
+ sPath[0] = 0;
+
+ WIN32_FIND_DATA find;
+ HANDLE h = FindFirstFile(argv[i], &find);
+ if (h != INVALID_HANDLE_VALUE) {
+ insertFile(a, sPath, &find);
+
+ while (FindNextFile(h,&find)) {
+ insertFile(a, sPath, &find);
+ }
+
+ FindClose(h);
+ }
+ }
+ else {
+ a.Insert(argv[i]);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void build(DataArchive& a, const char* sBasePath);
+
+void buildFile(DataArchive& a, const char* sPath, WIN32_FIND_DATA& find)
+{
+ if (find.cFileName[0] == '.') {
+ }
+
+ else if (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ char subdir[256];
+ if (sPath && *sPath)
+ sprintf(subdir, "%s/%s", sPath, find.cFileName);
+ else
+ sprintf(subdir, "%s", find.cFileName);
+
+ build(a, subdir);
+ }
+
+ else {
+ insertFile(a, sPath, &find);
+ }
+}
+
+void build(DataArchive& a, const char* sBasePath)
+{
+ char sPath[256];
+ char sFind[256];
+
+ if (sBasePath && *sBasePath) {
+ strcpy(sPath, sBasePath);
+ sprintf(sFind, "%s\\*.*", sPath);
+ }
+ else {
+ sPath[0] = 0;
+ strcpy(sFind, "*.*");
+ }
+
+ WIN32_FIND_DATA find;
+ HANDLE h = FindFirstFile(sFind, &find);
+ if (h != INVALID_HANDLE_VALUE) {
+ do
+ buildFile(a, sPath, find);
+
+ while (FindNextFile(h, &find));
+
+ FindClose(h);
+ }
+}
+
+void mak(DataArchive& a)
+{
+ build(a, 0);
+}
+
+// +--------------------------------------------------------------------+
+// for now, pattern must be either "*" or "*.???"
+
+int match(const char* sFile, const char* sPattern)
+{
+ int nPatternType = 0;
+ char* sExt = 0;
+
+ const int PATTERN_NOWILD = 0;
+ const int PATTERN_STAR = 1;
+ const int PATTERN_STAR_DOT_STAR = 2;
+ const int PATTERN_STAR_DOT_EXT = 3;
+
+ // what kind of pattern matching?
+ if (strchr(sPattern, '*')) {
+ if (strchr(sPattern, '.')) {
+ if (strcmp(sPattern, "*.*") == 0) {
+ nPatternType = PATTERN_STAR_DOT_STAR;
+ }
+ else {
+ nPatternType = PATTERN_STAR_DOT_EXT;
+ sExt = strchr(sPattern, '.');
+ }
+ }
+
+ else {
+ nPatternType = PATTERN_STAR;
+ }
+ }
+
+ int file_matches_pattern = 0;
+
+ switch (nPatternType) {
+ case PATTERN_NOWILD:
+ default:
+ file_matches_pattern = (stricmp(sFile, sPattern) == 0);
+ break;
+
+
+ case PATTERN_STAR:
+ case PATTERN_STAR_DOT_STAR:
+ file_matches_pattern = 1;
+ break;
+
+ case PATTERN_STAR_DOT_EXT:
+ file_matches_pattern = (strstr(sFile, sExt) != 0);
+ break;
+ }
+
+ return file_matches_pattern;
+}
+
+void ext(DataArchive& a, int argc, char* argv[])
+{
+ if (argc) {
+ char sPath[256];
+ char sPatt[256];
+ char* pDirSep;
+ int nPath;
+
+ for (int i = 0; i < argc; i++) {
+ if (strchr(argv[i], '*')) {
+ strcpy(sPath, argv[i]);
+
+ if ((pDirSep = strrchr(sPath, '\\')) != 0) {
+ strcpy(sPatt, pDirSep+1);
+ *pDirSep = 0;
+ nPath = strlen(sPath);
+ }
+
+ else if ((pDirSep = strrchr(sPath, '/')) != 0) {
+ strcpy(sPatt, pDirSep+1);
+ *pDirSep = 0;
+ nPath = strlen(sPath);
+ }
+
+ else {
+ strcpy(sPatt, sPath);
+ sPath[0] = 0;
+ nPath = 0;
+ }
+
+ // for each file in the archive:
+ for (unsigned j = 0; j < a.NumFiles(); j++) {
+ DataEntry* pde = a.GetFile(j);
+
+ if (pde) {
+ // if we are extracting from a sub-directory,
+ if (nPath) {
+ // and this file is in the sub-directory,
+ if (strnicmp(pde->name, sPath, nPath) == 0) {
+ // and this file matches the pattern:
+ if (match(pde->name+nPath+1, sPatt)) {
+ char sName[256];
+ strcpy(sName, pde->name);
+ a.Extract(sName);
+ }
+ }
+ }
+
+ // if we are extracting from the main directory,
+ else {
+ // and this file is in the main directory,
+ if (strchr(pde->name, '/') == 0) {
+ // and this file matches the pattern:
+ if (match(pde->name, sPatt)) {
+ char sName[256];
+ strcpy(sName, pde->name);
+ a.Extract(sName);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ else {
+ // for each file in the archive:
+ for (unsigned j = 0; j < a.NumFiles(); j++) {
+ DataEntry* pde = a.GetFile(j);
+
+ if (pde) {
+ if (stricmp(pde->name, argv[i]) == 0) {
+ a.Extract(argv[i]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // full archive extraction:
+ else {
+ for (int i = 0; i < (int)a.NumFiles(); i++)
+ a.Extract(a.GetFile(i)->name);
+ }
+}
+
+void del(DataArchive& a, int argc, char* argv[])
+{
+ char sPath[256];
+ char sPatt[256];
+ char* pDirSep;
+ int nPath;
+
+ for (int i = 0; i < argc; i++) {
+ if (strchr(argv[i], '*')) {
+ strcpy(sPath, argv[i]);
+
+ if ((pDirSep = strrchr(sPath, '\\')) != 0) {
+ strcpy(sPatt, pDirSep+1);
+ *pDirSep = 0;
+ nPath = strlen(sPath);
+ }
+
+ else if ((pDirSep = strrchr(sPath, '/')) != 0) {
+ strcpy(sPatt, pDirSep+1);
+ *pDirSep = 0;
+ nPath = strlen(sPath);
+ }
+
+ else {
+ strcpy(sPatt, sPath);
+ sPath[0] = 0;
+ nPath = 0;
+ }
+
+ // for each file in the archive:
+ for (unsigned j = 0; j < a.NumFiles(); j++) {
+ DataEntry* pde = a.GetFile(j);
+
+ if (pde) {
+ // if we are deleting from a sub-directory,
+ if (nPath) {
+ // and this file is in the sub-directory,
+ if (strnicmp(pde->name, sPath, nPath) == 0) {
+ // and this file matches the pattern:
+ if (match(pde->name+nPath+1, sPatt)) {
+ char sName[256];
+ strcpy(sName, pde->name);
+ a.Remove(sName);
+ }
+ }
+ }
+
+ // if we are deleting from the main directory,
+ else {
+ // and this file is in the main directory,
+ if (strchr(pde->name, '/') == 0) {
+ // and this file matches the pattern:
+ if (match(pde->name, sPatt)) {
+ char sName[256];
+ strcpy(sName, pde->name);
+ a.Remove(sName);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ else {
+ a.Remove(argv[i]);
+ }
+ }
+}
+
+
+// +--------------------------------------------------------------------+
+
+void Usage()
+{
+ printf("Usage: datafile <dat-file> -option <file list>\n");
+ printf("options: -ins (insert files into datafile)\n");
+ printf(" -ext (extract files from datafile)\n");
+ printf(" -del (delete files from datafile)\n");
+ printf(" -mak (insert all files in current directory and all subdirectories)\n");
+ printf(" -list (display list of entries in datafile)\n");
+
+ exit(-1);
+}
+
+#define OPT_NONE 0
+#define OPT_INS 1
+#define OPT_EXT 2
+#define OPT_DEL 3
+#define OPT_MAK 4
+#define OPT_LIST 5
+
+int main(int argc, char* argv[])
+{
+#ifdef MOD_MAKER
+ printf("MODFILE\n");
+
+ if (argc < 2) {
+ printf("Usage: modfile <dat-file>\n");
+ return 0;
+ }
+
+ ::unlink(argv[1]);
+ DataArchive a(argv[1]);
+ mak(a);
+
+ return 0;
+#else
+ printf("DATAFILE\n");
+
+ if (argc < 3)
+ Usage();
+
+ DataArchive a(argv[1]);
+ int option = OPT_NONE;
+
+ if (!stricmp(argv[2], "-ins")) option = OPT_INS;
+ else if (!stricmp(argv[2], "-ext")) option = OPT_EXT;
+ else if (!stricmp(argv[2], "-del")) option = OPT_DEL;
+ else if (!stricmp(argv[2], "-mak")) option = OPT_MAK;
+ else if (!stricmp(argv[2], "-list")) option = OPT_LIST;
+
+ argc -= 3;
+ argv += 3;
+
+ switch (option) {
+ default:
+ case OPT_NONE: Usage(); break;
+ case OPT_INS: ins(a, argc, argv); break;
+ case OPT_EXT: ext(a, argc, argv); break;
+ case OPT_DEL: del(a, argc, argv); break;
+ case OPT_MAK: mak(a); break;
+ case OPT_LIST: a.List(); break;
+ }
+#endif
+
+ return 0;
+}
+
diff --git a/Doc/3DS File Format.txt b/Doc/3DS File Format.txt
new file mode 100644
index 0000000..64cd23a
--- /dev/null
+++ b/Doc/3DS File Format.txt
@@ -0,0 +1,1557 @@
+
+
+--------------------------------------------------------------------------------
+
+
+The Unofficial 3DStudio 3DS File Format v1.0
+
+By Jeff Lewis (werewolf@worldgate.com)
+
+Notice
+
+This document is an attempt to document the AutoDesk 3DS file format. This was
+made difficult in that I don't own or have access to a copy of the program, only
+to sample files. Fortunately, someone used AutoDesk's own 3DS file development
+kit to create a program which dumps the contents of a 3DS file into a human
+readable form - albeit somewhat inaccurately. The codes listed and their names
+come from that program and have been confirmed by testing.
+
+It should be known that the 3DS format is, as far as I know, a proprietary
+format of AutoDesk and that the format details are not widely known or are
+protected by AutoDesk.
+
+It is not my intent to infringe on AutoDesk's rights, but simply to make a
+large collection of 3D image files accessable to people who do not use 3D
+Studio - or cannot use it because AutoDesk has not chosen to provide a version
+of 3D Studio for the computer they use (ie: The Macintosh in my case).
+
+Warning
+
+This document is not intended to be a definitive definition of the 3DS format
+and is not authorised by AutoDesk. While every effort has been made to ensure
+its accuracy, or at least warn you when there's doubt about its accuracy, no
+guarantee of accuracy in any of it can be given. Use this document at your own
+risk.
+
+
+Document layout and format information
+
+In the following document, chunk names which are in bold mean the chunk format
+has been determined with certainty. Chunk names which are not bold but have
+a struct following means that this is a guess but is not substantiated. All
+others are unknown.
+
+A short is always a two byte integer.
+A long is always a four byte integer.
+A float is always a four byte IEEE floating point number.
+A cstr is a zero byte terminated ASCII string without a length.
+A char is a single byte integer.
+
+
+3DS File Format
+
+A 3DS file consists of blocks of data called chunks. Every chunk starts the
+same way:
+
+ short chunk_id;
+ long chunk_len;
+
+The chunk_id is a unique code which identifies the type of data in this chunk
+and also may indicate the existence of subordinate chunks. The chunk_len
+indicates the length of following data to be associated with this chunk. Note,
+this may contain more data than just this chunk. If the length of data is
+greater than that needed to fill in the information for the chunk, additional
+subordinate chunks are attached to this chunk immediately following any data
+needed for this chunk, and should be parsed out. These subordinate chunks may
+themselves contain subordinate chunks.
+
+Unfortunately, there is no indication of the length of data which is owned by
+the current chunk, only the total length of data attached to the chunk, which
+means that the only way to parse out subordinate chunks is to know the exact
+format of the owning chunk. On the other hand, if a chunk is unknown, the
+parsing program canskip the entire chunk and subordinate chunks in one jump.
+
+In the following list, I try when possible to indicate that a chunk is likely to
+have subordinate chunks and what kinds of subordinate chunks I've seen attached
+to it.
+
+Another problem lies in cstr names. I've seen cases where the space used by a
+name is riddled with fragments of old names. It seems that the space reserved
+for a name is not cleared if a smaller name replaces it. If the name is removed,
+you'll get a zero byte indicating an immediate end of string, followed by an
+undetermined number of characters and nulls. This seems to happen only when the
+cstr is at the end of a block of data and so you can assume that the length of
+the chunk contains no other subchunks. See viewport_data for an example of this.
+
+
+0xxxH Group
+
+ 0000H
+ NULL_CHUNK
+ 0001H
+ Unknown chunk
+ float ???
+ 0002H
+ M3D_VERSION
+ short version;
+ 0005H
+ M3D_KFVERSION
+ 0010H
+ COLOR_F
+ float red, grn, blu;
+ 0011H
+ COLOR_24
+ char red, grn, blu;
+ 0012H
+ LIN_COLOR_24
+ char red, grn, blu;
+ 0013H
+ LIN_COLOR_F
+ float red, grn, blu;
+ 0030H
+ INT_PERCENTAGE
+ short percentage;
+ 0031H
+ FLOAT_PERCENTAGE
+ float percentage;
+ 0100H
+ MASTER_SCALE
+ float scale;
+ 0995H
+ ChunkType
+ 0996H
+ ChunkUnique
+ 0997H
+ NotChunk
+ 0998H
+ Container
+ 0999H
+ IsChunk
+ 0c3cH
+ C_SXP_SELFI_MASKDATA
+
+
+
+1xxxH Group
+
+ 1100H
+ BIT_MAP
+ cstr filename;
+ 1101H
+ USE_BIT_MAP
+ 1200H
+ SOLID_BGND; followed by color_f
+ 1201H
+ USE_SOLID_BGND
+ 1300H
+ V_GRADIENT; followed by three color_f: start, mid, end
+ float midpoint;
+ 1301H
+ USE_V_GRADIENT
+ 1400H
+ LO_SHADOW_BIAS
+ float bias;
+ 1410H
+ HI_SHADOW_BIAS
+ 1420H
+ SHADOW_MAP_SIZE
+ short size;
+ 1430H
+ SHADOW_SAMPLES
+ 1440H
+ SHADOW_RANGE
+ 1450H
+ SHADOW_FILTER
+ float filter;
+ 1460H
+ RAY_BIAS
+ float bias;
+ 1500H
+ O_CONSTS
+ float plane_x, plane_y, plane_z;
+
+
+
+2xxxH Group
+
+ 2100H
+ AMBIENT_LIGHT
+ 2200H
+ FOG; followed by color_f, fog_bgnd
+ float near_plane, near_density;
+ float far_plane, far_density;
+ 2201H
+ USE_FOG
+ 2210H
+ FOG_BGND
+ 2300H
+ DISTANCE_CUE followed by dcue_bgnd
+ float near_plane, near_density;
+ float far_plane, far_density;
+ 2301H
+ USE_DISTANCE_CUE
+ 2302H
+ LAYER_FOG
+ float fog_z_from, fog_z_to;
+ float fog_density;
+ short fog_type;
+ 2303H
+ USE_LAYER_FOG
+ 2310H
+ DCUE_BGND
+ 2d2dH
+ SMAGIC
+ 2d3dH
+ LMAGIC
+
+
+
+3xxxH Group
+
+ 3000H
+ DEFAULT_VIEW
+ 3010H
+ VIEW_TOP
+ float targe_x, target_y, target_z;
+ float view_width;
+ 3020H
+ VIEW_BOTTOM
+ float targe_x, target_y, target_z;
+ float view_width;
+ 3030H
+ VIEW_LEFT
+ float targe_x, target_y, target_z;
+ float view_width;
+ 3040H
+ VIEW_RIGHT
+ float targe_x, target_y, target_z;
+ float view_width;
+ 3050H
+ VIEW_FRONT
+ float targe_x, target_y, target_z;
+ float view_width;
+ 3060H
+ VIEW_BACK
+ float targe_x, target_y, target_z;
+ float view_width;
+ 3070H
+ VIEW_USER
+ float targe_x, target_y, target_z;
+ float view_width;
+ 3080H
+ VIEW_CAMERA
+ cstr camera_name;
+ 3090H
+ VIEW_WINDOW
+ 3d3dH
+ MDATA; Mesh Data Magic Number (.3DS files sub of 4d4d)
+ 3d3eH
+ MESH_VERSION
+ 3daaH
+ MLIBMAGIC; Material Library Magic Number (.MLI files)
+ 3dc2H
+ PRJMAGIC; 3dS Project Magic Number (.PRJ files)
+ 3dffH
+ MATMAGIC; Material File Magic Number (.MAT files)
+
+
+
+4xxxH Group
+
+ 4000H
+ NAMED_OBJECT
+ cstr name;
+ 4010H
+ OBJ_HIDDEN
+ 4011H
+ OBJ_VIS_LOFTER
+ 4012H
+ OBJ_DOESNT_CAST
+ 4013H
+ OBJ_MATTE
+ 4014H
+ OBJ_FAST
+ 4015H
+ OBJ_PROCEDURAL
+ 4016H
+ OBJ_FROZEN
+ 4017H
+ OBJ_DONT_RCVSHADOW
+ 4100H
+ N_TRI_OBJECT
+ named triangle object
+ followed by point_array, point_flag_array, mesh_matrix,
+ face_array
+ 4110H
+ POINT_ARRAY
+ short npoints;
+ struct {
+ float x, y, z;
+ } points[npoints];
+ 4111H
+ POINT_FLAG_ARRAY
+ short nflags;
+ short flags[nflags];
+ 4120H
+ FACE_ARRAY may be followed by smooth_group
+ short nfaces;
+ struct {
+ short vertex1, vertex2, vertex3;
+ short flags;
+ } facearray[nfaces];
+ 4130H
+ MSH_MAT_GROUP mesh_material_group
+ cstr material_name;
+ short nfaces;
+ short facenum[nfaces];
+ 4131H
+ OLD_MAT_GROUP
+ 4140H
+ TEX_VERTS
+ short nverts;
+ struct {
+ float x, y;
+ } vertices[nverts];
+ 4150H
+ SMOOTH_GROUP
+ short grouplist[n]; determined by length, seems to be 4 per face
+ 4160H
+ MESH_MATRIX
+ float matrix[4][3];
+ 4165H
+ MESH_COLOR
+ short color_index;
+ 4170H
+ MESH_TEXTURE_INFO
+ short map_type;
+ float x_tiling, y_tiling;
+ float icon_x, icon_y, icon_z;
+ float matrix[4][3];
+ float scaling, plan_icon_w, plan_icon_h, cyl_icon_h;
+ 4181H
+ PROC_NAME
+ 4182H
+ PROC_DATA
+ 4190H
+ MSH_BOXMAP
+ 4400H
+ N_D_L_OLD
+ 4500H
+ N_CAM_OLD
+ 4600H
+ N_DIRECT_LIGHT; followed by color_f
+ float x, y, z;
+ 4610H
+ DL_SPOTLIGHT
+ float target_x, target_y, target_z;
+ float hotspot_ang;
+ float falloff_ang;
+ 4620H
+ DL_OFF
+ 4625H
+ DL_ATTENUATE
+ 4627H
+ DL_RAYSHAD
+ 4630H
+ DL_SHADOWED
+ 4640H
+ DL_LOCAL_SHADOW
+ 4641H
+ DL_LOCAL_SHADOW2
+ 4650H
+ DL_SEE_CONE
+ 4651H
+ DL_SPOT_RECTANGULAR
+ 4652H
+ DL_SPOT_OVERSHOOT
+ 4653H
+ DL_SPOT_PROJECTOR
+ 4654H
+ DL_EXCLUDE
+ 4655H
+ DL_RANGE
+ 4656H
+ DL_SPOT_ROLL
+ float roll_ang;
+ 4657H
+ DL_SPOT_ASPECT
+ 4658H
+ DL_RAY_BIAS
+ float bias;
+ 4659H
+ DL_INNER_RANGE
+ float range;
+ 465aH
+ DL_OUTER_RANGE
+ float range;
+ 465bH
+ DL_MULTIPLIER
+ float multiple;
+ 4680H
+ N_AMBIENT_LIGHT
+ 4700H
+ N_CAMERA
+ float camera_x, camera_y, camera_z;
+ float target_x, target_y, target_z;
+ float bank_angle;
+ float focus;
+ 4710H
+ CAM_SEE_CONE
+ 4720H
+ CAM_RANGES
+ float near_range, far_range;
+ 4d4dH
+ M3DMAGIC; 3DS Magic Number (.3DS file)
+ 4f00H
+ HIERARCHY
+ 4f10H
+ PARENT_OBJECT
+ 4f20H
+ PIVOT_OBJECT
+ 4f30H
+ PIVOT_LIMITS
+ 4f40H
+ PIVOT_ORDER
+ 4f50H
+ XLATE_RANGE
+
+
+
+5xxxH Group
+
+ 5000H
+ POLY_2D
+ 5010H
+ SHAPE_OK
+ 5011H
+ SHAPE_NOT_OK
+ 5020H
+ SHAPE_HOOK
+
+
+
+6xxxH Group
+
+ 6000H
+ PATH_3D
+ 6005H
+ PATH_MATRIX
+ 6010H
+ SHAPE_2D
+ 6020H
+ M_SCALE
+ 6030H
+ M_TWIST
+ 6040H
+ M_TEETER
+ 6050H
+ M_FIT
+ 6060H
+ M_BEVEL
+ 6070H
+ XZ_CURVE
+ 6080H
+ YZ_CURVE
+ 6090H
+ INTERPCT
+ 60a0H
+ DEFORM_LIMIT
+ 6100H
+ USE_CONTOUR
+ 6110H
+ USE_TWEEN
+ 6120H
+ USE_SCALE
+ 6130H
+ USE_TWIST
+ 6140H
+ USE_TEETER
+ 6150H
+ USE_FIT
+ 6160H
+ USE_BEVEL
+
+
+
+7xxxH Group
+
+ 7000H
+ VIEWPORT_LAYOUT_OLD
+ 7001H
+ VIEWPORT_LAYOUT; followed by viewport_size, viewport_data
+ short form, top, ready, wstate, swapws, swapport, swapcur;
+ 7010H
+ VIEWPORT_DATA_OLD
+ 7011H
+ VIEWPORT_DATA
+ short flags, axis_lockout;
+ short win_x, win_y, win_w, winh_, win_view;
+ float zoom;
+ float worldcenter_x, worldcenter_y, worldcenter_z;
+ float horiz_ang, vert_ang;
+ cstr camera_name;
+ 7012H
+ VIEWPORT_DATA_3
+ short flags, axis_lockout;
+ short win_x, win_y, win_w, winh_, win_view;
+ float zoom;
+ float worldcenter_x, worldcenter_y, worldcenter_z;
+ float horiz_ang, vert_ang;
+ cstr camera_name;
+ 7020H
+ VIEWPORT_SIZE
+ short x, y, w, h;
+ 7030H
+ NETWORK_VIEW
+
+
+
+8xxxH Group
+
+ 8000H
+ XDATA_SECTION
+ 8001H
+ XDATA_ENTRY
+ 8002H
+ XDATA_APPNAME
+ 8003H
+ XDATA_STRING
+ 8004H
+ XDATA_FLOAT
+ 8005H
+ XDATA_DOUBLE
+ 8006H
+ XDATA_SHORT
+ 8007H
+ XDATA_LONG
+ 8008H
+ XDATA_VOID
+ 8009H
+ XDATA_GROUP
+ 800aH
+ XDATA_RFU6
+ 800bH
+ XDATA_RFU5
+ 800cH
+ XDATA_RFU4
+ 800dH
+ XDATA_RFU3
+ 800eH
+ XDATA_RFU2
+ 800fH
+ XDATA_RFU1
+ 80f0H
+ PARENT_NAME
+
+
+
+AxxxH Group
+
+ a000H
+ MAT_NAME
+ cstr material_name;
+ a010H
+ MAT_AMBIENT; followed by color chunk
+ a020H
+ MAT_DIFFUSE; followed by color chunk
+ a030H
+ MAT_SPECULAR; followed by color chunk
+ a040H
+ MAT_SHININESS; followed by percentage chunk
+ a041H
+ MAT_SHIN2PCT; followed by percentage chunk
+ a042H
+ MAT_SHIN3PCT; followed by percentage chunk
+ a050H
+ MAT_TRANSPARENCY; followed by percentage chunk
+ a052H
+ MAT_XPFALL; followed by percentage chunk
+ a053H
+ MAT_REFBLUR; followed by percentage chunk
+ a080H
+ MAT_SELF_ILLUM
+ a081H
+ MAT_TWO_SIDE
+ a082H
+ MAT_DECAL
+ a083H
+ MAT_ADDITIVE
+ a084H
+ MAT_SELF_ILPCT; followed by percentage chunk
+ a085H
+ MAT_WIRE
+ a086H
+ MAT_SUPERSMP
+ a087H
+ MAT_WIRESIZE
+ float wire_size;
+ a088H
+ MAT_FACEMAP
+ a08aH
+ MAT_XPFALLIN
+ a08cH
+ MAT_PHONGSOFT
+ a08eH
+ MAT_WIREABS
+ a100H
+ MAT_SHADING
+ short shading_value;
+ a200H
+ MAT_TEXMAP; followed by percentage chunk, mat_mapname,
+ mat_map_tiling, mat_map_texblur...
+ a204H
+ MAT_SPECMAP; followed by percentage_chunk, mat_mapname
+ a210H
+ MAT_OPACMAP; followed by percentage_chunk, mat_mapname
+ a220H
+ MAT_REFLMAP; followed by percentage_chunk, mat_mapname
+ a230H
+ MAT_BUMPMAP; followed by percentage_chunk, mat_mapname
+ a240H
+ MAT_USE_XPFALL
+ a250H
+ MAT_USE_REFBLUR
+ a252H
+ MAT_BUMP_PERCENT
+ a300H
+ MAT_MAPNAME
+ cstr filename;
+ a310H
+ MAT_ACUBIC
+ a320H
+ MAT_SXP_TEXT_DATA
+ a321H
+ MAT_SXP_TEXT2_DATA
+ a322H
+ MAT_SXP_OPAC_DATA
+ a324H
+ MAT_SXP_BUMP_DATA
+ a325H
+ MAT_SXP_SPEC_DATA
+ a326H
+ MAT_SXP_SHIN_DATA
+ a328H
+ MAT_SXP_SELFI_DATA
+ a32aH
+ MAT_SXP_TEXT_MASKDATA
+ a32cH
+ MAT_SXP_TEXT2_MASKDATA
+ a32eH
+ MAT_SXP_OPAC_MASKDATA
+ a330H
+ MAT_SXP_BUMP_MASKDATA
+ a332H
+ MAT_SXP_SPEC_MASKDATA
+ a334H
+ MAT_SXP_SHIN_MASKDATA
+ a336H
+ MAT_SXP_SELFI_MASKDATA
+ a338H
+ MAT_SXP_REFL_MASKDATA
+ a33aH
+ MAT_TEX2MAP
+ a33cH
+ MAT_SHINMAP
+ a33dH
+ MAT_SELFIMAP
+ a33eH
+ MAT_TEXMASK
+ a340H
+ MAT_TEX2MASK
+ a342H
+ MAT_OPACMASK
+ a344H
+ MAT_BUMPMASK
+ a346H
+ MAT_SHINMASK
+ a348H
+ MAT_SPECMASK
+ a34aH
+ MAT_SELFIMASK
+ a34cH
+ MAT_REFLMASK
+ a350H
+ MAT_MAP_TILINGOLD
+ a351H
+ MAT_MAP_TILING
+ short flags;
+ a352H
+ MAT_MAP_TEXBLUR_OLD
+ a353H
+ MAT_MAP_TEXBLUR
+ float blurring;
+ a354H
+ MAT_MAP_USCALE
+ a356H
+ MAT_MAP_VSCALE
+ a358H
+ MAT_MAP_UOFFSET
+ a35aH
+ MAT_MAP_VOFFSET
+ a35cH
+ MAT_MAP_ANG
+ a360H
+ MAT_MAP_COL1
+ a362H
+ MAT_MAP_COL2
+ a364H
+ MAT_MAP_RCOL
+ a366H
+ MAT_MAP_GCOL
+ a368H
+ MAT_MAP_BCOL
+ afffH
+ MAT_ENTRY
+
+
+
+BxxxH Group
+
+ b000H
+ KFDATA; followed by kfhdr
+ b001H
+ AMBIENT_NODE_TAG
+ b002H
+ OBJECT_NODE_TAG; followed by node_hdr, pivot, pos_track_tag,
+ rot_track_tag, scl_track_tag, morph_smooth...
+ b003H
+ CAMERA_NODE_TAG; followed by node_hdr, pos_track_tag, fov_track_tag,
+ roll_track_tag...
+ b004H
+ TARGET_NODE_TAG; followed by node_hdr, pos_track_tag...
+ b005H
+ LIGHT_NODE_TAG; followed by node_hdr, pos_track_tag, col_track_tag...
+ b006H
+ L_TARGET_NODE_TAG; followed by node_id, node_hdr, pos_track_tag
+ b007H
+ SPOTLIGHT_NODE_TAG; followed by node_id, node_hdr, pos_track_tag,
+ hot_track_tag, fall_track_tag, roll_track_tag, col_track_tag...
+ b008H
+ KFSEG
+ short start, end;
+ b009H
+ KFCURTIME
+ short curframe;
+ b00aH
+ KFHDR followed by viewport_layout, kfseg, kfcurtime, object_node_tag,
+ light_node_tag, target_node_tag, camera_node_tag, l_target_node_tag,
+ spotlight_node_tag, ambient_node_tag...
+ short revision;
+ cstr filename;
+ short animlen;
+ b010H
+ NODE_HDR
+ cstr objname;
+ short flags1;
+ short flags2;
+ short heirarchy; ?
+ b011H
+ INSTANCE_NAME
+ b012H
+ PRESCALE
+ b013H
+ PIVOT
+ float pivot_x, pivot_y, pivot_z;
+ b014H
+ BOUNDBOX
+ b015H
+ MORPH_SMOOTH
+ float morph_smoothing_angle_rad;
+ b020H
+ POS_TRACK_TAG
+ short flags;
+ short unknown[4];
+ short keys;
+ short unknown;
+ struct {
+ short framenum;
+ long unknown;
+ float pos_x, pos_y, pos_z;
+ } pos[keys];
+ b021H
+ ROT_TRACK_TAG
+ short flags;
+ short unknown[4];
+ short keys;
+ short unknown;
+ struct {
+ short framenum;
+ long unknown;
+ float rotation_rad;
+ float axis_x, axis_y, axis_z;
+ } rot[keys];
+ b022H
+ SCL_TRACK_TAG
+ short flags;
+ short unknown[4];
+ short keys;
+ short unknown;
+ struct {
+ short framenum;
+ long unknown;
+ float scale_x, scale_y, scale_z;
+ } scale[keys];
+ b023H
+ FOV_TRACK_TAG
+ short flags;
+ short unknown[4];
+ short keys;
+ short unknown;
+ struct {
+ short framenum;
+ long unknown;
+ float camera_field_of_view;
+ } fov[keys]
+ b024H
+ ROLL_TRACK_TAG
+ short flags;
+ short unknown[4];
+ short keys;
+ short unknown;
+ struct {
+ short framenum;
+ long unknown;
+ float camera_roll;
+ } roll[keys];
+ b025H
+ COL_TRACK_TAG
+ short flags;
+ short unknown[4];
+ short keys;
+ short unknown;
+ struct {
+ short framenum;
+ long unknown;
+ float red, rgn, blu;
+ } color[keys];
+ b026H
+ MORPH_TRACK_TAG
+ short flags;
+ short unknown[4];
+ short keys;
+ short unknown;
+ struct {
+ short framenum;
+ long unknown;
+ cstr obj_name;
+ } morph[keys];
+ b027H
+ HOT_TRACK_TAG
+ short flags;
+ short unknown[4];
+ short keys;
+ short unknown;
+ struct {
+ short framenum;
+ long unknown;
+ float hotspot_ang;
+ } hotspot[keys];
+ b028H
+ FALL_TRACK_TAG
+ short flags;
+ short unknown[4];
+ short keys;
+ short unknown;
+ struct {
+ short framenum;
+ long unknown;
+ float falloff_ang;
+ } falloff[keys];
+ b029H
+ HIDE_TRACK_TAG
+ b030H
+ NODE_ID
+ short id;
+
+
+
+CxxxH Group
+
+ c010H
+ C_MDRAWER
+ c020H
+ C_TDRAWER
+ c030H
+ C_SHPDRAWER
+ c040H
+ C_MODDRAWER
+ c050H
+ C_RIPDRAWER
+ c060H
+ C_TXDRAWER
+ c062H
+ C_PDRAWER
+ c064H
+ C_MTLDRAWER
+ c066H
+ C_FLIDRAWER
+ c067H
+ C_CUBDRAWER
+ c070H
+ C_MFILE
+ c080H
+ C_SHPFILE
+ c090H
+ C_MODFILE
+ c0a0H
+ C_RIPFILE
+ c0b0H
+ C_TXFILE
+ c0b2H
+ C_PFILE
+ c0b4H
+ C_MTLFILE
+ c0b6H
+ C_FLIFILE
+ c0b8H
+ C_PALFILE
+ c0c0H
+ C_TX_STRING
+ c0d0H
+ C_CONSTS
+ c0e0H
+ C_SNAPS
+ c0f0H
+ C_GRIDS
+ c100H
+ C_ASNAPS
+ c110H
+ C_GRID_RANGE
+ c120H
+ C_RENDTYPE
+ c130H
+ C_PROGMODE
+ c140H
+ C_PREVMODE
+ c150H
+ C_MODWMODE
+ c160H
+ C_MODMODEL
+ c170H
+ C_ALL_LINES
+ c180H
+ C_BACK_TYPE
+ c190H
+ C_MD_CS
+ c1a0H
+ C_MD_CE
+ c1b0H
+ C_MD_SML
+ c1c0H
+ C_MD_SMW
+ c1c3H
+ C_LOFT_WITH_TEXTURE
+ c1c4H
+ C_LOFT_L_REPEAT
+ c1c5H
+ C_LOFT_W_REPEAT
+ c1c6H
+ C_LOFT_UV_NORMALIZE
+ c1c7H
+ C_WELD_LOFT
+ c1d0H
+ C_MD_PDET
+ c1e0H
+ C_MD_SDET
+ c1f0H
+ C_RGB_RMODE
+ c200H
+ C_RGB_HIDE
+ c202H
+ C_RGB_MAPSW
+ c204H
+ C_RGB_TWOSIDE
+ c208H
+ C_RGB_SHADOW
+ c210H
+ C_RGB_AA
+ c220H
+ C_RGB_OVW
+ c230H
+ C_RGB_OVH
+ c23dH
+ CMAGIC
+ c240H
+ C_RGB_PICTYPE
+ c250H
+ C_RGB_OUTPUT
+ c253H
+ C_RGB_TODISK
+ c254H
+ C_RGB_COMPRESS
+ c255H
+ C_JPEG_COMPRESSION
+ c256H
+ C_RGB_DISPDEV
+ c259H
+ C_RGB_HARDDEV
+ c25aH
+ C_RGB_PATH
+ c25bH
+ C_BITMAP_DRAWER
+ c260H
+ C_RGB_FILE
+ c270H
+ C_RGB_OVASPECT
+ c271H
+ C_RGB_ANIMTYPE
+ c272H
+ C_RENDER_ALL
+ c273H
+ C_REND_FROM
+ c274H
+ C_REND_TO
+ c275H
+ C_REND_NTH
+ c276H
+ C_PAL_TYPE
+ c277H
+ C_RND_TURBO
+ c278H
+ C_RND_MIP
+ c279H
+ C_BGND_METHOD
+ c27aH
+ C_AUTO_REFLECT
+ c27bH
+ C_VP_FROM
+ c27cH
+ C_VP_TO
+ c27dH
+ C_VP_NTH
+ c27eH
+ C_REND_TSTEP
+ c27fH
+ C_VP_TSTEP
+ c280H
+ C_SRDIAM
+ c290H
+ C_SRDEG
+ c2a0H
+ C_SRSEG
+ c2b0H
+ C_SRDIR
+ c2c0H
+ C_HETOP
+ c2d0H
+ C_HEBOT
+ c2e0H
+ C_HEHT
+ c2f0H
+ C_HETURNS
+ c300H
+ C_HEDEG
+ c310H
+ C_HESEG
+ c320H
+ C_HEDIR
+ c330H
+ C_QUIKSTUFF
+ c340H
+ C_SEE_LIGHTS
+ c350H
+ C_SEE_CAMERAS
+ c360H
+ C_SEE_3D
+ c370H
+ C_MESHSEL
+ c380H
+ C_MESHUNSEL
+ c390H
+ C_POLYSEL
+ c3a0H
+ C_POLYUNSEL
+ c3a2H
+ C_SHPLOCAL
+ c3a4H
+ C_MSHLOCAL
+ c3b0H
+ C_NUM_FORMAT
+ c3c0H
+ C_ARCH_DENOM
+ c3d0H
+ C_IN_DEVICE
+ c3e0H
+ C_MSCALE
+ c3f0H
+ C_COMM_PORT
+ c400H
+ C_TAB_BASES
+ c410H
+ C_TAB_DIVS
+ c420H
+ C_MASTER_SCALES
+ c430H
+ C_SHOW_1STVERT
+ c440H
+ C_SHAPER_OK
+ c450H
+ C_LOFTER_OK
+ c460H
+ C_EDITOR_OK
+ c470H
+ C_KEYFRAMER_OK
+ c480H
+ C_PICKSIZE
+ c490H
+ C_MAPTYPE
+ c4a0H
+ C_MAP_DISPLAY
+ c4b0H
+ C_TILE_XY
+ c4c0H
+ C_MAP_XYZ
+ c4d0H
+ C_MAP_SCALE
+ c4e0H
+ C_MAP_MATRIX_OLD
+ c4e1H
+ C_MAP_MATRIX
+ c4f0H
+ C_MAP_WID_HT
+ c500H
+ C_OBNAME
+ c510H
+ C_CAMNAME
+ c520H
+ C_LTNAME
+ c525H
+ C_CUR_MNAME
+ c526H
+ C_CURMTL_FROM_MESH
+ c527H
+ C_GET_SHAPE_MAKE_FACES
+ c530H
+ C_DETAIL
+ c540H
+ C_VERTMARK
+ c550H
+ C_MSHAX
+ c560H
+ C_MSHCP
+ c570H
+ C_USERAX
+ c580H
+ C_SHOOK
+ c590H
+ C_RAX
+ c5a0H
+ C_STAPE
+ c5b0H
+ C_LTAPE
+ c5c0H
+ C_ETAPE
+ c5c8H
+ C_KTAPE
+ c5d0H
+ C_SPHSEGS
+ c5e0H
+ C_GEOSMOOTH
+ c5f0H
+ C_HEMISEGS
+ c600H
+ C_PRISMSEGS
+ c610H
+ C_PRISMSIDES
+ c620H
+ C_TUBESEGS
+ c630H
+ C_TUBESIDES
+ c640H
+ C_TORSEGS
+ c650H
+ C_TORSIDES
+ c660H
+ C_CONESIDES
+ c661H
+ C_CONESEGS
+ c670H
+ C_NGPARMS
+ c680H
+ C_PTHLEVEL
+ c690H
+ C_MSCSYM
+ c6a0H
+ C_MFTSYM
+ c6b0H
+ C_MTTSYM
+ c6c0H
+ C_SMOOTHING
+ c6d0H
+ C_MODICOUNT
+ c6e0H
+ C_FONTSEL
+ c6f0H
+ C_TESS_TYPE
+ c6f1H
+ C_TESS_TENSION
+ c700H
+ C_SEG_START
+ c705H
+ C_SEG_END
+ c710H
+ C_CURTIME
+ c715H
+ C_ANIMLENGTH
+ c720H
+ C_PV_FROM
+ c725H
+ C_PV_TO
+ c730H
+ C_PV_DOFNUM
+ c735H
+ C_PV_RNG
+ c740H
+ C_PV_NTH
+ c745H
+ C_PV_TYPE
+ c750H
+ C_PV_METHOD
+ c755H
+ C_PV_FPS
+ c765H
+ C_VTR_FRAMES
+ c770H
+ C_VTR_HDTL
+ c771H
+ C_VTR_HD
+ c772H
+ C_VTR_TL
+ c775H
+ C_VTR_IN
+ c780H
+ C_VTR_PK
+ c785H
+ C_VTR_SH
+ c790H
+ C_WORK_MTLS
+ c792H
+ C_WORK_MTLS_2
+ c793H
+ C_WORK_MTLS_3
+ c794H
+ C_WORK_MTLS_4
+ c7a1H
+ C_BGTYPE
+ c7b0H
+ C_MEDTILE
+ c7d0H
+ C_LO_CONTRAST
+ c7d1H
+ C_HI_CONTRAST
+ c7e0H
+ C_FROZ_DISPLAY
+ c7f0H
+ C_BOOLWELD
+ c7f1H
+ C_BOOLTYPE
+ c900H
+ C_ANG_THRESH
+ c901H
+ C_SS_THRESH
+ c903H
+ C_TEXTURE_BLUR_DEFAULT
+ ca00H
+ C_MAPDRAWER
+ ca01H
+ C_MAPDRAWER1
+ ca02H
+ C_MAPDRAWER2
+ ca03H
+ C_MAPDRAWER3
+ ca04H
+ C_MAPDRAWER4
+ ca05H
+ C_MAPDRAWER5
+ ca06H
+ C_MAPDRAWER6
+ ca07H
+ C_MAPDRAWER7
+ ca08H
+ C_MAPDRAWER8
+ ca09H
+ C_MAPDRAWER9
+ ca10H
+ C_MAPDRAWER_ENTRY
+ ca20H
+ C_BACKUP_FILE
+ ca21H
+ C_DITHER_256
+ ca22H
+ C_SAVE_LAST
+ ca23H
+ C_USE_ALPHA
+ ca24H
+ C_TGA_DEPTH
+ ca25H
+ C_REND_FIELDS
+ ca26H
+ C_REFLIP
+ ca27H
+ C_SEL_ITEMTOG
+ ca28H
+ C_SEL_RESET
+ ca29H
+ C_STICKY_KEYINF
+ ca2aH
+ C_WELD_THRESHOLD
+ ca2bH
+ C_ZCLIP_POINT
+ ca2cH
+ C_ALPHA_SPLIT
+ ca30H
+ C_KF_SHOW_BACKFACE
+ ca40H
+ C_OPTIMIZE_LOFT
+ ca42H
+ C_TENS_DEFAULT
+ ca44H
+ C_CONT_DEFAULT
+ ca46H
+ C_BIAS_DEFAULT
+ ca50H
+ C_DXFNAME_SRC
+ ca60H
+ C_AUTO_WELD
+ ca70H
+ C_AUTO_UNIFY
+ ca80H
+ C_AUTO_SMOOTH
+ ca90H
+ C_DXF_SMOOTH_ANG
+ caa0H
+ C_SMOOTH_ANG
+ cb00H
+ C_WORK_MTLS_5
+ cb01H
+ C_WORK_MTLS_6
+ cb02H
+ C_WORK_MTLS_7
+ cb03H
+ C_WORK_MTLS_8
+ cb04H
+ C_WORKMTL
+ cb10H
+ C_SXP_TEXT_DATA
+ cb11H
+ C_SXP_OPAC_DATA
+ cb12H
+ C_SXP_BUMP_DATA
+ cb13H
+ C_SXP_SHIN_DATA
+ cb20H
+ C_SXP_TEXT2_DATA
+ cb24H
+ C_SXP_SPEC_DATA
+ cb28H
+ C_SXP_SELFI_DATA
+ cb30H
+ C_SXP_TEXT_MASKDATA
+ cb32H
+ C_SXP_TEXT2_MASKDATA
+ cb34H
+ C_SXP_OPAC_MASKDATA
+ cb36H
+ C_SXP_BUMP_MASKDATA
+ cb38H
+ C_SXP_SPEC_MASKDATA
+ cb3aH
+ C_SXP_SHIN_MASKDATA
+ cb3eH
+ C_SXP_REFL_MASKDATA
+ cc00H
+ C_NET_USE_VPOST
+ cc10H
+ C_NET_USE_GAMMA
+ cc20H
+ C_NET_FIELD_ORDER
+ cd00H
+ C_BLUR_FRAMES
+ cd10H
+ C_BLUR_SAMPLES
+ cd20H
+ C_BLUR_DUR
+ cd30H
+ C_HOT_METHOD
+ cd40H
+ C_HOT_CHECK
+ cd50H
+ C_PIXEL_SIZE
+ cd60H
+ C_DISP_GAMMA
+ cd70H
+ C_FBUF_GAMMA
+ cd80H
+ C_FILE_OUT_GAMMA
+ cd82H
+ C_FILE_IN_GAMMA
+ cd84H
+ C_GAMMA_CORRECT
+ cd90H
+ C_APPLY_DISP_GAMMA
+ cda0H
+ C_APPLY_FBUF_GAMMA
+ cdb0H
+ C_APPLY_FILE_GAMMA
+ cdc0H
+ C_FORCE_WIRE
+ cdd0H
+ C_RAY_SHADOWS
+ cde0H
+ C_MASTER_AMBIENT
+ cdf0H
+ C_SUPER_SAMPLE
+ ce00H
+ C_OBJECT_MBLUR
+ ce10H
+ C_MBLUR_DITHER
+ ce20H
+ C_DITHER_24
+ ce30H
+ C_SUPER_BLACK
+ ce40H
+ C_SAFE_FRAME
+ ce50H
+ C_VIEW_PRES_RATIO
+ ce60H
+ C_BGND_PRES_RATIO
+ ce70H
+ C_NTH_SERIAL_NUM
+
+
+
+DxxxH Group
+
+ d000H
+ VPDATA
+ d100H
+ P_QUEUE_ENTRY
+ d110H
+ P_QUEUE_IMAGE
+ d114H
+ P_QUEUE_USEIGAMMA
+ d120H
+ P_QUEUE_PROC
+ d130H
+ P_QUEUE_SOLID
+ d140H
+ P_QUEUE_GRADIENT
+ d150H
+ P_QUEUE_KF
+ d152H
+ P_QUEUE_MOTBLUR
+ d153H
+ P_QUEUE_MB_REPEAT
+ d160H
+ P_QUEUE_NONE
+ d180H
+ P_QUEUE_RESIZE
+ d185H
+ P_QUEUE_OFFSET
+ d190H
+ P_QUEUE_ALIGN
+ d1a0H
+ P_CUSTOM_SIZE
+ d210H
+ P_ALPH_NONE
+ d220H
+ P_ALPH_PSEUDO
+ d221H
+ P_ALPH_OP_PSEUDO
+ d222H
+ P_ALPH_BLUR
+ d225H
+ P_ALPH_PCOL
+ d230H
+ P_ALPH_C0
+ d231H
+ P_ALPH_OP_KEY
+ d235H
+ P_ALPH_KCOL
+ d238H
+ P_ALPH_OP_NOCONV
+ d240H
+ P_ALPH_IMAGE
+ d250H
+ P_ALPH_ALPHA
+ d260H
+ P_ALPH_QUES
+ d265H
+ P_ALPH_QUEIMG
+ d270H
+ P_ALPH_CUTOFF
+ d280H
+ P_ALPHANEG
+ d300H
+ P_TRAN_NONE
+ d310H
+ P_TRAN_IMAGE
+ d312H
+ P_TRAN_FRAMES
+ d320H
+ P_TRAN_FADEIN
+ d330H
+ P_TRAN_FADEOUT
+ d340H
+ P_TRANNEG
+ d400H
+ P_RANGES
+ d500H
+ P_PROC_DATA
+
+
+
+FxxxH Group
+
+ f020H
+ POS_TRACK_TAG_KEY
+ f021H
+ ROT_TRACK_TAG_KEY
+ f022H
+ SCL_TRACK_TAG_KEY
+ f023H
+ FOV_TRACK_TAG_KEY
+ f024H
+ ROLL_TRACK_TAG_KEY
+ f025H
+ COL_TRACK_TAG_KEY
+ f026H
+ MORPH_TRACK_TAG_KEY
+ f027H
+ HOT_TRACK_TAG_KEY
+ f028H
+ FALL_TRACK_TAG_KEY
+ f110H
+ POINT_ARRAY_ENTRY
+ f111H
+ POINT_FLAG_ARRAY_ENTRY
+ f120H
+ FACE_ARRAY_ENTRY
+ f130H
+ MSH_MAT_GROUP_ENTRY
+ f140H
+ TEX_VERTS_ENTRY
+ f150H
+ SMOOTH_GROUP_ENTRY
+ ffffH
+ DUMMY
+
+
+
+
+
+
+
+The Unofficial 3DStudio 3DS File Format / CR-MM / mediatel-admin@mediatel.lu
+
+
+
+
+--------------------------------------------------------------------------------
+
+
+Comments? Send them to: webmaster@filespecs.com
+
+
+Copyright 2001 BreakPoint Software, Inc. All Rights Reserved.
+
diff --git a/Doc/AI.TXT b/Doc/AI.TXT
new file mode 100644
index 0000000..40071ca
--- /dev/null
+++ b/Doc/AI.TXT
@@ -0,0 +1,1248 @@
+==================== STARSHATTER ====================
+ ACTION
+
+Starshatter is a 3D action-simulation game in a space
+combat setting. The player will control a space
+ship of one type or another, and attempt to defeat
+various enemy forces composed of small, medium
+and large space ships, space stations, and ground-
+based resources.
+
+The game is designed as a series of combat missions
+in which the goal is to destroy an enemy resource,
+secure an enemy position, or defend a friendly resource
+from an enemy attack. Unlike most space combat sims,
+Starshatter missions are fought within the context of
+a large dynamic military operation.
+
+In the attack type of mission, victory is achieved
+by destroying the resource(s) and escaping to safety.
+In the secure and defend types of missions, victory is
+achieved by preventing the destruction of the resource
+while simultaneously destroying the enemy or forcing
+the enemy to retire. In any mission, achieving a
+partial victory is a possibility.
+
+Individual mission designs will be characterized by:
+1) the locale, including various space bodies such as
+ stars, planets, planetoids, asteroid fields, space
+ stations, nebulae, terrain, cities, and so forth
+2) the composition and placement of enemy forces
+3) the composition and placement of friendly or neutral
+ forces, if any
+4) the objectives / resources / victory conditions
+ for each force element
+
+
+RELEVANT TECHNOLOGY:
+
+Space ships and ground units are each endowed with a
+group of resources that they use in battle. These
+resources fall into eight different categories:
+
+Ship System List:
+
+ 1. Vital (Hull/Stabilizer/Core/Life Support)
+ 2. Econ (power management)
+ 3. Conventional Drive
+ 4. Hyperdrive (optional)
+ 5. Shields
+ 6. Weapons
+ 7. Flight Ops (optional)
+ 8.* Systems (computer)
+ a.Communications
+ b.Sensors
+ c.GNC
+ d.Tactical (target tracking)
+
+Large space ships are capable of supralight travel by
+means of "hyperdrive," which is similar to the same
+concepts used in Star Wars and Babylon 5.
+
+Smaller ships (fighters, attack) are not capable of long
+distance travel using hyperdrive, and must be carried
+to and from missions by a larger ship (a destroyer or cruiser).
+
+All combat will occur at sublight velocities using
+conventional plasma projection or fusion drive. However,
+damage to the hyperdrive may effect the outcome of a
+mission (e.g. by delaying a safe escape).
+
+Ships and ground targets may employ various types of
+weapons, both matter and radiant energy based. From
+the AI's point of view, all weapons are of one of three
+different types: guided, unguided, or regional. Guided
+weapons include their own AI to direct their flight path
+to a target. Unguided weapons simply follow the straight
+line course set for them at launch time. Regional weapons
+inflict damage on any resources that happen to occupy the
+same volume of space as the weapon during its deployment.
+Any single ship or ground target may employ several fire
+control AI systems to manage multiple beam weapons and
+missile launchers.
+
+Ships and ground targets may employ defensive screens
+or shields. These shields are invisible barriers that
+prevent the transmission of both matter and radiant
+energy weapons. Shields (actually, shield generators)
+are degraded by the process of deflecting weapons and
+will eventually burn out, leaving the ship open to
+attack. Larger ships will employ multiple shield
+generators to screen various parts of the ship. In
+addition, larger ships can mount more robust shield
+technology, requiring heavier weapons to penetrate.
+
+Laser/Particle Beam cannon types:
+* Alpha
+* Gamma
+* Delta
+* Omega
+
+Explosive Missile weapon types:
+* Pyrotechnic
+* Thermonuclear
+* Antimatter
+* Varion (Torpedo)
+
+In addition to basic laser and explosive missile types
+of weapons, Starshatter features several advanced weapon
+types:
+
+* Cruise Missiles (very long range guided munition)
+* Bomb-pumped Laser Warheads
+* CIWS Laser Turrets
+
+* Plasma Cannon (very high power energy weapon)
+* Fusion Burst (ultra high power energy weapon)
+* Graviton Pulse (goes through all shields)
+
+Whenever any ship system takes damage, the capabilities
+of the ship in that area are temporarily or permanently
+downgraded. Most systems are capable of limited self-
+repair. Safe bases will be provided for full repair
+both during and between missions.
+
+Although there will be no powerups just lying around,
+there will be both friendly and enemy supply ships on
+each mission. Small ships can resupply themselves at
+any time by docking with a friendly supply depot or
+ship.
+
+While ships are grouped into the broad categories of
+"large," "medium," and "small", there are several
+distinct classes within those categories. Ships in
+each class will have typical size parameters as
+follows:
+
+ CLASS LENGTH (m) CREW
+ ----------- ----------- -------
+ Fighter 10-20 1 Small
+ Attack 25-50 2-3
+ Destroyer 200+ Medium
+ Cruiser 400+
+ Battleship 800+ Large
+ Carrier 1000+
+
+ Station unlimited
+
+As the class names indicate, small ships are analogous
+to modern combat aircraft, while medium and large ships
+are analogous to modern naval surface warships. Medium
+and large ships are sometimes referred to as "Starships"
+because they are capable of interstellar travel.
+
+Medium ships may be outfitted with towing rigs. Large
+ships may be outfitted with flight decks. Both of these
+can be used to capture a ship. Flight decks can also
+be used to repair and re-supply smaller ships.
+
+Ships may be organized into battle groups that are led
+by a "flagship." The flagship is responsible for the
+general direction of the other ships in the battle group.
+This includes such things as setting the overall state
+of readiness, prioritizing targets, and deciding to
+pursue or retire.
+
+==================== STARSHATTER ====================
+ TACTICS
+
+Aside:
+
+I really want to avoid the typical "furball" (as seen
+in XvT, WCn, etc.) as much as possible. One thing I've
+always liked about DOOM style games is the measured pace
+of the action. During the course of a single level, the
+player faces a variety of enemies and situations in small
+packets, controlled by the level architecture. I would
+like to mimic this in Starshatter somehow, but the "open-
+ness" of the space setting makes it difficult.
+
+Some ideas:
+
+Missions will be sliced up into sections by distance and
+obstacles. So there might be a group of sentry ships to
+defeat as you enter the playfield, followed by a short
+flight to the next enemy force package. The enemy will
+be somewhat slow to react to the player's presence. Per-
+haps enemy ships will need to stick close by other resources
+on guard duty or some such.
+
+A challenge:
+
+Unlike DOOM, the player and enemy ships have the same
+capabilities. This means that with reasonable AI, the
+player will get killed about as often as the enemies.
+As a result, if the player is vastly out-numbered as
+usual he won't last very long with even odds. Result:
+we may need to fudge the damage on the player's ship
+somewhat to give him an edge. Either that, or the
+player will only be allowed to battle one or two enemy
+ships at a time. Or, the enemy AI should preferentially
+attack red-shirt pilots instead of the player.
+
+USING HYPERDRIVE:
+
+How about, most ships are equipped with hyperdrive.
+This allows the player to travel to places that are too
+far away to be seen with tactical scanners. In effect,
+jumping through hyperspace places "doors" between scenes
+of action.
+
+Let's postulate that hyperdrive allows supra-light travel
+at some ridiculous velocity. While in hyperspace the ship
+is essentially impervious to everything except gravitons.
+If the ship passes too close to a gravity well or gravitic
+weapon, the hyperdrive will "stall". The ship will return
+to normal space, and the hyperdrive will be offline for
+several seconds.
+
+Further postulate that shields and weapons are powered
+down during hyperspace transit. So you can run, or you
+can fight, but not both at the same time.
+
+While in hyperspace, the player can pilot the ship in
+the usual manner (or perhaps just using the keyboard),
+but the ship is no longer as agile (perhaps 1/5th as
+agile as in sublight flight, perhaps this is variable
+depending on ship design). The point is that the player
+can point and go, without some hokey "warp out" animation
+sequence.
+
+Of course, it's a big universe out there. There is no
+point in *making* the player wander around in the middle
+of nowhere for an hour because he overshot the battle. So
+we will also provide a nav map with preset way points.
+The player can plot a course to anywhere in the mission
+profile by clicking on the way points and pressing "GO".
+While under "auto-pilot" like this, the player can still
+operate all of the ship systems, review the database, etc.
+Nav courses can also be pre-plotted as part of the mission
+profile. Nav points should be reasonably close together,
+so as to provide a "breather" but not "boredom".
+
+At any time, the player can steer the ship and break out
+of autopilot, without the navigation system forgetting
+the plan. This provides a nice possibility of "secret areas"
+like checking for the secret base on the far side of the
+3rd moon.
+
+While in hyperspace, the view of the universe is altered.
+Sublight ships are invisible, as is space dust. Planets
+and other gravity wells are important, so these are still
+visible in some way. Probably want to replace the "star
+and nebula" sky shell with something more distinctive, a'la
+Bab5 hyperspace, might be nice to have it be animated.
+
+Even though you never see your own ship go into hyperspace,
+it would be great to have a suitable special effect for when
+another ship does so. Perhaps something like the spiral
+smoke trail from "Eraser". Whatever it is, it has to convey
+a sense of speed (without silly elongation effects or fake
+motion blur).
+
+SENSORS AND SCOUTING:
+
+Ships will have access to both passive (short range) and
+active (long range) sensors. Passive sensors rely on the
+detection of radiation emissions from target ship systems
+such as shields, weapons, drive, reactors, active sensors,
+and communications (this is analogous to IR or passive sonar).
+Active sensors transmit an energy beam into space and detect
+the "echo" of the energy bouncing off a target ship (in a
+manner analogous to radar or active sonar).
+
+The fundamental strategy of scouting is to detect the enemy
+targets while simultaneously thwarting the enemy's attempt
+to detect the player. The basic tool for implementing this
+strategy is Emission Control. The player must balance the
+energy output of his own ship against the ability of the
+enemy to detect and target him.
+
+The ships will have an Emission Control system, with a range
+of EmCon options. As the EmCon level is increased, more
+ship systems are enabled and allowed to radiate. The ship's
+computer will attempt to minimize the EmCon level to the
+greatest degree in accordance with safety. If unfriendly
+ships are detected at close range, the computer will increase
+the EmCon level to allow the use of shields and weapon systems.
+The player can also override the automatic EmCon setting in
+situations where stealth is paramount.
+
+Each ship will be modeled by a passive and active sensor
+cross-section. The passive cross-section (PCS) is a dynamic
+number that represents the current radiation level of the
+entire ship. The PCS is the current sum of the radiation
+levels produced by all active ship systems. The active
+cross-section (ACS) is a fixed number for each ship type
+which represents the amount of energy reflected by the ship's
+hull (aspect angle ignored).
+
+Each sensor system will be modeled by type and efficiency.
+During each frame of simulation, the sensor system will
+sample all targets in the game. For each target, two levels
+of detection will be computed as:
+
+ Detect_pas = PCS / range * Eff_pas
+ Detect_act = ACS / range^2 * Eff_act(Power_act)
+
+If the level of detection for a target is 0.5 or less, the
+avionics will not register the target as a contact, and will
+not display it in the HUD or tactical viewscreens. If the
+level of detection is 1.0 or greater, the avionics will
+register the target as a contact, display it in all view-
+screens, and allow weapons targeting of the contact.
+
+If the level of detection is between 0.51 and 0.99, the
+avionics will be able to detect position and classification
+only (not velocity or capability). Weapon targeting will
+not be possible.
+
+The target's affiliation will only be displayed if its IFF
+transponder is enabled. (in all cases?)
+
+-----------------------------------------------------------
+EmCon 1: Silent
+ Generator - idle
+ Shields - disabled, discharged
+ Weapons - disabled, discharged
+ Sensors - passive only
+ Comms - receive only, IFF disabled
+
+EmCon 2: Stealthy
+ Generator - 50% limit
+ Shields - disabled, discharged
+ Weapons - enabled, discharged (missiles only?)
+ Sensors - active low power
+ Comms - receive, transmit encrypted, IFF disabled
+
+EmCon 3: Normal (launch condition)
+ Generator - 100% limit
+ Shields - enabled, charged
+ Weapons - enabled, charged
+ Sensors - active full power
+ Comms - receive, transmit, IFF enabled
+-----------------------------------------------------------
+Table 1: EmCon Settings
+
+
+Short range (passive) sensors will be able to detect EmCon
+2 targets up to a range of 100K, and EmCon 4 targets up to
+a range of 500K. Long range (active) sensors will be able
+to track all targets up to a range of 1M (maybe as high
+as 2M).
+
+The flag ship serves as Wide Area Search Protocol (WASP)
+coordinator for the fleet. That is, all ships in the fleet
+will have access to the flag ship's target list. All ships
+will periodically send their own target list back to the
+flag ship for distribution to the fleet.
+
+==================== STARSHATTER ====================
+ STORY
+
+An ancient Evil has awoken in the galaxy...
+
+The story will be told against a backdrop of two warring
+powers, one peaceful/isolationist, one warlike/imperial.
+The warlike power seeks to control less sophisticated
+races and civilizations and to lead them in an attack on
+the peaceful power. The player is a member of one of
+these "less sophisticated" races (i.e. a human) who gets
+caught up in the subtle yet violent conflict between the
+two titans.
+
+Since the conflict between the powers involves the con-
+trol of lesser species, you can never really know who is
+on your side. Or even which side is really yours. As a
+soldier, you will be forced to choose between obeying
+orders from your superiors, and doing what you believe
+is right.
+
+The game begins with the player assigned to the Tekton-
+Organon Combat Training Facility in the Janus System.
+The player can choose a career path as a fighter pilot
+or as a starship commander. In either case, they are
+assigned a first vessel, and sent through a course of
+ten or so training missions with both simulated and live
+fire against combat instructors and drone targets.
+
+During the last training mission (a sort of final exam)
+the CTF is destroyed in a hit-and-run attack by the enemy.
+The player is immediately assigned to a combat billet and
+sent into the fray. (Note that once the player has
+completed the training missions, future games will not
+require the player to repeat them.)
+
+The remainder of the game takes the player through a
+series of combat missions against more and more difficult
+alien opponents. Midway through the game, the player
+learns that the true enemy is the EVIL that has instigated
+the conflict in the first place.
+
+In the end, the "good" power offers a weapon of last
+resort to the player. This weapon will cause the explosive
+annihilation of several dozen stars in enemy space. It
+will immediately result in several billion civilian deaths,
+with tens of billions of casualties resulting from radiation
+poisoning over the next several centuries. Other, more
+dire, and more global consequences will also result...
+
+==================== STARSHATTER ====================
+ STRATEGY
+
+Although Starshatter is fundamentally an action title,
+there is also a significant back-story and strategic
+element to the gameplay.
+
+The game occurs in a far-distant future of the Milky
+Way galaxy. Our corner of the galaxy, an area some
+ten or twenty thousand light years in diameter is popu-
+lated by thousands of human-intelligent species on tens
+of thousands of worlds. Many races live in approximate
+isolation, but most are organized into complex poly-
+specific civilizations spanning many star systems.
+Humans are not central in galactic affairs (as in Star
+Trek or Star Wars) but are merely another species. And
+some of the other species and civilizations are *much*
+older and *much* more advanced than ours.
+
+
+POLITICAL FACTIONS:
+
+- Starshatter (warlike/imperial power)
+
+* Sterat Empire (controlled by the Starshatter)
+
+* Brenel Core (military/intelligence corporation)
+
+* Zolon Empire (hive-like hereditary empire)
+
+* Marakan Hegemony (Failing human imperial civilization)
+
+* Terellian Alliance (peaceful commerce group)
+ Alliance of Terellian Republics (ATR)
+
++ Ele'aan Fusion (peaceful/isolationist power)
+
+The player is a human starfighter pilot in the military
+of one such civilization, Tekton-Organon, which is in
+turn part of the Terellian Alliance. The computer plays
+the part of the player's superior officers in assigning
+missions and offering criticism and praise for performance.
+In addition, the computer plays the role of all enemy
+pilots and politicians.
+
+The game follows the action in an interstellar conflict
+between the Terellian Alliance and those civilizations in
+the control of the Starshatter. The player is assigned
+missions and ships commensurate with his ability by the
+computer.
+
+==================== STARSHATTER ====================
+ MISSIONS
+
+Each of the three episodes follows the action in a military
+campaign against a single enemy civilization. Individual
+missions will be constructed on the fly by the dynamic
+campaign manager (or by the player if acting as operational
+commander).
+
+Each episode takes place in a fixed volume of space con-
+taining about a dozen star systems. The global campaign
+map will treat the star systems as points in a 2D map.
+The action will take place within individual star systems,
+which use a finer grained map. The enemy will control
+most of the star systems, although a few will be neutral,
+allied, or controlled by the player's faction. These may
+need to be defended if the enemy is able to incur.
+
+Rather than being "mission based", Starshatter is an
+"operational simulation". Each campaign episode is
+broken down into a series of military operations, each
+of which occur in a given star system. Different opera-
+tions will have different rules of engagement and victory
+conditions. Some will require the player to secure a
+neutral or allied star system from hostile threats.
+Others will involve incursion into hostile territory.
+Still others will have more limited objectives, such
+as securing a friendly position in a neutral system,
+without violating neutral airspace or damaging neutral
+assets.
+
+Major resources such as space stations, ship yards,
+SAM and sensor sites will be part of the fixed universe
+design. Likewise, the initial number, type, and location
+of warships is fixed at design time. It is hoped that
+the combination of large numbers of ships and small
+random factors (battle outcome, planetary position,
+time of attack) will result in increased variety of
+enounter for the player.
+
+During the game, the campaign manager must keep track
+of the location and make-up of each ship and battle group.
+As each ship is destroyed in battle, it must be removed
+(permanently) from play. New ships and battle groups
+can be constructed on each side, at a rate dependent
+on how many critical resources the side controls. In
+addition, reserves may be advanced from rearward areas
+to support an operation.
+
+For each mission, the computer will assemble a mission
+profile by selecting one or more objectives, selecting
+a location for the battle, and assigning friendly and
+enemy forces. The computer can choose randomly from
+among the nearby resources to attack (or defend) and
+the nearby ships on both sides to fight.
+
+Missions will be generated in accordance with the current
+strategic objectives for the operation. The dynamic
+campaign manager will keep a prioritized list of
+strategic objectives, mostly related to major fixed
+resources.
+
+Before the battle, the player will be given a gene-
+rated mission briefing describing the mission objec-
+tives, locale, and the makeup of his own group.
+
+Once the player completes the mission (or loses it),
+the computer will tally up the losses on both sides
+and remove those ships from the campaign roster. A
+generated mission debriefing will detail kills and
+losses, pilot ratings, etc.
+
+In order to keep the player doing something interesting,
+he will have to be assigned to an attack force, either
+a battle group or fighter wing. Early missions will be
+shorter and simpler, with a single objective in a
+single system. Later missions will get more involved,
+with multiple objectives and sometimes requiring free
+drive travel to other systems. In addition, the com-
+plexity of missions will be modulated by the user's
+chosen difficulty level.
+
+Sometimes, the objectives will be enemy forces: e.g.
+destroy a fighter wing or cripple a battle group.
+Other times, they will be fixed resources like space
+stations or fortified moons. Still other times, they
+will be distributed or moving targets: e.g. shut down
+interplanetary shipping throughout the system.
+
+At any time, the player's group may get assigned to
+a defensive mission (scramble). This can mean defending
+a fixed resource from an incoming enemy attack. Or it
+could mean escorting an important freight convey through
+unsecured space.
+
+Mission objectives can be constructed from verb-noun
+imperatives:
+
+ Verb List Object List
+ ---------------- -----------------
+ Intercept Ship
+ Shadow Group of Ships
+ Escort
+ Defend
+
+ Strike Space Station
+ Capture
+ Blockade
+ Defend
+
+ Launch From Starship or Station
+ Dock With
+ Dock/Rearm/Launch
+
+ Clear Mine Field / Satellite Grid
+
+ Go To Location
+ Patrol
+ Hold At
+
+
+We may also need to define some high-level tactics for
+achieving these objectives which can be used by the
+enemy AI.
+
+Here are some ideas:
+
+ Massed or Dispersed
+ Attack at Long Range or Up Close
+ Stealthy or Overt Approach (psychological impact, force disposition)
+
+ Clear space or Nebula (won't always have a choice)
+ * Find other ways to screw up sensors
+ - pulsars
+ - jammers
+
+ Guerre de Course?
+ Use Decoys?
+ Keep Reserves?
+ What kind of Antiscouting/Deception?
+ What kind of weapons?
+
+==================== STARSHATTER ====================
+ COMMAND
+
+A unique feature of STARSHATTER is the ability to choose
+between a career as a fighter jock or a starship commander.
+The fighter side of the game will play similar to a space
+combat sim or an air combat sim. The starship side will be
+quite different:
+
+Starships are big, expensive, powerful platforms, but they
+are not terribly maneuverable. In general, one does not
+"knife fight" with a star destroyer. Starships will make
+use of a variety of long range (even beyond visual range)
+weapons including cruise missiles, varion torpedoes, and
+attack ships. These can be used to weaken or destroy an
+enemy vessel while putting the ship at minimal risk. Once
+the enemy has been sufficiently weakened, the player can
+safely close range and finish the job with supremely power-
+ful but short range energy weapons.
+
+The commander can select and direct long range weapons
+to attack enemy sub-targets to achieve a specific goal,
+whether it is to disable, disarm, or outright destroy an
+opponent. It will probably be great fun to disable an
+enemy, close range, and ask them to "surrender...or DIE!"
+
+In these long range attacks, timing and distance will be
+everything. The player must be close enough to detect the
+enemy and coordinate a strike, but not too close to be
+detected first. Then the player must close range quickly
+enough to finish off the enemy before an effective counter
+attack can be mounted. The opportunity for deception and
+possum-playing on both sides will add to the tension.
+
+However if the player is unlucky enough to find himself
+on the receiving end of a BVR attack, he still has several
+interesting options. If the long range attack has revealed
+the location of the enemy (a likelihood), the player can
+request assistance from better positioned allies (if any).
+Alternately, he can choose to hunker down into a defensive
+posture or launch a counter-attack. If the situation seems
+truly grim, he can also choose to reconfigure power systems
+and run.
+
+To compensate for the lack of maneuverability, the player
+will need to be given control over a richer variety of
+defensive assets. In addition to basic fighter cover and
+CIWS lasers, starships will be able to carry and deploy
+sensor probes and weapons pods. Sensor probes are usually
+passive sensor arrays mounted on a stable platform analogous
+to a modern sonobouy. They are deployed by the starship and
+send target contact information back to it. Weapons pods
+are more like unmanned, unshielded, undriven fighters.
+They have the ability to orient themselves in space, and may
+even be capable of limited movement through translation
+thrusters. But basically they sit where they are left and
+fire lasers and missiles at any unfriendly targets that
+wander into range.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+Steps needed to get there:
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+- Passive and Active Sensors
+- Tactical Situtation MFD
+- System Targeting
+- System Damage Combat Results
+- Long Range Weapon Types
+- Sensor Probes and Weapons Pods
+- AA Turrets on Starships
+- Fighter AI Commands
+- Starship AI
+- Squadron AI Commands
+- Big Explosions and Shock Waves
+
+==================== STARSHATTER ====================
+ AI
+
+AI STATE
+-----------------------------------------------------
+Threats
+Targets
+Allies
+Destinations
+Objectives
+Self health
+Team strength
+Morale
+Tenacity/Recklessness
+Mood/Stability
+Difficulty
+
+
+High Level Behavior
+-----------------------------------------------------
+Choose to continue or retire the engagement
+Guard a position or resource
+Search for a target
+Plan an assault on a group of targets
+Assign an objective to a teammate
+Call for help
+
+Mid Level Behavior
+-----------------------------------------------------
+Assess a threat, or group of threats
+Select a target from a group of targets
+Select a subcomponent of a larger target
+Select a weapon to use when attacking
+Attack a Target using an offensive combat maneuver
+Assess own capabilities or "health"
+Assess team capabilities or strength
+Set levels of agression and desperation
+Set desired level of stealthiness
+Choose to pursue or break-off an attack
+Seek cover
+Seek repair or supplies
+Follow a flight plan
+Launch or recall a ship
+
+Low Level Behavior
+-----------------------------------------------------
+Navigation:
+ Idle
+ Evade a Threat
+ Seek an Objective
+ Avoid an Obstacle
+ Match Target Velocity
+ Form with Target (ally)
+ Dock with Target (ally)
+ Hold Position
+ Orbit(patrol) Position
+
+FireControl:
+ Fire upon a target
+ Set EMCON level
+
+Primitive Behavior
+-----------------------------------------------------
+Turn or Establish Heading
+Accelerate or Slow Down
+Fire a Weapon
+Target a Weapon
+Select Sensor Mode
+Set EMCON Level
+Activate Countermeasures
+
+==================== STARSHATTER ====================
+ INTERFACE
+
+SCREEN CONCEPTS:
+
+-----------------------------------------------------
+Main Menu
+-----------------------------------------------------
+
+ New Game
+ Continue Game
+ Quick Mission
+ Training
+
+ Player Info (callsign, difficulty level, stats)
+ Game Options (controls, audio-video settings)
+
+ Help!
+ Exit
+
+-----------------------------------------------------
+Help Menu
+-----------------------------------------------------
+
+ Online Manual
+ Keymap
+ About...
+ Credits
+
+-----------------------------------------------------
+Operational Command
+-----------------------------------------------------
+
+ Operation Name, Date, Status
+ ----
+ Orders (overview of operation)
+ Theater (system map showing resources)
+ Strat Plan (edit objective list)
+ Intel Rpt (news feed)
+ Status (current score, us vs. them)
+
+ - missions -
+ Active ## (current mission list)
+ Pending ## (missions in prep)
+ Scramble ## (inbound threat list)
+
+ Commit (engage current mission)
+ Cancel (back to main menu)
+
+ Also need controls to navigate the maps, and
+ control visual clutter.
+
+ [May want to offer simpler versions of this
+ screen for "Starship Command" and "Fighter
+ Command", allowing the player to focus on
+ combat simulation rather than strategy.]
+
+-----------------------------------------------------
+Mission Planning
+-----------------------------------------------------
+
+ Briefing Data (scrolling text and images)
+ Mission Title
+ Objective List
+ Force Package
+ package callsign
+ wingmen and loadout
+ Annotated Flight Plan
+ launch time
+ nav points and events
+ time on target
+ Intel Report
+ expected counterforce
+ degree of confidence
+
+ Package (select wingmen)
+ Loadout (select weapons)
+ Nav Map (edit nav points)
+ Targets (assign objectives to flights)
+
+ Commit (engage current mission)
+ Cancel (back to previous screen)
+
+ [NOTE: same screen can double as Alert Intercept
+ by changing the title and removing the edit buttons]
+
+
+==================== STARSHATTER ====================
+ TRAINING
+
+Training Missions (both services)
+
+1. Basic Flight and Navigation
+2. Guns
+3. Missiles
+4. Combat Maneuver
+5. Sensors / Stealth
+6. Wingman Interface
+7. Long Range Scan
+8. Tactical Navigation and Waypoint Setting
+9. Advanced Tactics
+ a. Starship Assault (fighter weapon school)
+ b. Cruise missiles & Flight Ops (fleet tactical school)
+10. Situation Awareness (Final Exam)
+
+
+=======================================================
+ MISCELLANIA
+
+Sensor ships and stations will be important resources in
+winning the information war. Both the player and the AI
+will need to exploit and protect them appropriately.
+
+Briefings/Debriefings and other Intelligence Reports
+must adequately communicate the overall game situation
+to the player in order to allow the player to make
+meaningful decisions. This is crucial to establishing
+immersion and making the player care about the outcome
+of the game.
+
+Briefings and Info Bursts will be more visceral if they
+include simulated "news footage" of the events being
+related.
+
+
+=======================================================
+ DYNAMIC CAMPAIGN NOTES
+
+1. Fighter jocks will be allowed to choose from a
+ menu of generated missions that have been assigned
+ to the player's squadron.
+
+2. Cruiser skippers will be given only one generated
+ mission at a time (like KA or SFC), but it will be
+ somewhat broader in scope.
+
+3. Carrier admirals will do what exactly?
+
+4. Both cruiser and carrier missions will often start
+ in the field or on patrol. Fighter missions will
+ always start and end at a base (carrier|station|
+ dirtside starbase).
+
+5. Cruiser and carrier missions will not normally
+ include the starsender translation to the area of
+ conflict. The player will only go through the
+ sender in real time as part of a mission (not as
+ prelude or ending to a mission).
+
+6. Admirals will need to be able to generate orders
+ for their forces during the battle. This implies
+ some kind of Op Command workstation.
+
+7. The mission planner algorithm must construct
+ generated missions that are "fun", not just logical
+ to the campaign. I have a vague notion of using
+ time/distance heuristics to place enemy units as
+ "challenges" on the battle field. e.g. For xxx
+ type of mission, place an enemy destroyer yyy
+ minutes away from the (player|objective|sender).
+
+8. The generated missions do not have to hang together
+ as well as previously planned. That is, we don't
+ need to maintain the feeling that the player is just
+ a unit in an RTS game. Instead, the campaign manager
+ is just a device for generating interesting single
+ player missions as would be found in any game with
+ a static campaign.
+
+ On the other hand, the player should not see any
+ obvious contradictions between the big board and
+ what occurred during their missions. That means,
+ any kills they witnessed must still be visible
+ between missions in the intel report/event log.
+
+
+=======================================================
+ SAMPLE OPERATIONAL SCENARIOS
+
+--- COLD WAR, LOW TENSION ---
+
+1) Training Center (see above)
+
+1a) Live Fire:
+ Engage in "live fire" exercise with a friendly
+ power.
+
+--- COLD WAR, HIGH TENSION ---
+
+2) Restore Order:
+ The enemy is supporting a terrorist puppet govt
+ in an allied star system. Aid the locals in putting
+ down the disturbance.
+
+3) Deliverance:
+ The enemy is sponsoring terrorists and pirates
+ harassing a small outpost system. Ensure free
+ access to the space lanes and aid the delivery
+ of needed supply convoys.
+
+4) Freedom Fighters:
+ The enemy is supporting a despotic govt that is
+ occupying a weaker friendly or neutral system.
+ Aid the locals in driving out the occupying force.
+
+5) Strategic Withdrawal:
+ Ownership of a former protected star system is
+ passing back to the enemy. Oversee the withdrawal
+ of friendly forces. Try to avoid active conflict.
+
+--- FORMAL DECLARATION OF WAR ---
+
+6) First Blood:
+ A research and educational center is on the front
+ lines and in harms way. Set up a strong defensive
+ posture, and oversee the evacuation of non-military
+ personnel.
+
+7) Sneak Attack!
+ The enemy has already struck a friendly system,
+ but they have not yet had time to fortify their
+ position. Coordinate with local forces and evict
+ the hostiles.
+
+8) Fortress of Light:
+ Protect an Eleaan frontier system from enemy
+ attack.
+
+9) Turning Point:
+ The enemy is seeking a decisive victory by wiping
+ out the home fleet. Turn the tables on them by
+ attacking first.
+
+10) Firestorm:
+ Press the advantage by invading a series of
+ enemy manufacturing and military systems.
+
+11) Nightfall:
+ Climactic operation against the enemy home system.
+
+
+=======================================================
+ MISSION TEMPLATE TYPES
+
+FIGHTER
+ TACTICAL FIGHTER WING
+
+ ATTACK SQUADRON
+
+DESTROYER/CRUISER
+ SQUADRON
+ Anti-shipping
+ Shipping Escort, anti-fighter
+ Shipping Escort, anti-cruiser
+ Shipping Escort, general
+ Picket
+ Squadron Hunter
+
+ BATTLE GROUP/TASK FORCE
+ Force-on-Force Engagement
+ Starbase Bombardment, pre-assault
+ Starbase Bombardment, anti-fighter
+
+CARRIER
+
+
+=======================================================
+
+Ship Names:
+
+F-32G Falcon Squadrons
+ Flying Tigers, Bearcats, Aces High, Cool Hands, Wizards
+
+F-36C Stormhawk Squadrons
+ Nighthawks, Mustangs, Stallions, Chargers, Blazers
+
+F/A-38D Talon Squadrons
+ Starknights, Lancers, Wild Bunch, Razorbacks, Buzzards
+
+
+Berents Class FF
+ Berents, Bosporus, Barth, Bering, Balen,
+ Clarkeston, Carlisle, Canton, Delmar, Dardenelles,
+ Darmstadt, Leyte, Messina, Mountebank, Morgan,
+ Malory, Parker, Rainier, Sorrel, Seychelles,
+ Tanly, Trieste, Yarmuz, Zephyr, Zodiac
+
+
+Spectre Class DD
+ Spectre, Wraith, Shadow, Warlock, Phantom,
+ Mistral, Nemesis, Necromancer, Merlin, Nightshade,
+ Moloch, Demon, Phobos, Deimos, Enigma,
+ Phantasm, Revenant, Banshee, Barrows, Gorgon
+
+Courageous Class DG
+ Courageous, Fearless, Stalwart, Defender, Guardian,
+ Steadfast, Assurance, Protector, Heroic, Valiant,
+ Dauntless, Vanguard, Redstone, Victory, Honor,
+ Braveheart, Defiant, Audacious, Valorous, Confident
+
+Devastator Class CA
+ Devastator, Annihilator, Onslaught, Relentless, Argent,
+ Stormwind, Thunder, Hurricane, Inferno, Torrent,
+ Typhoon, Vortex, Predator, Ravager, Demolisher,
+ Havoc, Scourge, Huntress, Warrior, Shrike
+
+Orion Class CV
+ Orion, Antares, Archon, Titan, Hyperion
+
+
+
+OTHERS
+ Wasp, Hornet, Scorpion, Deaths Head,
+ Wolf, Cobra, Viper, Raptor, Python
+
+ Jackal, Zodiac, Scarab, Sphinx, Osiris,
+ Onyx, Isis
+
+-------------------
+1 Fleet = 95 starships + 300 fighters
+
+5 carrier groups
+ 1 CV + 60 Fighters
+ 2 CA
+ 1 DG
+ 1 DD
+ 2 FF
+
+5 battle groups
+ 2 CA
+ 1 DG
+ 1 DD
+ 2 FF
+
+5 destroyer squadrons
+ 2 DG
+ 2 DD
+ 2 FF
+
+==================== STARSHATTER ====================
+ GALAXY : NAMES
+
+ Adavan
+ Aram
+ Borreal
+ Bress
+ Casalle
+ Chi
+ Dawn
+ Elkhart
+ Fenn
+ Gaul
+ Garrison
+ Haiche
+ Hemmik
+ Ilon
+ Janek (Janus-3)
+ Jarnell
+ Kala
+ Khalife
+ Kolchev
+ Korius
+ Lanos
+ Loris
+ Marak
+ Manse
+ Muir
+ Navara
+ Nephrys
+ Nergal
+ Oleanne
+ Omin
+ Patagon
+ Path
+ Pollus
+ Relay
+ Renser
+ Ri
+ Rodis
+ Senna
+ Suven
+ Tal Amin
+ Tarsus
+ Theramin
+ Thralis
+ Ur
+ Volante
+ Warren - world crack canyon/rain-forest settlement (must always face the sun)
+ Whorl
+ Xanthe
+ Zephyr
+ Zone
+
+==================== STARSHATTER ====================
+ GALAXY : PLANET TYPES
+
+ --= Landform Types =--
+ Arid Desert (Arrakis)
+ Frigid Desert (Mars)
+ Frozen Exotic (Titan)
+ Temperate Planar
+ Temperate Mountainous
+ Tropical Forest
+ Tropical Oceanic
+ Icy Oceanic (Scandinavia)
+ Rocky Planar
+ Rocky Mountainous
+ Volcanic (Venus, Io)
+ Barren (Pluto)
+
+ --= Atmospheric Types =--
+ Thin
+ Clear, Breathable
+ Dusty
+ Foggy/Cloudy
+ Dense Poisonous
+ Layered
+
+ --= Population Types =--
+ Agrarian
+ Industrial
+ Mining
+ Research/Technology
+ Military
+ Political
+ Terraforming
+
+==================== STARSHATTER ====================
+ GALAXY : RACES
+
+ Human
+
+ Ele'aan
+ Eltaar - "Those Who Speak for Ele'aas"
+
+ Marakan
+ Commar
+
+ Kasatti
+
+ Zolon Empire
+
+
+
+==================== STARSHATTER ====================
+ CAMPAIGN SEQUENCE
+
+--- COLD WAR, LOW TENSION ---
+
+1) Training Center - Live Fire:
+ Engage in "live fire" exercise with friendly
+ units on the border of Hegemony territory.
+
+--- COLD WAR, HIGH TENSION ---
+
+2) Restore Order:
+ An outlying Hegemony system is using terrorist
+ tactics to gain control of a neighboring neutral star
+ system to create "breathing room" between themselves
+ and the Alliance. Drive out the terrorists and get the
+ neutrals to sign a treaty with the Alliance.
+
+3) Freedom Fighters:
+ Hegemony taxation policy has driven an outlying
+ system to resort to despotism to collect needed
+ revenue. A group of local freedom fighters is
+ attempting to overthrow the Hegemony backed govt.
+ Aid the locals in driving out Hegemony forces.
+
+--- OPEN WARFARE ---
+
+In response to campaign 3, the Hegemony formally
+declares war on the Alliance.
+
+4) Fortress of Light:
+ The Hegemony has struck three Alliance systems,
+ but they have not yet had time to fortify their
+ position. Drive out the enemy forces, and
+ protect Alliance citizens and assets in the area.
+
+5) Firestorm:
+ Alliance Force:Command is planning a major offensive
+ against the core Hegemony systems. This operation
+ is to strike major war production and ship yards
+ to weaken the Hegemony resistance before the
+ main offensive.
+
+6) Nightfall:
+ Climactic operation against the enemy home system.
+ Discovery that the Hegemony worlds have been overrun
+ by the Zolon Empire.
+
+--- ALIEN STRIFE ---
+
+7) Spear of Triton
+
+8)
+
+
+
+
diff --git a/Doc/DESIGNER.TXT b/Doc/DESIGNER.TXT
new file mode 100644
index 0000000..237adb8
--- /dev/null
+++ b/Doc/DESIGNER.TXT
@@ -0,0 +1,173 @@
+Nicholas Walkland asked:
+
+>>I'm currently researching a feature [snip] about game design ideas
+>>and how to get developers and publishers
+>>interested. Basically, it's [for] people who think they
+>>have a good idea for a game [snip].
+>>How to present the ideas?
+>>Which companies accept these?
+>>Any games (good or bad) that have arisen from straightforward game
+>>plan/ideas?
+
+Kasey Chang answered,
+>Unfortunately, if you are NOT in the industry itself, it is
+>practically impossible to see your idea being even considered by major
+>companies, much less implemented. Almost all companies get their
+>ideas internally, esp. now in the days
+>of the sequels. In fact, I am NOT aware of ANY company that accepts
+>external ideas (probably due to possibility of law suits). [snip]
+>I imagine that it MAY be possible to get someone interested if one has
+>a complete game design document written and polished, preferably with
+>some sort of tech demo (minimal graphics), but that's more of a
+>"calling card" by one programmer or a small team hoping to become a
+>"studio" for a major developer, than one game enthusiast's fantasy.
+
+So now, I, Tom Sloper, add in my two cents:
+
+Very well said, Kasey! That is pretty much it, in a nutshell. Only a little
+more to add, which may help outsiders understand a little more of the reasoning
+behind this admittedly unfortunate reality.
+It has been said (right here at this NG) that "ideas are free" (which is
+considerably more true than what I would have said: that "ideas are a dime a
+dozen").
+Every employee in every game company's studio or production department (or R&D
+department, whatever yawannacallit) has at least one idea for their own "dream
+game." I myself have well over half a dozen such ideas that I've been wanting
+to do for years -- and I'm a "Senior Producer" (that just means I'm an /old/
+producer!) at a major game publishing company.
+So let's say that somebody -- "Joe Gamefan" -- writes to me and says (after
+signing a Disclosure Agreement in which we mutually agree that whatever he
+tells us may in fact be something we had already thought of, in which case we
+are not obligated to pay him if we had already started working on something
+similar, and a bunch of other clauses that cover a wide range of possible
+reasons for him to sue us for nothing more than the fact that he wrote us a
+letter), "Ooh! Ooh! I have a great idea for a new game -- why don't you guys
+do Fighter Raid (the classic Atari 2600 game originally published by my
+company, Publishcom), only set in Vietnam!"
+
+Several problems with this idea:
+
+- Like, uh, you think nobody here at Publishcom ever thought of this, or
+something really really close?
+- Like, duh! We LOST Vietnam, ya know???
+- Like, what hardware are you suggesting we do this game on? The Atari 2600?
+You didn't say!
+- Like, who is going to buy this game, why are they going to buy it, why is it
+better than a jillion other jet fighter games out there, etc., etc., etc.?
+- Like, who are you anyway? If we decide we like the idea, are you going to
+program it for us? Can you show it to me on a TV screen? Is that all you're
+sending me? A one-sentence idea in a letter???
+- Fighter Raid is a Publishcom trademark, fully owned and controlled by
+Publishcom. Now that you, Joe Gamefan, have proposed to us that we do an
+updated version, is the trademark diluted? If we do your idea, have you
+somehow weaseled your way into ownership of our trademark? What shenanigans do
+our lawyers have to perform to structure the contract with you so that we
+haven't somehow given away a valuable property over to you, after all you did
+was write a letter?
+
+So guess what I write back to Joe Gamefan? "Thank you very much for your
+submission. Unfortunately, it does not fit into Publishcom's plans at this
+time." Yadda yadda yadda.
+
+I have received numerous non-industry non-professionals' ideas over the years.
+Only one of them really made me sit up and take notice, not because of the game
+idea that it described, but rather because of its excellent presentation. I
+recommended that the submitter (obviously a budding artist of some spectacular
+talent) continue to pursue his interests, even though we didn't follow up on
+his game idea. I never heard from him again, unfortunately, so I don't know
+what he's doing now.
+
+I have also received numerous industry professionals' ideas in the 19 years
+I've been in this industry (I /told/ you I was old!). Guess how many of those
+made me (and my employer) sit up and take notice? Well, I don't have an exact
+count for you, but it was approximately two. Games that subsequently got
+produced and published on (1) the Super Nintendo and Sega Genesis, and (2) the
+Sony Playstation and Sega Saturn (the DOS version was eventually canceled).
+
+Here's the secret formula:
+
+1. Game ideas by a non-industry non-professional, described in a sentence (or a
+few sentences) on paper -- Chances: ZERO! Zilch. Zip. Forget it, it ain't
+gonna happen. Not in this lifetime, nosirreebob! Uh-uh, no way, Jose! Thanks
+for writing, so long now! My employer used to accept non-professional idea
+submissions, but I think the official stance now is that such things are more
+or less discouraged. I haven't checked recently.
+
+2. Games described in an impressive design document, by a very creative guy
+(non-industry, non-professional) who details all the aspects of the game, its
+prospective audience, its chances for success in the competitive marketplace,
+with detailed sketches and illustrations, and maybe even a hint of
+acknowledgment for the technical challenges inherent in the design -- Chances:
+A little better. At the very least, we would possibly (if there were openings)
+offer the creator of such a document a job as a game designer. His game most
+likely will not get made, however.
+
+3. Games adequately described in a written concept document, supplemented by an
+impressive video animation (viewable on a computer or game machine) -- Chances:
+Even better yet. One of the two games I referred to above came in to my
+employer in this form. We can see the idea, we can see that it is fun, we can
+see that the creator(s) can take the idea to the next step -- a finished game
+that we can sell. The party bringing us such a submission may well get a
+contract to develop the game into a real product that actually makes it to
+market. Note that anybody with the wherewithal to make such a presentation is
+most likely in the industry already.
+
+4. Game partially implemented, running on a computer or game machine --
+Chances: Better still. But the game had damn well better be really original
+and exciting, or filling a niche, or in keeping with current market hot
+buttons, or able to be adapted to a hot license, yadda yadda yadda. The other
+of the two games I referred to above came in to my employer in this form --
+from industry professionals.
+
+5. Game fully implemented. All a publisher has to do is put it in a box and
+it'll sell itself -- Chances: The Best. But the game had damn well better be
+really original and exciting, or filling a niche, or in keeping with current
+market hot buttons, or able to be adapted to a hot license, yadda yadda yadda.
+And you had better be ready to make changes if you want Publishcom to
+distribute it (and if they think it needs tweaking). Here's the kicker -- my
+employer gets these submissions in all the time, yet only one in twenty of
+these do we actually pick!
+
+Do the math. Games in category Five have a one in twenty chance -- and games
+in category One have a zero chance. The pattern should be clear to anyone
+who's got a good sense for games.
+
+Still don't understand why this is (why game publishers are so unreasonable)?
+Okay. Imagine for a second that we're talking about a movie idea instead of a
+game idea:
+1. You have a one-sentence idea for a movie, and you are an outsider -- you
+think a Hollywood studio is going to do anything with it? Hah!
+2. You have a full movie script, fully fleshed out, and/or a full movie
+storyboard -- but no actors, no producer, no director -- somebody is going to
+commit money to start production on it? How? Who? In what lifetime?
+3. You have a full script and a full storyboard, and some name actors who have
+read it and found it interesting -- looking a little better -- at least you
+have grounds for starting up some ulcers now, right?
+4. You have a full script, a full storyboard, some name actors ready to go,
+locations all lined up, equipment supplier identified, and a director who is
+interested -- all you need is a movie studio producer to put up some money so
+filming can start. Those ulcers are starting to grow pretty nicely now! Guess
+what industry you are in if you are at this stage??
+5. You have a completely finished, fully edited and post-produced film. All
+you need is to get it marketed and distributed. Have those ulcers eaten their
+way through the stomach lining yet? Surely you don't work in a profession
+unrelated to movie-making if you're in this position -- ???
+
+I reiterate what I have said before at this NG: You want to get your dream
+game produced and published? Get a job in the industry. Work your way up and
+become a producer (or above), or the president of your own company. That's
+step one. Step two will be self-evident once you have completed step one. In
+addition to knowing what step two is, you only need one more thing (assuming
+that the game idea is already world-class): luck. And good timing (I lied when
+I said there was only one more thing).
+
+I suppose, though, in the end, that Kasey's brief statement of the reality of
+this situation has not been added to in large measure by what I have said. His
+4 sentences said it all. All I did was beat that dead horse into the
+GROUND!!!!
+
+Tom Sloper
+(I have mentioned in previous posts what company I work for -- WELL, FORGET IT!
+ Pretend you never heard of me before! All I'm saying is, I'm stating my own
+opinions herein, and not the opinions of my employer. End of disclaimer.)
+:o) \ No newline at end of file
diff --git a/Doc/Engine.txt b/Doc/Engine.txt
new file mode 100644
index 0000000..ce256fe
--- /dev/null
+++ b/Doc/Engine.txt
@@ -0,0 +1,106 @@
+Generic 3D Game Engine
+----------------------
+
+Game // the main loop
+ <Specific Game>
+
+Video // abstract video interface
+ VideoDirect // any old full screen direct draw
+ VideoDirect3D // direct draw and direct 3d
+ VideoWindow // WinG or DDraw in a window
+
+PolyRend // abstract polygon renderer
+ PolyRendSW // software renderer
+ PolyRendD3D // Direct 3D renderer
+ PolyRend3Dfx // Glide renderer
+
+Screen // the whole screen, a collection of canvases
+ FullScreen // a single screen, full screen or in a single window
+ <Various Game Screens>
+ MultiWinScreen // multiple windows, one canvas per window
+
+Window // rectangular region of screen, basic 2D graphics
+ Canvas // window with View attached
+ Form // a collection of controls
+ Control // abstract dialog widgets
+ Label // static text
+ Edit // text input
+ List // single or multi-select
+ Button // various styles and states
+ Slider // also a gauge
+ Scrollbox // a scrolling window, user must subclass?
+
+WinEvent
+
+Cursor // mouse pointer
+
+View // what to draw in a canvas
+ CameraView // handles projection and clipping of visible polys
+ FormView
+ <Various Game Dialogs>
+ ImageView // displays a single bitmap
+ FadeView // fades the whole display in/out
+ MovieView // displays a movie or animation
+ <Various Game Views>
+
+Camera // position and point of view
+CameraClipper // view pyramid clipping for a given camera
+
+Color // RGBA color (32-bit)
+ColorIndex // index into 256 color palette (8-bit)
+
+fix // fixed point math
+Rect
+Vector
+Point
+Plane
+Poly
+Matrix
+
+// These are private to a particular camera view
+// Texture, MipMap, Cache, etc.
+
+DataLoader
+Asset
+ Palette (and inverse palette)
+ Bitmap
+ Model (3d)
+ Sound
+ Song (RedBook / MIDI ?)
+ Movie
+ Text
+ Font
+ ColorFont
+
+Physical // an entity in a 3d simulation that responds to physical
+ // forces (velocity, acceleration, drag, thrust)
+
+Director // something that controls something else, once per frame
+
+Universe // a collection of physical models
+ <Game universe>
+
+Graphic3D // a generic image in a 3d scene
+ Solid3D // polygon model
+ Sprite3D // textured billboard, with optional animation
+ Blob3D // textured poly, oriented towards camera
+ Composite3D // a graphic composed of other graphics (?)
+
+Light // a point light source
+
+Scene // collection of graphics and lights
+
+Sound // abstract sound (ambient or localized, memory or streamed)
+ SoundD3D // concrete sound using DirectSound(3D)
+SoundCard // something that plays sounds
+ SoundCardD3D // concrete sound card using DirectSound(3D)
+
+MotionController
+ Keyboard
+ Joystick
+ Mouse
+ ComboController
+ControllerInfo
+
+
+
diff --git a/Doc/Eschaton-Atmosphere.txt b/Doc/Eschaton-Atmosphere.txt
new file mode 100644
index 0000000..2a50e8a
--- /dev/null
+++ b/Doc/Eschaton-Atmosphere.txt
@@ -0,0 +1,9 @@
+Thanks. I'm currently working as a graphics programmer at Pandemic in LA, finishing Star Wars Battlefront 2. Its alright. I still ocassionally get the urge to finish cocommand, but its demands too much.
+
+The atmosphere shader is a somewhat straightforward optimization of a typical, if simplified, offline atmosphere model. The basic idea is to accumulate/integrate the in-scattering of light along a ray based on a density function which decreases exponentially with height above the sphere's surface. You integrate the density function along the ray which represents the total number of atmosphere particles hit along that ray. The in/out scattering (which I simplified to a single replacement % - an alpha value, really) are then determined from a simple exponential function of the accumulated density.
+
+The realtime aspect is that the ray integral density function is precomputed and fetched in a pixel shader with a texture lookup. This is straightforward because the model has just three basic scalar inputs - the ray's hit angle on the sphere, and the start and end intercepts along the ray (this model handles the cases where you are inside the atmosphere.) These setup parameters are calculated in the vertex shader.
+
+I later realized it could be further simplified to a 2D lookup. . .
+
+The atmosphere is rendered as the inner surface(reversed backface culling) of a sphere which surrounds the planet, and is necessarily somewhat larger than it. The shader is also rendered directly as a second pass on the planet geometry. The atmosphere sphere-shell is used for that sliver silhoutte of atmosphere overlapping empty space and for the atmosphere up close if you are near the planet surface. \ No newline at end of file
diff --git a/Doc/ExceptionHandler.cpp.txt b/Doc/ExceptionHandler.cpp.txt
new file mode 100644
index 0000000..4dd5938
--- /dev/null
+++ b/Doc/ExceptionHandler.cpp.txt
@@ -0,0 +1,446 @@
+
+/* ExceptionHandler.cpp */
+
+#include <windows.h>
+#include <imagehlp.h>
+
+extern void PrintLog(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+class ExceptionHandler
+{
+public:
+ ExceptionHandler();
+ ~ExceptionHandler();
+
+private:
+ static LPTOP_LEVEL_EXCEPTION_FILTER old_filter;
+
+ static LONG WINAPI ExceptionFilter(EXCEPTION_POINTERS* info);
+
+ static void PrintReport(EXCEPTION_POINTERS* info);
+
+ // Helper functions
+ static const char* GetExceptionString(DWORD dwCode);
+ static BOOL GetLogicalAddress(VOID* addr, char* module, int len,
+ DWORD& section, DWORD& offset);
+
+ static BOOL InitImageHelp();
+ static void ImageStackTrace(CONTEXT* context);
+ static void IntelStackTrace(CONTEXT* context);
+
+
+ // Make typedefs for some IMAGEHLP.DLL functions so that we can use them
+ // with GetProcAddress
+ typedef BOOL (__stdcall * SYMINITIALIZEPROC)(HANDLE, LPSTR, BOOL);
+ typedef BOOL (__stdcall * SYMCLEANUPPROC)(HANDLE);
+
+ typedef LPVOID (__stdcall *SYMFUNCTIONTABLEACCESSPROC)(HANDLE, DWORD);
+ typedef DWORD (__stdcall *SYMGETMODULEBASEPROC)(HANDLE, DWORD);
+ typedef BOOL (__stdcall *SYMGETSYMFROMADDRPROC)(HANDLE, DWORD, PDWORD, PIMAGEHLP_SYMBOL);
+
+ typedef BOOL (__stdcall * STACKWALKPROC)(DWORD,
+ HANDLE,
+ HANDLE,
+ LPSTACKFRAME,
+ LPVOID,
+ PREAD_PROCESS_MEMORY_ROUTINE,
+ PFUNCTION_TABLE_ACCESS_ROUTINE,
+ PGET_MODULE_BASE_ROUTINE,
+ PTRANSLATE_ADDRESS_ROUTINE);
+
+ static SYMINITIALIZEPROC SymInitialize;
+ static SYMCLEANUPPROC SymCleanup;
+ static STACKWALKPROC StackTrace;
+ static SYMFUNCTIONTABLEACCESSPROC SymFunctionTableAccess;
+ static SYMGETMODULEBASEPROC SymGetModuleBase;
+ static SYMGETSYMFROMADDRPROC SymGetSymFromAddr;
+};
+
+// +--------------------------------------------------------------------+
+
+LPTOP_LEVEL_EXCEPTION_FILTER ExceptionHandler::old_filter = 0;
+
+ExceptionHandler::SYMINITIALIZEPROC ExceptionHandler::SymInitialize = 0;
+ExceptionHandler::SYMCLEANUPPROC ExceptionHandler::SymCleanup = 0;
+ExceptionHandler::STACKWALKPROC ExceptionHandler::StackTrace = 0;
+
+ExceptionHandler::SYMFUNCTIONTABLEACCESSPROC
+ ExceptionHandler::SymFunctionTableAccess = 0;
+
+ExceptionHandler::SYMGETMODULEBASEPROC
+ ExceptionHandler::SymGetModuleBase = 0;
+
+ExceptionHandler::SYMGETSYMFROMADDRPROC
+ ExceptionHandler::SymGetSymFromAddr = 0;
+
+ExceptionHandler global_exception_handler;
+
+
+// +--------------------------------------------------------------------+
+
+ExceptionHandler::ExceptionHandler()
+{
+ old_filter = SetUnhandledExceptionFilter(ExceptionFilter);
+}
+
+ExceptionHandler::~ExceptionHandler()
+{
+ SetUnhandledExceptionFilter(old_filter);
+}
+
+// +--------------------------------------------------------------------+
+
+static bool in_filter = false;
+
+LONG WINAPI
+ExceptionHandler::ExceptionFilter(EXCEPTION_POINTERS* info)
+{
+ if (in_filter) {
+ PrintLog("\n\n*********************************************\n");
+ PrintLog("SECOND EXCEPTION CAUGHT: TERMINATING.\n");
+ PrintLog("*********************************************\n");
+ }
+
+ else {
+ in_filter = true;
+ PrintReport(info);
+ in_filter = false;
+ }
+
+ if (old_filter)
+ return old_filter(info);
+ else
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ExceptionHandler::PrintReport(EXCEPTION_POINTERS* info)
+{
+ EXCEPTION_RECORD* record = info->ExceptionRecord;
+ CONTEXT* context = info->ContextRecord;
+ DWORD code = record->ExceptionCode;
+
+ PrintLog("\n*********************************************\n");
+ PrintLog("FATAL EXCEPTION:\n");
+
+ PrintLog("\nRegisters:\n");
+ PrintLog("EAX: %08x\n", context->Eax);
+ PrintLog("EBX: %08x\n", context->Ebx);
+ PrintLog("ECX: %08x\n", context->Ecx);
+ PrintLog("EDX: %08x\n", context->Edx);
+ PrintLog("EDI: %08x\n", context->Edi);
+ PrintLog("ESI: %08x\n", context->Esi);
+ PrintLog("EBP: %08x\n", context->Ebp);
+ PrintLog("\n");
+ PrintLog("CS:EIP: %04x:%08x\n", context->SegCs, context->Eip);
+ PrintLog("SS:ESP: %04x:%08x\n", context->SegSs, context->Esp);
+ PrintLog("DS: %04x\n", context->SegDs);
+ PrintLog("ES: %04x\n", context->SegEs);
+ PrintLog("FS: %04x\n", context->SegFs);
+ PrintLog("GS: %04x\n", context->SegGs);
+ PrintLog("Flags: %08x\n", context->EFlags );
+ PrintLog("\n");
+
+ PrintLog("Exception Code: %08x %s\n",code, GetExceptionString(code));
+ PrintLog("Exception Addr: %08x \n", record->ExceptionAddress);
+
+ if (code == EXCEPTION_ACCESS_VIOLATION && record->NumberParameters >= 2) {
+ if (record->ExceptionInformation[0])
+ PrintLog(" Program attempted to WRITE to address 0x%08x\n", record->ExceptionInformation[1]);
+ else
+ PrintLog(" Program attempted to READ from address 0x%08x\n", record->ExceptionInformation[1]);
+ }
+
+ if (InitImageHelp()) {
+ ImageStackTrace(context);
+ SymCleanup(GetCurrentProcess());
+ }
+ else {
+ IntelStackTrace(context);
+ }
+
+ PrintLog("\n*********************************************\nPROGRAM TERMINATED.\n");
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+ExceptionHandler::GetExceptionString(DWORD code)
+{
+ #define EXCEPTION( x ) case EXCEPTION_##x: return #x;
+
+ switch (code) {
+ EXCEPTION( ACCESS_VIOLATION )
+ EXCEPTION( DATATYPE_MISALIGNMENT )
+ EXCEPTION( BREAKPOINT )
+ EXCEPTION( SINGLE_STEP )
+ EXCEPTION( ARRAY_BOUNDS_EXCEEDED )
+ EXCEPTION( FLT_DENORMAL_OPERAND )
+ EXCEPTION( FLT_DIVIDE_BY_ZERO )
+ EXCEPTION( FLT_INEXACT_RESULT )
+ EXCEPTION( FLT_INVALID_OPERATION )
+ EXCEPTION( FLT_OVERFLOW )
+ EXCEPTION( FLT_STACK_CHECK )
+ EXCEPTION( FLT_UNDERFLOW )
+ EXCEPTION( INT_DIVIDE_BY_ZERO )
+ EXCEPTION( INT_OVERFLOW )
+ EXCEPTION( PRIV_INSTRUCTION )
+ EXCEPTION( IN_PAGE_ERROR )
+ EXCEPTION( ILLEGAL_INSTRUCTION )
+ EXCEPTION( NONCONTINUABLE_EXCEPTION )
+ EXCEPTION( STACK_OVERFLOW )
+ EXCEPTION( INVALID_DISPOSITION )
+ EXCEPTION( GUARD_PAGE )
+ EXCEPTION( INVALID_HANDLE )
+ }
+
+ static char buffer[512] = { 0 };
+
+ FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
+ GetModuleHandle("NTDLL.DLL"),
+ code, 0, buffer, sizeof(buffer), 0 );
+
+ return buffer;
+}
+
+// +--------------------------------------------------------------------+
+
+BOOL
+ExceptionHandler::GetLogicalAddress(void* addr, char* mod_name, int len, DWORD& section, DWORD& offset)
+{
+ MEMORY_BASIC_INFORMATION mbi;
+
+ if (!VirtualQuery(addr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ DWORD hMod = (DWORD)mbi.AllocationBase;
+
+ if (!GetModuleFileName((HMODULE)hMod, mod_name, len))
+ return FALSE;
+
+ PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER) hMod;
+ PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + pDosHdr->e_lfanew);
+ PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr );
+
+ DWORD rva = (DWORD)addr - hMod; // RVA is offset from module load address
+
+ // Iterate through the section table, looking for the one that encompasses
+ // the linear address.
+ for (unsigned i = 0; i < pNtHdr->FileHeader.NumberOfSections; i++, pSection++ ) {
+ DWORD sectionStart = pSection->VirtualAddress;
+ DWORD sectionEnd = sectionStart
+ + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize);
+
+ // Is the address in this section???
+ if ((rva >= sectionStart) && (rva <= sectionEnd)) {
+ // Yes, address is in the section. Calculate section and offset,
+ // and store in the "section" & "offset" params, which were
+ // passed by reference.
+ section = i+1;
+ offset = rva - sectionStart;
+ return TRUE;
+ }
+ }
+
+ return FALSE; // Should never get here!
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ExceptionHandler::IntelStackTrace(CONTEXT* context)
+{
+ PrintLog("\nStack Trace (Intel):\n");
+ PrintLog("Address Frame Logical addr Module\n");
+
+ DWORD pc = context->Eip;
+ DWORD* pFrame;
+ DWORD* pPrevFrame;
+
+ pFrame = (DWORD*)context->Ebp;
+
+ do {
+ char mod_name[256] = { 0 };
+ DWORD section = 0, offset = 0;
+
+ GetLogicalAddress((void*)pc, mod_name, 256, section, offset);
+
+ PrintLog("%08X %08X %04X:%08X %s\n",
+ pc, pFrame, section, offset, mod_name);
+
+ pc = pFrame[1];
+ pPrevFrame = pFrame;
+ pFrame = (PDWORD)pFrame[0]; // proceed to next higher frame on stack
+
+ if ((DWORD)pFrame & 3) // Frame pointer must be aligned on a
+ break; // DWORD boundary. Bail if not so.
+
+ if (pFrame <= pPrevFrame)
+ break;
+
+ // Can two DWORDs be read from the supposed frame address?
+ if (IsBadWritePtr(pFrame, sizeof(PVOID)*2))
+ break;
+
+ }
+ while ( 1 );
+}
+
+// +--------------------------------------------------------------------+
+
+void ExceptionHandler::ImageStackTrace(CONTEXT* context)
+{
+ PrintLog("\nStack Trace (Symbolic):\n");
+ PrintLog("Address Frame\n");
+
+ // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
+ STACKFRAME sf;
+ memset(&sf, 0, sizeof(sf));
+
+ // Initialize the STACKFRAME structure for the first call. This is only
+ // necessary for Intel CPUs, and isn't mentioned in the documentation.
+ sf.AddrPC.Offset = context->Eip;
+ sf.AddrPC.Mode = AddrModeFlat;
+ sf.AddrStack.Offset = context->Esp;
+ sf.AddrStack.Mode = AddrModeFlat;
+ sf.AddrFrame.Offset = context->Ebp;
+ sf.AddrFrame.Mode = AddrModeFlat;
+
+ while ( 1 ) {
+ if (!StackTrace( IMAGE_FILE_MACHINE_I386,
+ GetCurrentProcess(),
+ GetCurrentThread(),
+ &sf,
+ context,
+ 0,
+ SymFunctionTableAccess,
+ SymGetModuleBase,
+ 0))
+ break;
+
+ if (sf.AddrFrame.Offset == 0) // Basic sanity check to make sure
+ break; // the frame is OK. Bail if not.
+
+ PrintLog("%08x %08x ", sf.AddrPC.Offset, sf.AddrFrame.Offset);
+
+ // IMAGEHLP is wacky, and requires you to pass in a pointer to an
+ // IMAGEHLP_SYMBOL structure. The problem is that this structure is
+ // variable length. That is, you determine how big the structure is
+ // at runtime. This means that you can't use sizeof(struct).
+ // So...make a buffer that's big enough, and make a pointer
+ // to the buffer. We also need to initialize not one, but TWO
+ // members of the structure before it can be used.
+
+ BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 512];
+ PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)symbolBuffer;
+ pSymbol->SizeOfStruct = sizeof(symbolBuffer);
+ pSymbol->MaxNameLength = 512;
+
+ DWORD symDisplacement = 0; // Displacement of the input address,
+ // relative to the start of the symbol
+
+ if (SymGetSymFromAddr(GetCurrentProcess(), sf.AddrPC.Offset,
+ &symDisplacement, pSymbol)) {
+ PrintLog("%-40s [%04X]\n", pSymbol->Name, symDisplacement);
+ }
+ else {
+ char mod_name[256] = { 0 };
+ DWORD section = 0, offset = 0;
+
+ GetLogicalAddress((PVOID)sf.AddrPC.Offset,
+ mod_name, 256, section, offset );
+
+ PrintLog("%04X:%08X %s\n", section, offset, mod_name);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+BOOL
+ExceptionHandler::InitImageHelp()
+{
+ PrintLog("\n");
+
+ HMODULE h = LoadLibrary("IMAGEHLP.DLL");
+ if (!h) {
+ PrintLog("--- could not load IMAGEHLP.DLL ---\n");
+ return FALSE;
+ }
+
+ SymInitialize = (SYMINITIALIZEPROC) GetProcAddress(h, "SymInitialize");
+ if (!SymInitialize) {
+ PrintLog("--- could not find SymInitialize ---\n");
+ return FALSE;
+ }
+
+ SymCleanup = (SYMCLEANUPPROC) GetProcAddress(h, "SymCleanup");
+ if (!SymCleanup) {
+ PrintLog("--- could not find SymCleanup ---\n");
+ return FALSE;
+ }
+
+ StackTrace = (STACKWALKPROC) GetProcAddress(h, "StackWalk");
+ if (!StackTrace) {
+ PrintLog("--- could not find StackWalk ---\n");
+ return FALSE;
+ }
+
+ SymFunctionTableAccess = (SYMFUNCTIONTABLEACCESSPROC)
+ GetProcAddress(h, "SymFunctionTableAccess");
+
+ if (!SymFunctionTableAccess) {
+ PrintLog("--- could not find SymFunctionTableAccess ---\n");
+ return FALSE;
+ }
+
+ SymGetModuleBase = (SYMGETMODULEBASEPROC) GetProcAddress(h, "SymGetModuleBase");
+ if (!SymGetModuleBase) {
+ PrintLog("--- could not find SymGetModuleBase ---\n");
+ return FALSE;
+ }
+
+ SymGetSymFromAddr = (SYMGETSYMFROMADDRPROC) GetProcAddress(h, "SymGetSymFromAddr");
+ if (!SymGetSymFromAddr) {
+ PrintLog("--- could not find SymGetSymFromAddr ---\n");
+ return FALSE;
+ }
+
+ if (!SymInitialize(GetCurrentProcess(), 0, TRUE)) {
+ PrintLog("--- could not Initialize IMAGEHLP.DLL ---\n");
+
+ DWORD glerr = GetLastError();
+
+ LPVOID lpMsgBuf;
+ if (!FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ glerr,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL ))
+ {
+ PrintLog(" get last error: %08x\n", glerr);
+ return FALSE;
+ }
+
+ // Display the string.
+ PrintLog((LPCTSTR)lpMsgBuf);
+ PrintLog("\n\n");
+
+ // Free the buffer.
+ LocalFree( lpMsgBuf );
+ return FALSE;
+ }
+
+ PrintLog("Loaded IMAGEHLP.DLL\n");
+ return TRUE;
+}
+
diff --git a/Doc/GAMEPLAY.TXT b/Doc/GAMEPLAY.TXT
new file mode 100644
index 0000000..a3f123d
--- /dev/null
+++ b/Doc/GAMEPLAY.TXT
@@ -0,0 +1,124 @@
+STARSHATTER GAMEPLAY DESIGN
+
+ Challenge Skill
+ ------------- ----------------
+ 1 Basic flight control the stick and throttle
+
+ 2 Destroy a target coordinate flight to track the target
+ use the gunsight to lead
+ select an appropriate weapon
+ fire upon the target
+
+ 3 Avoid being destroyed recognize a threat
+ perform evasive ACM
+
+ 4 Avoid a rear attack recognize the threat warning
+ perform reversal (split-s, loop, etc)
+
+ 5 Avoid a collision recognize the impending collision
+ steer away or slow down to miss
+
+ 6 Maintain ship integrity recognize low shield status
+ disengage and wait for shields to regenerate
+ -or- seek a repair site or powerup
+
+ 7 Locate a target interpret the scanner
+ fly towards the target
+
+ 8 Coordinate with a wingman avoid colliding
+ avoid friendly fire
+ coordinate target selection
+ provide cover fire
+
+
+
+(--misc challenges--)
+
+deal with large numbers of enemy forces
+assess enemy capabilities
+attack and destroy large (cap) ships
+partial loss of control (e.g. loss of stabilizer or loss of yaw control)
+
+(--misc skills--)
+
+manage resources and health status
+prioritize objectives
+lock and fire a smart weapon
+avoid an incoming smart weapon
+assign a target or an objective to a wingman
+navigate with ultradrive
+effectively use tactical view
+seek cover behind a large ship/station
+seek re-supply
+
+-------------------------
+avoid detection / detect an enemy
+
+This needs a little explanation. I hate cloaking systems.
+They are way too common (like everything else isn't?) and
+there is no skill to using them. Instead, we will use a
+system of reduced radiation signature reducing probability
+of detection (remember F-19 Stealth Fighter?)
+
+The ships will have an Emission Control mode switch. When
+in EmCon B mode, shields, weapons, target computer and IFF
+will be disabled. Long range sensors will be disabled, and
+the ship will need to rely on short range (passive) sensors
+alone. There will be a 3 to 5 second delay between commanding
+and completing an EmCon mode change.
+
+While running silent, the ship (and especially the drive
+flares will still be visible, but much harder to track or
+detect. Possibility of detection can be reduced further
+by reducing throttle or using a flare-less "stealth" drive.
+
+Short range (passive) sensors will be able to detect EmCon
+B targets up to a range of 30K, and EmCon A targets up to
+a range of 100K. Long range (active) sensors will be able
+to track all targets up to a range of 300K (maybe as high
+as 1M). [Actually, you probably want to make these prob-
+abilities of detection.]
+
+The flag ship serves as Wide Area Search And Track (WASAT)
+coordinator for the fleet. That is, all ships in the fleet
+will have access to the flag ship's target list. All ships
+will periodically send their own target list back to the
+flag ship for distribution to the fleet.
+
+-------------------------
+prioritize ship sub-targets
+
+When dealing with larger ships, there is the problem of
+"what to shoot first?" Larger ships have multiple subsystems,
+each of which may be targetted separately. (Of course, you
+can always just shoot for the hull and try to blow up the
+whole ship -- however, this will not always be the wisest
+course of action.) What system to target depends on what
+actions you are trying to prevent:
+
+Weapons: destroy these to prevent the cap-ship from attacking
+ another ship or space station. Doesn't prevent
+ escape or fighter operation.
+
+Ultra: destroy this to keep the ship from leaving.
+
+Drive: destroy this to keep the ship from advancing and
+ forming up. The ship can still turn and fire and
+ still launch and recover fighters. Also prevents
+ ultra navigation.
+
+Flt Dck: destroy this to keep the ship from launching or
+ recovering (or resupplying) fighters.
+
+Systems: damage here has a random effect. May take out
+ targetting, or navigation, or fighter ops. Wild
+ Card.
+
+Vital: the whole works. If the ship gets hulled, it dies.
+ This is most relevant for smaller ships. Big ships
+ have *huge* integrity values, and require huge
+ amounts of force to destroy.
+
+Each of these resources has a unique location on the ship.
+They are protected by whatever shield generator is protecting
+that part of the ship.
diff --git a/Doc/Manual.doc b/Doc/Manual.doc
new file mode 100644
index 0000000..00bca08
--- /dev/null
+++ b/Doc/Manual.doc
Binary files differ
diff --git a/Doc/Manual2.doc b/Doc/Manual2.doc
new file mode 100644
index 0000000..6796946
--- /dev/null
+++ b/Doc/Manual2.doc
Binary files differ
diff --git a/Doc/NEARMAP.TXT b/Doc/NEARMAP.TXT
new file mode 100644
index 0000000..1aa4585
--- /dev/null
+++ b/Doc/NEARMAP.TXT
@@ -0,0 +1,117 @@
+Stars farther than 0.00pc and closer than 7.00pc,
+Lum between 0.0 and 100.0
+Gliese StarName Spec DistPC X Y Z Xg Yg Zg Lum
+ 1 DM-37 15492 M4V 4.44 3.52, 0.04, -2.71| 1.04, -0.30, -4.31 0.01
+ 15A DM+43 44 M1Ve 3.55 2.56, 0.17, 2.45| -1.51, 3.01, -1.12 0.01
+ 15B M6Ve 3.55 2.56, 0.17, 2.45| -1.51, 3.01, -1.12 0.00
+ 19 BET HYI G1IV 6.29 1.35, 0.14, -6.14| 2.76, -3.97, -4.02 2.75
+ 33 DM+04 123 K2V 6.94 6.78, 1.37, 0.61| -1.95, 3.18, -5.86 0.22
+ 34A ETA CAS G0V 5.88 3.09, 0.63, 4.96| -3.16, 4.94, -0.52 1.32
+ 34B M0V 5.88 3.09, 0.63, 4.96| -3.16, 4.94, -0.52 0.03
+ 35 VAN MAANEN 2 G wD 4.18 4.08, 0.84, 0.38| -1.19, 1.91, -3.53 0.00
+ 54.1 L 725-32 M5de 6.90 6.28, 1.99, -2.05| -1.17, 0.67, -6.76 0.00
+ 65A L 726-8 M5de 2.72 2.36, 1.06, -0.85| -0.67, 0.05, -2.64 0.00
+ 65B UV CET M5de 2.72 2.36, 1.06, -0.85| -0.67, 0.05, -2.64 0.00
+ 66A P ERI K2V 6.54 3.29, 1.50, -5.45| 1.11, -3.11, -5.64 0.20
+ 66B DM-56 328 K5Ve 6.54 3.29, 1.50, -5.45| 1.11, -3.11, -5.64 0.17
+ 71 TAU CET G8Vp 3.61 3.13, 1.49, -1.01| -1.02, 0.12, -3.46 0.47
+ 83.1 L 1159-16 M8de 4.69 3.99, 2.24, 1.04| -2.73, 1.73, -3.40 0.00
+139 82 ERI G5V 6.21 2.94, 3.44, -4.26| -1.14, -3.27, -5.16 0.70
+144 EPS ERI K2Ve 3.31 1.98, 2.59, -0.55| -2.13, -0.60, -2.46 0.32
+166A OMI(2) ERI K1Ve 4.88 2.18, 4.32, -0.66| -3.60, -1.36, -3.00 0.37
+166B DM-07 781 A wD 4.88 2.17, 4.32, -0.66| -3.60, -1.36, -3.00 0.00
+166C M4de 4.88 2.17, 4.32, -0.66| -3.60, -1.36, -3.00 0.00
+169.1A AC+58 25001 M4d 5.21 1.06, 2.47, 4.46| -4.38, 2.73, 0.66 0.00
+169.1B AC+58 25002 C wD 5.21 1.06, 2.47, 4.46| -4.38, 2.73, 0.66 0.00
+191 DM-45 1841 M0V 3.91 0.60, 2.70, -2.76| -1.06, -2.97, -2.30 0.00
+205 DM-03 1123 M1Ve 5.88 0.79, 5.82, -0.38| -4.95, -2.51, -1.96 0.02
+213 AC+12 1800-2 M5d 5.95 0.53, 5.79, 1.29| -5.71, -1.39, -0.95 0.00
+223.2 LP 658-2 K wDe 6.02 0.19, 6.01, -0.44| -5.04, -2.93, -1.50 0.00
+229 DM-21 1377 M1Ve 5.75 -0.20, 5.33, -2.14| -3.61, -4.09, -1.82 0.02
+234A ROSS 614 M7de 3.97 -0.46, 3.94, -0.19| -3.31, -2.14, -0.43 0.00
+234B 3.97 -0.46, 3.94, -0.19| -3.31, -2.14, -0.43 91.20
+244A ALF CMA A1V 2.65 -0.47, 2.50, -0.76| -1.78, -1.92, -0.41 24.66
+244B A wD 2.65 -0.47, 2.50, -0.76| -1.78, -1.92, -0.41 0.00
+251 AC+33 25644 M4d 5.95 -1.11, 4.85, 3.27| -5.74, -0.29, 1.56 0.00
+268 AC+38 23616 M5de 5.92 -1.33, 4.43, 3.69| -5.56, 0.10, 2.02 0.00
+273 DM+05 1668 M5d 3.70 -1.33, 3.44, 0.35| -3.08, -1.95, 0.67 0.00
+280A ALF CMI F5IV+ 3.51 -1.43, 3.19, 0.33| -2.84, -1.90, 0.79 8.02
+280B F wD 3.51 -1.43, 3.19, 0.33| -2.84, -1.90, 0.79 0.00
+285 YZ CMI M4de 6.06 -2.61, 5.46, 0.39| -4.78, -3.45, 1.41 0.00
+293 L 97-12 C wD 5.78 -1.04, 1.94, -5.35| 0.97, -5.37, -1.92 0.00
+299 ROSS 619 M5d 6.62 -3.49, 5.53, 1.04| -5.09, -3.44, 2.47 0.00
+300 L 674-15 M 5.85 -2.94, 4.59, -2.13| -2.77, -5.10, 0.70 0.00
+338A DM+53 1320 M0Ve 6.02 -2.69, 2.44, 4.80| -4.27, 1.15, 4.08 0.03
+338B DM+53 1321 M0Ve 6.02 -2.69, 2.44, 4.80| -4.27, 1.15, 4.08 0.03
+380 DM+50 1725 K7Ve 4.50 -2.57, 1.36, 3.44| -2.68, 0.68, 3.56 0.04
+388 DM+20 2465 M4Ve 4.90 -4.14, 2.00, 1.69| -2.28, -1.69, 4.00 0.00
+406 WOLF 359 M8de 2.35 -2.23, 0.66, 0.30| -0.57, -1.17, 1.95 0.00
+408 AC+23 468-46 M3d 6.62 -5.87, 1.64, 2.60| -2.30, -1.70, 5.98 0.00
+411 DM+36 2147 M2Ve 2.52 -1.96, 0.52, 1.49| -1.04, -0.09, 2.29 0.01
+412A DM+44 2051 M2Ve 5.38 -3.76, 0.96, 3.72| -2.38, 0.49, 4.80 0.01
+412B WX UMA M8de 5.38 -3.76, 0.95, 3.72| -2.38, 0.49, 4.80 0.00
+440 L 145-141 C wD 4.85 -2.08, 0.15, -4.38| 2.12, -4.36, -0.24 0.00
+445 AC+79 3888 M4sd 5.10 -0.97, 0.07, 5.01| -2.41, 3.22, 3.14 0.00
+447 ROSS 128 M5d 3.32 -3.31, 0.22, 0.06| 0.00, -1.68, 2.86 0.00
+473A WOLF 424 M5de 4.33 -4.23, -0.57, 0.70| 0.45, -1.31, 4.10 0.00
+473B 4.33 -4.23, -0.57, 0.70| 0.45, -1.31, 4.10 0.00
+477.1 DM+46 1797 K4V 6.10 -4.19, -0.62, 4.39| -1.30, 1.48, 5.77 0.05
+526 DM+15 2620 M4Ve 4.98 -4.32, -2.09, 1.30| 1.49, -0.22, 4.74 0.01
+551 PROXIMA CEN M5de 1.31 -0.49, -0.36, -1.17| 0.91, -0.94, -0.04 0.00
+555 DM-11 3759 M4d 6.25 -4.82, -3.75, -1.33| 4.24, -1.68, 4.27 0.00
+559A ALF CEN G2V 1.35 -0.51, -0.42, -1.17| 0.96, -0.94, -0.02 1.66
+559B K0V 1.35 -0.51, -0.42, -1.17| 0.96, -0.94, -0.02 0.48
+566A XI BOO G8Ve 6.76 -4.72, -4.29, 2.23| 2.98, 1.27, 5.93 0.56
+566B K4Ve 6.76 -4.72, -4.29, 2.23| 2.98, 1.27, 5.93 0.08
+570A DM-20 4125 K5Ve 5.56 -3.75, -3.57, -2.01| 4.34, -1.73, 3.00 0.14
+570B DM-20 4123 M2V 5.56 -3.75, -3.57, -2.01| 4.34, -1.73, 3.00 0.02
+581 DM-07 4003 M5d 6.54 -4.23, -4.91, -0.86| 4.98, -0.51, 4.20 0.00
+588 DM-40 9712 M4 5.92 -2.73, -3.53, -3.89| 5.14, -2.65, 1.24 0.00
+628 DM-12 4523 M5d 4.02 -1.54, -3.61, -0.87| 3.67, 0.22, 1.61 0.00
+643 WOLF 629 M4sd 6.21 -1.78, -5.88, -0.89| 5.68, 1.11, 2.24 0.00
+644A DM-08 4352 M4de 6.21 -1.78, -5.88, -0.89| 5.69, 1.11, 2.24 0.00
+644B M4d 6.21 -1.78, -5.88, -0.89| 5.69, 1.11, 2.24 0.00
+644C VB 8 6.21 -1.77, -5.88, -0.90| 5.69, 1.11, 2.23 0.00
+661A DM+45 2505 M3d 6.45 -0.96, -4.40, 4.62| 1.67, 4.95, 3.79 0.00
+661B M3 6.45 -0.96, -4.40, 4.62| 1.67, 4.95, 3.79 0.00
+663A DM-26 12026 K1Ve 5.43 -1.01, -4.76, -2.43| 5.39, -0.16, 0.65 0.26
+663B K1Ve 5.43 -1.01, -4.76, -2.43| 5.39, -0.16, 0.65 0.25
+664 DM-26 12036 K5Ve 5.43 -0.99, -4.76, -2.42| 5.39, -0.15, 0.64 0.08
+674 DM-46 11540 M4 4.63 -0.48, -3.13, -3.38| 4.40, -1.34, -0.55 0.00
+682 DM-44 11909 M5 4.69 -0.39, -3.34, -3.28| 4.52, -1.13, -0.54 0.00
+687 DM+68 946 M3V 4.69 -0.18, -1.72, 4.36| -0.60, 3.94, 2.49 0.00
+693 L 205-128 M 5.88 -0.24, -3.17, -4.95| 5.18, -2.38, -1.47 0.00
+699 BARNARD'S ST M5V 1.81 -0.04, -1.81, 0.14| 1.51, 0.90, 0.44 0.00
+702A DM+02 3482 K0Ve 5.13 0.07, -5.12, 0.22| 4.36, 2.51, 1.01 0.49
+702B K5Ve 5.13 0.07, -5.12, 0.22| 4.36, 2.51, 1.01 0.10
+725A DM+59 1915 M4d 3.55 0.33, -1.77, 3.06| 0.04, 3.23, 1.45 0.00
+725B M5d 3.55 0.33, -1.77, 3.06| 0.04, 3.23, 1.45 0.00
+729 AC-24 2833-1 M4de 2.90 0.54, -2.60, -1.17| 2.80, 0.56, -0.52 0.00
+752A DM+04 4048 M3Ve 5.78 1.84, -5.46, 0.51| 4.39, 3.75, -0.33 0.01
+752B VB 10 M5de 5.78 1.84, -5.46, 0.51| 4.39, 3.74, -0.33 0.00
+754 L 347-14 M7 5.71 1.32, -3.77, -4.08| 5.18, -0.69, -2.31 0.00
+764 SIG DRA K0V 5.68 0.78, -1.82, 5.32| -1.04, 5.17, 2.12 0.39
+768 ALF AQL A7IV 5.05 2.27, -4.44, 0.77| 3.36, 3.69, -0.78 11.59
+780 DEL PAV G8V 5.71 1.18, -1.97, -5.23| 4.17, -2.43, -3.06 1.14
+783A DM-36 13940 K3V 5.65 2.41, -3.87, -3.34| 4.83, 0.44, -2.90 0.22
+783B M5d 5.65 2.41, -3.87, -3.34| 4.83, 0.44, -2.90 0.00
+784 DM-45 13677 M0V 6.10 2.31, -3.61, -4.34| 5.09, -0.46, -3.32 0.02
+820A 61 CYG K5Ve 3.38 1.91, -1.83, 2.10| 0.45, 3.33, -0.34 0.08
+820B K7Ve 3.38 1.91, -1.83, 2.10| 0.45, 3.33, -0.34 0.04
+825 DM-39 14192 M0Ve 3.85 2.24, -1.98, -2.42| 2.75, 0.19, -2.69 0.03
+829 AC+17 534-10 M4de 6.54 4.90, -3.86, 1.96| 2.09, 5.61, -2.62 0.00
+832 DM-49 13515 M1V 4.67 2.42, -1.86, -3.54| 3.17, -0.60, -3.38 0.01
+845 EPS IND K5Ve 3.44 1.62, -0.94, -2.88| 2.10, -0.92, -2.55 0.14
+860A DM+56 2783 M3d 3.95 1.95, -0.85, 3.33| -1.00, 3.82, -0.00 0.00
+860B DO CEP M4de 3.95 1.95, -0.85, 3.33| -1.00, 3.82, -0.00 0.00
+866 L 789-6 M7de 3.28 2.95, -1.13, -0.88| 1.22, 1.31, -2.75 0.00
+873 DM+43 4305 M4de 5.13 3.49, -1.19, 3.57| -0.92, 4.91, -1.16 0.00
+876 DM-15 6290 M5d 4.78 4.42, -1.38, -1.20| 1.49, 1.91, -4.13 0.00
+880 DM+15 4733 M2de 6.85 6.30, -1.86, 1.92| 0.27, 5.38, -4.24 0.01
+881 ALF PSA A3V 6.71 5.59, -1.63, -3.34| 2.66, 1.00, -6.08 14.06
+887 DM-36 15693 M2Ve 3.58 2.80, -0.72, -2.11| 1.46, 0.13, -3.27 0.01
+892 DM+56 2966 K3V 6.80 3.63, -0.79, 5.70| -2.31, 6.39, -0.38 0.25
+896A DM+19 5116 M4de 6.45 6.02, -0.81, 2.17| -0.75, 4.95, -4.07 0.00
+896B M6de 6.45 6.02, -0.81, 2.17| -0.75, 4.95, -4.07 0.00
+905 ROSS 248 M6de 3.14 2.26, -0.20, 2.18| -1.03, 2.83, -0.92 0.00
+908 DM+01 4774 M2Ve 5.71 5.70, -0.33, 0.21| -0.19, 3.12, -4.78 0.01
diff --git a/Doc/OUTLINE.TXT b/Doc/OUTLINE.TXT
new file mode 100644
index 0000000..b1f5809
--- /dev/null
+++ b/Doc/OUTLINE.TXT
@@ -0,0 +1,47 @@
+STARSHATTER DEMO 1.0 MANUAL (OUTLINE)
+
+1. INTRODUCTION
+ What is StarShatter?
+ What is this demo all about?
+ What is required to play?
+ Hardware
+ Operating System and Drivers
+
+2. INSTALLATION
+ (just unzip into a directory)
+
+3. SETUP / OPTIONS
+ Video Options
+ Software Only
+ Direct3D
+ Detail Settings
+ Sound Options
+ (none)
+ Keyboard Config
+
+4. GAMEPLAY INSTRUCTIONS
+ Fighters and Starships
+ The Displays
+ HUD
+ NAV
+ ENG
+ WEP
+ The Controls
+ Keyboard
+ Joystick
+ Using the Mouse
+ About Your Ship
+ Drive
+ Weapons
+ Shields
+ Sensors
+
+5. MISSION INFO
+ Training Cruise
+ Fighter Sweep
+ Air Superiority
+ Destroyer Duel
+
+6. TROUBLESHOOTING and KNOWN PROBLEMS
+
+7. CREDITS and CONTACT INFO
diff --git a/Doc/PAVLOV.TXT b/Doc/PAVLOV.TXT
new file mode 100644
index 0000000..662437e
--- /dev/null
+++ b/Doc/PAVLOV.TXT
@@ -0,0 +1,59 @@
+Freespace 2 - great game. Lots of action, great looking, good
+storyline, gotta love it.
+
+But I want the thing to be more realistic. Before you freak out and yell at me for wanting a game with subspace nodes, meson bombs, and alien critter to be realistic, let me explain what I mean.
+
+The background I'm coming from is that of a pretty serious flight simmer. Those military sims involve, like FS2 and other space sim, large amounts of moving ans shooting things, but military sims have reality to be compared against.
+
+If you look at the progression of flight sims since their inception, what you see is that the first sims were the "you against the world" sims, like Andy Hollis' F-19 Stealth Fighter and F-15 Strike Eagle 3. You'd go in by your lonesome and complete your mission.
+
+Around the time of Falcon 3, we started getting a more integrated world -- while much of the "immersion" of Falcon 3 was all cleverly faked, it sure looked good. You could plan missions for your
+squadron, enemy aircraft would show up over targets, friendly flights would appear to have their own mission, and everything would look pretty much like a real war.
+
+Moving onto Falcon 4, I think Microprose went overboard and the entire war on the Korean Peninsula is simulated down to battalion level. Sure, its really immersive, but hard as hell to debug. :-)
+
+Anyway, back to space sims.
+
+We started off with sims like Wing Commander. Remember Wing Commander and its limitations? The fixed number of objects and types. You would have you and your wingman, and up to two other flights of up to five fighters, with the possibility of a couple capital ships around. With your wingman not much better than cannon fodder (Gee! Pav, looks like you got 22 today! Spirit, you came up empty!) It pretty much was "you against the world."
+
+Enter X-Wing, Tie Fighter, X-Wing Alliance, WC 2, 3, 4, P, and
+Freespace. All these sims get better, are prettier, and allow for more ships, but unlike flight sims, they're still all canned and scripted missions. Not to fault canned missions, but they can lead to some odd situations.
+
+For example, in Freespace 2, there is a mission where a <spoiler friendly> BIG GOOD GUY SHIP faces off against a BIG BAD GUY SHIP, and what else is currently in the mission?
+
+Me. That's it. Two capital ships and me. What happened to the escorts of the big ships? What happened to the fighter cover? In this mission, the big ships never hard any support ships. Everything is canned.
+
+In Freespace 2, when you see a bunch of friendly ships jump in, what does that mean? It means that the designer told some friendly ships to jump in, and maybe some protect the others. More likely, a
+destroyer will jump in with no supporting ships, and just go attack the bad guy destroyer, also with no supporting ships.
+
+From a flight simmers perspective, used to realism and military
+organizations, this is just sort of odd. How many large ships cruise around without a battlegroup? Look at a US carrier battlegroup. One carrier, couple cruisers, some destroyers, maybe a few frigate, couple subs, and auxiliaries.
+
+Each unit has their responsbilities, and the overall goal is to
+protect the big ship at the center of the formation.
+
+<aside: Shouldn't it go Corvette->Destroyer->Cruiser when it comes to increasing ship size, and not Cruiser->Corvette->Destroyer?>
+
+Ditto for the fighters. Where do fighters in FS2 come from?
+Subspace, yeah, but why don't fighters scramble from fighterbays?
+
+It just feels artificial to me.
+
+What I want?
+
+In the missions themselves:
+I want to have a space sim where the ships on both sides fight as a cohesive unit. I want the ability to end the "massive waves of enemy fighters coming from nowhere" tactic that every major space sim relies on. I want to see a finite number of enemy fighters coming from *somewhere*, like a base or carrier somewhere. It might be too far away to reach, but that's another matter. I want to see a the GTD Aquitaine jump in with two corvettes and four cruisers in support. I want to see fighters erupt from the fighterbay when it detect the enemy. I want to *be* one of those erupting fighters.
+
+In the overall game:
+I want to have wingment with names and a dossier. I want a campaign in a space sim that doesn't consist of scripted missions. I want to see a big star map showing the current status of the war. I want to be assigned missions where I can pick and chose wingmen and loadouts. I want to be able to see my performance have effect on the war. I want to have my wingmen mean something, rather than just people who die and leave more bandits to come after you.
+
+So, in short, what do I want?
+
+I want space sims to take the step that flight sims did a long time ago -- namely, step away from completely canned missions and go for a more open-ended playing style.
+
+What suffers? The story, obviously. However, I would maintain that the story for a space sim, already secondary to gameplay and graphics, should go behind replayability.
+
+But then again, I'm a flight simmer first and foremost, and as such, I look for different things in my games than those raised on Quake.
+
+Thoughts, comments?
+
diff --git a/Doc/PLAYER.DOC b/Doc/PLAYER.DOC
new file mode 100644
index 0000000..0801142
--- /dev/null
+++ b/Doc/PLAYER.DOC
Binary files differ
diff --git a/Doc/PLAYER2.DOC b/Doc/PLAYER2.DOC
new file mode 100644
index 0000000..ba1b6b8
--- /dev/null
+++ b/Doc/PLAYER2.DOC
Binary files differ
diff --git a/Doc/Starshatter Campaign.txt b/Doc/Starshatter Campaign.txt
new file mode 100644
index 0000000..18dc72b
--- /dev/null
+++ b/Doc/Starshatter Campaign.txt
@@ -0,0 +1,998 @@
+
+==================== STARSHATTER ====================
+ STRATEGY
+
+Although Starshatter is fundamentally an action title,
+there is also a significant back-story and strategic
+element to the gameplay.
+
+The game occurs in a far-distant future of the Milky
+Way galaxy. Our corner of the galaxy, an area some
+ten or twenty thousand light years in diameter is popu-
+lated by thousands of human-intelligent species on tens
+of thousands of worlds. Many races live in approximate
+isolation, but most are organized into complex poly-
+specific civilizations spanning many star systems.
+Humans are not central in galactic affairs (as in Star
+Trek or Star Wars) but are merely another species. And
+some of the other species and civilizations are *much*
+older and *much* more advanced than ours.
+
+
+POLITICAL FACTIONS:
+
+- Starshatter (warlike/imperial power)
+
+* Sterat Empire (controlled by the Starshatter)
+
+* Brenel Core (military/intelligence corporation)
+
+* Zolon Empire (hive-like hereditary empire)
+
+* Marakan Hegemony (Failing human imperial civilization)
+
+* Terellian Alliance (peaceful commerce group)
+ Alliance of Terellian Republics (ATR)
+
++ Ele'aan Fusion (peaceful/isolationist power)
+
+The player is a human starfighter pilot in the military
+of one such civilization, the Terellian Alliance or ATR.
+The computer plays the part of the player's superior
+officers in assigning missions and offering criticism
+and praise for performance. In addition, the computer
+plays the role of all enemy pilots and politicians.
+
+The game follows the action in an interstellar conflict
+between the Terellian Alliance and those civilizations in
+the control of the Starshatter. The player is assigned
+missions and ships commensurate with his ability by the
+computer.
+
+==================== STARSHATTER ====================
+ MISSIONS
+
+Each of the three episodes follows the action in a military
+campaign against a single enemy civilization. Individual
+missions will be constructed on the fly by the dynamic
+campaign manager (or by the player if acting as operational
+commander).
+
+Each episode takes place in a fixed volume of space con-
+taining about a dozen star systems. The global campaign
+map will treat the star systems as points in a 2D map.
+The action will take place within individual star systems,
+which use a finer grained map. The enemy will control
+most of the star systems, although a few will be neutral,
+allied, or controlled by the player's faction. These may
+need to be defended if the enemy is able to incur.
+
+Rather than being "mission based", Starshatter is an
+"operational simulation". Each campaign episode is
+broken down into a series of military operations, each
+of which occur in a given star system. Different opera-
+tions will have different rules of engagement and victory
+conditions. Some will require the player to secure a
+neutral or allied star system from hostile threats.
+Others will involve incursion into hostile territory.
+Still others will have more limited objectives, such
+as securing a friendly position in a neutral system,
+without violating neutral airspace or damaging neutral
+assets.
+
+Major resources such as space stations, ship yards,
+SAM and sensor sites will be part of the fixed universe
+design. Likewise, the initial number, type, and location
+of warships is fixed at design time. It is hoped that
+the combination of large numbers of ships and small
+random factors (battle outcome, planetary position,
+time of attack) will result in increased variety of
+enounter for the player.
+
+During the game, the campaign manager must keep track
+of the location and make-up of each ship and battle group.
+As each ship is destroyed in battle, it must be removed
+(permanently) from play. New ships and battle groups
+can be constructed on each side, at a rate dependent
+on how many critical resources the side controls. In
+addition, reserves may be advanced from rearward areas
+to support an operation.
+
+For each mission, the computer will assemble a mission
+profile by selecting one or more objectives, selecting
+a location for the battle, and assigning friendly and
+enemy forces. The computer can choose randomly from
+among the nearby resources to attack (or defend) and
+the nearby ships on both sides to fight.
+
+Missions will be generated in accordance with the current
+strategic objectives for the operation. The dynamic
+campaign manager will keep a prioritized list of
+strategic objectives, mostly related to major fixed
+resources.
+
+Before the battle, the player will be given a gene-
+rated mission briefing describing the mission objec-
+tives, locale, and the makeup of his own group.
+
+Once the player completes the mission (or loses it),
+the computer will tally up the losses on both sides
+and remove those ships from the campaign roster. A
+generated mission debriefing will detail kills and
+losses, pilot ratings, etc.
+
+In order to keep the player doing something interesting,
+he will have to be assigned to an attack force, either
+a battle group or fighter wing. Early missions will be
+shorter and simpler, with a single objective in a
+single system. Later missions will get more involved,
+with multiple objectives and sometimes requiring free
+drive travel to other systems. In addition, the com-
+plexity of missions will be modulated by the user's
+chosen difficulty level.
+
+Sometimes, the objectives will be enemy forces: e.g.
+destroy a fighter wing or cripple a battle group.
+Other times, they will be fixed resources like space
+stations or fortified moons. Still other times, they
+will be distributed or moving targets: e.g. shut down
+interplanetary shipping throughout the system.
+
+At any time, the player's group may get assigned to
+a defensive mission (scramble). This can mean defending
+a fixed resource from an incoming enemy attack. Or it
+could mean escorting an important freight convey through
+unsecured space.
+
+Mission objectives can be constructed from verb-noun
+imperatives:
+
+ Verb List Object List
+ ---------------- -----------------
+ Intercept Ship
+ Shadow Group of Ships
+ Escort
+ Defend
+
+ Strike Space Station
+ Capture
+ Blockade
+ Defend
+
+ Recon Ship, building, or station
+ Jam
+ Designate
+
+ Launch From Starship or Station
+ Dock With
+ Dock/Rearm/Launch
+
+ Clear Mine Field / Satellite Grid
+ Deploy
+
+ Go To Location
+ Patrol
+ Hold At
+
+
+==================== STARSHATTER ====================
+ INTERFACE
+
+SCREEN CONCEPTS:
+
+-----------------------------------------------------
+Main Menu
+-----------------------------------------------------
+
+ New Game
+ Continue Game
+ Quick Mission
+ Training
+
+ Player Info (callsign, difficulty level, stats)
+ Game Options (controls, audio-video settings)
+
+ Help!
+ Exit
+
+-----------------------------------------------------
+Help Menu
+-----------------------------------------------------
+
+ Online Manual
+ Keymap
+ About...
+ Credits
+
+-----------------------------------------------------
+Operational Command
+-----------------------------------------------------
+
+ Operation Name, Date, Status
+ ----
+ Orders (overview of operation)
+ Theater (system map showing resources)
+ Strat Plan (edit objective list)
+ Intel Rpt (news feed)
+ Status (current score, us vs. them)
+
+ - missions -
+ Active ## (current mission list)
+ Pending ## (missions in prep)
+ Scramble ## (inbound threat list)
+
+ Commit (engage current mission)
+ Cancel (back to main menu)
+
+ Also need controls to navigate the maps, and
+ control visual clutter.
+
+ [May want to offer simpler versions of this
+ screen for "Starship Command" and "Fighter
+ Command", allowing the player to focus on
+ combat simulation rather than strategy.]
+
+-----------------------------------------------------
+Mission Planning
+-----------------------------------------------------
+
+ Briefing Data (scrolling text and images)
+ Mission Title
+ Objective List
+ Force Package
+ package callsign
+ wingmen and loadout
+ Annotated Flight Plan
+ launch time
+ nav points and events
+ time on target
+ Intel Report
+ expected counterforce
+ degree of confidence
+
+ Package (select wingmen)
+ Loadout (select weapons)
+ Nav Map (edit nav points)
+ Targets (assign objectives to flights)
+
+ Commit (engage current mission)
+ Cancel (back to previous screen)
+
+ [NOTE: same screen can double as Alert Intercept
+ by changing the title and removing the edit buttons]
+
+
+==================== STARSHATTER ====================
+ TRAINING
+
+Training Missions (both services)
+
+1. Basic Flight and Navigation
+2. Guns
+3. Missiles
+4. Combat Maneuver
+5. Sensors / Stealth
+6. Wingman Interface
+7. Long Range Scan
+8. Tactical Navigation and Waypoint Setting
+9. Advanced Tactics
+ a. Starship Assault (fighter weapon school)
+ b. Cruise missiles & Flight Ops (fleet tactical school)
+10. Situation Awareness (Final Exam)
+
+
+=======================================================
+ MISCELLANIA
+
+Sensor ships and stations will be important resources in
+winning the information war. Both the player and the AI
+will need to exploit and protect them appropriately.
+
+Briefings/Debriefings and other Intelligence Reports
+must adequately communicate the overall game situation
+to the player in order to allow the player to make
+meaningful decisions. This is crucial to establishing
+immersion and making the player care about the outcome
+of the game.
+
+Briefings and Info Bursts will be more visceral if they
+include simulated "news footage" of the events being
+related.
+
+
+=======================================================
+ DYNAMIC CAMPAIGN NOTES
+
+1. Fighter jocks will be allowed to choose from a
+ menu of generated missions that have been assigned
+ to the player's squadron.
+
+2. Cruiser skippers will be given only one generated
+ mission at a time (like KA or SFC), but it will be
+ somewhat broader in scope.
+
+3. Carrier admirals will do what exactly?
+
+4. Both cruiser and carrier missions will often start
+ in the field or on patrol. Fighter missions will
+ always start and end at a base (carrier|station|
+ dirtside starbase).
+
+5. Cruiser and carrier missions will not normally
+ include the starsender translation to the area of
+ conflict. The player will only go through the
+ sender in real time as part of a mission (not as
+ prelude or ending to a mission).
+
+6. Admirals will need to be able to generate orders
+ for their forces during the battle. This implies
+ some kind of Op Command workstation.
+
+7. The mission planner algorithm must construct
+ generated missions that are "fun", not just logical
+ to the campaign. I have a vague notion of using
+ time/distance heuristics to place enemy units as
+ "challenges" on the battle field. e.g. For xxx
+ type of mission, place an enemy destroyer yyy
+ minutes away from the (player|objective|sender).
+
+8. The generated missions do not have to hang together
+ as well as previously planned. That is, we don't
+ need to maintain the feeling that the player is just
+ a unit in an RTS game. Instead, the campaign manager
+ is just a device for generating interesting single
+ player missions as would be found in any game with
+ a static campaign.
+
+ On the other hand, the player should not see any
+ obvious contradictions between the big board and
+ what occurred during their missions. That means,
+ any kills they witnessed must still be visible
+ between missions in the intel report/event log.
+
+
+=======================================================
+ SHIP NAMES
+
+--- TERELLIAN SHIP NAMES ---
+
+F-32 Falcon Squadrons
+ Tigers Bearcats Avengers Windriders Wizards Warriors Aces
+
+F-36C Stormhawk Squadrons
+ Nighthawks Mustangs Stallions Chargers Hunters Broncos Trailblazers
+
+F/A-38D Talon Squadrons
+ Starknights Lancers Warbirds Razorbacks Rangers Sabres Kings
+
+
+Berents Class FF (922 - 951) 924
+ 1............2............3............4............5............6............7............
+22 Berents Bosporus Belfast Bering Dalton Surrey Halston
+27 Clarkeston Carlisle Canton Davis Delmar Kent Portsmouth
+32 Darvon Leyte Hemmet Messina Morgan Durham Bristol
+37 Avalon Argiles Essex Nalu Normandy Kingston Oxford
+42 Malory Parker Rainier Kelvin Nellis Saratoga Columbia
+47 Sorrel Suffolk Tanner Trieste Wolfram Broome Orleans
+
+Asher Class DDK (708 - 737) 710
+08 Asher Ryan Gerson Reynolds Nordic Vernor Arthur
+13 Shaw Card Simmons Hawker Hughes Nichols Smith
+18 Vance Gibson Raven Hale Salas Hoffman Parks
+23 Douglas Mahan Brunner Weber Smith Deforest Standish
+28 Robinson Clarke Gerrond Gamma Roberts Bear Drayton
+33 Keller Jackson Radix Lawrence Rand Chance Cavanaugh
+
+Spectre Class DD (350 - 395) 352
+50 Spectre Wraith Shadow Warlock Phantom
+55 Charon Nemesis Necromancer Merlin Nightshade
+60 Morlock Demon Hydra Typhon Enigma
+65 Phantasm Revenant Banshee Chimaera Gorgon
+70 Nightmare Darkmage Ogre Chaos Death's Head
+75 Ghola Efrite Ettin Incubus Secret Fire
+80 Siren Cerberus Mantis Orknies Fell Hunter
+85 Warspite Fire Angel Silence Mysterious Flame of Gorath
+90 Centaur Minotaur Dark Spirit Aegis Void Walker
+
+Courageous Class CG (521 - 545)
+ 1............2............3............4............5............6............7............
+21 Courageous Fearless Stalwart Defender Guardian Loyalty Forthright
+26 Steadfast Assurance Protector Heroic Valiant Justice Honesty
+31 Dauntless Vanguard Redstone Victorious Honorable Truth Gallantry
+36 Braveheart Defiant Audacious Valorous Confident Response Resolve
+41 Integrity Capable Fortitude Intrepid Virtuous Daring Bold
+
+Devastator Class CA (400 - 419)
+00 Devastator Annihilator Onslaught Relentless Furious Stormwind Demolisher
+05 --------- Thunder Hurricane Inferno Torrent Typhoon Shrike
+10 ------- Vortex Predator Ravager
+15 Havoc Scourge Huntress Warrior
+
+Orion Class CVF
+ Orion Antares Archon Titan Hyperion Atlas Hercules
+ Chronos Poseidon Cygnus
+
+--- MARAKAN SHIP NAMES ---
+
+F-MK1 Viper Squadrons
+ Scorpions Dragons Hornets Sharks Demons Vipers Black Widows
+
+F-MK2 Razor Squadrons
+ Raptors Vultures Cougars Vandals Vigilantes Wolfpack Black Diamonds
+
+F-MK3 Cobra Squadrons
+ Avengers Vampires Lions Kestrels Ravens Bounty Hunters Panthers
+
+Baikal Class FF
+ Baikal Irkutsk Olkha Iskra Lebedinka
+ Meget Homutovo Ivanovka Yagat Kyren
+ Vasad Nasaly Letkes Dorog Pomoz
+ Lovo Kerta Nova Marcali Csorna
+ Bajna Vorka Setla Rodina Markov
+ Dolina Porac Tarnov Udel Karalon
+
+Tiger Bay Class FF
+ 1............2............3............4............5............6............7............
+
+01 Tiger Bay
+08 Southern Isles
+15 Hellas Point
+22 Iron Canyon
+29 Mount Teras
+36 Black River
+43 Guard Hills
+50 Gulf of Kirt
+57 Star Island
+64 Rodis Station
+
+02 Tarsus Pride
+09 Eternal City
+16 Kala Docks
+23 Outer Rim
+30 Kolchev
+37 Rock Ridge
+44 Aenia
+51 Boma Beach
+58 Falcrest
+65 Angel Shore
+
+03 Valley of Flame
+10 Leopard Ridge
+17 Kalis Basin
+24 Cape Nor
+31 Blue Forest
+38 Landfall
+45 Lake Orsan
+52 Wind River
+59 Tolan Locks
+66 Mount Zoare
+
+04 Silas Cave
+11 River Ceres
+18 Elkhorn Pass
+25 Port Hanson
+32 Oak Hall
+39 North Point
+46 Loris Dunes - not used
+53 Beacon Bay - not used
+60 Sand Canyon - not used
+67 Iron Mountain - not used
+
+05 Khaital
+12 Ash Hills
+19 Albus Gulf
+26 Siren Island
+33 Neptune Station
+40 Trocanther
+47 Night City
+54 Joran Docks
+61 Hunter Coast
+68 Nephrys
+
+06 Storm Island
+13 Orinoco
+20 Jewel Beach
+27 Knife Ridge
+34 Secret Shore
+41 Crown of Fire
+48 Hilger Coast
+55 Sword Cliffs
+62 Capetown
+69 Smoke Forest
+
+07 Blackrock
+14 Sutton Lake
+21 Fourth Sector
+28 Kans Ferry
+35 Tol Harbor
+42 Gunston
+49 Fort Hollis
+56 Shield Mountains
+63 Sea of Glass
+70 Tannis Port
+
+80 Ramek
+83 Senna
+86 Radelix
+91 Arcturus
+94 Pelius
+96 Matrix
+98 Clavius
+
+
+Broadsword Class DD (3C21 - 3C51)
+ 1............2............3............4............5............6............7............
+21 Broadsword Starknight Battleaxe Claymore Scimitar Saber Morningstar
+28 Truncheon Poleaxe Clovis Gladius Trident Lance Crossbow
+35 Warsign Warhammer Longbow Ironhelm Boneclaw Shield Longsword
+42 Quickbolt Kindjal Siege Mattock Runesword Blacknife Pike
+49 Scepter Icefang Rapier
+56 Firebolt Shortsword Joust Attack
+
+Volnaris Class DD (4D01 - 4D40)
+
+01 Alin Volnaris
+05 Pav Enke
+09 Pol Basilus
+13 Hal Rifa
+17 Kalas Gannet
+
+21 Senn Alon
+25 Jan Boma
+29 Tremel Isa
+33 Joran Kas
+37 Ambassador Ston
+
+02 Tol Branta
+06 Gol Duvain
+10 Jiri Sutton
+14 Famis Tett
+18 Hett Galinas
+22 Nova Sem
+
+26 Gray Lars - 4th fleet?
+30 Penn Sidus
+34 Savan Joss
+38 Kans Sefa
+03 Jael Pelorus
+07 Kel Masten
+
+11 Mos Antares
+15 Vin Silas
+19 Lieutentant Bolis
+23 Telen Setana
+27 Galen Teras
+31 Ro Hollis
+
+35 Tor Palver
+39 Rath Karalan
+04 Nels Berens
+08 Masil Korius
+12 Toran Elos
+16 Thule Arven
+
+20 Jes Kirta
+24 Loma Alta
+28 Merin Asgar
+32 Dian Soma
+36 Commander Sumas
+40 Bail Ennis
+
+45 Rik Eranes
+49 Senator Hasla
+
+51 Variak
+53 Talus
+55 Mindalante
+57 Manarkan
+59 Velan
+
+Imperial Class CA (5C44 - 5C73)
+44 Imperial Coronation Clavius Ascendance Marak I
+51 Marak II Imperator Overlord Throne Glory
+58 Principality Bennet Powerful Viceroy Regal Primacy
+65 Excellence Cleon I Marak IV Eminence Royale
+
+
+Vendetta Class CA (5D15 - 5D39)
+15 Vendetta Malice Wrath Rage Fury Menace Intruder
+22 Risk Hazard Peril Threat Exigency Attack Avalanche
+29 Assailant Raider Agressor Invader Retaliator Pursuit Darkness
+36 Silencer Punisher Vengeance xxxxxx Terror Dread Savage
+43 Hood Omega
+
+
+Dragon Class CVF (7A1 - 7A7)
+ Dragon Serpent Gryphon Manticore Baldread Cyclops Argus Blackwing
+
+OTHERS
+ Wasp Hornet Scorpion Deaths Head
+ Wolf Cobra Viper Raptor Python
+
+ Jackal Zodiac Scarab Sphinx Osiris
+ Onyx Isis
+
+ Bellerophon Hephaestus
+
+ Tiger Panther Cougar Sea Lion Leopard Wildcat Jaguar
+ Bengal Claw Lynx Ursa Orca Jackal White Lion
+
+
+Terellian Fleet Makeup
+----------------------
+
+1 carrier battle group
+ 1 CVF + 60 Fighters + 8 LCAs
+ 1 CG
+ 2 DDK
+
+2 cruiser battle groups
+ 1 CA
+ 2 CG
+ 1 DDK
+
+2 destroyer squadrons
+ 2 DD
+ 2 FF
+
+1 destroyer/escort squadron
+ 2 DDK
+ 2 FF
+
+
+
+Marakan Fleet Makeup
+--------------------
+
+1 carrier battle group
+ 1 CVF + 60 Fighters + 8 LCAs
+ 2 DDK (Volnaris)
+ 2 FF (Tiger Bay)
+
+2 cruiser battle groups
+ 2 CA (Vendetta)
+ 1 DDK (Volnaris)
+ 1 FF (Tiger Bay)
+
+2 destroyer squadrons
+ 2 DD (Broadsword)
+ 2 FF (Tiger Bay)
+
+1 destroyer/escort squadron
+ 2 DDK (Volnaris)
+ 2 FF (Tiger Bay)
+
+
+==================== STARSHATTER ====================
+ COMMERCIAL NAMES
+Ahime-Maru
+Boko-Maru
+Hikawa-Maru
+Hokko-Maru
+Junyo-Maru
+Kaijin-Maru
+Kankoh-Maru
+Komagata-Maru
+Koro-Maru
+Tankai-Maru
+Tenyo-Maru
+Yumigo-Maru
+
+Roika Adventure
+Roika Endurance
+Roika Mariner
+Roika Vantage
+Roika Wayfarer
+
+Roika Challenger
+Roika Explorer
+Roika Seeker
+Roika Viking
+Roika Wanderer
+
+Roika Discovery
+Roika Harvester
+Roika Sojourner
+Roika Voyager
+Roika Windward
+
+Chemstar One (through Twelve)
+
+
+==================== STARSHATTER ====================
+ GALAXY : NAMES
+
+ Adavan Alban Aram Aslan Athenar Avalon
+ Borreal Borova Bryn
+ Casalle Chi
+ Dawn
+ Eiller Elkhart
+ Fenn
+ Gaul Garden Garrison
+ Haiche Hiro
+ Ilon Isham
+ Janek Jarnell
+ Kala Khalife Kolchev Korius
+ Lanos Loris
+ Marak Muir
+ Navara Nephrys Nergal
+ Oleanne Omin Ostara
+ Paragon Path Pirrin Pollus
+ Relay Renser Ri Radix
+ Senna Silessia Solus Suven
+ Tal Amin Tarsus Theramin Thrale Trocanther
+ Ur
+ Vail Vesta Volante
+ Warren Whorl
+ Xanthe
+ Zephyr Zoare
+
+
+
+==================== STARSHATTER ====================
+ SECTOR : NAMES
+
+TERELLIAN ALLIANCE
+ Jarnell
+ Adonai
+ Jarnell
+ Caladan
+ Dawn
+
+ Borova
+ Talistar
+ Maraner
+ Borova
+ Danova
+ Zhalisal
+ Tulkas
+
+ Athenar
+ Athenar
+ Pallas
+
+ Paragon
+ Dorrath
+ Paragon
+ Bryn
+ Atlan
+
+ Thralis
+ Pollux
+ Thrale
+ Hera
+ Zeus
+ Sparta
+
+ Janus
+ Dante
+ Janek Station
+ Janek
+ Alystra
+ Navara
+
+INDEPENDENT SYSTEMS
+ Solus
+ Meridian
+ Solus
+ Telmera
+ Omane
+ Jalah
+ Trellis
+
+ Silessia
+ Silessia
+ Prosenar
+ Vesta
+ Nero
+
+ Haiche
+ Haiche
+ Cygnus
+ Tal Amin
+ Kala
+
+MARAKAN SYSTEMS
+ Renser
+ Garana
+ Rhomindal
+ Renser
+ Marisol
+ Lornas
+ Khaital
+
+ Loris
+ Duna
+ Kaius
+ Loris
+ Casalle
+
+ Ostara
+ Aram
+
+ Kolchev
+ Ilya
+ Kolchev
+ Anatoly
+ Volova
+
+ Korius
+ Moneta
+ Korius
+ Adantar
+ Solnas
+
+ Tarsus
+ Tarsus
+ Theramin
+
+ Nephrys
+ Nergal
+
+ Radix
+ Senna
+ Radix
+ Cavenda
+
+ Isham
+ Garden
+ Senesca
+ Rom
+ Relay
+
+ Garrison
+ Garrison
+
+ Trocanther
+ Volante
+ Narcissus
+ Path
+
+ Marakan
+ Marak
+ Xanthe
+
+
+==================== STARSHATTER ====================
+ GALAXY : PLANET TYPES
+
+ --= Landform Types =--
+ Arid Desert (Arrakis)
+ Frigid Desert (Mars)
+ Frozen Exotic (Titan)
+ Temperate Planar
+ Temperate Mountainous
+ Tropical Forest
+ Tropical Oceanic
+ Icy Oceanic (Scandinavia)
+ Rocky Planar
+ Rocky Mountainous
+ Volcanic (Venus, Io)
+ Barren (Pluto)
+
+ --= Atmospheric Types =--
+ Thin
+ Clear, Breathable
+ Dusty
+ Foggy/Cloudy
+ Dense Poisonous
+ Layered
+
+ --= Population Types =--
+ Agrarian
+ Industrial
+ Mining
+ Research/Technology
+ Military
+ Political
+ Terraforming
+
+==================== STARSHATTER ====================
+ GALAXY : RACES
+
+ Human
+ Terrelian Alliance
+ Marakan Hegemony
+
+ Ele'aan
+
+ Zolon Empire
+
+
+==================== STARSHATTER ====================
+ CAMPAIGN SEQUENCE
+
+--- COLD WAR, LOW TENSION ---
+
+1) Training Center - Live Fire:
+ JANEK SHIPYARD / HEPHAESTUS STATION
+ Engage in "live fire" exercise with friendly
+ units on the border of Hegemony territory.
+
+--- COLD WAR, HIGH TENSION ---
+
+2) Highland:
+ SOLUS(N) RENSER(H)
+ An outlying Hegemony system is using terrorist
+ tactics to gain control of a neighboring neutral star
+ system to create "breathing room" between themselves
+ and the Alliance. Drive out the terrorists and get the
+ neutrals to sign a treaty with the Alliance.
+
+Quest Notes:
+* Restore free shipping by tracking down pirate base
+ in asteroid field.
+* Terrorists have declared they will destroy prominent
+ landmarks or assets in the system, stop them.
+* Find the terrorist base.
+* Protect local officials on a tour of afflicted areas.
+* Demonstrate Hegemony involvement by tracking down
+ Renser military ships supporting the terrorists.
+* Establish a no-fly zone around a vulnerable space
+ station.
+* Deliver humanitarian assistance to a station or city
+ that has been attacked by the terrorists.
+* Some kind of covert op in the Renser system - spy
+ insertion and/or recovery or hostage extraction.
+* Evacuate wounded civilians from an attacked station
+ or city.
+
+3) Freedom Fighters:
+ HAICHE(H)
+ Hegemony taxation policy has driven an outlying
+ system to resort to despotism to collect needed
+ revenue. A group of local freedom fighters is
+ attempting to overthrow the Hegemony backed govt.
+ Aid the locals in driving out Hegemony forces.
+
+--- OPEN WARFARE ---
+
+In response to campaign 3, the Hegemony formally
+declares war on the Alliance.
+
+4) Shining Fortress:
+ JARNELL BOROVA ATHENAR
+ The Hegemony has struck three Alliance systems,
+ but they have not yet had time to fortify their
+ position. Drive out the enemy forces, and
+ protect Alliance citizens and assets in the area.
+
+5) Firestorm:
+ OSTARA ARAM KOLCHEV KORIUS TARSUS LORIS NEPHRYS NERGAL
+ Alliance Force:Command is planning a major offensive
+ against the core Hegemony systems. This operation
+ is to strike major war production and ship yards
+ to weaken the Hegemony resistance before the
+ main offensive.
+
+6) Nightfall:
+ MARAKAN ISHAM GARRISON TROCANTHER
+ Climactic operation against the enemy home system.
+ Discovery that the Hegemony worlds have been overrun
+ by the Zolon Empire.
+
+--- ALIEN STRIFE ---
+
+7) Fatal Spear
+ Alliance forces attack the Zolon border worlds
+
+8) Iron Mountain
+ THRALIS PARAGON
+ Zolon counter-attack, defense of the remaining
+ core Alliance worlds
+
+9) No Refuge
+ Purge of Zolon Royal Breeding Colonies
+
+
+==================== STARSHATTER ====================
+ COMBAT SEQUENCE
+
+Each campaign will include a set of combat sequences
+for each combatant. The combat sequences will guide
+mission generation and statistical simulation, as well
+as providing supporting background fiction in the form
+of news items, intel reports, directives from Command,
+and game-rendered movie scripts/clips.
+
+
diff --git a/Doc/Starshatter Q&A.doc b/Doc/Starshatter Q&A.doc
new file mode 100644
index 0000000..d3ade28
--- /dev/null
+++ b/Doc/Starshatter Q&A.doc
Binary files differ
diff --git a/Doc/Starshatter Script - Endgames.doc b/Doc/Starshatter Script - Endgames.doc
new file mode 100644
index 0000000..9e5b7e7
--- /dev/null
+++ b/Doc/Starshatter Script - Endgames.doc
Binary files differ
diff --git a/Doc/Starshatter Script - Fleet Admiral Evars.doc b/Doc/Starshatter Script - Fleet Admiral Evars.doc
new file mode 100644
index 0000000..6d4023b
--- /dev/null
+++ b/Doc/Starshatter Script - Fleet Admiral Evars.doc
Binary files differ
diff --git a/Doc/Starshatter Script - Kash Anlon.doc b/Doc/Starshatter Script - Kash Anlon.doc
new file mode 100644
index 0000000..648d8ec
--- /dev/null
+++ b/Doc/Starshatter Script - Kash Anlon.doc
Binary files differ
diff --git a/Doc/Starshatter Script - Promotions.doc b/Doc/Starshatter Script - Promotions.doc
new file mode 100644
index 0000000..c27e70d
--- /dev/null
+++ b/Doc/Starshatter Script - Promotions.doc
Binary files differ
diff --git a/Doc/Starshatter Script - Sara Hunter.doc b/Doc/Starshatter Script - Sara Hunter.doc
new file mode 100644
index 0000000..e591458
--- /dev/null
+++ b/Doc/Starshatter Script - Sara Hunter.doc
Binary files differ
diff --git a/Doc/Starshatter Script - Vice Admiral Caldott.doc b/Doc/Starshatter Script - Vice Admiral Caldott.doc
new file mode 100644
index 0000000..a83ecd9
--- /dev/null
+++ b/Doc/Starshatter Script - Vice Admiral Caldott.doc
Binary files differ
diff --git a/Doc/Starshatter System Map.xls b/Doc/Starshatter System Map.xls
new file mode 100644
index 0000000..42044ef
--- /dev/null
+++ b/Doc/Starshatter System Map.xls
Binary files differ
diff --git a/Doc/Starshatter Text.doc b/Doc/Starshatter Text.doc
new file mode 100644
index 0000000..be93d7b
--- /dev/null
+++ b/Doc/Starshatter Text.doc
Binary files differ
diff --git a/Doc/TASK.TXT b/Doc/TASK.TXT
new file mode 100644
index 0000000..c0dc2ca
--- /dev/null
+++ b/Doc/TASK.TXT
@@ -0,0 +1,380 @@
+STARSHATTER TASK LIST AND SCHEDULE
+
+AUGUST 1997
+
+1 Magic Enhancements
+ * modify poly (finish)
+ * triangulate poly
+ * remove degenerate polys (fewer than 3 distinct verts)
+ * scale selection
+ * rotate selection
+ * primitives (sphere, cylinder, cone)
+
+SEPTEMBER 1997
+
+1 Magic Enhancements
+ * texture mapping
+ * new test ship designs and textures
+ + stabilization (bug fixing)
+
+1 Low-level AI (idle / attack / reverse / evade)
+ * Auto-bank on turn
+ * 2 AI fighters attacking each other
+ * 1+ AI fighters attacking the player
+
+1 Flocking AI
+ * Collision Avoidance
+ * Seek Target
+ * Flee, Avoid object
+ * Evade Threat
+ * Drop/re-acquire target
+ * Fire control based on Projected Silhouette
+ * Readiness (speed multiplier, likelihood of fire)
+
+1 Combat results (damage to resources/ships)
+ * Fix sprite animation rate
+ + Shields
+ * Explosions / Flashes / Particles
+ * Ship-to-ship collisions
+ * Impulse from collisions and shots
+
+1 Compound data file and asset manager
+ * including zlib compression
+
+ Misc.
+ * test mode
+ * respawn
+ * gaea texture generator
+
+OCTOBER 1997
+
+1 Sound
+ * Ambient Sound Effects
+ * 3D Sound Effects
+ + Streamed Sound (e.g. voice over)
+
+1 Renderer Architecture
+ * get rid of span buffer (CamView, SpanBuff)
+ * get rid of poly id and key (Geometry)
+ * single clipped polygon
+ * no more clipped_verts in poly (Geometry)
+ * ClipPoly just before ProjectPoly (CamView)
+ * Clip from source poly into dest poly (CamClip)
+ * fixed overhead of 16 verts in vertex set (Solid::Allocate)
+ * colored lights
+ * directional backlighting
+ * z-scaling for 3Dfx
+ * snapshot of D3D frame buffer (not working on 3Dfx?)
+ * remove Span pointer from poly
+
+1 Memory Leak Debugging
+ * MemDebug class
+
+1 Idle (schedule slip)
+ Played Wing Commander 3 and Darklight Conflict a bit
+ Thought a lot about game design
+ Learned to use Photoshop
+
+NOVEMBER 1997
+
+1 Renderer Architecture
+ * move texture indices from vertex to poly (Geometry)
+ * modify nGen to support new formats
+
+1 Misc.
+ * Polygon-accurate shot-intersection
+ * Translucent shield bubble
+ * Improved laser graphics
+ * Target "Pickle"
+
+1 Combat
+ * seeker missiles
+ * missile cam
+ * smooth camera zoom
+ * improved ship collision logic
+ * physical vibration
+
+DECEMBER 1997
+
+1 Misc.
+ * Lens flare occlusion
+ * improved collision avoidance AI
+ * improved seeker AI
+ * ship physics for missile carrying and launch
+ o starfield texture in sw (tried and removed: too slow)
+
+1 Misc.
+ * additional sounds (engines, shot-impact)
+ * 3Dfx bug fixes
+ * upgraded data archiver
+ * planetary rings
+ * clipping/zooming bug fixes
+ * simple ultra-drive
+
+1 Collision Detection
+ * RAPID Collision detection
+
+JANUARY 1998
+
+1 Misc.
+ * polygon properties (e.g. flat-shaded, transparent)
+ * bridge cam
+ * independent agility parameters
+ * LOD clipping of graphics
+ - LOD model support for ships and planets
+
+1 Key/joystick button mapper
+ * define key codes for actions and load from file
+ - GUI support for mapping keys within the game
+
+1 Weapon Aiming AI
+ * Improved missile graphics
+ * Load weapon/shot parameters from data file
+ * Handle slow charging / variable charge cap ship guns
+ * Compute deflection vector to lead target
+
+FEBRUARY
+ * completed font designer
+ * designed HUD font
+ * basic button control
+ * basic form class
+ * improved collision avoidance AI
+ * physical modeling - vibration
+ * improved shield graphics
+
+MARCH
+ * simple padlock view
+ * improved gaea planetary texture
+ * fixed planet texture mapping defects
+ * multi-quality bitmaps
+ * use 8-bit texture format in hardware, when available
+ * power management and multiple reactors
+ * HUD view
+ * 2D cockpit view
+
+ HUD enhancements
+ * IFF color indications
+ * markers for distant contacts
+ * weapon range bar
+ * mode indicator
+ * fullscreen mode for external cameras
+
+ Guns and Missiles
+ * weapon selection (G for guns, M for missiles?)
+ * weapon range indicator
+ * chase vector indicator
+ * missile lock
+ + missile track/lock graphic and audio cue
+
+APRIL
+ Misc
+ * HUD display off
+ * HUD color/brightness
+ * Doppler shift and distance roll off
+ * Orient theater/padlock views to world coordinates
+ * AI roll to orient with world coordinates (y+ is up)
+ * Fighter AI: roll+pitch instead of yaw
+
+ Sensor/Scanner and Emission Control
+ * passive and active sensors
+ - manual emcon
+ - environmental sensor efficiency
+
+MAY
+ Misc
+ * joystick configuration
+ * performance tuning (clip vertex set in view space)
+ * (x,y,z) translation thrusters
+ * movable external camera (rotate, zoom, center on object)
+ * "enhanced" two-frequency doppler effect
+
+ Tactical Situation MFD
+ * properly clipped 3D grid
+ * contact info for each contact
+ * tactical hud
+ * cycle view object
+ ? clipped circles indicating target weapon range
+
+ Magic Enhancements
+ * optional second texture and flag set per polygon
+ * specular highlight flag
+ * two-pass texture rendering in nGen (sw and d3d)
+
+ Combat Results
+ * System Damage Combat Results
+ * particle burst/trail for severe system damage
+ * caution and warning panel
+ - target damage feedback
+ - vocal warning system
+
+ System Damage Modeling
+ * avionics HUD/NAV degradation
+ * attitude stabilizer
+ - attitude control
+ - throttle control
+ - sensor weapon aiming control
+
+JUNE
+ Combat Enhancements
+ - subsystem targeting for starship targets
+ - AA turrets on starships
+ - mines and/or gunpods
+ - long range weapon types
+
+JULY
+ Simulation and Navigation
+ * star system modeling
+ * orbital mechanics
+ * navigation map
+ * autonav ai
+ * hyperdrive navigation
+ * improved planetary texture mapping
+
+AUGUST
+ Operational Simulation and Navigation
+ * performance optimization for large fleets
+ * enhanced hyperdrive auto nav ai
+ * animated hyperspace effects
+ * new navigation mfd design
+ * starship fire control mfd
+
+ Misc
+ * improved plasma trail effect
+ * corrected rendering of large grids
+ * improved performance of button control
+ * fixed lighting and lens flare bugs
+ * improved star shell texture mapping
+
+SEPTEMBER
+ Operational Simulation
+ * fire control mfd
+ * weapon firing orders
+ * flight deck modeling
+ * flight deck control (recover/launch ships)
+ * created starship ai
+ * improved fighter evasion techniques
+ * starship point defense
+
+ Graphics and Collision Detection
+ * fixed specular highlighting
+ * fixed collision matrix
+ * removed collision resolution for fighter/fighter
+ * render planetary atmosphere
+ * improved planet generator
+
+OCTOBER
+ Operational Simulation
+ - air tasking orders, manual order assignment
+ - starship ai threat assessment, targetting and navigation
+ - starship ai flight deck control
+ - time skip, including combat resolution
+ - hyperspatial navigation
+ - packages, cooperation, formations, orders
+ - communications and squadron commands
+
+----------------------------(unscheduled)------------------------------------
+
+ Other HUD Modes
+ - IDS (docking)
+ - RHAW
+ - MAW
+
+ Misc
+ - reactor power level control
+ - planet craters
+
+ Bug List
+ * faceting (flat-shading) in D3D
+ * nebula sky invisible during fade in
+ * specular highlights visible during fade in
+ * show missile count in HUD
+ * correct ship class determination
+ * improve fighter steering behavior
+ * extraneous keys on startup
+ - sound memory leak
+ - sound dropouts
+
+ Auto Pilots
+ - match velocity
+ - form up
+ - approach
+ - dock
+
+? DX6 DrawPrimitive support
+
+2 Mid/High level AI
+ - strategy and tactics
+ - stealth and detection
+ - morale simulation
+ - difficulty settings
+
+2 Docking and Resupply
+
+1 Ship model parts
+ - additional ship designs
+ - solid model loading using surfaces for sub parts
+ - Complex explosions for composite ships
+
+1 Fonts (including alpha blended anti-aliased)
+
+1 GUI logic / real menu tree
+
+8+ Final ship designs
+1 Final weapon designs
+
+1 Death-spiral Animations
+
+2 Special Effects
+ + Shields
+ + Hyperspace
+ - Starshatter and other Big Explosions
+ - Shock waves, weapon trails
+ * Planetary Rings
+
+1 Wingman command interface
+
+2 Cockpit designs
+
+1 Other External Views
+ + theater view
+ + padlock
+ - rear view
+ - (looping) flyby
+
+2 Save/Restore Game
+
+2 Galaxy Design
+2 Planet, moon, and starfield textures
+2 Campaign Logic
+? Mission Design
+1 Mission Objectives Representation
+
+1 Promotions / Scoring
+1 Mission Briefing System
+1 Animation Cutscene System
+1 Strategic Briefing Data and Design
+
+? Info Database and Help content
+ User's Manual
+
+2 Sound Effects Production
+ - Foley editing
+
+1 Music
+ - Midi or digital audio streaming
+
+3 Music Production
+ - Composition and recording
+
+ High quality renderer for Magic
+ - phong shading
+ - multiple colored light sources with shadows
+ * double precision z buffer
+ - very high resolution (1280 x 960)
+ - high polygon count
+
+
+(possibles)
+
+1 WinGlide support for 3Dfx?
+
diff --git a/Doc/Voice Over Pickup Script.doc b/Doc/Voice Over Pickup Script.doc
new file mode 100644
index 0000000..a538da8
--- /dev/null
+++ b/Doc/Voice Over Pickup Script.doc
Binary files differ
diff --git a/Doc/Weapons Rules.xls b/Doc/Weapons Rules.xls
new file mode 100644
index 0000000..ff2d37c
--- /dev/null
+++ b/Doc/Weapons Rules.xls
Binary files differ
diff --git a/Doc/Word Stems.xls b/Doc/Word Stems.xls
new file mode 100644
index 0000000..6778351
--- /dev/null
+++ b/Doc/Word Stems.xls
Binary files differ
diff --git a/Doc/backstory.doc b/Doc/backstory.doc
new file mode 100644
index 0000000..76a8785
--- /dev/null
+++ b/Doc/backstory.doc
Binary files differ
diff --git a/Doc/dynamic campaigns.txt b/Doc/dynamic campaigns.txt
new file mode 100644
index 0000000..75e3263
--- /dev/null
+++ b/Doc/dynamic campaigns.txt
@@ -0,0 +1,701 @@
+From - Mon Nov 02 22:29:24 1998
+Path: news.rdc2.occa.home.com!newshub1.home.com!news.home.com!news-peer.gip.net!news.gsl.net!gip.net!cpk-news-hub1.bbnplanet.com!news.news.gtei.net!firehose.mindspring.com!not-for-mail
+From: dennya@mindspring.com (Denny Atkin)
+Newsgroups: comp.sys.ibm.pc.games.flight-sim
+Subject: Dynamic Campaigns: Discuss.
+Date: Tue, 03 Nov 1998 04:03:25 GMT
+Organization: Not officially representing CGW here in my free time
+Lines: 40
+Message-ID: <364067e5.15703318@news.mindspring.com>
+Reply-To: dennya@mindspring.com
+NNTP-Posting-Host: user-38ld6ro.dialup.mindspring.com
+Mime-Version: 1.0
+Content-Type: text/plain; charset=us-ascii
+Content-Transfer-Encoding: 7bit
+X-Server-Date: 3 Nov 1998 04:05:20 GMT
+X-Newsreader: Forte Agent 1.5/32.451
+X-No-Archive: yes
+Xref: newshub1.home.com comp.sys.ibm.pc.games.flight-sim:30154235
+
+Okay, there are a number of folks here who SWEAR by dynamic campaigns, some
+going so far as to say they won't buy a sim that doesn't feature them.
+
+Here's the big question: Which sims are you holding up as examples of good
+dynamic campaigns? Because until VERY recently, there haven't been that many
+opportunities to experience a good dynamic campaign. Falcon 3.0's is often held
+up as the previous Holy Grail, but that's mostly people remembering things
+better than they were. Falcon 3.0 had a number of kludgy elements and faked
+occurrences in its campaign--such as the MiG-19s that often spontaneously
+spawned to harass you when you were landing. EF2000 had a fully dynamic
+campaign, but the mission structures were so repetitive that I bored of it as
+quickly as I did any "canned mission" sim. Longbow 2 had a good dynamic
+campaign, but the most interesting missions were the prescripted ones.
+
+So I pose these questions, just for discussion:
+1) What are the examples of good past dynamic campaigns? What made them good,
+and kept them from getting boring?
+
+2) If a mission featured a scripted campaign, but had features that kept it
+from being repetitive -- targets that stayed destroyed, enemy locations and
+force makeups that were randomly generated to keep them from being predictable,
+being retasked to destroy missed targets -- would that be an acceptable
+substitute?
+
+3) Is it replayability or the chance to affect the outcome of the war that
+makes dynamic campaigns appealing to you?
+
+4) A dynamic campaign is never going to generate a mission such as "shoot down
+Admiral Yamamoto in your P-38. " Don't scripted campaigns have the potential to
+be a lot more interesting?
+
+(I'm not at all saying I'm not a fan of dynamic campaigns, and I'll outline
+what I think the perfect campaign for today's techology is in a future post.
+But I want to hear from some of the rest of you without "leading" into a
+discussion of my own theories. And let's not even open hte multiplayer can of
+worms. :-)
+ ---------------------------------------------------
+ Denny Atkin / dennya@mindspring.com
+ I have not yet begun to procrastinate
+ ---------------------------------------------------
+From - Mon Nov 02 22:29:24 1998
+Path: news.rdc2.occa.home.com!newshub1.home.com!news.home.com!news-peer.gip.net!news.gsl.net!gip.net!ix.netcom.com!news
+From: "Greg Cisko" <gcisko@nOsPaMix.netcom.net>
+Newsgroups: comp.sys.ibm.pc.games.flight-sim
+Subject: Re: Dynamic Campaigns: Discuss.
+Date: Mon, 2 Nov 1998 22:19:54 -0600
+Organization: ICGNetcom
+Lines: 81
+Message-ID: <71m0g1$bim@sjx-ixn6.ix.netcom.com>
+References: <364067e5.15703318@news.mindspring.com>
+NNTP-Posting-Host: chf-il5-83.ix.netcom.com
+X-NETCOM-Date: Mon Nov 02 8:23:29 PM PST 1998
+X-Newsreader: Microsoft Outlook Express 4.72.3110.1
+X-MimeOLE: Produced By Microsoft MimeOLE V4.72.3110.3
+Xref: newshub1.home.com comp.sys.ibm.pc.games.flight-sim:30154240
+
+Denny Atkin wrote in message <364067e5.15703318@news.mindspring.com>...
+>Okay, there are a number of folks here who SWEAR by dynamic campaigns, some
+>going so far as to say they won't buy a sim that doesn't feature them.
+>
+>Here's the big question: Which sims are you holding up as examples of good
+>dynamic campaigns? Because until VERY recently, there haven't been that
+many
+
+
+In shirt I would almost have to say there are no good dynamic
+campaigns yet :-) I did hear the campaigns in iF22 were supposedly
+good. But the rest of the game was basicly crap so I wouldn't even
+give it a shot.
+
+>opportunities to experience a good dynamic campaign. Falcon 3.0's is often
+held
+>up as the previous Holy Grail, but that's mostly people remembering things
+>better than they were. Falcon 3.0 had a number of kludgy elements and faked
+
+
+Correct. I remember vividly the first mission if the Kurile(sp?) campaign
+was
+always the same mission. Not very dynamic imho. Also not very realistic
+to shoot 15 migs only to get wasted while I was on final approach. Like
+there wouldn't be any air defenses near an american base...
+
+>occurrences in its campaign--such as the MiG-19s that often spontaneously
+>spawned to harass you when you were landing. EF2000 had a fully dynamic
+>campaign, but the mission structures were so repetitive that I bored of it
+as
+>quickly as I did any "canned mission" sim. Longbow 2 had a good dynamic
+>campaign, but the most interesting missions were the prescripted ones.
+
+
+I guess you can't have your cake and eat it too :-)
+
+>2) If a mission featured a scripted campaign, but had features that kept it
+>from being repetitive -- targets that stayed destroyed, enemy locations and
+>force makeups that were randomly generated to keep them from being
+predictable,
+>being retasked to destroy missed targets -- would that be an acceptable
+>substitute?
+
+
+For me yes definately.
+
+>3) Is it replayability or the chance to affect the outcome of the war that
+>makes dynamic campaigns appealing to you?
+
+
+Replayability for me is not the issue. But rather a chance to effect the
+outcome.
+
+>4) A dynamic campaign is never going to generate a mission such as "shoot
+down
+>Admiral Yamamoto in your P-38. " Don't scripted campaigns have the
+potential to
+>be a lot more interesting?
+
+
+Yes. Until you run into one that you cannot beat. Which you always know
+a flight of migs will be at a certain place every time. Yada, yada yada :-)
+
+--
+Header address intentionally scrambled to ward off the spamming hordes.
+
+cisko [AT] ix [DOT] netcom [DOT] com
+
+>(I'm not at all saying I'm not a fan of dynamic campaigns, and I'll outline
+>what I think the perfect campaign for today's techology is in a future
+post.
+>But I want to hear from some of the rest of you without "leading" into a
+>discussion of my own theories. And let's not even open hte multiplayer can
+of
+>worms. :-)
+> ---------------------------------------------------
+> Denny Atkin / dennya@mindspring.com
+> I have not yet begun to procrastinate
+> ---------------------------------------------------
+
+
+From - Mon Nov 02 22:29:25 1998
+Path: news.rdc2.occa.home.com!newshub1.home.com!news.home.com!news.rdc1.bc.wave.home.com.POSTED!not-for-mail
+From: silenus@home.com (David Clark)
+Newsgroups: comp.sys.ibm.pc.games.flight-sim
+Subject: Re: Dynamic Campaigns: Discuss.
+Message-ID: <363e80ff.260853340@192.168.0.2>
+References: <364067e5.15703318@news.mindspring.com>
+X-Newsreader: Forte Free Agent 1.11/32.235
+Lines: 89
+Date: Tue, 03 Nov 1998 04:42:17 GMT
+NNTP-Posting-Host: 24.112.120.121
+NNTP-Posting-Date: Mon, 02 Nov 1998 20:42:17 PDT
+Organization: @Home Network Canada
+Xref: newshub1.home.com comp.sys.ibm.pc.games.flight-sim:30154251
+
+On Tue, 03 Nov 1998 04:03:25 GMT, dennya@mindspring.com (Denny Atkin)
+wrote:
+
+>Here's the big question: Which sims are you holding up as examples of good
+>dynamic campaigns?
+
+Umm... perhaps EF2000 and Longbow 2. The best "dynamic campaigns" that
+I've seen weren't Flight Sims at all - they were strategy games like
+X-Com and Jagged Alliance.
+
+>So I pose these questions, just for discussion:
+
+Good questions, well phrased.
+
+>1) What are the examples of good past dynamic campaigns? What made them good,
+>and kept them from getting boring?
+
+IMHO, the property that makes a dynamic campaign valuable is that the
+missions seem plausible, but unplanned. Modern warfare is
+extraordinarily fluid and chaotic, and set-piece battles are very
+rare. Battles should just sort of 'precipitate' out of the strategic
+situation - often large air battles are unplanned, and are simply the
+result of a few fighters running into one another - the furball grows
+as both sides dump in additional aircraft, while neither commander
+really understands the scale of the battle that is developing.
+
+The thing that made EF2000 so enjoyable was that most of the battles
+had a 'meeting engagement' feel to them - since there was no 'plot',
+my actions weren't constrained at all. I could avoid battles (if I was
+lucky), or search out more fights, depending on my understanding of
+the tactical situation.
+
+>2) If a mission featured a scripted campaign, but had features that kept it
+>from being repetitive -- targets that stayed destroyed, enemy locations and
+>force makeups that were randomly generated to keep them from being predictable,
+>being retasked to destroy missed targets -- would that be an acceptable
+>substitute?
+
+I guess so. My criteria is that the forces I face have a _reason_ for
+being there (even if the 'reason' is just random chance). If I feel
+like a scenario has been 'tweaked' for 'balance', my suspension of
+disbelief is gone. I want to fight the forces that would _probably be
+there_ in real life, not the ones that a scenario designer feels I can
+handle. I never minded being outnumbered in EF2000, since if I was, it
+was just my bad luck, not designer maliciousness.
+
+>3) Is it replayability or the chance to affect the outcome of the war that
+>makes dynamic campaigns appealing to you?
+
+The chance to affect the outcome of the war is not important to me at
+all. Most pilots won't affect the outcome of a war materially either
+- the reason I play flight sims is to role-play (however inaccurately)
+the experiences of someone with a much more interesting job than me -
+and the limits of individual power is part of that role.
+
+Of course, I also loved the occasional uneventful CAP mission in
+EF2000, so I guess I'm kind of peculiar.
+
+>4) A dynamic campaign is never going to generate a mission such as "shoot down
+>Admiral Yamamoto in your P-38. " Don't scripted campaigns have the potential to
+>be a lot more interesting?
+
+I suppose. I find the generated missions in EF2000 and Longbow2 to be
+extremely interesting - no two are the same, since the strategic
+situation, the position of the threat, etc are always different.
+Others disagree, and say that after a while, one dam-busting mission
+is pretty much like the next. I've never felt that way, but I can
+understand their position.
+
+>(I'm not at all saying I'm not a fan of dynamic campaigns, and I'll outline
+>what I think the perfect campaign for today's techology is in a future post.
+>But I want to hear from some of the rest of you without "leading" into a
+>discussion of my own theories. And let's not even open hte multiplayer can of
+>worms. :-)
+
+Without having seen the game, I guess Falcon4 offers the most
+promising type of dynamic campaign.
+
+What I really want to play is a highly detailed 'world simulator' in
+which both armies would carry out their war plans in real time. My
+aircraft would be one of the combatants, neither more or less
+influential than any other. In the end the victor would win because
+they _won_, not because I managed a 75% kill rate instead of a 65%...
+
+David Clark
+Third World War (GDW) website...
+http://members.home.net/silenus/tww/index.htm
+
+
+From - Mon Nov 02 22:29:25 1998
+Path: news.rdc2.occa.home.com!newshub1.home.com!news.home.com!news-peer.gip.net!news.gsl.net!gip.net!newsfeed.cwix.com!192.220.250.21!netnews1.nw.verio.net!netnews.nwnet.net!news.nodak.edu!not-for-mail
+From: "Michael J. Iverson" <micivers@badlands.nodak.edu>
+Newsgroups: comp.sys.ibm.pc.games.flight-sim
+Subject: Re: Dynamic Campaigns: Discuss.
+Date: Mon, 02 Nov 1998 22:48:53 -0600
+Organization: North Dakota Higher Ed. Network
+Lines: 62
+Message-ID: <363E8B35.64CBAFDD@badlands.nodak.edu>
+References: <364067e5.15703318@news.mindspring.com>
+NNTP-Posting-Host: und-as3p28.und.nodak.edu
+Mime-Version: 1.0
+Content-Type: text/plain; charset=us-ascii
+Content-Transfer-Encoding: 7bit
+X-Mailer: Mozilla 4.5 [en] (Win98; U)
+X-Accept-Language: en
+Xref: newshub1.home.com comp.sys.ibm.pc.games.flight-sim:30154263
+
+For me, a dynamic campaign should give me that "OK, time to go to work"
+feeling when I roll down the runway (or lift off from the FARP). It
+might not be the most challenging mission I've ever flown, but I know
+that I'm contributing towards the "war effort." I'd like to feel that
+the world is alive around me. One thing I didn't like in LB2 was that
+after a half hour or so, the "world" would just go to sleep - no more
+enemy CAPs or strike missions, no more enemy advances.
+
+Show me stats, supplies, squadron members, and info on my pilot. I
+think Red Baron II was one of the only sims that made me genuinely
+pissed off by having my pilot killed in action. I definitely wanted to
+stay alive in that sim.
+
+IMO, scripted missions have a tendency to be made a little too hard.
+Some game designers seem to think that impossible odds = fun. I
+disagree.
+
+Mike
+http://volunteers.warbirds.org
+
+From - Mon Nov 02 22:29:25 1998
+Path: news.rdc2.occa.home.com!newshub1.home.com!news.home.com!newsfeed.direct.ca!cyclone.bc.net!logbridge.uoregon.edu!news.ycc.yale.edu!mars.its.yale.edu!rjl26
+From: rjl26@mars.its.yale.edu (Robin Lee)
+Newsgroups: comp.sys.ibm.pc.games.flight-sim
+Subject: Re: Dynamic Campaigns: Discuss.
+Date: 3 Nov 1998 05:14:22 GMT
+Organization: YLS
+Lines: 61
+Message-ID: <71m3fe$pk3$1@news.ycc.yale.edu>
+References: <364067e5.15703318@news.mindspring.com>
+NNTP-Posting-Host: mars.its.yale.edu
+Xref: newshub1.home.com comp.sys.ibm.pc.games.flight-sim:30154269
+
+In article <364067e5.15703318@news.mindspring.com>,
+Denny Atkin <dennya@mindspring.com> wrote:
+>
+>(I'm not at all saying I'm not a fan of dynamic campaigns, and I'll outline
+>what I think the perfect campaign for today's techology is in a future post.
+>But I want to hear from some of the rest of you without "leading" into a
+>discussion of my own theories. And let's not even open hte multiplayer can of
+>worms. :-)
+
+Just a few thoughts...
+
+1. For me, the "generic mission" problem is not a particularly
+troublesome issue because I find little appeal in the high dramatics of a
+"mission that won the war." Drama on that level does not take place very
+often in air warfare; by and large, an air campaign consists of a series
+of routinized mission profiles run on targets that aircrews know little
+about. Even for those rare missions that do wind up having a hugely
+significant impact on the war, aircrews are not likely to know about this
+until after the fact, though they may have some good guesses as to what
+they are doing (witness Smallwood's account of the anti-Saddam missions
+during Desert Storm, for instance). A so-called "routine" sortie provides
+more than enough drama for me -- "blow up the Death Star" missions have
+a little too much of Hollywood about them for my taste.
+
+2. Extension of the first point: for training, planning, and proficiency
+reasons, mission types are by nature generic. You want aircrews to be
+able to fit the mission into one of a fixed number of profiles for which
+they have been trained. Even where the mission is genuinely different,
+the operations structure of a modern air force is going to try to force
+the situation into familiar terms. Therefore, missions that "feel the
+same" may be realistic, and while some may argue that they become boring
+and therefore undesireable, I don't necessarily see them as such.
+
+3. An interesting mission does not have to be scripted; a relatively
+generic, template-based sortie can be turned into a genuinely memorable
+mission by the introduction of those random events that make tactical
+military aviation such a chaotic affair -- assigned tanker could go down,
+somebody crashes and fouls the runway, somebody goes down and you get
+called to CAP the survivors, etc. There is no particular reason that
+these events need to be scripted; their very randomness is what makes them
+exciting.
+
+4. As a general matter, I view mission generation and campaign dynamics
+in much the same light as I view sausage production; I don't really want
+to inquire too closely into the details of either. The illusion that I
+want to maintain is that the missions are generated by a real operations
+staff reacting to unpredictable events, not by a software campaign engine
+or by selection from a fixed database of mission scripts. And like
+sausages, the worst imaginable case is when the mission itself reveals
+obvious hints as to its origin. To me, a so-called "dynamic
+campaign" produces missions that simply *look* less contrived than
+"handpainted" missions, and therefore is a little better at maintaining
+the illusion.
+
+
+-R.
+--
+_____
+Robin John Lee <amraam@ix.netcom.com>
+YLS '99 - New Haven, Connecticut, USA
+Vulture's Row/Russian Navy - <http://www.webcom.com/amraam>
+From - Mon Nov 02 22:29:25 1998
+Path: news.rdc2.occa.home.com!newshub1.home.com!news.home.com!news-peer.gip.net!news.gsl.net!gip.net!newsfeed.cwix.com!204.210.0.20!news.san.rr.com!not-for-mail
+From: "Jarrod Smith" <jsmith@scripps.edu>
+Newsgroups: comp.sys.ibm.pc.games.flight-sim
+References: <364067e5.15703318@news.mindspring.com>
+Subject: Re: Dynamic Campaigns: Discuss.
+Lines: 105
+MIME-Version: 1.0
+Content-Type: text/plain;
+ charset="iso-8859-1"
+Content-Transfer-Encoding: 7bit
+X-Newsreader: Microsoft Outlook Express 4.72.3155.0
+X-MimeOLE: Produced By Microsoft MimeOLE V4.72.3155.0
+Message-ID: <2Rw%1.5101$q15.153954@news.san.rr.com>
+Date: Mon, 2 Nov 1998 21:50:44 -0800
+NNTP-Posting-Host: 204.210.61.137
+X-Trace: news.san.rr.com 910072254 204.210.61.137 (Mon, 02 Nov 1998 21:50:54 PDT)
+NNTP-Posting-Date: Mon, 02 Nov 1998 21:50:54 PDT
+Organization: TWC Road Runner, San Diego, CA
+Xref: newshub1.home.com comp.sys.ibm.pc.games.flight-sim:30154285
+
+
+Denny Atkin wrote in message <364067e5.15703318@news.mindspring.com>...
+>Okay, there are a number of folks here who SWEAR by dynamic campaigns, some
+>going so far as to say they won't buy a sim that doesn't feature them.
+>
+>Here's the big question: Which sims are you holding up as examples of good
+>dynamic campaigns?
+
+For me, it is Longbow2 and Falcon 3.0. I had a hiatus from simming during
+my collegiate years (lack of hardware fundage :-), so I can't comment on
+anything in between.
+
+>
+>So I pose these questions, just for discussion:
+>1) What are the examples of good past dynamic campaigns? What made them
+good,
+>and kept them from getting boring?
+
+
+Essentially, I don't much care for branching type missions that make up a
+"campaign". It annoys me that the mission objectives will be the same each
+time you play through the campaign in one of the "winning branches". That
+detracts from replayability which is something I value very much in a sim.
+What keeps a dynamic campaign interesting for me:
+
+Each mission provides different objectives depending on the state of the
+total war (air and ground, supply lines, etc.). If your supply lines are
+out, you open them up. If you are losing ground, you cover the retreat. If
+you are taking ground, you take out critical elements of the enemy's
+defenses, and/or defend your ground assets as they move forward to capture
+territory. This type of ebb and flow can go on indefinitely until you make
+some decisive moves toward accomplishing your objective. This is limited by
+your performance both in the cockpit as well as inventory and personnel
+management, etc. This is what immerses me into a dynamic campaign. The
+individual missions are less important, and any good stout mission generator
+with subsequent mission editing options will fill the bill in that dept. I
+did like the splash of hollywood that was infused into LB2 by virtue of its
+scripted missions interspersed with the generated ones. You still can't
+argue with the entertainment value of a well-designed scripted mission. I
+guess for me, I seperate the mission from the campaign. I can enjoy flying
+a cool scripted mission. But it is different from fighting war from the
+campaign perspective. I guess I like the illusion of a strategic element
+that is infused into a good dynamic campaign engine.
+
+>2) If a mission featured a scripted campaign, but had features that kept it
+>from being repetitive -- targets that stayed destroyed, enemy locations and
+>force makeups that were randomly generated to keep them from being
+predictable,
+>being retasked to destroy missed targets -- would that be an acceptable
+>substitute?
+
+
+Much of what you said above would go a long way toward fixing some of the
+problems, but in the end, even though this is realistic and believable, it
+doesn't help the replayability aspect a whole lot. I'm not saying a sim
+like this wouldn't be fun, though. It certainly could be an acceptable
+substitute, but there would have to be some type of extraordinary
+multiplayer capability and/or mission builder to enhance the experience.
+Otherwise, it wouldn't last that long on my harddrive.
+
+>3) Is it replayability or the chance to affect the outcome of the war that
+>makes dynamic campaigns appealing to you?
+
+
+As outlined above, I think both are important to me. Also the illusion of
+strategy going on behind the scenes and you playing your part in that
+strategy makes a good dynamic campaign compelling to play.
+
+>4) A dynamic campaign is never going to generate a mission such as "shoot
+down
+>Admiral Yamamoto in your P-38. " Don't scripted campaigns have the
+potential to
+>be a lot more interesting?
+
+
+Scripted *missions* do have the potential to be more interesting. Scripted
+campaigns do not, IMO.
+
+>(I'm not at all saying I'm not a fan of dynamic campaigns, and I'll outline
+>what I think the perfect campaign for today's techology is in a future
+post.
+>But I want to hear from some of the rest of you without "leading" into a
+>discussion of my own theories. And let's not even open hte multiplayer can
+of
+>worms. :-)
+
+
+Here is what I think would be incredible in a "campaign engine". The engine
+calculates orders which get handed to you from on high. These orders state
+that you need to accomplish some objective to further the war effort. Say
+it is to reduce the opposition's EWR capabilities by some percentage, or
+reduce their naval capabilites in some capacity. Whatever. This becomes
+your short-term objective over the next handful of missions. The sim then
+hands you tools: Intelligence gathering tools, a sophisticated mission
+planner/builder, updated information on the assets that you have to complete
+the task, etc. Then you set out to work and build your own campaign, or at
+least part of it. Then jump in and fly whichever missions look to be the
+most fun to you. The combination of immersion, strategy, replayability, and
+challenge in a sim like this would make it irresistable, IMO.
+Unfortunately, it also sounds difficult and expensive to put together :-)
+
+Jarrod Smith
+The Scripps Research Institute
+http://www.scripps.edu/~jsmith
+
+From - Mon Nov 02 22:29:25 1998
+Path: news.rdc2.occa.home.com!newshub1.home.com!news.home.com!newsfeed.direct.ca!newspeer.monmouth.com!newsfeed-east.supernews.com!supernews.com!Supernews69!not-for-mail
+From: "enzo" <enzo@nospam.com>
+Newsgroups: comp.sys.ibm.pc.games.flight-sim
+Subject: Re: Dynamic Campaigns: Discuss.
+Date: Mon, 2 Nov 1998 22:07:25 -0800
+Organization: http://www.supernews.com, The World's Usenet: Discussions Start Here
+Lines: 186
+Message-ID: <71m6db$49n$1@supernews.com>
+References: <364067e5.15703318@news.mindspring.com>
+NNTP-Posting-Host: 207.211.61.222
+X-Trace: 910073067 WUENVH4GT3DDECFD3C usenet78.supernews.com
+X-Complaints-To: newsabuse@supernews.com
+X-Newsreader: Microsoft Outlook Express 4.72.3110.1
+X-MimeOLE: Produced By Microsoft MimeOLE V4.72.3110.3
+Xref: newshub1.home.com comp.sys.ibm.pc.games.flight-sim:30154288
+
+Denny's starting a new thread! I'm getting verklempt! ; )
+
+Ok, I've gotta bite on this one - can't allow the possibility for a new
+pro-canned movement (or even a "maybe scripted isn't so bad"
+movement, for that matter either!) : )
+
+Disclaimers - this is all MHO. I ended up getting really wordy. My apologies,
+ but I wanted to write until I got it all off my chest.
+
+Denny Atkin wrote in message <364067e5.15703318@news.mindspring.com>...
+>Okay, there are a number of folks here who SWEAR by dynamic campaigns, some
+>going so far as to say they won't buy a sim that doesn't feature them.
+>
+>Here's the big question: Which sims are you holding up as examples of good
+>dynamic campaigns? Because until VERY recently, there haven't been that many
+>opportunities to experience a good dynamic campaign. Falcon 3.0's is often held
+>up as the previous Holy Grail, but that's mostly people remembering things
+>better than they were. Falcon 3.0 had a number of kludgy elements and faked
+>occurrences in its campaign--such as the MiG-19s that often spontaneously
+>spawned to harass you when you were landing. EF2000 had a fully dynamic
+>campaign, but the mission structures were so repetitive that I bored of it as
+>quickly as I did any "canned mission" sim. Longbow 2 had a good dynamic
+>campaign, but the most interesting missions were the prescripted ones.
+>
+>So I pose these questions, just for discussion:
+>1) What are the examples of good past dynamic campaigns? What made them good,
+>and kept them from getting boring?
+
+
+----Top examples were Falcon 3 and Longbow 2.
+
+ Both integrate air and ground
+combat well, give a certain degree of flexibility, and both calculate the
+progress of the war according to the ability of the ground forces to fight, not
+according to your mission score. Therefore, you can get killed in a mission, but
+still win a battle, because you managed to make a positive impact. You are a
+part of the picture - important, but you are *in* the world, not "the world revolves
+around you" - critical distinction to make.
+
+--------Good examples include iF/A-18E, TAW, and apparently Tornado (although I
+didn't get very far into Tornado's campaign, but for non-campaign related reasons)
+
+iF/A-18E is good, but TALON still needs more refinement before it will realize it's
+goals properly IMHO. Very important that friendly assets (eg friendly naval ships)
+will try to protect you/themselves with their SAM screens - opens up possibilities
+TAW took an interesting "multiple scoreboard" approach, but it seems to have taken
+a purely political aspect to the war rather than considering the ground war. That
+pays too much homage to the low-intensity conflicts IMHO. That prevents it from
+being ranked with Falcon 3/Longbow 2.
+
+(I could even sneak X-COM into the picture! : )
+
+----------Mediocre examples include RB2, EF2000, M1TP2
+
+RB2 - basically a random mission generator. M1TP2 - same thing.
+EF2000 - no ground war, your FEBA moves as a result not of
+the battle, not even of your accomplishments, but exclusively as a
+result of your mission score.
+
+---------Examples that deserve to be shot are iF-22 Raptor and Team Apache.
+
+iF-22 Raptor claimed dynamic and was scripted. Team Apache either
+also claimed it, or at least hinted strongly at it with it's real time clock
+(that ended up being utterly superfluous). JSF might belong here, too, but
+frankly I got sick of it way too fast to figure that out.
+
+>2) If a mission featured a scripted campaign, but had features that kept it
+>from being repetitive -- targets that stayed destroyed, enemy locations and
+>force makeups that were randomly generated to keep them from being predictable,
+>being retasked to destroy missed targets -- would that be an acceptable
+>substitute?
+
+
+You seem to be describing Janes F-15 for the most past. My answer is no.
+
+For starters, poor scripted missions are no better than random mission generators.
+Good scripted missions, no matter how you shake or stir it, end up where the
+player is always trying to second-guess the designer. Also, "randomizing" a
+scripted mission isn't a whole lot more fun than shaking up one of those
+glass paperweights with the fake snow inside. Sure, a few elements may
+shift around, but you're looking at the same scene no matter what. IMHO, even
+when dynamic campaign missions get somewhat repetitive, it still doesn't
+feel as rigidly constrained.
+
+>3) Is it replayability or the chance to affect the outcome of the war that
+>makes dynamic campaigns appealing to you?
+
+
+Both, but there is more to it than that.
+
+Several things (most have a little interrelation):
+
+1- Yes, replayability
+
+2- Not doing the same mission over again just because I failed it the first time
+
+3- Being able to play a part/have an affect in a simulated war, absolutely -
+ as long as the designers carefully balance my ability to affect that war so as not
+ to over or under value my contribution. If I find myself *caring* about the state
+ of the war when I'm driving to class, that's a good sign. That's never happened
+ to me in any game except those with dynamic campaigns.
+
+4- related to #3 - What you do, actually **matters**. There is a point to it
+ other than simply "jumping through hoops", then getting Pavlovian
+ reinforcement by little FMV treats. :^P Phooey! I want a sense of
+ accomplishment that's more profound than that.
+
+5A - Not second guessing the designer. You are focused on what needs to be
+ done, what you can do to help the war effort, not "Would the mission
+ designers really hide an ambush over there? Do they expect me to have to resort
+ to guns or try something else? Maybe I'm supposed to use a stealthy fighter?"
+
+5B - MISSIONS ARE NOT PUZZLES!!!
+ the worst examples - "Ok - let me get this straight - I have to first go to point A
+ and hit the tanks with the Mavericks, then I have only a few seconds left to
+ afterburn to point B and use the AMRAAMs on the Mig-21's to protect my
+ rescue helo from being shot down, then I have to go to point C and dogfight
+ with another group of MiGs before they bomb my airbase. Whew, that only
+ took eight failures to figure out!"
+
+6 - Unpredictability - I've repeatedly heard the claim that only a scripted mission can
+ really surprise a player. I disagree - There are many things that I have experienced
+ in dynamic campaigns that were surprising, entertaining, and all the more fun
+ because elements create situations that mission designers couldn't or wouldn't
+ have possibly dreamt up.
+
+ Ask me if I'd prefer to explore the state by train, car, or helicopter...
+ Sure, a train may go through some nice scenery, but really you're just along for the ride.
+ In a car, you can go pretty much where you want. In a helicopter, you can do whatever
+ you want. Even if you still choose to travel in a straight line, just the knowledge that there
+ is a wide expanse of possibilities is very powerful.
+
+7 - Dynamic campaigns tend to have a world that reacts more to what you and other
+ vehicles do. Scripted campaigns tend to only care about what happens in the
+ context of the mission design. (Example - In Team Apache, enemy helicopters
+ and AAA are only located along the line created by your waypoints. Take the
+ scenic route instead, and you can completely bypass the vast majority of threats.
+ Worse yet, the helicopters in Team Apache can only operate in a little tiny area.
+ They're nothing more than flying roadblocks. Even if I was spotted in another
+ location, they couldn't be vectored in after me.) Now while there are exceptions,
+ the fact is that such omissions would be *glaringly* obvious in dynamic campaigns
+ where they may be more subtle in a scripted one. In other words, you can't fake things
+ as much.
+
+
+>4) A dynamic campaign is never going to generate a mission such as "shoot down
+>Admiral Yamamoto in your P-38. "
+
+
+Why can't it generate such a mission? It really wouldn't be all that hard if you think
+about it. Games like Warlords have "heroes" that lead the armies as they move
+around the board. A WW2 dynamic campaign could fairly easily model the
+movement of such VIPs as they travel around to lead their forces. Some dynamic
+campaigns already model intelligence assets. Given that the computer is tracking
+the movement of VIPs and an intelligence asset is present in the area (in this case,
+in-theatre codebreakers and a transmission intercept, as I recall), a dynamic "kill VIP
+mission" to "shoot down Yamamoto in your P-38", "Go shoot Saddam's Winnebago"
+or "drop a bunker-buster on Saddam's HQ" is certainly possible. It only takes
+imagination, a little development time, and perhaps the development of a few tools
+to help make such things possible.
+
+> Don't scripted campaigns have the potential to
+>be a lot more interesting?
+
+I don't believe so. The simplest way to put it is this - take the number of times
+you've finished a mission and said to yourself or a friend afterward "Wow! That
+was one hell of a mission!" For me, the number of times I had that feeling in
+a scripted campaign to the number of times I felt that in a dynamic campaign
+is not even remotely close. I'd honestly ballpark the frequency at an 8-to-1 ratio
+in favor of the dynamic campaign.
+
+>(I'm not at all saying I'm not a fan of dynamic campaigns, and I'll outline
+>what I think the perfect campaign for today's techology is in a future post.
+>But I want to hear from some of the rest of you without "leading" into a
+>discussion of my own theories. And let's not even open hte multiplayer can of
+>worms. :-)
+
+
+Good idea. Let's focus on the campaign. : )
+
+Bottom line for me - If forced to choose, I'd still take a mediocre dynamic
+campaign over the best scripted campaign any day of the week.
+
+Neil M.
+
+
diff --git a/Doc/landscape.txt b/Doc/landscape.txt
new file mode 100644
index 0000000..079a1fc
--- /dev/null
+++ b/Doc/landscape.txt
@@ -0,0 +1,88 @@
+Subject: Re: Landscape Eng
+From: "Ville Miettinen" <wili@surrender3d.com>
+Date: 1997/05/09
+Message-Id: <5kto6g$2tu$1@news.clinet.fi>
+References: <33704A59.50AB@csc.liv.ac.uk> <3371D8DE.7B8E@funcom.ie>
+X-MimeOLE: Produced By Microsoft MimeOLE Engine V4.71.0544.0
+Organization: Hybrid Holding-HH Ltd.
+Newsgroups: rec.games.programmer
+[Fewer Headers]
+
+After checking out the article in Edge magazine we implemented a similar
+landscape on a PC system for mainly experimental reasons. The main point
+here was not trying to solve the problem of showing enormous
+ amounts of high-detail data with less polygons, but rather adding extra
+detail to closer areas. I.e. your database consists of the 'lowest level'
+presentation of an area and if we want more precision, we can calculate it
+from the low level presentation.
+
+I used a spline heightfield where the "low level" presentation defined the
+control points. Fractal noise was then added (easiest way is to use a
+simple LUT) to produce more variance. The whole world is divided into a set
+of large grids. For each visible grid (clip the world to the view frustrum)
+we allocate
+a LOD (level of detail) value which determines how many times the grid will
+be subdivided. The LOD is calculated by a variety of factors including the
+grid bounding box projected height, z-distance etc. When subdividing the
+grid we calculate new vertex height coordinates by using the spline
+functions and fractal noise as stated before. Special care must be taken so
+that shared edges (I used a vertex cache system which finds common vertices
+between polygons) are set properly; otherwise you'll end up
+with gaps in the landscape when a lower LOD grid meets a higher LOD grid.
+
+With an octree (or quadtree for single-level heightfields) presentation, we
+can easily store higher
+resolution info for 'important areas' of the landscape. Also, using an
+efficient hidden surface removal system in as early phase as possible is
+important; otherwise you'll have a hard time rendering
+all the covered grids. A scanline-buffering (or CSG) coverage determination
+for octree branches works
+well and kills quickly most of the hidden areas.
+
+This was all simple. However the texturing proved quite a bit trickier.
+Apparently the Innerloop people hadn't solved the problem; I haven't seen
+the engine running, but all of the shots displayed landscapes with a single
+texture map wrapped over the whole world. In one of the pictures I think
+there was another texture as well, but there weren't any shots of
+individually textured polygons or grids.
+
+As our landscape engine was using individual texture maps for all 'large
+grids' (each texture map being real-time RGB alpha-blended from
+neighbouring textures), I made the grid subdivision to subdivide each grid
+into 4 parts and created four new textures for them. Each new texture was a
+corner of the old texture, bilinearly interpolated to same size, after
+which a microtexture ('detail texture') was added on top of it. This method
+brings in loads of extra detail (although random fractal detail) however
+close you zoom.
+
+cheers,
+-wili
+
+--
+Ville Miettinen
+3D Programmer, Hybrid Holding Ltd.
+http://hybrid.org/surrender (SurRender home page)
+http://hybrid.org/wtga (free quantization tools)
+ Ivar Just Olsen wrote in article <3371D8DE.7B8E@funcom.ie>...
+>N.R. Matthews wrote:
+>>
+>> Hi I'm trying to write a fast lasdscape engine, recenly I read an
+>> article in the Edge magazine and came across a group called Innerloop
+>> who had got a pretty amazing engine out. Now apparently this works of
+>> something called IFS (Iterated Function Systems) a mathematical term
+for
+>> a special type of fractals. Does anyone have any idea how you could use
+>> a fractal to efficiently render an entire landscape. On a undulating or
+>> "bumpy" landscape most of the landscape is hidden, depending on the
+>> bumpiness perhaps 90% hidden.
+>>
+>> Any ideas appeciated NatMat
+>
+>I know these guys, and I know how it's done. But the method is
+>copyrighted, so I can't tell. Sorry.
+>
+>Hint: Clever use of subdivision (and have a look at NURBS while thinking
+>;-)
+>
+>--
+>Ivar, just another Funcom programmer. \ No newline at end of file
diff --git a/Doc/old manual.doc b/Doc/old manual.doc
new file mode 100644
index 0000000..c2fd04f
--- /dev/null
+++ b/Doc/old manual.doc
Binary files differ
diff --git a/Doc/space sim market crash.txt b/Doc/space sim market crash.txt
new file mode 100644
index 0000000..3be1f21
--- /dev/null
+++ b/Doc/space sim market crash.txt
@@ -0,0 +1,32 @@
+
+1997:
+X-Wing vs. Tie Fighter (Balance of Power) 333,608 units ($15,333,000)
+Wing Commander Prophecy (and gold) 210,000 units ($7,110,000)
+
+1998
+Descent Freespace (Freespace 1) (and gold) 147,158 units ($4,563,000)
+Freespace expansion pack (alone) 23,000 units ($404,000)
+(so approximately 170,000 units and $5,000,000 for Freespace 1 in total)
+
+Independence War 61,000 units ($2,210,000)
+Interplay Re-release of Battlecruiser 3000 41,037 units ($405,000)
+
+1999
+X-Wing Alliance 235,920 units ($7,457,000)
+FreeSpace 2 (including game of the year) 83,484 units ($2,703,000)
+Independence War Deluxe 23,000 units ($487,000)
+(so approximately 84,000 units and $2,700,000 for Independence War and its
+expansion)
+
+2000
+Tachyon 61,200 units ($2,112,000)
+Starlancer 27,390 units ($1,147,000)
+Allegiance 12,700 units ($443,000)
+X-Beyond the Frontier 10,834 units ($295,000)
+
+2001
+Independence War 2 10,069 units ($407,000)
+
+
+I can't find Jump Gate's stats for some reason, but as I recall it sold less
+than 10,000 units.
diff --git a/Doc/squadron (wc) info.txt b/Doc/squadron (wc) info.txt
new file mode 100644
index 0000000..0352983
--- /dev/null
+++ b/Doc/squadron (wc) info.txt
@@ -0,0 +1,96 @@
+
+
+SQUADRON
+
+
+PRODUCT DESCRIPTION
+
+Background
+ By the year 2654 AD, mankind has spread throughout his section of the galaxy. Along the way, humanity has come in contact with a handful of sentient races. Soon after first contact, most of these races have proven friendly.
+ The Kilrathi definitely are not.
+ Vessels of the Empire of Humanity first encountered Kilrathi explorers twenty-five years ago, in 2629. The Imperials offered the standard, non-linguistic greeting to the new aliens this opening gesture of friendship was met with a volley of laser fire. Within five years, the Empire and the Kilrathi were locked in an all-out-war for survival.
+
+The Kilrathi
+ The Kilrathi are a vaguely mammalian race from a planet several hundred light years from Sol. Before they encountered humanity, the Kilrathi had never met a race capable of faster-than-light travel. Their warlike nature and technological superiority over their near neighbors led the Kilrathi to conquer and enslave every race they encountered. Over four centuries, the Kilrathi built a martial state two hundred light years across, ruled by the Supreme General of Kilrah.
+ When the Kilrathi met humankind, they had finally encountered a race whose technology and power rivaled their own. Unfortunately, half a millennium of experience made it impossible for the Kilrathi to accept another race as their equal -- their only method for dealing with alien cultures was to conquer and subjugate them.
+
+The War
+ In 2634, the Supreme General committed the entirety of the Kilrathi to the defeat or destruction of humankind. The ferocity of their initial attacks caught the Empire by surprise; thousands of worlds and billions of lives were lost before the Empire could marshal any serious opposition to the alien onslaught.
+ The desperate struggle between man and Kilrath has pitted the determination and ingenuity of human defenders against the specialization and fanaticism of the genetically-engineered Kilrathi war machine. Through desperation as much as genius, the humans have fought back after their early losses. Today, twenty years after the war broke out, the conflict has devolved into a bloody stalemate, typified by a constant stream of dirty little skirmishes over war-torn worlds.
+
+
+Premise
+ You are one of the Imperial Navy's finest --- a hot, young starfighter pilot, fresh from the Imperial Academy on Terra and combat flight training in the Vega system. All your life, you've heard the stories of the brave Imperial pilots, defending humanity against the vicious Kilrathi monsters. As you arrive on the Kilrathi frontier, your heart soars in anticipation of a life of glory and adventure.
+ A newly-commissioned officer, you've been assigned to a strike carrier, the ISS Tiger's Claw. Your exemplary performance back at Vega has earned you the position of a flight leader with the 2411th Imperial Fighter Squadron -- the well-known "Blood Hawks." The men and women who have served before you, both on the Tiger's Claw and in the Blood Hawks, have established a tradition of bravery, skill and excellence you will be proud to uphold.
+ One of the Empire's fastest carrier ships, the Tiger's Claw fulfills a troubleshooting role in the 36th Imperial Battle Fleet. It is one of the Fleet's "first response" vessels, usually the first Imperial ship to encounter the enemy in a combat situation.
+
+
+Spaceflight and Combat
+ The core of Squadron is a state-of-the-art spaceflight combat simulation in which you and your two wingmen dogfight with the fighters, corvettes, cruisers, dreadnoughts, and other ships of the Kilrathi Star Force.
+
+Imperial Starships
+ The starships that battle in the Squadron universe are not the simple, blocky polygon vessels that populate most space games. Instead, Squadron's starfighters -- and all the other vessels and objects in the game -- are amazingly photo-realistic, bit-mapped images, offering a crispness and tangibility previous games have lacked. The computer will depict every vessel using 64 ray-traced images, providing the smooth, realistic movement in any direction in three-dimensional space.
+ Approximately 25 to 30 different vessel designs will appear in Squadron. You will be able to lead wings composed of four or five different Imperial fighter ships, ranging from the small, lightly-armed one-man Hornet to the two-man Raptor, a heavy fighter featuring a wide array of laser and missile systems. Other vessels in the Imperial fleet will include the strike carrier Tiger's Claw as well as the Venture-class corvette, the Drayman-class transport and the Diligent-class tanker.
+
+Kilrathi Starships
+ The half-dozen starfighters representing the Kilrathi Star Fleet will run a gamut similar to that of the Imperial fighters, ranging from a light, highly-maneuverable ship to a tough, heavily-armed one. The Kilrathi battlefleet will be even more completely represented than the human one, though, five larger warships will be included as well; from smallest to largest, they include the corvette, the destroyed, the cruiser, the carrier, and the dreadnought.The dreadnought sports a number of independently-controlled weapons turrets, which pivot on the surface of the vessel to target enemy ships. Supporting the Kilrathi fleet will be transport and ranker ships, as well as a large starbase.
+ Adding color to the Kilrathi fleet are four unique vessels, flown by NPC Kilrathi aces in key battles; once encountered, these distinctive starfighters will be instantly recognizable, allowing you to establish rivalries with enemy pilots over the course of a campaign.
+
+The Environment
+ But starships are not the only photo-realistic, ray-traced objects in the Squadron universe. Some of the battles against the Kilrathi will take place in fields of spinning, drifting asteroids, or around large spare stations. And the projectiles launched by starships -- from laser bolts to mines to heat-seeking, image recognizing, or dead-fire misfiles -- will also be represented with three-dimensional, ray-traced graphics.
+ Beyond the spaceships, missiles and asteroids, at the very limit of Squadron space, are a variety of celestial bodies. Though they will be so far away from the player's vessel that they will seem fixed in place, these planets, stars and nebulae provide the game with a reference for the pilots. For example, one battle may take place in an alien solar system; the star would be similar in the distance on one side, while its planets could be seen in other directions. Of course, the inner planets, whose dark sides would be turned to the battlefield, would appear as only crescent-shaped slivers, while those farther out will be nearly-full spheres, since it is their lit faces that will be turned towards the combatants.
+
+
+In the Cockpit
+ But the breathtaking realism of Squadron doesn't end at the edge of the pilots windshield -- the interior of his ship is also depicted in vivid detail.
+
+Control Panels
+ In fact, each of the four or five fighter-ships you can fly will have a different interior. Arranged around the control panels are a variety of functional scanners, readouts, and displays. Two scanners show the position of the enemy ships, one short-range and the other long. Other displays indicate the ship's available power, its shield level, its armament. As your ship is damaged, various displays will spark, explode or go dead, depriving you of vital information.
+
+The Pilot
+ The heart of a starfighter is its pilot, and the ships in Squadron are no exception. Unlike existing airplane and spaceflight simulators, which show -- at best -- the pilots hand on the joystick, Squadron offers a full-body, over-the-shoulder of an animated pilot. Not only does his right hand steer the on-screen joystick along with your maneuvers, but his whole body moves in response to your actions and the environment. When the ship pulls a tight turn, the pilot's hand tilts with the G-force of the maneuver; when the ship is hit by lasers or missiles his whole body shakes with the impact. If you hit a key, to drop your shields or fire a missile, the on-screen pilot reaches with his left hand to hit the appropriate button on his control panel. The pilot even expresses emotion, to convey the drama of the dogfight -- when things get tense, you'll see him clench his fist to hold his nerves in check, and when you score a hit, he'll flash a quick, self-congratulatory thumbs-up sign!
+
+Radio Communications
+ One of the most important parts of any dogfight is the radio chatter that accompanies and describes the action. This too will be reflected in Squadron. A video screen on the pilot's control panel will display a close-up shot of who ever happens to be speaking to you at any given moment -- your commander back on the Tiger's Claw, one of your wingmen, or the alien ace you've met in previous battles. the speaker's situation will be reflected on the video screen-- if he's been damaged, the image will be fuzzy and flickering. If he blows up, the screen glows white, then goes dead.
+ The speaker's message will scroll across the bottom of the cockpit screen, like subtitles in a movie a tornado warning on television the speech of your fellow Imperials will appear as white text, while aliens talk in yellow or lime-green text.
+
+
+Scenarios
+ At its most basic level, Squadron can be played by electing any of roughly 10 to 15 stand-alone battle scenarios. Each scenario is a unique combat situation, pitting you and your wingmen against a different assortment of enemy starships, and offering a different mission objective. Before the Battle
+ Each scenario begins with a briefing, in which your squadron commander aboard the Tiger's Claw describes your mission, the opposition you are likely to face, and your mission objectives. Using a simple keyword system, you can ask your commander for additional information about the mission or strategic advice.
+ From the briefing, the action cuts to the flight deck, where you see yourself climbing into the cockpit of your starfighter. The screen then cuts to the cockpit interior, where you'll have a few moments to go over your equipment, pre-set your shield levels, review your array of missiles, etc. Looking out the viewscreen of the fighter, you can see down the launch tubes; when you're ready to begin your mission, you hit the launch button. Through the viewscreen, you'll see the launch tube rushing by at a frightening speed -- then blackness surrounds you and you're in space. Your wingmen are just a bit head of you, to the right and left. You check your long-range scanners for bogeys, and head off to intercept the enemy.
+
+Killed in Action
+ You should already have a feel for Squadron's remarkable space-combat simulation. Worth mentioning here, however, is the animated sequence which will run if your ship is destroyed.
+ Should you suffer a fatal hit, the action will cut from the interior cockpit view to an exterior shot of your vessel. In a brief animation, your ship will explode in bright red and orange flames, with small bits of debris flying off in all directions. Once the explosion has faded to black, the scene cuts back to the briefing room, for the post-mission debriefing. Your pilots seat in the room will be conspicuously absent at the debriefing.
+
+Coming Home
+ If you managed to survive to the conclusion of your mission, whether successful or not, the screen will cut from the cockpit view you saw throughout the battle to an exterior view of the Tiger's Claw. If the Claw suffered damage during the scenario, this will be apparent from this vantage.
+ Your ship, and those of your wingmen if they survived, can be seen approaching in the distance, returning to the carrier after the mission. As you draw near, a tractor beam from the Tiger's Claw grabs your ships and guides them into the landing bay on the flight deck. Next, the scene cuts to a view of you climbing out of your fighter while an attentive mechanic inspects the ship for damage. In the background are the ships of your wingmen, if they made it back; if they were lost in combat, the next two spaces in the landing bay are conspicuously empty.
+ When you leave the landing bay, you report to the briefing room for a mission analysis and debriefing. There, your squadron commander briefly analyzes your performance, and assesses mission success. He offers his congratulations if you've done well, or chews you out if you've performed poorly.
+ After debriefing, the game returns to the main menu screen, where you can select another stand-alone scenario or begin the full-scale Squadron campaign.
+
+
+The Campaign
+ Squadron is at its richest, most satisfying, and best when played as a Campaign. The Campaign version of Squadron allows you to participate in a series of crucial battles in the year 2654, at the height of the Imperial-Kilrathi war. By performing well in the scenarios, you can advance in rank, receive a variety of decorations, and be reassigned to various types of starfighters. Poor performance on the other hand, will weaken the Imperial position in the war, and result in more and harder scenarios later on, as the Kilrathi erode Imperial resources and personnel while suffering minimal losses themselves.
+
+Campaign Structure
+ The Campaign consists of 20 scenarios, linked by a branching flow-chart. Your performance on earlier missions will directly affect your position in later ones, not to mention occasionally determining which branch you'll follow through the Campaign.
+ In many cases, the effects of your performance might be subtle. For example, in the first scenario, you might be pitted against three alien starfighters. If any of these ships escape your dogfight, they will also appear in the second scenario, along with the ships which would appear in any case. Later on, if you fail to protect a supply ship, the Tiger's Claw may find itself short of material in the following scenarios, resulting in fuel and ammo rationing, and preventing certain repairs from being made to your starfighters between missions.
+ In other instances, your performance may divert the Campaign onto another path of scenarios. If you destroy a Kilrathi strike carrier in the Symmett system, for example, this may set you up to press your advantage, making a direct assault on the Kilrathi starbase in that system. Or, if you fail to make it back from a scouting mission in a certain period of time, you may return to the Tiger's Claw only to find it under attack by Kilrathi fighters, which you'll have to defeat before you can land.
+ In all, a typical run through the Squadron Campaign will involve you in about 12 of the 20 scenarios. This will add to the replay value of the game -- you'll have to play the Campaign several times before you've encountered every possible situation in every possible scenario. Further enhancing replay value is the fact that none of the stand-alone scenarios are included in the Campaign -- each battle in the Campaign will be completely new to the player.
+
+Beginning the Campaign
+ You'll begin the Squadron Campaign by selecting the appropriate option on the main men u screen. From there, the game will take you to briefing room, for your mission briefing, just as with a stand-alone scenario. At the end of your briefing, however, you'll be asked to choose your wingmen for the mission from among the members of your squadron.
+ A screen showing pictures and brief descriptions of the available pilots will appear. Based on your personal style of play and the information in the mission briefing, you may want to choose flashier, reckless and more daring pilots, or more careful and conservative wingmates. Of course, as you proceed through the Campaign, you'll come to know the members of your squadron better, and will settle on a handful of pilots you can count on as your regular wingman. In later scenarios, when you've moved up to a larger, two-man fighter, you'll also need to pick your own tailgunner.
+ After you've chosen your crew, the game will proceed just as described for a stand-alone scenario -- climbing into your ship, launching and fighting the battle.
+
+Back on the Carrier
+ After you've completed your missions, you'll return to the Tiger's Claw just as described for the stand-alone scenarios, above. The game will cut to the animated docking sequence, then the landing bay scene, and finally take you into the mission debriefing. After debriefing, if you've performed especially well, you may be awarded a promotion or a decoration. These honors are conferred by the commander of the Claw in an awards ceremony with simple animation.
+ Following the debriefing, the game may take you to any of a handful of inter-scenario scenes, showing you sharing a drink in the officer's club with your fellow pilots, enjoying a brief shore leave planetside, or watching the news in the Claw's recreation hall.
+ Of course, the lead story on the newscast is the latest update on the war against the Kilrathi. The holovision newscast will show highlights of your most recent battle as a miniaturized, holographic dogfight over the holoprojector in the rec hall. You and your fellow pilots can be seen sitting around the projector, watching the progress.
+
+Technical Notes
+ [Chris -- you'll have to add this yourself if you need it. I'd recommend covering all the things the program covers that are transparent to someone playing the game, such as NPC morale, NPC intelligence, and ship's characteristics.
+ You'll probably want to make a list of technical features, which I really can't do with any hope of accuracy. Things that would fit on this list would be the changing cockpit interiors for side and rear views, the ability to customize the key assignment for keyboard controls, the PC's ability to choose his complement of missiles for each mission, etc.]
+
diff --git a/FoundationEx/ArrayList.cpp b/FoundationEx/ArrayList.cpp
new file mode 100644
index 0000000..86fcbc3
--- /dev/null
+++ b/FoundationEx/ArrayList.cpp
@@ -0,0 +1,724 @@
+/* Project FoundationEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: FoundationEx
+ FILE: ArrayList.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Implementation of the untyped ArrayList class
+*/
+
+#include "MemDebug.h"
+#include "ArrayList.h"
+
+// +-------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+// +-------------------------------------------------------------------+
+
+ArrayList::ArrayList(const ArrayList& l)
+ : items(l.items), extent(l.extent)
+{
+#ifdef MEM_DEBUG
+ array = new(__FILE__,__LINE__) DWORD[extent];
+#else
+ array = new DWORD[extent];
+#endif
+
+ memcpy(array, l.array, extent*sizeof(DWORD));
+}
+
+void ArrayList::clear()
+{
+ delete [] array;
+ items = 0;
+ extent = 0;
+ array = 0;
+}
+
+// +-------------------------------------------------------------------+
+
+bool ArrayList::check(int& index) const
+{
+ if (index < 0) {
+ Print("Bounds error in ArrayList(%08x) index=%d min=0\n", (int)this, index);
+ index = 0;
+ }
+
+ else if (index >= items) {
+ Print("Bounds error in ArrayList(%08x) index=%d max=%d\n", (int)this,index, items-1);
+ index = items-1;
+ }
+
+ return (index >= 0 && index < items);
+}
+
+// +-------------------------------------------------------------------+
+
+DWORD ArrayList::operator[](int index) const
+{
+ if (check(index))
+ return array[index];
+ return 0;
+}
+
+DWORD& ArrayList::operator[](int index)
+{
+ if (check(index))
+ return array[index];
+
+ return array[0];
+}
+
+DWORD ArrayList::at(int index) const
+{
+ if (check(index))
+ return array[index];
+ return 0;
+}
+
+DWORD& ArrayList::at(int index)
+{
+ if (check(index))
+ return array[index];
+
+ return array[0];
+}
+
+// +-------------------------------------------------------------------+
+
+void ArrayList::resize(int newsize)
+{
+ if (newsize > extent) {
+ extent = 16 * (newsize/16 + 1);
+
+#ifdef MEM_DEBUG
+ DWORD* v = new(__FILE__,__LINE__) DWORD[extent];
+#else
+ DWORD* v = new DWORD[extent];
+#endif
+
+ for (int i = 0; i < items; i++)
+ v[i] = array[i];
+
+ for (; i < extent; i++)
+ v[i] = 0;
+
+ delete [] array;
+ array = v;
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void ArrayList::append(DWORD item)
+{
+ if (items+1 > extent)
+ resize(items+1);
+
+ array[items++] = item;
+}
+
+void ArrayList::append(const ArrayList& list)
+{
+ if (&list != this && list.items > 0) {
+ int need = items + list.items;
+ if (need > extent)
+ resize(need);
+
+ for (int i = 0; i < list.items; i++)
+ array[items++] = list.array[i];
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void ArrayList::insert(DWORD item, int index)
+{
+ if (index >= 0 && index <= items) {
+ if (items+1 > extent)
+ resize(items+1);
+
+ // slide right:
+ for (int i = items; i > index; i--)
+ array[i] = array[i-1];
+
+ array[index] = item;
+ items++;
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void ArrayList::insertSort(DWORD item)
+{
+ for (int i = 0; i < items; i++) {
+ if (item < array[i])
+ break;
+ }
+
+ insert(item, i);
+}
+
+// +-------------------------------------------------------------------+
+
+void ArrayList::remove(DWORD item)
+{
+ if (items < 1)
+ return;
+
+ for (int i = 0; i < items; i++) {
+ if (array[i] == item) {
+ removeIndex(i);
+ return;
+ }
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void ArrayList::removeIndex(int index)
+{
+ if (items < 1 || !check(index))
+ return;
+
+ // slide left:
+ for (int i = index; i < items-1; i++)
+ array[i] = array[i+1];
+
+ // blank out the hole we just created:
+ array[items-1] = 0;
+
+ items--;
+}
+
+// +-------------------------------------------------------------------+
+
+bool ArrayList::contains(DWORD val) const
+{
+ for (int i = 0; i < items; i++) {
+ if (array[i] == val)
+ return true;
+ }
+
+ return false;
+}
+
+// +-------------------------------------------------------------------+
+
+int ArrayList::count(DWORD val) const
+{
+ int c = 0;
+
+ for (int i = 0; i < items; i++) {
+ if (array[i] == val)
+ c++;
+ }
+
+ return c;
+}
+
+// +-------------------------------------------------------------------+
+
+int ArrayList::index(DWORD val) const
+{
+ for (int i = 0; i < items; i++) {
+ if (array[i] == val)
+ return i;
+ }
+
+ return -1;
+}
+
+// +-------------------------------------------------------------------+
+
+void ArrayList::swap(DWORD* a, int i, int j)
+{
+ if (i >= 0 && i < items && j >= 0 && j < items && i != j) {
+ DWORD t = a[i];
+ a[i] = a[j];
+ a[j] = t;
+ }
+}
+
+void ArrayList::qsort(DWORD* a, int lo0, int hi0)
+{
+ int lo = lo0;
+ int hi = hi0;
+
+ // zero or one element list, nothing to do:
+ if (lo >= hi) {
+ return;
+ }
+
+ // two element list, swap if needed:
+ else if (lo == hi-1) {
+ if (a[hi] < a[lo]) {
+ swap(a, lo, hi);
+ }
+ return;
+ }
+
+ // pick a pivot, and move it out of the way:
+ int mid = (lo+hi)/2;
+ DWORD pivot = a[mid];
+ a[mid] = a[hi];
+ a[hi] = pivot;
+
+ while (lo < hi) {
+ while ((a[lo] <= pivot) && lo < hi) lo++;
+ while ((pivot <= a[hi]) && lo < hi) hi--;
+
+ if (lo < hi) {
+ swap(a, lo, hi);
+ }
+ }
+
+ // Put the pivot into its final location:
+ a[hi0] = a[hi];
+ a[hi] = pivot;
+
+ qsort(a, lo0, lo-1);
+ qsort(a, hi+1, hi0);
+}
+
+void ArrayList::sort()
+{
+ if (items < 2)
+ return;
+
+ qsort(array, 0, items-1);
+}
+
+void ArrayList::shuffle()
+{
+ if (items < 3)
+ return;
+
+ for (int s = 0; s < 5; s++) {
+ for (int i = 0; i < items; i++) {
+ int j = (rand()>>4) % items;
+ swap(array, i, j);
+ }
+ }
+}
+
+
+// +===================================================================+
+
+DWORD ArrayListIter::value()
+{
+ if (list && step >= 0 && step < list->items)
+ return list->array[step];
+
+ return 0;
+}
+
+// +-------------------------------------------------------------------+
+
+void ArrayListIter::removeItem()
+{
+ if (list && step >= 0 && step < list->items)
+ list->removeIndex(step--);
+}
+
+// +-------------------------------------------------------------------+
+
+DWORD ArrayListIter::next()
+{
+ if (list && step >= -1 && step < list->items-1)
+ return list->array[++step];
+
+ return 0;
+}
+
+DWORD ArrayListIter::prev()
+{
+ if (list && step > 0 && step < list->items)
+ return list->array[--step];
+
+ return 0;
+}
+
+// +-------------------------------------------------------------------+
+
+void ArrayListIter::attach(ArrayList& l)
+{
+ list = &l;
+ step = -1;
+}
+
+// +-------------------------------------------------------------------+
+
+int ArrayListIter::size()
+{
+ if (!list) return 0;
+ return list->items;
+}
+
+// +-------------------------------------------------------------------+
+
+ArrayList& ArrayListIter::container()
+{
+ return *list;
+}
+
+
+
+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+
+FloatList::FloatList(const FloatList& l)
+ : items(l.items), extent(l.extent)
+{
+#ifdef MEM_DEBUG
+ array = new(__FILE__,__LINE__) float[extent];
+#else
+ array = new float[extent];
+#endif
+
+ memcpy(array, l.array, extent*sizeof(float));
+}
+
+void FloatList::clear()
+{
+ delete [] array;
+ items = 0;
+ extent = 0;
+ array = 0;
+}
+
+// +-------------------------------------------------------------------+
+
+bool FloatList::check(int& index) const
+{
+ if (index < 0) {
+ Print("Bounds error in FloatList(%08x) index=%d min=0\n", (int)this, index);
+ index = 0;
+ }
+
+ else if (index >= items) {
+ Print("Bounds error in FloatList(%08x) index=%d max=%d\n", (int)this,index, items-1);
+ index = items-1;
+ }
+
+ return (index >= 0 && index < items);
+}
+
+// +-------------------------------------------------------------------+
+
+float FloatList::operator[](int index) const
+{
+ if (check(index))
+ return array[index];
+ return 0;
+}
+
+float& FloatList::operator[](int index)
+{
+ if (check(index))
+ return array[index];
+
+ return array[0];
+}
+
+float FloatList::at(int index) const
+{
+ if (check(index))
+ return array[index];
+ return 0;
+}
+
+float& FloatList::at(int index)
+{
+ if (check(index))
+ return array[index];
+
+ return array[0];
+}
+
+// +-------------------------------------------------------------------+
+
+void FloatList::resize(int newsize)
+{
+ if (newsize > extent) {
+ extent = 16 * (newsize/16 + 1);
+
+#ifdef MEM_DEBUG
+ float* v = new(__FILE__,__LINE__) float[extent];
+#else
+ float* v = new float[extent];
+#endif
+
+ for (int i = 0; i < items; i++)
+ v[i] = array[i];
+
+ for (; i < extent; i++)
+ v[i] = 0;
+
+ delete [] array;
+ array = v;
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void FloatList::append(float item)
+{
+ if (items+1 > extent)
+ resize(items+1);
+
+ array[items++] = item;
+}
+
+void FloatList::append(const FloatList& list)
+{
+ if (&list != this && list.items > 0) {
+ int need = items + list.items;
+ if (need > extent)
+ resize(need);
+
+ for (int i = 0; i < list.items; i++)
+ array[items++] = list.array[i];
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void FloatList::insert(float item, int index)
+{
+ if (index >= 0 && index <= items) {
+ if (items+1 > extent)
+ resize(items+1);
+
+ // slide right:
+ for (int i = items; i > index; i--)
+ array[i] = array[i-1];
+
+ array[index] = item;
+ items++;
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void FloatList::insertSort(float item)
+{
+ for (int i = 0; i < items; i++) {
+ if (item < array[i])
+ break;
+ }
+
+ insert(item, i);
+}
+
+// +-------------------------------------------------------------------+
+
+void FloatList::remove(float item)
+{
+ if (items < 1)
+ return;
+
+ for (int i = 0; i < items; i++) {
+ if (array[i] == item) {
+ removeIndex(i);
+ return;
+ }
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void FloatList::removeIndex(int index)
+{
+ if (items < 1 || !check(index))
+ return;
+
+ // slide left:
+ for (int i = index; i < items-1; i++)
+ array[i] = array[i+1];
+
+ // blank out the hole we just created:
+ array[items-1] = 0;
+
+ items--;
+}
+
+// +-------------------------------------------------------------------+
+
+bool FloatList::contains(float val) const
+{
+ for (int i = 0; i < items; i++) {
+ if (array[i] == val)
+ return true;
+ }
+
+ return false;
+}
+
+// +-------------------------------------------------------------------+
+
+int FloatList::count(float val) const
+{
+ int c = 0;
+
+ for (int i = 0; i < items; i++) {
+ if (array[i] == val)
+ c++;
+ }
+
+ return c;
+}
+
+// +-------------------------------------------------------------------+
+
+int FloatList::index(float val) const
+{
+ for (int i = 0; i < items; i++) {
+ if (array[i] == val)
+ return i;
+ }
+
+ return -1;
+}
+
+// +-------------------------------------------------------------------+
+
+void FloatList::swap(float* a, int i, int j)
+{
+ if (i >= 0 && i < items && j >= 0 && j < items && i != j) {
+ float t = a[i];
+ a[i] = a[j];
+ a[j] = t;
+ }
+}
+
+void FloatList::qsort(float* a, int lo0, int hi0)
+{
+ int lo = lo0;
+ int hi = hi0;
+
+ // zero or one element list, nothing to do:
+ if (lo >= hi) {
+ return;
+ }
+
+ // two element list, swap if needed:
+ else if (lo == hi-1) {
+ if (a[hi] < a[lo]) {
+ swap(a, lo, hi);
+ }
+ return;
+ }
+
+ // pick a pivot, and move it out of the way:
+ int mid = (lo+hi)/2;
+ float pivot = a[mid];
+ a[mid] = a[hi];
+ a[hi] = pivot;
+
+ while (lo < hi) {
+ while ((a[lo] <= pivot) && lo < hi) lo++;
+ while ((pivot <= a[hi]) && lo < hi) hi--;
+
+ if (lo < hi) {
+ swap(a, lo, hi);
+ }
+ }
+
+ // Put the pivot into its final location:
+ a[hi0] = a[hi];
+ a[hi] = pivot;
+
+ qsort(a, lo0, lo-1);
+ qsort(a, hi+1, hi0);
+}
+
+void FloatList::sort()
+{
+ if (items < 2)
+ return;
+
+ qsort(array, 0, items-1);
+}
+
+void FloatList::shuffle()
+{
+ if (items < 3)
+ return;
+
+ for (int s = 0; s < 5; s++) {
+ for (int i = 0; i < items; i++) {
+ int j = (rand()>>4) % items;
+ swap(array, i, j);
+ }
+ }
+}
+
+
+// +===================================================================+
+
+float FloatListIter::value()
+{
+ if (list && step >= 0 && step < list->items)
+ return list->array[step];
+
+ return 0;
+}
+
+// +-------------------------------------------------------------------+
+
+void FloatListIter::removeItem()
+{
+ if (list && step >= 0 && step < list->items)
+ list->removeIndex(step--);
+}
+
+// +-------------------------------------------------------------------+
+
+float FloatListIter::next()
+{
+ if (list && step >= -1 && step < list->items-1)
+ return list->array[++step];
+
+ return 0;
+}
+
+float FloatListIter::prev()
+{
+ if (list && step > 0 && step < list->items)
+ return list->array[--step];
+
+ return 0;
+}
+
+// +-------------------------------------------------------------------+
+
+void FloatListIter::attach(FloatList& l)
+{
+ list = &l;
+ step = -1;
+}
+
+// +-------------------------------------------------------------------+
+
+int FloatListIter::size()
+{
+ if (!list) return 0;
+ return list->items;
+}
+
+// +-------------------------------------------------------------------+
+
+FloatList& FloatListIter::container()
+{
+ return *list;
+}
+
diff --git a/FoundationEx/ArrayList.h b/FoundationEx/ArrayList.h
new file mode 100644
index 0000000..6917d9b
--- /dev/null
+++ b/FoundationEx/ArrayList.h
@@ -0,0 +1,181 @@
+/* Project FoundationEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: FoundationEx
+ FILE: ArrayList.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Simple untyped array list
+*/
+
+#ifndef ArrayList_h
+#define ArrayList_h
+
+#ifdef WIN32
+#include <windows.h>
+#include <windowsx.h>
+#endif
+
+// +-------------------------------------------------------------------+
+
+class ArrayList
+{
+public:
+ ArrayList() : items(0), extent(0), array(0) { }
+ ArrayList(const ArrayList& l);
+ ~ArrayList() { delete [] array; }
+
+ DWORD operator[](int i) const;
+ DWORD& operator[](int i);
+ DWORD at(int i) const;
+ DWORD& at(int i);
+
+ void append(const ArrayList& list);
+ void append(const DWORD val);
+ void insert(const DWORD val, int index=0);
+ void insertSort(DWORD val);
+
+ DWORD first() const { return operator[](0); }
+ DWORD last() const { return operator[](items-1); }
+ void remove(DWORD val);
+ void removeIndex(int index);
+
+ void clear();
+
+ int size() const { return items; }
+ bool isEmpty() const { return !items; }
+
+ bool contains(DWORD val) const;
+ int count(DWORD val) const;
+ int index(DWORD val) const;
+
+ void sort();
+ void shuffle();
+
+private:
+ void qsort(DWORD* a, int lo, int hi);
+ void swap(DWORD* a, int i, int j);
+ void resize(int newsize);
+ bool check(int& index) const;
+
+ int items;
+ int extent;
+ DWORD* array;
+
+ friend class ArrayListIter;
+};
+
+// +-------------------------------------------------------------------+
+
+class ArrayListIter
+{
+public:
+ ArrayListIter() : list(0), step(-1) { }
+ ArrayListIter(const ArrayListIter& i) : list(i.list), step(i.step) { }
+ ArrayListIter(ArrayList& l) : list(&l), step(-1) { }
+
+ int operator++() { return next() != 0; }
+ int operator--() { return prev() != 0; }
+
+ void reset() { step = -1; }
+ DWORD next();
+ DWORD prev();
+ DWORD value();
+ void removeItem();
+
+ void attach(ArrayList& l);
+ ArrayList& container();
+ int size();
+ int index() { return step; }
+
+private:
+ ArrayList* list;
+ int step;
+};
+
+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+
+class FloatList
+{
+public:
+ FloatList() : items(0), extent(0), array(0) { }
+ FloatList(const FloatList& l);
+ ~FloatList() { delete [] array; }
+
+ float operator[](int i) const;
+ float& operator[](int i);
+ float at(int i) const;
+ float& at(int i);
+
+ void append(const FloatList& list);
+ void append(const float val);
+ void insert(const float val, int index=0);
+ void insertSort(float val);
+
+ float first() const { return operator[](0); }
+ float last() const { return operator[](items-1); }
+ void remove(float val);
+ void removeIndex(int index);
+
+ void clear();
+
+ int size() const { return items; }
+ bool isEmpty() const { return !items; }
+
+ bool contains(float val) const;
+ int count(float val) const;
+ int index(float val) const;
+
+ void sort();
+ void shuffle();
+
+private:
+ void qsort(float* a, int lo, int hi);
+ void swap(float* a, int i, int j);
+ void resize(int newsize);
+ bool check(int& index) const;
+
+ int items;
+ int extent;
+ float* array;
+
+ friend class FloatListIter;
+};
+
+// +-------------------------------------------------------------------+
+
+class FloatListIter
+{
+public:
+ FloatListIter() : list(0), step(-1) { }
+ FloatListIter(const FloatListIter& i) : list(i.list), step(i.step) { }
+ FloatListIter(FloatList& l) : list(&l), step(-1) { }
+
+ int operator++() { return next() != 0; }
+ int operator--() { return prev() != 0; }
+
+ void reset() { step = -1; }
+ float next();
+ float prev();
+ float value();
+ void removeItem();
+
+ void attach(FloatList& l);
+ FloatList& container();
+ int size();
+ int index() { return step; }
+
+private:
+ FloatList* list;
+ int step;
+};
+
+#endif ArrayList_h
+
diff --git a/FoundationEx/Dictionary.h b/FoundationEx/Dictionary.h
new file mode 100644
index 0000000..26ffd2e
--- /dev/null
+++ b/FoundationEx/Dictionary.h
@@ -0,0 +1,101 @@
+/* Project FoundationEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: FoundationEx
+ FILE: Dictionary.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Declaration of the Dictionary class
+*/
+
+#ifndef Dictionary_h
+#define Dictionary_h
+
+#include "Text.h"
+
+// +-------------------------------------------------------------------+
+
+template <class T> class Dictionary;
+template <class T> class DictionaryIter;
+template <class T> class DictionaryCell;
+
+// +-------------------------------------------------------------------+
+
+template <class T> class Dictionary
+{
+public:
+ Dictionary();
+ ~Dictionary();
+
+ T& operator[](const Text& key);
+
+ void insert(const Text& key, const T& val);
+ void remove(const Text& key);
+
+ void clear();
+
+ int size() const { return items; }
+ int isEmpty() const { return !items; }
+
+ int contains(const Text& key) const;
+ T find(const Text& key, T defval) const;
+
+private:
+ void init();
+
+ int items;
+
+ typedef DictionaryCell<T>* PTR;
+ PTR table[256];
+
+ friend class DictionaryIter<T>;
+};
+
+// +-------------------------------------------------------------------+
+
+template <class T> class DictionaryIter
+{
+public:
+ DictionaryIter(Dictionary<T>& l);
+ ~DictionaryIter();
+
+ int operator++(); // prefix
+
+ void reset();
+ void forth();
+
+ Text key() const;
+ T value() const;
+
+ void attach(Dictionary<T>& l);
+ Dictionary<T>& container();
+
+private:
+ Dictionary<T>* dict;
+ DictionaryCell<T>* here;
+ int chain;
+};
+
+// +-------------------------------------------------------------------+
+
+template <class T> class DictionaryCell
+{
+public:
+ DictionaryCell(const Text& k) : key(k), value( ), next(0) { }
+ DictionaryCell(const Text& k, const T& v) : key(k), value(v), next(0) { }
+ ~DictionaryCell() { }
+
+ Text key;
+ T value;
+ DictionaryCell<T>* next;
+};
+
+// +-------------------------------------------------------------------+
+
+#include "Dictionary.inl"
+#endif Dictionary_h
+
diff --git a/FoundationEx/Dictionary.inl b/FoundationEx/Dictionary.inl
new file mode 100644
index 0000000..98cdd05
--- /dev/null
+++ b/FoundationEx/Dictionary.inl
@@ -0,0 +1,260 @@
+/* Project FoundationEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: FoundationEx
+ FILE: Dictionary.inl
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Implementation of the Dictionary class
+*/
+
+#ifndef NDEBUG
+#define DICT_CHECK(a, b) if ((a) == 0) throw b;
+#else
+#define DICT_CHECK(a, b)
+#endif
+
+const int CHAINS = 256;
+
+// +-------------------------------------------------------------------+
+
+template <class T> Dictionary<T>::Dictionary()
+ : items(0)
+{ init(); }
+
+template <class T> Dictionary<T>::~Dictionary()
+{ clear(); }
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+void Dictionary<T>::init()
+{
+ items = 0;
+ memset(table, 0, CHAINS*sizeof(PTR));
+}
+
+template <class T>
+void Dictionary<T>::clear()
+{
+ for (int i = 0; i < CHAINS; i++) {
+ DictionaryCell<T>* link = table[i];
+
+ while (link) {
+ DictionaryCell<T>* n = link->next;
+ delete link;
+ link = n;
+ }
+ }
+
+ init();
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+T& Dictionary<T>::operator[](const Text& key)
+{
+ int idx = key.hash() % CHAINS;
+ DictionaryCell<T>* cell = table[idx];
+
+ if (cell == 0) { // empty chain
+ items++;
+
+#ifdef MEM_DEBUG
+ cell = new(__FILE__,__LINE__) DictionaryCell<T>(key);
+#else
+ cell = new DictionaryCell<T>(key);
+#endif
+
+ table[idx] = cell;
+
+ return cell->value;
+ }
+ else { // search for key
+ while (cell->next && cell->key != key)
+ cell = cell->next;
+
+ if (cell->key != key) { // not found in chain
+ items++;
+
+#ifdef MEM_DEBUG
+ cell->next = new(__FILE__,__LINE__) DictionaryCell<T>(key);
+#else
+ cell->next = new DictionaryCell<T>(key);
+#endif
+
+ return cell->next->value;
+ }
+ else { // found: return it!
+ return cell->value;
+ }
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+void Dictionary<T>::insert(const Text& key, const T& val)
+{
+ T& value = operator[](key);
+ value = val;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+void Dictionary<T>::remove(const Text& key)
+{
+ int idx = key.hash() % CHAINS;
+ DictionaryCell<T>* cell = table[idx];
+
+ if (cell == 0) { // empty chain
+ return;
+ }
+ else { // search for key
+ while (cell->next && cell->key != key)
+ cell = cell->next;
+
+ if (cell->key != key) { // not found in chain
+ return;
+ }
+ else { // found: remove it!
+ if (table[idx] == cell) {
+ table[idx] = cell->next;
+ delete cell;
+ }
+ else {
+ DictionaryCell<T>* p = table[idx];
+ while (p->next != cell)
+ p = p->next;
+ p->next = cell->next;
+ delete cell;
+ }
+ }
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+int Dictionary<T>::contains(const Text& key) const
+{
+ int idx = key.hash() % CHAINS;
+ DictionaryCell<T>* cell = table[idx];
+
+ if (cell != 0) {
+ while (cell->next && cell->key != key)
+ cell = cell->next;
+
+ if (cell->key == key)
+ return 1;
+ }
+
+ return 0;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+T Dictionary<T>::find(const Text& key, T defval) const
+{
+ int idx = key.hash() % CHAINS;
+ DictionaryCell<T>* cell = table[idx];
+
+ if (cell != 0) {
+ while (cell->next && cell->key != key)
+ cell = cell->next;
+
+ if (cell->key == key)
+ return cell->value;
+ }
+
+ return defval;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T> DictionaryIter<T>::DictionaryIter(Dictionary<T>& d)
+ : dict(&d), chain(0), here(0)
+{ }
+
+template <class T> DictionaryIter<T>::~DictionaryIter()
+{ }
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+void DictionaryIter<T>::reset()
+{
+ chain = 0;
+ here = 0;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+Text DictionaryIter<T>::key() const
+{
+ return here->key;
+}
+
+template <class T>
+T DictionaryIter<T>::value() const
+{
+ return here->value;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+int DictionaryIter<T>::operator++()
+{
+ forth();
+ int more = chain < CHAINS;
+ return more;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+void DictionaryIter<T>::forth()
+{
+ if (here) {
+ here = here->next;
+ if (!here) // off the end of this chain
+ chain++;
+ }
+
+ if (!here) {
+ while (!dict->table[chain] && chain < CHAINS)
+ chain++;
+
+ if (chain < CHAINS)
+ here = dict->table[chain];
+ else
+ here = 0;
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+void DictionaryIter<T>::attach(Dictionary<T>& d)
+{
+ dict = &d;
+ reset();
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+Dictionary<T>& DictionaryIter<T>::container()
+{
+ return *dict;
+}
+
diff --git a/FoundationEx/List.h b/FoundationEx/List.h
new file mode 100644
index 0000000..1460cf4
--- /dev/null
+++ b/FoundationEx/List.h
@@ -0,0 +1,107 @@
+/* Project FoundationEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: FoundationEx
+ FILE: List.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Declaration of the List class template
+*/
+
+#ifndef List_h
+#define List_h
+
+// +-------------------------------------------------------------------+
+
+template <class T> class List;
+template <class T> class ListIter;
+
+// +-------------------------------------------------------------------+
+
+template <class T> class List
+{
+public:
+ List() : items(0), extent(0), array(0) { }
+ List(const List<T>& l);
+ ~List() { delete [] array; }
+
+ T*& operator[](int i);
+ T* operator[](int i) const;
+ T*& at(int i);
+ T* at(int i) const;
+
+ void append(List<T>& list);
+ void append(const T* val);
+ void insert(const T* val, int index=0);
+ void insertSort(const T* val);
+
+ T* first() const { return operator[](0); }
+ T* last() const { return operator[](items-1); }
+ T* remove(const T* val);
+ T* removeIndex(int index);
+
+ void clear();
+ void destroy();
+
+ int size() const { return items; }
+ bool isEmpty() const { return !items; }
+
+ bool contains(const T* val) const;
+ int count(const T* val) const;
+ int index(const T* val) const;
+ T* find(const T* val) const;
+
+ void sort();
+ void shuffle();
+
+private:
+ typedef T* PTR;
+ void qsort(T** a, int lo, int hi);
+ void resize(int newsize);
+ bool check(int& index) const;
+ void swap(T** a, int i, int j);
+
+ int items;
+ int extent;
+ PTR* array;
+
+ friend class ListIter<T>;
+};
+
+// +-------------------------------------------------------------------+
+
+template <class T> class ListIter
+{
+public:
+ ListIter() : list(0), step(-1) { }
+ ListIter(const ListIter<T>& i) : list(i.list), step(i.step) { }
+ ListIter(List<T>& l) : list(&l), step(-1) { }
+
+ int operator++() { return next() != 0; }
+ int operator--() { return prev() != 0; }
+ T* operator->() { return value(); }
+ T& operator* () { return *value(); }
+
+ void reset() { step = -1; }
+ T* next();
+ T* prev();
+ T* value();
+ T* removeItem();
+
+ void attach(List<T>& l);
+ List<T>& container();
+ int size();
+ int index() { return step; }
+
+private:
+ List<T>* list;
+ int step;
+};
+
+#include "List.inl"
+#endif List_h
+
diff --git a/FoundationEx/List.inl b/FoundationEx/List.inl
new file mode 100644
index 0000000..841e690
--- /dev/null
+++ b/FoundationEx/List.inl
@@ -0,0 +1,445 @@
+/* Project FoundationEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: FoundationEx
+ FILE: List.inl
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Implementation of the List class template
+*/
+
+// +-------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+List<T>::List(const List<T>& l)
+ : items(l.items), extent(l.extent)
+{
+#ifdef MEM_DEBUG
+ array = new(__FILE__,__LINE__) PTR[extent];
+#else
+ array = new PTR[extent];
+#endif
+
+ for (int i = 0; i < extent; i++)
+ array[i] = l.array[i];
+}
+
+template <class T>
+void List<T>::clear()
+{
+ delete [] array;
+ items = 0;
+ extent = 0;
+ array = 0;
+}
+
+template <class T>
+void List<T>::destroy()
+{
+ if (items) {
+ items = 0; // prevent dangerous re-entrancy
+
+ for (int i = 0; i < extent; i++)
+ delete array[i];
+
+ delete [] array;
+ items = 0;
+ extent = 0;
+ array = 0;
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+bool List<T>::check(int& index) const
+{
+ if (index < 0) {
+ Print("Bounds error in List(%08x) T=%s index=%d min=0\n", (int)this, T::TYPENAME(), index);
+ index = 0;
+ }
+
+ else if (index >= items) {
+ Print("Bounds error in List(%08x) T=%s index=%d max=%d\n", (int)this, T::TYPENAME(), index, items-1);
+ index = items-1;
+ }
+
+ return (index >= 0 && index < items);
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+T*& List<T>::operator[](int index)
+{
+ if (check(index))
+ return array[index];
+
+ if (!array || !extent)
+ resize(1);
+
+ return array[0];
+}
+
+template <class T>
+T* List<T>::operator[](int index) const
+{
+ if (check(index))
+ return array[index];
+ return 0;
+}
+
+template <class T>
+T*& List<T>::at(int index)
+{
+ if (check(index))
+ return array[index];
+
+ if (!array || !extent)
+ resize(1);
+
+ return array[0];
+}
+
+template <class T>
+T* List<T>::at(int index) const
+{
+ if (check(index))
+ return array[index];
+ return 0;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+void List<T>::resize(int newsize)
+{
+ if (newsize > extent) {
+ extent = 16 * (newsize/16 + 1);
+
+#ifdef MEM_DEBUG
+ T** v = new(__FILE__,__LINE__) PTR[extent];
+#else
+ T** v = new PTR[extent];
+#endif
+
+ for (int i = 0; i < items; i++)
+ v[i] = array[i];
+
+ for (; i < extent; i++)
+ v[i] = 0;
+
+ delete [] array;
+ array = v;
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+void List<T>::append(const T* item)
+{
+ if (item) {
+ if (items+1 > extent) resize(items+1);
+ array[items++] = (T*)item;
+ }
+}
+
+template <class T>
+void List<T>::append(List<T>& list)
+{
+ if (&list != this && list.items > 0) {
+ int need = items + list.items;
+ if (need > extent) resize(need);
+
+ for (int i = 0; i < list.items; i++)
+ array[items++] = list.array[i];
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+void List<T>::insert(const T* item, int index)
+{
+ if (item && index >= 0 && index <= items) {
+ if (items+1 > extent) resize(items+1);
+
+ // slide right:
+ for (int i = items; i > index; i--)
+ array[i] = array[i-1];
+
+ array[index] = (T*)item;
+ items++;
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+void List<T>::insertSort(const T* item)
+{
+ if (item) {
+ for (int i = 0; i < items; i++) {
+ if (*item < *array[i])
+ break;
+ }
+
+ insert(item, i);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+T* List<T>::remove(const T* val)
+{
+ if (items == 0 || val == 0)
+ return 0;
+
+ for (int i = 0; i < items; i++) {
+ if (array[i] == val) {
+ return removeIndex(i);
+ }
+ }
+
+ return 0;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+T* List<T>::removeIndex(int index)
+{
+ if (!check(index))
+ return 0;
+
+ T* tmp = array[index];
+ array[index] = 0;
+
+ // slide left:
+ for (int i = index; i < items-1; i++)
+ array[i] = array[i+1];
+
+ // blank out the hole we just created:
+ array[items-1] = 0;
+
+ items--;
+ return tmp;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+bool List<T>::contains(const T* val) const
+{
+ if (val) {
+ for (int i = 0; i < items; i++) {
+ if (array[i] && ((*array[i])==(*val)))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+int List<T>::count(const T* val) const
+{
+ int c = 0;
+
+ if (val) {
+ for (int i = 0; i < items; i++) {
+ if (array[i] && ((*array[i])==(*val)))
+ c++;
+ }
+ }
+
+ return c;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+int List<T>::index(const T* val) const
+{
+ if (val) {
+ for (int i = 0; i < items; i++) {
+ if (array[i] && ((*array[i])==(*val)))
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+T* List<T>::find(const T* val) const
+{
+ if (val) {
+ for (int i = 0; i < items; i++) {
+ if (array[i] && ((*array[i])==(*val)))
+ return array[i];
+ }
+ }
+
+ return 0;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+void List<T>::swap(T** a, int i, int j)
+{
+ if (i >= 0 && i < items && j >= 0 && j < items && i != j) {
+ T* t = a[i];
+ a[i] = a[j];
+ a[j] = t;
+ }
+}
+
+template <class T>
+void List<T>::qsort(T** a, int lo0, int hi0)
+{
+ int lo = lo0;
+ int hi = hi0;
+
+ // zero or one element list, nothing to do:
+ if (lo >= hi) {
+ return;
+ }
+
+ // two element list, swap if needed:
+ else if (lo == hi-1) {
+ if (*a[hi] < *a[lo]) {
+ swap(a, lo, hi);
+ }
+ return;
+ }
+
+ // pick a pivot, and move it out of the way:
+ int mid = (lo+hi)/2;
+ T* pivot = a[mid];
+ a[mid] = a[hi];
+ a[hi] = pivot;
+
+ while (lo < hi) {
+ while ((*a[lo] <= *pivot) && lo < hi) lo++;
+ while ((*pivot <= *a[hi]) && lo < hi) hi--;
+
+ if (lo < hi) {
+ swap(a, lo, hi);
+ }
+ }
+
+ // Put the pivot into its final location:
+ a[hi0] = a[hi];
+ a[hi] = pivot;
+
+ qsort(a, lo0, lo-1);
+ qsort(a, hi+1, hi0);
+}
+
+template <class T>
+void List<T>::sort()
+{
+ if (items < 2)
+ return;
+
+ qsort(array, 0, items-1);
+}
+
+template <class T>
+void List<T>::shuffle()
+{
+ if (items < 3)
+ return;
+
+ for (int s = 0; s < 5; s++) {
+ for (int i = 0; i < items; i++) {
+ int j = (rand()>>4) % items;
+ swap(array, i, j);
+ }
+ }
+}
+
+// +===================================================================+
+
+template <class T>
+T* ListIter<T>::value()
+{
+ if (list && step >= 0 && step < list->items)
+ return list->array[step];
+
+ return 0;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+T* ListIter<T>::removeItem()
+{
+ if (list && step >= 0 && step < list->items)
+ return list->removeIndex(step--);
+
+ return 0;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+T* ListIter<T>::next()
+{
+ if (list && step >= -1 && step < list->items-1)
+ return list->array[++step];
+
+ return 0;
+}
+
+template <class T>
+T* ListIter<T>::prev()
+{
+ if (list && step > 0 && step < list->items)
+ return list->array[--step];
+
+ return 0;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+void ListIter<T>::attach(List<T>& l)
+{
+ list = &l;
+ step = -1;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+int ListIter<T>::size()
+{
+ if (!list) return 0;
+ return list->items;
+}
+
+// +-------------------------------------------------------------------+
+
+template <class T>
+List<T>& ListIter<T>::container()
+{
+ return *list;
+}
+
diff --git a/FoundationEx/MemDebug.cpp b/FoundationEx/MemDebug.cpp
new file mode 100644
index 0000000..ce8a63c
--- /dev/null
+++ b/FoundationEx/MemDebug.cpp
@@ -0,0 +1,212 @@
+/* Project nGen
+ John DiCamillo
+ Copyright © 1997-2001. All Rights Reserved.
+
+ SUBSYSTEM: foundation
+ FILE: MemDebug.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Memory Debugging class
+*/
+
+#include "MemDebug.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <crtdbg.h>
+#include <malloc.h>
+
+// +--------------------------------------------------------------------+
+
+static Memory::LEVEL mem_chk_level = Memory::PERIODIC;
+
+#ifdef _DEBUG
+static _CrtMemState mem_chk_p1,
+ mem_chk_p2;
+#endif
+
+static HANDLE mem_log_file = 0;
+
+// +--------------------------------------------------------------------+
+
+#ifdef _DEBUG
+#define CrtSetDebugField(a) _CrtSetDbgFlag((a) | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG))
+#define CrtClrDebugField(a) _CrtSetDbgFlag(~(a) & _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG))
+#endif
+
+static void heapdump()
+{
+ _HEAPINFO hinfo;
+ int heapstatus;
+ DWORD used = 0;
+ DWORD avail = 0;
+ char report[256];
+
+ hinfo._pentry = NULL;
+ while ((heapstatus = _heapwalk( &hinfo )) == _HEAPOK) {
+ sprintf(report, "%6s block at %Fp of size %4.4X\n",
+ ( hinfo._useflag == _USEDENTRY ? "USED" : "FREE" ),
+ hinfo._pentry, hinfo._size);
+
+ _RPT0(_CRT_WARN, report);
+
+ if (hinfo._useflag == _USEDENTRY)
+ used += hinfo._size;
+ else
+ avail += hinfo._size;
+ }
+
+ sprintf(report, "------\nUsed Blocks: %d\nAvail Blocks: %d\nTotal Blocks: %d\n", used, avail, used+avail);
+ _RPT0(_CRT_WARN, report);
+
+ switch (heapstatus) {
+ case _HEAPEMPTY:
+ _RPT0(_CRT_WARN, "OK - empty heap\n" );
+ break;
+ case _HEAPEND:
+ _RPT0(_CRT_WARN, "OK - end of heap\n" );
+ break;
+ case _HEAPBADPTR:
+ _RPT0(_CRT_WARN, "ERROR - bad pointer to heap\n" );
+ break;
+ case _HEAPBADBEGIN:
+ _RPT0(_CRT_WARN, "ERROR - bad start of heap\n" );
+ break;
+ case _HEAPBADNODE:
+ _RPT0(_CRT_WARN, "ERROR - bad node in heap\n" );
+ break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Memory::OpenLog(const char* filename)
+{
+#ifdef _DEBUG
+ if (!filename || !strlen(filename))
+ filename = "memdbg.txt";
+
+ mem_log_file = CreateFile(filename, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
+
+ if (mem_log_file) {
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ _CrtSetReportFile(_CRT_WARN, mem_log_file);
+ _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ _CrtSetReportFile(_CRT_ERROR, mem_log_file);
+ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ _CrtSetReportFile(_CRT_ASSERT, mem_log_file);
+ }
+
+ _CrtMemCheckpoint(&mem_chk_p1);
+#endif
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Memory::CloseLog()
+{
+#ifdef _DEBUG
+ if (mem_log_file) {
+ CloseHandle(mem_log_file);
+ mem_log_file = 0;
+
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
+ _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT);
+ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT);
+ }
+#endif
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Memory::Check()
+{
+#ifdef _DEBUG
+ if (! _CrtCheckMemory()) {
+ _RPT0(_CRT_ERROR, "\n\nMemory Check Failed.\n");
+ heapdump();
+ Checkpoint();
+ _asm { int 3 }
+ exit(1111);
+ }
+#endif
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Memory::Checkpoint()
+{
+#ifdef _DEBUG
+ if (mem_chk_level < PERIODIC) return;
+
+ _RPT0(_CRT_WARN, "\n\nMemory Checkpoint:\n"
+ "--------------------------------------------------\n");
+
+ _CrtMemState s;
+ _CrtMemCheckpoint(&mem_chk_p2);
+ _CrtMemDifference(&s, &mem_chk_p1, &mem_chk_p2);
+ _CrtMemDumpStatistics(&s);
+
+ memcpy(&mem_chk_p1, &mem_chk_p2, sizeof(mem_chk_p1));
+#endif
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Memory::Stats()
+{
+#ifdef _DEBUG
+ if (mem_chk_level < PERIODIC) return;
+
+ _RPT0(_CRT_WARN, "\n\nMemory Stats:\n"
+ "--------------------------------------------------\n");
+
+ _CrtMemState s;
+ _CrtMemCheckpoint(&s);
+ _CrtMemDumpStatistics(&s);
+#endif
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Memory::DumpLeaks()
+{
+#ifdef _DEBUG
+ _RPT0(_CRT_WARN, "\n\nMemory Dump Leaks:\n"
+ "--------------------------------------------------\n");
+ _CrtDumpMemoryLeaks();
+#endif
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Memory::SetLevel(LEVEL l)
+{
+#ifdef _DEBUG
+ mem_chk_level = l;
+
+ _CrtSetDbgFlag(0);
+
+ switch (mem_chk_level) {
+ case MAXIMAL: CrtSetDebugField(_CRTDBG_CHECK_ALWAYS_DF);
+ case PERIODIC: CrtSetDebugField(_CRTDBG_DELAY_FREE_MEM_DF);
+ case LEAKS: CrtSetDebugField(_CRTDBG_LEAK_CHECK_DF);
+ case OFF:
+ default: break;
+ }
+#endif
+}
+
diff --git a/FoundationEx/MemDebug.h b/FoundationEx/MemDebug.h
new file mode 100644
index 0000000..fcfea5b
--- /dev/null
+++ b/FoundationEx/MemDebug.h
@@ -0,0 +1,91 @@
+/* Project FoundationEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: FoundationEx
+ FILE: MemDebug.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Memory Debugging class
+*/
+
+
+#ifndef MemDebug_h
+#define MemDebug_h
+
+// +--------------------------------------------------------------------+
+
+#ifdef WIN32
+#include <windows.h>
+#include <windowsx.h>
+#endif
+
+#ifdef FOUNDATION_USE_MFC
+
+#ifndef _DEBUG
+
+inline void* __cdecl operator new(unsigned int s, const char*, int) { return ::operator new(s); }
+inline void __cdecl operator delete(void* p, const char*, int) { ::operator delete(p); }
+
+#else
+
+void* __cdecl operator new(unsigned int s, const char*, int);
+void __cdecl operator delete(void* p, const char*, int);
+
+#endif
+
+#else
+
+//
+// MEMORY DEBUGGING NOT SUPPORTED UNDER MFC
+//
+
+// +--------------------------------------------------------------------+
+
+class Memory
+{
+public:
+ enum LEVEL { OFF, LEAKS, PERIODIC, MAXIMAL };
+
+ static void OpenLog(const char* filename=0);
+ static void CloseLog();
+
+ static void Check();
+ static void Checkpoint();
+ static void Stats();
+ static void DumpLeaks();
+
+ static void SetLevel(LEVEL l);
+};
+
+// +--------------------------------------------------------------------+
+
+#ifndef _DEBUG
+
+inline void* __cdecl operator new(unsigned int s, const char*, int) { return ::operator new(s); }
+inline void __cdecl operator delete(void* p, const char*, int) { ::operator delete(p); }
+
+#else
+/*_CRTIMP*/
+void* __cdecl operator new(unsigned int, int, const char*, int);
+
+inline void* __cdecl operator new(unsigned int s, const char* f, int l)
+ { return ::operator new(s, 1, f, l); }
+
+inline void* __cdecl operator new(unsigned int s)
+ { return ::operator new(s, 1, __FILE__, __LINE__); }
+
+inline void __cdecl operator delete(void* p, const char*, int)
+ { ::operator delete(p); }
+
+#endif _DEBUG
+
+// +--------------------------------------------------------------------+
+
+#endif FOUNDATION_USE_MFC
+
+#endif MemDebug_h
+
diff --git a/FoundationEx/Text.cpp b/FoundationEx/Text.cpp
new file mode 100644
index 0000000..5f832ea
--- /dev/null
+++ b/FoundationEx/Text.cpp
@@ -0,0 +1,751 @@
+/* Project FoundationEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: FoundationEx
+ FILE: text.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Implementation of the Text class
+*/
+
+#include "MemDebug.h"
+#include "Text.h"
+#include "stdio.h"
+#include <ctype.h>
+
+// +-------------------------------------------------------------------+
+// SPECIAL TEXT REP FOR NULL STRINGS
+// This is used only by the default constructor for the Text object,
+// to prevent extra rep allocation when constructing empty strings.
+
+TextRep TextRep::nullrep;
+
+TextRep::TextRep()
+ : ref(1234567), data(0), length(0), hash(0), sensitive(true)
+{
+#ifdef MEM_DEBUG
+ data = new(__FILE__,__LINE__) char[4];
+#else
+ data = new char[4];
+#endif
+
+ if (data)
+ ZeroMemory(data, 4);
+}
+
+// +-------------------------------------------------------------------+
+
+ThreadSync TextRep::sync;
+
+
+TextRep::TextRep(const char* s)
+ : ref(1), length(0), sensitive(true)
+{
+ if (s) length = ::strlen(s);
+
+#ifdef MEM_DEBUG
+ data = new(__FILE__,__LINE__) char[length+1];
+#else
+ data = new char[length+1];
+#endif
+
+ if (data) {
+ if (s) ::strcpy(data, s);
+ else data[length] = '\0';
+
+ dohash();
+ }
+}
+
+TextRep::TextRep(const char* s, int len)
+ : ref(1), length(len), sensitive(true)
+{
+ if (length < 0) length = 0;
+
+#ifdef MEM_DEBUG
+ data = new(__FILE__,__LINE__) char[length+1];
+#else
+ data = new char[length+1];
+#endif
+
+ if (data) {
+ ::CopyMemory(data, s, length);
+ data[length] = '\0';
+ dohash();
+ }
+}
+
+TextRep::TextRep(char c, int len)
+ : ref(1), length(len), sensitive(true)
+{
+ if (length < 0) length = 0;
+
+#ifdef MEM_DEBUG
+ data = new(__FILE__,__LINE__) char[length+1];
+#else
+ data = new char[length+1];
+#endif
+
+ if (data) {
+ ::FillMemory(data, length, c);
+ data[length] = '\0';
+ dohash();
+ }
+}
+
+TextRep::TextRep(const TextRep* rep)
+ : ref(1)
+{
+ length = rep->length;
+
+#ifdef MEM_DEBUG
+ data = new(__FILE__,__LINE__) char[length+1];
+#else
+ data = new char[length+1];
+#endif
+
+ hash = rep->hash;
+ sensitive = rep->sensitive;
+
+ if (data)
+ ::strcpy(data, rep->data);
+}
+
+TextRep::~TextRep()
+{
+ delete[] data;
+}
+
+void
+TextRep::addref()
+{
+ sync.acquire();
+ ref++;
+ sync.release();
+}
+
+long
+TextRep::deref()
+{
+ sync.acquire();
+ long r = --ref;
+ sync.release();
+ return r;
+}
+
+inline static void mash(unsigned& hash, unsigned chars)
+{
+ hash = (chars ^ ((hash << 5) | (hash >> (8*sizeof(unsigned) - 5))));
+}
+
+void
+TextRep::dohash()
+{
+ unsigned hv = (unsigned)length; // Mix in the string length.
+ unsigned i = length*sizeof(char)/sizeof(unsigned);
+ const unsigned* p = (const unsigned*)data;
+
+ while (i--)
+ mash(hv, *p++); // XOR in the characters.
+
+ // XOR in any remaining characters:
+ i = length*sizeof(char)%sizeof(unsigned);
+ if (i) {
+ unsigned h = 0;
+ const char* c = (const char*)p;
+ while (i--)
+ h = ((h << 8*sizeof(char)) | *c++);
+ mash(hv, h);
+ }
+
+ hash = hv;
+}
+
+// +-------------------------------------------------------------------+
+
+Text::Text()
+{
+ rep = &TextRep::nullrep;
+ rep->addref();
+ sym = rep->data;
+}
+
+Text::Text(char c)
+{
+ char buf[2]; buf[0] = c; buf[1] = '\0';
+
+#ifdef MEM_DEBUG
+ rep = new(__FILE__,__LINE__) TextRep(buf);
+#else
+ rep = new TextRep(buf);
+#endif
+
+ if (!rep) {
+ rep = &TextRep::nullrep;
+ rep->addref();
+ }
+
+ sym = rep->data;
+}
+
+Text::Text(const char* s)
+{
+#ifdef MEM_DEBUG
+ rep = new(__FILE__,__LINE__) TextRep(s);
+#else
+ rep = new TextRep(s);
+#endif
+
+ if (!rep) {
+ rep = &TextRep::nullrep;
+ rep->addref();
+ }
+
+ sym = rep->data;
+}
+
+Text::Text(const char* s, int len)
+{
+#ifdef MEM_DEBUG
+ rep = new(__FILE__,__LINE__) TextRep(s, len);
+#else
+ rep = new TextRep(s, len);
+#endif
+
+ if (!rep) {
+ rep = &TextRep::nullrep;
+ rep->addref();
+ }
+
+ sym = rep->data;
+}
+
+Text::Text(char c, int len)
+{
+#ifdef MEM_DEBUG
+ rep = new(__FILE__,__LINE__) TextRep(c, len);
+#else
+ rep = new TextRep(c, len);
+#endif
+
+ if (!rep) {
+ rep = &TextRep::nullrep;
+ rep->addref();
+ }
+
+ sym = rep->data;
+}
+
+Text::Text(const Text& s)
+{
+ rep = s.rep;
+ rep->addref();
+ sym = rep->data;
+}
+
+Text::~Text()
+{
+ if (rep->deref() == 0) delete rep;
+
+ rep = &TextRep::nullrep;
+ sym = rep->data;
+}
+
+Text&
+Text::operator=(const char* s)
+{
+ if (rep->deref() == 0) delete rep;
+#ifdef MEM_DEBUG
+ rep = new(__FILE__,__LINE__) TextRep(s);
+#else
+ rep = new TextRep(s);
+#endif
+
+ if (!rep)
+ rep = &TextRep::nullrep;
+ sym = rep->data;
+ return *this;
+}
+
+Text&
+Text::operator=(const Text& s)
+{
+ s.rep->addref();
+ if (rep->deref() == 0) delete rep;
+ rep = s.rep;
+ sym = rep->data;
+ return *this;
+}
+
+Text
+Text::operator+(char c)
+{
+#ifdef MEM_DEBUG
+ char* buf = new(__FILE__,__LINE__) char[rep->length + 2];
+#else
+ char* buf = new char[rep->length + 2];
+#endif
+
+ if (buf) {
+ ::strcpy(buf, sym);
+ buf[rep->length] = c;
+ buf[rep->length+1] = '\0';
+ Text retval(buf);
+ delete [] buf;
+ return retval;
+ }
+
+ else {
+ return *this;
+ }
+}
+
+Text
+Text::operator+(const char* s)
+{
+#ifdef MEM_DEBUG
+ char* buf = new(__FILE__,__LINE__) char[::strlen(s) + rep->length + 1];
+#else
+ char* buf = new char[::strlen(s) + rep->length + 1];
+#endif
+
+ if (buf) {
+ ::strcpy(buf, sym);
+ ::strcat(buf, s);
+ Text retval(buf);
+ delete [] buf;
+ return retval;
+ }
+
+ else {
+ return *this;
+ }
+}
+
+Text
+Text::operator+(const Text& s)
+{
+#ifdef MEM_DEBUG
+ char* buf = new(__FILE__,__LINE__) char[s.rep->length + rep->length + 1];
+#else
+ char* buf = new char[s.rep->length + rep->length + 1];
+#endif
+
+ if (buf) {
+ ::strcpy(buf, sym);
+ ::strcat(buf, s.sym);
+ Text retval(buf);
+ delete [] buf;
+ return retval;
+ }
+
+ else {
+ return *this;
+ }
+}
+
+bool
+Text::isSensitive() const
+{
+ return rep->sensitive;
+}
+
+void
+Text::setSensitive(bool s)
+{
+ rep->sensitive = s;
+}
+
+Text&
+Text::append(char c)
+{
+#ifdef MEM_DEBUG
+ char* buf = new(__FILE__,__LINE__) char[rep->length + 2];
+#else
+ char* buf = new char[rep->length + 2];
+#endif
+
+ if (buf) {
+ ::strcpy(buf, sym);
+ buf[rep->length] = c;
+ buf[rep->length+1] = '\0';
+ if (rep->deref() == 0) delete rep;
+
+#ifdef MEM_DEBUG
+ rep = new(__FILE__,__LINE__) TextRep(buf);
+#else
+ rep = new TextRep(buf);
+#endif
+
+ if (!rep)
+ rep = &TextRep::nullrep;
+
+ sym = rep->data;
+ delete [] buf;
+ }
+
+ return *this;
+}
+
+Text&
+Text::append(const char* s)
+{
+#ifdef MEM_DEBUG
+ char* buf = new(__FILE__,__LINE__) char[::strlen(s) + rep->length + 1];
+#else
+ char* buf = new char[::strlen(s) + rep->length + 1];
+#endif
+
+ if (buf) {
+ ::strcpy(buf, sym);
+ ::strcat(buf, s);
+ if (rep->deref() == 0) delete rep;
+
+#ifdef MEM_DEBUG
+ rep = new(__FILE__,__LINE__) TextRep(buf);
+#else
+ rep = new TextRep(buf);
+#endif
+
+ if (!rep)
+ rep = &TextRep::nullrep;
+
+ sym = rep->data;
+ delete [] buf;
+ }
+
+ return *this;
+}
+
+Text&
+Text::append(const Text& s)
+{
+#ifdef MEM_DEBUG
+ char* buf = new(__FILE__,__LINE__) char[s.rep->length + rep->length + 1];
+#else
+ char* buf = new char[s.rep->length + rep->length + 1];
+#endif
+
+ if (buf) {
+ int lenA = rep->length;
+ int lenB = s.rep->length;
+
+ CopyMemory(buf, sym, lenA);
+ CopyMemory(buf + lenA, s.sym, lenB);
+ buf[lenA + lenB] = 0;
+
+ if (rep->deref() == 0) delete rep;
+
+#ifdef MEM_DEBUG
+ rep = new(__FILE__,__LINE__) TextRep(buf, lenA + lenB);
+#else
+ rep = new TextRep(buf);
+#endif
+
+ if (!rep)
+ rep = &TextRep::nullrep;
+
+ sym = rep->data;
+ delete [] buf;
+ }
+
+ return *this;
+}
+
+void
+Text::clone()
+{
+ if (rep->ref > 1) {
+ rep->deref();
+
+#ifdef MEM_DEBUG
+ TextRep* t = new(__FILE__,__LINE__) TextRep(rep);
+#else
+ TextRep* t = new TextRep(rep);
+#endif
+
+ rep = t;
+
+ if (!rep)
+ rep = &TextRep::nullrep;
+
+ sym = rep->data;
+ }
+}
+
+char
+Text::operator[](int index) const
+{
+ if (index < (int) rep->length)
+ return sym[index];
+ else
+ throw "BOUNDS ERROR";
+
+ return '\0';
+}
+
+char
+Text::operator()(int index) const
+{
+ return sym[index];
+}
+
+char&
+Text::operator[](int index)
+{
+ if (index < (int) rep->length) {
+ clone();
+ return (char&) sym[index];
+ }
+ else
+ throw "BOUNDS ERROR";
+
+ return (char&) sym[0];
+}
+
+char&
+Text::operator()(int index)
+{
+ clone();
+ return (char&) sym[index];
+}
+
+Text
+Text::operator()(int start, int len) const
+{
+ if (start > rep->length || len <= 0)
+ return Text();
+
+ if (start + len > rep->length)
+ len = rep->length - start;
+
+#ifdef MEM_DEBUG
+ char* buf = new(__FILE__,__LINE__) char[len+1];
+#else
+ char* buf = new char[len+1];
+#endif
+
+ if (buf) {
+ ::strncpy(buf, sym+start, len);
+ buf[len] = '\0';
+
+ Text retval(buf);
+ delete [] buf;
+ return retval;
+ }
+
+ return Text();
+}
+
+bool
+Text::contains(char c) const
+{
+ if (rep->length > 0) {
+ if (!rep->sensitive) {
+ char alt = c;
+ if (islower(alt)) alt = toupper(alt);
+ else if (isupper(alt)) alt = tolower(alt);
+
+ if (strchr(rep->data, alt) != 0)
+ return true;
+ }
+
+ if (strchr(rep->data, c) != 0)
+ return true;
+ }
+
+ return false;
+}
+
+bool
+Text::contains(const char* pattern) const
+{
+ if (rep->length > 0 && pattern && *pattern) {
+ if (rep->sensitive) {
+ if (strstr(rep->data, pattern) != 0)
+ return true;
+ }
+ else {
+ Text smash1(*this);
+ smash1.toLower();
+ Text smash2(pattern);
+ smash2.toLower();
+
+ if (strstr(smash1.data(), smash2.data()) != 0)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+Text::containsAnyOf(const char* charSet) const
+{
+ if (rep->length > 0 && charSet && *charSet) {
+ if (rep->sensitive) {
+ if (strpbrk(rep->data, charSet) != 0)
+ return true;
+ }
+ else {
+ Text smash1(*this);
+ smash1.toLower();
+ Text smash2(charSet);
+ smash2.toLower();
+
+ if (strpbrk(smash1.data(), smash2.data()) != 0)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int
+Text::indexOf(char c) const
+{
+ if (rep->length > 0) {
+ if (!rep->sensitive) {
+ char alt = c;
+ if (islower(alt)) alt = toupper(alt);
+ else if (isupper(alt)) alt = tolower(alt);
+
+ const char* p = strchr(rep->data, alt);
+
+ if (p)
+ return (p - rep->data);
+ }
+
+ const char* p = strchr(rep->data, c);
+
+ if (p)
+ return (p - rep->data);
+ }
+
+ return -1;
+}
+
+int
+Text::indexOf(const char* pattern) const
+{
+ if (rep->length > 0 && pattern && *pattern) {
+ if (rep->sensitive) {
+ const char* p = strstr(rep->data, pattern);
+ if (p) return (p - rep->data);
+ }
+ else {
+ Text smash1(*this);
+ smash1.toLower();
+ Text smash2(pattern);
+ smash2.toLower();
+
+ const char* p = strstr(smash1.data(), smash2.data());
+ if (p) return (p - smash1.data());
+ }
+ }
+
+ return -1;
+}
+
+void
+Text::toLower()
+{
+ clone();
+ size_t n = rep->length;
+ char* p = (char*) sym;
+ while (n--) {
+ *p = tolower((unsigned char)*p);
+ p++;
+ }
+
+ rep->dohash();
+}
+
+void
+Text::toUpper()
+{
+ clone();
+ size_t n = rep->length;
+ char* p = (char*) sym;
+ while ( n-- ) {
+ *p = toupper((unsigned char)*p);
+ p++;
+ }
+
+ rep->dohash();
+}
+
+Text
+Text::substring(int start, int length)
+{
+ Text result;
+
+ if (start >= 0 && start < (int) rep->length && length > 0) {
+ if (start + length > (int) rep->length)
+ length = (int) rep->length - start;
+
+ const char* s = sym + start;
+
+#ifdef MEM_DEBUG
+ result.rep = new(__FILE__,__LINE__) TextRep(s, length);
+#else
+ result.rep = new TextRep(s, length);
+#endif
+
+ if (!result.rep)
+ result.rep = &TextRep::nullrep;
+
+ result.sym = result.rep->data;
+ }
+
+ return result;
+}
+
+Text
+Text::trim()
+{
+ Text result;
+
+ if (rep->length) {
+ const char* p = sym;
+ const char* q = sym + rep->length-1;
+
+ while (p && *p && isspace(*p)) p++;
+ while (q>p && *q && isspace(*q)) q--;
+
+ result = substring(p-sym, q-p+1);
+ }
+
+ return result;
+}
+
+Text
+Text::replace(const char* pattern, const char* substitution)
+{
+ Text result;
+
+ if (rep->length && pattern && *pattern) {
+ int index = 0;
+ int skip = strlen(pattern);
+ do {
+ const char* p = strstr(rep->data + index, pattern);
+ if (p) {
+ int len = (p - rep->data + index);
+ result.append(substring(index, len));
+ result.append(substitution);
+ index += len + skip;
+ }
+ else if (index < rep->length) {
+ result.append(substring(index, rep->length-index));
+ index = -1;
+ }
+ }
+ while (index >= 0 && index < rep->length);
+ }
+
+ return result;
+}
diff --git a/FoundationEx/Text.h b/FoundationEx/Text.h
new file mode 100644
index 0000000..cb037ef
--- /dev/null
+++ b/FoundationEx/Text.h
@@ -0,0 +1,193 @@
+/* Project FoundationEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: FoundationEx
+ FILE: Text.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Declaration of the Text class
+*/
+
+#ifndef Text_h
+#define Text_h
+
+#include <string.h>
+#include <windows.h>
+#include "ThreadSync.h"
+
+// +-------------------------------------------------------------------+
+
+class TextRep
+{
+ friend class Text;
+
+public:
+ TextRep();
+ ~TextRep();
+
+private:
+ TextRep(const char* s);
+ TextRep(const char* s, int len);
+ TextRep(char c, int len);
+ TextRep(const TextRep* rep);
+
+ void addref();
+ long deref();
+
+ void dohash();
+
+ char* data;
+ long ref;
+ int length;
+ unsigned hash;
+ bool sensitive;
+
+ static ThreadSync sync;
+ static TextRep nullrep;
+};
+
+// +-------------------------------------------------------------------+
+
+class Text
+{
+public:
+ static const char* TYPENAME() { return "Text"; }
+
+ Text();
+ Text(char c);
+ Text(const char* s);
+ Text(const char* s, int len);
+ Text(char c, int len);
+ Text(const Text& s);
+ ~Text();
+
+ // case sensitivity
+ bool isSensitive() const;
+ void setSensitive(bool s);
+
+ // comparison
+ int compare(const char* s) const;
+ int compare(const Text& s) const;
+
+ // assignment
+ Text& operator=(const char* s);
+ Text& operator=(const Text& s);
+
+ // catenation
+ Text& append(char c);
+ Text& append(const char* s);
+ Text& append(const Text& s);
+
+ Text operator+(char c);
+ Text operator+(const char* s);
+ Text operator+(const Text& s);
+
+ Text& operator+=(char c) { return append(c); }
+ Text& operator+=(const char* s) { return append(s); }
+ Text& operator+=(const Text& s) { return append(s); }
+
+ // indexing
+ char operator[](int index) const;
+ char operator()(int index) const;
+ char& operator[](int index);
+ char& operator()(int index);
+
+ Text operator()(int start, int len) const;
+
+ // access
+ int length() const { return rep->length; }
+ unsigned hash() const { return rep->hash; }
+
+ const char* data() const { return sym; }
+ operator const char* () const { return sym; }
+
+ bool contains(char c) const;
+ bool contains(const char* s) const;
+
+ bool containsAnyOf(const char* charSet) const;
+
+ int indexOf(char c) const;
+ int indexOf(const char* s) const;
+
+ // mutation
+ void toLower();
+ void toUpper();
+
+ // substring
+ Text substring(int start, int length);
+ Text trim();
+ Text replace(const char* pattern, const char* substitution);
+
+private:
+ void clone();
+
+ const char* sym;
+ TextRep* rep;
+};
+
+// +-------------------------------------------------------------------+
+
+inline int Text::compare(const char* s) const
+{
+ if (rep->sensitive)
+ return strcmp(sym, s);
+ else
+ return stricmp(sym, s);
+}
+
+inline int Text::compare(const Text& s) const
+{
+ if (rep->sensitive && s.rep->sensitive)
+ return strcmp(sym, s.sym);
+ else
+ return stricmp(sym, s.sym);
+}
+
+// +-------------------------------------------------------------------+
+
+inline int operator==(const Text& l, const Text& r) {
+ return (l.length() == r.length()) && (l.compare(r) == 0); }
+inline int operator!=(const Text& l, const Text& r) { return l.compare(r) != 0; }
+inline int operator< (const Text& l, const Text& r) { return l.compare(r) < 0; }
+inline int operator<=(const Text& l, const Text& r) { return l.compare(r) <= 0; }
+inline int operator> (const Text& l, const Text& r) { return l.compare(r) > 0; }
+inline int operator>=(const Text& l, const Text& r) { return l.compare(r) >= 0; }
+
+inline int operator==(const char* l, const Text& r) { return r.compare(l) == 0; }
+inline int operator!=(const char* l, const Text& r) { return r.compare(l) != 0; }
+inline int operator< (const char* l, const Text& r) { return r.compare(l) < 0; }
+inline int operator<=(const char* l, const Text& r) { return r.compare(l) <= 0; }
+inline int operator> (const char* l, const Text& r) { return r.compare(l) > 0; }
+inline int operator>=(const char* l, const Text& r) { return r.compare(l) >= 0; }
+
+inline int operator==( char* l, const Text& r) { return r.compare(l) == 0; }
+inline int operator!=( char* l, const Text& r) { return r.compare(l) != 0; }
+inline int operator< ( char* l, const Text& r) { return r.compare(l) < 0; }
+inline int operator<=( char* l, const Text& r) { return r.compare(l) <= 0; }
+inline int operator> ( char* l, const Text& r) { return r.compare(l) > 0; }
+inline int operator>=( char* l, const Text& r) { return r.compare(l) >= 0; }
+
+inline int operator==(const Text& l, const char* r) { return l.compare(r) == 0; }
+inline int operator!=(const Text& l, const char* r) { return l.compare(r) != 0; }
+inline int operator< (const Text& l, const char* r) { return l.compare(r) < 0; }
+inline int operator<=(const Text& l, const char* r) { return l.compare(r) <= 0; }
+inline int operator> (const Text& l, const char* r) { return l.compare(r) > 0; }
+inline int operator>=(const Text& l, const char* r) { return l.compare(r) >= 0; }
+
+inline int operator==(const Text& l, char* r) { return l.compare(r) == 0; }
+inline int operator!=(const Text& l, char* r) { return l.compare(r) != 0; }
+inline int operator< (const Text& l, char* r) { return l.compare(r) < 0; }
+inline int operator<=(const Text& l, char* r) { return l.compare(r) <= 0; }
+inline int operator> (const Text& l, char* r) { return l.compare(r) > 0; }
+inline int operator>=(const Text& l, char* r) { return l.compare(r) >= 0; }
+
+inline Text operator+(const char* l, const Text& r) { return Text(l) + r; }
+inline Text operator+( char* l, const Text& r) { return Text(l) + r; }
+
+// +-------------------------------------------------------------------+
+
+#endif Text_h
diff --git a/FoundationEx/ThreadSync.h b/FoundationEx/ThreadSync.h
new file mode 100644
index 0000000..274bb7f
--- /dev/null
+++ b/FoundationEx/ThreadSync.h
@@ -0,0 +1,57 @@
+/* Project nGen
+ John DiCamillo
+ Copyright © 1997-2001. All Rights Reserved.
+
+ SUBSYSTEM: foundation
+ FILE: ThreadSync.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Declaration of the ThreadSync class
+*/
+
+#ifndef ThreadSync_h
+#define ThreadSync_h
+
+#include <windows.h>
+
+// +-------------------------------------------------------------------+
+
+class ThreadSync
+{
+#if defined(_MT) // MULTITHREADED: WITH SYNC ------------
+ CRITICAL_SECTION sync;
+
+public:
+ ThreadSync() { ::InitializeCriticalSection(&sync); }
+ ~ThreadSync() { ::DeleteCriticalSection(&sync); }
+
+ void acquire() { ::EnterCriticalSection(&sync); }
+ void release() { ::LeaveCriticalSection(&sync); }
+
+#else // SINGLE THREADED: NO SYNC ------------
+
+public:
+ ThreadSync() { }
+ ~ThreadSync() { }
+
+ void acquire() { }
+ void release() { }
+
+#endif
+};
+
+// +-------------------------------------------------------------------+
+
+class AutoThreadSync
+{
+public:
+ AutoThreadSync(ThreadSync& s) : sync(s) { sync.acquire(); }
+ ~AutoThreadSync() { sync.release(); }
+private:
+ ThreadSync& sync;
+};
+
+#endif ThreadSync_h
diff --git a/Magic2/AlphaInverse.cpp b/Magic2/AlphaInverse.cpp
new file mode 100644
index 0000000..464bfda
--- /dev/null
+++ b/Magic2/AlphaInverse.cpp
@@ -0,0 +1,4100 @@
+#include "stdafx.h"
+
+BYTE inverse_palette[32768] = {
+ 0, 47, 63, 63, 62, 62, 61, 61,
+ 61, 60, 60, 59, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 252, 252, 252, 252, 252, 252, 252, 252,
+ 47, 47, 63, 63, 62, 62, 61, 61,
+ 61, 60, 60, 59, 59, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 55, 252, 252, 252, 252, 252, 252, 252,
+ 47, 46, 63, 63, 62, 62, 61, 61,
+ 60, 60, 60, 59, 59, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 55,
+ 55, 252, 252, 252, 252, 252, 252, 252,
+ 46, 46, 46, 62, 62, 62, 61, 61,
+ 60, 60, 59, 59, 59, 58, 4, 4,
+ 4, 4, 4, 4, 4, 4, 55, 55,
+ 55, 54, 252, 252, 252, 252, 252, 252,
+ 46, 46, 45, 111, 111, 79, 79, 61,
+ 60, 60, 59, 59, 59, 58, 58, 57,
+ 4, 4, 4, 4, 56, 55, 55, 55,
+ 54, 54, 54, 252, 252, 252, 252, 252,
+ 111, 111, 111, 111, 111, 111, 79, 79,
+ 60, 60, 59, 59, 58, 58, 58, 57,
+ 57, 56, 56, 56, 56, 55, 55, 55,
+ 54, 54, 54, 252, 252, 252, 252, 252,
+ 111, 111, 111, 111, 111, 111, 111, 78,
+ 78, 78, 59, 59, 58, 58, 57, 57,
+ 57, 56, 56, 56, 55, 55, 55, 55,
+ 54, 54, 54, 54, 252, 252, 252, 252,
+ 111, 111, 111, 111, 111, 111, 110, 78,
+ 78, 77, 77, 77, 58, 58, 57, 57,
+ 57, 56, 56, 56, 55, 55, 55, 55,
+ 54, 54, 54, 54, 54, 252, 252, 252,
+ 111, 111, 111, 111, 110, 110, 110, 110,
+ 78, 77, 213, 213, 213, 213, 213, 213,
+ 56, 56, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 252, 252, 252,
+ 111, 110, 110, 110, 110, 110, 110, 109,
+ 109, 213, 213, 213, 213, 213, 213, 213,
+ 213, 213, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 252, 252,
+ 110, 110, 110, 110, 110, 109, 109, 109,
+ 109, 213, 213, 213, 213, 213, 213, 213,
+ 213, 213, 212, 6, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 53, 252,
+ 2, 110, 110, 110, 109, 109, 109, 109,
+ 213, 213, 213, 213, 213, 213, 213, 213,
+ 213, 212, 6, 6, 6, 6, 6, 54,
+ 54, 54, 54, 54, 54, 53, 53, 53,
+ 2, 2, 2, 109, 109, 109, 109, 108,
+ 213, 213, 213, 213, 213, 213, 213, 213,
+ 212, 6, 6, 6, 6, 6, 6, 6,
+ 54, 54, 54, 54, 53, 53, 53, 53,
+ 2, 2, 2, 2, 108, 108, 108, 108,
+ 213, 213, 213, 213, 213, 213, 213, 212,
+ 6, 6, 6, 6, 6, 6, 6, 211,
+ 211, 211, 70, 53, 53, 53, 53, 53,
+ 2, 2, 2, 2, 2, 108, 108, 108,
+ 213, 213, 213, 213, 213, 213, 212, 6,
+ 6, 6, 6, 6, 6, 6, 211, 211,
+ 211, 211, 211, 70, 70, 70, 53, 53,
+ 2, 2, 2, 2, 2, 2, 107, 107,
+ 107, 213, 213, 213, 213, 212, 6, 6,
+ 6, 6, 6, 6, 6, 211, 211, 211,
+ 211, 211, 211, 210, 210, 70, 70, 70,
+ 2, 2, 2, 2, 2, 2, 107, 107,
+ 107, 213, 213, 213, 212, 6, 6, 6,
+ 6, 6, 6, 6, 211, 211, 211, 211,
+ 211, 211, 210, 210, 210, 210, 210, 210,
+ 2, 2, 2, 2, 2, 2, 107, 107,
+ 106, 106, 213, 212, 6, 6, 6, 6,
+ 6, 6, 6, 211, 211, 211, 211, 211,
+ 211, 210, 210, 210, 210, 210, 210, 209,
+ 2, 2, 2, 2, 2, 2, 106, 106,
+ 106, 106, 106, 6, 6, 6, 6, 6,
+ 6, 6, 211, 211, 211, 211, 211, 211,
+ 210, 210, 210, 210, 210, 210, 209, 209,
+ 2, 2, 2, 2, 2, 2, 105, 105,
+ 105, 105, 105, 6, 6, 6, 6, 6,
+ 6, 211, 211, 211, 211, 211, 211, 210,
+ 210, 210, 210, 210, 210, 209, 209, 209,
+ 2, 2, 2, 2, 2, 2, 105, 105,
+ 105, 105, 105, 6, 6, 6, 6, 6,
+ 211, 211, 211, 211, 211, 211, 210, 210,
+ 210, 210, 210, 210, 209, 209, 209, 209,
+ 2, 2, 2, 2, 2, 2, 105, 105,
+ 104, 104, 104, 6, 6, 6, 6, 211,
+ 211, 211, 211, 211, 211, 210, 210, 210,
+ 210, 210, 210, 209, 209, 209, 209, 209,
+ 2, 2, 2, 2, 2, 104, 104, 104,
+ 104, 104, 104, 103, 6, 6, 211, 211,
+ 211, 211, 211, 211, 210, 210, 210, 210,
+ 210, 210, 209, 209, 209, 209, 209, 209,
+ 2, 2, 2, 2, 2, 104, 104, 104,
+ 104, 104, 103, 103, 103, 211, 211, 211,
+ 211, 211, 211, 210, 210, 210, 210, 210,
+ 210, 209, 209, 209, 209, 209, 209, 209,
+ 250, 250, 250, 250, 250, 104, 104, 104,
+ 103, 103, 103, 103, 103, 211, 211, 211,
+ 211, 211, 210, 210, 210, 210, 210, 210,
+ 209, 209, 209, 209, 209, 209, 209, 208,
+ 250, 250, 250, 250, 250, 250, 102, 102,
+ 103, 103, 103, 103, 103, 103, 211, 211,
+ 211, 210, 210, 210, 210, 210, 210, 209,
+ 209, 209, 209, 209, 209, 209, 208, 208,
+ 250, 250, 250, 250, 250, 250, 250, 102,
+ 102, 102, 103, 103, 103, 103, 211, 211,
+ 210, 210, 210, 210, 210, 210, 209, 209,
+ 209, 209, 209, 209, 209, 208, 208, 208,
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 102, 102, 102, 103, 103, 103, 211, 210,
+ 210, 210, 210, 210, 210, 209, 209, 209,
+ 209, 209, 209, 209, 208, 208, 208, 254,
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 250, 102, 102, 102, 103, 103, 103, 210,
+ 210, 210, 210, 210, 209, 209, 209, 209,
+ 209, 209, 209, 208, 208, 208, 254, 254,
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 250, 250, 102, 102, 102, 103, 103, 210,
+ 210, 210, 210, 209, 209, 209, 209, 209,
+ 209, 209, 208, 208, 208, 254, 254, 254,
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 250, 250, 250, 102, 102, 102, 102, 210,
+ 210, 210, 209, 209, 209, 209, 209, 209,
+ 209, 208, 208, 208, 254, 254, 254, 254,
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 250, 250, 250, 250, 102, 102, 102, 210,
+ 210, 209, 209, 209, 209, 209, 209, 209,
+ 208, 208, 208, 254, 254, 254, 254, 254,
+ 47, 47, 63, 63, 62, 62, 61, 61,
+ 61, 60, 60, 59, 59, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 55, 252, 252, 252, 252, 252, 252, 252,
+ 47, 46, 63, 63, 62, 62, 61, 61,
+ 60, 60, 60, 59, 59, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 55,
+ 55, 252, 252, 252, 252, 252, 252, 252,
+ 46, 46, 46, 62, 62, 62, 61, 61,
+ 60, 60, 59, 59, 59, 58, 4, 4,
+ 4, 4, 4, 4, 4, 4, 55, 55,
+ 55, 54, 252, 252, 252, 252, 252, 252,
+ 46, 46, 45, 45, 62, 79, 61, 61,
+ 60, 60, 59, 59, 59, 58, 58, 57,
+ 4, 4, 4, 4, 56, 55, 55, 55,
+ 54, 54, 54, 252, 252, 252, 252, 252,
+ 46, 45, 45, 45, 79, 79, 79, 79,
+ 60, 60, 59, 59, 58, 58, 58, 57,
+ 57, 56, 56, 56, 56, 55, 55, 55,
+ 54, 54, 54, 252, 252, 252, 252, 252,
+ 111, 111, 111, 111, 111, 79, 79, 79,
+ 78, 60, 59, 59, 58, 58, 57, 57,
+ 57, 56, 56, 56, 55, 55, 55, 55,
+ 54, 54, 54, 54, 252, 252, 252, 252,
+ 111, 111, 111, 111, 111, 111, 79, 78,
+ 78, 78, 77, 59, 58, 58, 57, 57,
+ 57, 56, 56, 56, 55, 55, 55, 55,
+ 54, 54, 54, 54, 54, 252, 252, 252,
+ 111, 111, 111, 111, 111, 110, 110, 78,
+ 78, 77, 77, 77, 58, 58, 57, 57,
+ 56, 56, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 252, 252, 252,
+ 111, 111, 111, 110, 110, 110, 110, 110,
+ 77, 77, 77, 213, 213, 213, 213, 213,
+ 56, 56, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 252, 252,
+ 110, 110, 110, 110, 110, 110, 109, 109,
+ 109, 213, 213, 213, 213, 213, 213, 213,
+ 213, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 53, 252,
+ 110, 110, 110, 110, 110, 109, 109, 109,
+ 109, 213, 213, 213, 213, 213, 213, 213,
+ 213, 213, 212, 73, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 53, 53, 53,
+ 2, 110, 110, 109, 109, 109, 109, 109,
+ 108, 213, 213, 213, 213, 213, 213, 213,
+ 213, 212, 212, 212, 6, 55, 55, 54,
+ 54, 54, 54, 54, 53, 53, 53, 53,
+ 2, 2, 2, 109, 109, 109, 108, 108,
+ 108, 213, 213, 213, 213, 213, 213, 213,
+ 212, 212, 212, 6, 6, 6, 6, 54,
+ 54, 54, 54, 53, 53, 53, 53, 53,
+ 2, 2, 2, 2, 108, 108, 108, 108,
+ 108, 213, 213, 213, 213, 213, 213, 212,
+ 212, 212, 6, 6, 6, 6, 211, 211,
+ 211, 70, 70, 53, 53, 53, 53, 53,
+ 2, 2, 2, 2, 108, 108, 108, 107,
+ 107, 213, 213, 213, 213, 213, 212, 212,
+ 212, 6, 6, 6, 6, 211, 211, 211,
+ 211, 211, 70, 70, 70, 70, 53, 53,
+ 2, 2, 2, 2, 2, 107, 107, 107,
+ 107, 107, 213, 213, 213, 212, 212, 212,
+ 6, 6, 6, 6, 211, 211, 211, 211,
+ 211, 211, 70, 70, 70, 70, 70, 70,
+ 2, 2, 2, 2, 2, 107, 107, 107,
+ 107, 106, 213, 213, 212, 212, 212, 6,
+ 6, 6, 6, 211, 211, 211, 211, 211,
+ 211, 211, 210, 210, 210, 210, 70, 70,
+ 2, 2, 2, 2, 2, 107, 107, 106,
+ 106, 106, 106, 212, 212, 212, 6, 6,
+ 6, 6, 211, 211, 211, 211, 211, 211,
+ 211, 210, 210, 210, 210, 210, 210, 209,
+ 2, 2, 2, 2, 2, 2, 106, 106,
+ 106, 106, 106, 212, 212, 6, 6, 6,
+ 6, 211, 211, 211, 211, 211, 211, 211,
+ 210, 210, 210, 210, 210, 210, 209, 209,
+ 2, 2, 2, 2, 2, 105, 105, 105,
+ 105, 105, 105, 212, 6, 6, 6, 6,
+ 211, 211, 211, 211, 211, 211, 211, 210,
+ 210, 210, 210, 210, 210, 209, 209, 209,
+ 2, 2, 2, 2, 2, 105, 105, 105,
+ 105, 105, 105, 104, 6, 6, 6, 211,
+ 211, 211, 211, 211, 211, 211, 210, 210,
+ 210, 210, 210, 210, 209, 209, 209, 209,
+ 2, 2, 2, 2, 2, 105, 105, 104,
+ 104, 104, 104, 104, 6, 6, 211, 211,
+ 211, 211, 211, 211, 211, 210, 210, 210,
+ 210, 210, 210, 209, 209, 209, 209, 209,
+ 2, 2, 2, 2, 2, 104, 104, 104,
+ 104, 104, 104, 103, 103, 211, 211, 211,
+ 211, 211, 211, 211, 210, 210, 210, 210,
+ 210, 210, 209, 209, 209, 209, 209, 209,
+ 2, 2, 2, 2, 104, 104, 104, 104,
+ 104, 103, 103, 103, 103, 103, 211, 211,
+ 211, 211, 211, 210, 210, 210, 210, 210,
+ 210, 209, 209, 209, 209, 209, 209, 208,
+ 250, 250, 250, 250, 102, 102, 102, 102,
+ 103, 103, 103, 103, 103, 103, 211, 211,
+ 211, 211, 210, 210, 210, 210, 210, 210,
+ 209, 209, 209, 209, 209, 209, 208, 208,
+ 250, 250, 250, 250, 250, 102, 102, 102,
+ 102, 103, 103, 103, 103, 103, 211, 211,
+ 211, 210, 210, 210, 210, 210, 210, 209,
+ 209, 209, 209, 209, 209, 208, 208, 208,
+ 250, 250, 250, 250, 250, 250, 102, 102,
+ 102, 102, 103, 103, 103, 103, 103, 211,
+ 210, 210, 210, 210, 210, 210, 209, 209,
+ 209, 209, 209, 209, 208, 208, 208, 208,
+ 250, 250, 250, 250, 250, 250, 250, 102,
+ 102, 102, 102, 103, 103, 103, 103, 210,
+ 210, 210, 210, 210, 210, 209, 209, 209,
+ 209, 209, 209, 208, 208, 208, 208, 208,
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 102, 102, 102, 102, 102, 103, 103, 210,
+ 210, 210, 210, 210, 209, 209, 209, 209,
+ 209, 209, 208, 208, 208, 208, 208, 254,
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 250, 102, 102, 102, 102, 102, 103, 210,
+ 210, 210, 210, 209, 209, 209, 209, 209,
+ 209, 208, 208, 208, 208, 208, 254, 254,
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 250, 250, 102, 102, 102, 102, 102, 210,
+ 210, 210, 209, 209, 209, 209, 209, 209,
+ 208, 208, 208, 208, 208, 254, 254, 254,
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 250, 250, 250, 102, 102, 102, 102, 210,
+ 210, 209, 209, 209, 209, 209, 209, 208,
+ 208, 208, 208, 208, 254, 254, 254, 254,
+ 47, 46, 63, 63, 62, 62, 61, 61,
+ 60, 60, 60, 59, 59, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 55,
+ 55, 252, 252, 252, 252, 252, 252, 252,
+ 46, 46, 46, 62, 62, 62, 61, 61,
+ 60, 60, 59, 59, 59, 58, 4, 4,
+ 4, 4, 4, 4, 4, 4, 55, 55,
+ 55, 54, 252, 252, 252, 252, 252, 252,
+ 46, 46, 45, 45, 62, 62, 61, 61,
+ 60, 60, 59, 59, 59, 58, 58, 57,
+ 4, 4, 4, 4, 56, 55, 55, 55,
+ 54, 54, 54, 252, 252, 252, 252, 252,
+ 46, 45, 45, 45, 45, 79, 79, 61,
+ 60, 60, 59, 59, 58, 58, 58, 57,
+ 57, 56, 56, 56, 56, 55, 55, 55,
+ 54, 54, 54, 252, 252, 252, 252, 252,
+ 45, 45, 45, 45, 79, 79, 79, 79,
+ 60, 60, 59, 59, 58, 58, 57, 57,
+ 57, 56, 56, 56, 55, 55, 55, 55,
+ 54, 54, 54, 54, 252, 252, 252, 252,
+ 111, 111, 111, 111, 111, 79, 79, 78,
+ 78, 78, 59, 59, 58, 58, 57, 57,
+ 57, 56, 56, 56, 55, 55, 55, 55,
+ 54, 54, 54, 54, 54, 252, 252, 252,
+ 111, 111, 111, 111, 111, 111, 78, 78,
+ 78, 77, 77, 77, 58, 58, 57, 57,
+ 56, 56, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 252, 252, 252,
+ 111, 111, 111, 111, 111, 110, 110, 78,
+ 78, 77, 77, 77, 76, 58, 57, 57,
+ 56, 56, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 252, 252,
+ 111, 111, 110, 110, 110, 110, 110, 109,
+ 77, 77, 77, 76, 76, 213, 75, 57,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 53, 252,
+ 110, 110, 110, 110, 110, 110, 109, 109,
+ 109, 77, 213, 213, 213, 213, 213, 213,
+ 75, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 53, 53, 53,
+ 110, 110, 110, 110, 109, 109, 109, 109,
+ 109, 213, 213, 213, 213, 213, 213, 213,
+ 213, 74, 73, 73, 55, 55, 55, 54,
+ 54, 54, 54, 54, 53, 53, 53, 53,
+ 110, 110, 110, 109, 109, 109, 109, 109,
+ 108, 213, 213, 213, 213, 213, 213, 213,
+ 212, 212, 212, 73, 73, 73, 54, 54,
+ 54, 54, 54, 53, 53, 53, 53, 53,
+ 2, 2, 109, 109, 109, 108, 108, 108,
+ 108, 213, 213, 213, 213, 213, 213, 212,
+ 212, 212, 212, 212, 73, 72, 72, 54,
+ 54, 54, 53, 53, 53, 53, 53, 53,
+ 2, 2, 2, 108, 108, 108, 108, 108,
+ 108, 213, 213, 213, 213, 213, 212, 212,
+ 212, 212, 212, 212, 212, 72, 72, 71,
+ 70, 70, 70, 53, 53, 53, 53, 53,
+ 2, 2, 2, 2, 108, 108, 107, 107,
+ 107, 107, 213, 213, 213, 212, 212, 212,
+ 212, 212, 212, 212, 6, 211, 211, 211,
+ 70, 70, 70, 70, 70, 70, 53, 53,
+ 2, 2, 2, 2, 107, 107, 107, 107,
+ 107, 107, 213, 213, 212, 212, 212, 212,
+ 212, 212, 212, 6, 211, 211, 211, 211,
+ 211, 70, 70, 70, 70, 70, 70, 70,
+ 2, 2, 2, 2, 107, 107, 107, 107,
+ 106, 106, 106, 212, 212, 212, 212, 212,
+ 212, 212, 6, 211, 211, 211, 211, 211,
+ 211, 210, 70, 70, 70, 70, 70, 70,
+ 2, 2, 2, 2, 2, 107, 106, 106,
+ 106, 106, 106, 212, 212, 212, 212, 212,
+ 212, 6, 211, 211, 211, 211, 211, 211,
+ 210, 210, 210, 210, 210, 210, 70, 70,
+ 2, 2, 2, 2, 2, 105, 106, 106,
+ 106, 106, 106, 106, 212, 212, 212, 212,
+ 6, 211, 211, 211, 211, 211, 211, 210,
+ 210, 210, 210, 210, 210, 210, 209, 209,
+ 2, 2, 2, 2, 2, 105, 105, 105,
+ 105, 105, 105, 105, 212, 212, 212, 6,
+ 211, 211, 211, 211, 211, 211, 210, 210,
+ 210, 210, 210, 210, 210, 209, 209, 209,
+ 2, 2, 2, 2, 105, 105, 105, 105,
+ 105, 105, 104, 104, 212, 212, 6, 211,
+ 211, 211, 211, 211, 211, 210, 210, 210,
+ 210, 210, 210, 210, 209, 209, 209, 209,
+ 2, 2, 2, 2, 105, 105, 104, 104,
+ 104, 104, 104, 104, 103, 6, 211, 211,
+ 211, 211, 211, 211, 210, 210, 210, 210,
+ 210, 210, 210, 209, 209, 209, 209, 209,
+ 2, 2, 2, 2, 104, 104, 104, 104,
+ 104, 104, 103, 103, 103, 103, 211, 211,
+ 211, 211, 211, 210, 210, 210, 210, 210,
+ 210, 210, 209, 209, 209, 209, 209, 209,
+ 2, 2, 2, 104, 104, 104, 104, 104,
+ 104, 103, 103, 103, 103, 103, 211, 211,
+ 211, 211, 210, 210, 210, 210, 210, 210,
+ 210, 209, 209, 209, 209, 209, 209, 208,
+ 250, 250, 250, 102, 102, 102, 102, 102,
+ 103, 103, 103, 103, 103, 103, 211, 211,
+ 211, 210, 210, 210, 210, 210, 210, 210,
+ 209, 209, 209, 209, 209, 209, 208, 208,
+ 250, 250, 250, 250, 102, 102, 102, 102,
+ 102, 103, 103, 103, 103, 103, 103, 211,
+ 210, 210, 210, 210, 210, 210, 210, 209,
+ 209, 209, 209, 209, 209, 208, 208, 208,
+ 250, 250, 250, 250, 250, 102, 102, 102,
+ 102, 102, 102, 103, 103, 103, 103, 210,
+ 210, 210, 210, 210, 210, 210, 209, 209,
+ 209, 209, 209, 209, 208, 208, 208, 208,
+ 250, 250, 250, 250, 250, 250, 102, 102,
+ 102, 102, 102, 102, 103, 103, 103, 210,
+ 210, 210, 210, 210, 210, 209, 209, 209,
+ 209, 209, 209, 208, 208, 208, 208, 208,
+ 250, 250, 250, 250, 250, 250, 250, 102,
+ 102, 102, 102, 102, 102, 103, 103, 210,
+ 210, 210, 210, 210, 209, 209, 209, 209,
+ 209, 209, 208, 208, 208, 208, 208, 208,
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 102, 102, 102, 102, 102, 102, 103, 210,
+ 210, 210, 210, 209, 209, 209, 209, 209,
+ 209, 208, 208, 208, 208, 208, 208, 254,
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 250, 102, 102, 102, 102, 102, 101, 210,
+ 210, 210, 209, 209, 209, 209, 209, 209,
+ 208, 208, 208, 208, 208, 208, 254, 254,
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 250, 250, 102, 102, 102, 102, 101, 101,
+ 210, 209, 209, 209, 209, 209, 209, 208,
+ 208, 208, 208, 208, 208, 254, 254, 254,
+ 247, 46, 46, 62, 62, 62, 61, 61,
+ 60, 60, 59, 59, 59, 58, 4, 4,
+ 4, 4, 4, 4, 4, 4, 55, 55,
+ 55, 54, 252, 252, 252, 252, 252, 252,
+ 46, 46, 45, 45, 62, 62, 61, 61,
+ 60, 60, 59, 59, 59, 58, 58, 57,
+ 4, 4, 4, 4, 56, 55, 55, 55,
+ 54, 54, 54, 252, 252, 252, 252, 252,
+ 46, 45, 45, 45, 45, 61, 61, 61,
+ 60, 60, 59, 59, 58, 58, 58, 57,
+ 57, 56, 56, 56, 56, 55, 55, 55,
+ 54, 54, 54, 252, 252, 252, 252, 252,
+ 45, 45, 45, 45, 95, 79, 79, 60,
+ 60, 60, 59, 59, 58, 58, 57, 57,
+ 57, 56, 56, 56, 55, 55, 55, 55,
+ 54, 54, 54, 54, 252, 252, 252, 252,
+ 45, 45, 45, 127, 95, 79, 79, 78,
+ 78, 59, 59, 59, 58, 58, 57, 57,
+ 57, 56, 56, 56, 55, 55, 55, 55,
+ 54, 54, 54, 54, 54, 252, 252, 252,
+ 45, 127, 127, 127, 127, 79, 78, 78,
+ 78, 78, 59, 59, 58, 58, 57, 57,
+ 56, 56, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 252, 252, 252,
+ 111, 111, 111, 111, 111, 111, 78, 78,
+ 78, 77, 77, 77, 58, 58, 57, 57,
+ 56, 56, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 252, 252,
+ 111, 111, 111, 111, 110, 110, 78, 78,
+ 77, 77, 77, 77, 76, 76, 57, 57,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 53, 252,
+ 111, 110, 110, 110, 110, 110, 110, 109,
+ 77, 77, 77, 76, 76, 75, 75, 75,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 53, 53, 53,
+ 110, 110, 110, 110, 110, 109, 109, 109,
+ 109, 77, 76, 76, 76, 75, 75, 75,
+ 74, 74, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 53, 53, 53, 53,
+ 110, 110, 110, 110, 109, 109, 109, 109,
+ 109, 76, 213, 213, 213, 213, 213, 74,
+ 74, 73, 73, 73, 55, 55, 54, 54,
+ 54, 54, 54, 53, 53, 53, 53, 53,
+ 110, 110, 109, 109, 109, 109, 109, 108,
+ 108, 108, 213, 213, 213, 213, 213, 213,
+ 74, 73, 73, 73, 73, 73, 54, 54,
+ 54, 54, 53, 53, 53, 53, 53, 53,
+ 2, 109, 109, 109, 109, 108, 108, 108,
+ 108, 108, 213, 213, 213, 213, 213, 212,
+ 212, 212, 73, 73, 73, 72, 72, 72,
+ 54, 53, 53, 53, 53, 53, 53, 53,
+ 2, 2, 108, 108, 108, 108, 108, 108,
+ 107, 107, 213, 213, 213, 213, 212, 212,
+ 212, 212, 212, 73, 72, 72, 72, 71,
+ 70, 70, 70, 53, 53, 53, 53, 53,
+ 2, 2, 2, 108, 108, 108, 107, 107,
+ 107, 107, 213, 213, 213, 212, 212, 212,
+ 212, 212, 212, 212, 211, 72, 71, 70,
+ 70, 70, 70, 70, 70, 70, 53, 53,
+ 2, 2, 2, 2, 107, 107, 107, 107,
+ 107, 106, 106, 213, 212, 212, 212, 212,
+ 212, 212, 212, 211, 211, 211, 211, 211,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 2, 2, 2, 2, 107, 107, 107, 106,
+ 106, 106, 106, 106, 212, 212, 212, 212,
+ 212, 212, 211, 211, 211, 211, 211, 211,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 2, 2, 2, 2, 107, 106, 106, 106,
+ 106, 106, 106, 106, 212, 212, 212, 212,
+ 212, 211, 211, 211, 211, 211, 211, 211,
+ 210, 210, 70, 70, 70, 70, 70, 70,
+ 2, 2, 2, 2, 105, 105, 105, 105,
+ 105, 105, 105, 106, 212, 212, 212, 212,
+ 211, 211, 211, 211, 211, 211, 211, 210,
+ 210, 210, 210, 210, 210, 210, 209, 209,
+ 2, 2, 2, 2, 105, 105, 105, 105,
+ 105, 105, 105, 105, 212, 212, 212, 211,
+ 211, 211, 211, 211, 211, 211, 210, 210,
+ 210, 210, 210, 210, 210, 209, 209, 209,
+ 2, 2, 2, 2, 105, 105, 105, 105,
+ 104, 104, 104, 104, 104, 212, 211, 211,
+ 211, 211, 211, 211, 211, 210, 210, 210,
+ 210, 210, 210, 210, 209, 209, 209, 209,
+ 2, 2, 2, 105, 104, 104, 104, 104,
+ 104, 104, 104, 103, 103, 103, 211, 211,
+ 211, 211, 211, 211, 210, 210, 210, 210,
+ 210, 210, 210, 209, 209, 209, 209, 209,
+ 2, 2, 2, 104, 104, 104, 104, 104,
+ 104, 104, 103, 103, 103, 103, 211, 211,
+ 211, 211, 211, 210, 210, 210, 210, 210,
+ 210, 210, 209, 209, 209, 209, 209, 209,
+ 2, 2, 102, 102, 102, 104, 104, 104,
+ 103, 103, 103, 103, 103, 103, 103, 211,
+ 211, 211, 210, 210, 210, 210, 210, 210,
+ 210, 209, 209, 209, 209, 209, 209, 208,
+ 250, 250, 102, 102, 102, 102, 102, 102,
+ 102, 103, 103, 103, 103, 103, 103, 211,
+ 211, 210, 210, 210, 210, 210, 210, 210,
+ 209, 209, 209, 209, 209, 209, 208, 208,
+ 250, 250, 250, 102, 102, 102, 102, 102,
+ 102, 102, 103, 103, 103, 103, 103, 211,
+ 210, 210, 210, 210, 210, 210, 210, 209,
+ 209, 209, 209, 209, 209, 208, 208, 208,
+ 250, 250, 250, 250, 102, 102, 102, 102,
+ 102, 102, 102, 103, 103, 103, 103, 103,
+ 210, 210, 210, 210, 210, 210, 209, 209,
+ 209, 209, 209, 209, 208, 208, 208, 208,
+ 250, 250, 250, 250, 250, 102, 102, 102,
+ 102, 102, 102, 102, 103, 103, 103, 103,
+ 210, 210, 210, 210, 210, 209, 209, 209,
+ 209, 209, 209, 208, 208, 208, 208, 208,
+ 250, 250, 250, 250, 250, 250, 102, 102,
+ 102, 102, 102, 102, 102, 103, 103, 103,
+ 210, 210, 210, 210, 209, 209, 209, 209,
+ 209, 209, 208, 208, 208, 208, 208, 208,
+ 250, 250, 250, 250, 250, 250, 250, 102,
+ 102, 102, 102, 102, 102, 102, 101, 101,
+ 210, 210, 210, 209, 209, 209, 209, 209,
+ 209, 208, 208, 208, 208, 208, 208, 208,
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 102, 102, 102, 102, 102, 101, 101, 101,
+ 210, 210, 209, 209, 209, 209, 209, 209,
+ 208, 208, 208, 208, 208, 208, 208, 254,
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 250, 102, 102, 102, 102, 101, 101, 101,
+ 210, 209, 209, 209, 209, 209, 209, 208,
+ 208, 208, 208, 208, 208, 208, 254, 254,
+ 247, 247, 247, 247, 62, 62, 61, 61,
+ 60, 60, 59, 59, 59, 58, 58, 57,
+ 4, 4, 4, 4, 56, 55, 55, 55,
+ 54, 54, 54, 252, 252, 252, 252, 252,
+ 247, 247, 247, 45, 45, 61, 61, 61,
+ 60, 60, 59, 59, 58, 58, 58, 57,
+ 57, 56, 56, 56, 56, 55, 55, 55,
+ 54, 54, 54, 252, 252, 252, 252, 252,
+ 247, 247, 45, 45, 44, 95, 61, 60,
+ 60, 60, 59, 59, 58, 58, 57, 57,
+ 57, 56, 56, 56, 55, 55, 55, 55,
+ 54, 54, 54, 54, 252, 252, 252, 252,
+ 247, 45, 45, 44, 44, 95, 79, 60,
+ 60, 59, 59, 59, 58, 58, 57, 57,
+ 57, 56, 56, 56, 55, 55, 55, 55,
+ 54, 54, 54, 54, 54, 252, 252, 252,
+ 45, 143, 143, 127, 44, 95, 79, 78,
+ 78, 59, 59, 59, 58, 58, 57, 57,
+ 56, 56, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 252, 252, 252,
+ 159, 159, 159, 127, 127, 95, 94, 78,
+ 78, 77, 77, 58, 58, 58, 57, 57,
+ 56, 56, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 252, 252,
+ 159, 159, 159, 127, 126, 126, 94, 78,
+ 77, 77, 77, 77, 58, 58, 57, 57,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 53, 252,
+ 111, 111, 111, 110, 126, 126, 94, 78,
+ 77, 77, 77, 76, 76, 76, 75, 56,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 53, 53, 53,
+ 110, 110, 110, 110, 110, 110, 109, 77,
+ 77, 77, 76, 76, 76, 75, 75, 75,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 53, 53, 53, 53,
+ 110, 110, 110, 110, 110, 109, 109, 109,
+ 77, 76, 76, 76, 75, 75, 75, 75,
+ 74, 74, 73, 55, 55, 55, 54, 54,
+ 54, 54, 54, 53, 53, 53, 53, 53,
+ 110, 110, 110, 109, 109, 109, 109, 109,
+ 108, 76, 76, 76, 75, 75, 75, 74,
+ 74, 73, 73, 73, 73, 55, 54, 54,
+ 54, 54, 53, 53, 53, 53, 53, 53,
+ 110, 109, 109, 109, 109, 109, 108, 108,
+ 108, 108, 213, 213, 213, 75, 74, 74,
+ 74, 73, 73, 73, 73, 72, 72, 54,
+ 54, 53, 53, 53, 53, 53, 53, 53,
+ 2, 109, 109, 109, 108, 108, 108, 108,
+ 108, 108, 213, 213, 213, 213, 74, 74,
+ 73, 73, 73, 73, 72, 72, 72, 71,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 2, 2, 108, 108, 108, 108, 108, 107,
+ 107, 107, 213, 213, 213, 213, 212, 212,
+ 212, 73, 73, 73, 72, 72, 72, 71,
+ 70, 70, 70, 53, 53, 53, 53, 53,
+ 2, 2, 108, 108, 108, 107, 107, 107,
+ 107, 107, 107, 213, 213, 212, 212, 212,
+ 212, 212, 212, 72, 72, 72, 71, 70,
+ 70, 70, 70, 70, 70, 70, 53, 53,
+ 2, 2, 2, 107, 107, 107, 107, 107,
+ 107, 106, 106, 106, 212, 212, 212, 212,
+ 212, 212, 212, 211, 211, 71, 71, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 2, 2, 2, 107, 107, 107, 107, 106,
+ 106, 106, 106, 106, 212, 212, 212, 212,
+ 212, 212, 211, 211, 211, 211, 211, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 2, 2, 2, 107, 107, 106, 106, 106,
+ 106, 106, 106, 106, 212, 212, 212, 212,
+ 212, 211, 211, 211, 211, 211, 211, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 2, 2, 2, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 212, 212, 212,
+ 211, 211, 211, 211, 211, 211, 211, 210,
+ 210, 210, 210, 70, 70, 70, 70, 70,
+ 2, 2, 2, 105, 105, 105, 105, 105,
+ 105, 105, 105, 104, 104, 212, 212, 211,
+ 211, 211, 211, 211, 211, 211, 210, 210,
+ 210, 210, 210, 210, 210, 209, 209, 209,
+ 2, 2, 2, 105, 105, 105, 105, 104,
+ 104, 104, 104, 104, 103, 103, 211, 211,
+ 211, 211, 211, 211, 211, 210, 210, 210,
+ 210, 210, 210, 210, 209, 209, 209, 209,
+ 2, 2, 2, 104, 104, 104, 104, 104,
+ 104, 104, 104, 103, 103, 103, 211, 211,
+ 211, 211, 211, 211, 210, 210, 210, 210,
+ 210, 210, 210, 209, 209, 209, 209, 209,
+ 2, 2, 104, 104, 104, 104, 104, 104,
+ 104, 103, 103, 103, 103, 103, 103, 211,
+ 211, 211, 211, 210, 210, 210, 210, 210,
+ 210, 210, 209, 209, 209, 209, 209, 209,
+ 2, 102, 102, 102, 102, 102, 102, 102,
+ 103, 103, 103, 103, 103, 103, 103, 211,
+ 211, 211, 210, 210, 210, 210, 210, 210,
+ 210, 209, 209, 209, 209, 209, 209, 208,
+ 250, 102, 102, 102, 102, 102, 102, 102,
+ 102, 103, 103, 103, 103, 103, 103, 211,
+ 211, 210, 210, 210, 210, 210, 210, 210,
+ 209, 209, 209, 209, 209, 209, 208, 208,
+ 250, 250, 102, 102, 102, 102, 102, 102,
+ 102, 102, 103, 103, 103, 103, 103, 103,
+ 210, 210, 210, 210, 210, 210, 210, 209,
+ 209, 209, 209, 209, 209, 208, 208, 208,
+ 250, 250, 250, 102, 102, 102, 102, 102,
+ 102, 102, 102, 103, 103, 103, 103, 103,
+ 210, 210, 210, 210, 210, 210, 209, 209,
+ 209, 209, 209, 209, 208, 208, 208, 208,
+ 250, 250, 250, 250, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 103, 103, 103,
+ 210, 210, 210, 210, 210, 209, 209, 209,
+ 209, 209, 209, 208, 208, 208, 208, 208,
+ 250, 250, 250, 250, 250, 102, 102, 102,
+ 102, 102, 102, 102, 102, 101, 101, 101,
+ 210, 210, 210, 210, 209, 209, 209, 209,
+ 209, 209, 208, 208, 208, 208, 208, 207,
+ 250, 250, 250, 250, 250, 250, 102, 102,
+ 102, 102, 102, 102, 102, 101, 101, 101,
+ 210, 210, 210, 209, 209, 209, 209, 209,
+ 209, 208, 208, 208, 208, 208, 207, 207,
+ 250, 250, 250, 250, 250, 250, 250, 102,
+ 102, 102, 102, 102, 101, 101, 101, 101,
+ 210, 210, 209, 209, 209, 209, 209, 209,
+ 208, 208, 208, 208, 208, 207, 207, 207,
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 102, 102, 102, 102, 101, 101, 101, 101,
+ 101, 209, 209, 209, 209, 209, 209, 208,
+ 208, 208, 208, 208, 207, 207, 207, 207,
+ 247, 247, 247, 247, 247, 223, 223, 223,
+ 223, 223, 59, 59, 58, 58, 58, 57,
+ 57, 56, 56, 56, 56, 55, 55, 55,
+ 54, 54, 54, 252, 252, 252, 252, 252,
+ 247, 247, 247, 247, 44, 223, 223, 223,
+ 223, 223, 59, 59, 58, 58, 57, 57,
+ 57, 56, 56, 56, 55, 55, 55, 55,
+ 54, 54, 54, 54, 252, 252, 252, 252,
+ 247, 247, 247, 143, 143, 44, 223, 223,
+ 223, 59, 59, 59, 58, 58, 57, 57,
+ 57, 56, 56, 56, 55, 55, 55, 55,
+ 54, 54, 54, 54, 54, 252, 252, 252,
+ 247, 247, 143, 143, 143, 95, 95, 78,
+ 60, 59, 59, 59, 58, 58, 57, 57,
+ 56, 56, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 252, 252, 252,
+ 143, 143, 143, 143, 143, 43, 43, 94,
+ 78, 77, 59, 58, 58, 58, 57, 57,
+ 56, 56, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 252, 252,
+ 159, 159, 159, 159, 159, 43, 94, 94,
+ 78, 77, 77, 77, 58, 58, 57, 57,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 53, 252,
+ 159, 159, 159, 159, 126, 126, 94, 93,
+ 77, 77, 77, 76, 76, 57, 57, 56,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 53, 53, 53,
+ 159, 159, 159, 126, 126, 126, 94, 93,
+ 77, 77, 76, 76, 76, 75, 75, 56,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 53, 53, 53, 53,
+ 110, 110, 110, 126, 126, 125, 125, 93,
+ 77, 77, 76, 76, 76, 75, 75, 75,
+ 74, 56, 56, 55, 55, 55, 54, 54,
+ 54, 54, 54, 53, 53, 53, 53, 53,
+ 110, 110, 110, 110, 109, 109, 125, 125,
+ 92, 76, 76, 76, 75, 75, 75, 74,
+ 74, 74, 73, 55, 55, 55, 54, 54,
+ 54, 54, 53, 53, 53, 53, 53, 53,
+ 110, 110, 109, 109, 109, 109, 109, 109,
+ 108, 76, 76, 76, 75, 75, 75, 74,
+ 74, 73, 73, 73, 73, 55, 54, 54,
+ 54, 53, 53, 53, 53, 53, 53, 53,
+ 110, 109, 109, 109, 109, 108, 108, 108,
+ 108, 108, 76, 75, 75, 75, 74, 74,
+ 74, 73, 73, 73, 73, 72, 72, 54,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 109, 109, 109, 108, 108, 108, 108, 108,
+ 108, 107, 107, 75, 75, 74, 74, 74,
+ 73, 73, 73, 73, 72, 72, 72, 71,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 2, 108, 108, 108, 108, 108, 108, 107,
+ 107, 107, 107, 213, 213, 74, 74, 74,
+ 73, 73, 73, 72, 72, 72, 71, 71,
+ 70, 70, 70, 53, 53, 53, 53, 53,
+ 2, 2, 108, 108, 107, 107, 107, 107,
+ 107, 107, 106, 106, 213, 212, 212, 212,
+ 73, 73, 73, 72, 72, 72, 71, 70,
+ 70, 70, 70, 70, 70, 70, 52, 52,
+ 2, 2, 107, 107, 107, 107, 107, 107,
+ 106, 106, 106, 106, 106, 212, 212, 212,
+ 212, 212, 72, 72, 72, 71, 71, 70,
+ 70, 70, 70, 70, 70, 70, 70, 52,
+ 2, 2, 2, 107, 107, 107, 106, 106,
+ 106, 106, 106, 106, 106, 212, 212, 212,
+ 212, 212, 211, 211, 71, 71, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 2, 2, 2, 107, 105, 105, 106, 106,
+ 106, 106, 106, 106, 106, 212, 212, 212,
+ 212, 211, 211, 211, 211, 211, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 2, 2, 2, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 212, 212, 212,
+ 211, 211, 211, 211, 211, 211, 211, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 2, 2, 105, 105, 105, 105, 105, 105,
+ 105, 105, 104, 104, 104, 103, 212, 211,
+ 211, 211, 211, 211, 211, 211, 210, 210,
+ 210, 210, 210, 70, 70, 70, 209, 209,
+ 2, 2, 105, 105, 105, 105, 104, 104,
+ 104, 104, 104, 104, 103, 103, 103, 211,
+ 211, 211, 211, 211, 211, 210, 210, 210,
+ 210, 210, 210, 210, 209, 209, 209, 209,
+ 2, 2, 104, 104, 104, 104, 104, 104,
+ 104, 104, 103, 103, 103, 103, 103, 211,
+ 211, 211, 211, 211, 210, 210, 210, 210,
+ 210, 210, 210, 209, 209, 209, 209, 209,
+ 2, 102, 104, 104, 104, 104, 104, 104,
+ 104, 103, 103, 103, 103, 103, 103, 211,
+ 211, 211, 211, 210, 210, 210, 210, 210,
+ 210, 210, 209, 209, 209, 209, 209, 209,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 103, 103, 103, 103, 103, 103, 103, 103,
+ 211, 211, 210, 210, 210, 210, 210, 210,
+ 210, 209, 209, 209, 209, 209, 209, 208,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 103, 103, 103, 103, 103, 103, 103,
+ 211, 210, 210, 210, 210, 210, 210, 210,
+ 209, 209, 209, 209, 209, 209, 208, 208,
+ 250, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 103, 103, 103, 103, 103,
+ 210, 210, 210, 210, 210, 210, 210, 209,
+ 209, 209, 209, 209, 209, 208, 208, 208,
+ 250, 250, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 103, 103, 103, 103,
+ 210, 210, 210, 210, 210, 210, 209, 209,
+ 209, 209, 209, 209, 208, 208, 208, 207,
+ 250, 250, 250, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 101, 101, 101, 101,
+ 210, 210, 210, 210, 210, 209, 209, 209,
+ 209, 209, 209, 208, 208, 208, 207, 207,
+ 250, 250, 250, 250, 102, 102, 102, 102,
+ 102, 102, 102, 102, 101, 101, 101, 101,
+ 101, 210, 210, 210, 209, 209, 209, 209,
+ 209, 209, 208, 208, 208, 207, 207, 207,
+ 250, 250, 250, 250, 250, 102, 102, 102,
+ 102, 102, 102, 102, 101, 101, 101, 101,
+ 100, 210, 210, 209, 209, 209, 209, 209,
+ 209, 208, 208, 208, 207, 207, 207, 207,
+ 250, 250, 250, 250, 250, 250, 102, 102,
+ 102, 102, 102, 101, 101, 101, 101, 101,
+ 100, 210, 209, 209, 209, 209, 209, 209,
+ 208, 208, 208, 207, 207, 207, 207, 207,
+ 250, 250, 250, 250, 250, 250, 250, 102,
+ 102, 102, 102, 101, 101, 101, 101, 101,
+ 100, 209, 209, 209, 209, 209, 209, 208,
+ 208, 208, 207, 207, 207, 207, 207, 207,
+ 246, 246, 246, 246, 223, 223, 223, 223,
+ 223, 223, 223, 222, 58, 58, 57, 57,
+ 57, 56, 56, 56, 55, 55, 55, 55,
+ 54, 54, 54, 54, 252, 252, 252, 252,
+ 246, 246, 246, 246, 223, 223, 223, 223,
+ 223, 223, 223, 222, 58, 58, 57, 57,
+ 57, 56, 56, 56, 55, 55, 55, 55,
+ 54, 54, 54, 54, 54, 252, 252, 252,
+ 246, 246, 246, 143, 143, 223, 223, 223,
+ 223, 223, 223, 59, 58, 58, 57, 57,
+ 56, 56, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 252, 252, 252,
+ 246, 246, 143, 143, 143, 143, 43, 223,
+ 223, 223, 59, 58, 58, 58, 57, 57,
+ 56, 56, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 252, 252,
+ 246, 143, 143, 143, 142, 43, 43, 94,
+ 15, 15, 77, 58, 58, 58, 57, 57,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 53, 252,
+ 159, 159, 159, 142, 142, 43, 43, 15,
+ 15, 77, 77, 76, 58, 57, 57, 56,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 53, 53, 53,
+ 159, 159, 158, 158, 158, 158, 42, 93,
+ 93, 77, 77, 76, 76, 76, 57, 56,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 53, 53, 53, 53,
+ 159, 158, 158, 158, 158, 125, 125, 93,
+ 93, 77, 76, 76, 76, 75, 75, 75,
+ 56, 56, 56, 55, 55, 55, 54, 54,
+ 54, 54, 54, 53, 53, 53, 53, 53,
+ 158, 158, 158, 158, 125, 125, 125, 125,
+ 92, 92, 76, 76, 75, 75, 75, 75,
+ 74, 74, 55, 55, 55, 55, 54, 54,
+ 54, 54, 53, 53, 53, 53, 53, 53,
+ 158, 158, 158, 125, 125, 125, 125, 124,
+ 92, 92, 76, 76, 75, 75, 75, 74,
+ 74, 74, 73, 73, 55, 55, 54, 54,
+ 54, 53, 53, 53, 53, 53, 53, 53,
+ 110, 110, 109, 109, 109, 125, 124, 124,
+ 92, 92, 76, 75, 75, 75, 74, 74,
+ 74, 73, 73, 73, 73, 72, 54, 54,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 109, 109, 109, 109, 109, 108, 108, 108,
+ 124, 123, 91, 75, 75, 75, 74, 74,
+ 74, 73, 73, 73, 72, 72, 72, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 109, 109, 108, 108, 108, 108, 108, 108,
+ 107, 107, 91, 75, 75, 74, 74, 74,
+ 73, 73, 73, 72, 72, 72, 72, 71,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 191, 108, 108, 108, 108, 108, 107, 107,
+ 107, 107, 107, 75, 74, 74, 74, 73,
+ 73, 73, 73, 72, 72, 72, 71, 71,
+ 70, 70, 70, 53, 53, 52, 52, 52,
+ 2, 108, 108, 107, 107, 107, 107, 107,
+ 107, 106, 106, 106, 74, 74, 74, 73,
+ 73, 73, 72, 72, 72, 71, 71, 70,
+ 70, 70, 70, 70, 70, 52, 52, 52,
+ 2, 2, 107, 107, 107, 107, 107, 106,
+ 106, 106, 106, 106, 106, 212, 212, 73,
+ 73, 72, 72, 72, 71, 71, 71, 70,
+ 70, 70, 70, 70, 70, 70, 70, 52,
+ 2, 2, 107, 107, 107, 106, 106, 106,
+ 106, 106, 106, 106, 106, 212, 212, 212,
+ 212, 72, 72, 72, 71, 71, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 2, 2, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 106, 106, 212, 212, 212,
+ 212, 211, 211, 71, 71, 71, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,
+ 2, 2, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 104, 212, 212,
+ 211, 211, 211, 211, 211, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 68,
+ 2, 2, 105, 105, 105, 105, 105, 105,
+ 105, 104, 104, 104, 104, 103, 103, 211,
+ 211, 211, 211, 211, 211, 211, 210, 70,
+ 70, 70, 70, 70, 70, 70, 68, 68,
+ 2, 105, 105, 105, 105, 104, 104, 104,
+ 104, 104, 104, 103, 103, 103, 103, 211,
+ 211, 211, 211, 211, 211, 210, 210, 210,
+ 210, 210, 210, 210, 209, 209, 209, 209,
+ 2, 104, 104, 104, 104, 104, 104, 104,
+ 104, 104, 103, 103, 103, 103, 103, 103,
+ 211, 211, 211, 211, 210, 210, 210, 210,
+ 210, 210, 210, 209, 209, 209, 209, 209,
+ 102, 102, 102, 102, 102, 104, 104, 104,
+ 103, 103, 103, 103, 103, 103, 103, 103,
+ 211, 211, 211, 210, 210, 210, 210, 210,
+ 210, 210, 209, 209, 209, 209, 209, 209,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 103, 103, 103, 103, 103, 103, 103,
+ 211, 211, 210, 210, 210, 210, 210, 210,
+ 210, 209, 209, 209, 209, 209, 209, 208,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 103, 103, 103, 103, 103, 103,
+ 103, 210, 210, 210, 210, 210, 210, 210,
+ 209, 209, 209, 209, 209, 209, 208, 207,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 103, 103, 103, 103, 103,
+ 103, 210, 210, 210, 210, 210, 210, 209,
+ 209, 209, 209, 209, 209, 208, 207, 207,
+ 250, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 101, 101, 101, 101,
+ 100, 210, 210, 210, 210, 210, 209, 209,
+ 209, 209, 209, 209, 208, 207, 207, 207,
+ 250, 250, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 101, 101, 101, 101, 101,
+ 100, 210, 210, 210, 210, 209, 209, 209,
+ 209, 209, 209, 208, 207, 207, 207, 207,
+ 250, 250, 250, 102, 102, 102, 102, 102,
+ 102, 102, 102, 101, 101, 101, 101, 101,
+ 100, 100, 210, 210, 209, 209, 209, 209,
+ 209, 209, 208, 207, 207, 207, 207, 207,
+ 250, 250, 250, 250, 102, 102, 102, 102,
+ 102, 102, 102, 101, 101, 101, 101, 100,
+ 100, 100, 210, 209, 209, 209, 209, 209,
+ 209, 208, 207, 207, 207, 207, 207, 207,
+ 250, 250, 250, 250, 250, 102, 102, 102,
+ 102, 102, 101, 101, 101, 101, 101, 100,
+ 100, 100, 209, 209, 209, 209, 209, 209,
+ 208, 207, 207, 207, 207, 207, 207, 207,
+ 250, 250, 250, 250, 250, 250, 102, 102,
+ 102, 102, 101, 101, 101, 101, 101, 100,
+ 100, 100, 209, 209, 209, 209, 209, 208,
+ 207, 207, 207, 207, 207, 207, 207, 207,
+ 246, 246, 246, 246, 223, 223, 223, 223,
+ 223, 223, 222, 222, 222, 222, 57, 57,
+ 57, 56, 56, 56, 55, 55, 55, 55,
+ 54, 54, 54, 54, 54, 252, 252, 252,
+ 246, 246, 246, 246, 223, 223, 223, 223,
+ 223, 223, 222, 222, 222, 58, 57, 57,
+ 56, 56, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 252, 252, 252,
+ 246, 246, 246, 246, 143, 223, 223, 223,
+ 223, 223, 222, 222, 222, 58, 57, 57,
+ 56, 56, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 252, 252,
+ 246, 246, 246, 143, 142, 142, 223, 223,
+ 223, 222, 222, 222, 58, 58, 57, 57,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 53, 252,
+ 246, 143, 143, 142, 142, 142, 43, 15,
+ 15, 15, 222, 222, 58, 57, 57, 56,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 53, 53, 53,
+ 159, 159, 142, 142, 142, 142, 15, 15,
+ 15, 15, 15, 76, 76, 57, 57, 56,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 53, 53, 53, 53,
+ 159, 158, 158, 158, 158, 158, 42, 15,
+ 15, 15, 92, 76, 76, 75, 75, 56,
+ 56, 56, 56, 55, 55, 55, 54, 54,
+ 54, 54, 54, 53, 53, 53, 53, 53,
+ 158, 158, 158, 158, 158, 141, 42, 42,
+ 41, 92, 92, 76, 76, 75, 75, 75,
+ 56, 56, 55, 55, 55, 55, 54, 54,
+ 54, 54, 53, 53, 53, 53, 53, 53,
+ 158, 158, 158, 158, 157, 125, 125, 41,
+ 92, 92, 92, 76, 75, 75, 75, 74,
+ 74, 74, 55, 55, 55, 55, 54, 54,
+ 54, 53, 53, 53, 53, 53, 53, 53,
+ 158, 158, 158, 157, 157, 125, 124, 124,
+ 92, 92, 91, 91, 75, 75, 75, 74,
+ 74, 74, 73, 73, 55, 55, 54, 54,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 191, 157, 157, 157, 124, 124, 124, 124,
+ 124, 92, 91, 91, 75, 75, 74, 74,
+ 74, 73, 73, 73, 72, 72, 54, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 191, 191, 157, 124, 124, 124, 124, 124,
+ 123, 123, 91, 91, 75, 74, 74, 74,
+ 73, 73, 73, 73, 72, 72, 72, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 191, 191, 108, 108, 108, 108, 108, 123,
+ 123, 123, 123, 90, 90, 74, 74, 74,
+ 73, 73, 73, 72, 72, 72, 71, 71,
+ 53, 53, 53, 53, 53, 52, 52, 52,
+ 191, 191, 108, 108, 108, 107, 107, 107,
+ 107, 123, 122, 90, 90, 74, 74, 73,
+ 73, 73, 72, 72, 72, 71, 71, 71,
+ 70, 70, 70, 52, 52, 52, 52, 52,
+ 191, 191, 107, 107, 107, 107, 107, 107,
+ 106, 106, 106, 106, 90, 74, 74, 73,
+ 73, 73, 72, 72, 72, 71, 71, 70,
+ 70, 70, 70, 70, 52, 52, 52, 52,
+ 191, 191, 107, 107, 107, 107, 106, 106,
+ 106, 106, 106, 106, 106, 74, 73, 73,
+ 73, 72, 72, 72, 71, 71, 71, 70,
+ 70, 70, 70, 70, 70, 70, 52, 52,
+ 191, 191, 107, 107, 106, 106, 106, 106,
+ 106, 106, 106, 106, 106, 106, 73, 73,
+ 72, 72, 72, 71, 71, 71, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 51,
+ 2, 105, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 105, 212, 212,
+ 72, 72, 72, 71, 71, 71, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 68,
+ 2, 105, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 104, 104, 104, 212, 212,
+ 211, 211, 71, 71, 71, 70, 70, 70,
+ 70, 70, 70, 70, 70, 68, 68, 68,
+ 2, 105, 105, 105, 105, 105, 105, 104,
+ 104, 104, 104, 104, 103, 103, 103, 211,
+ 211, 211, 211, 211, 211, 70, 70, 70,
+ 70, 70, 70, 70, 68, 68, 68, 68,
+ 2, 105, 105, 104, 104, 104, 104, 104,
+ 104, 104, 104, 103, 103, 103, 103, 103,
+ 211, 211, 211, 211, 210, 210, 210, 70,
+ 70, 70, 69, 68, 68, 68, 68, 68,
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ 104, 103, 103, 103, 103, 103, 103, 103,
+ 211, 211, 211, 210, 210, 210, 210, 210,
+ 210, 210, 210, 68, 68, 209, 209, 209,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 103, 103, 103, 103, 103, 103, 103, 103,
+ 211, 211, 210, 210, 210, 210, 210, 210,
+ 210, 210, 209, 209, 209, 209, 209, 206,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 103, 103, 103, 103, 103, 103, 103,
+ 103, 210, 210, 210, 210, 210, 210, 210,
+ 210, 209, 209, 209, 209, 209, 206, 206,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 103, 103, 103, 103, 103, 103,
+ 100, 210, 210, 210, 210, 210, 210, 210,
+ 209, 209, 209, 209, 209, 206, 206, 207,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 101, 101, 101, 101, 100,
+ 100, 210, 210, 210, 210, 210, 210, 209,
+ 209, 209, 209, 209, 206, 206, 207, 207,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 101, 101, 101, 101, 100,
+ 100, 100, 210, 210, 210, 210, 209, 209,
+ 209, 209, 209, 206, 206, 207, 207, 207,
+ 250, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 101, 101, 101, 101, 101, 100,
+ 100, 100, 210, 210, 210, 209, 209, 209,
+ 209, 209, 206, 206, 207, 207, 207, 207,
+ 250, 250, 102, 102, 102, 102, 102, 102,
+ 102, 102, 101, 101, 101, 101, 101, 100,
+ 100, 100, 210, 210, 209, 209, 209, 209,
+ 209, 206, 206, 207, 207, 207, 207, 207,
+ 250, 250, 250, 102, 102, 102, 102, 102,
+ 102, 102, 101, 101, 101, 101, 100, 100,
+ 100, 100, 99, 209, 209, 209, 209, 209,
+ 206, 206, 207, 207, 207, 207, 207, 207,
+ 250, 250, 250, 250, 102, 102, 102, 102,
+ 102, 101, 101, 101, 101, 101, 100, 100,
+ 100, 100, 98, 209, 209, 209, 209, 206,
+ 206, 207, 207, 207, 207, 207, 207, 207,
+ 250, 250, 250, 250, 250, 102, 102, 102,
+ 102, 101, 101, 101, 101, 101, 100, 100,
+ 100, 98, 98, 209, 209, 209, 206, 206,
+ 207, 207, 207, 207, 207, 207, 207, 207,
+ 245, 245, 245, 245, 223, 223, 223, 223,
+ 223, 222, 222, 222, 222, 222, 221, 221,
+ 56, 56, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 252, 252, 252,
+ 245, 245, 245, 245, 223, 223, 223, 223,
+ 223, 222, 222, 222, 222, 222, 221, 221,
+ 56, 56, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 252, 252,
+ 245, 245, 245, 245, 223, 223, 223, 223,
+ 223, 222, 222, 222, 222, 221, 221, 57,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 53, 252,
+ 245, 245, 245, 142, 142, 142, 223, 223,
+ 222, 222, 222, 222, 222, 221, 57, 56,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 53, 53, 53,
+ 245, 245, 142, 142, 142, 142, 15, 15,
+ 15, 222, 222, 222, 222, 57, 57, 56,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 53, 53, 53, 53,
+ 245, 142, 142, 142, 142, 141, 141, 15,
+ 15, 15, 15, 76, 76, 57, 57, 56,
+ 56, 56, 56, 55, 55, 55, 54, 54,
+ 54, 54, 54, 53, 53, 53, 53, 53,
+ 158, 158, 158, 158, 141, 141, 141, 15,
+ 15, 41, 92, 92, 76, 75, 75, 56,
+ 56, 56, 55, 55, 55, 55, 54, 54,
+ 54, 54, 53, 53, 53, 53, 53, 53,
+ 158, 158, 158, 158, 141, 141, 141, 41,
+ 41, 92, 92, 91, 91, 75, 75, 75,
+ 74, 56, 55, 55, 55, 55, 54, 54,
+ 54, 53, 53, 53, 53, 53, 53, 53,
+ 158, 158, 157, 157, 157, 157, 157, 41,
+ 41, 92, 91, 91, 91, 75, 75, 74,
+ 74, 74, 73, 55, 55, 55, 54, 54,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 158, 157, 157, 157, 157, 157, 124, 124,
+ 40, 40, 91, 91, 91, 75, 74, 74,
+ 74, 74, 73, 73, 55, 54, 54, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 191, 191, 157, 157, 157, 124, 124, 124,
+ 123, 123, 91, 91, 90, 90, 74, 74,
+ 74, 73, 73, 73, 72, 72, 53, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 191, 191, 191, 124, 124, 124, 124, 123,
+ 123, 123, 91, 90, 90, 90, 74, 74,
+ 73, 73, 73, 72, 72, 72, 72, 53,
+ 53, 53, 53, 53, 53, 52, 52, 52,
+ 191, 191, 191, 191, 124, 124, 123, 123,
+ 123, 123, 122, 90, 90, 90, 74, 74,
+ 73, 73, 73, 72, 72, 72, 71, 71,
+ 53, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 191, 191, 107, 107, 123, 123,
+ 122, 122, 122, 122, 90, 89, 74, 73,
+ 73, 73, 72, 72, 72, 71, 71, 71,
+ 70, 70, 52, 52, 52, 52, 52, 52,
+ 191, 191, 191, 191, 107, 107, 107, 107,
+ 122, 122, 122, 121, 89, 89, 89, 73,
+ 73, 72, 72, 72, 71, 71, 71, 70,
+ 70, 70, 70, 70, 52, 52, 52, 52,
+ 191, 191, 191, 107, 107, 107, 106, 106,
+ 106, 106, 121, 121, 121, 89, 89, 73,
+ 73, 72, 72, 72, 71, 71, 71, 70,
+ 70, 70, 70, 70, 70, 52, 51, 51,
+ 191, 191, 191, 105, 105, 105, 106, 106,
+ 106, 106, 106, 121, 121, 121, 88, 88,
+ 72, 72, 72, 71, 71, 71, 70, 70,
+ 70, 70, 70, 70, 70, 70, 51, 51,
+ 191, 191, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 120, 88, 88,
+ 72, 72, 71, 71, 71, 71, 70, 70,
+ 70, 70, 70, 70, 70, 68, 68, 68,
+ 191, 191, 105, 105, 105, 105, 105, 105,
+ 105, 105, 104, 104, 104, 104, 120, 87,
+ 72, 71, 71, 71, 71, 70, 70, 70,
+ 70, 70, 70, 69, 68, 68, 68, 68,
+ 191, 105, 105, 105, 105, 105, 104, 104,
+ 104, 104, 104, 104, 103, 103, 103, 103,
+ 211, 211, 71, 71, 70, 70, 70, 70,
+ 70, 69, 69, 68, 68, 68, 68, 68,
+ 105, 105, 104, 104, 104, 104, 104, 104,
+ 104, 104, 103, 103, 103, 103, 103, 103,
+ 211, 211, 211, 211, 70, 70, 70, 69,
+ 69, 69, 68, 68, 68, 68, 68, 68,
+ 102, 102, 104, 104, 104, 104, 104, 104,
+ 104, 103, 103, 103, 103, 103, 103, 103,
+ 103, 211, 211, 210, 210, 210, 210, 69,
+ 69, 68, 68, 68, 68, 68, 68, 68,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 103, 103, 103, 103, 103, 103, 103, 103,
+ 103, 211, 210, 210, 210, 210, 210, 210,
+ 210, 68, 68, 68, 68, 68, 206, 206,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 103, 103, 103, 103, 103, 100,
+ 100, 100, 210, 210, 210, 210, 210, 210,
+ 210, 209, 209, 209, 206, 206, 206, 206,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 101, 101, 101, 101, 100,
+ 100, 100, 210, 210, 210, 210, 210, 210,
+ 209, 209, 209, 206, 206, 206, 206, 206,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 101, 101, 101, 101, 100, 100,
+ 100, 100, 210, 210, 210, 210, 210, 209,
+ 209, 209, 206, 206, 206, 206, 206, 206,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 101, 101, 101, 101, 100, 100,
+ 100, 100, 99, 210, 210, 210, 209, 209,
+ 209, 206, 206, 206, 206, 206, 206, 206,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 101, 101, 101, 101, 101, 100, 100,
+ 100, 100, 99, 210, 210, 209, 209, 209,
+ 206, 206, 206, 206, 206, 206, 206, 206,
+ 250, 102, 102, 102, 102, 102, 102, 102,
+ 102, 101, 101, 101, 101, 101, 100, 100,
+ 100, 100, 99, 98, 209, 209, 209, 206,
+ 206, 206, 206, 206, 206, 206, 206, 206,
+ 250, 250, 102, 102, 102, 102, 102, 102,
+ 102, 101, 101, 101, 101, 100, 100, 100,
+ 100, 99, 98, 98, 209, 209, 206, 206,
+ 206, 206, 206, 206, 206, 206, 206, 206,
+ 250, 250, 250, 102, 102, 102, 102, 102,
+ 101, 101, 101, 101, 101, 100, 100, 100,
+ 100, 98, 98, 98, 209, 206, 206, 206,
+ 206, 206, 206, 206, 206, 206, 206, 206,
+ 250, 250, 250, 250, 102, 102, 102, 102,
+ 101, 101, 101, 101, 101, 100, 100, 100,
+ 98, 98, 98, 98, 206, 206, 206, 206,
+ 206, 206, 206, 206, 206, 206, 206, 206,
+ 245, 245, 245, 245, 245, 223, 223, 223,
+ 222, 222, 222, 222, 222, 221, 221, 221,
+ 221, 221, 56, 56, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 252, 252,
+ 245, 245, 245, 245, 245, 223, 223, 223,
+ 222, 222, 222, 222, 222, 221, 221, 221,
+ 221, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 53, 252,
+ 245, 245, 245, 245, 245, 223, 223, 223,
+ 222, 222, 222, 222, 221, 221, 221, 221,
+ 221, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 53, 53, 53,
+ 245, 245, 245, 245, 142, 223, 223, 222,
+ 222, 222, 222, 222, 221, 221, 221, 221,
+ 56, 56, 56, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 53, 53, 53, 53,
+ 245, 245, 245, 142, 142, 141, 141, 15,
+ 222, 222, 222, 222, 221, 221, 221, 56,
+ 56, 56, 56, 55, 55, 55, 54, 54,
+ 54, 54, 54, 53, 53, 53, 53, 53,
+ 245, 142, 142, 142, 141, 141, 141, 15,
+ 15, 15, 222, 222, 221, 221, 221, 56,
+ 56, 56, 55, 55, 55, 55, 54, 54,
+ 54, 54, 53, 53, 53, 53, 53, 53,
+ 158, 158, 158, 141, 141, 141, 141, 141,
+ 41, 41, 40, 91, 91, 91, 75, 75,
+ 56, 56, 55, 55, 55, 55, 54, 54,
+ 54, 53, 53, 53, 53, 53, 53, 53,
+ 158, 158, 141, 141, 141, 141, 141, 140,
+ 41, 40, 40, 91, 91, 91, 75, 74,
+ 74, 74, 55, 55, 55, 55, 54, 54,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 158, 157, 157, 157, 157, 157, 140, 140,
+ 40, 40, 91, 91, 91, 90, 90, 74,
+ 74, 74, 73, 55, 55, 54, 54, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 191, 191, 157, 157, 157, 156, 156, 156,
+ 40, 40, 91, 91, 90, 90, 90, 74,
+ 74, 73, 73, 73, 72, 54, 53, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 191, 191, 191, 156, 156, 156, 156, 123,
+ 123, 39, 39, 90, 90, 90, 90, 74,
+ 74, 73, 73, 72, 72, 72, 53, 53,
+ 53, 53, 53, 53, 53, 52, 52, 52,
+ 191, 191, 191, 191, 156, 156, 123, 123,
+ 123, 123, 38, 90, 90, 90, 89, 74,
+ 73, 73, 73, 72, 72, 72, 71, 53,
+ 53, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 191, 191, 156, 123, 123, 123,
+ 123, 122, 122, 90, 90, 89, 89, 89,
+ 73, 73, 72, 72, 72, 71, 71, 71,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 191, 191, 191, 123, 123, 122,
+ 122, 122, 122, 122, 89, 89, 89, 88,
+ 73, 72, 72, 72, 72, 71, 71, 71,
+ 70, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 191, 191, 191, 122, 122, 122,
+ 122, 122, 121, 121, 89, 89, 89, 88,
+ 88, 72, 72, 72, 71, 71, 71, 70,
+ 70, 70, 70, 52, 52, 52, 51, 51,
+ 191, 191, 191, 191, 191, 106, 106, 121,
+ 121, 121, 121, 121, 121, 89, 88, 88,
+ 88, 72, 72, 71, 71, 71, 71, 70,
+ 70, 70, 70, 70, 51, 51, 51, 51,
+ 191, 191, 191, 191, 105, 105, 105, 105,
+ 121, 121, 121, 121, 121, 120, 88, 88,
+ 88, 72, 71, 71, 71, 71, 70, 70,
+ 70, 70, 70, 69, 69, 51, 51, 51,
+ 191, 191, 191, 105, 105, 105, 105, 105,
+ 105, 105, 121, 120, 120, 120, 88, 88,
+ 87, 72, 71, 71, 71, 71, 70, 70,
+ 70, 69, 69, 69, 68, 68, 68, 68,
+ 191, 190, 190, 105, 105, 105, 105, 105,
+ 105, 104, 104, 104, 120, 120, 120, 87,
+ 87, 87, 71, 71, 71, 70, 70, 69,
+ 69, 69, 69, 68, 68, 68, 68, 68,
+ 190, 190, 105, 105, 105, 104, 104, 104,
+ 104, 104, 104, 103, 103, 103, 103, 87,
+ 87, 87, 71, 71, 70, 69, 69, 69,
+ 69, 69, 68, 68, 68, 68, 68, 68,
+ 190, 104, 104, 104, 104, 104, 104, 104,
+ 104, 104, 103, 103, 103, 103, 103, 103,
+ 87, 86, 71, 71, 69, 69, 69, 69,
+ 69, 68, 68, 68, 68, 68, 68, 68,
+ 102, 102, 102, 102, 102, 102, 104, 104,
+ 103, 103, 103, 103, 103, 103, 103, 103,
+ 100, 211, 211, 69, 69, 69, 69, 69,
+ 68, 68, 68, 68, 68, 68, 68, 68,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 103, 103, 103, 103, 103, 103, 100,
+ 100, 100, 210, 210, 210, 69, 69, 68,
+ 68, 68, 68, 68, 68, 68, 68, 206,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 101, 101, 101, 101, 100, 100,
+ 100, 100, 99, 210, 210, 210, 210, 68,
+ 68, 68, 68, 206, 206, 206, 206, 206,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 101, 101, 101, 101, 100, 100,
+ 100, 100, 99, 210, 210, 210, 210, 210,
+ 209, 206, 206, 206, 206, 206, 206, 206,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 101, 101, 101, 101, 100, 100, 100,
+ 100, 100, 99, 99, 210, 210, 210, 209,
+ 206, 206, 206, 206, 206, 206, 206, 206,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 101, 101, 101, 101, 100, 100, 100,
+ 100, 99, 99, 99, 210, 210, 209, 206,
+ 206, 206, 206, 206, 206, 206, 206, 206,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 101, 101, 101, 101, 101, 100, 100, 100,
+ 100, 99, 99, 99, 98, 209, 206, 206,
+ 206, 206, 206, 206, 206, 206, 206, 206,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 101, 101, 101, 101, 101, 100, 100, 100,
+ 100, 99, 98, 98, 98, 206, 206, 206,
+ 206, 206, 206, 206, 206, 206, 206, 206,
+ 250, 102, 102, 102, 102, 102, 102, 102,
+ 101, 101, 101, 101, 100, 100, 100, 100,
+ 99, 98, 98, 98, 98, 206, 206, 206,
+ 206, 206, 206, 206, 206, 206, 206, 206,
+ 250, 250, 102, 102, 102, 102, 102, 101,
+ 101, 101, 101, 101, 100, 100, 100, 100,
+ 98, 98, 98, 98, 98, 206, 206, 206,
+ 206, 206, 206, 206, 206, 206, 206, 205,
+ 250, 250, 250, 102, 102, 102, 102, 101,
+ 101, 101, 101, 101, 100, 100, 100, 98,
+ 98, 98, 98, 98, 98, 206, 206, 206,
+ 206, 206, 206, 206, 206, 206, 205, 205,
+ 244, 244, 244, 244, 244, 223, 223, 222,
+ 222, 222, 222, 222, 221, 221, 221, 221,
+ 221, 220, 220, 5, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 54, 53, 252,
+ 244, 244, 244, 244, 244, 223, 223, 222,
+ 222, 222, 222, 222, 221, 221, 221, 221,
+ 220, 220, 220, 220, 55, 55, 55, 54,
+ 54, 54, 54, 54, 54, 53, 53, 53,
+ 244, 244, 244, 244, 244, 223, 223, 222,
+ 222, 222, 222, 221, 221, 221, 221, 221,
+ 220, 220, 220, 55, 55, 55, 55, 54,
+ 54, 54, 54, 54, 53, 53, 53, 53,
+ 244, 244, 244, 244, 244, 141, 222, 222,
+ 222, 222, 222, 221, 221, 221, 221, 221,
+ 220, 220, 220, 55, 55, 55, 54, 54,
+ 54, 54, 54, 53, 53, 53, 53, 53,
+ 244, 244, 244, 141, 141, 141, 141, 222,
+ 222, 222, 222, 221, 221, 221, 221, 221,
+ 220, 220, 55, 55, 55, 55, 54, 54,
+ 54, 54, 53, 53, 53, 53, 53, 53,
+ 244, 244, 141, 141, 141, 141, 141, 141,
+ 15, 222, 222, 221, 221, 221, 221, 221,
+ 220, 56, 55, 55, 55, 55, 54, 54,
+ 54, 53, 53, 53, 53, 53, 53, 53,
+ 244, 141, 141, 141, 141, 141, 140, 140,
+ 140, 40, 40, 91, 91, 91, 90, 90,
+ 56, 56, 55, 55, 55, 55, 54, 54,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 158, 141, 141, 141, 141, 140, 140, 140,
+ 140, 40, 40, 91, 91, 90, 90, 90,
+ 74, 74, 55, 55, 55, 54, 54, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 191, 191, 157, 157, 140, 140, 140, 140,
+ 40, 40, 39, 39, 90, 90, 90, 90,
+ 74, 74, 73, 55, 55, 54, 53, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 191, 191, 191, 156, 156, 156, 156, 156,
+ 139, 39, 39, 38, 90, 90, 90, 90,
+ 74, 73, 73, 73, 72, 53, 53, 53,
+ 53, 53, 53, 53, 53, 52, 52, 52,
+ 191, 191, 191, 156, 156, 156, 156, 155,
+ 123, 39, 38, 38, 90, 90, 90, 89,
+ 88, 73, 73, 72, 72, 72, 53, 53,
+ 53, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 191, 191, 156, 156, 155, 155,
+ 123, 122, 38, 90, 90, 89, 89, 89,
+ 88, 73, 72, 72, 72, 72, 71, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 191, 191, 191, 155, 155, 155,
+ 122, 122, 122, 37, 89, 89, 89, 88,
+ 88, 88, 72, 72, 72, 71, 71, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 191, 191, 191, 155, 155, 122,
+ 122, 122, 122, 121, 89, 89, 89, 88,
+ 88, 88, 72, 72, 71, 71, 71, 71,
+ 52, 52, 52, 52, 52, 52, 51, 51,
+ 191, 191, 191, 191, 191, 155, 122, 122,
+ 122, 121, 121, 121, 121, 89, 88, 88,
+ 88, 88, 72, 72, 71, 71, 71, 70,
+ 70, 70, 52, 51, 51, 51, 51, 51,
+ 191, 191, 191, 191, 191, 122, 121, 121,
+ 121, 121, 121, 121, 121, 89, 88, 88,
+ 88, 87, 72, 71, 71, 71, 71, 70,
+ 70, 70, 69, 51, 51, 51, 51, 51,
+ 191, 191, 191, 191, 191, 105, 121, 121,
+ 121, 121, 121, 120, 120, 120, 88, 88,
+ 87, 87, 87, 71, 71, 71, 70, 70,
+ 69, 69, 69, 69, 68, 51, 51, 50,
+ 191, 190, 190, 190, 105, 105, 105, 105,
+ 121, 121, 120, 120, 120, 120, 120, 87,
+ 87, 87, 87, 71, 71, 70, 69, 69,
+ 69, 69, 69, 68, 68, 68, 68, 50,
+ 190, 190, 190, 190, 105, 105, 105, 105,
+ 104, 104, 120, 120, 120, 120, 119, 87,
+ 87, 87, 86, 71, 71, 69, 69, 69,
+ 69, 69, 68, 68, 68, 68, 68, 68,
+ 190, 190, 190, 105, 104, 104, 104, 104,
+ 104, 104, 104, 103, 120, 119, 119, 119,
+ 87, 86, 86, 71, 69, 69, 69, 69,
+ 69, 68, 68, 68, 68, 68, 68, 68,
+ 3, 190, 190, 104, 104, 104, 104, 104,
+ 104, 103, 103, 103, 103, 103, 118, 118,
+ 118, 86, 86, 69, 69, 69, 69, 69,
+ 68, 68, 68, 68, 68, 68, 68, 68,
+ 3, 102, 102, 102, 102, 102, 102, 102,
+ 103, 103, 103, 103, 103, 103, 100, 100,
+ 118, 86, 86, 69, 69, 69, 69, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 101, 101, 101, 101, 100, 100, 100,
+ 100, 100, 99, 69, 69, 69, 68, 68,
+ 68, 68, 68, 68, 68, 68, 67, 67,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 101, 101, 101, 101, 100, 100, 100,
+ 100, 99, 99, 99, 69, 68, 68, 68,
+ 68, 68, 68, 68, 67, 206, 206, 206,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 101, 101, 101, 101, 100, 100, 100,
+ 100, 99, 99, 99, 210, 210, 68, 68,
+ 68, 206, 206, 206, 206, 206, 206, 206,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 101, 101, 101, 101, 100, 100, 100, 100,
+ 100, 99, 99, 99, 99, 210, 206, 206,
+ 206, 206, 206, 206, 206, 206, 206, 206,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 101, 101, 101, 101, 100, 100, 100, 100,
+ 99, 99, 99, 99, 99, 206, 206, 206,
+ 206, 206, 206, 206, 206, 206, 206, 205,
+ 102, 102, 102, 102, 102, 102, 102, 101,
+ 101, 101, 101, 101, 100, 100, 100, 100,
+ 99, 99, 99, 98, 98, 206, 206, 206,
+ 206, 206, 206, 206, 206, 206, 205, 205,
+ 102, 102, 102, 102, 102, 102, 102, 101,
+ 101, 101, 101, 101, 100, 100, 100, 100,
+ 99, 98, 98, 98, 98, 206, 206, 206,
+ 206, 206, 206, 206, 206, 205, 205, 205,
+ 102, 102, 102, 102, 102, 102, 102, 101,
+ 101, 101, 101, 100, 100, 100, 100, 99,
+ 98, 98, 98, 98, 98, 206, 206, 206,
+ 206, 206, 206, 206, 205, 205, 205, 205,
+ 250, 102, 102, 102, 102, 102, 101, 101,
+ 101, 101, 101, 100, 100, 100, 100, 98,
+ 98, 98, 98, 98, 98, 206, 206, 206,
+ 206, 206, 206, 205, 205, 205, 205, 205,
+ 250, 250, 102, 102, 102, 102, 101, 101,
+ 101, 101, 101, 100, 100, 100, 98, 98,
+ 98, 98, 98, 98, 98, 206, 206, 206,
+ 206, 206, 205, 205, 205, 205, 205, 205,
+ 243, 243, 243, 244, 244, 244, 222, 222,
+ 222, 222, 222, 221, 221, 221, 221, 221,
+ 220, 220, 5, 5, 5, 5, 5, 54,
+ 54, 54, 54, 54, 54, 53, 53, 53,
+ 243, 243, 244, 244, 244, 223, 222, 222,
+ 222, 222, 222, 221, 221, 221, 221, 220,
+ 220, 220, 220, 5, 5, 55, 55, 54,
+ 54, 54, 54, 54, 53, 53, 53, 53,
+ 243, 244, 244, 244, 244, 223, 222, 222,
+ 222, 222, 221, 221, 221, 221, 221, 220,
+ 220, 220, 220, 220, 219, 55, 54, 54,
+ 54, 54, 54, 53, 53, 53, 53, 53,
+ 244, 244, 244, 244, 244, 141, 222, 222,
+ 222, 222, 221, 221, 221, 221, 221, 220,
+ 220, 220, 220, 220, 55, 55, 54, 54,
+ 54, 54, 53, 53, 53, 53, 53, 53,
+ 244, 244, 244, 244, 141, 141, 141, 222,
+ 222, 222, 221, 221, 221, 221, 221, 220,
+ 220, 220, 220, 55, 55, 55, 54, 54,
+ 54, 53, 53, 53, 53, 53, 53, 53,
+ 244, 244, 244, 141, 141, 141, 140, 140,
+ 140, 222, 221, 221, 221, 221, 221, 220,
+ 220, 220, 220, 55, 55, 55, 54, 54,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 244, 141, 141, 141, 140, 140, 140, 140,
+ 140, 40, 40, 39, 221, 221, 221, 220,
+ 220, 220, 55, 55, 55, 54, 54, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 191, 201, 201, 201, 140, 140, 140, 140,
+ 139, 139, 39, 39, 38, 90, 14, 14,
+ 14, 14, 55, 55, 55, 54, 53, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 191, 191, 201, 201, 140, 140, 140, 139,
+ 139, 139, 39, 38, 38, 90, 14, 14,
+ 14, 14, 73, 73, 55, 53, 53, 53,
+ 53, 53, 53, 53, 53, 52, 52, 52,
+ 191, 191, 191, 201, 156, 156, 139, 139,
+ 139, 39, 38, 38, 90, 14, 14, 14,
+ 14, 88, 88, 72, 72, 53, 53, 53,
+ 53, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 191, 191, 156, 156, 155, 155,
+ 155, 38, 38, 38, 90, 14, 14, 14,
+ 88, 88, 88, 72, 72, 72, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 191, 191, 155, 155, 155, 155,
+ 155, 122, 38, 37, 37, 89, 89, 88,
+ 88, 88, 88, 72, 72, 71, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 191, 191, 191, 155, 155, 155,
+ 122, 122, 122, 37, 37, 89, 89, 88,
+ 88, 88, 88, 72, 72, 71, 71, 52,
+ 52, 52, 52, 52, 52, 52, 51, 51,
+ 191, 191, 191, 191, 191, 155, 155, 122,
+ 122, 122, 121, 121, 89, 89, 88, 88,
+ 88, 88, 87, 72, 71, 71, 71, 71,
+ 52, 52, 52, 51, 51, 51, 51, 51,
+ 191, 191, 191, 191, 191, 155, 154, 122,
+ 121, 121, 121, 121, 121, 89, 88, 88,
+ 88, 87, 87, 71, 71, 71, 71, 70,
+ 70, 51, 51, 51, 51, 51, 51, 51,
+ 191, 191, 191, 191, 191, 191, 121, 121,
+ 121, 121, 121, 121, 120, 120, 88, 88,
+ 87, 87, 87, 87, 71, 71, 71, 69,
+ 69, 69, 51, 51, 51, 51, 51, 50,
+ 191, 190, 190, 190, 190, 190, 121, 121,
+ 121, 121, 120, 120, 120, 120, 88, 87,
+ 87, 87, 87, 86, 71, 71, 69, 69,
+ 69, 69, 69, 68, 51, 50, 50, 50,
+ 190, 190, 190, 190, 190, 190, 121, 121,
+ 121, 120, 120, 120, 120, 120, 119, 87,
+ 87, 87, 86, 86, 71, 69, 69, 69,
+ 69, 69, 68, 68, 68, 68, 50, 50,
+ 190, 190, 190, 190, 190, 105, 104, 120,
+ 120, 120, 120, 120, 120, 119, 119, 87,
+ 87, 86, 86, 86, 69, 69, 69, 69,
+ 69, 68, 68, 68, 68, 68, 68, 50,
+ 3, 190, 190, 190, 104, 104, 104, 104,
+ 104, 120, 120, 119, 119, 119, 119, 118,
+ 87, 86, 86, 86, 69, 69, 69, 69,
+ 68, 68, 68, 68, 68, 68, 68, 68,
+ 3, 3, 190, 190, 104, 104, 104, 104,
+ 104, 103, 103, 119, 119, 118, 118, 118,
+ 118, 86, 86, 86, 69, 69, 69, 68,
+ 68, 68, 68, 68, 68, 68, 68, 67,
+ 3, 3, 189, 102, 102, 102, 102, 102,
+ 102, 101, 101, 101, 101, 100, 118, 118,
+ 118, 117, 86, 85, 69, 69, 68, 68,
+ 68, 68, 68, 68, 68, 67, 67, 67,
+ 3, 189, 102, 102, 102, 102, 102, 102,
+ 101, 101, 101, 101, 100, 100, 100, 100,
+ 100, 99, 99, 85, 69, 68, 68, 68,
+ 68, 68, 68, 67, 67, 67, 67, 67,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 101, 101, 101, 101, 100, 100, 100, 100,
+ 99, 99, 99, 99, 68, 68, 68, 68,
+ 68, 67, 67, 67, 67, 67, 67, 205,
+ 102, 102, 102, 102, 102, 102, 102, 102,
+ 101, 101, 101, 101, 100, 100, 100, 100,
+ 99, 99, 99, 99, 99, 68, 68, 67,
+ 67, 67, 67, 206, 206, 206, 205, 205,
+ 102, 102, 102, 102, 102, 102, 102, 101,
+ 101, 101, 101, 100, 100, 100, 100, 100,
+ 99, 99, 99, 99, 99, 98, 67, 67,
+ 206, 206, 206, 206, 206, 205, 205, 205,
+ 102, 102, 102, 102, 102, 102, 102, 101,
+ 101, 101, 101, 100, 100, 100, 100, 99,
+ 99, 99, 99, 99, 98, 98, 206, 206,
+ 206, 206, 206, 206, 205, 205, 205, 205,
+ 102, 102, 102, 102, 102, 102, 101, 101,
+ 101, 101, 101, 100, 100, 100, 100, 99,
+ 99, 99, 98, 98, 98, 98, 206, 206,
+ 206, 206, 206, 205, 205, 205, 205, 205,
+ 102, 102, 102, 102, 102, 102, 101, 101,
+ 101, 101, 101, 100, 100, 100, 100, 99,
+ 98, 98, 98, 98, 98, 98, 206, 206,
+ 206, 206, 205, 205, 205, 205, 205, 205,
+ 102, 102, 102, 102, 102, 102, 101, 101,
+ 101, 101, 100, 100, 100, 100, 99, 98,
+ 98, 98, 98, 98, 98, 97, 206, 206,
+ 206, 205, 205, 205, 205, 205, 205, 205,
+ 102, 102, 102, 102, 102, 101, 101, 101,
+ 101, 101, 100, 100, 100, 100, 98, 98,
+ 98, 98, 98, 98, 98, 97, 206, 206,
+ 205, 205, 205, 205, 205, 205, 205, 205,
+ 250, 102, 102, 102, 102, 101, 101, 101,
+ 101, 101, 100, 100, 100, 98, 98, 98,
+ 98, 98, 98, 98, 98, 97, 206, 205,
+ 205, 205, 205, 205, 205, 205, 205, 205,
+ 243, 243, 243, 243, 243, 243, 222, 222,
+ 222, 222, 221, 221, 221, 221, 221, 220,
+ 220, 5, 5, 5, 5, 5, 5, 5,
+ 54, 54, 54, 54, 53, 53, 53, 53,
+ 243, 243, 243, 243, 243, 243, 222, 222,
+ 222, 222, 221, 221, 221, 221, 220, 220,
+ 220, 220, 5, 5, 5, 5, 219, 54,
+ 54, 54, 54, 53, 53, 53, 53, 53,
+ 243, 243, 243, 243, 243, 243, 222, 222,
+ 222, 221, 221, 221, 221, 221, 220, 220,
+ 220, 220, 220, 219, 219, 219, 219, 54,
+ 54, 54, 53, 53, 53, 53, 53, 53,
+ 243, 243, 243, 243, 175, 175, 222, 222,
+ 222, 221, 221, 221, 221, 221, 220, 220,
+ 220, 220, 220, 219, 219, 219, 54, 54,
+ 54, 53, 53, 53, 53, 53, 53, 53,
+ 243, 243, 243, 175, 175, 201, 140, 222,
+ 222, 221, 221, 221, 221, 221, 220, 220,
+ 220, 220, 220, 219, 219, 55, 54, 54,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 243, 243, 175, 175, 201, 201, 201, 140,
+ 140, 221, 221, 221, 221, 221, 220, 220,
+ 220, 220, 220, 219, 219, 54, 54, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 175, 175, 201, 201, 201, 201, 201, 140,
+ 139, 139, 139, 221, 221, 221, 220, 220,
+ 220, 220, 219, 219, 55, 54, 53, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 191, 201, 201, 201, 201, 201, 201, 139,
+ 139, 139, 139, 38, 14, 14, 14, 14,
+ 14, 14, 14, 55, 55, 53, 53, 53,
+ 53, 53, 53, 53, 53, 52, 52, 52,
+ 191, 191, 201, 201, 201, 201, 201, 139,
+ 139, 139, 38, 38, 14, 14, 14, 14,
+ 14, 14, 14, 88, 53, 53, 53, 53,
+ 53, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 191, 201, 201, 201, 201, 139,
+ 139, 139, 38, 38, 14, 14, 14, 14,
+ 14, 14, 88, 88, 72, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 191, 191, 201, 201, 155, 155,
+ 155, 138, 38, 37, 14, 14, 14, 14,
+ 14, 88, 88, 88, 72, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 191, 191, 201, 155, 155, 155,
+ 138, 138, 37, 37, 37, 14, 14, 14,
+ 88, 88, 88, 88, 72, 71, 52, 52,
+ 52, 52, 52, 52, 52, 52, 51, 51,
+ 191, 191, 191, 191, 191, 155, 155, 154,
+ 154, 154, 37, 37, 36, 36, 89, 88,
+ 88, 88, 87, 87, 71, 71, 52, 52,
+ 52, 52, 52, 51, 51, 51, 51, 51,
+ 191, 191, 191, 191, 191, 155, 154, 154,
+ 154, 154, 121, 36, 36, 36, 88, 88,
+ 88, 87, 87, 87, 71, 71, 71, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ 191, 191, 191, 191, 191, 154, 154, 154,
+ 121, 121, 121, 121, 36, 35, 88, 88,
+ 87, 87, 87, 87, 86, 71, 71, 69,
+ 51, 51, 51, 51, 51, 51, 51, 50,
+ 191, 190, 190, 190, 190, 190, 154, 121,
+ 121, 121, 120, 120, 120, 120, 88, 87,
+ 87, 87, 87, 86, 86, 71, 69, 69,
+ 69, 69, 51, 51, 51, 50, 50, 50,
+ 190, 190, 190, 190, 190, 190, 121, 121,
+ 121, 120, 120, 120, 120, 120, 119, 87,
+ 87, 87, 86, 86, 86, 69, 69, 69,
+ 69, 69, 68, 50, 50, 50, 50, 50,
+ 190, 190, 190, 190, 190, 190, 121, 120,
+ 120, 120, 120, 120, 120, 119, 119, 87,
+ 87, 86, 86, 86, 86, 69, 69, 69,
+ 69, 68, 68, 68, 68, 50, 50, 50,
+ 3, 190, 190, 190, 190, 190, 120, 120,
+ 120, 120, 120, 119, 119, 119, 119, 118,
+ 87, 86, 86, 86, 86, 69, 69, 69,
+ 68, 68, 68, 68, 68, 68, 50, 50,
+ 3, 3, 190, 190, 190, 190, 104, 120,
+ 120, 120, 119, 119, 119, 119, 118, 118,
+ 118, 86, 86, 86, 85, 69, 69, 68,
+ 68, 68, 68, 68, 68, 68, 67, 67,
+ 3, 3, 189, 189, 189, 102, 104, 104,
+ 103, 119, 119, 119, 118, 118, 118, 118,
+ 118, 86, 86, 85, 85, 69, 68, 68,
+ 68, 68, 68, 68, 67, 67, 67, 67,
+ 3, 189, 189, 189, 102, 102, 102, 102,
+ 101, 101, 101, 101, 118, 118, 118, 118,
+ 117, 117, 85, 85, 85, 68, 68, 68,
+ 68, 68, 67, 67, 67, 67, 67, 67,
+ 3, 189, 189, 102, 102, 102, 102, 101,
+ 101, 101, 101, 100, 100, 100, 100, 100,
+ 99, 99, 117, 85, 85, 68, 68, 68,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 189, 189, 102, 102, 102, 102, 102, 101,
+ 101, 101, 101, 100, 100, 100, 100, 99,
+ 99, 99, 99, 99, 84, 68, 67, 67,
+ 67, 67, 67, 67, 67, 67, 66, 66,
+ 189, 102, 102, 102, 102, 102, 102, 101,
+ 101, 101, 101, 100, 100, 100, 100, 99,
+ 99, 99, 99, 99, 99, 67, 67, 67,
+ 67, 67, 67, 67, 66, 205, 205, 205,
+ 102, 102, 102, 102, 102, 102, 101, 101,
+ 101, 101, 100, 100, 100, 100, 100, 99,
+ 99, 99, 99, 99, 98, 97, 67, 67,
+ 67, 67, 205, 205, 205, 205, 205, 205,
+ 102, 102, 102, 102, 102, 102, 101, 101,
+ 101, 101, 100, 100, 100, 100, 99, 99,
+ 99, 99, 99, 98, 98, 97, 97, 206,
+ 205, 205, 205, 205, 205, 205, 205, 205,
+ 102, 102, 102, 102, 102, 101, 101, 101,
+ 101, 101, 100, 100, 100, 100, 99, 99,
+ 99, 98, 98, 98, 98, 97, 97, 205,
+ 205, 205, 205, 205, 205, 205, 205, 205,
+ 102, 102, 102, 102, 102, 101, 101, 101,
+ 101, 101, 100, 100, 100, 100, 99, 98,
+ 98, 98, 98, 98, 98, 97, 97, 205,
+ 205, 205, 205, 205, 205, 205, 205, 204,
+ 102, 102, 102, 102, 102, 101, 101, 101,
+ 101, 100, 100, 100, 100, 99, 98, 98,
+ 98, 98, 98, 98, 97, 97, 205, 205,
+ 205, 205, 205, 205, 205, 205, 204, 204,
+ 102, 102, 102, 102, 101, 101, 101, 101,
+ 101, 100, 100, 100, 100, 98, 98, 98,
+ 98, 98, 98, 98, 97, 97, 205, 205,
+ 205, 205, 205, 205, 205, 204, 204, 204,
+ 102, 102, 102, 102, 101, 101, 101, 101,
+ 101, 100, 100, 100, 98, 98, 98, 98,
+ 98, 98, 98, 98, 97, 97, 205, 205,
+ 205, 205, 205, 205, 204, 204, 204, 204,
+ 242, 242, 242, 242, 242, 242, 222, 222,
+ 222, 221, 221, 221, 221, 221, 220, 220,
+ 5, 5, 5, 5, 5, 5, 5, 218,
+ 218, 218, 218, 53, 53, 53, 53, 53,
+ 242, 242, 242, 242, 242, 175, 222, 222,
+ 222, 221, 221, 221, 221, 220, 220, 220,
+ 220, 5, 5, 5, 5, 219, 219, 218,
+ 218, 218, 53, 53, 53, 53, 53, 53,
+ 242, 242, 242, 242, 175, 175, 175, 222,
+ 221, 221, 221, 221, 221, 220, 220, 220,
+ 220, 220, 219, 219, 219, 219, 219, 218,
+ 218, 53, 53, 53, 53, 53, 53, 53,
+ 242, 242, 242, 175, 175, 175, 175, 222,
+ 221, 221, 221, 221, 221, 220, 220, 220,
+ 220, 220, 219, 219, 219, 219, 218, 218,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 242, 175, 175, 175, 175, 175, 201, 201,
+ 221, 221, 221, 221, 221, 220, 220, 220,
+ 220, 220, 219, 219, 219, 219, 218, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 175, 175, 175, 175, 175, 201, 201, 201,
+ 201, 221, 221, 221, 221, 220, 220, 220,
+ 220, 220, 219, 219, 219, 219, 53, 53,
+ 53, 53, 53, 53, 53, 53, 53, 53,
+ 175, 175, 175, 201, 201, 201, 201, 201,
+ 201, 139, 139, 221, 221, 220, 220, 220,
+ 220, 219, 219, 219, 219, 219, 53, 53,
+ 53, 53, 53, 53, 53, 52, 52, 52,
+ 175, 201, 201, 201, 201, 201, 201, 201,
+ 139, 139, 139, 38, 14, 14, 14, 14,
+ 14, 14, 219, 219, 219, 53, 53, 53,
+ 53, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 201, 201, 201, 201, 201, 201,
+ 139, 138, 138, 14, 14, 14, 14, 14,
+ 14, 14, 14, 13, 53, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 191, 201, 201, 201, 201, 201,
+ 138, 138, 138, 14, 14, 14, 14, 14,
+ 14, 14, 88, 13, 13, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 191, 201, 201, 201, 201, 138,
+ 138, 138, 138, 37, 14, 14, 14, 14,
+ 14, 88, 88, 13, 13, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 51, 51,
+ 191, 191, 191, 191, 201, 201, 155, 138,
+ 138, 138, 138, 37, 14, 14, 14, 14,
+ 88, 88, 13, 87, 87, 52, 52, 52,
+ 52, 52, 52, 51, 51, 51, 51, 51,
+ 191, 191, 191, 191, 191, 155, 154, 154,
+ 154, 154, 137, 36, 36, 36, 35, 88,
+ 88, 88, 87, 87, 87, 86, 52, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ 191, 191, 191, 191, 191, 154, 154, 154,
+ 154, 154, 153, 36, 36, 35, 35, 88,
+ 88, 87, 87, 87, 86, 86, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 50,
+ 191, 190, 190, 190, 190, 154, 154, 154,
+ 154, 153, 153, 120, 35, 35, 35, 34,
+ 87, 87, 87, 86, 86, 86, 69, 51,
+ 51, 51, 51, 51, 51, 50, 50, 50,
+ 190, 190, 190, 190, 190, 190, 154, 153,
+ 153, 120, 120, 120, 120, 35, 34, 87,
+ 87, 87, 86, 86, 86, 86, 69, 69,
+ 69, 51, 51, 50, 50, 50, 50, 50,
+ 190, 190, 190, 190, 190, 190, 153, 153,
+ 120, 120, 120, 120, 120, 119, 34, 87,
+ 87, 86, 86, 86, 86, 85, 69, 69,
+ 69, 68, 50, 50, 50, 50, 50, 50,
+ 3, 190, 190, 190, 190, 190, 120, 120,
+ 120, 120, 120, 119, 119, 119, 119, 87,
+ 87, 86, 86, 86, 85, 85, 69, 69,
+ 68, 68, 68, 68, 50, 50, 50, 50,
+ 3, 3, 190, 190, 190, 190, 120, 120,
+ 120, 120, 119, 119, 119, 119, 118, 118,
+ 86, 86, 86, 85, 85, 85, 69, 68,
+ 68, 68, 68, 68, 68, 67, 49, 49,
+ 3, 3, 189, 189, 189, 189, 120, 120,
+ 120, 119, 119, 119, 119, 118, 118, 118,
+ 118, 86, 85, 85, 85, 85, 68, 68,
+ 68, 68, 68, 67, 67, 67, 67, 49,
+ 3, 189, 189, 189, 189, 189, 102, 120,
+ 119, 119, 119, 119, 118, 118, 118, 118,
+ 117, 117, 85, 85, 85, 85, 68, 68,
+ 68, 67, 67, 67, 67, 67, 67, 67,
+ 3, 189, 189, 189, 189, 102, 102, 101,
+ 101, 101, 118, 118, 118, 118, 118, 118,
+ 117, 117, 85, 85, 85, 84, 68, 67,
+ 67, 67, 67, 67, 67, 67, 67, 66,
+ 189, 189, 189, 189, 102, 102, 101, 101,
+ 101, 101, 100, 100, 100, 100, 100, 117,
+ 117, 117, 116, 84, 84, 84, 67, 67,
+ 67, 67, 67, 67, 67, 66, 66, 66,
+ 189, 189, 189, 102, 102, 102, 101, 101,
+ 101, 101, 100, 100, 100, 100, 99, 99,
+ 99, 99, 99, 116, 84, 67, 67, 67,
+ 67, 67, 67, 67, 66, 66, 66, 66,
+ 188, 188, 102, 102, 102, 102, 101, 101,
+ 101, 101, 100, 100, 100, 100, 99, 99,
+ 99, 99, 99, 99, 99, 67, 67, 67,
+ 67, 67, 66, 66, 66, 66, 66, 204,
+ 188, 188, 102, 102, 102, 101, 101, 101,
+ 101, 100, 100, 100, 100, 100, 99, 99,
+ 99, 99, 99, 98, 97, 97, 67, 67,
+ 66, 66, 66, 204, 204, 204, 204, 204,
+ 188, 102, 102, 102, 102, 101, 101, 101,
+ 101, 100, 100, 100, 100, 99, 99, 99,
+ 99, 99, 98, 98, 97, 97, 97, 66,
+ 204, 204, 204, 204, 204, 204, 204, 204,
+ 102, 102, 102, 102, 101, 101, 101, 101,
+ 101, 100, 100, 100, 100, 99, 99, 99,
+ 98, 98, 98, 98, 97, 97, 97, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204,
+ 102, 102, 102, 102, 101, 101, 101, 101,
+ 101, 100, 100, 100, 100, 99, 98, 98,
+ 98, 98, 98, 98, 97, 97, 97, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204,
+ 102, 102, 102, 102, 101, 101, 101, 101,
+ 100, 100, 100, 100, 99, 98, 98, 98,
+ 98, 98, 98, 97, 97, 97, 97, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204,
+ 102, 102, 102, 101, 101, 101, 101, 101,
+ 100, 100, 100, 100, 98, 98, 98, 98,
+ 98, 98, 98, 97, 97, 97, 97, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204,
+ 102, 102, 102, 101, 101, 101, 101, 101,
+ 100, 100, 100, 98, 98, 98, 98, 98,
+ 98, 98, 98, 97, 97, 97, 97, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204,
+ 242, 242, 241, 241, 241, 241, 175, 222,
+ 221, 221, 221, 221, 221, 220, 220, 5,
+ 5, 5, 5, 5, 5, 5, 218, 218,
+ 218, 218, 217, 217, 217, 53, 53, 53,
+ 242, 241, 241, 241, 175, 175, 175, 222,
+ 221, 221, 221, 221, 220, 220, 220, 220,
+ 5, 5, 5, 5, 219, 219, 218, 218,
+ 218, 218, 217, 217, 53, 53, 53, 53,
+ 241, 241, 241, 175, 175, 175, 175, 175,
+ 221, 221, 221, 221, 220, 220, 220, 220,
+ 220, 219, 219, 219, 219, 219, 218, 218,
+ 218, 218, 53, 53, 53, 53, 53, 53,
+ 241, 241, 175, 175, 175, 175, 175, 175,
+ 221, 221, 221, 221, 220, 220, 220, 220,
+ 220, 219, 219, 219, 219, 218, 218, 218,
+ 218, 53, 53, 53, 53, 53, 53, 53,
+ 175, 175, 175, 175, 175, 175, 175, 201,
+ 221, 221, 221, 221, 220, 220, 220, 220,
+ 220, 219, 219, 219, 219, 218, 218, 218,
+ 218, 53, 53, 53, 53, 53, 53, 53,
+ 175, 175, 175, 175, 175, 175, 201, 201,
+ 201, 221, 221, 221, 220, 220, 220, 220,
+ 220, 219, 219, 219, 219, 218, 218, 218,
+ 53, 53, 53, 53, 53, 52, 52, 52,
+ 175, 175, 175, 175, 201, 201, 201, 201,
+ 201, 201, 139, 221, 220, 220, 220, 220,
+ 219, 219, 219, 219, 219, 218, 218, 53,
+ 53, 52, 52, 52, 52, 52, 52, 52,
+ 175, 175, 201, 201, 201, 201, 201, 201,
+ 201, 138, 138, 14, 14, 14, 14, 14,
+ 14, 219, 219, 219, 219, 218, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 201, 201, 201, 201, 201, 201,
+ 201, 138, 138, 14, 14, 14, 14, 14,
+ 14, 14, 13, 13, 13, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 191, 191, 201, 201, 201, 201, 201, 201,
+ 138, 138, 138, 14, 14, 14, 14, 14,
+ 14, 14, 13, 13, 13, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 51, 51,
+ 191, 191, 191, 201, 201, 201, 201, 138,
+ 138, 138, 138, 137, 14, 14, 14, 14,
+ 14, 13, 13, 13, 13, 52, 52, 52,
+ 52, 52, 52, 51, 51, 51, 51, 51,
+ 191, 191, 191, 191, 201, 200, 200, 138,
+ 138, 137, 137, 137, 14, 14, 14, 14,
+ 13, 13, 13, 13, 13, 13, 52, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ 191, 191, 191, 191, 191, 200, 154, 154,
+ 154, 137, 137, 137, 36, 35, 35, 13,
+ 13, 13, 13, 13, 86, 86, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 50,
+ 191, 190, 190, 190, 190, 200, 154, 154,
+ 153, 153, 153, 153, 35, 35, 35, 34,
+ 13, 13, 13, 86, 86, 86, 51, 51,
+ 51, 51, 51, 51, 51, 50, 50, 50,
+ 190, 190, 190, 190, 190, 190, 154, 153,
+ 153, 153, 153, 153, 35, 35, 34, 34,
+ 13, 87, 86, 86, 86, 86, 85, 51,
+ 51, 51, 51, 50, 50, 50, 50, 50,
+ 190, 190, 190, 190, 190, 190, 153, 153,
+ 153, 153, 153, 120, 120, 34, 34, 33,
+ 87, 86, 86, 86, 86, 85, 85, 69,
+ 51, 51, 50, 50, 50, 50, 50, 50,
+ 3, 190, 190, 190, 190, 190, 153, 153,
+ 153, 120, 120, 119, 119, 119, 33, 33,
+ 33, 86, 86, 86, 85, 85, 85, 69,
+ 68, 50, 50, 50, 50, 50, 50, 50,
+ 3, 3, 190, 190, 190, 190, 153, 153,
+ 120, 120, 119, 119, 119, 119, 119, 33,
+ 86, 86, 86, 85, 85, 85, 85, 68,
+ 68, 68, 68, 50, 50, 50, 49, 49,
+ 3, 3, 189, 189, 189, 189, 189, 120,
+ 120, 119, 119, 119, 119, 118, 118, 118,
+ 86, 86, 85, 85, 85, 85, 85, 68,
+ 68, 67, 67, 67, 67, 49, 49, 49,
+ 3, 189, 189, 189, 189, 189, 189, 120,
+ 119, 119, 119, 119, 118, 118, 118, 118,
+ 117, 85, 85, 85, 85, 85, 84, 67,
+ 67, 67, 67, 67, 67, 67, 49, 49,
+ 3, 189, 189, 189, 189, 189, 189, 119,
+ 119, 119, 119, 118, 118, 118, 118, 117,
+ 117, 117, 85, 85, 85, 84, 84, 67,
+ 67, 67, 67, 67, 67, 67, 66, 66,
+ 189, 189, 189, 189, 189, 189, 189, 101,
+ 101, 118, 118, 118, 118, 118, 118, 117,
+ 117, 117, 116, 84, 84, 84, 67, 67,
+ 67, 67, 67, 67, 67, 66, 66, 66,
+ 189, 189, 189, 189, 189, 189, 101, 101,
+ 101, 100, 100, 100, 100, 118, 117, 117,
+ 117, 116, 116, 116, 84, 84, 67, 67,
+ 67, 67, 67, 66, 66, 66, 66, 66,
+ 188, 188, 188, 188, 188, 101, 101, 101,
+ 101, 100, 100, 100, 100, 99, 99, 99,
+ 99, 116, 116, 116, 84, 84, 67, 67,
+ 67, 66, 66, 66, 66, 66, 66, 66,
+ 188, 188, 188, 188, 102, 101, 101, 101,
+ 101, 100, 100, 100, 100, 99, 99, 99,
+ 99, 99, 99, 99, 115, 83, 67, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 188, 188, 188, 188, 101, 101, 101, 101,
+ 100, 100, 100, 100, 100, 99, 99, 99,
+ 99, 99, 98, 97, 97, 97, 66, 66,
+ 66, 66, 66, 66, 66, 204, 204, 204,
+ 188, 188, 188, 102, 101, 101, 101, 101,
+ 100, 100, 100, 100, 99, 99, 99, 99,
+ 99, 98, 98, 97, 97, 97, 97, 66,
+ 66, 204, 204, 204, 204, 204, 204, 204,
+ 188, 188, 102, 101, 101, 101, 101, 101,
+ 100, 100, 100, 100, 99, 99, 99, 98,
+ 98, 98, 98, 97, 97, 97, 97, 96,
+ 204, 204, 204, 204, 204, 204, 204, 204,
+ 187, 187, 102, 101, 101, 101, 101, 101,
+ 100, 100, 100, 100, 99, 98, 98, 98,
+ 98, 98, 98, 97, 97, 97, 97, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204,
+ 187, 102, 102, 101, 101, 101, 101, 100,
+ 100, 100, 100, 99, 98, 98, 98, 98,
+ 98, 98, 97, 97, 97, 97, 97, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204,
+ 187, 102, 101, 101, 101, 101, 101, 100,
+ 100, 100, 100, 98, 98, 98, 98, 98,
+ 98, 98, 97, 97, 97, 97, 96, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204,
+ 187, 102, 101, 101, 101, 101, 101, 100,
+ 100, 100, 98, 98, 98, 98, 98, 98,
+ 98, 98, 97, 97, 97, 97, 96, 204,
+ 204, 204, 204, 204, 204, 204, 204, 203,
+ 241, 241, 241, 241, 241, 175, 175, 175,
+ 221, 221, 221, 221, 220, 220, 5, 5,
+ 5, 5, 5, 5, 5, 218, 218, 218,
+ 218, 217, 217, 217, 217, 217, 216, 216,
+ 241, 241, 241, 241, 175, 175, 175, 175,
+ 221, 221, 221, 220, 220, 220, 220, 5,
+ 5, 5, 5, 219, 219, 218, 218, 218,
+ 218, 217, 217, 217, 217, 217, 53, 53,
+ 241, 241, 175, 175, 175, 175, 175, 175,
+ 221, 221, 221, 220, 220, 220, 220, 220,
+ 219, 219, 219, 219, 219, 218, 218, 218,
+ 218, 217, 217, 217, 217, 53, 53, 53,
+ 241, 175, 175, 175, 175, 175, 175, 175,
+ 221, 221, 221, 220, 220, 220, 220, 220,
+ 219, 219, 219, 219, 218, 218, 218, 218,
+ 218, 217, 217, 217, 53, 53, 53, 53,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 201, 221, 221, 220, 220, 220, 220, 220,
+ 219, 219, 219, 219, 218, 218, 218, 218,
+ 218, 217, 53, 53, 53, 52, 52, 52,
+ 175, 175, 175, 175, 175, 175, 175, 201,
+ 201, 201, 221, 220, 220, 220, 220, 220,
+ 219, 219, 219, 219, 218, 218, 218, 218,
+ 218, 52, 52, 52, 52, 52, 52, 52,
+ 175, 175, 175, 175, 175, 201, 201, 201,
+ 201, 201, 138, 220, 220, 220, 220, 219,
+ 219, 219, 219, 219, 218, 218, 218, 218,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 175, 175, 175, 201, 201, 201, 201, 201,
+ 201, 201, 138, 138, 14, 14, 14, 14,
+ 219, 219, 219, 219, 218, 218, 218, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52,
+ 191, 201, 201, 201, 201, 201, 201, 201,
+ 200, 138, 138, 138, 14, 14, 14, 14,
+ 14, 13, 13, 13, 218, 218, 52, 52,
+ 52, 52, 52, 52, 52, 52, 51, 51,
+ 191, 191, 201, 201, 201, 201, 200, 200,
+ 200, 138, 137, 137, 14, 14, 14, 14,
+ 14, 13, 13, 13, 13, 13, 52, 52,
+ 52, 52, 52, 51, 51, 51, 51, 51,
+ 191, 191, 191, 201, 201, 200, 200, 200,
+ 200, 137, 137, 137, 14, 14, 14, 14,
+ 13, 13, 13, 13, 13, 13, 52, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ 191, 191, 191, 191, 200, 200, 200, 200,
+ 137, 137, 137, 137, 14, 14, 14, 13,
+ 13, 13, 13, 13, 13, 13, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 50,
+ 191, 190, 190, 190, 200, 200, 200, 200,
+ 137, 137, 137, 137, 136, 35, 13, 13,
+ 13, 13, 13, 13, 13, 86, 51, 51,
+ 51, 51, 51, 51, 51, 50, 50, 50,
+ 190, 190, 190, 190, 190, 200, 200, 153,
+ 153, 153, 153, 136, 136, 35, 34, 13,
+ 13, 13, 13, 13, 86, 86, 85, 51,
+ 51, 51, 51, 50, 50, 50, 50, 50,
+ 190, 190, 190, 190, 190, 190, 153, 153,
+ 153, 153, 153, 136, 136, 34, 34, 13,
+ 13, 13, 86, 86, 86, 85, 85, 51,
+ 51, 51, 50, 50, 50, 50, 50, 50,
+ 3, 190, 190, 190, 190, 190, 153, 153,
+ 153, 153, 152, 152, 152, 34, 33, 33,
+ 33, 86, 86, 86, 85, 85, 85, 85,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 3, 3, 190, 190, 190, 190, 153, 153,
+ 152, 152, 152, 152, 119, 119, 33, 33,
+ 32, 86, 86, 85, 85, 85, 85, 85,
+ 50, 50, 50, 50, 50, 50, 49, 49,
+ 3, 3, 189, 189, 189, 189, 189, 152,
+ 152, 152, 119, 119, 119, 119, 118, 32,
+ 32, 86, 85, 85, 85, 85, 85, 84,
+ 67, 67, 49, 49, 49, 49, 49, 49,
+ 3, 189, 189, 189, 189, 189, 189, 152,
+ 152, 119, 119, 119, 118, 118, 118, 118,
+ 32, 31, 85, 85, 85, 85, 84, 84,
+ 67, 67, 67, 67, 49, 49, 49, 49,
+ 3, 189, 189, 189, 189, 189, 189, 152,
+ 119, 119, 119, 118, 118, 118, 118, 117,
+ 117, 117, 85, 85, 85, 84, 84, 67,
+ 67, 67, 67, 67, 67, 49, 49, 49,
+ 189, 189, 189, 189, 189, 189, 189, 119,
+ 119, 119, 118, 118, 118, 118, 117, 117,
+ 117, 117, 85, 84, 84, 84, 84, 67,
+ 67, 67, 67, 67, 66, 66, 66, 48,
+ 189, 189, 189, 189, 189, 189, 189, 119,
+ 118, 118, 118, 118, 118, 118, 117, 117,
+ 117, 116, 116, 84, 84, 84, 84, 67,
+ 67, 67, 66, 66, 66, 66, 66, 66,
+ 188, 188, 188, 188, 188, 188, 188, 101,
+ 100, 100, 100, 118, 118, 117, 117, 117,
+ 116, 116, 116, 116, 84, 84, 83, 67,
+ 66, 66, 66, 66, 66, 66, 66, 66,
+ 188, 188, 188, 188, 188, 188, 101, 101,
+ 100, 100, 100, 100, 99, 99, 99, 99,
+ 116, 116, 116, 116, 115, 83, 83, 66,
+ 66, 66, 66, 66, 66, 66, 66, 65,
+ 188, 188, 188, 188, 188, 101, 101, 101,
+ 100, 100, 100, 100, 99, 99, 99, 99,
+ 99, 99, 116, 115, 115, 83, 83, 66,
+ 66, 66, 66, 66, 66, 65, 65, 65,
+ 188, 188, 188, 188, 188, 101, 101, 100,
+ 100, 100, 100, 100, 99, 99, 99, 99,
+ 99, 98, 97, 97, 97, 97, 83, 66,
+ 66, 66, 66, 65, 65, 65, 65, 203,
+ 188, 188, 188, 188, 101, 101, 101, 100,
+ 100, 100, 100, 99, 99, 99, 99, 99,
+ 98, 98, 97, 97, 97, 97, 96, 66,
+ 65, 65, 65, 204, 204, 204, 203, 203,
+ 187, 187, 187, 187, 101, 101, 101, 100,
+ 100, 100, 100, 99, 99, 99, 98, 98,
+ 98, 98, 97, 97, 97, 97, 96, 96,
+ 204, 204, 204, 204, 204, 203, 203, 203,
+ 187, 187, 187, 101, 101, 101, 101, 100,
+ 100, 100, 100, 99, 98, 98, 98, 98,
+ 98, 98, 97, 97, 97, 97, 96, 96,
+ 204, 204, 204, 204, 203, 203, 203, 203,
+ 187, 187, 187, 101, 101, 101, 100, 100,
+ 100, 100, 99, 98, 98, 98, 98, 98,
+ 98, 97, 97, 97, 97, 97, 96, 96,
+ 204, 204, 204, 203, 203, 203, 203, 203,
+ 187, 187, 187, 101, 101, 101, 100, 100,
+ 100, 100, 98, 98, 98, 98, 98, 98,
+ 98, 97, 97, 97, 97, 96, 96, 96,
+ 204, 204, 203, 203, 203, 203, 203, 203,
+ 187, 187, 101, 101, 101, 101, 100, 100,
+ 100, 98, 98, 98, 98, 98, 98, 98,
+ 98, 97, 97, 97, 97, 96, 96, 96,
+ 204, 203, 203, 203, 203, 203, 203, 203,
+ 1, 240, 240, 240, 240, 175, 175, 175,
+ 221, 221, 221, 220, 220, 5, 5, 5,
+ 5, 5, 5, 5, 218, 218, 218, 218,
+ 217, 217, 217, 217, 217, 216, 216, 216,
+ 240, 240, 240, 175, 175, 175, 175, 175,
+ 221, 221, 220, 220, 220, 220, 5, 5,
+ 5, 5, 219, 219, 218, 218, 218, 218,
+ 217, 217, 217, 217, 217, 216, 216, 216,
+ 240, 240, 175, 175, 175, 175, 175, 175,
+ 175, 221, 220, 220, 220, 220, 220, 219,
+ 219, 219, 219, 219, 218, 218, 218, 218,
+ 217, 217, 217, 217, 217, 216, 216, 216,
+ 240, 175, 175, 175, 175, 175, 175, 175,
+ 175, 221, 220, 220, 220, 220, 220, 219,
+ 219, 219, 219, 218, 218, 218, 218, 218,
+ 217, 217, 217, 217, 217, 216, 216, 52,
+ 175, 175, 175, 175, 175, 175, 175, 175,
+ 175, 221, 220, 220, 220, 220, 220, 219,
+ 219, 219, 219, 218, 218, 218, 218, 218,
+ 217, 217, 217, 217, 217, 52, 52, 52,
+ 175, 175, 175, 175, 175, 175, 175, 174,
+ 201, 201, 220, 220, 220, 220, 220, 219,
+ 219, 219, 219, 218, 218, 218, 218, 218,
+ 217, 217, 217, 52, 52, 52, 52, 52,
+ 175, 175, 175, 174, 174, 174, 201, 201,
+ 201, 200, 200, 220, 220, 220, 219, 219,
+ 219, 219, 219, 218, 218, 218, 218, 218,
+ 217, 217, 52, 52, 52, 52, 52, 52,
+ 174, 174, 174, 174, 174, 201, 201, 200,
+ 200, 200, 200, 138, 14, 14, 14, 219,
+ 219, 219, 219, 218, 218, 218, 218, 218,
+ 52, 52, 52, 52, 52, 52, 51, 51,
+ 174, 174, 174, 201, 201, 200, 200, 200,
+ 200, 200, 200, 137, 14, 14, 14, 14,
+ 13, 13, 13, 218, 218, 218, 218, 52,
+ 52, 52, 52, 51, 51, 51, 51, 51,
+ 191, 191, 201, 201, 200, 200, 200, 200,
+ 200, 200, 137, 137, 14, 14, 14, 14,
+ 13, 13, 13, 13, 13, 13, 52, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ 191, 191, 191, 200, 200, 200, 200, 200,
+ 200, 200, 137, 137, 136, 14, 14, 13,
+ 13, 13, 13, 13, 13, 13, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 50,
+ 191, 190, 190, 200, 200, 200, 200, 200,
+ 200, 137, 137, 136, 136, 136, 13, 13,
+ 13, 13, 13, 13, 13, 13, 51, 51,
+ 51, 51, 51, 51, 51, 50, 50, 50,
+ 190, 190, 190, 190, 200, 200, 200, 200,
+ 137, 137, 136, 136, 136, 136, 13, 13,
+ 13, 13, 13, 13, 13, 12, 12, 51,
+ 51, 51, 51, 50, 50, 50, 50, 50,
+ 190, 190, 190, 190, 190, 200, 200, 200,
+ 153, 136, 136, 136, 136, 34, 13, 13,
+ 13, 13, 13, 13, 12, 12, 12, 51,
+ 51, 51, 50, 50, 50, 50, 50, 50,
+ 3, 190, 190, 190, 190, 190, 200, 153,
+ 153, 152, 136, 136, 136, 34, 33, 13,
+ 13, 13, 13, 12, 12, 12, 85, 51,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 3, 3, 190, 190, 190, 190, 153, 152,
+ 152, 152, 152, 152, 152, 33, 33, 33,
+ 32, 32, 32, 12, 85, 85, 85, 85,
+ 50, 50, 50, 50, 50, 50, 49, 49,
+ 3, 3, 189, 189, 189, 189, 152, 152,
+ 152, 152, 152, 152, 151, 33, 33, 32,
+ 32, 32, 31, 85, 85, 85, 85, 84,
+ 50, 50, 49, 49, 49, 49, 49, 49,
+ 3, 189, 189, 189, 189, 189, 189, 152,
+ 152, 152, 152, 151, 119, 118, 118, 32,
+ 32, 31, 85, 85, 85, 85, 84, 84,
+ 67, 49, 49, 49, 49, 49, 49, 49,
+ 3, 189, 189, 189, 189, 189, 189, 152,
+ 152, 152, 151, 119, 118, 118, 118, 117,
+ 31, 31, 85, 85, 84, 84, 84, 84,
+ 67, 67, 67, 49, 49, 49, 49, 49,
+ 189, 189, 189, 189, 189, 189, 189, 152,
+ 152, 151, 118, 118, 118, 118, 117, 117,
+ 117, 117, 85, 84, 84, 84, 84, 84,
+ 67, 67, 67, 66, 49, 49, 48, 48,
+ 189, 189, 189, 189, 189, 189, 189, 152,
+ 151, 118, 118, 118, 118, 118, 117, 117,
+ 117, 116, 116, 84, 84, 84, 84, 83,
+ 67, 66, 66, 66, 66, 66, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 118, 118, 118, 118, 118, 117, 117, 117,
+ 116, 116, 116, 84, 84, 84, 83, 83,
+ 66, 66, 66, 66, 66, 66, 66, 48,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 118, 118, 118, 118, 117, 117, 117, 116,
+ 116, 116, 116, 115, 83, 83, 83, 83,
+ 66, 66, 66, 66, 66, 66, 65, 65,
+ 188, 188, 188, 188, 188, 188, 188, 100,
+ 100, 100, 100, 99, 99, 99, 117, 116,
+ 116, 116, 116, 115, 115, 83, 83, 66,
+ 66, 66, 66, 66, 65, 65, 65, 65,
+ 188, 188, 188, 188, 188, 188, 188, 100,
+ 100, 100, 100, 99, 99, 99, 99, 99,
+ 99, 116, 115, 115, 115, 83, 83, 66,
+ 66, 66, 65, 65, 65, 65, 65, 65,
+ 188, 188, 188, 188, 188, 188, 100, 100,
+ 100, 100, 100, 99, 99, 99, 99, 99,
+ 98, 97, 97, 97, 97, 97, 83, 66,
+ 65, 65, 65, 65, 65, 65, 65, 65,
+ 187, 187, 187, 187, 187, 187, 100, 100,
+ 100, 100, 99, 99, 99, 99, 99, 98,
+ 98, 97, 97, 97, 97, 96, 96, 96,
+ 65, 65, 65, 65, 65, 203, 203, 203,
+ 187, 187, 187, 187, 187, 101, 100, 100,
+ 100, 100, 99, 99, 99, 98, 98, 98,
+ 98, 97, 97, 97, 97, 96, 96, 96,
+ 65, 65, 203, 203, 203, 203, 203, 203,
+ 187, 187, 187, 187, 187, 101, 100, 100,
+ 100, 100, 99, 98, 98, 98, 98, 98,
+ 98, 97, 97, 97, 97, 96, 96, 96,
+ 96, 203, 203, 203, 203, 203, 203, 203,
+ 187, 187, 187, 187, 187, 100, 100, 100,
+ 100, 99, 98, 98, 98, 98, 98, 98,
+ 97, 97, 97, 97, 97, 96, 96, 96,
+ 203, 203, 203, 203, 203, 203, 203, 203,
+ 187, 187, 187, 187, 101, 100, 100, 100,
+ 100, 98, 98, 98, 98, 98, 98, 98,
+ 97, 97, 97, 97, 96, 96, 96, 96,
+ 203, 203, 203, 203, 203, 203, 203, 203,
+ 186, 186, 186, 186, 101, 100, 100, 100,
+ 98, 98, 98, 98, 98, 98, 98, 98,
+ 97, 97, 97, 97, 96, 96, 96, 96,
+ 203, 203, 203, 203, 203, 203, 203, 203,
+ 1, 240, 240, 240, 240, 175, 175, 175,
+ 175, 221, 220, 220, 5, 5, 5, 5,
+ 5, 5, 5, 218, 218, 218, 218, 217,
+ 217, 217, 217, 217, 216, 216, 216, 216,
+ 240, 240, 240, 240, 175, 175, 175, 175,
+ 175, 220, 220, 220, 220, 5, 5, 5,
+ 5, 219, 219, 218, 218, 218, 218, 217,
+ 217, 217, 217, 217, 216, 216, 216, 216,
+ 240, 240, 175, 175, 175, 175, 175, 175,
+ 175, 220, 220, 220, 220, 220, 219, 219,
+ 219, 219, 219, 218, 218, 218, 218, 217,
+ 217, 217, 217, 217, 216, 216, 216, 216,
+ 240, 175, 175, 175, 175, 175, 175, 175,
+ 175, 220, 220, 220, 220, 220, 219, 219,
+ 219, 219, 218, 218, 218, 218, 218, 217,
+ 217, 217, 217, 217, 216, 216, 216, 216,
+ 175, 175, 175, 175, 175, 174, 174, 174,
+ 174, 220, 220, 220, 220, 220, 219, 219,
+ 219, 219, 218, 218, 218, 218, 218, 217,
+ 217, 217, 217, 217, 216, 216, 216, 216,
+ 175, 174, 174, 174, 174, 174, 174, 174,
+ 174, 200, 220, 220, 220, 220, 219, 219,
+ 219, 219, 218, 218, 218, 218, 218, 217,
+ 217, 217, 217, 216, 216, 216, 52, 52,
+ 174, 174, 174, 174, 174, 174, 174, 174,
+ 200, 200, 200, 220, 220, 219, 219, 219,
+ 219, 219, 218, 218, 218, 218, 218, 217,
+ 217, 217, 217, 216, 52, 52, 51, 51,
+ 174, 174, 174, 174, 174, 174, 200, 200,
+ 200, 200, 200, 200, 14, 219, 219, 219,
+ 219, 219, 218, 218, 218, 218, 218, 217,
+ 217, 217, 52, 51, 51, 51, 51, 51,
+ 174, 174, 174, 174, 174, 200, 200, 200,
+ 200, 200, 200, 137, 137, 14, 14, 13,
+ 13, 13, 218, 218, 218, 218, 218, 217,
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ 173, 173, 173, 200, 200, 200, 200, 200,
+ 200, 200, 200, 137, 136, 14, 14, 13,
+ 13, 13, 13, 13, 13, 218, 217, 51,
+ 51, 51, 51, 51, 51, 51, 51, 50,
+ 191, 173, 200, 200, 200, 200, 200, 200,
+ 200, 200, 137, 136, 136, 136, 13, 13,
+ 13, 13, 13, 13, 13, 13, 51, 51,
+ 51, 51, 51, 51, 51, 50, 50, 50,
+ 190, 190, 190, 200, 200, 200, 200, 200,
+ 200, 199, 136, 136, 136, 136, 13, 13,
+ 13, 13, 13, 13, 13, 12, 12, 51,
+ 51, 51, 51, 50, 50, 50, 50, 50,
+ 190, 190, 190, 190, 200, 200, 200, 200,
+ 199, 136, 136, 136, 136, 136, 13, 13,
+ 13, 13, 13, 13, 12, 12, 12, 51,
+ 51, 51, 50, 50, 50, 50, 50, 50,
+ 3, 190, 190, 190, 200, 200, 199, 199,
+ 199, 136, 136, 136, 136, 136, 13, 13,
+ 13, 13, 13, 12, 12, 12, 12, 12,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 3, 3, 190, 190, 190, 199, 199, 199,
+ 152, 152, 152, 136, 135, 135, 33, 13,
+ 13, 13, 12, 12, 12, 12, 12, 12,
+ 50, 50, 50, 50, 50, 50, 49, 49,
+ 3, 3, 189, 189, 189, 189, 199, 152,
+ 152, 152, 152, 152, 151, 151, 33, 32,
+ 32, 32, 12, 12, 12, 12, 12, 84,
+ 50, 50, 49, 49, 49, 49, 49, 49,
+ 3, 189, 189, 189, 189, 189, 152, 152,
+ 152, 152, 152, 151, 151, 151, 32, 32,
+ 32, 31, 12, 12, 12, 12, 84, 84,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 3, 189, 189, 189, 189, 189, 189, 152,
+ 152, 152, 151, 151, 151, 151, 32, 32,
+ 31, 31, 31, 12, 84, 84, 84, 84,
+ 84, 49, 49, 49, 49, 49, 49, 49,
+ 189, 189, 189, 189, 189, 189, 189, 152,
+ 152, 151, 151, 151, 151, 118, 117, 117,
+ 31, 31, 30, 30, 84, 84, 84, 84,
+ 83, 49, 49, 49, 49, 49, 48, 48,
+ 189, 189, 189, 189, 189, 189, 189, 151,
+ 151, 151, 151, 151, 118, 117, 117, 117,
+ 117, 30, 30, 84, 84, 84, 84, 83,
+ 83, 66, 66, 66, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 151, 151, 118, 118, 118, 117, 117, 117,
+ 116, 116, 116, 84, 84, 84, 83, 83,
+ 83, 66, 66, 66, 66, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 151, 150, 118, 118, 117, 117, 117, 116,
+ 116, 116, 116, 115, 83, 83, 83, 83,
+ 66, 66, 66, 66, 66, 65, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 150, 118, 118, 117, 117, 117, 117, 116,
+ 116, 116, 115, 115, 83, 83, 83, 83,
+ 66, 66, 66, 65, 65, 65, 65, 65,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 100, 100, 99, 99, 117, 117, 116, 116,
+ 116, 116, 115, 115, 115, 83, 83, 82,
+ 66, 65, 65, 65, 65, 65, 65, 65,
+ 188, 188, 188, 188, 188, 188, 187, 100,
+ 100, 100, 99, 99, 99, 99, 99, 116,
+ 116, 115, 115, 115, 115, 114, 83, 82,
+ 65, 65, 65, 65, 65, 65, 65, 9,
+ 187, 187, 187, 187, 187, 187, 187, 100,
+ 100, 100, 99, 99, 99, 99, 99, 98,
+ 97, 97, 97, 97, 114, 114, 114, 82,
+ 65, 65, 65, 65, 65, 65, 65, 9,
+ 187, 187, 187, 187, 187, 187, 187, 100,
+ 100, 99, 99, 99, 99, 99, 98, 98,
+ 97, 97, 97, 97, 96, 96, 96, 96,
+ 65, 65, 65, 65, 65, 64, 9, 9,
+ 187, 187, 187, 187, 187, 187, 100, 100,
+ 100, 99, 99, 99, 98, 98, 98, 98,
+ 97, 97, 97, 97, 96, 96, 96, 96,
+ 96, 65, 65, 203, 203, 203, 202, 202,
+ 187, 187, 187, 187, 187, 187, 100, 100,
+ 100, 99, 98, 98, 98, 98, 98, 98,
+ 97, 97, 97, 97, 96, 96, 96, 96,
+ 96, 203, 203, 203, 203, 202, 202, 202,
+ 187, 187, 187, 187, 186, 186, 100, 100,
+ 99, 98, 98, 98, 98, 98, 98, 97,
+ 97, 97, 97, 97, 96, 96, 96, 96,
+ 96, 203, 203, 203, 202, 202, 202, 202,
+ 186, 186, 186, 186, 186, 186, 100, 100,
+ 98, 98, 98, 98, 98, 98, 98, 97,
+ 97, 97, 97, 96, 96, 96, 96, 96,
+ 96, 203, 203, 202, 202, 202, 202, 202,
+ 186, 186, 186, 186, 186, 186, 100, 98,
+ 98, 98, 98, 98, 98, 98, 98, 97,
+ 97, 97, 97, 96, 96, 96, 96, 96,
+ 96, 203, 202, 202, 202, 202, 202, 202,
+ 239, 239, 239, 239, 239, 239, 175, 175,
+ 175, 220, 220, 5, 5, 5, 5, 5,
+ 5, 5, 218, 218, 218, 218, 217, 217,
+ 217, 217, 217, 216, 216, 216, 216, 216,
+ 239, 239, 239, 239, 175, 175, 175, 175,
+ 175, 220, 220, 220, 5, 5, 5, 5,
+ 219, 219, 218, 218, 218, 218, 217, 217,
+ 217, 217, 217, 216, 216, 216, 216, 216,
+ 239, 239, 239, 175, 175, 175, 175, 174,
+ 174, 220, 220, 220, 220, 219, 219, 219,
+ 219, 219, 218, 218, 218, 218, 217, 217,
+ 217, 217, 217, 216, 216, 216, 216, 216,
+ 239, 239, 175, 174, 174, 174, 174, 174,
+ 174, 174, 220, 220, 220, 219, 219, 219,
+ 219, 218, 218, 218, 218, 218, 217, 217,
+ 217, 217, 217, 216, 216, 216, 216, 215,
+ 174, 174, 174, 174, 174, 174, 174, 174,
+ 174, 174, 220, 220, 220, 219, 219, 219,
+ 219, 218, 218, 218, 218, 218, 217, 217,
+ 217, 217, 217, 216, 216, 216, 216, 215,
+ 174, 174, 174, 174, 174, 174, 174, 174,
+ 174, 174, 220, 220, 220, 219, 219, 219,
+ 219, 218, 218, 218, 218, 218, 217, 217,
+ 217, 217, 216, 216, 216, 216, 216, 215,
+ 174, 174, 174, 174, 174, 174, 174, 174,
+ 200, 200, 200, 220, 219, 219, 219, 219,
+ 219, 218, 218, 218, 218, 218, 217, 217,
+ 217, 217, 216, 216, 216, 216, 216, 51,
+ 174, 174, 174, 174, 174, 174, 174, 200,
+ 200, 200, 200, 200, 199, 219, 219, 219,
+ 219, 218, 218, 218, 218, 218, 217, 217,
+ 217, 217, 216, 216, 51, 51, 51, 51,
+ 173, 173, 173, 173, 173, 200, 200, 200,
+ 200, 200, 200, 199, 136, 136, 13, 13,
+ 219, 218, 218, 218, 218, 218, 217, 217,
+ 217, 217, 51, 51, 51, 51, 51, 50,
+ 173, 173, 173, 173, 173, 200, 200, 200,
+ 200, 199, 199, 199, 136, 136, 13, 13,
+ 13, 13, 13, 13, 218, 217, 217, 217,
+ 51, 51, 51, 51, 51, 50, 50, 50,
+ 173, 173, 173, 173, 200, 200, 200, 200,
+ 199, 199, 199, 136, 136, 136, 13, 13,
+ 13, 13, 13, 13, 13, 12, 12, 51,
+ 51, 51, 51, 50, 50, 50, 50, 50,
+ 190, 190, 173, 200, 200, 200, 200, 199,
+ 199, 199, 199, 136, 136, 136, 13, 13,
+ 13, 13, 13, 13, 12, 12, 12, 51,
+ 51, 51, 50, 50, 50, 50, 50, 50,
+ 3, 190, 190, 200, 200, 199, 199, 199,
+ 199, 199, 136, 136, 136, 136, 13, 13,
+ 13, 13, 13, 12, 12, 12, 12, 12,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 3, 3, 190, 190, 199, 199, 199, 199,
+ 199, 199, 135, 135, 135, 135, 13, 13,
+ 13, 13, 12, 12, 12, 12, 12, 12,
+ 50, 50, 50, 50, 50, 50, 49, 49,
+ 3, 3, 189, 189, 189, 199, 199, 199,
+ 199, 135, 135, 135, 135, 135, 135, 13,
+ 32, 12, 12, 12, 12, 12, 12, 12,
+ 50, 50, 49, 49, 49, 49, 49, 49,
+ 3, 189, 189, 189, 189, 189, 199, 199,
+ 152, 135, 135, 135, 135, 151, 134, 32,
+ 32, 12, 12, 12, 12, 12, 12, 12,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 3, 189, 189, 189, 189, 189, 199, 152,
+ 152, 151, 151, 151, 151, 151, 134, 32,
+ 31, 31, 12, 12, 12, 12, 84, 84,
+ 11, 49, 49, 49, 49, 49, 49, 49,
+ 189, 189, 189, 189, 189, 189, 189, 152,
+ 151, 151, 151, 151, 151, 151, 150, 31,
+ 31, 31, 30, 12, 12, 84, 84, 84,
+ 11, 49, 49, 49, 49, 49, 48, 48,
+ 189, 189, 189, 189, 189, 189, 189, 151,
+ 151, 151, 151, 151, 150, 150, 117, 117,
+ 31, 30, 30, 29, 84, 84, 84, 83,
+ 83, 49, 49, 49, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 151,
+ 151, 151, 150, 150, 150, 150, 117, 117,
+ 116, 30, 29, 29, 84, 84, 83, 83,
+ 83, 66, 66, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 150, 150, 150, 150, 150, 117, 117, 116,
+ 116, 116, 29, 29, 83, 83, 83, 83,
+ 82, 66, 66, 66, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 150, 150, 150, 150, 117, 117, 117, 116,
+ 116, 116, 115, 115, 83, 83, 83, 83,
+ 82, 66, 65, 65, 65, 65, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 150, 150, 150, 117, 117, 117, 116, 116,
+ 116, 116, 115, 115, 115, 83, 83, 82,
+ 82, 65, 65, 65, 65, 65, 65, 65,
+ 188, 188, 188, 188, 188, 188, 187, 187,
+ 150, 150, 150, 117, 117, 116, 116, 116,
+ 116, 115, 115, 115, 115, 83, 82, 82,
+ 82, 65, 65, 65, 65, 65, 65, 9,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 100, 99, 99, 99, 99, 99, 116, 116,
+ 116, 115, 115, 115, 114, 114, 82, 82,
+ 65, 65, 65, 65, 65, 64, 64, 9,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 100, 99, 99, 99, 99, 99, 98, 97,
+ 97, 97, 97, 114, 114, 114, 114, 81,
+ 65, 65, 65, 64, 64, 64, 9, 9,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 99, 99, 99, 99, 99, 98, 98, 97,
+ 97, 97, 97, 96, 96, 96, 96, 96,
+ 65, 64, 64, 64, 64, 9, 9, 9,
+ 187, 187, 187, 187, 187, 187, 187, 100,
+ 99, 99, 99, 98, 98, 98, 98, 97,
+ 97, 97, 97, 96, 96, 96, 96, 96,
+ 96, 64, 64, 64, 202, 202, 202, 202,
+ 187, 187, 187, 187, 186, 186, 186, 100,
+ 99, 98, 98, 98, 98, 98, 98, 97,
+ 97, 97, 97, 96, 96, 96, 96, 96,
+ 96, 202, 202, 202, 202, 202, 202, 202,
+ 186, 186, 186, 186, 186, 186, 186, 99,
+ 98, 98, 98, 98, 98, 98, 97, 97,
+ 97, 97, 97, 96, 96, 96, 96, 96,
+ 96, 202, 202, 202, 202, 202, 202, 202,
+ 186, 186, 186, 186, 186, 186, 186, 98,
+ 98, 98, 98, 98, 98, 98, 97, 97,
+ 97, 97, 96, 96, 96, 96, 96, 96,
+ 96, 202, 202, 202, 202, 202, 202, 202,
+ 186, 186, 186, 186, 186, 186, 186, 98,
+ 98, 98, 98, 98, 98, 98, 97, 97,
+ 97, 97, 96, 96, 96, 96, 96, 96,
+ 96, 202, 202, 202, 202, 202, 202, 202,
+ 238, 238, 238, 238, 238, 238, 238, 175,
+ 175, 220, 5, 5, 5, 5, 5, 5,
+ 5, 218, 218, 218, 218, 217, 217, 217,
+ 217, 217, 216, 216, 216, 216, 216, 215,
+ 238, 238, 238, 238, 238, 238, 174, 174,
+ 174, 220, 220, 5, 5, 5, 5, 219,
+ 219, 218, 218, 218, 218, 217, 217, 217,
+ 217, 217, 216, 216, 216, 216, 216, 215,
+ 238, 238, 238, 238, 174, 174, 174, 174,
+ 174, 174, 220, 220, 219, 219, 219, 219,
+ 219, 218, 218, 218, 218, 217, 217, 217,
+ 217, 217, 216, 216, 216, 216, 216, 215,
+ 238, 238, 174, 174, 174, 174, 174, 174,
+ 174, 174, 220, 220, 219, 219, 219, 219,
+ 218, 218, 218, 218, 218, 217, 217, 217,
+ 217, 217, 216, 216, 216, 216, 215, 215,
+ 174, 174, 174, 174, 174, 174, 174, 174,
+ 174, 174, 220, 220, 219, 219, 219, 219,
+ 218, 218, 218, 218, 218, 217, 217, 217,
+ 217, 217, 216, 216, 216, 216, 215, 215,
+ 174, 174, 174, 174, 174, 174, 174, 174,
+ 174, 174, 200, 220, 219, 219, 219, 219,
+ 218, 218, 218, 218, 218, 217, 217, 217,
+ 217, 216, 216, 216, 216, 216, 215, 215,
+ 174, 174, 174, 174, 174, 174, 174, 174,
+ 174, 200, 200, 199, 219, 219, 219, 219,
+ 218, 218, 218, 218, 218, 217, 217, 217,
+ 217, 216, 216, 216, 216, 216, 215, 215,
+ 173, 173, 173, 173, 173, 173, 173, 200,
+ 200, 200, 199, 199, 199, 219, 219, 219,
+ 218, 218, 218, 218, 218, 217, 217, 217,
+ 217, 216, 216, 216, 216, 216, 215, 215,
+ 173, 173, 173, 173, 173, 173, 173, 200,
+ 199, 199, 199, 199, 199, 136, 219, 219,
+ 218, 218, 218, 218, 218, 217, 217, 217,
+ 217, 216, 216, 216, 216, 50, 50, 50,
+ 173, 173, 173, 173, 173, 173, 200, 199,
+ 199, 199, 199, 199, 199, 136, 13, 13,
+ 13, 13, 13, 218, 217, 217, 217, 217,
+ 216, 216, 51, 50, 50, 50, 50, 50,
+ 173, 173, 173, 173, 173, 199, 199, 199,
+ 199, 199, 199, 199, 136, 136, 13, 13,
+ 13, 13, 13, 13, 12, 12, 12, 217,
+ 51, 51, 50, 50, 50, 50, 50, 50,
+ 173, 173, 173, 173, 199, 199, 199, 199,
+ 199, 199, 199, 199, 135, 135, 13, 13,
+ 13, 13, 13, 12, 12, 12, 12, 12,
+ 50, 50, 50, 50, 50, 50, 50, 50,
+ 3, 3, 173, 199, 199, 199, 199, 199,
+ 199, 199, 199, 135, 135, 135, 135, 13,
+ 13, 13, 12, 12, 12, 12, 12, 12,
+ 50, 50, 50, 50, 50, 50, 49, 49,
+ 3, 3, 189, 189, 199, 199, 199, 199,
+ 199, 199, 135, 135, 135, 135, 135, 13,
+ 13, 12, 12, 12, 12, 12, 12, 12,
+ 50, 50, 49, 49, 49, 49, 49, 49,
+ 3, 189, 189, 189, 199, 199, 199, 199,
+ 199, 135, 135, 135, 135, 135, 134, 134,
+ 12, 12, 12, 12, 12, 12, 12, 11,
+ 11, 49, 49, 49, 49, 49, 49, 49,
+ 3, 189, 189, 189, 189, 199, 199, 199,
+ 135, 135, 135, 135, 134, 134, 134, 133,
+ 12, 12, 12, 12, 12, 12, 12, 11,
+ 11, 49, 49, 49, 49, 49, 49, 49,
+ 189, 189, 189, 189, 189, 189, 199, 198,
+ 135, 135, 151, 151, 134, 134, 134, 133,
+ 31, 12, 12, 12, 12, 12, 11, 11,
+ 11, 49, 49, 49, 49, 49, 48, 48,
+ 189, 189, 189, 189, 189, 189, 189, 151,
+ 151, 151, 151, 150, 150, 150, 133, 133,
+ 31, 30, 30, 12, 12, 11, 11, 11,
+ 11, 49, 49, 49, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 151,
+ 151, 150, 150, 150, 150, 150, 133, 133,
+ 30, 30, 29, 29, 29, 11, 11, 11,
+ 11, 11, 48, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 150,
+ 150, 150, 150, 150, 150, 149, 149, 149,
+ 116, 29, 29, 29, 28, 83, 83, 83,
+ 82, 82, 48, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 150, 150, 150, 150, 150, 149, 149, 116,
+ 116, 116, 29, 28, 28, 83, 83, 82,
+ 82, 82, 65, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 150, 150, 150, 150, 149, 149, 116, 116,
+ 116, 115, 115, 115, 28, 83, 82, 82,
+ 82, 82, 65, 65, 65, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 187, 187,
+ 150, 150, 150, 149, 149, 149, 116, 116,
+ 116, 115, 115, 115, 114, 83, 82, 82,
+ 82, 65, 65, 65, 65, 65, 64, 64,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 150, 149, 149, 149, 116, 116, 116,
+ 115, 115, 115, 115, 114, 114, 82, 82,
+ 81, 65, 65, 65, 64, 64, 64, 9,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 150, 149, 99, 116, 116, 116, 116,
+ 115, 115, 115, 114, 114, 114, 82, 81,
+ 81, 65, 64, 64, 64, 64, 9, 9,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 99, 99, 99, 99, 98, 97, 97,
+ 97, 115, 114, 114, 114, 114, 113, 81,
+ 81, 64, 64, 64, 64, 64, 9, 9,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 99, 99, 99, 99, 98, 98, 97, 97,
+ 97, 97, 96, 96, 96, 96, 113, 113,
+ 81, 64, 64, 64, 64, 9, 9, 9,
+ 187, 187, 187, 187, 186, 186, 186, 186,
+ 99, 99, 98, 98, 98, 98, 97, 97,
+ 97, 97, 96, 96, 96, 96, 96, 96,
+ 112, 64, 64, 64, 9, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 98, 98, 98, 98, 98, 98, 97, 97,
+ 97, 97, 96, 96, 96, 96, 96, 96,
+ 96, 64, 202, 202, 202, 202, 202, 202,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 98, 98, 98, 98, 98, 97, 97, 97,
+ 97, 97, 96, 96, 96, 96, 96, 96,
+ 96, 202, 202, 202, 202, 202, 202, 202,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 98, 98, 98, 98, 98, 97, 97, 97,
+ 97, 96, 96, 96, 96, 96, 96, 96,
+ 96, 202, 202, 202, 202, 202, 202, 202,
+ 186, 186, 186, 186, 186, 186, 186, 98,
+ 98, 98, 98, 98, 98, 97, 97, 97,
+ 97, 96, 96, 96, 96, 96, 96, 96,
+ 96, 202, 202, 202, 202, 202, 202, 202,
+ 238, 238, 238, 238, 238, 237, 237, 237,
+ 174, 174, 5, 5, 5, 5, 5, 5,
+ 218, 218, 218, 218, 217, 217, 217, 217,
+ 217, 216, 216, 216, 216, 216, 215, 215,
+ 238, 238, 238, 238, 237, 237, 174, 174,
+ 174, 174, 5, 5, 5, 5, 219, 219,
+ 218, 218, 218, 218, 217, 217, 217, 217,
+ 217, 216, 216, 216, 216, 216, 215, 215,
+ 238, 238, 238, 237, 174, 174, 174, 174,
+ 174, 174, 220, 219, 219, 219, 219, 219,
+ 218, 218, 218, 218, 217, 217, 217, 217,
+ 217, 216, 216, 216, 216, 216, 215, 215,
+ 238, 238, 174, 174, 174, 174, 174, 174,
+ 174, 174, 220, 219, 219, 219, 219, 218,
+ 218, 218, 218, 218, 217, 217, 217, 217,
+ 217, 216, 216, 216, 216, 215, 215, 215,
+ 238, 174, 174, 174, 174, 174, 174, 174,
+ 174, 174, 174, 219, 219, 219, 219, 218,
+ 218, 218, 218, 218, 217, 217, 217, 217,
+ 217, 216, 216, 216, 216, 215, 215, 215,
+ 174, 174, 174, 174, 174, 174, 174, 174,
+ 174, 174, 174, 219, 219, 219, 219, 218,
+ 218, 218, 218, 218, 217, 217, 217, 217,
+ 216, 216, 216, 216, 216, 215, 215, 215,
+ 173, 173, 173, 173, 173, 173, 173, 173,
+ 174, 199, 199, 199, 219, 219, 219, 218,
+ 218, 218, 218, 218, 217, 217, 217, 217,
+ 216, 216, 216, 216, 216, 215, 215, 215,
+ 173, 173, 173, 173, 173, 173, 173, 173,
+ 199, 199, 199, 199, 199, 219, 219, 218,
+ 218, 218, 218, 218, 217, 217, 217, 217,
+ 216, 216, 216, 216, 216, 215, 215, 215,
+ 173, 173, 173, 173, 173, 173, 173, 199,
+ 199, 199, 199, 199, 199, 199, 219, 218,
+ 218, 218, 218, 218, 217, 217, 217, 217,
+ 216, 216, 216, 216, 216, 215, 215, 215,
+ 173, 173, 173, 173, 173, 173, 173, 199,
+ 199, 199, 199, 199, 199, 199, 135, 13,
+ 13, 13, 218, 217, 217, 217, 217, 216,
+ 216, 216, 216, 216, 50, 50, 50, 50,
+ 173, 173, 173, 173, 173, 173, 199, 199,
+ 199, 199, 199, 199, 199, 135, 135, 13,
+ 13, 13, 13, 12, 12, 12, 217, 216,
+ 216, 216, 50, 50, 50, 50, 50, 50,
+ 173, 173, 173, 173, 173, 199, 199, 199,
+ 199, 199, 199, 199, 135, 135, 135, 13,
+ 13, 13, 12, 12, 12, 12, 12, 12,
+ 50, 50, 50, 50, 50, 50, 49, 49,
+ 173, 173, 173, 173, 199, 199, 199, 199,
+ 199, 199, 199, 135, 135, 135, 135, 13,
+ 13, 12, 12, 12, 12, 12, 12, 12,
+ 11, 50, 49, 49, 49, 49, 49, 49,
+ 3, 189, 189, 199, 199, 199, 199, 199,
+ 199, 198, 198, 135, 135, 135, 134, 134,
+ 12, 12, 12, 12, 12, 12, 12, 11,
+ 11, 49, 49, 49, 49, 49, 49, 49,
+ 3, 189, 189, 189, 199, 199, 199, 199,
+ 198, 198, 135, 135, 134, 134, 134, 134,
+ 12, 12, 12, 12, 12, 12, 11, 11,
+ 11, 49, 49, 49, 49, 49, 49, 49,
+ 189, 189, 189, 189, 189, 199, 198, 198,
+ 198, 198, 135, 134, 134, 134, 134, 133,
+ 133, 12, 12, 12, 12, 12, 11, 11,
+ 11, 11, 49, 49, 49, 49, 48, 48,
+ 189, 189, 189, 189, 189, 189, 198, 198,
+ 198, 135, 134, 134, 134, 134, 133, 133,
+ 133, 12, 12, 12, 12, 11, 11, 11,
+ 11, 11, 49, 49, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 198, 198,
+ 198, 150, 150, 150, 150, 133, 133, 133,
+ 133, 30, 29, 29, 11, 11, 11, 11,
+ 11, 11, 48, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 150,
+ 150, 150, 150, 150, 150, 150, 133, 133,
+ 132, 29, 29, 29, 11, 11, 11, 11,
+ 11, 11, 48, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 150,
+ 150, 150, 150, 150, 150, 149, 149, 149,
+ 132, 29, 29, 28, 28, 11, 11, 11,
+ 82, 82, 48, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 188,
+ 150, 150, 150, 150, 149, 149, 149, 148,
+ 148, 115, 28, 28, 28, 27, 82, 82,
+ 82, 82, 82, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 187, 187,
+ 150, 150, 150, 149, 149, 149, 149, 148,
+ 148, 115, 115, 28, 27, 27, 82, 82,
+ 82, 82, 65, 65, 65, 48, 48, 48,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 150, 150, 150, 149, 149, 149, 148, 148,
+ 115, 115, 115, 114, 114, 27, 82, 82,
+ 81, 81, 65, 64, 64, 64, 64, 9,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 150, 149, 149, 149, 148, 148, 115,
+ 115, 115, 114, 114, 114, 114, 82, 81,
+ 81, 81, 64, 64, 64, 64, 64, 9,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 149, 149, 149, 149, 148, 116, 115,
+ 115, 115, 114, 114, 114, 114, 113, 81,
+ 81, 81, 64, 64, 64, 64, 9, 9,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 149, 99, 99, 98, 97, 97, 115,
+ 115, 114, 114, 114, 114, 113, 113, 81,
+ 81, 81, 64, 64, 64, 9, 9, 9,
+ 187, 187, 187, 187, 186, 186, 186, 186,
+ 186, 99, 99, 98, 98, 97, 97, 97,
+ 97, 96, 96, 96, 113, 113, 113, 113,
+ 81, 64, 64, 64, 64, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 98, 98, 98, 98, 97, 97, 97,
+ 97, 96, 96, 96, 96, 96, 96, 112,
+ 112, 8, 64, 64, 9, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 98, 98, 98, 98, 97, 97, 97,
+ 97, 96, 96, 96, 96, 96, 96, 96,
+ 8, 8, 8, 202, 202, 202, 202, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 98, 98, 98, 97, 97, 97, 97,
+ 97, 96, 96, 96, 96, 96, 96, 96,
+ 8, 8, 202, 202, 202, 202, 202, 202,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 98, 98, 98, 98, 97, 97, 97, 97,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 8, 8, 202, 202, 202, 202, 202, 202,
+ 186, 186, 186, 186, 186, 185, 185, 185,
+ 98, 98, 98, 98, 97, 97, 97, 97,
+ 96, 96, 96, 96, 96, 96, 96, 96,
+ 8, 8, 202, 202, 202, 202, 202, 202,
+ 237, 237, 237, 237, 237, 237, 237, 237,
+ 237, 174, 5, 5, 5, 5, 5, 218,
+ 218, 218, 218, 217, 217, 217, 217, 217,
+ 216, 216, 216, 216, 216, 215, 215, 215,
+ 237, 237, 237, 237, 237, 237, 237, 174,
+ 174, 174, 5, 5, 5, 219, 219, 218,
+ 218, 218, 218, 217, 217, 217, 217, 217,
+ 216, 216, 216, 216, 216, 215, 215, 215,
+ 237, 237, 237, 237, 237, 237, 174, 174,
+ 174, 174, 219, 219, 219, 219, 219, 218,
+ 218, 218, 218, 217, 217, 217, 217, 217,
+ 216, 216, 216, 216, 216, 215, 215, 215,
+ 237, 237, 237, 237, 174, 174, 174, 174,
+ 174, 174, 174, 219, 219, 219, 218, 218,
+ 218, 218, 218, 217, 217, 217, 217, 217,
+ 216, 216, 216, 216, 215, 215, 215, 215,
+ 237, 237, 174, 174, 174, 174, 174, 174,
+ 174, 174, 174, 219, 219, 219, 218, 218,
+ 218, 218, 218, 217, 217, 217, 217, 217,
+ 216, 216, 216, 216, 215, 215, 215, 215,
+ 173, 173, 173, 173, 173, 173, 173, 173,
+ 173, 174, 174, 219, 219, 219, 218, 218,
+ 218, 218, 218, 217, 217, 217, 217, 216,
+ 216, 216, 216, 216, 215, 215, 215, 215,
+ 173, 173, 173, 173, 173, 173, 173, 173,
+ 173, 173, 199, 199, 219, 219, 218, 218,
+ 218, 218, 218, 217, 217, 217, 217, 216,
+ 216, 216, 216, 216, 215, 215, 215, 215,
+ 173, 173, 173, 173, 173, 173, 173, 173,
+ 173, 199, 199, 199, 199, 219, 218, 218,
+ 218, 218, 218, 217, 217, 217, 217, 216,
+ 216, 216, 216, 216, 215, 215, 215, 215,
+ 173, 173, 173, 173, 173, 173, 173, 173,
+ 199, 199, 199, 199, 199, 199, 218, 218,
+ 218, 218, 218, 217, 217, 217, 217, 216,
+ 216, 216, 216, 216, 215, 215, 215, 215,
+ 173, 173, 173, 173, 173, 173, 173, 199,
+ 199, 199, 199, 199, 199, 198, 135, 13,
+ 13, 218, 217, 217, 217, 217, 216, 216,
+ 216, 216, 216, 215, 215, 215, 215, 215,
+ 173, 173, 173, 173, 173, 173, 199, 199,
+ 199, 199, 199, 198, 198, 198, 135, 13,
+ 13, 13, 12, 12, 12, 217, 216, 216,
+ 216, 216, 216, 215, 50, 50, 49, 49,
+ 172, 172, 172, 172, 172, 199, 199, 199,
+ 199, 199, 198, 198, 198, 135, 135, 134,
+ 13, 12, 12, 12, 12, 12, 12, 12,
+ 11, 50, 49, 49, 49, 49, 49, 49,
+ 172, 172, 172, 172, 172, 199, 199, 199,
+ 198, 198, 198, 198, 198, 134, 134, 134,
+ 12, 12, 12, 12, 12, 12, 12, 11,
+ 11, 49, 49, 49, 49, 49, 49, 49,
+ 172, 172, 172, 172, 199, 199, 198, 198,
+ 198, 198, 198, 198, 134, 134, 134, 134,
+ 133, 12, 12, 12, 12, 12, 11, 11,
+ 11, 11, 49, 49, 49, 49, 49, 49,
+ 189, 189, 189, 189, 199, 198, 198, 198,
+ 198, 198, 198, 198, 134, 134, 134, 133,
+ 133, 12, 12, 12, 12, 11, 11, 11,
+ 11, 11, 49, 49, 49, 49, 48, 48,
+ 189, 189, 189, 189, 198, 198, 198, 198,
+ 198, 198, 198, 134, 134, 134, 133, 133,
+ 133, 12, 12, 12, 12, 11, 11, 11,
+ 11, 11, 49, 49, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 198, 198, 198,
+ 198, 198, 134, 134, 134, 133, 133, 133,
+ 133, 132, 12, 12, 11, 11, 11, 11,
+ 11, 11, 48, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 198, 198,
+ 198, 150, 150, 150, 150, 133, 133, 133,
+ 132, 132, 29, 11, 11, 11, 11, 11,
+ 11, 11, 48, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 198,
+ 150, 150, 150, 150, 150, 133, 133, 132,
+ 132, 132, 29, 28, 11, 11, 11, 11,
+ 11, 11, 48, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 150,
+ 150, 150, 150, 150, 149, 149, 149, 132,
+ 132, 132, 28, 28, 28, 11, 11, 11,
+ 11, 82, 48, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 187, 187,
+ 150, 150, 150, 149, 149, 149, 149, 148,
+ 148, 131, 131, 28, 27, 27, 27, 82,
+ 82, 82, 81, 48, 48, 48, 48, 48,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 150, 150, 150, 149, 149, 149, 148, 148,
+ 148, 147, 147, 27, 27, 27, 26, 82,
+ 82, 81, 81, 64, 64, 64, 48, 48,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 150, 149, 149, 149, 148, 148, 148,
+ 147, 147, 114, 114, 114, 26, 26, 81,
+ 81, 81, 81, 64, 64, 64, 64, 9,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 149, 149, 149, 149, 148, 148, 148,
+ 147, 114, 114, 114, 114, 113, 26, 81,
+ 81, 81, 81, 64, 64, 64, 9, 9,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 149, 149, 149, 148, 148, 148, 147,
+ 147, 114, 114, 114, 114, 113, 113, 81,
+ 81, 81, 64, 64, 64, 64, 9, 9,
+ 187, 187, 187, 187, 186, 186, 186, 186,
+ 186, 186, 149, 148, 148, 148, 147, 147,
+ 114, 114, 114, 114, 113, 113, 113, 112,
+ 81, 80, 64, 64, 64, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 98, 98, 97, 97, 97, 97,
+ 96, 96, 96, 113, 113, 113, 113, 112,
+ 112, 80, 64, 64, 9, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 98, 98, 97, 97, 97, 97,
+ 96, 96, 96, 96, 96, 113, 112, 112,
+ 8, 8, 8, 64, 9, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 98, 98, 97, 97, 97, 97,
+ 96, 96, 96, 96, 96, 96, 96, 8,
+ 8, 8, 8, 9, 9, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 98, 98, 97, 97, 97, 97, 97,
+ 96, 96, 96, 96, 96, 96, 96, 8,
+ 8, 8, 8, 202, 202, 202, 202, 202,
+ 186, 186, 186, 186, 186, 185, 185, 185,
+ 182, 182, 98, 97, 97, 97, 97, 96,
+ 96, 96, 96, 96, 96, 96, 96, 8,
+ 8, 8, 8, 202, 202, 202, 202, 202,
+ 185, 185, 185, 185, 185, 185, 185, 185,
+ 182, 182, 98, 97, 97, 97, 97, 96,
+ 96, 96, 96, 96, 96, 96, 96, 8,
+ 8, 8, 8, 202, 202, 202, 202, 202,
+ 236, 236, 236, 236, 236, 236, 236, 236,
+ 236, 236, 5, 5, 5, 5, 218, 218,
+ 218, 218, 217, 217, 217, 217, 217, 216,
+ 216, 216, 216, 216, 215, 215, 215, 215,
+ 236, 236, 236, 236, 236, 236, 236, 236,
+ 236, 174, 5, 5, 219, 219, 218, 218,
+ 218, 218, 217, 217, 217, 217, 217, 216,
+ 216, 216, 216, 216, 215, 215, 215, 215,
+ 236, 236, 236, 236, 236, 236, 236, 174,
+ 174, 174, 174, 219, 219, 219, 218, 218,
+ 218, 218, 217, 217, 217, 217, 217, 216,
+ 216, 216, 216, 216, 215, 215, 215, 215,
+ 236, 236, 236, 236, 236, 174, 174, 174,
+ 174, 174, 174, 219, 219, 218, 218, 218,
+ 218, 218, 217, 217, 217, 217, 217, 216,
+ 216, 216, 216, 215, 215, 215, 215, 215,
+ 236, 236, 236, 173, 173, 173, 173, 173,
+ 173, 173, 174, 219, 219, 218, 218, 218,
+ 218, 218, 217, 217, 217, 217, 217, 216,
+ 216, 216, 216, 215, 215, 215, 215, 215,
+ 173, 173, 173, 173, 173, 173, 173, 173,
+ 173, 173, 173, 219, 219, 218, 218, 218,
+ 218, 218, 217, 217, 217, 217, 216, 216,
+ 216, 216, 216, 215, 215, 215, 215, 215,
+ 173, 173, 173, 173, 173, 173, 173, 173,
+ 173, 173, 199, 199, 199, 218, 218, 218,
+ 218, 218, 217, 217, 217, 217, 216, 216,
+ 216, 216, 216, 215, 215, 215, 215, 215,
+ 173, 173, 173, 173, 173, 173, 173, 173,
+ 173, 199, 199, 199, 199, 198, 218, 218,
+ 218, 218, 217, 217, 217, 217, 216, 216,
+ 216, 216, 216, 215, 215, 215, 215, 215,
+ 173, 173, 173, 173, 173, 173, 173, 173,
+ 199, 199, 199, 199, 198, 198, 198, 218,
+ 218, 218, 217, 217, 217, 217, 216, 216,
+ 216, 216, 216, 215, 215, 215, 215, 214,
+ 172, 172, 172, 172, 173, 173, 173, 199,
+ 199, 199, 198, 198, 198, 198, 198, 197,
+ 218, 217, 217, 217, 217, 216, 216, 216,
+ 216, 216, 215, 215, 215, 215, 215, 214,
+ 172, 172, 172, 172, 172, 172, 172, 199,
+ 198, 198, 198, 198, 198, 198, 197, 197,
+ 197, 12, 12, 12, 217, 216, 216, 216,
+ 216, 216, 215, 215, 215, 215, 215, 49,
+ 172, 172, 172, 172, 172, 172, 199, 198,
+ 198, 198, 198, 198, 198, 197, 197, 197,
+ 197, 12, 12, 12, 12, 12, 12, 11,
+ 216, 216, 215, 49, 49, 49, 49, 49,
+ 172, 172, 172, 172, 172, 198, 198, 198,
+ 198, 198, 198, 198, 198, 197, 197, 197,
+ 133, 12, 12, 12, 12, 12, 11, 11,
+ 11, 11, 49, 49, 49, 49, 49, 49,
+ 172, 172, 172, 172, 172, 198, 198, 198,
+ 198, 198, 198, 198, 197, 197, 197, 133,
+ 133, 12, 12, 12, 12, 11, 11, 11,
+ 11, 11, 49, 49, 49, 49, 48, 48,
+ 172, 172, 172, 172, 198, 198, 198, 198,
+ 198, 198, 198, 197, 197, 197, 133, 133,
+ 133, 133, 12, 12, 11, 11, 11, 11,
+ 11, 11, 11, 49, 48, 48, 48, 48,
+ 188, 188, 188, 188, 198, 198, 198, 198,
+ 198, 198, 197, 197, 197, 197, 133, 133,
+ 133, 132, 12, 12, 11, 11, 11, 11,
+ 11, 11, 11, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 198, 198, 198,
+ 198, 198, 197, 197, 197, 133, 133, 133,
+ 132, 132, 132, 11, 11, 11, 11, 11,
+ 11, 11, 11, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 198, 198,
+ 198, 197, 197, 197, 133, 133, 133, 132,
+ 132, 132, 131, 11, 11, 11, 11, 11,
+ 11, 11, 11, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 188, 198,
+ 197, 197, 150, 150, 133, 133, 132, 132,
+ 132, 132, 131, 11, 11, 11, 11, 11,
+ 11, 11, 10, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 187, 150,
+ 150, 150, 150, 150, 149, 149, 149, 132,
+ 132, 131, 131, 131, 11, 11, 11, 11,
+ 11, 10, 10, 48, 48, 48, 48, 48,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 150, 150, 150, 149, 149, 149, 148, 148,
+ 148, 131, 131, 131, 27, 27, 26, 10,
+ 10, 10, 10, 10, 48, 48, 48, 48,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 150, 150, 149, 149, 149, 148, 148, 148,
+ 147, 147, 147, 130, 27, 26, 26, 10,
+ 10, 10, 10, 80, 64, 64, 64, 48,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 149, 149, 149, 149, 148, 148, 148,
+ 147, 147, 147, 114, 26, 26, 26, 25,
+ 81, 81, 80, 80, 64, 64, 9, 9,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 149, 149, 149, 148, 148, 148, 147,
+ 147, 147, 114, 114, 113, 113, 25, 25,
+ 81, 80, 80, 64, 64, 64, 9, 9,
+ 187, 187, 187, 187, 186, 186, 186, 186,
+ 186, 149, 149, 148, 148, 148, 147, 147,
+ 147, 147, 114, 114, 113, 113, 113, 81,
+ 80, 80, 80, 64, 64, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 149, 148, 148, 148, 147, 147,
+ 147, 114, 114, 113, 113, 113, 113, 112,
+ 80, 80, 80, 64, 9, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 148, 148, 148, 147, 147, 147,
+ 114, 114, 113, 113, 113, 113, 112, 112,
+ 112, 80, 80, 64, 9, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 181, 97, 97, 97, 97, 96,
+ 96, 96, 96, 96, 113, 112, 112, 112,
+ 8, 8, 8, 8, 9, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 182, 181, 97, 97, 97, 97, 96,
+ 96, 96, 96, 96, 96, 112, 112, 8,
+ 8, 8, 8, 8, 9, 9, 9, 9,
+ 186, 186, 186, 186, 186, 185, 185, 185,
+ 182, 182, 181, 97, 97, 97, 97, 96,
+ 96, 96, 96, 96, 96, 96, 8, 8,
+ 8, 8, 8, 8, 9, 9, 9, 9,
+ 185, 185, 185, 185, 185, 185, 185, 185,
+ 182, 182, 181, 181, 97, 97, 96, 96,
+ 96, 96, 96, 96, 96, 96, 8, 8,
+ 8, 8, 8, 8, 202, 202, 202, 202,
+ 185, 185, 185, 185, 185, 185, 185, 182,
+ 182, 182, 181, 181, 97, 97, 96, 96,
+ 96, 96, 96, 96, 96, 96, 8, 8,
+ 8, 8, 8, 8, 202, 202, 202, 202,
+ 236, 236, 236, 236, 236, 236, 236, 236,
+ 236, 236, 236, 5, 5, 218, 218, 218,
+ 218, 217, 217, 217, 217, 217, 216, 216,
+ 216, 216, 216, 215, 215, 215, 215, 214,
+ 236, 236, 236, 236, 236, 236, 236, 236,
+ 236, 236, 236, 219, 219, 218, 218, 218,
+ 218, 217, 217, 217, 217, 217, 216, 216,
+ 216, 216, 216, 215, 215, 215, 215, 214,
+ 236, 236, 236, 236, 236, 236, 236, 236,
+ 236, 174, 174, 219, 219, 218, 218, 218,
+ 218, 217, 217, 217, 217, 217, 216, 216,
+ 216, 216, 216, 215, 215, 215, 215, 214,
+ 236, 236, 236, 236, 236, 236, 236, 173,
+ 173, 173, 173, 219, 218, 218, 218, 218,
+ 218, 217, 217, 217, 217, 217, 216, 216,
+ 216, 216, 215, 215, 215, 215, 215, 214,
+ 236, 236, 236, 236, 173, 173, 173, 173,
+ 173, 173, 173, 219, 218, 218, 218, 218,
+ 218, 217, 217, 217, 217, 217, 216, 216,
+ 216, 216, 215, 215, 215, 215, 215, 214,
+ 236, 173, 173, 173, 173, 173, 173, 173,
+ 173, 173, 173, 173, 218, 218, 218, 218,
+ 218, 217, 217, 217, 217, 216, 216, 216,
+ 216, 216, 215, 215, 215, 215, 215, 214,
+ 173, 173, 173, 173, 173, 173, 173, 173,
+ 173, 173, 173, 199, 198, 218, 218, 218,
+ 218, 217, 217, 217, 217, 216, 216, 216,
+ 216, 216, 215, 215, 215, 215, 215, 214,
+ 172, 172, 173, 173, 173, 173, 173, 173,
+ 173, 173, 198, 198, 198, 198, 218, 218,
+ 218, 217, 217, 217, 217, 216, 216, 216,
+ 216, 216, 215, 215, 215, 215, 215, 214,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 198, 198, 198, 198, 198, 198, 218,
+ 218, 217, 217, 217, 217, 216, 216, 216,
+ 216, 216, 215, 215, 215, 215, 214, 214,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 198, 198, 198, 198, 198, 198, 197, 197,
+ 197, 217, 217, 217, 216, 216, 216, 216,
+ 216, 215, 215, 215, 215, 215, 214, 214,
+ 172, 172, 172, 172, 172, 172, 172, 198,
+ 198, 198, 198, 198, 198, 197, 197, 197,
+ 197, 197, 217, 217, 216, 216, 216, 216,
+ 216, 215, 215, 215, 215, 215, 214, 214,
+ 172, 172, 172, 172, 172, 172, 172, 198,
+ 198, 198, 198, 198, 198, 197, 197, 197,
+ 197, 196, 12, 12, 12, 12, 11, 216,
+ 216, 215, 215, 215, 215, 215, 49, 49,
+ 172, 172, 172, 172, 172, 172, 198, 198,
+ 198, 198, 198, 198, 197, 197, 197, 197,
+ 196, 196, 12, 12, 12, 11, 11, 11,
+ 11, 11, 11, 49, 49, 49, 48, 48,
+ 171, 171, 172, 172, 172, 198, 198, 198,
+ 198, 198, 198, 197, 197, 197, 197, 196,
+ 196, 196, 12, 12, 11, 11, 11, 11,
+ 11, 11, 11, 49, 48, 48, 48, 48,
+ 171, 171, 171, 171, 171, 198, 198, 198,
+ 198, 198, 198, 197, 197, 197, 197, 196,
+ 196, 196, 132, 11, 11, 11, 11, 11,
+ 11, 11, 11, 48, 48, 48, 48, 48,
+ 171, 171, 171, 171, 198, 198, 198, 198,
+ 198, 198, 197, 197, 197, 197, 196, 196,
+ 196, 132, 132, 11, 11, 11, 11, 11,
+ 11, 11, 11, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 198, 198, 198,
+ 198, 197, 197, 197, 197, 196, 196, 196,
+ 132, 132, 132, 11, 11, 11, 11, 11,
+ 11, 11, 11, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 198, 198, 198,
+ 197, 197, 197, 197, 196, 196, 196, 132,
+ 132, 132, 131, 131, 11, 11, 11, 11,
+ 11, 11, 10, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 188, 198, 198,
+ 197, 197, 197, 196, 196, 196, 132, 132,
+ 132, 131, 131, 131, 11, 11, 11, 11,
+ 11, 10, 10, 10, 48, 48, 48, 48,
+ 187, 187, 187, 187, 187, 187, 187, 197,
+ 197, 197, 197, 196, 196, 149, 132, 132,
+ 131, 131, 131, 131, 11, 11, 11, 10,
+ 10, 10, 10, 10, 48, 48, 48, 48,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 197, 150, 149, 149, 149, 148, 148, 148,
+ 131, 131, 131, 130, 130, 26, 26, 10,
+ 10, 10, 10, 10, 10, 48, 48, 48,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 150, 149, 149, 149, 149, 148, 148, 148,
+ 147, 147, 147, 130, 130, 26, 26, 10,
+ 10, 10, 10, 10, 80, 64, 64, 9,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 149, 149, 149, 148, 148, 148, 147,
+ 147, 147, 146, 146, 146, 26, 25, 10,
+ 10, 10, 80, 80, 80, 64, 9, 9,
+ 187, 187, 187, 187, 186, 186, 186, 186,
+ 186, 149, 149, 148, 148, 148, 147, 147,
+ 147, 147, 146, 146, 113, 25, 25, 24,
+ 24, 80, 80, 80, 80, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 149, 148, 148, 148, 147, 147,
+ 147, 146, 146, 113, 113, 113, 112, 24,
+ 24, 80, 80, 80, 64, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 148, 148, 148, 147, 147, 147,
+ 147, 146, 113, 113, 113, 113, 112, 112,
+ 7, 80, 80, 80, 9, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 181, 148, 147, 147, 147, 147,
+ 146, 113, 113, 113, 113, 112, 112, 112,
+ 112, 8, 22, 22, 9, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 182, 181, 181, 179, 147, 147, 96,
+ 96, 96, 113, 113, 112, 112, 112, 8,
+ 8, 8, 8, 8, 9, 9, 9, 9,
+ 186, 186, 186, 186, 186, 185, 185, 185,
+ 182, 182, 181, 181, 179, 97, 96, 96,
+ 96, 96, 96, 96, 112, 112, 8, 8,
+ 8, 8, 8, 8, 9, 9, 9, 9,
+ 185, 185, 185, 185, 185, 185, 185, 185,
+ 182, 182, 181, 181, 179, 179, 96, 96,
+ 96, 96, 96, 96, 96, 8, 8, 8,
+ 8, 8, 8, 8, 8, 9, 9, 9,
+ 185, 185, 185, 185, 185, 185, 185, 182,
+ 182, 182, 181, 181, 179, 179, 96, 96,
+ 96, 96, 96, 96, 96, 8, 8, 8,
+ 8, 8, 8, 8, 8, 202, 202, 202,
+ 185, 185, 185, 185, 185, 185, 185, 182,
+ 182, 182, 181, 181, 179, 179, 96, 96,
+ 96, 96, 96, 96, 96, 8, 8, 8,
+ 8, 8, 8, 8, 8, 202, 202, 202,
+ 235, 235, 235, 235, 235, 235, 235, 234,
+ 234, 234, 234, 5, 218, 218, 218, 218,
+ 217, 217, 217, 217, 217, 216, 216, 216,
+ 216, 216, 215, 215, 215, 215, 214, 214,
+ 235, 235, 235, 235, 235, 235, 234, 234,
+ 234, 234, 234, 219, 218, 218, 218, 218,
+ 217, 217, 217, 217, 217, 216, 216, 216,
+ 216, 216, 215, 215, 215, 215, 214, 214,
+ 235, 235, 235, 235, 235, 234, 234, 234,
+ 234, 234, 228, 219, 218, 218, 218, 218,
+ 217, 217, 217, 217, 217, 216, 216, 216,
+ 216, 216, 215, 215, 215, 215, 214, 214,
+ 235, 235, 235, 235, 234, 234, 234, 234,
+ 234, 228, 227, 227, 218, 218, 218, 218,
+ 217, 217, 217, 217, 217, 216, 216, 216,
+ 216, 215, 215, 215, 215, 215, 214, 214,
+ 235, 235, 235, 234, 234, 234, 173, 173,
+ 173, 173, 227, 227, 218, 218, 218, 218,
+ 217, 217, 217, 217, 217, 216, 216, 216,
+ 216, 215, 215, 215, 215, 215, 214, 214,
+ 235, 235, 173, 173, 173, 173, 173, 173,
+ 173, 173, 227, 226, 226, 218, 218, 218,
+ 217, 217, 217, 217, 216, 216, 216, 216,
+ 216, 215, 215, 215, 215, 215, 214, 214,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 226, 226, 226, 218, 218, 218,
+ 217, 217, 217, 217, 216, 216, 216, 216,
+ 216, 215, 215, 215, 215, 215, 214, 214,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 198, 198, 198, 225, 218, 218,
+ 217, 217, 217, 217, 216, 216, 216, 216,
+ 216, 215, 215, 215, 215, 215, 214, 214,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 198, 198, 198, 198, 198, 197, 197,
+ 217, 217, 217, 217, 216, 216, 216, 216,
+ 216, 215, 215, 215, 215, 214, 214, 214,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 198, 198, 198, 198, 198, 198, 197, 197,
+ 197, 217, 217, 216, 216, 216, 216, 216,
+ 215, 215, 215, 215, 215, 214, 214, 214,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 198, 198, 198, 198, 198, 197, 197, 197,
+ 197, 196, 217, 216, 216, 216, 216, 216,
+ 215, 215, 215, 215, 215, 214, 214, 214,
+ 172, 172, 172, 172, 172, 172, 172, 198,
+ 198, 198, 198, 198, 197, 197, 197, 197,
+ 197, 196, 196, 12, 12, 216, 216, 216,
+ 215, 215, 215, 215, 215, 214, 214, 214,
+ 171, 171, 171, 171, 172, 172, 198, 198,
+ 198, 198, 198, 197, 197, 197, 197, 197,
+ 196, 196, 196, 12, 11, 11, 11, 11,
+ 11, 11, 215, 215, 48, 48, 48, 48,
+ 171, 171, 171, 171, 171, 171, 198, 198,
+ 198, 198, 198, 197, 197, 197, 197, 196,
+ 196, 196, 195, 11, 11, 11, 11, 11,
+ 11, 11, 11, 48, 48, 48, 48, 48,
+ 171, 171, 171, 171, 171, 198, 198, 198,
+ 198, 198, 197, 197, 197, 197, 196, 196,
+ 196, 196, 195, 11, 11, 11, 11, 11,
+ 11, 11, 11, 48, 48, 48, 48, 48,
+ 171, 171, 171, 171, 171, 198, 198, 198,
+ 198, 197, 197, 197, 197, 196, 196, 196,
+ 196, 195, 195, 11, 11, 11, 11, 11,
+ 11, 11, 11, 48, 48, 48, 48, 48,
+ 171, 171, 171, 171, 171, 198, 198, 198,
+ 197, 197, 197, 197, 197, 196, 196, 196,
+ 195, 195, 195, 131, 11, 11, 11, 11,
+ 11, 11, 11, 48, 48, 48, 48, 48,
+ 188, 188, 188, 188, 188, 198, 198, 198,
+ 197, 197, 197, 197, 196, 196, 196, 195,
+ 195, 195, 131, 131, 11, 11, 11, 11,
+ 11, 10, 10, 10, 48, 48, 48, 48,
+ 187, 187, 187, 187, 187, 187, 198, 197,
+ 197, 197, 197, 196, 196, 196, 195, 195,
+ 195, 131, 131, 131, 11, 11, 11, 11,
+ 10, 10, 10, 10, 48, 48, 48, 48,
+ 187, 187, 187, 187, 187, 187, 187, 197,
+ 197, 197, 196, 196, 196, 196, 195, 195,
+ 195, 131, 131, 130, 130, 11, 10, 10,
+ 10, 10, 10, 10, 10, 48, 48, 48,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 197, 196, 196, 196, 196, 195, 195, 195,
+ 131, 131, 130, 130, 130, 130, 10, 10,
+ 10, 10, 10, 10, 10, 48, 48, 48,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 196, 196, 196, 195, 148, 148, 147,
+ 147, 147, 130, 130, 130, 129, 10, 10,
+ 10, 10, 10, 10, 80, 80, 9, 9,
+ 187, 187, 187, 187, 186, 186, 186, 186,
+ 186, 149, 149, 148, 148, 148, 148, 147,
+ 147, 147, 146, 146, 146, 129, 25, 10,
+ 10, 10, 80, 80, 80, 80, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 149, 148, 148, 148, 147, 147,
+ 147, 146, 146, 146, 145, 145, 24, 24,
+ 24, 80, 80, 80, 80, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 148, 148, 148, 147, 147, 147,
+ 147, 146, 146, 145, 145, 145, 24, 24,
+ 7, 23, 80, 80, 22, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 181, 148, 147, 147, 147, 147,
+ 146, 146, 145, 145, 145, 112, 112, 112,
+ 23, 23, 22, 22, 22, 9, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 182, 181, 181, 147, 147, 147, 147,
+ 146, 145, 145, 145, 112, 112, 112, 112,
+ 8, 22, 22, 22, 21, 9, 9, 9,
+ 186, 186, 186, 186, 186, 185, 185, 185,
+ 182, 182, 181, 181, 179, 179, 147, 145,
+ 145, 145, 145, 112, 112, 112, 112, 8,
+ 8, 8, 8, 21, 21, 21, 9, 9,
+ 185, 185, 185, 185, 185, 185, 185, 185,
+ 182, 182, 181, 181, 179, 179, 179, 96,
+ 96, 96, 96, 112, 112, 112, 8, 8,
+ 8, 8, 8, 21, 21, 20, 9, 9,
+ 185, 185, 185, 185, 185, 185, 185, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 96, 96, 96, 96, 8, 8, 8, 8,
+ 8, 8, 8, 8, 20, 20, 19, 9,
+ 185, 185, 185, 185, 185, 185, 185, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 96, 96, 96, 96, 8, 8, 8, 8,
+ 8, 8, 8, 8, 20, 19, 19, 19,
+ 185, 185, 185, 185, 185, 185, 183, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 96, 96, 96, 96, 8, 8, 8, 8,
+ 8, 8, 8, 8, 19, 19, 19, 18,
+ 235, 234, 234, 234, 234, 234, 234, 234,
+ 234, 234, 234, 228, 218, 218, 218, 217,
+ 217, 217, 217, 217, 216, 216, 216, 216,
+ 216, 215, 215, 215, 215, 214, 214, 214,
+ 234, 234, 234, 234, 234, 234, 234, 234,
+ 234, 234, 228, 228, 218, 218, 218, 217,
+ 217, 217, 217, 217, 216, 216, 216, 216,
+ 216, 215, 215, 215, 215, 214, 214, 214,
+ 234, 234, 234, 234, 234, 234, 234, 234,
+ 234, 228, 228, 227, 227, 218, 218, 217,
+ 217, 217, 217, 217, 216, 216, 216, 216,
+ 216, 215, 215, 215, 215, 214, 214, 214,
+ 234, 234, 234, 234, 234, 234, 234, 234,
+ 228, 228, 227, 227, 227, 218, 218, 217,
+ 217, 217, 217, 217, 216, 216, 216, 216,
+ 215, 215, 215, 215, 215, 214, 214, 214,
+ 234, 234, 234, 234, 234, 234, 234, 228,
+ 228, 227, 227, 227, 226, 218, 218, 217,
+ 217, 217, 217, 217, 216, 216, 216, 216,
+ 215, 215, 215, 215, 215, 214, 214, 214,
+ 234, 234, 234, 172, 172, 172, 172, 172,
+ 227, 227, 227, 226, 226, 226, 218, 217,
+ 217, 217, 217, 216, 216, 216, 216, 216,
+ 215, 215, 215, 215, 215, 214, 214, 214,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 227, 227, 226, 226, 226, 225, 225, 217,
+ 217, 217, 217, 216, 216, 216, 216, 216,
+ 215, 215, 215, 215, 215, 214, 214, 214,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 226, 226, 226, 225, 225, 225, 217,
+ 217, 217, 217, 216, 216, 216, 216, 216,
+ 215, 215, 215, 215, 215, 214, 214, 214,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 226, 226, 225, 225, 225, 224, 224,
+ 217, 217, 217, 216, 216, 216, 216, 216,
+ 215, 215, 215, 215, 214, 214, 214, 214,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 198, 198, 198, 225, 224, 224, 224,
+ 224, 217, 216, 216, 216, 216, 216, 215,
+ 215, 215, 215, 215, 214, 214, 214, 214,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 198, 198, 198, 198, 197, 197, 197, 197,
+ 197, 196, 196, 216, 216, 216, 216, 215,
+ 215, 215, 215, 215, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 172, 172, 198,
+ 198, 198, 198, 197, 197, 197, 197, 197,
+ 196, 196, 196, 196, 216, 216, 216, 215,
+ 215, 215, 215, 215, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 171, 198,
+ 198, 198, 198, 197, 197, 197, 197, 196,
+ 196, 196, 196, 195, 11, 11, 11, 11,
+ 11, 215, 215, 215, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 198, 198,
+ 198, 198, 197, 197, 197, 197, 197, 196,
+ 196, 196, 195, 195, 11, 11, 11, 11,
+ 11, 11, 11, 11, 48, 48, 48, 48,
+ 171, 171, 171, 171, 171, 171, 198, 198,
+ 198, 197, 197, 197, 197, 197, 196, 196,
+ 196, 195, 195, 195, 11, 11, 11, 11,
+ 11, 11, 11, 11, 48, 48, 48, 48,
+ 171, 171, 171, 171, 171, 171, 198, 198,
+ 197, 197, 197, 197, 197, 196, 196, 196,
+ 195, 195, 195, 194, 11, 11, 11, 11,
+ 11, 11, 11, 10, 48, 48, 48, 48,
+ 171, 171, 171, 171, 171, 198, 198, 198,
+ 197, 197, 197, 197, 196, 196, 196, 196,
+ 195, 195, 194, 194, 194, 11, 11, 11,
+ 11, 11, 10, 10, 48, 48, 48, 48,
+ 187, 187, 187, 171, 171, 198, 198, 197,
+ 197, 197, 197, 196, 196, 196, 196, 195,
+ 195, 195, 194, 194, 194, 11, 11, 11,
+ 10, 10, 10, 10, 10, 48, 48, 48,
+ 187, 187, 187, 187, 187, 187, 197, 197,
+ 197, 197, 197, 196, 196, 196, 195, 195,
+ 195, 194, 194, 194, 194, 11, 11, 10,
+ 10, 10, 10, 10, 10, 48, 48, 48,
+ 187, 187, 187, 187, 187, 187, 187, 197,
+ 197, 197, 196, 196, 196, 195, 195, 195,
+ 195, 194, 194, 194, 130, 130, 10, 10,
+ 10, 10, 10, 10, 10, 10, 48, 48,
+ 187, 187, 187, 187, 187, 187, 187, 187,
+ 197, 196, 196, 196, 195, 195, 195, 195,
+ 194, 194, 194, 130, 130, 129, 10, 10,
+ 10, 10, 10, 10, 10, 10, 48, 48,
+ 187, 187, 187, 187, 186, 186, 186, 186,
+ 196, 196, 196, 196, 195, 195, 195, 194,
+ 194, 194, 130, 130, 129, 129, 10, 10,
+ 10, 10, 10, 10, 80, 80, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 196, 196, 195, 195, 195, 195, 147,
+ 147, 146, 146, 129, 129, 129, 128, 10,
+ 10, 10, 80, 80, 80, 80, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 195, 195, 195, 147, 147, 147,
+ 147, 146, 146, 145, 145, 145, 128, 24,
+ 7, 23, 23, 22, 22, 22, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 148, 148, 148, 147, 147, 147,
+ 146, 146, 145, 145, 145, 128, 128, 7,
+ 23, 23, 22, 22, 22, 21, 9, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 182, 181, 181, 147, 147, 147, 147,
+ 146, 145, 145, 145, 145, 144, 144, 23,
+ 23, 22, 22, 22, 21, 21, 21, 9,
+ 186, 186, 186, 186, 186, 185, 185, 185,
+ 182, 182, 181, 181, 179, 179, 147, 145,
+ 145, 145, 145, 145, 144, 144, 112, 112,
+ 22, 22, 22, 21, 21, 21, 20, 9,
+ 185, 185, 185, 185, 185, 185, 185, 185,
+ 182, 182, 181, 181, 179, 179, 179, 145,
+ 145, 145, 145, 144, 144, 112, 8, 8,
+ 8, 8, 21, 21, 21, 20, 20, 19,
+ 185, 185, 185, 185, 185, 185, 185, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 177, 145, 145, 144, 112, 8, 8, 8,
+ 8, 8, 21, 21, 20, 20, 19, 19,
+ 185, 185, 185, 185, 185, 185, 185, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 177, 176, 176, 112, 8, 8, 8, 8,
+ 8, 8, 8, 20, 20, 19, 19, 19,
+ 185, 185, 185, 185, 185, 185, 183, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 177, 176, 176, 8, 8, 8, 8, 8,
+ 8, 8, 8, 20, 19, 19, 19, 18,
+ 185, 185, 185, 185, 185, 183, 183, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 177, 176, 176, 176, 8, 8, 8, 8,
+ 8, 8, 8, 19, 19, 19, 18, 18,
+ 234, 234, 234, 234, 234, 234, 234, 234,
+ 234, 229, 229, 228, 228, 218, 217, 217,
+ 217, 217, 217, 216, 216, 216, 216, 216,
+ 215, 215, 215, 215, 214, 214, 214, 214,
+ 234, 234, 234, 234, 234, 234, 234, 234,
+ 229, 229, 228, 228, 227, 218, 217, 217,
+ 217, 217, 217, 216, 216, 216, 216, 216,
+ 215, 215, 215, 215, 214, 214, 214, 214,
+ 234, 234, 234, 234, 234, 234, 234, 229,
+ 229, 228, 228, 227, 227, 227, 217, 217,
+ 217, 217, 217, 216, 216, 216, 216, 216,
+ 215, 215, 215, 215, 214, 214, 214, 214,
+ 234, 234, 234, 234, 234, 234, 229, 229,
+ 228, 228, 227, 227, 227, 226, 217, 217,
+ 217, 217, 217, 216, 216, 216, 216, 215,
+ 215, 215, 215, 215, 214, 214, 214, 214,
+ 234, 234, 234, 234, 234, 229, 229, 228,
+ 228, 227, 227, 227, 226, 226, 226, 217,
+ 217, 217, 217, 216, 216, 216, 216, 215,
+ 215, 215, 215, 215, 214, 214, 214, 214,
+ 234, 234, 234, 234, 172, 229, 228, 228,
+ 227, 227, 227, 226, 226, 226, 225, 217,
+ 217, 217, 216, 216, 216, 216, 216, 215,
+ 215, 215, 215, 215, 214, 214, 214, 214,
+ 172, 172, 172, 172, 172, 172, 228, 227,
+ 227, 227, 226, 226, 226, 225, 225, 225,
+ 217, 217, 216, 216, 216, 216, 216, 215,
+ 215, 215, 215, 215, 214, 214, 214, 214,
+ 172, 172, 172, 172, 172, 172, 172, 227,
+ 227, 226, 226, 226, 225, 225, 225, 224,
+ 224, 217, 216, 216, 216, 216, 216, 215,
+ 215, 215, 215, 215, 214, 214, 214, 214,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 226, 226, 226, 225, 225, 225, 224, 224,
+ 224, 217, 216, 216, 216, 216, 216, 215,
+ 215, 215, 215, 214, 214, 214, 214, 214,
+ 172, 172, 172, 172, 172, 172, 172, 172,
+ 226, 226, 225, 225, 225, 224, 224, 224,
+ 224, 224, 216, 216, 216, 216, 215, 215,
+ 215, 215, 215, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 171, 172,
+ 226, 225, 225, 225, 224, 224, 224, 224,
+ 224, 224, 224, 216, 216, 216, 215, 215,
+ 215, 215, 215, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 198, 198, 225, 197, 197, 224, 224, 224,
+ 196, 196, 196, 195, 216, 216, 215, 215,
+ 215, 215, 215, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 198, 198, 197, 197, 197, 197, 197, 196,
+ 196, 196, 196, 195, 194, 11, 11, 11,
+ 215, 215, 215, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 171, 198,
+ 198, 197, 197, 197, 197, 197, 196, 196,
+ 196, 196, 195, 195, 194, 11, 11, 11,
+ 11, 11, 11, 214, 214, 214, 48, 48,
+ 171, 171, 171, 171, 171, 171, 171, 198,
+ 197, 197, 197, 197, 197, 196, 196, 196,
+ 196, 195, 195, 194, 194, 11, 11, 11,
+ 11, 11, 11, 10, 48, 48, 48, 48,
+ 171, 171, 171, 171, 171, 171, 171, 198,
+ 197, 197, 197, 197, 197, 196, 196, 196,
+ 195, 195, 195, 194, 194, 11, 11, 11,
+ 11, 11, 10, 10, 10, 48, 48, 48,
+ 170, 171, 171, 171, 171, 171, 198, 197,
+ 197, 197, 197, 197, 196, 196, 196, 195,
+ 195, 195, 194, 194, 194, 194, 11, 11,
+ 10, 10, 10, 10, 10, 48, 48, 48,
+ 170, 170, 170, 171, 171, 169, 197, 197,
+ 197, 197, 197, 196, 196, 196, 195, 195,
+ 195, 194, 194, 194, 194, 193, 11, 10,
+ 10, 10, 10, 10, 10, 10, 48, 48,
+ 187, 187, 187, 187, 169, 169, 169, 197,
+ 197, 197, 196, 196, 196, 196, 195, 195,
+ 195, 194, 194, 194, 193, 193, 193, 10,
+ 10, 10, 10, 10, 10, 10, 48, 48,
+ 187, 187, 187, 187, 187, 187, 169, 197,
+ 197, 196, 196, 196, 196, 195, 195, 195,
+ 194, 194, 194, 194, 193, 193, 193, 10,
+ 10, 10, 10, 10, 10, 10, 10, 48,
+ 187, 187, 187, 187, 186, 186, 186, 167,
+ 197, 196, 196, 196, 195, 195, 195, 195,
+ 194, 194, 194, 193, 193, 193, 192, 10,
+ 10, 10, 10, 10, 10, 10, 80, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 167, 196, 196, 195, 195, 195, 195, 194,
+ 194, 194, 193, 193, 193, 192, 192, 10,
+ 10, 10, 10, 10, 22, 22, 22, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 196, 195, 195, 195, 195, 194, 194,
+ 194, 193, 193, 193, 129, 192, 192, 128,
+ 10, 23, 23, 22, 22, 22, 21, 9,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 166, 195, 195, 195, 195, 194, 194,
+ 193, 193, 193, 145, 145, 192, 128, 128,
+ 23, 23, 22, 22, 22, 21, 21, 21,
+ 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 182, 181, 195, 195, 147, 147, 193,
+ 193, 145, 145, 145, 145, 128, 128, 128,
+ 23, 22, 22, 22, 21, 21, 21, 20,
+ 186, 186, 186, 186, 186, 185, 185, 185,
+ 182, 182, 181, 181, 179, 147, 147, 145,
+ 145, 145, 145, 145, 144, 144, 144, 144,
+ 22, 22, 22, 21, 21, 21, 20, 20,
+ 185, 185, 185, 185, 185, 185, 185, 185,
+ 182, 182, 181, 181, 179, 179, 179, 145,
+ 145, 145, 145, 145, 144, 144, 144, 144,
+ 22, 22, 21, 21, 21, 20, 20, 19,
+ 185, 185, 185, 185, 185, 185, 185, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 145, 145, 145, 144, 144, 144, 144, 8,
+ 8, 21, 21, 21, 20, 20, 19, 19,
+ 185, 185, 185, 185, 185, 185, 185, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 177, 176, 144, 144, 144, 144, 8, 8,
+ 8, 21, 21, 20, 20, 19, 19, 19,
+ 185, 185, 185, 185, 185, 185, 183, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 177, 176, 176, 176, 8, 8, 8, 8,
+ 8, 8, 20, 20, 19, 19, 19, 18,
+ 185, 185, 185, 185, 185, 183, 183, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 177, 176, 176, 176, 8, 8, 8, 8,
+ 8, 8, 20, 19, 19, 19, 18, 18,
+ 184, 184, 184, 184, 183, 183, 183, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 177, 176, 176, 176, 176, 8, 8, 8,
+ 8, 8, 19, 19, 19, 18, 18, 18,
+ 233, 233, 233, 233, 233, 233, 233, 233,
+ 229, 229, 228, 228, 228, 227, 217, 217,
+ 217, 217, 216, 216, 216, 216, 216, 215,
+ 215, 215, 215, 214, 214, 214, 214, 253,
+ 233, 233, 233, 233, 233, 233, 233, 229,
+ 229, 228, 228, 228, 227, 227, 217, 217,
+ 217, 217, 216, 216, 216, 216, 216, 215,
+ 215, 215, 215, 214, 214, 214, 214, 214,
+ 233, 233, 233, 233, 233, 233, 229, 229,
+ 228, 228, 228, 227, 227, 227, 217, 217,
+ 217, 217, 216, 216, 216, 216, 216, 215,
+ 215, 215, 215, 214, 214, 214, 214, 214,
+ 233, 233, 233, 233, 233, 229, 229, 228,
+ 228, 228, 227, 227, 227, 226, 226, 217,
+ 217, 217, 216, 216, 216, 216, 215, 215,
+ 215, 215, 215, 214, 214, 214, 214, 214,
+ 233, 233, 233, 233, 229, 229, 228, 228,
+ 228, 227, 227, 227, 226, 226, 226, 217,
+ 217, 217, 216, 216, 216, 216, 215, 215,
+ 215, 215, 215, 214, 214, 214, 214, 214,
+ 233, 233, 233, 229, 229, 228, 228, 228,
+ 227, 227, 227, 226, 226, 226, 225, 225,
+ 217, 216, 216, 216, 216, 216, 215, 215,
+ 215, 215, 215, 214, 214, 214, 214, 214,
+ 233, 172, 172, 229, 228, 228, 228, 227,
+ 227, 227, 226, 226, 226, 225, 225, 225,
+ 224, 216, 216, 216, 216, 216, 215, 215,
+ 215, 215, 215, 214, 214, 214, 214, 214,
+ 172, 172, 172, 172, 172, 228, 227, 227,
+ 227, 226, 226, 226, 225, 225, 225, 224,
+ 224, 216, 216, 216, 216, 216, 215, 215,
+ 215, 215, 215, 214, 214, 214, 214, 214,
+ 171, 171, 172, 172, 172, 172, 227, 227,
+ 226, 226, 226, 225, 225, 225, 224, 224,
+ 224, 224, 216, 216, 216, 216, 215, 215,
+ 215, 215, 214, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 171, 226,
+ 226, 226, 225, 225, 225, 224, 224, 224,
+ 224, 224, 224, 216, 216, 215, 215, 215,
+ 215, 215, 214, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 171, 226,
+ 226, 225, 225, 225, 224, 224, 224, 224,
+ 224, 224, 224, 216, 216, 215, 215, 215,
+ 215, 215, 214, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 225, 225, 225, 224, 224, 224, 224, 224,
+ 224, 224, 224, 224, 216, 215, 215, 215,
+ 215, 215, 214, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 225, 225, 224, 224, 224, 224, 224, 224,
+ 224, 196, 195, 195, 194, 194, 11, 215,
+ 215, 215, 214, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 198, 197, 197, 197, 197, 197, 196, 196,
+ 196, 195, 195, 194, 194, 194, 11, 11,
+ 11, 11, 214, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 171, 171,
+ 197, 197, 197, 197, 197, 196, 196, 196,
+ 196, 195, 195, 194, 194, 194, 11, 11,
+ 11, 11, 10, 10, 10, 48, 48, 48,
+ 170, 170, 171, 171, 171, 171, 171, 197,
+ 197, 197, 197, 197, 196, 196, 196, 196,
+ 195, 195, 194, 194, 194, 194, 11, 11,
+ 11, 10, 10, 10, 10, 48, 48, 48,
+ 170, 170, 170, 170, 171, 171, 169, 197,
+ 197, 197, 197, 196, 196, 196, 196, 195,
+ 195, 195, 194, 194, 194, 194, 193, 10,
+ 10, 10, 10, 10, 10, 10, 48, 48,
+ 170, 170, 170, 170, 169, 169, 169, 197,
+ 197, 197, 197, 196, 196, 196, 195, 195,
+ 195, 194, 194, 194, 194, 193, 193, 10,
+ 10, 10, 10, 10, 10, 10, 48, 48,
+ 170, 168, 168, 168, 169, 169, 169, 169,
+ 197, 197, 196, 196, 196, 195, 195, 195,
+ 194, 194, 194, 194, 193, 193, 193, 10,
+ 10, 10, 10, 10, 10, 10, 10, 48,
+ 168, 168, 168, 168, 169, 169, 169, 169,
+ 197, 196, 196, 196, 195, 195, 195, 195,
+ 194, 194, 194, 193, 193, 193, 193, 192,
+ 10, 10, 10, 10, 10, 10, 10, 48,
+ 186, 186, 186, 168, 168, 167, 167, 167,
+ 167, 196, 196, 196, 195, 195, 195, 194,
+ 194, 194, 193, 193, 193, 193, 192, 192,
+ 10, 10, 10, 10, 10, 22, 22, 21,
+ 186, 186, 186, 186, 186, 167, 167, 167,
+ 167, 167, 196, 195, 195, 195, 195, 194,
+ 194, 193, 193, 193, 193, 192, 192, 192,
+ 192, 10, 10, 22, 22, 22, 21, 21,
+ 186, 186, 186, 186, 186, 186, 167, 167,
+ 167, 167, 195, 195, 195, 195, 194, 194,
+ 194, 193, 193, 193, 192, 192, 192, 192,
+ 192, 23, 22, 22, 22, 21, 21, 21,
+ 186, 186, 186, 186, 186, 186, 186, 166,
+ 166, 166, 166, 195, 195, 194, 194, 194,
+ 193, 193, 193, 193, 192, 192, 192, 128,
+ 128, 22, 22, 22, 21, 21, 21, 20,
+ 186, 186, 186, 186, 186, 185, 185, 166,
+ 166, 166, 181, 181, 195, 194, 194, 193,
+ 193, 193, 145, 145, 192, 128, 128, 128,
+ 22, 22, 22, 21, 21, 21, 20, 20,
+ 185, 185, 185, 185, 185, 185, 185, 185,
+ 182, 182, 181, 181, 179, 179, 163, 193,
+ 145, 145, 145, 145, 144, 144, 144, 144,
+ 22, 22, 21, 21, 21, 20, 20, 19,
+ 185, 185, 185, 185, 185, 185, 185, 182,
+ 182, 182, 181, 181, 179, 179, 163, 163,
+ 145, 145, 145, 144, 144, 144, 144, 144,
+ 22, 21, 21, 21, 20, 20, 19, 19,
+ 185, 185, 185, 185, 185, 185, 185, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 177, 176, 144, 144, 144, 144, 144, 144,
+ 21, 21, 21, 20, 20, 19, 19, 19,
+ 185, 185, 185, 185, 185, 185, 183, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 177, 176, 176, 144, 144, 144, 144, 8,
+ 21, 21, 20, 20, 19, 19, 19, 18,
+ 185, 185, 185, 185, 185, 183, 183, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 177, 176, 176, 176, 176, 8, 8, 8,
+ 8, 20, 20, 19, 19, 19, 18, 18,
+ 184, 184, 184, 184, 183, 183, 183, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 177, 176, 176, 176, 176, 8, 8, 8,
+ 8, 20, 19, 19, 19, 18, 18, 18,
+ 184, 184, 184, 184, 183, 183, 183, 182,
+ 182, 181, 181, 181, 179, 179, 177, 177,
+ 177, 176, 176, 176, 176, 160, 160, 8,
+ 8, 19, 19, 19, 18, 18, 18, 17,
+ 233, 232, 232, 232, 232, 232, 232, 229,
+ 229, 229, 228, 228, 228, 227, 217, 217,
+ 217, 216, 216, 216, 216, 216, 215, 215,
+ 215, 215, 214, 214, 214, 214, 253, 253,
+ 232, 232, 232, 232, 232, 232, 229, 229,
+ 229, 228, 228, 228, 227, 227, 227, 217,
+ 217, 216, 216, 216, 216, 216, 215, 215,
+ 215, 215, 214, 214, 214, 214, 214, 253,
+ 232, 232, 232, 232, 232, 229, 229, 229,
+ 228, 228, 228, 227, 227, 227, 226, 217,
+ 217, 216, 216, 216, 216, 216, 215, 215,
+ 215, 215, 214, 214, 214, 214, 214, 214,
+ 232, 232, 232, 232, 229, 229, 229, 228,
+ 228, 228, 227, 227, 227, 226, 226, 226,
+ 217, 216, 216, 216, 216, 215, 215, 215,
+ 215, 215, 214, 214, 214, 214, 214, 214,
+ 232, 232, 232, 229, 229, 229, 228, 228,
+ 228, 227, 227, 227, 226, 226, 226, 225,
+ 217, 216, 216, 216, 216, 215, 215, 215,
+ 215, 215, 214, 214, 214, 214, 214, 214,
+ 232, 232, 229, 229, 229, 228, 228, 228,
+ 227, 227, 227, 226, 226, 226, 225, 225,
+ 225, 216, 216, 216, 216, 215, 215, 215,
+ 215, 215, 214, 214, 214, 214, 214, 214,
+ 232, 229, 229, 229, 228, 228, 228, 227,
+ 227, 227, 226, 226, 226, 225, 225, 225,
+ 224, 216, 216, 216, 216, 215, 215, 215,
+ 215, 215, 214, 214, 214, 214, 214, 214,
+ 171, 171, 229, 228, 228, 228, 227, 227,
+ 227, 226, 226, 226, 225, 225, 225, 224,
+ 224, 224, 216, 216, 216, 215, 215, 215,
+ 215, 215, 214, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 227, 227, 227,
+ 226, 226, 226, 225, 225, 225, 224, 224,
+ 224, 224, 224, 216, 216, 215, 215, 215,
+ 215, 214, 214, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 227, 226,
+ 226, 226, 225, 225, 225, 224, 224, 224,
+ 224, 224, 224, 216, 215, 215, 215, 215,
+ 215, 214, 214, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 171, 226,
+ 226, 225, 225, 225, 224, 224, 224, 224,
+ 224, 224, 224, 224, 215, 215, 215, 215,
+ 215, 214, 214, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 171, 226,
+ 225, 225, 225, 224, 224, 224, 224, 224,
+ 224, 224, 224, 224, 224, 215, 215, 215,
+ 215, 214, 214, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 171, 225,
+ 225, 225, 224, 224, 224, 224, 224, 224,
+ 224, 224, 224, 195, 194, 194, 215, 215,
+ 215, 214, 214, 214, 214, 214, 214, 214,
+ 170, 171, 171, 171, 171, 171, 171, 171,
+ 225, 224, 224, 224, 224, 224, 224, 224,
+ 224, 195, 195, 194, 194, 194, 194, 11,
+ 11, 214, 214, 214, 214, 214, 214, 214,
+ 170, 170, 170, 171, 171, 171, 171, 171,
+ 224, 224, 224, 224, 224, 224, 224, 196,
+ 195, 195, 194, 194, 194, 194, 194, 11,
+ 11, 10, 10, 10, 10, 214, 214, 214,
+ 170, 170, 170, 170, 170, 171, 171, 169,
+ 197, 197, 197, 197, 196, 196, 196, 195,
+ 195, 195, 194, 194, 194, 194, 193, 193,
+ 10, 10, 10, 10, 10, 10, 48, 48,
+ 170, 170, 170, 170, 170, 169, 169, 169,
+ 197, 197, 197, 196, 196, 196, 196, 195,
+ 195, 194, 194, 194, 194, 193, 193, 193,
+ 10, 10, 10, 10, 10, 10, 10, 48,
+ 170, 170, 170, 169, 169, 169, 169, 169,
+ 169, 197, 196, 196, 196, 196, 195, 195,
+ 195, 194, 194, 194, 193, 193, 193, 193,
+ 10, 10, 10, 10, 10, 10, 10, 48,
+ 168, 168, 168, 168, 169, 169, 169, 169,
+ 169, 196, 196, 196, 196, 195, 195, 195,
+ 194, 194, 194, 194, 193, 193, 193, 192,
+ 192, 10, 10, 10, 10, 10, 10, 10,
+ 168, 168, 168, 168, 169, 169, 169, 169,
+ 167, 196, 196, 196, 195, 195, 195, 194,
+ 194, 194, 194, 193, 193, 193, 192, 192,
+ 192, 10, 10, 10, 10, 10, 22, 21,
+ 168, 168, 168, 168, 168, 167, 167, 167,
+ 167, 167, 196, 195, 195, 195, 195, 194,
+ 194, 194, 193, 193, 193, 193, 192, 192,
+ 192, 192, 10, 10, 22, 22, 21, 21,
+ 168, 168, 168, 168, 167, 167, 167, 167,
+ 167, 167, 167, 195, 195, 195, 194, 194,
+ 194, 193, 193, 193, 193, 192, 192, 192,
+ 192, 192, 22, 22, 22, 21, 21, 21,
+ 186, 186, 186, 186, 167, 167, 167, 167,
+ 167, 167, 167, 195, 195, 195, 194, 194,
+ 193, 193, 193, 193, 192, 192, 192, 192,
+ 192, 192, 22, 22, 21, 21, 21, 20,
+ 186, 186, 186, 186, 166, 166, 166, 166,
+ 166, 166, 166, 195, 195, 194, 194, 193,
+ 193, 193, 193, 192, 192, 192, 192, 192,
+ 128, 22, 22, 21, 21, 21, 20, 20,
+ 185, 185, 185, 185, 185, 166, 166, 166,
+ 166, 166, 166, 181, 163, 163, 193, 193,
+ 193, 193, 193, 192, 192, 192, 192, 128,
+ 128, 22, 21, 21, 21, 20, 20, 19,
+ 185, 185, 185, 185, 185, 185, 185, 164,
+ 182, 182, 181, 181, 179, 163, 163, 163,
+ 193, 145, 145, 192, 144, 144, 144, 144,
+ 22, 21, 21, 21, 20, 20, 19, 19,
+ 185, 185, 185, 185, 185, 185, 185, 182,
+ 182, 182, 181, 181, 179, 163, 163, 163,
+ 163, 145, 145, 144, 144, 144, 144, 144,
+ 21, 21, 21, 20, 20, 19, 19, 19,
+ 185, 185, 185, 185, 185, 185, 183, 182,
+ 182, 182, 181, 181, 179, 179, 163, 163,
+ 177, 176, 176, 144, 144, 144, 144, 144,
+ 21, 21, 20, 20, 19, 19, 19, 18,
+ 185, 185, 185, 185, 185, 183, 183, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 177, 176, 176, 176, 144, 144, 144, 21,
+ 21, 20, 20, 19, 19, 19, 18, 18,
+ 184, 184, 184, 184, 183, 183, 183, 182,
+ 182, 182, 181, 181, 179, 179, 179, 177,
+ 177, 176, 176, 176, 176, 176, 8, 8,
+ 20, 20, 19, 19, 19, 18, 18, 18,
+ 184, 184, 184, 184, 183, 183, 183, 182,
+ 182, 181, 181, 181, 179, 179, 177, 177,
+ 177, 176, 176, 176, 176, 160, 160, 160,
+ 20, 19, 19, 19, 18, 18, 18, 17,
+ 184, 184, 184, 184, 183, 183, 183, 182,
+ 182, 181, 181, 181, 179, 179, 177, 177,
+ 177, 176, 176, 176, 176, 160, 160, 160,
+ 160, 19, 19, 18, 18, 18, 17, 17,
+ 232, 232, 232, 232, 232, 230, 230, 229,
+ 229, 229, 228, 228, 228, 227, 227, 217,
+ 216, 216, 216, 216, 216, 215, 215, 215,
+ 215, 214, 214, 214, 214, 253, 253, 253,
+ 232, 232, 232, 232, 230, 230, 229, 229,
+ 229, 228, 228, 228, 227, 227, 227, 226,
+ 216, 216, 216, 216, 216, 215, 215, 215,
+ 215, 214, 214, 214, 214, 214, 253, 253,
+ 232, 232, 232, 230, 230, 229, 229, 229,
+ 228, 228, 228, 227, 227, 227, 226, 226,
+ 216, 216, 216, 216, 216, 215, 215, 215,
+ 215, 214, 214, 214, 214, 214, 214, 253,
+ 232, 232, 230, 230, 229, 229, 229, 228,
+ 228, 228, 227, 227, 227, 226, 226, 226,
+ 216, 216, 216, 216, 215, 215, 215, 215,
+ 215, 214, 214, 214, 214, 214, 214, 214,
+ 232, 230, 230, 229, 229, 229, 228, 228,
+ 228, 227, 227, 227, 226, 226, 226, 225,
+ 225, 216, 216, 216, 215, 215, 215, 215,
+ 215, 214, 214, 214, 214, 214, 214, 214,
+ 230, 230, 229, 229, 229, 228, 228, 228,
+ 227, 227, 227, 226, 226, 226, 225, 225,
+ 225, 216, 216, 216, 215, 215, 215, 215,
+ 215, 214, 214, 214, 214, 214, 214, 214,
+ 230, 229, 229, 229, 228, 228, 228, 227,
+ 227, 227, 226, 226, 226, 225, 225, 225,
+ 224, 224, 216, 216, 215, 215, 215, 215,
+ 215, 214, 214, 214, 214, 214, 214, 214,
+ 229, 229, 229, 228, 228, 228, 227, 227,
+ 227, 226, 226, 226, 225, 225, 225, 224,
+ 224, 224, 224, 216, 215, 215, 215, 215,
+ 215, 214, 214, 214, 214, 214, 214, 214,
+ 171, 171, 171, 228, 228, 227, 227, 227,
+ 226, 226, 226, 225, 225, 225, 224, 224,
+ 224, 224, 224, 216, 215, 215, 215, 215,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 227, 227, 226,
+ 226, 226, 225, 225, 225, 224, 224, 224,
+ 224, 224, 224, 224, 215, 215, 215, 215,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 226, 226,
+ 226, 225, 225, 225, 224, 224, 224, 224,
+ 224, 224, 224, 224, 224, 215, 215, 215,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 171, 171, 226, 226,
+ 225, 225, 225, 224, 224, 224, 224, 224,
+ 224, 224, 224, 224, 224, 215, 215, 215,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 170, 170, 171, 171, 171, 171, 171, 225,
+ 225, 225, 224, 224, 224, 224, 224, 224,
+ 224, 224, 224, 224, 194, 194, 215, 215,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 170, 170, 170, 170, 171, 171, 171, 225,
+ 225, 224, 224, 224, 224, 224, 224, 224,
+ 224, 224, 224, 194, 194, 194, 194, 194,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 170, 170, 170, 170, 170, 170, 170, 225,
+ 224, 224, 224, 224, 224, 224, 224, 224,
+ 224, 195, 194, 194, 194, 194, 194, 193,
+ 193, 10, 10, 10, 214, 214, 214, 214,
+ 170, 170, 170, 170, 170, 170, 169, 169,
+ 224, 224, 224, 224, 224, 224, 224, 195,
+ 195, 194, 194, 194, 194, 194, 193, 193,
+ 193, 10, 10, 10, 10, 10, 10, 48,
+ 170, 170, 170, 170, 169, 169, 169, 169,
+ 169, 224, 224, 224, 196, 196, 195, 195,
+ 195, 194, 194, 194, 194, 193, 193, 193,
+ 192, 10, 10, 10, 10, 10, 10, 48,
+ 170, 170, 168, 169, 169, 169, 169, 169,
+ 169, 169, 196, 196, 196, 195, 195, 195,
+ 194, 194, 194, 194, 193, 193, 193, 193,
+ 192, 192, 10, 10, 10, 10, 10, 10,
+ 168, 168, 168, 168, 169, 169, 169, 169,
+ 169, 169, 196, 196, 196, 195, 195, 195,
+ 194, 194, 194, 193, 193, 193, 193, 192,
+ 192, 192, 10, 10, 10, 10, 10, 21,
+ 168, 168, 168, 168, 169, 169, 169, 169,
+ 167, 167, 196, 196, 195, 195, 195, 194,
+ 194, 194, 193, 193, 193, 193, 192, 192,
+ 192, 192, 192, 10, 10, 22, 21, 21,
+ 168, 168, 168, 168, 168, 167, 167, 167,
+ 167, 167, 167, 195, 195, 195, 194, 194,
+ 194, 193, 193, 193, 193, 192, 192, 192,
+ 192, 192, 192, 22, 22, 21, 21, 21,
+ 168, 168, 168, 168, 167, 167, 167, 167,
+ 167, 167, 167, 195, 195, 195, 194, 194,
+ 194, 193, 193, 193, 193, 192, 192, 192,
+ 192, 192, 22, 22, 21, 21, 21, 20,
+ 165, 165, 165, 167, 167, 167, 167, 167,
+ 167, 167, 167, 167, 195, 194, 194, 194,
+ 193, 193, 193, 193, 192, 192, 192, 192,
+ 192, 192, 22, 21, 21, 21, 20, 20,
+ 165, 165, 165, 165, 166, 166, 166, 166,
+ 166, 166, 166, 166, 163, 194, 194, 193,
+ 193, 193, 193, 192, 192, 192, 192, 192,
+ 192, 22, 21, 21, 21, 20, 20, 19,
+ 165, 165, 165, 165, 165, 166, 166, 166,
+ 166, 166, 166, 181, 163, 163, 163, 193,
+ 193, 193, 192, 192, 192, 192, 192, 192,
+ 128, 21, 21, 21, 20, 20, 19, 19,
+ 185, 185, 185, 185, 165, 164, 164, 164,
+ 164, 182, 181, 181, 163, 163, 163, 163,
+ 163, 193, 192, 192, 192, 144, 144, 144,
+ 144, 21, 21, 20, 20, 19, 19, 19,
+ 185, 185, 185, 185, 185, 164, 164, 164,
+ 182, 182, 181, 181, 179, 163, 163, 163,
+ 163, 163, 176, 144, 144, 144, 144, 144,
+ 21, 21, 20, 20, 19, 19, 19, 18,
+ 185, 185, 185, 185, 185, 183, 183, 182,
+ 182, 182, 181, 181, 179, 163, 163, 163,
+ 163, 176, 176, 176, 144, 144, 144, 144,
+ 21, 20, 20, 19, 19, 19, 18, 18,
+ 184, 184, 184, 184, 183, 183, 183, 182,
+ 182, 182, 181, 181, 179, 179, 163, 163,
+ 177, 176, 176, 176, 176, 144, 144, 21,
+ 20, 20, 19, 19, 19, 18, 18, 18,
+ 184, 184, 184, 184, 183, 183, 183, 182,
+ 182, 181, 181, 181, 179, 179, 177, 177,
+ 177, 176, 176, 176, 176, 160, 160, 160,
+ 20, 19, 19, 19, 18, 18, 18, 17,
+ 184, 184, 184, 184, 183, 183, 183, 182,
+ 182, 181, 181, 181, 179, 179, 177, 177,
+ 177, 176, 176, 176, 176, 160, 160, 160,
+ 160, 19, 19, 18, 18, 18, 17, 17,
+ 184, 184, 184, 184, 183, 183, 183, 182,
+ 182, 181, 181, 181, 179, 179, 177, 177,
+ 177, 176, 176, 176, 176, 160, 160, 160,
+ 160, 19, 18, 18, 18, 17, 17, 17,
+ 249, 231, 231, 231, 230, 230, 230, 229,
+ 229, 229, 228, 228, 228, 227, 227, 227,
+ 216, 216, 216, 216, 215, 215, 215, 215,
+ 214, 214, 214, 214, 253, 253, 253, 253,
+ 231, 231, 231, 230, 230, 230, 229, 229,
+ 229, 228, 228, 228, 227, 227, 227, 226,
+ 216, 216, 216, 216, 215, 215, 215, 215,
+ 214, 214, 214, 214, 214, 253, 253, 253,
+ 231, 231, 230, 230, 230, 229, 229, 229,
+ 228, 228, 228, 227, 227, 227, 226, 226,
+ 226, 216, 216, 216, 215, 215, 215, 215,
+ 214, 214, 214, 214, 214, 214, 253, 253,
+ 231, 230, 230, 230, 229, 229, 229, 228,
+ 228, 228, 227, 227, 227, 226, 226, 226,
+ 225, 216, 216, 215, 215, 215, 215, 215,
+ 214, 214, 214, 214, 214, 214, 214, 253,
+ 230, 230, 230, 229, 229, 229, 228, 228,
+ 228, 227, 227, 227, 226, 226, 226, 225,
+ 225, 216, 216, 215, 215, 215, 215, 215,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 230, 230, 229, 229, 229, 228, 228, 228,
+ 227, 227, 227, 226, 226, 226, 225, 225,
+ 225, 224, 216, 215, 215, 215, 215, 215,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 230, 229, 229, 229, 228, 228, 228, 227,
+ 227, 227, 226, 226, 226, 225, 225, 225,
+ 224, 224, 224, 215, 215, 215, 215, 215,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 229, 229, 229, 228, 228, 228, 227, 227,
+ 227, 226, 226, 226, 225, 225, 225, 224,
+ 224, 224, 224, 215, 215, 215, 215, 215,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 171, 171, 228, 228, 228, 227, 227, 227,
+ 226, 226, 226, 225, 225, 225, 224, 224,
+ 224, 224, 224, 224, 215, 215, 215, 214,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 171, 171, 171, 171, 227, 227, 227, 226,
+ 226, 226, 225, 225, 225, 224, 224, 224,
+ 224, 224, 224, 224, 215, 215, 215, 214,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 170, 171, 171, 171, 171, 227, 226, 226,
+ 226, 225, 225, 225, 224, 224, 224, 224,
+ 224, 224, 224, 224, 224, 215, 215, 214,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 170, 170, 170, 171, 171, 171, 226, 226,
+ 225, 225, 225, 224, 224, 224, 224, 224,
+ 224, 224, 224, 224, 224, 215, 215, 214,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 170, 170, 170, 170, 170, 170, 226, 225,
+ 225, 225, 224, 224, 224, 224, 224, 224,
+ 224, 224, 224, 224, 224, 194, 215, 214,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 170, 170, 170, 170, 170, 170, 170, 225,
+ 225, 224, 224, 224, 224, 224, 224, 224,
+ 224, 224, 224, 224, 194, 194, 194, 193,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 170, 170, 170, 170, 170, 170, 170, 225,
+ 224, 224, 224, 224, 224, 224, 224, 224,
+ 224, 224, 194, 194, 194, 194, 193, 193,
+ 193, 10, 10, 214, 214, 214, 214, 214,
+ 170, 170, 170, 170, 170, 169, 169, 169,
+ 224, 224, 224, 224, 224, 224, 224, 224,
+ 224, 194, 194, 194, 194, 193, 193, 193,
+ 193, 192, 10, 10, 10, 10, 10, 214,
+ 170, 170, 170, 170, 169, 169, 169, 169,
+ 169, 224, 224, 224, 224, 224, 224, 195,
+ 194, 194, 194, 194, 194, 193, 193, 193,
+ 192, 192, 10, 10, 10, 10, 10, 10,
+ 170, 168, 168, 169, 169, 169, 169, 169,
+ 169, 169, 224, 224, 224, 195, 195, 195,
+ 194, 194, 194, 194, 193, 193, 193, 192,
+ 192, 192, 192, 10, 10, 10, 10, 10,
+ 168, 168, 168, 168, 169, 169, 169, 169,
+ 169, 169, 169, 196, 195, 195, 195, 194,
+ 194, 194, 194, 193, 193, 193, 193, 192,
+ 192, 192, 192, 10, 10, 10, 21, 21,
+ 168, 168, 168, 168, 169, 169, 169, 169,
+ 167, 167, 167, 195, 195, 195, 195, 194,
+ 194, 194, 193, 193, 193, 193, 192, 192,
+ 192, 192, 192, 192, 22, 21, 21, 21,
+ 168, 168, 168, 168, 168, 167, 167, 167,
+ 167, 167, 167, 167, 195, 195, 194, 194,
+ 194, 193, 193, 193, 193, 192, 192, 192,
+ 192, 192, 192, 22, 21, 21, 21, 20,
+ 168, 168, 168, 168, 167, 167, 167, 167,
+ 167, 167, 167, 167, 195, 194, 194, 194,
+ 193, 193, 193, 193, 192, 192, 192, 192,
+ 192, 192, 192, 21, 21, 21, 20, 20,
+ 165, 165, 165, 167, 167, 167, 167, 167,
+ 167, 167, 167, 167, 167, 194, 194, 193,
+ 193, 193, 193, 193, 192, 192, 192, 192,
+ 192, 192, 21, 21, 21, 20, 20, 19,
+ 165, 165, 165, 165, 166, 166, 166, 166,
+ 166, 166, 166, 166, 163, 163, 163, 193,
+ 193, 193, 193, 192, 192, 192, 192, 192,
+ 192, 192, 21, 21, 20, 20, 19, 19,
+ 165, 165, 165, 165, 165, 166, 166, 166,
+ 166, 166, 166, 181, 163, 163, 163, 163,
+ 193, 193, 192, 192, 192, 192, 192, 192,
+ 192, 21, 21, 20, 20, 19, 19, 19,
+ 165, 165, 165, 165, 165, 164, 164, 164,
+ 164, 164, 181, 181, 163, 163, 163, 163,
+ 163, 163, 192, 192, 192, 192, 192, 144,
+ 21, 21, 20, 20, 19, 19, 19, 18,
+ 165, 165, 165, 165, 164, 164, 164, 164,
+ 164, 182, 181, 181, 163, 163, 163, 163,
+ 163, 163, 176, 192, 144, 144, 144, 144,
+ 21, 20, 20, 19, 19, 19, 18, 18,
+ 184, 184, 184, 184, 164, 164, 164, 164,
+ 182, 182, 181, 181, 179, 163, 163, 163,
+ 163, 176, 176, 176, 176, 144, 144, 144,
+ 20, 20, 19, 19, 19, 18, 18, 18,
+ 184, 184, 184, 184, 183, 183, 183, 182,
+ 182, 181, 181, 181, 179, 163, 163, 163,
+ 163, 176, 176, 176, 176, 160, 160, 160,
+ 20, 19, 19, 19, 18, 18, 18, 17,
+ 184, 184, 184, 184, 183, 183, 183, 182,
+ 182, 181, 181, 181, 179, 179, 163, 177,
+ 177, 176, 176, 176, 176, 160, 160, 160,
+ 160, 19, 19, 18, 18, 18, 17, 17,
+ 184, 184, 184, 184, 183, 183, 183, 182,
+ 182, 181, 181, 181, 179, 179, 177, 177,
+ 177, 176, 176, 176, 176, 160, 160, 160,
+ 160, 19, 18, 18, 18, 17, 17, 17,
+ 251, 184, 184, 184, 183, 183, 183, 182,
+ 180, 180, 181, 178, 178, 179, 177, 177,
+ 177, 176, 176, 176, 160, 160, 160, 160,
+ 160, 160, 18, 18, 17, 17, 17, 16,
+ 249, 249, 231, 231, 230, 230, 230, 229,
+ 229, 229, 228, 228, 228, 227, 227, 227,
+ 216, 216, 216, 215, 215, 215, 215, 214,
+ 214, 214, 214, 253, 253, 253, 253, 253,
+ 249, 231, 231, 230, 230, 230, 229, 229,
+ 229, 228, 228, 228, 227, 227, 227, 226,
+ 226, 216, 216, 215, 215, 215, 215, 214,
+ 214, 214, 214, 214, 253, 253, 253, 253,
+ 231, 231, 230, 230, 230, 229, 229, 229,
+ 228, 228, 228, 227, 227, 227, 226, 226,
+ 226, 216, 216, 215, 215, 215, 215, 214,
+ 214, 214, 214, 214, 214, 253, 253, 253,
+ 231, 230, 230, 230, 229, 229, 229, 228,
+ 228, 228, 227, 227, 227, 226, 226, 226,
+ 225, 225, 215, 215, 215, 215, 215, 214,
+ 214, 214, 214, 214, 214, 214, 253, 253,
+ 230, 230, 230, 229, 229, 229, 228, 228,
+ 228, 227, 227, 227, 226, 226, 226, 225,
+ 225, 225, 215, 215, 215, 215, 215, 214,
+ 214, 214, 214, 214, 214, 214, 214, 253,
+ 230, 230, 229, 229, 229, 228, 228, 228,
+ 227, 227, 227, 226, 226, 226, 225, 225,
+ 225, 224, 224, 215, 215, 215, 215, 214,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 230, 229, 229, 229, 228, 228, 228, 227,
+ 227, 227, 226, 226, 226, 225, 225, 225,
+ 224, 224, 224, 215, 215, 215, 215, 214,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 229, 229, 229, 228, 228, 228, 227, 227,
+ 227, 226, 226, 226, 225, 225, 225, 224,
+ 224, 224, 224, 224, 215, 215, 215, 214,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 229, 229, 228, 228, 228, 227, 227, 227,
+ 226, 226, 226, 225, 225, 225, 224, 224,
+ 224, 224, 224, 224, 215, 215, 214, 214,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 170, 170, 171, 228, 227, 227, 227, 226,
+ 226, 226, 225, 225, 225, 224, 224, 224,
+ 224, 224, 224, 224, 224, 215, 214, 214,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 170, 170, 170, 170, 227, 227, 226, 226,
+ 226, 225, 225, 225, 224, 224, 224, 224,
+ 224, 224, 224, 224, 224, 215, 214, 214,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 170, 170, 170, 170, 170, 226, 226, 226,
+ 225, 225, 225, 224, 224, 224, 224, 224,
+ 224, 224, 224, 224, 224, 224, 214, 214,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 170, 170, 170, 170, 170, 170, 226, 225,
+ 225, 225, 224, 224, 224, 224, 224, 224,
+ 224, 224, 224, 224, 224, 224, 214, 214,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 170, 170, 170, 170, 170, 170, 170, 225,
+ 225, 224, 224, 224, 224, 224, 224, 224,
+ 224, 224, 224, 224, 224, 194, 194, 193,
+ 214, 214, 214, 214, 214, 214, 214, 214,
+ 170, 170, 170, 170, 170, 170, 169, 169,
+ 224, 224, 224, 224, 224, 224, 224, 224,
+ 224, 224, 224, 194, 194, 194, 193, 193,
+ 193, 192, 214, 214, 214, 214, 214, 214,
+ 170, 170, 170, 170, 170, 169, 169, 169,
+ 224, 224, 224, 224, 224, 224, 224, 224,
+ 224, 224, 194, 194, 194, 193, 193, 193,
+ 193, 192, 192, 10, 10, 10, 214, 214,
+ 170, 170, 170, 169, 169, 169, 169, 169,
+ 169, 224, 224, 224, 224, 224, 224, 224,
+ 194, 194, 194, 194, 193, 193, 193, 193,
+ 192, 192, 192, 192, 10, 10, 10, 10,
+ 168, 168, 168, 169, 169, 169, 169, 169,
+ 169, 169, 224, 224, 224, 224, 224, 194,
+ 194, 194, 194, 193, 193, 193, 193, 192,
+ 192, 192, 192, 192, 10, 10, 10, 21,
+ 168, 168, 168, 168, 169, 169, 169, 169,
+ 169, 169, 169, 224, 224, 195, 195, 194,
+ 194, 194, 193, 193, 193, 193, 192, 192,
+ 192, 192, 192, 192, 192, 21, 21, 21,
+ 168, 168, 168, 168, 169, 169, 169, 169,
+ 167, 167, 167, 167, 195, 195, 194, 194,
+ 194, 194, 193, 193, 193, 193, 192, 192,
+ 192, 192, 192, 192, 21, 21, 21, 20,
+ 168, 168, 168, 168, 168, 167, 167, 167,
+ 167, 167, 167, 167, 167, 195, 194, 194,
+ 194, 193, 193, 193, 193, 192, 192, 192,
+ 192, 192, 192, 192, 21, 21, 20, 20,
+ 168, 168, 168, 168, 167, 167, 167, 167,
+ 167, 167, 167, 167, 167, 194, 194, 194,
+ 193, 193, 193, 193, 192, 192, 192, 192,
+ 192, 192, 192, 21, 21, 20, 20, 19,
+ 165, 165, 165, 167, 167, 167, 167, 167,
+ 167, 167, 167, 167, 167, 194, 194, 193,
+ 193, 193, 193, 192, 192, 192, 192, 192,
+ 192, 192, 21, 21, 20, 20, 19, 19,
+ 165, 165, 165, 165, 166, 166, 166, 166,
+ 166, 166, 166, 166, 163, 163, 163, 163,
+ 193, 193, 193, 192, 192, 192, 192, 192,
+ 192, 192, 21, 20, 20, 19, 19, 19,
+ 165, 165, 165, 165, 165, 166, 166, 166,
+ 166, 166, 166, 166, 163, 163, 163, 163,
+ 163, 193, 192, 192, 192, 192, 192, 192,
+ 192, 21, 20, 20, 19, 19, 19, 18,
+ 165, 165, 165, 165, 165, 164, 164, 164,
+ 164, 164, 164, 163, 163, 163, 163, 163,
+ 163, 163, 192, 192, 192, 192, 192, 192,
+ 21, 20, 20, 19, 19, 19, 18, 18,
+ 165, 165, 165, 165, 164, 164, 164, 164,
+ 164, 164, 181, 181, 163, 163, 163, 163,
+ 163, 163, 176, 176, 192, 144, 144, 144,
+ 20, 20, 19, 19, 19, 18, 18, 18,
+ 165, 165, 165, 164, 164, 164, 164, 164,
+ 164, 181, 181, 181, 163, 163, 163, 163,
+ 163, 163, 176, 176, 176, 160, 160, 160,
+ 20, 19, 19, 19, 18, 18, 18, 17,
+ 184, 184, 184, 184, 164, 164, 164, 182,
+ 182, 181, 181, 181, 179, 163, 163, 163,
+ 163, 176, 176, 176, 176, 160, 160, 160,
+ 160, 19, 19, 18, 18, 18, 17, 17,
+ 184, 184, 184, 184, 183, 183, 183, 182,
+ 182, 181, 181, 181, 179, 163, 163, 163,
+ 177, 176, 176, 176, 176, 160, 160, 160,
+ 160, 19, 18, 18, 18, 17, 17, 17,
+ 251, 184, 184, 184, 183, 183, 183, 182,
+ 180, 180, 181, 178, 178, 162, 163, 177,
+ 177, 176, 176, 176, 160, 160, 160, 160,
+ 160, 160, 18, 18, 17, 17, 17, 16,
+ 251, 251, 184, 184, 183, 183, 183, 180,
+ 180, 180, 180, 178, 178, 162, 162, 177,
+ 161, 176, 176, 176, 160, 160, 160, 160,
+ 160, 160, 18, 17, 17, 17, 16, 16,
+};
diff --git a/Magic2/AlphaPalette.cpp b/Magic2/AlphaPalette.cpp
new file mode 100644
index 0000000..7f7c2e1
--- /dev/null
+++ b/Magic2/AlphaPalette.cpp
@@ -0,0 +1,260 @@
+#include "stdafx.h"
+
+PALETTEENTRY standard_palette[256] = {
+ { 0, 0, 0, 0 },
+ { 128, 0, 0, 0 },
+ { 0, 128, 0, 0 },
+ { 128, 128, 0, 0 },
+ { 0, 0, 128, 0 },
+ { 128, 0, 128, 0 },
+ { 0, 128, 128, 0 },
+ { 192, 192, 192, 0 },
+ { 192, 220, 192, 0 },
+ { 166, 202, 240, 0 },
+ { 186, 172, 189, 0 },
+ { 167, 147, 172, 0 },
+ { 143, 129, 148, 0 },
+ { 122, 107, 126, 0 },
+ { 100, 87, 104, 0 },
+ { 55, 49, 57, 0 },
+ { 247, 247, 247, 0 },
+ { 240, 240, 240, 0 },
+ { 232, 232, 232, 0 },
+ { 225, 225, 225, 0 },
+ { 217, 217, 217, 0 },
+ { 210, 210, 210, 0 },
+ { 202, 202, 202, 0 },
+ { 195, 195, 195, 0 },
+ { 188, 188, 188, 0 },
+ { 180, 180, 180, 0 },
+ { 173, 173, 173, 0 },
+ { 165, 165, 165, 0 },
+ { 158, 158, 158, 0 },
+ { 150, 150, 150, 0 },
+ { 143, 143, 143, 0 },
+ { 136, 136, 136, 0 },
+ { 128, 128, 128, 0 },
+ { 121, 121, 121, 0 },
+ { 113, 113, 113, 0 },
+ { 106, 106, 106, 0 },
+ { 98, 98, 98, 0 },
+ { 91, 91, 91, 0 },
+ { 83, 83, 83, 0 },
+ { 76, 76, 76, 0 },
+ { 69, 69, 69, 0 },
+ { 61, 61, 61, 0 },
+ { 54, 54, 54, 0 },
+ { 42, 42, 42, 0 },
+ { 31, 31, 31, 0 },
+ { 21, 21, 21, 0 },
+ { 10, 10, 10, 0 },
+ { 5, 5, 5, 0 },
+ { 149, 149, 234, 0 },
+ { 134, 134, 227, 0 },
+ { 117, 117, 223, 0 },
+ { 104, 104, 215, 0 },
+ { 87, 87, 210, 0 },
+ { 70, 70, 206, 0 },
+ { 49, 49, 185, 0 },
+ { 45, 45, 168, 0 },
+ { 36, 36, 134, 0 },
+ { 31, 31, 118, 0 },
+ { 27, 27, 101, 0 },
+ { 22, 22, 84, 0 },
+ { 18, 18, 67, 0 },
+ { 13, 13, 51, 0 },
+ { 9, 9, 34, 0 },
+ { 4, 4, 17, 0 },
+ { 159, 191, 223, 0 },
+ { 143, 181, 218, 0 },
+ { 128, 170, 213, 0 },
+ { 112, 159, 207, 0 },
+ { 96, 149, 202, 0 },
+ { 85, 138, 191, 0 },
+ { 69, 128, 186, 0 },
+ { 68, 117, 166, 0 },
+ { 62, 106, 151, 0 },
+ { 55, 96, 136, 0 },
+ { 53, 85, 117, 0 },
+ { 47, 74, 102, 0 },
+ { 43, 64, 85, 0 },
+ { 35, 53, 71, 0 },
+ { 29, 43, 56, 0 },
+ { 21, 32, 43, 0 },
+ { 184, 190, 199, 0 },
+ { 171, 182, 190, 0 },
+ { 159, 169, 181, 0 },
+ { 148, 162, 171, 0 },
+ { 136, 150, 162, 0 },
+ { 123, 138, 153, 0 },
+ { 112, 128, 143, 0 },
+ { 102, 118, 132, 0 },
+ { 92, 105, 120, 0 },
+ { 86, 97, 105, 0 },
+ { 77, 85, 94, 0 },
+ { 67, 74, 82, 0 },
+ { 57, 66, 70, 0 },
+ { 48, 53, 58, 0 },
+ { 38, 44, 47, 0 },
+ { 29, 32, 35, 0 },
+ { 154, 228, 154, 0 },
+ { 137, 224, 137, 0 },
+ { 120, 220, 120, 0 },
+ { 112, 207, 112, 0 },
+ { 96, 202, 96, 0 },
+ { 80, 197, 80, 0 },
+ { 64, 191, 64, 0 },
+ { 58, 175, 77, 0 },
+ { 53, 159, 67, 0 },
+ { 48, 143, 63, 0 },
+ { 43, 128, 64, 0 },
+ { 37, 112, 56, 0 },
+ { 32, 96, 50, 0 },
+ { 27, 80, 43, 0 },
+ { 21, 64, 33, 0 },
+ { 16, 48, 27, 0 },
+ { 186, 204, 179, 0 },
+ { 175, 194, 167, 0 },
+ { 164, 184, 156, 0 },
+ { 152, 174, 145, 0 },
+ { 140, 167, 131, 0 },
+ { 130, 157, 119, 0 },
+ { 119, 148, 107, 0 },
+ { 111, 136, 98, 0 },
+ { 99, 124, 88, 0 },
+ { 88, 112, 80, 0 },
+ { 81, 97, 73, 0 },
+ { 70, 84, 65, 0 },
+ { 61, 73, 54, 0 },
+ { 51, 61, 46, 0 },
+ { 40, 48, 37, 0 },
+ { 30, 36, 28, 0 },
+ { 209, 191, 173, 0 },
+ { 202, 181, 159, 0 },
+ { 191, 170, 149, 0 },
+ { 183, 159, 136, 0 },
+ { 175, 149, 122, 0 },
+ { 167, 138, 109, 0 },
+ { 159, 128, 96, 0 },
+ { 147, 117, 87, 0 },
+ { 129, 106, 84, 0 },
+ { 116, 96, 75, 0 },
+ { 103, 85, 67, 0 },
+ { 90, 73, 58, 0 },
+ { 78, 64, 50, 0 },
+ { 64, 53, 43, 0 },
+ { 51, 43, 34, 0 },
+ { 38, 32, 26, 0 },
+ { 209, 196, 173, 0 },
+ { 202, 186, 159, 0 },
+ { 191, 175, 149, 0 },
+ { 183, 169, 136, 0 },
+ { 175, 159, 122, 0 },
+ { 167, 150, 109, 0 },
+ { 159, 140, 96, 0 },
+ { 143, 128, 90, 0 },
+ { 131, 119, 82, 0 },
+ { 116, 106, 75, 0 },
+ { 103, 95, 67, 0 },
+ { 90, 83, 58, 0 },
+ { 78, 70, 50, 0 },
+ { 64, 60, 43, 0 },
+ { 51, 48, 34, 0 },
+ { 38, 36, 26, 0 },
+ { 255, 255, 170, 0 },
+ { 255, 255, 128, 0 },
+ { 255, 244, 106, 0 },
+ { 249, 232, 113, 0 },
+ { 255, 213, 43, 0 },
+ { 255, 197, 21, 0 },
+ { 255, 186, 43, 0 },
+ { 255, 175, 43, 0 },
+ { 255, 150, 21, 0 },
+ { 255, 143, 32, 0 },
+ { 244, 111, 11, 0 },
+ { 215, 92, 19, 0 },
+ { 190, 72, 22, 0 },
+ { 168, 60, 23, 0 },
+ { 146, 39, 24, 0 },
+ { 128, 27, 21, 0 },
+ { 244, 244, 138, 0 },
+ { 242, 242, 119, 0 },
+ { 248, 248, 92, 0 },
+ { 240, 240, 100, 0 },
+ { 247, 247, 72, 0 },
+ { 239, 239, 80, 0 },
+ { 237, 237, 61, 0 },
+ { 236, 236, 40, 0 },
+ { 235, 235, 20, 0 },
+ { 215, 215, 19, 0 },
+ { 194, 194, 18, 0 },
+ { 175, 175, 16, 0 },
+ { 156, 156, 14, 0 },
+ { 136, 136, 13, 0 },
+ { 117, 117, 11, 0 },
+ { 98, 98, 9, 0 },
+ { 215, 183, 168, 0 },
+ { 211, 170, 150, 0 },
+ { 205, 157, 135, 0 },
+ { 199, 148, 120, 0 },
+ { 193, 135, 104, 0 },
+ { 187, 123, 89, 0 },
+ { 174, 109, 70, 0 },
+ { 151, 96, 62, 0 },
+ { 128, 83, 53, 0 },
+ { 105, 68, 44, 0 },
+ { 143, 239, 239, 0 },
+ { 124, 237, 237, 0 },
+ { 106, 234, 234, 0 },
+ { 87, 232, 232, 0 },
+ { 70, 227, 227, 0 },
+ { 47, 230, 230, 0 },
+ { 32, 223, 223, 0 },
+ { 29, 205, 205, 0 },
+ { 26, 176, 176, 0 },
+ { 20, 150, 150, 0 },
+ { 17, 121, 121, 0 },
+ { 13, 94, 94, 0 },
+ { 223, 32, 223, 0 },
+ { 204, 30, 204, 0 },
+ { 186, 27, 186, 0 },
+ { 168, 23, 168, 0 },
+ { 149, 21, 149, 0 },
+ { 131, 18, 131, 0 },
+ { 112, 16, 112, 0 },
+ { 94, 13, 94, 0 },
+ { 74, 11, 74, 0 },
+ { 56, 7, 56, 0 },
+ { 249, 91, 91, 0 },
+ { 248, 80, 80, 0 },
+ { 247, 68, 68, 0 },
+ { 247, 55, 55, 0 },
+ { 247, 43, 43, 0 },
+ { 245, 31, 31, 0 },
+ { 244, 19, 19, 0 },
+ { 240, 11, 11, 0 },
+ { 228, 10, 10, 0 },
+ { 217, 9, 9, 0 },
+ { 204, 9, 9, 0 },
+ { 192, 7, 7, 0 },
+ { 180, 7, 7, 0 },
+ { 168, 6, 6, 0 },
+ { 156, 5, 5, 0 },
+ { 145, 4, 4, 0 },
+ { 131, 5, 5, 0 },
+ { 118, 5, 5, 0 },
+ { 106, 4, 4, 0 },
+ { 94, 4, 4, 0 },
+ { 80, 5, 5, 0 },
+ { 66, 6, 6, 0 },
+ { 53, 6, 6, 0 },
+ { 41, 5, 5, 0 },
+ { 128, 128, 128, 0 },
+ { 255, 0, 0, 0 },
+ { 0, 255, 0, 0 },
+ { 255, 255, 0, 0 },
+ { 0, 0, 255, 0 },
+ { 255, 0, 255, 0 },
+ { 0, 255, 255, 0 },
+ { 255, 255, 255, 0 },
+};
diff --git a/Magic2/Command.cpp b/Magic2/Command.cpp
new file mode 100644
index 0000000..0b52082
--- /dev/null
+++ b/Magic2/Command.cpp
@@ -0,0 +1,52 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: Command.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Base class (interface) for command pattern. A
+ command encapsulates a single editing operation
+ that may be performed on a document. Each command
+ may be done or undone multiple times. The specialized
+ implementations for each type of operation are re-
+ sponsible for providing a means to return the model
+ to the prior state.
+*/
+
+#include "stdafx.h"
+#include "Command.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* msg, ...);
+
+// +--------------------------------------------------------------------+
+
+Command::Command(const char* n, MagicDoc* d)
+ : name(n), document(d)
+{
+}
+
+Command::~Command()
+{
+ document = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Command::Do()
+{
+ Print("WARNING: Command::Do() called for '%s'\n", name.data());
+}
+
+void
+Command::Undo()
+{
+ Print("WARNING: Command::Undo() called for '%s'\n", name.data());
+}
diff --git a/Magic2/Command.h b/Magic2/Command.h
new file mode 100644
index 0000000..30f568e
--- /dev/null
+++ b/Magic2/Command.h
@@ -0,0 +1,55 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: Command.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Base class (interface) for command pattern. A
+ command encapsulates a single editing operation
+ that may be performed on a document. Each command
+ may be done or undone multiple times. The specialized
+ implementations for each type of operation are re-
+ sponsible for providing a means to return the model
+ to the prior state.
+*/
+
+#ifndef Command_h
+#define Command_h
+
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class MagicDoc;
+class Solid;
+class Model;
+
+// +--------------------------------------------------------------------+
+
+class Command
+{
+public:
+ static const char* TYPENAME() { return "Command"; }
+
+ Command(const char* name, MagicDoc* document);
+ virtual ~Command();
+
+ // operations
+ virtual void Do();
+ virtual void Undo();
+
+ const char* Name() const { return name; }
+
+protected:
+ Text name;
+ MagicDoc* document;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Command_h
diff --git a/Magic2/Editor.cpp b/Magic2/Editor.cpp
new file mode 100644
index 0000000..c8e35be
--- /dev/null
+++ b/Magic2/Editor.cpp
@@ -0,0 +1,412 @@
+/* Project MAGIC
+ John DiCamillo Software Consulting
+ Copyright © 1994-1997. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe Application
+ FILE: Editor.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Source file for implementation of Selector
+*/
+
+
+#include "stdafx.h"
+#include "Editor.h"
+#include "MagicDoc.h"
+#include "ModelView.h"
+#include "Selection.h"
+
+// +----------------------------------------------------------------------+
+
+
+static float project_u(Vec3& v, int style)
+{
+ switch (style) {
+ case 0: return v.x; // PLAN
+ case 1: return v.x; // FRONT
+ case 2: return v.z; // SIDE
+ }
+
+ return v.x;
+}
+
+static float project_v(Vec3& v, int style)
+{
+ switch (style) {
+ case 0: return -v.y; // PLAN
+ case 1: return -v.z; // FRONT
+ case 2: return -v.y; // SIDE
+ }
+
+ return -v.y;
+}
+
+static float project_u_cylindrical(Vec3& v, int axis)
+{
+ float t = 0.0f;
+
+ switch (axis) {
+ // PLAN
+ case 0: if (v.x == 0)
+ return 0.0f;
+ t = v.y/v.x;
+ return (float) atan(t);
+
+ // FRONT
+ case 1: if (v.x == 0)
+ return 0.0f;
+ t = v.z/v.x;
+ return (float) atan(t);
+
+ // SIDE
+ case 2: return (float) atan2(v.z, v.y); // SIDE
+ }
+
+ return project_u(v, axis);
+}
+
+static float project_v_cylindrical(Vec3& v, int axis)
+{
+ switch (axis) {
+ case 0: return v.z; // PLAN
+ case 1: return v.y; // FRONT
+ case 2: return v.x; // SIDE
+ }
+
+ return project_v(v, axis);
+}
+
+void
+Editor::ApplyMaterial(Material* material, List<Poly>& polys,
+ int mapping, int axis, float scale_u, float scale_v,
+ int flip, int mirror, int rotate)
+{
+ // save state:
+ EditCommand* command = new EditCommand("ApplyMaterial", document);
+ document->Exec(command);
+
+ // do the job:
+ if (mapping == MAP_CYLINDRICAL) {
+ ApplyMaterialCylindrical(material, polys, axis, scale_u, scale_v, flip, mirror, rotate);
+ return;
+ }
+
+ if (mapping == MAP_SPHERICAL) {
+ ApplyMaterialSpherical(material, polys, axis, scale_u, scale_v, flip, mirror, rotate);
+ return;
+ }
+
+ VertexSet* vset = polys.first()->vertex_set;
+
+ Vec3* loc = vset->loc;
+ float min_u = 100000.0f, max_u = -100000.0f;
+ float min_v = 100000.0f, max_v = -100000.0f;
+
+ ListIter<Poly> iter = polys;
+
+ // compute range and scale:
+ if (mapping == MAP_PLANAR) {
+ while (++iter) {
+ Poly* poly = iter.value();
+ for (int i = 0; i < poly->nverts; i++) {
+ int v = poly->verts[i];
+
+ float u0 = project_u(loc[v], axis);
+ float v0 = project_v(loc[v], axis);
+
+ if (u0 < min_u) min_u = u0;
+ if (v0 < min_v) min_v = v0;
+ if (u0 > max_u) max_u = u0;
+ if (v0 > max_v) max_v = v0;
+ }
+ }
+ }
+
+ float base_u = 0.0f;
+ float base_v = 0.0f;
+
+ if (max_u != min_u) base_u = 1.0f / (max_u - min_u);
+ if (max_v != min_v) base_v = 1.0f / (max_v - min_v);
+
+ iter.reset();
+
+ // assign texture id and coordinates:
+ while (++iter) {
+ Poly* poly = iter.value();
+
+ poly->material = material;
+
+ if (mapping == MAP_NONE)
+ continue;
+
+ for (int i = 0; i < poly->nverts; i++) {
+ int v = poly->verts[i];
+
+ // planar projection
+ if (mapping == MAP_PLANAR) {
+ if (!rotate) {
+ if (mirror)
+ vset->tu[v] = (1.0f - base_u * (project_u(loc[v], axis) - min_u)) * scale_u;
+ else
+ vset->tu[v] = (project_u(loc[v], axis) - min_u) * scale_u * base_u;
+
+ if (flip)
+ vset->tv[v] = (1.0f - base_v * (project_v(loc[v], axis) - min_v)) * scale_v;
+ else
+ vset->tv[v] = (project_v(loc[v], axis) - min_v) * scale_v * base_v;
+ }
+ else {
+ if (!mirror)
+ vset->tv[v] = (1.0f - base_u * (project_u(loc[v], axis) - min_u)) * scale_u;
+ else
+ vset->tv[v] = (project_u(loc[v], axis) - min_u) * scale_u * base_u;
+
+ if (flip)
+ vset->tu[v] = (1.0f - base_v * (project_v(loc[v], axis) - min_v)) * scale_v;
+ else
+ vset->tu[v] = (project_v(loc[v], axis) - min_v) * scale_v * base_v;
+ }
+ }
+
+ // stretch to fit
+ else if (mapping == MAP_STRETCH) {
+ if (scale_u < 0.001) scale_u = 1;
+ if (scale_v < 0.001) scale_v = 1;
+
+ if (!rotate) {
+ if (mirror)
+ vset->tu[v] = scale_u * (float) (i < 1 || i > 2);
+ else
+ vset->tu[v] = scale_u * (float) (i > 0 && i < 3);
+
+ if (flip)
+ vset->tv[v] = scale_v * (float) (i <= 1);
+ else
+ vset->tv[v] = scale_v * (float) (i > 1);
+ }
+ else {
+ if (!mirror)
+ vset->tv[v] = scale_v * (float) (i < 1 || i > 2);
+ else
+ vset->tv[v] = scale_v * (float) (i > 0 && i < 3);
+
+ if (flip)
+ vset->tu[v] = scale_u * (float) (i <= 1);
+ else
+ vset->tu[v] = scale_u * (float) (i > 1);
+ }
+ }
+ }
+ }
+
+ Resegment();
+}
+
+void
+Editor::ApplyMaterialCylindrical(Material* material, List<Poly>& polys,
+ int axis, float scale_u, float scale_v,
+ int flip, int mirror, int rotate)
+{
+ VertexSet* vset = polys.first()->vertex_set;
+
+ Vec3* loc = vset->loc;
+ float min_u = 100000.0f, max_u = -100000.0f;
+ float min_v = 100000.0f, max_v = -100000.0f;
+
+ ListIter<Poly> iter = polys;
+
+ // compute range and scale:
+ while (++iter) {
+ Poly* poly = iter.value();
+ for (int i = 0; i < poly->nverts; i++) {
+ int v = poly->verts[i];
+
+ float u0 = project_u_cylindrical(loc[v], axis);
+ float v0 = project_v_cylindrical(loc[v], axis);
+
+ if (u0 < min_u) min_u = u0;
+ if (v0 < min_v) min_v = v0;
+ if (u0 > max_u) max_u = u0;
+ if (v0 > max_v) max_v = v0;
+ }
+ }
+
+ float base_u = 0.0f;
+ float base_v = 0.0f;
+
+ if (max_u != min_u) base_u = 1.0f / (max_u - min_u);
+ if (max_v != min_v) base_v = 1.0f / (max_v - min_v);
+
+ iter.reset();
+
+ // assign texture id and coordinates:
+ while (++iter) {
+ Poly* poly = iter.value();
+
+ poly->material = material;
+
+ for (int i = 0; i < poly->nverts; i++) {
+ int v = poly->verts[i];
+ float u0 = project_u_cylindrical(loc[v], axis);
+ float v0 = project_v_cylindrical(loc[v], axis);
+
+ if (!rotate) {
+ if (mirror)
+ vset->tu[v] = (1.0f - base_u * (u0 - min_u)) * scale_u;
+ else
+ vset->tu[v] = (u0 - min_u) * scale_u * base_u;
+
+ if (flip)
+ vset->tv[v] = (1.0f - base_v * (v0 - min_v)) * scale_v;
+ else
+ vset->tv[v] = (v0 - min_v) * scale_v * base_v;
+ }
+ else {
+ if (!mirror)
+ vset->tv[v] = (1.0f - base_u * (u0 - min_u)) * scale_u;
+ else
+ vset->tv[v] = (u0 - min_u) * scale_u * base_u;
+
+ if (flip)
+ vset->tu[v] = (1.0f - base_v * (v0 - min_v)) * scale_v;
+ else
+ vset->tu[v] = (v0 - min_v) * scale_v * base_v;
+ }
+ }
+ }
+
+ Resegment();
+}
+
+void
+Editor::ApplyMaterialSpherical(Material* material, List<Poly>& polys,
+ int axis, float scale_u, float scale_v,
+ int flip, int mirror, int rotate)
+{
+}
+
+// +----------------------------------------------------------------------+
+
+static int mcomp(const void* a, const void* b)
+{
+ Poly* pa = (Poly*) a;
+ Poly* pb = (Poly*) b;
+
+ if (pa->sortval == pb->sortval)
+ return 0;
+
+ if (pa->sortval < pb->sortval)
+ return -1;
+
+ return 1;
+}
+
+void
+Editor::Resegment()
+{
+ if (model) {
+ ListIter<Surface> iter = model->GetSurfaces();
+ while (++iter) {
+ Surface* surface = iter.value();
+ Poly* polys = surface->GetPolys();
+ int npolys = surface->NumPolys();
+
+ for (int n = 0; n < npolys; n++) {
+ Poly* p = polys + n;
+ Material* m = p->material;
+ int sortval = model->GetMaterials().index(m) + 1;
+
+ if (p->sortval != sortval)
+ p->sortval = sortval;
+ }
+
+ // destroy the old segments and video data:
+ VideoPrivateData* video_data = surface->GetVideoPrivateData();
+ surface->SetVideoPrivateData(0);
+ surface->GetSegments().destroy();
+
+ delete video_data;
+
+ // sort the polys by material index:
+ qsort((void*) polys, npolys, sizeof(Poly), mcomp);
+
+ // create new cohesive segments:
+ Segment* segment = 0;
+
+ for (n = 0; n < npolys; n++) {
+ if (segment && segment->material == polys[n].material) {
+ segment->npolys++;
+ }
+ else {
+ segment = 0;
+ }
+
+ if (!segment) {
+ segment = new Segment;
+
+ segment->npolys = 1;
+ segment->polys = polys + n;
+ segment->material = segment->polys->material;
+
+ surface->GetSegments().append(segment);
+ }
+ }
+ }
+ }
+}
+
+
+// +----------------------------------------------------------------------+
+// +----------------------------------------------------------------------+
+// +----------------------------------------------------------------------+
+
+EditCommand::EditCommand(const char* n, MagicDoc* d)
+ : Command(n, d), model1(0), model2(0)
+{
+}
+
+EditCommand::~EditCommand()
+{
+ delete model1;
+ delete model2;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+EditCommand::Do()
+{
+ if (document) {
+ Solid* solid = document->GetSolid();
+
+ // first application:
+ if (!model2) {
+ if (!model1)
+ model1 = new Model(*solid->GetModel());
+ }
+ // re-do:
+ else {
+ solid->GetModel()->operator=(*model2);
+ }
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+EditCommand::Undo()
+{
+ if (document && model1) {
+ Solid* solid = document->GetSolid();
+
+ // save current state for later re-do:
+ if (!model2)
+ model2 = new Model(*solid->GetModel());
+
+ solid->GetModel()->operator=(*model1);
+ }
+}
+
diff --git a/Magic2/Editor.h b/Magic2/Editor.h
new file mode 100644
index 0000000..c0326e2
--- /dev/null
+++ b/Magic2/Editor.h
@@ -0,0 +1,84 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: Editor.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Classes for rendering solid meshes of polygons
+*/
+
+#ifndef Editor_h
+#define Editor_h
+
+#include "MagicDoc.h"
+#include "Command.h"
+#include "Polygon.h"
+#include "Solid.h"
+#include "Video.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Selection;
+class ModelView;
+
+// +--------------------------------------------------------------------+
+
+class Editor
+{
+public:
+ Editor(MagicDoc* doc) : document(doc), model(0) { }
+
+ // accessors / mutators
+ void UseModel(Model* m) { model = m; }
+ Model* GetModel() const { return model; }
+
+ // operations
+
+ enum { MAP_NONE, MAP_PLANAR, MAP_CYLINDRICAL, MAP_SPHERICAL, MAP_STRETCH };
+
+ void ApplyMaterial(Material* material, List<Poly>& polys,
+ int mapping, int axis, float scale_u, float scale_v,
+ int flip, int mirror, int rotate);
+
+ void ApplyMaterialCylindrical(Material* material, List<Poly>& polys,
+ int axis, float scale_u, float scale_v,
+ int flip, int mirror, int rotate);
+
+ void ApplyMaterialSpherical(Material* material, List<Poly>& polys,
+ int axis, float scale_u, float scale_v,
+ int flip, int mirror, int rotate);
+
+
+ void Resegment();
+
+protected:
+ MagicDoc* document;
+ Model* model;
+};
+
+// +--------------------------------------------------------------------+
+
+class EditCommand : public Command
+{
+public:
+ EditCommand(const char* name, MagicDoc* doc);
+ virtual ~EditCommand();
+
+ virtual void Do();
+ virtual void Undo();
+
+private:
+ Model* model1;
+ Model* model2;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Editor_h
+
diff --git a/Magic2/Grid.cpp b/Magic2/Grid.cpp
new file mode 100644
index 0000000..b00767c
--- /dev/null
+++ b/Magic2/Grid.cpp
@@ -0,0 +1,346 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: Grid.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Implementation of the Grid class
+*/
+
+#include "stdafx.h"
+#include "Magic.h"
+
+#include "MagicDoc.h"
+#include "Grid.h"
+
+#include "ActiveWindow.h"
+#include "Color.h"
+#include "Polygon.h"
+#include "Scene.h"
+#include "Screen.h"
+#include "Solid.h"
+#include "Video.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+const int MAJOR_COUNT = 64;
+
+// +--------------------------------------------------------------------+
+
+Grid::Grid()
+ : x_major(0), y_major(0), x_minor(0), y_minor(0),
+ bmp_plan(0), bmp_front(0), bmp_side(0)
+{
+ strcpy(name, "Grid");
+
+ plane = GRID_XZ; // plan
+
+ show = true;
+ show_ref = true;
+ snap = true;
+ minor = true;
+
+ x_size = 4;
+ y_size = 4;
+ x_skip = 4;
+ y_skip = 4;
+
+ x_major = new Vec3[2*MAJOR_COUNT];
+ y_major = new Vec3[2*MAJOR_COUNT];
+}
+
+Grid::~Grid()
+{
+ delete [] x_major;
+ delete [] y_major;
+ delete [] x_minor;
+ delete [] y_minor;
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+Grid::GetReferencePlan() const
+{
+ if (bmp_plan)
+ return bmp_plan->GetFilename();
+
+ return "";
+}
+
+void
+Grid::SetReferencePlan(const char* fname)
+{
+ bmp_plan = 0;
+
+ if (fname && *fname)
+ LoadTexture(fname, bmp_plan);
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+Grid::GetReferenceFront() const
+{
+ if (bmp_front)
+ return bmp_front->GetFilename();
+
+ return "";
+}
+
+void
+Grid::SetReferenceFront(const char* fname)
+{
+ bmp_front = 0;
+
+ if (fname && *fname)
+ LoadTexture(fname, bmp_front);
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+Grid::GetReferenceSide() const
+{
+ if (bmp_side)
+ return bmp_side->GetFilename();
+
+ return "";
+}
+
+void
+Grid::SetReferenceSide(const char* fname)
+{
+ bmp_side = 0;
+
+ if (fname && *fname)
+ LoadTexture(fname, bmp_side);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Grid::Render(Video* video, DWORD flags)
+{
+ Vec3 origin[4];
+
+ for (int i = 0; i < 4; i++)
+ origin[i] = loc;
+
+ const float EXTENT = (float) MAJOR_COUNT * 64.0f;
+
+ switch (plane) {
+ case GRID_XY: // FRONT
+ origin[0].x += -EXTENT;
+ origin[1].x += EXTENT;
+ origin[2].y += -EXTENT;
+ origin[3].y += EXTENT;
+
+ for (i = 0; i < 2*MAJOR_COUNT; i += 2) {
+ float x = (float) (i-MAJOR_COUNT) * 64.0f;
+ float y = (float) (i-MAJOR_COUNT) * 64.0f;
+
+ x_major[i ] = loc + Vec3(x, -EXTENT, 0.0f);
+ x_major[i+1] = loc + Vec3(x, EXTENT, 0.0f);
+ y_major[i ] = loc + Vec3(-EXTENT, y, 0.0f);
+ y_major[i+1] = loc + Vec3( EXTENT, y, 0.0f);
+ }
+
+ break;
+
+ case GRID_XZ: // PLAN
+ origin[0].x += -EXTENT;
+ origin[1].x += EXTENT;
+ origin[2].z += -EXTENT;
+ origin[3].z += EXTENT;
+
+ for (i = 0; i < 2*MAJOR_COUNT; i += 2) {
+ float x = (float) (i-MAJOR_COUNT) * 64.0f;
+ float z = (float) (i-MAJOR_COUNT) * 64.0f;
+
+ x_major[i ] = loc + Vec3(x, 0.0f, -EXTENT);
+ x_major[i+1] = loc + Vec3(x, 0.0f, EXTENT);
+ y_major[i ] = loc + Vec3(-EXTENT, 0.0f, z);
+ y_major[i+1] = loc + Vec3( EXTENT, 0.0f, z);
+ }
+
+ break;
+
+ case GRID_YZ: // SIDE
+ origin[0].y += -EXTENT;
+ origin[1].y += EXTENT;
+ origin[2].z += -EXTENT;
+ origin[3].z += EXTENT;
+
+ for (i = 0; i < 2*MAJOR_COUNT; i += 2) {
+ float y = (float) (i-MAJOR_COUNT) * 64.0f;
+ float z = (float) (i-MAJOR_COUNT) * 64.0f;
+
+ x_major[i ] = loc + Vec3(0.0f, y, -EXTENT);
+ x_major[i+1] = loc + Vec3(0.0f, y, EXTENT);
+ y_major[i ] = loc + Vec3(0.0f, -EXTENT, z);
+ y_major[i+1] = loc + Vec3(0.0f, EXTENT, z);
+ }
+
+ break;
+ }
+
+ RenderReference(video);
+
+ video->SetRenderState(Video::FILL_MODE, Video::FILL_SOLID);
+ video->SetRenderState(Video::LIGHTING_ENABLE, FALSE);
+ video->SetRenderState(Video::Z_ENABLE, TRUE);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, TRUE);
+
+ video->DrawLines(MAJOR_COUNT, x_major, Color::Gray, 1);
+ video->DrawLines(MAJOR_COUNT, y_major, Color::Gray, 1);
+ video->DrawLines( 2, origin, Color::DarkGray, 1);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Grid::RenderReference(Video* video)
+{
+ if (!show_ref) return;
+
+ Bitmap* bmp = 0;
+
+ switch (plane) {
+ case GRID_XY: bmp = bmp_front; break;
+ case GRID_XZ: bmp = bmp_plan; break;
+ case GRID_YZ: bmp = bmp_side; break;
+ }
+
+ if (!bmp) return;
+
+ Material mtl;
+ VertexSet vset(4);
+ Poly poly;
+ Vec3 nrm;
+
+ float vx = (float) bmp->Width();
+ float vy = (float) bmp->Height();
+ float vz = -100.0f;
+
+ if (vx <= 256 || vy <= 256) {
+ vx *= 2.0f;
+ vy *= 2.0f;
+ }
+
+ switch (plane) {
+ case GRID_XY: // FRONT
+ nrm = Vec3(0.0f, 0.0f, 1.0f);
+ vset.loc[0] = loc + Vec3(-vx, -vy, vz);
+ vset.loc[1] = loc + Vec3( vx, -vy, vz);
+ vset.loc[2] = loc + Vec3( vx, vy, vz);
+ vset.loc[3] = loc + Vec3(-vx, vy, vz);
+ break;
+
+ case GRID_XZ: // PLAN
+ nrm = Vec3(0.0f, 1.0f, 0.0f);
+ vset.loc[0] = loc + Vec3(-vx, vz, vy);
+ vset.loc[1] = loc + Vec3( vx, vz, vy);
+ vset.loc[2] = loc + Vec3( vx, vz, -vy);
+ vset.loc[3] = loc + Vec3(-vx, vz, -vy);
+ break;
+
+ case GRID_YZ: // SIDE
+ nrm = Vec3(1.0f, 0.0f, 0.0f);
+ vset.loc[0] = loc + Vec3( vz, -vx, -vy);
+ vset.loc[1] = loc + Vec3( vz, vx, -vy);
+ vset.loc[2] = loc + Vec3( vz, vx, vy);
+ vset.loc[3] = loc + Vec3( vz, -vx, vy);
+ break;
+
+ default:
+ return;
+ }
+
+ mtl.Ka = Color::White;
+ mtl.Kd = Color::Black;
+ mtl.tex_diffuse = bmp;
+
+ vset.nrm[0] = nrm;
+ vset.nrm[1] = nrm;
+ vset.nrm[2] = nrm;
+ vset.nrm[3] = nrm;
+
+ vset.tu[0] = 0.0f;
+ vset.tv[0] = 0.0f;
+ vset.tu[1] = 1.0f;
+ vset.tv[1] = 0.0f;
+ vset.tu[2] = 1.0f;
+ vset.tv[2] = 1.0f;
+ vset.tu[3] = 0.0f;
+ vset.tv[3] = 1.0f;
+
+ poly.nverts = 4;
+ poly.verts[0] = 0;
+ poly.verts[1] = 1;
+ poly.verts[2] = 2;
+ poly.verts[3] = 3;
+ poly.material = &mtl;
+ poly.vertex_set = &vset;
+
+ video->SetRenderState(Video::FILL_MODE, Video::FILL_SOLID);
+ video->SetRenderState(Video::LIGHTING_ENABLE, TRUE);
+ video->SetRenderState(Video::Z_ENABLE, FALSE);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, FALSE);
+ video->SetAmbient(Color::White);
+
+ video->DrawPolys(1, &poly);
+}
+
+// +----------------------------------------------------------------------+
+
+CPoint&
+Grid::Snap(CPoint& p)
+{
+ p.x = snapto(p.x, x_size);
+ p.y = snapto(p.y, y_size);
+
+ return p;
+}
+
+// +----------------------------------------------------------------------+
+
+int
+Grid::snapto(int i, int dim)
+{
+ if (!snap)
+ return i;
+
+ int n = i + dim/2;
+ int m = n % dim;
+
+ return (n - m);
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Grid::SetSize(int x, int y)
+{
+ x_size = x;
+ y_size = y ? y : x;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Grid::SetSkip(int x, int y)
+{
+ x_skip = x;
+ y_skip = y ? y : x;
+}
+
diff --git a/Magic2/Grid.h b/Magic2/Grid.h
new file mode 100644
index 0000000..93c0201
--- /dev/null
+++ b/Magic2/Grid.h
@@ -0,0 +1,81 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: Grid.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Interface of the Grid class
+*/
+
+
+#ifndef Grid_h
+#define Grid_h
+
+#include "Bitmap.h"
+#include "Graphic.h"
+
+// +--------------------------------------------------------------------+
+
+class Grid : public Graphic
+{
+public:
+ enum PLANE { GRID_XY, GRID_XZ, GRID_YZ };
+
+ Grid();
+ virtual ~Grid();
+
+ CPoint& Snap(CPoint& p);
+
+ bool IsSnap() const { return snap; }
+ bool IsShow() const { return show; }
+ void SetSnap(bool s) { snap = s; }
+ void SetShow(bool s) { show = s; }
+ void SetSize(int x, int y = 0);
+ void SetSkip(int x, int y = 0);
+ void ShowMinor(bool show) { minor = show; }
+ void ShowPlane(int p) { plane = p; }
+ void ShowReference(bool show) { show_ref = show; }
+
+ const char* GetReferencePlan() const;
+ void SetReferencePlan(const char* fname);
+ const char* GetReferenceFront() const;
+ void SetReferenceFront(const char* fname);
+ const char* GetReferenceSide() const;
+ void SetReferenceSide(const char* fname);
+
+ int GetSize() const { return x_size; }
+
+ // operations
+ virtual void Render(Video* video, DWORD flags);
+ virtual void RenderReference(Video* video);
+
+protected:
+ int snapto(int i, int dim);
+
+ bool show;
+ bool show_ref;
+ bool snap;
+ bool minor;
+
+ int x_size, y_size;
+ int x_skip, y_skip;
+ int plane;
+
+ Vec3* x_major;
+ Vec3* x_minor;
+ Vec3* y_major;
+ Vec3* y_minor;
+
+ Bitmap* bmp_plan;
+ Bitmap* bmp_front;
+ Bitmap* bmp_side;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Grid_h
diff --git a/Magic2/GridProps.cpp b/Magic2/GridProps.cpp
new file mode 100644
index 0000000..8b252ec
--- /dev/null
+++ b/Magic2/GridProps.cpp
@@ -0,0 +1,126 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: GridProps.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Grid Properties Dialog implementation file
+*/
+
+
+#include "stdafx.h"
+#include "Magic.h"
+#include "GridProps.h"
+#include "Grid.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+// +--------------------------------------------------------------------+
+// GridProps dialog
+// +--------------------------------------------------------------------+
+
+GridProps::GridProps(Grid* g, CWnd* pParent /*=NULL*/)
+ : CDialog(GridProps::IDD, pParent), grid(g)
+{
+ //{{AFX_DATA_INIT(GridProps)
+ mGridShow = grid->IsShow();
+ mGridSnap = grid->IsSnap();
+ mReferencePlan = grid->GetReferencePlan();
+ mReferenceFront = grid->GetReferenceFront();
+ mReferenceSide = grid->GetReferenceSide();
+ mGridSize = grid->GetSize();
+ //}}AFX_DATA_INIT
+}
+
+static const char* C(CString& str)
+{
+ static char buf[512];
+
+ for (int i = 0; i < str.GetLength(); i++)
+ buf[i] = (char) str.GetAt(i);
+ buf[i] = 0;
+
+ return buf;
+}
+
+void GridProps::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(GridProps)
+ DDX_Check(pDX, IDC_GRID_SHOW, mGridShow);
+ DDX_Check(pDX, IDC_GRID_SNAP, mGridSnap);
+ DDX_Text(pDX, IDC_REFERENCE_PLAN, mReferencePlan);
+ DDX_Text(pDX, IDC_REFERENCE_FRONT, mReferenceFront);
+ DDX_Text(pDX, IDC_REFERENCE_SIDE, mReferenceSide);
+ DDX_Text(pDX, IDC_GRID_SIZE, mGridSize);
+ DDV_MinMaxInt(pDX, mGridSize, 1, 64);
+ //}}AFX_DATA_MAP
+
+ // if saving, write the values back to the grid
+ if (pDX->m_bSaveAndValidate) {
+ grid->SetSnap(mGridSnap ? true : false);
+ grid->SetShow(mGridShow ? true : false);
+ grid->SetSize(mGridSize);
+
+ grid->SetReferencePlan(C(mReferencePlan));
+ grid->SetReferenceFront(C(mReferenceFront));
+ grid->SetReferenceSide(C(mReferenceSide));
+ }
+}
+
+
+BEGIN_MESSAGE_MAP(GridProps, CDialog)
+ //{{AFX_MSG_MAP(GridProps)
+ ON_BN_CLICKED(IDC_FILE_PLAN, OnFilePlan)
+ ON_BN_CLICKED(IDC_FILE_FRONT, OnFileFront)
+ ON_BN_CLICKED(IDC_FILE_SIDE, OnFileSide)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+// +--------------------------------------------------------------------+
+
+static void OnImageFile(CString& strImageFile)
+{
+ char filename[512];
+ filename[0] = '\0';
+ CFileDialog ofd(TRUE, "pcx");
+
+ ofd.m_ofn.lpstrFilter = "PCX Files\0*.pcx\0All Files\0*.*\0\0";
+ ofd.m_ofn.lpstrFile = filename;
+ ofd.m_ofn.nMaxFile = sizeof(filename);
+
+ if (ofd.DoModal() != IDOK)
+ return;
+
+ char tex_name[512];
+ sprintf(tex_name, "%s", ofd.GetFileName().GetBuffer(0));
+
+ strImageFile = tex_name;
+}
+
+void GridProps::OnFilePlan()
+{
+ OnImageFile(mReferencePlan);
+ UpdateData(FALSE);
+}
+
+void GridProps::OnFileFront()
+{
+ OnImageFile(mReferenceFront);
+ UpdateData(FALSE);
+}
+
+void GridProps::OnFileSide()
+{
+ OnImageFile(mReferenceSide);
+ UpdateData(FALSE);
+}
diff --git a/Magic2/GridProps.h b/Magic2/GridProps.h
new file mode 100644
index 0000000..c2dd77a
--- /dev/null
+++ b/Magic2/GridProps.h
@@ -0,0 +1,69 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: GridProps.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Grid Properties Dialog interface file
+*/
+
+#if !defined(AFX_GRIDPROPS_H__79C78890_ABB9_4789_BFFC_29617CAC606E__INCLUDED_)
+#define AFX_GRIDPROPS_H__79C78890_ABB9_4789_BFFC_29617CAC606E__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+// +--------------------------------------------------------------------+
+// GridProps dialog
+// +--------------------------------------------------------------------+
+
+class Grid;
+
+class GridProps : public CDialog
+{
+// Construction
+public:
+ GridProps(Grid* g, CWnd* pParent = NULL); // standard constructor
+
+// Dialog Data
+ //{{AFX_DATA(GridProps)
+ enum { IDD = IDD_GRIDPROPS };
+ BOOL mGridShow;
+ BOOL mGridSnap;
+ CString mReferencePlan;
+ CString mReferenceFront;
+ CString mReferenceSide;
+ int mGridSize;
+ //}}AFX_DATA
+
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(GridProps)
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+ Grid* grid;
+
+ // Generated message map functions
+ //{{AFX_MSG(GridProps)
+ afx_msg void OnFilePlan();
+ afx_msg void OnFileFront();
+ afx_msg void OnFileSide();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif
diff --git a/Magic2/M3DS.cpp b/Magic2/M3DS.cpp
new file mode 100644
index 0000000..21ac41a
--- /dev/null
+++ b/Magic2/M3DS.cpp
@@ -0,0 +1,263 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: ModelFile3DS.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ File loader for 3DStudio MAX 3DS format models
+*/
+
+#include "stdafx.h"
+#include "Magic.h"
+#include "MagicDoc.h"
+#include "ModelFile3DS.h"
+
+#include "Bitmap.h"
+#include "Polygon.h"
+#include "Text.h"
+#include "List.h"
+#include "ArrayList.h"
+
+// +--------------------------------------------------------------------+
+
+struct Chunk
+{
+ WORD id;
+ DWORD len;
+ BYTE* start;
+
+ List<Chunk> chunks;
+};
+
+// +--------------------------------------------------------------------+
+
+struct Vec2
+{
+ float u,v;
+};
+
+struct Triangle
+{
+ WORD a,b,c;
+};
+
+struct Triangle2
+{
+ Vec3 vloc[3];
+ Vec3 vnrm[3];
+ Vec2 vtex[3];
+ Vec3 fnrm;
+ DWORD mtl_id;
+};
+
+struct TextureMap
+{
+ char fname[256];
+ float strength;
+ float u_scale;
+ float v_scale;
+ float u_offset;
+ float v_offset;
+ float angle;
+};
+
+// +--------------------------------------------------------------------+
+
+class Object3DS
+{
+public:
+ Object3DS();
+ virtual ~Object3DS();
+
+ virtual const char* GetName();
+ virtual bool IsObject(const char* name);
+
+protected:
+ Text name;
+};
+
+// +--------------------------------------------------------------------+
+
+class Material3DS : public Object3DS
+{
+public:
+ Material3DS();
+ virtual ~Material3DS();
+
+ DWORD GetID();
+ TextureMap& GetTextureMap1();
+ TextureMap& GetTextureMap2();
+ TextureMap& GetOpacityMap();
+ TextureMap& GetSpecularMap();
+ TextureMap& GetBumpMap();
+ TextureMap& GetReflectionMap();
+ ColorValue GetAmbientColor();
+ ColorValue GetDiffuseColor();
+ ColorValue GetSpecularColor();
+ float GetShininess();
+ float GetTransparency();
+ DWORD GetShadingType();
+
+ // this methods should not be used by the "user", they're used internally to fill the class
+ // with valid data when reading from file. If you're about to add an importer for another format you'LL
+ // have to use these methods
+
+ void SetID(DWORD value);
+ void SetAmbientColor(const ColorValue &color);
+ void SetDiffuseColor(const ColorValue &color);
+ void SetSpecularColor(const ColorValue &color);
+ void SetShininess(float value);
+ void SetTransparency(float value);
+ void SetShadingType(DWORD shading);
+
+protected:
+ int id;
+ TextureMap texMap1;
+ TextureMap texMap2;
+ TextureMap opacMap;
+ TextureMap refTextureMap;
+ TextureMap bumpMap;
+ TextureMap specMap;
+ ColorValue ambient;
+ ColorValue diffuse;
+ ColorValue specular;
+ float shininess;
+ float transparency;
+ DWORD shading;
+};
+
+// +--------------------------------------------------------------------+
+
+class Mesh3DS : public Object3DS
+{
+public:
+ Mesh3DS();
+ virtual ~Mesh3DS();
+
+ void Clear();
+
+ DWORD GetVertexCount();
+ void SetVertexArraySize(DWORD value);
+ DWORD GetTriangleCount();
+ void SetTriangleArraySize(DWORD value);
+
+ const Vec3& GetVertex(DWORD index);
+ const Vec3& GetNormal(DWORD index);
+ const Vec2& GetUV(DWORD index);
+ const Vec3& GetTangent(DWORD index);
+ const Vec3& GetBinormal(DWORD index);
+
+ void SetVertex(const Vec3 &vec, DWORD index);
+ void SetNormal(const Vec3 &vec, DWORD index);
+ void SetUV(const Vec2 &vec, DWORD index);
+ void SetTangent(const Vec3 &vec, DWORD index);
+ void SetBinormal(const Vec3 &vec, DWORD index);
+
+ const Triangle& GetTriangle(DWORD index);
+ Triangle2 GetTriangle2(DWORD index);
+
+ Matrix& GetMatrix();
+ void SetMatrix(Matrix& m);
+ void Optimize(int level);
+
+ DWORD GetMaterial(DWORD index);
+ DWORD AddMaterial(DWORD id);
+ DWORD GetMaterialCount();
+
+protected:
+ // the vertices, normals, etc.
+ List<Vec3> vertices;
+ List<Vec3> normals;
+ List<Vec3> binormals;
+ List<Vec3> tangents;
+ List<Vec2> uv;
+
+ // triangles
+ List<Triangle> triangles;
+
+ // the transformation matrix.
+ Matrix matrix;
+
+ // the material ID array
+ ArrayList materials;
+
+ void CalcNormals(bool useSmoothingGroups);
+ void CalcTextureSpace();
+
+ void TransformVertices();
+};
+
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+ModelFile3DS::ModelFile3DS(const char* fname)
+ : ModelFile(fname)
+{
+}
+
+ModelFile3DS::~ModelFile3DS()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+static int mcomp(const void* a, const void* b)
+{
+ Poly* pa = (Poly*) a;
+ Poly* pb = (Poly*) b;
+
+ if (pa->sortval == pb->sortval)
+ return 0;
+
+ if (pa->sortval < pb->sortval)
+ return 1;
+
+ return -1;
+}
+
+bool
+ModelFile3DS::Load(Model* m, double scale)
+{
+ if (m && scale > 0 && strlen(filename) > 0) {
+ ModelFile::Load(m, scale);
+
+ FILE* fp = fopen(filename, "rb");
+ fclose(fp);
+
+ m->Normalize();
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+ModelFile3DS::Save(Model* m)
+{
+ if (m) {
+ ModelFile::Save(m);
+
+ FILE* f = fopen(filename, "w");
+ if (!f) {
+ ::MessageBox(0, "Export Failed: Magic could not open the file for writing", "ERROR", MB_OK);
+ return false;
+ }
+
+ fclose(f);
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
diff --git a/Magic2/M3DS.h b/Magic2/M3DS.h
new file mode 100644
index 0000000..b60ff71
--- /dev/null
+++ b/Magic2/M3DS.h
@@ -0,0 +1,329 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: M3DS.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Window class
+*/
+
+// parts copyright (c) 2001-2002 Lev Povalahev
+
+#ifndef M3DS_H
+#define M3DS_H
+
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+struct Chunk
+{
+ WORD id;
+ DWORD len;
+ BYTE* start;
+
+ List<Chunk> chunks;
+};
+
+// +--------------------------------------------------------------------+
+
+struct LTriangle
+{
+ WORD a,b,c;
+};
+
+struct LTriangle2
+{
+ Point vertices[3];
+ Vec3 vertexNormals[3];
+ LVector2 textureCoords[3];
+ Vec3 faceNormal;
+ DWORD materialId;
+};
+
+struct TextureMap
+{
+ char fname[256];
+ float strength;
+ float u_scale;
+ float v_scale;
+ float u_offset;
+ float v_offset;
+ float angle;
+};
+
+// +--------------------------------------------------------------------+
+
+class LObject
+{
+public:
+ LObject();
+ virtual ~LObject();
+
+ virtual const char* GetName();
+ virtual bool IsObject(const const char* name);
+
+protected:
+ Text name;
+};
+
+// +--------------------------------------------------------------------+
+
+class LMaterial : public LObject
+{
+public:
+ LMaterial();
+ virtual ~LMaterial();
+
+ DWORD GetID();
+ TextureMap& GetTextureMap1();
+ TextureMap& GetTextureMap2();
+ TextureMap& GetOpacityMap();
+ TextureMap& GetSpecularMap();
+ TextureMap& GetBumpMap();
+ TextureMap& GetReflectionMap();
+ ColorValue GetAmbientColor();
+ ColorValue GetDiffuseColor();
+ ColorValue GetSpecularColor();
+ float GetShininess();
+ float GetTransparency();
+ DWORD GetShadingType();
+
+ // this methods should not be used by the "user", they're used internally to fill the class
+ // with valid data when reading from file. If you're about to add an importer for another format you'LL
+ // have to use these methods
+
+ void SetID(DWORD value);
+ void SetAmbientColor(const ColorValue &color);
+ void SetDiffuseColor(const ColorValue &color);
+ void SetSpecularColor(const ColorValue &color);
+ void SetShininess(float value);
+ void SetTransparency(float value);
+ void SetShadingType(DWORD shading);
+
+protected:
+ int id;
+ TextureMap texMap1;
+ TextureMap texMap2;
+ TextureMap opacMap;
+ TextureMap refTextureMap;
+ TextureMap bumpMap;
+ TextureMap specMap;
+ ColorValue ambient;
+ ColorValue diffuse;
+ ColorValue specular;
+ float shininess;
+ float transparency;
+ DWORD shading;
+};
+
+// +--------------------------------------------------------------------+
+
+class LMesh : public LObject
+{
+public:
+ LMesh();
+ virtual ~LMesh();
+
+ void Clear();
+
+ DWORD GetVertexCount();
+ void SetVertexArraySize(DWORD value);
+ DWORD GetTriangleCount();
+ void SetTriangleArraySize(DWORD value);
+
+ // returns given vertex
+ const LVector4& GetVertex(DWORD index);
+ // returns the given normal
+ const LVector3& GetNormal(DWORD index);
+ // returns the given texture coordinates vector
+ const LVector2& GetUV(DWORD index);
+ // returns the pointer to the array of tangents
+ const LVector3& GetTangent(DWORD index);
+ // returns the pointer to the array of binormals
+ const LVector3& GetBinormal(DWORD index);
+ // sets the vertex at a given index to "vec" - for internal use
+ void SetVertex(const LVector4 &vec, DWORD index);
+ // sets the normal at a given index to "vec" - for internal use
+ void SetNormal(const LVector3 &vec, DWORD index);
+ // sets the texture coordinates vector at a given index to "vec" - for internal use
+ void SetUV(const LVector2 &vec, DWORD index);
+ // sets the tangent at a given index to "vec" - for internal use
+ void SetTangent(const LVector3 &vec, DWORD index);
+ // sets the binormal at a given index to "vec" - for internal use
+ void SetBinormal(const LVector3 &vec, DWORD index);
+ // returns the triangle with a given index
+ const LTriangle& GetTriangle(DWORD index);
+ // returns the triangle with a given index, see LTriangle2 structure description
+ LTriangle2 GetTriangle2(DWORD index);
+ // returns the mesh matrix, should be identity matrix after loading
+ LMatrix4 GetMatrix();
+ // sets the mesh matrix to a given matrix - for internal use
+ void SetMatrix(LMatrix4 m);
+ // optimizises the mesh using a given optimization level
+ void Optimize(LOptimizationLevel value);
+ // sets an internal triangle structure with index "index" - for internal use only
+ void SetTri(const LTri &tri, DWORD index);
+ // returns the pointer to the internal triangle structure - for internal use only
+ LTri& GetTri(DWORD index);
+ // returns the material id with a given index for the mesh
+ DWORD GetMaterial(DWORD index);
+ // adds a material to the mesh and returns its index - for internal use
+ DWORD AddMaterial(DWORD id);
+ // returns the number of materials used in the mesh
+ DWORD GetMaterialCount();
+protected:
+ // the vertices, normals, etc.
+ List<LVector4> vertices;
+ List<LVector3> normals;
+ List<LVector3> binormals;
+ List<LVector3> tangents;
+ List<LVector2> uv;
+
+ // triangles
+ List<LTriangle> triangles;
+
+ //used internally
+ List<LTri> tris;
+
+ // the transformation matrix.
+ Matrix matrix;
+
+ // the material ID array
+ List<DWORD> materials;
+
+ // calculates the normals, either using the smoothing groups information or not
+ void CalcNormals(bool useSmoothingGroups);
+ // calculates the texture(tangent) space for each vertex
+ void CalcTextureSpace();
+ // transforms the vertices by the mesh matrix
+ void TransformVertices();
+};
+
+//------------------------------------------------
+
+class LImporter
+{
+public:
+ // the default constructor
+ LImporter();
+ // the destructor
+ virtual ~LImporter();
+ // reads the model from a file, must be overriden by the child classes
+ virtual bool LoadFile(const char *filename) = 0;
+ // returns the number of meshes in the scene
+ DWORD GetMeshCount();
+ // returns the number of lights in the scene
+ DWORD GetLightCount();
+ // returns the number of materials in the scene
+ DWORD GetMaterialCount();
+ // returns a pointer to a mesh
+ LMesh& GetMesh(DWORD index);
+ // returns a pointer to a light at a given index
+ LLight& GetLight(DWORD index);
+ // returns the pointer to the material
+ LMaterial& GetMaterial(DWORD index);
+ // returns the pointer to the material with a given name, or NULL if the material was not found
+ LMaterial* FindMaterial(const Text &name);
+ // returns the pointer to the mesh with a given name, or NULL if the mesh with such name
+ // is not present in the scene
+ LMesh* FindMesh(const Text &name);
+ // returns the pointer to the light with a given name, or NULL if not found
+ LLight* FindLight(const Text &name);
+ // sets the optimization level to a given value
+ void SetOptimizationLevel(LOptimizationLevel value);
+ // returns the current optimization level
+ LOptimizationLevel GetOptimizationLevel();
+protected:
+ // the lights found in the scene
+ List<LLight> lights;
+ // triangular meshes
+ List<LMesh> meshes;
+ // the materials in the scene
+ List<LMaterial> materials;
+ // level of optimization to perform on the meshes
+ LOptimizationLevel optLevel;
+ // clears all data.
+ virtual void Clear();
+};
+//------------------------------------------------
+
+class L3DS : public LImporter
+{
+public:
+ // the default contructor
+ L3DS();
+ // constructs the object and loads the file
+ L3DS(const char *filename);
+ // destructor
+ virtual ~L3DS();
+ // load 3ds file
+ virtual bool LoadFile(const char *filename);
+protected:
+ // used internally for reading
+ char objName[100];
+ // true if end of file is reached
+ bool eof;
+ // buffer for loading, used for speedup
+ unsigned char *buffer;
+ // the size of the buffer
+ DWORD bufferSize;
+ // the current cursor position in the buffer
+ DWORD pos;
+
+ // reads a short value from the buffer
+ short ReadShort();
+ // reads an int value from the buffer
+ int ReadInt();
+ // reads a char from the buffer
+ char ReadChar();
+ //reada a floatvalue from the buffer
+ float ReadFloat();
+ //reads an unsigned byte from the buffer
+ byte ReadByte();
+ //reads an asciiz string
+ int ReadASCIIZ(char *buf, int max_count);
+ // seek wihtin the buffer
+ void Seek(int offset, int origin);
+ // returns the position of the cursor
+ DWORD Pos();
+
+ // read the chunk and return it.
+ LChunk ReadChunk();
+ // read until given chunk is found
+ bool FindChunk(LChunk &target, const LChunk &parent);
+ // skip to the end of chunk "chunk"
+ void SkipChunk(const LChunk &chunk);
+ // goes to the beginning of the data in teh given chunk
+ void GotoChunk(const LChunk &chunk);
+
+ // the function read the color chunk (any of the color chunks)
+ ColorValue ReadColor(const LChunk &chunk);
+ // the function that read the percentage chunk and returns a float from 0 to 1
+ float ReadPercentage(const LChunk &chunk);
+ // this is where 3ds file is being read
+ bool Read3DS();
+ // read a light chunk
+ void ReadLight(const LChunk &parent);
+ // read a trimesh chunk
+ void ReadMesh(const LChunk &parent);
+ // reads the face list, face materials, smoothing groups... and fill rthe information into the mesh
+ void ReadFaceList(const LChunk &chunk, LMesh &mesh);
+ // reads the material
+ void ReadMaterial(const LChunk &parent);
+ // reads the map info and fills the given map with this information
+ void ReadMap(const LChunk &chunk, TextureMap& map);
+ // reads keyframer data of the OBJECT_NODE_TAG chunk
+ void ReadKeyframeData(const LChunk &parent);
+ // reads the keyheader structure from the current offset and returns the frame number
+ long ReadKeyheader();
+};
+
+//---------------------------------------------------------
+
+#endif \ No newline at end of file
diff --git a/Magic2/Magic.cpp b/Magic2/Magic.cpp
new file mode 100644
index 0000000..63142db
--- /dev/null
+++ b/Magic2/Magic.cpp
@@ -0,0 +1,202 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: Magic.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Implementation of the main application class
+*/
+
+#include "stdafx.h"
+#include "Magic.h"
+
+#include "MainFrm.h"
+#include "MagicDoc.h"
+#include "MagicView.h"
+
+#include "MachineInfo.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+// +--------------------------------------------------------------------+
+
+BEGIN_MESSAGE_MAP(Magic, CWinApp)
+ //{{AFX_MSG_MAP(Magic)
+ ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
+ // NOTE - the ClassWizard will add and remove mapping macros here.
+ // DO NOT EDIT what you see in these blocks of generated code!
+ //}}AFX_MSG_MAP
+ // Standard file based document commands
+ ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
+ ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
+ // Standard print setup command
+ ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
+END_MESSAGE_MAP()
+
+// +--------------------------------------------------------------------+
+
+extern FILE* ErrLog;
+extern int VD3D_describe_things;
+void Print(const char* msg, ...);
+
+static void PrintLogHeader()
+{
+ ErrLog = fopen("errlog.txt", "w");
+ Print("+====================================================================+\n");
+ Print("| Magic 2.0 |\n");
+
+ VD3D_describe_things = 4;
+ MachineInfo::DescribeMachine();
+}
+
+// +--------------------------------------------------------------------+
+
+Magic::Magic()
+{
+ PrintLogHeader();
+}
+
+Magic::~Magic()
+{
+ Print("+====================================================================+\n");
+ Print(" END OF LINE.\n");
+
+ fclose(ErrLog);
+}
+
+// +--------------------------------------------------------------------+
+//
+// The one and only Magic object
+
+Magic theApp;
+
+// +--------------------------------------------------------------------+
+//
+// Magic initialization
+
+BOOL Magic::InitInstance()
+{
+ AfxEnableControlContainer();
+
+ // Standard initialization
+ // If you are not using these features and wish to reduce the size
+ // of your final executable, you should remove from the following
+ // the specific initialization routines you do not need.
+
+#ifdef _AFXDLL
+ Enable3dControls(); // Call this when using MFC in a shared DLL
+#else
+ Enable3dControlsStatic(); // Call this when linking to MFC statically
+#endif
+
+ // Change the registry key under which our settings are stored.
+ // TODO: You should modify this string to be something appropriate
+ // such as the name of your company or organization.
+ SetRegistryKey(_T("Local AppWizard-Generated Applications"));
+
+ LoadStdProfileSettings(); // Load standard INI file options (including MRU)
+
+ // Register the application's document templates. Document templates
+ // serve as the connection between documents, frame windows and views.
+
+ CSingleDocTemplate* pDocTemplate;
+ pDocTemplate = new CSingleDocTemplate(
+ IDR_MAINFRAME,
+ RUNTIME_CLASS(MagicDoc),
+ RUNTIME_CLASS(MainFrame), // main SDI frame window
+ RUNTIME_CLASS(MagicView));
+ AddDocTemplate(pDocTemplate);
+
+ // Parse command line for standard shell commands, DDE, file open
+ CCommandLineInfo cmdInfo;
+ ParseCommandLine(cmdInfo);
+
+ // Dispatch commands specified on the command line
+ if (!ProcessShellCommand(cmdInfo))
+ return FALSE;
+
+ // The one and only window has been initialized, so show and update it.
+ m_pMainWnd->ShowWindow(SW_SHOW);
+ m_pMainWnd->UpdateWindow();
+
+ return TRUE;
+}
+
+
+// +--------------------------------------------------------------------+
+//
+// CAboutDlg dialog used for App About
+
+class CAboutDlg : public CDialog
+{
+public:
+ CAboutDlg();
+
+// Dialog Data
+ //{{AFX_DATA(CAboutDlg)
+ enum { IDD = IDD_ABOUTBOX };
+ //}}AFX_DATA
+
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CAboutDlg)
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+ //{{AFX_MSG(CAboutDlg)
+ // No message handlers
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
+{
+ //{{AFX_DATA_INIT(CAboutDlg)
+ //}}AFX_DATA_INIT
+}
+
+void CAboutDlg::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CAboutDlg)
+ //}}AFX_DATA_MAP
+}
+
+BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
+ //{{AFX_MSG_MAP(CAboutDlg)
+ // No message handlers
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+// App command to run the dialog
+void Magic::OnAppAbout()
+{
+ CAboutDlg aboutDlg;
+ aboutDlg.DoModal();
+}
+
+// +--------------------------------------------------------------------+
+//
+// Magic message handlers
+
+BOOL Magic::OnIdle(LONG lCount)
+{
+ CWinApp::OnIdle(lCount);
+
+ if (!app_active)
+ Sleep(50);
+
+ m_pMainWnd->SendMessage(WM_COMMAND, ID_VIEW_RENDER);
+ return TRUE; // always request more time
+}
diff --git a/Magic2/Magic.dsp b/Magic2/Magic.dsp
new file mode 100644
index 0000000..5c46b0e
--- /dev/null
+++ b/Magic2/Magic.dsp
@@ -0,0 +1,608 @@
+# Microsoft Developer Studio Project File - Name="Magic" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=Magic - 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 "Magic.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 "Magic.mak" CFG="Magic - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "Magic - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "Magic - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "Magic - Win32 Release"
+
+# PROP BASE Use_MFC 6
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 6
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "../pnglib" /I "../Opcode/OpcodeLib" /I "../libpng" /I "../Magic2" /I "../FoundationEx" /I "../nGenEx" /I "../zlib" /I "../parser" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "FOUNDATION_USE_MFC" /D "_AFXDLL" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL"
+# ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /nologo /subsystem:windows /machine:I386
+# ADD LINK32 ..\Opcode\OpcodeLib\Release\OpcodeLib.lib ..\ngenex\release\ngenex.lib ..\zlib\release\zlib.lib ..\libpng\release\libpng.lib wsock32.lib dinput.lib dsound.lib d3d9.lib d3dx9.lib dxguid.lib winmm.lib version.lib ogg_static.lib vorbis_static.lib vorbisfile_static.lib vfw32.lib /nologo /subsystem:windows /incremental:yes /map /machine:I386 /nodefaultlib:"LIBC"
+
+!ELSEIF "$(CFG)" == "Magic - Win32 Debug"
+
+# PROP BASE Use_MFC 6
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 6
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../Opcode/OpcodeLib" /I "../libpng" /I "../Magic2" /I "../FoundationEx" /I "../nGenEx" /I "../zlib" /I "../parser" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "FOUNDATION_USE_MFC" /D "_AFXDLL" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL"
+# ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 ..\Opcode\OpcodeLib\Debug\OpcodeLib.lib ..\zlib\debug\zlib.lib ..\libpng\debug\libpng.lib wsock32.lib dinput.lib dsound.lib d3d9.lib d3dx9.lib dxguid.lib winmm.lib version.lib ogg_static.lib vorbis_static.lib vorbisfile_static.lib vfw32.lib /nologo /subsystem:windows /debug /machine:I386 /nodefaultlib:"LIBCMT" /nodefaultlib:"LIBCD" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "Magic - Win32 Release"
+# Name "Magic - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\nGenEx\ActiveWindow.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\AlphaInverse.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\AlphaPalette.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Archive.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\ArrayList.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\AviFile.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Bitmap.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Bmp.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Bolt.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Button.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Camera.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\CameraView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Color.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\ComboBox.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\ComboList.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Command.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\D3DXImage.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\DataLoader.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\EditBox.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Editor.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Encrypt.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\EventDispatch.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\FadeView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Fix.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Font.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\FontMgr.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\FormatUtil.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\FormDef.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\FormWindow.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Game.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Geometry.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Graphic.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Grid.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\GridProps.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\ImageBox.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\ImgView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Joystick.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Keyboard.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\l3ds.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Layout.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Light.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\ListBox.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\MachineInfo.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Magic.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Magic.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\MagicDoc.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MagicView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MainFrm.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MaterialDialog.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\MCIWave.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Menu.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModelFile3DS.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModelFileMAG.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModelFileOBJ.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModelView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Mouse.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\MouseController.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\MultiController.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Parser.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\ParseUtil.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Particles.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\PCX.CPP
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Physical.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\PngImage.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Polygon.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Projector.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Random.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Reader.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Res.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\RichTextBox.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Scene.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Screen.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\ScrollWindow.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Selection.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Selector.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Sha1.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Shadow.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Slider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Solid.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Sound.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\SoundCard.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\SoundD3D.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Sprite.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.cpp
+# ADD CPP /Yc"stdafx.h"
+# End Source File
+# Begin Source File
+
+SOURCE=.\SurfacePropertiesDialog.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Term.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\TexDX9.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\Text.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\TextureMapDialog.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Thumbnail.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Token.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\UVMapView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Video.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\VideoDX9.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\VideoDX9Enum.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\VideoDX9VertexBuffer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\VideoFactory.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\VideoSettings.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Window.cpp
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\nGenEx\AviFile.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Command.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Editor.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Grid.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\GridProps.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\l3ds.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Magic.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MagicDoc.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MagicView.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MainFrm.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MaterialDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModelFile3DS.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModelFileMAG.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModelFileOBJ.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModelView.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resource.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Selection.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Selector.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SurfacePropertiesDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TextureMapDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Thumbnail.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\UVMapView.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\res\Magic.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\Magic.rc2
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\MagicDoc.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\res\Toolbar.bmp
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Magic2/Magic.dsw b/Magic2/Magic.dsw
new file mode 100644
index 0000000..0a0bb24
--- /dev/null
+++ b/Magic2/Magic.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Magic"=.\Magic.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Magic2/Magic.h b/Magic2/Magic.h
new file mode 100644
index 0000000..07318b1
--- /dev/null
+++ b/Magic2/Magic.h
@@ -0,0 +1,63 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: Magic.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Interface of the main application class
+*/
+
+
+#if !defined(AFX_MAGIC_H__E61FB5C3_42B6_42D1_BEFA_17F0B038E33B__INCLUDED_)
+#define AFX_MAGIC_H__E61FB5C3_42B6_42D1_BEFA_17F0B038E33B__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#ifndef __AFXWIN_H__
+ #error include 'stdafx.h' before including this file for PCH
+#endif
+
+#include "resource.h" // main symbols
+
+
+class Magic : public CWinApp
+{
+public:
+ Magic();
+ virtual ~Magic();
+
+ bool AppActivated() const { return app_active; }
+ void SetAppActivated(bool a) { app_active = a; }
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(Magic)
+ public:
+ virtual BOOL InitInstance();
+ virtual BOOL OnIdle(LONG lCount);
+ //}}AFX_VIRTUAL
+
+// Implementation
+ //{{AFX_MSG(Magic)
+ afx_msg void OnAppAbout();
+ // NOTE - the ClassWizard will add and remove member functions here.
+ // DO NOT EDIT what you see in these blocks of generated code !
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+
+private:
+ bool app_active;
+};
+
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_MAGIC_H__E61FB5C3_42B6_42D1_BEFA_17F0B038E33B__INCLUDED_)
diff --git a/Magic2/Magic.rc b/Magic2/Magic.rc
new file mode 100644
index 0000000..be89067
--- /dev/null
+++ b/Magic2/Magic.rc
@@ -0,0 +1,1065 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#define _AFX_NO_SPLITTER_RESOURCES\r\n"
+ "#define _AFX_NO_OLE_RESOURCES\r\n"
+ "#define _AFX_NO_TRACKER_RESOURCES\r\n"
+ "#define _AFX_NO_PROPERTY_RESOURCES\r\n"
+ "\r\n"
+ "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
+ "#ifdef _WIN32\r\n"
+ "LANGUAGE 9, 1\r\n"
+ "#pragma code_page(1252)\r\n"
+ "#endif //_WIN32\r\n"
+ "#include ""res\\Magic.rc2"" // non-Microsoft Visual C++ edited resources\r\n"
+ "#include ""afxres.rc"" // Standard components\r\n"
+ "#include ""afxprint.rc"" // printing/print preview resources\r\n"
+ "#endif\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDR_MAINFRAME ICON DISCARDABLE "res\\Magic.ico"
+IDR_MAGICTYPE ICON DISCARDABLE "res\\MagicDoc.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+IDR_MAINFRAME BITMAP MOVEABLE PURE "res\\Toolbar.bmp"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Toolbar
+//
+
+IDR_MAINFRAME TOOLBAR DISCARDABLE 16, 15
+BEGIN
+ BUTTON ID_FILE_NEW
+ BUTTON ID_FILE_OPEN
+ BUTTON ID_FILE_SAVE
+ SEPARATOR
+ BUTTON ID_EDIT_CUT
+ BUTTON ID_EDIT_COPY
+ BUTTON ID_EDIT_PASTE
+ SEPARATOR
+ BUTTON ID_EDIT_UNDO
+ BUTTON ID_EDIT_REDO
+ SEPARATOR
+ BUTTON ID_VIEW_ZOOM_IN
+ BUTTON ID_VIEW_ZOOM_OUT
+ SEPARATOR
+ BUTTON ID_CREATE_CUBE
+ BUTTON ID_CREATE_CONE
+ BUTTON ID_CREATE_CYLINDER
+ BUTTON ID_CREATE_SPHERE
+ SEPARATOR
+ BUTTON ID_MODIFY_DRAG_VERTS
+ BUTTON ID_MODIFY_SCALE_VERTS
+ BUTTON ID_MODIFY_ROTATE
+ SEPARATOR
+ BUTTON ID_MODIFY_SCALE_X
+ BUTTON ID_MODIFY_SCALE_Y
+ BUTTON ID_MODIFY_SCALE_Z
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_MAINFRAME MENU PRELOAD DISCARDABLE
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "&New\tCtrl+N", ID_FILE_NEW
+ MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN
+ MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE
+ MENUITEM "Save &As...", ID_FILE_SAVE_AS
+ MENUITEM SEPARATOR
+ MENUITEM "Import...", ID_FILE_IMPORT
+ MENUITEM "Export...", ID_FILE_EXPORT
+ MENUITEM SEPARATOR
+ MENUITEM "Recent File", ID_FILE_MRU_FILE2, GRAYED
+ MENUITEM SEPARATOR
+ MENUITEM "E&xit", ID_APP_EXIT
+ END
+ POPUP "&Edit"
+ BEGIN
+ MENUITEM "&Undo\tCtrl+Z", ID_EDIT_UNDO
+ MENUITEM "Red&o\tCtrl+Y", ID_EDIT_REDO
+ MENUITEM SEPARATOR
+ MENUITEM "&Clone\tCtrl+C", ID_EDIT_CLONE
+ MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE
+ MENUITEM "&Delete\tDelete", ID_EDIT_DELETE
+ MENUITEM "Select &All\tCtrl+A", ID_EDIT_SELECT_ALL
+ MENUITEM "Select &None\tCtrl+D", ID_EDIT_SELECT_NONE
+ MENUITEM "Select &Inverse\tCtrl+Shift+I", ID_EDIT_SELECT_INVERSE
+ MENUITEM SEPARATOR
+ MENUITEM "&Flip Normal\tShift+F", ID_EDIT_FLIP_NORMAL
+ MENUITEM "&Remove Poly", ID_EDIT_REMOVE_POLY
+ MENUITEM "&Mirror Selection", ID_EDIT_MIRROR
+ MENUITEM "S&eal Selection\tShift+S", ID_EDIT_SEAL
+ MENUITEM "&Reduce Verts...\tCtrl+R", ID_VERTS_REDUCE
+ MENUITEM SEPARATOR
+ MENUITEM "Flatten &X", ID_MODIFY_FLATTEN_X
+ MENUITEM "Flatten &Y", ID_MODIFY_FLATTEN_Y
+ MENUITEM "Flatten &Z", ID_MODIFY_FLATTEN_Z
+ END
+ POPUP "&View"
+ BEGIN
+ POPUP "Mode"
+ BEGIN
+ MENUITEM "Wireframe", ID_VIEW_MODE_WIREFRAME
+ MENUITEM "Solid", ID_VIEW_MODE_SOLID
+ MENUITEM "Textured", ID_VIEW_MODE_TEXTURED
+ END
+ POPUP "Zoom"
+ BEGIN
+ MENUITEM "Zoom &In", ID_VIEW_ZOOM_IN
+ MENUITEM "Zoom &Out", ID_VIEW_ZOOM_OUT
+ MENUITEM "Zoom &Normal", ID_VIEW_ZOOM_NORMAL
+ END
+ MENUITEM "Background &Color...", ID_VIEW_BACK_COLOR
+ MENUITEM "&Grid...", ID_PROP_GRID
+ MENUITEM SEPARATOR
+ MENUITEM "All", ID_VIEW_ALL
+ MENUITEM "Front", ID_VIEW_FRONT
+ MENUITEM "Side", ID_VIEW_SIDE
+ MENUITEM "Top", ID_VIEW_TOP
+ MENUITEM "Perspective", ID_VIEW_PERSPECTIVE
+ MENUITEM SEPARATOR
+ MENUITEM "&Toolbar", ID_VIEW_TOOLBAR
+ MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR
+ MENUITEM SEPARATOR
+ MENUITEM "Bumpmaps", ID_VIEW_BUMPMAPS
+ MENUITEM "Shadows", ID_VIEW_SHADOWS
+ MENUITEM "Animate Light", ID_VIEW_ANIMATELIGHT
+ MENUITEM "Vertex Shader", ID_VIEW_VERTEXSHADER
+ MENUITEM "Pixel Shader", ID_VIEW_PIXELSHADER
+ MENUITEM "Visible Shadows", ID_VIEW_VISIBLESHADOWS
+ END
+ POPUP "&Surface"
+ BEGIN
+ MENUITEM "&Properties...", ID_PROP_SURFACE
+ MENUITEM "&Optimize Mesh", ID_SURFACE_OPTIMIZE
+ MENUITEM "&Explode Mesh", ID_SURFACE_EXPLODE
+ END
+ POPUP "&Primatives"
+ BEGIN
+ MENUITEM "&Poly...", ID_CREATE_POLY
+ MENUITEM "&Cube...", ID_CREATE_CUBE
+ MENUITEM "C&one...", ID_CREATE_CONE
+ MENUITEM "C&ylinder...", ID_CREATE_CYLINDER
+ MENUITEM "&Sphere...", ID_CREATE_SPHERE
+ END
+ POPUP "&Modify"
+ BEGIN
+ MENUITEM "&Polygon...", ID_MODIFY_POLY
+ MENUITEM "&Material...", ID_MODIFY_MATERIAL
+ MENUITEM "&Texture Mapping...", ID_MODIFY_TEXTURE_MAP
+ MENUITEM "&UV Mapping...", ID_MODIFY_UV_MAP
+ MENUITEM SEPARATOR
+ MENUITEM "&Extrude", ID_MODIFY_EXTRUDE
+ MENUITEM "&Bevel", ID_MODIFY_BEVEL
+ MENUITEM "&Stud", ID_MODIFY_STUD
+ MENUITEM SEPARATOR
+ MENUITEM "Sub&divide", ID_MODIFY_SUBDIVIDE
+ MENUITEM "Split &1", ID_MODIFY_SPLIT1
+ MENUITEM "Split &2", ID_MODIFY_SPLIT2
+ MENUITEM SEPARATOR
+ MENUITEM "&Reduce Verts...\tCtrl+R", ID_VERTS_REDUCE
+ END
+ POPUP "&Help"
+ BEGIN
+ MENUITEM "&About Magic...", ID_APP_ABOUT
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerator
+//
+
+IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE
+BEGIN
+ "A", ID_EDIT_SELECT_ALL, VIRTKEY, CONTROL, NOINVERT
+ "C", ID_EDIT_CLONE, VIRTKEY, CONTROL, NOINVERT
+ "D", ID_EDIT_SELECT_NONE, VIRTKEY, CONTROL, NOINVERT
+ "F", ID_EDIT_FLIP_NORMAL, VIRTKEY, SHIFT, NOINVERT
+ "G", ID_GRID_SNAP, VIRTKEY, CONTROL, NOINVERT
+ "G", ID_GRID_SHOW, VIRTKEY, SHIFT, CONTROL,
+ NOINVERT
+ "I", ID_EDIT_SELECT_INVERSE, VIRTKEY, SHIFT, CONTROL,
+ NOINVERT
+ "N", ID_FILE_NEW, VIRTKEY, CONTROL, NOINVERT
+ "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT
+ "R", ID_VERTS_REDUCE, VIRTKEY, CONTROL, NOINVERT
+ "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT
+ "S", ID_EDIT_SEAL, VIRTKEY, SHIFT, NOINVERT
+ "V", ID_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT
+ VK_ADD, ID_VIEW_ZOOM_IN, VIRTKEY, CONTROL, NOINVERT
+ VK_BACK, ID_EDIT_UNDO, VIRTKEY, ALT, NOINVERT
+ VK_DELETE, ID_EDIT_DELETE, VIRTKEY, NOINVERT
+ VK_DOWN, ID_NUDGE_DOWN, VIRTKEY, NOINVERT
+ VK_DOWN, ID_STEP_DOWN, VIRTKEY, SHIFT, NOINVERT
+ VK_F2, ID_MODIFY_DRAG_VERTS, VIRTKEY, NOINVERT
+ VK_F3, ID_MODIFY_SCALE_VERTS, VIRTKEY, NOINVERT
+ VK_F4, ID_MODIFY_ROTATE, VIRTKEY, NOINVERT
+ VK_F6, ID_NEXT_PANE, VIRTKEY, NOINVERT
+ VK_F6, ID_PREV_PANE, VIRTKEY, SHIFT, NOINVERT
+ VK_LEFT, ID_NUDGE_LEFT, VIRTKEY, NOINVERT
+ VK_LEFT, ID_STEP_LEFT, VIRTKEY, SHIFT, NOINVERT
+ VK_RIGHT, ID_NUDGE_RIGHT, VIRTKEY, NOINVERT
+ VK_RIGHT, ID_STEP_RIGHT, VIRTKEY, SHIFT, NOINVERT
+ VK_SUBTRACT, ID_VIEW_ZOOM_OUT, VIRTKEY, CONTROL, NOINVERT
+ VK_TAB, ID_FILE_MRU_FILE2, VIRTKEY, CONTROL, NOINVERT
+ VK_UP, ID_NUDGE_UP, VIRTKEY, NOINVERT
+ VK_UP, ID_STEP_UP, VIRTKEY, SHIFT, NOINVERT
+ "X", ID_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT
+ "X", ID_MODIFY_SCALE_X, VIRTKEY, SHIFT, NOINVERT
+ "X", ID_MODIFY_FLATTEN_X, VIRTKEY, SHIFT, CONTROL,
+ NOINVERT
+ "Y", ID_EDIT_REDO, VIRTKEY, CONTROL, NOINVERT
+ "Y", ID_MODIFY_SCALE_Y, VIRTKEY, SHIFT, NOINVERT
+ "Y", ID_MODIFY_FLATTEN_Y, VIRTKEY, SHIFT, CONTROL,
+ NOINVERT
+ "Z", ID_EDIT_UNDO, VIRTKEY, CONTROL, NOINVERT
+ "Z", ID_MODIFY_SCALE_Z, VIRTKEY, SHIFT, NOINVERT
+ "Z", ID_MODIFY_FLATTEN_Z, VIRTKEY, SHIFT, CONTROL,
+ NOINVERT
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_ABOUTBOX DIALOG DISCARDABLE 0, 0, 217, 117
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "About Magic 2.0"
+FONT 8, "MS Sans Serif"
+BEGIN
+ ICON IDR_MAINFRAME,IDC_STATIC,7,13,20,20
+ LTEXT "Magic 2.0.0",IDC_STATIC,40,10,119,8,SS_NOPREFIX
+ LTEXT "Copyright © 1997-2004\nDestroyer Studios LLC",
+ IDC_STATIC,40,25,146,19
+ DEFPUSHBUTTON "OK",IDOK,178,7,32,14,WS_GROUP
+ LTEXT "Magic is a 3D low-poly modeling tool for the game Starshatter by Destroyer Studios.\n\nhttp://www.starshatter.com\n\n3DS File Loader Copyright © 2001 Lev Povalahev",
+ IDC_STATIC,40,51,170,59
+END
+
+IDD_GRIDPROPS DIALOG DISCARDABLE 0, 0, 248, 174
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Grid Properties"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,185,10,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,185,26,50,14
+ CONTROL "Snap",IDC_GRID_SNAP,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,47,41,33,10
+ GROUPBOX "Properties",IDC_STATIC,7,8,155,69
+ CONTROL "Show",IDC_GRID_SHOW,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,47,56,34,10
+ GROUPBOX "Reference Images",IDC_STATIC,7,91,223,74
+ LTEXT "Plan:",IDC_STATIC,19,108,17,8
+ LTEXT "Side:",IDC_STATIC,19,127,17,8
+ LTEXT "Front:",IDC_STATIC,19,146,19,8
+ EDITTEXT IDC_REFERENCE_PLAN,47,105,138,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_REFERENCE_SIDE,47,124,138,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_REFERENCE_FRONT,47,143,138,14,ES_AUTOHSCROLL
+ LTEXT "Size:",IDC_STATIC,18,24,16,8
+ EDITTEXT IDC_GRID_SIZE,47,21,40,14,ES_AUTOHSCROLL
+ PUSHBUTTON "...",IDC_FILE_PLAN,197,105,15,14
+ PUSHBUTTON "...",IDC_FILE_SIDE,197,124,15,14
+ PUSHBUTTON "...",IDC_FILE_FRONT,197,143,15,14
+END
+
+IDD_SURFACE_PROPS DIALOG DISCARDABLE 0, 0, 265, 122
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Surface Properties"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,208,7,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,208,24,50,14
+ LTEXT "Surface:",IDC_STATIC,7,11,28,8
+ COMBOBOX IDC_SURFACE_NAME,42,7,112,56,CBS_DROPDOWN | CBS_SORT |
+ WS_VSCROLL | WS_TABSTOP
+ LTEXT "Verts:",IDC_STATIC,7,26,19,8
+ LTEXT "Polys:",IDC_STATIC,7,40,20,8
+ LTEXT "00000",IDC_SURFACE_NVERTS,43,26,21,8
+ LTEXT "00000",IDC_SURFACE_NPOLYS,43,40,21,8
+ LTEXT "Radius:",IDC_STATIC,7,54,25,8
+ LTEXT "Length:",IDC_STATIC,7,68,25,8
+ LTEXT "Width:",IDC_STATIC,7,82,22,8
+ LTEXT "Height:",IDC_STATIC,7,96,24,8
+ LTEXT "0",IDC_SURFACE_RADIUS,43,54,138,8
+ LTEXT "0",IDC_SURFACE_LENGTH,43,68,138,8
+ LTEXT "0",IDC_SURFACE_WIDTH,43,82,138,8
+ LTEXT "0",IDC_SURFACE_HEIGHT,43,96,138,8
+END
+
+IDD_VERTS_REDUCE DIALOG DISCARDABLE 0, 0, 175, 71
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Reduce Verts"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,29,50,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,99,50,50,14
+ LTEXT "Distance Threshold:",IDC_STATIC,53,7,64,8
+ EDITTEXT IDC_THRESHOLD,53,18,66,14,ES_AUTOHSCROLL
+END
+
+IDD_MODIFY_POLY DIALOG DISCARDABLE 0, 0, 268, 175
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Modify Polygon"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "Close",IDOK,211,7,50,14
+ LTEXT "Verts:",IDC_STATIC,7,7,19,8
+ LTEXT "4",IDC_POLY_VERTS,79,7,64,8
+ LTEXT "Curvature:",IDC_STATIC,7,125,34,8
+ LTEXT "Surface:",IDC_STATIC,7,141,28,8
+ LTEXT "Texture:",IDC_STATIC,7,157,27,8
+ LTEXT "0.0000",IDC_POLY_CURVE,77,125,65,8
+ LTEXT "default",IDC_POLY_SURF,77,141,65,8
+ LTEXT "(none)",IDC_POLY_TEX,77,157,62,8
+ PUSHBUTTON "Flatten",IDC_FLATTEN,211,47,50,14
+ PUSHBUTTON "Flip Normal",IDC_FLIP_NORM,211,67,50,14
+ PUSHBUTTON "Double Side",IDC_DOUBLE_SIDE,211,87,50,14
+ PUSHBUTTON "Triangulate",IDC_TRIANGULATE,211,27,50,14
+ PUSHBUTTON "Remove",IDC_REMOVE,211,108,50,14
+ CONTROL "Flat Shaded",IDC_FLAG_FLAT,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,77,23,54,10
+ CONTROL "Luminous",IDC_FLAG_LUMINOUS,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,77,37,46,10
+ CONTROL "Translucent",IDC_FLAG_TRANSLUCENT,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,77,51,53,10
+ LTEXT "Flags:",IDC_STATIC,7,23,20,8
+ CONTROL "Transparent",IDC_FLAG_TRANSPARENT,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,77,66,53,10
+ CONTROL "Specular 1",IDC_FLAG_SPECULAR1,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,77,80,53,10
+ RTEXT "0",IDC_POLY_INDEX,213,158,48,10
+ CONTROL "Specular 2",IDC_FLAG_SPECULAR2,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,77,94,53,10
+ CONTROL "Texture Clamp",IDC_FLAG_TEXCLAMP,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,77,109,79,10
+END
+
+IDD_CREATE_CUBE DIALOG DISCARDABLE 0, 0, 307, 117
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Create Cube"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,250,7,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,250,24,50,14
+ GROUPBOX "Location",IDC_STATIC,7,7,94,87
+ GROUPBOX "Size",IDC_STATIC,122,7,94,87
+ LTEXT "X:",IDC_STATIC,15,22,8,8
+ LTEXT "Y:",IDC_STATIC,15,45,8,8
+ LTEXT "Z:",IDC_STATIC,15,68,8,8
+ EDITTEXT IDC_LOC_X,42,19,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_LOC_Y,42,42,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_LOC_Z,42,65,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_SIZE_X,157,20,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_SIZE_Y,157,43,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_SIZE_Z,157,66,40,14,ES_AUTOHSCROLL
+ LTEXT "X:",IDC_STATIC,130,22,8,8
+ LTEXT "Y:",IDC_STATIC,130,45,8,8
+ LTEXT "Z:",IDC_STATIC,130,68,8,8
+END
+
+IDD_CREATE_CYLINDER DIALOG DISCARDABLE 0, 0, 309, 145
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Create Cylinder"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,252,7,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,252,24,50,14
+ GROUPBOX "Location",IDC_STATIC,7,7,94,87
+ GROUPBOX "Size",IDC_STATIC,122,7,94,87
+ LTEXT "X:",IDC_STATIC,15,22,8,8
+ LTEXT "Y:",IDC_STATIC,15,45,8,8
+ LTEXT "Z:",IDC_STATIC,15,68,8,8
+ EDITTEXT IDC_LOC_X,42,19,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_LOC_Y,42,42,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_LOC_Z,42,65,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_SIZE_X,157,20,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_SIZE_Y,157,43,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_SIZE_Z,157,66,40,14,ES_AUTOHSCROLL
+ LTEXT "X:",IDC_STATIC,130,22,8,8
+ LTEXT "Y:",IDC_STATIC,130,45,8,8
+ LTEXT "Z:",IDC_STATIC,130,68,8,8
+ LTEXT "Faces:",IDC_STATIC,7,108,22,8
+ EDITTEXT IDC_SECTORS,42,106,40,14,ES_AUTOHSCROLL
+END
+
+IDD_CREATE_SPHERE DIALOG DISCARDABLE 0, 0, 311, 154
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Create Sphere"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,254,7,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,254,24,50,14
+ GROUPBOX "Location",IDC_STATIC,7,7,94,87
+ GROUPBOX "Size",IDC_STATIC,122,7,94,87
+ LTEXT "X:",IDC_STATIC,15,22,8,8
+ LTEXT "Y:",IDC_STATIC,15,45,8,8
+ LTEXT "Z:",IDC_STATIC,15,68,8,8
+ EDITTEXT IDC_LOC_X,42,19,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_LOC_Y,42,42,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_LOC_Z,42,65,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_SIZE_X,157,20,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_SIZE_Y,157,43,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_SIZE_Z,157,66,40,14,ES_AUTOHSCROLL
+ LTEXT "X:",IDC_STATIC,130,22,8,8
+ LTEXT "Y:",IDC_STATIC,130,45,8,8
+ LTEXT "Z:",IDC_STATIC,130,68,8,8
+ LTEXT "Sectors:",IDC_STATIC,7,108,27,8
+ EDITTEXT IDC_SECTORS,42,106,40,14,ES_AUTOHSCROLL
+ LTEXT "Rings:",IDC_STATIC,127,108,21,8
+ EDITTEXT IDC_RINGS,157,106,40,14,ES_AUTOHSCROLL
+END
+
+IDD_MODIFY_TEXTURE DIALOG DISCARDABLE 0, 0, 338, 215
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Texture Mapping"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,281,7,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,281,27,50,14
+ LTEXT "Material:",IDC_STATIC,7,7,28,8
+ LTEXT "Mapping:",IDC_STATIC,7,30,30,8
+ COMBOBOX IDC_MATERIAL,57,7,77,69,CBS_DROPDOWNLIST | WS_VSCROLL |
+ WS_TABSTOP
+ COMBOBOX IDC_MAPPING,57,29,78,74,CBS_DROPDOWNLIST | WS_VSCROLL |
+ WS_TABSTOP
+ GROUPBOX "Align",IDC_STATIC,7,52,127,61
+ GROUPBOX "Scale",IDC_STATIC,159,127,128,65
+ CONTROL "X Axis",IDC_ALIGN_X,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP,56,65,35,10
+ CONTROL "Y Axis",IDC_ALIGN_Y,"Button",BS_AUTORADIOBUTTON,56,79,
+ 35,10
+ CONTROL "Z Axis",IDC_ALIGN_Z,"Button",BS_AUTORADIOBUTTON,56,93,
+ 35,10
+ EDITTEXT IDC_SCALE_U,207,142,53,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_SCALE_V,207,161,53,14,ES_AUTOHSCROLL
+ LTEXT "U:",IDC_STATIC,177,145,8,8
+ LTEXT "V:",IDC_STATIC,177,163,8,8
+ LTEXT "Preview:",IDC_STATIC,160,7,28,8
+ GROUPBOX "Reflect",IDC_STATIC,7,127,127,65
+ CONTROL "Flip",IDC_ALIGN_FLIP,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,56,140,27,10
+ CONTROL "Mirror",IDC_ALIGN_MIRROR,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,56,155,33,10
+ CONTROL "Rotate",IDC_ALIGN_ROTATE,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,56,170,37,10
+ CONTROL "",IDC_TEXTURE_PREVIEW,"Static",SS_BLACKFRAME |
+ SS_SUNKEN,159,18,88,81
+END
+
+IDD_MODIFY_POLYS DIALOG DISCARDABLE 0, 0, 300, 162
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Modify Multiple Polygons"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "Close",IDOK,243,7,50,14
+ LTEXT "Polys:",IDC_STATIC,7,7,19,8
+ LTEXT "4",IDC_POLY_VERTS,79,7,64,8
+ LTEXT "Surface:",IDC_STATIC,7,125,28,8
+ LTEXT "Texture:",IDC_STATIC,7,141,27,8
+ LTEXT "default",IDC_POLY_SURF,77,125,65,8
+ LTEXT "(none)",IDC_POLY_TEX,77,141,62,8
+ CONTROL "Flat Shaded",IDC_FLAG_FLAT,"Button",BS_AUTO3STATE |
+ WS_TABSTOP,77,23,54,10
+ CONTROL "Luminous",IDC_FLAG_LUMINOUS,"Button",BS_AUTO3STATE |
+ WS_TABSTOP,77,37,46,10
+ CONTROL "Translucent",IDC_FLAG_TRANSLUCENT,"Button",
+ BS_AUTO3STATE | WS_TABSTOP,77,51,53,10
+ LTEXT "Flags:",IDC_STATIC,7,23,20,8
+ CONTROL "Transparent",IDC_FLAG_TRANSPARENT,"Button",
+ BS_AUTO3STATE | WS_TABSTOP,77,65,53,10
+ CONTROL "Specular 1",IDC_FLAG_SPECULAR1,"Button",BS_AUTO3STATE |
+ WS_TABSTOP,77,79,53,10
+ CONTROL "Flat Shaded",IDC_FLAG_FLAT2,"Button",BS_AUTO3STATE |
+ WS_TABSTOP,160,23,54,10
+ CONTROL "Luminous",IDC_FLAG_LUMINOUS2,"Button",BS_AUTO3STATE |
+ WS_TABSTOP,160,37,46,10
+ CONTROL "Translucent",IDC_FLAG_TRANSLUCENT2,"Button",
+ BS_AUTO3STATE | WS_TABSTOP,160,51,53,10
+ CONTROL "Transparent",IDC_FLAG_TRANSPARENT2,"Button",
+ BS_AUTO3STATE | WS_TABSTOP,160,65,53,10
+ CONTROL "Specular 1",IDC_FLAG_SPECULAR12,"Button",BS_AUTO3STATE |
+ WS_TABSTOP,160,79,53,10
+ PUSHBUTTON "Flatten",IDC_FLATTEN,243,46,50,14
+ PUSHBUTTON "Flip Normal",IDC_FLIP_NORM,243,66,50,14
+ PUSHBUTTON "Triangulate",IDC_TRIANGULATE,243,26,50,14
+ CONTROL "Specular 2",IDC_FLAG_SPECULAR2,"Button",BS_AUTO3STATE |
+ WS_TABSTOP,77,93,53,10
+ CONTROL "Specular 2",IDC_FLAG_SPECULAR22,"Button",BS_AUTO3STATE |
+ WS_TABSTOP,159,93,53,10
+ CONTROL "Texture Clamp",IDC_FLAG_TEXCLAMP,"Button",BS_AUTO3STATE |
+ WS_TABSTOP,77,107,61,10
+ CONTROL "Texture Clamp",IDC_FLAG_TEXCLAMP2,"Button",
+ BS_AUTO3STATE | WS_TABSTOP,159,107,60,10
+END
+
+IDD_CREATE_POLY DIALOG DISCARDABLE 0, 0, 307, 116
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Create Poly"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,250,7,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,250,24,50,14
+ GROUPBOX "Location",IDC_STATIC,7,7,94,87
+ LTEXT "X:",IDC_STATIC,15,22,8,8
+ LTEXT "Y:",IDC_STATIC,15,45,8,8
+ LTEXT "Z:",IDC_STATIC,15,68,8,8
+ EDITTEXT IDC_LOC_X,42,19,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_LOC_Y,42,42,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_LOC_Z,42,65,40,14,ES_AUTOHSCROLL
+ COMBOBOX IDC_NSIDES,121,19,86,73,CBS_DROPDOWNLIST | CBS_SORT |
+ WS_VSCROLL | WS_TABSTOP
+ LTEXT "Number of Sides:",IDC_STATIC,121,7,55,8
+END
+
+IDD_EXTRUSION_PROPS DIALOG DISCARDABLE 0, 0, 183, 71
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Extrusion"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,27,50,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,97,50,50,14
+ LTEXT "Distance:",IDC_STATIC,53,7,64,8
+ EDITTEXT IDC_DISTANCE,53,18,66,14,ES_AUTOHSCROLL
+END
+
+IDD_MODIFY_MATERIAL DIALOG DISCARDABLE 0, 0, 428, 198
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Material Editor"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "Close",IDOK,371,177,50,14
+ CONTROL "",IDC_MATERIAL_PREVIEW,"Static",SS_BLACKFRAME |
+ SS_SUNKEN,333,7,88,81
+ LISTBOX IDC_MATERIAL_LIST,7,18,75,86,LBS_NOINTEGRALHEIGHT |
+ WS_VSCROLL | WS_TABSTOP
+ LTEXT "Materials:",IDC_STATIC,7,7,31,8
+ PUSHBUTTON "New Material",IDC_NEW_MATERIAL,7,113,76,14
+ PUSHBUTTON "Delete Material",IDC_DEL_MATERIAL,7,133,76,14
+ EDITTEXT IDC_MATERIAL_NAME,140,7,68,14,ES_AUTOHSCROLL
+ LTEXT "Name:",IDC_STATIC,101,7,22,8
+ LTEXT "Ambient:",IDC_STATIC,101,35,28,8
+ LTEXT "Diffuse:",IDC_STATIC,101,55,25,8
+ LTEXT "Specular:",IDC_STATIC,101,75,31,8
+ LTEXT "Emissive:",IDC_STATIC,101,95,30,8
+ LTEXT "Bump:",IDC_STATIC,101,115,21,8
+ LTEXT "Power:",IDC_STATIC,101,135,23,8
+ LTEXT "Brilliance:",IDC_STATIC,101,155,31,8
+ LTEXT "Blend:",IDC_STATIC,215,141,21,8
+ EDITTEXT IDC_DIFFUSE_TEXTURE,215,52,72,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_SPECULAR_TEXTURE,215,73,72,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_EMISSIVE_TEXTURE,215,92,72,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_BUMP_TEXTURE,215,112,72,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_AMBIENT_VALUE,140,33,32,14,ES_AUTOHSCROLL
+ CONTROL "",IDC_AMBIENT_COLOR,"Static",SS_OWNERDRAW | SS_NOTIFY |
+ SS_SUNKEN,183,33,18,14
+ EDITTEXT IDC_DIFFUSE_VALUE,140,52,32,14,ES_AUTOHSCROLL
+ CONTROL "",IDC_DIFFUSE_COLOR,"Static",SS_OWNERDRAW | SS_NOTIFY |
+ SS_SUNKEN,183,52,18,14
+ EDITTEXT IDC_SPECULAR_VALUE,140,73,32,14,ES_AUTOHSCROLL
+ CONTROL "",IDC_SPECULAR_COLOR,"Static",SS_OWNERDRAW | SS_NOTIFY |
+ SS_SUNKEN,183,73,18,14
+ EDITTEXT IDC_EMISSIVE_VALUE,140,92,32,14,ES_AUTOHSCROLL
+ CONTROL "",IDC_EMISSIVE_COLOR,"Static",SS_OWNERDRAW | SS_NOTIFY |
+ SS_SUNKEN,183,92,18,14
+ EDITTEXT IDC_BUMP_VALUE,140,112,32,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_POWER_VALUE,140,131,32,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_BRILLIANCE_VALUE,140,152,32,14,ES_AUTOHSCROLL
+ COMBOBOX IDC_BLEND_MODE,215,153,51,42,CBS_DROPDOWN | WS_VSCROLL |
+ WS_TABSTOP
+ PUSHBUTTON "...",IDC_FILE_DIFFUSE,296,52,15,14
+ PUSHBUTTON "...",IDC_FILE_SPECULAR,296,74,15,14
+ PUSHBUTTON "...",IDC_FILE_EMISSIVE,296,93,15,14
+ PUSHBUTTON "...",IDC_FILE_BUMP,296,112,15,14
+ GROUPBOX "Textures",IDC_STATIC,208,40,116,95
+ PUSHBUTTON "Select Polys",IDC_SELECT_POLYS,7,153,76,14
+ LTEXT "Shadow:",IDC_STATIC,273,141,29,8
+ COMBOBOX IDC_SHADOW,273,153,42,42,CBS_DROPDOWN | WS_VSCROLL |
+ WS_TABSTOP
+ EDITTEXT IDC_MATERIAL_SHADER,333,152,68,14,ES_AUTOHSCROLL
+ LTEXT "Shader:",IDC_STATIC,334,141,26,8
+END
+
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 2,0,0,1
+ PRODUCTVERSION 2,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "Comments", "\0"
+ VALUE "CompanyName", "\0"
+ VALUE "FileDescription", "Magic 3D Modeling Application\0"
+ VALUE "FileVersion", "2, 0, 0, 1\0"
+ VALUE "InternalName", "Magic\0"
+ VALUE "LegalCopyright", "Copyright © 2004\0"
+ VALUE "LegalTrademarks", "\0"
+ VALUE "OriginalFilename", "Magic.exe\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "Magic Application\0"
+ VALUE "ProductVersion", "2, 0, 0, 1\0"
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // !_MAC
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_ABOUTBOX, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 210
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 110
+ END
+
+ IDD_SURFACE_PROPS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 258
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 115
+ END
+
+ IDD_VERTS_REDUCE, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 168
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 64
+ END
+
+ IDD_MODIFY_POLY, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 261
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 168
+ END
+
+ IDD_CREATE_CUBE, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 300
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 110
+ END
+
+ IDD_CREATE_CYLINDER, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 302
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 138
+ END
+
+ IDD_CREATE_SPHERE, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 304
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 147
+ END
+
+ IDD_MODIFY_TEXTURE, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 331
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 208
+ END
+
+ IDD_MODIFY_POLYS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 293
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 154
+ END
+
+ IDD_CREATE_POLY, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 300
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 109
+ END
+
+ IDD_EXTRUSION_PROPS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 176
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 64
+ END
+
+ IDD_MODIFY_MATERIAL, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 421
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 191
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog Info
+//
+
+IDD_MODIFY_TEXTURE DLGINIT
+BEGIN
+ IDC_MAPPING, 0x403, 5, 0
+0x6f4e, 0x656e, "\000"
+ IDC_MAPPING, 0x403, 7, 0
+0x6c50, 0x6e61, 0x7261, "\000"
+ IDC_MAPPING, 0x403, 12, 0
+0x7943, 0x696c, 0x646e, 0x6972, 0x6163, 0x006c,
+ IDC_MAPPING, 0x403, 10, 0
+0x7053, 0x6568, 0x6972, 0x6163, 0x006c,
+ IDC_MAPPING, 0x403, 8, 0
+0x7453, 0x6572, 0x6374, 0x0068,
+ 0
+END
+
+IDD_CREATE_POLY DLGINIT
+BEGIN
+ IDC_NSIDES, 0x403, 2, 0
+0x0033,
+ IDC_NSIDES, 0x403, 2, 0
+0x0034,
+ 0
+END
+
+IDD_MODIFY_MATERIAL DLGINIT
+BEGIN
+ IDC_BLEND_MODE, 0x403, 6, 0
+0x6f53, 0x696c, 0x0064,
+ IDC_BLEND_MODE, 0x403, 6, 0
+0x6c41, 0x6870, 0x0061,
+ IDC_BLEND_MODE, 0x403, 9, 0
+0x6441, 0x6964, 0x6974, 0x6576, "\000"
+ IDC_SHADOW, 0x403, 8, 0
+0x6944, 0x6173, 0x6c62, 0x0065,
+ IDC_SHADOW, 0x403, 7, 0
+0x6e45, 0x6261, 0x656c, "\000"
+ 0
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE PRELOAD DISCARDABLE
+BEGIN
+ IDR_MAINFRAME "Magic 2.0\n\nMagic\nMagic Files (*.mag)\n.mag\nMagic.Document\nMagic Document"
+END
+
+STRINGTABLE PRELOAD DISCARDABLE
+BEGIN
+ AFX_IDS_APP_TITLE "Magic"
+ AFX_IDS_IDLEMESSAGE "Ready"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ ID_INDICATOR_EXT "EXT"
+ ID_INDICATOR_CAPS "CAP"
+ ID_INDICATOR_NUM "NUM"
+ ID_INDICATOR_SCRL "SCRL"
+ ID_INDICATOR_OVR "OVR"
+ ID_INDICATOR_REC "REC"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ ID_FILE_NEW "Create a new document\nNew"
+ ID_FILE_OPEN "Open an existing document\nOpen"
+ ID_FILE_CLOSE "Close the active document\nClose"
+ ID_FILE_SAVE "Save the active document\nSave"
+ ID_FILE_SAVE_AS "Save the active document with a new name\nSave As"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ ID_APP_ABOUT "Display program information, version number and copyright\nAbout"
+ ID_APP_EXIT "Quit the application; prompts to save documents\nExit"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ ID_FILE_MRU_FILE1 "Open this document"
+ ID_FILE_MRU_FILE2 "Open this document"
+ ID_FILE_MRU_FILE3 "Open this document"
+ ID_FILE_MRU_FILE4 "Open this document"
+ ID_FILE_MRU_FILE5 "Open this document"
+ ID_FILE_MRU_FILE6 "Open this document"
+ ID_FILE_MRU_FILE7 "Open this document"
+ ID_FILE_MRU_FILE8 "Open this document"
+ ID_FILE_MRU_FILE9 "Open this document"
+ ID_FILE_MRU_FILE10 "Open this document"
+ ID_FILE_MRU_FILE11 "Open this document"
+ ID_FILE_MRU_FILE12 "Open this document"
+ ID_FILE_MRU_FILE13 "Open this document"
+ ID_FILE_MRU_FILE14 "Open this document"
+ ID_FILE_MRU_FILE15 "Open this document"
+ ID_FILE_MRU_FILE16 "Open this document"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ ID_NEXT_PANE "Switch to the next window pane\nNext Pane"
+ ID_PREV_PANE "Switch back to the previous window pane\nPrevious Pane"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ ID_WINDOW_SPLIT "Split the active window into panes\nSplit"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ ID_EDIT_CLEAR "Erase the selection\nErase"
+ ID_EDIT_CLEAR_ALL "Erase everything\nErase All"
+ ID_EDIT_COPY "Copy the selection and put it on the Clipboard\nCopy"
+ ID_EDIT_CUT "Cut the selection and put it on the Clipboard\nCut"
+ ID_EDIT_FIND "Find the specified text\nFind"
+ ID_EDIT_PASTE "Insert Clipboard contents\nPaste"
+ ID_EDIT_REPEAT "Repeat the last action\nRepeat"
+ ID_EDIT_REPLACE "Replace specific text with different text\nReplace"
+ ID_EDIT_SELECT_ALL "Select the entire document\nSelect All"
+ ID_EDIT_UNDO "Undo the last action\nUndo"
+ ID_EDIT_REDO "Redo the previously undone action\nRedo"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ ID_VIEW_TOOLBAR "Show or hide the toolbar\nToggle ToolBar"
+ ID_VIEW_STATUS_BAR "Show or hide the status bar\nToggle StatusBar"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ AFX_IDS_SCSIZE "Change the window size"
+ AFX_IDS_SCMOVE "Change the window position"
+ AFX_IDS_SCMINIMIZE "Reduce the window to an icon"
+ AFX_IDS_SCMAXIMIZE "Enlarge the window to full size"
+ AFX_IDS_SCNEXTWINDOW "Switch to the next document window"
+ AFX_IDS_SCPREVWINDOW "Switch to the previous document window"
+ AFX_IDS_SCCLOSE "Close the active window and prompts to save the documents"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ AFX_IDS_SCRESTORE "Restore the window to normal size"
+ AFX_IDS_SCTASKLIST "Activate Task List"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ ID_VIEW_ZOOM_IN "Increase the magnification\nZoom In"
+ ID_VIEW_ZOOM_OUT "Decrease the magnification\nZoom Out"
+ ID_VIEW_ZOOM_NORMAL "Set the magnification to 100%\nZoom Normal"
+ ID_VIEW_CENTER "Center the view on the world origin\nCenter View"
+ ID_SURFACE_CREATE "Modify surface parameters or create a new surface\nModify Surface"
+ ID_MODIFY_DRAG_VERTS "Drag the selected verts using the mouse\nDrag Verts"
+ ID_CREATE_CUBE "Create a cube primitive\nCreate Cube"
+ ID_CREATE_TETRA "Create a tetrahedron primitive\nCreate Pyramid"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ ID_MODIFY_EXTRUDE "Extrude new polygons from the current selection\nExtrude"
+ ID_MODIFY_BEVEL "Extrude a bevel from the current selection\nBevel"
+ ID_MODIFY_STUD "Extrude a stud from the current selection\nStud"
+ ID_MODIFY_SUBDIVIDE "Subdivide all selected polys\nSubdivide"
+ ID_PROP_SURFACE "Display surface properties dialog\nSurface Properties"
+ ID_PROP_CAMERA "Display camera properties dialog\nCamera Properties"
+ ID_EDIT_FLIP_NORMAL "Reverse the direction of the normal for the current polygon\nFlip Normal"
+ ID_EDIT_REMOVE_POLY "Remove the currently selected polys\nRemove Poly"
+ ID_EDIT_MERGE_POLY "Merge the selected polys into a single poly\nMerge Polys"
+ ID_MODIFY_TEXTURE "Apply a texture to the current selection\nTexture"
+ ID_OPTIONS_VERTEX_NORMALS
+ "Show the normal vector for each selected vertex\nVertex Normals"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ ID_PROP_GRID "Modify the grid properties\nGrid Properties"
+ ID_EDIT_DELETE "Delete the currently selected polys and verts\nDelete"
+ ID_CREATE_POLY "Create a new poly\nCreate Poly"
+ ID_CREATE_VERT "Create a new vert\nCreate Vert"
+ ID_CREATE_CYLINDER "Create a cylinder primitive\nCreate Cylinder"
+ ID_CREATE_SPHERE "Create a sphere primitive\nCreate Sphere"
+ ID_FILE_IMPORT "Import a surface from a MAG file\nFile Import"
+ ID_FILE_EXPORT "Export the selected surface into a MAG file\nFile Export"
+ ID_MODIFY_POLY "Modify selected polys\nModify Polygon"
+ ID_MODIFY_VERTS "Modify selected verts\nModify Verts"
+ ID_VERTS_REDUCE "Combine nearby verts\nReduce Verts"
+ ID_VERTS_SNAP "Snap selected verts to grid points\nSnap Verts"
+ ID_MODIFY_SCALE_VERTS "Scale the current selection by moving the mouse\nScale Verts"
+ ID_MODIFY_ROTATE "Rotate the current selection about its centroid\nRotate Selection"
+ ID_CREATE_CONE "Create a conic primitive\nCreate Cone"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ ID_MODIFY_SCALE_X "Scale the current selection along the X axis\nScale X"
+ ID_MODIFY_SCALE_Z "Scale the current selection along the Z axis\nScale Z"
+ ID_MODIFY_SCALE_Y "Scale the current selection along the Y axis\nScale Y"
+ ID_MODIFY_SPLIT1 "Split all selected polys\nSplit 1"
+ ID_MODIFY_SPLIT2 "Split all selected polys\nSplit 2"
+ ID_EDIT_MIRROR "Mirror selected polys from left to right\nMirror Selection"
+ ID_EDIT_SEAL "Create a polygon connecting the selected vertices\nSeal"
+ ID_EDIT_CLONE "Clone the selection\tClone"
+ ID_GRID_SNAP "Toggle Grid Snap\tGrid Snap"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ ID_EDIT_SELECT_NONE "Drop the entire selection\nSelect None"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ ID_VIEW_BACK_COLOR "Select a background color for the current view\nBack Color"
+ ID_EDIT_SELECT_INVERSE "Drop selected polys and select unselected polys\nSelect Inverse"
+ ID_MODIFY_UV_MAP "Edit UV coords for selected polys\nUV Mapping"
+ ID_VIEW_SHADOWS "Enable or disable volume shadow rendering"
+ ID_VIEW_BUMPMAPS "Enable or disable bumpmapping"
+ ID_VIEW_VERTEXSHADER "Enable or disable vertex shaders"
+ ID_VIEW_PIXELSHADER "Enable or disable pixel shader"
+ ID_SURFACE_OPTIMIZE "Optimize the mesh by combining similar vertices.\nOptimize Mesh"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ ID_SURFACE_EXPLODE "Explode the mesh by creating distinct vertices for each poly.\nExplode Mesh"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#define _AFX_NO_SPLITTER_RESOURCES
+#define _AFX_NO_OLE_RESOURCES
+#define _AFX_NO_TRACKER_RESOURCES
+#define _AFX_NO_PROPERTY_RESOURCES
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE 9, 1
+#pragma code_page(1252)
+#endif //_WIN32
+#include "res\Magic.rc2" // non-Microsoft Visual C++ edited resources
+#include "afxres.rc" // Standard components
+#include "afxprint.rc" // printing/print preview resources
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Magic2/Magic.vcxproj b/Magic2/Magic.vcxproj
new file mode 100644
index 0000000..7824182
--- /dev/null
+++ b/Magic2/Magic.vcxproj
@@ -0,0 +1,277 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <SccProjectName />
+ <SccLocalPath />
+ <Keyword>MFCProj</Keyword>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseOfMfc>Dynamic</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseOfMfc>Dynamic</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>.\Debug\</OutDir>
+ <IntDir>.\Debug\</IntDir>
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>.\Release\</OutDir>
+ <IntDir>.\Release\</IntDir>
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+ <FunctionLevelLinking>false</FunctionLevelLinking>
+ <Optimization>Disabled</Optimization>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <WarningLevel>Level3</WarningLevel>
+ <MinimalRebuild>true</MinimalRebuild>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <AdditionalIncludeDirectories>../Opcode/OpcodeLib;../libpng;../Magic2;../FoundationEx;../nGenEx;../zlib;../parser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_DEBUG;WIN32;_WINDOWS;FOUNDATION_USE_MFC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AssemblerListingLocation>.\Debug\</AssemblerListingLocation>
+ <BrowseInformation>true</BrowseInformation>
+ <PrecompiledHeaderOutputFile>.\Debug\Magic.pch</PrecompiledHeaderOutputFile>
+ <ObjectFileName>.\Debug\</ObjectFileName>
+ <ProgramDataBaseFileName>.\Debug\</ProgramDataBaseFileName>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ </ClCompile>
+ <Midl>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <TypeLibraryName>.\Debug\Magic.tlb</TypeLibraryName>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ </Midl>
+ <ResourceCompile>
+ <Culture>0x0409</Culture>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug\Magic.bsc</OutputFile>
+ </Bscmake>
+ <Link>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <IgnoreSpecificDefaultLibraries>LIBCMT;LIBCD;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
+ <OutputFile>.\Debug\Magic.exe</OutputFile>
+ <AdditionalDependencies>..\Opcode\OpcodeLib\Debug\OpcodeLib.lib;..\zlib\debug\zlib.lib;..\libpng\debug\libpng.lib;wsock32.lib;dinput.lib;dsound.lib;d3d9.lib;d3dx9.lib;dxguid.lib;winmm.lib;version.lib;ogg_static.lib;vorbis_static.lib;vorbisfile_static.lib;vfw32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <StringPooling>true</StringPooling>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <Optimization>MaxSpeed</Optimization>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <WarningLevel>Level3</WarningLevel>
+ <AdditionalIncludeDirectories>../pnglib;../Opcode/OpcodeLib;../libpng;../Magic2;../FoundationEx;../nGenEx;../zlib;../parser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>NDEBUG;WIN32;_WINDOWS;FOUNDATION_USE_MFC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AssemblerListingLocation>.\Release\</AssemblerListingLocation>
+ <PrecompiledHeaderOutputFile>.\Release\Magic.pch</PrecompiledHeaderOutputFile>
+ <ObjectFileName>.\Release\</ObjectFileName>
+ <ProgramDataBaseFileName>.\Release\</ProgramDataBaseFileName>
+ </ClCompile>
+ <Midl>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <TypeLibraryName>.\Release\Magic.tlb</TypeLibraryName>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ </Midl>
+ <ResourceCompile>
+ <Culture>0x0409</Culture>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release\Magic.bsc</OutputFile>
+ </Bscmake>
+ <Link>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <SubSystem>Windows</SubSystem>
+ <IgnoreSpecificDefaultLibraries>LIBC;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
+ <OutputFile>.\Release\Magic.exe</OutputFile>
+ <AdditionalDependencies>..\Opcode\OpcodeLib\Release\OpcodeLib.lib;..\ngenex\release\ngenex.lib;..\zlib\release\zlib.lib;..\libpng\release\libpng.lib;wsock32.lib;dinput.lib;dsound.lib;d3d9.lib;d3dx9.lib;dxguid.lib;winmm.lib;version.lib;ogg_static.lib;vorbis_static.lib;vorbisfile_static.lib;vfw32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\nGenEx\ActiveWindow.cpp" />
+ <ClCompile Include="AlphaInverse.cpp" />
+ <ClCompile Include="AlphaPalette.cpp" />
+ <ClCompile Include="..\nGenEx\Archive.cpp" />
+ <ClCompile Include="..\FoundationEx\ArrayList.cpp" />
+ <ClCompile Include="..\nGenEx\AviFile.cpp" />
+ <ClCompile Include="..\nGenEx\Bitmap.cpp" />
+ <ClCompile Include="..\nGenEx\Bmp.cpp" />
+ <ClCompile Include="..\nGenEx\Bolt.cpp" />
+ <ClCompile Include="..\nGenEx\Button.cpp" />
+ <ClCompile Include="..\nGenEx\Camera.cpp" />
+ <ClCompile Include="..\nGenEx\CameraView.cpp" />
+ <ClCompile Include="..\nGenEx\Color.cpp" />
+ <ClCompile Include="..\nGenEx\ComboBox.cpp" />
+ <ClCompile Include="..\nGenEx\ComboList.cpp" />
+ <ClCompile Include="Command.cpp" />
+ <ClCompile Include="..\nGenEx\D3DXImage.cpp" />
+ <ClCompile Include="..\nGenEx\DataLoader.cpp" />
+ <ClCompile Include="..\nGenEx\EditBox.cpp" />
+ <ClCompile Include="Editor.cpp" />
+ <ClCompile Include="..\nGenEx\Encrypt.cpp" />
+ <ClCompile Include="..\nGenEx\EventDispatch.cpp" />
+ <ClCompile Include="..\nGenEx\FadeView.cpp" />
+ <ClCompile Include="..\nGenEx\Fix.cpp" />
+ <ClCompile Include="..\nGenEx\Font.cpp" />
+ <ClCompile Include="..\nGenEx\FontMgr.cpp" />
+ <ClCompile Include="..\nGenEx\FormatUtil.cpp" />
+ <ClCompile Include="..\nGenEx\FormDef.cpp" />
+ <ClCompile Include="..\nGenEx\FormWindow.cpp" />
+ <ClCompile Include="..\nGenEx\Game.cpp" />
+ <ClCompile Include="..\nGenEx\Geometry.cpp" />
+ <ClCompile Include="..\nGenEx\Graphic.cpp" />
+ <ClCompile Include="Grid.cpp" />
+ <ClCompile Include="GridProps.cpp" />
+ <ClCompile Include="..\nGenEx\ImageBox.cpp" />
+ <ClCompile Include="..\nGenEx\ImgView.cpp" />
+ <ClCompile Include="..\nGenEx\Joystick.cpp" />
+ <ClCompile Include="..\nGenEx\Keyboard.cpp" />
+ <ClCompile Include="l3ds.cpp" />
+ <ClCompile Include="..\nGenEx\Layout.cpp" />
+ <ClCompile Include="..\nGenEx\Light.cpp" />
+ <ClCompile Include="..\nGenEx\ListBox.cpp" />
+ <ClCompile Include="..\nGenEx\MachineInfo.cpp" />
+ <ClCompile Include="Magic.cpp" />
+ <ClCompile Include="MagicDoc.cpp" />
+ <ClCompile Include="MagicView.cpp" />
+ <ClCompile Include="MainFrm.cpp" />
+ <ClCompile Include="MaterialDialog.cpp" />
+ <ClCompile Include="..\nGenEx\MCIWave.cpp" />
+ <ClCompile Include="..\nGenEx\Menu.cpp" />
+ <ClCompile Include="ModelFile3DS.cpp" />
+ <ClCompile Include="ModelFileMAG.cpp" />
+ <ClCompile Include="ModelFileOBJ.cpp" />
+ <ClCompile Include="ModelView.cpp" />
+ <ClCompile Include="..\nGenEx\Mouse.cpp" />
+ <ClCompile Include="..\nGenEx\MouseController.cpp" />
+ <ClCompile Include="..\nGenEx\MultiController.cpp" />
+ <ClCompile Include="..\Parser\Parser.cpp" />
+ <ClCompile Include="..\nGenEx\ParseUtil.cpp" />
+ <ClCompile Include="..\nGenEx\Particles.cpp" />
+ <ClCompile Include="..\nGenEx\PCX.CPP" />
+ <ClCompile Include="..\nGenEx\Physical.cpp" />
+ <ClCompile Include="..\nGenEx\PngImage.cpp" />
+ <ClCompile Include="..\nGenEx\Polygon.cpp" />
+ <ClCompile Include="..\nGenEx\Projector.cpp" />
+ <ClCompile Include="..\nGenEx\Random.cpp" />
+ <ClCompile Include="..\Parser\Reader.cpp" />
+ <ClCompile Include="..\nGenEx\Res.cpp" />
+ <ClCompile Include="..\nGenEx\RichTextBox.cpp" />
+ <ClCompile Include="..\nGenEx\Scene.cpp" />
+ <ClCompile Include="..\nGenEx\Screen.cpp" />
+ <ClCompile Include="..\nGenEx\ScrollWindow.cpp" />
+ <ClCompile Include="Selection.cpp" />
+ <ClCompile Include="Selector.cpp" />
+ <ClCompile Include="..\nGenEx\Sha1.cpp" />
+ <ClCompile Include="..\nGenEx\Shadow.cpp" />
+ <ClCompile Include="..\nGenEx\Slider.cpp" />
+ <ClCompile Include="..\nGenEx\Solid.cpp" />
+ <ClCompile Include="..\nGenEx\Sound.cpp" />
+ <ClCompile Include="..\nGenEx\SoundCard.cpp" />
+ <ClCompile Include="..\nGenEx\SoundD3D.cpp" />
+ <ClCompile Include="..\nGenEx\Sprite.cpp" />
+ <ClCompile Include="StdAfx.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">stdafx.h</PrecompiledHeaderFile>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">stdafx.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <ClCompile Include="SurfacePropertiesDialog.cpp" />
+ <ClCompile Include="..\Parser\Term.cpp" />
+ <ClCompile Include="..\nGenEx\TexDX9.cpp" />
+ <ClCompile Include="..\FoundationEx\Text.cpp" />
+ <ClCompile Include="TextureMapDialog.cpp" />
+ <ClCompile Include="Thumbnail.cpp" />
+ <ClCompile Include="..\Parser\Token.cpp" />
+ <ClCompile Include="UVMapView.cpp" />
+ <ClCompile Include="..\nGenEx\Video.cpp" />
+ <ClCompile Include="..\nGenEx\VideoDX9.cpp" />
+ <ClCompile Include="..\nGenEx\VideoDX9Enum.cpp" />
+ <ClCompile Include="..\nGenEx\VideoDX9VertexBuffer.cpp" />
+ <ClCompile Include="..\nGenEx\VideoFactory.cpp" />
+ <ClCompile Include="..\nGenEx\VideoSettings.cpp" />
+ <ClCompile Include="..\nGenEx\Window.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="Magic.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\nGenEx\AviFile.h" />
+ <ClInclude Include="Command.h" />
+ <ClInclude Include="Editor.h" />
+ <ClInclude Include="Grid.h" />
+ <ClInclude Include="GridProps.h" />
+ <ClInclude Include="l3ds.h" />
+ <ClInclude Include="Magic.h" />
+ <ClInclude Include="MagicDoc.h" />
+ <ClInclude Include="MagicView.h" />
+ <ClInclude Include="MainFrm.h" />
+ <ClInclude Include="MaterialDialog.h" />
+ <ClInclude Include="ModelFile3DS.h" />
+ <ClInclude Include="ModelFileMAG.h" />
+ <ClInclude Include="ModelFileOBJ.h" />
+ <ClInclude Include="ModelView.h" />
+ <ClInclude Include="Resource.h" />
+ <ClInclude Include="Selection.h" />
+ <ClInclude Include="Selector.h" />
+ <ClInclude Include="StdAfx.h" />
+ <ClInclude Include="SurfacePropertiesDialog.h" />
+ <ClInclude Include="TextureMapDialog.h" />
+ <ClInclude Include="Thumbnail.h" />
+ <ClInclude Include="UVMapView.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="res\Magic.ico" />
+ <CustomBuild Include="res\Magic.rc2">
+ <FileType>RC</FileType>
+ </CustomBuild>
+ <CustomBuild Include="res\MagicDoc.ico" />
+ <CustomBuild Include="res\Toolbar.bmp" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Magic2/Magic.vcxproj.filters b/Magic2/Magic.vcxproj.filters
new file mode 100644
index 0000000..70aae0d
--- /dev/null
+++ b/Magic2/Magic.vcxproj.filters
@@ -0,0 +1,403 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{c28b9293-6762-4549-bb14-4620a2ccd434}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{8142f85d-610a-4186-93af-8db06e35939f}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{83051df7-cc8f-4151-b8c0-7085cfa957c3}</UniqueIdentifier>
+ <Extensions>ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\nGenEx\ActiveWindow.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="AlphaInverse.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="AlphaPalette.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Archive.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\FoundationEx\ArrayList.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\AviFile.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Bitmap.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Bmp.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Bolt.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Button.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Camera.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\CameraView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Color.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\ComboBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\ComboList.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Command.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\D3DXImage.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\DataLoader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\EditBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Editor.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Encrypt.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\EventDispatch.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\FadeView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Fix.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Font.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\FontMgr.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\FormatUtil.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\FormDef.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\FormWindow.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Game.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Geometry.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Graphic.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Grid.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="GridProps.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\ImageBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\ImgView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Joystick.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Keyboard.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="l3ds.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Layout.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Light.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\ListBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\MachineInfo.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Magic.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MagicDoc.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MagicView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MainFrm.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MaterialDialog.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\MCIWave.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Menu.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ModelFile3DS.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ModelFileMAG.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ModelFileOBJ.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ModelView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Mouse.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\MouseController.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\MultiController.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Parser\Parser.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\ParseUtil.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Particles.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\PCX.CPP">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Physical.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\PngImage.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Polygon.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Projector.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Random.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Parser\Reader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Res.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\RichTextBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Scene.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Screen.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\ScrollWindow.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Selection.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Selector.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Sha1.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Shadow.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Slider.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Solid.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Sound.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\SoundCard.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\SoundD3D.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Sprite.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="StdAfx.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SurfacePropertiesDialog.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Parser\Term.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\TexDX9.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\FoundationEx\Text.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TextureMapDialog.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Thumbnail.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Parser\Token.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="UVMapView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Video.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\VideoDX9.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\VideoDX9Enum.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\VideoDX9VertexBuffer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\VideoFactory.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\VideoSettings.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Window.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="Magic.rc">
+ <Filter>Source Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\nGenEx\AviFile.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Command.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Editor.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Grid.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="GridProps.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="l3ds.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Magic.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MagicDoc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MagicView.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MainFrm.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MaterialDialog.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ModelFile3DS.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ModelFileMAG.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ModelFileOBJ.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ModelView.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Selection.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Selector.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="StdAfx.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SurfacePropertiesDialog.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="TextureMapDialog.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Thumbnail.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="UVMapView.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="res\Magic.ico">
+ <Filter>Resource Files</Filter>
+ </CustomBuild>
+ <CustomBuild Include="res\Magic.rc2">
+ <Filter>Resource Files</Filter>
+ </CustomBuild>
+ <CustomBuild Include="res\MagicDoc.ico">
+ <Filter>Resource Files</Filter>
+ </CustomBuild>
+ <CustomBuild Include="res\Toolbar.bmp">
+ <Filter>Resource Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Magic2/MagicDoc.cpp b/Magic2/MagicDoc.cpp
new file mode 100644
index 0000000..640fa41
--- /dev/null
+++ b/Magic2/MagicDoc.cpp
@@ -0,0 +1,615 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: MagicDoc.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Implementation of the MagicDoc class
+*/
+
+#include "stdafx.h"
+#include "Magic.h"
+
+#include "MagicDoc.h"
+#include "ModelFileMAG.h"
+#include "ModelFileOBJ.h"
+#include "ModelFile3DS.h"
+#include "Selection.h"
+#include "Selector.h"
+#include "Editor.h"
+#include "Command.h"
+
+#include "Bitmap.h"
+#include "Color.h"
+#include "D3DXImage.h"
+#include "Geometry.h"
+#include "Pcx.h"
+#include "Polygon.h"
+#include "Solid.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+// +--------------------------------------------------------------------+
+
+IMPLEMENT_DYNCREATE(MagicDoc, CDocument)
+
+BEGIN_MESSAGE_MAP(MagicDoc, CDocument)
+ //{{AFX_MSG_MAP(MagicDoc)
+ ON_COMMAND(ID_SURFACE_OPTIMIZE, OnSurfaceOptimize)
+ ON_COMMAND(ID_SURFACE_EXPLODE, OnSurfaceExplode)
+ ON_UPDATE_COMMAND_UI(ID_SURFACE_OPTIMIZE, OnUpdateSurfaceOptimize)
+ ON_UPDATE_COMMAND_UI(ID_SURFACE_EXPLODE, OnUpdateSurfaceExplode)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+// +--------------------------------------------------------------------+
+
+MagicDoc::MagicDoc()
+ : solid(0), selection(0)
+{
+ solid = new Solid;
+ selection = new Selection;
+ selector = new Selector(selection);
+ editor = new Editor(this);
+}
+
+MagicDoc::~MagicDoc()
+{
+ if (editor) delete editor;
+ if (selector) delete selector;
+ if (selection) delete selection;
+ if (solid) delete solid;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MagicDoc::InitCommandStack()
+{
+ nundo = 0;
+ commands.destroy();
+}
+
+void
+MagicDoc::Exec(Command* command)
+{
+ int nredo = commands.size() - nundo;
+
+ while (nredo) {
+ delete commands.removeIndex(commands.size()-1);
+ nredo--;
+ }
+
+ if (nundo < 100) {
+ nundo++;
+ }
+ else {
+ delete commands.removeIndex(0);
+ }
+
+ command->Do();
+ commands.append(command);
+}
+
+int
+MagicDoc::NumUndo() const
+{
+ return nundo;
+}
+
+int
+MagicDoc::NumRedo() const
+{
+ return commands.size() - nundo;
+}
+
+const char*
+MagicDoc::GetUndoName() const
+{
+ if (nundo > 0 && nundo <= commands.size())
+ return commands[nundo-1]->Name();
+ else
+ return "";
+}
+
+const char*
+MagicDoc::GetRedoName() const
+{
+ if (nundo >= 0 && nundo < commands.size())
+ return commands[nundo]->Name();
+ else
+ return "";
+}
+
+void
+MagicDoc::Undo()
+{
+ if (nundo > 0 && nundo <= commands.size())
+ commands[--nundo]->Undo();
+}
+
+void
+MagicDoc::Redo()
+{
+ if (nundo >= 0 && nundo < commands.size())
+ commands[nundo++]->Do();
+}
+
+// +--------------------------------------------------------------------+
+
+BOOL MagicDoc::OnNewDocument()
+{
+ if (!CDocument::OnNewDocument())
+ return FALSE;
+
+ InitCommandStack();
+
+ if (solid) delete solid;
+ solid = new Solid;
+
+ if (selection)
+ selection->Clear();
+
+ return TRUE;
+}
+
+// +--------------------------------------------------------------------+
+
+void MagicDoc::Serialize(CArchive& ar)
+{
+}
+
+// +--------------------------------------------------------------------+
+
+#ifdef _DEBUG
+void MagicDoc::AssertValid() const
+{
+ CDocument::AssertValid();
+}
+
+void MagicDoc::Dump(CDumpContext& dc) const
+{
+ CDocument::Dump(dc);
+}
+#endif //_DEBUG
+
+// +--------------------------------------------------------------------+
+
+BOOL MagicDoc::OnSaveDocument(LPCTSTR path_name)
+{
+ SetModifiedFlag(FALSE);
+
+ ModelFileMAG mod_file(path_name);
+ mod_file.Save(solid->GetModel());
+
+ SetModifiedFlag(FALSE);
+ UpdateAllViews(NULL);
+ return TRUE;
+}
+
+BOOL MagicDoc::OnOpenDocument(LPCTSTR path_name)
+{
+ FILE* fp = fopen(path_name, "rb");
+ if (!fp) {
+ ::MessageBox(0, "Open Failed: could not open file", "ERROR", MB_OK);
+ return FALSE;
+ }
+
+ int version = 1;
+ char file_id[5];
+ fread(file_id, 4, 1, fp);
+ file_id[4] = '\0';
+ fclose(fp);
+
+ if (strncmp(file_id, "MAG", 3)) {
+ ::MessageBox(0, "Open Failed: Invalid file type", "ERROR", MB_OK);
+ return FALSE;
+ }
+
+ switch (file_id[3]) {
+ case '6': version = 6; break;
+ case '5': version = 5; break;
+ default: version = 0; break;
+ }
+
+ if (version < 5 || version > 6) {
+ ::MessageBox(0, "Open Failed: Unsupported version", "ERROR", MB_OK);
+ return FALSE;
+ }
+
+ DeleteContents();
+
+ ModelFileMAG mod_file(path_name);
+ solid->Load(&mod_file);
+ solid->CreateShadows();
+
+ SetModifiedFlag(FALSE);
+ UpdateAllViews(NULL);
+ return TRUE;
+}
+
+bool
+MagicDoc::ImportFile(LPCTSTR path_name)
+{
+ if (strstr(path_name, ".obj") || strstr(path_name, ".OBJ")) {
+ ModelFileOBJ obj_file(path_name);
+
+ if (solid->GetModel()) {
+ Solid* s = new Solid;
+
+ if (s->Load(&obj_file)) {
+ // todo: insert command here
+ Model* orig = solid->GetModel();
+ Model* imported = s->GetModel();
+
+ orig->GetMaterials().append(imported->GetMaterials());
+ orig->GetSurfaces().append(imported->GetSurfaces());
+ orig->OptimizeMaterials();
+
+ imported->GetMaterials().clear();
+ imported->GetSurfaces().clear();
+
+ SetModifiedFlag(FALSE);
+ UpdateAllViews(NULL);
+ delete s;
+ return true;
+ }
+
+ delete s;
+ }
+ else {
+ if (solid->Load(&obj_file)) {
+ SetModifiedFlag(FALSE);
+ UpdateAllViews(NULL);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (strstr(path_name, ".3ds") || strstr(path_name, ".3DS")) {
+ ModelFile3DS model_file(path_name);
+
+ if (solid->GetModel()) {
+ Solid* s = new Solid;
+
+ if (s->Load(&model_file)) {
+ // todo: insert command here
+ Model* orig = solid->GetModel();
+ Model* imported = s->GetModel();
+
+ orig->GetMaterials().append(imported->GetMaterials());
+ orig->GetSurfaces().append(imported->GetSurfaces());
+ orig->OptimizeMaterials();
+
+ imported->GetMaterials().clear();
+ imported->GetSurfaces().clear();
+
+ SetModifiedFlag(FALSE);
+ UpdateAllViews(NULL);
+ delete s;
+ return true;
+ }
+
+ delete s;
+ }
+ else {
+ if (solid->Load(&model_file)) {
+ SetModifiedFlag(FALSE);
+ UpdateAllViews(NULL);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ FILE* fp = fopen(path_name, "rb");
+ if (!fp) {
+ ::MessageBox(0, "Import Failed: could not open file", "ERROR", MB_OK);
+ return false;
+ }
+
+ int version = 1;
+ char file_id[5];
+ fread(file_id, 4, 1, fp);
+ file_id[4] = '\0';
+ fclose(fp);
+
+ if (strncmp(file_id, "MAG", 3)) {
+ ::MessageBox(0, "Open Failed: Invalid file type", "ERROR", MB_OK);
+ return false;
+ }
+
+ switch (file_id[3]) {
+ case '6': version = 6; break;
+ case '5': version = 5; break;
+ default: version = 0; break;
+ }
+
+ if (version < 5 || version > 6) {
+ ::MessageBox(0, "Open Failed: Unsupported version", "ERROR", MB_OK);
+ return false;
+ }
+
+ ModelFileMAG mag_file(path_name);
+
+ if (solid->GetModel()) {
+ Solid* s = new Solid;
+ if (s->Load(&mag_file)) {
+ // todo: insert command here
+ Model* orig = solid->GetModel();
+ Model* imported = s->GetModel();
+
+ orig->GetMaterials().append(imported->GetMaterials());
+ orig->GetSurfaces().append(imported->GetSurfaces());
+ orig->OptimizeMaterials();
+
+ imported->GetMaterials().clear();
+ imported->GetSurfaces().clear();
+
+ SetModifiedFlag(FALSE);
+ UpdateAllViews(NULL);
+ delete s;
+ return true;
+ }
+
+ delete s;
+ }
+ else {
+ InitCommandStack();
+
+ if (solid->Load(&mag_file)) {
+ SetModifiedFlag(FALSE);
+ UpdateAllViews(NULL);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+MagicDoc::ExportFile(LPCTSTR path_name)
+{
+ if (!solid->GetModel())
+ return false;
+
+ if (strstr(path_name, ".obj") || strstr(path_name, ".OBJ")) {
+ ModelFileOBJ obj_file(path_name);
+ obj_file.Save(solid->GetModel());
+ return true;
+ }
+
+ if (strstr(path_name, ".3ds") || strstr(path_name, ".3DS")) {
+ return false;
+ }
+
+ if (strstr(path_name, ".mag") || strstr(path_name, ".MAG")) {
+ ModelFileMAG mod_file(path_name);
+ mod_file.Save(solid->GetModel());
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+int LoadBuffer(const char* filename, BYTE*& buf, bool null_terminate)
+{
+ buf = 0;
+
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ ::fseek(f, 0, SEEK_END);
+ int len = ftell(f);
+ ::fseek(f, 0, SEEK_SET);
+
+ if (null_terminate) {
+ buf = new BYTE[len+1];
+ if (buf)
+ buf[len] = 0;
+ }
+
+ else {
+ buf = new BYTE[len];
+ }
+
+ if (buf)
+ ::fread(buf, len, 1, f);
+
+ ::fclose(f);
+
+ return len;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void MagicDoc::DeleteContents()
+{
+ CDocument::DeleteContents();
+ InitCommandStack();
+
+ if (solid) {
+ delete solid;
+ solid = new Solid;
+ }
+
+ if (selection)
+ selection->Clear();
+}
+
+// +--------------------------------------------------------------------+
+
+int LoadTexture(const char* fname, Bitmap*& bitmap, int type)
+{
+ int result = 0;
+
+ if (!fname || !*fname)
+ return result;
+
+ bitmap = Bitmap::CheckCache(fname);
+
+ if (!bitmap) {
+ bool pcx_file = strstr(fname, ".pcx") || strstr(fname, ".PCX");
+
+ // handle PCX formats:
+ if (pcx_file) {
+ PcxImage pcx;
+
+ if (pcx.Load((char*) fname) == PCX_OK) {
+ bitmap = new Bitmap;
+
+ // 32-bit image
+ if (pcx.himap) {
+ bitmap->CopyHighColorImage(pcx.width, pcx.height, pcx.himap);
+ }
+
+ // 8-bit image, check for 32-bit image as well
+ else if (pcx.bitmap) {
+ bitmap->CopyImage(pcx.width, pcx.height, pcx.bitmap);
+
+ char tmp[256];
+ int len = strlen(fname);
+ bool found = false;
+
+ ZeroMemory(tmp, sizeof(tmp));
+
+ for (int i = 0; i < len && !found; i++) {
+ if (strstr(fname + i, ".pcx") == (fname+i)) {
+ found = true;
+ }
+ else {
+ tmp[i] = fname[i];
+ }
+ }
+
+ if (found) {
+ strcat(tmp, "+.pcx");
+ if (pcx.Load(tmp) == PCX_OK && pcx.himap != 0) {
+ bitmap->CopyHighColorImage(pcx.width, pcx.height, pcx.himap);
+ }
+ }
+ }
+ }
+ }
+
+ // for all other formats, use D3DX:
+ else {
+ D3DXImage d3dx;
+ if (d3dx.Load((char*) fname)) {
+ bitmap = new Bitmap;
+ bitmap->CopyHighColorImage(d3dx.width, d3dx.height, d3dx.image);
+ }
+ }
+
+ if (bitmap) {
+ LoadAlpha(fname, *bitmap, type);
+
+ bitmap->SetFilename(fname);
+ bitmap->SetType(type);
+ bitmap->MakeTexture();
+
+ Bitmap::AddToCache(bitmap);
+ }
+ }
+
+ return result;
+}
+
+int LoadAlpha(const char* name, Bitmap& bitmap, int type)
+{
+ PcxImage pcx;
+ D3DXImage d3dx;
+ bool pcx_file = strstr(name, ".pcx") || strstr(name, ".PCX");
+ bool bmp_file = strstr(name, ".bmp") || strstr(name, ".BMP");
+ bool jpg_file = strstr(name, ".jpg") || strstr(name, ".JPG");
+ bool png_file = strstr(name, ".png") || strstr(name, ".PNG");
+ bool tga_file = strstr(name, ".tga") || strstr(name, ".TGA");
+
+ // check for an associated alpha-only (grayscale) bitmap:
+ char filename[256];
+ strcpy(filename, name);
+
+ char* dot = strrchr(filename, '.');
+ if (dot && pcx_file)
+ strcpy(dot, "@.pcx");
+ else if (dot && bmp_file)
+ strcpy(dot, "@.bmp");
+ else if (dot && jpg_file)
+ strcpy(dot, "@.jpg");
+ else if (dot && png_file)
+ strcpy(dot, "@.png");
+ else if (dot && tga_file)
+ strcpy(dot, "@.tga");
+ else
+ return 0;
+
+ // first try to load from current directory:
+ bool loaded = false;
+
+ if (pcx_file)
+ loaded = pcx.Load(filename) == PCX_OK;
+
+ else
+ loaded = d3dx.Load(filename);
+
+ // now copy the alpha values into the bitmap:
+ if (loaded) {
+ if (pcx_file && pcx.bitmap) {
+ bitmap.CopyAlphaImage(pcx.width, pcx.height, pcx.bitmap);
+ }
+ else if (pcx_file && pcx.himap) {
+ bitmap.CopyAlphaRedChannel(pcx.width, pcx.height, pcx.himap);
+ }
+ else if (d3dx.image) {
+ bitmap.CopyAlphaRedChannel(d3dx.width, d3dx.height, d3dx.image);
+ }
+ }
+
+ return 0;
+}
+
+
+void MagicDoc::OnSurfaceOptimize()
+{
+ if (solid && solid->GetModel()) {
+ solid->GetModel()->OptimizeMesh();
+ solid->InvalidateSurfaceData();
+ solid->InvalidateSegmentData();
+ }
+}
+
+void MagicDoc::OnUpdateSurfaceOptimize(CCmdUI* pCmdUI)
+{
+ pCmdUI->Enable(solid && solid->GetModel());
+}
+
+void MagicDoc::OnSurfaceExplode()
+{
+ if (solid && solid->GetModel()) {
+ solid->GetModel()->ExplodeMesh();
+ solid->InvalidateSurfaceData();
+ solid->InvalidateSegmentData();
+ }
+}
+
+void MagicDoc::OnUpdateSurfaceExplode(CCmdUI* pCmdUI)
+{
+ pCmdUI->Enable(solid && solid->GetModel());
+}
diff --git a/Magic2/MagicDoc.h b/Magic2/MagicDoc.h
new file mode 100644
index 0000000..489f5dc
--- /dev/null
+++ b/Magic2/MagicDoc.h
@@ -0,0 +1,120 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: MagicDoc.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Interface of the MagicDoc class
+*/
+
+
+#if !defined(AFX_MAGICDOC_H__8B8D63A3_30F9_4023_BFA8_DB79891487C2__INCLUDED_)
+#define AFX_MAGICDOC_H__8B8D63A3_30F9_4023_BFA8_DB79891487C2__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+// +--------------------------------------------------------------------+
+
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+class Command;
+class Editor;
+class Solid;
+class Model;
+class Surface;
+class Segment;
+class Selection;
+class Selector;
+
+// +--------------------------------------------------------------------+
+
+class MagicDoc : public CDocument
+{
+protected: // create from serialization only
+ MagicDoc();
+ DECLARE_DYNCREATE(MagicDoc)
+
+// Attributes
+public:
+
+// Operations
+public:
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(MagicDoc)
+ public:
+ virtual BOOL OnNewDocument();
+ virtual void Serialize(CArchive& ar);
+ virtual BOOL OnSaveDocument(LPCTSTR lpszPathName);
+ virtual BOOL OnOpenDocument(LPCTSTR lpszPathName);
+ virtual void DeleteContents();
+ //}}AFX_VIRTUAL
+
+ void InitCommandStack();
+ void Exec(Command* command);
+ void Undo();
+ void Redo();
+ int NumUndo() const;
+ int NumRedo() const;
+ const char* GetUndoName() const;
+ const char* GetRedoName() const;
+
+// Implementation
+public:
+ virtual ~MagicDoc();
+#ifdef _DEBUG
+ virtual void AssertValid() const;
+ virtual void Dump(CDumpContext& dc) const;
+#endif
+
+ Solid* GetSolid() { return solid; }
+ Selection* GetSelection() { return selection; }
+ Selector* GetSelector() { return selector; }
+ Editor* GetEditor() { return editor; }
+
+ bool ImportFile(LPCTSTR path_name);
+ bool ExportFile(LPCTSTR path_name);
+
+protected:
+ Solid* solid;
+ Selection* selection;
+ Selector* selector;
+ Editor* editor;
+
+ List<Command> commands;
+ int nundo;
+
+// Generated message map functions
+protected:
+ //{{AFX_MSG(MagicDoc)
+ afx_msg void OnSurfaceOptimize();
+ afx_msg void OnSurfaceExplode();
+ afx_msg void OnUpdateSurfaceOptimize(CCmdUI* pCmdUI);
+ afx_msg void OnUpdateSurfaceExplode(CCmdUI* pCmdUI);
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+// +--------------------------------------------------------------------+
+
+int LoadBuffer(const char* filename, BYTE*& buf, bool null_terminate=false);
+int LoadTexture(const char* name, Bitmap*& bmp, int type=0);
+int LoadAlpha(const char* name, Bitmap& bitmap, int type=0);
+
+// +--------------------------------------------------------------------+
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_MAGICDOC_H__8B8D63A3_30F9_4023_BFA8_DB79891487C2__INCLUDED_)
diff --git a/Magic2/MagicView.cpp b/Magic2/MagicView.cpp
new file mode 100644
index 0000000..c97e641
--- /dev/null
+++ b/Magic2/MagicView.cpp
@@ -0,0 +1,1437 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: MagicView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Implementation of the MagicView class
+*/
+
+#include "stdafx.h"
+#include "Magic.h"
+
+#include "MagicDoc.h"
+#include "MagicView.h"
+#include "MainFrm.h"
+#include "MaterialDialog.h"
+#include "SurfacePropertiesDialog.h"
+#include "TextureMapDialog.h"
+#include "Editor.h"
+#include "Grid.h"
+#include "GridProps.h"
+#include "Selection.h"
+#include "Selector.h"
+#include "UVMapView.h"
+
+#include "ActiveWindow.h"
+#include "Color.h"
+#include "Layout.h"
+#include "Light.h"
+#include "Scene.h"
+#include "Screen.h"
+#include "Shadow.h"
+#include "Solid.h"
+#include "Video.h"
+#include "VideoDX9.h"
+#include "VideoSettings.h"
+
+#include "ModelView.h"
+
+DWORD GetRealTime();
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+extern PALETTEENTRY standard_palette[256];
+extern BYTE inverse_palette[32768];
+
+// +--------------------------------------------------------------------+
+
+IMPLEMENT_DYNCREATE(MagicView, CView)
+
+BEGIN_MESSAGE_MAP(MagicView, CView)
+ //{{AFX_MSG_MAP(MagicView)
+ ON_WM_SIZE()
+ ON_COMMAND(ID_VIEW_RENDER, OnRender)
+ ON_WM_PAINT()
+ ON_COMMAND(ID_VIEW_ALL, OnViewAll)
+ ON_UPDATE_COMMAND_UI(ID_VIEW_ALL, OnUpdateViewAll)
+ ON_COMMAND(ID_VIEW_FRONT, OnViewFront)
+ ON_UPDATE_COMMAND_UI(ID_VIEW_FRONT, OnUpdateViewFront)
+ ON_COMMAND(ID_VIEW_PERSPECTIVE, OnViewPerspective)
+ ON_UPDATE_COMMAND_UI(ID_VIEW_PERSPECTIVE, OnUpdateViewPerspective)
+ ON_COMMAND(ID_VIEW_SIDE, OnViewSide)
+ ON_UPDATE_COMMAND_UI(ID_VIEW_SIDE, OnUpdateViewSide)
+ ON_COMMAND(ID_VIEW_TOP, OnViewTop)
+ ON_UPDATE_COMMAND_UI(ID_VIEW_TOP, OnUpdateViewTop)
+ ON_COMMAND(ID_MODIFY_TEXTURE_MAP, OnTextureMap)
+ ON_COMMAND(ID_MODIFY_MATERIAL, OnModifyMaterial)
+ ON_WM_LBUTTONDOWN()
+ ON_WM_LBUTTONUP()
+ ON_WM_LBUTTONDBLCLK()
+ ON_WM_RBUTTONDOWN()
+ ON_WM_RBUTTONUP()
+ ON_WM_MOUSEMOVE()
+ ON_WM_MOUSEWHEEL()
+ ON_COMMAND(ID_VIEW_ZOOM_NORMAL, OnViewZoomNormal)
+ ON_COMMAND(ID_VIEW_ZOOM_IN, OnViewZoomIn)
+ ON_COMMAND(ID_VIEW_ZOOM_OUT, OnViewZoomOut)
+ ON_COMMAND(ID_VIEW_MODE_WIREFRAME, OnViewModeWireframe)
+ ON_COMMAND(ID_VIEW_MODE_SOLID, OnViewModeSolid)
+ ON_COMMAND(ID_VIEW_MODE_TEXTURED, OnViewModeTextured)
+ ON_WM_RBUTTONDBLCLK()
+ ON_COMMAND(ID_PROP_GRID, OnGridProperties)
+ ON_COMMAND(ID_GRID_SHOW, OnGridShow)
+ ON_COMMAND(ID_GRID_SNAP, OnGridSnap)
+ ON_UPDATE_COMMAND_UI(ID_GRID_SNAP, OnUpdateGridSnap)
+ ON_COMMAND(ID_VIEW_BACK_COLOR, OnViewBackColor)
+ ON_COMMAND(ID_FILE_IMPORT, OnFileImport)
+ ON_COMMAND(ID_FILE_EXPORT, OnFileExport)
+ ON_COMMAND(ID_EDIT_SELECT_ALL, OnSelectAll)
+ ON_COMMAND(ID_EDIT_SELECT_NONE, OnSelectNone)
+ ON_UPDATE_COMMAND_UI(ID_MODIFY_TEXTURE_MAP, OnUpdateTextureMap)
+ ON_UPDATE_COMMAND_UI(ID_MODIFY_MATERIAL, OnUpdateModifyMaterial)
+ ON_COMMAND(ID_EDIT_SELECT_INVERSE, OnSelectInverse)
+ ON_COMMAND(ID_MODIFY_UV_MAP, OnModifyUVMap)
+ ON_UPDATE_COMMAND_UI(ID_MODIFY_UV_MAP, OnUpdateModifyUVMap)
+ ON_COMMAND(ID_VIEW_SHADOWS, OnViewShadows)
+ ON_UPDATE_COMMAND_UI(ID_VIEW_SHADOWS, OnUpdateViewShadows)
+ ON_COMMAND(ID_VIEW_ANIMATELIGHT, OnViewAnimatelight)
+ ON_UPDATE_COMMAND_UI(ID_VIEW_ANIMATELIGHT, OnUpdateViewAnimatelight)
+ ON_COMMAND(ID_VIEW_BUMPMAPS, OnViewBumpmaps)
+ ON_UPDATE_COMMAND_UI(ID_VIEW_BUMPMAPS, OnUpdateViewBumpmaps)
+ ON_COMMAND(ID_VIEW_VERTEXSHADER, OnViewVertexshader)
+ ON_UPDATE_COMMAND_UI(ID_VIEW_VERTEXSHADER, OnUpdateViewVertexshader)
+ ON_COMMAND(ID_VIEW_PIXELSHADER, OnViewPixelshader)
+ ON_UPDATE_COMMAND_UI(ID_VIEW_PIXELSHADER, OnUpdateViewPixelshader)
+ ON_COMMAND(ID_VIEW_VISIBLESHADOWS, OnViewVisibleshadows)
+ ON_UPDATE_COMMAND_UI(ID_VIEW_VISIBLESHADOWS, OnUpdateViewVisibleshadows)
+ ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
+ ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
+ ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
+ ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo)
+ ON_COMMAND(ID_PROP_SURFACE, OnSurfaceProperties)
+ ON_UPDATE_COMMAND_UI(ID_PROP_SURFACE, OnUpdateSurfaceProperties)
+ //}}AFX_MSG_MAP
+ // Standard printing commands
+ ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
+ ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
+ ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
+END_MESSAGE_MAP()
+
+// +--------------------------------------------------------------------+
+
+static MagicView* magic_view = 0;
+
+MagicView::MagicView()
+ : video(0), video_settings(0), screen(0), scene(0),
+ drag_left(false), drag_right(false), grid(0),
+ main_light(0), back_light(0), view_shadows(true), view_bumpmaps(true),
+ animate_light(false)
+{
+ window_style = 0;
+ is_minimized = false;
+ is_maximized = false;
+ is_sizing = false;
+ view_mode = VIEW_ALL;
+
+ main_win = 0;
+ view_win[0] = 0;
+ view_win[1] = 0;
+ view_win[2] = 0;
+ view_win[3] = 0;
+ model_view[0] = 0;
+ model_view[1] = 0;
+ model_view[2] = 0;
+ model_view[3] = 0;
+ uvmap_win = 0;
+
+ grid = new Grid;
+ magic_view = this;
+
+ Solid::EnableCollision(false);
+}
+
+MagicView::~MagicView()
+{
+ if (grid) {
+ delete grid;
+ }
+
+ if (scene) {
+ scene->Graphics().clear();
+ delete scene;
+ }
+
+ if (screen) delete screen;
+ if (video) delete video;
+ if (video_settings) delete video_settings;
+
+ if (magic_view == this)
+ magic_view = 0;
+}
+
+MagicView* MagicView::GetInstance()
+{
+ return magic_view;
+}
+
+BOOL MagicView::PreCreateWindow(CREATESTRUCT& cs)
+{
+ return CView::PreCreateWindow(cs);
+}
+
+// +--------------------------------------------------------------------+
+//
+// MagicView diagnostics
+
+#ifdef _DEBUG
+void MagicView::AssertValid() const
+{
+ CView::AssertValid();
+}
+
+void MagicView::Dump(CDumpContext& dc) const
+{
+ CView::Dump(dc);
+}
+
+MagicDoc* MagicView::GetDocument() // non-debug version is inline
+{
+ ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(MagicDoc)));
+ return (MagicDoc*)m_pDocument;
+}
+#endif //_DEBUG
+
+
+// +--------------------------------------------------------------------+
+
+void MagicView::OnInitialUpdate()
+{
+ CView::OnInitialUpdate();
+ Color::SetPalette(standard_palette, 256, inverse_palette);
+
+ if (!video_settings)
+ video_settings = new VideoSettings;
+
+ GetClientRect(&client_rect);
+
+ // Use client area to set video window size
+ int w = client_rect.right - client_rect.left;
+ int h = client_rect.bottom - client_rect.top;
+
+ video_settings->is_windowed = true;
+ video_settings->window_width = w;
+ video_settings->window_height = h;
+
+ if (!video) {
+ video = new VideoDX9(GetSafeHwnd(), video_settings);
+ *video_settings = *video->GetVideoSettings();
+
+ if (video) {
+ Color::UseVideo(video);
+ video->UseXFont("System", 12, false, false);
+
+ screen = new Screen(video);
+ if (!screen) {
+ ::Print("ERROR: Could not create Screen object.\n");
+ return;
+ }
+
+ ::Print(" Created screen object (%d x %d).\n", w, h);
+
+ if (!screen->SetBackgroundColor(Color::Black))
+ ::Print(" WARNING: could not set video background color to Black\n");
+
+ screen->ClearAllFrames(true);
+
+ ::Print(" Established requested video parameters.\n");
+ ::Print(" ---------------------------------------\n\n");
+
+ if (!scene) {
+ scene = new Scene;
+
+ scene->SetAmbient(Color(60,60,60));
+
+ Point light_pos(3e6, 5e6, 4e6);
+
+ main_light = new Light(1.0f); //1.25f);
+ main_light->MoveTo(light_pos);
+ main_light->SetType(Light::LIGHT_DIRECTIONAL);
+ main_light->SetColor(Color::White);
+ main_light->SetShadow(true);
+
+ scene->AddLight(main_light);
+
+ back_light = new Light(0.5f);
+ back_light->MoveTo(light_pos * -1);
+ back_light->SetType(Light::LIGHT_DIRECTIONAL);
+ back_light->SetColor(Color::White);
+
+ scene->AddLight(back_light);
+
+ Selection* seln = GetDocument()->GetSelection();
+ Selector* selector = GetDocument()->GetSelector();
+
+ if (seln && selector) {
+ scene->Graphics().clear();
+ scene->AddGraphic(seln);
+ scene->AddGraphic(selector);
+
+ selector->UseModel(0);
+ }
+ }
+
+ int mins[2] = { 0, 0 };
+ float weights[2] = { 1, 1 };
+
+ main_win = new ActiveWindow(screen, 0, 0, w, h, 100, 0);
+ main_win->UseLayout(2, 2, mins, mins, weights, weights);
+ main_win->SetBackColor(Color::Gray);
+
+ screen->AddWindow(main_win);
+
+ DWORD view_types[] = {
+ ModelView::VIEW_PLAN,
+ ModelView::VIEW_PROJECT,
+ ModelView::VIEW_SIDE,
+ ModelView::VIEW_FRONT
+ };
+
+ for (int row = 0; row < 2; row++) {
+ for (int col = 0; col < 2; col++) {
+ int index = 2*row + col;
+
+ ActiveWindow* win = new ActiveWindow(screen,
+ col*w/2,
+ row*h/2,
+ w/2,
+ h/2,
+ 101+index,
+ WIN_BLACK_FRAME,
+ main_win);
+
+ win->SetCells(col, row, 1, 1);
+ win->SetCellInsets(Insets(1,1,1,1));
+ win->SetBackColor(Color(160,160,160));
+
+ ModelView* mv = new ModelView(win, scene, view_types[index]);
+
+ if (view_types[index] == ModelView::VIEW_PROJECT)
+ mv->SetFillMode(ModelView::FILL_TEXTURE);
+
+ mv->UseGrid(grid);
+ win->AddView(mv);
+
+ view_win[index] = win;
+ model_view[index] = mv;
+ }
+ }
+
+ view_win[0]->SetStyle(WIN_WHITE_FRAME);
+
+ uvmap_win = new ActiveWindow(screen, 0, 0, w, h, 110, WIN_BLACK_FRAME, main_win);
+ uvmap_view = new UVMapView(uvmap_win);
+ uvmap_win->AddView(uvmap_view);
+
+ main_win->DoLayout();
+ }
+
+ else {
+ ::Print(" Could not establish requested video parameters.\n");
+ ::Print(" -----------------------------------------------\n\n");
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MagicView::SetupModelViews()
+{
+ switch (view_mode) {
+ case VIEW_ALL: {
+ for (int row = 0; row < 2; row++) {
+ for (int col = 0; col < 2; col++) {
+ int index = 2*row + col;
+
+ ActiveWindow* win = view_win[index];
+
+ win->Show();
+ win->SetCells(col, row, 1, 1);
+ win->SetCellInsets(Insets(1,1,1,1));
+ }
+ }
+
+ uvmap_win->Hide();
+ uvmap_win->SetCells(0,0,0,0);
+ }
+ break;
+
+ case VIEW_TOP:
+ case VIEW_SIDE:
+ case VIEW_FRONT:
+ case VIEW_PERSPECTIVE: {
+ view_focus = view_mode;
+
+ for (int i = 0; i < 4; i++) {
+ ActiveWindow* win = view_win[i];
+
+ if (i == view_mode) {
+ win->Show();
+ win->SetCells(0,0,2,2);
+ win->SetStyle(WIN_WHITE_FRAME);
+ }
+
+ else {
+ win->Hide();
+ win->SetCells(0,0,0,0);
+ win->SetStyle(WIN_BLACK_FRAME);
+ }
+ }
+
+ uvmap_win->Hide();
+ uvmap_win->SetCells(0,0,0,0);
+ }
+ break;
+
+ case VIEW_UV_MAP: {
+ view_focus = view_mode;
+
+ for (int i = 0; i < 4; i++) {
+ ActiveWindow* win = view_win[i];
+ win->Hide();
+ win->SetCells(0,0,0,0);
+ win->SetStyle(WIN_BLACK_FRAME);
+ }
+
+ uvmap_win->Show();
+ uvmap_win->SetCells(0,0,2,2);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ main_win->DoLayout();
+}
+
+void
+MagicView::SetFocusModelView(int f)
+{
+ if (view_mode == VIEW_ALL) {
+ view_focus = f;
+
+ for (int row = 0; row < 2; row++) {
+ for (int col = 0; col < 2; col++) {
+ int index = 2*row + col;
+
+ ActiveWindow* win = view_win[index];
+
+ win->Show();
+ win->SetCells(col, row, 1, 1);
+ win->SetCellInsets(Insets(1,1,1,1));
+
+ if (index == view_focus) {
+ win->SetStyle(WIN_WHITE_FRAME);
+ }
+ else {
+ win->SetStyle(WIN_BLACK_FRAME);
+ }
+ }
+ }
+ }
+ else if (IsUVEdit()) {
+ view_focus = view_mode;
+ }
+ else {
+ view_mode = f;
+ view_focus = f;
+
+ for (int i = 0; i < 4; i++) {
+ ActiveWindow* win = view_win[i];
+
+ if (i == view_mode) {
+ win->Show();
+ win->SetCells(0,0,2,2);
+ win->SetStyle(WIN_WHITE_FRAME);
+ }
+
+ else {
+ win->Hide();
+ win->SetCells(0,0,0,0);
+ win->SetStyle(WIN_BLACK_FRAME);
+ }
+ }
+ }
+
+ main_win->DoLayout();
+}
+
+int
+MagicView::GetWinIndexByPoint(int x, int y)
+{
+ if (view_mode == VIEW_ALL) {
+ for (int row = 0; row < 2; row++) {
+ for (int col = 0; col < 2; col++) {
+ int index = 2*row + col;
+
+ ActiveWindow* win = view_win[index];
+ if (win->GetRect().Contains(x, y))
+ return index;
+ }
+ }
+ }
+
+ return view_mode;
+}
+
+ModelView*
+MagicView::GetModelViewByIndex(int index)
+{
+ if (index >= 0 && index < 4) {
+ return model_view[index];
+ }
+
+ return model_view[0];
+}
+
+// +--------------------------------------------------------------------+
+
+void MagicView::OnPaint()
+{
+ ValidateRect(0);
+ OnRender();
+}
+
+void MagicView::OnDraw(CDC* dc)
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void MagicView::ResizeVideo()
+{
+ if (!video || !video_settings) return;
+
+ HRESULT hr = S_OK;
+ RECT client_old;
+
+ client_old = client_rect;
+
+ // Update window properties
+ GetClientRect(&client_rect);
+
+ if (client_old.right - client_old.left !=
+ client_rect.right - client_rect.left ||
+ client_old.bottom - client_old.top !=
+ client_rect.bottom - client_rect.top) {
+
+ // A new window size will require a new backbuffer
+ // size, so the 3D structures must be changed accordingly.
+
+ video_settings->is_windowed = true;
+ video_settings->window_width = client_rect.right - client_rect.left;
+ video_settings->window_height = client_rect.bottom - client_rect.top;
+
+ ::Print("ResizeVideo() %d x %d\n", video_settings->window_width, video_settings->window_height);
+
+ if (video) {
+ video->Reset(video_settings);
+ }
+ }
+
+ // save a copy of the device-specific video settings:
+ if (video->GetVideoSettings()) {
+ *video_settings = *video->GetVideoSettings();
+ }
+
+ if (screen)
+ screen->Resize(video_settings->window_width,
+ video_settings->window_height);
+
+ video->InvalidateCache();
+ video->SetShadowEnabled(view_shadows);
+ video->SetBumpMapEnabled(view_bumpmaps);
+}
+
+// +--------------------------------------------------------------------+
+
+void MagicView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
+{
+ CView::OnUpdate(pSender, lHint, pHint);
+
+ Solid* solid = GetDocument()->GetSolid();
+ Selection* seln = GetDocument()->GetSelection();
+ Selector* selector = GetDocument()->GetSelector();
+
+ if (solid && scene) {
+ scene->Graphics().clear();
+ scene->AddGraphic(solid);
+ scene->AddGraphic(seln);
+ scene->AddGraphic(selector);
+ }
+
+ if (selector)
+ selector->UseModel(solid->GetModel());
+}
+
+CPoint
+MagicView::LPtoWP(const CPoint& p)
+{
+ CPoint result;
+ ModelView* view = GetModelViewByIndex(view_focus);
+ CPoint origin = view->ProjectPoint(Vec3(0,0,0));
+ double scale = view->GetFieldOfView() / 2;
+
+ result.x = (LONG) (( p.x - origin.x) / scale);
+ result.y = (LONG) ((-p.y + origin.y) / scale);
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+//
+// MagicView message handlers
+
+void MagicView::OnEnterSizeMove()
+{
+ is_sizing = true;
+}
+
+void MagicView::OnExitSizeMove()
+{
+ is_sizing = false;
+ ResizeVideo();
+}
+
+void MagicView::OnSize(UINT nType, int cx, int cy)
+{
+ CView::OnSize(nType, cx, cy);
+
+ window_style = GetWindowLong(m_hWnd, GWL_STYLE);
+
+ if (nType == SIZE_MINIMIZED) {
+ is_minimized = true;
+ is_maximized = false;
+ }
+
+ else if (nType == SIZE_MAXIMIZED) {
+ is_minimized = false;
+ is_maximized = true;
+ ResizeVideo();
+ }
+
+ else if (nType == SIZE_RESTORED) {
+ if (is_maximized) {
+ is_maximized = false;
+ ResizeVideo();
+ }
+
+ else if (is_minimized) {
+ is_minimized = false;
+ ResizeVideo();
+ }
+ else if (!is_sizing) {
+ // if this is not a resize due to dragging...
+ ResizeVideo();
+ }
+ else {
+ // If we're neither maximized nor minimized, the window size
+ // is changing by the user dragging the window edges. In this
+ // case, we don't reset the device yet -- we wait until the
+ // user stops dragging, and a WM_EXITSIZEMOVE message comes.
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void MagicView::OnRender()
+{
+ if (!screen || !video)
+ return;
+
+ double s = sin(timeGetTime() * 0.001);
+ double c = cos(timeGetTime() * 0.001);
+
+ // IF LIGHTS ANIMATED:
+ if (animate_light) {
+ Point light_pos(3e6*s, 5e6*c, 4e6*s);
+
+ if (main_light) {
+ main_light->SetType(Light::LIGHT_POINT);
+ main_light->MoveTo(light_pos);
+ main_light->SetType(Light::LIGHT_DIRECTIONAL);
+ }
+
+ if (back_light) {
+ back_light->SetType(Light::LIGHT_POINT);
+ back_light->MoveTo(light_pos * -1);
+ back_light->SetType(Light::LIGHT_DIRECTIONAL);
+ }
+ }
+
+ if (screen->Refresh()) {
+ video->Present();
+ }
+ else {
+ ::Print("ERROR: Screen refresh failed.\n");
+ }
+}
+
+void MagicView::CloseUVEditor()
+{
+ if (IsUVEdit()) {
+ MagicDoc* doc = GetDocument();
+ Editor* editor = doc->GetEditor();
+ Solid* solid = doc->GetSolid();
+
+ if (editor && solid) {
+ editor->UseModel(solid->GetModel());
+ editor->Resegment();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void MagicView::OnViewAll()
+{
+ CloseUVEditor();
+ view_mode = VIEW_ALL;
+ SetupModelViews();
+}
+
+void MagicView::OnUpdateViewAll(CCmdUI* pCmdUI)
+{
+ if (pCmdUI)
+ pCmdUI->SetCheck(view_mode == VIEW_ALL);
+}
+
+void MagicView::OnViewFront()
+{
+ CloseUVEditor();
+ view_mode = VIEW_FRONT;
+ SetupModelViews();
+}
+
+void MagicView::OnUpdateViewFront(CCmdUI* pCmdUI)
+{
+ if (pCmdUI)
+ pCmdUI->SetCheck(view_mode == VIEW_FRONT);
+}
+
+void MagicView::OnViewPerspective()
+{
+ CloseUVEditor();
+ view_mode = VIEW_PERSPECTIVE;
+ SetupModelViews();
+}
+
+void MagicView::OnUpdateViewPerspective(CCmdUI* pCmdUI)
+{
+ if (pCmdUI)
+ pCmdUI->SetCheck(view_mode == VIEW_PERSPECTIVE);
+}
+
+void MagicView::OnViewSide()
+{
+ CloseUVEditor();
+ view_mode = VIEW_SIDE;
+ SetupModelViews();
+}
+
+void MagicView::OnUpdateViewSide(CCmdUI* pCmdUI)
+{
+ if (pCmdUI)
+ pCmdUI->SetCheck(view_mode == VIEW_SIDE);
+}
+
+void MagicView::OnViewTop()
+{
+ CloseUVEditor();
+ view_mode = VIEW_TOP;
+ SetupModelViews();
+}
+
+void MagicView::OnUpdateViewTop(CCmdUI* pCmdUI)
+{
+ if (pCmdUI)
+ pCmdUI->SetCheck(view_mode == VIEW_TOP);
+}
+
+// +--------------------------------------------------------------------+
+
+void MagicView::OnFileImport()
+{
+ DWORD err = 0;
+ char filename[256];
+ filename[0] = '\0';
+ CFileDialog ofd(TRUE, "mag");
+
+ ofd.m_ofn.lpstrFilter = "All 3D Files\0*.mag; *.obj; *.3ds\0Magic Files (*.mag)\0*.mag\0Wavefront/OBJ Files (*.obj)\0*.obj\0003DS MAX Files (*.3ds)\0*.3ds\0\0";
+ ofd.m_ofn.lpstrFile = filename;
+ ofd.m_ofn.nMaxFile = sizeof(filename);
+
+ if (ofd.DoModal() != IDOK)
+ return;
+
+ char mag_name[256];
+ sprintf(mag_name, "%s", ofd.GetFileName().GetBuffer(0));
+
+ MagicDoc* pDoc = GetDocument();
+ ASSERT_VALID(pDoc);
+
+ if (pDoc->ImportFile(mag_name)) {
+ Invalidate();
+ pDoc->SetModifiedFlag(TRUE);
+ pDoc->UpdateAllViews(this);
+ }
+}
+
+void MagicView::OnFileExport()
+{
+ DWORD err = 0;
+ char filename[256];
+ filename[0] = '\0';
+ CFileDialog ofd(FALSE, "mag");
+
+ ofd.m_ofn.lpstrFilter = "All 3D Files\0*.mag; *.obj; *.3ds\0Magic Files (*.mag)\0*.mag\0Wavefront/OBJ Files (*.obj)\0*.obj\0003DS MAX Files (*.3ds)\0*.3ds\0\0";
+ ofd.m_ofn.lpstrFile = filename;
+ ofd.m_ofn.nMaxFile = sizeof(filename);
+
+ if (ofd.DoModal() != IDOK)
+ return;
+
+ char mag_name[256];
+ sprintf(mag_name, "%s", ofd.GetFileName().GetBuffer(0));
+
+ MagicDoc* pDoc = GetDocument();
+ ASSERT_VALID(pDoc);
+
+ if (pDoc->ExportFile(mag_name)) {
+ pDoc->SetModifiedFlag(FALSE);
+ pDoc->UpdateAllViews(this);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void MagicView::OnSurfaceProperties()
+{
+ SurfacePropertiesDialog dlg(this);
+ dlg.DoModal();
+}
+
+void MagicView::OnUpdateSurfaceProperties(CCmdUI* pCmdUI)
+{
+ Solid* solid = GetDocument()->GetSolid();
+ pCmdUI->Enable(solid && solid->GetModel());
+}
+
+// +--------------------------------------------------------------------+
+
+void MagicView::OnTextureMap()
+{
+ TextureMapDialog dlg(this);
+ if (dlg.DoModal() == IDOK) {
+ MagicDoc* doc = GetDocument();
+ Solid* solid = doc->GetSolid();
+ Selection* seln = doc->GetSelection();
+ Selector* selector = doc->GetSelector();
+ Editor* editor = doc->GetEditor();
+ Material* mtl = 0;
+
+ if (dlg.mMaterialIndex >= 0) {
+ mtl = solid->GetModel()->GetMaterials()[dlg.mMaterialIndex];
+ }
+
+ editor->UseModel(solid->GetModel());
+ editor->ApplyMaterial(mtl, seln->GetPolys(),
+ dlg.mMapType, 2-dlg.mAxis, (float) dlg.mScaleU, (float) dlg.mScaleV,
+ dlg.mFlip, dlg.mMirror, dlg.mRotate);
+
+ selector->Reselect();
+
+ Invalidate();
+ doc->SetModifiedFlag(TRUE);
+ doc->UpdateAllViews(this);
+ }
+}
+
+void MagicView::OnUpdateTextureMap(CCmdUI* pCmdUI)
+{
+ Solid* solid = GetDocument()->GetSolid();
+ Selection* seln = GetDocument()->GetSelection();
+
+ pCmdUI->Enable(solid && solid->GetModel() && seln && seln->GetPolys().size() > 0);
+}
+
+void MagicView::OnModifyMaterial()
+{
+ MaterialDialog dlg(this);
+ dlg.DoModal();
+
+ Invalidate();
+ GetDocument()->SetModifiedFlag(TRUE);
+ GetDocument()->UpdateAllViews(this);
+}
+
+void MagicView::OnUpdateModifyMaterial(CCmdUI* pCmdUI)
+{
+ Solid* solid = GetDocument()->GetSolid();
+ pCmdUI->Enable(solid && solid->GetModel());
+}
+
+void MagicView::OnModifyUVMap()
+{
+ Selection* seln = GetDocument()->GetSelection();
+
+ view_mode = VIEW_UV_MAP;
+ SetupModelViews();
+
+ if (seln && uvmap_view) {
+ Poly* p = seln->GetPolys().first();
+
+ if (p) {
+ uvmap_view->UseMaterial(p->material);
+ uvmap_view->UsePolys(seln->GetPolys());
+ }
+ }
+}
+
+void MagicView::OnUpdateModifyUVMap(CCmdUI* pCmdUI)
+{
+ OnUpdateTextureMap(pCmdUI);
+}
+
+// +--------------------------------------------------------------------+
+
+void MagicView::OnGridProperties()
+{
+ GridProps dlg(grid, this);
+ dlg.DoModal();
+}
+
+void MagicView::OnGridShow()
+{
+ if (grid)
+ grid->SetShow(!grid->IsShow());
+}
+
+void MagicView::OnGridSnap()
+{
+ if (grid)
+ grid->SetSnap(!grid->IsSnap());
+}
+
+void MagicView::OnUpdateGridSnap(CCmdUI* pCmdUI)
+{
+ if (grid)
+ pCmdUI->SetCheck(grid->IsSnap());
+}
+
+// +--------------------------------------------------------------------+
+
+void MagicView::OnLButtonDown(UINT nFlags, CPoint point)
+{
+ CView::OnLButtonDown(nFlags, point);
+ SetCapture();
+
+ // set focus to the view that was clicked:
+ int index = GetWinIndexByPoint(point.x, point.y);
+ SetFocusModelView(index);
+
+ drag_start = point;
+ drag_left = true;
+ drag_right = false;
+
+ ModelView* mv = GetModelViewByIndex(index);
+ MagicDoc* pDoc = GetDocument();
+ Model* model = pDoc->GetSolid()->GetModel();
+ Selector* selector = pDoc->GetSelector();
+
+ if (IsUVEdit()) {
+ int select_mode = UVMapView::SELECT_APPEND;
+
+ if (nFlags & MK_CONTROL)
+ select_mode = UVMapView::SELECT_REMOVE;
+
+ if (!uvmap_view->WillSelect(point)) {
+ uvmap_view->Begin(select_mode);
+ uvmap_view->AddMark(point);
+ }
+ }
+
+ else if (mv && selector) {
+ int select_mode = Selector::SELECT_APPEND;
+
+ if (nFlags & MK_CONTROL)
+ select_mode = Selector::SELECT_REMOVE;
+
+ selector->Begin(model, mv->GetViewMode(), select_mode);
+ selector->AddMark(point);
+ }
+}
+
+void MagicView::OnLButtonUp(UINT nFlags, CPoint point)
+{
+ CView::OnLButtonUp(nFlags, point);
+ ReleaseCapture();
+
+ drag_left = false;
+
+ MagicDoc* pDoc = GetDocument();
+ Selector* selector = pDoc->GetSelector();
+
+ if (IsUVEdit())
+ uvmap_view->End();
+
+ else if (selector && selector->IsActive())
+ selector->End();
+}
+
+void MagicView::OnLButtonDblClk(UINT nFlags, CPoint point)
+{
+ CView::OnLButtonDblClk(nFlags, point);
+
+ drag_left = false;
+
+ MagicDoc* pDoc = GetDocument();
+ Selector* selector = pDoc->GetSelector();
+
+ if (IsUVEdit())
+ uvmap_view->Clear();
+
+ else if (selector)
+ selector->Clear();
+}
+
+void MagicView::OnRButtonDown(UINT nFlags, CPoint point)
+{
+ CView::OnRButtonDown(nFlags, point);
+ SetCapture();
+
+ // set focus to the view that was clicked:
+ int index = GetWinIndexByPoint(point.x, point.y);
+ SetFocusModelView(index);
+
+ drag_start = point;
+ drag_left = false;
+ drag_right = true;
+}
+
+void MagicView::OnRButtonUp(UINT nFlags, CPoint point)
+{
+ CView::OnRButtonUp(nFlags, point);
+ ReleaseCapture();
+
+ drag_right = false;
+}
+
+void MagicView::OnRButtonDblClk(UINT nFlags, CPoint point)
+{
+ CView::OnRButtonDblClk(nFlags, point);
+ ReleaseCapture();
+
+ drag_right = false;
+
+ if (view_mode == VIEW_ALL) {
+ view_mode = view_focus;
+ }
+ else if (IsUVEdit()) {
+ CloseUVEditor();
+ view_mode = VIEW_ALL;
+ SetupModelViews();
+ }
+ else {
+ view_mode = VIEW_ALL;
+ }
+
+ SetFocusModelView(view_focus);
+}
+
+void MagicView::OnMouseMove(UINT nFlags, CPoint point)
+{
+ if (drag_right) {
+ CPoint offset = point - drag_start;
+
+ if (view_focus == VIEW_PERSPECTIVE) {
+ ModelView* view = GetModelViewByIndex(view_focus);
+ view->SpinBy(offset.x * 0.5 * DEGREES,
+ offset.y * 0.5 * DEGREES);
+ }
+
+ else if (IsUVEdit()) {
+ uvmap_view->MoveBy(offset.x, offset.y);
+ }
+
+ else {
+ ModelView* view = GetModelViewByIndex(view_focus);
+ view->MoveBy(offset.x, offset.y);
+ }
+
+ drag_start = point;
+ Invalidate();
+ }
+
+ else if (drag_left) {
+ CPoint offset = point - drag_start;
+ MagicDoc* pDoc = GetDocument();
+ Selector* selector = pDoc->GetSelector();
+
+ if (IsUVEdit()) {
+ if (uvmap_view->IsActive()) {
+ uvmap_view->AddMark(point);
+ }
+ else {
+ uvmap_view->DragBy(offset.x, offset.y);
+ drag_start = point;
+ }
+ }
+
+ else if (selector && selector->IsActive()) {
+ selector->AddMark(point);
+ }
+ }
+
+ // xy status message:
+ if (view_focus != VIEW_PERSPECTIVE) {
+ char xy[80];
+ CPoint mouse = LPtoWP(point);
+ Selection* seln = GetDocument()->GetSelection();
+
+ int nv = seln ? seln->GetVerts().size() : 0;
+ int np = seln ? seln->GetPolys().size() : 0;
+
+ if (np || nv)
+ sprintf(xy, "(%05d,%05d) Verts:%d Polys:%d", mouse.x, mouse.y, nv, np);
+ else
+ sprintf(xy, "(%05d,%05d)", mouse.x, mouse.y);
+ MainFrame::StatusXY(xy);
+ }
+
+ CView::OnMouseMove(nFlags, point);
+}
+
+BOOL MagicView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
+{
+ if (view_focus == VIEW_PERSPECTIVE) {
+ ModelView* view = GetModelViewByIndex(view_focus);
+
+ if (view) {
+ Camera* cam = view->GetCamera();
+ Point pos = cam->Pos();
+ double len = pos.length();
+
+ if (zDelta < 0) {
+ if (len < 10000)
+ pos *= 1.15;
+ }
+ else {
+ if (len > 0.10)
+ pos *= 0.85;
+ }
+
+ cam->MoveTo(pos);
+ }
+ }
+
+ else if (IsUVEdit()) {
+ if (zDelta < 0) {
+ uvmap_view->ZoomOut();
+ }
+ else {
+ uvmap_view->ZoomIn();
+ }
+ }
+
+ else {
+ if (zDelta > 0)
+ OnViewZoomIn();
+ else
+ OnViewZoomOut();
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void MagicView::OnViewZoomNormal()
+{
+ for (int i = 0; i < 4; i++) {
+ ModelView* view = GetModelViewByIndex(i);
+
+ if (view) {
+ view->ZoomNormal();
+ }
+ }
+}
+
+void MagicView::OnViewZoomIn()
+{
+ for (int i = 0; i < 4; i++) {
+ ModelView* view = GetModelViewByIndex(i);
+
+ if (view && view->GetViewMode() != ModelView::VIEW_PROJECT) {
+ double fov = view->GetFieldOfView() * 1.15;
+ view->SetFieldOfView(fov);
+ }
+ }
+}
+
+void MagicView::OnViewZoomOut()
+{
+ for (int i = 0; i < 4; i++) {
+ ModelView* view = GetModelViewByIndex(i);
+
+ if (view && view->GetViewMode() != ModelView::VIEW_PROJECT) {
+ double fov = view->GetFieldOfView() * 0.85;
+ view->SetFieldOfView(fov);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void MagicView::OnViewModeWireframe()
+{
+ ModelView* view = GetModelViewByIndex(view_focus);
+
+ if (view) {
+ view->SetFillMode(ModelView::FILL_WIRE);
+ }
+}
+
+void MagicView::OnViewModeSolid()
+{
+ ModelView* view = GetModelViewByIndex(view_focus);
+
+ if (view) {
+ view->SetFillMode(ModelView::FILL_SOLID);
+ }
+}
+
+void MagicView::OnViewModeTextured()
+{
+ ModelView* view = GetModelViewByIndex(view_focus);
+
+ if (view) {
+ view->SetFillMode(ModelView::FILL_TEXTURE);
+ }
+}
+
+void MagicView::OnViewBackColor()
+{
+ ModelView* view = GetModelViewByIndex(view_focus);
+
+ if (view) {
+ ActiveWindow* win = (ActiveWindow*) view->GetWindow();
+ Color c = win->GetBackColor();
+ COLORREF crgb = RGB(c.Red(), c.Green(), c.Blue());
+ CColorDialog chooser(crgb);
+
+ if (chooser.DoModal() == IDOK) {
+ crgb = chooser.GetColor();
+ win->SetBackColor( Color(GetRValue(crgb), GetGValue(crgb), GetBValue(crgb)) );
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void MagicView::OnSelectAll()
+{
+ Solid* solid = GetDocument()->GetSolid();
+ Selector* selector = GetDocument()->GetSelector();
+
+ if (IsUVEdit()) {
+ uvmap_view->SelectAll();
+ }
+
+ else if (solid && selector) {
+ selector->UseModel(solid->GetModel());
+ selector->SelectAll(Selector::SELECT_APPEND);
+ }
+}
+
+void MagicView::OnSelectNone()
+{
+ Solid* solid = GetDocument()->GetSolid();
+ Selector* selector = GetDocument()->GetSelector();
+
+ if (IsUVEdit()) {
+ uvmap_view->SelectNone();
+ }
+
+ else if (solid && selector) {
+ selector->UseModel(solid->GetModel());
+ selector->SelectAll(Selector::SELECT_REMOVE);
+ }
+}
+
+void MagicView::OnSelectInverse()
+{
+ Solid* solid = GetDocument()->GetSolid();
+ Selector* selector = GetDocument()->GetSelector();
+
+ if (IsUVEdit()) {
+ uvmap_view->SelectInverse();
+ }
+
+ else if (solid && selector) {
+ selector->UseModel(solid->GetModel());
+ selector->SelectInverse();
+ }
+}
+
+void MagicView::OnViewShadows()
+{
+ view_shadows = !view_shadows;
+
+ if (video)
+ video->SetShadowEnabled(view_shadows);
+}
+
+void MagicView::OnUpdateViewShadows(CCmdUI* pCmdUI)
+{
+ pCmdUI->SetCheck(view_shadows);
+}
+
+void MagicView::OnViewAnimatelight()
+{
+ animate_light = !animate_light;
+}
+
+void MagicView::OnUpdateViewAnimatelight(CCmdUI* pCmdUI)
+{
+ pCmdUI->SetCheck(animate_light);
+}
+
+void MagicView::OnViewBumpmaps()
+{
+ view_bumpmaps = !view_bumpmaps;
+
+ if (video)
+ video->SetBumpMapEnabled(view_bumpmaps);
+}
+
+void MagicView::OnUpdateViewBumpmaps(CCmdUI* pCmdUI)
+{
+ pCmdUI->SetCheck(view_bumpmaps);
+}
+
+void MagicView::OnViewVertexshader()
+{
+ if (video) {
+ VideoSettings* vs = (VideoSettings*) video->GetVideoSettings();
+ vs->enable_vs = !vs->enable_vs;
+ }
+}
+
+void MagicView::OnUpdateViewVertexshader(CCmdUI* pCmdUI)
+{
+ if (video)
+ pCmdUI->SetCheck(video->GetVideoSettings()->enable_vs);
+}
+
+void MagicView::OnViewPixelshader()
+{
+ if (video) {
+ VideoSettings* vs = (VideoSettings*) video->GetVideoSettings();
+ vs->enable_ps = !vs->enable_ps;
+ }
+}
+
+void MagicView::OnUpdateViewPixelshader(CCmdUI* pCmdUI)
+{
+ if (video)
+ pCmdUI->SetCheck(video->GetVideoSettings()->enable_ps);
+}
+
+void MagicView::OnViewVisibleshadows()
+{
+ Shadow::SetVisibleShadowVolumes(!Shadow::GetVisibleShadowVolumes());
+}
+
+void MagicView::OnUpdateViewVisibleshadows(CCmdUI* pCmdUI)
+{
+ pCmdUI->SetCheck(Shadow::GetVisibleShadowVolumes());
+}
+
+void MagicView::OnEditUndo()
+{
+ MagicDoc* pDoc = GetDocument();
+ ASSERT_VALID(pDoc);
+ pDoc->Undo();
+
+ Solid* solid = GetDocument()->GetSolid();
+ Selector* selector = GetDocument()->GetSelector();
+
+ if (selector) {
+ selector->UseModel(solid->GetModel());
+ selector->Reselect();
+ }
+
+ Invalidate();
+ pDoc->SetModifiedFlag(TRUE);
+ pDoc->UpdateAllViews(this);
+}
+
+void MagicView::OnUpdateEditUndo(CCmdUI* pCmdUI)
+{
+ MagicDoc* pDoc = GetDocument();
+
+ if (pDoc->NumUndo() > 0) {
+ pCmdUI->Enable(TRUE);
+ pCmdUI->SetText(CString("Undo ") + pDoc->GetUndoName() + CString("\tCtrl+Z"));
+ }
+ else {
+ pCmdUI->Enable(FALSE);
+ pCmdUI->SetText("Can't Undo\tCtrl+Z");
+ }
+}
+
+void MagicView::OnEditRedo()
+{
+ MagicDoc* pDoc = GetDocument();
+ ASSERT_VALID(pDoc);
+ pDoc->Redo();
+
+ Solid* solid = GetDocument()->GetSolid();
+ Selector* selector = GetDocument()->GetSelector();
+
+ if (selector) {
+ selector->UseModel(solid->GetModel());
+ selector->Reselect();
+ }
+
+ Invalidate();
+ pDoc->SetModifiedFlag(TRUE);
+ pDoc->UpdateAllViews(this);
+}
+
+void MagicView::OnUpdateEditRedo(CCmdUI* pCmdUI)
+{
+ MagicDoc* pDoc = GetDocument();
+
+ if (pDoc->NumRedo() > 0) {
+ pCmdUI->Enable(TRUE);
+ pCmdUI->SetText(CString("Redo ") + pDoc->GetRedoName() + CString("\tCtrl+Y"));
+ }
+ else {
+ pCmdUI->Enable(FALSE);
+ pCmdUI->SetText("Can't Redo");
+ }
+}
diff --git a/Magic2/MagicView.h b/Magic2/MagicView.h
new file mode 100644
index 0000000..8bd5e28
--- /dev/null
+++ b/Magic2/MagicView.h
@@ -0,0 +1,201 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: MagicView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Interface of the MagicView class
+*/
+
+
+#if !defined(AFX_MAGICVIEW_H__387B567A_8235_41B8_A993_E41567E680D7__INCLUDED_)
+#define AFX_MAGICVIEW_H__387B567A_8235_41B8_A993_E41567E680D7__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+// +--------------------------------------------------------------------+
+
+class MagicDoc;
+class ActiveWindow;
+class Grid;
+class Light;
+class Scene;
+class Screen;
+class Video;
+class VideoSettings;
+class ModelView;
+class UVMapView;
+
+// +--------------------------------------------------------------------+
+
+class MagicView : public CView
+{
+protected: // create from serialization only
+ MagicView();
+ DECLARE_DYNCREATE(MagicView)
+
+// Attributes
+public:
+ MagicDoc* GetDocument();
+
+ static MagicView* GetInstance();
+
+// Operations
+public:
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(MagicView)
+ public:
+ virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
+ virtual void OnInitialUpdate();
+ protected:
+ virtual void OnDraw(CDC* pDC);
+ virtual void OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint);
+ //}}AFX_VIRTUAL
+
+// Implementation
+public:
+ virtual ~MagicView();
+#ifdef _DEBUG
+ virtual void AssertValid() const;
+ virtual void Dump(CDumpContext& dc) const;
+#endif
+
+ virtual void OnEnterSizeMove();
+ virtual void OnExitSizeMove();
+ virtual void ResizeVideo();
+ virtual CPoint LPtoWP(const CPoint& p);
+
+protected:
+ Video* video;
+ VideoSettings* video_settings;
+ Screen* screen;
+ RECT client_rect;
+ DWORD window_style;
+ bool is_minimized;
+ bool is_maximized;
+ bool is_sizing;
+
+ Grid* grid;
+ Scene* scene;
+ Light* main_light;
+ Light* back_light;
+ ActiveWindow* main_win;
+ ActiveWindow* view_win[4];
+ ModelView* model_view[4];
+ ActiveWindow* uvmap_win;
+ UVMapView* uvmap_view;
+ int view_mode;
+ int view_focus;
+ bool drag_left;
+ bool drag_right;
+ CPoint drag_start;
+
+ bool view_bumpmaps;
+ bool view_shadows;
+ bool animate_light;
+
+ enum VIEW_MODE {
+ VIEW_TOP = 0,
+ VIEW_PERSPECTIVE = 1,
+ VIEW_SIDE = 2,
+ VIEW_FRONT = 3,
+ VIEW_ALL = 4,
+ VIEW_UV_MAP = 5
+ };
+
+ virtual void SetupModelViews();
+ virtual void SetFocusModelView(int f);
+ virtual int GetWinIndexByPoint(int x, int y);
+ ModelView* GetModelViewByIndex(int index);
+
+ bool IsUVEdit() const { return (view_mode == VIEW_UV_MAP) && (uvmap_view != 0); }
+ void CloseUVEditor();
+
+// Generated message map functions
+protected:
+ //{{AFX_MSG(MagicView)
+ afx_msg void OnSize(UINT nType, int cx, int cy);
+ afx_msg void OnRender();
+ afx_msg void OnPaint();
+ afx_msg void OnViewAll();
+ afx_msg void OnUpdateViewAll(CCmdUI* pCmdUI);
+ afx_msg void OnViewFront();
+ afx_msg void OnUpdateViewFront(CCmdUI* pCmdUI);
+ afx_msg void OnViewPerspective();
+ afx_msg void OnUpdateViewPerspective(CCmdUI* pCmdUI);
+ afx_msg void OnViewSide();
+ afx_msg void OnUpdateViewSide(CCmdUI* pCmdUI);
+ afx_msg void OnViewTop();
+ afx_msg void OnUpdateViewTop(CCmdUI* pCmdUI);
+ afx_msg void OnTextureMap();
+ afx_msg void OnModifyMaterial();
+ afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
+ afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
+ afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
+ afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
+ afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
+ afx_msg void OnMouseMove(UINT nFlags, CPoint point);
+ afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
+ afx_msg void OnViewZoomNormal();
+ afx_msg void OnViewZoomIn();
+ afx_msg void OnViewZoomOut();
+ afx_msg void OnViewModeWireframe();
+ afx_msg void OnViewModeSolid();
+ afx_msg void OnViewModeTextured();
+ afx_msg void OnRButtonDblClk(UINT nFlags, CPoint point);
+ afx_msg void OnGridProperties();
+ afx_msg void OnGridShow();
+ afx_msg void OnGridSnap();
+ afx_msg void OnUpdateGridSnap(CCmdUI* pCmdUI);
+ afx_msg void OnViewBackColor();
+ afx_msg void OnFileImport();
+ afx_msg void OnFileExport();
+ afx_msg void OnSelectAll();
+ afx_msg void OnSelectNone();
+ afx_msg void OnUpdateTextureMap(CCmdUI* pCmdUI);
+ afx_msg void OnUpdateModifyMaterial(CCmdUI* pCmdUI);
+ afx_msg void OnSelectInverse();
+ afx_msg void OnModifyUVMap();
+ afx_msg void OnUpdateModifyUVMap(CCmdUI* pCmdUI);
+ afx_msg void OnViewShadows();
+ afx_msg void OnUpdateViewShadows(CCmdUI* pCmdUI);
+ afx_msg void OnViewAnimatelight();
+ afx_msg void OnUpdateViewAnimatelight(CCmdUI* pCmdUI);
+ afx_msg void OnViewBumpmaps();
+ afx_msg void OnUpdateViewBumpmaps(CCmdUI* pCmdUI);
+ afx_msg void OnViewVertexshader();
+ afx_msg void OnUpdateViewVertexshader(CCmdUI* pCmdUI);
+ afx_msg void OnViewPixelshader();
+ afx_msg void OnUpdateViewPixelshader(CCmdUI* pCmdUI);
+ afx_msg void OnViewVisibleshadows();
+ afx_msg void OnUpdateViewVisibleshadows(CCmdUI* pCmdUI);
+ afx_msg void OnEditUndo();
+ afx_msg void OnUpdateEditUndo(CCmdUI* pCmdUI);
+ afx_msg void OnEditRedo();
+ afx_msg void OnUpdateEditRedo(CCmdUI* pCmdUI);
+ afx_msg void OnSurfaceProperties();
+ afx_msg void OnUpdateSurfaceProperties(CCmdUI* pCmdUI);
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+#ifndef _DEBUG // debug version in MagicView.cpp
+inline MagicDoc* MagicView::GetDocument()
+ { return (MagicDoc*)m_pDocument; }
+#endif
+
+// +--------------------------------------------------------------------+
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_MAGICVIEW_H__387B567A_8235_41B8_A993_E41567E680D7__INCLUDED_)
diff --git a/Magic2/MainFrm.cpp b/Magic2/MainFrm.cpp
new file mode 100644
index 0000000..9cc2193
--- /dev/null
+++ b/Magic2/MainFrm.cpp
@@ -0,0 +1,152 @@
+// MainFrm.cpp : implementation of the MainFrame class
+//
+
+#include "stdafx.h"
+#include "Magic.h"
+
+#include "MainFrm.h"
+#include "MagicView.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// MainFrame
+
+#include "Bitmap.h"
+
+IMPLEMENT_DYNCREATE(MainFrame, CFrameWnd)
+
+BEGIN_MESSAGE_MAP(MainFrame, CFrameWnd)
+ //{{AFX_MSG_MAP(MainFrame)
+ ON_WM_CREATE()
+ ON_WM_ACTIVATEAPP()
+ ON_COMMAND(ID_VIEW_RENDER, OnRender)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+static UINT indicators[] =
+{
+ ID_SEPARATOR, // status line indicator
+ ID_SEPARATOR, // xy indicator
+ ID_INDICATOR_CAPS,
+ ID_INDICATOR_NUM,
+ ID_INDICATOR_SCRL,
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// MainFrame construction/destruction
+
+MainFrame* MainFrame::statframe;
+
+
+MainFrame::MainFrame()
+{
+ statframe = this;
+
+}
+
+MainFrame::~MainFrame()
+{
+ Bitmap::ClearCache();
+}
+
+BOOL MainFrame::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
+{
+ if (message == WM_ENTERSIZEMOVE) {
+ MagicView* magic_view = MagicView::GetInstance();
+ if (magic_view)
+ magic_view->OnEnterSizeMove();
+ }
+
+ else if (message == WM_EXITSIZEMOVE) {
+ MagicView* magic_view = MagicView::GetInstance();
+ if (magic_view)
+ magic_view->OnExitSizeMove();
+ }
+
+ return CFrameWnd::OnWndMsg(message, wParam, lParam, pResult);
+}
+
+int MainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
+{
+ if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
+ return -1;
+
+ if (!m_wndToolBar.Create(this) ||
+ !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
+ {
+ TRACE0("Failed to create toolbar\n");
+ return -1; // fail to create
+ }
+
+ if (!m_wndStatusBar.Create(this) ||
+ !m_wndStatusBar.SetIndicators(indicators,
+ sizeof(indicators)/sizeof(UINT)))
+ {
+ TRACE0("Failed to create status bar\n");
+ return -1; // fail to create
+ }
+
+ // TODO: Remove this if you don't want tool tips or a resizeable toolbar
+ m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
+ CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
+
+ // TODO: Delete these three lines if you don't want the toolbar to
+ // be dockable
+ m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
+ EnableDocking(CBRS_ALIGN_ANY);
+ DockControlBar(&m_wndToolBar);
+
+ return 0;
+}
+
+BOOL MainFrame::PreCreateWindow(CREATESTRUCT& cs)
+{
+ if( !CFrameWnd::PreCreateWindow(cs) )
+ return FALSE;
+ // TODO: Modify the Window class or styles here by modifying
+ // the CREATESTRUCT cs
+
+ return TRUE;
+}
+
+void MainFrame::StatusXY(const char* xy)
+{
+ statframe->m_wndStatusBar.SetPaneText(1, xy, TRUE);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// MainFrame diagnostics
+
+#ifdef _DEBUG
+void MainFrame::AssertValid() const
+{
+ CFrameWnd::AssertValid();
+}
+
+void MainFrame::Dump(CDumpContext& dc) const
+{
+ CFrameWnd::Dump(dc);
+}
+
+#endif //_DEBUG
+
+/////////////////////////////////////////////////////////////////////////////
+// MainFrame message handlers
+
+
+void MainFrame::OnActivateApp(BOOL bActive, HTASK hTask)
+{
+ ((Magic*)AfxGetApp())->SetAppActivated(bActive ? true : false);
+}
+
+void MainFrame::OnRender()
+{
+ MagicView* magic_view = MagicView::GetInstance();
+ if (magic_view)
+ magic_view->SendMessage(WM_COMMAND, ID_VIEW_RENDER);
+}
diff --git a/Magic2/MainFrm.h b/Magic2/MainFrm.h
new file mode 100644
index 0000000..f7de5db
--- /dev/null
+++ b/Magic2/MainFrm.h
@@ -0,0 +1,62 @@
+// MainFrm.h : interface of the MainFrame class
+//
+/////////////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_MAINFRM_H__F5D788D5_8FA2_4A6B_94F0_7938C797BE5D__INCLUDED_)
+#define AFX_MAINFRM_H__F5D788D5_8FA2_4A6B_94F0_7938C797BE5D__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+class MainFrame : public CFrameWnd
+{
+
+protected: // create from serialization only
+ MainFrame();
+ DECLARE_DYNCREATE(MainFrame)
+
+// Attributes
+public:
+ static MainFrame* statframe;
+
+// Operations
+public:
+ static void StatusXY(const char* xy);
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(MainFrame)
+ virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
+ //}}AFX_VIRTUAL
+
+// Implementation
+public:
+ virtual ~MainFrame();
+#ifdef _DEBUG
+ virtual void AssertValid() const;
+ virtual void Dump(CDumpContext& dc) const;
+#endif
+
+protected: // control bar embedded members
+ CStatusBar m_wndStatusBar;
+ CToolBar m_wndToolBar;
+
+ virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult);
+
+// Generated message map functions
+protected:
+ //{{AFX_MSG(MainFrame)
+ afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
+ afx_msg void OnActivateApp(BOOL bActive, HTASK hTask);
+ afx_msg void OnRender();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_MAINFRM_H__F5D788D5_8FA2_4A6B_94F0_7938C797BE5D__INCLUDED_)
diff --git a/Magic2/MaterialDialog.cpp b/Magic2/MaterialDialog.cpp
new file mode 100644
index 0000000..1da19d9
--- /dev/null
+++ b/Magic2/MaterialDialog.cpp
@@ -0,0 +1,693 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: MaterialDialog.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Material Editor Dialog interface file
+*/
+
+#include "stdafx.h"
+#include "Magic.h"
+#include "MagicDoc.h"
+#include "MagicView.h"
+#include "MaterialDialog.h"
+#include "Selection.h"
+#include "Selector.h"
+#include "Thumbnail.h"
+
+#include "Bitmap.h"
+#include "Polygon.h"
+#include "Solid.h"
+#include "Video.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+inline int BlendModeToSelection(int mode)
+{
+ switch (mode) {
+ case Material::MTL_SOLID: return 0;
+ case Material::MTL_TRANSLUCENT: return 1;
+ case Material::MTL_ADDITIVE: return 2;
+ }
+
+ return 0;
+}
+
+inline int SelectionToBlendMode(int sel)
+{
+ switch (sel) {
+ case 0: return Material::MTL_SOLID;
+ case 1: return Material::MTL_TRANSLUCENT;
+ case 2: return Material::MTL_ADDITIVE;
+ }
+
+ return Material::MTL_SOLID;
+}
+
+// +--------------------------------------------------------------------+
+// MaterialDialog dialog
+// +--------------------------------------------------------------------+
+
+static Material emergency_material;
+
+MaterialDialog::MaterialDialog(MagicView* pParent /*=NULL*/)
+ : CDialog(MaterialDialog::IDD, pParent), solid(0), material(0), doc(0)
+{
+ //{{AFX_DATA_INIT(MaterialDialog)
+ mAmbientValue = 0.0;
+ mBrillianceValue = 0.0;
+ mBumpValue = 0.0;
+ mDiffuseValue = 0.0;
+ mEmissiveValue = 0.0;
+ mMaterialName = _T("");
+ mPowerValue = 0.0;
+ mSpecularValue = 0.0;
+ mSpecularTexture = _T("");
+ mDiffuseTexture = _T("");
+ mBumpTexture = _T("");
+ mEmissiveTexture = _T("");
+ mMaterialShader = _T("");
+ //}}AFX_DATA_INIT
+
+ doc = pParent->GetDocument();
+
+ if (doc && doc->GetSolid()) {
+ solid = doc->GetSolid();
+
+ Selection* seln = doc->GetSelection();
+
+ if (seln->GetPolys().size() > 0) {
+ material = seln->GetPolys().first()->material;
+ }
+ }
+}
+
+MaterialDialog::~MaterialDialog()
+{
+}
+
+
+void MaterialDialog::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(MaterialDialog)
+ DDX_Control(pDX, IDC_SHADOW, mShadowList);
+ DDX_Control(pDX, IDC_BLEND_MODE, mBlendModeList);
+ DDX_Control(pDX, IDC_MATERIAL_LIST, mMaterialList);
+ DDX_Control(pDX, IDC_MATERIAL_PREVIEW, mMaterialThumb);
+ DDX_Control(pDX, IDC_SPECULAR_COLOR, mSpecularColor);
+ DDX_Control(pDX, IDC_EMISSIVE_COLOR, mEmissiveColor);
+ DDX_Control(pDX, IDC_DIFFUSE_COLOR, mDiffuseColor);
+ DDX_Control(pDX, IDC_AMBIENT_COLOR, mAmbientColor);
+ DDX_Text(pDX, IDC_AMBIENT_VALUE, mAmbientValue);
+ DDX_Text(pDX, IDC_BRILLIANCE_VALUE, mBrillianceValue);
+ DDX_Text(pDX, IDC_BUMP_VALUE, mBumpValue);
+ DDX_Text(pDX, IDC_DIFFUSE_VALUE, mDiffuseValue);
+ DDX_Text(pDX, IDC_EMISSIVE_VALUE, mEmissiveValue);
+ DDX_Text(pDX, IDC_MATERIAL_NAME, mMaterialName);
+ DDX_Text(pDX, IDC_POWER_VALUE, mPowerValue);
+ DDX_Text(pDX, IDC_SPECULAR_VALUE, mSpecularValue);
+ DDX_Text(pDX, IDC_SPECULAR_TEXTURE, mSpecularTexture);
+ DDX_Text(pDX, IDC_DIFFUSE_TEXTURE, mDiffuseTexture);
+ DDX_Text(pDX, IDC_BUMP_TEXTURE, mBumpTexture);
+ DDX_Text(pDX, IDC_EMISSIVE_TEXTURE, mEmissiveTexture);
+ DDX_Text(pDX, IDC_MATERIAL_SHADER, mMaterialShader);
+ //}}AFX_DATA_MAP
+
+ if (!pDX->m_bSaveAndValidate) {
+ if (solid && solid->GetModel()) {
+ mMaterialList.ResetContent();
+
+ Model* model = solid->GetModel();
+ for (int i = 0; i < model->NumMaterials(); i++) {
+ Material* m = model->GetMaterials()[i];
+ mMaterialList.AddString(m->name);
+
+ if (m == material)
+ mMaterialList.SetCurSel(i);
+ }
+ }
+
+ if (mBlendModeList.GetSafeHwnd())
+ mBlendModeList.SetCurSel(BlendModeToSelection(material->blend));
+
+ if (mShadowList.GetSafeHwnd())
+ mShadowList.SetCurSel(material->shadow);
+ }
+}
+
+
+BEGIN_MESSAGE_MAP(MaterialDialog, CDialog)
+ //{{AFX_MSG_MAP(MaterialDialog)
+ ON_WM_PAINT()
+ ON_BN_CLICKED(IDC_AMBIENT_COLOR, OnAmbientColor)
+ ON_BN_CLICKED(IDC_DIFFUSE_COLOR, OnDiffuseColor)
+ ON_BN_CLICKED(IDC_EMISSIVE_COLOR, OnEmissiveColor)
+ ON_BN_CLICKED(IDC_SPECULAR_COLOR, OnSpecularColor)
+ ON_WM_DRAWITEM()
+ ON_EN_CHANGE(IDC_AMBIENT_VALUE, OnChangeMaterialValue)
+ ON_BN_CLICKED(IDC_FILE_DIFFUSE, OnFileDiffuse)
+ ON_BN_CLICKED(IDC_FILE_SPECULAR, OnFileSpecular)
+ ON_BN_CLICKED(IDC_FILE_EMISSIVE, OnFileEmissive)
+ ON_BN_CLICKED(IDC_FILE_BUMP, OnFileBump)
+ ON_EN_CHANGE(IDC_DIFFUSE_TEXTURE, OnChangeDiffuseTexture)
+ ON_EN_CHANGE(IDC_SPECULAR_TEXTURE, OnChangeSpecularTexture)
+ ON_EN_CHANGE(IDC_EMISSIVE_TEXTURE, OnChangeEmissiveTexture)
+ ON_EN_CHANGE(IDC_BUMP_TEXTURE, OnChangeBumpTexture)
+ ON_EN_CHANGE(IDC_MATERIAL_NAME, OnChangeMaterialName)
+ ON_LBN_SELCHANGE(IDC_MATERIAL_LIST, OnSelectMaterial)
+ ON_BN_CLICKED(IDC_SELECT_POLYS, OnSelectPolys)
+ ON_BN_CLICKED(IDC_NEW_MATERIAL, OnNewMaterial)
+ ON_BN_CLICKED(IDC_DEL_MATERIAL, OnDelMaterial)
+ ON_CBN_SELCHANGE(IDC_BLEND_MODE, OnSelectBlendMode)
+ ON_CBN_SELCHANGE(IDC_SHADOW, OnSelectShadow)
+ ON_EN_CHANGE(IDC_BRILLIANCE_VALUE, OnChangeMaterialValue)
+ ON_EN_CHANGE(IDC_BUMP_VALUE, OnChangeMaterialValue)
+ ON_EN_CHANGE(IDC_DIFFUSE_VALUE, OnChangeMaterialValue)
+ ON_EN_CHANGE(IDC_EMISSIVE_VALUE, OnChangeMaterialValue)
+ ON_EN_CHANGE(IDC_POWER_VALUE, OnChangeMaterialValue)
+ ON_EN_CHANGE(IDC_SPECULAR_VALUE, OnChangeMaterialValue)
+ ON_EN_CHANGE(IDC_MATERIAL_SHADER, OnChangeMaterialShader)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+// +--------------------------------------------------------------------+
+
+void MaterialDialog::UpdateMaterial()
+{
+ if (material) {
+ UpdateData();
+
+ strcpy(material->name, mMaterialName);
+ strcpy(material->shader, mMaterialShader);
+
+ material->ambient_value = mAmbientValue;
+ material->diffuse_value = mDiffuseValue;
+ material->specular_value = mSpecularValue;
+ material->emissive_value = mEmissiveValue;
+ material->power = mPowerValue;
+ material->brilliance = mBrillianceValue;
+ material->bump = mBumpValue;
+ material->blend = SelectionToBlendMode(mBlendModeList.GetCurSel());
+ material->shadow = mShadowList.GetCurSel() ? true : false;
+
+ material->Ka = ColorValue(material->ambient_color) * material->ambient_value;
+ material->Kd = ColorValue(material->diffuse_color) * material->diffuse_value;
+ material->Ks = ColorValue(material->specular_color) * material->specular_value;
+ material->Ke = ColorValue(material->emissive_color) * material->emissive_value;
+
+ material->CreateThumbnail();
+
+ InvalidateRect(NULL, FALSE);
+ }
+}
+
+// +--------------------------------------------------------------------+
+// MaterialDialog message handlers
+// +--------------------------------------------------------------------+
+
+BOOL MaterialDialog::OnInitDialog()
+{
+ if (solid && solid->GetModel() && !material) {
+ Model* model = solid->GetModel();
+
+ if (model->NumMaterials() > 0)
+ material = model->GetMaterials().first();
+ }
+
+ if (!material)
+ material = &emergency_material;
+
+ mMaterialName = material->name;
+ mMaterialShader = material->shader;
+ mAmbientValue = material->ambient_value;
+ mDiffuseValue = material->diffuse_value;
+ mSpecularValue = material->specular_value;
+ mEmissiveValue = material->emissive_value;
+ mPowerValue = material->power;
+ mBrillianceValue = material->brilliance;
+ mBumpValue = material->bump;
+
+ mDiffuseTexture = material->tex_diffuse ?
+ material->tex_diffuse->GetFilename() : "";
+
+ mSpecularTexture = material->tex_specular ?
+ material->tex_specular->GetFilename() : "";
+
+ mEmissiveTexture = material->tex_emissive ?
+ material->tex_emissive->GetFilename() : "";
+
+ mBumpTexture = material->tex_bumpmap ?
+ material->tex_bumpmap->GetFilename() : "";
+
+ CDialog::OnInitDialog();
+
+ UpdateMaterial();
+
+ return TRUE; // return TRUE unless you set the focus to a control
+ // EXCEPTION: OCX Property Pages should return FALSE
+}
+
+void MaterialDialog::OnPaint()
+{
+ CPaintDC dc(this); // device context for painting
+
+ if (material && material->thumbnail) {
+ ThumbPreview(mMaterialThumb.GetSafeHwnd(), material->thumbnail);
+ }
+}
+
+void MaterialDialog::OnAmbientColor()
+{
+ if (material) {
+ Color c = material->ambient_color;
+ COLORREF crgb = RGB(c.Red(), c.Green(), c.Blue());
+ CColorDialog chooser(crgb);
+
+ if (chooser.DoModal() == IDOK) {
+ crgb = chooser.GetColor();
+ material->ambient_color = Color(GetRValue(crgb), GetGValue(crgb), GetBValue(crgb));
+ UpdateMaterial();
+ }
+ }
+}
+
+void MaterialDialog::OnDiffuseColor()
+{
+ if (material) {
+ Color c = material->diffuse_color;
+ COLORREF crgb = RGB(c.Red(), c.Green(), c.Blue());
+ CColorDialog chooser(crgb);
+
+ if (chooser.DoModal() == IDOK) {
+ crgb = chooser.GetColor();
+ material->diffuse_color = Color(GetRValue(crgb), GetGValue(crgb), GetBValue(crgb));
+ UpdateMaterial();
+ }
+ }
+}
+
+void MaterialDialog::OnSpecularColor()
+{
+ if (material) {
+ Color c = material->specular_color;
+ COLORREF crgb = RGB(c.Red(), c.Green(), c.Blue());
+ CColorDialog chooser(crgb);
+
+ if (chooser.DoModal() == IDOK) {
+ crgb = chooser.GetColor();
+ material->specular_color = Color(GetRValue(crgb), GetGValue(crgb), GetBValue(crgb));
+ UpdateMaterial();
+ }
+ }
+}
+
+void MaterialDialog::OnEmissiveColor()
+{
+ if (material) {
+ Color c = material->emissive_color;
+ COLORREF crgb = RGB(c.Red(), c.Green(), c.Blue());
+ CColorDialog chooser(crgb);
+
+ if (chooser.DoModal() == IDOK) {
+ crgb = chooser.GetColor();
+ material->emissive_color = Color(GetRValue(crgb), GetGValue(crgb), GetBValue(crgb));
+ UpdateMaterial();
+ }
+ }
+}
+
+void MaterialDialog::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
+{
+ CWnd* wnd = GetDlgItem(nIDCtl);
+ Color c = Color::LightGray;
+
+ if (material) {
+ if (wnd->GetSafeHwnd() == mAmbientColor.GetSafeHwnd())
+ c = material->ambient_color;
+
+ else if (wnd->GetSafeHwnd() == mDiffuseColor.GetSafeHwnd())
+ c = material->diffuse_color;
+
+ else if (wnd->GetSafeHwnd() == mSpecularColor.GetSafeHwnd())
+ c = material->specular_color;
+
+ else if (wnd->GetSafeHwnd() == mEmissiveColor.GetSafeHwnd())
+ c = material->emissive_color;
+ }
+
+ CBrush brush(RGB(c.Red(), c.Green(), c.Blue()));
+
+ ::FillRect(lpDrawItemStruct->hDC,
+ &lpDrawItemStruct->rcItem,
+ brush);
+
+ CDialog::OnDrawItem(nIDCtl, lpDrawItemStruct);
+}
+
+// +--------------------------------------------------------------------+
+
+void MaterialDialog::OnChangeMaterialValue()
+{
+ UpdateMaterial();
+}
+
+// +--------------------------------------------------------------------+
+
+void MaterialDialog::OnFileTexture(int type)
+{
+ if (!material)
+ return;
+
+ char filename[512];
+ filename[0] = '\0';
+ CFileDialog ofd(TRUE, "pcx");
+
+ ofd.m_ofn.lpstrFilter = "All Image Files\0*.bmp; *.dds; *.jpg; *.pcx; *.png; *.tga\0Bitmap Files (*.bmp)\0*.bmp\0JPEG Files (*.jpg)\0*.pcx\0PCX Files (*.pcx)\0*.pcx\0PNG Files (*.png)\0*.png\0Truevision Targa Files (*.tga)\0*.png\0All Files\0*.*\0\0";
+ ofd.m_ofn.lpstrFile = filename;
+ ofd.m_ofn.nMaxFile = sizeof(filename);
+
+ if (ofd.DoModal() != IDOK)
+ return;
+
+ char tex_name[512];
+ sprintf(tex_name, "%s", ofd.GetFileName().GetBuffer(0));
+
+ ChangeFileTexture(tex_name, type);
+}
+
+void MaterialDialog::ChangeFileTexture(char* fname, int type)
+{
+ Bitmap* bmp = 0;
+
+ LoadTexture(fname, bmp);
+
+ if (bmp) {
+ switch (type) {
+ case 0:
+ material->tex_diffuse = bmp;
+ mDiffuseTexture = fname;
+ break;
+
+ case 1:
+ material->tex_specular = bmp;
+ mSpecularTexture = fname;
+ break;
+
+ case 2:
+ material->tex_emissive = bmp;
+ mEmissiveTexture = fname;
+ break;
+
+ case 3:
+ material->tex_bumpmap = bmp;
+ mBumpTexture = fname;
+ break;
+ }
+
+ UpdateData(FALSE);
+ UpdateMaterial();
+ }
+}
+
+void MaterialDialog::OnFileDiffuse()
+{
+ OnFileTexture(0);
+}
+
+void MaterialDialog::OnFileSpecular()
+{
+ OnFileTexture(1);
+}
+
+void MaterialDialog::OnFileEmissive()
+{
+ OnFileTexture(2);
+}
+
+void MaterialDialog::OnFileBump()
+{
+ OnFileTexture(3);
+}
+
+void MaterialDialog::OnChangeDiffuseTexture()
+{
+ if (material) {
+ UpdateData();
+
+ if (mDiffuseTexture.GetLength() < 1) {
+ material->tex_diffuse = 0;
+ UpdateMaterial();
+ return;
+ }
+
+ char* filename = mDiffuseTexture.LockBuffer();
+ ChangeFileTexture(filename, 0);
+ mDiffuseTexture.UnlockBuffer();
+ }
+}
+
+void MaterialDialog::OnChangeSpecularTexture()
+{
+ if (material) {
+ UpdateData();
+
+ if (mSpecularTexture.GetLength() < 1) {
+ material->tex_specular = 0;
+ UpdateMaterial();
+ return;
+ }
+
+ char* filename = mSpecularTexture.LockBuffer();
+ ChangeFileTexture(filename, 1);
+ mSpecularTexture.UnlockBuffer();
+ }
+}
+
+void MaterialDialog::OnChangeEmissiveTexture()
+{
+ if (material) {
+ UpdateData();
+
+ if (mEmissiveTexture.GetLength() < 1) {
+ material->tex_emissive = 0;
+ UpdateMaterial();
+ return;
+ }
+
+ char* filename = mEmissiveTexture.LockBuffer();
+ ChangeFileTexture(filename, 2);
+ mEmissiveTexture.UnlockBuffer();
+ }
+}
+
+void MaterialDialog::OnChangeBumpTexture()
+{
+ if (material) {
+ UpdateData();
+
+ if (mBumpTexture.GetLength() < 1) {
+ material->tex_bumpmap = 0;
+ UpdateMaterial();
+ return;
+ }
+
+ char* filename = mBumpTexture.LockBuffer();
+ ChangeFileTexture(filename, 3);
+ mBumpTexture.UnlockBuffer();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void MaterialDialog::OnChangeMaterialName()
+{
+ if (material) {
+ UpdateData();
+ strcpy(material->name, mMaterialName);
+ }
+}
+
+void MaterialDialog::OnChangeMaterialShader()
+{
+ if (material) {
+ UpdateData();
+ strcpy(material->shader, mMaterialShader);
+ }
+}
+
+void MaterialDialog::OnSelectMaterial()
+{
+ int selected = mMaterialList.GetCurSel();
+ Material* mtl = 0;
+
+ if (solid && solid->GetModel()) {
+ Model* model = solid->GetModel();
+
+ if (model->NumMaterials() > 0 && selected < model->NumMaterials())
+ mtl = model->GetMaterials()[selected];
+ }
+
+ if (!mtl)
+ mtl = &emergency_material;
+
+ if (material != mtl) {
+ material = mtl;
+
+ mMaterialName = material->name;
+ mMaterialShader = material->shader;
+ mAmbientValue = material->ambient_value;
+ mDiffuseValue = material->diffuse_value;
+ mSpecularValue = material->specular_value;
+ mEmissiveValue = material->emissive_value;
+ mPowerValue = material->power;
+ mBrillianceValue = material->brilliance;
+ mBumpValue = material->bump;
+
+ mDiffuseTexture = material->tex_diffuse ?
+ material->tex_diffuse->GetFilename() : "";
+
+ mSpecularTexture = material->tex_specular ?
+ material->tex_specular->GetFilename() : "";
+
+ mEmissiveTexture = material->tex_emissive ?
+ material->tex_emissive->GetFilename() : "";
+
+ mBumpTexture = material->tex_bumpmap ?
+ material->tex_bumpmap->GetFilename() : "";
+
+ if (mBlendModeList.GetSafeHwnd())
+ mBlendModeList.SetCurSel(BlendModeToSelection(material->blend));
+
+ if (mShadowList.GetSafeHwnd())
+ mShadowList.SetCurSel(material->shadow);
+
+ UpdateData(FALSE);
+ UpdateMaterial();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void MaterialDialog::OnNewMaterial()
+{
+ if (solid && material && material != &emergency_material) {
+ Model* model = solid->GetModel();
+ Material* mtl = new Material;
+
+ if (model && mtl) {
+ mtl->Ka = Color::DarkGray;
+ mtl->Kd = Color::LightGray;
+ mtl->Ks = ColorValue(0.1f,0.1f,0.1f);
+ mtl->power = 10.0f;
+
+ mtl->ambient_value = 0.2f;
+ mtl->ambient_color = Color::DarkGray;
+ mtl->diffuse_value = 0.8f;
+ mtl->diffuse_color = Color::LightGray;
+ mtl->specular_value = 0.5f;
+ mtl->specular_color = Color::White;
+ strcpy(mtl->name, "(new)");
+
+ model->GetMaterials().append(mtl);
+
+ material = 0;
+ mMaterialList.AddString(mtl->name);
+ mMaterialList.SetCurSel(model->NumMaterials()-1);
+
+ mBlendModeList.SetCurSel(0);
+ mShadowList.SetCurSel(1);
+
+ OnSelectMaterial();
+ }
+ }
+}
+
+void MaterialDialog::OnDelMaterial()
+{
+ if (solid && material && material != &emergency_material) {
+ Model* model = solid->GetModel();
+
+ // do not delete the last material:
+ if (model->NumMaterials() > 1 && model->GetMaterials().contains(material)) {
+ Material* mtl = model->GetMaterials().first();
+
+ if (mtl == material)
+ mtl = model->GetMaterials()[1];
+
+ // reassign the material for any polys and segments
+ // that are using the one we are about to delete:
+ ListIter<Surface> iter = model->GetSurfaces();
+
+ while (++iter) {
+ Surface* s = iter.value();
+
+ for (int i = 0; i < s->NumPolys(); i++) {
+ Poly* p = s->GetPolys() + i;
+
+ if (p->material == material) {
+ p->material = mtl;
+ }
+ }
+
+ ListIter<Segment> seg_iter = s->GetSegments();
+ while (++seg_iter) {
+ Segment* segment = seg_iter.value();
+ if (segment->material == material)
+ segment->material = mtl;
+ }
+ }
+
+ // now delete the material:
+ model->GetMaterials().remove(material);
+ delete material;
+
+ material = 0;
+ mMaterialList.SetCurSel(0);
+ OnSelectMaterial();
+ }
+ }
+}
+
+void MaterialDialog::OnSelectPolys()
+{
+ if (solid && doc && doc->GetSelector()) {
+ Selector* selector = doc->GetSelector();
+
+ if (!material || material == &emergency_material)
+ selector->SelectMaterial(0);
+ else
+ selector->SelectMaterial(material);
+ }
+}
+
+void MaterialDialog::OnSelectBlendMode()
+{
+ if (material)
+ material->blend = SelectionToBlendMode(mBlendModeList.GetCurSel());
+}
+
+void MaterialDialog::OnSelectShadow()
+{
+ if (material)
+ material->shadow = mShadowList.GetCurSel() ? true : false;
+}
+
+void MaterialDialog::OnOK()
+{
+ Video* video = Video::GetInstance();
+ if (video)
+ video->InvalidateCache();
+
+ CDialog::OnOK();
+}
diff --git a/Magic2/MaterialDialog.h b/Magic2/MaterialDialog.h
new file mode 100644
index 0000000..b6fabcf
--- /dev/null
+++ b/Magic2/MaterialDialog.h
@@ -0,0 +1,117 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: MaterialDialog.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Material Editor Dialog interface file
+*/
+
+#if !defined(AFX_MATERIALDIALOG_H__DD978D83_EB03_479B_92A4_D1FCC333BECA__INCLUDED_)
+#define AFX_MATERIALDIALOG_H__DD978D83_EB03_479B_92A4_D1FCC333BECA__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+// +--------------------------------------------------------------------+
+
+class MagicDoc;
+class MagicView;
+struct Material;
+
+// +--------------------------------------------------------------------+
+// MaterialDialog dialog
+// +--------------------------------------------------------------------+
+
+class MaterialDialog : public CDialog
+{
+// Construction
+public:
+ MaterialDialog(MagicView* pParent = NULL);
+ virtual ~MaterialDialog();
+
+// Dialog Data
+ //{{AFX_DATA(MaterialDialog)
+ enum { IDD = IDD_MODIFY_MATERIAL };
+ CComboBox mShadowList;
+ CComboBox mBlendModeList;
+ CListBox mMaterialList;
+ CStatic mMaterialThumb;
+ CStatic mSpecularColor;
+ CStatic mEmissiveColor;
+ CStatic mDiffuseColor;
+ CStatic mAmbientColor;
+ float mAmbientValue;
+ float mBrillianceValue;
+ float mBumpValue;
+ float mDiffuseValue;
+ float mEmissiveValue;
+ CString mMaterialName;
+ float mPowerValue;
+ float mSpecularValue;
+ CString mSpecularTexture;
+ CString mDiffuseTexture;
+ CString mBumpTexture;
+ CString mEmissiveTexture;
+ CString mMaterialShader;
+ //}}AFX_DATA
+
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(MaterialDialog)
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+ void UpdateMaterial();
+ void OnFileTexture(int type);
+ void ChangeFileTexture(char* fname, int type);
+
+ MagicDoc* doc;
+ Solid* solid;
+ Material* material;
+
+ // Generated message map functions
+ //{{AFX_MSG(MaterialDialog)
+ virtual BOOL OnInitDialog();
+ afx_msg void OnPaint();
+ afx_msg void OnAmbientColor();
+ afx_msg void OnDiffuseColor();
+ afx_msg void OnEmissiveColor();
+ afx_msg void OnSpecularColor();
+ afx_msg void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct);
+ afx_msg void OnChangeMaterialValue();
+ afx_msg void OnFileDiffuse();
+ afx_msg void OnFileSpecular();
+ afx_msg void OnFileEmissive();
+ afx_msg void OnFileBump();
+ afx_msg void OnChangeDiffuseTexture();
+ afx_msg void OnChangeSpecularTexture();
+ afx_msg void OnChangeEmissiveTexture();
+ afx_msg void OnChangeBumpTexture();
+ afx_msg void OnChangeMaterialName();
+ afx_msg void OnSelectMaterial();
+ afx_msg void OnSelectPolys();
+ afx_msg void OnNewMaterial();
+ afx_msg void OnDelMaterial();
+ afx_msg void OnSelectBlendMode();
+ afx_msg void OnSelectShadow();
+ virtual void OnOK();
+ afx_msg void OnChangeMaterialShader();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif
diff --git a/Magic2/ModelFile3DS.cpp b/Magic2/ModelFile3DS.cpp
new file mode 100644
index 0000000..f4381bb
--- /dev/null
+++ b/Magic2/ModelFile3DS.cpp
@@ -0,0 +1,283 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: ModelFile3DS.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ File loader for 3DStudio MAX 3DS format models
+*/
+
+#include "stdafx.h"
+#include "Magic.h"
+#include "MagicDoc.h"
+#include "ModelFile3DS.h"
+
+#include "Bitmap.h"
+#include "Polygon.h"
+#include "Text.h"
+#include "List.h"
+#include "l3ds.h"
+
+// +--------------------------------------------------------------------+
+
+ModelFile3DS::ModelFile3DS(const char* fname)
+ : ModelFile(fname)
+{
+}
+
+ModelFile3DS::~ModelFile3DS()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+static int mcomp(const void* a, const void* b)
+{
+ Poly* pa = (Poly*) a;
+ Poly* pb = (Poly*) b;
+
+ if (pa->sortval == pb->sortval)
+ return 0;
+
+ if (pa->sortval < pb->sortval)
+ return 1;
+
+ return -1;
+}
+
+bool
+ModelFile3DS::Load(Model* model, double scale)
+{
+ if (model && scale > 0 && strlen(filename) > 0) {
+ ModelFile::Load(model, scale);
+
+ L3DS loader;
+
+ if (!loader.LoadFile(filename)) {
+ ::MessageBox(0, "3DS Import Failed: Magic could not open the file for reading", "ERROR", MB_OK);
+ return false;
+ }
+
+ int ntex = 0;
+ int nsurfs = 0;
+ int nverts = 0;
+ int npolys = 0;
+ int nmatls = 0;
+ int nmeshs = loader.GetMeshCount();
+
+ int* mesh_verts = new int[nmeshs];
+
+ for (int m = 0; m < nmeshs; m++) {
+ LMesh& mesh = loader.GetMesh(m);
+
+ mesh_verts[m] = nverts;
+
+ // count verts and polys:
+ nverts += mesh.GetVertexCount();
+ npolys += mesh.GetTriangleCount();
+ }
+
+ if (nverts > Model::MAX_VERTS || npolys > Model::MAX_POLYS) {
+ ::MessageBox(0, "3DS Import Failed: model was too large (max 16000 polys)", "ERROR", MB_OK);
+ delete [] mesh_verts;
+ return FALSE;
+ }
+
+ // get materials:
+ nmatls = loader.GetMaterialCount();
+
+ for (int i = 0; i < nmatls; i++) {
+ LMaterial& matl = loader.GetMaterial(i);
+ LMap& tex_diff = matl.GetTextureMap1();
+ LMap& tex_spec = matl.GetSpecularMap();
+ LMap& tex_bump = matl.GetBumpMap();
+ LMap& tex_glow = matl.GetTextureMap2();
+
+ Material* material = new Material;
+
+ strncpy(material->name, matl.GetName().c_str(), Material::NAMELEN);
+
+ material->Ka.Set( matl.GetAmbientColor().r,
+ matl.GetAmbientColor().g,
+ matl.GetAmbientColor().b );
+
+ material->ambient_value = 1.0f;
+ material->ambient_color = material->Ka.ToColor();
+
+ material->Kd.Set( matl.GetDiffuseColor().r,
+ matl.GetDiffuseColor().g,
+ matl.GetDiffuseColor().b );
+
+ material->diffuse_value = 1.0f;
+ material->diffuse_color = material->Kd.ToColor();
+
+ material->Ks.Set( matl.GetSpecularColor().r,
+ matl.GetSpecularColor().g,
+ matl.GetSpecularColor().b );
+
+ material->specular_value = 1.0f;
+ material->specular_color = material->Ks.ToColor();
+
+ material->power = matl.GetShininess() * 100;
+
+ if (tex_diff.mapName[0])
+ LoadTexture(tex_diff.mapName, material->tex_diffuse);
+
+ if (tex_spec.mapName[0])
+ LoadTexture(tex_spec.mapName, material->tex_specular);
+
+ if (tex_bump.mapName[0])
+ LoadTexture(tex_bump.mapName, material->tex_bumpmap);
+
+ if (tex_glow.mapName[0])
+ LoadTexture(tex_glow.mapName, material->tex_emissive);
+
+ model->GetMaterials().append(material);
+ }
+
+ Surface* surface = new Surface;
+ model->GetSurfaces().append(surface);
+
+ surface->CreateVerts(nverts);
+ surface->CreatePolys(npolys);
+
+ VertexSet* vset = surface->GetVertexSet();
+ Poly* polys = surface->GetPolys();
+ float radius = 0;
+ int v = 0;
+
+ for (m = 0; m < nmeshs && v < nverts; m++) {
+ LMesh& mesh = loader.GetMesh(m);
+
+ // read vertex set:
+ for (i = 0; i < mesh.GetVertexCount() && v < nverts; i++) {
+ vset->loc[v].x = mesh.GetVertex(i).x;
+ vset->loc[v].y = mesh.GetVertex(i).z;
+ vset->loc[v].z = mesh.GetVertex(i).y;
+
+ vset->nrm[v].x = mesh.GetNormal(i).x;
+ vset->nrm[v].y = mesh.GetNormal(i).z;
+ vset->nrm[v].z = mesh.GetNormal(i).y;
+
+ vset->tu[v] = mesh.GetUV(i).x;
+ vset->tv[v] = mesh.GetUV(i).y;
+
+ float d = vset->loc[v].length();
+ if (d > radius)
+ radius = d;
+
+ v++;
+ }
+ }
+
+ if (radius < 16) {
+ model->ScaleBy(256.0f / radius);
+ radius = 256.0f;
+ }
+
+ int n = 0;
+
+ for (m = 0; m < nmeshs && n < npolys; m++) {
+ LMesh& mesh = loader.GetMesh(m);
+
+ // read polys:
+ int mesh_tris = mesh.GetTriangleCount();
+ for (int i = 0; i < mesh_tris && n < npolys; i++) {
+ Poly* p = surface->GetPolys() + n++;
+ const LTriangle& tri = mesh.GetTriangle(i);
+ LTriangle2 tri2 = mesh.GetTriangle2(i);
+
+ p->nverts = 3;
+
+ p->verts[0] = tri.a + mesh_verts[m];
+ p->verts[1] = tri.b + mesh_verts[m];
+ p->verts[2] = tri.c + mesh_verts[m];
+
+ if (p->verts[0] > nverts || p->verts[1] > nverts || p->verts[2] > nverts) {
+ p->verts[0] = 0;
+ p->verts[1] = 1;
+ p->verts[2] = 2;
+ }
+
+ if (tri2.vertexNormals[0] == tri2.vertexNormals[1] &&
+ tri2.vertexNormals[0] == tri2.vertexNormals[2])
+ p->flatness = 1.0f;
+ else
+ p->flatness = 0.0f;
+
+ p->vertex_set = vset;
+ p->material = model->GetMaterials()[ tri2.materialId ];
+ p->sortval = tri2.materialId;
+
+ p->plane = Plane(vset->loc[ p->verts[0] ],
+ vset->loc[ p->verts[2] ],
+ vset->loc[ p->verts[1] ]);
+
+ surface->AddIndices(p->nverts);
+ }
+ }
+
+ // sort the polys by material index:
+ qsort((void*) polys, npolys, sizeof(Poly), mcomp);
+
+ // then assign them to cohesive segments:
+ Segment* segment = 0;
+
+ for (n = 0; n < npolys; n++) {
+ if (segment && segment->material == polys[n].material) {
+ segment->npolys++;
+ }
+ else {
+ segment = 0;
+ }
+
+ if (!segment) {
+ segment = new Segment;
+
+ segment->npolys = 1;
+ segment->polys = &polys[n];
+ segment->material = segment->polys->material;
+
+ surface->GetSegments().append(segment);
+ }
+ }
+
+ *pnverts = nverts;
+ *pnpolys = npolys;
+ *pradius = radius;
+
+ model->Normalize();
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+ModelFile3DS::Save(Model* m)
+{
+ if (m) {
+ ModelFile::Save(m);
+
+ FILE* f = fopen(filename, "w");
+ if (!f) {
+ ::MessageBox(0, "3DS Export Failed: Magic could not open the file for writing", "ERROR", MB_OK);
+ return false;
+ }
+
+ fclose(f);
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
diff --git a/Magic2/ModelFile3DS.h b/Magic2/ModelFile3DS.h
new file mode 100644
index 0000000..bf49d09
--- /dev/null
+++ b/Magic2/ModelFile3DS.h
@@ -0,0 +1,37 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: ModelFile3DS.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ File loader for 3DStudio MAX 3DS format models
+*/
+
+#ifndef ModelFile3DS_h
+#define ModelFile3DS_h
+
+#include "Solid.h"
+
+// +--------------------------------------------------------------------+
+
+class ModelFile3DS : public ModelFile
+{
+public:
+ ModelFile3DS(const char* fname);
+ virtual ~ModelFile3DS();
+
+ virtual bool Load(Model* m, double scale=1.0);
+ virtual bool Save(Model* m);
+
+protected:
+};
+
+// +--------------------------------------------------------------------+
+
+#endif ModelFile3DS_h
+
diff --git a/Magic2/ModelFileMAG.cpp b/Magic2/ModelFileMAG.cpp
new file mode 100644
index 0000000..829d5e1
--- /dev/null
+++ b/Magic2/ModelFileMAG.cpp
@@ -0,0 +1,824 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: ModelFileMAG.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ File loader for MAG format models
+*/
+
+#include "stdafx.h"
+#include "Magic.h"
+#include "MagicDoc.h"
+#include "ModelFileMAG.h"
+
+#include "Bitmap.h"
+#include "Polygon.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+struct MaterialMag6 {
+ char name[Material::NAMELEN];
+ char shader[Material::NAMELEN];
+ float power; // highlight sharpness (big=shiny)
+ float brilliance; // diffuse power function
+ float bump; // bump level (0=none)
+ DWORD blend; // alpha blend type
+ bool shadow; // material casts shadow
+ bool luminous; // verts have their own lighting
+
+ Color ambient_color;
+ Color diffuse_color;
+ Color specular_color;
+ Color emissive_color;
+
+ float ambient_value;
+ float diffuse_value;
+ float specular_value;
+ float emissive_value;
+
+ BYTE tex_diffuse;
+ BYTE tex_specular;
+ BYTE tex_bumpmap;
+ BYTE tex_emissive;
+};
+
+// +--------------------------------------------------------------------+
+
+ModelFileMAG::ModelFileMAG(const char* fname)
+ : ModelFile(fname)
+{
+}
+
+ModelFileMAG::~ModelFileMAG()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+ModelFileMAG::Load(Model* m, double scale)
+{
+ if (m && scale > 0 && strlen(filename) > 0) {
+ ModelFile::Load(m, scale);
+
+ bool result = false;
+ FILE* fp = fopen(filename, "rb");
+
+ // check MAG file:
+ if (!fp) {
+ ::MessageBox(0, "File Open Failed:\nMagic could not open the requested file.", "ERROR", MB_OK);
+ return result;
+ }
+
+ ZeroMemory(pname, 64);
+ strncpy(pname, filename, 63);
+
+ char file_id[5];
+ fread(file_id, 4, 1, fp);
+ file_id[4] = '\0';
+ int version = 1;
+
+ if (!strcmp(file_id, "MAG6")) {
+ version = 6;
+ }
+ else if (!strcmp(file_id, "MAG5")) {
+ version = 5;
+ }
+ else if (!strcmp(file_id, "MAG4")) {
+ version = 4;
+ }
+ else {
+ ::MessageBox(0, "File Open Failed:\nThe requested file uses an invalid format.", "ERROR", MB_OK);
+ fclose(fp);
+ return result;
+ }
+
+ // get ready to load, delete existing model:
+ m->GetSurfaces().destroy();
+ m->GetMaterials().destroy();
+ *pnverts = 0;
+ *pnpolys = 0;
+
+ // now load the model:
+ switch (version) {
+ case 4:
+ case 5:
+ result = LoadMag5(fp, m, scale);
+ break;
+
+ case 6:
+ result = LoadMag6(fp, m, scale);
+ break;
+
+ default:
+ break;
+ }
+
+ fclose(fp);
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+ModelFileMAG::Save(Model* m)
+{
+ if (m) {
+ ModelFile::Save(m);
+
+ FILE* fp = fopen(filename, "wb");
+ if (!fp) {
+ ::MessageBox(0, "Save Failed:\nMagic could not open the file for writing.", "ERROR", MB_OK);
+ return FALSE;
+ }
+
+ fwrite("MAG6", 4, 1, fp);
+
+ int i = 0;
+ int ntex = 0;
+ int nmtls = 0;
+ int nsurfs = m->NumSurfaces();
+ List<Bitmap> textures;
+
+ ListIter<Material> m_iter = m->GetMaterials();
+ while (++m_iter) {
+ Material* mtl = m_iter.value();
+ Bitmap* bmp = mtl->tex_diffuse;
+
+ if (bmp && !textures.contains(bmp)) {
+ textures.append(bmp);
+ }
+
+ bmp = mtl->tex_specular;
+
+ if (bmp && !textures.contains(bmp)) {
+ textures.append(bmp);
+ }
+
+ bmp = mtl->tex_emissive;
+
+ if (bmp && !textures.contains(bmp)) {
+ textures.append(bmp);
+ }
+
+ bmp = mtl->tex_bumpmap;
+
+ if (bmp && !textures.contains(bmp)) {
+ textures.append(bmp);
+ }
+
+ nmtls++;
+ }
+
+ ListIter<Bitmap> t_iter = textures;
+ while (++t_iter) {
+ Bitmap* bmp = t_iter.value();
+ ntex += strlen(bmp->GetFilename()) + 1;
+ }
+
+ nsurfs = m->GetSurfaces().size();
+
+ fwrite(&ntex, 4, 1, fp);
+ fwrite(&nmtls, 4, 1, fp);
+ fwrite(&nsurfs, 4, 1, fp);
+
+ if (ntex) {
+ t_iter.reset();
+ while (++t_iter) {
+ Bitmap* bmp = t_iter.value();
+
+ fwrite(bmp->GetFilename(),
+ strlen(bmp->GetFilename()) + 1,
+ 1,
+ fp);
+ }
+ }
+
+ if (nmtls) {
+ m_iter.reset();
+ while (++m_iter) {
+ Material* mtl = m_iter.value();
+ MaterialMag6 m6;
+
+ ZeroMemory(&m6, sizeof(m6));
+
+ CopyMemory(m6.name, mtl->name, Material::NAMELEN);
+ CopyMemory(m6.shader, mtl->shader, Material::NAMELEN);
+
+ m6.ambient_value = mtl->ambient_value;
+ m6.ambient_color = mtl->ambient_color;
+ m6.diffuse_value = mtl->diffuse_value;
+ m6.diffuse_color = mtl->diffuse_color;
+ m6.specular_value = mtl->specular_value;
+ m6.specular_color = mtl->specular_color;
+ m6.emissive_value = mtl->emissive_value;
+ m6.emissive_color = mtl->emissive_color;
+
+ m6.power = mtl->power;
+ m6.brilliance = mtl->brilliance;
+ m6.bump = mtl->bump;
+ m6.blend = mtl->blend;
+ m6.shadow = mtl->shadow;
+ m6.luminous = mtl->luminous;
+
+ if (mtl->tex_diffuse)
+ m6.tex_diffuse = textures.index(mtl->tex_diffuse) + 1;
+
+ if (mtl->tex_specular)
+ m6.tex_specular = textures.index(mtl->tex_specular) + 1;
+
+ if (mtl->tex_emissive)
+ m6.tex_emissive = textures.index(mtl->tex_emissive) + 1;
+
+ if (mtl->tex_bumpmap)
+ m6.tex_bumpmap = textures.index(mtl->tex_bumpmap) + 1;
+
+ fwrite(&m6, sizeof(m6), 1, fp);
+ }
+ }
+
+ ListIter<Surface> s_iter = m->GetSurfaces();
+ while (++s_iter) {
+ Surface* s = s_iter.value();
+
+ int nverts = s->NumVerts();
+ int npolys = s->NumPolys();
+ BYTE namelen = strlen(s->Name()) + 1;
+
+ fwrite(&nverts, 4, 1, fp);
+ fwrite(&npolys, 4, 1, fp);
+ fwrite(&namelen, 1, 1, fp);
+ fwrite(s->Name(), 1, namelen, fp);
+
+ VertexSet* vset = s->GetVertexSet();
+ Poly* polys = s->GetPolys();
+
+ // write vertex set:
+ for (int v = 0; v < nverts; v++) {
+ fwrite(&vset->loc[v], sizeof(float), 3, fp);
+ fwrite(&vset->nrm[v], sizeof(float), 3, fp);
+ fwrite(&vset->tu[v], sizeof(float), 1, fp);
+ fwrite(&vset->tv[v], sizeof(float), 1, fp);
+ }
+
+ // write polys:
+ for (int n = 0; n < npolys; n++) {
+ Poly& poly = polys[n];
+ BYTE poly_nverts = (BYTE) poly.nverts;
+ BYTE material_index = 0;
+ WORD poly_verts[8];
+
+ m_iter.reset();
+ while (++m_iter && !material_index) {
+ if (poly.material == m_iter.value())
+ material_index = m_iter.index() + 1;
+ }
+
+ for (int i = 0; i < poly_nverts; i++) {
+ poly_verts[i] = poly.verts[i];
+ }
+
+ fwrite(&poly_nverts, sizeof(BYTE), 1, fp);
+ fwrite(&material_index, sizeof(BYTE), 1, fp);
+ fwrite(&poly_verts[0], sizeof(WORD), poly_nverts, fp);
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+struct HomogenousPlane
+{
+ double distance;
+ double normal_x;
+ double normal_y;
+ double normal_z;
+ double normal_w;
+};
+
+static void LoadPlane(Plane& p, FILE* fp)
+{
+ HomogenousPlane tmp;
+ fread(&tmp, sizeof(HomogenousPlane), 1, fp);
+}
+
+static void LoadFlags(LPDWORD flags, FILE* fp)
+{
+ DWORD magic_flags;
+ fread(&magic_flags, sizeof(DWORD), 1, fp);
+
+ /** MAGIC FLAGS
+ enum { FLAT_SHADED = 1,
+ LUMINOUS = 2,
+ TRANSLUCENT = 4, \\ must swap
+ CHROMAKEY = 8, // these two
+ FOREGROUND = 16, -- not used
+ WIREFRAME = 32, -- not used
+ SPECULAR1 = 64,
+ SPECULAR2 = 128 };
+ ***/
+
+ const DWORD magic_mask = 0x0fc3;
+
+ *flags = magic_flags & magic_mask;
+}
+
+// +--------------------------------------------------------------------+
+
+static int mcomp(const void* a, const void* b)
+{
+ Poly* pa = (Poly*) a;
+ Poly* pb = (Poly*) b;
+
+ if (pa->sortval == pb->sortval)
+ return 0;
+
+ if (pa->sortval < pb->sortval)
+ return -1;
+
+ return 1;
+}
+
+bool
+ModelFileMAG::LoadMag5(FILE* fp, Model* m, double scale)
+{
+ bool result = false;
+ int ntex = 0;
+ int nsurfs = 0;
+ double radius = 0;
+
+ fread(&ntex, sizeof(ntex), 1, fp);
+ fread(&nsurfs, sizeof(nsurfs), 1, fp);
+
+ // create a default gray material:
+ Material* mtl = new Material;
+
+ if (mtl) {
+ mtl->Ka = Color::DarkGray;
+ mtl->Kd = Color::LightGray;
+ mtl->Ks = ColorValue(0.1f,0.1f,0.1f);
+ mtl->power = 10.0f;
+
+ mtl->ambient_value = 0.2f;
+ mtl->ambient_color = Color::DarkGray;
+ mtl->diffuse_value = 0.8f;
+ mtl->diffuse_color = Color::LightGray;
+ mtl->specular_value = 0.5f;
+ mtl->specular_color = Color::White;
+ strcpy(mtl->name, "(default)");
+
+ m->GetMaterials().append(mtl);
+ }
+
+ // read texture list:
+ for (int i = 0; i < ntex; i++) {
+ Material* mtl = new Material;
+ char tname[32];
+
+ if (mtl) {
+ mtl->Ka = ColorValue(0.5f,0.5f,0.5f);
+ mtl->Kd = ColorValue(1.0f,1.0f,1.0f);
+ mtl->Ks = ColorValue(0.2f,0.2f,0.2f);
+ mtl->power = 20.0f;
+
+ mtl->ambient_value = 1.0f;
+ mtl->ambient_color = Color::Gray;
+ mtl->diffuse_value = 1.0f;
+ mtl->diffuse_color = Color::White;
+ mtl->specular_value = 0.2f;
+ mtl->specular_color = Color::White;
+
+ fread(tname, 32, 1, fp);
+ LoadTexture(tname, mtl->tex_diffuse, Bitmap::BMP_SOLID);
+ strcpy(mtl->name, tname);
+
+ char* dot = strrchr(mtl->name, '.');
+ if (dot)
+ *dot = 0;
+
+ char* plus = strrchr(mtl->name, '+');
+ if (plus)
+ *plus = 0;
+
+ m->GetMaterials().append(mtl);
+ }
+ }
+
+ int nverts = 0;
+ int npolys = 0;
+
+ fread(&nverts, 4, 1, fp);
+ fread(&npolys, 4, 1, fp);
+
+ // plan on creating four verts per poly:
+ int mag_nverts = nverts;
+ int next_vert = nverts;
+
+ nverts = npolys * 4;
+ Surface* s = new Surface;
+ VertexSet* vset = 0;
+ Poly* polys = 0;
+
+ if (s) {
+ s->SetName("default");
+ s->CreateVerts(nverts);
+ s->CreatePolys(npolys);
+
+ vset = s->GetVertexSet();
+ polys = s->GetPolys();
+
+ ZeroMemory(vset->loc, nverts * sizeof(Vec3));
+ ZeroMemory(vset->diffuse, nverts * sizeof(DWORD));
+ ZeroMemory(vset->specular, nverts * sizeof(DWORD));
+ ZeroMemory(vset->tu, nverts * sizeof(float));
+ ZeroMemory(vset->tv, nverts * sizeof(float));
+ ZeroMemory(vset->rw, nverts * sizeof(float));
+
+ // read vertex set:
+ for (int v = 0; v < mag_nverts; v++) {
+ Vec3 vert, norm;
+ DWORD vstate;
+
+ fread(&vert, sizeof(Vec3), 1, fp);
+ fread(&norm, sizeof(Vec3), 1, fp);
+ fread(&vstate, sizeof(DWORD), 1, fp);
+
+ vert.SwapYZ();
+ vert *= (float) scale;
+
+ norm.SwapYZ();
+
+ vset->loc[v] = vert;
+ vset->nrm[v] = norm;
+
+ double d = vert.length();
+ if (d > radius)
+ radius = (float) d;
+ }
+
+ while (v < nverts)
+ vset->nrm[v++] = Vec3(1,0,0);
+
+ // read polys:
+ Vec3 dummy_center;
+ DWORD dummy_flags;
+ DWORD dummy_color;
+ int texture_num;
+ int poly_nverts;
+ int vert_index_buffer[32];
+ float texture_index_buffer[32];
+
+ for (int n = 0; n < npolys; n++) {
+ Poly& poly = polys[n];
+ poly.vertex_set = vset;
+
+ fread(&dummy_flags, sizeof(DWORD), 1, fp);
+ fread(&dummy_center, sizeof(Vec3), 1, fp);
+ LoadPlane(poly.plane, fp);
+ fread(&dummy_color, sizeof(DWORD), 1, fp);
+ fread(&texture_num, sizeof(int), 1, fp);
+
+ if (texture_num >= 0 && texture_num < ntex) {
+ texture_num++;
+
+ poly.material = m->GetMaterials()[texture_num];
+ poly.sortval = texture_num;
+
+ if (dummy_flags & 2) { // luminous
+ Material* mtl = m->GetMaterials()[texture_num];
+
+ mtl->ambient_value = 0.0f;
+ mtl->ambient_color = Color::Black;
+ mtl->diffuse_value = 0.0f;
+ mtl->diffuse_color = Color::Black;
+ mtl->specular_value = 0.0f;
+ mtl->specular_color = Color::Black;
+ mtl->emissive_value = 1.0f;
+ mtl->emissive_color = Color::White;
+
+ mtl->Ka = ColorValue(0,0,0,0);
+ mtl->Kd = ColorValue(0,0,0,0);
+ mtl->Ks = ColorValue(0,0,0,0);
+ mtl->Ke = ColorValue(1,1,1,1);
+
+ mtl->tex_emissive = mtl->tex_diffuse;
+ }
+ }
+ else {
+ poly.material = m->GetMaterials().first(); // default material
+ poly.sortval = 1000;
+ }
+
+ // hack: store flat shaded flag in unused visible byte
+ poly.visible = (BYTE) (dummy_flags & 1);
+
+ fread(&poly_nverts, sizeof(int), 1, fp);
+ fread(vert_index_buffer, sizeof(int), poly_nverts, fp);
+
+ if (poly_nverts == 3)
+ s->AddIndices(3);
+
+ else if (poly_nverts == 4)
+ s->AddIndices(6);
+
+ poly.nverts = poly_nverts;
+ for (int vi = 0; vi < poly_nverts; vi++) {
+ int v = vert_index_buffer[vi];
+
+ if (vset->rw[v] > 0) {
+ vset->CopyVertex(next_vert, v);
+ v = next_vert++;
+ }
+
+ vset->rw[v] = 1;
+ poly.verts[vi] = v;
+ }
+
+ fread(texture_index_buffer, sizeof(float), poly_nverts, fp); // tu's
+ for (vi = 0; vi < poly_nverts; vi++) {
+ int v = poly.verts[vi];
+ vset->tu[v] = texture_index_buffer[vi];
+ }
+
+ fread(texture_index_buffer, sizeof(float), poly_nverts, fp); // tv's
+ for (vi = 0; vi < poly_nverts; vi++) {
+ int v = poly.verts[vi];
+ vset->tv[v] = texture_index_buffer[vi];
+ }
+
+ DWORD unused[32];
+ fread(unused, 16, 1, fp);
+ }
+
+ // pass 2 (adjust vertex normals for flat polys):
+ for (n = 0; n < npolys; n++) {
+ Poly& poly = polys[n];
+
+ poly.plane = Plane(vset->loc[poly.verts[0]],
+ vset->loc[poly.verts[2]],
+ vset->loc[poly.verts[1]]);
+
+ // hack: retrieve flat shaded flag from unused visible byte
+ if (poly.visible) {
+ int poly_nverts = poly.nverts;
+
+ for (int vi = 0; vi < poly_nverts; vi++) {
+ int v = poly.verts[vi];
+ vset->nrm[v] = poly.plane.normal;
+ }
+ }
+ }
+
+ // sort the polys by material index:
+ qsort((void*) polys, npolys, sizeof(Poly), mcomp);
+
+ // then assign them to cohesive segments:
+ Segment* segment = 0;
+
+ for (n = 0; n < npolys; n++) {
+ if (segment && segment->material == polys[n].material) {
+ segment->npolys++;
+ }
+ else {
+ segment = 0;
+ }
+
+ if (!segment) {
+ segment = new Segment;
+
+ segment->npolys = 1;
+ segment->polys = &polys[n];
+ segment->material = segment->polys->material;
+
+ s->GetSegments().append(segment);
+ }
+ }
+
+ s->BuildHull();
+ m->GetSurfaces().append(s);
+
+ *pnverts = nverts;
+ *pnpolys = npolys;
+ *pradius = (float) radius;
+
+ result = nverts && npolys;
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+ModelFileMAG::LoadMag6(FILE* fp, Model* m, double scale)
+{
+ bool result = false;
+ int i = 0;
+ int ntex = 0;
+ int nmtls = 0;
+ int nsurfs = 0;
+ double radius = 0;
+ List<Bitmap> textures;
+
+ fread(&ntex, sizeof(ntex), 1, fp); // size of texture block
+ fread(&nmtls, sizeof(nmtls), 1, fp); // number of materials
+ fread(&nsurfs, sizeof(nsurfs), 1, fp); // number of surfaces
+
+ // read texture list:
+ if (ntex) {
+ char* buffer = new char[ntex];
+ char* p = buffer;
+ Bitmap* bmp = 0;
+
+ fread(buffer, ntex, 1, fp);
+
+ while (p < buffer + ntex) {
+ LoadTexture(p, bmp, Bitmap::BMP_SOLID);
+ textures.append(bmp);
+
+ p += strlen(p) + 1;
+ }
+
+ delete [] buffer;
+ }
+
+ for (i = 0; i < nmtls; i++) {
+ MaterialMag6 m6;
+ Material* mtl = new Material;
+
+ fread(&m6, sizeof(m6), 1, fp);
+
+ if (mtl) {
+ CopyMemory(mtl->name, m6.name, Material::NAMELEN);
+ CopyMemory(mtl->shader, m6.shader, Material::NAMELEN);
+
+ mtl->ambient_value = m6.ambient_value;
+ mtl->ambient_color = m6.ambient_color;
+ mtl->diffuse_value = m6.diffuse_value;
+ mtl->diffuse_color = m6.diffuse_color;
+ mtl->specular_value = m6.specular_value;
+ mtl->specular_color = m6.specular_color;
+ mtl->emissive_value = m6.emissive_value;
+ mtl->emissive_color = m6.emissive_color;
+
+ mtl->Ka = ColorValue(mtl->ambient_color) * mtl->ambient_value;
+ mtl->Kd = ColorValue(mtl->diffuse_color) * mtl->diffuse_value;
+ mtl->Ks = ColorValue(mtl->specular_color) * mtl->specular_value;
+ mtl->Ke = ColorValue(mtl->emissive_color) * mtl->emissive_value;
+
+ mtl->power = m6.power;
+ mtl->brilliance = m6.brilliance;
+ mtl->bump = m6.bump;
+ mtl->blend = m6.blend;
+ mtl->shadow = m6.shadow;
+ mtl->luminous = m6.luminous;
+
+ if (m6.tex_diffuse && m6.tex_diffuse <= textures.size())
+ mtl->tex_diffuse = textures[m6.tex_diffuse - 1];
+
+ if (m6.tex_specular && m6.tex_specular <= textures.size())
+ mtl->tex_specular = textures[m6.tex_specular - 1];
+
+ if (m6.tex_emissive && m6.tex_emissive <= textures.size())
+ mtl->tex_emissive = textures[m6.tex_emissive - 1];
+
+ if (m6.tex_bumpmap && m6.tex_bumpmap <= textures.size())
+ mtl->tex_bumpmap = textures[m6.tex_bumpmap - 1];
+
+ m->GetMaterials().append(mtl);
+ }
+ }
+
+ for (i = 0; i < nsurfs; i++) {
+ int nverts = 0;
+ int npolys = 0;
+ BYTE namelen = 0;
+ char name[128];
+
+ fread(&nverts, 4, 1, fp);
+ fread(&npolys, 4, 1, fp);
+ fread(&namelen, 1, 1, fp);
+ fread(name, 1, namelen, fp);
+
+ Surface* surface = new Surface;
+ surface->SetName(name);
+ surface->CreateVerts(nverts);
+ surface->CreatePolys(npolys);
+
+ VertexSet* vset = surface->GetVertexSet();
+ Poly* polys = surface->GetPolys();
+
+ ZeroMemory(polys, sizeof(Poly) * npolys);
+
+ // read vertex set:
+ for (int v = 0; v < nverts; v++) {
+ fread(&vset->loc[v], sizeof(float), 3, fp);
+ fread(&vset->nrm[v], sizeof(float), 3, fp);
+ fread(&vset->tu[v], sizeof(float), 1, fp);
+ fread(&vset->tv[v], sizeof(float), 1, fp);
+
+ double d = vset->loc[v].length();
+ if (d > radius)
+ radius = d;
+ }
+
+ // read polys:
+ for (int n = 0; n < npolys; n++) {
+ Poly& poly = polys[n];
+ BYTE poly_nverts = 0;
+ BYTE material_index = 0;
+ WORD poly_verts[8];
+
+ fread(&poly_nverts, sizeof(BYTE), 1, fp);
+ fread(&material_index, sizeof(BYTE), 1, fp);
+ fread(&poly_verts[0], sizeof(WORD), poly_nverts, fp);
+
+ if (poly_nverts >= 3) {
+ poly.nverts = poly_nverts;
+
+ for (int i = 0; i < poly_nverts; i++) {
+ poly.verts[i] = poly_verts[i];
+ }
+ }
+ else {
+ poly.sortval = 666;
+ }
+
+ if (material_index > 0) {
+ poly.material = m->GetMaterials()[material_index-1];
+ poly.sortval = material_index;
+ }
+ else if (m->NumMaterials()) {
+ poly.material = m->GetMaterials().first();
+ poly.sortval = 1;
+ }
+ else {
+ poly.sortval = 1000;
+ }
+
+ if (poly.nverts == 3)
+ surface->AddIndices(3);
+
+ else if (poly.nverts == 4)
+ surface->AddIndices(6);
+
+ poly.vertex_set = vset;
+ poly.plane = Plane(vset->loc[poly.verts[0]],
+ vset->loc[poly.verts[2]],
+ vset->loc[poly.verts[1]]);
+ }
+
+ // sort the polys by material index:
+ qsort((void*) polys, npolys, sizeof(Poly), mcomp);
+
+ // then assign them to cohesive segments:
+ Segment* segment = 0;
+
+ for (n = 0; n < npolys; n++) {
+ if (segment && segment->material == polys[n].material) {
+ segment->npolys++;
+ }
+ else {
+ segment = 0;
+ }
+
+ if (!segment) {
+ segment = new Segment;
+
+ segment->npolys = 1;
+ segment->polys = &polys[n];
+ segment->material = segment->polys->material;
+
+ surface->GetSegments().append(segment);
+ }
+ }
+
+ surface->ComputeTangents();
+ surface->BuildHull();
+ m->GetSurfaces().append(surface);
+
+ *pnverts = nverts;
+ *pnpolys = npolys;
+ *pradius = (float) radius;
+
+ result = nverts && npolys;
+ }
+
+ return result;
+}
+
diff --git a/Magic2/ModelFileMAG.h b/Magic2/ModelFileMAG.h
new file mode 100644
index 0000000..cf3d6de
--- /dev/null
+++ b/Magic2/ModelFileMAG.h
@@ -0,0 +1,39 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: ModelFileMAG.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ File loader for MAG format models
+*/
+
+#ifndef ModelFileMAG_h
+#define ModelFileMAG_h
+
+#include "Solid.h"
+
+// +--------------------------------------------------------------------+
+
+class ModelFileMAG : public ModelFile
+{
+public:
+ ModelFileMAG(const char* fname);
+ virtual ~ModelFileMAG();
+
+ virtual bool Load(Model* m, double scale=1.0);
+ virtual bool Save(Model* m);
+
+protected:
+ virtual bool LoadMag5(FILE* fp, Model* m, double scale);
+ virtual bool LoadMag6(FILE* fp, Model* m, double scale);
+};
+
+// +--------------------------------------------------------------------+
+
+#endif ModelFileMAG_h
+
diff --git a/Magic2/ModelFileOBJ.cpp b/Magic2/ModelFileOBJ.cpp
new file mode 100644
index 0000000..8226d22
--- /dev/null
+++ b/Magic2/ModelFileOBJ.cpp
@@ -0,0 +1,783 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: ModelFileOBJ.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ File loader for Wavefront/OBJ format models
+*/
+
+#include "stdafx.h"
+#include "Magic.h"
+#include "MagicDoc.h"
+#include "ModelFileOBJ.h"
+
+#include "Bitmap.h"
+#include "Polygon.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+ModelFileOBJ::ModelFileOBJ(const char* fname)
+ : ModelFile(fname)
+{
+}
+
+ModelFileOBJ::~ModelFileOBJ()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+const int MAX_OBJ_FACE_VERTS = 32;
+
+struct ObjFace {
+ int v[MAX_OBJ_FACE_VERTS];
+ int n[MAX_OBJ_FACE_VERTS];
+ int t[MAX_OBJ_FACE_VERTS];
+
+ int nverts;
+
+ ObjFace() {
+ nverts = 0;
+
+ for (int i = 0; i < MAX_OBJ_FACE_VERTS; i++)
+ v[i] = n[i] = t[i] = 0;
+ }
+
+ ObjFace(const ObjFace& f) {
+ nverts = f.nverts;
+
+ for (int i = 0; i < MAX_OBJ_FACE_VERTS; i++) {
+ v[i] = f.v[i];
+ n[i] = f.n[i];
+ t[i] = f.t[i];
+ }
+ }
+
+ void ReverseOrder() {
+ int i, tmp[MAX_OBJ_FACE_VERTS];
+
+ for (i = 0; i < nverts; i++) tmp[i] = v[i];
+ for (i = 0; i < nverts; i++) v[i] = tmp[nverts-1-i];
+
+ for (i = 0; i < nverts; i++) tmp[i] = n[i];
+ for (i = 0; i < nverts; i++) n[i] = tmp[nverts-1-i];
+
+ for (i = 0; i < nverts; i++) tmp[i] = t[i];
+ for (i = 0; i < nverts; i++) t[i] = tmp[nverts-1-i];
+ }
+};
+
+static void ParsePoly(const char* line, ObjFace* face)
+{
+ int v[MAX_OBJ_FACE_VERTS];
+ int n[MAX_OBJ_FACE_VERTS];
+ int t[MAX_OBJ_FACE_VERTS];
+
+ for (int i = 0; i < MAX_OBJ_FACE_VERTS; i++) {
+ v[i] = n[i] = t[i] = 0;
+ }
+
+ const char* p = line + 1;
+
+ while (isspace(*p))
+ p++;
+
+ i = 0;
+ while (*p && i < MAX_OBJ_FACE_VERTS) {
+ int factor = 1;
+
+ if (*p == '-') {
+ factor = -1;
+ p++;
+ }
+
+ while (isdigit(*p)) {
+ v[i] = v[i]*10 + *p - '0';
+ p++;
+ }
+ v[i] *= factor;
+
+ if (*p == '/') { p++; // slash one
+
+ factor = 1;
+
+ if (*p == '-') {
+ factor = -1;
+ p++;
+ }
+
+ while (isdigit(*p)) {
+ t[i] = t[i]*10 + *p - '0';
+ p++;
+ }
+ t[i] *= factor;
+
+ if (*p == '/') { p++; // slash two
+
+ factor = 1;
+
+ if (*p == '-') {
+ factor = -1;
+ p++;
+ }
+
+ while (isdigit(*p)) {
+ n[i] = n[i]*10 + *p - '0';
+ p++;
+ }
+ n[i] *= factor;
+ }}
+
+ while (isspace(*p)) p++;
+ i++;
+ }
+
+ face->nverts = i;
+
+ for (i = 0; i < MAX_OBJ_FACE_VERTS; i++) {
+ face->v[i] = v[i];
+ face->n[i] = n[i];
+ face->t[i] = t[i];
+ }
+}
+
+static int LoadMatls(const char* lpszPathName, Model* m)
+{
+ int nmatls = 0;
+
+ FILE* fp = fopen(lpszPathName, "r");
+ if (!fp) {
+ ::MessageBox(0, "Open Failed: could not open file", "ERROR", MB_OK);
+ return 0;
+ }
+
+ Material* mtl = 0;
+
+ while (!feof(fp)) {
+ char raw_line[512];
+ char line[512];
+ fgets(raw_line, 512, fp);
+
+ strcpy(line, Text(raw_line).trim().data());
+
+ if (strstr(line, "newmtl")) {
+ mtl = new Material;
+ strncpy(mtl->name, line+7, Material::NAMELEN - 1);
+
+ m->GetMaterials().append(mtl);
+ }
+
+ else if (line[0] == 'K' && line[1] == 'a') {
+ float r,g,b;
+ sscanf(line, "Ka %f %f %f", &r, &g, &b);
+ mtl->Ka = ColorValue(r,g,b);
+
+ mtl->ambient_value = 1.0f;
+ mtl->ambient_color = mtl->Ka.ToColor();
+ }
+
+ else if (line[0] == 'K' && line[1] == 'd') {
+ float r,g,b;
+ sscanf(line, "Kd %f %f %f", &r, &g, &b);
+ mtl->Kd = ColorValue(r,g,b);
+
+ mtl->diffuse_value = 1.0f;
+ mtl->diffuse_color = mtl->Kd.ToColor();
+ }
+
+ else if (line[0] == 'K' && line[1] == 's') {
+ float r,g,b;
+ sscanf(line, "Ks %f %f %f", &r, &g, &b);
+ mtl->Ks = ColorValue(r,g,b);
+
+ mtl->specular_value = 1.0f;
+ mtl->specular_color = mtl->Ks.ToColor();
+ }
+
+ else if (line[0] == 'N' && line[1] == 's') {
+ float ns;
+ sscanf(line, "Ns %f", &ns);
+ mtl->power = ns;
+ }
+
+ else if (strstr(line, "map_Kd")) {
+ const char* src = strstr(line, "map_Kd") + 7;
+ while (!isalnum(*src)) src++;
+
+ LoadTexture(src, mtl->tex_diffuse, Bitmap::BMP_SOLID);
+ }
+ }
+
+ fclose(fp);
+ return nmatls;
+}
+
+static Material* FindMatl(const char* mtl_name, Model* model)
+{
+ ListIter<Material> iter = model->GetMaterials();
+ while (++iter) {
+ Material* m = iter.value();
+ if (!strcmp(m->name, mtl_name))
+ return m;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+static int mcomp(const void* a, const void* b)
+{
+ Poly* pa = (Poly*) a;
+ Poly* pb = (Poly*) b;
+
+ if (pa->sortval == pb->sortval)
+ return 0;
+
+ if (pa->sortval < pb->sortval)
+ return 1;
+
+ return -1;
+}
+
+bool
+ModelFileOBJ::Load(Model* m, double scale)
+{
+ if (m && scale > 0 && strlen(filename) > 0) {
+ ModelFile::Load(m, scale);
+
+ FILE* fp = fopen(filename, "rb");
+
+ // ok, now start reading the data:
+ int ntex = 0;
+ int nverts = 0;
+ int npv = 0;
+ int npolys = 0;
+ int vi = 0;
+ int vn = 0;
+ int vt = 0;
+
+ char root_path[256];
+ ZeroMemory(root_path, 256);
+
+ if (strrchr(filename, '\\')) {
+ strcpy(root_path, filename);
+ char* p = strrchr(root_path, '\\');
+ if (p)
+ *(p+1) = 0;
+ }
+
+ else if (strrchr(filename, '/')) {
+ strcpy(root_path, filename);
+ char* p = strrchr(root_path, '/');
+ if (p)
+ *(p+1) = 0;
+ }
+
+ // count verts and polys:
+ while (!feof(fp)) {
+ char line[256];
+ fgets(line, 255, fp);
+
+ if (line[0] == 'v') {
+ switch (line[1]) {
+ case ' ': vi++; break;
+ case 'n': vn++; break;
+ case 't': vt++; break;
+ }
+ }
+
+ else if (line[0] == 'f' && line[1] == ' ') {
+ npolys++;
+
+ ObjFace f;
+ ParsePoly(line, &f);
+ f.ReverseOrder();
+
+ npv += f.nverts;
+ }
+
+ else if (strstr(line, "mtllib")) {
+ const char* libname = strstr(line, "mtllib");
+ libname += 7;
+ while (isspace(*libname))
+ libname++;
+
+ char libpath[256];
+ strcpy(libpath, root_path);
+ strcat(libpath, libname);
+ int n = strlen(libpath);
+ char* p = &libpath[n-1];
+ while (isspace(*p)) *p-- = 0;
+
+ int nmatls = LoadMatls(libpath, model);
+ }
+ }
+
+ nverts = npv;
+ if (vi > nverts) nverts = vi;
+ if (vn > nverts) nverts = vn;
+ if (vt > nverts) nverts = vt;
+
+ if (nverts > Model::MAX_VERTS || npolys > Model::MAX_POLYS) {
+ ::MessageBox(0, "Wavefront/OBJ Import Failed: that model is just too darn complicated!", "ERROR", MB_OK);
+ return false;
+ }
+
+ vi = 0;
+ vn = 0;
+ vt = 0;
+
+ fseek(fp, 0, SEEK_SET);
+
+ Surface* surface = new Surface;
+ m->GetSurfaces().append(surface);
+
+ surface->CreateVerts(nverts);
+ surface->CreatePolys(npolys);
+
+ VertexSet* vset = surface->GetVertexSet();
+ Poly* polys = surface->GetPolys();
+
+ // read vertex set:
+ Vec3* vloc = new Vec3[nverts];
+ Vec3* vnrm = new Vec3[nverts];
+ float* vtu = new float[nverts];
+ float* vtv = new float[nverts];
+
+ float radius = 0;
+
+ while (!feof(fp)) {
+ char line[256];
+ fgets(line, 255, fp);
+
+ if (line[0] == 'v') {
+ if (line[1] == ' ') {
+ const char* p = line + 2;
+
+ while (isspace(*p)) p++;
+ sscanf(p, "%f", &vloc[vi].x);
+
+ while (!isspace(*p)) p++;
+ while (isspace(*p)) p++;
+ sscanf(p, "%f", &vloc[vi].y);
+
+ while (!isspace(*p)) p++;
+ while (isspace(*p)) p++;
+ sscanf(p, "%f", &vloc[vi].z);
+
+ float d = vloc[vi].length();
+ if (d > radius)
+ radius = d;
+
+ vi++;
+ }
+
+ else if (line[1] == 'n') {
+ const char* p = line + 2;
+
+ while (isspace(*p)) p++;
+ sscanf(p, "%f", &vnrm[vn].x);
+
+ while (!isspace(*p)) p++;
+ while (isspace(*p)) p++;
+ sscanf(p, "%f", &vnrm[vn].y);
+
+ while (!isspace(*p)) p++;
+ while (isspace(*p)) p++;
+ sscanf(p, "%f", &vnrm[vn].z);
+
+ vn++;
+ }
+
+ else if (line[1] == 't') {
+ const char* p = line + 2;
+
+ while (isspace(*p)) p++;
+ sscanf(p, "%f", &vtu[vt]);
+
+ while (!isspace(*p)) p++;
+ while (isspace(*p)) p++;
+ sscanf(p, "%f", &vtv[vt]);
+
+ vtv[vt] = 1.0f - vtv[vt];
+
+ vt++;
+ }
+ }
+ }
+
+ fseek(fp, 0, SEEK_SET);
+
+ // read polys:
+ int poly = 0;
+ char line[256];
+ ObjFace face;
+ Material* material = 0;
+ int mtl_index = 0;
+
+ if (m->NumMaterials())
+ material = m->GetMaterials().first();
+
+ int current_v = 1;
+ int current_vn = 1;
+ int current_vt = 1;
+ int v = 0; // vset index pointer
+
+ while (!feof(fp)) {
+ char raw_line[256];
+ fgets(raw_line, 256, fp);
+
+ strcpy(line, Text(raw_line).trim().data());
+
+ if (strstr(line, "usemtl")) {
+ material = FindMatl(line + 7, model);
+ ListIter<Material> iter = model->GetMaterials();
+ while (++iter)
+ if (material == iter.value())
+ mtl_index = iter.index();
+ }
+
+ else if (line[0] == 'v') {
+ if (line[1] == ' ') current_v++;
+ else if (line[1] == 'n') current_vn++;
+ else if (line[1] == 't') current_vt++;
+ }
+
+ else if (line[0] == 'f') {
+ ParsePoly(line, &face);
+ face.ReverseOrder();
+
+ for (int n = 0; n < face.nverts; n++) {
+ if (face.v[n] < 0)
+ face.v[n] += current_v;
+
+ if (face.n[n] < 0)
+ face.n[n] += current_vn;
+
+ if (face.t[n] < 0)
+ face.t[n] += current_vt;
+ }
+
+ if (face.nverts > 4) {
+ npolys += face.nverts - 3;
+
+ for (int tri = 2; tri < face.nverts; tri++) {
+ Poly* p = polys + poly;
+ poly++;
+
+ p->nverts = 3;
+
+ vset->loc[v+0] = vloc[face.v[ tri ] -1];
+ vset->loc[v+1] = vloc[face.v[ tri-1 ] -1];
+ vset->loc[v+2] = vloc[face.v[ 0] -1];
+
+ if (face.n[0] > 0) {
+ vset->nrm[v+0] = vnrm[face.n[ tri ] -1];
+ vset->nrm[v+1] = vnrm[face.n[ tri-1 ] -1];
+ vset->nrm[v+2] = vnrm[face.n[ 0] -1];
+ }
+ else {
+ vset->nrm[v+0] = vloc[v+0]; vset->nrm[v+0].Normalize();
+ vset->nrm[v+1] = vloc[v+1]; vset->nrm[v+1].Normalize();
+ vset->nrm[v+2] = vloc[v+2]; vset->nrm[v+2].Normalize();
+ }
+
+ if (face.t[0] > 0) {
+ vset->tu[v+0] = vtu[face.t[ tri ] -1];
+ vset->tv[v+0] = vtv[face.t[ tri ] -1];
+ vset->tu[v+1] = vtu[face.t[ tri-1 ] -1];
+ vset->tv[v+1] = vtv[face.t[ tri-1 ] -1];
+ vset->tu[v+2] = vtu[face.t[ 0 ] -1];
+ vset->tv[v+2] = vtv[face.t[ 0 ] -1];
+ }
+
+ p->verts[0] = v+0;
+ p->verts[1] = v+1;
+ p->verts[2] = v+2;
+
+ p->material = material;
+ p->sortval = mtl_index;
+ p->vertex_set = vset;
+
+ p->plane = Plane(vset->loc[p->verts[0]],
+ vset->loc[p->verts[2]],
+ vset->loc[p->verts[1]]);
+
+ v += p->nverts;
+ }
+ }
+
+ else if (face.nverts == 3 || face.nverts == 4) {
+ Poly* p = polys + poly;
+ poly++;
+
+ p->nverts = face.nverts;
+
+ bool flat = true;
+ int first = v;
+
+ for (int i = 0; i < p->nverts; i++) {
+ int face_index = i;
+
+ vset->loc[v] = vloc[face.v[face_index]-1];
+
+ if (face.n[face_index] > 0)
+ vset->nrm[v] = vnrm[face.n[face_index]-1];
+ else
+ vset->nrm[v] = vset->loc[v];
+
+ vset->nrm[v].Normalize();
+
+ if (vset->nrm[v] != vset->nrm[first])
+ flat = false;
+
+ if (face.t[face_index] > 0) {
+ vset->tu[v] = vtu [face.t[face_index]-1];
+ vset->tv[v] = vtv [face.t[face_index]-1];
+ }
+
+ p->verts[i] = v++;
+ }
+
+ p->material = material;
+ p->sortval = mtl_index;
+ p->flatness = flat ? 1.0f : 0.0f;
+
+ if (p->nverts == 3)
+ surface->AddIndices(3);
+
+ else if (p->nverts == 4)
+ surface->AddIndices(6);
+
+ p->vertex_set = vset;
+ p->plane = Plane(vset->loc[p->verts[0]],
+ vset->loc[p->verts[2]],
+ vset->loc[p->verts[1]]);
+ }
+
+ if (poly >= npolys)
+ break;
+ }
+ }
+
+ // sort the polys by material index:
+ qsort((void*) polys, npolys, sizeof(Poly), mcomp);
+
+ // then assign them to cohesive segments:
+ Segment* segment = 0;
+
+ for (int n = 0; n < npolys; n++) {
+ if (segment && segment->material == polys[n].material) {
+ segment->npolys++;
+ }
+ else {
+ segment = 0;
+ }
+
+ if (!segment) {
+ segment = new Segment;
+
+ segment->npolys = 1;
+ segment->polys = &polys[n];
+ segment->material = segment->polys->material;
+
+ surface->GetSegments().append(segment);
+ }
+ }
+
+ delete [] vloc;
+ delete [] vnrm;
+ delete [] vtu;
+ delete [] vtv;
+
+ *pnverts = nverts;
+ *pnpolys = npolys;
+ *pradius = radius;
+
+ fclose(fp);
+
+ m->Normalize();
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+static bool
+SaveWaveFrontMatLib(const char* path, const char* root, Model* model)
+{
+ char filename[256];
+ sprintf(filename, "%s%s.mtl", path, root);
+
+ FILE* f = fopen(filename, "w");
+ if (f) {
+ fprintf(f, "#\n# %s Material Library exported by Magic 2.0\n#\n\n", root);
+
+ ListIter<Material> iter = model->GetMaterials();
+ while (++iter) {
+ Material* mtl = iter.value();
+
+ fprintf(f, "newmtl %s\n", mtl->name);
+ fprintf(f, "Ka %.5f %.5f %.5f\n", mtl->Ka.Red(), mtl->Ka.Green(), mtl->Ka.Blue());
+ fprintf(f, "Kd %.5f %.5f %.5f\n", mtl->Kd.Red(), mtl->Kd.Green(), mtl->Kd.Blue());
+ fprintf(f, "Ks %.5f %.5f %.5f\n", mtl->Ks.Red(), mtl->Ks.Green(), mtl->Ks.Blue());
+ fprintf(f, "Ns %.5f\n", mtl->power);
+ fprintf(f, "illum 2\n");
+ if (mtl->tex_diffuse)
+ fprintf(f, "map_Kd %s\n", mtl->tex_diffuse->GetFilename());
+ fprintf(f, "\n");
+ }
+
+ fclose(f);
+ return true;
+ }
+
+ return false;
+}
+
+bool
+ModelFileOBJ::Save(Model* m)
+{
+ if (m) {
+ ModelFile::Save(m);
+ char pathname[256];
+ char rootname[256];
+
+ ZeroMemory(pathname, sizeof(pathname));
+ ZeroMemory(rootname, sizeof(rootname));
+
+ const char* ext = strstr(filename, ".obj");
+ if (!ext)
+ ext = strstr(filename, ".OBJ");
+
+ const char* sep = strrchr(filename, '/');
+ if (!sep)
+ sep = strrchr(filename, '\\');
+
+ const char* src = filename;
+ char* dst = pathname;
+
+ if (sep) {
+ while (src != sep)
+ *dst++ = *src++;
+ *dst++ = *src++;
+ }
+
+ if (ext) {
+ dst = rootname;
+ while (src != ext)
+ *dst++ = *src++;
+ }
+ else {
+ strcpy(rootname, src);
+ }
+
+ strcpy(filename, pathname);
+ strcat(filename, rootname);
+ strcat(filename, ".obj");
+
+ FILE* f = fopen(filename, "w");
+ if (!f) {
+ ::MessageBox(0, "Export Failed: Magic could not open the file for writing", "ERROR", MB_OK);
+ return false;
+ }
+
+ fprintf(f, "# Wavefront OBJ exported by Magic 2.0\n\n");
+ fprintf(f, "mtllib %s.mtl\n", rootname);
+
+ ListIter<Surface> s_iter = m->GetSurfaces();
+
+ // vertex locations
+ fprintf(f, "\n# VERTEX LOCATIONS: %d\n", m->NumVerts());
+ while (++s_iter) {
+ Surface* s = s_iter.value();
+ VertexSet* vset = s->GetVertexSet();
+
+ for (int n = 0; n < vset->nverts; n++) {
+ fprintf(f, "v %12.5f %12.5f %12.5f\n",
+ vset->loc[n].x,
+ vset->loc[n].y,
+ vset->loc[n].z);
+ }
+ }
+
+ s_iter.reset();
+
+ // vertex normals
+ fprintf(f, "\n# VERTEX NORMALS: %d\n", m->NumVerts());
+ while (++s_iter) {
+ Surface* s = s_iter.value();
+ VertexSet* vset = s->GetVertexSet();
+
+ for (int n = 0; n < vset->nverts; n++) {
+ fprintf(f, "vn %8.3f %8.3f %8.3f\n",
+ vset->nrm[n].x,
+ vset->nrm[n].y,
+ vset->nrm[n].z);
+ }
+ }
+
+ s_iter.reset();
+
+ // texture coordinates
+ fprintf(f, "\n# TEXTURE COORDINATES: %d\n", m->NumVerts());
+ while (++s_iter) {
+ Surface* s = s_iter.value();
+ VertexSet* vset = s->GetVertexSet();
+
+ for (int n = 0; n < vset->nverts; n++) {
+ fprintf(f, "vt %8.3f %8.3f\n",
+ vset->tu[n], 1 - vset->tv[n]);
+ }
+ }
+
+ s_iter.reset();
+
+ // faces
+ Material* current_material = 0;
+
+ fprintf(f, "\n# FACES: %d\n", m->NumPolys());
+ while (++s_iter) {
+ Surface* s = s_iter.value();
+
+ for (int n = 0; n < s->NumPolys(); n++) {
+ const Poly* p = s->GetPolys() + n;
+ int nv = p->nverts;
+ Material* mtl = p->material;
+
+ if (current_material != mtl) {
+ fprintf(f, "\n\nusemtl %s\n", mtl->name);
+ current_material = mtl;
+ }
+
+ fprintf(f, "\nf ");
+ for (int v = nv-1; v >= 0; v--) {
+ fprintf(f, "%d/%d/%d ",
+ p->verts[v] + 1,
+ p->verts[v] + 1,
+ p->verts[v] + 1);
+ }
+ }
+ }
+
+ fprintf(f, "\n\n\n# END OF FILE.\n");
+ fclose(f);
+
+ return SaveWaveFrontMatLib(pathname, rootname, m);
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
diff --git a/Magic2/ModelFileOBJ.h b/Magic2/ModelFileOBJ.h
new file mode 100644
index 0000000..1fe123b
--- /dev/null
+++ b/Magic2/ModelFileOBJ.h
@@ -0,0 +1,37 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: ModelFileOBJ.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ File loader for Wavefront/OBJ format models
+*/
+
+#ifndef ModelFileOBJ_h
+#define ModelFileOBJ_h
+
+#include "Solid.h"
+
+// +--------------------------------------------------------------------+
+
+class ModelFileOBJ : public ModelFile
+{
+public:
+ ModelFileOBJ(const char* fname);
+ virtual ~ModelFileOBJ();
+
+ virtual bool Load(Model* m, double scale=1.0);
+ virtual bool Save(Model* m);
+
+protected:
+};
+
+// +--------------------------------------------------------------------+
+
+#endif ModelFileOBJ_h
+
diff --git a/Magic2/ModelView.cpp b/Magic2/ModelView.cpp
new file mode 100644
index 0000000..21feaa8
--- /dev/null
+++ b/Magic2/ModelView.cpp
@@ -0,0 +1,417 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: ModelView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Implementation of the ModelView class
+*/
+
+#include "stdafx.h"
+#include "Magic.h"
+
+#include "MagicDoc.h"
+#include "ModelView.h"
+#include "Grid.h"
+#include "Selector.h"
+#include "Selection.h"
+
+#include "ActiveWindow.h"
+#include "Color.h"
+#include "Light.h"
+#include "Scene.h"
+#include "Screen.h"
+#include "Shadow.h"
+#include "Solid.h"
+#include "Video.h"
+
+DWORD GetRealTime();
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+static ModelView* views[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+// +--------------------------------------------------------------------+
+
+ModelView::ModelView(Window* c, Scene* s, DWORD m)
+ : CameraView(c, 0, s), view_mode(100), fill_mode(FILL_WIRE),
+ grid(0), az(-PI/4), el(PI/4)
+{
+ UseCamera(&cam);
+ SetViewMode(m);
+}
+
+ModelView::~ModelView()
+{
+ if (views[view_mode] == this)
+ views[view_mode] = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ModelView::SetViewMode(DWORD m)
+{
+ if (view_mode != m) {
+ view_mode = m;
+
+ double x = 1e3 * sin(az) * cos(el);
+ double y = 1e3 * cos(az) * cos(el);
+ double z = 1e3 * sin(el);
+
+ switch (view_mode) {
+ case VIEW_PLAN:
+ cam.LookAt(Point(0,0,0),
+ Point(0,1e3,0),
+ Point(0,0,1));
+ SetProjectionType(Video::PROJECTION_ORTHOGONAL);
+ SetFieldOfView(1.75);
+ break;
+
+ case VIEW_FRONT:
+ cam.LookAt(Point(0,0,0),
+ Point(0,0,1e3),
+ Point(0,1,0));
+ SetProjectionType(Video::PROJECTION_ORTHOGONAL);
+ SetFieldOfView(1.75);
+ break;
+
+ case VIEW_SIDE:
+ cam.LookAt(Point(0,0,0),
+ Point(1e3,0,0),
+ Point(0,1,0));
+ SetProjectionType(Video::PROJECTION_ORTHOGONAL);
+ SetFieldOfView(1.75);
+ break;
+
+ case VIEW_PROJECT:
+ cam.LookAt(Point(0,0,0),
+ Point(x,z,y),
+ Point(0,1,0));
+ SetProjectionType(Video::PROJECTION_PERSPECTIVE);
+ SetFieldOfView(1.75);
+ break;
+ }
+
+ views[view_mode] = this;
+ }
+}
+
+ModelView*
+ModelView::FindView(DWORD mode)
+{
+ if (mode >= 0 && mode < 8)
+ return views[mode];
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ModelView::MoveTo(Point pos)
+{
+ switch (view_mode) {
+ case VIEW_PLAN:
+ cam.LookAt( pos - Point(0,1e3,0),
+ pos,
+ Point(0,0,1));
+ break;
+
+ case VIEW_FRONT:
+ cam.LookAt( pos - Point(0,0,1e3),
+ pos,
+ Point(0,1,0));
+ break;
+
+ case VIEW_SIDE:
+ cam.LookAt( pos - Point(1e3,0,0),
+ pos,
+ Point(0,1,0));
+ break;
+ }
+}
+
+void
+ModelView::MoveBy(double dx, double dy)
+{
+ Point pos = cam.Pos();
+
+ dx *= 2.00/GetFieldOfView();
+ dy *= 2.00/GetFieldOfView();
+
+ switch (view_mode) {
+ case VIEW_PLAN:
+ pos.x -= dx;
+ pos.z += dy;
+
+ cam.LookAt( pos - Point(0,1e3,0),
+ pos,
+ Point(0,0,1));
+ break;
+
+ case VIEW_FRONT:
+ pos.x += dx;
+ pos.y += dy;
+
+ cam.LookAt( pos - Point(0,0,1e3),
+ pos,
+ Point(0,1,0));
+ break;
+
+ case VIEW_SIDE:
+ pos.z -= dx;
+ pos.y += dy;
+
+ cam.LookAt( pos - Point(1e3,0,0),
+ pos,
+ Point(0,1,0));
+ break;
+ }
+}
+
+void
+ModelView::SpinBy(double phi, double theta)
+{
+ const double limit = (0.43 * PI);
+
+ Point pos = cam.Pos();
+ double len = pos.length();
+
+ az += phi;
+ el += theta;
+
+ if (az > PI)
+ az = -2*PI + az;
+
+ else if (az < -PI)
+ az = 2*PI + az;
+
+ if (el > limit)
+ el = limit;
+ else if (el < -limit)
+ el = -limit;
+
+ double x = len * sin(az) * cos(el);
+ double y = len * cos(az) * cos(el);
+ double z = len * sin(el);
+
+ cam.LookAt(Point(0,0,0), Point(x,z,y), Point(0,1,0));
+}
+
+void
+ModelView::UseGrid(Grid* g)
+{
+ grid = g;
+}
+
+void
+ModelView::ZoomNormal()
+{
+ DWORD v = view_mode; // remember current view mode
+ view_mode = 100; // force set view mode to setup the view
+ az = -PI/4; // if this happens to be a perspective view,
+ el = PI/4; // reset the spin to the original 3/4ths view
+
+ SetViewMode(v); // restore default view params for this mode
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ModelView::RenderScene()
+{
+ // activate all lights:
+ ListIter<Light> light_iter = scene->Lights();
+
+ while (++light_iter) {
+ Light* light = light_iter.value();
+ light->SetActive(true);
+ }
+
+ video->SetLights(scene->Lights());
+
+ RenderGrid();
+
+
+ bool old_shadows = video->IsShadowEnabled();
+ bool old_bumps = video->IsBumpMapEnabled();
+
+ if (fill_mode != FILL_TEXTURE) {
+ video->SetShadowEnabled(false);
+ video->SetBumpMapEnabled(false);
+ CameraView::RenderScene();
+ }
+
+ else {
+ CameraView::RenderScene();
+ }
+
+ const char* title = "ModelView";
+ switch (view_mode) {
+ case VIEW_PLAN: title = "Top"; break;
+ case VIEW_FRONT: title = "Front"; break;
+ case VIEW_SIDE: title = "Right Side"; break;
+ case VIEW_PROJECT: title = "Perspective"; break;
+ }
+
+ int len = strlen(title);
+ Rect r(6,4,200,20);
+
+ r.x += window->GetRect().x;
+ r.y += window->GetRect().y;
+
+ video->DrawText(title, len, r, DT_LEFT|DT_SINGLELINE, Color::Black);
+
+ r.x--;
+ r.y--;
+
+ video->DrawText(title, len, r, DT_LEFT|DT_SINGLELINE, Color::White);
+ video->SetShadowEnabled(old_shadows);
+ video->SetBumpMapEnabled(old_bumps);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ModelView::Render(Graphic* g, DWORD flags)
+{
+ if (!g)
+ return;
+
+ if (!strcmp(g->Name(), "Selector")) {
+ Selector* selector = (Selector*) g;
+
+ if (selector->GetViewMode() != (int) view_mode)
+ return;
+ }
+
+ else if (!strcmp(g->Name(), "Selection")) {
+ Selection* selection = (Selection*) g;
+ selection->UseView(this);
+ }
+
+ if (fill_mode == FILL_WIRE) {
+ Material m;
+ video->UseMaterial(&m);
+ video->SetRenderState(Video::Z_ENABLE, FALSE);
+
+ if (g->IsSolid()) {
+ m.Kd = ColorValue(0.0f, 0.0f, 0.0f, 0.5f);
+ m.Ke = ColorValue(0.8f, 0.8f, 0.9f, 1.0f);
+ m.blend = Video::BLEND_ALPHA;
+ m.luminous = true;
+
+ video->SetRenderState(Video::FILL_MODE, Video::FILL_SOLID);
+ g->Render(video, flags);
+ }
+
+ m.Ka = Color::Black;
+ m.Kd = Color::Black;
+ m.Ks = Color::Black;
+ m.Ke = Color::Black;
+ m.blend = Video::BLEND_SOLID;
+
+ video->SetRenderState(Video::FILL_MODE, Video::FILL_WIREFRAME);
+
+ g->Render(video, flags);
+ video->UseMaterial(0);
+ }
+
+ else if (fill_mode == FILL_SOLID) {
+ if (g->IsSolid() && flags != Graphic::RENDER_SOLID)
+ return;
+
+ Material m;
+ video->UseMaterial(&m);
+ video->SetRenderState(Video::Z_ENABLE, TRUE);
+
+ m.Kd = ColorValue(0.5f, 0.5f, 0.5f, 1.0f);
+ m.blend = Video::BLEND_SOLID;
+
+ video->SetRenderState(Video::FILL_MODE, Video::FILL_SOLID);
+
+ if (g->IsSolid()) {
+ MarkVisibleLights(g, flags);
+ video->SetLights(scene->Lights());
+ }
+
+ g->Render(video, flags);
+ video->UseMaterial(0);
+ }
+
+ else if (fill_mode == FILL_TEXTURE) {
+ video->SetRenderState(Video::Z_ENABLE, TRUE);
+ video->UseMaterial(0);
+
+ if (g->IsSolid()) {
+ MarkVisibleLights(g, flags);
+ video->SetLights(scene->Lights());
+ }
+
+ g->Render(video, flags);
+ }
+}
+
+void
+ModelView::RenderGrid()
+{
+ if (!grid || !grid->IsShow())
+ return;
+
+ int plane = Grid::GRID_XZ;
+
+ if (view_mode == VIEW_FRONT)
+ plane = Grid::GRID_XY;
+
+ else if (view_mode == VIEW_SIDE)
+ plane = Grid::GRID_YZ;
+
+ grid->ShowPlane(plane);
+ grid->MoveTo(camera_loc * -1);
+ grid->ShowReference(view_mode != VIEW_PROJECT);
+ grid->Render(video, Graphic::RENDER_SOLID);
+
+ video->SetRenderState(Video::Z_ENABLE, TRUE);
+ video->SetRenderState(Video::LIGHTING_ENABLE, TRUE);
+ video->UseMaterial(0);
+}
+
+// +--------------------------------------------------------------------+
+
+CPoint
+ModelView::ProjectPoint(Vec3& p)
+{
+ CPoint result;
+
+ switch (view_mode) {
+ case VIEW_PLAN:
+ case VIEW_FRONT:
+ case VIEW_SIDE:
+ case VIEW_PROJECT: {
+ Vec3 pt = p;
+ projector.Transform(pt);
+ projector.Project(pt, false);
+
+ result.x = (LONG) (pt.x + GetWindow()->X());
+ result.y = (LONG) (pt.y + GetWindow()->Y());
+ }
+ break;
+
+ default:
+ result.x = 0;
+ result.y = 0;
+ break;
+ }
+
+ return result;
+}
diff --git a/Magic2/ModelView.h b/Magic2/ModelView.h
new file mode 100644
index 0000000..27fc9b6
--- /dev/null
+++ b/Magic2/ModelView.h
@@ -0,0 +1,64 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: ModelView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Interface of the ModelView class
+*/
+
+
+#ifndef ModelView_h
+#define ModelView_h
+
+#include "CameraView.h"
+#include "Grid.h"
+
+// +--------------------------------------------------------------------+
+
+class ModelView : public CameraView
+{
+public:
+ enum VIEW_MODE { VIEW_PLAN=1, VIEW_FRONT, VIEW_SIDE, VIEW_PROJECT };
+ enum FILL_MODE { FILL_WIRE=1, FILL_SOLID, FILL_TEXTURE };
+
+ ModelView(Window* c, Scene* s, DWORD m);
+ virtual ~ModelView();
+
+ virtual void RenderScene();
+ virtual void Render(Graphic* g, DWORD flags);
+
+ DWORD GetViewMode() const { return view_mode; }
+ void SetViewMode(DWORD m);
+ DWORD GetFillMode() const { return fill_mode; }
+ void SetFillMode(DWORD m) { fill_mode = m; }
+
+ void MoveTo(Point origin);
+ void MoveBy(double dx, double dy);
+ void SpinBy(double phi, double theta);
+
+ void UseGrid(Grid* g);
+ void RenderGrid();
+ void ZoomNormal();
+
+ CPoint ProjectPoint(Vec3& p);
+
+ static ModelView* FindView(DWORD mode);
+
+protected:
+ Camera cam;
+ DWORD view_mode;
+ DWORD fill_mode;
+ double az;
+ double el;
+ Grid* grid;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif ModelView_h
diff --git a/Magic2/Primitives.cpp b/Magic2/Primitives.cpp
new file mode 100644
index 0000000..8846646
--- /dev/null
+++ b/Magic2/Primitives.cpp
@@ -0,0 +1,52 @@
+/* Project MAGIC
+ John DiCamillo Software Consulting
+ Copyright © 1994-1997. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe Application
+ FILE: Primitives.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Commands for adding basic geometric primitives to a mesh
+*/
+
+
+#include "stdafx.h"
+#include "Primitives.h"
+#include "MagicDoc.h"
+#include "ModelView.h"
+#include "Selection.h"
+
+// +----------------------------------------------------------------------+
+// +----------------------------------------------------------------------+
+// +----------------------------------------------------------------------+
+
+CreatePolyCommand::CreatePolyCommand(MagicDoc* d)
+ : Command(n, d)
+{
+}
+
+CreatePolyCommand::~CreatePolyCommand()
+{
+}
+
+// +----------------------------------------------------------------------+
+
+void
+CreatePolyCommand::Do()
+{
+ if (document) {
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+EditCommand::Undo()
+{
+ if (document) {
+ }
+}
+
diff --git a/Magic2/Primitives.h b/Magic2/Primitives.h
new file mode 100644
index 0000000..2da16fe
--- /dev/null
+++ b/Magic2/Primitives.h
@@ -0,0 +1,55 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: Primitives.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Commands for adding basic geometric primitives to a mesh
+*/
+
+#ifndef Primitives_h
+#define Primitives_h
+
+#include "MagicDoc.h"
+#include "Command.h"
+#include "Polygon.h"
+#include "Solid.h"
+#include "Video.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Selection;
+class ModelView;
+
+// +--------------------------------------------------------------------+
+
+class CreatePolyCommand : public Command
+{
+public:
+ CreatePolyCommand(MagicDoc* doc,
+ int nsides,
+ double lx,
+ double ly,
+ double lz);
+ virtual ~CreatePolyCommand();
+
+ virtual void Do();
+ virtual void Undo();
+
+private:
+ int nsides;
+ double lx;
+ double ly;
+ double lz;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Primitives_h
+
diff --git a/Magic2/Res/Magic.ico b/Magic2/Res/Magic.ico
new file mode 100644
index 0000000..8a145fe
--- /dev/null
+++ b/Magic2/Res/Magic.ico
Binary files differ
diff --git a/Magic2/Res/Magic.rc2 b/Magic2/Res/Magic.rc2
new file mode 100644
index 0000000..dede416
--- /dev/null
+++ b/Magic2/Res/Magic.rc2
@@ -0,0 +1,13 @@
+//
+// MAGIC.RC2 - resources Microsoft Visual C++ does not edit directly
+//
+
+#ifdef APSTUDIO_INVOKED
+ #error this file is not editable by Microsoft Visual C++
+#endif //APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Add manually edited resources here...
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/Magic2/Res/MagicDoc.ico b/Magic2/Res/MagicDoc.ico
new file mode 100644
index 0000000..c2c87cf
--- /dev/null
+++ b/Magic2/Res/MagicDoc.ico
Binary files differ
diff --git a/Magic2/Res/Orig Toolbar.bmp b/Magic2/Res/Orig Toolbar.bmp
new file mode 100644
index 0000000..d501723
--- /dev/null
+++ b/Magic2/Res/Orig Toolbar.bmp
Binary files differ
diff --git a/Magic2/Res/Toolbar.bmp b/Magic2/Res/Toolbar.bmp
new file mode 100644
index 0000000..f9712e2
--- /dev/null
+++ b/Magic2/Res/Toolbar.bmp
Binary files differ
diff --git a/Magic2/Selection.cpp b/Magic2/Selection.cpp
new file mode 100644
index 0000000..1345c11
--- /dev/null
+++ b/Magic2/Selection.cpp
@@ -0,0 +1,171 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: Selection.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Classes for rendering solid meshes of polygons
+*/
+
+#include "stdafx.h"
+#include "Magic.h"
+#include "Selection.h"
+#include "ModelView.h"
+#include "Projector.h"
+
+#include "Solid.h"
+#include "Scene.h"
+#include "Bitmap.h"
+
+#include <stdio.h>
+
+// +--------------------------------------------------------------------+
+
+Selection::Selection()
+ : model(0), model_view(0)
+{
+ strcpy(name, "Selection");
+}
+
+// +--------------------------------------------------------------------+
+
+Selection::~Selection()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Selection::Render(Video* video, DWORD flags)
+{
+ if (video && flags == Graphic::RENDER_ALPHA) {
+ ListIter<Poly> iter = polys;
+ while (++iter) {
+ Poly* p = iter.value();
+ Vec3 vloc[16];
+
+ if (p->nverts >= 3) {
+ for (int i = 0; i < p->nverts; i++) {
+ int n = (i==p->nverts-1) ? 0 : i+1;
+
+ vloc[2*i+0] = loc + p->vertex_set->loc[ p->verts[i] ];
+ vloc[2*i+1] = loc + p->vertex_set->loc[ p->verts[n] ];
+ }
+
+ video->SetRenderState(Video::Z_ENABLE, FALSE);
+ video->DrawLines(p->nverts, vloc, Color(255,255,128));
+ }
+ }
+
+ if (model) {
+ for (int i = 0; i < verts.size(); i++) {
+ DWORD value = verts[i];
+ WORD index = (WORD) (value >> 16);
+ WORD vert = (WORD) (value & 0xffff);
+
+ if (index < model->NumSurfaces()) {
+ Surface* s = model->GetSurfaces()[index];
+ Vec3 v = loc + s->GetVertexSet()->loc[vert];
+
+ if (model_view) {
+ CPoint p = model_view->ProjectPoint(v);
+ float handle[16];
+ int x1 = p.x-1;
+ int y1 = p.y-1;
+ int x2 = p.x+1;
+ int y2 = p.y+1;
+
+ handle[ 0] = (float) x1;
+ handle[ 1] = (float) y1;
+ handle[ 2] = (float) x2;
+ handle[ 3] = (float) y1;
+
+ handle[ 4] = (float) x2;
+ handle[ 5] = (float) y1;
+ handle[ 6] = (float) x2;
+ handle[ 7] = (float) y2;
+
+ handle[ 8] = (float) x2;
+ handle[ 9] = (float) y2;
+ handle[10] = (float) x1;
+ handle[11] = (float) y2;
+
+ handle[12] = (float) x1;
+ handle[13] = (float) y2;
+ handle[14] = (float) x1;
+ handle[15] = (float) y1;
+
+ video->DrawScreenLines(4, handle, Color(255,255,128));
+ }
+
+ else {
+ Vec3 vloc[8];
+
+ vloc[0] = v + Vec3(-1.0f, -1.0f, 0.0f);
+ vloc[1] = v + Vec3( 1.0f, -1.0f, 0.0f);
+ vloc[2] = v + Vec3( 1.0f, -1.0f, 0.0f);
+ vloc[3] = v + Vec3( 1.0f, 1.0f, 0.0f);
+ vloc[4] = v + Vec3( 1.0f, 1.0f, 0.0f);
+ vloc[5] = v + Vec3(-1.0f, 1.0f, 0.0f);
+ vloc[6] = v + Vec3(-1.0f, 1.0f, 0.0f);
+ vloc[7] = v + Vec3(-1.0f, -1.0f, 0.0f);
+
+ video->SetRenderState(Video::Z_ENABLE, FALSE);
+ video->DrawLines(4, vloc, Color(255,255,128));
+ }
+ }
+ }
+ }
+
+ video->UseMaterial(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Selection::AddPoly(Poly* p)
+{
+ if (!polys.contains(p))
+ polys.append(p);
+}
+
+void
+Selection::RemovePoly(Poly* p)
+{
+ polys.remove(p);
+}
+
+bool
+Selection::Contains(Poly* p) const
+{
+ return polys.contains(p);
+}
+
+void
+Selection::AddVert(WORD s, WORD v)
+{
+ DWORD value = (s << 16) | v;
+
+ if (!verts.contains(value))
+ verts.append(value);
+}
+
+void
+Selection::RemoveVert(WORD s, WORD v)
+{
+ DWORD value = (s << 16) | v;
+ verts.remove(value);
+}
+
+bool
+Selection::Contains(WORD s, WORD v) const
+{
+ DWORD value = (s << 16) | v;
+ return verts.contains(value);
+} \ No newline at end of file
diff --git a/Magic2/Selection.h b/Magic2/Selection.h
new file mode 100644
index 0000000..092f0ac
--- /dev/null
+++ b/Magic2/Selection.h
@@ -0,0 +1,74 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: Selection.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Classes for rendering solid meshes of polygons
+*/
+
+#ifndef Selection_h
+#define Selection_h
+
+#include "Polygon.h"
+#include "Graphic.h"
+#include "Video.h"
+#include "List.h"
+#include "ArrayList.h"
+
+// +--------------------------------------------------------------------+
+
+class Selection;
+class Solid;
+class Model;
+class ModelView;
+class Surface;
+class Segment;
+
+// +--------------------------------------------------------------------+
+
+class Selection : public Graphic
+{
+public:
+ static const char* TYPENAME() { return "Selection"; }
+
+ Selection();
+ virtual ~Selection();
+
+ // operations
+ virtual void Render(Video* video, DWORD flags);
+ virtual bool CheckVisibility(Projector& projector) { return true; }
+
+ // accessors / mutators
+ void UseModel(Model* m) { model = m; }
+ void UseView(ModelView* v){ model_view = v; }
+ Model* GetModel() const { return model; }
+ List<Poly>& GetPolys() { return polys; }
+ ArrayList& GetVerts() { return verts; }
+
+ virtual void Clear() { polys.clear();
+ verts.clear(); }
+
+ void AddPoly(Poly* p);
+ void AddVert(WORD s, WORD v);
+ void RemovePoly(Poly* p);
+ void RemoveVert(WORD s, WORD v);
+ bool Contains(Poly* p) const;
+ bool Contains(WORD s, WORD v) const;
+
+protected:
+ Model* model;
+ ModelView* model_view;
+ List<Poly> polys;
+ ArrayList verts;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Selection_h
+
diff --git a/Magic2/Selector.cpp b/Magic2/Selector.cpp
new file mode 100644
index 0000000..f0338ac
--- /dev/null
+++ b/Magic2/Selector.cpp
@@ -0,0 +1,390 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: Selector.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Source file for implementation of Selector
+*/
+
+
+#include "stdafx.h"
+#include "Selector.h"
+#include "MagicDoc.h"
+#include "ModelView.h"
+#include "Selection.h"
+
+#include "Solid.h"
+
+// +----------------------------------------------------------------------+
+
+Selector::Selector(Selection* s)
+ : nmarks(0), view_mode(0), select_mode(SELECT_REPLACE), model(0)
+{
+ if (s) {
+ selection = s;
+ own_selection = false;
+ }
+ else {
+ selection = new Selection;
+ own_selection = true;
+ }
+
+ strcpy(name, "Selector");
+}
+
+Selector::~Selector()
+{
+ if (selection && own_selection)
+ delete selection;
+}
+
+// +----------------------------------------------------------------------+
+
+const int BATCH_SIZE = 64;
+
+void
+Selector::Render(Video* video, DWORD flags)
+{
+ if (nmarks < 2 || flags == Graphic::RENDER_SOLID)
+ return;
+
+ float points[4*BATCH_SIZE + 4];
+
+ for (int batch = 0; batch < nmarks; batch += BATCH_SIZE) {
+ int end = batch + BATCH_SIZE;
+ if (end > nmarks-1)
+ end = nmarks-1;
+ int nlines = end-batch;
+
+ for (int i = 0; i < nlines; i++) {
+ points[4*i+0] = (float) marks[batch + i].x;
+ points[4*i+1] = (float) marks[batch + i].y;
+ points[4*i+2] = (float) marks[batch + i + 1].x;
+ points[4*i+3] = (float) marks[batch + i + 1].y;
+ }
+
+ video->DrawScreenLines(nlines, points, Color::Cyan);
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Selector::Clear()
+{
+ if (selection)
+ selection->Clear();
+}
+
+void
+Selector::Begin(Model* m, int mode, int seln_mode)
+{
+ nmarks = 0;
+ model = m;
+ view_mode = mode;
+ select_mode = seln_mode;
+}
+
+void
+Selector::AddMark(CPoint& p)
+{
+ if (nmarks < MAX_MARK)
+ marks[nmarks++] = p;
+}
+
+void
+Selector::End()
+{
+ ModelView* view = ModelView::FindView(view_mode);
+ view_mode = 0;
+
+ // get the model:
+ if (!model || !nmarks || !view) return;
+
+ // if not adding to selection:
+ if (select_mode == SELECT_REPLACE) {
+ Clear();
+ }
+
+ // if only one mark:
+ if (nmarks < 2) {
+ CPoint pts[Poly::MAX_VERTS];
+
+ // find all selected polys:
+ ListIter<Surface> s_iter = model->GetSurfaces();
+ while (++s_iter) {
+ Surface* s = s_iter.value();
+
+ if (s->IsHidden() || s->IsLocked() || s->IsSimplified())
+ continue;
+
+ for (int i = 0; i < s->NumPolys(); i++) {
+ Poly* poly = s->GetPolys() + i;
+
+ if (poly->nverts < 3)
+ continue;
+
+ for (int v = 0; v < poly->nverts; v++)
+ pts[v] = view->ProjectPoint(s->GetVertexSet()->loc[poly->verts[v]]);
+
+ CRgn rgn;
+ rgn.CreatePolygonRgn(pts, poly->nverts, ALTERNATE);
+
+ if (rgn.PtInRegion(marks[0])) {
+ if (select_mode == SELECT_REMOVE) {
+ selection->RemovePoly(poly);
+ }
+ else {
+ selection->AddPoly(poly);
+ }
+
+ for (int v = 0; v < poly->nverts; v++) {
+ WORD vert = poly->verts[v];
+ if (select_mode == SELECT_REMOVE) {
+ selection->RemoveVert((WORD) s_iter.index(), vert);
+ }
+ else {
+ selection->AddVert((WORD) s_iter.index(), vert);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // otherwise, build a region:
+ else {
+ CRgn rgn;
+ rgn.CreatePolygonRgn(marks, nmarks, WINDING);
+
+ // find all selected verts:
+ ListIter<Surface> s_iter = model->GetSurfaces();
+ while (++s_iter) {
+ Surface* s = s_iter.value();
+ VertexSet* vset = s->GetVertexSet();
+
+ if (s->IsHidden() || s->IsLocked() || s->IsSimplified())
+ continue;
+
+ for (WORD i = 0; i < vset->nverts; i++) {
+ CPoint p = view->ProjectPoint(s->GetVertexSet()->loc[i]);
+ if (rgn.PtInRegion(p)) {
+ if (select_mode == SELECT_REMOVE) {
+ selection->RemoveVert((WORD) s_iter.index(), i);
+ }
+ else {
+ selection->AddVert((WORD) s_iter.index(), i);
+ }
+ }
+ }
+
+ // find all selected polys:
+ for (i = 0; i < s->NumPolys(); i++) {
+ Poly* poly = s->GetPolys() + i;
+
+ bool will_select = true;
+ for (int v = 0; v < poly->nverts && will_select; v++)
+ will_select = will_select &&
+ selection->Contains((WORD) s_iter.index(), poly->verts[v]);
+
+ if (will_select)
+ selection->AddPoly(poly);
+ else
+ selection->RemovePoly(poly);
+ }
+ }
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Selector::UseModel(Model* m)
+{
+ model = m;
+
+ if (selection)
+ selection->UseModel(model);
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Selector::SelectAll(int select_mode)
+{
+ Clear();
+
+ if (model && select_mode != SELECT_REMOVE) {
+ ListIter<Surface> iter = model->GetSurfaces();
+
+ while (++iter) {
+ Surface* s = iter.value();
+
+ for (int i = 0; i < s->NumPolys(); i++) {
+ selection->GetPolys().append(s->GetPolys() + i);
+ }
+
+ for (i = 0; i < s->NumVerts(); i++) {
+ DWORD value = (iter.index() << 16) | i;
+ selection->GetVerts().append(value);
+ }
+ }
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Selector::SelectInverse()
+{
+ if (model && selection) {
+ ListIter<Surface> iter = model->GetSurfaces();
+
+ while (++iter) {
+ Surface* s = iter.value();
+ WORD s_index = iter.index();
+
+ for (int i = 0; i < s->NumPolys(); i++) {
+ Poly* p = s->GetPolys() + i;
+
+ if (selection->Contains(p))
+ selection->RemovePoly(p);
+ else
+ selection->AddPoly(p);
+ }
+
+ for (i = 0; i < s->NumVerts(); i++) {
+ if (selection->Contains(s_index, i))
+ selection->RemoveVert(s_index, i);
+ else
+ selection->AddVert(s_index, i);
+ }
+ }
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Selector::SelectSurface(Surface* s, int select_mode)
+{
+ if (!s || !model)
+ return;
+
+ WORD index = (WORD) model->GetSurfaces().index(s);
+
+ if (select_mode == SELECT_REMOVE) {
+ for (int i = 0; i < s->NumPolys(); i++) {
+ selection->RemovePoly(s->GetPolys() + i);
+ }
+
+ for (i = 0; i < s->NumVerts(); i++) {
+ selection->RemoveVert(index, i);
+ }
+ }
+ else {
+ for (WORD i = 0; i < s->NumPolys(); i++) {
+ selection->AddPoly(s->GetPolys() + i);
+ }
+
+ for (i = 0; i < s->NumVerts(); i++) {
+ selection->AddVert(index, i);
+ }
+ }
+}
+
+void
+Selector::SelectVert(Surface* s, int v, int select_mode)
+{
+ if (!s || !model)
+ return;
+
+ WORD index = (WORD) model->GetSurfaces().index(s);
+
+ if (select_mode == SELECT_REMOVE) {
+ selection->RemoveVert(index, (WORD) v);
+ }
+ else {
+ selection->AddVert(index, (WORD) v);
+ }
+}
+
+void
+Selector::SelectPoly(Poly* poly, int select_mode)
+{
+ if (!poly || !model)
+ return;
+
+ if (select_mode == SELECT_REMOVE) {
+ selection->RemovePoly(poly);
+ }
+ else {
+ selection->AddPoly(poly);
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Selector::SelectMaterial(Material* m, int select_mode)
+{
+ if (select_mode == SELECT_REPLACE)
+ Clear();
+
+ if (model && select_mode != SELECT_REMOVE) {
+ ListIter<Surface> iter = model->GetSurfaces();
+
+ while (++iter) {
+ Surface* s = iter.value();
+ WORD s_index = iter.index();
+
+ for (int i = 0; i < s->NumPolys(); i++) {
+ Poly* p = s->GetPolys() + i;
+
+ if (p->material == m) {
+ selection->AddPoly(p);
+
+ for (int v = 0; v < p->nverts; v++) {
+ selection->AddVert(s_index, p->verts[v]);
+ }
+ }
+ }
+ }
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Selector::Reselect()
+{
+ selection->GetPolys().clear();
+
+ if (model) {
+ ListIter<Surface> iter = model->GetSurfaces();
+
+ while (++iter) {
+ Surface* s = iter.value();
+ WORD s_index = iter.index();
+
+ // find all selected polys:
+ for (int i = 0; i < s->NumPolys(); i++) {
+ Poly* poly = s->GetPolys() + i;
+
+ bool will_select = true;
+ for (int v = 0; v < poly->nverts && will_select; v++)
+ will_select = will_select &&
+ selection->Contains(s_index, poly->verts[v]);
+
+ if (will_select)
+ selection->AddPoly(poly);
+ }
+ }
+ }
+}
diff --git a/Magic2/Selector.h b/Magic2/Selector.h
new file mode 100644
index 0000000..2962b59
--- /dev/null
+++ b/Magic2/Selector.h
@@ -0,0 +1,76 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: Selector.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Class definition for Selector (free-form selection tool)
+*/
+
+#ifndef Selector_h
+#define Selector_h
+
+#include "Polygon.h"
+#include "Graphic.h"
+#include "Video.h"
+
+class ModelView;
+class Model;
+class Selection;
+class Surface;
+
+// +----------------------------------------------------------------------+
+
+class Selector : public Graphic
+{
+public:
+ Selector(Selection* s=0);
+ virtual ~Selector();
+
+ enum SELECT_MODE { SELECT_REMOVE=-1, SELECT_REPLACE=0, SELECT_APPEND=1 };
+
+ // Operations
+ virtual void Render(Video* video, DWORD flags);
+ virtual bool CheckVisibility(Projector& projector) { return true; }
+
+ void Clear();
+ void Begin(Model* m, int mode, int select_mode = SELECT_REPLACE);
+ void AddMark(CPoint& p);
+ void End();
+
+ bool IsActive() const { return view_mode ? true : false; }
+ int GetViewMode() const { return view_mode; }
+ Selection* GetSelection() const { return selection; }
+
+ void UseModel(Model* m);
+ void SelectAll(int select_mode = SELECT_REPLACE);
+ void SelectInverse();
+ void SelectSurface(Surface* s, int select_mode = SELECT_REPLACE);
+ void SelectVert(Surface* s, int v, int select_mode = SELECT_REPLACE);
+ void SelectPoly(Poly* p, int select_mode = SELECT_REPLACE);
+ void SelectMaterial(Material* m, int select_mode = SELECT_REPLACE);
+
+ void Reselect();
+
+protected:
+ enum { MAX_MARK = 4096 };
+
+ int view_mode;
+ int nmarks;
+ CPoint marks[MAX_MARK];
+ int select_mode;
+
+ bool own_selection;
+ Selection* selection;
+ Model* model;
+};
+
+
+// +----------------------------------------------------------------------+
+
+#endif Selector_h
diff --git a/Magic2/StdAfx.cpp b/Magic2/StdAfx.cpp
new file mode 100644
index 0000000..c2d4ca6
--- /dev/null
+++ b/Magic2/StdAfx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// Magic.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+
+
diff --git a/Magic2/StdAfx.h b/Magic2/StdAfx.h
new file mode 100644
index 0000000..aa81375
--- /dev/null
+++ b/Magic2/StdAfx.h
@@ -0,0 +1,39 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#if !defined(AFX_STDAFX_H__E37C2030_8F5E_4A38_92DD_72F0EAE0DA81__INCLUDED_)
+#define AFX_STDAFX_H__E37C2030_8F5E_4A38_92DD_72F0EAE0DA81__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
+
+#include <afxwin.h> // MFC core and standard components
+#include <afxext.h> // MFC extensions
+#include <afxdisp.h> // MFC Automation classes
+#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h> // MFC support for Windows Common Controls
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+// Enable extra D3D debugging in debug builds if using the debug DirectX runtime.
+// This makes D3D objects work well in the debugger watch window, but slows down
+// performance slightly.
+#if defined(DEBUG) | defined(_DEBUG)
+#define D3D_DEBUG_INFO
+#endif
+
+// Direct3D includes
+#include <d3d9.h>
+#include <d3dx9.h>
+#include <mmsystem.h>
+#include <mmreg.h>
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__E37C2030_8F5E_4A38_92DD_72F0EAE0DA81__INCLUDED_)
diff --git a/Magic2/SurfacePropertiesDialog.cpp b/Magic2/SurfacePropertiesDialog.cpp
new file mode 100644
index 0000000..267f7e6
--- /dev/null
+++ b/Magic2/SurfacePropertiesDialog.cpp
@@ -0,0 +1,93 @@
+// SurfacePropertiesDialog.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "Magic.h"
+#include "MagicDoc.h"
+#include "MagicView.h"
+#include "Solid.h"
+#include "SurfacePropertiesDialog.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// SurfacePropertiesDialog dialog
+
+
+SurfacePropertiesDialog::SurfacePropertiesDialog(MagicView* pParent /*=NULL*/)
+ : CDialog(SurfacePropertiesDialog::IDD, pParent), doc(0)
+{
+ //{{AFX_DATA_INIT(SurfacePropertiesDialog)
+ mSurfaceName = _T("");
+ mNumPolys = _T("");
+ mNumVerts = _T("");
+ mSurfaceHeight = _T("");
+ mSurfaceLength = _T("");
+ mSurfaceRadius = _T("");
+ mSurfaceWidth = _T("");
+ //}}AFX_DATA_INIT
+
+ if (pParent) {
+ doc = pParent->GetDocument();
+
+ if (doc && doc->GetSolid()) {
+ Model* model = doc->GetSolid()->GetModel();
+ Surface* surface = model->GetSurfaces().first();
+ Point plus;
+ Point minus;
+
+ surface->GetVertexSet()->CalcExtents(plus, minus);
+
+ char buffer[256];
+ strcpy(buffer, surface->Name());
+ mSurfaceName = buffer;
+
+ sprintf(buffer, "%d", surface->NumPolys());
+ mNumPolys = buffer;
+
+ sprintf(buffer, "%d", surface->NumVerts());
+ mNumVerts = buffer;
+
+ sprintf(buffer, "%.1f", surface->Radius());
+ mSurfaceRadius = buffer;
+
+ sprintf(buffer, "%.1f (%.1f - %.1f)", plus.z-minus.z, minus.z, plus.z);
+ mSurfaceLength = buffer;
+
+ sprintf(buffer, "%.1f (%.1f - %.1f)", plus.x-minus.x, minus.x, plus.x);
+ mSurfaceWidth = buffer;
+
+ sprintf(buffer, "%.1f (%.1f - %.1f)", plus.y-minus.y, minus.y, plus.y);
+ mSurfaceHeight = buffer;
+ }
+ }
+}
+
+
+void SurfacePropertiesDialog::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(SurfacePropertiesDialog)
+ DDX_CBString(pDX, IDC_SURFACE_NAME, mSurfaceName);
+ DDX_Text(pDX, IDC_SURFACE_NPOLYS, mNumPolys);
+ DDX_Text(pDX, IDC_SURFACE_NVERTS, mNumVerts);
+ DDX_Text(pDX, IDC_SURFACE_HEIGHT, mSurfaceHeight);
+ DDX_Text(pDX, IDC_SURFACE_LENGTH, mSurfaceLength);
+ DDX_Text(pDX, IDC_SURFACE_RADIUS, mSurfaceRadius);
+ DDX_Text(pDX, IDC_SURFACE_WIDTH, mSurfaceWidth);
+ //}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(SurfacePropertiesDialog, CDialog)
+ //{{AFX_MSG_MAP(SurfacePropertiesDialog)
+ // NOTE: the ClassWizard will add message map macros here
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// SurfacePropertiesDialog message handlers
diff --git a/Magic2/SurfacePropertiesDialog.h b/Magic2/SurfacePropertiesDialog.h
new file mode 100644
index 0000000..ae9f687
--- /dev/null
+++ b/Magic2/SurfacePropertiesDialog.h
@@ -0,0 +1,73 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: SurfacePropertiesDialog.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Surface Properties Dialog interface file
+*/
+
+#if !defined(AFX_SURFACEPROPERTIESDIALOG_H__8121A894_106E_4A17_9CE1_ADDD88F6A0CD__INCLUDED_)
+#define AFX_SURFACEPROPERTIESDIALOG_H__8121A894_106E_4A17_9CE1_ADDD88F6A0CD__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+class MagicDoc;
+class MagicView;
+class Model;
+
+// +--------------------------------------------------------------------+
+// SurfacePropertiesDialog dialog
+// +--------------------------------------------------------------------+
+
+class SurfacePropertiesDialog : public CDialog
+{
+// Construction
+public:
+ SurfacePropertiesDialog(MagicView* pParent = NULL); // standard constructor
+
+// Dialog Data
+ //{{AFX_DATA(SurfacePropertiesDialog)
+ enum { IDD = IDD_SURFACE_PROPS };
+ CString mSurfaceName;
+ CString mNumPolys;
+ CString mNumVerts;
+ CString mSurfaceHeight;
+ CString mSurfaceLength;
+ CString mSurfaceRadius;
+ CString mSurfaceWidth;
+ //}}AFX_DATA
+
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(SurfacePropertiesDialog)
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+ MagicDoc* doc;
+
+ // Generated message map functions
+ //{{AFX_MSG(SurfacePropertiesDialog)
+ // NOTE: the ClassWizard will add member functions here
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif
diff --git a/Magic2/TextureMapDialog.cpp b/Magic2/TextureMapDialog.cpp
new file mode 100644
index 0000000..e62e066
--- /dev/null
+++ b/Magic2/TextureMapDialog.cpp
@@ -0,0 +1,185 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: TextureMapDialog.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Texture Mapping Dialog implementation file
+*/
+
+
+#include "stdafx.h"
+#include "Magic.h"
+#include "MagicDoc.h"
+#include "MagicView.h"
+#include "Selection.h"
+#include "TextureMapDialog.h"
+#include "Thumbnail.h"
+
+#include "Bitmap.h"
+#include "Solid.h"
+#include "Polygon.h"
+#include "Pcx.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+// +--------------------------------------------------------------------+
+// TextureMapDialog dialog
+// +--------------------------------------------------------------------+
+
+TextureMapDialog::TextureMapDialog(MagicView* pParent)
+ : CDialog(TextureMapDialog::IDD, pParent), doc(0), material(0), model(0), blank(0)
+{
+ //{{AFX_DATA_INIT(TextureMapDialog)
+ mMaterialIndex = -1;
+ mFlip = FALSE;
+ mMirror = FALSE;
+ mRotate = FALSE;
+ mScaleV = 0.0;
+ mAxis = -1;
+ mScaleU = 0.0;
+ mMapType = -1;
+ //}}AFX_DATA_INIT
+
+ Color gray = Color::LightGray;
+ blank = new Bitmap(1,1,&gray);
+ blank->ScaleTo(128,128);
+
+ if (pParent) {
+ doc = pParent->GetDocument();
+
+ if (doc && doc->GetSolid()) {
+ model = doc->GetSolid()->GetModel();
+ }
+
+ if (doc && doc->GetSelection()) {
+ Selection* seln = doc->GetSelection();
+
+ if (seln->GetPolys().size() > 0) {
+ material = seln->GetPolys().first()->material;
+ mMaterialIndex = model->GetMaterials().index(material) + 1;
+ }
+ }
+ }
+}
+
+TextureMapDialog::~TextureMapDialog()
+{
+ delete blank;
+}
+
+void TextureMapDialog::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(TextureMapDialog)
+ DDX_Control(pDX, IDC_MAPPING, mMapping);
+ DDX_Control(pDX, IDC_MATERIAL, mMaterialList);
+ DDX_Control(pDX, IDC_TEXTURE_PREVIEW, mMaterialThumb);
+ DDX_CBIndex(pDX, IDC_MATERIAL, mMaterialIndex);
+ DDX_Check(pDX, IDC_ALIGN_FLIP, mFlip);
+ DDX_Check(pDX, IDC_ALIGN_MIRROR, mMirror);
+ DDX_Check(pDX, IDC_ALIGN_ROTATE, mRotate);
+ DDX_Text(pDX, IDC_SCALE_V, mScaleV);
+ DDX_Radio(pDX, IDC_ALIGN_X, mAxis);
+ DDX_Text(pDX, IDC_SCALE_U, mScaleU);
+ DDX_CBIndex(pDX, IDC_MAPPING, mMapType);
+ //}}AFX_DATA_MAP
+
+ if (pDX->m_bSaveAndValidate) {
+ mMaterialIndex = mMaterialList.GetCurSel()-1;
+ }
+}
+
+
+BEGIN_MESSAGE_MAP(TextureMapDialog, CDialog)
+ //{{AFX_MSG_MAP(TextureMapDialog)
+ ON_WM_PAINT()
+ ON_CBN_SELCHANGE(IDC_MATERIAL, OnSelectMaterial)
+ ON_BN_CLICKED(IDC_ALIGN_X, OnAlign)
+ ON_BN_CLICKED(IDC_ALIGN_Y, OnAlign)
+ ON_BN_CLICKED(IDC_ALIGN_Z, OnAlign)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+// +--------------------------------------------------------------------+
+// TextureMapDialog message handlers
+// +--------------------------------------------------------------------+
+
+BOOL TextureMapDialog::OnInitDialog()
+{
+ CDialog::OnInitDialog();
+
+ mMaterialList.AddString("<none>");
+
+ if (model && model->NumMaterials()) {
+ ListIter<Material> iter = model->GetMaterials();
+ while (++iter) {
+ Material* mtl = iter.value();
+ mMaterialList.AddString(mtl->name);
+ }
+ }
+
+ mMaterialList.SetCurSel(mMaterialIndex);
+ mMapping.SetCurSel(0);
+
+ if (material) {
+ material->CreateThumbnail();
+ ThumbPreview(mMaterialThumb.GetSafeHwnd(), material->thumbnail);
+ }
+
+ return TRUE;
+}
+
+// +--------------------------------------------------------------------+
+
+void TextureMapDialog::OnPaint()
+{
+ CPaintDC dc(this); // device context for painting
+
+ if (material && material->thumbnail) {
+ ThumbPreview(mMaterialThumb.GetSafeHwnd(), material->thumbnail);
+ }
+ else {
+ ThumbPreview(mMaterialThumb.GetSafeHwnd(), blank);
+ }
+}
+
+void TextureMapDialog::OnSelectMaterial()
+{
+ mMaterialIndex = mMaterialList.GetCurSel()-1;
+ material = 0;
+
+ if (model && mMaterialIndex >= 0 && mMaterialIndex < model->NumMaterials()) {
+ material = model->GetMaterials()[mMaterialIndex];
+ }
+
+ if (material) {
+ material->CreateThumbnail();
+ ThumbPreview(mMaterialThumb.GetSafeHwnd(), material->thumbnail);
+ }
+ else {
+ ThumbPreview(mMaterialThumb.GetSafeHwnd(), blank);
+ }
+}
+
+void TextureMapDialog::OnAlign()
+{
+ if (mMapping.GetCurSel() == 0) {
+ mMapping.SetCurSel(1);
+ UpdateData(TRUE);
+
+ mScaleU = 1;
+ mScaleV = 1;
+ mMaterialIndex++;
+ UpdateData(FALSE);
+ }
+}
diff --git a/Magic2/TextureMapDialog.h b/Magic2/TextureMapDialog.h
new file mode 100644
index 0000000..3ca1791
--- /dev/null
+++ b/Magic2/TextureMapDialog.h
@@ -0,0 +1,85 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: TextureMapDialog.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Texture Mapping Dialog interface file
+*/
+
+#if !defined(AFX_TEXTUREMAPDIALOG_H__F8EDA550_FF19_4E91_9A9C_597FC357C563__INCLUDED_)
+#define AFX_TEXTUREMAPDIALOG_H__F8EDA550_FF19_4E91_9A9C_597FC357C563__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+class MagicDoc;
+class MagicView;
+struct Material;
+class Model;
+
+// +--------------------------------------------------------------------+
+// TextureMapDialog dialog
+// +--------------------------------------------------------------------+
+
+class TextureMapDialog : public CDialog
+{
+// Construction
+public:
+ TextureMapDialog(MagicView* pParent = NULL);
+ virtual ~TextureMapDialog();
+
+// Dialog Data
+ //{{AFX_DATA(TextureMapDialog)
+ enum { IDD = IDD_MODIFY_TEXTURE };
+ CComboBox mMapping;
+ CComboBox mMaterialList;
+ CStatic mMaterialThumb;
+ int mMaterialIndex;
+ BOOL mFlip;
+ BOOL mMirror;
+ BOOL mRotate;
+ double mScaleV;
+ int mAxis;
+ double mScaleU;
+ int mMapType;
+ //}}AFX_DATA
+
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(TextureMapDialog)
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+ MagicDoc* doc;
+ Material* material;
+ Model* model;
+ Bitmap* blank;
+
+ // Generated message map functions
+ //{{AFX_MSG(TextureMapDialog)
+ virtual BOOL OnInitDialog();
+ afx_msg void OnPaint();
+ afx_msg void OnSelectMaterial();
+ afx_msg void OnAlign();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif
diff --git a/Magic2/Thumbnail.cpp b/Magic2/Thumbnail.cpp
new file mode 100644
index 0000000..f855084
--- /dev/null
+++ b/Magic2/Thumbnail.cpp
@@ -0,0 +1,57 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: Thumbnail.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Thumbnail.cpp : implementation file
+*/
+
+#include "stdafx.h"
+#include "Bitmap.h"
+
+// +--------------------------------------------------------------------+
+// Preview a bitmap image in the desired window
+// +--------------------------------------------------------------------+
+
+void ThumbPreview(HWND hprev, Bitmap* bitmap)
+{
+ HDC hdc = ::GetDC(hprev);
+ RECT rect;
+ BITMAPINFO* pbmiDIB = new BITMAPINFO;
+
+ if (!pbmiDIB)
+ return;
+
+ ::GetClientRect(hprev, &rect);
+
+ pbmiDIB->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ pbmiDIB->bmiHeader.biWidth = bitmap->Width();
+ pbmiDIB->bmiHeader.biHeight = -bitmap->Height();
+ pbmiDIB->bmiHeader.biPlanes = 1;
+ pbmiDIB->bmiHeader.biBitCount = 32;
+ pbmiDIB->bmiHeader.biCompression = BI_RGB;
+ pbmiDIB->bmiHeader.biSizeImage = 0;
+ pbmiDIB->bmiHeader.biXPelsPerMeter = 0;
+ pbmiDIB->bmiHeader.biYPelsPerMeter = 0;
+ pbmiDIB->bmiHeader.biClrUsed = 0;
+ pbmiDIB->bmiHeader.biClrImportant = 0;
+
+ int result =
+ ::StretchDIBits(hdc,
+ 1, 1, 128, 128,
+ 0, 0, bitmap->Width(), bitmap->Height(),
+ bitmap->HiPixels(),
+ pbmiDIB,
+ DIB_RGB_COLORS,
+ SRCCOPY);
+
+ ::ReleaseDC(hprev, hdc);
+
+ delete pbmiDIB;
+}
diff --git a/Magic2/Thumbnail.h b/Magic2/Thumbnail.h
new file mode 100644
index 0000000..1f7acfc
--- /dev/null
+++ b/Magic2/Thumbnail.h
@@ -0,0 +1,31 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: Thumbnail.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Function declarations for thumbnail img previewer
+*/
+
+#ifndef Thumbnail_h
+#define Thumbnail_h
+
+#ifndef __AFXWIN_H__
+ #error include 'stdafx.h' before including 'Thumbnail.h' file for PCH
+#endif
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+
+// +--------------------------------------------------------------------+
+
+void ThumbPreview(HWND hprev, Bitmap* bitmap);
+
+#endif Thumbnail_h
+
diff --git a/Magic2/UVMapView.cpp b/Magic2/UVMapView.cpp
new file mode 100644
index 0000000..305782b
--- /dev/null
+++ b/Magic2/UVMapView.cpp
@@ -0,0 +1,414 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: UVMapView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Implementation of the UVMapView class
+*/
+
+#include "stdafx.h"
+#include "Magic.h"
+
+#include "MagicDoc.h"
+#include "UVMapView.h"
+#include "Selector.h"
+#include "Selection.h"
+
+#include "ActiveWindow.h"
+#include "Color.h"
+#include "Screen.h"
+#include "Video.h"
+
+DWORD GetRealTime();
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+// +--------------------------------------------------------------------+
+
+UVMapView::UVMapView(Window* c)
+ : View(c), material(0), zoom(1), x_offset(0), y_offset(0),
+ nmarks(0), select_mode(SELECT_REPLACE), active(false)
+{
+}
+
+UVMapView::~UVMapView()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+const int BATCH_SIZE = 64;
+
+void
+UVMapView::Refresh()
+{
+ video = Video::GetInstance();
+ if (!video)
+ return;
+
+ window->FillRect(window->GetRect(), Color::LightGray);
+
+ if (material && material->tex_diffuse) {
+ Bitmap* bmp = material->tex_diffuse;
+ int w = bmp->Width();
+ int h = bmp->Height();
+
+ double cx = window->Width() / 2 + x_offset;
+ double cy = window->Height() / 2 + y_offset;
+
+ int x1 = (int) (cx - (zoom * w/2));
+ int x2 = (int) (cx + (zoom * w/2));
+ int y1 = (int) (cy - (zoom * h/2));
+ int y2 = (int) (cy + (zoom * h/2));
+
+ window->DrawBitmap(x1, y1, x2, y2, bmp);
+
+ ListIter<Poly> iter = polys;
+ while (++iter) {
+ Poly* p = iter.value();
+ VertexSet* vset = p->vertex_set;
+
+ if (p->material != material)
+ continue;
+
+ for (int i = 0; i < p->nverts; i++) {
+ int n1 = p->verts[i];
+ int n2 = p->verts[0];
+ if (i < p->nverts-1)
+ n2 = p->verts[i+1];
+
+ double tu1 = vset->tu[n1];
+ double tv1 = vset->tv[n1];
+ double tu2 = vset->tu[n2];
+ double tv2 = vset->tv[n2];
+
+ x1 = (int) (cx + zoom * w * (tu1-0.5));
+ x2 = (int) (cx + zoom * w * (tu2-0.5));
+ y1 = (int) (cy + zoom * h * (tv1-0.5));
+ y2 = (int) (cy + zoom * h * (tv2-0.5));
+
+ window->DrawLine(x1, y1, x2, y2, Color::Yellow);
+
+ if (IsSelected(p, i))
+ window->FillRect(x1-3, y1-3, x1+3, y1+3, Color::Yellow);
+ else
+ window->DrawRect(x1-2, y1-2, x1+2, y1+2, Color::Yellow);
+ }
+ }
+ }
+
+ if (active && nmarks > 1) {
+ float points[4*BATCH_SIZE + 4];
+
+ for (int batch = 0; batch < nmarks; batch += BATCH_SIZE) {
+ int end = batch + BATCH_SIZE;
+ if (end > nmarks-1)
+ end = nmarks-1;
+ int nlines = end-batch;
+
+ for (int i = 0; i < nlines; i++) {
+ points[4*i+0] = (float) marks[batch + i].x;
+ points[4*i+1] = (float) marks[batch + i].y;
+ points[4*i+2] = (float) marks[batch + i + 1].x;
+ points[4*i+3] = (float) marks[batch + i + 1].y;
+ }
+
+ video->DrawScreenLines(nlines, points, Color::Cyan);
+ }
+ }
+
+ const char* title = "UV Editor";
+
+ int len = strlen(title);
+ Rect r(6,4,200,20);
+
+ r.x += window->GetRect().x;
+ r.y += window->GetRect().y;
+
+ video->DrawText(title, len, r, DT_LEFT|DT_SINGLELINE, Color::Black);
+
+ r.x--;
+ r.y--;
+
+ video->DrawText(title, len, r, DT_LEFT|DT_SINGLELINE, Color::White);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+UVMapView::UseMaterial(Material* m)
+{
+ if (material != m) {
+ material = m;
+ zoom = 1;
+ x_offset = 0;
+ y_offset = 0;
+ }
+}
+
+void
+UVMapView::UsePolys(List<Poly>& p)
+{
+ polys.clear();
+ polys.append(p);
+}
+
+void
+UVMapView::MoveBy(double dx, double dy)
+{
+ x_offset += dx;
+ y_offset += dy;
+}
+
+void
+UVMapView::DragBy(double dx, double dy)
+{
+ if (!material || !material->tex_diffuse || selverts.size() < 1)
+ return;
+
+ Bitmap* bmp = material->tex_diffuse;
+ double w = zoom * bmp->Width();
+ double h = zoom * bmp->Height();
+ float du = (float) (dx/w);
+ float dv = (float) (dy/h);
+
+ for (int i = 0; i < selverts.size(); i++) {
+ DWORD value = selverts[i];
+ DWORD p = value >> 16;
+ DWORD n = value & 0xffff;
+
+ Poly* poly = polys[p];
+ if (poly && n < poly->nverts) {
+ VertexSet* vset = poly->vertex_set;
+ int v = poly->verts[n];
+
+ vset->tu[v] += du;
+ vset->tv[v] += dv;
+ }
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+UVMapView::Clear()
+{
+ selverts.clear();
+}
+
+void
+UVMapView::Begin(int seln_mode)
+{
+ nmarks = 0;
+ select_mode = seln_mode;
+ active = true;
+}
+
+void
+UVMapView::AddMark(CPoint& p)
+{
+ if (nmarks < MAX_MARK)
+ marks[nmarks++] = p;
+}
+
+void
+UVMapView::End()
+{
+ active = false;
+
+ // get the model:
+ if (!nmarks || !material || !material->tex_diffuse) return;
+
+ // if not adding to selection:
+ if (select_mode == SELECT_REPLACE) {
+ Clear();
+ }
+
+ Bitmap* bmp = material->tex_diffuse;
+ int w = bmp->Width();
+ int h = bmp->Height();
+
+ double cx = window->Width() / 2 + x_offset;
+ double cy = window->Height() / 2 + y_offset;
+
+
+ // if only one mark:
+ if (nmarks < 2) {
+ // find all selected verts:
+ ListIter<Poly> iter = polys;
+ while (++iter) {
+ Poly* p = iter.value();
+ VertexSet* vset = p->vertex_set;
+
+ for (int i = 0; i < p->nverts; i++) {
+ int n1 = p->verts[i];
+ double tu1 = vset->tu[n1];
+ double tv1 = vset->tv[n1];
+
+ int x1 = (int) (cx + zoom * w * (tu1-0.5));
+ int y1 = (int) (cy + zoom * h * (tv1-0.5));
+
+ int dx = abs(marks[0].x - x1);
+ int dy = abs(marks[0].y - y1);
+
+ if (dx < 4 && dy < 4) {
+ WORD p_index = iter.index();
+ DWORD value = (p_index << 16) | i;
+
+ if (select_mode == SELECT_REMOVE) {
+ selverts.remove(value);
+ }
+ else if (!selverts.contains(value)) {
+ selverts.append(value);
+ }
+ }
+ }
+ }
+ }
+
+ // otherwise, build a region:
+ else {
+ CRgn rgn;
+ rgn.CreatePolygonRgn(marks, nmarks, WINDING);
+
+ // find all selected verts:
+ ListIter<Poly> iter = polys;
+ while (++iter) {
+ Poly* p = iter.value();
+ VertexSet* vset = p->vertex_set;
+
+ for (int i = 0; i < p->nverts; i++) {
+ int n1 = p->verts[i];
+ double tu1 = vset->tu[n1];
+ double tv1 = vset->tv[n1];
+
+ int x1 = (int) (cx + zoom * w * (tu1-0.5));
+ int y1 = (int) (cy + zoom * h * (tv1-0.5));
+
+ CPoint p(x1,y1);
+
+ if (rgn.PtInRegion(p)) {
+ WORD p_index = iter.index();
+ DWORD value = (p_index << 16) | i;
+
+ if (select_mode == SELECT_REMOVE) {
+ selverts.remove(value);
+ }
+ else if (!selverts.contains(value)) {
+ selverts.append(value);
+ }
+ }
+ }
+ }
+ }
+
+ nmarks = 0;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+UVMapView::SelectAll()
+{
+ selverts.clear();
+
+ ListIter<Poly> iter = polys;
+ while (++iter) {
+ Poly* p = iter.value();
+
+ if (p->material != material)
+ continue;
+
+ for (int i = 0; i < p->nverts; i++) {
+ WORD p_index = iter.index();
+ DWORD value = (p_index << 16) | i;
+ selverts.append(value);
+ }
+ }
+}
+
+void
+UVMapView::SelectNone()
+{
+ selverts.clear();
+}
+
+void
+UVMapView::SelectInverse()
+{
+ ListIter<Poly> iter = polys;
+ while (++iter) {
+ Poly* p = iter.value();
+
+ if (p->material != material)
+ continue;
+
+ for (int i = 0; i < p->nverts; i++) {
+ WORD p_index = iter.index();
+ DWORD value = (p_index << 16) | i;
+
+ if (selverts.contains(value))
+ selverts.remove(value);
+ else
+ selverts.append(value);
+ }
+ }
+}
+
+bool
+UVMapView::IsSelected(Poly* poly, WORD v)
+{
+ WORD p = polys.index(poly);
+ DWORD value = (p << 16) | v;
+
+ return selverts.contains(value);
+}
+
+bool
+UVMapView::WillSelect(CPoint& p)
+{
+ if (!material || !material->tex_diffuse)
+ return false;
+
+ Bitmap* bmp = material->tex_diffuse;
+ int w = bmp->Width();
+ int h = bmp->Height();
+
+ double cx = window->Width() / 2 + x_offset;
+ double cy = window->Height() / 2 + y_offset;
+
+ // find first selected vert:
+ ListIter<Poly> iter = polys;
+ while (++iter) {
+ Poly* poly = iter.value();
+ VertexSet* vset = poly->vertex_set;
+
+ for (int i = 0; i < poly->nverts; i++) {
+ int n1 = poly->verts[i];
+ double tu1 = vset->tu[n1];
+ double tv1 = vset->tv[n1];
+
+ int x1 = (int) (cx + zoom * w * (tu1-0.5));
+ int y1 = (int) (cy + zoom * h * (tv1-0.5));
+
+ int dx = abs(p.x - x1);
+ int dy = abs(p.y - y1);
+
+ if (dx < 4 && dy < 4) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
diff --git a/Magic2/UVMapView.h b/Magic2/UVMapView.h
new file mode 100644
index 0000000..87b0eeb
--- /dev/null
+++ b/Magic2/UVMapView.h
@@ -0,0 +1,82 @@
+/* Project Magic 2.0
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Magic.exe
+ FILE: UVMapView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Interface of the UVMapView class
+*/
+
+
+#ifndef UVMapView_h
+#define UVMapView_h
+
+#include "View.h"
+#include "Polygon.h"
+#include "List.h"
+#include "ArrayList.h"
+
+// +--------------------------------------------------------------------+
+
+class Video;
+
+// +--------------------------------------------------------------------+
+
+class UVMapView : public View
+{
+public:
+ UVMapView(Window* c);
+ virtual ~UVMapView();
+
+ enum SELECT_MODE { SELECT_REMOVE=-1, SELECT_REPLACE=0, SELECT_APPEND=1 };
+
+ virtual void Refresh();
+
+ void UseMaterial(Material* m);
+ void UsePolys(List<Poly>& p);
+
+ void MoveBy(double dx, double dy);
+ void DragBy(double dx, double dy);
+ void ZoomIn() { zoom *= 1.15; }
+ void ZoomOut() { zoom *= 0.85; }
+
+ void Clear();
+ void Begin(int select_mode = SELECT_REPLACE);
+ void AddMark(CPoint& p);
+ void End();
+
+ bool IsActive() const { return active; }
+ void SelectAll();
+ void SelectNone();
+ void SelectInverse();
+
+ bool IsSelected(Poly* p, WORD v);
+ bool WillSelect(CPoint& p);
+
+protected:
+ enum { MAX_MARK = 4096 };
+
+ Material* material;
+ List<Poly> polys;
+ Video* video;
+
+ double zoom;
+ double x_offset;
+ double y_offset;
+
+ int nmarks;
+ CPoint marks[MAX_MARK];
+ int select_mode;
+ bool active;
+
+ ArrayList selverts;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif UVMapView_h
diff --git a/Magic2/l3ds.cpp b/Magic2/l3ds.cpp
new file mode 100644
index 0000000..ac310af
--- /dev/null
+++ b/Magic2/l3ds.cpp
@@ -0,0 +1,1795 @@
+// copyright (c) 2001 Lev Povalahev
+
+
+#include "stdafx.h"
+
+#include "l3ds.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+//using namespace std;
+
+//-------------------------------------------------------
+// generic stuff
+//-------------------------------------------------------
+
+typedef unsigned long ulong;
+
+#define SEEK_START 1900
+#define SEEK_CURSOR 1901
+
+// common chunks
+// colors
+#define COLOR_F 0x0010
+#define COLOR_24 0x0011
+#define LIN_COLOR_24 0x0012
+#define LIN_COLOR_F 0x0013
+// percentage
+#define INT_PERCENTAGE 0x0030
+#define FLOAT_PERCENTAGE 0x0031
+// ambient light
+#define AMBIENT_LIGHT 0x2100
+
+
+#define MAIN3DS 0x4D4D
+#define EDIT3DS 0x3D3D // this is the start of the editor config
+
+// keyframer chunk ids
+#define KFDATA 0xB000 // the keyframer section
+#define KFHDR 0xB00A
+#define OBJECT_NODE_TAG 0xB002
+#define NODE_HDR 0xB010
+#define PIVOT 0xB013
+#define POS_TRACK_TAG 0xB020
+#define ROT_TRACK_TAG 0xB021
+#define SCL_TRACK_TAG 0xB022
+
+// material entries
+#define MAT_ENTRY 0xAFFF
+#define MAT_NAME 0xA000
+#define MAT_AMBIENT 0xA010
+#define MAT_DIFFUSE 0xA020
+#define MAT_SPECULAR 0xA030
+#define MAT_SHININESS 0xA040
+#define MAT_SHIN2PCT 0xA041
+#define MAT_TRANSPARENCY 0xA050
+#define MAT_SHADING 0xA100
+#define MAT_TWO_SIDE 0xA081
+#define MAT_ADDITIVE 0xA083
+#define MAT_WIRE 0xA085
+#define MAT_FACEMAP 0xA088
+#define MAT_WIRESIZE 0xA087
+#define MAT_DECAL 0xA082
+#define MAT_TEXMAP 0xA200
+#define MAT_MAPNAME 0xA300
+#define MAT_MAP_TILING 0xA351
+#define MAT_MAP_USCALE 0xA354
+#define MAT_MAP_VSCALE 0xA356
+#define MAT_MAP_UOFFSET 0xA358
+#define MAT_MAP_VOFFSET 0xA35A
+#define MAT_MAP_ANG 0xA35C
+#define MAT_TEX2MAP 0xA33A
+#define MAT_OPACMAP 0xA210
+#define MAT_BUMPMAP 0xA230
+#define MAT_SPECMAP 0xA204
+#define MAT_SHINMAP 0xA33C
+#define MAT_REFLMAP 0xA220
+#define MAT_ACUBIC 0xA310
+
+#define EDIT_OBJECT 0x4000
+#define OBJ_TRIMESH 0x4100
+#define OBJ_LIGHT 0x4600
+#define OBJ_CAMERA 0x4700
+#define LIT_OFF 0x4620
+#define LIT_SPOT 0x4610
+#define TRI_VERTEXLIST 0x4110
+#define TRI_VERTEXOPTIONS 0x4111
+
+#define TRI_FACELIST 0x4120
+ #define TRI_MAT_GROUP 0x4130
+ #define TRI_SMOOTH_GROUP 0x4150
+
+#define TRI_FACEMAPPING 0x4140
+#define TRI_MATRIX 0x4160
+
+#define SPOTLIGHT 0x4610
+
+//----------------------------------
+
+#define MAX_SHARED_TRIS 100
+
+// the error reporting routine
+
+void ErrorMsg(const char *msg)
+{
+
+}
+
+struct LChunk
+{
+ unsigned short id;
+ uint start;
+ uint end;
+};
+
+struct LTri
+{
+ unsigned short a;
+ unsigned short b;
+ unsigned short c;
+ ulong smoothingGroups;
+ LVector3 normal;
+ LVector3 tangent;
+ LVector3 binormal;
+ uint materialId;
+};
+
+// globals
+
+LColor3 black = {0, 0, 0};
+
+LVector3 zero3 = {0, 0, 0};
+
+LVector4 zero4 = {0, 0, 0, 0};
+
+LMap emptyMap = {0, "", 1, 1, 0, 0, 0};
+
+LVector3 _4to3(const LVector4 &vec)
+{
+ LVector3 t;
+ t.x = vec.x;
+ t.y = vec.y;
+ t.z = vec.z;
+ return t;
+}
+
+LVector3 AddVectors(const LVector3 &a, const LVector3 &b)
+{
+ LVector3 t;
+ t.x = a.x+b.x;
+ t.y = a.y+b.y;
+ t.z = a.z+b.z;
+ return t;
+}
+
+LVector3 SubtractVectors(const LVector3 &a, const LVector3 &b)
+{
+ LVector3 t;
+ t.x = a.x-b.x;
+ t.y = a.y-b.y;
+ t.z = a.z-b.z;
+ return t;
+}
+
+float VectorLength(const LVector3 &vec)
+{
+ return (float)sqrt(vec.x*vec.x + vec.y*vec.y+vec.z*vec.z);
+}
+
+LVector3 NormalizeVector(const LVector3 &vec)
+{
+ float a = VectorLength(vec);
+ if (a == 0)
+ return vec;
+ float b = 1/a;
+ LVector3 v;
+ v.x = vec.x*b;
+ v.y = vec.y*b;
+ v.z = vec.z*b;
+ return v;
+}
+
+LVector3 CrossProduct(const LVector3 &a, const LVector3 &b)
+{
+ LVector3 v;
+ v.x = a.y*b.z - a.z*b.y;
+ v.y = a.z*b.x - a.x*b.z;
+ v.z = a.x*b.y - a.y*b.x;
+ return v;
+}
+
+void LoadIdentityMatrix(LMatrix4 &m)
+{
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 4; j++)
+ m.m[i][j] = (i==j) ? 1.0f : 0.0f;
+}
+
+LVector4 VectorByMatrix(const LMatrix4 &m, const LVector4 &vec)
+{
+ LVector4 res;
+
+ res.x = m.m[0][0]*vec.x + m.m[1][0]*vec.y + m.m[2][0]*vec.z + m.m[3][0]*vec.w;
+ res.y = m.m[0][1]*vec.x + m.m[1][1]*vec.y + m.m[2][1]*vec.z + m.m[3][1]*vec.w;
+ res.z = m.m[0][2]*vec.x + m.m[1][2]*vec.y + m.m[2][2]*vec.z + m.m[3][2]*vec.w;
+ res.w = m.m[0][3]*vec.x + m.m[1][3]*vec.y + m.m[2][3]*vec.z + m.m[3][3]*vec.w;
+
+ if (res.w != 0) {
+ float b = 1/res.w;
+ res.x *= b;
+ res.y *= b;
+ res.z *= b;
+ res.w = 1;
+ }
+ else {
+ res.w = 1;
+ }
+
+ return res;
+}
+
+void QuatToMatrix(const LVector4 &quat, LMatrix4 &m)
+{
+
+}
+
+//-------------------------------------------------------
+// LObject implementation
+//-------------------------------------------------------
+
+LObject::LObject()
+{
+ m_name = "";//.clear();
+}
+
+LObject::~LObject()
+{
+ // nothing here
+}
+
+void LObject::SetName(const std::string& value)
+{
+ m_name = value;
+}
+
+const std::string& LObject::GetName()
+{
+ return m_name;
+}
+
+bool LObject::IsObject(const std::string &name)
+{
+ return (m_name == name);
+}
+
+
+//-------------------------------------------------------
+// LMaterial implementation
+//-------------------------------------------------------
+
+LMaterial::LMaterial()
+: LObject()
+{
+ m_id = 0;
+ m_texMap1 = emptyMap;
+ m_texMap2 = emptyMap;
+ m_opacMap = emptyMap;
+ m_bumpMap = emptyMap;
+ m_reflMap = emptyMap;
+ m_specMap = emptyMap;
+ m_ambient = black;
+ m_diffuse = black;
+ m_specular = black;
+ m_shading = sGouraud;
+ m_shininess = 0;
+ m_transparency = 0;
+}
+
+LMaterial::~LMaterial()
+{
+
+}
+
+uint LMaterial::GetID()
+{
+ return m_id;
+}
+
+LMap& LMaterial::GetTextureMap1()
+{
+ return m_texMap1;
+}
+
+LMap& LMaterial::GetTextureMap2()
+{
+ return m_texMap2;
+}
+
+LMap& LMaterial::GetOpacityMap()
+{
+ return m_opacMap;
+}
+
+LMap& LMaterial::GetSpecularMap()
+{
+ return m_specMap;
+}
+
+LMap& LMaterial::GetBumpMap()
+{
+ return m_bumpMap;
+}
+
+LMap& LMaterial::GetReflectionMap()
+{
+ return m_reflMap;
+}
+
+LColor3 LMaterial::GetAmbientColor()
+{
+ return m_ambient;
+}
+
+LColor3 LMaterial::GetDiffuseColor()
+{
+ return m_diffuse;
+}
+
+LColor3 LMaterial::GetSpecularColor()
+{
+ return m_specular;
+}
+
+float LMaterial::GetShininess()
+{
+ return m_shininess;
+}
+
+float LMaterial::GetTransparency()
+{
+ return m_transparency;
+}
+
+LShading LMaterial::GetShadingType()
+{
+ return m_shading;
+}
+
+void LMaterial::SetID(uint value)
+{
+ m_id = value;
+}
+
+void LMaterial::SetAmbientColor(const LColor3 &color)
+{
+ m_ambient = color;
+}
+
+void LMaterial::SetDiffuseColor(const LColor3 &color)
+{
+ m_diffuse = color;
+}
+
+void LMaterial::SetSpecularColor(const LColor3 &color)
+{
+ m_specular = color;
+}
+
+void LMaterial::SetShininess(float value)
+{
+ m_shininess = value;
+ if (m_shininess < 0)
+ m_shininess = 0;
+ if (m_shininess > 1)
+ m_shininess = 1;
+}
+
+void LMaterial::SetTransparency(float value)
+{
+ m_transparency = value;
+ if (m_transparency < 0)
+ m_transparency = 0;
+ if (m_transparency > 1)
+ m_transparency = 1;
+}
+
+void LMaterial::SetShadingType(LShading shading)
+{
+ m_shading = shading;
+}
+
+//-------------------------------------------------------
+// LMesh implementation
+//-------------------------------------------------------
+
+LMesh::LMesh()
+: LObject()
+{
+ Clear();
+}
+
+LMesh::~LMesh()
+{
+ Clear();
+}
+
+void LMesh::Clear()
+{
+ m_vertices.clear();
+ m_normals.clear();
+ m_uv.clear();
+ m_tangents.clear();
+ m_binormals.clear();
+ m_triangles.clear();
+ m_tris.clear();
+ m_materials.clear();
+ LoadIdentityMatrix(m_matrix);
+}
+
+uint LMesh::GetVertexCount()
+{
+ return m_vertices.size();
+}
+
+void LMesh::SetVertexArraySize(uint value)
+{
+ m_vertices.resize(value);
+ m_normals.resize(value);
+ m_uv.resize(value);
+ m_tangents.resize(value);
+ m_binormals.resize(value);
+}
+
+uint LMesh::GetTriangleCount()
+{
+ return m_triangles.size();
+}
+
+void LMesh::SetTriangleArraySize(uint value)
+{
+ m_triangles.resize(value);
+ m_tris.resize(value);
+}
+
+const LVector4& LMesh::GetVertex(uint index)
+{
+ return m_vertices[index];
+}
+
+const LVector3& LMesh::GetNormal(uint index)
+{
+ return m_normals[index];
+}
+
+const LVector2& LMesh::GetUV(uint index)
+{
+ return m_uv[index];
+}
+
+const LVector3& LMesh::GetTangent(uint index)
+{
+ return m_tangents[index];
+}
+
+const LVector3& LMesh::GetBinormal(uint index)
+{
+ return m_binormals[index];
+}
+
+void LMesh::SetVertex(const LVector4 &vec, uint index)
+{
+ if (index >= m_vertices.size())
+ return;
+ m_vertices[index] = vec;
+}
+
+void LMesh::SetNormal(const LVector3 &vec, uint index)
+{
+ if (index >= m_vertices.size())
+ return;
+ m_normals[index] = vec;
+}
+
+void LMesh::SetUV(const LVector2 &vec, uint index)
+{
+ if (index >= m_vertices.size())
+ return;
+ m_uv[index] = vec;
+}
+
+void LMesh::SetTangent(const LVector3 &vec, uint index)
+{
+ if (index >= m_vertices.size())
+ return;
+ m_tangents[index] = vec;
+}
+
+void LMesh::SetBinormal(const LVector3 &vec, uint index)
+{
+ if (index >= m_vertices.size())
+ return;
+ m_binormals[index] = vec;
+}
+
+const LTriangle& LMesh::GetTriangle(uint index)
+{
+ return m_triangles[index];
+}
+
+LTriangle2 LMesh::GetTriangle2(uint index)
+{
+ LTriangle2 f;
+ LTriangle t = GetTriangle(index);
+ f.vertices[0] = GetVertex(t.a);
+ f.vertices[1] = GetVertex(t.b);
+ f.vertices[2] = GetVertex(t.c);
+
+ f.vertexNormals[0] = GetNormal(t.a);
+ f.vertexNormals[1] = GetNormal(t.b);
+ f.vertexNormals[2] = GetNormal(t.c);
+
+ f.textureCoords[0] = GetUV(t.a);
+ f.textureCoords[1] = GetUV(t.b);
+ f.textureCoords[2] = GetUV(t.c);
+
+ LVector3 a, b;
+
+ a = SubtractVectors(_4to3(f.vertices[1]), _4to3(f.vertices[0]));
+ b = SubtractVectors(_4to3(f.vertices[1]), _4to3(f.vertices[2]));
+
+ f.faceNormal = CrossProduct(b, a);
+
+ f.faceNormal = NormalizeVector(f.faceNormal);
+
+ f.materialId = m_tris[index].materialId;
+
+ return f;
+}
+
+LMatrix4 LMesh::GetMatrix()
+{
+ return m_matrix;
+}
+
+void LMesh::SetMatrix(LMatrix4 m)
+{
+ m_matrix = m;
+}
+
+void LMesh::TransformVertices()
+{
+ for (uint i=0; i<m_vertices.size(); i++)
+ m_vertices[i] = VectorByMatrix(m_matrix, m_vertices[i]);
+
+ LoadIdentityMatrix(m_matrix);
+}
+
+void LMesh::CalcNormals(bool useSmoothingGroups)
+{
+ uint i;
+ // first calculate the face normals
+ for (i=0; i<m_triangles.size(); i++)
+ {
+ LVector3 a, b;
+ a = SubtractVectors(_4to3(m_vertices[m_tris[i].b]), _4to3(m_vertices[m_tris[i].a]));
+ b = SubtractVectors(_4to3(m_vertices[m_tris[i].b]), _4to3(m_vertices[m_tris[i].c]));
+ m_tris[i].normal = NormalizeVector(CrossProduct(b, a));
+ }
+
+ std::vector< std::vector<int> > array;
+ array.resize(m_vertices.size());
+ for (i=0; i<m_triangles.size(); i++)
+ {
+ uint k = m_tris[i].a;
+ array[k].push_back(i);
+
+ k = m_tris[i].b;
+ array[k].push_back(i);
+
+ k = m_tris[i].c;
+ array[k].push_back(i);
+ }
+
+ LVector3 temp;
+
+ if (!useSmoothingGroups)
+ {
+ // now calculate the normals without using smoothing groups
+ for (i=0; i<m_vertices.size(); i++)
+ {
+ temp = zero3;
+ int t = array[i].size();
+
+ for (int k=0; k<t; k++)
+ {
+ temp.x += m_tris[array[i][k]].normal.x;
+ temp.y += m_tris[array[i][k]].normal.y;
+ temp.z += m_tris[array[i][k]].normal.z;
+ }
+ m_normals[i] = NormalizeVector(temp);
+ }
+ }
+ else
+ {
+ // now calculate the normals _USING_ smoothing groups
+ // I'm assuming a triangle can only belong to one smoothing group at a time!
+ std::vector<ulong> smGroups;
+ std::vector< std::vector <uint> > smList;
+
+ uint loop_size = m_vertices.size();
+
+ for (i=0; i<loop_size; i++)
+ {
+ int t = array[i].size();
+
+ if (t == 0)
+ continue;
+
+ smGroups.clear();
+ smList.clear();
+ smGroups.push_back(m_tris[array[i][0]].smoothingGroups);
+ smList.resize(smGroups.size());
+ smList[smGroups.size()-1].push_back(array[i][0]);
+
+ // first build a list of smoothing groups for the vertex
+ for (int k=0; k<t; k++)
+ {
+ bool found = false;
+ for (uint j=0; j<smGroups.size(); j++)
+ {
+ if (m_tris[array[i][k]].smoothingGroups == smGroups[j])
+ {
+ smList[j].push_back(array[i][k]);
+ found = true;
+ }
+ }
+ if (!found)
+ {
+ smGroups.push_back(m_tris[array[i][k]].smoothingGroups);
+ smList.resize(smGroups.size());
+ smList[smGroups.size()-1].push_back(array[i][k]);
+ }
+ }
+ // now we have the list of faces for the vertex sorted by smoothing groups
+
+
+ // now duplicate the vertices so that there's only one smoothing group "per vertex"
+ if (smGroups.size() > 1)
+ for (uint j=1; j< smGroups.size(); j++)
+ {
+ m_vertices.push_back(m_vertices[i]);
+ m_normals.push_back(m_normals[i]);
+ m_uv.push_back(m_uv[i]);
+ m_tangents.push_back(m_tangents[i]);
+ m_binormals.push_back(m_binormals[i]);
+
+ uint t = m_vertices.size()-1;
+ for (uint h=0; h<smList[j].size(); h++)
+ {
+ if (m_tris[smList[j][h]].a == i)
+ m_tris[smList[j][h]].a = t;
+ if (m_tris[smList[j][h]].b == i)
+ m_tris[smList[j][h]].b = t;
+ if (m_tris[smList[j][h]].c == i)
+ m_tris[smList[j][h]].c = t;
+ }
+ }
+ }
+
+ // now rebuild a face list for each vertex, since the old one is invalidated
+ for (i=0; i<array.size(); i++)
+ array[i].clear();
+ array.clear();
+ array.resize(m_vertices.size());
+ for (i=0; i<m_triangles.size(); i++)
+ {
+ uint k = m_tris[i].a;
+ array[k].push_back(i);
+
+ k = m_tris[i].b;
+ array[k].push_back(i);
+
+ k = m_tris[i].c;
+ array[k].push_back(i);
+ }
+
+ // now compute the normals
+ for (i=0; i<m_vertices.size(); i++)
+ {
+ temp = zero3;
+ int t = array[i].size();
+
+ for (int k=0; k<t; k++)
+ {
+ temp.x += m_tris[array[i][k]].normal.x;
+ temp.y += m_tris[array[i][k]].normal.y;
+ temp.z += m_tris[array[i][k]].normal.z;
+ }
+ m_normals[i] = NormalizeVector(temp);
+ }
+
+ }
+
+ // copy m_tris to m_triangles
+ for (i=0; i<m_triangles.size(); i++)
+ {
+ m_triangles[i].a = m_tris[i].a;
+ m_triangles[i].b = m_tris[i].b;
+ m_triangles[i].c = m_tris[i].c;
+ }
+
+}
+
+void LMesh::CalcTextureSpace()
+{
+ // a understandable description of how to do that can be found here:
+ // http://members.rogers.com/deseric/tangentspace.htm
+ // first calculate the tangent for each triangle
+ LVector3 x_vec,
+ y_vec,
+ z_vec;
+ LVector3 v1, v2;
+ for (uint i=0; i<m_triangles.size(); i++)
+ {
+ v1.x = m_vertices[m_tris[i].b].x - m_vertices[m_tris[i].a].x;
+ v1.y = m_uv[m_tris[i].b].x - m_uv[m_tris[i].a].x;
+ v1.z = m_uv[m_tris[i].b].y - m_uv[m_tris[i].a].y;
+
+ v2.x = m_vertices[m_tris[i].c].x - m_vertices[m_tris[i].a].x;
+ v2.y = m_uv[m_tris[i].c].x - m_uv[m_tris[i].a].x;
+ v2.z = m_uv[m_tris[i].c].y - m_uv[m_tris[i].a].y;
+
+ x_vec = CrossProduct(v1, v2);
+
+ v1.x = m_vertices[m_tris[i].b].y - m_vertices[m_tris[i].a].y;
+ v1.y = m_uv[m_tris[i].b].x - m_uv[m_tris[i].a].x;
+ v1.z = m_uv[m_tris[i].b].y - m_uv[m_tris[i].a].y;
+
+ v2.x = m_vertices[m_tris[i].c].y - m_vertices[m_tris[i].a].y;
+ v2.y = m_uv[m_tris[i].c].x - m_uv[m_tris[i].a].x;
+ v2.z = m_uv[m_tris[i].c].y - m_uv[m_tris[i].a].y;
+
+ y_vec = CrossProduct(v1, v2);
+
+ v1.x = m_vertices[m_tris[i].b].z - m_vertices[m_tris[i].a].z;
+ v1.y = m_uv[m_tris[i].b].x - m_uv[m_tris[i].a].x;
+ v1.z = m_uv[m_tris[i].b].y - m_uv[m_tris[i].a].y;
+
+ v2.x = m_vertices[m_tris[i].c].z - m_vertices[m_tris[i].a].z;
+ v2.y = m_uv[m_tris[i].c].x - m_uv[m_tris[i].a].x;
+ v2.z = m_uv[m_tris[i].c].y - m_uv[m_tris[i].a].y;
+
+ z_vec = CrossProduct(v1, v2);
+
+ m_tris[i].tangent.x = -(x_vec.y/x_vec.x);
+ m_tris[i].tangent.y = -(y_vec.y/y_vec.x);
+ m_tris[i].tangent.z = -(z_vec.y/z_vec.x);
+
+ m_tris[i].binormal.x = -(x_vec.z/x_vec.x);
+ m_tris[i].binormal.y = -(y_vec.z/y_vec.x);
+ m_tris[i].binormal.z = -(z_vec.z/z_vec.x);
+
+ }
+
+ // now for each vertex build a list of face that share this vertex
+ std::vector< std::vector<int> > array;
+ array.resize(m_vertices.size());
+ for (i=0; i<m_triangles.size(); i++)
+ {
+ uint k = m_tris[i].a;
+ array[k].push_back(i);
+
+ k = m_tris[i].b;
+ array[k].push_back(i);
+
+ k = m_tris[i].c;
+ array[k].push_back(i);
+ }
+
+ // now average the tangents and compute the binormals as (tangent X normal)
+ for (i=0; i<m_vertices.size(); i++)
+ {
+ v1 = zero3;
+ v2 = zero3;
+ int t = array[i].size();
+
+ for (int k=0; k<t; k++)
+ {
+ v1.x += m_tris[array[i][k]].tangent.x;
+ v1.y += m_tris[array[i][k]].tangent.y;
+ v1.z += m_tris[array[i][k]].tangent.z;
+
+ v2.x += m_tris[array[i][k]].binormal.x;
+ v2.y += m_tris[array[i][k]].binormal.y;
+ v2.z += m_tris[array[i][k]].binormal.z;
+ }
+ m_tangents[i] = NormalizeVector(v1);
+ //m_binormals[i] = NormalizeVector(v2);
+
+ m_binormals[i] = NormalizeVector(CrossProduct(m_tangents[i], m_normals[i]));
+ }
+}
+
+void LMesh::Optimize(LOptimizationLevel value)
+{
+ switch (value)
+ {
+ case oNone:
+ TransformVertices();
+ break;
+ case oSimple:
+ TransformVertices();
+ CalcNormals(false);
+ break;
+ case oFull:
+ TransformVertices();
+ CalcNormals(true);
+ CalcTextureSpace();
+ break;
+ }
+}
+
+void LMesh::SetTri(const LTri &tri, uint index)
+{
+ if (index >= m_triangles.size())
+ return;
+ m_tris[index] = tri;
+}
+
+LTri& LMesh::GetTri(uint index)
+{
+ return m_tris[index];
+}
+
+uint LMesh::GetMaterial(uint index)
+{
+ return m_materials[index];
+}
+
+uint LMesh::AddMaterial(uint id)
+{
+ m_materials.push_back(id);
+ return m_materials.size()-1;
+}
+
+uint LMesh::GetMaterialCount()
+{
+ return m_materials.size();
+}
+
+//-------------------------------------------------------
+// LLight implementation
+//-------------------------------------------------------
+
+LLight::LLight()
+: LObject()
+{
+ Clear();
+}
+
+LLight::~LLight()
+{
+
+}
+
+void LLight::Clear()
+{
+ m_pos.x = m_pos.y = m_pos.z = 0.0f;
+ m_color.r = m_color.g = m_color.b = 0.0f;
+ m_spotlight = false;
+}
+
+void LLight::SetPosition(LVector3 vec)
+{
+ m_pos = vec;
+}
+
+LVector3 LLight::GetPosition()
+{
+ return m_pos;
+}
+
+void LLight::SetColor(LColor3 color)
+{
+ m_color = color;
+}
+
+LColor3 LLight::GetColor()
+{
+ return m_color;
+}
+
+void LLight::SetSpotlight(bool value)
+{
+ m_spotlight = value;
+}
+
+bool LLight::GetSpotlight()
+{
+ return m_spotlight;
+}
+
+void LLight::SetTarget(LVector3 target)
+{
+ m_target = target;
+}
+
+LVector3 LLight::GetTarget()
+{
+ return m_target;
+}
+
+void LLight::SetHotspot(float value)
+{
+ m_hotspot = value;
+}
+
+float LLight::GetHotspot()
+{
+ return m_hotspot;
+}
+
+void LLight::SetFalloff(float value)
+{
+ m_falloff = value;
+}
+
+float LLight::GetFalloff()
+{
+ return m_falloff;
+}
+
+//-------------------------------------------------------
+// LImporter implementation
+//-------------------------------------------------------
+
+LImporter::LImporter()
+{
+ Clear();
+}
+
+LImporter::~LImporter()
+{
+ Clear();
+}
+
+uint LImporter::GetMeshCount()
+{
+ return m_meshes.size();
+}
+
+uint LImporter::GetLightCount()
+{
+ return m_lights.size();
+}
+
+uint LImporter::GetMaterialCount()
+{
+ return m_materials.size();
+}
+
+LMesh& LImporter::GetMesh(uint index)
+{
+ return m_meshes[index];
+}
+
+LLight& LImporter::GetLight(uint index)
+{
+ return m_lights[index];
+}
+
+LMaterial& LImporter::GetMaterial(uint index)
+{
+ return m_materials[index];
+}
+
+LMaterial* LImporter::FindMaterial(const std::string& name)
+{
+ for (uint i=0; i<m_materials.size(); i++)
+ if (m_materials[i].IsObject(name))
+ return &m_materials[i];
+ return 0;
+}
+
+LMesh* LImporter::FindMesh(const std::string& name)
+{
+ for (uint i=0; i<m_meshes.size(); i++)
+ if (m_meshes[i].IsObject(name))
+ return &m_meshes[i];
+ return 0;
+}
+
+LLight* LImporter::FindLight(const std::string& name)
+{
+ for (uint i=0; i<m_lights.size(); i++)
+ if (m_lights[i].IsObject(name))
+ return &m_lights[i];
+ return 0;
+}
+
+void LImporter::Clear()
+{
+ m_meshes.clear();
+ m_lights.clear();
+ m_materials.clear();
+ m_optLevel = oFull;
+}
+
+void LImporter::SetOptimizationLevel(LOptimizationLevel value)
+{
+ m_optLevel = value;
+}
+
+LOptimizationLevel LImporter::GetOptimizationLevel()
+{
+ return m_optLevel;
+}
+
+//-------------------------------------------------------
+// L3DS implementation
+//-------------------------------------------------------
+
+L3DS::L3DS()
+: LImporter()
+{
+ m_buffer = 0;
+ m_bufferSize = 0;
+ m_pos = 0;
+ m_eof = false;
+}
+
+L3DS::L3DS(const char *filename)
+: LImporter()
+{
+ m_buffer = 0;
+ m_bufferSize = 0;
+ m_pos = 0;
+ m_eof = false;
+ LoadFile(filename);
+}
+
+L3DS::~L3DS()
+{
+ if (m_bufferSize > 0)
+ free(m_buffer);
+}
+
+bool L3DS::LoadFile(const char *filename)
+{
+ FILE *f;
+ f = fopen(filename, "rb");
+ if (f == 0)
+ {
+ ErrorMsg("L3DS::LoadFile - cannot open file");
+ return false;
+ }
+ fseek(f, 0, SEEK_END);
+ m_bufferSize = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ m_buffer = (unsigned char*) calloc(m_bufferSize, 1);
+ if (m_buffer == 0)
+ {
+ ErrorMsg("L3DS::LoadFile - not enough memory (malloc failed)");
+ return false;
+ }
+ if (fread(m_buffer, m_bufferSize, 1, f) != 1)
+ {
+ fclose(f);
+ free(m_buffer);
+ m_bufferSize = 0;
+ ErrorMsg("L3DS::LoadFile - error reading from file");
+ return false;
+ }
+ fclose(f);
+ Clear();
+ bool res = Read3DS();
+ free(m_buffer);
+ m_buffer = 0;
+ m_bufferSize = 0;
+ return res;
+}
+
+short L3DS::ReadShort()
+{
+ if ((m_buffer!=0) && (m_bufferSize != 0) && ((m_pos+2)<m_bufferSize))
+ {
+ short *w = (short*)(m_buffer+m_pos);
+ short s = *w;//(short)*(m_buffer+m_pos);
+ m_pos += 2;
+ return s;
+ }
+ m_eof = true;
+ return 0;
+}
+
+int L3DS::ReadInt()
+{
+ if ((m_buffer!=0) && (m_bufferSize != 0) && ((m_pos+4)<m_bufferSize))
+ {
+ int *w = (int*)(m_buffer+m_pos);
+ int s = *w;//(int)*(m_buffer+m_pos);
+ m_pos += 4;
+ return s;
+ }
+ m_eof = true;
+ return 0;
+}
+
+char L3DS::ReadChar()
+{
+ if ((m_buffer!=0) && (m_bufferSize != 0) && ((m_pos+1)<m_bufferSize))
+ {
+ char s = (char)*(m_buffer+m_pos);
+ m_pos += 1;
+ return s;
+ }
+ m_eof = true;
+ return 0;
+}
+
+float L3DS::ReadFloat()
+{
+ if ((m_buffer!=0) && (m_bufferSize != 0) && ((m_pos+4)<m_bufferSize))
+ {
+ float *w = (float*)(m_buffer+m_pos);
+ float s = *w;//(float)*(m_buffer+m_pos);
+ m_pos += 4;
+ return s;
+ }
+ m_eof = true;
+ return 0.0;
+}
+
+byte L3DS::ReadByte()
+{
+ if ((m_buffer!=0) && (m_bufferSize != 0) && ((m_pos+1)<m_bufferSize))
+ {
+ byte s = (byte)*(m_buffer+m_pos);
+ m_pos += 1;
+ return s;
+ }
+ m_eof = true;
+ return 0;
+}
+
+int L3DS::ReadASCIIZ(char *buf, int max_count)
+{
+ int count;
+ if ((m_buffer==0) || (m_bufferSize == 0) || (m_pos>=m_bufferSize))
+ {
+ count = 0;
+ m_eof = true;
+ return count;
+ }
+ count = 0;
+ char c = ReadChar();
+ while ((c!=0) && (count<max_count-1))
+ {
+ buf[count] = c;
+ count ++;
+ c = ReadChar();
+ }
+ buf[count] = 0;
+ return count;
+}
+
+void L3DS::Seek(int offset, int origin)
+{
+ if (origin == SEEK_START)
+ m_pos = offset;
+ if (origin == SEEK_CURSOR)
+ m_pos += offset;
+ if (m_pos < 0)
+ m_pos = 0;
+ if (m_pos >= m_bufferSize)
+ m_pos = m_bufferSize-1;
+ m_eof = false;
+}
+
+uint L3DS::Pos()
+{
+ return m_pos;
+}
+
+LChunk L3DS::ReadChunk()
+{
+ LChunk chunk;
+ chunk.id = ReadShort();
+ int a = ReadInt();
+ chunk.start = Pos();
+ chunk.end = chunk.start+a-6;
+ return chunk;
+}
+
+bool L3DS::FindChunk(LChunk &target, const LChunk &parent)
+{
+ if (Pos() >= parent.end)
+ return false;
+ LChunk chunk;
+ chunk = ReadChunk();
+ while (( chunk.id != target.id) && (chunk.end <= parent.end))
+ {
+ SkipChunk(chunk);
+ if (chunk.end >= parent.end)
+ break;
+ chunk = ReadChunk();
+ }
+ if (chunk.id == target.id)
+ {
+ target.start = chunk.start;
+ target.end = chunk.end;
+ return true;
+ }
+ return false;
+}
+
+void L3DS::SkipChunk(const LChunk &chunk)
+{
+ Seek(chunk.end, SEEK_START);
+}
+
+void L3DS::GotoChunk(const LChunk &chunk)
+{
+ Seek(chunk.start, SEEK_START);
+}
+
+LColor3 L3DS::ReadColor(const LChunk &chunk)
+{
+ LColor3 col = black;
+ GotoChunk(chunk);
+ switch (chunk.id)
+ {
+ case COLOR_F:
+ col.r = ReadFloat();
+ col.g = ReadFloat();
+ col.b = ReadFloat();
+ break;
+ case COLOR_24:
+ col.r = ReadByte()/255.0f;
+ col.g = ReadByte()/255.0f;
+ col.b = ReadByte()/255.0f;
+ break;
+ case LIN_COLOR_F:
+ col.r = ReadFloat();
+ col.g = ReadFloat();
+ col.b = ReadFloat();
+ break;
+ case LIN_COLOR_24:
+ col.r = ReadByte()/255.0f;
+ col.g = ReadByte()/255.0f;
+ col.b = ReadByte()/255.0f;
+ break;
+ default:
+ ErrorMsg("L3DS::ReadColor - error this is not a color chunk");
+ }
+ return col;
+}
+
+float L3DS::ReadPercentage(const LChunk &chunk)
+{
+ GotoChunk(chunk);
+ switch (chunk.id)
+ {
+ case INT_PERCENTAGE:
+ return (ReadShort()/100.0f);
+ case FLOAT_PERCENTAGE:
+ return ReadFloat();
+ }
+ ErrorMsg("L3DS::ReadPercentage - error, the chunk is not a percentage chunk");
+ return 0;
+}
+
+bool L3DS::Read3DS()
+{
+ LChunk mainchunk;
+ LChunk edit;
+ edit.id = EDIT3DS;
+ mainchunk = ReadChunk();
+ if (mainchunk.id != MAIN3DS)
+ {
+ ErrorMsg("L3DS::Read3DS - wrong file format");
+ return false;
+ }
+ if (!FindChunk(edit, mainchunk))
+ return false;
+ LChunk obj;
+ LChunk ml;
+
+ GotoChunk(edit);
+ obj.id = MAT_ENTRY;
+ while (FindChunk(obj, edit))
+ {
+ ReadMaterial(obj);
+ SkipChunk(obj);
+ }
+ GotoChunk(edit);
+
+ obj.id = EDIT_OBJECT;
+ {
+ while (FindChunk(obj, edit))
+ {
+ ReadASCIIZ(m_objName, 99);
+ ml = ReadChunk();
+ if (ml.id == OBJ_TRIMESH)
+ ReadMesh(ml);
+ if (ml.id == OBJ_LIGHT)
+ ReadLight(ml);
+ SkipChunk(obj);
+ }
+ }
+
+ // read the keyframer data here to find out correct object orientation
+
+ LChunk keyframer;
+ keyframer.id = KFDATA;
+
+ LChunk objtrack;
+ objtrack.id = OBJECT_NODE_TAG;
+
+ GotoChunk(mainchunk);
+ if (FindChunk(keyframer, mainchunk))
+ { // keyframer chunk is present
+ GotoChunk(keyframer);
+ while (FindChunk(objtrack, keyframer))
+ {
+ ReadKeyframeData(objtrack);
+ SkipChunk(objtrack);
+ }
+ }
+
+ for (uint i=0; i<m_meshes.size(); i++) {
+ m_meshes[i].Optimize(m_optLevel);
+ }
+
+ m_pos = 0;
+ strcpy(m_objName, "");
+ return true;
+}
+
+void L3DS::ReadLight(const LChunk &parent)
+{
+ float t;
+ LVector3 v;
+ LLight light;
+ light.SetName(m_objName);
+ GotoChunk(parent);
+ LChunk chunk = ReadChunk();
+ if (chunk.id == OBJ_LIGHT)
+ {
+ v.x = ReadFloat();
+ v.y = ReadFloat();
+ v.z = ReadFloat();
+ if (chunk.end < parent.end)
+ chunk = ReadChunk();
+ while (chunk.end <= parent.end)
+ {
+ switch (chunk.id)
+ {
+ case COLOR_24:
+ case COLOR_F:
+ case LIN_COLOR_F:
+ case LIN_COLOR_24:
+ light.SetColor(ReadColor(chunk));
+ break;
+ case SPOTLIGHT:
+ v.x = ReadFloat();
+ v.y = ReadFloat();
+ v.z = ReadFloat();
+ light.SetTarget(v);
+ t = ReadFloat();
+ light.SetHotspot(t);
+ t = ReadFloat();
+ light.SetFalloff(t);
+ break;
+ default:
+ break;
+ }
+ SkipChunk(chunk);
+ if (chunk.end >= parent.end)
+ break;
+ chunk = ReadChunk();
+
+ }
+ }
+ m_lights.push_back(light);
+}
+
+void L3DS::ReadMesh(const LChunk &parent)
+{
+ unsigned short count, i;
+ LVector4 p;
+ LMatrix4 m;
+ LVector2 t;
+ p.w = 1.0f;
+ LMesh mesh;
+ mesh.SetName(m_objName);
+ GotoChunk(parent);
+ LChunk chunk = ReadChunk();
+ while (chunk.end <= parent.end)
+ {
+ switch (chunk.id)
+ {
+ case TRI_VERTEXLIST:
+ count = ReadShort();
+ mesh.SetVertexArraySize(count);
+ for (i=0; i < count; i++)
+ {
+ p.x = ReadFloat();
+ p.y = ReadFloat();
+ p.z = ReadFloat();
+ mesh.SetVertex(p, i);
+ }
+ break;
+ case TRI_FACEMAPPING:
+ count = ReadShort();
+ if (mesh.GetVertexCount() == 0)
+ mesh.SetVertexArraySize(count);
+ for (i=0; i < count; i++)
+ {
+ t.x = ReadFloat();
+ t.y = ReadFloat();
+ mesh.SetUV(t, i);
+ }
+ break;
+ case TRI_FACELIST:
+ ReadFaceList(chunk, mesh);
+ break;
+ case TRI_MATRIX:
+ {
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 3; j++)
+ m.m[i][j] = ReadFloat();
+
+ m.m[0][3] = 0.0f;
+ m.m[1][3] = 0.0f;
+ m.m[2][3] = 0.0f;
+ m.m[3][3] = 1.0f;
+
+ mesh.SetMatrix(m);
+ }
+ break;
+ default:
+ break;
+ }
+ SkipChunk(chunk);
+ if (chunk.end >= parent.end)
+ break;
+ chunk = ReadChunk();
+ }
+ m_meshes.push_back(mesh);
+}
+
+void L3DS::ReadFaceList(const LChunk &chunk, LMesh &mesh)
+{
+ // variables
+ unsigned short count, t;
+ uint i;
+ LTri tri;
+ LChunk ch;
+ char str[20];
+ //uint mat;
+
+ // consistency checks
+ if (chunk.id != TRI_FACELIST)
+ {
+ ErrorMsg("L3DS::ReadFaceList - internal error: wrong chunk passed as parameter");
+ return;
+ }
+ GotoChunk(chunk);
+ tri.smoothingGroups = 1;
+ // read the number of faces
+ count = ReadShort();
+ mesh.SetTriangleArraySize(count);
+ for (i=0; i<count; i++)
+ {
+ tri.a = ReadShort();
+ tri.b = ReadShort();
+ tri.c = ReadShort();
+ ReadShort();
+ mesh.SetTri(tri, i);
+ }
+ // now read the optional chunks
+ ch = ReadChunk();
+ while (ch.end <= chunk.end)
+ {
+ switch (ch.id)
+ {
+ case TRI_MAT_GROUP: {
+ ReadASCIIZ(str, 20);
+ int mat_id = FindMaterial(str)->GetID();
+ mesh.AddMaterial(mat_id);
+ count = ReadShort();
+ for (i=0; i<count; i++) {
+ t = ReadShort();
+ mesh.GetTri(t).materialId = mat_id;
+ }
+ }
+ break;
+
+ case TRI_SMOOTH_GROUP:
+ for (i=0; i<mesh.GetTriangleCount(); i++)
+ mesh.GetTri(i).smoothingGroups = (ulong) ReadInt();
+ break;
+ }
+ SkipChunk(ch);
+ ch = ReadChunk();
+ }
+}
+
+void L3DS::ReadMaterial(const LChunk &parent)
+{
+ // variables
+ LChunk chunk;
+ LChunk child;
+ char str[30];
+ LMaterial mat;
+ short sh;
+
+ GotoChunk(parent);
+
+ chunk = ReadChunk();
+ while (chunk.end <= parent.end)
+ {
+ switch (chunk.id)
+ {
+ case MAT_NAME:
+ ReadASCIIZ(str, 30);
+ mat.SetName(str);
+ break;
+ case MAT_AMBIENT:
+ child = ReadChunk();
+ mat.SetAmbientColor(ReadColor(child));
+ break;
+ case MAT_DIFFUSE:
+ child = ReadChunk();
+ mat.SetDiffuseColor(ReadColor(child));
+ break;
+ case MAT_SPECULAR:
+ child = ReadChunk();
+ mat.SetSpecularColor(ReadColor(child));
+ break;
+ case MAT_SHININESS:
+ child = ReadChunk();
+ mat.SetShininess(ReadPercentage(child));
+ break;
+ case MAT_TRANSPARENCY:
+ child = ReadChunk();
+ mat.SetTransparency(ReadPercentage(child));
+ break;
+ case MAT_SHADING:
+ sh = ReadShort();
+ switch (sh)
+ {
+ case 0:
+ mat.SetShadingType(sWireframe);
+ break;
+ case 1:
+ mat.SetShadingType(sFlat);
+ break;
+ case 2:
+ mat.SetShadingType(sGouraud);
+ break;
+ case 3:
+ mat.SetShadingType(sPhong);
+ break;
+ case 4:
+ mat.SetShadingType(sMetal);
+ break;
+ }
+ break;
+ case MAT_WIRE:
+ mat.SetShadingType(sWireframe);
+ break;
+ case MAT_TEXMAP:
+ ReadMap(chunk, mat.GetTextureMap1());
+ break;
+ case MAT_TEX2MAP:
+ ReadMap(chunk, mat.GetTextureMap2());
+ break;
+ case MAT_OPACMAP:
+ ReadMap(chunk, mat.GetOpacityMap());
+ break;
+ case MAT_BUMPMAP:
+ ReadMap(chunk, mat.GetBumpMap());
+ break;
+ case MAT_SPECMAP:
+ ReadMap(chunk, mat.GetSpecularMap());
+ break;
+ case MAT_REFLMAP:
+ child = ReadChunk();
+ mat.GetReflectionMap().strength = ReadPercentage(child);
+ SkipChunk(child);
+ child = ReadChunk();
+ if (child.id != MAT_MAPNAME)
+ {
+ ErrorMsg("L3DS::ReadMaterial - error, expected chunk not found");
+ return;
+ }
+ ReadASCIIZ(str, 30);
+ if (strcmp(str, "") == 0)
+ strcpy(mat.GetReflectionMap().mapName, "auto");
+ break;
+ }
+
+ SkipChunk(chunk);
+ chunk = ReadChunk();
+ }
+ m_materials.push_back(mat);
+ m_materials[m_materials.size()-1].SetID(m_materials.size()-1);
+}
+
+void L3DS::ReadMap(const LChunk &chunk, LMap& map)
+{
+ LChunk child;
+ char str[20];
+ GotoChunk(chunk);
+ child = ReadChunk();
+ while (child.end <= chunk.end)
+ {
+ switch (child.id)
+ {
+ case INT_PERCENTAGE:
+ map.strength = ReadPercentage(child);
+ break;
+ case MAT_MAPNAME:
+ ReadASCIIZ(str, 20);
+ strcpy(map.mapName, str);
+ break;
+ case MAT_MAP_USCALE:
+ map.uScale = ReadFloat();
+ break;
+ case MAT_MAP_VSCALE:
+ map.vScale = ReadFloat();
+ break;
+ case MAT_MAP_UOFFSET:
+ map.uOffset = ReadFloat();
+ break;
+ case MAT_MAP_VOFFSET:
+ map.vOffset = ReadFloat();
+ break;
+ case MAT_MAP_ANG:
+ map.angle = ReadFloat();
+ break;
+ }
+ SkipChunk(child);
+ child = ReadChunk();
+ }
+}
+
+void L3DS::ReadKeyframeData(const LChunk &parent)
+{
+ uint frames = 0;
+
+ LChunk node_hdr;
+ node_hdr.id = NODE_HDR;
+
+ char str[20];
+ LMesh *mesh;
+
+ GotoChunk(parent);
+ if (!FindChunk(node_hdr, parent))
+ return;
+ GotoChunk(node_hdr);
+ ReadASCIIZ(str, 19);
+ mesh = FindMesh(str);
+ if (mesh == 0)
+ return;
+ GotoChunk(parent);
+
+ // read the pivot
+ LVector3 pivot = zero3;
+
+ LChunk pivotchunk;
+ pivotchunk.id = PIVOT;
+ if (FindChunk(pivotchunk, parent))
+ {
+ GotoChunk(pivotchunk);
+ pivot.x = ReadFloat();
+ pivot.y = ReadFloat();
+ pivot.z = ReadFloat();
+ }
+ GotoChunk(parent);
+
+ // read frame 0 from the position track
+ LVector3 pos = zero3;
+
+ frames = 0;
+
+ LChunk poschunk;
+ poschunk.id = POS_TRACK_TAG;
+ if (FindChunk(poschunk, parent))
+ {
+ GotoChunk(poschunk);
+ // read the trackheader structure
+ ReadShort();
+ ReadInt();
+ ReadInt();
+ frames = ReadInt();
+ if (frames > 0)
+ {
+ ReadKeyheader();
+ pos.x = ReadFloat();
+ pos.y = ReadFloat();
+ pos.z = ReadFloat();
+ }
+ }
+ GotoChunk(parent);
+
+ // now read the rotation track
+ LVector4 rot = zero4;
+
+ LChunk rotchunk;
+ rotchunk.id = ROT_TRACK_TAG;
+
+ frames = 0;
+ if (FindChunk(rotchunk, parent))
+ {
+ GotoChunk(rotchunk);
+ // read the trackheader structure
+ ReadShort();
+ ReadInt();
+ ReadInt();
+ frames = ReadInt();
+ if (frames > 0)
+ {
+ ReadKeyheader();
+ rot.x = ReadFloat();
+ rot.y = ReadFloat();
+ rot.z = ReadFloat();
+ rot.w = ReadFloat();
+ }
+ }
+ GotoChunk(parent);
+
+ // now read the scaling chunk
+ LVector3 scale;
+ scale.x = 1;
+ scale.y = 1;
+ scale.z = 1;
+
+ LChunk scalechunk;
+ scalechunk.id = SCL_TRACK_TAG;
+
+ frames = 0;
+
+ if (FindChunk(scalechunk, parent))
+ {
+ GotoChunk(scalechunk);
+ // read the trackheader structure
+ ReadShort();
+ ReadInt();
+ ReadInt();
+ frames = ReadInt();
+ if (frames > 0)
+ {
+ ReadKeyheader();
+ scale.x = ReadFloat();
+ scale.y = ReadFloat();
+ scale.z = ReadFloat();
+ }
+ }
+ GotoChunk(parent);
+
+
+}
+
+long L3DS::ReadKeyheader()
+{
+ long frame;
+ frame = ReadInt();
+ short opts = ReadShort();
+ if (opts & 32768) // 32768 is 1000000000000000 binary
+ { // tension is present
+ ReadFloat();
+ }
+ if (opts & 16384) // 16384 is 0100000000000000 binary
+ { // continuity is present
+ ReadFloat();
+ }
+ if (opts & 8192)
+ { // bias info present
+ ReadFloat();
+ }
+ if (opts & 4096)
+ { // "ease to" present
+ ReadFloat();
+ }
+ if (opts & 2048)
+ { // "ease from" present
+ ReadFloat();
+ }
+ return frame;
+} \ No newline at end of file
diff --git a/Magic2/l3ds.h b/Magic2/l3ds.h
new file mode 100644
index 0000000..b38e1e8
--- /dev/null
+++ b/Magic2/l3ds.h
@@ -0,0 +1,457 @@
+// copyright (c) 2001-2002 Lev Povalahev
+// this is a 3ds importer version 2
+
+#ifndef L3DS_H
+#define L3DS_H
+
+// includes
+#include <vector>
+#include <string>
+
+//---------------------------------------------------------
+
+typedef unsigned int uint;
+typedef unsigned char byte;
+
+enum LShading {sWireframe, sFlat, sGouraud, sPhong, sMetal};
+
+enum LOptimizationLevel {oNone, oSimple, oFull};
+
+// for internal use
+struct LChunk;
+struct LTri;
+
+//------------------------------------------------
+
+struct LVector4
+{
+ float x;
+ float y;
+ float z;
+ float w;
+
+ int operator==(const LVector4& v) const { return x==v.x && y==v.y && z==v.z && w==v.w; }
+};
+
+struct LVector3
+{
+ float x;
+ float y;
+ float z;
+
+ int operator==(const LVector3& v) const { return x==v.x && y==v.y && z==v.z; }
+};
+
+struct LVector2
+{
+ float x;
+ float y;
+
+ int operator==(const LVector2& v) const { return x==v.x && y==v.y; }
+};
+
+struct LColor3
+{
+ float r;
+ float g;
+ float b;
+
+ int operator==(const LColor3& v) const { return r==v.r && g==v.g && b==v.b; }
+};
+
+//------------------------------------------------
+
+struct LTriangle
+{
+ unsigned short a;
+ unsigned short b;
+ unsigned short c;
+};
+
+struct LMatrix4
+{
+ float m[4][4];
+};
+
+struct LTriangle2
+{
+ LVector4 vertices[3];
+ LVector3 vertexNormals[3];
+ LVector2 textureCoords[3];
+ LVector3 faceNormal;
+ uint materialId;
+};
+
+// a structure for a texture map
+struct LMap
+{
+ // the strength of the texture map
+ float strength;
+ // the file name of the map. only 8.3 format in 3ds files :(
+ char mapName[255];
+ float uScale;
+ float vScale;
+ float uOffset;
+ float vOffset;
+ float angle;
+};
+
+//------------------------------------------------
+
+class LObject
+{
+public:
+ // the default constructor, initilializes the m_name here
+ LObject();
+ // the destructor frees memory (m_name)
+ virtual ~LObject();
+ // call this to get the name of the object
+ virtual const std::string& GetName();
+
+ // this methods should not be used by the "user", they're used internally to fill the class
+ // with valid data when reading from file. If you're about to add an importer for another format you'LL
+ // have to use these methods
+ // call this to set the name of the object
+ virtual void SetName(const std::string& value);
+ // returns true if the object's name is the name passed as parameter
+ bool IsObject(const std::string &name);
+protected:
+ // the name of the object
+ std::string m_name;
+};
+
+//------------------------------------------------
+
+class LMaterial : public LObject
+{
+public:
+ // the default constructor, does the initialization
+ LMaterial();
+ // the destructor
+ virtual ~LMaterial();
+ // returns the material ID
+ uint GetID();
+ // returns the pointer to teh texture map 1
+ LMap& GetTextureMap1();
+ // returns the pointer to the texture map 2
+ LMap& GetTextureMap2();
+ // returns the pointer to teh opacity map
+ LMap& GetOpacityMap();
+ // returns the pointer to the specular (gloss) map
+ LMap& GetSpecularMap();
+ // returns the pointer to the bump map
+ LMap& GetBumpMap();
+ // returns the pointer to the reflection map
+ LMap& GetReflectionMap();
+ // returns the ambient color of the material
+ LColor3 GetAmbientColor();
+ // returns the diffuse color of the material
+ LColor3 GetDiffuseColor();
+ // returns the specular color of the material
+ LColor3 GetSpecularColor();
+ // returns the shininess of the material, ranging from 0(matte) to 1(shiny)
+ float GetShininess();
+ // returns the transparency of the material, ranging from 1(fully transparent) to 0(opaque)
+ float GetTransparency();
+ // returns the type of shading, see LShading type
+ LShading GetShadingType();
+
+ // this methods should not be used by the "user", they're used internally to fill the class
+ // with valid data when reading from file. If you're about to add an importer for another format you'LL
+ // have to use these methods
+ // sets the material ID to "value"
+ void SetID(uint value);
+ // call this to set the ambient color of the material
+ void SetAmbientColor(const LColor3 &color);
+ // sets the diffuse color of the material
+ void SetDiffuseColor(const LColor3 &color);
+ // sets the specular color of the material
+ void SetSpecularColor(const LColor3 &color);
+ // sets the shininess of the material
+ void SetShininess(float value);
+ // sets the transparency of the material
+ void SetTransparency(float value);
+ // sets the shading type
+ void SetShadingType(LShading shading);
+protected:
+ // the unique material ID
+ int m_id;
+ // the first texture map
+ LMap m_texMap1;
+ // the second texture map
+ LMap m_texMap2;
+ // the opacity map
+ LMap m_opacMap;
+ // the reflection map
+ LMap m_reflMap;
+ // the bump map
+ LMap m_bumpMap;
+ // specular map
+ LMap m_specMap;
+ // material ambient color
+ LColor3 m_ambient;
+ // material diffuse color
+ LColor3 m_diffuse;
+ // material specular color
+ LColor3 m_specular;
+ // shininess
+ float m_shininess;
+ // transparency
+ float m_transparency;
+ // the shading type for the material
+ LShading m_shading;
+};
+
+//------------------------------------------------
+
+class LMesh : public LObject
+{
+public:
+ // the default constructor
+ LMesh();
+ // the destructor
+ virtual ~LMesh();
+ // clears the mesh, deleteing all data
+ void Clear();
+ // returns the number of vertices in the mesh
+ uint GetVertexCount();
+ // sets the the size fo the vertex array - for internal use
+ void SetVertexArraySize(uint value);
+ // returns the number of triangles in the mesh
+ uint GetTriangleCount();
+ // sets the size of the triangle array - for internal use
+ void SetTriangleArraySize(uint value);
+ // returns given vertex
+ const LVector4& GetVertex(uint index);
+ // returns the given normal
+ const LVector3& GetNormal(uint index);
+ // returns the given texture coordinates vector
+ const LVector2& GetUV(uint index);
+ // returns the pointer to the array of tangents
+ const LVector3& GetTangent(uint index);
+ // returns the pointer to the array of binormals
+ const LVector3& GetBinormal(uint index);
+ // sets the vertex at a given index to "vec" - for internal use
+ void SetVertex(const LVector4 &vec, uint index);
+ // sets the normal at a given index to "vec" - for internal use
+ void SetNormal(const LVector3 &vec, uint index);
+ // sets the texture coordinates vector at a given index to "vec" - for internal use
+ void SetUV(const LVector2 &vec, uint index);
+ // sets the tangent at a given index to "vec" - for internal use
+ void SetTangent(const LVector3 &vec, uint index);
+ // sets the binormal at a given index to "vec" - for internal use
+ void SetBinormal(const LVector3 &vec, uint index);
+ // returns the triangle with a given index
+ const LTriangle& GetTriangle(uint index);
+ // returns the triangle with a given index, see LTriangle2 structure description
+ LTriangle2 GetTriangle2(uint index);
+ // returns the mesh matrix, should be identity matrix after loading
+ LMatrix4 GetMatrix();
+ // sets the mesh matrix to a given matrix - for internal use
+ void SetMatrix(LMatrix4 m);
+ // optimizises the mesh using a given optimization level
+ void Optimize(LOptimizationLevel value);
+ // sets an internal triangle structure with index "index" - for internal use only
+ void SetTri(const LTri &tri, uint index);
+ // returns the pointer to the internal triangle structure - for internal use only
+ LTri& GetTri(uint index);
+ // returns the material id with a given index for the mesh
+ uint GetMaterial(uint index);
+ // adds a material to the mesh and returns its index - for internal use
+ uint AddMaterial(uint id);
+ // returns the number of materials used in the mesh
+ uint GetMaterialCount();
+protected:
+ // the vertices, normals, etc.
+ std::vector<LVector4> m_vertices;
+ std::vector<LVector3> m_normals;
+ std::vector<LVector3> m_binormals;
+ std::vector<LVector3> m_tangents;
+ std::vector<LVector2> m_uv;
+
+ // triangles
+ std::vector<LTriangle> m_triangles;
+
+ //used internally
+ std::vector<LTri> m_tris;
+
+ // the transformation matrix.
+ LMatrix4 m_matrix;
+
+ // the material ID array
+ std::vector<uint> m_materials;
+
+ // calculates the normals, either using the smoothing groups information or not
+ void CalcNormals(bool useSmoothingGroups);
+ // calculates the texture(tangent) space for each vertex
+ void CalcTextureSpace();
+ // transforms the vertices by the mesh matrix
+ void TransformVertices();
+};
+
+//------------------------------------------------
+
+class LLight : public LObject
+{
+public:
+ // the default constructor
+ LLight();
+ // the destructor
+ virtual ~LLight();
+ // clears the data the class holds
+ void Clear();
+ // sets the position of the light source - for internal use
+ void SetPosition(LVector3 vec);
+ // returns the position of the light source
+ LVector3 GetPosition();
+ // sets the color of the light - for internal use
+ void SetColor(LColor3 color);
+ // returns the color of the light
+ LColor3 GetColor();
+ // sets whether the light is a spotlight or not - internal use
+ void SetSpotlight(bool value);
+ // returns true if the light is a spotlight
+ bool GetSpotlight();
+ // sets the target of the light - internal use
+ void SetTarget(LVector3 target);
+ // returns the target of the spotlight
+ LVector3 GetTarget();
+ // sets the hotspot - internal use
+ void SetHotspot(float value);
+ // returns the hotspot
+ float GetHotspot();
+ // sets falloff - internal use
+ void SetFalloff(float value);
+ // returns falloff
+ float GetFalloff();
+protected:
+ LVector3 m_pos;
+ LColor3 m_color;
+ bool m_spotlight;
+ LVector3 m_target;
+ float m_hotspot;
+ float m_falloff;
+};
+
+//------------------------------------------------
+
+class LImporter
+{
+public:
+ // the default constructor
+ LImporter();
+ // the destructor
+ virtual ~LImporter();
+ // reads the model from a file, must be overriden by the child classes
+ virtual bool LoadFile(const char *filename) = 0;
+ // returns the number of meshes in the scene
+ uint GetMeshCount();
+ // returns the number of lights in the scene
+ uint GetLightCount();
+ // returns the number of materials in the scene
+ uint GetMaterialCount();
+ // returns a pointer to a mesh
+ LMesh& GetMesh(uint index);
+ // returns a pointer to a light at a given index
+ LLight& GetLight(uint index);
+ // returns the pointer to the material
+ LMaterial& GetMaterial(uint index);
+ // returns the pointer to the material with a given name, or NULL if the material was not found
+ LMaterial* FindMaterial(const std::string &name);
+ // returns the pointer to the mesh with a given name, or NULL if the mesh with such name
+ // is not present in the scene
+ LMesh* FindMesh(const std::string &name);
+ // returns the pointer to the light with a given name, or NULL if not found
+ LLight* FindLight(const std::string &name);
+ // sets the optimization level to a given value
+ void SetOptimizationLevel(LOptimizationLevel value);
+ // returns the current optimization level
+ LOptimizationLevel GetOptimizationLevel();
+protected:
+ // the lights found in the scene
+ std::vector<LLight> m_lights;
+ // triangular meshes
+ std::vector<LMesh> m_meshes;
+ // the materials in the scene
+ std::vector<LMaterial> m_materials;
+ // level of optimization to perform on the meshes
+ LOptimizationLevel m_optLevel;
+ // clears all data.
+ virtual void Clear();
+};
+//------------------------------------------------
+
+class L3DS : public LImporter
+{
+public:
+ // the default contructor
+ L3DS();
+ // constructs the object and loads the file
+ L3DS(const char *filename);
+ // destructor
+ virtual ~L3DS();
+ // load 3ds file
+ virtual bool LoadFile(const char *filename);
+protected:
+ // used internally for reading
+ char m_objName[100];
+ // true if end of file is reached
+ bool m_eof;
+ // buffer for loading, used for speedup
+ unsigned char *m_buffer;
+ // the size of the buffer
+ uint m_bufferSize;
+ // the current cursor position in the buffer
+ uint m_pos;
+
+ // reads a short value from the buffer
+ short ReadShort();
+ // reads an int value from the buffer
+ int ReadInt();
+ // reads a char from the buffer
+ char ReadChar();
+ //reada a floatvalue from the buffer
+ float ReadFloat();
+ //reads an unsigned byte from the buffer
+ byte ReadByte();
+ //reads an asciiz string
+ int ReadASCIIZ(char *buf, int max_count);
+ // seek wihtin the buffer
+ void Seek(int offset, int origin);
+ // returns the position of the cursor
+ uint Pos();
+
+ // read the chunk and return it.
+ LChunk ReadChunk();
+ // read until given chunk is found
+ bool FindChunk(LChunk &target, const LChunk &parent);
+ // skip to the end of chunk "chunk"
+ void SkipChunk(const LChunk &chunk);
+ // goes to the beginning of the data in teh given chunk
+ void GotoChunk(const LChunk &chunk);
+
+ // the function read the color chunk (any of the color chunks)
+ LColor3 ReadColor(const LChunk &chunk);
+ // the function that read the percentage chunk and returns a float from 0 to 1
+ float ReadPercentage(const LChunk &chunk);
+ // this is where 3ds file is being read
+ bool Read3DS();
+ // read a light chunk
+ void ReadLight(const LChunk &parent);
+ // read a trimesh chunk
+ void ReadMesh(const LChunk &parent);
+ // reads the face list, face materials, smoothing groups... and fill rthe information into the mesh
+ void ReadFaceList(const LChunk &chunk, LMesh &mesh);
+ // reads the material
+ void ReadMaterial(const LChunk &parent);
+ // reads the map info and fills the given map with this information
+ void ReadMap(const LChunk &chunk, LMap& map);
+ // reads keyframer data of the OBJECT_NODE_TAG chunk
+ void ReadKeyframeData(const LChunk &parent);
+ // reads the keyheader structure from the current offset and returns the frame number
+ long ReadKeyheader();
+};
+
+//---------------------------------------------------------
+
+#endif \ No newline at end of file
diff --git a/Magic2/resource.h b/Magic2/resource.h
new file mode 100644
index 0000000..f183426
--- /dev/null
+++ b/Magic2/resource.h
@@ -0,0 +1,233 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by Magic.rc
+//
+#define ID_RESET 3
+#define IDC_NEW_MATERIAL 3
+#define ID_BACK_COLOR 4
+#define IDC_DEL_MATERIAL 4
+#define IDC_SELECT_POLYS 5
+#define IDD_ABOUTBOX 100
+#define IDR_MAINFRAME 128
+#define IDR_MAGICTYPE 129
+#define IDD_SURFACE_PROPS 130
+#define IDD_CAMERA_PROPS 131
+#define IDD_MODIFY_SURFACE 135
+#define IDD_VERTS_REDUCE 136
+#define IDD_MODIFY_POLY 137
+#define IDD_CREATE_CUBE 138
+#define IDD_CREATE_CYLINDER 139
+#define IDD_CREATE_SPHERE 140
+#define IDD_MODIFY_TEXTURE 141
+#define IDD_GRIDPROPS 142
+#define IDD_MODIFY_POLYS 143
+#define IDD_CREATE_POLY 144
+#define IDD_EXTRUSION_PROPS 146
+#define IDD_MODIFY_MATERIAL 147
+#define IDC_SURFACE_NAME 1000
+#define IDC_SURFACE_NVERTS 1001
+#define IDC_SURFACE_NPOLYS 1002
+#define IDC_SURFACE_COLOR 1003
+#define IDC_SURFACE_RADIUS 1003
+#define IDC_SURFACE_LENGTH 1004
+#define IDC_SURFACE_WIDTH 1005
+#define IDC_CAMERA_DISTANCE 1006
+#define IDC_SURFACE_HEIGHT 1006
+#define IDC_CAMERA_FOV 1007
+#define IDC_CAMERA_MODE 1008
+#define IDC_SURFACE_LIST 1009
+#define IDC_SURF_NAME 1010
+#define IDC_SURF_POLYS 1011
+#define IDC_SURF_COLOR 1012
+#define IDC_SURF_HIDDEN 1013
+#define IDC_SURF_LOCKED 1014
+#define IDC_SURF_CREATE 1015
+#define IDC_SURF_DISCARD 1016
+#define IDC_SURF_CLONE 1017
+#define IDC_SURF_DELETE 1018
+#define IDC_SURF_ADD_POLYS 1019
+#define IDC_THRESHOLD 1020
+#define IDC_SURF_ID 1020
+#define IDC_FLATTEN 1021
+#define IDC_FLIP_NORM 1022
+#define IDC_DOUBLE_SIDE 1023
+#define IDC_TRIANGULATE 1024
+#define IDC_REMOVE 1025
+#define IDC_POLY_VERTS 1026
+#define IDC_POLY_X 1027
+#define IDC_POLY_Y 1028
+#define IDC_POLY_Z 1029
+#define IDC_POLY_CURVE 1030
+#define IDC_POLY_SURF 1031
+#define IDC_POLY_TEX 1032
+#define IDC_LOC_X 1033
+#define IDC_LOC_Y 1034
+#define IDC_LOC_Z 1035
+#define IDC_SIZE_X 1036
+#define IDC_SIZE_Y 1037
+#define IDC_SIZE_Z 1038
+#define IDC_SECTORS 1039
+#define IDC_RINGS 1040
+#define IDC_GRID_SNAP 1041
+#define IDC_TEXTURE 1041
+#define IDC_MATERIAL 1041
+#define IDC_GRID_SIZE 1042
+#define IDC_MAPPING 1042
+#define IDC_GRID_SHOW 1043
+#define IDC_ALIGN_X 1043
+#define IDC_GRID_LINES 1044
+#define IDC_ALIGN_Y 1044
+#define IDC_GRID_DOTS 1045
+#define IDC_ALIGN_Z 1045
+#define IDC_SCALE_U 1046
+#define IDC_SCALE_V 1047
+#define IDC_TEXTURE_ADD 1048
+#define IDC_TEXTURE_DELETE 1049
+#define IDC_ALIGN_FLIP 1050
+#define IDC_ALIGN_MIRROR 1051
+#define IDC_ALIGN_ROTATE 1052
+#define IDC_TEXTURE_RELOAD 1053
+#define IDC_TEXTURE_PREVIEW 1054
+#define IDC_TEMPLATE 1055
+#define IDC_AMBIENT_COLOR 1055
+#define IDC_FLAG_FLAT 1056
+#define IDC_DIFFUSE_COLOR 1056
+#define IDC_FLAG_LUMINOUS 1057
+#define IDC_NSIDES 1057
+#define IDC_SPECULAR_COLOR 1057
+#define IDC_FLAG_TRANSLUCENT 1058
+#define IDC_EMISSIVE_COLOR 1058
+#define IDC_FLAG_TRANSPARENT 1059
+#define IDC_SECONDARY 1059
+#define IDC_FLAG_SPECULAR 1060
+#define IDC_FLAG_FLAT2 1061
+#define IDC_FLAG_LUMINOUS2 1062
+#define IDC_POLY_INDEX 1062
+#define IDC_FLAG_TRANSLUCENT2 1063
+#define IDC_FLAG_TRANSPARENT2 1064
+#define IDC_FLAG_SPECULAR2 1065
+#define IDC_FLAG_TEXCLAMP 1066
+#define IDC_DISTANCE 1067
+#define IDC_FLAG_SPECULAR22 1067
+#define IDC_FLAG_SPECULAR1 1068
+#define IDC_FLAG_SPECULAR12 1069
+#define IDC_FLAG_TEXCLAMP2 1070
+#define IDC_MATERIAL_LIST 1070
+#define IDC_MATERIAL_NAME 1071
+#define IDC_DIFFUSE_TEXTURE 1072
+#define IDC_SPECULAR_TEXTURE 1073
+#define IDC_EMISSIVE_TEXTURE 1074
+#define IDC_BUMP_TEXTURE 1075
+#define IDC_AMBIENT_VALUE 1076
+#define IDC_DIFFUSE_VALUE 1077
+#define IDC_SPECULAR_VALUE 1078
+#define IDC_EMISSIVE_VALUE 1079
+#define IDC_BUMP_VALUE 1080
+#define IDC_POWER_VALUE 1081
+#define IDC_BRILLIANCE_VALUE 1082
+#define IDC_BLEND_MODE 1083
+#define IDC_MATERIAL_PREVIEW 1084
+#define IDC_FILE_DIFFUSE 1085
+#define IDC_FILE_SPECULAR 1086
+#define IDC_REFERENCE_PLAN 1086
+#define IDC_FILE_EMISSIVE 1087
+#define IDC_REFERENCE_SIDE 1087
+#define IDC_FILE_BUMP 1088
+#define IDC_REFERENCE_FRONT 1088
+#define IDC_SHADOW 1089
+#define IDC_FILE_PLAN 1090
+#define IDC_MATERIAL_SHADER 1090
+#define IDC_FILE_SIDE 1091
+#define IDC_FILE_FRONT 1092
+#define ID_GRID_PROPS 32771
+#define ID_VIEW_ZOOM_IN 32771
+#define ID_VIEW_ZOOM_OUT 32772
+#define ID_VIEW_ZOOM_NORMAL 32773
+#define ID_VIEW_CENTER 32774
+#define ID_SURFACE_CREATE 32779
+#define ID_MODIFY_DRAG_VERTS 32780
+#define ID_CREATE_CUBE 32781
+#define ID_CREATE_TETRA 32782
+#define ID_MODIFY_EXTRUDE 32784
+#define ID_MODIFY_BEVEL 32785
+#define ID_MODIFY_STUD 32786
+#define ID_MODIFY_SUBDIVIDE 32787
+#define ID_PROP_SURFACE 32788
+#define ID_PROP_CAMERA 32789
+#define ID_EDIT_FLIP_NORMAL 32790
+#define ID_EDIT_REMOVE_POLY 32791
+#define ID_EDIT_MERGE_POLY 32792
+#define ID_MODIFY_TEXTURE 32795
+#define ID_OPTIONS_VERTEX_NORMALS 32798
+#define ID_PROP_GRID 32800
+#define ID_EDIT_DELETE 32801
+#define ID_CREATE_POLY 32802
+#define ID_CREATE_VERT 32803
+#define ID_CREATE_CYLINDER 32804
+#define ID_CREATE_SPHERE 32805
+#define ID_FILE_IMPORT 32806
+#define ID_FILE_EXPORT 32807
+#define ID_MODIFY_POLY 32808
+#define ID_MODIFY_VERTS 32809
+#define ID_VERTS_REDUCE 32810
+#define ID_VERTS_SNAP 32811
+#define ID_MODIFY_SCALE_VERTS 32812
+#define ID_MODIFY_ROTATE 32813
+#define ID_CREATE_CONE 32814
+#define ID_FINAL_RENDER 32815
+#define ID_MODIFY_SCALE_X 32817
+#define ID_MODIFY_SCALE_Z 32819
+#define ID_MODIFY_SCALE_Y 32822
+#define ID_MODIFY_SPLIT1 32824
+#define ID_MODIFY_SPLIT2 32825
+#define ID_EDIT_MIRROR 32826
+#define ID_EDIT_SEAL 32827
+#define ID_EDIT_CLONE 32828
+#define ID_GRID_SNAP 32829
+#define ID_MODIFY_FLATTEN_X 32830
+#define ID_MODIFY_FLATTEN_Y 32831
+#define ID_MODIFY_FLATTEN_Z 32832
+#define ID_EDIT_SELECT_NONE 32833
+#define ID_NUDGE_RIGHT 32834
+#define ID_NUDGE_LEFT 32835
+#define ID_NUDGE_UP 32836
+#define ID_NUDGE_DOWN 32837
+#define ID_STEP_LEFT 32838
+#define ID_STEP_RIGHT 32839
+#define ID_STEP_UP 32840
+#define ID_STEP_DOWN 32841
+#define ID_VIEW_RENDER 32842
+#define ID_VIEW_ALL 32843
+#define ID_VIEW_FRONT 32844
+#define ID_VIEW_SIDE 32845
+#define ID_VIEW_TOP 32846
+#define ID_VIEW_PERSPECTIVE 32847
+#define ID_MODIFY_TEXTURE_MAP 32848
+#define ID_MODIFY_MATERIAL 32849
+#define ID_VIEW_MODE_WIREFRAME 32850
+#define ID_VIEW_MODE_SOLID 32851
+#define ID_VIEW_MODE_TEXTURED 32852
+#define ID_GRID_SHOW 32853
+#define ID_VIEW_BACK_COLOR 32854
+#define ID_EDIT_SELECT_INVERSE 32855
+#define ID_MODIFY_UV_MAP 32856
+#define ID_VIEW_SHADOWS 32857
+#define ID_VIEW_ANIMATELIGHT 32858
+#define ID_VIEW_BUMPMAPS 32859
+#define ID_VIEW_VERTEXSHADER 32860
+#define ID_VIEW_PIXELSHADER 32861
+#define ID_VIEW_VISIBLESHADOWS 32862
+#define ID_SURFACE_OPTIMIZE 32863
+#define ID_SURFACE_EXPLODE 32864
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_3D_CONTROLS 1
+#define _APS_NEXT_RESOURCE_VALUE 148
+#define _APS_NEXT_COMMAND_VALUE 32865
+#define _APS_NEXT_CONTROL_VALUE 1091
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/NetEx/HttpClient.cpp b/NetEx/HttpClient.cpp
new file mode 100644
index 0000000..3f7e204
--- /dev/null
+++ b/NetEx/HttpClient.cpp
@@ -0,0 +1,68 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: HttpClient.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Server Pump for HTTP Server
+*/
+
+
+#include "MemDebug.h"
+#include "HttpClient.h"
+#include "NetHost.h"
+#include "NetLayer.h"
+#include <mmsystem.h>
+
+// +-------------------------------------------------------------------+
+
+HttpClient::HttpClient(const NetAddr& server_addr)
+ : NetClient(server_addr)
+{
+}
+
+HttpClient::~HttpClient()
+{
+ cookies.destroy();
+}
+
+HttpResponse*
+HttpClient::DoRequest(HttpRequest& request)
+{
+ // add existing cookies to request before sending:
+ CombineCookies(request.GetCookies(), cookies);
+
+ Text req = request.operator Text();
+ Text msg = SendRecv(req);
+ HttpResponse* response = new(__FILE__,__LINE__) HttpResponse(msg);
+
+ if (response) {
+ // save cookies returned in response:
+ CombineCookies(cookies, response->GetCookies());
+ }
+
+ return response;
+}
+
+void
+HttpClient::CombineCookies(List<HttpParam>& dst, List<HttpParam>& src)
+{
+ for (int i = 0; i < src.size(); i++) {
+ HttpParam* s = src[i];
+ HttpParam* d = dst.find(s);
+
+ if (d) {
+ d->value = s->value;
+ }
+ else {
+ HttpParam* cookie = new(__FILE__,__LINE__) HttpParam(s->name, s->value);
+ if (cookie)
+ dst.append(cookie);
+ }
+ }
+} \ No newline at end of file
diff --git a/NetEx/HttpClient.h b/NetEx/HttpClient.h
new file mode 100644
index 0000000..51ea161
--- /dev/null
+++ b/NetEx/HttpClient.h
@@ -0,0 +1,43 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: HttpClient.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ HTTP/1.1 client class
+*/
+
+
+#ifndef HttpClient_h
+#define HttpClient_h
+
+#include "NetClient.h"
+#include "HttpServer.h"
+
+// +-------------------------------------------------------------------+
+
+class HttpClient : public NetClient
+{
+public:
+ static const char* TYPENAME() { return "HttpClient"; }
+
+ HttpClient(const NetAddr& server_addr);
+ virtual ~HttpClient();
+
+ int operator == (const HttpClient& c) const { return this == &c; }
+
+ HttpResponse* DoRequest(HttpRequest& request);
+
+protected:
+ void CombineCookies(List<HttpParam>& dst, List<HttpParam>& src);
+
+ List<HttpParam> cookies;
+};
+
+
+#endif HttpClient_h \ No newline at end of file
diff --git a/NetEx/HttpServer.cpp b/NetEx/HttpServer.cpp
new file mode 100644
index 0000000..a9a016a
--- /dev/null
+++ b/NetEx/HttpServer.cpp
@@ -0,0 +1,1003 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: HttpServer.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Server Pump for HTTP Server
+*/
+
+
+#include "MemDebug.h"
+#include "HttpServer.h"
+#include "NetLayer.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+// +-------------------------------------------------------------------+
+
+HttpServer::HttpServer(WORD port, int poolsize)
+ : NetServer(port, poolsize)
+{
+ http_server_name = "Generic HttpServer 1.0";
+}
+
+HttpServer::~HttpServer()
+{ }
+
+// +--------------------------------------------------------------------+
+
+Text
+HttpServer::ProcessRequest(Text msg, const NetAddr& addr)
+{
+ HttpRequest request(msg);
+ HttpResponse response;
+
+ request.SetClientAddr(addr);
+
+ switch (request.Method()) {
+ case HttpRequest::HTTP_GET:
+ if (DoGet(request, response))
+ return response;
+
+ case HttpRequest::HTTP_POST:
+ if (DoPost(request, response))
+ return response;
+
+ case HttpRequest::HTTP_HEAD:
+ if (DoHead(request, response))
+ return response;
+ }
+
+ return ErrorResponse();
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+HttpServer::GetServerName()
+{
+ return http_server_name;
+}
+
+void
+HttpServer::SetServerName(const char* name)
+{
+ http_server_name = name;
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+HttpServer::DefaultResponse()
+{
+ Text response = "HTTP/1.1 200 OK\nServer: ";
+ response += http_server_name;
+ response += "\nMIME-Version: 1.0\nContent-Type: text/html\nConnection: close\n\n";
+
+ return response;
+}
+
+Text
+HttpServer::ErrorResponse()
+{
+ Text response = "HTTP/1.1 500 Internal Server Error\nServer:";
+ response += http_server_name;
+ response += "\nMIME-Version: 1.0\nContent-Type: text/html\nConnection: close\n\n";
+
+ response += "<html><head><title>";
+ response += http_server_name;
+ response += " Error</title></head>\n";
+ response += "<body bgcolor=\"black\" text=\"white\">\n<h1>";
+ response += http_server_name;
+ response += "</h1>\n<p>Veruca... sweetheart... angel... I'm not a magician!\n";
+ response += "</body></html>\n\n";
+
+ return response;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+HttpServer::DoGet(HttpRequest& request, HttpResponse& response)
+{
+ char buffer[1024];
+ Text content;
+
+ content = "<html><head><title>";
+ content += http_server_name;
+ content += "</title></head>\n";
+ content += "<body bgcolor=\"white\" text=\"black\">\n<h1>";
+ content += http_server_name;
+ content += "</h1>\n";
+ content += "<br><h3>Client Address:</h3><p>\n";
+
+ sprintf(buffer, "%d.%d.%d.%d:%d<br><br>\n",
+ client_addr.B1(),
+ client_addr.B2(),
+ client_addr.B3(),
+ client_addr.B4(),
+ client_addr.Port());
+
+ content += buffer;
+ content += "<h3>Request Method:</h3><p>\n";
+
+ switch (request.Method()) {
+ case HttpRequest::HTTP_GET:
+ content += "GET";
+ break;
+
+ case HttpRequest::HTTP_POST:
+ content += "POST";
+ break;
+
+ case HttpRequest::HTTP_HEAD:
+ content += "HEAD";
+ break;
+
+ default:
+ content += "(unsupported?)";
+ break;
+ }
+
+ content += "<br>\n";
+ content += "<br><h3>URI Requested:</h3><p>\n";
+ content += request.URI();
+ content += "<br>\n";
+
+ if (request.GetQuery().size() > 0) {
+ content += "<br><h3>Query Parameters:</h3>\n";
+
+ ListIter<HttpParam> q_iter = request.GetQuery();
+ while (++q_iter) {
+ HttpParam* q = q_iter.value();
+ sprintf(buffer, "<b>%s:</b> <i>%s</i><br>\n", q->name.data(), q->value.data());
+ content += buffer;
+ }
+ }
+
+ content += "<br><h3>Request Headers:</h3>\n";
+ ListIter<HttpParam> h_iter = request.GetHeaders();
+ while (++h_iter) {
+ HttpParam* h = h_iter.value();
+ sprintf(buffer, "<b>%s:</b> <i>%s</i><br>\n", h->name.data(), h->value.data());
+ content += buffer;
+ }
+
+ content += "</body></html>\n\n";
+
+ response.SetStatus(HttpResponse::SC_OK);
+ response.AddHeader("Server", http_server_name);
+ response.AddHeader("MIME-Version", "1.0");
+ response.AddHeader("Content-Type", "text/html");
+ response.SetContent(content);
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+HttpServer::DoPost(HttpRequest& request, HttpResponse& response)
+{
+ return DoGet(request, response);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+HttpServer::DoHead(HttpRequest& request, HttpResponse& response)
+{
+ if (DoGet(request, response)) {
+ int len = response.Content().length();
+
+ char buffer[256];
+ sprintf(buffer, "%d", len);
+ response.SetHeader("Content-Length", buffer);
+ response.SetContent("");
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+HttpRequest::HttpRequest(const char* r)
+ : method(0)
+{
+ if (r && *r)
+ ParseRequest(r);
+}
+
+HttpRequest::~HttpRequest()
+{
+ query.destroy();
+ headers.destroy();
+ cookies.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HttpRequest::ParseRequest(Text request)
+{
+ if (request.length() <= 8)
+ return;
+
+ const char* pReq = 0;
+ const char* pURI = 0;
+ const char* pQuery = 0;
+
+ switch (request[0]) {
+ case 'G':
+ if (request.indexOf("GET") == 0)
+ method = HTTP_GET;
+ break;
+
+ case 'P':
+ if (request.indexOf("POST") == 0)
+ method = HTTP_POST;
+ break;
+
+ case 'H':
+ if (request.indexOf("HEAD") == 0)
+ method = HTTP_HEAD;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!method) return;
+
+ char buffer[1024];
+ int i = 0;
+
+ // save the request line:
+ pReq = request.data();
+ while (*pReq && *pReq != '\n')
+ buffer[i++] = *pReq++;
+ buffer[i] = 0;
+
+ request_line = buffer;
+ i = 0;
+
+ // find the URI:
+ pURI = request.data();
+ while (*pURI && !isspace(*pURI))
+ pURI++;
+
+ while (*pURI && isspace(*pURI))
+ pURI++;
+
+ // copy the URI and find the query string:
+ while (*pURI && *pURI != '?' && !isspace(*pURI)) {
+ buffer[i++] = *pURI++;
+ }
+
+ buffer[i] = 0;
+ uri = buffer;
+ pQuery = pURI;
+
+ // parse the query string:
+ if (*pQuery == '?') {
+ pQuery++;
+
+ while (*pQuery && !isspace(*pQuery)) {
+ char name_buf[1024];
+ char value_buf[1024];
+
+ i = 0;
+ while (*pQuery && *pQuery != '=' && !isspace(*pQuery))
+ name_buf[i++] = *pQuery++;
+ name_buf[i] = 0;
+
+ if (*pQuery == '=')
+ pQuery++;
+
+ i = 0;
+ while (*pQuery && *pQuery != '&' && !isspace(*pQuery))
+ value_buf[i++] = *pQuery++;
+ value_buf[i] = 0;
+
+ if (*pQuery == '&')
+ pQuery++;
+
+ HttpParam* param = new(__FILE__,__LINE__) HttpParam(name_buf, DecodeParam(value_buf));
+ if (param)
+ query.append(param);
+ }
+ }
+
+ // get the headers:
+ const char* p = request.data();
+ while (*p && *p != '\n')
+ p++;
+
+ if (*p == '\n') p++;
+
+ while (*p && *p != '\r' && *p != '\n') {
+ char name_buf[1024];
+ char value_buf[1024];
+
+ i = 0;
+ while (*p && *p != ':')
+ name_buf[i++] = *p++;
+ name_buf[i] = 0;
+
+ p++; // skip ':'
+ while (isspace(*p)) p++; // skip spaces
+
+ i = 0;
+ while (*p && *p != '\r' && *p != '\n') // read to end of header line
+ value_buf[i++] = *p++;
+ value_buf[i] = 0;
+
+ if (!stricmp(name_buf, "Cookie")) {
+ ParseCookie(value_buf);
+ }
+ else {
+ HttpParam* param = new(__FILE__,__LINE__) HttpParam(name_buf, value_buf);
+ if (param)
+ headers.append(param);
+ }
+
+ while (*p && *p != '\n')
+ p++;
+
+ if (*p == '\n') p++;
+ }
+
+ if (method == HTTP_POST && *p) {
+ while (*p == '\n' || *p == '\r')
+ p++;
+
+ content = *p;
+ pQuery = p;
+
+ while (*pQuery && !isspace(*pQuery)) {
+ char name_buf[1024];
+ char value_buf[1024];
+
+ i = 0;
+ while (*pQuery && *pQuery != '=' && !isspace(*pQuery))
+ name_buf[i++] = *pQuery++;
+ name_buf[i] = 0;
+
+ if (*pQuery == '=')
+ pQuery++;
+
+ i = 0;
+ while (*pQuery && *pQuery != '&' && !isspace(*pQuery))
+ value_buf[i++] = *pQuery++;
+ value_buf[i] = 0;
+
+ if (*pQuery == '&')
+ pQuery++;
+
+ HttpParam* param = new(__FILE__,__LINE__) HttpParam(name_buf, DecodeParam(value_buf));
+ if (param)
+ query.append(param);
+ }
+ }
+}
+
+void
+HttpRequest::ParseCookie(const char* param)
+{
+ const char* p = param;
+
+ while (p && *p) {
+ while (isspace(*p)) p++;
+
+ // just ignore reserved attributes
+ if (*p == '$') {
+ while (*p && !isspace(*p) && *p != ';') p++;
+
+ if (*p == ';')
+ p++;
+ }
+
+ // found a cookie!
+ else if (isalpha(*p)) {
+ char name[1024];
+ char data[1024];
+
+ char* d = name;
+ while (*p && *p != '=')
+ *d++ = *p++;
+ *d = 0;
+
+ if (*p == '=')
+ p++;
+
+ if (*p == '"')
+ p++;
+
+ d = data;
+ while (*p && *p != '"' && *p != ';')
+ *d++ = *p++;
+ *d = 0;
+
+ if (*p == '"')
+ p++;
+
+ if (*p == ';')
+ p++;
+
+ HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, data);
+ if (param)
+ cookies.append(param);
+ }
+
+ // this shouldn't happen - abandon the parse
+ else {
+ return;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+HttpRequest::GetParam(const char* name)
+{
+ ListIter<HttpParam> iter = query;
+ while (++iter) {
+ HttpParam* p = iter.value();
+
+ if (p->name == name)
+ return p->value;
+ }
+
+ return Text();
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+HttpRequest::GetHeader(const char* name)
+{
+ ListIter<HttpParam> iter = headers;
+ while (++iter) {
+ HttpParam* p = iter.value();
+
+ if (p->name == name)
+ return p->value;
+ }
+
+ return Text();
+}
+
+void
+HttpRequest::SetHeader(const char* name, const char* value)
+{
+ ListIter<HttpParam> iter = headers;
+ while (++iter) {
+ HttpParam* p = iter.value();
+
+ if (p->name == name) {
+ p->value = value;
+ return;
+ }
+ }
+
+ HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value);
+ if (param)
+ headers.append(param);
+}
+
+void
+HttpRequest::AddHeader(const char* name, const char* value)
+{
+ HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value);
+ if (param)
+ headers.append(param);
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+HttpRequest::GetCookie(const char* name)
+{
+ ListIter<HttpParam> iter = cookies;
+ while (++iter) {
+ HttpParam* p = iter.value();
+
+ if (p->name == name)
+ return p->value;
+ }
+
+ return Text();
+}
+
+void
+HttpRequest::SetCookie(const char* name, const char* value)
+{
+ ListIter<HttpParam> iter = cookies;
+ while (++iter) {
+ HttpParam* p = iter.value();
+
+ if (p->name == name) {
+ p->value = value;
+ return;
+ }
+ }
+
+ HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value);
+ if (param)
+ cookies.append(param);
+}
+
+void
+HttpRequest::AddCookie(const char* name, const char* value)
+{
+ HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value);
+ if (param)
+ cookies.append(param);
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+HttpRequest::DecodeParam(const char* value)
+{
+ if (!value || !*value) return "";
+
+ int size = strlen(value);
+ char val = 0;
+ char code[4];
+ char sbuf[256];
+ char* lbuf = 0;
+
+ char* dst = sbuf;
+ char* p = sbuf;
+
+ if (size > 255) {
+ lbuf = new(__FILE__,__LINE__) char[size+1];
+ dst = lbuf;
+ p = lbuf;
+ }
+
+ if (p) {
+ while (*value) {
+ switch (*value) {
+ default: *p++ = *value; break;
+ case '+': *p++ = ' '; break;
+
+ case '%':
+ value++;
+ code[0] = *value++;
+ code[1] = *value;
+ code[2] = 0;
+
+ val = (char) strtol(code, 0, 16);
+ *p++ = val;
+ break;
+ }
+
+ value++;
+ }
+
+ *p = 0;
+ }
+
+ Text result = dst;
+
+ if (lbuf)
+ delete [] lbuf;
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+HttpRequest::EncodeParam(const char* value)
+{
+ if (!value || !*value) return "";
+
+ int size = strlen(value);
+ char hex1 = 0;
+ char hex2 = 0;
+
+ char sbuf[1024];
+ char* lbuf = 0;
+
+ char* dst = sbuf;
+ char* p = sbuf;
+
+ if (size > 255) {
+ lbuf = new(__FILE__,__LINE__) char[4*size+1];
+ dst = lbuf;
+ p = lbuf;
+ }
+
+ if (p) {
+ while (*value) {
+ switch (*value) {
+ default: *p++ = *value; break;
+ case ' ': *p++ = '+'; break;
+
+ case '?': *p++ = '%'; *p++ = '3'; *p++ = 'F'; break;
+ case '&': *p++ = '%'; *p++ = '2'; *p++ = '6'; break;
+ case ':': *p++ = '%'; *p++ = '3'; *p++ = 'A'; break;
+ case '/': *p++ = '%'; *p++ = '2'; *p++ = 'F'; break;
+ case '\\': *p++ = '%'; *p++ = '5'; *p++ = 'C'; break;
+ case '%': *p++ = '%'; *p++ = '2'; *p++ = '5'; break;
+ case '|': *p++ = '%'; *p++ = '7'; *p++ = 'C'; break;
+ case '<': *p++ = '%'; *p++ = '3'; *p++ = 'C'; break;
+ case '>': *p++ = '%'; *p++ = '3'; *p++ = 'E'; break;
+ case '[': *p++ = '%'; *p++ = '5'; *p++ = 'B'; break;
+ case ']': *p++ = '%'; *p++ = '5'; *p++ = 'D'; break;
+ case '{': *p++ = '%'; *p++ = '7'; *p++ = 'B'; break;
+ case '}': *p++ = '%'; *p++ = '7'; *p++ = 'D'; break;
+ case '"': *p++ = '%'; *p++ = '2'; *p++ = '2'; break;
+ case '^': *p++ = '%'; *p++ = '5'; *p++ = 'E'; break;
+ case '`': *p++ = '%'; *p++ = '6'; *p++ = '0'; break;
+ case '\n': break;
+ case '\r': break;
+ case '\t': break;
+ }
+
+ value++;
+ }
+
+ *p = 0;
+ }
+
+ Text result = dst;
+
+ if (lbuf)
+ delete [] lbuf;
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+HttpRequest::operator Text()
+{
+ Text response = request_line.data();
+ response += "\n";
+
+ for (int i = 0; i < headers.size(); i++) {
+ HttpParam* h = headers[i];
+ response += h->name;
+ response += ": ";
+ response += h->value;
+ response += "\n";
+ }
+
+ for (i = 0; i < cookies.size(); i++) {
+ HttpParam* c = cookies[i];
+ response += "Cookie: ";
+ response += c->name;
+ response += "=\"";
+ response += c->value;
+ response += "\"\n";
+ }
+
+ response += "Connection: close\n\n";
+ response += content;
+
+ return response;
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+HttpResponse::HttpResponse(int stat, const char* data)
+ : status(stat), content(data)
+{ }
+
+HttpResponse::HttpResponse(const char* r)
+ : status(0), content(r)
+{
+ if (r && *r)
+ ParseResponse(r);
+}
+
+HttpResponse::~HttpResponse()
+{
+ headers.destroy();
+ cookies.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+HttpResponse::operator Text()
+{
+ Text response;
+
+ switch (status) {
+ case SC_CONTINUE : response = "HTTP/1.1 100 Continue\n"; break;
+ case SC_SWITCHING_PROTOCOLS : response = "HTTP/1.1 101 Switching Protocols\n"; break;
+
+ case SC_OK : response = "HTTP/1.1 200 OK\n"; break;
+ case SC_CREATED : response = "HTTP/1.1 201 Created\n"; break;
+ case SC_ACCEPTED : response = "HTTP/1.1 202 Accepted\n"; break;
+ case SC_NON_AUTHORITATIVE : response = "HTTP/1.1 203 Non Authoritative\n"; break;
+ case SC_NO_CONTENT : response = "HTTP/1.1 204 No Content\n"; break;
+ case SC_RESET_CONTENT : response = "HTTP/1.1 205 Reset Content\n"; break;
+ case SC_PARTIAL_CONTENT : response = "HTTP/1.1 206 Partial Content\n"; break;
+
+ case SC_MULTIPLE_CHOICES : response = "HTTP/1.1 300 Multiple Choices\n"; break;
+ case SC_MOVED_PERMANENTLY : response = "HTTP/1.1 301 Moved Permanently\n"; break;
+ case SC_FOUND : response = "HTTP/1.1 302 Found\n"; break;
+ case SC_SEE_OTHER : response = "HTTP/1.1 303 See Other\n"; break;
+ case SC_NOT_MODIFIED : response = "HTTP/1.1 304 Not Modified\n"; break;
+ case SC_USE_PROXY : response = "HTTP/1.1 305 Use Proxy\n"; break;
+ case SC_TEMPORARY_REDIRECT : response = "HTTP/1.1 307 Temporary Redirect\n"; break;
+
+ case SC_BAD_REQUEST : response = "HTTP/1.1 400 Bad Request\n"; break;
+ case SC_UNAUTHORIZED : response = "HTTP/1.1 401 Unauthorized\n"; break;
+ case SC_PAYMENT_REQUIRED : response = "HTTP/1.1 402 Payment Required\n"; break;
+ case SC_FORBIDDEN : response = "HTTP/1.1 403 Forbidden\n"; break;
+ case SC_NOT_FOUND : response = "HTTP/1.1 404 Not Found\n"; break;
+ case SC_METHOD_NOT_ALLOWED : response = "HTTP/1.1 405 Method Not Allowed\n"; break;
+ case SC_NOT_ACCEPTABLE : response = "HTTP/1.1 406 Not Acceptable\n"; break;
+ case SC_PROXY_AUTH_REQ : response = "HTTP/1.1 407 Proxy Authorization Req\n"; break;
+ case SC_REQUEST_TIME_OUT : response = "HTTP/1.1 408 Request Timeout\n"; break;
+ case SC_CONFLICT : response = "HTTP/1.1 409 Conflict\n"; break;
+ case SC_GONE : response = "HTTP/1.1 410 Gone\n"; break;
+ case SC_LENGTH_REQUIRED : response = "HTTP/1.1 411 Length Required\n"; break;
+
+ default:
+ case SC_SERVER_ERROR : response = "HTTP/1.1 500 Internal Server Error\n"; break;
+ case SC_NOT_IMPLEMENTED : response = "HTTP/1.1 501 Not Implemented\n"; break;
+ case SC_BAD_GATEWAY : response = "HTTP/1.1 502 Bad Gateway\n"; break;
+ case SC_SERVICE_UNAVAILABLE : response = "HTTP/1.1 503 Service Unavailable\n"; break;
+ case SC_GATEWAY_TIMEOUT : response = "HTTP/1.1 504 Gateway Timeout\n"; break;
+ case SC_VERSION_NOT_SUPPORTED: response = "HTTP/1.1 505 HTTP Version Not Supported\n"; break;
+ }
+
+ SetHeader("Connection", "close");
+
+ char buffer[256];
+
+ if (content.length()) {
+ sprintf(buffer, "%d", content.length());
+ SetHeader("Content-Length", buffer);
+ }
+
+ for (int i = 0; i < cookies.size(); i++) {
+ HttpParam* cookie = cookies.at(i);
+ sprintf(buffer, "%s=\"%s\"; Version=\"1\"", cookie->name.data(), cookie->value.data());
+
+ AddHeader("Set-Cookie", buffer);
+ }
+
+ for (i = 0; i < headers.size(); i++) {
+ const HttpParam* p = headers.at(i);
+ sprintf(buffer, "%s: %s\n", p->name.data(), p->value.data());
+ response += buffer;
+ }
+
+ response += "\n";
+ response += content;
+
+ return response;
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+HttpResponse::GetHeader(const char* name)
+{
+ ListIter<HttpParam> iter = headers;
+ while (++iter) {
+ HttpParam* p = iter.value();
+
+ if (p->name == name)
+ return p->value;
+ }
+
+ return Text();
+}
+
+void
+HttpResponse::SetHeader(const char* name, const char* value)
+{
+ ListIter<HttpParam> iter = headers;
+ while (++iter) {
+ HttpParam* p = iter.value();
+
+ if (p->name == name) {
+ p->value = value;
+ return;
+ }
+ }
+
+ HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value);
+ if (param)
+ headers.append(param);
+}
+
+void
+HttpResponse::AddHeader(const char* name, const char* value)
+{
+ HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value);
+ if (param)
+ headers.append(param);
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+HttpResponse::GetCookie(const char* name)
+{
+ ListIter<HttpParam> iter = cookies;
+ while (++iter) {
+ HttpParam* p = iter.value();
+
+ if (p->name == name)
+ return p->value;
+ }
+
+ return Text();
+}
+
+void
+HttpResponse::SetCookie(const char* name, const char* value)
+{
+ ListIter<HttpParam> iter = cookies;
+ while (++iter) {
+ HttpParam* p = iter.value();
+
+ if (p->name == name) {
+ p->value = value;
+ return;
+ }
+ }
+
+ HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value);
+ if (param)
+ cookies.append(param);
+}
+
+void
+HttpResponse::AddCookie(const char* name, const char* value)
+{
+ HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value);
+ if (param)
+ cookies.append(param);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HttpResponse::SendRedirect(const char* url)
+{
+ status = SC_TEMPORARY_REDIRECT;
+ SetHeader("Location", url);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HttpResponse::ParseResponse(Text response)
+{
+ if (response.length() <= 12 || response.indexOf("HTTP/1.") != 0)
+ return;
+
+ const char* pStatus = response.data() + 9;
+
+ sscanf(pStatus, "%d", &status);
+ if (!status) return;
+
+ int i = 0;
+
+ // get the headers:
+ const char* p = response.data();
+ while (*p && *p != '\n')
+ p++;
+
+ if (*p == '\n') p++;
+
+ while (*p && *p != '\r' && *p != '\n') {
+ char name_buf[1024];
+ char value_buf[1024];
+
+ i = 0;
+ while (*p && *p != ':')
+ name_buf[i++] = *p++;
+ name_buf[i] = 0;
+
+ p++; // skip ':'
+ while (isspace(*p)) p++; // skip spaces
+
+ i = 0;
+ while (*p && *p != '\r' && *p != '\n') // read to end of header line
+ value_buf[i++] = *p++;
+ value_buf[i] = 0;
+
+ if (!stricmp(name_buf, "Set-Cookie")) {
+ ParseCookie(value_buf);
+ }
+ else {
+ HttpParam* param = new(__FILE__,__LINE__) HttpParam(name_buf, value_buf);
+ if (param)
+ headers.append(param);
+ }
+
+ while (*p && *p != '\n')
+ p++;
+
+ if (*p == '\n') p++;
+ }
+
+ if (*p == '\n') p++;
+ content = p;
+}
+
+void
+HttpResponse::ParseCookie(const char* param)
+{
+ const char* p = param;
+
+ while (p && *p) {
+ while (isspace(*p)) p++;
+
+ // just ignore reserved attributes
+ if (*p == '$') {
+ while (*p && !isspace(*p) && *p != ';') p++;
+
+ if (*p == ';')
+ p++;
+ }
+
+ // found a cookie!
+ else if (isalpha(*p)) {
+ char name[1024];
+ char data[1024];
+
+ char* d = name;
+ while (*p && *p != '=')
+ *d++ = *p++;
+ *d = 0;
+
+ if (*p == '=')
+ p++;
+
+ if (*p == '"')
+ p++;
+
+ d = data;
+ while (*p && *p != '"' && *p != ';')
+ *d++ = *p++;
+ *d = 0;
+
+ if (*p == '"')
+ p++;
+
+ if (*p == ';')
+ p++;
+
+ // ignore the version attribute
+ if (stricmp(name, "version")) {
+ HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, data);
+ if (param)
+ cookies.append(param);
+ }
+ }
+
+ // this shouldn't happen - abandon the parse
+ else {
+ return;
+ }
+ }
+}
+
diff --git a/NetEx/HttpServer.h b/NetEx/HttpServer.h
new file mode 100644
index 0000000..fe811b3
--- /dev/null
+++ b/NetEx/HttpServer.h
@@ -0,0 +1,217 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: HttpServer.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Server Pump for HTTP Server
+*/
+
+
+#ifndef HttpServer_h
+#define HttpServer_h
+
+#include "NetServer.h"
+
+// +-------------------------------------------------------------------+
+
+class HttpParam;
+class HttpRequest;
+class HttpResponse;
+
+// +-------------------------------------------------------------------+
+
+class HttpServer : public NetServer
+{
+public:
+ static const char* TYPENAME() { return "HttpServer"; }
+
+ HttpServer(WORD port, int poolsize=1);
+ virtual ~HttpServer();
+
+ int operator == (const HttpServer& l) const { return addr == l.addr; }
+
+ virtual Text ProcessRequest(Text request, const NetAddr& addr);
+ virtual Text DefaultResponse();
+ virtual Text ErrorResponse();
+
+ virtual bool DoGet(HttpRequest& request, HttpResponse& response);
+ virtual bool DoPost(HttpRequest& request, HttpResponse& response);
+ virtual bool DoHead(HttpRequest& request, HttpResponse& response);
+
+ virtual Text GetServerName();
+ virtual void SetServerName(const char* name);
+
+protected:
+ Text http_server_name;
+};
+
+// +-------------------------------------------------------------------+
+
+class HttpParam
+{
+public:
+ static const char* TYPENAME() { return "HttpParam"; }
+
+ HttpParam(const char* n, const char* v) : name(n), value(v) { }
+
+ int operator == (const HttpParam& p) const { return name == p.name; }
+
+ Text name;
+ Text value;
+};
+
+// +-------------------------------------------------------------------+
+
+class HttpRequest
+{
+public:
+ static const char* TYPENAME() { return "HttpRequest"; }
+
+ enum METHOD {
+ HTTP_OPTIONS,
+ HTTP_GET,
+ HTTP_HEAD,
+ HTTP_POST,
+ HTTP_PUT,
+ HTTP_DELETE,
+ HTTP_TRACE,
+ HTTP_CONNECT
+ };
+
+ HttpRequest(const char* request=0);
+ ~HttpRequest();
+
+ operator Text();
+
+ void ParseRequest(Text request);
+ void ParseCookie(const char* param);
+
+ int Method() const { return method; }
+ Text URI() const { return uri; }
+ Text Content() const { return content; }
+ Text RequestLine() const { return request_line; }
+
+ List<HttpParam>& GetQuery() { return query; }
+ List<HttpParam>& GetHeaders() { return headers; }
+ List<HttpParam>& GetCookies() { return cookies; }
+
+ NetAddr GetClientAddr() const { return client_addr; }
+ void SetClientAddr(const NetAddr& a) { client_addr = a; }
+
+ Text GetParam(const char* name);
+
+ Text GetHeader(const char* name);
+ void SetHeader(const char* name, const char* value);
+ void AddHeader(const char* name, const char* value);
+ Text GetCookie(const char* name);
+ void SetCookie(const char* name, const char* value);
+ void AddCookie(const char* name, const char* value);
+
+ Text DecodeParam(const char* value);
+ static Text EncodeParam(const char* value);
+
+private:
+ int method;
+ Text uri;
+ Text content;
+ Text request_line;
+ NetAddr client_addr;
+
+ List<HttpParam> query;
+ List<HttpParam> headers;
+ List<HttpParam> cookies;
+};
+
+// +-------------------------------------------------------------------+
+
+class HttpResponse
+{
+public:
+ static const char* TYPENAME() { return "HttpResponse"; }
+
+ enum STATUS {
+ SC_CONTINUE = 100,
+ SC_SWITCHING_PROTOCOLS = 101,
+
+ SC_OK = 200,
+ SC_CREATED = 201,
+ SC_ACCEPTED = 202,
+ SC_NON_AUTHORITATIVE = 203,
+ SC_NO_CONTENT = 204,
+ SC_RESET_CONTENT = 205,
+ SC_PARTIAL_CONTENT = 206,
+
+ SC_MULTIPLE_CHOICES = 300,
+ SC_MOVED_PERMANENTLY = 301,
+ SC_FOUND = 302,
+ SC_SEE_OTHER = 303,
+ SC_NOT_MODIFIED = 304,
+ SC_USE_PROXY = 305,
+ SC_TEMPORARY_REDIRECT = 307,
+
+ SC_BAD_REQUEST = 400,
+ SC_UNAUTHORIZED = 401,
+ SC_PAYMENT_REQUIRED = 402,
+ SC_FORBIDDEN = 403,
+ SC_NOT_FOUND = 404,
+ SC_METHOD_NOT_ALLOWED = 405,
+ SC_NOT_ACCEPTABLE = 406,
+ SC_PROXY_AUTH_REQ = 407,
+ SC_REQUEST_TIME_OUT = 408,
+ SC_CONFLICT = 409,
+ SC_GONE = 410,
+ SC_LENGTH_REQUIRED = 411,
+
+ SC_SERVER_ERROR = 500,
+ SC_NOT_IMPLEMENTED = 501,
+ SC_BAD_GATEWAY = 502,
+ SC_SERVICE_UNAVAILABLE = 503,
+ SC_GATEWAY_TIMEOUT = 504,
+ SC_VERSION_NOT_SUPPORTED= 505
+ };
+
+
+ HttpResponse(int status=500, const char* content=0);
+ HttpResponse(const char* response);
+ ~HttpResponse();
+
+ operator Text();
+
+ void ParseResponse(Text request);
+ void ParseCookie(const char* param);
+
+ int Status() const { return status; }
+ void SetStatus(int s) { status = s; }
+
+ Text Content() const { return content; }
+ void SetContent(Text t) { content = t; }
+ void AddContent(Text t) { content += t; }
+
+ List<HttpParam>& GetHeaders() { return headers; }
+ List<HttpParam>& GetCookies() { return cookies; }
+
+ Text GetHeader(const char* name);
+ void SetHeader(const char* name, const char* value);
+ void AddHeader(const char* name, const char* value);
+ Text GetCookie(const char* name);
+ void SetCookie(const char* name, const char* value);
+ void AddCookie(const char* name, const char* value);
+
+ void SendRedirect(const char* url);
+
+private:
+ int status;
+ Text content;
+
+ List<HttpParam> headers;
+ List<HttpParam> cookies;
+};
+
+
+#endif HttpServer_h \ No newline at end of file
diff --git a/NetEx/HttpServlet.cpp b/NetEx/HttpServlet.cpp
new file mode 100644
index 0000000..504f2fa
--- /dev/null
+++ b/NetEx/HttpServlet.cpp
@@ -0,0 +1,223 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: HttpServlet.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Server Pump for HTTP Server
+*/
+
+
+#include "MemDebug.h"
+#include "HttpServlet.h"
+#include "NetLayer.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+// +-------------------------------------------------------------------+
+
+HttpServlet::HttpServlet()
+ : session(0)
+{ }
+
+HttpServlet::~HttpServlet()
+{ }
+
+// +--------------------------------------------------------------------+
+
+bool
+HttpServlet::Service(HttpRequest& request, HttpResponse& response)
+{
+ bool result = false;
+
+ switch (request.Method()) {
+ case HttpRequest::HTTP_GET:
+ result = DoGet(request, response);
+ break;
+
+ case HttpRequest::HTTP_POST:
+ result = DoPost(request, response);
+ break;
+
+ case HttpRequest::HTTP_HEAD:
+ result = DoHead(request, response);
+ break;
+
+ default:
+ break;
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+HttpServlet::DoGet(HttpRequest& request, HttpResponse& response)
+{
+ return false;
+}
+
+bool
+HttpServlet::DoPost(HttpRequest& request, HttpResponse& response)
+{
+ return DoGet(request, response);
+}
+
+bool
+HttpServlet::DoHead(HttpRequest& request, HttpResponse& response)
+{
+ if (DoGet(request, response)) {
+ int len = response.Content().length();
+
+ char buffer[256];
+ sprintf(buffer, "%d", len);
+ response.SetHeader("Content-Length", buffer);
+ response.SetContent("");
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+HttpSession::HttpSession()
+{
+ id = GenerateUniqueID();
+ access_time = NetLayer::GetUTC();
+}
+
+HttpSession::~HttpSession()
+{
+ attributes.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+HttpSession::GetAttribute(const char* name)
+{
+ ListIter<HttpParam> iter = attributes;
+ while (++iter) {
+ HttpParam* p = iter.value();
+
+ if (p->name == name)
+ return p->value;
+ }
+
+ return Text();
+}
+
+void
+HttpSession::SetAttribute(const char* name, const char* value)
+{
+ ListIter<HttpParam> iter = attributes;
+ while (++iter) {
+ HttpParam* p = iter.value();
+
+ if (p->name == name) {
+ p->value = value;
+ return;
+ }
+ }
+
+ HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, value);
+ if (param)
+ attributes.append(param);
+}
+
+void
+HttpSession::DelAttribute(const char* name)
+{
+ ListIter<HttpParam> iter = attributes;
+ while (++iter) {
+ HttpParam* p = iter.value();
+
+ if (p->name == name) {
+ delete iter.removeItem();
+ return;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+HttpSession::GetIntAttribute(const char* name)
+{
+ ListIter<HttpParam> iter = attributes;
+ while (++iter) {
+ HttpParam* p = iter.value();
+
+ if (p->name == name) {
+ int result = ::atoi(p->value.data());
+ return result;
+ }
+ }
+
+ return 0;
+}
+
+void
+HttpSession::SetIntAttribute(const char* name, int value)
+{
+ char buf[32];
+ sprintf(buf, "%d", value);
+
+ ListIter<HttpParam> iter = attributes;
+ while (++iter) {
+ HttpParam* p = iter.value();
+
+ if (p->name == name) {
+ p->value = buf;
+ return;
+ }
+ }
+
+ HttpParam* param = new(__FILE__,__LINE__) HttpParam(name, buf);
+ if (param)
+ attributes.append(param);
+}
+
+void
+HttpSession::DelIntAttribute(const char* name)
+{
+ DelAttribute(name);
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+HttpSession::GenerateUniqueID()
+{
+ char unique[17];
+
+ for (int i = 0; i < 16; i++) {
+ char c = rand() % 25 + 'a';
+ unique[i] = c;
+ }
+
+ unique[16] = 0;
+ return unique;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HttpSession::Access()
+{
+ access_time = NetLayer::GetUTC();
+}
+
+
diff --git a/NetEx/HttpServlet.h b/NetEx/HttpServlet.h
new file mode 100644
index 0000000..99ac06d
--- /dev/null
+++ b/NetEx/HttpServlet.h
@@ -0,0 +1,85 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: HttpServlet.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Server Pump for HTTP Server
+*/
+
+
+#ifndef HttpServlet_h
+#define HttpServlet_h
+
+#include "HttpServer.h"
+
+// +-------------------------------------------------------------------+
+
+class HttpServlet;
+class HttpSession;
+
+// +-------------------------------------------------------------------+
+
+class HttpServlet
+{
+public:
+ static const char* TYPENAME() { return "HttpServlet"; }
+
+ HttpServlet();
+ virtual ~HttpServlet();
+
+ virtual bool Service(HttpRequest& request, HttpResponse& response);
+
+ virtual bool DoGet(HttpRequest& request, HttpResponse& response);
+ virtual bool DoPost(HttpRequest& request, HttpResponse& response);
+ virtual bool DoHead(HttpRequest& request, HttpResponse& response);
+
+ virtual HttpSession* GetSession() { return session; }
+ virtual void SetSession(HttpSession* s) { session = s; }
+
+protected:
+ HttpSession* session;
+};
+
+// +-------------------------------------------------------------------+
+
+class HttpSession
+{
+public:
+ static const char* TYPENAME() { return "HttpSession"; }
+
+ HttpSession();
+ virtual ~HttpSession();
+
+ int operator == (const HttpSession& s) const { return id == s.id; }
+
+ Text GenerateUniqueID();
+
+ Text GetID() const { return id; }
+ void SetID(const char* i) { id = i; }
+ int GetLastAccess() const { return access_time;}
+ void Access();
+
+ List<HttpParam>& GetAttributes() { return attributes; }
+
+ Text GetAttribute(const char* name);
+ void SetAttribute(const char* name, const char* value);
+ void DelAttribute(const char* name);
+
+ int GetIntAttribute(const char* name);
+ void SetIntAttribute(const char* name, int value);
+ void DelIntAttribute(const char* name);
+
+protected:
+ Text id;
+ int access_time;
+ List<HttpParam> attributes;
+};
+
+
+#endif HttpServlet_h \ No newline at end of file
diff --git a/NetEx/HttpServletExec.cpp b/NetEx/HttpServletExec.cpp
new file mode 100644
index 0000000..7d302cf
--- /dev/null
+++ b/NetEx/HttpServletExec.cpp
@@ -0,0 +1,233 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: HttpServletExec.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Server Pump for HTTP Server
+*/
+
+
+#include "MemDebug.h"
+#include "HttpServletExec.h"
+#include "HttpServlet.h"
+#include "NetLayer.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+// +-------------------------------------------------------------------+
+
+class HttpTestServlet : public HttpServlet
+{
+public:
+ HttpTestServlet() { }
+ virtual ~HttpTestServlet() { }
+
+ virtual bool DoGet(HttpRequest& request, HttpResponse& response)
+ {
+ char buffer[1024];
+ Text content;
+
+ content = "<html><head><title>HttpTestServlet</title></head>\n";
+ content += "<body bgcolor=\"#c0c0c0\" text=\"black\">\n<h1>HttpTestServlet</h1>\n";
+
+ content += "<br><h3>HttpSessionId:</h3><p>\n";
+ if (session)
+ content += session->GetID();
+ else
+ content += "No Session Found";
+ content += "<br>\n";
+
+ content += "<br><h3>URI Requested:</h3><p>\n";
+ content += request.URI();
+ content += "<br>\n";
+
+ if (request.GetQuery().size() > 0) {
+ content += "<br><h3>Query Parameters:</h3>\n";
+
+ ListIter<HttpParam> q_iter = request.GetQuery();
+ while (++q_iter) {
+ HttpParam* q = q_iter.value();
+ sprintf(buffer, "<b>%s:</b> <i>%s</i><br>\n", q->name.data(), q->value.data());
+ content += buffer;
+ }
+ }
+
+ content += "<br><h3>Request Headers:</h3>\n";
+ ListIter<HttpParam> h_iter = request.GetHeaders();
+ while (++h_iter) {
+ HttpParam* h = h_iter.value();
+ sprintf(buffer, "<b>%s:</b> <i>%s</i><br>\n", h->name.data(), h->value.data());
+ content += buffer;
+ }
+
+ if (request.GetCookies().size() > 0) {
+ content += "<br><h3>Cookies:</h3>\n";
+ ListIter<HttpParam> c_iter = request.GetCookies();
+ while (++c_iter) {
+ HttpParam* c = c_iter.value();
+ sprintf(buffer, "<b>%s:</b> <i>%s</i><br>\n", c->name.data(), c->value.data());
+ content += buffer;
+ }
+ }
+
+ content += "</body></html>\n\n";
+
+ response.SetStatus(HttpResponse::SC_OK);
+ response.AddHeader("MIME-Version", "1.0");
+ response.AddHeader("Content-Type", "text/html");
+ response.SetContent(content);
+
+ return true;
+ }
+};
+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+
+DWORD WINAPI HttpServletExecSessionProc(LPVOID link);
+
+HttpServletExec::HttpServletExec(WORD port, int poolsize)
+ : HttpServer(port, poolsize), session_timeout(60), exec_shutdown(false)
+{
+ http_server_name = "Generic HttpServletExec 1.0";
+
+ DWORD thread_id = 0;
+ hsession = CreateThread(0, 4096, HttpServletExecSessionProc,
+ (LPVOID) this, 0, &thread_id);
+}
+
+HttpServletExec::~HttpServletExec()
+{
+ if (!exec_shutdown)
+ exec_shutdown = true;
+
+ WaitForSingleObject(hsession, 1000);
+ CloseHandle(hsession);
+ hsession = 0;
+
+ sessions.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+HttpServlet*
+HttpServletExec::GetServlet(HttpRequest& request)
+{
+ return new(__FILE__,__LINE__) HttpTestServlet;
+}
+
+// +--------------------------------------------------------------------+
+
+HttpSession*
+HttpServletExec::GetSession(HttpRequest& request)
+{
+ HttpSession* session = 0;
+ Text reqID = request.GetCookie("SessionID");
+
+ if (reqID.length() > 0) {
+ ListIter<HttpSession> iter = sessions;
+ while (++iter && !session) {
+ HttpSession* s = iter.value();
+
+ if (s->GetID() == reqID) {
+ session = s;
+ session->Access();
+ }
+ }
+ }
+
+ if (!session) {
+ session = new(__FILE__,__LINE__) HttpSession;
+ if (session) {
+ sessions.append(session);
+
+ ::Print("HttpServletExec created new session '%s' for request '%s'\n",
+ (const char*) session->GetID(),
+ (const char*) request.RequestLine());
+ }
+ else {
+ ::Print("HttpServletExec out of memory for request '%s'\n",
+ (const char*) request.RequestLine());
+ }
+ }
+
+ return session;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+HttpServletExec::DoGet(HttpRequest& request, HttpResponse& response)
+{
+ bool result = false;
+ HttpSession* session = GetSession(request);
+ HttpServlet* servlet = GetServlet(request);
+
+ if (servlet) {
+ servlet->SetSession(session);
+ result = servlet->Service(request, response);
+ delete servlet;
+ }
+
+ if (result) {
+ response.SetHeader("Server", http_server_name);
+
+ if (session)
+ response.SetCookie("SessionID", session->GetID());
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD WINAPI HttpServletExecSessionProc(LPVOID link)
+{
+ HttpServletExec* exec = (HttpServletExec*) link;
+
+ if (exec)
+ return exec->CheckSessions();
+
+ return (DWORD) E_POINTER;
+}
+
+DWORD
+HttpServletExec::CheckSessions()
+{
+ while (!exec_shutdown) {
+ sync.acquire();
+
+ if (sessions.size()) {
+ ListIter<HttpSession> iter = sessions;
+ while (++iter) {
+ HttpSession* s = iter.value();
+
+ if (NetLayer::GetUTC() - s->GetLastAccess() > session_timeout) {
+ ::Print("HttpServletExec deleting expired session '%s'\n", (const char*) s->GetID());
+ delete iter.removeItem();
+ }
+ }
+ }
+
+ DoSyncedCheck();
+
+ sync.release();
+ Sleep(100);
+ }
+
+ return 0;
+}
+
+void
+HttpServletExec::DoSyncedCheck()
+{
+} \ No newline at end of file
diff --git a/NetEx/HttpServletExec.h b/NetEx/HttpServletExec.h
new file mode 100644
index 0000000..bead257
--- /dev/null
+++ b/NetEx/HttpServletExec.h
@@ -0,0 +1,57 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: HttpServletExec.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Server Pump for HTTP Server
+*/
+
+
+#ifndef HttpServletExec_h
+#define HttpServletExec_h
+
+#include "HttpServer.h"
+
+// +-------------------------------------------------------------------+
+
+class HttpServlet;
+class HttpSession;
+
+// +-------------------------------------------------------------------+
+
+class HttpServletExec : public HttpServer
+{
+public:
+ static const char* TYPENAME() { return "HttpServletExec"; }
+
+ HttpServletExec(WORD port, int poolsize=1);
+ virtual ~HttpServletExec();
+
+ int operator == (const HttpServletExec& l) const { return addr == l.addr; }
+
+ virtual bool DoGet(HttpRequest& request, HttpResponse& response);
+
+ virtual HttpServlet* GetServlet(HttpRequest& request);
+ virtual HttpSession* GetSession(HttpRequest& request);
+
+ virtual DWORD CheckSessions();
+
+ virtual int GetSessionTimeout() const { return session_timeout; }
+ virtual void SetSessionTimeout(int t) { session_timeout = t; }
+
+protected:
+ virtual void DoSyncedCheck();
+
+ List<HttpSession> sessions;
+ int session_timeout;
+ HANDLE hsession;
+ bool exec_shutdown;
+};
+
+#endif HttpServletExec_h \ No newline at end of file
diff --git a/NetEx/NetAddr.cpp b/NetEx/NetAddr.cpp
new file mode 100644
index 0000000..53d6c79
--- /dev/null
+++ b/NetEx/NetAddr.cpp
@@ -0,0 +1,107 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetAddr.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Address
+*/
+
+
+#include "MemDebug.h"
+#include "NetAddr.h"
+#include "NetHost.h"
+#include "NetLayer.h"
+#include <ctype.h>
+
+NetAddr::NetAddr(const char* host_name, WORD p)
+ : addr(0), port(p)
+{
+ if (host_name && *host_name) {
+ HOSTENT* h = 0;
+
+ if (isdigit(*host_name)) {
+ DWORD a = inet_addr(host_name);
+ h = gethostbyaddr((const char*) &a, 4, AF_INET);
+ }
+ else {
+ h = gethostbyname(host_name);
+ }
+
+ if (h) {
+ if (h->h_addr_list) {
+ addr = **(DWORD**) (h->h_addr_list);
+ }
+ }
+ }
+
+ Init();
+}
+
+NetAddr::NetAddr(DWORD a, WORD p)
+ : addr(a), port(p)
+{
+ Init();
+}
+
+NetAddr::NetAddr(const NetAddr& n)
+ : addr(n.addr), port(n.port)
+{
+ Init();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetAddr::Init()
+{
+ ZeroMemory(&sadr, sizeof(sadr));
+
+ sadr.sin_family = AF_INET;
+ sadr.sin_port = ::htons(port);
+ sadr.sin_addr.s_addr = addr;
+}
+
+void
+NetAddr::InitFromSockAddr()
+{
+ addr = sadr.sin_addr.s_addr;
+ port = ::ntohs(sadr.sin_port);
+}
+
+// +--------------------------------------------------------------------+
+
+sockaddr*
+NetAddr::GetSockAddr() const
+{
+ return (sockaddr*) &sadr;
+}
+
+size_t
+NetAddr::GetSockAddrLength() const
+{
+ return sizeof(sadr);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetAddr::SetSockAddr(sockaddr* s, int size)
+{
+ if (s) {
+ ZeroMemory(&sadr, sizeof(sadr));
+
+ if (size > sizeof(sadr))
+ CopyMemory(&sadr, s, sizeof(sadr));
+ else
+ CopyMemory(&sadr, s, size);
+
+ InitFromSockAddr();
+ }
+}
+
diff --git a/NetEx/NetAddr.h b/NetEx/NetAddr.h
new file mode 100644
index 0000000..562dc64
--- /dev/null
+++ b/NetEx/NetAddr.h
@@ -0,0 +1,58 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetAddr.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Address (specifically, Internet Protocol)
+*/
+
+
+#ifndef NetAddr_h
+#define NetAddr_h
+
+#include <windows.h>
+
+// +-------------------------------------------------------------------+
+
+class NetAddr
+{
+public:
+ static const char* TYPENAME() { return "NetAddr"; }
+
+ NetAddr(const char* a, WORD p=0);
+ NetAddr(DWORD a=0, WORD p=0);
+ NetAddr(const NetAddr& n);
+
+ int operator == (const NetAddr& a) const { return addr==a.addr && port==a.port; }
+
+ DWORD IPAddr() const { return addr; }
+ BYTE B4() const { return (BYTE) ((addr & 0xff000000) >> 24); }
+ BYTE B3() const { return (BYTE) ((addr & 0x00ff0000) >> 16); }
+ BYTE B2() const { return (BYTE) ((addr & 0x0000ff00) >> 8); }
+ BYTE B1() const { return (BYTE) ((addr & 0x000000ff) ); }
+
+ WORD Port() const { return port; }
+ void SetPort(WORD p) { port = p; }
+
+ sockaddr* GetSockAddr() const;
+ size_t GetSockAddrLength() const;
+
+ void SetSockAddr(sockaddr* s, int size);
+ void InitFromSockAddr();
+
+private:
+ void Init();
+
+ DWORD addr; // IP addr in host byte order
+ WORD port; // IP port in host byte order
+ sockaddr_in sadr;
+};
+
+
+#endif NetAddr_h \ No newline at end of file
diff --git a/NetEx/NetClient.cpp b/NetEx/NetClient.cpp
new file mode 100644
index 0000000..4f831fb
--- /dev/null
+++ b/NetEx/NetClient.cpp
@@ -0,0 +1,116 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetClient.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Server Pump for HTTP Server
+*/
+
+
+#include "MemDebug.h"
+#include "NetClient.h"
+#include "NetHost.h"
+#include "NetLayer.h"
+#include <mmsystem.h>
+
+// +-------------------------------------------------------------------+
+
+NetClient::NetClient(const NetAddr& server_addr)
+ : addr(server_addr), sock(0), delta(0), time(0), err(0)
+{
+}
+
+NetClient::~NetClient()
+{
+ delete sock;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+NetClient::Send(Text msg)
+{
+ if (msg.length() > 0) {
+ if (sock)
+ delete sock;
+
+ sock = new(__FILE__,__LINE__) NetSock(addr, true);
+ delta = 0;
+ time = timeGetTime();
+
+ if (!sock) {
+ err = ERR_NOBUFS;
+ return false;
+ }
+
+ err = sock->send(msg);
+ if (err < 0) {
+ err = NetLayer::GetLastError();
+ return false;
+ }
+
+ err = sock->shutdown_output();
+ if (err < 0) {
+ err = NetLayer::GetLastError();
+ return false;
+ }
+
+ return true;
+ }
+
+ else {
+ delete sock;
+ sock = 0;
+ }
+
+ return false;
+}
+
+Text
+NetClient::Recv()
+{
+ Text response;
+
+ if (sock) {
+ int ready = sock->select();
+
+ while (!ready && timeGetTime() - time < 2000) {
+ Sleep(5);
+ ready = sock->select();
+ }
+
+ if (ready) {
+ Text msg = sock->recv();
+
+ while (msg.length() > 0) {
+ response += msg;
+ msg = sock->recv();
+ }
+
+ delta = timeGetTime() - time;
+ }
+
+ delete sock;
+ sock = 0;
+ }
+
+ return response;
+}
+
+Text
+NetClient::SendRecv(Text msg)
+{
+ Text response;
+
+ if (msg.length() > 0 && Send(msg)) {
+ response = Recv();
+ }
+
+ return response;
+}
diff --git a/NetEx/NetClient.h b/NetEx/NetClient.h
new file mode 100644
index 0000000..bbc668a
--- /dev/null
+++ b/NetEx/NetClient.h
@@ -0,0 +1,103 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetClient.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Stream-oriented network client class
+*/
+
+
+#ifndef NetClient_h
+#define NetClient_h
+
+#include <windows.h>
+#include "NetAddr.h"
+#include "NetGram.h"
+#include "NetSock.h"
+#include "List.h"
+
+// +-------------------------------------------------------------------+
+
+class NetClient
+{
+public:
+ static const char* TYPENAME() { return "NetClient"; }
+
+ NetClient(const NetAddr& server_addr);
+ virtual ~NetClient();
+
+ int operator == (const NetClient& c) const { return this == &c; }
+
+ NetAddr GetServerAddr() const { return addr; }
+
+ bool Send(Text msg);
+ Text Recv();
+ Text SendRecv(Text msg);
+
+ int GetLastError() const { return err; }
+ DWORD GetTime() const { return delta; }
+
+protected:
+ NetAddr addr;
+ NetSock* sock;
+ DWORD delta;
+ DWORD time;
+ int err;
+
+public:
+ enum ERRS {
+ ERR_INTR = 10004,
+ ERR_BADF = 10009,
+ ERR_ACCES = 10013,
+ ERR_FAULT = 10014,
+ ERR_INVAL = 10022,
+ ERR_MFILE = 10024,
+
+ ERR_WOULDBLOCK = 10035,
+ ERR_INPROGRESS = 10036,
+ ERR_ALREADY = 10037,
+ ERR_NOTSOCK = 10038,
+ ERR_DESTADDRREQ = 10039,
+ ERR_MSGSIZE = 10040,
+ ERR_PROTOTYPE = 10041,
+ ERR_NOPROTOOPT = 10042,
+ ERR_PROTONOSUPPORT = 10043,
+ ERR_SOCKTNOSUPPORT = 10044,
+ ERR_OPNOTSUPP = 10045,
+ ERR_PFNOSUPPORT = 10046,
+ ERR_AFNOSUPPORT = 10047,
+ ERR_ADDRINUSE = 10048,
+ ERR_ADDRNOTAVAIL = 10049,
+ ERR_NETDOWN = 10050,
+ ERR_NETUNREACH = 10051,
+ ERR_NETRESET = 10052,
+ ERR_CONNABORTED = 10053,
+ ERR_CONNRESET = 10054,
+ ERR_NOBUFS = 10055,
+ ERR_ISCONN = 10056,
+ ERR_NOTCONN = 10057,
+ ERR_SHUTDOWN = 10058,
+ ERR_TOOMANYREFS = 10059,
+ ERR_TIMEDOUT = 10060,
+ ERR_CONNREFUSED = 10061,
+ ERR_LOOP = 10062,
+ ERR_NAMETOOLONG = 10063,
+ ERR_HOSTDOWN = 10064,
+ ERR_HOSTUNREACH = 10065,
+ ERR_NOTEMPTY = 10066,
+ ERR_PROCLIM = 10067,
+ ERR_USERS = 10068,
+ ERR_DQUOT = 10069,
+ ERR_STALE = 10070,
+ ERR_REMOTE = 10071
+ };
+};
+
+
+#endif NetClient_h \ No newline at end of file
diff --git a/NetEx/NetEx.dsp b/NetEx/NetEx.dsp
new file mode 100644
index 0000000..8fdf1a2
--- /dev/null
+++ b/NetEx/NetEx.dsp
@@ -0,0 +1,204 @@
+# Microsoft Developer Studio Project File - Name="NetEx" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=NetEx - 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 "NetEx.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 "NetEx.mak" CFG="NetEx - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "NetEx - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "NetEx - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "NetEx - 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 "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "../FoundationEx" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "SYNC_THREADS" /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
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF "$(CFG)" == "NetEx - 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 "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../FoundationEx" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "SYNC_THREADS" /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
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF
+
+# Begin Target
+
+# Name "NetEx - Win32 Release"
+# Name "NetEx - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\HttpClient.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\HttpServer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\HttpServlet.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\HttpServletExec.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetAddr.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetClient.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetGram.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetHost.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetLayer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetLink.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetMsg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetPeer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetServer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetSock.cpp
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\HttpClient.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HttpServer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HttpServlet.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HttpServletExec.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetAddr.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetClient.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetGram.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetHost.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetLayer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetLink.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetMsg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetPeer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetServer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetSock.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/NetEx/NetEx.dsw b/NetEx/NetEx.dsw
new file mode 100644
index 0000000..306d540
--- /dev/null
+++ b/NetEx/NetEx.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "NetEx"=.\NetEx.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/NetEx/NetEx.vcxproj b/NetEx/NetEx.vcxproj
new file mode 100644
index 0000000..b5d391d
--- /dev/null
+++ b/NetEx/NetEx.vcxproj
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Template|Win32">
+ <Configuration>Template</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <SccProjectName />
+ <SccLocalPath />
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Template|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Template|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>.\Debug\</OutDir>
+ <IntDir>.\Debug\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>.\Release\</OutDir>
+ <IntDir>.\Release\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+ <FunctionLevelLinking>false</FunctionLevelLinking>
+ <Optimization>Disabled</Optimization>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <WarningLevel>Level3</WarningLevel>
+ <MinimalRebuild>true</MinimalRebuild>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <AdditionalIncludeDirectories>../FoundationEx;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_DEBUG;WIN32;_LIB;SYNC_THREADS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AssemblerListingLocation>.\Debug\</AssemblerListingLocation>
+ <PrecompiledHeaderOutputFile>.\Debug\NetEx.pch</PrecompiledHeaderOutputFile>
+ <ObjectFileName>.\Debug\</ObjectFileName>
+ <ProgramDataBaseFileName>.\Debug\</ProgramDataBaseFileName>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ </ClCompile>
+ <ResourceCompile>
+ <Culture>0x0409</Culture>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug\NetEx.bsc</OutputFile>
+ </Bscmake>
+ <Lib>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug\NetEx.lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <StringPooling>true</StringPooling>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <Optimization>MaxSpeed</Optimization>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <WarningLevel>Level3</WarningLevel>
+ <AdditionalIncludeDirectories>../FoundationEx;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>NDEBUG;WIN32;_LIB;SYNC_THREADS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AssemblerListingLocation>.\Release\</AssemblerListingLocation>
+ <PrecompiledHeaderOutputFile>.\Release\NetEx.pch</PrecompiledHeaderOutputFile>
+ <ObjectFileName>.\Release\</ObjectFileName>
+ <ProgramDataBaseFileName>.\Release\</ProgramDataBaseFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <Culture>0x0409</Culture>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release\NetEx.bsc</OutputFile>
+ </Bscmake>
+ <Lib>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release\NetEx.lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="HttpClient.cpp" />
+ <ClCompile Include="HttpServer.cpp" />
+ <ClCompile Include="HttpServlet.cpp" />
+ <ClCompile Include="HttpServletExec.cpp" />
+ <ClCompile Include="NetAddr.cpp" />
+ <ClCompile Include="NetClient.cpp" />
+ <ClCompile Include="NetGram.cpp" />
+ <ClCompile Include="NetHost.cpp" />
+ <ClCompile Include="NetLayer.cpp" />
+ <ClCompile Include="NetLink.cpp" />
+ <ClCompile Include="NetMsg.cpp" />
+ <ClCompile Include="NetPeer.cpp" />
+ <ClCompile Include="NetServer.cpp" />
+ <ClCompile Include="NetSock.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="HttpClient.h" />
+ <ClInclude Include="HttpServer.h" />
+ <ClInclude Include="HttpServlet.h" />
+ <ClInclude Include="HttpServletExec.h" />
+ <ClInclude Include="NetAddr.h" />
+ <ClInclude Include="NetClient.h" />
+ <ClInclude Include="NetGram.h" />
+ <ClInclude Include="NetHost.h" />
+ <ClInclude Include="NetLayer.h" />
+ <ClInclude Include="NetLink.h" />
+ <ClInclude Include="NetMsg.h" />
+ <ClInclude Include="NetPeer.h" />
+ <ClInclude Include="NetServer.h" />
+ <ClInclude Include="NetSock.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/NetEx/NetEx.vcxproj.filters b/NetEx/NetEx.vcxproj.filters
new file mode 100644
index 0000000..a702de6
--- /dev/null
+++ b/NetEx/NetEx.vcxproj.filters
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{f368c362-33c9-4cf0-897f-25cd35c75ddb}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{fe3ab52e-099a-4f2f-a496-d7d42f337ab5}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="HttpClient.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="HttpServer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="HttpServlet.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="HttpServletExec.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetAddr.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetClient.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetGram.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetHost.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetLayer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetLink.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetMsg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetPeer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetServer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetSock.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="HttpClient.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="HttpServer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="HttpServlet.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="HttpServletExec.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetAddr.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetClient.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetGram.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetHost.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetLayer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetLink.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetMsg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetPeer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetServer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetSock.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/NetEx/NetGram.cpp b/NetEx/NetGram.cpp
new file mode 100644
index 0000000..94e1ca2
--- /dev/null
+++ b/NetEx/NetGram.cpp
@@ -0,0 +1,111 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetGram.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Generic Network Packet (Datagram) Implementation
+*/
+
+
+#include "MemDebug.h"
+#include "NetGram.h"
+#include "NetLayer.h"
+
+// +-------------------------------------------------------------------+
+
+static DWORD net_gram_sequence = 1;
+
+// +-------------------------------------------------------------------+
+
+/**
+ * NetGram constructor for ACK packets
+ */
+NetGram::NetGram()
+ : retries(0), packet_id(0), send_time(0)
+{ }
+
+/**
+ * NetGram constructor for receiving packets from remote hosts
+ */
+NetGram::NetGram(const NetAddr& src, Text msg)
+ : addr(src), retries(0), send_time(0)
+{
+ body = msg;
+
+ if (body.length() >= NET_GRAM_HEADER_SIZE) {
+ BYTE* data = (BYTE*) body.data();
+
+ packet_id = (((DWORD) data[0]) << 24) +
+ (((DWORD) data[1]) << 16) +
+ (((DWORD) data[2]) << 8) +
+ ((DWORD) data[3]);
+ }
+}
+
+/**
+ * NetGram constructor for composing packets to send to remote hosts
+ */
+NetGram::NetGram(const NetAddr& dst, Text user_data, int r)
+ : addr(dst), retries(r)
+{
+ send_time = NetLayer::GetTime();
+ packet_id = net_gram_sequence++;
+
+ if (retries)
+ packet_id |= NET_GRAM_RELIABLE;
+
+ static BYTE buf[NET_GRAM_MAX_SIZE];
+ buf[0] = (BYTE) (packet_id >> 24) & 0xff;
+ buf[1] = (BYTE) (packet_id >> 16) & 0xff;
+ buf[2] = (BYTE) (packet_id >> 8) & 0xff;
+ buf[3] = (BYTE) (packet_id) & 0xff;
+
+ int len = user_data.length();
+ if (len >= NET_GRAM_MAX_SIZE - NET_GRAM_HEADER_SIZE)
+ len = NET_GRAM_MAX_SIZE - NET_GRAM_HEADER_SIZE - 1;
+
+ CopyMemory(buf+NET_GRAM_HEADER_SIZE, user_data.data(), len);
+
+ body = Text((char*) buf, len+NET_GRAM_HEADER_SIZE);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGram::Retry()
+{
+ if (retries > 0) {
+ retries--;
+ send_time = NetLayer::GetTime();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+NetGram
+NetGram::Ack()
+{
+ NetGram ack;
+
+ ack.packet_id = packet_id | NET_GRAM_ACK;
+ ack.send_time = NetLayer::GetTime();
+
+ static BYTE buf[NET_GRAM_HEADER_SIZE];
+ buf[0] = (BYTE) (ack.packet_id >> 24) & 0xff;
+ buf[1] = (BYTE) (ack.packet_id >> 16) & 0xff;
+ buf[2] = (BYTE) (ack.packet_id >> 8) & 0xff;
+ buf[3] = (BYTE) (ack.packet_id) & 0xff;
+
+ ack.body = Text((char*) buf, NET_GRAM_HEADER_SIZE);
+
+ return ack;
+}
+
+
+
diff --git a/NetEx/NetGram.h b/NetEx/NetGram.h
new file mode 100644
index 0000000..e836e8d
--- /dev/null
+++ b/NetEx/NetGram.h
@@ -0,0 +1,79 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetGram.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Datagram (UDP) packet that implements the basic
+ packet-oriented network protocol.
+*/
+
+
+#ifndef NetGram_h
+#define NetGram_h
+
+#include <windows.h>
+#include "NetAddr.h"
+#include "Text.h"
+
+// +-------------------------------------------------------------------+
+
+const int NET_GRAM_ACK = 0x80000000;
+const int NET_GRAM_RELIABLE = 0x40000000;
+const int NET_GRAM_SEQ_MASK = 0x3fffffff;
+
+const int NET_GRAM_HEADER_SIZE = 4;
+const int NET_GRAM_MAX_SIZE = 1024;
+
+// +-------------------------------------------------------------------+
+
+class NetGram
+{
+public:
+ static const char* TYPENAME() { return "NetGram"; }
+
+ // for receiving packets from remote hosts:
+ NetGram(const NetAddr& src, Text msg);
+
+ // for composing packets to send to remote hosts:
+ NetGram(const NetAddr& dst, Text user_data, int retries);
+
+ int operator == (const NetGram& g) const { return packet_id == g.packet_id &&
+ addr == g.addr; }
+ int operator < (const NetGram& g) const { return Sequence() < g.Sequence(); }
+
+ DWORD PacketID() const { return packet_id; }
+ DWORD Sequence() const { return packet_id & NET_GRAM_SEQ_MASK; }
+ DWORD SendTime() const { return send_time; }
+ BYTE* Data() const { return (BYTE*) body.data(); }
+ BYTE* UserData() const { return (BYTE*) body.data() + NET_GRAM_HEADER_SIZE; }
+ int Size() const { return body.length(); }
+ const Text& Body() const { return body; }
+ const NetAddr& Address() const { return addr; }
+
+ bool IsAck() const { return packet_id & NET_GRAM_ACK ? true : false; }
+ bool IsReliable() const { return packet_id & NET_GRAM_RELIABLE ? true : false; }
+ int Retries() const { return retries; }
+
+ void Retry();
+ NetGram Ack();
+ void ClearAck() { packet_id &= ~NET_GRAM_ACK; }
+
+protected:
+ NetGram();
+
+ NetAddr addr; // network address of remote host
+ int retries; // number of retries remaining (reliable packets only)
+ DWORD send_time; // time in msec of most recent send attempt
+
+ DWORD packet_id; // copy of packet id from header in body
+ Text body; // header plus user data
+};
+
+
+#endif NetGram_h \ No newline at end of file
diff --git a/NetEx/NetHost.cpp b/NetEx/NetHost.cpp
new file mode 100644
index 0000000..d42ec2a
--- /dev/null
+++ b/NetEx/NetHost.cpp
@@ -0,0 +1,103 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetHost.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Host
+*/
+
+
+#include "MemDebug.h"
+#include "NetHost.h"
+#include "NetLayer.h"
+#include <ctype.h>
+
+NetHost::NetHost()
+{
+ char host_name[256];
+ gethostname(host_name, sizeof(host_name));
+
+ Init(host_name);
+}
+
+NetHost::NetHost(const char* host_name)
+{
+ Init(host_name);
+}
+
+void NetHost::Init(const char* host_name)
+{
+ if (host_name && *host_name) {
+ HOSTENT* h = 0;
+
+ if (isdigit(*host_name)) {
+ DWORD addr = inet_addr(host_name);
+ h = gethostbyaddr((const char*) &addr, 4, AF_INET);
+ }
+ else {
+ h = gethostbyname(host_name);
+ }
+
+ if (h) {
+ name = h->h_name;
+
+ char** alias = h->h_aliases;
+ while (*alias) {
+ aliases.append(new Text(*alias));
+ alias++;
+ }
+
+ char** addr = h->h_addr_list;
+ while (*addr) {
+ NetAddr* pna = new(__FILE__,__LINE__) NetAddr(**(DWORD**) addr);
+ if (pna)
+ addresses.append(pna);
+ addr++;
+ }
+ }
+ }
+}
+
+NetHost::NetHost(const NetHost& n)
+{
+ if (&n != this) {
+ NetHost& nh = (NetHost&) n;
+
+ name = nh.name;
+
+ ListIter<Text> alias = nh.aliases;
+ while (++alias)
+ aliases.append(new Text(*alias.value()));
+
+ ListIter<NetAddr> addr = nh.addresses;
+ while (++addr)
+ addresses.append(new NetAddr(*addr.value()));
+ }
+}
+
+NetHost::~NetHost()
+{
+ aliases.destroy();
+ addresses.destroy();
+}
+
+const char*
+NetHost::Name()
+{
+ return name;
+}
+
+NetAddr
+NetHost::Address()
+{
+ if (addresses.size())
+ return *(addresses[0]);
+
+ return NetAddr((DWORD) 0);
+} \ No newline at end of file
diff --git a/NetEx/NetHost.h b/NetEx/NetHost.h
new file mode 100644
index 0000000..2240eab
--- /dev/null
+++ b/NetEx/NetHost.h
@@ -0,0 +1,50 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetHost.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Host
+*/
+
+#ifndef NET_HOST_H
+#define NET_HOST_H
+
+#include <windows.h>
+#include "NetAddr.h"
+#include "Text.h"
+#include "List.h"
+
+// +-------------------------------------------------------------------+
+
+class NetHost
+{
+public:
+ static const char* TYPENAME() { return "NetHost"; }
+
+ NetHost();
+ NetHost(const char* host_addr);
+ NetHost(const NetHost& n);
+ ~NetHost();
+
+ const char* Name();
+ NetAddr Address();
+
+ List<Text>& Aliases() { return aliases; }
+ List<NetAddr>& AddressList() { return addresses; }
+
+private:
+ void Init(const char* host_name);
+
+ Text name;
+ List<Text> aliases;
+ List<NetAddr> addresses;
+};
+
+
+#endif // NET_HOST_H \ No newline at end of file
diff --git a/NetEx/NetLayer.cpp b/NetEx/NetLayer.cpp
new file mode 100644
index 0000000..8494f72
--- /dev/null
+++ b/NetEx/NetLayer.cpp
@@ -0,0 +1,93 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetLayer.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Wrapper for WinSock Library
+*/
+
+
+#include "MemDebug.h"
+#include <windows.h>
+#include "NetLayer.h"
+
+#include <mmsystem.h>
+#include <time.h>
+
+// +-------------------------------------------------------------------+
+
+static DWORD baseTime = timeGetTime();
+
+// +-------------------------------------------------------------------+
+
+NetLayer::NetLayer()
+{
+ fail = false;
+ ZeroMemory(&info, sizeof(info));
+
+ WORD ver = MAKEWORD(2,2);
+ int err = WSAStartup(ver, &info);
+
+ if (err)
+ fail = true;
+}
+
+NetLayer::~NetLayer()
+{
+ WSACleanup();
+}
+
+bool
+NetLayer::OK() const
+{
+ return !fail;
+}
+
+const char*
+NetLayer::Description() const
+{
+ return info.szDescription;
+}
+
+int
+NetLayer::GetLastError()
+{
+ return WSAGetLastError();
+}
+
+DWORD
+NetLayer::GetTime()
+{
+ DWORD msec = timeGetTime();
+
+ if (msec >= baseTime) {
+ return msec - baseTime;
+ }
+
+ else {
+ DWORD extra = 0xffffffff;
+ return msec + extra - baseTime;
+ }
+}
+
+long
+NetLayer::GetUTC()
+{
+ return (long) time(0);
+}
+
+Text
+NetLayer::GetHostName()
+{
+ char hostname[256];
+ ZeroMemory(hostname, sizeof(hostname));
+ ::gethostname(hostname, sizeof(hostname));
+
+ return hostname;
+}
diff --git a/NetEx/NetLayer.h b/NetEx/NetLayer.h
new file mode 100644
index 0000000..fbb2b52
--- /dev/null
+++ b/NetEx/NetLayer.h
@@ -0,0 +1,45 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetLayer.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Wrapper for WinSock Library
+*/
+
+#ifndef NetLayer_h
+#define NetLayer_h
+
+#include <windows.h>
+#include "Text.h"
+
+// +-------------------------------------------------------------------+
+
+class NetLayer
+{
+public:
+ static const char* TYPENAME() { return "NetLayer"; }
+
+ NetLayer();
+ ~NetLayer();
+
+ bool OK() const;
+ const char* Description() const;
+
+ static int GetLastError();
+ static DWORD GetTime();
+ static long GetUTC();
+ static Text GetHostName();
+
+private:
+ WSADATA info;
+ bool fail;
+};
+
+
+#endif NetLayer_h \ No newline at end of file
diff --git a/NetEx/NetLink.cpp b/NetEx/NetLink.cpp
new file mode 100644
index 0000000..7cd6f4e
--- /dev/null
+++ b/NetEx/NetLink.cpp
@@ -0,0 +1,487 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetLink.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network (IP) Socket Wrapper Implementation
+*/
+
+
+#include "MemDebug.h"
+#include "NetLink.h"
+#include "NetGram.h"
+#include "NetMsg.h"
+#include "NetPeer.h"
+#include "NetLayer.h"
+
+// +-------------------------------------------------------------------+
+
+DWORD WINAPI NetLinkProc(LPVOID link);
+
+const DWORD UDP_HEADER_SIZE = 34;
+
+// +-------------------------------------------------------------------+
+// client-side ctor
+NetLink::NetLink()
+ : hnet(0), shutdown(false), traffic_time(50), resend_time(300),
+ packets_sent(0), packets_recv(0), bytes_sent(0), bytes_recv(0), retries(0), drops(0), lag(100)
+{
+ ZeroMemory(lag_samples, sizeof(lag_samples));
+ lag_index = 0;
+
+ DWORD thread_id = 0;
+ hnet = CreateThread(0, 4096, NetLinkProc, (LPVOID) this, 0, &thread_id);
+}
+
+// server-side ctor
+NetLink::NetLink(NetAddr& a)
+ : addr(a), hnet(0), shutdown(false), traffic_time(50), resend_time(300),
+ packets_sent(0), packets_recv(0), bytes_sent(0), bytes_recv(0), retries(0), drops(0), lag(100)
+{
+ ZeroMemory(lag_samples, sizeof(lag_samples));
+ lag_index = 0;
+
+ sock.bind(addr);
+ DWORD thread_id = 0;
+ hnet = CreateThread(0, 4096, NetLinkProc, (LPVOID) this, 0, &thread_id);
+}
+
+NetLink::~NetLink()
+{
+ if (!shutdown) {
+ shutdown = true;
+ }
+
+ if (hnet) {
+ WaitForSingleObject(hnet, 2000);
+ CloseHandle(hnet);
+ }
+
+ send_list.destroy(); // packets waiting to be ack'ed must be destroyed
+ recv_list.clear(); // received messages are owned by the peers
+ peer_list.destroy(); // but the net link owns the peers!
+}
+
+// +--------------------------------------------------------------------+
+
+static DWORD base_netid = 1000;
+
+DWORD
+NetLink::AddPeer(const char* a, WORD p)
+{
+ return AddPeer(NetAddr(a, p));
+}
+
+DWORD
+NetLink::AddPeer(DWORD a, WORD p)
+{
+ return AddPeer(NetAddr(a, p));
+}
+
+DWORD
+NetLink::AddPeer(const NetAddr& a)
+{
+ if (!a.IPAddr())
+ return 0;
+
+ AutoThreadSync auto_sync(sync);
+
+ NetPeer* peer = FindPeer(a);
+
+ if (!peer) {
+ peer = new(__FILE__, __LINE__) NetPeer(a, base_netid++);
+ if (peer)
+ peer_list.append(peer);
+ }
+
+ if (peer)
+ return peer->NetID();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+NetLink::SendMessage(DWORD nid, void* d, int l, BYTE f)
+{
+ return SendMessage(new(__FILE__,__LINE__) NetMsg(nid, d, l, f));
+}
+
+bool
+NetLink::SendMessage(DWORD nid, BYTE type, const char* text, int len, BYTE f)
+{
+ return SendMessage(new(__FILE__,__LINE__) NetMsg(nid, type, text, len, f));
+}
+
+bool
+NetLink::SendMessage(NetMsg* msg)
+{
+ if (msg) {
+ if (msg->Type() != NetMsg::INVALID &&
+ msg->Type() < NetMsg::RESERVED &&
+ msg->NetID()) {
+
+ NetPeer* p = FindPeer(msg->NetID());
+ if (p)
+ return p->SendMessage(msg);
+ }
+
+ delete msg;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+NetMsg*
+NetLink::GetMessage(DWORD netid)
+{
+ NetMsg* msg = 0;
+
+ // receive from specific host:
+ if (netid) {
+ NetPeer* p = FindPeer(netid);
+ if (p) {
+ msg = p->GetMessage();
+
+ sync.acquire();
+ recv_list.remove(msg);
+ sync.release();
+ }
+ }
+
+ return msg;
+}
+
+// +--------------------------------------------------------------------+
+
+NetMsg*
+NetLink::GetMessage()
+{
+ NetMsg* msg = 0;
+
+ // get first available packet:
+
+ // Double-checked locking:
+ if (recv_list.size()) {
+ sync.acquire();
+ if (recv_list.size()) {
+ msg = recv_list.removeIndex(0);
+ }
+ sync.release();
+
+ if (msg && msg->NetID()) {
+ NetPeer* p = FindPeer(msg->NetID());
+ if (p) {
+ p->GetMessage(); // remove message from peer's list
+ // don't do this inside of sync block -
+ // it might cause a deadlock
+ }
+ }
+ }
+
+ return msg;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLink::Shutdown()
+{
+ shutdown = true;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD WINAPI NetLinkProc(LPVOID link)
+{
+ NetLink* netlink = (NetLink*) link;
+
+ if (netlink)
+ return netlink->DoSendRecv();
+
+ return (DWORD) E_POINTER;
+}
+
+DWORD
+NetLink::DoSendRecv()
+{
+ while (!shutdown) {
+ ReadPackets();
+ SendPackets();
+
+ // discard reeeeally old peers:
+ sync.acquire();
+
+ ListIter<NetPeer> iter = peer_list;
+ while (!shutdown && ++iter) {
+ NetPeer* peer = iter.value();
+
+ if ((NetLayer::GetUTC() - peer->LastReceiveTime()) > 300)
+ delete iter.removeItem();
+ }
+
+ sync.release();
+ Sleep(traffic_time);
+ }
+
+ return 0;
+}
+
+void
+NetLink::ReadPackets()
+{
+ while (!shutdown && sock.select(NetSock::SELECT_READ) > 0) {
+ NetGram* gram = RecvNetGram();
+
+ if (gram && gram->IsReliable()) {
+ if (gram->IsAck()) {
+ ProcessAck(gram);
+ delete gram;
+ }
+ else {
+ AckNetGram(gram);
+ QueueNetGram(gram);
+ }
+ }
+ else {
+ QueueNetGram(gram);
+ }
+ }
+}
+
+void
+NetLink::SendPackets()
+{
+ if (shutdown)
+ return;
+
+ if (sock.select(NetSock::SELECT_WRITE) > 0) {
+ DoRetries();
+ }
+
+ AutoThreadSync auto_sync(sync);
+
+ ListIter<NetPeer> iter = peer_list;
+ while (!shutdown && ++iter) {
+ NetPeer* p = iter.value();
+ NetGram* g = 0;
+
+ do {
+ if (sock.select(NetSock::SELECT_WRITE) > 0) {
+ g = p->ComposeGram();
+ if (g) {
+ SendNetGram(g);
+ }
+ }
+ else {
+ g = 0;
+ }
+ }
+ while (!shutdown && g);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLink::SendNetGram(NetGram* gram)
+{
+ if (gram) {
+ if (gram->IsReliable()) {
+ send_list.append(gram);
+ }
+
+ int err = sock.sendto(gram->Body(), gram->Address());
+
+ if (err < 0) {
+ err = NetLayer::GetLastError();
+ }
+ else {
+ packets_sent += 1;
+ bytes_sent += gram->Size() + UDP_HEADER_SIZE;
+ }
+
+ if (!gram->IsReliable())
+ delete gram;
+ }
+}
+
+NetGram*
+NetLink::RecvNetGram()
+{
+ NetAddr from;
+ Text msg = sock.recvfrom(&from);
+
+ packets_recv += 1;
+ bytes_recv += msg.length() + UDP_HEADER_SIZE;
+
+ return new(__FILE__, __LINE__) NetGram(from, msg);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLink::AckNetGram(NetGram* gram)
+{
+ if (gram) {
+ NetGram ack = gram->Ack();
+
+ int err = sock.sendto(ack.Body(), gram->Address());
+ if (err < 0)
+ err = NetLayer::GetLastError();
+ }
+ else {
+ Print("NetLink::AckNetGram( NULL!!! )\n");
+ }
+}
+
+void
+NetLink::ProcessAck(NetGram* gram)
+{
+ if (!shutdown && send_list.size()) {
+ AutoThreadSync auto_sync(sync);
+
+ // remove the ack flag:
+ gram->ClearAck();
+
+ // find a matching outgoing packet:
+ int sent = send_list.index(gram);
+ if (sent >= 0) {
+ NetGram* orig = send_list.removeIndex(sent);
+ DWORD time = NetLayer::GetTime();
+ DWORD msec = time - orig->SendTime();
+ double dlag = 0.75 * lag + 0.25 * msec;
+
+ if (lag_index >= 10) lag_index = 0;
+ lag_samples[lag_index++] = msec;
+
+ NetPeer* peer = FindPeer(orig->Address());
+ if (peer)
+ peer->SetLastReceiveTime(NetLayer::GetUTC());
+
+ delete orig;
+
+ lag = (DWORD) dlag;
+
+ if (lag > 100)
+ resend_time = 3 * lag;
+ else
+ resend_time = 300;
+ }
+ }
+}
+
+void
+NetLink::QueueNetGram(NetGram* gram)
+{
+ if (!shutdown) {
+ AutoThreadSync auto_sync(sync);
+
+ DWORD sequence = 0;
+ NetPeer* peer = FindPeer(gram->Address());
+
+ if (peer) {
+ sequence = peer->Sequence();
+ }
+ else {
+ peer = new(__FILE__, __LINE__) NetPeer(gram->Address(), base_netid++);
+ if (peer)
+ peer_list.append(peer);
+ }
+
+ if (!gram->IsReliable()) {
+ if (gram->Sequence() < sequence) { // discard, too old
+ delete gram;
+ return;
+ }
+ }
+
+ // sort this gram into the recv list(s) based on sequence:
+ if (peer) {
+ peer->ReceiveGram(gram, &recv_list);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLink::DoRetries()
+{
+ if (!shutdown) {
+ AutoThreadSync auto_sync(sync);
+
+ if (send_list.size()) {
+ int time = (int) NetLayer::GetTime();
+
+ ListIter<NetGram> iter = send_list;
+ while (!shutdown && ++iter) {
+ NetGram* gram = iter.value();
+
+ // still trying ?
+ if (gram->Retries() > 0) {
+ DWORD last_send = gram->SendTime();
+ DWORD delta = time - last_send;
+
+ if (delta > resend_time) {
+ gram->Retry();
+ sock.sendto(gram->Body(), gram->Address());
+ retries++;
+ }
+ }
+
+ // oh, give it up:
+ else {
+ iter.removeItem();
+ delete gram;
+ drops++;
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+NetPeer*
+NetLink::FindPeer(DWORD netid)
+{
+ AutoThreadSync auto_sync(sync);
+ NetPeer* peer = 0;
+
+ ListIter<NetPeer> iter = peer_list;
+ while (++iter && !peer) {
+ NetPeer* p = iter.value();
+
+ if (p->NetID() == netid)
+ peer = p;
+ }
+
+ return peer;
+}
+
+NetPeer*
+NetLink::FindPeer(const NetAddr& a)
+{
+ AutoThreadSync auto_sync(sync);
+ NetPeer* peer = 0;
+
+ ListIter<NetPeer> iter = peer_list;
+ while (++iter && !peer) {
+ NetPeer* p = iter.value();
+
+ if (p->Address() == a)
+ peer = p;
+ }
+
+ return peer;
+}
diff --git a/NetEx/NetLink.h b/NetEx/NetLink.h
new file mode 100644
index 0000000..8d83ced
--- /dev/null
+++ b/NetEx/NetLink.h
@@ -0,0 +1,113 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetLink.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Link for Remote Player
+*/
+
+
+#ifndef NetLink_h
+#define NetLink_h
+
+#include <windows.h>
+#include "NetAddr.h"
+#include "NetSock.h"
+#include "List.h"
+#include "ThreadSync.h"
+
+// +-------------------------------------------------------------------+
+
+class NetGram;
+class NetMsg;
+class NetPeer;
+
+// +-------------------------------------------------------------------+
+
+class NetLink
+{
+public:
+ static const char* TYPENAME() { return "NetLink"; }
+
+ NetLink();
+ NetLink(NetAddr& a);
+ virtual ~NetLink();
+
+ int operator == (const NetLink& that) const { return this == &that; }
+
+ const NetAddr& GetAddress() const { return addr; }
+
+ DWORD AddPeer(const char* a, WORD p=12345);
+ DWORD AddPeer(DWORD a, WORD p=12345);
+ DWORD AddPeer(const NetAddr& a);
+
+ bool SendMessage(DWORD nid, void* d, int l, BYTE f=0);
+ bool SendMessage(DWORD nid, BYTE type, const char* text, int len, BYTE f=0);
+ bool SendMessage(NetMsg* msg);
+
+ NetMsg* GetMessage();
+ NetMsg* GetMessage(DWORD netid);
+
+ virtual void Shutdown();
+ DWORD DoSendRecv();
+
+ DWORD GetResendInterval() const { return resend_time; }
+ void SetResendInterval(DWORD t) { resend_time = t; }
+ DWORD GetTrafficInterval() const { return traffic_time; }
+ void SetTrafficInterval(DWORD t) { traffic_time = t; }
+
+ DWORD GetPacketsSent() const { return packets_sent; }
+ DWORD GetPacketsRecv() const { return packets_recv; }
+ DWORD GetBytesSent() const { return bytes_sent; }
+ DWORD GetBytesRecv() const { return bytes_recv; }
+ DWORD GetRetries() const { return retries; }
+ DWORD GetDrops() const { return drops; }
+ DWORD GetLag() const { return lag; }
+
+ NetPeer* FindPeer(const NetAddr& a);
+ NetPeer* FindPeer(DWORD netid);
+
+protected:
+ void SendNetGram(NetGram* g);
+ NetGram* RecvNetGram();
+ void AckNetGram(NetGram* gram);
+ void ProcessAck(NetGram* gram);
+ void QueueNetGram(NetGram* gram);
+
+ void ReadPackets();
+ void SendPackets();
+ void DoRetries();
+
+ NetAddr addr;
+ NetSock sock;
+ List<NetGram> send_list;
+ List<NetMsg> recv_list;
+ List<NetPeer> peer_list;
+
+ HANDLE hnet;
+ bool shutdown;
+ ThreadSync sync;
+
+ DWORD resend_time;
+ DWORD traffic_time;
+
+ DWORD packets_sent;
+ DWORD packets_recv;
+ DWORD bytes_sent;
+ DWORD bytes_recv;
+ DWORD retries;
+ DWORD drops;
+ DWORD lag;
+
+ DWORD lag_samples[10];
+ int lag_index;
+};
+
+
+#endif NetLink_h \ No newline at end of file
diff --git a/NetEx/NetMsg.cpp b/NetEx/NetMsg.cpp
new file mode 100644
index 0000000..dec0bfd
--- /dev/null
+++ b/NetEx/NetMsg.cpp
@@ -0,0 +1,89 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetMsg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ User level network message
+*/
+
+
+#include "MemDebug.h"
+#include <windows.h>
+#include "NetMsg.h"
+
+// +-------------------------------------------------------------------+
+
+static DWORD net_msg_sequence = 1;
+
+// +-------------------------------------------------------------------+
+
+NetMsg::NetMsg(DWORD nid, void* d, int l, BYTE f)
+ : msgid(net_msg_sequence++), netid(nid), len(l), flags(f)
+{
+ data = new(__FILE__,__LINE__) BYTE[len];
+
+ if (data) {
+ CopyMemory(data, d, len);
+
+ if (len < MAX_SIZE)
+ data[1] = len;
+ else
+ data[1] = 0;
+ }
+ else {
+ len = 0;
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+NetMsg::NetMsg(DWORD nid, BYTE type, const char* text, int l, BYTE f)
+ : msgid(net_msg_sequence++), netid(nid), len(2+l), flags(f)
+{
+ data = new(__FILE__,__LINE__) BYTE[len];
+
+ if (data) {
+ data[0] = type;
+
+ if (len < MAX_SIZE)
+ data[1] = len;
+ else
+ data[1] = 0;
+
+ if (len > 2)
+ CopyMemory(data+2, text, len-2);
+ }
+ else {
+ len = 0;
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+NetMsg::~NetMsg()
+{
+ delete [] data;
+}
+
+// +-------------------------------------------------------------------+
+
+int NetMsg::operator < (const NetMsg& m) const
+{
+ if (data[0] == MULTIPART && m.data[0] == MULTIPART) {
+ NetMsgMultipart* p1 = (NetMsgMultipart*) data;
+ NetMsgMultipart* p2 = (NetMsgMultipart*) m.data;
+
+ if (p1->msgid == p2->msgid)
+ return p1->partno < p2->partno;
+
+ return p1->msgid < p2->msgid;
+ }
+
+ return msgid < m.msgid;
+}
diff --git a/NetEx/NetMsg.h b/NetEx/NetMsg.h
new file mode 100644
index 0000000..1c2a91a
--- /dev/null
+++ b/NetEx/NetMsg.h
@@ -0,0 +1,81 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetMsg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ User level network message
+*/
+
+
+#ifndef NetMsg_h
+#define NetMsg_h
+
+#include <windows.h>
+#include "NetAddr.h"
+#include "NetGram.h"
+#include "NetSock.h"
+#include "List.h"
+
+// +-------------------------------------------------------------------+
+
+class NetMsg
+{
+public:
+ static const char* TYPENAME() { return "NetMsg"; }
+
+ enum FLAGS { RELIABLE = 0x01, PRIORITY = 0x02, SCATTER = 0x04 };
+ enum TYPES { INVALID = 0,
+ RESERVED = 0xF0,
+ MULTIPART = 0xF1
+ };
+ enum { MAX_SIZE = 250 };
+
+ NetMsg(DWORD nid, void* d, int l, BYTE f=0);
+ NetMsg(DWORD nid, BYTE type, const char* text, int len, BYTE f=0);
+ ~NetMsg();
+
+ int operator == (const NetMsg& m) const { return msgid == m.msgid &&
+ netid == m.netid; }
+ int operator < (const NetMsg& m) const;
+
+ DWORD Sequence() const { return msgid; }
+ DWORD NetID() const { return netid; }
+ const BYTE* Data() const { return data; }
+ BYTE Type() const { return data ? *data : 0; }
+ int Length() const { return len; }
+ BYTE Flags() const { return flags; }
+
+ bool IsReliable() const { return flags & RELIABLE ? true : false; }
+ bool IsPriority() const { return flags & PRIORITY ? true : false; }
+ bool IsScatter() const { return flags & SCATTER ? true : false; }
+
+ void SetSequence(DWORD s) { msgid = s; }
+
+private:
+ DWORD msgid;
+ DWORD netid;
+ BYTE* data;
+ int len;
+ BYTE flags;
+};
+
+// +-------------------------------------------------------------------+
+
+struct NetMsgMultipart {
+ BYTE type;
+ BYTE len;
+ DWORD msgid;
+ DWORD partno;
+ DWORD nparts;
+ BYTE payload[256];
+};
+
+// +-------------------------------------------------------------------+
+
+#endif NetMsg_h \ No newline at end of file
diff --git a/NetEx/NetPeer.cpp b/NetEx/NetPeer.cpp
new file mode 100644
index 0000000..6bf5d18
--- /dev/null
+++ b/NetEx/NetPeer.cpp
@@ -0,0 +1,441 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetPeer.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ One side of a UDP net link connection
+*/
+
+
+#include "MemDebug.h"
+#include <windows.h>
+#include "NetPeer.h"
+#include "NetGram.h"
+#include "NetMsg.h"
+#include "NetLayer.h"
+
+#include <stdio.h>
+
+// +-------------------------------------------------------------------+
+
+const int MULTIPART_CHUNKSIZE = 232;
+const int MULTIPART_HEADER = 16;
+const int UDP_HEADER_SIZE = 34;
+
+static NetMsgMultipart multi_part_buffer;
+static DWORD multi_msg_sequence = 1;
+
+// +-------------------------------------------------------------------+
+
+NetPeer::NetPeer(const NetAddr& a, DWORD id)
+ : addr(a), netid(id), sequence(0), pps(0), bps(0), max_qsize(0),
+ status(OK), hist_indx(0), send_size(0), recv_size(0),
+ chunk_size(MULTIPART_CHUNKSIZE)
+{
+ ZeroMemory(hist_time, sizeof(hist_time));
+ ZeroMemory(hist_size, sizeof(hist_size));
+
+ last_recv_time = NetLayer::GetUTC();
+}
+
+NetPeer::~NetPeer()
+{
+ send_list.destroy();
+ recv_list.destroy();
+
+ multi_send_list.destroy();
+ multi_recv_list.destroy();
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+NetPeer::SendMessage(NetMsg* msg)
+{
+ if (msg) {
+ if (max_qsize > 0 && msg->Length() + send_size > max_qsize) {
+ status = SEND_OVERFLOW;
+ delete msg;
+ return false;
+ }
+
+ // simple message
+ if (msg->Length() <= (int) chunk_size) {
+ if (msg->IsPriority())
+ send_list.insert(msg);
+ else
+ send_list.append(msg);
+
+ send_size += msg->Length();
+ }
+
+ // multipart message
+ else {
+ List<NetMsg>* list = &send_list;
+
+ if (msg->IsScatter())
+ list = &multi_send_list;
+
+ DWORD nparts = msg->Length() / chunk_size;
+ DWORD extra = msg->Length() % chunk_size;
+
+ if (extra > 0) nparts++;
+
+ multi_part_buffer.type = NetMsg::MULTIPART;
+ multi_part_buffer.msgid = multi_msg_sequence++;
+ multi_part_buffer.nparts = nparts;
+
+ DWORD header_size = (DWORD) (&multi_part_buffer.payload) -
+ (DWORD) (&multi_part_buffer);
+
+ const BYTE* p = msg->Data();
+
+ for (DWORD i = 0; i < nparts; i++) {
+ multi_part_buffer.partno = i;
+ NetMsg* part = 0;
+ DWORD part_size = chunk_size;
+
+ if (i == nparts-1 && extra > 0) // last partial payload
+ part_size = extra;
+
+ CopyMemory(multi_part_buffer.payload, p, part_size);
+ p += part_size;
+ part = new(__FILE__,__LINE__) NetMsg(msg->NetID(),
+ &multi_part_buffer,
+ header_size + part_size,
+ msg->Flags());
+
+ if (part) {
+ list->append(part);
+ send_size += part->Length();
+ }
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+// +-------------------------------------------------------------------+
+
+NetMsg*
+NetPeer::GetMessage()
+{
+ if (recv_list.size() > 0) {
+ NetMsg* msg = recv_list.removeIndex(0);
+ recv_size -= msg->Length();
+ return msg;
+ }
+
+ return 0;
+}
+
+// +-------------------------------------------------------------------+
+
+NetGram*
+NetPeer::ComposeGram()
+{
+ NetGram* g = 0;
+
+ if ((send_list.size() || multi_send_list.size()) && OKtoSend()) {
+ AutoThreadSync auto_sync(sync);
+
+ int xmit_size = send_size;
+ int nmsg = send_list.size();
+ int limit = NET_GRAM_MAX_SIZE;
+ bool reliable = false;
+ bool is_multi = false;
+
+ NetMsg* multi_msg = 0;
+ List<NetMsg>* list = &send_list;
+
+ if (xmit_size > limit) {
+ xmit_size = 0;
+ nmsg = 0;
+
+ if (send_list.size() > 0) {
+ NetMsg* msg = 0;
+
+ // if there is regular traffic, and multipart traffic
+ if (multi_send_list.size()) {
+ // just send one multipart message in this packet
+ multi_msg = multi_send_list.removeIndex(0);
+ limit -= msg->Length();
+ reliable = true;
+ is_multi = true;
+ }
+
+ for (int i = 0; i < send_list.size(); i++) {
+ NetMsg* msg = send_list[i];
+
+ if (xmit_size + msg->Length() < limit) {
+ xmit_size += msg->Length();
+ nmsg++;
+ }
+ else {
+ break;
+ }
+ }
+ }
+ else {
+ // if there is only multipart traffic,
+ // send as many multipart messages as will fit:
+ list = &multi_send_list;
+ reliable = true;
+ is_multi = true;
+
+ for (int i = 0; i < multi_send_list.size(); i++) {
+ NetMsg* msg = multi_send_list[i];
+
+ if (xmit_size + msg->Length() < limit) {
+ xmit_size += msg->Length();
+ nmsg++;
+ }
+ else {
+ break;
+ }
+ }
+ }
+ }
+
+ if (xmit_size > 0 && nmsg > 0) {
+ BYTE* buffer = new(__FILE__,__LINE__) BYTE[xmit_size];
+ BYTE* p = buffer;
+
+ if (multi_msg) {
+ if (buffer) {
+ CopyMemory(p, multi_msg->Data(), multi_msg->Length());
+ p[1] = multi_msg->Length();
+ p += multi_msg->Length();
+ }
+ delete multi_msg;
+ }
+
+ while (nmsg-- && p < buffer + xmit_size) {
+ NetMsg* msg = list->removeIndex(0);
+
+ if (msg) {
+ if (msg->IsReliable()) reliable = true;
+ if (buffer) {
+ CopyMemory(p, msg->Data(), msg->Length());
+ p[1] = msg->Length();
+ p += msg->Length();
+ }
+ delete msg;
+ }
+ }
+
+ if (buffer) {
+ Text user_data((const char*) buffer, xmit_size);
+ int retries = 0;
+
+ if (reliable)
+ retries = 5;
+
+ if (is_multi)
+ retries = 10;
+
+ send_size -= xmit_size;
+
+ hist_size[hist_indx] = xmit_size + UDP_HEADER_SIZE;
+ hist_time[hist_indx] = NetLayer::GetTime();
+ hist_indx++;
+
+ if (hist_indx >= HIST_SIZE)
+ hist_indx = 0;
+
+ g = new(__FILE__,__LINE__) NetGram(addr, user_data, retries);
+ delete buffer;
+ }
+ }
+
+ // the next msg is too big to fit in a single packet
+ else {
+ NetMsg* m = send_list.removeIndex(0);
+ send_size -= m->Length();
+ delete m;
+ }
+ }
+
+ return g;
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+NetPeer::ReceiveGram(NetGram* g, List<NetMsg>* q)
+{
+ if (g) {
+ if (max_qsize > 0 && recv_size + g->Size() > max_qsize) {
+ status = RECV_OVERFLOW;
+ delete g;
+ return false;
+ }
+
+ sequence = g->Sequence();
+ recv_size += g->Size() - NET_GRAM_HEADER_SIZE;
+
+ // PARSE THE BLOCKS:
+ BYTE* p = g->UserData();
+
+ while (p < g->Data() + g->Size()) {
+ BYTE block_type = p[0];
+ BYTE block_size = p[1];
+
+ if (!block_type || !block_size)
+ break;
+
+ NetMsg* msg = new(__FILE__,__LINE__) NetMsg(netid, p, block_size);
+
+ if (msg) {
+ if (msg->Type() < NetMsg::RESERVED) {
+ msg->SetSequence(sequence);
+
+ recv_list.insertSort(msg);
+
+ if (q)
+ q->insertSort(msg);
+
+ p += block_size;
+ }
+
+ else if (msg->Type() == NetMsg::MULTIPART) {
+ multi_recv_list.insertSort(msg);
+ p += block_size;
+
+ CheckMultiRecv(q);
+ }
+ }
+ }
+
+ last_recv_time = NetLayer::GetUTC();
+
+ delete g;
+ return true;
+ }
+
+ return false;
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+NetPeer::OKtoSend() const
+{
+ if (pps || bps) {
+ DWORD hist_total = 0;
+ DWORD hist_count = 0;
+ DWORD now = NetLayer::GetTime();
+ DWORD hist_oldest = now;
+ DWORD hist_newest = 0;
+
+ for (int i = 0; i < HIST_SIZE; i++) {
+ if (hist_size[i] > 0) {
+ hist_total += hist_size[i];
+ hist_count++;
+ }
+
+ if (hist_time[i] > 0) {
+ if (hist_time[i] < hist_oldest)
+ hist_oldest = hist_time[i];
+
+ if (hist_time[i] > hist_newest)
+ hist_newest = hist_time[i];
+ }
+ }
+
+ if (now - hist_newest < (DWORD) pps)
+ return false;
+
+ DWORD delta = now - hist_oldest;
+ DWORD avg_bps = hist_total / delta;
+
+ if (bps > 0 && avg_bps > (DWORD) bps)
+ return false;
+ }
+
+ return true;
+}
+
+// +-------------------------------------------------------------------+
+
+struct PacketAssembly {
+ DWORD msgid;
+ DWORD netid;
+ int nreq;
+ int nparts;
+ int nbytes;
+};
+
+void
+NetPeer::CheckMultiRecv(List<NetMsg>* q)
+{
+ const int MAX_SIMULTANEOUS_MULTI_SEQUENCES = 8;
+
+ PacketAssembly assy[MAX_SIMULTANEOUS_MULTI_SEQUENCES];
+ ZeroMemory(assy, sizeof(assy));
+
+ DWORD header_size = (DWORD) (&multi_part_buffer.payload) -
+ (DWORD) (&multi_part_buffer);
+
+ // Catalog how much of each multipart sequence has been received:
+ for (int i = 0; i < multi_recv_list.size(); i++) {
+ NetMsg* msg = multi_recv_list[i];
+ NetMsgMultipart* m = (NetMsgMultipart*) msg->Data();
+
+ for (int n = 0; n < MAX_SIMULTANEOUS_MULTI_SEQUENCES; n++) {
+ PacketAssembly* a = assy + n;
+
+ if (a->msgid == 0 || (a->msgid == m->msgid && a->netid == msg->NetID())) {
+ a->msgid = m->msgid;
+ a->netid = msg->NetID();
+ a->nreq = m->nparts;
+ a->nparts += 1;
+ a->nbytes += m->len - header_size;
+ break;
+ }
+ }
+ }
+
+ for (int n = 0; n < MAX_SIMULTANEOUS_MULTI_SEQUENCES; n++) {
+ PacketAssembly* a = assy + n;
+
+ // is this sequence complete?
+ if (a->msgid && a->nparts == a->nreq) {
+ BYTE* buffer = new BYTE[a->nbytes];
+ BYTE* p = buffer;
+ WORD nid = 0;
+
+ ListIter<NetMsg> iter = multi_recv_list;
+ while (++iter) {
+ netid = iter->NetID();
+ NetMsgMultipart* m = (NetMsgMultipart*) iter->Data();
+
+ // found part of the sequence
+ if (m->msgid == a->msgid && netid == a->netid) {
+ // copy it into the buffer
+ CopyMemory(p, m->payload, m->len - header_size);
+ p += m->len - header_size;
+
+ delete iter.removeItem();
+ }
+ }
+
+ NetMsg* msg = new(__FILE__,__LINE__) NetMsg(netid, buffer, a->nbytes, NetMsg::RELIABLE);
+ if (msg) {
+ recv_list.insertSort(msg);
+
+ if (q)
+ q->insertSort(msg);
+ }
+ }
+ }
+}
diff --git a/NetEx/NetPeer.h b/NetEx/NetPeer.h
new file mode 100644
index 0000000..b2ec882
--- /dev/null
+++ b/NetEx/NetPeer.h
@@ -0,0 +1,98 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetPeer.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ One side of a UDP net link connection
+*/
+
+
+#ifndef NetPeer_h
+#define NetPeer_h
+
+#include <windows.h>
+#include "NetAddr.h"
+#include "List.h"
+#include "ThreadSync.h"
+
+// +-------------------------------------------------------------------+
+
+class NetGram;
+class NetMsg;
+
+// +-------------------------------------------------------------------+
+
+class NetPeer
+{
+public:
+ static const char* TYPENAME() { return "NetPeer"; }
+
+ enum STATUS { OK, SEND_OVERFLOW, RECV_OVERFLOW };
+
+ NetPeer(const NetAddr& addr, DWORD id);
+ ~NetPeer();
+
+ int operator == (const NetPeer& p) const { return netid == p.netid; }
+
+ bool SendMessage(NetMsg* msg);
+ NetMsg* GetMessage();
+
+ NetGram* ComposeGram();
+ bool ReceiveGram(NetGram* g, List<NetMsg>* q=0);
+
+ const NetAddr& Address() const { return addr; }
+ DWORD NetID() const { return netid; }
+ DWORD Sequence() const { return sequence; }
+
+ int GetMaxPPS() const { return pps; }
+ void SetMaxPPS(int p) { pps = p; }
+ int GetMaxBPS() const { return bps; }
+ void SetMaxBPS(int b) { bps = b; }
+ int GetMaxQSize() const { return max_qsize; }
+ void SetMaxQSize(int q) { max_qsize = q; }
+
+ DWORD GetChunkSize() const { return chunk_size; }
+ void SetChunkSize(DWORD s) { chunk_size = s; }
+
+ DWORD LastReceiveTime() const { return last_recv_time; }
+ void SetLastReceiveTime(DWORD t) { last_recv_time = t; }
+
+private:
+ bool OKtoSend() const;
+ void CheckMultiRecv(List<NetMsg>* q);
+
+ NetAddr addr; // remote network address
+ DWORD sequence; // highest packet id received
+ DWORD netid; // unique id for this peer
+ int pps; // max packets per second
+ int bps; // max bits per second
+ int max_qsize; // max bytes in either queue
+ int status; // ok or error code
+ DWORD chunk_size; // size of multipart message chunk
+
+ enum HIST { HIST_SIZE=8 };
+
+ DWORD last_recv_time; // time of last received packet
+ DWORD hist_time[HIST_SIZE]; // history for pps check
+ DWORD hist_size[HIST_SIZE]; // history for bps check
+ int hist_indx; // index into history
+
+ int send_size; // total bytes in send list
+ int recv_size; // total bytes in recv list
+ List<NetMsg> send_list; // queue of messages waiting to be sent
+ List<NetMsg> recv_list; // queue of messages waiting to be read
+
+ List<NetMsg> multi_send_list;
+ List<NetMsg> multi_recv_list;
+
+ ThreadSync sync;
+};
+
+
+#endif NetPeer_h \ No newline at end of file
diff --git a/NetEx/NetServer.cpp b/NetEx/NetServer.cpp
new file mode 100644
index 0000000..6534202
--- /dev/null
+++ b/NetEx/NetServer.cpp
@@ -0,0 +1,260 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetServer.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Server Pump for HTTP Server
+*/
+
+
+#include "MemDebug.h"
+#include "NetServer.h"
+#include "NetHost.h"
+#include "NetLayer.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <mmsystem.h>
+
+// +-------------------------------------------------------------------+
+
+DWORD WINAPI NetServerListenerProc(LPVOID link);
+DWORD WINAPI NetServerReaderProc(LPVOID link);
+
+struct PoolItem { NetServer* server; int thread_index; };
+
+// +-------------------------------------------------------------------+
+
+NetServer::NetServer(WORD port, int nthreads)
+ : sock(true), pool(0), conn(0), poolsize(nthreads), err(0),
+ server_shutdown(false), hreader(0)
+{
+ NetHost host;
+ addr = NetAddr(host.Address().IPAddr(), port);
+
+ sock.bind(addr);
+ sock.listen(3);
+
+ if (poolsize < 1) poolsize = 1;
+
+ pool = new(__FILE__,__LINE__) HANDLE[poolsize];
+ conn = new(__FILE__,__LINE__) NetSock*[poolsize];
+ clients = new(__FILE__,__LINE__) NetAddr[poolsize];
+
+ if (pool && conn && clients) {
+ ZeroMemory(pool, poolsize * sizeof(HANDLE));
+ ZeroMemory(conn, poolsize * sizeof(NetSock*));
+
+ DWORD thread_id = 0;
+
+ for (int i = 0; i < poolsize; i++) {
+ thread_id = 0;
+ PoolItem* item = new PoolItem;
+ item->server = this;
+ item->thread_index = i;
+
+ pool[i] = CreateThread(0, 4096, NetServerReaderProc, (LPVOID) item, 0, &thread_id);
+ }
+
+ thread_id = 0;
+ hreader = CreateThread(0, 4096, NetServerListenerProc, (LPVOID) this, 0, &thread_id);
+ }
+}
+
+NetServer::~NetServer()
+{
+ if (!server_shutdown) {
+ server_shutdown = true;
+ sock.close();
+ }
+
+ if (hreader) {
+ WaitForSingleObject(hreader, 1000);
+ CloseHandle(hreader);
+ }
+
+ if (pool && poolsize) {
+ for (int i = 0; i < poolsize; i++) {
+ WaitForSingleObject(pool[i], 1000);
+ CloseHandle(pool[i]);
+ delete conn[i];
+ conn[i] = 0;
+ }
+
+ delete [] pool;
+ delete [] conn;
+ delete [] clients;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetServer::Shutdown()
+{
+ server_shutdown = true;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD WINAPI NetServerListenerProc(LPVOID link)
+{
+ NetServer* net_server = (NetServer*) link;
+
+ if (net_server)
+ return net_server->Listener();
+
+ return (DWORD) E_POINTER;
+}
+
+DWORD
+NetServer::Listener()
+{
+ while (!server_shutdown) {
+ NetSock* s = sock.accept(&client_addr);
+
+ while (s) {
+ sync.acquire();
+
+ for (int i = 0; i < poolsize; i++) {
+ if (conn[i] == 0) {
+ conn[i] = s;
+ clients[i] = client_addr;
+ s = 0;
+ break;
+ }
+ }
+
+ sync.release();
+
+ // wait for a thread to become not busy
+ if (s)
+ Sleep(10);
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD WINAPI NetServerReaderProc(LPVOID link)
+{
+ if (!link) return (DWORD) E_POINTER;
+
+ PoolItem* item = (PoolItem*) link;
+ NetServer* net_server = item->server;
+ int index = item->thread_index;
+
+ delete item;
+
+ if (net_server)
+ return net_server->Reader(index);
+
+ return (DWORD) E_POINTER;
+}
+
+DWORD
+NetServer::Reader(int index)
+{
+ // init random seed for this thread:
+ srand(timeGetTime());
+
+ while (!server_shutdown) {
+ sync.acquire();
+ NetSock* s = conn[index];
+ sync.release();
+
+ if (s) {
+ const int MAX_REQUEST = 4096;
+ Text request;
+
+ /***
+ *** NOT SURE WHY, BUT THIS DOESN'T WORK FOR SHIT
+ ***
+ *** Setting the socket timeout to 2 seconds caused it
+ *** to wait for two seconds, read nothing, and give up
+ *** with a WSAETIMEDOUT error. Meanwhile, the client
+ *** immediately registered a failure (during the 2 sec
+ *** delay) and aborted the request.
+ ***
+
+ s->set_timeout(2000);
+ Text msg = s->recv();
+
+ while (msg.length() > 0 && request.length() < MAX_REQUEST) {
+ request += msg;
+ msg = s->recv();
+ }
+
+ ***/
+
+ request = s->recv();
+
+ if (request.length() > 0 && !s->is_closed()) {
+ Text response = ProcessRequest(request, clients[index]);
+ err = s->send(response);
+ if (err < 0) {
+ err = NetLayer::GetLastError();
+ }
+ }
+
+ sync.acquire();
+ delete conn[index];
+ conn[index] = 0;
+ sync.release();
+ }
+ else {
+ Sleep(5);
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+NetServer::ProcessRequest(Text msg, const NetAddr& addr)
+{
+ if (msg.indexOf("GET ") == 0)
+ return DefaultResponse();
+
+ return ErrorResponse();
+}
+
+Text
+NetServer::DefaultResponse()
+{
+ Text response =
+ "HTTP/1.0 200 OK\nServer: Generic NetServer 1.0\nMIME-Version: 1.0\nContent-type: text/html\n\n";
+
+ response += "<html><head><title>Generic NetServer 1.0</title></head>\n\n";
+ response += "<body bgcolor=\"black\" text=\"white\">\n";
+ response += "<h1>Generic NetServer 1.0</h1>\n";
+ response += "<p>Didn't think I could do it, did ya?\n";
+ response += "</body></html>\n\n";
+
+ return response;
+}
+
+Text
+NetServer::ErrorResponse()
+{
+ Text response =
+ "HTTP/1.0 501 Not Implemented\nServer: Generic NetServer 1.0\nMIME-Version: 1.0\nContent-type: text/html\n\n";
+
+ response += "<html><head><title>Generic NetServer 1.0</title></head>\n\n";
+ response += "<body bgcolor=\"black\" text=\"white\">\n";
+ response += "<h1>Generic NetServer 1.0</h1>\n";
+ response += "<p>Sorry charlie... I'm not a magician.\n";
+ response += "</body></html>\n\n";
+
+ return response;
+}
diff --git a/NetEx/NetServer.h b/NetEx/NetServer.h
new file mode 100644
index 0000000..d8996d7
--- /dev/null
+++ b/NetEx/NetServer.h
@@ -0,0 +1,65 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetServer.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Server Pump for HTTP Server
+*/
+
+
+#ifndef NetServer_h
+#define NetServer_h
+
+#include <windows.h>
+#include "NetAddr.h"
+#include "NetGram.h"
+#include "NetSock.h"
+#include "List.h"
+
+// +-------------------------------------------------------------------+
+
+class NetServer
+{
+public:
+ static const char* TYPENAME() { return "NetServer"; }
+
+ NetServer(WORD port, int poolsize=1);
+ virtual ~NetServer();
+
+ int operator == (const NetServer& l) const { return addr == l.addr; }
+
+ virtual void Shutdown();
+ virtual DWORD Listener();
+ virtual DWORD Reader(int index);
+
+ virtual Text ProcessRequest(Text request, const NetAddr& addr);
+ virtual Text DefaultResponse();
+ virtual Text ErrorResponse();
+
+ const NetAddr& GetAddress() const { return addr; }
+ int GetLastError() const { return err; }
+
+protected:
+ NetAddr addr;
+ NetSock sock;
+ NetAddr client_addr;
+
+ int poolsize;
+ HANDLE hreader;
+ HANDLE* pool;
+ NetSock** conn;
+ NetAddr* clients;
+ int err;
+ bool server_shutdown;
+
+ ThreadSync sync;
+};
+
+
+#endif NetServer_h \ No newline at end of file
diff --git a/NetEx/NetSock.cpp b/NetEx/NetSock.cpp
new file mode 100644
index 0000000..47c50d8
--- /dev/null
+++ b/NetEx/NetSock.cpp
@@ -0,0 +1,342 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetSock.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network (IP) Socket Wrapper Implementation
+*/
+
+
+// WINSOCK2.H MUST COME FIRST!!
+#include <winsock2.h>
+
+#include "MemDebug.h"
+#include "NetSock.h"
+#include "NetLayer.h"
+
+// +-------------------------------------------------------------------+
+
+/**
+ * Server-side socket constructor
+ */
+NetSock::NetSock(bool str)
+ : stream(str), closed(false), stat(0), current_timeout(9999)
+{
+ if (stream)
+ s = ::socket(AF_INET, SOCK_STREAM, 0);
+ else
+ s = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+}
+
+/**
+ * Server-side socket constructor
+ *
+ * PRIVATE: used only by the accept call to build a socket for
+ * a client connection to this server
+ */
+NetSock::NetSock(SOCKET sock, bool str)
+ : s(sock), stream(str), closed(false), stat(0), current_timeout(9999)
+{ }
+
+/**
+ * Client-side socket constructor
+ *
+ * Will connect to server at "addr"
+ */
+NetSock::NetSock(const NetAddr& addr, bool str)
+ : stream(str), closed(false), stat(0), current_timeout(9999)
+{
+ if (stream)
+ s = ::socket(AF_INET, SOCK_STREAM, 0);
+ else
+ s = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ connect(addr);
+}
+
+NetSock::~NetSock()
+{
+ close();
+}
+
+// +--------------------------------------------------------------------+
+
+int
+NetSock::bind(const NetAddr& addr)
+{
+ if (closed || s == INVALID_SOCKET) return INVALID_SOCKET;
+ return stat = ::bind(s, addr.GetSockAddr(), addr.GetSockAddrLength());
+}
+
+int
+NetSock::connect(const NetAddr& addr)
+{
+ if (closed || s == INVALID_SOCKET) return INVALID_SOCKET;
+ return stat = ::connect(s, addr.GetSockAddr(), addr.GetSockAddrLength());
+}
+
+int
+NetSock::listen(int max_connections)
+{
+ if (closed || s == INVALID_SOCKET) return INVALID_SOCKET;
+ return stat = ::listen(s, max_connections);
+}
+
+NetSock*
+NetSock::accept(NetAddr* addr)
+{
+ if (closed || s == INVALID_SOCKET) return 0;
+
+ SOCKET conn = INVALID_SOCKET;
+
+ if (addr) {
+ sockaddr a;
+ int asize = sizeof(a);
+
+ conn = ::accept(s, &a, &asize);
+
+ if (conn != INVALID_SOCKET && asize > 0) {
+ addr->SetSockAddr(&a, asize);
+ }
+ }
+ else {
+ conn = ::accept(s, 0, 0);
+ }
+
+ if (conn == INVALID_SOCKET)
+ return 0;
+
+ return new(__FILE__,__LINE__) NetSock(conn, stream);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+NetSock::available()
+{
+ if (closed || s == INVALID_SOCKET) return 0;
+
+ DWORD nbytes = 0;
+ if (::ioctlsocket(s, FIONREAD, &nbytes) == 0)
+ return (int) nbytes;
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+NetSock::send(Text msg)
+{
+ if (closed || s == INVALID_SOCKET) return INVALID_SOCKET;
+ return stat = ::send(s, msg.data(), msg.length(), 0);
+}
+
+Text
+NetSock::recv()
+{
+ if (closed || s == INVALID_SOCKET) return "";
+
+ static char rbuf[8192];
+ int rlen = -1;
+
+ if (stream) {
+ rlen = ::recv(s, rbuf, sizeof(rbuf), 0);
+ }
+ else {
+ rlen = ::recvfrom(s, rbuf, sizeof(rbuf), 0, 0, 0);
+ }
+
+ if (rlen < 0) {
+ stat = NetLayer::GetLastError();
+
+ switch (stat) {
+ case WSAENETDOWN:
+ case WSAENETRESET:
+ case WSAEINTR:
+ case WSAESHUTDOWN:
+ case WSAECONNABORTED:
+ case WSAECONNRESET:
+ case WSAETIMEDOUT:
+ close();
+ break;
+
+ case WSAEWOULDBLOCK:
+ stat = WSAEWOULDBLOCK;
+ break;
+ }
+
+ return Text();
+ }
+
+ else if (rlen == 0) {
+ return Text();
+ }
+
+ return Text(rbuf, rlen);
+}
+
+
+// +--------------------------------------------------------------------+
+
+int
+NetSock::sendto(Text msg, const NetAddr& dest)
+{
+ if (closed || s == INVALID_SOCKET) return INVALID_SOCKET;
+ return stat = ::sendto(s, msg.data(), msg.length(),
+ 0, dest.GetSockAddr(), dest.GetSockAddrLength());
+}
+
+Text
+NetSock::recvfrom(NetAddr* a)
+{
+ if (closed || s == INVALID_SOCKET) return "";
+
+ static char rbuf[4096];
+ int rlen = 0;
+
+ if (a) {
+ int addrlen = a->GetSockAddrLength();
+ rlen = ::recvfrom(s, rbuf, sizeof(rbuf), 0, a->GetSockAddr(), &addrlen);
+ a->InitFromSockAddr();
+ }
+ else {
+ rlen = ::recvfrom(s, rbuf, sizeof(rbuf), 0, 0, 0);
+ }
+
+ if (rlen < 0) {
+ stat = NetLayer::GetLastError();
+ return Text();
+ }
+
+ else if (rlen == 0) {
+ return Text();
+ }
+
+ return Text(rbuf, rlen);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+NetSock::select(SELECT_TYPE t, long seconds, long microseconds)
+{
+ if (closed || s == INVALID_SOCKET) return INVALID_SOCKET;
+
+ FD_SET fd;
+ ZeroMemory(&fd, sizeof(fd));
+
+ FD_SET(s, &fd);
+ TIMEVAL timeval = {seconds, microseconds};
+ TIMEVAL* timeout = &timeval;
+
+ if (t == SELECT_READ)
+ return stat = ::select(1, &fd, 0, 0, timeout);
+
+ else if (t == SELECT_WRITE)
+ return stat = ::select(1, 0, &fd, 0, timeout);
+
+ else if (t == (SELECT_READ|SELECT_WRITE))
+ return stat = ::select(1, &fd, &fd, 0, timeout);
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+NetSock::set_timeout(int msecs)
+{
+ if (closed || s == INVALID_SOCKET) return 0;
+ if (msecs == current_timeout) return 1;
+
+ // zero timeout means non-blocking
+ if (msecs == 0) {
+ u_long nonblocking = 1;
+ if (::ioctlsocket(s, FIONBIO, &nonblocking) == SOCKET_ERROR) {
+ stat = NetLayer::GetLastError();
+ return stat;
+ }
+ }
+
+ // non-zero timeout means blocking
+ else {
+ if (current_timeout == 0) {
+ u_long nonblocking = 0; // disable non-blocking
+ if (::ioctlsocket(s, FIONBIO, &nonblocking) == SOCKET_ERROR) {
+ stat = NetLayer::GetLastError();
+ return stat;
+ }
+ }
+
+ // max timeout means infinite wait
+ if (msecs >= NET_MAX_TIMEOUT) {
+ int maxto = 0;
+ ::setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*) &maxto, sizeof(maxto));
+ ::setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*) &maxto, sizeof(maxto));
+ }
+
+ // otherwise, set the timeout
+ else {
+ ::setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*) &msecs, sizeof(msecs));
+ ::setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*) &msecs, sizeof(msecs));
+ }
+ }
+
+ current_timeout = msecs;
+ return 1;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+NetSock::shutdown_input()
+{
+ if (closed || s == INVALID_SOCKET) return INVALID_SOCKET;
+ ::shutdown(s, SD_RECEIVE);
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+NetSock::shutdown_output()
+{
+ if (closed || s == INVALID_SOCKET) return INVALID_SOCKET;
+ ::shutdown(s, SD_SEND);
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+NetSock::close()
+{
+ if (s != INVALID_SOCKET && !closed) {
+ ::shutdown(s, SD_BOTH);
+ ::closesocket(s);
+
+ closed = true;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+NetSock::max_packet_size() const
+{
+ DWORD size = 0;
+ int len = sizeof(size);
+
+ ::getsockopt(s, SOL_SOCKET, 0x2003, (char*) &size, &len);
+
+ return size;
+} \ No newline at end of file
diff --git a/NetEx/NetSock.h b/NetEx/NetSock.h
new file mode 100644
index 0000000..826d80c
--- /dev/null
+++ b/NetEx/NetSock.h
@@ -0,0 +1,73 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: NetEx.lib
+ FILE: NetSock.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network (IP) Socket
+*/
+
+#ifndef NetSock_h
+#define NetSock_h
+
+#include <windows.h>
+#include "NetAddr.h"
+#include "Text.h"
+
+// +-------------------------------------------------------------------+
+
+#define NET_MAX_TIMEOUT 1e9
+
+class NetSock
+{
+public:
+ static const char* TYPENAME() { return "NetSock"; }
+
+ enum SELECT_TYPE {
+ SELECT_READ = 1,
+ SELECT_WRITE = 2
+ };
+
+ NetSock(bool stream=false);
+ NetSock(const NetAddr& addr, bool stream=false);
+ ~NetSock();
+
+ int bind(const NetAddr& addr);
+ int connect(const NetAddr& addr);
+ int listen(int max_connections=5);
+ NetSock* accept(NetAddr* addr=0);
+ int available();
+ int send(Text msg);
+ Text recv();
+ int sendto(Text msg, const NetAddr& dest);
+ Text recvfrom(NetAddr* a=0);
+ int select(SELECT_TYPE t=SELECT_READ,
+ long seconds=0, long microseconds=0);
+
+ int shutdown_input();
+ int shutdown_output();
+ int set_timeout(int msecs);
+ int close();
+
+ DWORD max_packet_size() const;
+ bool is_stream() const { return stream; }
+ bool is_closed() const { return closed; }
+ int status() const { return stat; }
+
+private:
+ NetSock(SOCKET s, bool stream);
+
+ SOCKET s;
+ bool stream;
+ bool closed;
+ int stat;
+ int current_timeout;
+};
+
+
+#endif NetSock_h \ No newline at end of file
diff --git a/Opcode/Demo/IceCharacter.dll b/Opcode/Demo/IceCharacter.dll
new file mode 100644
index 0000000..560222f
--- /dev/null
+++ b/Opcode/Demo/IceCharacter.dll
Binary files differ
diff --git a/Opcode/Demo/IceCore.dll b/Opcode/Demo/IceCore.dll
new file mode 100644
index 0000000..90d2f86
--- /dev/null
+++ b/Opcode/Demo/IceCore.dll
Binary files differ
diff --git a/Opcode/Demo/IceDX7Renderer.dll b/Opcode/Demo/IceDX7Renderer.dll
new file mode 100644
index 0000000..4ada015
--- /dev/null
+++ b/Opcode/Demo/IceDX7Renderer.dll
Binary files differ
diff --git a/Opcode/Demo/IceImageWork.dll b/Opcode/Demo/IceImageWork.dll
new file mode 100644
index 0000000..35fd638
--- /dev/null
+++ b/Opcode/Demo/IceImageWork.dll
Binary files differ
diff --git a/Opcode/Demo/IceMaths.dll b/Opcode/Demo/IceMaths.dll
new file mode 100644
index 0000000..c2a454e
--- /dev/null
+++ b/Opcode/Demo/IceMaths.dll
Binary files differ
diff --git a/Opcode/Demo/IceRenderManager.dll b/Opcode/Demo/IceRenderManager.dll
new file mode 100644
index 0000000..01b4ba1
--- /dev/null
+++ b/Opcode/Demo/IceRenderManager.dll
Binary files differ
diff --git a/Opcode/Demo/IceRenderer.dll b/Opcode/Demo/IceRenderer.dll
new file mode 100644
index 0000000..a96bd98
--- /dev/null
+++ b/Opcode/Demo/IceRenderer.dll
Binary files differ
diff --git a/Opcode/Demo/IceTerrain.dll b/Opcode/Demo/IceTerrain.dll
new file mode 100644
index 0000000..8a9cb94
--- /dev/null
+++ b/Opcode/Demo/IceTerrain.dll
Binary files differ
diff --git a/Opcode/Demo/Meshmerizer.dll b/Opcode/Demo/Meshmerizer.dll
new file mode 100644
index 0000000..ed5943e
--- /dev/null
+++ b/Opcode/Demo/Meshmerizer.dll
Binary files differ
diff --git a/Opcode/Demo/Opcode.dll b/Opcode/Demo/Opcode.dll
new file mode 100644
index 0000000..f6079db
--- /dev/null
+++ b/Opcode/Demo/Opcode.dll
Binary files differ
diff --git a/Opcode/Demo/Opcode.exe b/Opcode/Demo/Opcode.exe
new file mode 100644
index 0000000..473d1a4
--- /dev/null
+++ b/Opcode/Demo/Opcode.exe
Binary files differ
diff --git a/Opcode/Demo/Rapid.dll b/Opcode/Demo/Rapid.dll
new file mode 100644
index 0000000..84dc41a
--- /dev/null
+++ b/Opcode/Demo/Rapid.dll
Binary files differ
diff --git a/Opcode/Demo/Readme.txt b/Opcode/Demo/Readme.txt
new file mode 100644
index 0000000..d9e241a
--- /dev/null
+++ b/Opcode/Demo/Readme.txt
@@ -0,0 +1,142 @@
+
+ ======================================
+ OPCODE (OPtimized COllision DEtection)
+ ======================================
+
+ Demo version 1.3b
+
+ This demo compares RAPID 2.01 vs OPCODE 1.0.
+
+ Information about RAPID can be found there:
+ http://www.cs.unc.edu/~geom/OBB/OBBT.html
+
+ Information about OPCODE can be found there:
+ http://www.codercorner.com/Opcode.htm
+
+ -----
+
+ I tried to be as fair as possible, and selected several scenes
+ where both RAPID and OPCODE show their forces. RAPID is usually
+ faster in close-proximity scenarii, especially when one object
+ is totally surrounded by another. OPCODE is usually faster when
+ objects deeply overlap. On my machine (Celeron 500Mhz), I have
+ found OPCODE to be faster overall. See for yourself.
+
+ By the way, don't forget OPCODE's primary goal was *memory*, not
+ speed. Considering this, I'm quite pleased with the results!
+
+ -----
+
+ Scenes have been exported with Flexporter:
+ http://www.codercorner.com/Flexporter.htm
+
+ If you don't like them, you can export your own test scenes from
+ MAX - just ensure there are only 2 meshes in them.
+
+ -----
+
+ How to proceed:
+ 1) Run Opcode.exe
+ 2) Drag&drop a ZCB file on the window
+ Messages such as "Chunk MOVE not found" are normal.
+ 3) Play!
+
+ -----
+
+ OPCODE-related keys:
+
+ 1 Toggle contact mode
+ - All contacts: report all colliding triangles
+ - First contact: report first contact only (a simple yes/no
+ answer to the overlap question)
+
+ 2 Toggle BV-BV tests
+ - full tests: standard SAT (15 separating axes)
+ - no class III: SAT-lite (6 separating axes)
+
+ 3 Toggle Prim-BV tests
+ - full tests: standard SAT (15 separating axes)
+ - no class III: SAT-lite (6 separating axes)
+
+ 4 Toggle leaf nodes
+ - discarded: use N-1 nodes only for a complete tree
+ - kept: standard complete tree with 2*N-1 nodes
+
+ 5 Toggle compression
+ - enabled: use quantized trees
+ - disabled: use normal trees
+
+ 6 Toggle temporal coherence (only for first contact mode)
+ - enabled: test the previous pair of colliding triangles
+ before everything else.
+ - disabled: well, do not....
+
+ -----
+
+ NB: in order to switch from one feature to another quickly, I create
+ 4 trees for each mesh: normal, no-leaf, quantized, quantized no-leaf.
+
+ i.e. 4 OPCODE models + 1 RAPID model / mesh, which explains why the
+ building phase is not very fast... Especially in the last scene, it
+ takes a while - please wait!
+
+ -----
+
+ Generic keys to play with:
+
+ F1 Texture control
+ F2 Camera control
+ F3 Mesh control
+ F4 Material control
+ F5 Scene control
+ F6 Light control
+ F7 Helper control
+
+ To move the camera or a mesh, use the mouse. Should be intuitive.
+ In camera mode, press +/- to jump from one camera to another.
+ Actually +/- jump to "next current" or "previous current", where
+ "current" is a mesh, camera, texture, etc - depends on selected
+ control mode.
+
+ To test collision detection, useful keys are:
+ - S to spin a model automatically (on/off)
+ - P to pause any animation
+ - O (in pause mode) to play one frame only
+
+ Other keys shouldn't be useful there but anyway:
+ - F displays the framerate on/off (BTW, I do 2 collision queries/frame
+ and displays a lot of slow text, so don't pay too much attention to
+ the overall framerate)
+ - T toggles texturing
+ - G toggles gouraud
+ - W toggles wireframe
+ - R toggles the profiling
+ - I toggles general information
+ - B toggles bounding boxes
+
+ Special keys
+ - Y should subdivide a mesh with the Butterfly algorithm. Since it
+ implies rebuilding all collision trees, it may be quite slow.
+ - L should smooth a mesh (same remark)
+ - M should make a mesh manifold, in case ICE complains....
+ - U should unfold a mesh, so beware.
+
+ Otherwise, play with the menu *at your own risk*.
+
+
+ -----
+
+ In situations where RAPID is really faster (for example when a mesh
+ is surrounded by another one) try re-enabling leaf nodes....
+
+ -----
+
+ Pierre Terdiman
+ May 03, 2001
+
+ p.terdiman@wanadoo.fr
+ p.terdiman@codercorner.com
+
+ www.codercorner.com
+
+ \ No newline at end of file
diff --git a/Opcode/Demo/Test scenes/Opcode00_KnotKnot.zcb b/Opcode/Demo/Test scenes/Opcode00_KnotKnot.zcb
new file mode 100644
index 0000000..d7b52ee
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode00_KnotKnot.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode01_VenusRoom.zcb b/Opcode/Demo/Test scenes/Opcode01_VenusRoom.zcb
new file mode 100644
index 0000000..842add7
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode01_VenusRoom.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode02_TorusTorus.zcb b/Opcode/Demo/Test scenes/Opcode02_TorusTorus.zcb
new file mode 100644
index 0000000..f448050
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode02_TorusTorus.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode03_TeapotTeapot.zcb b/Opcode/Demo/Test scenes/Opcode03_TeapotTeapot.zcb
new file mode 100644
index 0000000..fffe753
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode03_TeapotTeapot.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode04_SphereCylinder.zcb b/Opcode/Demo/Test scenes/Opcode04_SphereCylinder.zcb
new file mode 100644
index 0000000..3948365
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode04_SphereCylinder.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode05_KnotKnot2.zcb b/Opcode/Demo/Test scenes/Opcode05_KnotKnot2.zcb
new file mode 100644
index 0000000..bae81d6
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode05_KnotKnot2.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode06_VenusVenus.zcb b/Opcode/Demo/Test scenes/Opcode06_VenusVenus.zcb
new file mode 100644
index 0000000..b421513
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode06_VenusVenus.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode07_VenusTorus.zcb b/Opcode/Demo/Test scenes/Opcode07_VenusTorus.zcb
new file mode 100644
index 0000000..37c8660
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode07_VenusTorus.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode08_BoxBox.zcb b/Opcode/Demo/Test scenes/Opcode08_BoxBox.zcb
new file mode 100644
index 0000000..9ebd29c
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode08_BoxBox.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode09_Stairs.zcb b/Opcode/Demo/Test scenes/Opcode09_Stairs.zcb
new file mode 100644
index 0000000..e4ea480
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode09_Stairs.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode10_Explora.zcb b/Opcode/Demo/Test scenes/Opcode10_Explora.zcb
new file mode 100644
index 0000000..b672aef
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode10_Explora.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode11_Gears.zcb b/Opcode/Demo/Test scenes/Opcode11_Gears.zcb
new file mode 100644
index 0000000..505ca79
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode11_Gears.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode12_Irion.zcb b/Opcode/Demo/Test scenes/Opcode12_Irion.zcb
new file mode 100644
index 0000000..b017080
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode12_Irion.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode13_Pen.zcb b/Opcode/Demo/Test scenes/Opcode13_Pen.zcb
new file mode 100644
index 0000000..55159d1
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode13_Pen.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode14_Shad.zcb b/Opcode/Demo/Test scenes/Opcode14_Shad.zcb
new file mode 100644
index 0000000..623fe67
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode14_Shad.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode15_Chevy.zcb b/Opcode/Demo/Test scenes/Opcode15_Chevy.zcb
new file mode 100644
index 0000000..4fce879
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode15_Chevy.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode16_Char.zcb b/Opcode/Demo/Test scenes/Opcode16_Char.zcb
new file mode 100644
index 0000000..cc44da6
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode16_Char.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode17_PCP_Big.zcb b/Opcode/Demo/Test scenes/Opcode17_PCP_Big.zcb
new file mode 100644
index 0000000..f6b7eca
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode17_PCP_Big.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode17_PCP_Small.zcb b/Opcode/Demo/Test scenes/Opcode17_PCP_Small.zcb
new file mode 100644
index 0000000..068fb7d
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode17_PCP_Small.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/Opcode18_Skewed.zcb b/Opcode/Demo/Test scenes/Opcode18_Skewed.zcb
new file mode 100644
index 0000000..0203fe6
--- /dev/null
+++ b/Opcode/Demo/Test scenes/Opcode18_Skewed.zcb
Binary files differ
diff --git a/Opcode/Demo/Test scenes/ice.log b/Opcode/Demo/Test scenes/ice.log
new file mode 100644
index 0000000..4274454
--- /dev/null
+++ b/Opcode/Demo/Test scenes/ice.log
@@ -0,0 +1,192 @@
+// Opening Ice Core
+
+Registering dynamic identifier 1 (Out of memory)
+Registering dynamic identifier 2 (File not found)
+Registering dynamic identifier 3 (Invalid callback)
+Registering dynamic identifier 4 (Cloning process has failed)
+Registering dynamic identifier 5 (Too many unread messages. Kernel has automatically disabled messages.)
+Registering dynamic identifier 6 (Shared data block)
+Registering dynamic identifier 7 (Kernel has sent a self-destruction order)
+Registering dynamic identifier 8 (Kernel is corrupted)
+Registering dynamic identifier 9 (Kernel contains lost cells)
+Registering dynamic identifier 10 (A cell has been deleted while still referenced)
+Registering dynamic identifier 11 (Provided reference is now invalid)
+Registering dynamic identifier 12 (Provided cell has been modified by the kernel)
+Registering dynamic identifier 13 (Provided field has been modified)
+Registering dynamic identifier 14 (Fields are not compatible)
+Registering dynamic identifier 15 (Custom list)
+Unknown processor type.
+FPU detected.
+MMX detected.
+RDTSC supported.
+CMOV supported.
+FCOMI supported.
+Registering API: IceCore
+// Opening Ice Maths
+
+Registering API: IceMaths
+// Opening Ice Meshmerizer
+
+Registering dynamic identifier 16 (Only works with manifold meshes)
+// Opening Ice Character
+
+Registering dynamic identifier 17 (An invalid motion creation structure has been used)
+Registering dynamic identifier 18 (An invalid number of bones has been specified)
+Registering dynamic identifier 19 (An invalid CSID has been specified)
+Registering dynamic identifier 20 (A CSID has been multiply defined)
+// Opening Ice ImageWork
+
+// Opening Ice Renderer
+
+Registering dynamic identifier 21 (Texture size exceeds device caps)
+Registering dynamic identifier 22 (An unsupported render state has been used)
+// Opening Ice DX7 Renderer
+
+Registering dynamic identifier 23 (An unsupported render state has been used)
+Registering dynamic identifier 24 (Unable to find valid device)
+Registering dynamic identifier 25 (Unable to find valid video mode)
+Registering dynamic identifier 26 (A DirectDraw method has failed)
+Registering dynamic identifier 27 (Unable to query interface)
+Registering dynamic identifier 28 (Unable to set cooperative level)
+Registering dynamic identifier 29 (Unable to set display mode)
+Registering dynamic identifier 30 (Unable to create surface)
+Registering dynamic identifier 31 (Unable to create clipper)
+Registering dynamic identifier 32 (Can't get surface)
+Registering dynamic identifier 33 (Unable to create device)
+Registering dynamic identifier 34 (Enumeration has failed)
+Registering dynamic identifier 35 (Unable to find a valid ZBuffer)
+// Opening Ice Z-Collide
+
+// Opening OPCODE
+
+// Opening Ice Terrain
+
+// Opening Ice Render Manager
+
+Registering dynamic identifier 36 (RM Scene)
+Registering dynamic identifier 37 (RM Camera)
+Registering dynamic identifier 38 (RM Controller)
+Registering dynamic identifier 39 (RM Light)
+Registering dynamic identifier 40 (RM Material)
+Registering dynamic identifier 41 (RM Mesh)
+Registering dynamic identifier 42 (RM Shape)
+Registering dynamic identifier 43 (RM Texture)
+Registering dynamic identifier 44 (RM Helper)
+Registering dynamic identifier 45 (RM Skin)
+Registering dynamic identifier 46 (RM Skeleton)
+Registering dynamic identifier 47 (RM Cloth)
+Registering dynamic identifier 48 (RM ClothSupport)
+Registering dynamic identifier 49 (RM Water)
+Registering dynamic identifier 50 (RM Terrain)
+Registering dynamic identifier 51 (RM Portal)
+Registering dynamic identifier 52 (RM Particle System)
+Registering dynamic identifier 53 (Invalid ZCB file)
+Registering dynamic identifier 54 (Translucency code must be recomputed)
+Registering dynamic identifier 55 (ChangeShowFPS)
+Registering dynamic identifier 56 (ChangeShowInfo)
+Registering dynamic identifier 57 (ChangeProfiling)
+Registering dynamic identifier 58 (ChangeShowCoords)
+Registering dynamic identifier 59 (Dump)
+Registering dynamic identifier 60 (ChangePause)
+Registering dynamic identifier 61 (ChangeOneFrame)
+Registering dynamic identifier 62 (SetTextureControl)
+Registering dynamic identifier 63 (SetCameraControl)
+Registering dynamic identifier 64 (SetMeshControl)
+Registering dynamic identifier 65 (SetMaterialControl)
+Registering dynamic identifier 66 (SetSceneControl)
+Registering dynamic identifier 67 (SetLightControl)
+Registering dynamic identifier 68 (SetHelperControl)
+Registering dynamic identifier 69 (SetShapeControl)
+Registering dynamic identifier 70 (ChangeBBoxMode)
+Registering dynamic identifier 71 (ChangeTexturing)
+Registering dynamic identifier 72 (ChangeAntialiasing)
+Registering dynamic identifier 73 (ChangeFillMode)
+Registering dynamic identifier 74 (ChangeShading)
+Registering dynamic identifier 75 (ChangeAutoNormalize)
+Registering dynamic identifier 76 (ChangeComputeNormals)
+Registering dynamic identifier 77 (MakeManifold)
+Registering dynamic identifier 78 (Unfold)
+Registering dynamic identifier 79 (ChangeAutoSpin)
+Registering dynamic identifier 80 (Butterfly)
+Registering dynamic identifier 81 (LockedSmooth)
+Registering dynamic identifier 82 (DeleteCurrent)
+Registering dynamic identifier 83 (NextCurrent)
+Registering dynamic identifier 84 (PrevCurrent)
+Registering dynamic identifier 85 (UserIncrease)
+Registering dynamic identifier 86 (UserDecrease)
+Registering dynamic identifier 87 (UserIncrease2)
+Registering dynamic identifier 88 (UserDecrease2)
+Registering dynamic identifier 89 (ChangeHidden)
+Registering dynamic identifier 90 (ChangeScreenQuad)
+Registering dynamic identifier 91 (ChangeFirstContact)
+Registering dynamic identifier 92 (ChangeFullBoxBoxTest)
+Registering dynamic identifier 93 (ChangeFullPrimBoxTest)
+Registering dynamic identifier 94 (ChangeNoLeaf)
+Registering dynamic identifier 95 (ChangeQuantizedTree)
+Registering dynamic identifier 96 (ChangeTemporalCoherence)
+Renderer: enumerating drivers...
+Driver name: display
+Driver description: Primary Display Driver
+Selecting TnL HAL device...
+ZBuffer depth: 32
+Stencil depth: 8
+3D Card identifier: VID = 4318, DID = 835, Rev = 161 (unknown card)
+Importing 3 cameras...
+Importing 1 lights...
+New point light..Type 0, TDist: -1.000000
+Importing 2 materials...
+Importing 2 meshes...
+Importing 1 shapes...
+Vertex cloud reducer: 288 (before) => 288 (after)
+Surface optimization succeeded.
+MeshBuilder: reduced 288 to 288 vertices (0.0%)
+Creating vertex buffer (XYZ NORMAL WRITEONLY - 6912 bytes)
+Optimizing vertex buffer...
+Vertex cloud reducer: 288 (before) => 288 (after)
+Surface optimization succeeded.
+MeshBuilder: reduced 288 to 288 vertices (0.0%)
+Creating vertex buffer (XYZ NORMAL WRITEONLY - 6912 bytes)
+Optimizing vertex buffer...
+Original tree: 1151 nodes, depth 13
+AABB Collision tree: 1151 nodes, 32228 bytes - Alignment: 8
+Original tree: 1151 nodes, depth 13
+AABB quantized tree: 575 nodes, 18400 bytes - Alignment: 16
+Original tree: 1151 nodes, depth 13
+AABB quantized tree: 1151 nodes, 18416 bytes - Alignment: 16
+Original tree: 1151 nodes, depth 13
+AABB quantized no-leaf tree: 575 nodes, 11500 bytes - Alignment: 8
+Original tree: 1151 nodes, depth 12
+AABB Collision tree: 1151 nodes, 32228 bytes - Alignment: 8192
+Original tree: 1151 nodes, depth 12
+AABB quantized tree: 575 nodes, 18400 bytes - Alignment: 16
+Original tree: 1151 nodes, depth 12
+AABB quantized tree: 1151 nodes, 18416 bytes - Alignment: 8
+Original tree: 1151 nodes, depth 12
+AABB quantized no-leaf tree: 575 nodes, 11500 bytes - Alignment: 16
+// Closing Ice Render Manager
+
+// Closing Ice Terrain
+
+// Closing OPCODE
+
+// Closing Ice Z-Collide
+
+// Closing Ice DX7 Renderer
+
+// Closing Ice Renderer
+
+// Closing Ice ImageWork
+
+// Closing Ice Character
+
+// Closing Ice Meshmerizer
+
+// Closing Ice Maths
+
+// Closing Ice Core
+
+0 entries left in kernel (max = 31), 1228 bytes used by ref-tracking.
+96 registered dynamic identifiers.
+28 flushed messages.
+0 flushed errors.
+Trash buffer size: 2928 bytes
diff --git a/Opcode/Demo/ZCollide.dll b/Opcode/Demo/ZCollide.dll
new file mode 100644
index 0000000..c3e261f
--- /dev/null
+++ b/Opcode/Demo/ZCollide.dll
Binary files differ
diff --git a/Opcode/Demo/ZLib.dll b/Opcode/Demo/ZLib.dll
new file mode 100644
index 0000000..3e3fc04
--- /dev/null
+++ b/Opcode/Demo/ZLib.dll
Binary files differ
diff --git a/Opcode/Demo/ice.ini b/Opcode/Demo/ice.ini
new file mode 100644
index 0000000..58afcc1
--- /dev/null
+++ b/Opcode/Demo/ice.ini
@@ -0,0 +1,28 @@
+// ICE - (C) 2000 Pierre Terdiman
+// Visit: www.codercorner.com
+
+// OPCODE (OPtimized COllision DEtection)
+// Demo v1.3b
+
+// Global path
+//Path c:\
+
+// Resolution
+//Width 640
+//Height 480
+Width 1024
+Height 768
+
+// Fullscreen or window mode
+Fullscreen 0
+
+// Wants a stencil buffer ? (make sure you run the program in 32bits)
+Stencil 0
+
+// Framerate (Hz)
+DeltaT 60
+
+// FSAA
+Antialias 0
+
+// End of file \ No newline at end of file
diff --git a/Opcode/Demo/libbz2.dll b/Opcode/Demo/libbz2.dll
new file mode 100644
index 0000000..4179f55
--- /dev/null
+++ b/Opcode/Demo/libbz2.dll
Binary files differ
diff --git a/Opcode/Doc/OpcodeUserManual.pdf b/Opcode/Doc/OpcodeUserManual.pdf
new file mode 100644
index 0000000..4ca3f12
--- /dev/null
+++ b/Opcode/Doc/OpcodeUserManual.pdf
Binary files differ
diff --git a/Opcode/Ice/IceAABB.cpp b/Opcode/Ice/IceAABB.cpp
new file mode 100644
index 0000000..149211c
--- /dev/null
+++ b/Opcode/Ice/IceAABB.cpp
@@ -0,0 +1,405 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains AABB-related code.
+ * \file IceAABB.cpp
+ * \author Pierre Terdiman
+ * \date January, 29, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * AABB class.
+ * \class AABB
+ * \author Pierre Terdiman
+ * \version 1.0
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the sum of two AABBs.
+ * \param aabb [in] the other AABB
+ * \return Self-Reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABB& AABB::Add(const AABB& aabb)
+{
+ // Compute new min & max values
+ IcePoint Min; GetMin(Min);
+ IcePoint Tmp; aabb.GetMin(Tmp);
+ Min.Min(Tmp);
+
+ IcePoint Max; GetMax(Max);
+ aabb.GetMax(Tmp);
+ Max.Max(Tmp);
+
+ // Update this
+ SetMinMax(Min, Max);
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Makes a cube from the AABB.
+ * \param cube [out] the cube AABB
+ * \return cube edge length
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float AABB::MakeCube(AABB& cube) const
+{
+ IcePoint Ext; GetExtents(Ext);
+ float Max = Ext.Max();
+
+ IcePoint Cnt; GetCenter(Cnt);
+ cube.SetCenterExtents(Cnt, IcePoint(Max, Max, Max));
+ return Max;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Makes a sphere from the AABB.
+ * \param sphere [out] sphere containing the AABB
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABB::MakeSphere(Sphere& sphere) const
+{
+ GetExtents(sphere.mCenter);
+ sphere.mRadius = sphere.mCenter.Magnitude() * 1.00001f; // To make sure sphere::Contains(*this) succeeds
+ GetCenter(sphere.mCenter);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks a box is inside another box.
+ * \param box [in] the other AABB
+ * \return true if current box is inside input box
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABB::IsInside(const AABB& box) const
+{
+ if(box.GetMin(0)>GetMin(0)) return false;
+ if(box.GetMin(1)>GetMin(1)) return false;
+ if(box.GetMin(2)>GetMin(2)) return false;
+ if(box.GetMax(0)<GetMax(0)) return false;
+ if(box.GetMax(1)<GetMax(1)) return false;
+ if(box.GetMax(2)<GetMax(2)) return false;
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the AABB planes.
+ * \param planes [out] 6 planes surrounding the box
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABB::ComputePlanes(Plane* planes) const
+{
+ // Checkings
+ if(!planes) return false;
+
+ IcePoint Center, Extents;
+ GetCenter(Center);
+ GetExtents(Extents);
+
+ // Writes normals
+ planes[0].n = IcePoint(1.0f, 0.0f, 0.0f);
+ planes[1].n = IcePoint(-1.0f, 0.0f, 0.0f);
+ planes[2].n = IcePoint(0.0f, 1.0f, 0.0f);
+ planes[3].n = IcePoint(0.0f, -1.0f, 0.0f);
+ planes[4].n = IcePoint(0.0f, 0.0f, 1.0f);
+ planes[5].n = IcePoint(0.0f, 0.0f, -1.0f);
+
+ // Compute a point on each plane
+ IcePoint p0 = IcePoint(Center.x+Extents.x, Center.y, Center.z);
+ IcePoint p1 = IcePoint(Center.x-Extents.x, Center.y, Center.z);
+ IcePoint p2 = IcePoint(Center.x, Center.y+Extents.y, Center.z);
+ IcePoint p3 = IcePoint(Center.x, Center.y-Extents.y, Center.z);
+ IcePoint p4 = IcePoint(Center.x, Center.y, Center.z+Extents.z);
+ IcePoint p5 = IcePoint(Center.x, Center.y, Center.z-Extents.z);
+
+ // Compute d
+ planes[0].d = -(planes[0].n|p0);
+ planes[1].d = -(planes[1].n|p1);
+ planes[2].d = -(planes[2].n|p2);
+ planes[3].d = -(planes[3].n|p3);
+ planes[4].d = -(planes[4].n|p4);
+ planes[5].d = -(planes[5].n|p5);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the aabb points.
+ * \param pts [out] 8 box points
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABB::ComputePoints(IcePoint* pts) const
+{
+ // Checkings
+ if(!pts) return false;
+
+ // Get box corners
+ IcePoint min; GetMin(min);
+ IcePoint max; GetMax(max);
+
+ // 7+------+6 0 = ---
+ // /| /| 1 = +--
+ // / | / | 2 = ++-
+ // / 4+---/--+5 3 = -+-
+ // 3+------+2 / y z 4 = --+
+ // | / | / | / 5 = +-+
+ // |/ |/ |/ 6 = +++
+ // 0+------+1 *---x 7 = -++
+
+ // Generate 8 corners of the bbox
+ pts[0] = IcePoint(min.x, min.y, min.z);
+ pts[1] = IcePoint(max.x, min.y, min.z);
+ pts[2] = IcePoint(max.x, max.y, min.z);
+ pts[3] = IcePoint(min.x, max.y, min.z);
+ pts[4] = IcePoint(min.x, min.y, max.z);
+ pts[5] = IcePoint(max.x, min.y, max.z);
+ pts[6] = IcePoint(max.x, max.y, max.z);
+ pts[7] = IcePoint(min.x, max.y, max.z);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets vertex normals.
+ * \param pts [out] 8 box points
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const IcePoint* AABB::GetVertexNormals() const
+{
+ static float VertexNormals[] =
+ {
+ -INVSQRT3, -INVSQRT3, -INVSQRT3,
+ INVSQRT3, -INVSQRT3, -INVSQRT3,
+ INVSQRT3, INVSQRT3, -INVSQRT3,
+ -INVSQRT3, INVSQRT3, -INVSQRT3,
+ -INVSQRT3, -INVSQRT3, INVSQRT3,
+ INVSQRT3, -INVSQRT3, INVSQRT3,
+ INVSQRT3, INVSQRT3, INVSQRT3,
+ -INVSQRT3, INVSQRT3, INVSQRT3
+ };
+ return (const IcePoint*)VertexNormals;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns edges.
+ * \return 24 indices (12 edges) indexing the list returned by ComputePoints()
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const udword* AABB::GetEdges() const
+{
+ static udword Indices[] = {
+ 0, 1, 1, 2, 2, 3, 3, 0,
+ 7, 6, 6, 5, 5, 4, 4, 7,
+ 1, 5, 6, 2,
+ 3, 7, 4, 0
+ };
+ return Indices;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns edge normals.
+ * \return edge normals in local space
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const IcePoint* AABB::GetEdgeNormals() const
+{
+ static float EdgeNormals[] =
+ {
+ 0, -INVSQRT2, -INVSQRT2, // 0-1
+ INVSQRT2, 0, -INVSQRT2, // 1-2
+ 0, INVSQRT2, -INVSQRT2, // 2-3
+ -INVSQRT2, 0, -INVSQRT2, // 3-0
+
+ 0, INVSQRT2, INVSQRT2, // 7-6
+ INVSQRT2, 0, INVSQRT2, // 6-5
+ 0, -INVSQRT2, INVSQRT2, // 5-4
+ -INVSQRT2, 0, INVSQRT2, // 4-7
+
+ INVSQRT2, -INVSQRT2, 0, // 1-5
+ INVSQRT2, INVSQRT2, 0, // 6-2
+ -INVSQRT2, INVSQRT2, 0, // 3-7
+ -INVSQRT2, -INVSQRT2, 0 // 4-0
+ };
+ return (const IcePoint*)EdgeNormals;
+}
+
+// ===========================================================================
+// (C) 1996-98 Vienna University of Technology
+// ===========================================================================
+// NAME: bboxarea
+// TYPE: c++ code
+// PROJECT: Bounding Box Area
+// CONTENT: Computes area of 2D projection of 3D oriented bounding box
+// VERSION: 1.0
+// ===========================================================================
+// AUTHORS: ds Dieter Schmalstieg
+// ep Erik Pojar
+// ===========================================================================
+// HISTORY:
+//
+// 19-sep-99 15:23:03 ds last modification
+// 01-dec-98 15:23:03 ep created
+// ===========================================================================
+
+//----------------------------------------------------------------------------
+// SAMPLE CODE STARTS HERE
+//----------------------------------------------------------------------------
+
+// NOTE: This sample program requires OPEN INVENTOR!
+
+//indexlist: this table stores the 64 possible cases of classification of
+//the eyepoint with respect to the 6 defining planes of the bbox (2^6=64)
+//only 26 (3^3-1, where 1 is "inside" cube) of these cases are valid.
+//the first 6 numbers in each row are the indices of the bbox vertices that
+//form the outline of which we want to compute the area (counterclockwise
+//ordering), the 7th entry means the number of vertices in the outline.
+//there are 6 cases with a single face and and a 4-vertex outline, and
+//20 cases with 2 or 3 faces and a 6-vertex outline. a value of 0 indicates
+//an invalid case.
+
+
+// Original list was made of 7 items, I added an 8th element:
+// - to padd on a cache line
+// - to repeat the first entry to avoid modulos
+//
+// I also replaced original ints with sbytes.
+
+static const sbyte gIndexList[64][8] =
+{
+ {-1,-1,-1,-1,-1,-1,-1, 0}, // 0 inside
+ { 0, 4, 7, 3, 0,-1,-1, 4}, // 1 left
+ { 1, 2, 6, 5, 1,-1,-1, 4}, // 2 right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, // 3 -
+ { 0, 1, 5, 4, 0,-1,-1, 4}, // 4 bottom
+ { 0, 1, 5, 4, 7, 3, 0, 6}, // 5 bottom, left
+ { 0, 1, 2, 6, 5, 4, 0, 6}, // 6 bottom, right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, // 7 -
+ { 2, 3, 7, 6, 2,-1,-1, 4}, // 8 top
+ { 0, 4, 7, 6, 2, 3, 0, 6}, // 9 top, left
+ { 1, 2, 3, 7, 6, 5, 1, 6}, //10 top, right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //11 -
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //12 -
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //13 -
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //14 -
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //15 -
+ { 0, 3, 2, 1, 0,-1,-1, 4}, //16 front
+ { 0, 4, 7, 3, 2, 1, 0, 6}, //17 front, left
+ { 0, 3, 2, 6, 5, 1, 0, 6}, //18 front, right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //19 -
+ { 0, 3, 2, 1, 5, 4, 0, 6}, //20 front, bottom
+ { 1, 5, 4, 7, 3, 2, 1, 6}, //21 front, bottom, left
+ { 0, 3, 2, 6, 5, 4, 0, 6}, //22 front, bottom, right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //23 -
+ { 0, 3, 7, 6, 2, 1, 0, 6}, //24 front, top
+ { 0, 4, 7, 6, 2, 1, 0, 6}, //25 front, top, left
+ { 0, 3, 7, 6, 5, 1, 0, 6}, //26 front, top, right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //27 -
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //28 -
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //29 -
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //30 -
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //31 -
+ { 4, 5, 6, 7, 4,-1,-1, 4}, //32 back
+ { 0, 4, 5, 6, 7, 3, 0, 6}, //33 back, left
+ { 1, 2, 6, 7, 4, 5, 1, 6}, //34 back, right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //35 -
+ { 0, 1, 5, 6, 7, 4, 0, 6}, //36 back, bottom
+ { 0, 1, 5, 6, 7, 3, 0, 6}, //37 back, bottom, left
+ { 0, 1, 2, 6, 7, 4, 0, 6}, //38 back, bottom, right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //39 -
+ { 2, 3, 7, 4, 5, 6, 2, 6}, //40 back, top
+ { 0, 4, 5, 6, 2, 3, 0, 6}, //41 back, top, left
+ { 1, 2, 3, 7, 4, 5, 1, 6}, //42 back, top, right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //43 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //44 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //45 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //46 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //47 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //48 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //49 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //50 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //51 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //52 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //53 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //54 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //55 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //56 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //57 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //58 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //59 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //60 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //61 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //62 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0} //63 invalid
+};
+
+const sbyte* AABB::ComputeOutline(const IcePoint& local_eye, sdword& num) const
+{
+ // Get box corners
+ IcePoint min; GetMin(min);
+ IcePoint max; GetMax(max);
+
+ // Compute 6-bit code to classify eye with respect to the 6 defining planes of the bbox
+ int pos = ((local_eye.x < min.x) ? 1 : 0) // 1 = left
+ + ((local_eye.x > max.x) ? 2 : 0) // 2 = right
+ + ((local_eye.y < min.y) ? 4 : 0) // 4 = bottom
+ + ((local_eye.y > max.y) ? 8 : 0) // 8 = top
+ + ((local_eye.z < min.z) ? 16 : 0) // 16 = front
+ + ((local_eye.z > max.z) ? 32 : 0); // 32 = back
+
+ // Look up number of vertices in outline
+ num = (sdword)gIndexList[pos][7];
+ // Zero indicates invalid case
+ if(!num) return null;
+
+ return &gIndexList[pos][0];
+}
+
+// calculateBoxArea: computes the screen-projected 2D area of an oriented 3D bounding box
+
+//const IcePoint& eye, //eye point (in bbox object coordinates)
+//const AABB& box, //3d bbox
+//const Matrix4x4& mat, //free transformation for bbox
+//float width, float height, int& num)
+float AABB::ComputeBoxArea(const IcePoint& eye, const Matrix4x4& mat, float width, float height, sdword& num) const
+{
+ const sbyte* Outline = ComputeOutline(eye, num);
+ if(!Outline) return -1.0f;
+
+ // Compute box vertices
+ IcePoint vertexBox[8], dst[8];
+ ComputePoints(vertexBox);
+
+ // Transform all outline corners into 2D screen space
+ for(sdword i=0;i<num;i++)
+ {
+ HPoint Projected;
+ vertexBox[Outline[i]].ProjectToScreen(width, height, mat, Projected);
+ dst[i] = Projected;
+ }
+
+ float Sum = (dst[num-1][0] - dst[0][0]) * (dst[num-1][1] + dst[0][1]);
+
+ for(int i=0; i<num-1; i++)
+ Sum += (dst[i][0] - dst[i+1][0]) * (dst[i][1] + dst[i+1][1]);
+
+ return Sum * 0.5f; //return computed value corrected by 0.5
+}
diff --git a/Opcode/Ice/IceAABB.h b/Opcode/Ice/IceAABB.h
new file mode 100644
index 0000000..573629e
--- /dev/null
+++ b/Opcode/Ice/IceAABB.h
@@ -0,0 +1,505 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains AABB-related code. (axis-aligned bounding box)
+ * \file IceAABB.h
+ * \author Pierre Terdiman
+ * \date January, 13, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEAABB_H__
+#define __ICEAABB_H__
+
+ // Forward declarations
+ class Sphere;
+
+//! Declarations of type-independent methods (most of them implemented in the .cpp)
+#define AABB_COMMON_METHODS \
+ AABB& Add(const AABB& aabb); \
+ float MakeCube(AABB& cube) const; \
+ void MakeSphere(Sphere& sphere) const; \
+ const sbyte* ComputeOutline(const Point& local_eye, sdword& num) const; \
+ float ComputeBoxArea(const Point& eye, const Matrix4x4& mat, float width, float height, sdword& num) const; \
+ bool IsInside(const AABB& box) const; \
+ bool ComputePlanes(Plane* planes) const; \
+ bool ComputePoints(Point* pts) const; \
+ const Point* GetVertexNormals() const; \
+ const udword* GetEdges() const; \
+ const Point* GetEdgeNormals() const; \
+ inline_ BOOL ContainsPoint(const Point& p) const \
+ { \
+ if(p.x > GetMax(0) || p.x < GetMin(0)) return FALSE; \
+ if(p.y > GetMax(1) || p.y < GetMin(1)) return FALSE; \
+ if(p.z > GetMax(2) || p.z < GetMin(2)) return FALSE; \
+ return TRUE; \
+ }
+
+ enum AABBType
+ {
+ AABB_RENDER = 0, //!< AABB used for rendering. Not visible == not rendered.
+ AABB_UPDATE = 1, //!< AABB used for dynamic updates. Not visible == not updated.
+
+ AABB_FORCE_DWORD = 0x7fffffff,
+ };
+
+#ifdef USE_MINMAX
+
+ struct ICEMATHS_API ShadowAABB
+ {
+ Point mMin;
+ Point mMax;
+ };
+
+ class ICEMATHS_API AABB
+ {
+ public:
+ //! Constructor
+ inline_ AABB() {}
+ //! Destructor
+ inline_ ~AABB() {}
+
+ //! Type-independent methods
+ AABB_COMMON_METHODS;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups an AABB from min & max vectors.
+ * \param min [in] the min point
+ * \param max [in] the max point
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetMinMax(const Point& min, const Point& max) { mMin = min; mMax = max; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups an AABB from center & extents vectors.
+ * \param c [in] the center point
+ * \param e [in] the extents vector
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetCenterExtents(const Point& c, const Point& e) { mMin = c - e; mMax = c + e; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups an empty AABB.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetEmpty() { Point p(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); mMin = -p; mMax = p;}
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups a point AABB.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetPoint(const Point& pt) { mMin = mMax = pt; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the size of the AABB. The size is defined as the longest extent.
+ * \return the size of the AABB
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ float GetSize() const { Point e; GetExtents(e); return e.Max(); }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Extends the AABB.
+ * \param p [in] the next point
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void Extend(const Point& p)
+ {
+ if(p.x > mMax.x) mMax.x = p.x;
+ if(p.x < mMin.x) mMin.x = p.x;
+
+ if(p.y > mMax.y) mMax.y = p.y;
+ if(p.y < mMin.y) mMin.y = p.y;
+
+ if(p.z > mMax.z) mMax.z = p.z;
+ if(p.z < mMin.z) mMin.z = p.z;
+ }
+ // Data access
+
+ //! Get min point of the box
+ inline_ void GetMin(Point& min) const { min = mMin; }
+ //! Get max point of the box
+ inline_ void GetMax(Point& max) const { max = mMax; }
+
+ //! Get component of the box's min point along a given axis
+ inline_ float GetMin(udword axis) const { return mMin[axis]; }
+ //! Get component of the box's max point along a given axis
+ inline_ float GetMax(udword axis) const { return mMax[axis]; }
+
+ //! Get box center
+ inline_ void GetCenter(Point& center) const { center = (mMax + mMin)*0.5f; }
+ //! Get box extents
+ inline_ void GetExtents(Point& extents) const { extents = (mMax - mMin)*0.5f; }
+
+ //! Get component of the box's center along a given axis
+ inline_ float GetCenter(udword axis) const { return (mMax[axis] + mMin[axis])*0.5f; }
+ //! Get component of the box's extents along a given axis
+ inline_ float GetExtents(udword axis) const { return (mMax[axis] - mMin[axis])*0.5f; }
+
+ //! Get box diagonal
+ inline_ void GetDiagonal(Point& diagonal) const { diagonal = mMax - mMin; }
+ inline_ float GetWidth() const { return mMax.x - mMin.x; }
+ inline_ float GetHeight() const { return mMax.y - mMin.y; }
+ inline_ float GetDepth() const { return mMax.z - mMin.z; }
+
+ //! Volume
+ inline_ float GetVolume() const { return GetWidth() * GetHeight() * GetDepth(); }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the intersection between two AABBs.
+ * \param a [in] the other AABB
+ * \return true on intersection
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL Intersect(const AABB& a) const
+ {
+ if(mMax.x < a.mMin.x
+ || a.mMax.x < mMin.x
+ || mMax.y < a.mMin.y
+ || a.mMax.y < mMin.y
+ || mMax.z < a.mMin.z
+ || a.mMax.z < mMin.z) return FALSE;
+
+ return TRUE;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the 1D-intersection between two AABBs, on a given axis.
+ * \param a [in] the other AABB
+ * \param axis [in] the axis (0, 1, 2)
+ * \return true on intersection
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL Intersect(const AABB& a, udword axis) const
+ {
+ if(mMax[axis] < a.mMin[axis] || a.mMax[axis] < mMin[axis]) return FALSE;
+ return TRUE;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Recomputes the AABB after an arbitrary transform by a 4x4 matrix.
+ * Original code by Charles Bloom on the GD-Algorithm list. (I slightly modified it)
+ * \param mtx [in] the transform matrix
+ * \param aabb [out] the transformed AABB [can be *this]
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void Rotate(const Matrix4x4& mtx, AABB& aabb) const
+ {
+ // The three edges transformed: you can efficiently transform an X-only vector
+ // by just getting the "X" column of the matrix
+ Point vx,vy,vz;
+ mtx.GetRow(0, vx); vx *= (mMax.x - mMin.x);
+ mtx.GetRow(1, vy); vy *= (mMax.y - mMin.y);
+ mtx.GetRow(2, vz); vz *= (mMax.z - mMin.z);
+
+ // Transform the min point
+ aabb.mMin = aabb.mMax = mMin * mtx;
+
+ // Take the transformed min & axes and find new extents
+ // Using CPU code in the right place is faster...
+ if(IS_NEGATIVE_FLOAT(vx.x)) aabb.mMin.x += vx.x; else aabb.mMax.x += vx.x;
+ if(IS_NEGATIVE_FLOAT(vx.y)) aabb.mMin.y += vx.y; else aabb.mMax.y += vx.y;
+ if(IS_NEGATIVE_FLOAT(vx.z)) aabb.mMin.z += vx.z; else aabb.mMax.z += vx.z;
+ if(IS_NEGATIVE_FLOAT(vy.x)) aabb.mMin.x += vy.x; else aabb.mMax.x += vy.x;
+ if(IS_NEGATIVE_FLOAT(vy.y)) aabb.mMin.y += vy.y; else aabb.mMax.y += vy.y;
+ if(IS_NEGATIVE_FLOAT(vy.z)) aabb.mMin.z += vy.z; else aabb.mMax.z += vy.z;
+ if(IS_NEGATIVE_FLOAT(vz.x)) aabb.mMin.x += vz.x; else aabb.mMax.x += vz.x;
+ if(IS_NEGATIVE_FLOAT(vz.y)) aabb.mMin.y += vz.y; else aabb.mMax.y += vz.y;
+ if(IS_NEGATIVE_FLOAT(vz.z)) aabb.mMin.z += vz.z; else aabb.mMax.z += vz.z;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks the AABB is valid.
+ * \return true if the box is valid
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL IsValid() const
+ {
+ // Consistency condition for (Min, Max) boxes: min < max
+ if(mMin.x > mMax.x) return FALSE;
+ if(mMin.y > mMax.y) return FALSE;
+ if(mMin.z > mMax.z) return FALSE;
+ return TRUE;
+ }
+
+ //! Operator for AABB *= float. Scales the extents, keeps same center.
+ inline_ AABB& operator*=(float s)
+ {
+ Point Center; GetCenter(Center);
+ Point Extents; GetExtents(Extents);
+ SetCenterExtents(Center, Extents * s);
+ return *this;
+ }
+
+ //! Operator for AABB /= float. Scales the extents, keeps same center.
+ inline_ AABB& operator/=(float s)
+ {
+ Point Center; GetCenter(Center);
+ Point Extents; GetExtents(Extents);
+ SetCenterExtents(Center, Extents / s);
+ return *this;
+ }
+
+ //! Operator for AABB += Point. Translates the box.
+ inline_ AABB& operator+=(const Point& trans)
+ {
+ mMin+=trans;
+ mMax+=trans;
+ return *this;
+ }
+ private:
+ Point mMin; //!< Min point
+ Point mMax; //!< Max point
+ };
+
+#else
+
+ class ICEMATHS_API AABB
+ {
+ public:
+ //! Constructor
+ inline_ AABB() {}
+ //! Destructor
+ inline_ ~AABB() {}
+
+ //! Type-independent methods
+ AABB_COMMON_METHODS;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups an AABB from min & max vectors.
+ * \param min [in] the min point
+ * \param max [in] the max point
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetMinMax(const Point& min, const Point& max) { mCenter = (max + min)*0.5f; mExtents = (max - min)*0.5f; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups an AABB from center & extents vectors.
+ * \param c [in] the center point
+ * \param e [in] the extents vector
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetCenterExtents(const Point& c, const Point& e) { mCenter = c; mExtents = e; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups an empty AABB.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetEmpty() { mCenter.Zero(); mExtents.Set(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT);}
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups a point AABB.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetPoint(const Point& pt) { mCenter = pt; mExtents.Zero(); }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the size of the AABB. The size is defined as the longest extent.
+ * \return the size of the AABB
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ float GetSize() const { return mExtents.Max(); }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Extends the AABB.
+ * \param p [in] the next point
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void Extend(const Point& p)
+ {
+ Point Max = mCenter + mExtents;
+ Point Min = mCenter - mExtents;
+
+ if(p.x > Max.x) Max.x = p.x;
+ if(p.x < Min.x) Min.x = p.x;
+
+ if(p.y > Max.y) Max.y = p.y;
+ if(p.y < Min.y) Min.y = p.y;
+
+ if(p.z > Max.z) Max.z = p.z;
+ if(p.z < Min.z) Min.z = p.z;
+
+ SetMinMax(Min, Max);
+ }
+ // Data access
+
+ //! Get min point of the box
+ inline_ void GetMin(Point& min) const { min = mCenter - mExtents; }
+ //! Get max point of the box
+ inline_ void GetMax(Point& max) const { max = mCenter + mExtents; }
+
+ //! Get component of the box's min point along a given axis
+ inline_ float GetMin(udword axis) const { return mCenter[axis] - mExtents[axis]; }
+ //! Get component of the box's max point along a given axis
+ inline_ float GetMax(udword axis) const { return mCenter[axis] + mExtents[axis]; }
+
+ //! Get box center
+ inline_ void GetCenter(Point& center) const { center = mCenter; }
+ //! Get box extents
+ inline_ void GetExtents(Point& extents) const { extents = mExtents; }
+
+ //! Get component of the box's center along a given axis
+ inline_ float GetCenter(udword axis) const { return mCenter[axis]; }
+ //! Get component of the box's extents along a given axis
+ inline_ float GetExtents(udword axis) const { return mExtents[axis]; }
+
+ //! Get box diagonal
+ inline_ void GetDiagonal(Point& diagonal) const { diagonal = mExtents * 2.0f; }
+ inline_ float GetWidth() const { return mExtents.x * 2.0f; }
+ inline_ float GetHeight() const { return mExtents.y * 2.0f; }
+ inline_ float GetDepth() const { return mExtents.z * 2.0f; }
+
+ //! Volume
+ inline_ float GetVolume() const { return mExtents.x * mExtents.y * mExtents.z * 8.0f; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the intersection between two AABBs.
+ * \param a [in] the other AABB
+ * \return true on intersection
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL Intersect(const AABB& a) const
+ {
+ float tx = mCenter.x - a.mCenter.x; float ex = a.mExtents.x + mExtents.x; if(AIR(tx) > IR(ex)) return FALSE;
+ float ty = mCenter.y - a.mCenter.y; float ey = a.mExtents.y + mExtents.y; if(AIR(ty) > IR(ey)) return FALSE;
+ float tz = mCenter.z - a.mCenter.z; float ez = a.mExtents.z + mExtents.z; if(AIR(tz) > IR(ez)) return FALSE;
+ return TRUE;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * The standard intersection method from Gamasutra. Just here to check its speed against the one above.
+ * \param a [in] the other AABB
+ * \return true on intersection
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ bool GomezIntersect(const AABB& a)
+ {
+ Point T = mCenter - a.mCenter; // Vector from A to B
+ return ((fabsf(T.x) <= (a.mExtents.x + mExtents.x))
+ && (fabsf(T.y) <= (a.mExtents.y + mExtents.y))
+ && (fabsf(T.z) <= (a.mExtents.z + mExtents.z)));
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the 1D-intersection between two AABBs, on a given axis.
+ * \param a [in] the other AABB
+ * \param axis [in] the axis (0, 1, 2)
+ * \return true on intersection
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL Intersect(const AABB& a, udword axis) const
+ {
+ float t = mCenter[axis] - a.mCenter[axis];
+ float e = a.mExtents[axis] + mExtents[axis];
+ if(AIR(t) > IR(e)) return FALSE;
+ return TRUE;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Recomputes the AABB after an arbitrary transform by a 4x4 matrix.
+ * \param mtx [in] the transform matrix
+ * \param aabb [out] the transformed AABB [can be *this]
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void Rotate(const Matrix4x4& mtx, AABB& aabb) const
+ {
+ // Compute new center
+ aabb.mCenter = mCenter * mtx;
+
+ // Compute new extents. FPU code & CPU code have been interleaved for improved performance.
+ Point Ex(mtx.m[0][0] * mExtents.x, mtx.m[0][1] * mExtents.x, mtx.m[0][2] * mExtents.x);
+ IR(Ex.x)&=0x7fffffff; IR(Ex.y)&=0x7fffffff; IR(Ex.z)&=0x7fffffff;
+
+ Point Ey(mtx.m[1][0] * mExtents.y, mtx.m[1][1] * mExtents.y, mtx.m[1][2] * mExtents.y);
+ IR(Ey.x)&=0x7fffffff; IR(Ey.y)&=0x7fffffff; IR(Ey.z)&=0x7fffffff;
+
+ Point Ez(mtx.m[2][0] * mExtents.z, mtx.m[2][1] * mExtents.z, mtx.m[2][2] * mExtents.z);
+ IR(Ez.x)&=0x7fffffff; IR(Ez.y)&=0x7fffffff; IR(Ez.z)&=0x7fffffff;
+
+ aabb.mExtents.x = Ex.x + Ey.x + Ez.x;
+ aabb.mExtents.y = Ex.y + Ey.y + Ez.y;
+ aabb.mExtents.z = Ex.z + Ey.z + Ez.z;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks the AABB is valid.
+ * \return true if the box is valid
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL IsValid() const
+ {
+ // Consistency condition for (Center, Extents) boxes: Extents >= 0
+ if(IS_NEGATIVE_FLOAT(mExtents.x)) return FALSE;
+ if(IS_NEGATIVE_FLOAT(mExtents.y)) return FALSE;
+ if(IS_NEGATIVE_FLOAT(mExtents.z)) return FALSE;
+ return TRUE;
+ }
+
+ //! Operator for AABB *= float. Scales the extents, keeps same center.
+ inline_ AABB& operator*=(float s) { mExtents*=s; return *this; }
+
+ //! Operator for AABB /= float. Scales the extents, keeps same center.
+ inline_ AABB& operator/=(float s) { mExtents/=s; return *this; }
+
+ //! Operator for AABB += Point. Translates the box.
+ inline_ AABB& operator+=(const Point& trans)
+ {
+ mCenter+=trans;
+ return *this;
+ }
+ private:
+ Point mCenter; //!< AABB Center
+ Point mExtents; //!< x, y and z extents
+ };
+
+#endif
+
+ inline_ void ComputeMinMax(const Point& p, Point& min, Point& max)
+ {
+ if(p.x > max.x) max.x = p.x;
+ if(p.x < min.x) min.x = p.x;
+
+ if(p.y > max.y) max.y = p.y;
+ if(p.y < min.y) min.y = p.y;
+
+ if(p.z > max.z) max.z = p.z;
+ if(p.z < min.z) min.z = p.z;
+ }
+
+ inline_ void ComputeAABB(AABB& aabb, const Point* list, udword nb_pts)
+ {
+ if(list)
+ {
+ Point Maxi(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT);
+ Point Mini(MAX_FLOAT, MAX_FLOAT, MAX_FLOAT);
+ while(nb_pts--)
+ {
+// _prefetch(list+1); // off by one ?
+ ComputeMinMax(*list++, Mini, Maxi);
+ }
+ aabb.SetMinMax(Mini, Maxi);
+ }
+ }
+
+#endif // __ICEAABB_H__
diff --git a/Opcode/Ice/IceAxes.h b/Opcode/Ice/IceAxes.h
new file mode 100644
index 0000000..39004a9
--- /dev/null
+++ b/Opcode/Ice/IceAxes.h
@@ -0,0 +1,54 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains axes definition.
+ * \file IceAxes.h
+ * \author Pierre Terdiman
+ * \date January, 29, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEAXES_H__
+#define __ICEAXES_H__
+
+ enum PointComponent
+ {
+ _X = 0,
+ _Y = 1,
+ _Z = 2,
+ _W = 3,
+
+ _FORCE_DWORD = 0x7fffffff
+ };
+
+ enum AxisOrder
+ {
+ AXES_XYZ = (_X)|(_Y<<2)|(_Z<<4),
+ AXES_XZY = (_X)|(_Z<<2)|(_Y<<4),
+ AXES_YXZ = (_Y)|(_X<<2)|(_Z<<4),
+ AXES_YZX = (_Y)|(_Z<<2)|(_X<<4),
+ AXES_ZXY = (_Z)|(_X<<2)|(_Y<<4),
+ AXES_ZYX = (_Z)|(_Y<<2)|(_X<<4),
+
+ AXES_FORCE_DWORD = 0x7fffffff
+ };
+
+ class ICEMATHS_API Axes
+ {
+ public:
+
+ inline_ Axes(AxisOrder order)
+ {
+ mAxis0 = (order ) & 3;
+ mAxis1 = (order>>2) & 3;
+ mAxis2 = (order>>4) & 3;
+ }
+ inline_ ~Axes() {}
+
+ udword mAxis0;
+ udword mAxis1;
+ udword mAxis2;
+ };
+
+#endif // __ICEAXES_H__
diff --git a/Opcode/Ice/IceBoundingSphere.h b/Opcode/Ice/IceBoundingSphere.h
new file mode 100644
index 0000000..c66524c
--- /dev/null
+++ b/Opcode/Ice/IceBoundingSphere.h
@@ -0,0 +1,142 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code to compute the minimal bounding sphere.
+ * \file IceBoundingSphere.h
+ * \author Pierre Terdiman
+ * \date January, 29, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEBOUNDINGSPHERE_H__
+#define __ICEBOUNDINGSPHERE_H__
+
+ enum BSphereMethod
+ {
+ BS_NONE,
+ BS_GEMS,
+ BS_MINIBALL,
+
+ BS_FORCE_DWORD = 0x7fffffff
+ };
+
+ class ICEMATHS_API Sphere
+ {
+ public:
+ //! Constructor
+ inline_ Sphere() {}
+ //! Constructor
+ inline_ Sphere(const Point& center, float radius) : mCenter(center), mRadius(radius) {}
+ //! Constructor
+ Sphere(udword nb_verts, const Point* verts);
+ //! Copy constructor
+ inline_ Sphere(const Sphere& sphere) : mCenter(sphere.mCenter), mRadius(sphere.mRadius) {}
+ //! Destructor
+ inline_ ~Sphere() {}
+
+ BSphereMethod Compute(udword nb_verts, const Point* verts);
+ bool FastCompute(udword nb_verts, const Point* verts);
+
+ // Access methods
+ inline_ const Point& GetCenter() const { return mCenter; }
+ inline_ float GetRadius() const { return mRadius; }
+
+ inline_ const Point& Center() const { return mCenter; }
+ inline_ float Radius() const { return mRadius; }
+
+ inline_ Sphere& Set(const Point& center, float radius) { mCenter = center; mRadius = radius; return *this; }
+ inline_ Sphere& SetCenter(const Point& center) { mCenter = center; return *this; }
+ inline_ Sphere& SetRadius(float radius) { mRadius = radius; return *this; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Tests if a point is contained within the sphere.
+ * \param p [in] the point to test
+ * \return true if inside the sphere
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ bool Contains(const Point& p) const
+ {
+ return mCenter.SquareDistance(p) <= mRadius*mRadius;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Tests if a sphere is contained within the sphere.
+ * \param sphere [in] the sphere to test
+ * \return true if inside the sphere
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ bool Contains(const Sphere& sphere) const
+ {
+ // If our radius is the smallest, we can't possibly contain the other sphere
+ if(mRadius < sphere.mRadius) return false;
+ // So r is always positive or null now
+ float r = mRadius - sphere.mRadius;
+ return mCenter.SquareDistance(sphere.mCenter) <= r*r;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Tests if a box is contained within the sphere.
+ * \param aabb [in] the box to test
+ * \return true if inside the sphere
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL Contains(const AABB& aabb) const
+ {
+ // I assume if all 8 box vertices are inside the sphere, so does the whole box.
+ // Sounds ok but maybe there's a better way?
+ float R2 = mRadius * mRadius;
+#ifdef USE_MIN_MAX
+ const Point& Max = ((ShadowAABB&)&aabb).mMax;
+ const Point& Min = ((ShadowAABB&)&aabb).mMin;
+#else
+ Point Max; aabb.GetMax(Max);
+ Point Min; aabb.GetMin(Min);
+#endif
+ Point p;
+ p.x=Max.x; p.y=Max.y; p.z=Max.z; if(mCenter.SquareDistance(p)>=R2) return FALSE;
+ p.x=Min.x; if(mCenter.SquareDistance(p)>=R2) return FALSE;
+ p.x=Max.x; p.y=Min.y; if(mCenter.SquareDistance(p)>=R2) return FALSE;
+ p.x=Min.x; if(mCenter.SquareDistance(p)>=R2) return FALSE;
+ p.x=Max.x; p.y=Max.y; p.z=Min.z; if(mCenter.SquareDistance(p)>=R2) return FALSE;
+ p.x=Min.x; if(mCenter.SquareDistance(p)>=R2) return FALSE;
+ p.x=Max.x; p.y=Min.y; if(mCenter.SquareDistance(p)>=R2) return FALSE;
+ p.x=Min.x; if(mCenter.SquareDistance(p)>=R2) return FALSE;
+
+ return TRUE;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Tests if the sphere intersects another sphere
+ * \param sphere [in] the other sphere
+ * \return true if spheres overlap
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ bool Intersect(const Sphere& sphere) const
+ {
+ float r = mRadius + sphere.mRadius;
+ return mCenter.SquareDistance(sphere.mCenter) <= r*r;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks the sphere is valid.
+ * \return true if the box is valid
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL IsValid() const
+ {
+ // Consistency condition for spheres: Radius >= 0.0f
+ if(mRadius < 0.0f) return FALSE;
+ return TRUE;
+ }
+ public:
+ Point mCenter; //!< Sphere center
+ float mRadius; //!< Sphere radius
+ };
+
+#endif // __ICEBOUNDINGSPHERE_H__
diff --git a/Opcode/Ice/IceContainer.cpp b/Opcode/Ice/IceContainer.cpp
new file mode 100644
index 0000000..e2c42d1
--- /dev/null
+++ b/Opcode/Ice/IceContainer.cpp
@@ -0,0 +1,345 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a simple container class.
+ * \file IceContainer.cpp
+ * \author Pierre Terdiman
+ * \date February, 5, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a list of 32-bits values.
+ * Use this class when you need to store an unknown number of values. The list is automatically
+ * resized and can contains 32-bits entities (dwords or floats)
+ *
+ * \class Container
+ * \author Pierre Terdiman
+ * \version 1.0
+ * \date 08.15.98
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceCore;
+
+// Static members
+#ifdef CONTAINER_STATS
+udword Container::mNbContainers = 0;
+udword Container::mUsedRam = 0;
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor. No entries allocated there.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Container::Container() : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(2.0f)
+{
+#ifdef CONTAINER_STATS
+ mNbContainers++;
+ mUsedRam+=sizeof(Container);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor. Also allocates a given number of entries.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Container::Container(udword size, float growth_factor) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(growth_factor)
+{
+#ifdef CONTAINER_STATS
+ mNbContainers++;
+ mUsedRam+=sizeof(Container);
+#endif
+ SetSize(size);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Copy constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Container::Container(const Container& object) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(2.0f)
+{
+#ifdef CONTAINER_STATS
+ mNbContainers++;
+ mUsedRam+=sizeof(Container);
+#endif
+ *this = object;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor. Frees everything and leaves.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Container::~Container()
+{
+ Empty();
+#ifdef CONTAINER_STATS
+ mNbContainers--;
+ mUsedRam-=GetUsedRam();
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Clears the container. All stored values are deleted, and it frees used ram.
+ * \see Reset()
+ * \return Self-Reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Container& Container::Empty()
+{
+#ifdef CONTAINER_STATS
+ mUsedRam-=mMaxNbEntries*sizeof(udword);
+#endif
+ DELETEARRAY(mEntries);
+ mCurNbEntries = mMaxNbEntries = 0;
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resizes the container.
+ * \param needed [in] assume the container can be added at least "needed" values
+ * \return true if success.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Container::Resize(udword needed)
+{
+#ifdef CONTAINER_STATS
+ // Subtract previous amount of bytes
+ mUsedRam-=mMaxNbEntries*sizeof(udword);
+#endif
+
+ // Get more entries
+ mMaxNbEntries = mMaxNbEntries ? udword(float(mMaxNbEntries)*mGrowthFactor) : 2; // Default nb Entries = 2
+ if(mMaxNbEntries<mCurNbEntries + needed) mMaxNbEntries = mCurNbEntries + needed;
+
+ // Get some bytes for new entries
+ udword* NewEntries = new udword[mMaxNbEntries];
+ CHECKALLOC(NewEntries);
+
+#ifdef CONTAINER_STATS
+ // Add current amount of bytes
+ mUsedRam+=mMaxNbEntries*sizeof(udword);
+#endif
+
+ // Copy old data if needed
+ if(mCurNbEntries) CopyMemory(NewEntries, mEntries, mCurNbEntries*sizeof(udword));
+
+ // Delete old data
+ DELETEARRAY(mEntries);
+
+ // Assign new pointer
+ mEntries = NewEntries;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets the initial size of the container. If it already contains something, it's discarded.
+ * \param nb [in] Number of entries
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Container::SetSize(udword nb)
+{
+ // Make sure it's empty
+ Empty();
+
+ // Checkings
+ if(!nb) return false;
+
+ // Initialize for nb entries
+ mMaxNbEntries = nb;
+
+ // Get some bytes for new entries
+ mEntries = new udword[mMaxNbEntries];
+ CHECKALLOC(mEntries);
+
+#ifdef CONTAINER_STATS
+ // Add current amount of bytes
+ mUsedRam+=mMaxNbEntries*sizeof(udword);
+#endif
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the container and get rid of unused bytes.
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Container::Refit()
+{
+#ifdef CONTAINER_STATS
+ // Subtract previous amount of bytes
+ mUsedRam-=mMaxNbEntries*sizeof(udword);
+#endif
+
+ // Get just enough entries
+ mMaxNbEntries = mCurNbEntries;
+ if(!mMaxNbEntries) return false;
+
+ // Get just enough bytes
+ udword* NewEntries = new udword[mMaxNbEntries];
+ CHECKALLOC(NewEntries);
+
+#ifdef CONTAINER_STATS
+ // Add current amount of bytes
+ mUsedRam+=mMaxNbEntries*sizeof(udword);
+#endif
+
+ // Copy old data
+ CopyMemory(NewEntries, mEntries, mCurNbEntries*sizeof(udword));
+
+ // Delete old data
+ DELETEARRAY(mEntries);
+
+ // Assign new pointer
+ mEntries = NewEntries;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks whether the container already contains a given value.
+ * \param entry [in] the value to look for in the container
+ * \param location [out] a possible pointer to store the entry location
+ * \see Add(udword entry)
+ * \see Add(float entry)
+ * \see Empty()
+ * \return true if the value has been found in the container, else false.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Container::Contains(udword entry, udword* location) const
+{
+ // Look for the entry
+ for(udword i=0;i<mCurNbEntries;i++)
+ {
+ if(mEntries[i]==entry)
+ {
+ if(location) *location = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Deletes an entry. If the container contains such an entry, it's removed.
+ * \param entry [in] the value to delete.
+ * \return true if the value has been found in the container, else false.
+ * \warning This method is arbitrary slow (O(n)) and should be used carefully. Insertion order is not preserved.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Container::Delete(udword entry)
+{
+ // Look for the entry
+ for(udword i=0;i<mCurNbEntries;i++)
+ {
+ if(mEntries[i]==entry)
+ {
+ // Entry has been found at index i. The strategy is to copy the last current entry at index i, and decrement the current number of entries.
+ DeleteIndex(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Deletes an entry, preserving the insertion order. If the container contains such an entry, it's removed.
+ * \param entry [in] the value to delete.
+ * \return true if the value has been found in the container, else false.
+ * \warning This method is arbitrary slow (O(n)) and should be used carefully.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Container::DeleteKeepingOrder(udword entry)
+{
+ // Look for the entry
+ for(udword i=0;i<mCurNbEntries;i++)
+ {
+ if(mEntries[i]==entry)
+ {
+ // Entry has been found at index i.
+ // Shift entries to preserve order. You really should use a linked list instead.
+ mCurNbEntries--;
+ for(udword j=i;j<mCurNbEntries;j++)
+ {
+ mEntries[j] = mEntries[j+1];
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the next entry, starting from input one.
+ * \param entry [in/out] On input, the entry to look for. On output, the next entry
+ * \param find_mode [in] wrap/clamp
+ * \return Self-Reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Container& Container::FindNext(udword& entry, FindMode find_mode)
+{
+ udword Location;
+ if(Contains(entry, &Location))
+ {
+ Location++;
+ if(Location==mCurNbEntries) Location = find_mode==FIND_WRAP ? 0 : mCurNbEntries-1;
+ entry = mEntries[Location];
+ }
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the previous entry, starting from input one.
+ * \param entry [in/out] On input, the entry to look for. On output, the previous entry
+ * \param find_mode [in] wrap/clamp
+ * \return Self-Reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Container& Container::FindPrev(udword& entry, FindMode find_mode)
+{
+ udword Location;
+ if(Contains(entry, &Location))
+ {
+ Location--;
+ if(Location==0xffffffff) Location = find_mode==FIND_WRAP ? mCurNbEntries-1 : 0;
+ entry = mEntries[Location];
+ }
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the ram used by the container.
+ * \return the ram used in bytes.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword Container::GetUsedRam() const
+{
+ return sizeof(Container) + mMaxNbEntries * sizeof(udword);
+}
+
+void Container::operator=(const Container& object)
+{
+ SetSize(object.GetNbEntries());
+ CopyMemory(mEntries, object.GetEntries(), mMaxNbEntries*sizeof(udword));
+ mCurNbEntries = mMaxNbEntries;
+}
diff --git a/Opcode/Ice/IceContainer.h b/Opcode/Ice/IceContainer.h
new file mode 100644
index 0000000..9f06ada
--- /dev/null
+++ b/Opcode/Ice/IceContainer.h
@@ -0,0 +1,212 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a simple container class.
+ * \file IceContainer.h
+ * \author Pierre Terdiman
+ * \date February, 5, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICECONTAINER_H__
+#define __ICECONTAINER_H__
+
+ #define CONTAINER_STATS
+
+ enum FindMode
+ {
+ FIND_CLAMP,
+ FIND_WRAP,
+
+ FIND_FORCE_DWORD = 0x7fffffff
+ };
+
+ class ICECORE_API Container
+ {
+ public:
+ // Constructor / Destructor
+ Container();
+ Container(const Container& object);
+ Container(udword size, float growth_factor);
+ ~Container();
+ // Management
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * A O(1) method to add a value in the container. The container is automatically resized if needed.
+ * The method is inline, not the resize. The call overhead happens on resizes only, which is not a problem since the resizing operation
+ * costs a lot more than the call overhead...
+ *
+ * \param entry [in] a udword to store in the container
+ * \see Add(float entry)
+ * \see Empty()
+ * \see Contains(udword entry)
+ * \return Self-Reference
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ Container& Add(udword entry)
+ {
+ // Resize if needed
+ if(mCurNbEntries==mMaxNbEntries) Resize();
+
+ // Add new entry
+ mEntries[mCurNbEntries++] = entry;
+ return *this;
+ }
+
+ inline_ Container& Add(const udword* entries, udword nb)
+ {
+ // Resize if needed
+ if(mCurNbEntries+nb>mMaxNbEntries) Resize(nb);
+
+ // Add new entry
+ CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(udword));
+ mCurNbEntries+=nb;
+ return *this;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * A O(1) method to add a value in the container. The container is automatically resized if needed.
+ * The method is inline, not the resize. The call overhead happens on resizes only, which is not a problem since the resizing operation
+ * costs a lot more than the call overhead...
+ *
+ * \param entry [in] a float to store in the container
+ * \see Add(udword entry)
+ * \see Empty()
+ * \see Contains(udword entry)
+ * \return Self-Reference
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ Container& Add(float entry)
+ {
+ // Resize if needed
+ if(mCurNbEntries==mMaxNbEntries) Resize();
+
+ // Add new entry
+ mEntries[mCurNbEntries++] = IR(entry);
+ return *this;
+ }
+
+ inline_ Container& Add(const float* entries, udword nb)
+ {
+ // Resize if needed
+ if(mCurNbEntries+nb>mMaxNbEntries) Resize(nb);
+
+ // Add new entry
+ CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(float));
+ mCurNbEntries+=nb;
+ return *this;
+ }
+
+ //! Add unique [slow]
+ inline_ Container& AddUnique(udword entry)
+ {
+ if(!Contains(entry)) Add(entry);
+ return *this;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Clears the container. All stored values are deleted, and it frees used ram.
+ * \see Reset()
+ * \return Self-Reference
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Container& Empty();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Resets the container. Stored values are discarded but the buffer is kept so that further calls don't need resizing again.
+ * That's a kind of temporal coherence.
+ * \see Empty()
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void Reset()
+ {
+ // Avoid the write if possible
+ // ### CMOV
+ if(mCurNbEntries) mCurNbEntries = 0;
+ }
+
+ // HANDLE WITH CARE
+ inline_ void ForceSize(udword size)
+ {
+ mCurNbEntries = size;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Sets the initial size of the container. If it already contains something, it's discarded.
+ * \param nb [in] Number of entries
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool SetSize(udword nb);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Refits the container and get rid of unused bytes.
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool Refit();
+
+ // Checks whether the container already contains a given value.
+ bool Contains(udword entry, udword* location=null) const;
+ // Deletes an entry - doesn't preserve insertion order.
+ bool Delete(udword entry);
+ // Deletes an entry - does preserve insertion order.
+ bool DeleteKeepingOrder(udword entry);
+ //! Deletes the very last entry.
+ inline_ void DeleteLastEntry() { if(mCurNbEntries) mCurNbEntries--; }
+ //! Deletes the entry whose index is given
+ inline_ void DeleteIndex(udword index) { mEntries[index] = mEntries[--mCurNbEntries]; }
+
+ // Helpers
+ Container& FindNext(udword& entry, FindMode find_mode=FIND_CLAMP);
+ Container& FindPrev(udword& entry, FindMode find_mode=FIND_CLAMP);
+ // Data access.
+ inline_ udword GetNbEntries() const { return mCurNbEntries; } //!< Returns the current number of entries.
+ inline_ udword GetEntry(udword i) const { return mEntries[i]; } //!< Returns ith entry
+ inline_ udword* GetEntries() const { return mEntries; } //!< Returns the list of entries.
+
+ inline_ udword GetFirst() const { return mEntries[0]; }
+ inline_ udword GetLast() const { return mEntries[mCurNbEntries-1]; }
+
+ // Growth control
+ inline_ float GetGrowthFactor() const { return mGrowthFactor; } //!< Returns the growth factor
+ inline_ void SetGrowthFactor(float growth) { mGrowthFactor = growth; } //!< Sets the growth factor
+ inline_ bool IsFull() const { return mCurNbEntries==mMaxNbEntries; } //!< Checks the container is full
+ inline_ BOOL IsNotEmpty() const { return mCurNbEntries; } //!< Checks the container is empty
+
+ //! Read-access as an array
+ inline_ udword operator[](udword i) const { ASSERT(i>=0 && i<mCurNbEntries); return mEntries[i]; }
+ //! Write-access as an array
+ inline_ udword& operator[](udword i) { ASSERT(i>=0 && i<mCurNbEntries); return mEntries[i]; }
+
+ // Stats
+ udword GetUsedRam() const;
+
+ //! Operator for "Container A = Container B"
+ void operator = (const Container& object);
+
+#ifdef CONTAINER_STATS
+ inline_ udword GetNbContainers() const { return mNbContainers; }
+ inline_ udword GetTotalBytes() const { return mUsedRam; }
+ private:
+
+ static udword mNbContainers; //!< Number of containers around
+ static udword mUsedRam; //!< Amount of bytes used by containers in the system
+#endif
+ private:
+ // Resizing
+ bool Resize(udword needed=1);
+ // Data
+ udword mMaxNbEntries; //!< Maximum possible number of entries
+ udword mCurNbEntries; //!< Current number of entries
+ udword* mEntries; //!< List of entries
+ float mGrowthFactor; //!< Resize: new number of entries = old number * mGrowthFactor
+ };
+
+#endif // __ICECONTAINER_H__
diff --git a/Opcode/Ice/IceFPU.h b/Opcode/Ice/IceFPU.h
new file mode 100644
index 0000000..9e57960
--- /dev/null
+++ b/Opcode/Ice/IceFPU.h
@@ -0,0 +1,317 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains FPU related code.
+ * \file IceFPU.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEFPU_H__
+#define __ICEFPU_H__
+
+ #define SIGN_BITMASK 0x80000000
+
+ //! Integer representation of a floating-point value.
+ #define IR(x) ((udword&)(x))
+
+ //! Signed integer representation of a floating-point value.
+ #define SIR(x) ((sdword&)(x))
+
+ //! Absolute integer representation of a floating-point value
+ #define AIR(x) (IR(x)&0x7fffffff)
+
+ //! Floating-point representation of an integer value.
+ #define FR(x) ((float&)(x))
+
+ //! Integer-based comparison of a floating point value.
+ //! Don't use it blindly, it can be faster or slower than the FPU comparison, depends on the context.
+ #define IS_NEGATIVE_FLOAT(x) (IR(x)&0x80000000)
+
+ //! Fast fabs for floating-point values. It just clears the sign bit.
+ //! Don't use it blindy, it can be faster or slower than the FPU comparison, depends on the context.
+ inline_ float FastFabs(float x)
+ {
+ udword FloatBits = IR(x)&0x7fffffff;
+ return FR(FloatBits);
+ }
+
+ //! Fast square root for floating-point values.
+ inline_ float FastSqrt(float square)
+ {
+ float retval;
+
+ __asm {
+ mov eax, square
+ sub eax, 0x3F800000
+ sar eax, 1
+ add eax, 0x3F800000
+ mov [retval], eax
+ }
+ return retval;
+ }
+
+ //! Saturates positive to zero.
+ inline_ float fsat(float f)
+ {
+ udword y = (udword&)f & ~((sdword&)f >>31);
+ return (float&)y;
+ }
+
+ //! Computes 1.0f / sqrtf(x).
+ inline_ float frsqrt(float f)
+ {
+ float x = f * 0.5f;
+ udword y = 0x5f3759df - ((udword&)f >> 1);
+ // Iteration...
+ (float&)y = (float&)y * ( 1.5f - ( x * (float&)y * (float&)y ) );
+ // Result
+ return (float&)y;
+ }
+
+ //! Computes 1.0f / sqrtf(x). Comes from NVIDIA.
+ inline_ float InvSqrt(const float& x)
+ {
+ udword tmp = (udword(IEEE_1_0 << 1) + IEEE_1_0 - *(udword*)&x) >> 1;
+ float y = *(float*)&tmp;
+ return y * (1.47f - 0.47f * x * y * y);
+ }
+
+ //! Computes 1.0f / sqrtf(x). Comes from Quake3. Looks like the first one I had above.
+ //! See http://www.magic-software.com/3DGEDInvSqrt.html
+ inline_ float RSqrt(float number)
+ {
+ long i;
+ float x2, y;
+ const float threehalfs = 1.5f;
+
+ x2 = number * 0.5f;
+ y = number;
+ i = * (long *) &y;
+ i = 0x5f3759df - (i >> 1);
+ y = * (float *) &i;
+ y = y * (threehalfs - (x2 * y * y));
+
+ return y;
+ }
+
+ //! TO BE DOCUMENTED
+ inline_ float fsqrt(float f)
+ {
+ udword y = ( ( (sdword&)f - 0x3f800000 ) >> 1 ) + 0x3f800000;
+ // Iteration...?
+ // (float&)y = (3.0f - ((float&)y * (float&)y) / f) * (float&)y * 0.5f;
+ // Result
+ return (float&)y;
+ }
+
+ //! Returns the float ranged espilon value.
+ inline_ float fepsilon(float f)
+ {
+ udword b = (udword&)f & 0xff800000;
+ udword a = b | 0x00000001;
+ (float&)a -= (float&)b;
+ // Result
+ return (float&)a;
+ }
+
+ //! Is the float valid ?
+ inline_ bool IsNAN(float value) { return (IR(value)&0x7f800000) == 0x7f800000; }
+ inline_ bool IsIndeterminate(float value) { return IR(value) == 0xffc00000; }
+ inline_ bool IsPlusInf(float value) { return IR(value) == 0x7f800000; }
+ inline_ bool IsMinusInf(float value) { return IR(value) == 0xff800000; }
+
+ inline_ bool IsValidFloat(float value)
+ {
+ if(IsNAN(value)) return false;
+ if(IsIndeterminate(value)) return false;
+ if(IsPlusInf(value)) return false;
+ if(IsMinusInf(value)) return false;
+ return true;
+ }
+
+ #define CHECK_VALID_FLOAT(x) ASSERT(IsValidFloat(x));
+
+/*
+ //! FPU precision setting function.
+ inline_ void SetFPU()
+ {
+ // This function evaluates whether the floating-point
+ // control word is set to single precision/round to nearest/
+ // exceptions disabled. If these conditions don't hold, the
+ // function changes the control word to set them and returns
+ // TRUE, putting the old control word value in the passback
+ // location pointed to by pwOldCW.
+ {
+ uword wTemp, wSave;
+
+ __asm fstcw wSave
+ if (wSave & 0x300 || // Not single mode
+ 0x3f != (wSave & 0x3f) || // Exceptions enabled
+ wSave & 0xC00) // Not round to nearest mode
+ {
+ __asm
+ {
+ mov ax, wSave
+ and ax, not 300h ;; single mode
+ or ax, 3fh ;; disable all exceptions
+ and ax, not 0xC00 ;; round to nearest mode
+ mov wTemp, ax
+ fldcw wTemp
+ }
+ }
+ }
+ }
+*/
+ //! This function computes the slowest possible floating-point value (you can also directly use FLT_EPSILON)
+ inline_ float ComputeFloatEpsilon()
+ {
+ float f = 1.0f;
+ ((udword&)f)^=1;
+ return f - 1.0f; // You can check it's the same as FLT_EPSILON
+ }
+
+ inline_ bool IsFloatZero(float x, float epsilon=1e-6f)
+ {
+ return x*x < epsilon;
+ }
+
+ #define FCOMI_ST0 _asm _emit 0xdb _asm _emit 0xf0
+ #define FCOMIP_ST0 _asm _emit 0xdf _asm _emit 0xf0
+ #define FCMOVB_ST0 _asm _emit 0xda _asm _emit 0xc0
+ #define FCMOVNB_ST0 _asm _emit 0xdb _asm _emit 0xc0
+
+ #define FCOMI_ST1 _asm _emit 0xdb _asm _emit 0xf1
+ #define FCOMIP_ST1 _asm _emit 0xdf _asm _emit 0xf1
+ #define FCMOVB_ST1 _asm _emit 0xda _asm _emit 0xc1
+ #define FCMOVNB_ST1 _asm _emit 0xdb _asm _emit 0xc1
+
+ #define FCOMI_ST2 _asm _emit 0xdb _asm _emit 0xf2
+ #define FCOMIP_ST2 _asm _emit 0xdf _asm _emit 0xf2
+ #define FCMOVB_ST2 _asm _emit 0xda _asm _emit 0xc2
+ #define FCMOVNB_ST2 _asm _emit 0xdb _asm _emit 0xc2
+
+ #define FCOMI_ST3 _asm _emit 0xdb _asm _emit 0xf3
+ #define FCOMIP_ST3 _asm _emit 0xdf _asm _emit 0xf3
+ #define FCMOVB_ST3 _asm _emit 0xda _asm _emit 0xc3
+ #define FCMOVNB_ST3 _asm _emit 0xdb _asm _emit 0xc3
+
+ #define FCOMI_ST4 _asm _emit 0xdb _asm _emit 0xf4
+ #define FCOMIP_ST4 _asm _emit 0xdf _asm _emit 0xf4
+ #define FCMOVB_ST4 _asm _emit 0xda _asm _emit 0xc4
+ #define FCMOVNB_ST4 _asm _emit 0xdb _asm _emit 0xc4
+
+ #define FCOMI_ST5 _asm _emit 0xdb _asm _emit 0xf5
+ #define FCOMIP_ST5 _asm _emit 0xdf _asm _emit 0xf5
+ #define FCMOVB_ST5 _asm _emit 0xda _asm _emit 0xc5
+ #define FCMOVNB_ST5 _asm _emit 0xdb _asm _emit 0xc5
+
+ #define FCOMI_ST6 _asm _emit 0xdb _asm _emit 0xf6
+ #define FCOMIP_ST6 _asm _emit 0xdf _asm _emit 0xf6
+ #define FCMOVB_ST6 _asm _emit 0xda _asm _emit 0xc6
+ #define FCMOVNB_ST6 _asm _emit 0xdb _asm _emit 0xc6
+
+ #define FCOMI_ST7 _asm _emit 0xdb _asm _emit 0xf7
+ #define FCOMIP_ST7 _asm _emit 0xdf _asm _emit 0xf7
+ #define FCMOVB_ST7 _asm _emit 0xda _asm _emit 0xc7
+ #define FCMOVNB_ST7 _asm _emit 0xdb _asm _emit 0xc7
+
+ //! A global function to find MAX(a,b) using FCOMI/FCMOV
+ inline_ float FCMax2(float a, float b)
+ {
+ float Res;
+ _asm fld [a]
+ _asm fld [b]
+ FCOMI_ST1
+ FCMOVB_ST1
+ _asm fstp [Res]
+ _asm fcomp
+ return Res;
+ }
+
+ //! A global function to find MIN(a,b) using FCOMI/FCMOV
+ inline_ float FCMin2(float a, float b)
+ {
+ float Res;
+ _asm fld [a]
+ _asm fld [b]
+ FCOMI_ST1
+ FCMOVNB_ST1
+ _asm fstp [Res]
+ _asm fcomp
+ return Res;
+ }
+
+ //! A global function to find MAX(a,b,c) using FCOMI/FCMOV
+ inline_ float FCMax3(float a, float b, float c)
+ {
+ float Res;
+ _asm fld [a]
+ _asm fld [b]
+ _asm fld [c]
+ FCOMI_ST1
+ FCMOVB_ST1
+ FCOMI_ST2
+ FCMOVB_ST2
+ _asm fstp [Res]
+ _asm fcompp
+ return Res;
+ }
+
+ //! A global function to find MIN(a,b,c) using FCOMI/FCMOV
+ inline_ float FCMin3(float a, float b, float c)
+ {
+ float Res;
+ _asm fld [a]
+ _asm fld [b]
+ _asm fld [c]
+ FCOMI_ST1
+ FCMOVNB_ST1
+ FCOMI_ST2
+ FCMOVNB_ST2
+ _asm fstp [Res]
+ _asm fcompp
+ return Res;
+ }
+
+ inline_ int ConvertToSortable(float f)
+ {
+ int& Fi = (int&)f;
+ int Fmask = (Fi>>31);
+ Fi ^= Fmask;
+ Fmask &= ~(1<<31);
+ Fi -= Fmask;
+ return Fi;
+ }
+
+ enum FPUMode
+ {
+ FPU_FLOOR = 0,
+ FPU_CEIL = 1,
+ FPU_BEST = 2,
+
+ FPU_FORCE_DWORD = 0x7fffffff
+ };
+
+ FUNCTION ICECORE_API FPUMode GetFPUMode();
+ FUNCTION ICECORE_API void SaveFPU();
+ FUNCTION ICECORE_API void RestoreFPU();
+ FUNCTION ICECORE_API void SetFPUFloorMode();
+ FUNCTION ICECORE_API void SetFPUCeilMode();
+ FUNCTION ICECORE_API void SetFPUBestMode();
+
+ FUNCTION ICECORE_API void SetFPUPrecision24();
+ FUNCTION ICECORE_API void SetFPUPrecision53();
+ FUNCTION ICECORE_API void SetFPUPrecision64();
+ FUNCTION ICECORE_API void SetFPURoundingChop();
+ FUNCTION ICECORE_API void SetFPURoundingUp();
+ FUNCTION ICECORE_API void SetFPURoundingDown();
+ FUNCTION ICECORE_API void SetFPURoundingNear();
+
+ FUNCTION ICECORE_API int intChop(const float& f);
+ FUNCTION ICECORE_API int intFloor(const float& f);
+ FUNCTION ICECORE_API int intCeil(const float& f);
+
+#endif // __ICEFPU_H__
diff --git a/Opcode/Ice/IceHPoint.cpp b/Opcode/Ice/IceHPoint.cpp
new file mode 100644
index 0000000..2074543
--- /dev/null
+++ b/Opcode/Ice/IceHPoint.cpp
@@ -0,0 +1,70 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for homogeneous points.
+ * \file IceHPoint.cpp
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Homogeneous point.
+ *
+ * Use it:
+ * - for clipping in homogeneous space (standard way)
+ * - to differentiate between points (w=1) and vectors (w=0).
+ * - in some cases you can also use it instead of IcePoint for padding reasons.
+ *
+ * \class HPoint
+ * \author Pierre Terdiman
+ * \version 1.0
+ * \warning No cross-product in 4D.
+ * \warning HPoint *= Matrix3x3 doesn't exist, the matrix is first casted to a 4x4
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// IcePoint Mul = HPoint * Matrix3x3;
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+IcePoint HPoint::operator*(const Matrix3x3& mat) const
+{
+ return IcePoint(
+ x * mat.m[0][0] + y * mat.m[1][0] + z * mat.m[2][0],
+ x * mat.m[0][1] + y * mat.m[1][1] + z * mat.m[2][1],
+ x * mat.m[0][2] + y * mat.m[1][2] + z * mat.m[2][2] );
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// HPoint Mul = HPoint * Matrix4x4;
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HPoint HPoint::operator*(const Matrix4x4& mat) const
+{
+ return HPoint(
+ x * mat.m[0][0] + y * mat.m[1][0] + z * mat.m[2][0] + w * mat.m[3][0],
+ x * mat.m[0][1] + y * mat.m[1][1] + z * mat.m[2][1] + w * mat.m[3][1],
+ x * mat.m[0][2] + y * mat.m[1][2] + z * mat.m[2][2] + w * mat.m[3][2],
+ x * mat.m[0][3] + y * mat.m[1][3] + z * mat.m[2][3] + w * mat.m[3][3]);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// HPoint *= Matrix4x4
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HPoint& HPoint::operator*=(const Matrix4x4& mat)
+{
+ float xp = x * mat.m[0][0] + y * mat.m[1][0] + z * mat.m[2][0] + w * mat.m[3][0];
+ float yp = x * mat.m[0][1] + y * mat.m[1][1] + z * mat.m[2][1] + w * mat.m[3][1];
+ float zp = x * mat.m[0][2] + y * mat.m[1][2] + z * mat.m[2][2] + w * mat.m[3][2];
+ float wp = x * mat.m[0][3] + y * mat.m[1][3] + z * mat.m[2][3] + w * mat.m[3][3];
+
+ x = xp; y = yp; z = zp; w = wp;
+
+ return *this;
+}
+
diff --git a/Opcode/Ice/IceHPoint.h b/Opcode/Ice/IceHPoint.h
new file mode 100644
index 0000000..3065d8e
--- /dev/null
+++ b/Opcode/Ice/IceHPoint.h
@@ -0,0 +1,157 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for homogeneous points.
+ * \file IceHPoint.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEHPOINT_H__
+#define __ICEHPOINT_H__
+
+ class ICEMATHS_API HPoint : public Point
+ {
+ public:
+
+ //! Empty constructor
+ inline_ HPoint() {}
+ //! Constructor from floats
+ inline_ HPoint(float _x, float _y, float _z, float _w=0.0f) : Point(_x, _y, _z), w(_w) {}
+ //! Constructor from array
+ inline_ HPoint(const float f[4]) : Point(f), w(f[3]) {}
+ //! Constructor from a Point
+ inline_ HPoint(const Point& p, float _w=0.0f) : Point(p), w(_w) {}
+ //! Destructor
+ inline_ ~HPoint() {}
+
+ //! Clear the point
+ inline_ HPoint& Zero() { x = y = z = w = 0.0f; return *this; }
+
+ //! Assignment from values
+ inline_ HPoint& Set(float _x, float _y, float _z, float _w ) { x = _x; y = _y; z = _z; w = _w; return *this; }
+ //! Assignment from array
+ inline_ HPoint& Set(const float f[4]) { x = f[_X]; y = f[_Y]; z = f[_Z]; w = f[_W]; return *this; }
+ //! Assignment from another h-point
+ inline_ HPoint& Set(const HPoint& src) { x = src.x; y = src.y; z = src.z; w = src.w; return *this; }
+
+ //! Add a vector
+ inline_ HPoint& Add(float _x, float _y, float _z, float _w ) { x += _x; y += _y; z += _z; w += _w; return *this; }
+ //! Add a vector
+ inline_ HPoint& Add(const float f[4]) { x += f[_X]; y += f[_Y]; z += f[_Z]; w += f[_W]; return *this; }
+
+ //! Subtract a vector
+ inline_ HPoint& Sub(float _x, float _y, float _z, float _w ) { x -= _x; y -= _y; z -= _z; w -= _w; return *this; }
+ //! Subtract a vector
+ inline_ HPoint& Sub(const float f[4]) { x -= f[_X]; y -= f[_Y]; z -= f[_Z]; w -= f[_W]; return *this; }
+
+ //! Multiplies by a scalar
+ inline_ HPoint& Mul(float s) { x *= s; y *= s; z *= s; w *= s; return *this; }
+
+ //! Returns MIN(x, y, z, w);
+ float Min() const { return MIN(x, MIN(y, MIN(z, w))); }
+ //! Returns MAX(x, y, z, w);
+ float Max() const { return MAX(x, MAX(y, MAX(z, w))); }
+ //! Sets each element to be componentwise minimum
+ HPoint& Min(const HPoint& p) { x = MIN(x, p.x); y = MIN(y, p.y); z = MIN(z, p.z); w = MIN(w, p.w); return *this; }
+ //! Sets each element to be componentwise maximum
+ HPoint& Max(const HPoint& p) { x = MAX(x, p.x); y = MAX(y, p.y); z = MAX(z, p.z); w = MAX(w, p.w); return *this; }
+
+ //! Computes square magnitude
+ inline_ float SquareMagnitude() const { return x*x + y*y + z*z + w*w; }
+ //! Computes magnitude
+ inline_ float Magnitude() const { return sqrtf(x*x + y*y + z*z + w*w); }
+
+ //! Normalize the vector
+ inline_ HPoint& Normalize()
+ {
+ float M = Magnitude();
+ if(M)
+ {
+ M = 1.0f / M;
+ x *= M;
+ y *= M;
+ z *= M;
+ w *= M;
+ }
+ return *this;
+ }
+
+ // Arithmetic operators
+ //! Operator for HPoint Negate = - HPoint;
+ inline_ HPoint operator-() const { return HPoint(-x, -y, -z, -w); }
+
+ //! Operator for HPoint Plus = HPoint + HPoint;
+ inline_ HPoint operator+(const HPoint& p) const { return HPoint(x + p.x, y + p.y, z + p.z, w + p.w); }
+ //! Operator for HPoint Minus = HPoint - HPoint;
+ inline_ HPoint operator-(const HPoint& p) const { return HPoint(x - p.x, y - p.y, z - p.z, w - p.w); }
+
+ //! Operator for HPoint Mul = HPoint * HPoint;
+ inline_ HPoint operator*(const HPoint& p) const { return HPoint(x * p.x, y * p.y, z * p.z, w * p.w); }
+ //! Operator for HPoint Scale = HPoint * float;
+ inline_ HPoint operator*(float s) const { return HPoint(x * s, y * s, z * s, w * s); }
+ //! Operator for HPoint Scale = float * HPoint;
+ inline_ friend HPoint operator*(float s, const HPoint& p) { return HPoint(s * p.x, s * p.y, s * p.z, s * p.w); }
+
+ //! Operator for HPoint Div = HPoint / HPoint;
+ inline_ HPoint operator/(const HPoint& p) const { return HPoint(x / p.x, y / p.y, z / p.z, w / p.w); }
+ //! Operator for HPoint Scale = HPoint / float;
+ inline_ HPoint operator/(float s) const { s = 1.0f / s; return HPoint(x * s, y * s, z * s, w * s); }
+ //! Operator for HPoint Scale = float / HPoint;
+ inline_ friend HPoint operator/(float s, const HPoint& p) { return HPoint(s / p.x, s / p.y, s / p.z, s / p.w); }
+
+ //! Operator for float DotProd = HPoint | HPoint;
+ inline_ float operator|(const HPoint& p) const { return x*p.x + y*p.y + z*p.z + w*p.w; }
+ // No cross-product in 4D
+
+ //! Operator for HPoint += HPoint;
+ inline_ HPoint& operator+=(const HPoint& p) { x += p.x; y += p.y; z += p.z; w += p.w; return *this; }
+ //! Operator for HPoint += float;
+ inline_ HPoint& operator+=(float s) { x += s; y += s; z += s; w += s; return *this; }
+
+ //! Operator for HPoint -= HPoint;
+ inline_ HPoint& operator-=(const HPoint& p) { x -= p.x; y -= p.y; z -= p.z; w -= p.w; return *this; }
+ //! Operator for HPoint -= float;
+ inline_ HPoint& operator-=(float s) { x -= s; y -= s; z -= s; w -= s; return *this; }
+
+ //! Operator for HPoint *= HPoint;
+ inline_ HPoint& operator*=(const HPoint& p) { x *= p.x; y *= p.y; z *= p.z; w *= p.w; return *this; }
+ //! Operator for HPoint *= float;
+ inline_ HPoint& operator*=(float s) { x*=s; y*=s; z*=s; w*=s; return *this; }
+
+ //! Operator for HPoint /= HPoint;
+ inline_ HPoint& operator/=(const HPoint& p) { x /= p.x; y /= p.y; z /= p.z; w /= p.w; return *this; }
+ //! Operator for HPoint /= float;
+ inline_ HPoint& operator/=(float s) { s = 1.0f / s; x*=s; y*=s; z*=s; w*=s; return *this; }
+
+ // Arithmetic operators
+
+ //! Operator for Point Mul = HPoint * Matrix3x3;
+ Point operator*(const Matrix3x3& mat) const;
+ //! Operator for HPoint Mul = HPoint * Matrix4x4;
+ HPoint operator*(const Matrix4x4& mat) const;
+
+ // HPoint *= Matrix3x3 doesn't exist, the matrix is first casted to a 4x4
+ //! Operator for HPoint *= Matrix4x4
+ HPoint& operator*=(const Matrix4x4& mat);
+
+ // Logical operators
+
+ //! Operator for "if(HPoint==HPoint)"
+ inline_ bool operator==(const HPoint& p) const { return ( (x==p.x)&&(y==p.y)&&(z==p.z)&&(w==p.w)); }
+ //! Operator for "if(HPoint!=HPoint)"
+ inline_ bool operator!=(const HPoint& p) const { return ( (x!=p.x)||(y!=p.y)||(z!=p.z)||(w!=p.w)); }
+
+ // Cast operators
+
+ //! Cast a HPoint to a Point. w is discarded.
+ inline_ operator Point() const { return Point(x, y, z); }
+
+ public:
+ float w;
+ };
+
+#endif // __ICEHPOINT_H__
+
diff --git a/Opcode/Ice/IceIndexedTriangle.cpp b/Opcode/Ice/IceIndexedTriangle.cpp
new file mode 100644
index 0000000..d680f24
--- /dev/null
+++ b/Opcode/Ice/IceIndexedTriangle.cpp
@@ -0,0 +1,548 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a handy indexed triangle class.
+ * \file IceIndexedTriangle.cpp
+ * \author Pierre Terdiman
+ * \date January, 17, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains an indexed triangle class.
+ *
+ * \class Triangle
+ * \author Pierre Terdiman
+ * \version 1.0
+ * \date 08.15.98
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Flips the winding order.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void IndexedTriangle::Flip()
+{
+ Swap(mVRef[1], mVRef[2]);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle area.
+ * \param verts [in] the list of indexed vertices
+ * \return the area
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float IndexedTriangle::Area(const IcePoint* verts) const
+{
+ if(!verts) return 0.0f;
+ const IcePoint& p0 = verts[0];
+ const IcePoint& p1 = verts[1];
+ const IcePoint& p2 = verts[2];
+ return ((p0-p1)^(p0-p2)).Magnitude() * 0.5f;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle perimeter.
+ * \param verts [in] the list of indexed vertices
+ * \return the perimeter
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float IndexedTriangle::Perimeter(const IcePoint* verts) const
+{
+ if(!verts) return 0.0f;
+ const IcePoint& p0 = verts[0];
+ const IcePoint& p1 = verts[1];
+ const IcePoint& p2 = verts[2];
+ return p0.Distance(p1)
+ + p0.Distance(p2)
+ + p1.Distance(p2);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle compacity.
+ * \param verts [in] the list of indexed vertices
+ * \return the compacity
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float IndexedTriangle::Compacity(const IcePoint* verts) const
+{
+ if(!verts) return 0.0f;
+ float P = Perimeter(verts);
+ if(P==0.0f) return 0.0f;
+ return (4.0f*PI*Area(verts)/(P*P));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle normal.
+ * \param verts [in] the list of indexed vertices
+ * \param normal [out] the computed normal
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void IndexedTriangle::Normal(const IcePoint* verts, IcePoint& normal) const
+{
+ if(!verts) return;
+
+ const IcePoint& p0 = verts[mVRef[0]];
+ const IcePoint& p1 = verts[mVRef[1]];
+ const IcePoint& p2 = verts[mVRef[2]];
+ normal = ((p2-p1)^(p0-p1)).Normalize();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle denormalized normal.
+ * \param verts [in] the list of indexed vertices
+ * \param normal [out] the computed normal
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void IndexedTriangle::DenormalizedNormal(const IcePoint* verts, IcePoint& normal) const
+{
+ if(!verts) return;
+
+ const IcePoint& p0 = verts[mVRef[0]];
+ const IcePoint& p1 = verts[mVRef[1]];
+ const IcePoint& p2 = verts[mVRef[2]];
+ normal = ((p2-p1)^(p0-p1));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle center.
+ * \param verts [in] the list of indexed vertices
+ * \param center [out] the computed center
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void IndexedTriangle::Center(const IcePoint* verts, IcePoint& center) const
+{
+ if(!verts) return;
+
+ const IcePoint& p0 = verts[mVRef[0]];
+ const IcePoint& p1 = verts[mVRef[1]];
+ const IcePoint& p2 = verts[mVRef[2]];
+ center = (p0+p1+p2)*INV3;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the centered normal
+ * \param verts [in] the list of indexed vertices
+ * \param normal [out] the computed centered normal
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void IndexedTriangle::CenteredNormal(const IcePoint* verts, IcePoint& normal) const
+{
+ if(!verts) return;
+
+ const IcePoint& p0 = verts[mVRef[0]];
+ const IcePoint& p1 = verts[mVRef[1]];
+ const IcePoint& p2 = verts[mVRef[2]];
+ IcePoint Center = (p0+p1+p2)*INV3;
+ normal = Center + ((p2-p1)^(p0-p1)).Normalize();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes a random point within the triangle.
+ * \param verts [in] the list of indexed vertices
+ * \param normal [out] the computed centered normal
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void IndexedTriangle::RandomPoint(const IcePoint* verts, IcePoint& random) const
+{
+ if(!verts) return;
+
+ // Random barycentric coords
+ float Alpha = UnitRandomFloat();
+ float Beta = UnitRandomFloat();
+ float Gamma = UnitRandomFloat();
+ float OneOverTotal = 1.0f / (Alpha + Beta + Gamma);
+ Alpha *= OneOverTotal;
+ Beta *= OneOverTotal;
+ Gamma *= OneOverTotal;
+
+ const IcePoint& p0 = verts[mVRef[0]];
+ const IcePoint& p1 = verts[mVRef[1]];
+ const IcePoint& p2 = verts[mVRef[2]];
+ random = Alpha*p0 + Beta*p1 + Gamma*p2;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes backface culling.
+ * \param verts [in] the list of indexed vertices
+ * \param source [in] source point (in local space) from which culling must be computed
+ * \return true if the triangle is visible from the source point
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool IndexedTriangle::IsVisible(const IcePoint* verts, const IcePoint& source) const
+{
+ // Checkings
+ if(!verts) return false;
+
+ const IcePoint& p0 = verts[mVRef[0]];
+ const IcePoint& p1 = verts[mVRef[1]];
+ const IcePoint& p2 = verts[mVRef[2]];
+
+ // Compute denormalized normal
+ IcePoint Normal = (p2 - p1)^(p0 - p1);
+
+ // Backface culling
+ return (Normal | source) >= 0.0f;
+
+// Same as:
+// Plane PL(verts[mVRef[0]], verts[mVRef[1]], verts[mVRef[2]]);
+// return PL.Distance(source) > PL.d;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes backface culling.
+ * \param verts [in] the list of indexed vertices
+ * \param source [in] source point (in local space) from which culling must be computed
+ * \return true if the triangle is visible from the source point
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool IndexedTriangle::BackfaceCulling(const IcePoint* verts, const IcePoint& source) const
+{
+ // Checkings
+ if(!verts) return false;
+
+ const IcePoint& p0 = verts[mVRef[0]];
+ const IcePoint& p1 = verts[mVRef[1]];
+ const IcePoint& p2 = verts[mVRef[2]];
+
+ // Compute base
+// IcePoint Base = (p0 + p1 + p2)*INV3;
+
+ // Compute denormalized normal
+ IcePoint Normal = (p2 - p1)^(p0 - p1);
+
+ // Backface culling
+// return (Normal | (source - Base)) >= 0.0f;
+ return (Normal | (source - p0)) >= 0.0f;
+
+// Same as: (but a bit faster)
+// Plane PL(verts[mVRef[0]], verts[mVRef[1]], verts[mVRef[2]]);
+// return PL.Distance(source)>0.0f;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the occlusion potential of the triangle.
+ * \param verts [in] the list of indexed vertices
+ * \param source [in] source point (in local space) from which occlusion potential must be computed
+ * \return the occlusion potential
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float IndexedTriangle::ComputeOcclusionPotential(const IcePoint* verts, const IcePoint& view) const
+{
+ if(!verts) return 0.0f;
+ // Occlusion potential: -(A * (N|V) / d^2)
+ // A = polygon area
+ // N = polygon normal
+ // V = view vector
+ // d = distance viewpoint-center of polygon
+
+ float A = Area(verts);
+ IcePoint N; Normal(verts, N);
+ IcePoint C; Center(verts, C);
+ float d = view.Distance(C);
+ return -(A*(N|view))/(d*d);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Replaces a vertex reference with another one.
+ * \param oldref [in] the vertex reference to replace
+ * \param newref [in] the new vertex reference
+ * \return true if success, else false if the input vertex reference doesn't belong to the triangle
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool IndexedTriangle::ReplaceVertex(udword oldref, udword newref)
+{
+ if(mVRef[0]==oldref) { mVRef[0] = newref; return true; }
+ else if(mVRef[1]==oldref) { mVRef[1] = newref; return true; }
+ else if(mVRef[2]==oldref) { mVRef[2] = newref; return true; }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks whether the triangle is degenerate or not. A degenerate triangle has two common vertex references. This is a zero-area triangle.
+ * \return true if the triangle is degenerate
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool IndexedTriangle::IsDegenerate() const
+{
+ if(mVRef[0]==mVRef[1]) return true;
+ if(mVRef[1]==mVRef[2]) return true;
+ if(mVRef[2]==mVRef[0]) return true;
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks whether the input vertex reference belongs to the triangle or not.
+ * \param ref [in] the vertex reference to look for
+ * \return true if the triangle contains the vertex reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool IndexedTriangle::HasVertex(udword ref) const
+{
+ if(mVRef[0]==ref) return true;
+ if(mVRef[1]==ref) return true;
+ if(mVRef[2]==ref) return true;
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks whether the input vertex reference belongs to the triangle or not.
+ * \param ref [in] the vertex reference to look for
+ * \param index [out] the corresponding index in the triangle
+ * \return true if the triangle contains the vertex reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool IndexedTriangle::HasVertex(udword ref, udword* index) const
+{
+ if(mVRef[0]==ref) { *index = 0; return true; }
+ if(mVRef[1]==ref) { *index = 1; return true; }
+ if(mVRef[2]==ref) { *index = 2; return true; }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Finds an edge in a tri, given two vertex references.
+ * \param vref0 [in] the edge's first vertex reference
+ * \param vref1 [in] the edge's second vertex reference
+ * \return the edge number between 0 and 2, or 0xff if input refs are wrong.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ubyte IndexedTriangle::FindEdge(udword vref0, udword vref1) const
+{
+ if(mVRef[0]==vref0 && mVRef[1]==vref1) return 0;
+ else if(mVRef[0]==vref1 && mVRef[1]==vref0) return 0;
+ else if(mVRef[0]==vref0 && mVRef[2]==vref1) return 1;
+ else if(mVRef[0]==vref1 && mVRef[2]==vref0) return 1;
+ else if(mVRef[1]==vref0 && mVRef[2]==vref1) return 2;
+ else if(mVRef[1]==vref1 && mVRef[2]==vref0) return 2;
+ return 0xff;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the last reference given the first two.
+ * \param vref0 [in] the first vertex reference
+ * \param vref1 [in] the second vertex reference
+ * \return the last reference, or INVALID_ID if input refs are wrong.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword IndexedTriangle::OppositeVertex(udword vref0, udword vref1) const
+{
+ if(mVRef[0]==vref0 && mVRef[1]==vref1) return mVRef[2];
+ else if(mVRef[0]==vref1 && mVRef[1]==vref0) return mVRef[2];
+ else if(mVRef[0]==vref0 && mVRef[2]==vref1) return mVRef[1];
+ else if(mVRef[0]==vref1 && mVRef[2]==vref0) return mVRef[1];
+ else if(mVRef[1]==vref0 && mVRef[2]==vref1) return mVRef[0];
+ else if(mVRef[1]==vref1 && mVRef[2]==vref0) return mVRef[0];
+ return INVALID_ID;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the three sorted vertex references according to an edge number.
+ * edgenb = 0 => edge 0-1, returns references 0, 1, 2
+ * edgenb = 1 => edge 0-2, returns references 0, 2, 1
+ * edgenb = 2 => edge 1-2, returns references 1, 2, 0
+ *
+ * \param edgenb [in] the edge number, 0, 1 or 2
+ * \param vref0 [out] the returned first vertex reference
+ * \param vref1 [out] the returned second vertex reference
+ * \param vref2 [out] the returned third vertex reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void IndexedTriangle::GetVRefs(ubyte edgenb, udword& vref0, udword& vref1, udword& vref2) const
+{
+ if(edgenb==0)
+ {
+ vref0 = mVRef[0];
+ vref1 = mVRef[1];
+ vref2 = mVRef[2];
+ }
+ else if(edgenb==1)
+ {
+ vref0 = mVRef[0];
+ vref1 = mVRef[2];
+ vref2 = mVRef[1];
+ }
+ else if(edgenb==2)
+ {
+ vref0 = mVRef[1];
+ vref1 = mVRef[2];
+ vref2 = mVRef[0];
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle's smallest edge length.
+ * \param verts [in] the list of indexed vertices
+ * \return the smallest edge length
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float IndexedTriangle::MinEdgeLength(const IcePoint* verts) const
+{
+ if(!verts) return 0.0f;
+
+ float Min = MAX_FLOAT;
+ float Length01 = verts[0].Distance(verts[1]);
+ float Length02 = verts[0].Distance(verts[2]);
+ float Length12 = verts[1].Distance(verts[2]);
+ if(Length01 < Min) Min = Length01;
+ if(Length02 < Min) Min = Length02;
+ if(Length12 < Min) Min = Length12;
+ return Min;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle's largest edge length.
+ * \param verts [in] the list of indexed vertices
+ * \return the largest edge length
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float IndexedTriangle::MaxEdgeLength(const IcePoint* verts) const
+{
+ if(!verts) return 0.0f;
+
+ float Max = MIN_FLOAT;
+ float Length01 = verts[0].Distance(verts[1]);
+ float Length02 = verts[0].Distance(verts[2]);
+ float Length12 = verts[1].Distance(verts[2]);
+ if(Length01 > Max) Max = Length01;
+ if(Length02 > Max) Max = Length02;
+ if(Length12 > Max) Max = Length12;
+ return Max;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes a point on the triangle according to the stabbing information.
+ * \param verts [in] the list of indexed vertices
+ * \param u,v [in] point's barycentric coordinates
+ * \param pt [out] point on triangle
+ * \param nearvtx [out] index of nearest vertex
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void IndexedTriangle::ComputePoint(const IcePoint* verts, float u, float v, IcePoint& pt, udword* nearvtx) const
+{
+ // Checkings
+ if(!verts) return;
+
+ // Get face in local or global space
+ const IcePoint& p0 = verts[mVRef[0]];
+ const IcePoint& p1 = verts[mVRef[1]];
+ const IcePoint& p2 = verts[mVRef[2]];
+
+ // Compute point coordinates
+ pt = (1.0f - u - v)*p0 + u*p1 + v*p2;
+
+ // Compute nearest vertex if needed
+ if(nearvtx)
+ {
+ // Compute distance vector
+ IcePoint d(p0.SquareDistance(pt), // Distance^2 from vertex 0 to point on the face
+ p1.SquareDistance(pt), // Distance^2 from vertex 1 to point on the face
+ p2.SquareDistance(pt)); // Distance^2 from vertex 2 to point on the face
+
+ // Get smallest distance
+ *nearvtx = mVRef[d.SmallestAxis()];
+ }
+}
+
+ //**************************************
+ // Angle between two vectors (in radians)
+ // we use this formula
+ // uv = |u||v| cos(u,v)
+ // u ^ v = w
+ // |w| = |u||v| |sin(u,v)|
+ //**************************************
+ float Angle(const IcePoint& u, const IcePoint& v)
+ {
+ float NormU = u.Magnitude(); // |u|
+ float NormV = v.Magnitude(); // |v|
+ float Product = NormU*NormV; // |u||v|
+ if(Product==0.0f) return 0.0f;
+ float OneOverProduct = 1.0f / Product;
+
+ // Cosinus
+ float Cosinus = (u|v) * OneOverProduct;
+
+ // Sinus
+ IcePoint w = u^v;
+ float NormW = w.Magnitude();
+
+ float AbsSinus = NormW * OneOverProduct;
+
+ // Remove degeneracy
+ if(AbsSinus > 1.0f) AbsSinus = 1.0f;
+ if(AbsSinus < -1.0f) AbsSinus = -1.0f;
+
+ if(Cosinus>=0.0f) return asinf(AbsSinus);
+ else return (PI-asinf(AbsSinus));
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the angle between two triangles.
+ * \param tri [in] the other triangle
+ * \param verts [in] the list of indexed vertices
+ * \return the angle in radians
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float IndexedTriangle::Angle(const IndexedTriangle& tri, const IcePoint* verts) const
+{
+ // Checkings
+ if(!verts) return 0.0f;
+
+ // Compute face normals
+ IcePoint n0, n1;
+ Normal(verts, n0);
+ tri.Normal(verts, n1);
+
+ // Compute angle
+ float dp = n0|n1;
+ if(dp>1.0f) return 0.0f;
+ if(dp<-1.0f) return PI;
+ return acosf(dp);
+
+// return ::Angle(n0,n1);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks a triangle is the same as another one.
+ * \param tri [in] the other triangle
+ * \return true if same triangle
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool IndexedTriangle::Equal(const IndexedTriangle& tri) const
+{
+ // Test all vertex references
+ return (HasVertex(tri.mVRef[0]) &&
+ HasVertex(tri.mVRef[1]) &&
+ HasVertex(tri.mVRef[2]));
+}
diff --git a/Opcode/Ice/IceIndexedTriangle.h b/Opcode/Ice/IceIndexedTriangle.h
new file mode 100644
index 0000000..0c497ee
--- /dev/null
+++ b/Opcode/Ice/IceIndexedTriangle.h
@@ -0,0 +1,68 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a handy indexed triangle class.
+ * \file IceIndexedTriangle.h
+ * \author Pierre Terdiman
+ * \date January, 17, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEINDEXEDTRIANGLE_H__
+#define __ICEINDEXEDTRIANGLE_H__
+
+ // Forward declarations
+ enum CubeIndex;
+
+ // An indexed triangle class.
+ class ICEMATHS_API IndexedTriangle
+ {
+ public:
+ //! Constructor
+ inline_ IndexedTriangle() {}
+ //! Constructor
+ inline_ IndexedTriangle(udword r0, udword r1, udword r2) { mVRef[0]=r0; mVRef[1]=r1; mVRef[2]=r2; }
+ //! Copy constructor
+ inline_ IndexedTriangle(const IndexedTriangle& triangle)
+ {
+ mVRef[0] = triangle.mVRef[0];
+ mVRef[1] = triangle.mVRef[1];
+ mVRef[2] = triangle.mVRef[2];
+ }
+ //! Destructor
+ inline_ ~IndexedTriangle() {}
+ //! Vertex-references
+ udword mVRef[3];
+
+ // Methods
+ void Flip();
+ float Area(const Point* verts) const;
+ float Perimeter(const Point* verts) const;
+ float Compacity(const Point* verts) const;
+ void Normal(const Point* verts, Point& normal) const;
+ void DenormalizedNormal(const Point* verts, Point& normal) const;
+ void Center(const Point* verts, Point& center) const;
+ void CenteredNormal(const Point* verts, Point& normal) const;
+ void RandomPoint(const Point* verts, Point& random) const;
+ bool IsVisible(const Point* verts, const Point& source) const;
+ bool BackfaceCulling(const Point* verts, const Point& source) const;
+ float ComputeOcclusionPotential(const Point* verts, const Point& view) const;
+ bool ReplaceVertex(udword oldref, udword newref);
+ bool IsDegenerate() const;
+ bool HasVertex(udword ref) const;
+ bool HasVertex(udword ref, udword* index) const;
+ ubyte FindEdge(udword vref0, udword vref1) const;
+ udword OppositeVertex(udword vref0, udword vref1) const;
+ inline_ udword OppositeVertex(ubyte edgenb) const { return mVRef[2-edgenb]; }
+ void GetVRefs(ubyte edgenb, udword& vref0, udword& vref1, udword& vref2) const;
+ float MinEdgeLength(const Point* verts) const;
+ float MaxEdgeLength(const Point* verts) const;
+ void ComputePoint(const Point* verts, float u, float v, Point& pt, udword* nearvtx=null) const;
+ float Angle(const IndexedTriangle& tri, const Point* verts) const;
+ inline_ Plane PlaneEquation(const Point* verts) const { return Plane(verts[mVRef[0]], verts[mVRef[1]], verts[mVRef[2]]); }
+ bool Equal(const IndexedTriangle& tri) const;
+ CubeIndex ComputeCubeIndex(const Point* verts) const;
+ };
+
+#endif // __ICEINDEXEDTRIANGLE_H__
diff --git a/Opcode/Ice/IceLSS.h b/Opcode/Ice/IceLSS.h
new file mode 100644
index 0000000..c16c29f
--- /dev/null
+++ b/Opcode/Ice/IceLSS.h
@@ -0,0 +1,75 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for line-swept spheres.
+ * \file IceLSS.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICELSS_H__
+#define __ICELSS_H__
+
+ class ICEMATHS_API LSS : public Segment
+ {
+ public:
+ //! Constructor
+ inline_ LSS() {}
+ //! Constructor
+ inline_ LSS(const Segment& seg, float radius) : Segment(seg), mRadius(radius) {}
+ //! Destructor
+ inline_ ~LSS() {}
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes an OBB surrounding the LSS.
+ * \param box [out] the OBB
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void ComputeOBB(OBB& box);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Tests if a point is contained within the LSS.
+ * \param pt [in] the point to test
+ * \return true if inside the LSS
+ * \warning point and LSS must be in same space
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ bool Contains(const Point& pt) const { return SquareDistance(pt) <= mRadius*mRadius; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Tests if a sphere is contained within the LSS.
+ * \param sphere [in] the sphere to test
+ * \return true if inside the LSS
+ * \warning sphere and LSS must be in same space
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ bool Contains(const Sphere& sphere)
+ {
+ float d = mRadius - sphere.mRadius;
+ if(d>=0.0f) return SquareDistance(sphere.mCenter) <= d*d;
+ else return false;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Tests if an LSS is contained within the LSS.
+ * \param lss [in] the LSS to test
+ * \return true if inside the LSS
+ * \warning both LSS must be in same space
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ bool Contains(const LSS& lss)
+ {
+ // We check the LSS contains the two spheres at the start and end of the sweep
+ return Contains(Sphere(lss.mP0, lss.mRadius)) && Contains(Sphere(lss.mP0, lss.mRadius));
+ }
+
+ float mRadius; //!< Sphere radius
+ };
+
+#endif // __ICELSS_H__
diff --git a/Opcode/Ice/IceMatrix3x3.cpp b/Opcode/Ice/IceMatrix3x3.cpp
new file mode 100644
index 0000000..189d39d
--- /dev/null
+++ b/Opcode/Ice/IceMatrix3x3.cpp
@@ -0,0 +1,48 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for 3x3 matrices.
+ * \file IceMatrix3x3.cpp
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 3x3 matrix.
+ * DirectX-compliant, ie row-column order, ie m[Row][Col].
+ * Same as:
+ * m11 m12 m13 first row.
+ * m21 m22 m23 second row.
+ * m31 m32 m33 third row.
+ * Stored in memory as m11 m12 m13 m21...
+ *
+ * Multiplication rules:
+ *
+ * [x'y'z'] = [xyz][M]
+ *
+ * x' = x*m11 + y*m21 + z*m31
+ * y' = x*m12 + y*m22 + z*m32
+ * z' = x*m13 + y*m23 + z*m33
+ *
+ * \class Matrix3x3
+ * \author Pierre Terdiman
+ * \version 1.0
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+// Cast operator
+Matrix3x3::operator Matrix4x4() const
+{
+ return Matrix4x4(
+ m[0][0], m[0][1], m[0][2], 0.0f,
+ m[1][0], m[1][1], m[1][2], 0.0f,
+ m[2][0], m[2][1], m[2][2], 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+}
diff --git a/Opcode/Ice/IceMatrix3x3.h b/Opcode/Ice/IceMatrix3x3.h
new file mode 100644
index 0000000..2266fc5
--- /dev/null
+++ b/Opcode/Ice/IceMatrix3x3.h
@@ -0,0 +1,496 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for 3x3 matrices.
+ * \file IceMatrix3x3.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEMATRIX3X3_H__
+#define __ICEMATRIX3X3_H__
+
+ // Forward declarations
+ class Quat;
+
+ #define MATRIX3X3_EPSILON (1.0e-7f)
+
+ class ICEMATHS_API Matrix3x3
+ {
+ public:
+ //! Empty constructor
+ inline_ Matrix3x3() {}
+ //! Constructor from 9 values
+ inline_ Matrix3x3(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22)
+ {
+ m[0][0] = m00; m[0][1] = m01; m[0][2] = m02;
+ m[1][0] = m10; m[1][1] = m11; m[1][2] = m12;
+ m[2][0] = m20; m[2][1] = m21; m[2][2] = m22;
+ }
+ //! Copy constructor
+ inline_ Matrix3x3(const Matrix3x3& mat) { CopyMemory(m, &mat.m, 9*sizeof(float)); }
+ //! Destructor
+ inline_ ~Matrix3x3() {}
+
+ //! Assign values
+ inline_ void Set(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22)
+ {
+ m[0][0] = m00; m[0][1] = m01; m[0][2] = m02;
+ m[1][0] = m10; m[1][1] = m11; m[1][2] = m12;
+ m[2][0] = m20; m[2][1] = m21; m[2][2] = m22;
+ }
+
+ //! Sets the scale from a Point. The point is put on the diagonal.
+ inline_ void SetScale(const Point& p) { m[0][0] = p.x; m[1][1] = p.y; m[2][2] = p.z; }
+
+ //! Sets the scale from floats. Values are put on the diagonal.
+ inline_ void SetScale(float sx, float sy, float sz) { m[0][0] = sx; m[1][1] = sy; m[2][2] = sz; }
+
+ //! Scales from a Point. Each row is multiplied by a component.
+ inline_ void Scale(const Point& p)
+ {
+ m[0][0] *= p.x; m[0][1] *= p.x; m[0][2] *= p.x;
+ m[1][0] *= p.y; m[1][1] *= p.y; m[1][2] *= p.y;
+ m[2][0] *= p.z; m[2][1] *= p.z; m[2][2] *= p.z;
+ }
+
+ //! Scales from floats. Each row is multiplied by a value.
+ inline_ void Scale(float sx, float sy, float sz)
+ {
+ m[0][0] *= sx; m[0][1] *= sx; m[0][2] *= sx;
+ m[1][0] *= sy; m[1][1] *= sy; m[1][2] *= sy;
+ m[2][0] *= sz; m[2][1] *= sz; m[2][2] *= sz;
+ }
+
+ //! Copy from a Matrix3x3
+ inline_ void Copy(const Matrix3x3& source) { CopyMemory(m, source.m, 9*sizeof(float)); }
+
+ // Row-column access
+ //! Returns a row.
+ inline_ void GetRow(const udword r, Point& p) const { p.x = m[r][0]; p.y = m[r][1]; p.z = m[r][2]; }
+ //! Returns a row.
+ inline_ const Point& GetRow(const udword r) const { return *(const Point*)&m[r][0]; }
+ //! Returns a row.
+ inline_ Point& GetRow(const udword r) { return *(Point*)&m[r][0]; }
+ //! Sets a row.
+ inline_ void SetRow(const udword r, const Point& p) { m[r][0] = p.x; m[r][1] = p.y; m[r][2] = p.z; }
+ //! Returns a column.
+ inline_ void GetCol(const udword c, Point& p) const { p.x = m[0][c]; p.y = m[1][c]; p.z = m[2][c]; }
+ //! Sets a column.
+ inline_ void SetCol(const udword c, const Point& p) { m[0][c] = p.x; m[1][c] = p.y; m[2][c] = p.z; }
+
+ //! Computes the trace. The trace is the sum of the 3 diagonal components.
+ inline_ float Trace() const { return m[0][0] + m[1][1] + m[2][2]; }
+ //! Clears the matrix.
+ inline_ void Zero() { ZeroMemory(&m, sizeof(m)); }
+ //! Sets the identity matrix.
+ inline_ void Identity() { Zero(); m[0][0] = m[1][1] = m[2][2] = 1.0f; }
+ //! Checks for identity
+ inline_ bool IsIdentity() const
+ {
+ if(IR(m[0][0])!=IEEE_1_0) return false;
+ if(IR(m[0][1])!=0) return false;
+ if(IR(m[0][2])!=0) return false;
+
+ if(IR(m[1][0])!=0) return false;
+ if(IR(m[1][1])!=IEEE_1_0) return false;
+ if(IR(m[1][2])!=0) return false;
+
+ if(IR(m[2][0])!=0) return false;
+ if(IR(m[2][1])!=0) return false;
+ if(IR(m[2][2])!=IEEE_1_0) return false;
+
+ return true;
+ }
+
+ //! Checks matrix validity
+ inline_ BOOL IsValid() const
+ {
+ for(udword j=0;j<3;j++)
+ {
+ for(udword i=0;i<3;i++)
+ {
+ if(!IsValidFloat(m[j][i])) return FALSE;
+ }
+ }
+ return TRUE;
+ }
+
+ //! Makes a skew-symmetric matrix (a.k.a. Star(*) Matrix)
+ //! [ 0.0 -a.z a.y ]
+ //! [ a.z 0.0 -a.x ]
+ //! [ -a.y a.x 0.0 ]
+ //! This is also called a "cross matrix" since for any vectors A and B,
+ //! A^B = Skew(A) * B = - B * Skew(A);
+ inline_ void SkewSymmetric(const Point& a)
+ {
+ m[0][0] = 0.0f;
+ m[0][1] = -a.z;
+ m[0][2] = a.y;
+
+ m[1][0] = a.z;
+ m[1][1] = 0.0f;
+ m[1][2] = -a.x;
+
+ m[2][0] = -a.y;
+ m[2][1] = a.x;
+ m[2][2] = 0.0f;
+ }
+
+ //! Negates the matrix
+ inline_ void Neg()
+ {
+ m[0][0] = -m[0][0]; m[0][1] = -m[0][1]; m[0][2] = -m[0][2];
+ m[1][0] = -m[1][0]; m[1][1] = -m[1][1]; m[1][2] = -m[1][2];
+ m[2][0] = -m[2][0]; m[2][1] = -m[2][1]; m[2][2] = -m[2][2];
+ }
+
+ //! Neg from another matrix
+ inline_ void Neg(const Matrix3x3& mat)
+ {
+ m[0][0] = -mat.m[0][0]; m[0][1] = -mat.m[0][1]; m[0][2] = -mat.m[0][2];
+ m[1][0] = -mat.m[1][0]; m[1][1] = -mat.m[1][1]; m[1][2] = -mat.m[1][2];
+ m[2][0] = -mat.m[2][0]; m[2][1] = -mat.m[2][1]; m[2][2] = -mat.m[2][2];
+ }
+
+ //! Add another matrix
+ inline_ void Add(const Matrix3x3& mat)
+ {
+ m[0][0] += mat.m[0][0]; m[0][1] += mat.m[0][1]; m[0][2] += mat.m[0][2];
+ m[1][0] += mat.m[1][0]; m[1][1] += mat.m[1][1]; m[1][2] += mat.m[1][2];
+ m[2][0] += mat.m[2][0]; m[2][1] += mat.m[2][1]; m[2][2] += mat.m[2][2];
+ }
+
+ //! Sub another matrix
+ inline_ void Sub(const Matrix3x3& mat)
+ {
+ m[0][0] -= mat.m[0][0]; m[0][1] -= mat.m[0][1]; m[0][2] -= mat.m[0][2];
+ m[1][0] -= mat.m[1][0]; m[1][1] -= mat.m[1][1]; m[1][2] -= mat.m[1][2];
+ m[2][0] -= mat.m[2][0]; m[2][1] -= mat.m[2][1]; m[2][2] -= mat.m[2][2];
+ }
+ //! Mac
+ inline_ void Mac(const Matrix3x3& a, const Matrix3x3& b, float s)
+ {
+ m[0][0] = a.m[0][0] + b.m[0][0] * s;
+ m[0][1] = a.m[0][1] + b.m[0][1] * s;
+ m[0][2] = a.m[0][2] + b.m[0][2] * s;
+
+ m[1][0] = a.m[1][0] + b.m[1][0] * s;
+ m[1][1] = a.m[1][1] + b.m[1][1] * s;
+ m[1][2] = a.m[1][2] + b.m[1][2] * s;
+
+ m[2][0] = a.m[2][0] + b.m[2][0] * s;
+ m[2][1] = a.m[2][1] + b.m[2][1] * s;
+ m[2][2] = a.m[2][2] + b.m[2][2] * s;
+ }
+ //! Mac
+ inline_ void Mac(const Matrix3x3& a, float s)
+ {
+ m[0][0] += a.m[0][0] * s; m[0][1] += a.m[0][1] * s; m[0][2] += a.m[0][2] * s;
+ m[1][0] += a.m[1][0] * s; m[1][1] += a.m[1][1] * s; m[1][2] += a.m[1][2] * s;
+ m[2][0] += a.m[2][0] * s; m[2][1] += a.m[2][1] * s; m[2][2] += a.m[2][2] * s;
+ }
+
+ //! this = A * s
+ inline_ void Mult(const Matrix3x3& a, float s)
+ {
+ m[0][0] = a.m[0][0] * s; m[0][1] = a.m[0][1] * s; m[0][2] = a.m[0][2] * s;
+ m[1][0] = a.m[1][0] * s; m[1][1] = a.m[1][1] * s; m[1][2] = a.m[1][2] * s;
+ m[2][0] = a.m[2][0] * s; m[2][1] = a.m[2][1] * s; m[2][2] = a.m[2][2] * s;
+ }
+
+ inline_ void Add(const Matrix3x3& a, const Matrix3x3& b)
+ {
+ m[0][0] = a.m[0][0] + b.m[0][0]; m[0][1] = a.m[0][1] + b.m[0][1]; m[0][2] = a.m[0][2] + b.m[0][2];
+ m[1][0] = a.m[1][0] + b.m[1][0]; m[1][1] = a.m[1][1] + b.m[1][1]; m[1][2] = a.m[1][2] + b.m[1][2];
+ m[2][0] = a.m[2][0] + b.m[2][0]; m[2][1] = a.m[2][1] + b.m[2][1]; m[2][2] = a.m[2][2] + b.m[2][2];
+ }
+
+ inline_ void Sub(const Matrix3x3& a, const Matrix3x3& b)
+ {
+ m[0][0] = a.m[0][0] - b.m[0][0]; m[0][1] = a.m[0][1] - b.m[0][1]; m[0][2] = a.m[0][2] - b.m[0][2];
+ m[1][0] = a.m[1][0] - b.m[1][0]; m[1][1] = a.m[1][1] - b.m[1][1]; m[1][2] = a.m[1][2] - b.m[1][2];
+ m[2][0] = a.m[2][0] - b.m[2][0]; m[2][1] = a.m[2][1] - b.m[2][1]; m[2][2] = a.m[2][2] - b.m[2][2];
+ }
+
+ //! this = a * b
+ inline_ void Mult(const Matrix3x3& a, const Matrix3x3& b)
+ {
+ m[0][0] = a.m[0][0] * b.m[0][0] + a.m[0][1] * b.m[1][0] + a.m[0][2] * b.m[2][0];
+ m[0][1] = a.m[0][0] * b.m[0][1] + a.m[0][1] * b.m[1][1] + a.m[0][2] * b.m[2][1];
+ m[0][2] = a.m[0][0] * b.m[0][2] + a.m[0][1] * b.m[1][2] + a.m[0][2] * b.m[2][2];
+ m[1][0] = a.m[1][0] * b.m[0][0] + a.m[1][1] * b.m[1][0] + a.m[1][2] * b.m[2][0];
+ m[1][1] = a.m[1][0] * b.m[0][1] + a.m[1][1] * b.m[1][1] + a.m[1][2] * b.m[2][1];
+ m[1][2] = a.m[1][0] * b.m[0][2] + a.m[1][1] * b.m[1][2] + a.m[1][2] * b.m[2][2];
+ m[2][0] = a.m[2][0] * b.m[0][0] + a.m[2][1] * b.m[1][0] + a.m[2][2] * b.m[2][0];
+ m[2][1] = a.m[2][0] * b.m[0][1] + a.m[2][1] * b.m[1][1] + a.m[2][2] * b.m[2][1];
+ m[2][2] = a.m[2][0] * b.m[0][2] + a.m[2][1] * b.m[1][2] + a.m[2][2] * b.m[2][2];
+ }
+
+ //! this = transpose(a) * b
+ inline_ void MultAtB(const Matrix3x3& a, const Matrix3x3& b)
+ {
+ m[0][0] = a.m[0][0] * b.m[0][0] + a.m[1][0] * b.m[1][0] + a.m[2][0] * b.m[2][0];
+ m[0][1] = a.m[0][0] * b.m[0][1] + a.m[1][0] * b.m[1][1] + a.m[2][0] * b.m[2][1];
+ m[0][2] = a.m[0][0] * b.m[0][2] + a.m[1][0] * b.m[1][2] + a.m[2][0] * b.m[2][2];
+ m[1][0] = a.m[0][1] * b.m[0][0] + a.m[1][1] * b.m[1][0] + a.m[2][1] * b.m[2][0];
+ m[1][1] = a.m[0][1] * b.m[0][1] + a.m[1][1] * b.m[1][1] + a.m[2][1] * b.m[2][1];
+ m[1][2] = a.m[0][1] * b.m[0][2] + a.m[1][1] * b.m[1][2] + a.m[2][1] * b.m[2][2];
+ m[2][0] = a.m[0][2] * b.m[0][0] + a.m[1][2] * b.m[1][0] + a.m[2][2] * b.m[2][0];
+ m[2][1] = a.m[0][2] * b.m[0][1] + a.m[1][2] * b.m[1][1] + a.m[2][2] * b.m[2][1];
+ m[2][2] = a.m[0][2] * b.m[0][2] + a.m[1][2] * b.m[1][2] + a.m[2][2] * b.m[2][2];
+ }
+
+ //! this = a * transpose(b)
+ inline_ void MultABt(const Matrix3x3& a, const Matrix3x3& b)
+ {
+ m[0][0] = a.m[0][0] * b.m[0][0] + a.m[0][1] * b.m[0][1] + a.m[0][2] * b.m[0][2];
+ m[0][1] = a.m[0][0] * b.m[1][0] + a.m[0][1] * b.m[1][1] + a.m[0][2] * b.m[1][2];
+ m[0][2] = a.m[0][0] * b.m[2][0] + a.m[0][1] * b.m[2][1] + a.m[0][2] * b.m[2][2];
+ m[1][0] = a.m[1][0] * b.m[0][0] + a.m[1][1] * b.m[0][1] + a.m[1][2] * b.m[0][2];
+ m[1][1] = a.m[1][0] * b.m[1][0] + a.m[1][1] * b.m[1][1] + a.m[1][2] * b.m[1][2];
+ m[1][2] = a.m[1][0] * b.m[2][0] + a.m[1][1] * b.m[2][1] + a.m[1][2] * b.m[2][2];
+ m[2][0] = a.m[2][0] * b.m[0][0] + a.m[2][1] * b.m[0][1] + a.m[2][2] * b.m[0][2];
+ m[2][1] = a.m[2][0] * b.m[1][0] + a.m[2][1] * b.m[1][1] + a.m[2][2] * b.m[1][2];
+ m[2][2] = a.m[2][0] * b.m[2][0] + a.m[2][1] * b.m[2][1] + a.m[2][2] * b.m[2][2];
+ }
+
+ //! Makes a rotation matrix mapping vector "from" to vector "to".
+ Matrix3x3& FromTo(const Point& from, const Point& to);
+
+ //! Set a rotation matrix around the X axis.
+ //! 1 0 0
+ //! RX = 0 cx sx
+ //! 0 -sx cx
+ void RotX(float angle);
+ //! Set a rotation matrix around the Y axis.
+ //! cy 0 -sy
+ //! RY = 0 1 0
+ //! sy 0 cy
+ void RotY(float angle);
+ //! Set a rotation matrix around the Z axis.
+ //! cz sz 0
+ //! RZ = -sz cz 0
+ //! 0 0 1
+ void RotZ(float angle);
+ //! cy sx.sy -sy.cx
+ //! RY.RX 0 cx sx
+ //! sy -sx.cy cx.cy
+ void RotYX(float y, float x);
+
+ //! Make a rotation matrix about an arbitrary axis
+ Matrix3x3& Rot(float angle, const Point& axis);
+
+ //! Transpose the matrix.
+ void Transpose()
+ {
+ IR(m[1][0]) ^= IR(m[0][1]); IR(m[0][1]) ^= IR(m[1][0]); IR(m[1][0]) ^= IR(m[0][1]);
+ IR(m[2][0]) ^= IR(m[0][2]); IR(m[0][2]) ^= IR(m[2][0]); IR(m[2][0]) ^= IR(m[0][2]);
+ IR(m[2][1]) ^= IR(m[1][2]); IR(m[1][2]) ^= IR(m[2][1]); IR(m[2][1]) ^= IR(m[1][2]);
+ }
+
+ //! this = Transpose(a)
+ void Transpose(const Matrix3x3& a)
+ {
+ m[0][0] = a.m[0][0]; m[0][1] = a.m[1][0]; m[0][2] = a.m[2][0];
+ m[1][0] = a.m[0][1]; m[1][1] = a.m[1][1]; m[1][2] = a.m[2][1];
+ m[2][0] = a.m[0][2]; m[2][1] = a.m[1][2]; m[2][2] = a.m[2][2];
+ }
+
+ //! Compute the determinant of the matrix. We use the rule of Sarrus.
+ float Determinant() const
+ {
+ return (m[0][0]*m[1][1]*m[2][2] + m[0][1]*m[1][2]*m[2][0] + m[0][2]*m[1][0]*m[2][1])
+ - (m[2][0]*m[1][1]*m[0][2] + m[2][1]*m[1][2]*m[0][0] + m[2][2]*m[1][0]*m[0][1]);
+ }
+/*
+ //! Compute a cofactor. Used for matrix inversion.
+ float CoFactor(ubyte row, ubyte column) const
+ {
+ static sdword gIndex[3+2] = { 0, 1, 2, 0, 1 };
+ return (m[gIndex[row+1]][gIndex[column+1]]*m[gIndex[row+2]][gIndex[column+2]] - m[gIndex[row+2]][gIndex[column+1]]*m[gIndex[row+1]][gIndex[column+2]]);
+ }
+*/
+ //! Invert the matrix. Determinant must be different from zero, else matrix can't be inverted.
+ Matrix3x3& Invert()
+ {
+ float Det = Determinant(); // Must be !=0
+ float OneOverDet = 1.0f / Det;
+
+ Matrix3x3 Temp;
+ Temp.m[0][0] = +(m[1][1] * m[2][2] - m[2][1] * m[1][2]) * OneOverDet;
+ Temp.m[1][0] = -(m[1][0] * m[2][2] - m[2][0] * m[1][2]) * OneOverDet;
+ Temp.m[2][0] = +(m[1][0] * m[2][1] - m[2][0] * m[1][1]) * OneOverDet;
+ Temp.m[0][1] = -(m[0][1] * m[2][2] - m[2][1] * m[0][2]) * OneOverDet;
+ Temp.m[1][1] = +(m[0][0] * m[2][2] - m[2][0] * m[0][2]) * OneOverDet;
+ Temp.m[2][1] = -(m[0][0] * m[2][1] - m[2][0] * m[0][1]) * OneOverDet;
+ Temp.m[0][2] = +(m[0][1] * m[1][2] - m[1][1] * m[0][2]) * OneOverDet;
+ Temp.m[1][2] = -(m[0][0] * m[1][2] - m[1][0] * m[0][2]) * OneOverDet;
+ Temp.m[2][2] = +(m[0][0] * m[1][1] - m[1][0] * m[0][1]) * OneOverDet;
+
+ *this = Temp;
+
+ return *this;
+ }
+
+ Matrix3x3& Normalize();
+
+ //! this = exp(a)
+ Matrix3x3& Exp(const Matrix3x3& a);
+
+void FromQuat(const Quat &q);
+void FromQuatL2(const Quat &q, float l2);
+
+ // Arithmetic operators
+ //! Operator for Matrix3x3 Plus = Matrix3x3 + Matrix3x3;
+ inline_ Matrix3x3 operator+(const Matrix3x3& mat) const
+ {
+ return Matrix3x3(
+ m[0][0] + mat.m[0][0], m[0][1] + mat.m[0][1], m[0][2] + mat.m[0][2],
+ m[1][0] + mat.m[1][0], m[1][1] + mat.m[1][1], m[1][2] + mat.m[1][2],
+ m[2][0] + mat.m[2][0], m[2][1] + mat.m[2][1], m[2][2] + mat.m[2][2]);
+ }
+
+ //! Operator for Matrix3x3 Minus = Matrix3x3 - Matrix3x3;
+ inline_ Matrix3x3 operator-(const Matrix3x3& mat) const
+ {
+ return Matrix3x3(
+ m[0][0] - mat.m[0][0], m[0][1] - mat.m[0][1], m[0][2] - mat.m[0][2],
+ m[1][0] - mat.m[1][0], m[1][1] - mat.m[1][1], m[1][2] - mat.m[1][2],
+ m[2][0] - mat.m[2][0], m[2][1] - mat.m[2][1], m[2][2] - mat.m[2][2]);
+ }
+
+ //! Operator for Matrix3x3 Mul = Matrix3x3 * Matrix3x3;
+ inline_ Matrix3x3 operator*(const Matrix3x3& mat) const
+ {
+ return Matrix3x3(
+ m[0][0]*mat.m[0][0] + m[0][1]*mat.m[1][0] + m[0][2]*mat.m[2][0],
+ m[0][0]*mat.m[0][1] + m[0][1]*mat.m[1][1] + m[0][2]*mat.m[2][1],
+ m[0][0]*mat.m[0][2] + m[0][1]*mat.m[1][2] + m[0][2]*mat.m[2][2],
+
+ m[1][0]*mat.m[0][0] + m[1][1]*mat.m[1][0] + m[1][2]*mat.m[2][0],
+ m[1][0]*mat.m[0][1] + m[1][1]*mat.m[1][1] + m[1][2]*mat.m[2][1],
+ m[1][0]*mat.m[0][2] + m[1][1]*mat.m[1][2] + m[1][2]*mat.m[2][2],
+
+ m[2][0]*mat.m[0][0] + m[2][1]*mat.m[1][0] + m[2][2]*mat.m[2][0],
+ m[2][0]*mat.m[0][1] + m[2][1]*mat.m[1][1] + m[2][2]*mat.m[2][1],
+ m[2][0]*mat.m[0][2] + m[2][1]*mat.m[1][2] + m[2][2]*mat.m[2][2]);
+ }
+
+ //! Operator for Point Mul = Matrix3x3 * Point;
+ inline_ Point operator*(const Point& v) const { return Point(GetRow(0)|v, GetRow(1)|v, GetRow(2)|v); }
+
+ //! Operator for Matrix3x3 Mul = Matrix3x3 * float;
+ inline_ Matrix3x3 operator*(float s) const
+ {
+ return Matrix3x3(
+ m[0][0]*s, m[0][1]*s, m[0][2]*s,
+ m[1][0]*s, m[1][1]*s, m[1][2]*s,
+ m[2][0]*s, m[2][1]*s, m[2][2]*s);
+ }
+
+ //! Operator for Matrix3x3 Mul = float * Matrix3x3;
+ inline_ friend Matrix3x3 operator*(float s, const Matrix3x3& mat)
+ {
+ return Matrix3x3(
+ s*mat.m[0][0], s*mat.m[0][1], s*mat.m[0][2],
+ s*mat.m[1][0], s*mat.m[1][1], s*mat.m[1][2],
+ s*mat.m[2][0], s*mat.m[2][1], s*mat.m[2][2]);
+ }
+
+ //! Operator for Matrix3x3 Div = Matrix3x3 / float;
+ inline_ Matrix3x3 operator/(float s) const
+ {
+ if (s) s = 1.0f / s;
+ return Matrix3x3(
+ m[0][0]*s, m[0][1]*s, m[0][2]*s,
+ m[1][0]*s, m[1][1]*s, m[1][2]*s,
+ m[2][0]*s, m[2][1]*s, m[2][2]*s);
+ }
+
+ //! Operator for Matrix3x3 Div = float / Matrix3x3;
+ inline_ friend Matrix3x3 operator/(float s, const Matrix3x3& mat)
+ {
+ return Matrix3x3(
+ s/mat.m[0][0], s/mat.m[0][1], s/mat.m[0][2],
+ s/mat.m[1][0], s/mat.m[1][1], s/mat.m[1][2],
+ s/mat.m[2][0], s/mat.m[2][1], s/mat.m[2][2]);
+ }
+
+ //! Operator for Matrix3x3 += Matrix3x3
+ inline_ Matrix3x3& operator+=(const Matrix3x3& mat)
+ {
+ m[0][0] += mat.m[0][0]; m[0][1] += mat.m[0][1]; m[0][2] += mat.m[0][2];
+ m[1][0] += mat.m[1][0]; m[1][1] += mat.m[1][1]; m[1][2] += mat.m[1][2];
+ m[2][0] += mat.m[2][0]; m[2][1] += mat.m[2][1]; m[2][2] += mat.m[2][2];
+ return *this;
+ }
+
+ //! Operator for Matrix3x3 -= Matrix3x3
+ inline_ Matrix3x3& operator-=(const Matrix3x3& mat)
+ {
+ m[0][0] -= mat.m[0][0]; m[0][1] -= mat.m[0][1]; m[0][2] -= mat.m[0][2];
+ m[1][0] -= mat.m[1][0]; m[1][1] -= mat.m[1][1]; m[1][2] -= mat.m[1][2];
+ m[2][0] -= mat.m[2][0]; m[2][1] -= mat.m[2][1]; m[2][2] -= mat.m[2][2];
+ return *this;
+ }
+
+ //! Operator for Matrix3x3 *= Matrix3x3
+ inline_ Matrix3x3& operator*=(const Matrix3x3& mat)
+ {
+ Point TempRow;
+
+ GetRow(0, TempRow);
+ m[0][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0];
+ m[0][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1];
+ m[0][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2];
+
+ GetRow(1, TempRow);
+ m[1][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0];
+ m[1][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1];
+ m[1][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2];
+
+ GetRow(2, TempRow);
+ m[2][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0];
+ m[2][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1];
+ m[2][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2];
+ return *this;
+ }
+
+ //! Operator for Matrix3x3 *= float
+ inline_ Matrix3x3& operator*=(float s)
+ {
+ m[0][0] *= s; m[0][1] *= s; m[0][2] *= s;
+ m[1][0] *= s; m[1][1] *= s; m[1][2] *= s;
+ m[2][0] *= s; m[2][1] *= s; m[2][2] *= s;
+ return *this;
+ }
+
+ //! Operator for Matrix3x3 /= float
+ inline_ Matrix3x3& operator/=(float s)
+ {
+ if (s) s = 1.0f / s;
+ m[0][0] *= s; m[0][1] *= s; m[0][2] *= s;
+ m[1][0] *= s; m[1][1] *= s; m[1][2] *= s;
+ m[2][0] *= s; m[2][1] *= s; m[2][2] *= s;
+ return *this;
+ }
+
+ // Cast operators
+ //! Cast a Matrix3x3 to a Matrix4x4.
+ operator Matrix4x4() const;
+ //! Cast a Matrix3x3 to a Quat.
+ operator Quat() const;
+
+ inline_ const Point& operator[](int row) const { return *(const Point*)&m[row][0]; }
+ inline_ Point& operator[](int row) { return *(Point*)&m[row][0]; }
+
+ public:
+
+ float m[3][3];
+ };
+
+#endif // __ICEMATRIX3X3_H__
+
diff --git a/Opcode/Ice/IceMatrix4x4.cpp b/Opcode/Ice/IceMatrix4x4.cpp
new file mode 100644
index 0000000..4c539c9
--- /dev/null
+++ b/Opcode/Ice/IceMatrix4x4.cpp
@@ -0,0 +1,135 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for 4x4 matrices.
+ * \file IceMatrix4x4.cpp
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 4x4 matrix.
+ * DirectX-compliant, ie row-column order, ie m[Row][Col].
+ * Same as:
+ * m11 m12 m13 m14 first row.
+ * m21 m22 m23 m24 second row.
+ * m31 m32 m33 m34 third row.
+ * m41 m42 m43 m44 fourth row.
+ * Translation is (m41, m42, m43), (m14, m24, m34, m44) = (0, 0, 0, 1).
+ * Stored in memory as m11 m12 m13 m14 m21...
+ *
+ * Multiplication rules:
+ *
+ * [x'y'z'1] = [xyz1][M]
+ *
+ * x' = x*m11 + y*m21 + z*m31 + m41
+ * y' = x*m12 + y*m22 + z*m32 + m42
+ * z' = x*m13 + y*m23 + z*m33 + m43
+ * 1' = 0 + 0 + 0 + m44
+ *
+ * \class Matrix4x4
+ * \author Pierre Terdiman
+ * \version 1.0
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Inverts a PR matrix. (which only contains a rotation and a translation)
+ * This is faster and less subject to FPU errors than the generic inversion code.
+ *
+ * \relates Matrix4x4
+ * \fn InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src)
+ * \param dest [out] destination matrix
+ * \param src [in] source matrix
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ICEMATHS_API void IceMaths::InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src)
+{
+ dest.m[0][0] = src.m[0][0];
+ dest.m[1][0] = src.m[0][1];
+ dest.m[2][0] = src.m[0][2];
+ dest.m[3][0] = -(src.m[3][0]*src.m[0][0] + src.m[3][1]*src.m[0][1] + src.m[3][2]*src.m[0][2]);
+
+ dest.m[0][1] = src.m[1][0];
+ dest.m[1][1] = src.m[1][1];
+ dest.m[2][1] = src.m[1][2];
+ dest.m[3][1] = -(src.m[3][0]*src.m[1][0] + src.m[3][1]*src.m[1][1] + src.m[3][2]*src.m[1][2]);
+
+ dest.m[0][2] = src.m[2][0];
+ dest.m[1][2] = src.m[2][1];
+ dest.m[2][2] = src.m[2][2];
+ dest.m[3][2] = -(src.m[3][0]*src.m[2][0] + src.m[3][1]*src.m[2][1] + src.m[3][2]*src.m[2][2]);
+
+ dest.m[0][3] = 0.0f;
+ dest.m[1][3] = 0.0f;
+ dest.m[2][3] = 0.0f;
+ dest.m[3][3] = 1.0f;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Compute the cofactor of the Matrix at a specified location
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float Matrix4x4::CoFactor(udword row, udword col) const
+{
+ return (( m[(row+1)&3][(col+1)&3]*m[(row+2)&3][(col+2)&3]*m[(row+3)&3][(col+3)&3] +
+ m[(row+1)&3][(col+2)&3]*m[(row+2)&3][(col+3)&3]*m[(row+3)&3][(col+1)&3] +
+ m[(row+1)&3][(col+3)&3]*m[(row+2)&3][(col+1)&3]*m[(row+3)&3][(col+2)&3])
+ - (m[(row+3)&3][(col+1)&3]*m[(row+2)&3][(col+2)&3]*m[(row+1)&3][(col+3)&3] +
+ m[(row+3)&3][(col+2)&3]*m[(row+2)&3][(col+3)&3]*m[(row+1)&3][(col+1)&3] +
+ m[(row+3)&3][(col+3)&3]*m[(row+2)&3][(col+1)&3]*m[(row+1)&3][(col+2)&3])) * ((row + col) & 1 ? -1.0f : +1.0f);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Compute the determinant of the Matrix
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float Matrix4x4::Determinant() const
+{
+ return m[0][0] * CoFactor(0, 0) +
+ m[0][1] * CoFactor(0, 1) +
+ m[0][2] * CoFactor(0, 2) +
+ m[0][3] * CoFactor(0, 3);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Compute the inverse of the matrix
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Matrix4x4& Matrix4x4::Invert()
+{
+ float Det = Determinant();
+ Matrix4x4 Temp;
+
+ if(fabsf(Det) < MATRIX4X4_EPSILON)
+ return *this; // The matrix is not invertible! Singular case!
+
+ float IDet = 1.0f / Det;
+
+ Temp.m[0][0] = CoFactor(0,0) * IDet;
+ Temp.m[1][0] = CoFactor(0,1) * IDet;
+ Temp.m[2][0] = CoFactor(0,2) * IDet;
+ Temp.m[3][0] = CoFactor(0,3) * IDet;
+ Temp.m[0][1] = CoFactor(1,0) * IDet;
+ Temp.m[1][1] = CoFactor(1,1) * IDet;
+ Temp.m[2][1] = CoFactor(1,2) * IDet;
+ Temp.m[3][1] = CoFactor(1,3) * IDet;
+ Temp.m[0][2] = CoFactor(2,0) * IDet;
+ Temp.m[1][2] = CoFactor(2,1) * IDet;
+ Temp.m[2][2] = CoFactor(2,2) * IDet;
+ Temp.m[3][2] = CoFactor(2,3) * IDet;
+ Temp.m[0][3] = CoFactor(3,0) * IDet;
+ Temp.m[1][3] = CoFactor(3,1) * IDet;
+ Temp.m[2][3] = CoFactor(3,2) * IDet;
+ Temp.m[3][3] = CoFactor(3,3) * IDet;
+
+ *this = Temp;
+
+ return *this;
+}
+
diff --git a/Opcode/Ice/IceMatrix4x4.h b/Opcode/Ice/IceMatrix4x4.h
new file mode 100644
index 0000000..d61a461
--- /dev/null
+++ b/Opcode/Ice/IceMatrix4x4.h
@@ -0,0 +1,455 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for 4x4 matrices.
+ * \file IceMatrix4x4.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEMATRIX4X4_H__
+#define __ICEMATRIX4X4_H__
+
+ // Forward declarations
+ class PRS;
+ class PR;
+
+ #define MATRIX4X4_EPSILON (1.0e-7f)
+
+ class ICEMATHS_API Matrix4x4
+ {
+// void LUBackwardSubstitution( sdword *indx, float* b );
+// void LUDecomposition( sdword* indx, float* d );
+
+ public:
+ //! Empty constructor.
+ inline_ Matrix4x4() {}
+ //! Constructor from 16 values
+ inline_ Matrix4x4( float m00, float m01, float m02, float m03,
+ float m10, float m11, float m12, float m13,
+ float m20, float m21, float m22, float m23,
+ float m30, float m31, float m32, float m33)
+ {
+ m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; m[0][3] = m03;
+ m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; m[1][3] = m13;
+ m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; m[2][3] = m23;
+ m[3][0] = m30; m[3][1] = m31; m[3][2] = m32; m[3][3] = m33;
+ }
+ //! Copy constructor
+ inline_ Matrix4x4(const Matrix4x4& mat) { CopyMemory(m, &mat.m, 16*sizeof(float)); }
+ //! Destructor.
+ inline_ ~Matrix4x4() {}
+
+ //! Assign values (rotation only)
+ inline_ Matrix4x4& Set( float m00, float m01, float m02,
+ float m10, float m11, float m12,
+ float m20, float m21, float m22)
+ {
+ m[0][0] = m00; m[0][1] = m01; m[0][2] = m02;
+ m[1][0] = m10; m[1][1] = m11; m[1][2] = m12;
+ m[2][0] = m20; m[2][1] = m21; m[2][2] = m22;
+ return *this;
+ }
+ //! Assign values
+ inline_ Matrix4x4& Set( float m00, float m01, float m02, float m03,
+ float m10, float m11, float m12, float m13,
+ float m20, float m21, float m22, float m23,
+ float m30, float m31, float m32, float m33)
+ {
+ m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; m[0][3] = m03;
+ m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; m[1][3] = m13;
+ m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; m[2][3] = m23;
+ m[3][0] = m30; m[3][1] = m31; m[3][2] = m32; m[3][3] = m33;
+ return *this;
+ }
+
+ //! Copy from a Matrix4x4
+ inline_ void Copy(const Matrix4x4& source) { CopyMemory(m, source.m, 16*sizeof(float)); }
+
+ // Row-column access
+ //! Returns a row.
+ inline_ void GetRow(const udword r, HPoint& p) const { p.x=m[r][0]; p.y=m[r][1]; p.z=m[r][2]; p.w=m[r][3]; }
+ //! Returns a row.
+ inline_ void GetRow(const udword r, Point& p) const { p.x=m[r][0]; p.y=m[r][1]; p.z=m[r][2]; }
+ //! Returns a row.
+ inline_ const HPoint& GetRow(const udword r) const { return *(const HPoint*)&m[r][0]; }
+ //! Returns a row.
+ inline_ HPoint& GetRow(const udword r) { return *(HPoint*)&m[r][0]; }
+ //! Sets a row.
+ inline_ void SetRow(const udword r, const HPoint& p) { m[r][0]=p.x; m[r][1]=p.y; m[r][2]=p.z; m[r][3]=p.w; }
+ //! Sets a row.
+ inline_ void SetRow(const udword r, const Point& p) { m[r][0]=p.x; m[r][1]=p.y; m[r][2]=p.z; m[r][3]= (r!=3) ? 0.0f : 1.0f; }
+ //! Returns a column.
+ inline_ void GetCol(const udword c, HPoint& p) const { p.x=m[0][c]; p.y=m[1][c]; p.z=m[2][c]; p.w=m[3][c]; }
+ //! Returns a column.
+ inline_ void GetCol(const udword c, Point& p) const { p.x=m[0][c]; p.y=m[1][c]; p.z=m[2][c]; }
+ //! Sets a column.
+ inline_ void SetCol(const udword c, const HPoint& p) { m[0][c]=p.x; m[1][c]=p.y; m[2][c]=p.z; m[3][c]=p.w; }
+ //! Sets a column.
+ inline_ void SetCol(const udword c, const Point& p) { m[0][c]=p.x; m[1][c]=p.y; m[2][c]=p.z; m[3][c]= (c!=3) ? 0.0f : 1.0f; }
+
+ // Translation
+ //! Returns the translation part of the matrix.
+ inline_ const HPoint& GetTrans() const { return GetRow(3); }
+ //! Gets the translation part of the matrix
+ inline_ void GetTrans(Point& p) const { p.x=m[3][0]; p.y=m[3][1]; p.z=m[3][2]; }
+ //! Sets the translation part of the matrix, from a Point.
+ inline_ void SetTrans(const Point& p) { m[3][0]=p.x; m[3][1]=p.y; m[3][2]=p.z; }
+ //! Sets the translation part of the matrix, from a HPoint.
+ inline_ void SetTrans(const HPoint& p) { m[3][0]=p.x; m[3][1]=p.y; m[3][2]=p.z; m[3][3]=p.w; }
+ //! Sets the translation part of the matrix, from floats.
+ inline_ void SetTrans(float tx, float ty, float tz) { m[3][0]=tx; m[3][1]=ty; m[3][2]=tz; }
+
+ // Scale
+ //! Sets the scale from a Point. The point is put on the diagonal.
+ inline_ void SetScale(const Point& p) { m[0][0]=p.x; m[1][1]=p.y; m[2][2]=p.z; }
+ //! Sets the scale from floats. Values are put on the diagonal.
+ inline_ void SetScale(float sx, float sy, float sz) { m[0][0]=sx; m[1][1]=sy; m[2][2]=sz; }
+ //! Scales from a Point. Each row is multiplied by a component.
+ void Scale(const Point& p)
+ {
+ m[0][0] *= p.x; m[1][0] *= p.y; m[2][0] *= p.z;
+ m[0][1] *= p.x; m[1][1] *= p.y; m[2][1] *= p.z;
+ m[0][2] *= p.x; m[1][2] *= p.y; m[2][2] *= p.z;
+ }
+ //! Scales from floats. Each row is multiplied by a value.
+ void Scale(float sx, float sy, float sz)
+ {
+ m[0][0] *= sx; m[1][0] *= sy; m[2][0] *= sz;
+ m[0][1] *= sx; m[1][1] *= sy; m[2][1] *= sz;
+ m[0][2] *= sx; m[1][2] *= sy; m[2][2] *= sz;
+ }
+/*
+ //! Returns a row.
+ inline_ HPoint GetRow(const udword row) const { return mRow[row]; }
+ //! Sets a row.
+ inline_ Matrix4x4& SetRow(const udword row, const HPoint& p) { mRow[row] = p; return *this; }
+ //! Sets a row.
+ Matrix4x4& SetRow(const udword row, const Point& p)
+ {
+ m[row][0] = p.x;
+ m[row][1] = p.y;
+ m[row][2] = p.z;
+ m[row][3] = (row != 3) ? 0.0f : 1.0f;
+ return *this;
+ }
+ //! Returns a column.
+ HPoint GetCol(const udword col) const
+ {
+ HPoint Res;
+ Res.x = m[0][col];
+ Res.y = m[1][col];
+ Res.z = m[2][col];
+ Res.w = m[3][col];
+ return Res;
+ }
+ //! Sets a column.
+ Matrix4x4& SetCol(const udword col, const HPoint& p)
+ {
+ m[0][col] = p.x;
+ m[1][col] = p.y;
+ m[2][col] = p.z;
+ m[3][col] = p.w;
+ return *this;
+ }
+ //! Sets a column.
+ Matrix4x4& SetCol(const udword col, const Point& p)
+ {
+ m[0][col] = p.x;
+ m[1][col] = p.y;
+ m[2][col] = p.z;
+ m[3][col] = (col != 3) ? 0.0f : 1.0f;
+ return *this;
+ }
+*/
+ //! Computes the trace. The trace is the sum of the 4 diagonal components.
+ inline_ float Trace() const { return m[0][0] + m[1][1] + m[2][2] + m[3][3]; }
+ //! Computes the trace of the upper 3x3 matrix.
+ inline_ float Trace3x3() const { return m[0][0] + m[1][1] + m[2][2]; }
+ //! Clears the matrix.
+ inline_ void Zero() { ZeroMemory(&m, sizeof(m)); }
+ //! Sets the identity matrix.
+ inline_ void Identity() { Zero(); m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1.0f; }
+ //! Checks for identity
+ inline_ bool IsIdentity() const
+ {
+ if(IR(m[0][0])!=IEEE_1_0) return false;
+ if(IR(m[0][1])!=0) return false;
+ if(IR(m[0][2])!=0) return false;
+ if(IR(m[0][3])!=0) return false;
+
+ if(IR(m[1][0])!=0) return false;
+ if(IR(m[1][1])!=IEEE_1_0) return false;
+ if(IR(m[1][2])!=0) return false;
+ if(IR(m[1][3])!=0) return false;
+
+ if(IR(m[2][0])!=0) return false;
+ if(IR(m[2][1])!=0) return false;
+ if(IR(m[2][2])!=IEEE_1_0) return false;
+ if(IR(m[2][3])!=0) return false;
+
+ if(IR(m[3][0])!=0) return false;
+ if(IR(m[3][1])!=0) return false;
+ if(IR(m[3][2])!=0) return false;
+ if(IR(m[3][3])!=IEEE_1_0) return false;
+ return true;
+ }
+
+ //! Checks matrix validity
+ inline_ BOOL IsValid() const
+ {
+ for(udword j=0;j<4;j++)
+ {
+ for(udword i=0;i<4;i++)
+ {
+ if(!IsValidFloat(m[j][i])) return FALSE;
+ }
+ }
+ return TRUE;
+ }
+
+ //! Sets a rotation matrix around the X axis.
+ void RotX(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[1][1] = m[2][2] = Cos; m[2][1] = -Sin; m[1][2] = Sin; }
+ //! Sets a rotation matrix around the Y axis.
+ void RotY(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[0][0] = m[2][2] = Cos; m[2][0] = Sin; m[0][2] = -Sin; }
+ //! Sets a rotation matrix around the Z axis.
+ void RotZ(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[0][0] = m[1][1] = Cos; m[1][0] = -Sin; m[0][1] = Sin; }
+
+ //! Makes a rotation matrix about an arbitrary axis
+ Matrix4x4& Rot(float angle, Point& p1, Point& p2);
+
+ //! Transposes the matrix.
+ void Transpose()
+ {
+ IR(m[1][0]) ^= IR(m[0][1]); IR(m[0][1]) ^= IR(m[1][0]); IR(m[1][0]) ^= IR(m[0][1]);
+ IR(m[2][0]) ^= IR(m[0][2]); IR(m[0][2]) ^= IR(m[2][0]); IR(m[2][0]) ^= IR(m[0][2]);
+ IR(m[3][0]) ^= IR(m[0][3]); IR(m[0][3]) ^= IR(m[3][0]); IR(m[3][0]) ^= IR(m[0][3]);
+ IR(m[1][2]) ^= IR(m[2][1]); IR(m[2][1]) ^= IR(m[1][2]); IR(m[1][2]) ^= IR(m[2][1]);
+ IR(m[1][3]) ^= IR(m[3][1]); IR(m[3][1]) ^= IR(m[1][3]); IR(m[1][3]) ^= IR(m[3][1]);
+ IR(m[2][3]) ^= IR(m[3][2]); IR(m[3][2]) ^= IR(m[2][3]); IR(m[2][3]) ^= IR(m[3][2]);
+ }
+
+ //! Computes a cofactor. Used for matrix inversion.
+ float CoFactor(udword row, udword col) const;
+ //! Computes the determinant of the matrix.
+ float Determinant() const;
+ //! Inverts the matrix. Determinant must be different from zero, else matrix can't be inverted.
+ Matrix4x4& Invert();
+// Matrix& ComputeAxisMatrix(Point& axis, float angle);
+
+ // Cast operators
+ //! Casts a Matrix4x4 to a Matrix3x3.
+ inline_ operator Matrix3x3() const
+ {
+ return Matrix3x3(
+ m[0][0], m[0][1], m[0][2],
+ m[1][0], m[1][1], m[1][2],
+ m[2][0], m[2][1], m[2][2]);
+ }
+ //! Casts a Matrix4x4 to a Quat.
+ operator Quat() const;
+ //! Casts a Matrix4x4 to a PR.
+ operator PR() const;
+
+ // Arithmetic operators
+ //! Operator for Matrix4x4 Plus = Matrix4x4 + Matrix4x4;
+ inline_ Matrix4x4 operator+(const Matrix4x4& mat) const
+ {
+ return Matrix4x4(
+ m[0][0]+mat.m[0][0], m[0][1]+mat.m[0][1], m[0][2]+mat.m[0][2], m[0][3]+mat.m[0][3],
+ m[1][0]+mat.m[1][0], m[1][1]+mat.m[1][1], m[1][2]+mat.m[1][2], m[1][3]+mat.m[1][3],
+ m[2][0]+mat.m[2][0], m[2][1]+mat.m[2][1], m[2][2]+mat.m[2][2], m[2][3]+mat.m[2][3],
+ m[3][0]+mat.m[3][0], m[3][1]+mat.m[3][1], m[3][2]+mat.m[3][2], m[3][3]+mat.m[3][3]);
+ }
+
+ //! Operator for Matrix4x4 Minus = Matrix4x4 - Matrix4x4;
+ inline_ Matrix4x4 operator-(const Matrix4x4& mat) const
+ {
+ return Matrix4x4(
+ m[0][0]-mat.m[0][0], m[0][1]-mat.m[0][1], m[0][2]-mat.m[0][2], m[0][3]-mat.m[0][3],
+ m[1][0]-mat.m[1][0], m[1][1]-mat.m[1][1], m[1][2]-mat.m[1][2], m[1][3]-mat.m[1][3],
+ m[2][0]-mat.m[2][0], m[2][1]-mat.m[2][1], m[2][2]-mat.m[2][2], m[2][3]-mat.m[2][3],
+ m[3][0]-mat.m[3][0], m[3][1]-mat.m[3][1], m[3][2]-mat.m[3][2], m[3][3]-mat.m[3][3]);
+ }
+
+ //! Operator for Matrix4x4 Mul = Matrix4x4 * Matrix4x4;
+ inline_ Matrix4x4 operator*(const Matrix4x4& mat) const
+ {
+ return Matrix4x4(
+ m[0][0]*mat.m[0][0] + m[0][1]*mat.m[1][0] + m[0][2]*mat.m[2][0] + m[0][3]*mat.m[3][0],
+ m[0][0]*mat.m[0][1] + m[0][1]*mat.m[1][1] + m[0][2]*mat.m[2][1] + m[0][3]*mat.m[3][1],
+ m[0][0]*mat.m[0][2] + m[0][1]*mat.m[1][2] + m[0][2]*mat.m[2][2] + m[0][3]*mat.m[3][2],
+ m[0][0]*mat.m[0][3] + m[0][1]*mat.m[1][3] + m[0][2]*mat.m[2][3] + m[0][3]*mat.m[3][3],
+
+ m[1][0]*mat.m[0][0] + m[1][1]*mat.m[1][0] + m[1][2]*mat.m[2][0] + m[1][3]*mat.m[3][0],
+ m[1][0]*mat.m[0][1] + m[1][1]*mat.m[1][1] + m[1][2]*mat.m[2][1] + m[1][3]*mat.m[3][1],
+ m[1][0]*mat.m[0][2] + m[1][1]*mat.m[1][2] + m[1][2]*mat.m[2][2] + m[1][3]*mat.m[3][2],
+ m[1][0]*mat.m[0][3] + m[1][1]*mat.m[1][3] + m[1][2]*mat.m[2][3] + m[1][3]*mat.m[3][3],
+
+ m[2][0]*mat.m[0][0] + m[2][1]*mat.m[1][0] + m[2][2]*mat.m[2][0] + m[2][3]*mat.m[3][0],
+ m[2][0]*mat.m[0][1] + m[2][1]*mat.m[1][1] + m[2][2]*mat.m[2][1] + m[2][3]*mat.m[3][1],
+ m[2][0]*mat.m[0][2] + m[2][1]*mat.m[1][2] + m[2][2]*mat.m[2][2] + m[2][3]*mat.m[3][2],
+ m[2][0]*mat.m[0][3] + m[2][1]*mat.m[1][3] + m[2][2]*mat.m[2][3] + m[2][3]*mat.m[3][3],
+
+ m[3][0]*mat.m[0][0] + m[3][1]*mat.m[1][0] + m[3][2]*mat.m[2][0] + m[3][3]*mat.m[3][0],
+ m[3][0]*mat.m[0][1] + m[3][1]*mat.m[1][1] + m[3][2]*mat.m[2][1] + m[3][3]*mat.m[3][1],
+ m[3][0]*mat.m[0][2] + m[3][1]*mat.m[1][2] + m[3][2]*mat.m[2][2] + m[3][3]*mat.m[3][2],
+ m[3][0]*mat.m[0][3] + m[3][1]*mat.m[1][3] + m[3][2]*mat.m[2][3] + m[3][3]*mat.m[3][3]);
+ }
+
+ //! Operator for HPoint Mul = Matrix4x4 * HPoint;
+ inline_ HPoint operator*(const HPoint& v) const { return HPoint(GetRow(0)|v, GetRow(1)|v, GetRow(2)|v, GetRow(3)|v); }
+
+ //! Operator for Point Mul = Matrix4x4 * Point;
+ inline_ Point operator*(const Point& v) const
+ {
+ return Point( m[0][0]*v.x + m[0][1]*v.y + m[0][2]*v.z + m[0][3],
+ m[1][0]*v.x + m[1][1]*v.y + m[1][2]*v.z + m[1][3],
+ m[2][0]*v.x + m[2][1]*v.y + m[2][2]*v.z + m[2][3] );
+ }
+
+ //! Operator for Matrix4x4 Scale = Matrix4x4 * float;
+ inline_ Matrix4x4 operator*(float s) const
+ {
+ return Matrix4x4(
+ m[0][0]*s, m[0][1]*s, m[0][2]*s, m[0][3]*s,
+ m[1][0]*s, m[1][1]*s, m[1][2]*s, m[1][3]*s,
+ m[2][0]*s, m[2][1]*s, m[2][2]*s, m[2][3]*s,
+ m[3][0]*s, m[3][1]*s, m[3][2]*s, m[3][3]*s);
+ }
+
+ //! Operator for Matrix4x4 Scale = float * Matrix4x4;
+ inline_ friend Matrix4x4 operator*(float s, const Matrix4x4& mat)
+ {
+ return Matrix4x4(
+ s*mat.m[0][0], s*mat.m[0][1], s*mat.m[0][2], s*mat.m[0][3],
+ s*mat.m[1][0], s*mat.m[1][1], s*mat.m[1][2], s*mat.m[1][3],
+ s*mat.m[2][0], s*mat.m[2][1], s*mat.m[2][2], s*mat.m[2][3],
+ s*mat.m[3][0], s*mat.m[3][1], s*mat.m[3][2], s*mat.m[3][3]);
+ }
+
+ //! Operator for Matrix4x4 Div = Matrix4x4 / float;
+ inline_ Matrix4x4 operator/(float s) const
+ {
+ if(s) s = 1.0f / s;
+
+ return Matrix4x4(
+ m[0][0]*s, m[0][1]*s, m[0][2]*s, m[0][3]*s,
+ m[1][0]*s, m[1][1]*s, m[1][2]*s, m[1][3]*s,
+ m[2][0]*s, m[2][1]*s, m[2][2]*s, m[2][3]*s,
+ m[3][0]*s, m[3][1]*s, m[3][2]*s, m[3][3]*s);
+ }
+
+ //! Operator for Matrix4x4 Div = float / Matrix4x4;
+ inline_ friend Matrix4x4 operator/(float s, const Matrix4x4& mat)
+ {
+ return Matrix4x4(
+ s/mat.m[0][0], s/mat.m[0][1], s/mat.m[0][2], s/mat.m[0][3],
+ s/mat.m[1][0], s/mat.m[1][1], s/mat.m[1][2], s/mat.m[1][3],
+ s/mat.m[2][0], s/mat.m[2][1], s/mat.m[2][2], s/mat.m[2][3],
+ s/mat.m[3][0], s/mat.m[3][1], s/mat.m[3][2], s/mat.m[3][3]);
+ }
+
+ //! Operator for Matrix4x4 += Matrix4x4;
+ inline_ Matrix4x4& operator+=(const Matrix4x4& mat)
+ {
+ m[0][0]+=mat.m[0][0]; m[0][1]+=mat.m[0][1]; m[0][2]+=mat.m[0][2]; m[0][3]+=mat.m[0][3];
+ m[1][0]+=mat.m[1][0]; m[1][1]+=mat.m[1][1]; m[1][2]+=mat.m[1][2]; m[1][3]+=mat.m[1][3];
+ m[2][0]+=mat.m[2][0]; m[2][1]+=mat.m[2][1]; m[2][2]+=mat.m[2][2]; m[2][3]+=mat.m[2][3];
+ m[3][0]+=mat.m[3][0]; m[3][1]+=mat.m[3][1]; m[3][2]+=mat.m[3][2]; m[3][3]+=mat.m[3][3];
+ return *this;
+ }
+
+ //! Operator for Matrix4x4 -= Matrix4x4;
+ inline_ Matrix4x4& operator-=(const Matrix4x4& mat)
+ {
+ m[0][0]-=mat.m[0][0]; m[0][1]-=mat.m[0][1]; m[0][2]-=mat.m[0][2]; m[0][3]-=mat.m[0][3];
+ m[1][0]-=mat.m[1][0]; m[1][1]-=mat.m[1][1]; m[1][2]-=mat.m[1][2]; m[1][3]-=mat.m[1][3];
+ m[2][0]-=mat.m[2][0]; m[2][1]-=mat.m[2][1]; m[2][2]-=mat.m[2][2]; m[2][3]-=mat.m[2][3];
+ m[3][0]-=mat.m[3][0]; m[3][1]-=mat.m[3][1]; m[3][2]-=mat.m[3][2]; m[3][3]-=mat.m[3][3];
+ return *this;
+ }
+
+ //! Operator for Matrix4x4 *= Matrix4x4;
+ Matrix4x4& operator*=(const Matrix4x4& mat)
+ {
+ HPoint TempRow;
+
+ GetRow(0, TempRow);
+ m[0][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0];
+ m[0][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1];
+ m[0][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2];
+ m[0][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3];
+
+ GetRow(1, TempRow);
+ m[1][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0];
+ m[1][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1];
+ m[1][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2];
+ m[1][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3];
+
+ GetRow(2, TempRow);
+ m[2][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0];
+ m[2][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1];
+ m[2][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2];
+ m[2][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3];
+
+ GetRow(3, TempRow);
+ m[3][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0];
+ m[3][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1];
+ m[3][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2];
+ m[3][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3];
+
+ return *this;
+ }
+
+ //! Operator for Matrix4x4 *= float;
+ inline_ Matrix4x4& operator*=(float s)
+ {
+ m[0][0]*=s; m[0][1]*=s; m[0][2]*=s; m[0][3]*=s;
+ m[1][0]*=s; m[1][1]*=s; m[1][2]*=s; m[1][3]*=s;
+ m[2][0]*=s; m[2][1]*=s; m[2][2]*=s; m[2][3]*=s;
+ m[3][0]*=s; m[3][1]*=s; m[3][2]*=s; m[3][3]*=s;
+ return *this;
+ }
+
+ //! Operator for Matrix4x4 /= float;
+ inline_ Matrix4x4& operator/=(float s)
+ {
+ if(s) s = 1.0f / s;
+ m[0][0]*=s; m[0][1]*=s; m[0][2]*=s; m[0][3]*=s;
+ m[1][0]*=s; m[1][1]*=s; m[1][2]*=s; m[1][3]*=s;
+ m[2][0]*=s; m[2][1]*=s; m[2][2]*=s; m[2][3]*=s;
+ m[3][0]*=s; m[3][1]*=s; m[3][2]*=s; m[3][3]*=s;
+ return *this;
+ }
+
+ inline_ const HPoint& operator[](int row) const { return *(const HPoint*)&m[row][0]; }
+ inline_ HPoint& operator[](int row) { return *(HPoint*)&m[row][0]; }
+
+ public:
+
+ float m[4][4];
+ };
+
+ //! Quickly rotates & translates a vector, using the 4x3 part of a 4x4 matrix
+ inline_ void TransformPoint4x3(Point& dest, const Point& source, const Matrix4x4& rot)
+ {
+ dest.x = rot.m[3][0] + source.x * rot.m[0][0] + source.y * rot.m[1][0] + source.z * rot.m[2][0];
+ dest.y = rot.m[3][1] + source.x * rot.m[0][1] + source.y * rot.m[1][1] + source.z * rot.m[2][1];
+ dest.z = rot.m[3][2] + source.x * rot.m[0][2] + source.y * rot.m[1][2] + source.z * rot.m[2][2];
+ }
+
+ //! Quickly rotates a vector, using the 3x3 part of a 4x4 matrix
+ inline_ void TransformPoint3x3(Point& dest, const Point& source, const Matrix4x4& rot)
+ {
+ dest.x = source.x * rot.m[0][0] + source.y * rot.m[1][0] + source.z * rot.m[2][0];
+ dest.y = source.x * rot.m[0][1] + source.y * rot.m[1][1] + source.z * rot.m[2][1];
+ dest.z = source.x * rot.m[0][2] + source.y * rot.m[1][2] + source.z * rot.m[2][2];
+ }
+
+ ICEMATHS_API void InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src);
+
+#endif // __ICEMATRIX4X4_H__
+
diff --git a/Opcode/Ice/IceMemoryMacros.h b/Opcode/Ice/IceMemoryMacros.h
new file mode 100644
index 0000000..490ecd1
--- /dev/null
+++ b/Opcode/Ice/IceMemoryMacros.h
@@ -0,0 +1,105 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains all memory macros.
+ * \file IceMemoryMacros.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEMEMORYMACROS_H__
+#define __ICEMEMORYMACROS_H__
+
+#undef ZeroMemory
+#undef CopyMemory
+#undef MoveMemory
+#undef FillMemory
+
+ //! Clears a buffer.
+ //! \param addr [in] buffer address
+ //! \param size [in] buffer length
+ //! \see FillMemory
+ //! \see StoreDwords
+ //! \see CopyMemory
+ //! \see MoveMemory
+ inline_ void ZeroMemory(void* addr, udword size) { memset(addr, 0, size); }
+
+ //! Fills a buffer with a given byte.
+ //! \param addr [in] buffer address
+ //! \param size [in] buffer length
+ //! \param val [in] the byte value
+ //! \see StoreDwords
+ //! \see ZeroMemory
+ //! \see CopyMemory
+ //! \see MoveMemory
+ inline_ void FillMemory(void* dest, udword size, ubyte val) { memset(dest, val, size); }
+
+ //! Fills a buffer with a given dword.
+ //! \param addr [in] buffer address
+ //! \param nb [in] number of dwords to write
+ //! \param value [in] the dword value
+ //! \see FillMemory
+ //! \see ZeroMemory
+ //! \see CopyMemory
+ //! \see MoveMemory
+ //! \warning writes nb*4 bytes !
+ inline_ void StoreDwords(udword* dest, udword nb, udword value)
+ {
+ // The asm code below **SHOULD** be equivalent to one of those C versions
+ // or the other if your compiled is good: (checked on VC++ 6.0)
+ //
+ // 1) while(nb--) *dest++ = value;
+ //
+ // 2) for(udword i=0;i<nb;i++) dest[i] = value;
+ //
+ _asm push eax
+ _asm push ecx
+ _asm push edi
+ _asm mov edi, dest
+ _asm mov ecx, nb
+ _asm mov eax, value
+ _asm rep stosd
+ _asm pop edi
+ _asm pop ecx
+ _asm pop eax
+ }
+
+ //! Copies a buffer.
+ //! \param addr [in] destination buffer address
+ //! \param addr [in] source buffer address
+ //! \param size [in] buffer length
+ //! \see ZeroMemory
+ //! \see FillMemory
+ //! \see StoreDwords
+ //! \see MoveMemory
+ inline_ void CopyMemory(void* dest, const void* src, udword size) { memcpy(dest, src, size); }
+
+ //! Moves a buffer.
+ //! \param addr [in] destination buffer address
+ //! \param addr [in] source buffer address
+ //! \param size [in] buffer length
+ //! \see ZeroMemory
+ //! \see FillMemory
+ //! \see StoreDwords
+ //! \see CopyMemory
+ inline_ void MoveMemory(void* dest, const void* src, udword size) { memmove(dest, src, size); }
+
+ #define SIZEOFOBJECT sizeof(*this) //!< Gives the size of current object. Avoid some mistakes (e.g. "sizeof(this)").
+ //#define CLEAROBJECT { memset(this, 0, SIZEOFOBJECT); } //!< Clears current object. Laziness is my business. HANDLE WITH CARE.
+ #define DELETESINGLE(x) if (x) { delete x; x = null; } //!< Deletes an instance of a class.
+ #define DELETEARRAY(x) if (x) { delete []x; x = null; } //!< Deletes an array.
+ #define SAFE_RELEASE(x) if (x) { (x)->Release(); (x) = null; } //!< Safe D3D-style release
+ #define SAFE_DESTRUCT(x) if (x) { (x)->SelfDestruct(); (x) = null; } //!< Safe ICE-style release
+
+#ifdef __ICEERROR_H__
+ #define CHECKALLOC(x) if(!x) return SetIceError("Out of memory.", EC_OUT_OF_MEMORY); //!< Standard alloc checking. HANDLE WITH CARE.
+#else
+ #define CHECKALLOC(x) if(!x) return false;
+#endif
+
+ //! Standard allocation cycle
+ #define SAFE_ALLOC(ptr, type, count) DELETEARRAY(ptr); ptr = new type[count]; CHECKALLOC(ptr);
+
+#endif // __ICEMEMORYMACROS_H__
diff --git a/Opcode/Ice/IceOBB.cpp b/Opcode/Ice/IceOBB.cpp
new file mode 100644
index 0000000..439c58c
--- /dev/null
+++ b/Opcode/Ice/IceOBB.cpp
@@ -0,0 +1,323 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains OBB-related code.
+ * \file IceOBB.cpp
+ * \author Pierre Terdiman
+ * \date January, 29, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * An Oriented Bounding Box (OBB).
+ * \class OBB
+ * \author Pierre Terdiman
+ * \version 1.0
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Tests if a point is contained within the OBB.
+ * \param p [in] the world point to test
+ * \return true if inside the OBB
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool OBB::ContainsPoint(const IcePoint& p) const
+{
+ // IcePoint in OBB test using lazy evaluation and early exits
+
+ // Translate to box space
+ IcePoint RelPoint = p - mCenter;
+
+ // IcePoint * mRot maps from box space to world space
+ // mRot * IcePoint maps from world space to box space (what we need here)
+
+ float f = mRot.m[0][0] * RelPoint.x + mRot.m[0][1] * RelPoint.y + mRot.m[0][2] * RelPoint.z;
+ if(f >= mExtents.x || f <= -mExtents.x) return false;
+
+ f = mRot.m[1][0] * RelPoint.x + mRot.m[1][1] * RelPoint.y + mRot.m[1][2] * RelPoint.z;
+ if(f >= mExtents.y || f <= -mExtents.y) return false;
+
+ f = mRot.m[2][0] * RelPoint.x + mRot.m[2][1] * RelPoint.y + mRot.m[2][2] * RelPoint.z;
+ if(f >= mExtents.z || f <= -mExtents.z) return false;
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds an OBB from an AABB and a world transform.
+ * \param aabb [in] the aabb
+ * \param mat [in] the world transform
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBB::Create(const AABB& aabb, const Matrix4x4& mat)
+{
+ // Note: must be coherent with Rotate()
+
+ aabb.GetCenter(mCenter);
+ aabb.GetExtents(mExtents);
+ // Here we have the same as OBB::Rotate(mat) where the obb is (mCenter, mExtents, Identity).
+
+ // So following what's done in Rotate:
+ // - x-form the center
+ mCenter *= mat;
+ // - combine rotation with identity, i.e. just use given matrix
+ mRot = mat;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the obb planes.
+ * \param planes [out] 6 box planes
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool OBB::ComputePlanes(Plane* planes) const
+{
+ // Checkings
+ if(!planes) return false;
+
+ IcePoint Axis0 = mRot[0];
+ IcePoint Axis1 = mRot[1];
+ IcePoint Axis2 = mRot[2];
+
+ // Writes normals
+ planes[0].n = Axis0;
+ planes[1].n = -Axis0;
+ planes[2].n = Axis1;
+ planes[3].n = -Axis1;
+ planes[4].n = Axis2;
+ planes[5].n = -Axis2;
+
+ // Compute a point on each plane
+ IcePoint p0 = mCenter + Axis0 * mExtents.x;
+ IcePoint p1 = mCenter - Axis0 * mExtents.x;
+ IcePoint p2 = mCenter + Axis1 * mExtents.y;
+ IcePoint p3 = mCenter - Axis1 * mExtents.y;
+ IcePoint p4 = mCenter + Axis2 * mExtents.z;
+ IcePoint p5 = mCenter - Axis2 * mExtents.z;
+
+ // Compute d
+ planes[0].d = -(planes[0].n|p0);
+ planes[1].d = -(planes[1].n|p1);
+ planes[2].d = -(planes[2].n|p2);
+ planes[3].d = -(planes[3].n|p3);
+ planes[4].d = -(planes[4].n|p4);
+ planes[5].d = -(planes[5].n|p5);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the obb points.
+ * \param pts [out] 8 box points
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool OBB::ComputePoints(IcePoint* pts) const
+{
+ // Checkings
+ if(!pts) return false;
+
+ IcePoint Axis0 = mRot[0];
+ IcePoint Axis1 = mRot[1];
+ IcePoint Axis2 = mRot[2];
+
+ Axis0 *= mExtents.x;
+ Axis1 *= mExtents.y;
+ Axis2 *= mExtents.z;
+
+ // 7+------+6 0 = ---
+ // /| /| 1 = +--
+ // / | / | 2 = ++-
+ // / 4+---/--+5 3 = -+-
+ // 3+------+2 / y z 4 = --+
+ // | / | / | / 5 = +-+
+ // |/ |/ |/ 6 = +++
+ // 0+------+1 *---x 7 = -++
+
+ pts[0] = mCenter - Axis0 - Axis1 - Axis2;
+ pts[1] = mCenter + Axis0 - Axis1 - Axis2;
+ pts[2] = mCenter + Axis0 + Axis1 - Axis2;
+ pts[3] = mCenter - Axis0 + Axis1 - Axis2;
+ pts[4] = mCenter - Axis0 - Axis1 + Axis2;
+ pts[5] = mCenter + Axis0 - Axis1 + Axis2;
+ pts[6] = mCenter + Axis0 + Axis1 + Axis2;
+ pts[7] = mCenter - Axis0 + Axis1 + Axis2;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes vertex normals.
+ * \param pts [out] 8 box points
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool OBB::ComputeVertexNormals(IcePoint* pts) const
+{
+ static float VertexNormals[] =
+ {
+ -INVSQRT3, -INVSQRT3, -INVSQRT3,
+ INVSQRT3, -INVSQRT3, -INVSQRT3,
+ INVSQRT3, INVSQRT3, -INVSQRT3,
+ -INVSQRT3, INVSQRT3, -INVSQRT3,
+ -INVSQRT3, -INVSQRT3, INVSQRT3,
+ INVSQRT3, -INVSQRT3, INVSQRT3,
+ INVSQRT3, INVSQRT3, INVSQRT3,
+ -INVSQRT3, INVSQRT3, INVSQRT3
+ };
+
+ if(!pts) return false;
+
+ const IcePoint* VN = (const IcePoint*)VertexNormals;
+ for(udword i=0;i<8;i++)
+ {
+ pts[i] = VN[i] * mRot;
+ }
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns edges.
+ * \return 24 indices (12 edges) indexing the list returned by ComputePoints()
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const udword* OBB::GetEdges() const
+{
+ static udword Indices[] = {
+ 0, 1, 1, 2, 2, 3, 3, 0,
+ 7, 6, 6, 5, 5, 4, 4, 7,
+ 1, 5, 6, 2,
+ 3, 7, 4, 0
+ };
+ return Indices;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns local edge normals.
+ * \return edge normals in local space
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const IcePoint* OBB::GetLocalEdgeNormals() const
+{
+ static float EdgeNormals[] =
+ {
+ 0, -INVSQRT2, -INVSQRT2, // 0-1
+ INVSQRT2, 0, -INVSQRT2, // 1-2
+ 0, INVSQRT2, -INVSQRT2, // 2-3
+ -INVSQRT2, 0, -INVSQRT2, // 3-0
+
+ 0, INVSQRT2, INVSQRT2, // 7-6
+ INVSQRT2, 0, INVSQRT2, // 6-5
+ 0, -INVSQRT2, INVSQRT2, // 5-4
+ -INVSQRT2, 0, INVSQRT2, // 4-7
+
+ INVSQRT2, -INVSQRT2, 0, // 1-5
+ INVSQRT2, INVSQRT2, 0, // 6-2
+ -INVSQRT2, INVSQRT2, 0, // 3-7
+ -INVSQRT2, -INVSQRT2, 0 // 4-0
+ };
+ return (const IcePoint*)EdgeNormals;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns world edge normal
+ * \param edge_index [in] 0 <= edge index < 12
+ * \param world_normal [out] edge normal in world space
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBB::ComputeWorldEdgeNormal(udword edge_index, IcePoint& world_normal) const
+{
+ ASSERT(edge_index<12);
+ world_normal = GetLocalEdgeNormals()[edge_index] * mRot;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes an LSS surrounding the OBB.
+ * \param lss [out] the LSS
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBB::ComputeLSS(LSS& lss) const
+{
+ IcePoint Axis0 = mRot[0];
+ IcePoint Axis1 = mRot[1];
+ IcePoint Axis2 = mRot[2];
+
+ switch(mExtents.LargestAxis())
+ {
+ case 0:
+ lss.mRadius = (mExtents.y + mExtents.z)*0.5f;
+ lss.mP0 = mCenter + Axis0 * (mExtents.x - lss.mRadius);
+ lss.mP1 = mCenter - Axis0 * (mExtents.x - lss.mRadius);
+ break;
+ case 1:
+ lss.mRadius = (mExtents.x + mExtents.z)*0.5f;
+ lss.mP0 = mCenter + Axis1 * (mExtents.y - lss.mRadius);
+ lss.mP1 = mCenter - Axis1 * (mExtents.y - lss.mRadius);
+ break;
+ case 2:
+ lss.mRadius = (mExtents.x + mExtents.y)*0.5f;
+ lss.mP0 = mCenter + Axis2 * (mExtents.z - lss.mRadius);
+ lss.mP1 = mCenter - Axis2 * (mExtents.z - lss.mRadius);
+ break;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks the OBB is inside another OBB.
+ * \param box [in] the other OBB
+ * \return TRUE if we're inside the other box
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+BOOL OBB::IsInside(const OBB& box) const
+{
+ // Make a 4x4 from the box & inverse it
+ Matrix4x4 M0Inv;
+ {
+ Matrix4x4 M0 = box.mRot;
+ M0.SetTrans(box.mCenter);
+ InvertPRMatrix(M0Inv, M0);
+ }
+
+ // With our inversed 4x4, create box1 in space of box0
+ OBB _1in0;
+ Rotate(M0Inv, _1in0);
+
+ // This should cancel out box0's rotation, i.e. it's now an AABB.
+ // => Center(0,0,0), Rot(identity)
+
+ // The two boxes are in the same space so now we can compare them.
+
+ // Create the AABB of (box1 in space of box0)
+ const Matrix3x3& mtx = _1in0.mRot;
+
+ float f = fabsf(mtx.m[0][0] * mExtents.x) + fabsf(mtx.m[1][0] * mExtents.y) + fabsf(mtx.m[2][0] * mExtents.z) - box.mExtents.x;
+ if(f > _1in0.mCenter.x) return FALSE;
+ if(-f < _1in0.mCenter.x) return FALSE;
+
+ f = fabsf(mtx.m[0][1] * mExtents.x) + fabsf(mtx.m[1][1] * mExtents.y) + fabsf(mtx.m[2][1] * mExtents.z) - box.mExtents.y;
+ if(f > _1in0.mCenter.y) return FALSE;
+ if(-f < _1in0.mCenter.y) return FALSE;
+
+ f = fabsf(mtx.m[0][2] * mExtents.x) + fabsf(mtx.m[1][2] * mExtents.y) + fabsf(mtx.m[2][2] * mExtents.z) - box.mExtents.z;
+ if(f > _1in0.mCenter.z) return FALSE;
+ if(-f < _1in0.mCenter.z) return FALSE;
+
+ return TRUE;
+}
diff --git a/Opcode/Ice/IceOBB.h b/Opcode/Ice/IceOBB.h
new file mode 100644
index 0000000..4747f7f
--- /dev/null
+++ b/Opcode/Ice/IceOBB.h
@@ -0,0 +1,177 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains OBB-related code. (oriented bounding box)
+ * \file IceOBB.h
+ * \author Pierre Terdiman
+ * \date January, 13, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEOBB_H__
+#define __ICEOBB_H__
+
+ // Forward declarations
+ class LSS;
+
+ class ICEMATHS_API OBB
+ {
+ public:
+ //! Constructor
+ inline_ OBB() {}
+ //! Constructor
+ inline_ OBB(const Point& center, const Point& extents, const Matrix3x3& rot) : mCenter(center), mExtents(extents), mRot(rot) {}
+ //! Destructor
+ inline_ ~OBB() {}
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups an empty OBB.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetEmpty()
+ {
+ mCenter.Zero();
+ mExtents.Set(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT);
+ mRot.Identity();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Tests if a point is contained within the OBB.
+ * \param p [in] the world point to test
+ * \return true if inside the OBB
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool ContainsPoint(const Point& p) const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Builds an OBB from an AABB and a world transform.
+ * \param aabb [in] the aabb
+ * \param mat [in] the world transform
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void Create(const AABB& aabb, const Matrix4x4& mat);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Recomputes the OBB after an arbitrary transform by a 4x4 matrix.
+ * \param mtx [in] the transform matrix
+ * \param obb [out] the transformed OBB
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void Rotate(const Matrix4x4& mtx, OBB& obb) const
+ {
+ // The extents remain constant
+ obb.mExtents = mExtents;
+ // The center gets x-formed
+ obb.mCenter = mCenter * mtx;
+ // Combine rotations
+ obb.mRot = mRot * Matrix3x3(mtx);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks the OBB is valid.
+ * \return true if the box is valid
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL IsValid() const
+ {
+ // Consistency condition for (Center, Extents) boxes: Extents >= 0.0f
+ if(mExtents.x < 0.0f) return FALSE;
+ if(mExtents.y < 0.0f) return FALSE;
+ if(mExtents.z < 0.0f) return FALSE;
+ return TRUE;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the obb planes.
+ * \param planes [out] 6 box planes
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool ComputePlanes(Plane* planes) const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the obb points.
+ * \param pts [out] 8 box points
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool ComputePoints(Point* pts) const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes vertex normals.
+ * \param pts [out] 8 box points
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool ComputeVertexNormals(Point* pts) const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Returns edges.
+ * \return 24 indices (12 edges) indexing the list returned by ComputePoints()
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ const udword* GetEdges() const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Returns local edge normals.
+ * \return edge normals in local space
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ const Point* GetLocalEdgeNormals() const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Returns world edge normal
+ * \param edge_index [in] 0 <= edge index < 12
+ * \param world_normal [out] edge normal in world space
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void ComputeWorldEdgeNormal(udword edge_index, Point& world_normal) const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes an LSS surrounding the OBB.
+ * \param lss [out] the LSS
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void ComputeLSS(LSS& lss) const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks the OBB is inside another OBB.
+ * \param box [in] the other OBB
+ * \return TRUE if we're inside the other box
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ BOOL IsInside(const OBB& box) const;
+
+ inline_ const Point& GetCenter() const { return mCenter; }
+ inline_ const Point& GetExtents() const { return mExtents; }
+ inline_ const Matrix3x3& GetRot() const { return mRot; }
+
+ inline_ void GetRotatedExtents(Matrix3x3& extents) const
+ {
+ extents = mRot;
+ extents.Scale(mExtents);
+ }
+
+ Point mCenter; //!< B for Box
+ Point mExtents; //!< B for Bounding
+ Matrix3x3 mRot; //!< O for Oriented
+
+ // Orientation is stored in row-major format,
+ // i.e. rows = eigen vectors of the covariance matrix
+ };
+
+#endif // __ICEOBB_H__
diff --git a/Opcode/Ice/IcePairs.h b/Opcode/Ice/IcePairs.h
new file mode 100644
index 0000000..35e3a07
--- /dev/null
+++ b/Opcode/Ice/IcePairs.h
@@ -0,0 +1,45 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a simple pair class.
+ * \file IcePairs.h
+ * \author Pierre Terdiman
+ * \date January, 13, 2003
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEPAIRS_H__
+#define __ICEPAIRS_H__
+
+ //! A generic couple structure
+ struct ICECORE_API Pair
+ {
+ inline_ Pair() {}
+ inline_ Pair(udword i0, udword i1) : id0(i0), id1(i1) {}
+
+ udword id0; //!< First index of the pair
+ udword id1; //!< Second index of the pair
+ };
+
+ class ICECORE_API Pairs : private Container
+ {
+ public:
+ // Constructor / Destructor
+ Pairs() {}
+ ~Pairs() {}
+
+ inline_ udword GetNbPairs() const { return GetNbEntries()>>1; }
+ inline_ const Pair* GetPairs() const { return (const Pair*)GetEntries(); }
+ inline_ const Pair* GetPair(udword i) const { return (const Pair*)&GetEntries()[i+i]; }
+
+ inline_ BOOL HasPairs() const { return IsNotEmpty(); }
+
+ inline_ void ResetPairs() { Reset(); }
+ inline_ void DeleteLastPair() { DeleteLastEntry(); DeleteLastEntry(); }
+
+ inline_ void AddPair(const Pair& p) { Add(p.id0).Add(p.id1); }
+ inline_ void AddPair(udword id0, udword id1) { Add(id0).Add(id1); }
+ };
+
+#endif // __ICEPAIRS_H__
diff --git a/Opcode/Ice/IcePlane.cpp b/Opcode/Ice/IcePlane.cpp
new file mode 100644
index 0000000..36fe473
--- /dev/null
+++ b/Opcode/Ice/IcePlane.cpp
@@ -0,0 +1,45 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for planes.
+ * \file IcePlane.cpp
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Plane class.
+ * \class Plane
+ * \author Pierre Terdiman
+ * \version 1.0
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the plane equation from 3 points.
+ * \param p0 [in] first point
+ * \param p1 [in] second point
+ * \param p2 [in] third point
+ * \return Self-reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Plane& Plane::Set(const Point& p0, const Point& p1, const Point& p2)
+{
+ Point Edge0 = p1 - p0;
+ Point Edge1 = p2 - p0;
+
+ n = Edge0 ^ Edge1;
+ n.Normalize();
+
+ d = -(p0 | n);
+
+ return *this;
+}
diff --git a/Opcode/Ice/IcePlane.h b/Opcode/Ice/IcePlane.h
new file mode 100644
index 0000000..0e0604c
--- /dev/null
+++ b/Opcode/Ice/IcePlane.h
@@ -0,0 +1,113 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for planes.
+ * \file IcePlane.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEPLANE_H__
+#define __ICEPLANE_H__
+
+ #define PLANE_EPSILON (1.0e-7f)
+
+ class ICEMATHS_API Plane
+ {
+ public:
+ //! Constructor
+ inline_ Plane() { }
+ //! Constructor from a normal and a distance
+ inline_ Plane(float nx, float ny, float nz, float d) { Set(nx, ny, nz, d); }
+ //! Constructor from a point on the plane and a normal
+ inline_ Plane(const Point& p, const Point& n) { Set(p, n); }
+ //! Constructor from three points
+ inline_ Plane(const Point& p0, const Point& p1, const Point& p2) { Set(p0, p1, p2); }
+ //! Constructor from a normal and a distance
+ inline_ Plane(const Point& _n, float _d) { n = _n; d = _d; }
+ //! Copy constructor
+ inline_ Plane(const Plane& plane) : n(plane.n), d(plane.d) { }
+ //! Destructor
+ inline_ ~Plane() { }
+
+ inline_ Plane& Zero() { n.Zero(); d = 0.0f; return *this; }
+ inline_ Plane& Set(float nx, float ny, float nz, float _d) { n.Set(nx, ny, nz); d = _d; return *this; }
+ inline_ Plane& Set(const Point& p, const Point& _n) { n = _n; d = - p | _n; return *this; }
+ Plane& Set(const Point& p0, const Point& p1, const Point& p2);
+
+ inline_ float Distance(const Point& p) const { return (p | n) + d; }
+ inline_ bool Belongs(const Point& p) const { return fabsf(Distance(p)) < PLANE_EPSILON; }
+
+ inline_ void Normalize()
+ {
+ float Denom = 1.0f / n.Magnitude();
+ n.x *= Denom;
+ n.y *= Denom;
+ n.z *= Denom;
+ d *= Denom;
+ }
+ public:
+ // Members
+ Point n; //!< The normal to the plane
+ float d; //!< The distance from the origin
+
+ // Cast operators
+ inline_ operator Point() const { return n; }
+ inline_ operator HPoint() const { return HPoint(n, d); }
+
+ // Arithmetic operators
+ inline_ Plane operator*(const Matrix4x4& m) const
+ {
+ // Old code from Irion. Kept for reference.
+ Plane Ret(*this);
+ return Ret *= m;
+ }
+
+ inline_ Plane& operator*=(const Matrix4x4& m)
+ {
+ // Old code from Irion. Kept for reference.
+ Point n2 = HPoint(n, 0.0f) * m;
+ d = -((Point) (HPoint( -d*n, 1.0f ) * m) | n2);
+ n = n2;
+ return *this;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Transforms a plane by a 4x4 matrix. Same as Plane * Matrix4x4 operator, but faster.
+ * \param transformed [out] transformed plane
+ * \param plane [in] source plane
+ * \param transform [in] transform matrix
+ * \warning the plane normal must be unit-length
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void TransformPlane(Plane& transformed, const Plane& plane, const Matrix4x4& transform)
+ {
+ // Rotate the normal using the rotation part of the 4x4 matrix
+ transformed.n = plane.n * Matrix3x3(transform);
+
+ // Compute new d
+ transformed.d = plane.d - (Point(transform.GetTrans())|transformed.n);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Transforms a plane by a 4x4 matrix. Same as Plane * Matrix4x4 operator, but faster.
+ * \param plane [in/out] source plane (transformed on return)
+ * \param transform [in] transform matrix
+ * \warning the plane normal must be unit-length
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void TransformPlane(Plane& plane, const Matrix4x4& transform)
+ {
+ // Rotate the normal using the rotation part of the 4x4 matrix
+ plane.n *= Matrix3x3(transform);
+
+ // Compute new d
+ plane.d -= Point(transform.GetTrans())|plane.n;
+ }
+
+#endif // __ICEPLANE_H__
diff --git a/Opcode/Ice/IcePoint.cpp b/Opcode/Ice/IcePoint.cpp
new file mode 100644
index 0000000..9fd7570
--- /dev/null
+++ b/Opcode/Ice/IcePoint.cpp
@@ -0,0 +1,193 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for 3D vectors.
+ * \file IcePoint.cpp
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 3D point.
+ *
+ * The name is "Point" instead of "Vector" since a vector is N-dimensional, whereas a point is an implicit "vector of dimension 3".
+ * So the choice was between "Point" and "Vector3", the first one looked better (IMHO).
+ *
+ * Some people, then, use a typedef to handle both points & vectors using the same class: typedef Point Vector3;
+ * This is bad since it opens the door to a lot of confusion while reading the code. I know it may sounds weird but check this out:
+ *
+ * \code
+ * Point P0,P1 = some 3D points;
+ * Point Delta = P1 - P0;
+ * \endcode
+ *
+ * This compiles fine, although you should have written:
+ *
+ * \code
+ * Point P0,P1 = some 3D points;
+ * Vector3 Delta = P1 - P0;
+ * \endcode
+ *
+ * Subtle things like this are not caught at compile-time, and when you find one in the code, you never know whether it's a mistake
+ * from the author or something you don't get.
+ *
+ * One way to handle it at compile-time would be to use different classes for Point & Vector3, only overloading operator "-" for vectors.
+ * But then, you get a lot of redundant code in thoses classes, and basically it's really a lot of useless work.
+ *
+ * Another way would be to use homogeneous points: w=1 for points, w=0 for vectors. That's why the HPoint class exists. Now, to store
+ * your model's vertices and in most cases, you really want to use Points to save ram.
+ *
+ * \class Point
+ * \author Pierre Terdiman
+ * \version 1.0
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Creates a positive unit random vector.
+ * \return Self-reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Point& Point::PositiveUnitRandomVector()
+{
+ x = UnitRandomFloat();
+ y = UnitRandomFloat();
+ z = UnitRandomFloat();
+ Normalize();
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Creates a unit random vector.
+ * \return Self-reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Point& Point::UnitRandomVector()
+{
+ x = UnitRandomFloat() - 0.5f;
+ y = UnitRandomFloat() - 0.5f;
+ z = UnitRandomFloat() - 0.5f;
+ Normalize();
+ return *this;
+}
+
+// Cast operator
+// WARNING: not inlined
+Point::operator HPoint() const { return HPoint(x, y, z, 0.0f); }
+
+Point& Point::Refract(const Point& eye, const Point& n, float refractindex, Point& refracted)
+{
+ // Point EyePt = eye position
+ // Point p = current vertex
+ // Point n = vertex normal
+ // Point rv = refracted vector
+ // Eye vector - doesn't need to be normalized
+ Point Env;
+ Env.x = eye.x - x;
+ Env.y = eye.y - y;
+ Env.z = eye.z - z;
+
+ float NDotE = n|Env;
+ float NDotN = n|n;
+ NDotE /= refractindex;
+
+ // Refracted vector
+ refracted = n*NDotE - Env*NDotN;
+
+ return *this;
+}
+
+Point& Point::ProjectToPlane(const Plane& p)
+{
+ *this-= (p.d + (*this|p.n))*p.n;
+ return *this;
+}
+
+void Point::ProjectToScreen(float halfrenderwidth, float halfrenderheight, const Matrix4x4& mat, HPoint& projected) const
+{
+ projected = HPoint(x, y, z, 1.0f) * mat;
+ projected.w = 1.0f / projected.w;
+
+ projected.x*=projected.w;
+ projected.y*=projected.w;
+ projected.z*=projected.w;
+
+ projected.x *= halfrenderwidth; projected.x += halfrenderwidth;
+ projected.y *= -halfrenderheight; projected.y += halfrenderheight;
+}
+
+void Point::SetNotUsed()
+{
+ // We use a particular integer pattern : 0xffffffff everywhere. This is a NAN.
+ IR(x) = 0xffffffff;
+ IR(y) = 0xffffffff;
+ IR(z) = 0xffffffff;
+}
+
+BOOL Point::IsNotUsed() const
+{
+ if(IR(x)!=0xffffffff) return FALSE;
+ if(IR(y)!=0xffffffff) return FALSE;
+ if(IR(z)!=0xffffffff) return FALSE;
+ return TRUE;
+}
+
+Point& Point::Mult(const Matrix3x3& mat, const Point& a)
+{
+ x = a.x * mat.m[0][0] + a.y * mat.m[0][1] + a.z * mat.m[0][2];
+ y = a.x * mat.m[1][0] + a.y * mat.m[1][1] + a.z * mat.m[1][2];
+ z = a.x * mat.m[2][0] + a.y * mat.m[2][1] + a.z * mat.m[2][2];
+ return *this;
+}
+
+Point& Point::Mult2(const Matrix3x3& mat1, const Point& a1, const Matrix3x3& mat2, const Point& a2)
+{
+ x = a1.x * mat1.m[0][0] + a1.y * mat1.m[0][1] + a1.z * mat1.m[0][2] + a2.x * mat2.m[0][0] + a2.y * mat2.m[0][1] + a2.z * mat2.m[0][2];
+ y = a1.x * mat1.m[1][0] + a1.y * mat1.m[1][1] + a1.z * mat1.m[1][2] + a2.x * mat2.m[1][0] + a2.y * mat2.m[1][1] + a2.z * mat2.m[1][2];
+ z = a1.x * mat1.m[2][0] + a1.y * mat1.m[2][1] + a1.z * mat1.m[2][2] + a2.x * mat2.m[2][0] + a2.y * mat2.m[2][1] + a2.z * mat2.m[2][2];
+ return *this;
+}
+
+Point& Point::Mac(const Matrix3x3& mat, const Point& a)
+{
+ x += a.x * mat.m[0][0] + a.y * mat.m[0][1] + a.z * mat.m[0][2];
+ y += a.x * mat.m[1][0] + a.y * mat.m[1][1] + a.z * mat.m[1][2];
+ z += a.x * mat.m[2][0] + a.y * mat.m[2][1] + a.z * mat.m[2][2];
+ return *this;
+}
+
+Point& Point::TransMult(const Matrix3x3& mat, const Point& a)
+{
+ x = a.x * mat.m[0][0] + a.y * mat.m[1][0] + a.z * mat.m[2][0];
+ y = a.x * mat.m[0][1] + a.y * mat.m[1][1] + a.z * mat.m[2][1];
+ z = a.x * mat.m[0][2] + a.y * mat.m[1][2] + a.z * mat.m[2][2];
+ return *this;
+}
+
+Point& Point::Transform(const Point& r, const Matrix3x3& rotpos, const Point& linpos)
+{
+ x = r.x * rotpos.m[0][0] + r.y * rotpos.m[0][1] + r.z * rotpos.m[0][2] + linpos.x;
+ y = r.x * rotpos.m[1][0] + r.y * rotpos.m[1][1] + r.z * rotpos.m[1][2] + linpos.y;
+ z = r.x * rotpos.m[2][0] + r.y * rotpos.m[2][1] + r.z * rotpos.m[2][2] + linpos.z;
+ return *this;
+}
+
+Point& Point::InvTransform(const Point& r, const Matrix3x3& rotpos, const Point& linpos)
+{
+ float sx = r.x - linpos.x;
+ float sy = r.y - linpos.y;
+ float sz = r.z - linpos.z;
+ x = sx * rotpos.m[0][0] + sy * rotpos.m[1][0] + sz * rotpos.m[2][0];
+ y = sx * rotpos.m[0][1] + sy * rotpos.m[1][1] + sz * rotpos.m[2][1];
+ z = sx * rotpos.m[0][2] + sy * rotpos.m[1][2] + sz * rotpos.m[2][2];
+ return *this;
+}
diff --git a/Opcode/Ice/IcePoint.h b/Opcode/Ice/IcePoint.h
new file mode 100644
index 0000000..78148d6
--- /dev/null
+++ b/Opcode/Ice/IcePoint.h
@@ -0,0 +1,528 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for 3D vectors.
+ * \file IcePoint.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEPOINT_H__
+#define __ICEPOINT_H__
+
+ // Forward declarations
+ class HPoint;
+ class Plane;
+ class Matrix3x3;
+ class Matrix4x4;
+
+ #define CROSS2D(a, b) (a.x*b.y - b.x*a.y)
+
+ const float EPSILON2 = 1.0e-20f;
+
+ class ICEMATHS_API Point
+ {
+ public:
+
+ //! Empty constructor
+ inline_ Point() {}
+ //! Constructor from a single float
+// inline_ Point(float val) : x(val), y(val), z(val) {}
+// Removed since it introduced the nasty "Point T = *Matrix4x4.GetTrans();" bug.......
+ //! Constructor from floats
+ inline_ Point(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
+ //! Constructor from array
+ inline_ Point(const float f[3]) : x(f[_X]), y(f[_Y]), z(f[_Z]) {}
+ //! Copy constructor
+ inline_ Point(const Point& p) : x(p.x), y(p.y), z(p.z) {}
+ //! Destructor
+ inline_ ~Point() {}
+
+ //! Clears the vector
+ inline_ Point& Zero() { x = y = z = 0.0f; return *this; }
+
+ //! + infinity
+ inline_ Point& SetPlusInfinity() { x = y = z = MAX_FLOAT; return *this; }
+ //! - infinity
+ inline_ Point& SetMinusInfinity() { x = y = z = MIN_FLOAT; return *this; }
+
+ //! Sets positive unit random vector
+ Point& PositiveUnitRandomVector();
+ //! Sets unit random vector
+ Point& UnitRandomVector();
+
+ //! Assignment from values
+ inline_ Point& Set(float _x, float _y, float _z) { x = _x; y = _y; z = _z; return *this; }
+ //! Assignment from array
+ inline_ Point& Set(const float f[3]) { x = f[_X]; y = f[_Y]; z = f[_Z]; return *this; }
+ //! Assignment from another point
+ inline_ Point& Set(const Point& src) { x = src.x; y = src.y; z = src.z; return *this; }
+
+ //! Adds a vector
+ inline_ Point& Add(const Point& p) { x += p.x; y += p.y; z += p.z; return *this; }
+ //! Adds a vector
+ inline_ Point& Add(float _x, float _y, float _z) { x += _x; y += _y; z += _z; return *this; }
+ //! Adds a vector
+ inline_ Point& Add(const float f[3]) { x += f[_X]; y += f[_Y]; z += f[_Z]; return *this; }
+ //! Adds vectors
+ inline_ Point& Add(const Point& p, const Point& q) { x = p.x+q.x; y = p.y+q.y; z = p.z+q.z; return *this; }
+
+ //! Subtracts a vector
+ inline_ Point& Sub(const Point& p) { x -= p.x; y -= p.y; z -= p.z; return *this; }
+ //! Subtracts a vector
+ inline_ Point& Sub(float _x, float _y, float _z) { x -= _x; y -= _y; z -= _z; return *this; }
+ //! Subtracts a vector
+ inline_ Point& Sub(const float f[3]) { x -= f[_X]; y -= f[_Y]; z -= f[_Z]; return *this; }
+ //! Subtracts vectors
+ inline_ Point& Sub(const Point& p, const Point& q) { x = p.x-q.x; y = p.y-q.y; z = p.z-q.z; return *this; }
+
+ //! this = -this
+ inline_ Point& Neg() { x = -x; y = -y; z = -z; return *this; }
+ //! this = -a
+ inline_ Point& Neg(const Point& a) { x = -a.x; y = -a.y; z = -a.z; return *this; }
+
+ //! Multiplies by a scalar
+ inline_ Point& Mult(float s) { x *= s; y *= s; z *= s; return *this; }
+
+ //! this = a * scalar
+ inline_ Point& Mult(const Point& a, float scalar)
+ {
+ x = a.x * scalar;
+ y = a.y * scalar;
+ z = a.z * scalar;
+ return *this;
+ }
+
+ //! this = a + b * scalar
+ inline_ Point& Mac(const Point& a, const Point& b, float scalar)
+ {
+ x = a.x + b.x * scalar;
+ y = a.y + b.y * scalar;
+ z = a.z + b.z * scalar;
+ return *this;
+ }
+
+ //! this = this + a * scalar
+ inline_ Point& Mac(const Point& a, float scalar)
+ {
+ x += a.x * scalar;
+ y += a.y * scalar;
+ z += a.z * scalar;
+ return *this;
+ }
+
+ //! this = a - b * scalar
+ inline_ Point& Msc(const Point& a, const Point& b, float scalar)
+ {
+ x = a.x - b.x * scalar;
+ y = a.y - b.y * scalar;
+ z = a.z - b.z * scalar;
+ return *this;
+ }
+
+ //! this = this - a * scalar
+ inline_ Point& Msc(const Point& a, float scalar)
+ {
+ x -= a.x * scalar;
+ y -= a.y * scalar;
+ z -= a.z * scalar;
+ return *this;
+ }
+
+ //! this = a + b * scalarb + c * scalarc
+ inline_ Point& Mac2(const Point& a, const Point& b, float scalarb, const Point& c, float scalarc)
+ {
+ x = a.x + b.x * scalarb + c.x * scalarc;
+ y = a.y + b.y * scalarb + c.y * scalarc;
+ z = a.z + b.z * scalarb + c.z * scalarc;
+ return *this;
+ }
+
+ //! this = a - b * scalarb - c * scalarc
+ inline_ Point& Msc2(const Point& a, const Point& b, float scalarb, const Point& c, float scalarc)
+ {
+ x = a.x - b.x * scalarb - c.x * scalarc;
+ y = a.y - b.y * scalarb - c.y * scalarc;
+ z = a.z - b.z * scalarb - c.z * scalarc;
+ return *this;
+ }
+
+ //! this = mat * a
+ inline_ Point& Mult(const Matrix3x3& mat, const Point& a);
+
+ //! this = mat1 * a1 + mat2 * a2
+ inline_ Point& Mult2(const Matrix3x3& mat1, const Point& a1, const Matrix3x3& mat2, const Point& a2);
+
+ //! this = this + mat * a
+ inline_ Point& Mac(const Matrix3x3& mat, const Point& a);
+
+ //! this = transpose(mat) * a
+ inline_ Point& TransMult(const Matrix3x3& mat, const Point& a);
+
+ //! Linear interpolate between two vectors: this = a + t * (b - a)
+ inline_ Point& Lerp(const Point& a, const Point& b, float t)
+ {
+ x = a.x + t * (b.x - a.x);
+ y = a.y + t * (b.y - a.y);
+ z = a.z + t * (b.z - a.z);
+ return *this;
+ }
+
+ //! Hermite interpolate between p1 and p2. p0 and p3 are used for finding gradient at p1 and p2.
+ //! this = p0 * (2t^2 - t^3 - t)/2
+ //! + p1 * (3t^3 - 5t^2 + 2)/2
+ //! + p2 * (4t^2 - 3t^3 + t)/2
+ //! + p3 * (t^3 - t^2)/2
+ inline_ Point& Herp(const Point& p0, const Point& p1, const Point& p2, const Point& p3, float t)
+ {
+ float t2 = t * t;
+ float t3 = t2 * t;
+ float kp0 = (2.0f * t2 - t3 - t) * 0.5f;
+ float kp1 = (3.0f * t3 - 5.0f * t2 + 2.0f) * 0.5f;
+ float kp2 = (4.0f * t2 - 3.0f * t3 + t) * 0.5f;
+ float kp3 = (t3 - t2) * 0.5f;
+ x = p0.x * kp0 + p1.x * kp1 + p2.x * kp2 + p3.x * kp3;
+ y = p0.y * kp0 + p1.y * kp1 + p2.y * kp2 + p3.y * kp3;
+ z = p0.z * kp0 + p1.z * kp1 + p2.z * kp2 + p3.z * kp3;
+ return *this;
+ }
+
+ //! this = rotpos * r + linpos
+ inline_ Point& Transform(const Point& r, const Matrix3x3& rotpos, const Point& linpos);
+
+ //! this = trans(rotpos) * (r - linpos)
+ inline_ Point& InvTransform(const Point& r, const Matrix3x3& rotpos, const Point& linpos);
+
+ //! Returns MIN(x, y, z);
+ inline_ float Min() const { return MIN(x, MIN(y, z)); }
+ //! Returns MAX(x, y, z);
+ inline_ float Max() const { return MAX(x, MAX(y, z)); }
+ //! Sets each element to be componentwise minimum
+ inline_ Point& Min(const Point& p) { x = MIN(x, p.x); y = MIN(y, p.y); z = MIN(z, p.z); return *this; }
+ //! Sets each element to be componentwise maximum
+ inline_ Point& Max(const Point& p) { x = MAX(x, p.x); y = MAX(y, p.y); z = MAX(z, p.z); return *this; }
+
+ //! Clamps each element
+ inline_ Point& Clamp(float min, float max)
+ {
+ if(x<min) x=min; if(x>max) x=max;
+ if(y<min) y=min; if(y>max) y=max;
+ if(z<min) z=min; if(z>max) z=max;
+ return *this;
+ }
+
+ //! Computes square magnitude
+ inline_ float SquareMagnitude() const { return x*x + y*y + z*z; }
+ //! Computes magnitude
+ inline_ float Magnitude() const { return sqrtf(x*x + y*y + z*z); }
+ //! Computes volume
+ inline_ float Volume() const { return x * y * z; }
+
+ //! Checks the point is near zero
+ inline_ bool ApproxZero() const { return SquareMagnitude() < EPSILON2; }
+
+ //! Tests for exact zero vector
+ inline_ BOOL IsZero() const
+ {
+ if(IR(x) || IR(y) || IR(z)) return FALSE;
+ return TRUE;
+ }
+
+ //! Checks point validity
+ inline_ BOOL IsValid() const
+ {
+ if(!IsValidFloat(x)) return FALSE;
+ if(!IsValidFloat(y)) return FALSE;
+ if(!IsValidFloat(z)) return FALSE;
+ return TRUE;
+ }
+
+ //! Slighty moves the point
+ void Tweak(udword coord_mask, udword tweak_mask)
+ {
+ if(coord_mask&1) { udword Dummy = IR(x); Dummy^=tweak_mask; x = FR(Dummy); }
+ if(coord_mask&2) { udword Dummy = IR(y); Dummy^=tweak_mask; y = FR(Dummy); }
+ if(coord_mask&4) { udword Dummy = IR(z); Dummy^=tweak_mask; z = FR(Dummy); }
+ }
+
+ #define TWEAKMASK 0x3fffff
+ #define TWEAKNOTMASK ~TWEAKMASK
+ //! Slighty moves the point out
+ inline_ void TweakBigger()
+ {
+ udword Dummy = (IR(x)&TWEAKNOTMASK); if(!IS_NEGATIVE_FLOAT(x)) Dummy+=TWEAKMASK+1; x = FR(Dummy);
+ Dummy = (IR(y)&TWEAKNOTMASK); if(!IS_NEGATIVE_FLOAT(y)) Dummy+=TWEAKMASK+1; y = FR(Dummy);
+ Dummy = (IR(z)&TWEAKNOTMASK); if(!IS_NEGATIVE_FLOAT(z)) Dummy+=TWEAKMASK+1; z = FR(Dummy);
+ }
+
+ //! Slighty moves the point in
+ inline_ void TweakSmaller()
+ {
+ udword Dummy = (IR(x)&TWEAKNOTMASK); if(IS_NEGATIVE_FLOAT(x)) Dummy+=TWEAKMASK+1; x = FR(Dummy);
+ Dummy = (IR(y)&TWEAKNOTMASK); if(IS_NEGATIVE_FLOAT(y)) Dummy+=TWEAKMASK+1; y = FR(Dummy);
+ Dummy = (IR(z)&TWEAKNOTMASK); if(IS_NEGATIVE_FLOAT(z)) Dummy+=TWEAKMASK+1; z = FR(Dummy);
+ }
+
+ //! Normalizes the vector
+ inline_ Point& Normalize()
+ {
+ float M = x*x + y*y + z*z;
+ if(M)
+ {
+ M = 1.0f / sqrtf(M);
+ x *= M;
+ y *= M;
+ z *= M;
+ }
+ return *this;
+ }
+
+ //! Sets vector length
+ inline_ Point& SetLength(float length)
+ {
+ float NewLength = length / Magnitude();
+ x *= NewLength;
+ y *= NewLength;
+ z *= NewLength;
+ return *this;
+ }
+
+ //! Clamps vector length
+ inline_ Point& ClampLength(float limit_length)
+ {
+ if(limit_length>=0.0f) // Magnitude must be positive
+ {
+ float CurrentSquareLength = SquareMagnitude();
+
+ if(CurrentSquareLength > limit_length * limit_length)
+ {
+ float Coeff = limit_length / sqrtf(CurrentSquareLength);
+ x *= Coeff;
+ y *= Coeff;
+ z *= Coeff;
+ }
+ }
+ return *this;
+ }
+
+ //! Computes distance to another point
+ inline_ float Distance(const Point& b) const
+ {
+ return sqrtf((x - b.x)*(x - b.x) + (y - b.y)*(y - b.y) + (z - b.z)*(z - b.z));
+ }
+
+ //! Computes square distance to another point
+ inline_ float SquareDistance(const Point& b) const
+ {
+ return ((x - b.x)*(x - b.x) + (y - b.y)*(y - b.y) + (z - b.z)*(z - b.z));
+ }
+
+ //! Dot product dp = this|a
+ inline_ float Dot(const Point& p) const { return p.x * x + p.y * y + p.z * z; }
+
+ //! Cross product this = a x b
+ inline_ Point& Cross(const Point& a, const Point& b)
+ {
+ x = a.y * b.z - a.z * b.y;
+ y = a.z * b.x - a.x * b.z;
+ z = a.x * b.y - a.y * b.x;
+ return *this;
+ }
+
+ //! Vector code ( bitmask = sign(z) | sign(y) | sign(x) )
+ inline_ udword VectorCode() const
+ {
+ return (IR(x)>>31) | ((IR(y)&SIGN_BITMASK)>>30) | ((IR(z)&SIGN_BITMASK)>>29);
+ }
+
+ //! Returns largest axis
+ inline_ PointComponent LargestAxis() const
+ {
+ const float* Vals = &x;
+ PointComponent m = _X;
+ if(Vals[_Y] > Vals[m]) m = _Y;
+ if(Vals[_Z] > Vals[m]) m = _Z;
+ return m;
+ }
+
+ //! Returns closest axis
+ inline_ PointComponent ClosestAxis() const
+ {
+ const float* Vals = &x;
+ PointComponent m = _X;
+ if(AIR(Vals[_Y]) > AIR(Vals[m])) m = _Y;
+ if(AIR(Vals[_Z]) > AIR(Vals[m])) m = _Z;
+ return m;
+ }
+
+ //! Returns smallest axis
+ inline_ PointComponent SmallestAxis() const
+ {
+ const float* Vals = &x;
+ PointComponent m = _X;
+ if(Vals[_Y] < Vals[m]) m = _Y;
+ if(Vals[_Z] < Vals[m]) m = _Z;
+ return m;
+ }
+
+ //! Refracts the point
+ Point& Refract(const Point& eye, const Point& n, float refractindex, Point& refracted);
+
+ //! Projects the point onto a plane
+ Point& ProjectToPlane(const Plane& p);
+
+ //! Projects the point onto the screen
+ void ProjectToScreen(float halfrenderwidth, float halfrenderheight, const Matrix4x4& mat, HPoint& projected) const;
+
+ //! Unfolds the point onto a plane according to edge(a,b)
+ Point& Unfold(Plane& p, Point& a, Point& b);
+
+ //! Hash function from Ville Miettinen
+ inline_ udword GetHashValue() const
+ {
+ const udword* h = (const udword*)(this);
+ udword f = (h[0]+h[1]*11-(h[2]*17)) & 0x7fffffff; // avoid problems with +-0
+ return (f>>22)^(f>>12)^(f);
+ }
+
+ //! Stuff magic values in the point, marking it as explicitely not used.
+ void SetNotUsed();
+ //! Checks the point is marked as not used
+ BOOL IsNotUsed() const;
+
+ // Arithmetic operators
+
+ //! Unary operator for Point Negate = - Point
+ inline_ Point operator-() const { return Point(-x, -y, -z); }
+
+ //! Operator for Point Plus = Point + Point.
+ inline_ Point operator+(const Point& p) const { return Point(x + p.x, y + p.y, z + p.z); }
+ //! Operator for Point Minus = Point - Point.
+ inline_ Point operator-(const Point& p) const { return Point(x - p.x, y - p.y, z - p.z); }
+
+ //! Operator for Point Mul = Point * Point.
+ inline_ Point operator*(const Point& p) const { return Point(x * p.x, y * p.y, z * p.z); }
+ //! Operator for Point Scale = Point * float.
+ inline_ Point operator*(float s) const { return Point(x * s, y * s, z * s ); }
+ //! Operator for Point Scale = float * Point.
+ inline_ friend Point operator*(float s, const Point& p) { return Point(s * p.x, s * p.y, s * p.z); }
+
+ //! Operator for Point Div = Point / Point.
+ inline_ Point operator/(const Point& p) const { return Point(x / p.x, y / p.y, z / p.z); }
+ //! Operator for Point Scale = Point / float.
+ inline_ Point operator/(float s) const { s = 1.0f / s; return Point(x * s, y * s, z * s); }
+ //! Operator for Point Scale = float / Point.
+ inline_ friend Point operator/(float s, const Point& p) { return Point(s / p.x, s / p.y, s / p.z); }
+
+ //! Operator for float DotProd = Point | Point.
+ inline_ float operator|(const Point& p) const { return x*p.x + y*p.y + z*p.z; }
+ //! Operator for Point VecProd = Point ^ Point.
+ inline_ Point operator^(const Point& p) const
+ {
+ return Point(
+ y * p.z - z * p.y,
+ z * p.x - x * p.z,
+ x * p.y - y * p.x );
+ }
+
+ //! Operator for Point += Point.
+ inline_ Point& operator+=(const Point& p) { x += p.x; y += p.y; z += p.z; return *this; }
+ //! Operator for Point += float.
+ inline_ Point& operator+=(float s) { x += s; y += s; z += s; return *this; }
+
+ //! Operator for Point -= Point.
+ inline_ Point& operator-=(const Point& p) { x -= p.x; y -= p.y; z -= p.z; return *this; }
+ //! Operator for Point -= float.
+ inline_ Point& operator-=(float s) { x -= s; y -= s; z -= s; return *this; }
+
+ //! Operator for Point *= Point.
+ inline_ Point& operator*=(const Point& p) { x *= p.x; y *= p.y; z *= p.z; return *this; }
+ //! Operator for Point *= float.
+ inline_ Point& operator*=(float s) { x *= s; y *= s; z *= s; return *this; }
+
+ //! Operator for Point /= Point.
+ inline_ Point& operator/=(const Point& p) { x /= p.x; y /= p.y; z /= p.z; return *this; }
+ //! Operator for Point /= float.
+ inline_ Point& operator/=(float s) { s = 1.0f/s; x *= s; y *= s; z *= s; return *this; }
+
+ // Logical operators
+
+ //! Operator for "if(Point==Point)"
+ inline_ bool operator==(const Point& p) const { return ( (IR(x)==IR(p.x))&&(IR(y)==IR(p.y))&&(IR(z)==IR(p.z))); }
+ //! Operator for "if(Point!=Point)"
+ inline_ bool operator!=(const Point& p) const { return ( (IR(x)!=IR(p.x))||(IR(y)!=IR(p.y))||(IR(z)!=IR(p.z))); }
+
+ // Arithmetic operators
+
+ //! Operator for Point Mul = Point * Matrix3x3.
+ inline_ Point operator*(const Matrix3x3& mat) const
+ {
+ class ShadowMatrix3x3{ public: float m[3][3]; }; // To allow inlining
+ const ShadowMatrix3x3* Mat = (const ShadowMatrix3x3*)&mat;
+
+ return Point(
+ x * Mat->m[0][0] + y * Mat->m[1][0] + z * Mat->m[2][0],
+ x * Mat->m[0][1] + y * Mat->m[1][1] + z * Mat->m[2][1],
+ x * Mat->m[0][2] + y * Mat->m[1][2] + z * Mat->m[2][2] );
+ }
+
+ //! Operator for Point Mul = Point * Matrix4x4.
+ inline_ Point operator*(const Matrix4x4& mat) const
+ {
+ class ShadowMatrix4x4{ public: float m[4][4]; }; // To allow inlining
+ const ShadowMatrix4x4* Mat = (const ShadowMatrix4x4*)&mat;
+
+ return Point(
+ x * Mat->m[0][0] + y * Mat->m[1][0] + z * Mat->m[2][0] + Mat->m[3][0],
+ x * Mat->m[0][1] + y * Mat->m[1][1] + z * Mat->m[2][1] + Mat->m[3][1],
+ x * Mat->m[0][2] + y * Mat->m[1][2] + z * Mat->m[2][2] + Mat->m[3][2]);
+ }
+
+ //! Operator for Point *= Matrix3x3.
+ inline_ Point& operator*=(const Matrix3x3& mat)
+ {
+ class ShadowMatrix3x3{ public: float m[3][3]; }; // To allow inlining
+ const ShadowMatrix3x3* Mat = (const ShadowMatrix3x3*)&mat;
+
+ float xp = x * Mat->m[0][0] + y * Mat->m[1][0] + z * Mat->m[2][0];
+ float yp = x * Mat->m[0][1] + y * Mat->m[1][1] + z * Mat->m[2][1];
+ float zp = x * Mat->m[0][2] + y * Mat->m[1][2] + z * Mat->m[2][2];
+
+ x = xp; y = yp; z = zp;
+
+ return *this;
+ }
+
+ //! Operator for Point *= Matrix4x4.
+ inline_ Point& operator*=(const Matrix4x4& mat)
+ {
+ class ShadowMatrix4x4{ public: float m[4][4]; }; // To allow inlining
+ const ShadowMatrix4x4* Mat = (const ShadowMatrix4x4*)&mat;
+
+ float xp = x * Mat->m[0][0] + y * Mat->m[1][0] + z * Mat->m[2][0] + Mat->m[3][0];
+ float yp = x * Mat->m[0][1] + y * Mat->m[1][1] + z * Mat->m[2][1] + Mat->m[3][1];
+ float zp = x * Mat->m[0][2] + y * Mat->m[1][2] + z * Mat->m[2][2] + Mat->m[3][2];
+
+ x = xp; y = yp; z = zp;
+
+ return *this;
+ }
+
+ // Cast operators
+
+ //! Cast a Point to a HPoint. w is set to zero.
+ operator HPoint() const;
+
+ inline_ operator const float*() const { return &x; }
+ inline_ operator float*() { return &x; }
+
+ public:
+ float x, y, z;
+ };
+
+ FUNCTION ICEMATHS_API void Normalize1(Point& a);
+ FUNCTION ICEMATHS_API void Normalize2(Point& a);
+
+#endif //__ICEPOINT_H__
diff --git a/Opcode/Ice/IcePreprocessor.h b/Opcode/Ice/IcePreprocessor.h
new file mode 100644
index 0000000..bb0ef7b
--- /dev/null
+++ b/Opcode/Ice/IcePreprocessor.h
@@ -0,0 +1,128 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains preprocessor stuff. This should be the first included header.
+ * \file IcePreprocessor.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEPREPROCESSOR_H__
+#define __ICEPREPROCESSOR_H__
+
+ // Check platform
+ #if defined( _WIN32 ) || defined( WIN32 )
+ #pragma message("Compiling on Windows...")
+ #define PLATFORM_WINDOWS
+ #else
+ #pragma message("Compiling on unknown platform...")
+ #endif
+
+ // Check compiler
+ #if defined(_MSC_VER)
+ #pragma message("Compiling with VC++...")
+ #define COMPILER_VISUAL_CPP
+ #else
+ #pragma message("Compiling with unknown compiler...")
+ #endif
+
+ // Check compiler options. If this file is included in user-apps, this
+ // shouldn't be needed, so that they can use what they like best.
+ #ifndef ICE_DONT_CHECK_COMPILER_OPTIONS
+ #ifdef COMPILER_VISUAL_CPP
+ #if defined(_CHAR_UNSIGNED)
+ #endif
+
+ #if defined(_CPPRTTI)
+ #error Please disable RTTI...
+ #endif
+
+ #if defined(_CPPUNWIND)
+ #error Please disable exceptions...
+ #endif
+
+ #if defined(_MT)
+ // Multithreading
+ #endif
+ #endif
+ #endif
+
+ // Check debug mode
+ #ifdef DEBUG // May be defined instead of _DEBUG. Let's fix it.
+ #ifndef _DEBUG
+ #define _DEBUG
+ #endif
+ #endif
+
+ #ifdef _DEBUG
+ // Here you may define items for debug builds
+ #endif
+
+ #ifndef THIS_FILE
+ #define THIS_FILE __FILE__
+ #endif
+
+ #ifndef ICE_NO_DLL
+ #ifdef ICECORE_EXPORTS
+ #define ICECORE_API __declspec(dllexport)
+ #else
+ #define ICECORE_API __declspec(dllimport)
+ #endif
+ #else
+ #define ICECORE_API
+ #endif
+
+ // Don't override new/delete
+// #define DEFAULT_NEWDELETE
+ #define DONT_TRACK_MEMORY_LEAKS
+
+ #define FUNCTION extern "C"
+
+ // Cosmetic stuff [mainly useful with multiple inheritance]
+ #define override(base_class) virtual
+
+ // Our own inline keyword, so that:
+ // - we can switch to __forceinline to check it's really better or not
+ // - we can remove __forceinline if the compiler doesn't support it
+// #define inline_ __forceinline
+// #define inline_ inline
+
+ // Contributed by Bruce Mitchener
+ #if defined(COMPILER_VISUAL_CPP)
+ #define inline_ __forceinline
+// #define inline_ inline
+ #elif defined(__GNUC__) && __GNUC__ < 3
+ #define inline_ inline
+ #elif defined(__GNUC__)
+ #define inline_ inline __attribute__ ((always_inline))
+ #else
+ #define inline_ inline
+ #endif
+
+ // Down the hatch
+ #pragma inline_depth( 255 )
+
+ #ifdef COMPILER_VISUAL_CPP
+ #pragma intrinsic(memcmp)
+ #pragma intrinsic(memcpy)
+ #pragma intrinsic(memset)
+ #pragma intrinsic(strcat)
+ #pragma intrinsic(strcmp)
+ #pragma intrinsic(strcpy)
+ #pragma intrinsic(strlen)
+ #pragma intrinsic(abs)
+ #pragma intrinsic(labs)
+ #endif
+
+ // ANSI compliance
+ #ifdef _DEBUG
+ // Remove painful warning in debug
+ inline_ bool __False__(){ return false; }
+ #define for if(__False__()){} else for
+ #else
+ #define for if(0){} else for
+ #endif
+
+#endif // __ICEPREPROCESSOR_H__
diff --git a/Opcode/Ice/IceRandom.cpp b/Opcode/Ice/IceRandom.cpp
new file mode 100644
index 0000000..0139be6
--- /dev/null
+++ b/Opcode/Ice/IceRandom.cpp
@@ -0,0 +1,35 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for random generators.
+ * \file IceRandom.cpp
+ * \author Pierre Terdiman
+ * \date August, 9, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceCore;
+
+void IceCore:: SRand(udword seed)
+{
+ srand(seed);
+}
+
+udword IceCore::Rand()
+{
+ return rand();
+}
+
+
+static BasicRandom gRandomGenerator(42);
+
+udword IceCore::GetRandomIndex(udword max_index)
+{
+ // We don't use rand() since it's limited to RAND_MAX
+ udword Index = gRandomGenerator.Randomize();
+ return Index % max_index;
+}
+
diff --git a/Opcode/Ice/IceRandom.h b/Opcode/Ice/IceRandom.h
new file mode 100644
index 0000000..3584769
--- /dev/null
+++ b/Opcode/Ice/IceRandom.h
@@ -0,0 +1,42 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for random generators.
+ * \file IceRandom.h
+ * \author Pierre Terdiman
+ * \date August, 9, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICERANDOM_H__
+#define __ICERANDOM_H__
+
+ FUNCTION ICECORE_API void SRand(udword seed);
+ FUNCTION ICECORE_API udword Rand();
+
+ //! Returns a unit random floating-point value
+ inline_ float UnitRandomFloat() { return float(Rand()) * ONE_OVER_RAND_MAX; }
+
+ //! Returns a random index so that 0<= index < max_index
+ ICECORE_API udword GetRandomIndex(udword max_index);
+
+ class ICECORE_API BasicRandom
+ {
+ public:
+
+ //! Constructor
+ inline_ BasicRandom(udword seed=0) : mRnd(seed) {}
+ //! Destructor
+ inline_ ~BasicRandom() {}
+
+ inline_ void SetSeed(udword seed) { mRnd = seed; }
+ inline_ udword GetCurrentValue() const { return mRnd; }
+ inline_ udword Randomize() { mRnd = mRnd * 2147001325 + 715136305; return mRnd; }
+
+ private:
+ udword mRnd;
+ };
+
+#endif // __ICERANDOM_H__
+
diff --git a/Opcode/Ice/IceRay.cpp b/Opcode/Ice/IceRay.cpp
new file mode 100644
index 0000000..be04043
--- /dev/null
+++ b/Opcode/Ice/IceRay.cpp
@@ -0,0 +1,84 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for rays.
+ * \file IceRay.cpp
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Ray class.
+ * A ray is a half-line P(t) = mOrig + mDir * t, with 0 <= t <= +infinity
+ * \class Ray
+ * \author Pierre Terdiman
+ * \version 1.0
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+ O = Origin = impact point
+ i = normalized vector along the x axis
+ j = normalized vector along the y axis = actually the normal vector in O
+ D = Direction vector, norm |D| = 1
+ N = Projection of D on y axis, norm |N| = normal reaction
+ T = Projection of D on x axis, norm |T| = tangential reaction
+ R = Reflexion vector
+
+ ^y
+ |
+ |
+ |
+ _ _ _| _ _ _
+ * * *|
+ \ | /
+ \ |N / |
+ R\ | /D
+ \ | / |
+ \ | /
+ _________\|/______*_______>x
+ O T
+
+ Let define theta = angle between D and N. Then cos(theta) = |N| / |D| = |N| since D is normalized.
+
+ j|D = |j|*|D|*cos(theta) => |N| = j|D
+
+ Then we simply have:
+
+ D = N + T
+
+ To compute tangential reaction :
+
+ T = D - N
+
+ To compute reflexion vector :
+
+ R = N - T = N - (D-N) = 2*N - D
+*/
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+float Ray::SquareDistance(const Point& point, float* t) const
+{
+ Point Diff = point - mOrig;
+ float fT = Diff | mDir;
+
+ if(fT<=0.0f)
+ {
+ fT = 0.0f;
+ }
+ else
+ {
+ fT /= mDir.SquareMagnitude();
+ Diff -= fT*mDir;
+ }
+
+ if(t) *t = fT;
+
+ return Diff.SquareMagnitude();
+}
diff --git a/Opcode/Ice/IceRay.h b/Opcode/Ice/IceRay.h
new file mode 100644
index 0000000..f99a78b
--- /dev/null
+++ b/Opcode/Ice/IceRay.h
@@ -0,0 +1,98 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for rays.
+ * \file IceRay.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICERAY_H__
+#define __ICERAY_H__
+
+ class ICEMATHS_API Ray
+ {
+ public:
+ //! Constructor
+ inline_ Ray() {}
+ //! Constructor
+ inline_ Ray(const Point& orig, const Point& dir) : mOrig(orig), mDir(dir) {}
+ //! Copy constructor
+ inline_ Ray(const Ray& ray) : mOrig(ray.mOrig), mDir(ray.mDir) {}
+ //! Destructor
+ inline_ ~Ray() {}
+
+ float SquareDistance(const Point& point, float* t=null) const;
+ inline_ float Distance(const Point& point, float* t=null) const { return sqrtf(SquareDistance(point, t)); }
+
+ Point mOrig; //!< Ray origin
+ Point mDir; //!< Normalized direction
+ };
+
+ inline_ void ComputeReflexionVector(Point& reflected, const Point& incoming_dir, const Point& outward_normal)
+ {
+ reflected = incoming_dir - outward_normal * 2.0f * (incoming_dir|outward_normal);
+ }
+
+ inline_ void ComputeReflexionVector(Point& reflected, const Point& source, const Point& impact, const Point& normal)
+ {
+ Point V = impact - source;
+ reflected = V - normal * 2.0f * (V|normal);
+ }
+
+ inline_ void DecomposeVector(Point& normal_compo, Point& tangent_compo, const Point& outward_dir, const Point& outward_normal)
+ {
+ normal_compo = outward_normal * (outward_dir|outward_normal);
+ tangent_compo = outward_dir - normal_compo;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Transforms a direction vector from world space to local space
+ * \param local_dir [out] direction vector in local space
+ * \param world_dir [in] direction vector in world space
+ * \param world [in] world transform
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void ComputeLocalDirection(Point& local_dir, const Point& world_dir, const Matrix4x4& world)
+ {
+ // Get world direction back in local space
+// Matrix3x3 InvWorld = world;
+// local_dir = InvWorld * world_dir;
+ local_dir = Matrix3x3(world) * world_dir;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Transforms a position vector from world space to local space
+ * \param local_pt [out] position vector in local space
+ * \param world_pt [in] position vector in world space
+ * \param world [in] world transform
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void ComputeLocalPoint(Point& local_pt, const Point& world_pt, const Matrix4x4& world)
+ {
+ // Get world vertex back in local space
+ Matrix4x4 InvWorld = world;
+ InvWorld.Invert();
+ local_pt = world_pt * InvWorld;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Transforms a ray from world space to local space
+ * \param local_ray [out] ray in local space
+ * \param world_ray [in] ray in world space
+ * \param world [in] world transform
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void ComputeLocalRay(Ray& local_ray, const Ray& world_ray, const Matrix4x4& world)
+ {
+ // Get world ray back in local space
+ ComputeLocalDirection(local_ray.mDir, world_ray.mDir, world);
+ ComputeLocalPoint(local_ray.mOrig, world_ray.mOrig, world);
+ }
+
+#endif // __ICERAY_H__
diff --git a/Opcode/Ice/IceRevisitedRadix.cpp b/Opcode/Ice/IceRevisitedRadix.cpp
new file mode 100644
index 0000000..f55b0cf
--- /dev/null
+++ b/Opcode/Ice/IceRevisitedRadix.cpp
@@ -0,0 +1,520 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains source code from the article "Radix Sort Revisited".
+ * \file IceRevisitedRadix.cpp
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Revisited Radix Sort.
+ * This is my new radix routine:
+ * - it uses indices and doesn't recopy the values anymore, hence wasting less ram
+ * - it creates all the histograms in one run instead of four
+ * - it sorts words faster than dwords and bytes faster than words
+ * - it correctly sorts negative floating-point values by patching the offsets
+ * - it automatically takes advantage of temporal coherence
+ * - multiple keys support is a side effect of temporal coherence
+ * - it may be worth recoding in asm... (mainly to use FCOMI, FCMOV, etc) [it's probably memory-bound anyway]
+ *
+ * History:
+ * - 08.15.98: very first version
+ * - 04.04.00: recoded for the radix article
+ * - 12.xx.00: code lifting
+ * - 09.18.01: faster CHECK_PASS_VALIDITY thanks to Mark D. Shattuck (who provided other tips, not included here)
+ * - 10.11.01: added local ram support
+ * - 01.20.02: bugfix! In very particular cases the last pass was skipped in the float code-path, leading to incorrect sorting......
+ * - 01.02.02: - "mIndices" renamed => "mRanks". That's a rank sorter after all.
+ * - ranks are not "reset" anymore, but implicit on first calls
+ * - 07.05.02: - offsets rewritten with one less indirection.
+ * - 11.03.02: - "bool" replaced with RadixHint enum
+ *
+ * \class RadixSort
+ * \author Pierre Terdiman
+ * \version 1.4
+ * \date August, 15, 1998
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+To do:
+ - add an offset parameter between two input values (avoid some data recopy sometimes)
+ - unroll ? asm ?
+ - 11 bits trick & 3 passes as Michael did
+ - prefetch stuff the day I have a P3
+ - make a version with 16-bits indices ?
+*/
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceCore;
+
+#define INVALIDATE_RANKS mCurrentSize|=0x80000000
+#define VALIDATE_RANKS mCurrentSize&=0x7fffffff
+#define CURRENT_SIZE (mCurrentSize&0x7fffffff)
+#define INVALID_RANKS (mCurrentSize&0x80000000)
+
+#define CHECK_RESIZE(n) \
+ if(n!=mPreviousSize) \
+ { \
+ if(n>mCurrentSize) Resize(n); \
+ else ResetRanks(); \
+ mPreviousSize = n; \
+ }
+
+#define CREATE_HISTOGRAMS(type, buffer) \
+ /* Clear counters/histograms */ \
+ ZeroMemory(mHistogram, 256*4*sizeof(udword)); \
+ \
+ /* Prepare to count */ \
+ ubyte* p = (ubyte*)input; \
+ ubyte* pe = &p[nb*4]; \
+ udword* h0= &mHistogram[0]; /* Histogram for first pass (LSB) */ \
+ udword* h1= &mHistogram[256]; /* Histogram for second pass */ \
+ udword* h2= &mHistogram[512]; /* Histogram for third pass */ \
+ udword* h3= &mHistogram[768]; /* Histogram for last pass (MSB) */ \
+ \
+ bool AlreadySorted = true; /* Optimism... */ \
+ \
+ if(INVALID_RANKS) \
+ { \
+ /* Prepare for temporal coherence */ \
+ type* Running = (type*)buffer; \
+ type PrevVal = *Running; \
+ \
+ while(p!=pe) \
+ { \
+ /* Read input buffer in previous sorted order */ \
+ type Val = *Running++; \
+ /* Check whether already sorted or not */ \
+ if(Val<PrevVal) { AlreadySorted = false; break; } /* Early out */ \
+ /* Update for next iteration */ \
+ PrevVal = Val; \
+ \
+ /* Create histograms */ \
+ h0[*p++]++; h1[*p++]++; h2[*p++]++; h3[*p++]++; \
+ } \
+ \
+ /* If all input values are already sorted, we just have to return and leave the */ \
+ /* previous list unchanged. That way the routine may take advantage of temporal */ \
+ /* coherence, for example when used to sort transparent faces. */ \
+ if(AlreadySorted) \
+ { \
+ mNbHits++; \
+ for(udword i=0;i<nb;i++) mRanks[i] = i; \
+ return *this; \
+ } \
+ } \
+ else \
+ { \
+ /* Prepare for temporal coherence */ \
+ udword* Indices = mRanks; \
+ type PrevVal = (type)buffer[*Indices]; \
+ \
+ while(p!=pe) \
+ { \
+ /* Read input buffer in previous sorted order */ \
+ type Val = (type)buffer[*Indices++]; \
+ /* Check whether already sorted or not */ \
+ if(Val<PrevVal) { AlreadySorted = false; break; } /* Early out */ \
+ /* Update for next iteration */ \
+ PrevVal = Val; \
+ \
+ /* Create histograms */ \
+ h0[*p++]++; h1[*p++]++; h2[*p++]++; h3[*p++]++; \
+ } \
+ \
+ /* If all input values are already sorted, we just have to return and leave the */ \
+ /* previous list unchanged. That way the routine may take advantage of temporal */ \
+ /* coherence, for example when used to sort transparent faces. */ \
+ if(AlreadySorted) { mNbHits++; return *this; } \
+ } \
+ \
+ /* Else there has been an early out and we must finish computing the histograms */ \
+ while(p!=pe) \
+ { \
+ /* Create histograms without the previous overhead */ \
+ h0[*p++]++; h1[*p++]++; h2[*p++]++; h3[*p++]++; \
+ }
+
+#define CHECK_PASS_VALIDITY(pass) \
+ /* Shortcut to current counters */ \
+ udword* CurCount = &mHistogram[pass<<8]; \
+ \
+ /* Reset flag. The sorting pass is supposed to be performed. (default) */ \
+ bool PerformPass = true; \
+ \
+ /* Check pass validity */ \
+ \
+ /* If all values have the same byte, sorting is useless. */ \
+ /* It may happen when sorting bytes or words instead of dwords. */ \
+ /* This routine actually sorts words faster than dwords, and bytes */ \
+ /* faster than words. Standard running time (O(4*n))is reduced to O(2*n) */ \
+ /* for words and O(n) for bytes. Running time for floats depends on actual values... */ \
+ \
+ /* Get first byte */ \
+ ubyte UniqueVal = *(((ubyte*)input)+pass); \
+ \
+ /* Check that byte's counter */ \
+ if(CurCount[UniqueVal]==nb) PerformPass=false;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+RadixSort::RadixSort() : mRanks(null), mRanks2(null), mCurrentSize(0), mTotalCalls(0), mNbHits(0)
+{
+#ifndef RADIX_LOCAL_RAM
+ // Allocate input-independent ram
+ mHistogram = new udword[256*4];
+ mOffset = new udword[256];
+#endif
+ // Initialize indices
+ INVALIDATE_RANKS;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+RadixSort::~RadixSort()
+{
+ // Release everything
+#ifndef RADIX_LOCAL_RAM
+ DELETEARRAY(mOffset);
+ DELETEARRAY(mHistogram);
+#endif
+ DELETEARRAY(mRanks2);
+ DELETEARRAY(mRanks);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resizes the inner lists.
+ * \param nb [in] new size (number of dwords)
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool RadixSort::Resize(udword nb)
+{
+ // Free previously used ram
+ DELETEARRAY(mRanks2);
+ DELETEARRAY(mRanks);
+
+ // Get some fresh one
+ mRanks = new udword[nb]; CHECKALLOC(mRanks);
+ mRanks2 = new udword[nb]; CHECKALLOC(mRanks2);
+
+ return true;
+}
+
+inline_ void RadixSort::CheckResize(udword nb)
+{
+ udword CurSize = CURRENT_SIZE;
+ if(nb!=CurSize)
+ {
+ if(nb>CurSize) Resize(nb);
+ mCurrentSize = nb;
+ INVALIDATE_RANKS;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Main sort routine.
+ * This one is for integer values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data.
+ * \param input [in] a list of integer values to sort
+ * \param nb [in] number of values to sort, must be < 2^31
+ * \param hint [in] RADIX_SIGNED to handle negative values, RADIX_UNSIGNED if you know your input buffer only contains positive values
+ * \return Self-Reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+RadixSort& RadixSort::Sort(const udword* input, udword nb, RadixHint hint)
+{
+ // Checkings
+ if(!input || !nb || nb&0x80000000) return *this;
+
+ // Stats
+ mTotalCalls++;
+
+ // Resize lists if needed
+ CheckResize(nb);
+
+#ifdef RADIX_LOCAL_RAM
+ // Allocate histograms & offsets on the stack
+ udword mHistogram[256*4];
+// udword mOffset[256];
+ udword* mLink[256];
+#endif
+
+ // Create histograms (counters). Counters for all passes are created in one run.
+ // Pros: read input buffer once instead of four times
+ // Cons: mHistogram is 4Kb instead of 1Kb
+ // We must take care of signed/unsigned values for temporal coherence.... I just
+ // have 2 code paths even if just a single opcode changes. Self-modifying code, someone?
+ if(hint==RADIX_UNSIGNED) { CREATE_HISTOGRAMS(udword, input); }
+ else { CREATE_HISTOGRAMS(sdword, input); }
+
+ // Compute #negative values involved if needed
+ udword NbNegativeValues = 0;
+ if(hint==RADIX_SIGNED)
+ {
+ // An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128
+ // last values of the last histogram. Last histogram because that's the one for the Most Significant Byte,
+ // responsible for the sign. 128 last values because the 128 first ones are related to positive numbers.
+ udword* h3= &mHistogram[768];
+ for(udword i=128;i<256;i++) NbNegativeValues += h3[i]; // 768 for last histogram, 128 for negative part
+ }
+
+ // Radix sort, j is the pass number (0=LSB, 3=MSB)
+ for(udword j=0;j<4;j++)
+ {
+ CHECK_PASS_VALIDITY(j);
+
+ // Sometimes the fourth (negative) pass is skipped because all numbers are negative and the MSB is 0xFF (for example). This is
+ // not a problem, numbers are correctly sorted anyway.
+ if(PerformPass)
+ {
+ // Should we care about negative values?
+ if(j!=3 || hint==RADIX_UNSIGNED)
+ {
+ // Here we deal with positive values only
+
+ // Create offsets
+// mOffset[0] = 0;
+// for(udword i=1;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1];
+ mLink[0] = mRanks2;
+ for(udword i=1;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1];
+ }
+ else
+ {
+ // This is a special case to correctly handle negative integers. They're sorted in the right order but at the wrong place.
+
+ // Create biased offsets, in order for negative numbers to be sorted as well
+// mOffset[0] = NbNegativeValues; // First positive number takes place after the negative ones
+ mLink[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones
+// for(udword i=1;i<128;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers
+ for(udword i=1;i<128;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers
+
+ // Fixing the wrong place for negative values
+// mOffset[128] = 0;
+ mLink[128] = mRanks2;
+// for(i=129;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1];
+ for(udword i=129;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1];
+ }
+
+ // Perform Radix Sort
+ ubyte* InputBytes = (ubyte*)input;
+ InputBytes += j;
+ if(INVALID_RANKS)
+ {
+// for(udword i=0;i<nb;i++) mRanks2[mOffset[InputBytes[i<<2]]++] = i;
+ for(udword i=0;i<nb;i++) *mLink[InputBytes[i<<2]]++ = i;
+ VALIDATE_RANKS;
+ }
+ else
+ {
+ udword* Indices = mRanks;
+ udword* IndicesEnd = &mRanks[nb];
+ while(Indices!=IndicesEnd)
+ {
+ udword id = *Indices++;
+// mRanks2[mOffset[InputBytes[id<<2]]++] = id;
+ *mLink[InputBytes[id<<2]]++ = id;
+ }
+ }
+
+ // Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
+ udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
+ }
+ }
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Main sort routine.
+ * This one is for floating-point values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data.
+ * \param input [in] a list of floating-point values to sort
+ * \param nb [in] number of values to sort, must be < 2^31
+ * \return Self-Reference
+ * \warning only sorts IEEE floating-point values
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+RadixSort& RadixSort::Sort(const float* input2, udword nb)
+{
+ // Checkings
+ if(!input2 || !nb || nb&0x80000000) return *this;
+
+ // Stats
+ mTotalCalls++;
+
+ udword* input = (udword*)input2;
+
+ // Resize lists if needed
+ CheckResize(nb);
+
+#ifdef RADIX_LOCAL_RAM
+ // Allocate histograms & offsets on the stack
+ udword mHistogram[256*4];
+// udword mOffset[256];
+ udword* mLink[256];
+#endif
+
+ // Create histograms (counters). Counters for all passes are created in one run.
+ // Pros: read input buffer once instead of four times
+ // Cons: mHistogram is 4Kb instead of 1Kb
+ // Floating-point values are always supposed to be signed values, so there's only one code path there.
+ // Please note the floating point comparison needed for temporal coherence! Although the resulting asm code
+ // is dreadful, this is surprisingly not such a performance hit - well, I suppose that's a big one on first
+ // generation Pentiums....We can't make comparison on integer representations because, as Chris said, it just
+ // wouldn't work with mixed positive/negative values....
+ { CREATE_HISTOGRAMS(float, input2); }
+
+ // Compute #negative values involved if needed
+ udword NbNegativeValues = 0;
+ // An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128
+ // last values of the last histogram. Last histogram because that's the one for the Most Significant Byte,
+ // responsible for the sign. 128 last values because the 128 first ones are related to positive numbers.
+ udword* h3= &mHistogram[768];
+ for(udword i=128;i<256;i++) NbNegativeValues += h3[i]; // 768 for last histogram, 128 for negative part
+
+ // Radix sort, j is the pass number (0=LSB, 3=MSB)
+ for(udword j=0;j<4;j++)
+ {
+ // Should we care about negative values?
+ if(j!=3)
+ {
+ // Here we deal with positive values only
+ CHECK_PASS_VALIDITY(j);
+
+ if(PerformPass)
+ {
+ // Create offsets
+// mOffset[0] = 0;
+ mLink[0] = mRanks2;
+// for(udword i=1;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1];
+ for(udword i=1;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1];
+
+ // Perform Radix Sort
+ ubyte* InputBytes = (ubyte*)input;
+ InputBytes += j;
+ if(INVALID_RANKS)
+ {
+// for(i=0;i<nb;i++) mRanks2[mOffset[InputBytes[i<<2]]++] = i;
+ for(udword i=0;i<nb;i++) *mLink[InputBytes[i<<2]]++ = i;
+ VALIDATE_RANKS;
+ }
+ else
+ {
+ udword* Indices = mRanks;
+ udword* IndicesEnd = &mRanks[nb];
+ while(Indices!=IndicesEnd)
+ {
+ udword id = *Indices++;
+// mRanks2[mOffset[InputBytes[id<<2]]++] = id;
+ *mLink[InputBytes[id<<2]]++ = id;
+ }
+ }
+
+ // Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
+ udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
+ }
+ }
+ else
+ {
+ // This is a special case to correctly handle negative values
+ CHECK_PASS_VALIDITY(j);
+
+ if(PerformPass)
+ {
+ // Create biased offsets, in order for negative numbers to be sorted as well
+// mOffset[0] = NbNegativeValues; // First positive number takes place after the negative ones
+ mLink[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones
+// for(udword i=1;i<128;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers
+ for(udword i=1;i<128;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers
+
+ // We must reverse the sorting order for negative numbers!
+// mOffset[255] = 0;
+ mLink[255] = mRanks2;
+// for(i=0;i<127;i++) mOffset[254-i] = mOffset[255-i] + CurCount[255-i]; // Fixing the wrong order for negative values
+ for(udword i=0;i<127;i++) mLink[254-i] = mLink[255-i] + CurCount[255-i]; // Fixing the wrong order for negative values
+// for(i=128;i<256;i++) mOffset[i] += CurCount[i]; // Fixing the wrong place for negative values
+ for(udword i=128;i<256;i++) mLink[i] += CurCount[i]; // Fixing the wrong place for negative values
+
+ // Perform Radix Sort
+ if(INVALID_RANKS)
+ {
+ for(udword i=0;i<nb;i++)
+ {
+ udword Radix = input[i]>>24; // Radix byte, same as above. AND is useless here (udword).
+ // ### cmp to be killed. Not good. Later.
+// if(Radix<128) mRanks2[mOffset[Radix]++] = i; // Number is positive, same as above
+// else mRanks2[--mOffset[Radix]] = i; // Number is negative, flip the sorting order
+ if(Radix<128) *mLink[Radix]++ = i; // Number is positive, same as above
+ else *(--mLink[Radix]) = i; // Number is negative, flip the sorting order
+ }
+ VALIDATE_RANKS;
+ }
+ else
+ {
+ for(udword i=0;i<nb;i++)
+ {
+ udword Radix = input[mRanks[i]]>>24; // Radix byte, same as above. AND is useless here (udword).
+ // ### cmp to be killed. Not good. Later.
+// if(Radix<128) mRanks2[mOffset[Radix]++] = mRanks[i]; // Number is positive, same as above
+// else mRanks2[--mOffset[Radix]] = mRanks[i]; // Number is negative, flip the sorting order
+ if(Radix<128) *mLink[Radix]++ = mRanks[i]; // Number is positive, same as above
+ else *(--mLink[Radix]) = mRanks[i]; // Number is negative, flip the sorting order
+ }
+ }
+ // Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
+ udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
+ }
+ else
+ {
+ // The pass is useless, yet we still have to reverse the order of current list if all values are negative.
+ if(UniqueVal>=128)
+ {
+ if(INVALID_RANKS)
+ {
+ // ###Possible?
+ for(udword i=0;i<nb;i++) mRanks2[i] = nb-i-1;
+ VALIDATE_RANKS;
+ }
+ else
+ {
+ for(udword i=0;i<nb;i++) mRanks2[i] = mRanks[nb-i-1];
+ }
+
+ // Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
+ udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
+ }
+ }
+ }
+ }
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the ram used.
+ * \return memory used in bytes
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword RadixSort::GetUsedRam() const
+{
+ udword UsedRam = sizeof(RadixSort);
+#ifndef RADIX_LOCAL_RAM
+ UsedRam += 256*4*sizeof(udword); // Histograms
+ UsedRam += 256*sizeof(udword); // Offsets
+#endif
+ UsedRam += 2*CURRENT_SIZE*sizeof(udword); // 2 lists of indices
+ return UsedRam;
+}
diff --git a/Opcode/Ice/IceRevisitedRadix.h b/Opcode/Ice/IceRevisitedRadix.h
new file mode 100644
index 0000000..ec2f6b1
--- /dev/null
+++ b/Opcode/Ice/IceRevisitedRadix.h
@@ -0,0 +1,65 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains source code from the article "Radix Sort Revisited".
+ * \file IceRevisitedRadix.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICERADIXSORT_H__
+#define __ICERADIXSORT_H__
+
+ //! Allocate histograms & offsets locally
+ #define RADIX_LOCAL_RAM
+
+ enum RadixHint
+ {
+ RADIX_SIGNED, //!< Input values are signed
+ RADIX_UNSIGNED, //!< Input values are unsigned
+
+ RADIX_FORCE_DWORD = 0x7fffffff
+ };
+
+ class ICECORE_API RadixSort
+ {
+ public:
+ // Constructor/Destructor
+ RadixSort();
+ ~RadixSort();
+ // Sorting methods
+ RadixSort& Sort(const udword* input, udword nb, RadixHint hint=RADIX_SIGNED);
+ RadixSort& Sort(const float* input, udword nb);
+
+ //! Access to results. mRanks is a list of indices in sorted order, i.e. in the order you may further process your data
+ inline_ const udword* GetRanks() const { return mRanks; }
+
+ //! mIndices2 gets trashed on calling the sort routine, but otherwise you can recycle it the way you want.
+ inline_ udword* GetRecyclable() const { return mRanks2; }
+
+ // Stats
+ udword GetUsedRam() const;
+ //! Returns the total number of calls to the radix sorter.
+ inline_ udword GetNbTotalCalls() const { return mTotalCalls; }
+ //! Returns the number of eraly exits due to temporal coherence.
+ inline_ udword GetNbHits() const { return mNbHits; }
+
+ private:
+#ifndef RADIX_LOCAL_RAM
+ udword* mHistogram; //!< Counters for each byte
+ udword* mOffset; //!< Offsets (nearly a cumulative distribution function)
+#endif
+ udword mCurrentSize; //!< Current size of the indices list
+ udword* mRanks; //!< Two lists, swapped each pass
+ udword* mRanks2;
+ // Stats
+ udword mTotalCalls; //!< Total number of calls to the sort routine
+ udword mNbHits; //!< Number of early exits due to coherence
+ // Internal methods
+ void CheckResize(udword nb);
+ bool Resize(udword nb);
+ };
+
+#endif // __ICERADIXSORT_H__
diff --git a/Opcode/Ice/IceSegment.cpp b/Opcode/Ice/IceSegment.cpp
new file mode 100644
index 0000000..4b51343
--- /dev/null
+++ b/Opcode/Ice/IceSegment.cpp
@@ -0,0 +1,57 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for segments.
+ * \file IceSegment.cpp
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Segment class.
+ * A segment is defined by S(t) = mP0 * (1 - t) + mP1 * t, with 0 <= t <= 1
+ * Alternatively, a segment is S(t) = Origin + t * Direction for 0 <= t <= 1.
+ * Direction is not necessarily unit length. The end points are Origin = mP0 and Origin + Direction = mP1.
+ *
+ * \class Segment
+ * \author Pierre Terdiman
+ * \version 1.0
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+float Segment::SquareDistance(const Point& point, float* t) const
+{
+ Point Diff = point - mP0;
+ Point Dir = mP1 - mP0;
+ float fT = Diff | Dir;
+
+ if(fT<=0.0f)
+ {
+ fT = 0.0f;
+ }
+ else
+ {
+ float SqrLen= Dir.SquareMagnitude();
+ if(fT>=SqrLen)
+ {
+ fT = 1.0f;
+ Diff -= Dir;
+ }
+ else
+ {
+ fT /= SqrLen;
+ Diff -= fT*Dir;
+ }
+ }
+
+ if(t) *t = fT;
+
+ return Diff.SquareMagnitude();
+}
diff --git a/Opcode/Ice/IceSegment.h b/Opcode/Ice/IceSegment.h
new file mode 100644
index 0000000..3dc8314
--- /dev/null
+++ b/Opcode/Ice/IceSegment.h
@@ -0,0 +1,55 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for segments.
+ * \file IceSegment.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICESEGMENT_H__
+#define __ICESEGMENT_H__
+
+ class ICEMATHS_API Segment
+ {
+ public:
+ //! Constructor
+ inline_ Segment() {}
+ //! Constructor
+ inline_ Segment(const Point& p0, const Point& p1) : mP0(p0), mP1(p1) {}
+ //! Copy constructor
+ inline_ Segment(const Segment& seg) : mP0(seg.mP0), mP1(seg.mP1) {}
+ //! Destructor
+ inline_ ~Segment() {}
+
+ inline_ const Point& GetOrigin() const { return mP0; }
+ inline_ Point ComputeDirection() const { return mP1 - mP0; }
+ inline_ void ComputeDirection(Point& dir) const { dir = mP1 - mP0; }
+ inline_ float ComputeLength() const { return mP1.Distance(mP0); }
+ inline_ float ComputeSquareLength() const { return mP1.SquareDistance(mP0); }
+
+ inline_ void SetOriginDirection(const Point& origin, const Point& direction)
+ {
+ mP0 = mP1 = origin;
+ mP1 += direction;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes a point on the segment
+ * \param pt [out] point on segment
+ * \param t [in] point's parameter [t=0 => pt = mP0, t=1 => pt = mP1]
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void ComputePoint(Point& pt, float t) const { pt = mP0 + t * (mP1 - mP0); }
+
+ float SquareDistance(const Point& point, float* t=null) const;
+ inline_ float Distance(const Point& point, float* t=null) const { return sqrtf(SquareDistance(point, t)); }
+
+ Point mP0; //!< Start of segment
+ Point mP1; //!< End of segment
+ };
+
+#endif // __ICESEGMENT_H__
diff --git a/Opcode/Ice/IceTriangle.cpp b/Opcode/Ice/IceTriangle.cpp
new file mode 100644
index 0000000..ef89109
--- /dev/null
+++ b/Opcode/Ice/IceTriangle.cpp
@@ -0,0 +1,286 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a handy triangle class.
+ * \file IceTriangle.cpp
+ * \author Pierre Terdiman
+ * \date January, 17, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a triangle class.
+ *
+ * \class Tri
+ * \author Pierre Terdiman
+ * \version 1.0
+ * \date 08.15.98
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static sdword VPlaneSideEps(const Point& v, const Plane& plane, float epsilon)
+{
+ // Compute distance from current vertex to the plane
+ float Dist = plane.Distance(v);
+ // Compute side:
+ // 1 = the vertex is on the positive side of the plane
+ // -1 = the vertex is on the negative side of the plane
+ // 0 = the vertex is on the plane (within epsilon)
+ return Dist > epsilon ? 1 : Dist < -epsilon ? -1 : 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Flips the winding order.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Triangle::Flip()
+{
+ Point Tmp = mVerts[1];
+ mVerts[1] = mVerts[2];
+ mVerts[2] = Tmp;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle area.
+ * \return the area
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float Triangle::Area() const
+{
+ const Point& p0 = mVerts[0];
+ const Point& p1 = mVerts[1];
+ const Point& p2 = mVerts[2];
+ return ((p0 - p1)^(p0 - p2)).Magnitude() * 0.5f;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle perimeter.
+ * \return the perimeter
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float Triangle::Perimeter() const
+{
+ const Point& p0 = mVerts[0];
+ const Point& p1 = mVerts[1];
+ const Point& p2 = mVerts[2];
+ return p0.Distance(p1)
+ + p0.Distance(p2)
+ + p1.Distance(p2);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle compacity.
+ * \return the compacity
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float Triangle::Compacity() const
+{
+ float P = Perimeter();
+ if(P==0.0f) return 0.0f;
+ return (4.0f*PI*Area()/(P*P));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle normal.
+ * \param normal [out] the computed normal
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Triangle::Normal(Point& normal) const
+{
+ const Point& p0 = mVerts[0];
+ const Point& p1 = mVerts[1];
+ const Point& p2 = mVerts[2];
+ normal = ((p0 - p1)^(p0 - p2)).Normalize();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle denormalized normal.
+ * \param normal [out] the computed normal
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Triangle::DenormalizedNormal(Point& normal) const
+{
+ const Point& p0 = mVerts[0];
+ const Point& p1 = mVerts[1];
+ const Point& p2 = mVerts[2];
+ normal = ((p0 - p1)^(p0 - p2));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle center.
+ * \param center [out] the computed center
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Triangle::Center(Point& center) const
+{
+ const Point& p0 = mVerts[0];
+ const Point& p1 = mVerts[1];
+ const Point& p2 = mVerts[2];
+ center = (p0 + p1 + p2)*INV3;
+}
+
+PartVal Triangle::TestAgainstPlane(const Plane& plane, float epsilon) const
+{
+ bool Pos = false, Neg = false;
+
+ // Loop through all vertices
+ for(udword i=0;i<3;i++)
+ {
+ // Compute side:
+ sdword Side = VPlaneSideEps(mVerts[i], plane, epsilon);
+
+ if (Side < 0) Neg = true;
+ else if (Side > 0) Pos = true;
+ }
+
+ if (!Pos && !Neg) return TRI_ON_PLANE;
+ else if (Pos && Neg) return TRI_INTERSECT;
+ else if (Pos && !Neg) return TRI_PLUS_SPACE;
+ else if (!Pos && Neg) return TRI_MINUS_SPACE;
+
+ // What?!
+ return TRI_FORCEDWORD;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle moment.
+ * \param m [out] the moment
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+void Triangle::ComputeMoment(Moment& m)
+{
+ // Compute the area of the triangle
+ m.mArea = Area();
+
+ // Compute the centroid
+ Center(m.mCentroid);
+
+ // Second-order components. Handle zero-area faces.
+ Point& p = mVerts[0];
+ Point& q = mVerts[1];
+ Point& r = mVerts[2];
+ if(m.mArea==0.0f)
+ {
+ // This triangle has zero area. The second order components would be eliminated with the usual formula, so, for the
+ // sake of robustness we use an alternative form. These are the centroid and second-order components of the triangle's vertices.
+ m.mCovariance.m[0][0] = (p.x*p.x + q.x*q.x + r.x*r.x);
+ m.mCovariance.m[0][1] = (p.x*p.y + q.x*q.y + r.x*r.y);
+ m.mCovariance.m[0][2] = (p.x*p.z + q.x*q.z + r.x*r.z);
+ m.mCovariance.m[1][1] = (p.y*p.y + q.y*q.y + r.y*r.y);
+ m.mCovariance.m[1][2] = (p.y*p.z + q.y*q.z + r.y*r.z);
+ m.mCovariance.m[2][2] = (p.z*p.z + q.z*q.z + r.z*r.z);
+ m.mCovariance.m[2][1] = m.mCovariance.m[1][2];
+ m.mCovariance.m[1][0] = m.mCovariance.m[0][1];
+ m.mCovariance.m[2][0] = m.mCovariance.m[0][2];
+ }
+ else
+ {
+ const float OneOverTwelve = 1.0f / 12.0f;
+ m.mCovariance.m[0][0] = m.mArea * (9.0f * m.mCentroid.x*m.mCentroid.x + p.x*p.x + q.x*q.x + r.x*r.x) * OneOverTwelve;
+ m.mCovariance.m[0][1] = m.mArea * (9.0f * m.mCentroid.x*m.mCentroid.y + p.x*p.y + q.x*q.y + r.x*r.y) * OneOverTwelve;
+ m.mCovariance.m[1][1] = m.mArea * (9.0f * m.mCentroid.y*m.mCentroid.y + p.y*p.y + q.y*q.y + r.y*r.y) * OneOverTwelve;
+ m.mCovariance.m[0][2] = m.mArea * (9.0f * m.mCentroid.x*m.mCentroid.z + p.x*p.z + q.x*q.z + r.x*r.z) * OneOverTwelve;
+ m.mCovariance.m[1][2] = m.mArea * (9.0f * m.mCentroid.y*m.mCentroid.z + p.y*p.z + q.y*q.z + r.y*r.z) * OneOverTwelve;
+ m.mCovariance.m[2][2] = m.mArea * (9.0f * m.mCentroid.z*m.mCentroid.z + p.z*p.z + q.z*q.z + r.z*r.z) * OneOverTwelve;
+ m.mCovariance.m[2][1] = m.mCovariance.m[1][2];
+ m.mCovariance.m[1][0] = m.mCovariance.m[0][1];
+ m.mCovariance.m[2][0] = m.mCovariance.m[0][2];
+ }
+}
+*/
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle's smallest edge length.
+ * \return the smallest edge length
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float Triangle::MinEdgeLength() const
+{
+ float Min = MAX_FLOAT;
+ float Length01 = mVerts[0].Distance(mVerts[1]);
+ float Length02 = mVerts[0].Distance(mVerts[2]);
+ float Length12 = mVerts[1].Distance(mVerts[2]);
+ if(Length01 < Min) Min = Length01;
+ if(Length02 < Min) Min = Length02;
+ if(Length12 < Min) Min = Length12;
+ return Min;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle's largest edge length.
+ * \return the largest edge length
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float Triangle::MaxEdgeLength() const
+{
+ float Max = MIN_FLOAT;
+ float Length01 = mVerts[0].Distance(mVerts[1]);
+ float Length02 = mVerts[0].Distance(mVerts[2]);
+ float Length12 = mVerts[1].Distance(mVerts[2]);
+ if(Length01 > Max) Max = Length01;
+ if(Length02 > Max) Max = Length02;
+ if(Length12 > Max) Max = Length12;
+ return Max;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes a point on the triangle according to the stabbing information.
+ * \param u,v [in] point's barycentric coordinates
+ * \param pt [out] point on triangle
+ * \param nearvtx [out] index of nearest vertex
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Triangle::ComputePoint(float u, float v, Point& pt, udword* nearvtx) const
+{
+ // Compute point coordinates
+ pt = (1.0f - u - v)*mVerts[0] + u*mVerts[1] + v*mVerts[2];
+
+ // Compute nearest vertex if needed
+ if(nearvtx)
+ {
+ // Compute distance vector
+ Point d(mVerts[0].SquareDistance(pt), // Distance^2 from vertex 0 to point on the face
+ mVerts[1].SquareDistance(pt), // Distance^2 from vertex 1 to point on the face
+ mVerts[2].SquareDistance(pt)); // Distance^2 from vertex 2 to point on the face
+
+ // Get smallest distance
+ *nearvtx = d.SmallestAxis();
+ }
+}
+
+void Triangle::Inflate(float fat_coeff, bool constant_border)
+{
+ // Compute triangle center
+ Point TriangleCenter;
+ Center(TriangleCenter);
+
+ // Don't normalize?
+ // Normalize => add a constant border, regardless of triangle size
+ // Don't => add more to big triangles
+ for(udword i=0;i<3;i++)
+ {
+ Point v = mVerts[i] - TriangleCenter;
+
+ if(constant_border) v.Normalize();
+
+ mVerts[i] += v * fat_coeff;
+ }
+}
diff --git a/Opcode/Ice/IceTriangle.h b/Opcode/Ice/IceTriangle.h
new file mode 100644
index 0000000..02e03ce
--- /dev/null
+++ b/Opcode/Ice/IceTriangle.h
@@ -0,0 +1,68 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a handy triangle class.
+ * \file IceTriangle.h
+ * \author Pierre Terdiman
+ * \date January, 17, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICETRIANGLE_H__
+#define __ICETRIANGLE_H__
+
+ // Forward declarations
+ class Moment;
+
+ // Partitioning values
+ enum PartVal
+ {
+ TRI_MINUS_SPACE = 0, //!< Triangle is in the negative space
+ TRI_PLUS_SPACE = 1, //!< Triangle is in the positive space
+ TRI_INTERSECT = 2, //!< Triangle intersects plane
+ TRI_ON_PLANE = 3, //!< Triangle and plane are coplanar
+
+ TRI_FORCEDWORD = 0x7fffffff
+ };
+
+ // A triangle class.
+ class ICEMATHS_API Triangle
+ {
+ public:
+ //! Constructor
+ inline_ Triangle() {}
+ //! Constructor
+ inline_ Triangle(const Point& p0, const Point& p1, const Point& p2) { mVerts[0]=p0; mVerts[1]=p1; mVerts[2]=p2; }
+ //! Copy constructor
+ inline_ Triangle(const Triangle& triangle)
+ {
+ mVerts[0] = triangle.mVerts[0];
+ mVerts[1] = triangle.mVerts[1];
+ mVerts[2] = triangle.mVerts[2];
+ }
+ //! Destructor
+ inline_ ~Triangle() {}
+ //! Vertices
+ Point mVerts[3];
+
+ // Methods
+ void Flip();
+ float Area() const;
+ float Perimeter() const;
+ float Compacity() const;
+ void Normal(Point& normal) const;
+ void DenormalizedNormal(Point& normal) const;
+ void Center(Point& center) const;
+ inline_ Plane PlaneEquation() const { return Plane(mVerts[0], mVerts[1], mVerts[2]); }
+
+ PartVal TestAgainstPlane(const Plane& plane, float epsilon) const;
+// float Distance(Point& cp, Point& cq, Tri& tri);
+ void ComputeMoment(Moment& m);
+ float MinEdgeLength() const;
+ float MaxEdgeLength() const;
+ void ComputePoint(float u, float v, Point& pt, udword* nearvtx=null) const;
+ void Inflate(float fat_coeff, bool constant_border);
+ };
+
+#endif // __ICETRIANGLE_H__
diff --git a/Opcode/Ice/IceTrilist.h b/Opcode/Ice/IceTrilist.h
new file mode 100644
index 0000000..7a8d9de
--- /dev/null
+++ b/Opcode/Ice/IceTrilist.h
@@ -0,0 +1,61 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a triangle container.
+ * \file IceTrilist.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICETRILIST_H__
+#define __ICETRILIST_H__
+
+ class ICEMATHS_API TriList : public Container
+ {
+ public:
+ // Constructor / Destructor
+ TriList() {}
+ ~TriList() {}
+
+ inline_ udword GetNbTriangles() const { return GetNbEntries()/9; }
+ inline_ Triangle* GetTriangles() const { return (Triangle*)GetEntries(); }
+
+ void AddTri(const Triangle& tri)
+ {
+ Add(tri.mVerts[0].x).Add(tri.mVerts[0].y).Add(tri.mVerts[0].z);
+ Add(tri.mVerts[1].x).Add(tri.mVerts[1].y).Add(tri.mVerts[1].z);
+ Add(tri.mVerts[2].x).Add(tri.mVerts[2].y).Add(tri.mVerts[2].z);
+ }
+
+ void AddTri(const Point& p0, const Point& p1, const Point& p2)
+ {
+ Add(p0.x).Add(p0.y).Add(p0.z);
+ Add(p1.x).Add(p1.y).Add(p1.z);
+ Add(p2.x).Add(p2.y).Add(p2.z);
+ }
+ };
+
+ class ICEMATHS_API TriangleList : public Container
+ {
+ public:
+ // Constructor / Destructor
+ TriangleList() {}
+ ~TriangleList() {}
+
+ inline_ udword GetNbTriangles() const { return GetNbEntries()/3; }
+ inline_ IndexedTriangle* GetTriangles() const { return (IndexedTriangle*)GetEntries();}
+
+ void AddTriangle(const IndexedTriangle& tri)
+ {
+ Add(tri.mVRef[0]).Add(tri.mVRef[1]).Add(tri.mVRef[2]);
+ }
+
+ void AddTriangle(udword vref0, udword vref1, udword vref2)
+ {
+ Add(vref0).Add(vref1).Add(vref2);
+ }
+ };
+
+#endif //__ICETRILIST_H__
diff --git a/Opcode/Ice/IceTypes.h b/Opcode/Ice/IceTypes.h
new file mode 100644
index 0000000..dac0a71
--- /dev/null
+++ b/Opcode/Ice/IceTypes.h
@@ -0,0 +1,157 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains custom types.
+ * \file IceTypes.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICETYPES_H__
+#define __ICETYPES_H__
+
+ #define USE_HANDLE_MANAGER
+
+ // Constants
+ #define PI 3.1415926535897932384626433832795028841971693993751f //!< PI
+ #define HALFPI 1.57079632679489661923f //!< 0.5 * PI
+ #define TWOPI 6.28318530717958647692f //!< 2.0 * PI
+ #define INVPI 0.31830988618379067154f //!< 1.0 / PI
+
+ #define RADTODEG 57.2957795130823208768f //!< 180.0 / PI, convert radians to degrees
+ #define DEGTORAD 0.01745329251994329577f //!< PI / 180.0, convert degrees to radians
+
+ #define EXP 2.71828182845904523536f //!< e
+ #define INVLOG2 3.32192809488736234787f //!< 1.0 / log10(2)
+ #define LN2 0.693147180559945f //!< ln(2)
+ #define INVLN2 1.44269504089f //!< 1.0f / ln(2)
+
+ #define INV3 0.33333333333333333333f //!< 1/3
+ #define INV6 0.16666666666666666666f //!< 1/6
+ #define INV7 0.14285714285714285714f //!< 1/7
+ #define INV9 0.11111111111111111111f //!< 1/9
+ #define INV255 0.00392156862745098039f //!< 1/255
+
+ #define SQRT2 1.41421356237f //!< sqrt(2)
+ #define INVSQRT2 0.707106781188f //!< 1 / sqrt(2)
+
+ #define SQRT3 1.73205080757f //!< sqrt(3)
+ #define INVSQRT3 0.577350269189f //!< 1 / sqrt(3)
+
+ #define null 0 //!< our own NULL pointer
+
+ // Custom types used in ICE
+ typedef signed char sbyte; //!< sizeof(sbyte) must be 1
+ typedef unsigned char ubyte; //!< sizeof(ubyte) must be 1
+ typedef signed short sword; //!< sizeof(sword) must be 2
+ typedef unsigned short uword; //!< sizeof(uword) must be 2
+ typedef signed int sdword; //!< sizeof(sdword) must be 4
+ typedef unsigned int udword; //!< sizeof(udword) must be 4
+ typedef signed __int64 sqword; //!< sizeof(sqword) must be 8
+ typedef unsigned __int64 uqword; //!< sizeof(uqword) must be 8
+ typedef float float32; //!< sizeof(float32) must be 4
+ typedef double float64; //!< sizeof(float64) must be 4
+
+ ICE_COMPILE_TIME_ASSERT(sizeof(bool)==1); // ...otherwise things might fail with VC++ 4.2 !
+ ICE_COMPILE_TIME_ASSERT(sizeof(ubyte)==1);
+ ICE_COMPILE_TIME_ASSERT(sizeof(sbyte)==1);
+ ICE_COMPILE_TIME_ASSERT(sizeof(sword)==2);
+ ICE_COMPILE_TIME_ASSERT(sizeof(uword)==2);
+ ICE_COMPILE_TIME_ASSERT(sizeof(udword)==4);
+ ICE_COMPILE_TIME_ASSERT(sizeof(sdword)==4);
+ ICE_COMPILE_TIME_ASSERT(sizeof(uqword)==8);
+ ICE_COMPILE_TIME_ASSERT(sizeof(sqword)==8);
+
+ //! TO BE DOCUMENTED
+ #define DECLARE_ICE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name
+
+ typedef udword DynID; //!< Dynamic identifier
+#ifdef USE_HANDLE_MANAGER
+ typedef udword KID; //!< Kernel ID
+// DECLARE_ICE_HANDLE(KID);
+#else
+ typedef uword KID; //!< Kernel ID
+#endif
+ typedef udword RTYPE; //!< Relationship-type (!) between owners and references
+ #define INVALID_ID 0xffffffff //!< Invalid dword ID (counterpart of null pointers)
+#ifdef USE_HANDLE_MANAGER
+ #define INVALID_KID 0xffffffff //!< Invalid Kernel ID
+#else
+ #define INVALID_KID 0xffff //!< Invalid Kernel ID
+#endif
+ #define INVALID_NUMBER 0xDEADBEEF //!< Standard junk value
+
+ // Define BOOL if needed
+ #ifndef BOOL
+ typedef int BOOL; //!< Another boolean type.
+ #endif
+
+ //! Union of a float and a sdword
+ typedef union {
+ float f; //!< The float
+ sdword d; //!< The integer
+ }scell;
+
+ //! Union of a float and a udword
+ typedef union {
+ float f; //!< The float
+ udword d; //!< The integer
+ }ucell;
+
+ // Type ranges
+ #define MAX_SBYTE 0x7f //!< max possible sbyte value
+ #define MIN_SBYTE 0x80 //!< min possible sbyte value
+ #define MAX_UBYTE 0xff //!< max possible ubyte value
+ #define MIN_UBYTE 0x00 //!< min possible ubyte value
+ #define MAX_SWORD 0x7fff //!< max possible sword value
+ #define MIN_SWORD 0x8000 //!< min possible sword value
+ #define MAX_UWORD 0xffff //!< max possible uword value
+ #define MIN_UWORD 0x0000 //!< min possible uword value
+ #define MAX_SDWORD 0x7fffffff //!< max possible sdword value
+ #define MIN_SDWORD 0x80000000 //!< min possible sdword value
+ #define MAX_UDWORD 0xffffffff //!< max possible udword value
+ #define MIN_UDWORD 0x00000000 //!< min possible udword value
+ #define MAX_FLOAT FLT_MAX //!< max possible float value
+ #define MIN_FLOAT (-FLT_MAX) //!< min possible loat value
+ #define IEEE_1_0 0x3f800000 //!< integer representation of 1.0
+ #define IEEE_255_0 0x437f0000 //!< integer representation of 255.0
+ #define IEEE_MAX_FLOAT 0x7f7fffff //!< integer representation of MAX_FLOAT
+ #define IEEE_MIN_FLOAT 0xff7fffff //!< integer representation of MIN_FLOAT
+ #define IEEE_UNDERFLOW_LIMIT 0x1a000000
+
+ #define ONE_OVER_RAND_MAX (1.0f / float(RAND_MAX)) //!< Inverse of the max possible value returned by rand()
+
+ typedef int (__stdcall* PROC)(); //!< A standard procedure call.
+ typedef bool (*ENUMERATION)(udword value, udword param, udword context); //!< ICE standard enumeration call
+ typedef void** VTABLE; //!< A V-Table.
+
+ #undef MIN
+ #undef MAX
+ #define MIN(a, b) ((a) < (b) ? (a) : (b)) //!< Returns the min value between a and b
+ #define MAX(a, b) ((a) > (b) ? (a) : (b)) //!< Returns the max value between a and b
+ #define MAXMAX(a,b,c) ((a) > (b) ? MAX (a,c) : MAX (b,c)) //!< Returns the max value between a, b and c
+
+ template<class T> inline_ const T& TMin (const T& a, const T& b) { return b < a ? b : a; }
+ template<class T> inline_ const T& TMax (const T& a, const T& b) { return a < b ? b : a; }
+ template<class T> inline_ void TSetMin (T& a, const T& b) { if(a>b) a = b; }
+ template<class T> inline_ void TSetMax (T& a, const T& b) { if(a<b) a = b; }
+
+ #define SQR(x) ((x)*(x)) //!< Returns x square
+ #define CUBE(x) ((x)*(x)*(x)) //!< Returns x cube
+
+ #define AND & //!< ...
+ #define OR | //!< ...
+ #define XOR ^ //!< ...
+
+ #define QUADRAT(x) ((x)*(x)) //!< Returns x square
+
+#ifdef _WIN32
+# define srand48(x) srand((unsigned int) (x))
+# define srandom(x) srand((unsigned int) (x))
+# define random() ((double) rand())
+# define drand48() ((double) (((double) rand()) / ((double) RAND_MAX)))
+#endif
+
+#endif // __ICETYPES_H__
diff --git a/Opcode/Ice/IceUtils.cpp b/Opcode/Ice/IceUtils.cpp
new file mode 100644
index 0000000..d877203
--- /dev/null
+++ b/Opcode/Ice/IceUtils.cpp
@@ -0,0 +1,39 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains misc. useful macros & defines.
+ * \file IceUtils.cpp
+ * \author Pierre Terdiman (collected from various sources)
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceCore;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the alignment of the input address.
+ * \fn Alignment()
+ * \param address [in] address to check
+ * \return the best alignment (e.g. 1 for odd addresses, etc)
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword IceCore::Alignment(udword address)
+{
+ // Returns 0 for null addresses
+ if(!address) return 0;
+
+ // Test all bits
+ udword Align = 1;
+ for(udword i=1;i<32;i++)
+ {
+ // Returns as soon as the alignment is broken
+ if(address&Align) return Align;
+ Align<<=1;
+ }
+ // Here all bits are null, except the highest one (else the address would be null)
+ return Align;
+}
diff --git a/Opcode/Ice/IceUtils.h b/Opcode/Ice/IceUtils.h
new file mode 100644
index 0000000..0e6161e
--- /dev/null
+++ b/Opcode/Ice/IceUtils.h
@@ -0,0 +1,256 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains misc. useful macros & defines.
+ * \file IceUtils.h
+ * \author Pierre Terdiman (collected from various sources)
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEUTILS_H__
+#define __ICEUTILS_H__
+
+ #define START_RUNONCE { static bool __RunOnce__ = false; if(!__RunOnce__){
+ #define END_RUNONCE __RunOnce__ = true;}}
+
+ //! Reverse all the bits in a 32 bit word (from Steve Baker's Cute Code Collection)
+ //! (each line can be done in any order.
+ inline_ void ReverseBits(udword& n)
+ {
+ n = ((n >> 1) & 0x55555555) | ((n << 1) & 0xaaaaaaaa);
+ n = ((n >> 2) & 0x33333333) | ((n << 2) & 0xcccccccc);
+ n = ((n >> 4) & 0x0f0f0f0f) | ((n << 4) & 0xf0f0f0f0);
+ n = ((n >> 8) & 0x00ff00ff) | ((n << 8) & 0xff00ff00);
+ n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000);
+ // Etc for larger intergers (64 bits in Java)
+ // NOTE: the >> operation must be unsigned! (>>> in java)
+ }
+
+ //! Count the number of '1' bits in a 32 bit word (from Steve Baker's Cute Code Collection)
+ inline_ udword CountBits(udword n)
+ {
+ // This relies of the fact that the count of n bits can NOT overflow
+ // an n bit interger. EG: 1 bit count takes a 1 bit interger, 2 bit counts
+ // 2 bit interger, 3 bit count requires only a 2 bit interger.
+ // So we add all bit pairs, then each nible, then each byte etc...
+ n = (n & 0x55555555) + ((n & 0xaaaaaaaa) >> 1);
+ n = (n & 0x33333333) + ((n & 0xcccccccc) >> 2);
+ n = (n & 0x0f0f0f0f) + ((n & 0xf0f0f0f0) >> 4);
+ n = (n & 0x00ff00ff) + ((n & 0xff00ff00) >> 8);
+ n = (n & 0x0000ffff) + ((n & 0xffff0000) >> 16);
+ // Etc for larger intergers (64 bits in Java)
+ // NOTE: the >> operation must be unsigned! (>>> in java)
+ return n;
+ }
+
+ //! Even faster?
+ inline_ udword CountBits2(udword bits)
+ {
+ bits = bits - ((bits >> 1) & 0x55555555);
+ bits = ((bits >> 2) & 0x33333333) + (bits & 0x33333333);
+ bits = ((bits >> 4) + bits) & 0x0F0F0F0F;
+ return (bits * 0x01010101) >> 24;
+ }
+
+ //! Spread out bits. EG 00001111 -> 0101010101
+ //! 00001010 -> 0100010000
+ //! This is used to interleve to intergers to produce a `Morten Key'
+ //! used in Space Filling Curves (See DrDobbs Journal, July 1999)
+ //! Order is important.
+ inline_ void SpreadBits(udword& n)
+ {
+ n = ( n & 0x0000ffff) | (( n & 0xffff0000) << 16);
+ n = ( n & 0x000000ff) | (( n & 0x0000ff00) << 8);
+ n = ( n & 0x000f000f) | (( n & 0x00f000f0) << 4);
+ n = ( n & 0x03030303) | (( n & 0x0c0c0c0c) << 2);
+ n = ( n & 0x11111111) | (( n & 0x22222222) << 1);
+ }
+
+ // Next Largest Power of 2
+ // Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm
+ // that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with
+ // the same most significant 1 as x, but all 1's below it. Adding 1 to that value yields the next
+ // largest power of 2. For a 32-bit value:
+ inline_ udword nlpo2(udword x)
+ {
+ x |= (x >> 1);
+ x |= (x >> 2);
+ x |= (x >> 4);
+ x |= (x >> 8);
+ x |= (x >> 16);
+ return x+1;
+ }
+
+ //! Test to see if a number is an exact power of two (from Steve Baker's Cute Code Collection)
+ inline_ bool IsPowerOfTwo(udword n) { return ((n&(n-1))==0); }
+
+ //! Zero the least significant '1' bit in a word. (from Steve Baker's Cute Code Collection)
+ inline_ void ZeroLeastSetBit(udword& n) { n&=(n-1); }
+
+ //! Set the least significant N bits in a word. (from Steve Baker's Cute Code Collection)
+ inline_ void SetLeastNBits(udword& x, udword n) { x|=~(~0<<n); }
+
+ //! Classic XOR swap (from Steve Baker's Cute Code Collection)
+ //! x ^= y; /* x' = (x^y) */
+ //! y ^= x; /* y' = (y^(x^y)) = x */
+ //! x ^= y; /* x' = (x^y)^x = y */
+ inline_ void Swap(udword& x, udword& y) { x ^= y; y ^= x; x ^= y; }
+
+ //! Little/Big endian (from Steve Baker's Cute Code Collection)
+ //!
+ //! Extra comments by Kenny Hoff:
+ //! Determines the byte-ordering of the current machine (little or big endian)
+ //! by setting an integer value to 1 (so least significant bit is now 1); take
+ //! the address of the int and cast to a byte pointer (treat integer as an
+ //! array of four bytes); check the value of the first byte (must be 0 or 1).
+ //! If the value is 1, then the first byte least significant byte and this
+ //! implies LITTLE endian. If the value is 0, the first byte is the most
+ //! significant byte, BIG endian. Examples:
+ //! integer 1 on BIG endian: 00000000 00000000 00000000 00000001
+ //! integer 1 on LITTLE endian: 00000001 00000000 00000000 00000000
+ //!---------------------------------------------------------------------------
+ //! int IsLittleEndian() { int x=1; return ( ((char*)(&x))[0] ); }
+ inline_ char LittleEndian() { int i = 1; return *((char*)&i); }
+
+ //!< Alternative abs function
+ inline_ udword abs_(sdword x) { sdword y= x >> 31; return (x^y)-y; }
+
+ //!< Alternative min function
+ inline_ sdword min_(sdword a, sdword b) { sdword delta = b-a; return a + (delta&(delta>>31)); }
+
+ // Determine if one of the bytes in a 4 byte word is zero
+ inline_ BOOL HasNullByte(udword x) { return ((x + 0xfefefeff) & (~x) & 0x80808080); }
+
+ // To find the smallest 1 bit in a word EG: ~~~~~~10---0 => 0----010---0
+ inline_ udword LowestOneBit(udword w) { return ((w) & (~(w)+1)); }
+// inline_ udword LowestOneBit_(udword w) { return ((w) & (-(w))); }
+
+ // Most Significant 1 Bit
+ // Given a binary integer value x, the most significant 1 bit (highest numbered element of a bit set)
+ // can be computed using a SWAR algorithm that recursively "folds" the upper bits into the lower bits.
+ // This process yields a bit vector with the same most significant 1 as x, but all 1's below it.
+ // Bitwise AND of the original value with the complement of the "folded" value shifted down by one
+ // yields the most significant bit. For a 32-bit value:
+ inline_ udword msb32(udword x)
+ {
+ x |= (x >> 1);
+ x |= (x >> 2);
+ x |= (x >> 4);
+ x |= (x >> 8);
+ x |= (x >> 16);
+ return (x & ~(x >> 1));
+ }
+
+ /*
+ "Just call it repeatedly with various input values and always with the same variable as "memory".
+ The sharpness determines the degree of filtering, where 0 completely filters out the input, and 1
+ does no filtering at all.
+
+ I seem to recall from college that this is called an IIR (Infinite Impulse Response) filter. As opposed
+ to the more typical FIR (Finite Impulse Response).
+
+ Also, I'd say that you can make more intelligent and interesting filters than this, for example filters
+ that remove wrong responses from the mouse because it's being moved too fast. You'd want such a filter
+ to be applied before this one, of course."
+
+ (JCAB on Flipcode)
+ */
+ inline_ float FeedbackFilter(float val, float& memory, float sharpness)
+ {
+ ASSERT(sharpness>=0.0f && sharpness<=1.0f && "Invalid sharpness value in feedback filter");
+ if(sharpness<0.0f) sharpness = 0.0f;
+ else if(sharpness>1.0f) sharpness = 1.0f;
+ return memory = val * sharpness + memory * (1.0f - sharpness);
+ }
+
+ //! If you can guarantee that your input domain (i.e. value of x) is slightly
+ //! limited (abs(x) must be < ((1<<31u)-32767)), then you can use the
+ //! following code to clamp the resulting value into [-32768,+32767] range:
+ inline_ int ClampToInt16(int x)
+ {
+// ASSERT(abs(x) < (int)((1<<31u)-32767));
+
+ int delta = 32767 - x;
+ x += (delta>>31) & delta;
+ delta = x + 32768;
+ x -= (delta>>31) & delta;
+ return x;
+ }
+
+ // Generic functions
+ template<class Type> inline_ void TSwap(Type& a, Type& b) { const Type c = a; a = b; b = c; }
+ template<class Type> inline_ Type TClamp(const Type& x, const Type& lo, const Type& hi) { return ((x<lo) ? lo : (x>hi) ? hi : x); }
+
+ template<class Type> inline_ void TSort(Type& a, Type& b)
+ {
+ if(a>b) TSwap(a, b);
+ }
+
+ template<class Type> inline_ void TSort(Type& a, Type& b, Type& c)
+ {
+ if(a>b) TSwap(a, b);
+ if(b>c) TSwap(b, c);
+ if(a>b) TSwap(a, b);
+ if(b>c) TSwap(b, c);
+ }
+
+ // Prevent nasty user-manipulations (strategy borrowed from Charles Bloom)
+// #define PREVENT_COPY(curclass) void operator = (const curclass& object) { ASSERT(!"Bad use of operator ="); }
+ // ... actually this is better !
+ #define PREVENT_COPY(cur_class) private: cur_class(const cur_class& object); cur_class& operator=(const cur_class& object);
+
+ //! TO BE DOCUMENTED
+ #define OFFSET_OF(Class, Member) (size_t)&(((Class*)0)->Member)
+ //! TO BE DOCUMENTED
+ #define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0]))
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Returns the alignment of the input address.
+ * \fn Alignment()
+ * \param address [in] address to check
+ * \return the best alignment (e.g. 1 for odd addresses, etc)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ FUNCTION ICECORE_API udword Alignment(udword address);
+
+ #define IS_ALIGNED_2(x) ((x&1)==0)
+ #define IS_ALIGNED_4(x) ((x&3)==0)
+ #define IS_ALIGNED_8(x) ((x&7)==0)
+
+ inline_ void _prefetch(void const* ptr) { (void)*(char const volatile *)ptr; }
+
+ // Compute implicit coords from an index:
+ // The idea is to get back 2D coords from a 1D index.
+ // For example:
+ //
+ // 0 1 2 ... nbu-1
+ // nbu nbu+1 i ...
+ //
+ // We have i, we're looking for the equivalent (u=2, v=1) location.
+ // i = u + v*nbu
+ // <=> i/nbu = u/nbu + v
+ // Since 0 <= u < nbu, u/nbu = 0 (integer)
+ // Hence: v = i/nbu
+ // Then we simply put it back in the original equation to compute u = i - v*nbu
+ inline_ void Compute2DCoords(udword& u, udword& v, udword i, udword nbu)
+ {
+ v = i / nbu;
+ u = i - (v * nbu);
+ }
+
+ // In 3D: i = u + v*nbu + w*nbu*nbv
+ // <=> i/(nbu*nbv) = u/(nbu*nbv) + v/nbv + w
+ // u/(nbu*nbv) is null since u/nbu was null already.
+ // v/nbv is null as well for the same reason.
+ // Hence w = i/(nbu*nbv)
+ // Then we're left with a 2D problem: i' = i - w*nbu*nbv = u + v*nbu
+ inline_ void Compute3DCoords(udword& u, udword& v, udword& w, udword i, udword nbu, udword nbu_nbv)
+ {
+ w = i / (nbu_nbv);
+ Compute2DCoords(u, v, i - (w * nbu_nbv), nbu);
+ }
+
+#endif // __ICEUTILS_H__
diff --git a/Opcode/OPC_AABBCollider.cpp b/Opcode/OPC_AABBCollider.cpp
new file mode 100644
index 0000000..7d9346e
--- /dev/null
+++ b/Opcode/OPC_AABBCollider.cpp
@@ -0,0 +1,696 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for an AABB collider.
+ * \file OPC_AABBCollider.cpp
+ * \author Pierre Terdiman
+ * \date January, 1st, 2002
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains an AABB-vs-tree collider.
+ *
+ * \class AABBCollider
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date January, 1st, 2002
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+#include "OPC_BoxBoxOverlap.h"
+#include "OPC_TriBoxOverlap.h"
+
+#define SET_CONTACT(prim_index, flag) \
+ /* Set contact status */ \
+ mFlags |= flag; \
+ mTouchedPrimitives->Add(prim_index);
+
+//! AABB-triangle test
+#define AABB_PRIM(prim_index, flag) \
+ /* Request vertices from the app */ \
+ VertexPointers VP; mIMesh->GetTriangle(VP, prim_index);\
+ mLeafVerts[0] = *VP.Vertex[0]; \
+ mLeafVerts[1] = *VP.Vertex[1]; \
+ mLeafVerts[2] = *VP.Vertex[2]; \
+ /* Perform triangle-box overlap test */ \
+ if(TriBoxOverlap()) \
+ { \
+ SET_CONTACT(prim_index, flag) \
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBCollider::AABBCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBCollider::~AABBCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Generic collision query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - with GetNbTouchedPrimitives()
+ * - with GetTouchedPrimitives()
+ *
+ * \param cache [in/out] a box cache
+ * \param box [in] collision AABB in world space
+ * \param model [in] Opcode model to collide with
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const Model& model)
+{
+ // Checkings
+ if(!Setup(&model)) return false;
+
+ // Init collision query
+ if(InitQuery(cache, box)) return true;
+
+ if(!model.HasLeafNodes())
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ }
+ else
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Initializes a collision query :
+ * - reset stats & contact status
+ * - check temporal coherence
+ *
+ * \param cache [in/out] a box cache
+ * \param box [in] AABB in world space
+ * \return TRUE if we can return immediately
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+BOOL AABBCollider::InitQuery(AABBCache& cache, const CollisionAABB& box)
+{
+ // 1) Call the base method
+ VolumeCollider::InitQuery();
+
+ // 2) Keep track of the query box
+ mBox = box;
+
+ // 3) Setup destination pointer
+ mTouchedPrimitives = &cache.TouchedPrimitives;
+
+ // 4) Special case: 1-triangle meshes [Opcode 1.3]
+ if(mCurrentModel && mCurrentModel->HasSingleNode())
+ {
+ if(!SkipPrimitiveTests())
+ {
+ // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
+ mTouchedPrimitives->Reset();
+
+ // Perform overlap test between the unique triangle and the box (and set contact status if needed)
+ AABB_PRIM(udword(0), OPC_CONTACT)
+
+ // Return immediately regardless of status
+ return TRUE;
+ }
+ }
+
+ // 5) Check temporal coherence :
+ if(TemporalCoherenceEnabled())
+ {
+ // Here we use temporal coherence
+ // => check results from previous frame before performing the collision query
+ if(FirstContactEnabled())
+ {
+ // We're only interested in the first contact found => test the unique previously touched face
+ if(mTouchedPrimitives->GetNbEntries())
+ {
+ // Get index of previously touched face = the first entry in the array
+ udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0);
+
+ // Then reset the array:
+ // - if the overlap test below is successful, the index we'll get added back anyway
+ // - if it isn't, then the array should be reset anyway for the normal query
+ mTouchedPrimitives->Reset();
+
+ // Perform overlap test between the cached triangle and the box (and set contact status if needed)
+ AABB_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT)
+
+ // Return immediately if possible
+ if(GetContactStatus()) return TRUE;
+ }
+ // else no face has been touched during previous query
+ // => we'll have to perform a normal query
+ }
+ else
+ {
+ // We're interested in all contacts =>test the new real box N(ew) against the previous fat box P(revious):
+ if(IsCacheValid(cache) && mBox.IsInside(cache.FatBox))
+ {
+ // - if N is included in P, return previous list
+ // => we simply leave the list (mTouchedFaces) unchanged
+
+ // Set contact status if needed
+ if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT;
+
+ // In any case we don't need to do a query
+ return TRUE;
+ }
+ else
+ {
+ // - else do the query using a fat N
+
+ // Reset cache since we'll about to perform a real query
+ mTouchedPrimitives->Reset();
+
+ // Make a fat box so that coherence will work for subsequent frames
+ mBox.mExtents *= cache.FatCoeff;
+
+ // Update cache with query data (signature for cached faces)
+ cache.FatBox = mBox;
+ }
+ }
+ }
+ else
+ {
+ // Here we don't use temporal coherence => do a normal query
+ mTouchedPrimitives->Reset();
+ }
+
+ // 5) Precompute min & max bounds if needed
+ mMin = box.mCenter - box.mExtents;
+ mMax = box.mCenter + box.mExtents;
+
+ return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Collision query for vanilla AABB trees.
+ * \param cache [in/out] a box cache
+ * \param box [in] collision AABB in world space
+ * \param tree [in] AABB tree
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const AABBTree* tree)
+{
+ // This is typically called for a scene tree, full of -AABBs-, not full of triangles.
+ // So we don't really have "primitives" to deal with. Hence it doesn't work with
+ // "FirstContact" + "TemporalCoherence".
+ ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) );
+
+ // Checkings
+ if(!tree) return false;
+
+ // Init collision query
+ if(InitQuery(cache, box)) return true;
+
+ // Perform collision query
+ _Collide(tree);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks the AABB completely contains the box. In which case we can end the query sooner.
+ * \param bc [in] box center
+ * \param be [in] box extents
+ * \return true if the AABB contains the whole box
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL AABBCollider::AABBContainsBox(const Point& bc, const Point& be)
+{
+ if(mMin.x > bc.x - be.x) return FALSE;
+ if(mMin.y > bc.y - be.y) return FALSE;
+ if(mMin.z > bc.z - be.z) return FALSE;
+
+ if(mMax.x < bc.x + be.x) return FALSE;
+ if(mMax.y < bc.y + be.y) return FALSE;
+ if(mMax.z < bc.z + be.z) return FALSE;
+
+ return TRUE;
+}
+
+#define TEST_BOX_IN_AABB(center, extents) \
+ if(AABBContainsBox(center, extents)) \
+ { \
+ /* Set contact status */ \
+ mFlags |= OPC_CONTACT; \
+ _Dump(node); \
+ return; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_Collide(const AABBCollisionNode* node)
+{
+ // Perform AABB-AABB overlap test
+ if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
+
+ TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->IsLeaf())
+ {
+ AABB_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _Collide(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node)
+{
+ // Perform AABB-AABB overlap test
+ if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
+
+ TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->IsLeaf())
+ {
+ SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _CollideNoPrimitiveTest(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_Collide(const AABBQuantizedNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform AABB-AABB overlap test
+ if(!AABBAABBOverlap(Extents, Center)) return;
+
+ TEST_BOX_IN_AABB(Center, Extents)
+
+ if(node->IsLeaf())
+ {
+ AABB_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _Collide(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform AABB-AABB overlap test
+ if(!AABBAABBOverlap(Extents, Center)) return;
+
+ TEST_BOX_IN_AABB(Center, Extents)
+
+ if(node->IsLeaf())
+ {
+ SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _CollideNoPrimitiveTest(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_Collide(const AABBNoLeafNode* node)
+{
+ // Perform AABB-AABB overlap test
+ if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
+
+ TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->HasPosLeaf()) { AABB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { AABB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for no-leaf AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node)
+{
+ // Perform AABB-AABB overlap test
+ if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
+
+ TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_Collide(const AABBQuantizedNoLeafNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform AABB-AABB overlap test
+ if(!AABBAABBOverlap(Extents, Center)) return;
+
+ TEST_BOX_IN_AABB(Center, Extents)
+
+ if(node->HasPosLeaf()) { AABB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { AABB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized no-leaf AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform AABB-AABB overlap test
+ if(!AABBAABBOverlap(Extents, Center)) return;
+
+ TEST_BOX_IN_AABB(Center, Extents)
+
+ if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for vanilla AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_Collide(const AABBTreeNode* node)
+{
+ // Perform AABB-AABB overlap test
+ Point Center, Extents;
+ node->GetAABB()->GetCenter(Center);
+ node->GetAABB()->GetExtents(Extents);
+ if(!AABBAABBOverlap(Center, Extents)) return;
+
+ if(node->IsLeaf() || AABBContainsBox(Center, Extents))
+ {
+ mFlags |= OPC_CONTACT;
+ mTouchedPrimitives->Add(node->GetPrimitives(), node->GetNbPrimitives());
+ }
+ else
+ {
+ _Collide(node->GetPos());
+ _Collide(node->GetNeg());
+ }
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridAABBCollider::HybridAABBCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridAABBCollider::~HybridAABBCollider()
+{
+}
+
+bool HybridAABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const HybridModel& model)
+{
+ // We don't want primitive tests here!
+ mFlags |= OPC_NO_PRIMITIVE_TESTS;
+
+ // Checkings
+ if(!Setup(&model)) return false;
+
+ // Init collision query
+ if(InitQuery(cache, box)) return true;
+
+ // Special case for 1-leaf trees
+ if(mCurrentModel && mCurrentModel->HasSingleNode())
+ {
+ // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles
+ udword Nb = mIMesh->GetNbTriangles();
+
+ // Loop through all triangles
+ for(udword i=0;i<Nb;i++)
+ {
+ AABB_PRIM(i, OPC_CONTACT)
+ }
+ return true;
+ }
+
+ // Override destination array since we're only going to get leaf boxes here
+ mTouchedBoxes.Reset();
+ mTouchedPrimitives = &mTouchedBoxes;
+
+ // Now, do the actual query against leaf boxes
+ if(!model.HasLeafNodes())
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ }
+ else
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ }
+
+ // We only have a list of boxes so far
+ if(GetContactStatus())
+ {
+ // Reset contact status, since it currently only reflects collisions with leaf boxes
+ Collider::InitQuery();
+
+ // Change dest container so that we can use built-in overlap tests and get collided primitives
+ cache.TouchedPrimitives.Reset();
+ mTouchedPrimitives = &cache.TouchedPrimitives;
+
+ // Read touched leaf boxes
+ udword Nb = mTouchedBoxes.GetNbEntries();
+ const udword* Touched = mTouchedBoxes.GetEntries();
+
+ const LeafTriangles* LT = model.GetLeafTriangles();
+ const udword* Indices = model.GetIndices();
+
+ // Loop through touched leaves
+ while(Nb--)
+ {
+ const LeafTriangles& CurrentLeaf = LT[*Touched++];
+
+ // Each leaf box has a set of triangles
+ udword NbTris = CurrentLeaf.GetNbTriangles();
+ if(Indices)
+ {
+ const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ udword TriangleIndex = *T++;
+ AABB_PRIM(TriangleIndex, OPC_CONTACT)
+ }
+ }
+ else
+ {
+ udword BaseIndex = CurrentLeaf.GetTriangleIndex();
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ udword TriangleIndex = BaseIndex++;
+ AABB_PRIM(TriangleIndex, OPC_CONTACT)
+ }
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/Opcode/OPC_AABBCollider.h b/Opcode/OPC_AABBCollider.h
new file mode 100644
index 0000000..8e2b3e4
--- /dev/null
+++ b/Opcode/OPC_AABBCollider.h
@@ -0,0 +1,97 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for an AABB collider.
+ * \file OPC_AABBCollider.h
+ * \author Pierre Terdiman
+ * \date January, 1st, 2002
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_AABBCOLLIDER_H__
+#define __OPC_AABBCOLLIDER_H__
+
+ struct OPCODE_API AABBCache : VolumeCache
+ {
+ AABBCache() : FatCoeff(1.1f)
+ {
+ FatBox.mCenter.Zero();
+ FatBox.mExtents.Zero();
+ }
+
+ // Cached faces signature
+ CollisionAABB FatBox; //!< Box used when performing the query resulting in cached faces
+ // User settings
+ float FatCoeff; //!< mRadius2 multiplier used to create a fat sphere
+ };
+
+ class OPCODE_API AABBCollider : public VolumeCollider
+ {
+ public:
+ // Constructor / Destructor
+ AABBCollider();
+ virtual ~AABBCollider();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Generic collision query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - with GetNbTouchedPrimitives()
+ * - with GetTouchedPrimitives()
+ *
+ * \param cache [in/out] a box cache
+ * \param box [in] collision AABB in world space
+ * \param model [in] Opcode model to collide with
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool Collide(AABBCache& cache, const CollisionAABB& box, const Model& model);
+ //
+ bool Collide(AABBCache& cache, const CollisionAABB& box, const AABBTree* tree);
+ protected:
+ CollisionAABB mBox; //!< Query box in (center, extents) form
+ Point mMin; //!< Query box min point
+ Point mMax; //!< Query box max point
+ // Leaf description
+ Point mLeafVerts[3]; //!< Triangle vertices
+ // Internal methods
+ void _Collide(const AABBCollisionNode* node);
+ void _Collide(const AABBNoLeafNode* node);
+ void _Collide(const AABBQuantizedNode* node);
+ void _Collide(const AABBQuantizedNoLeafNode* node);
+ void _Collide(const AABBTreeNode* node);
+ void _CollideNoPrimitiveTest(const AABBCollisionNode* node);
+ void _CollideNoPrimitiveTest(const AABBNoLeafNode* node);
+ void _CollideNoPrimitiveTest(const AABBQuantizedNode* node);
+ void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node);
+ // Overlap tests
+ inline_ BOOL AABBContainsBox(const Point& bc, const Point& be);
+ inline_ BOOL AABBAABBOverlap(const Point& b, const Point& Pb);
+ inline_ BOOL TriBoxOverlap();
+ // Init methods
+ BOOL InitQuery(AABBCache& cache, const CollisionAABB& box);
+ };
+
+ class OPCODE_API HybridAABBCollider : public AABBCollider
+ {
+ public:
+ // Constructor / Destructor
+ HybridAABBCollider();
+ virtual ~HybridAABBCollider();
+
+ bool Collide(AABBCache& cache, const CollisionAABB& box, const HybridModel& model);
+ protected:
+ Container mTouchedBoxes;
+ };
+
+#endif // __OPC_AABBCOLLIDER_H__
diff --git a/Opcode/OPC_AABBTree.cpp b/Opcode/OPC_AABBTree.cpp
new file mode 100644
index 0000000..5738f9b
--- /dev/null
+++ b/Opcode/OPC_AABBTree.cpp
@@ -0,0 +1,573 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a versatile AABB tree.
+ * \file OPC_AABBTree.cpp
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a generic AABB tree node.
+ *
+ * \class AABBTreeNode
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a generic AABB tree.
+ * This is a vanilla AABB tree, without any particular optimization. It contains anonymous references to
+ * user-provided primitives, which can theoretically be anything - triangles, boxes, etc. Each primitive
+ * is surrounded by an AABB, regardless of the primitive's nature. When the primitive is a triangle, the
+ * resulting tree can be converted into an optimized tree. If the primitive is a box, the resulting tree
+ * can be used for culling - VFC or occlusion -, assuming you cull on a mesh-by-mesh basis (modern way).
+ *
+ * \class AABBTree
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBTreeNode::AABBTreeNode() :
+ mPos (null),
+#ifndef OPC_NO_NEG_VANILLA_TREE
+ mNeg (null),
+#endif
+ mNbPrimitives (0),
+ mNodePrimitives (null)
+{
+#ifdef OPC_USE_TREE_COHERENCE
+ mBitmask = 0;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBTreeNode::~AABBTreeNode()
+{
+ // Opcode 1.3:
+ const AABBTreeNode* Pos = GetPos();
+ const AABBTreeNode* Neg = GetNeg();
+#ifndef OPC_NO_NEG_VANILLA_TREE
+ if(!(mPos&1)) DELETESINGLE(Pos);
+ if(!(mNeg&1)) DELETESINGLE(Neg);
+#else
+ if(!(mPos&1)) DELETEARRAY(Pos);
+#endif
+ mNodePrimitives = null; // This was just a shortcut to the global list => no release
+ mNbPrimitives = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Splits the node along a given axis.
+ * The list of indices is reorganized according to the split values.
+ * \param axis [in] splitting axis index
+ * \param builder [in] the tree builder
+ * \return the number of primitives assigned to the first child
+ * \warning this method reorganizes the internal list of primitives
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword AABBTreeNode::Split(udword axis, AABBTreeBuilder* builder)
+{
+ // Get node split value
+ float SplitValue = builder->GetSplittingValue(mNodePrimitives, mNbPrimitives, mBV, axis);
+
+ udword NbPos = 0;
+ // Loop through all node-related primitives. Their indices range from mNodePrimitives[0] to mNodePrimitives[mNbPrimitives-1].
+ // Those indices map the global list in the tree builder.
+ for(udword i=0;i<mNbPrimitives;i++)
+ {
+ // Get index in global list
+ udword Index = mNodePrimitives[i];
+
+ // Test against the splitting value. The primitive value is tested against the enclosing-box center.
+ // [We only need an approximate partition of the enclosing box here.]
+ float PrimitiveValue = builder->GetSplittingValue(Index, axis);
+
+ // Reorganize the list of indices in this order: positive - negative.
+ if(PrimitiveValue > SplitValue)
+ {
+ // Swap entries
+ udword Tmp = mNodePrimitives[i];
+ mNodePrimitives[i] = mNodePrimitives[NbPos];
+ mNodePrimitives[NbPos] = Tmp;
+ // Count primitives assigned to positive space
+ NbPos++;
+ }
+ }
+ return NbPos;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Subdivides the node.
+ *
+ * N
+ * / \
+ * / \
+ * N/2 N/2
+ * / \ / \
+ * N/4 N/4 N/4 N/4
+ * (etc)
+ *
+ * A well-balanced tree should have a O(log n) depth.
+ * A degenerate tree would have a O(n) depth.
+ * Note a perfectly-balanced tree is not well-suited to collision detection anyway.
+ *
+ * \param builder [in] the tree builder
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeNode::Subdivide(AABBTreeBuilder* builder)
+{
+ // Checkings
+ if(!builder) return false;
+
+ // Stop subdividing if we reach a leaf node. This is always performed here,
+ // else we could end in trouble if user overrides this.
+ if(mNbPrimitives==1) return true;
+
+ // Let the user validate the subdivision
+ if(!builder->ValidateSubdivision(mNodePrimitives, mNbPrimitives, mBV)) return true;
+
+ bool ValidSplit = true; // Optimism...
+ udword NbPos;
+ if(builder->mSettings.mRules & SPLIT_LARGEST_AXIS)
+ {
+ // Find the largest axis to split along
+ Point Extents; mBV.GetExtents(Extents); // Box extents
+ udword Axis = Extents.LargestAxis(); // Index of largest axis
+
+ // Split along the axis
+ NbPos = Split(Axis, builder);
+
+ // Check split validity
+ if(!NbPos || NbPos==mNbPrimitives) ValidSplit = false;
+ }
+ else if(builder->mSettings.mRules & SPLIT_SPLATTER_POINTS)
+ {
+ // Compute the means
+ Point Means(0.0f, 0.0f, 0.0f);
+ for(udword i=0;i<mNbPrimitives;i++)
+ {
+ udword Index = mNodePrimitives[i];
+ Means.x+=builder->GetSplittingValue(Index, 0);
+ Means.y+=builder->GetSplittingValue(Index, 1);
+ Means.z+=builder->GetSplittingValue(Index, 2);
+ }
+ Means/=float(mNbPrimitives);
+
+ // Compute variances
+ Point Vars(0.0f, 0.0f, 0.0f);
+ for(udword i=0;i<mNbPrimitives;i++)
+ {
+ udword Index = mNodePrimitives[i];
+ float Cx = builder->GetSplittingValue(Index, 0);
+ float Cy = builder->GetSplittingValue(Index, 1);
+ float Cz = builder->GetSplittingValue(Index, 2);
+ Vars.x += (Cx - Means.x)*(Cx - Means.x);
+ Vars.y += (Cy - Means.y)*(Cy - Means.y);
+ Vars.z += (Cz - Means.z)*(Cz - Means.z);
+ }
+ Vars/=float(mNbPrimitives-1);
+
+ // Choose axis with greatest variance
+ udword Axis = Vars.LargestAxis();
+
+ // Split along the axis
+ NbPos = Split(Axis, builder);
+
+ // Check split validity
+ if(!NbPos || NbPos==mNbPrimitives) ValidSplit = false;
+ }
+ else if(builder->mSettings.mRules & SPLIT_BALANCED)
+ {
+ // Test 3 axis, take the best
+ float Results[3];
+ NbPos = Split(0, builder); Results[0] = float(NbPos)/float(mNbPrimitives);
+ NbPos = Split(1, builder); Results[1] = float(NbPos)/float(mNbPrimitives);
+ NbPos = Split(2, builder); Results[2] = float(NbPos)/float(mNbPrimitives);
+ Results[0]-=0.5f; Results[0]*=Results[0];
+ Results[1]-=0.5f; Results[1]*=Results[1];
+ Results[2]-=0.5f; Results[2]*=Results[2];
+ udword Min=0;
+ if(Results[1]<Results[Min]) Min = 1;
+ if(Results[2]<Results[Min]) Min = 2;
+
+ // Split along the axis
+ NbPos = Split(Min, builder);
+
+ // Check split validity
+ if(!NbPos || NbPos==mNbPrimitives) ValidSplit = false;
+ }
+ else if(builder->mSettings.mRules & SPLIT_BEST_AXIS)
+ {
+ // Test largest, then middle, then smallest axis...
+
+ // Sort axis
+ Point Extents; mBV.GetExtents(Extents); // Box extents
+ udword SortedAxis[] = { 0, 1, 2 };
+ float* Keys = (float*)&Extents.x;
+ for(udword j=0;j<3;j++)
+ {
+ for(udword i=0;i<2;i++)
+ {
+ if(Keys[SortedAxis[i]]<Keys[SortedAxis[i+1]])
+ {
+ udword Tmp = SortedAxis[i];
+ SortedAxis[i] = SortedAxis[i+1];
+ SortedAxis[i+1] = Tmp;
+ }
+ }
+ }
+
+ // Find the largest axis to split along
+ udword CurAxis = 0;
+ ValidSplit = false;
+ while(!ValidSplit && CurAxis!=3)
+ {
+ NbPos = Split(SortedAxis[CurAxis], builder);
+ // Check the subdivision has been successful
+ if(!NbPos || NbPos==mNbPrimitives) CurAxis++;
+ else ValidSplit = true;
+ }
+ }
+ else if(builder->mSettings.mRules & SPLIT_FIFTY)
+ {
+ // Don't even bother splitting (mainly a performance test)
+ NbPos = mNbPrimitives>>1;
+ }
+ else return false; // Unknown splitting rules
+
+ // Check the subdivision has been successful
+ if(!ValidSplit)
+ {
+ // Here, all boxes lie in the same sub-space. Two strategies:
+ // - if the tree *must* be complete, make an arbitrary 50-50 split
+ // - else stop subdividing
+// if(builder->mSettings.mRules&SPLIT_COMPLETE)
+ if(builder->mSettings.mLimit==1)
+ {
+ builder->IncreaseNbInvalidSplits();
+ NbPos = mNbPrimitives>>1;
+ }
+ else return true;
+ }
+
+ // Now create children and assign their pointers.
+ if(builder->mNodeBase)
+ {
+ // We use a pre-allocated linear pool for complete trees [Opcode 1.3]
+ AABBTreeNode* Pool = (AABBTreeNode*)builder->mNodeBase;
+ udword Count = builder->GetCount() - 1; // Count begins to 1...
+ // Set last bit to tell it shouldn't be freed ### pretty ugly, find a better way. Maybe one bit in mNbPrimitives
+ ASSERT(!(udword(&Pool[Count+0])&1));
+ ASSERT(!(udword(&Pool[Count+1])&1));
+ mPos = udword(&Pool[Count+0])|1;
+#ifndef OPC_NO_NEG_VANILLA_TREE
+ mNeg = udword(&Pool[Count+1])|1;
+#endif
+ }
+ else
+ {
+ // Non-complete trees and/or Opcode 1.2 allocate nodes on-the-fly
+#ifndef OPC_NO_NEG_VANILLA_TREE
+ mPos = (udword)new AABBTreeNode; CHECKALLOC(mPos);
+ mNeg = (udword)new AABBTreeNode; CHECKALLOC(mNeg);
+#else
+ AABBTreeNode* PosNeg = new AABBTreeNode[2];
+ CHECKALLOC(PosNeg);
+ mPos = (udword)PosNeg;
+#endif
+ }
+
+ // Update stats
+ builder->IncreaseCount(2);
+
+ // Assign children
+ AABBTreeNode* Pos = (AABBTreeNode*)GetPos();
+ AABBTreeNode* Neg = (AABBTreeNode*)GetNeg();
+ Pos->mNodePrimitives = &mNodePrimitives[0];
+ Pos->mNbPrimitives = NbPos;
+ Neg->mNodePrimitives = &mNodePrimitives[NbPos];
+ Neg->mNbPrimitives = mNbPrimitives - NbPos;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive hierarchy building in a top-down fashion.
+ * \param builder [in] the tree builder
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeNode::_BuildHierarchy(AABBTreeBuilder* builder)
+{
+ // 1) Compute the global box for current node. The box is stored in mBV.
+ builder->ComputeGlobalBox(mNodePrimitives, mNbPrimitives, mBV);
+
+ // 2) Subdivide current node
+ Subdivide(builder);
+
+ // 3) Recurse
+ AABBTreeNode* Pos = (AABBTreeNode*)GetPos();
+ AABBTreeNode* Neg = (AABBTreeNode*)GetNeg();
+ if(Pos) Pos->_BuildHierarchy(builder);
+ if(Neg) Neg->_BuildHierarchy(builder);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the tree (top-down).
+ * \param builder [in] the tree builder
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeNode::_Refit(AABBTreeBuilder* builder)
+{
+ // 1) Recompute the new global box for current node
+ builder->ComputeGlobalBox(mNodePrimitives, mNbPrimitives, mBV);
+
+ // 2) Recurse
+ AABBTreeNode* Pos = (AABBTreeNode*)GetPos();
+ AABBTreeNode* Neg = (AABBTreeNode*)GetNeg();
+ if(Pos) Pos->_Refit(builder);
+ if(Neg) Neg->_Refit(builder);
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBTree::AABBTree() : mIndices(null), mTotalNbNodes(0), mPool(null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBTree::~AABBTree()
+{
+ Release();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Releases the tree.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTree::Release()
+{
+ DELETEARRAY(mPool);
+ DELETEARRAY(mIndices);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds a generic AABB tree from a tree builder.
+ * \param builder [in] the tree builder
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTree::Build(AABBTreeBuilder* builder)
+{
+ // Checkings
+ if(!builder || !builder->mNbPrimitives) return false;
+
+ // Release previous tree
+ Release();
+
+ // Init stats
+ builder->SetCount(1);
+ builder->SetNbInvalidSplits(0);
+
+ // Initialize indices. This list will be modified during build.
+ mIndices = new udword[builder->mNbPrimitives];
+ CHECKALLOC(mIndices);
+ // Identity permutation
+ for(udword i=0;i<builder->mNbPrimitives;i++) mIndices[i] = i;
+
+ // Setup initial node. Here we have a complete permutation of the app's primitives.
+ mNodePrimitives = mIndices;
+ mNbPrimitives = builder->mNbPrimitives;
+
+ // Use a linear array for complete trees (since we can predict the final number of nodes) [Opcode 1.3]
+// if(builder->mRules&SPLIT_COMPLETE)
+ if(builder->mSettings.mLimit==1)
+ {
+ // Allocate a pool of nodes
+ mPool = new AABBTreeNode[builder->mNbPrimitives*2 - 1];
+
+ builder->mNodeBase = mPool; // ### ugly !
+ }
+
+ // Build the hierarchy
+ _BuildHierarchy(builder);
+
+ // Get back total number of nodes
+ mTotalNbNodes = builder->GetCount();
+
+ // For complete trees, check the correct number of nodes has been created [Opcode 1.3]
+ if(mPool) ASSERT(mTotalNbNodes==builder->mNbPrimitives*2 - 1);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the depth of the tree.
+ * A well-balanced tree should have a log(n) depth. A degenerate tree O(n) depth.
+ * \return depth of the tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword AABBTree::ComputeDepth() const
+{
+ return Walk(null, null); // Use the walking code without callback
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Walks the tree, calling the user back for each node.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword AABBTree::Walk(WalkingCallback callback, void* user_data) const
+{
+ // Call it without callback to compute max depth
+ udword MaxDepth = 0;
+ udword CurrentDepth = 0;
+
+ struct Local
+ {
+ static void _Walk(const AABBTreeNode* current_node, udword& max_depth, udword& current_depth, WalkingCallback callback, void* user_data)
+ {
+ // Checkings
+ if(!current_node) return;
+ // Entering a new node => increase depth
+ current_depth++;
+ // Keep track of max depth
+ if(current_depth>max_depth) max_depth = current_depth;
+
+ // Callback
+ if(callback && !(callback)(current_node, current_depth, user_data)) return;
+
+ // Recurse
+ if(current_node->GetPos()) { _Walk(current_node->GetPos(), max_depth, current_depth, callback, user_data); current_depth--; }
+ if(current_node->GetNeg()) { _Walk(current_node->GetNeg(), max_depth, current_depth, callback, user_data); current_depth--; }
+ }
+ };
+ Local::_Walk(this, MaxDepth, CurrentDepth, callback, user_data);
+ return MaxDepth;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the tree in a top-down way.
+ * \param builder [in] the tree builder
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTree::Refit(AABBTreeBuilder* builder)
+{
+ if(!builder) return false;
+ _Refit(builder);
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the tree in a bottom-up way.
+ * \param builder [in] the tree builder
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTree::Refit2(AABBTreeBuilder* builder)
+{
+ // Checkings
+ if(!builder) return false;
+
+ ASSERT(mPool);
+
+ // Bottom-up update
+ Point Min,Max;
+ Point Min_,Max_;
+ udword Index = mTotalNbNodes;
+ while(Index--)
+ {
+ AABBTreeNode& Current = mPool[Index];
+
+ if(Current.IsLeaf())
+ {
+ builder->ComputeGlobalBox(Current.GetPrimitives(), Current.GetNbPrimitives(), *(AABB*)Current.GetAABB());
+ }
+ else
+ {
+ Current.GetPos()->GetAABB()->GetMin(Min);
+ Current.GetPos()->GetAABB()->GetMax(Max);
+
+ Current.GetNeg()->GetAABB()->GetMin(Min_);
+ Current.GetNeg()->GetAABB()->GetMax(Max_);
+
+ Min.Min(Min_);
+ Max.Max(Max_);
+
+ ((AABB*)Current.GetAABB())->SetMinMax(Min, Max);
+ }
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the number of bytes used by the tree.
+ * \return number of bytes used
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword AABBTree::GetUsedBytes() const
+{
+ udword TotalSize = mTotalNbNodes*GetNodeSize();
+ if(mIndices) TotalSize+=mNbPrimitives*sizeof(udword);
+ return TotalSize;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks the tree is a complete tree or not.
+ * A complete tree is made of 2*N-1 nodes, where N is the number of primitives in the tree.
+ * \return true for complete trees
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTree::IsComplete() const
+{
+ return (GetNbNodes()==GetNbPrimitives()*2-1);
+}
diff --git a/Opcode/OPC_AABBTree.h b/Opcode/OPC_AABBTree.h
new file mode 100644
index 0000000..377fbcb
--- /dev/null
+++ b/Opcode/OPC_AABBTree.h
@@ -0,0 +1,137 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a versatile AABB tree.
+ * \file OPC_AABBTree.h
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_AABBTREE_H__
+#define __OPC_AABBTREE_H__
+
+#ifdef OPC_NO_NEG_VANILLA_TREE
+ //! TO BE DOCUMENTED
+ #define IMPLEMENT_TREE(base_class, volume) \
+ public: \
+ /* Constructor / Destructor */ \
+ base_class(); \
+ ~base_class(); \
+ /* Data access */ \
+ inline_ const volume* Get##volume() const { return &mBV; } \
+ /* Clear the last bit */ \
+ inline_ const base_class* GetPos() const { return (const base_class*)(mPos&~1); } \
+ inline_ const base_class* GetNeg() const { const base_class* P = GetPos(); return P ? P+1 : null;} \
+ \
+ /* We don't need to test both nodes since we can't have one without the other */ \
+ inline_ bool IsLeaf() const { return !GetPos(); } \
+ \
+ /* Stats */ \
+ inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \
+ protected: \
+ /* Tree-independent data */ \
+ /* Following data always belong to the BV-tree, regardless of what the tree actually contains.*/ \
+ /* Whatever happens we need the two children and the enclosing volume.*/ \
+ volume mBV; /* Global bounding-volume enclosing all the node-related primitives */ \
+ udword mPos; /* "Positive" & "Negative" children */
+#else
+ //! TO BE DOCUMENTED
+ #define IMPLEMENT_TREE(base_class, volume) \
+ public: \
+ /* Constructor / Destructor */ \
+ base_class(); \
+ ~base_class(); \
+ /* Data access */ \
+ inline_ const volume* Get##volume() const { return &mBV; } \
+ /* Clear the last bit */ \
+ inline_ const base_class* GetPos() const { return (const base_class*)(mPos&~1); } \
+ inline_ const base_class* GetNeg() const { return (const base_class*)(mNeg&~1); } \
+ \
+/* inline_ bool IsLeaf() const { return (!GetPos() && !GetNeg()); } */ \
+ /* We don't need to test both nodes since we can't have one without the other */ \
+ inline_ bool IsLeaf() const { return !GetPos(); } \
+ \
+ /* Stats */ \
+ inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \
+ protected: \
+ /* Tree-independent data */ \
+ /* Following data always belong to the BV-tree, regardless of what the tree actually contains.*/ \
+ /* Whatever happens we need the two children and the enclosing volume.*/ \
+ volume mBV; /* Global bounding-volume enclosing all the node-related primitives */ \
+ udword mPos; /* "Positive" child */ \
+ udword mNeg; /* "Negative" child */
+#endif
+
+ typedef void (*CullingCallback) (udword nb_primitives, udword* node_primitives, BOOL need_clipping, void* user_data);
+
+ class OPCODE_API AABBTreeNode
+ {
+ IMPLEMENT_TREE(AABBTreeNode, AABB)
+ public:
+ // Data access
+ inline_ const udword* GetPrimitives() const { return mNodePrimitives; }
+ inline_ udword GetNbPrimitives() const { return mNbPrimitives; }
+
+ protected:
+ // Tree-dependent data
+ udword* mNodePrimitives; //!< Node-related primitives (shortcut to a position in mIndices below)
+ udword mNbPrimitives; //!< Number of primitives for this node
+ // Internal methods
+ udword Split(udword axis, AABBTreeBuilder* builder);
+ bool Subdivide(AABBTreeBuilder* builder);
+ void _BuildHierarchy(AABBTreeBuilder* builder);
+ void _Refit(AABBTreeBuilder* builder);
+ };
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * User-callback, called for each node by the walking code.
+ * \param current [in] current node
+ * \param depth [in] current node's depth
+ * \param user_data [in] user-defined data
+ * \return true to recurse through children, else false to bypass them
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ typedef bool (*WalkingCallback) (const AABBTreeNode* current, udword depth, void* user_data);
+
+ class OPCODE_API AABBTree : public AABBTreeNode
+ {
+ public:
+ // Constructor / Destructor
+ AABBTree();
+ ~AABBTree();
+ // Build
+ bool Build(AABBTreeBuilder* builder);
+ void Release();
+
+ // Data access
+ inline_ const udword* GetIndices() const { return mIndices; } //!< Catch the indices
+ inline_ udword GetNbNodes() const { return mTotalNbNodes; } //!< Catch the number of nodes
+
+ // Infos
+ bool IsComplete() const;
+ // Stats
+ udword ComputeDepth() const;
+ udword GetUsedBytes() const;
+ udword Walk(WalkingCallback callback, void* user_data) const;
+
+ bool Refit(AABBTreeBuilder* builder);
+ bool Refit2(AABBTreeBuilder* builder);
+ private:
+ udword* mIndices; //!< Indices in the app list. Indices are reorganized during build (permutation).
+ AABBTreeNode* mPool; //!< Linear pool of nodes for complete trees. Null otherwise. [Opcode 1.3]
+ // Stats
+ udword mTotalNbNodes; //!< Number of nodes in the tree.
+ };
+
+#endif // __OPC_AABBTREE_H__
diff --git a/Opcode/OPC_BaseModel.cpp b/Opcode/OPC_BaseModel.cpp
new file mode 100644
index 0000000..4e15809
--- /dev/null
+++ b/Opcode/OPC_BaseModel.cpp
@@ -0,0 +1,138 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains base model interface.
+ * \file OPC_BaseModel.cpp
+ * \author Pierre Terdiman
+ * \date May, 18, 2003
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * The base class for collision models.
+ *
+ * \class BaseModel
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date May, 18, 2003
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPCODECREATE::OPCODECREATE()
+{
+ mIMesh = null;
+ mSettings.mRules = SPLIT_SPLATTER_POINTS | SPLIT_GEOM_CENTER;
+ mSettings.mLimit = 1; // Mandatory for complete trees
+ mNoLeaf = true;
+ mQuantized = true;
+#ifdef __MESHMERIZER_H__
+ mCollisionHull = false;
+#endif // __MESHMERIZER_H__
+ mKeepOriginal = false;
+ mCanRemap = false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+BaseModel::BaseModel() : mIMesh(null), mModelCode(0), mSource(null), mTree(null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+BaseModel::~BaseModel()
+{
+ ReleaseBase();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Releases everything.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void BaseModel::ReleaseBase()
+{
+ DELETESINGLE(mSource);
+ DELETESINGLE(mTree);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Creates an optimized tree according to user-settings, and setups mModelCode.
+ * \param no_leaf [in] true for "no leaf" tree
+ * \param quantized [in] true for quantized tree
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool BaseModel::CreateTree(bool no_leaf, bool quantized)
+{
+ DELETESINGLE(mTree);
+
+ // Setup model code
+ if(no_leaf) mModelCode |= OPC_NO_LEAF;
+ else mModelCode &= ~OPC_NO_LEAF;
+
+ if(quantized) mModelCode |= OPC_QUANTIZED;
+ else mModelCode &= ~OPC_QUANTIZED;
+
+ // Create the correct class
+ if(mModelCode & OPC_NO_LEAF)
+ {
+ if(mModelCode & OPC_QUANTIZED) mTree = new AABBQuantizedNoLeafTree;
+ else mTree = new AABBNoLeafTree;
+ }
+ else
+ {
+ if(mModelCode & OPC_QUANTIZED) mTree = new AABBQuantizedTree;
+ else mTree = new AABBCollisionTree;
+ }
+ CHECKALLOC(mTree);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the collision model. This can be used to handle dynamic meshes. Usage is:
+ * 1. modify your mesh vertices (keep the topology constant!)
+ * 2. refit the tree (call this method)
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool BaseModel::Refit()
+{
+ // Refit the optimized tree
+ return mTree->Refit(mIMesh);
+
+// Old code kept for reference : refit the source tree then rebuild !
+// if(!mSource) return false;
+// // Ouch...
+// mSource->Refit(&mTB);
+// // Ouch...
+// return mTree->Build(mSource);
+}
diff --git a/Opcode/OPC_BaseModel.h b/Opcode/OPC_BaseModel.h
new file mode 100644
index 0000000..15fc423
--- /dev/null
+++ b/Opcode/OPC_BaseModel.h
@@ -0,0 +1,175 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains base model interface.
+ * \file OPC_BaseModel.h
+ * \author Pierre Terdiman
+ * \date May, 18, 2003
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_BASEMODEL_H__
+#define __OPC_BASEMODEL_H__
+
+ //! Model creation structure
+ struct OPCODE_API OPCODECREATE
+ {
+ //! Constructor
+ OPCODECREATE();
+
+ MeshInterface* mIMesh; //!< Mesh interface (access to triangles & vertices) (*)
+ BuildSettings mSettings; //!< Builder's settings
+ bool mNoLeaf; //!< true => discard leaf nodes (else use a normal tree)
+ bool mQuantized; //!< true => quantize the tree (else use a normal tree)
+#ifdef __MESHMERIZER_H__
+ bool mCollisionHull; //!< true => use convex hull + GJK
+#endif // __MESHMERIZER_H__
+ bool mKeepOriginal; //!< true => keep a copy of the original tree (debug purpose)
+ bool mCanRemap; //!< true => allows OPCODE to reorganize client arrays
+
+ // (*) This pointer is saved internally and used by OPCODE until collision structures are released,
+ // so beware of the object's lifetime.
+ };
+
+ enum ModelFlag
+ {
+ OPC_QUANTIZED = (1<<0), //!< Compressed/uncompressed tree
+ OPC_NO_LEAF = (1<<1), //!< Leaf/NoLeaf tree
+ OPC_SINGLE_NODE = (1<<2) //!< Special case for 1-node models
+ };
+
+ class OPCODE_API BaseModel
+ {
+ public:
+ // Constructor/Destructor
+ BaseModel();
+ virtual ~BaseModel();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Builds a collision model.
+ * \param create [in] model creation structure
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual bool Build(const OPCODECREATE& create) = 0;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the number of bytes used by the tree.
+ * \return amount of bytes used
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual udword GetUsedBytes() const = 0;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Refits the collision model. This can be used to handle dynamic meshes. Usage is:
+ * 1. modify your mesh vertices (keep the topology constant!)
+ * 2. refit the tree (call this method)
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual bool Refit();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the source tree.
+ * \return generic tree
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ const AABBTree* GetSourceTree() const { return mSource; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the tree.
+ * \return the collision tree
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ const AABBOptimizedTree* GetTree() const { return mTree; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the tree.
+ * \return the collision tree
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ AABBOptimizedTree* GetTree() { return mTree; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the number of nodes in the tree.
+ * Should be 2*N-1 for normal trees and N-1 for optimized ones.
+ * \return number of nodes
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbNodes() const { return mTree->GetNbNodes(); }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks whether the tree has leaf nodes or not.
+ * \return true if the tree has leaf nodes (normal tree), else false (optimized tree)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL HasLeafNodes() const { return !(mModelCode & OPC_NO_LEAF); }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks whether the tree is quantized or not.
+ * \return true if the tree is quantized
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL IsQuantized() const { return mModelCode & OPC_QUANTIZED; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks whether the model has a single node or not. This special case must be handled separately.
+ * \return true if the model has only 1 node
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL HasSingleNode() const { return mModelCode & OPC_SINGLE_NODE; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the model's code.
+ * \return model's code
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetModelCode() const { return mModelCode; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the mesh interface.
+ * \return mesh interface
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ const MeshInterface* GetMeshInterface() const { return mIMesh; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Sets the mesh interface.
+ * \param imesh [in] mesh interface
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetMeshInterface(const MeshInterface* imesh) { mIMesh = imesh; }
+
+ protected:
+ const MeshInterface* mIMesh; //!< User-defined mesh interface
+ udword mModelCode; //!< Model code = combination of ModelFlag(s)
+ AABBTree* mSource; //!< Original source tree
+ AABBOptimizedTree* mTree; //!< Optimized tree owned by the model
+ // Internal methods
+ void ReleaseBase();
+ bool CreateTree(bool no_leaf, bool quantized);
+ };
+
+#endif //__OPC_BASEMODEL_H__ \ No newline at end of file
diff --git a/Opcode/OPC_BoxBoxOverlap.h b/Opcode/OPC_BoxBoxOverlap.h
new file mode 100644
index 0000000..550ab01
--- /dev/null
+++ b/Opcode/OPC_BoxBoxOverlap.h
@@ -0,0 +1,122 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * OBB-OBB overlap test using the separating axis theorem.
+ * - original code by Gomez / Gamasutra (similar to Gottschalk's one in RAPID)
+ * - optimized for AABB trees by computing the rotation matrix once (SOLID-fashion)
+ * - the fabs matrix is precomputed as well and epsilon-tweaked (RAPID-style, we found this almost mandatory)
+ * - Class III axes can be disabled... (SOLID & Intel fashion)
+ * - ...or enabled to perform some profiling
+ * - CPU comparisons used when appropriate
+ * - lazy evaluation sometimes saves some work in case of early exits (unlike SOLID)
+ *
+ * \param ea [in] extents from box A
+ * \param ca [in] center from box A
+ * \param eb [in] extents from box B
+ * \param cb [in] center from box B
+ * \return true if boxes overlap
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL AABBTreeCollider::BoxBoxOverlap(const Point& ea, const Point& ca, const Point& eb, const Point& cb)
+{
+ // Stats
+ mNbBVBVTests++;
+
+ float t,t2;
+
+ // Class I : A's basis vectors
+ float Tx = (mR1to0.m[0][0]*cb.x + mR1to0.m[1][0]*cb.y + mR1to0.m[2][0]*cb.z) + mT1to0.x - ca.x;
+ t = ea.x + eb.x*mAR.m[0][0] + eb.y*mAR.m[1][0] + eb.z*mAR.m[2][0];
+ if(GREATER(Tx, t)) return FALSE;
+
+ float Ty = (mR1to0.m[0][1]*cb.x + mR1to0.m[1][1]*cb.y + mR1to0.m[2][1]*cb.z) + mT1to0.y - ca.y;
+ t = ea.y + eb.x*mAR.m[0][1] + eb.y*mAR.m[1][1] + eb.z*mAR.m[2][1];
+ if(GREATER(Ty, t)) return FALSE;
+
+ float Tz = (mR1to0.m[0][2]*cb.x + mR1to0.m[1][2]*cb.y + mR1to0.m[2][2]*cb.z) + mT1to0.z - ca.z;
+ t = ea.z + eb.x*mAR.m[0][2] + eb.y*mAR.m[1][2] + eb.z*mAR.m[2][2];
+ if(GREATER(Tz, t)) return FALSE;
+
+ // Class II : B's basis vectors
+ t = Tx*mR1to0.m[0][0] + Ty*mR1to0.m[0][1] + Tz*mR1to0.m[0][2]; t2 = ea.x*mAR.m[0][0] + ea.y*mAR.m[0][1] + ea.z*mAR.m[0][2] + eb.x;
+ if(GREATER(t, t2)) return FALSE;
+
+ t = Tx*mR1to0.m[1][0] + Ty*mR1to0.m[1][1] + Tz*mR1to0.m[1][2]; t2 = ea.x*mAR.m[1][0] + ea.y*mAR.m[1][1] + ea.z*mAR.m[1][2] + eb.y;
+ if(GREATER(t, t2)) return FALSE;
+
+ t = Tx*mR1to0.m[2][0] + Ty*mR1to0.m[2][1] + Tz*mR1to0.m[2][2]; t2 = ea.x*mAR.m[2][0] + ea.y*mAR.m[2][1] + ea.z*mAR.m[2][2] + eb.z;
+ if(GREATER(t, t2)) return FALSE;
+
+ // Class III : 9 cross products
+ // Cool trick: always perform the full test for first level, regardless of settings.
+ // That way pathological cases (such as the pencils scene) are quickly rejected anyway !
+ if(mFullBoxBoxTest || mNbBVBVTests==1)
+ {
+ t = Tz*mR1to0.m[0][1] - Ty*mR1to0.m[0][2]; t2 = ea.y*mAR.m[0][2] + ea.z*mAR.m[0][1] + eb.y*mAR.m[2][0] + eb.z*mAR.m[1][0]; if(GREATER(t, t2)) return FALSE; // L = A0 x B0
+ t = Tz*mR1to0.m[1][1] - Ty*mR1to0.m[1][2]; t2 = ea.y*mAR.m[1][2] + ea.z*mAR.m[1][1] + eb.x*mAR.m[2][0] + eb.z*mAR.m[0][0]; if(GREATER(t, t2)) return FALSE; // L = A0 x B1
+ t = Tz*mR1to0.m[2][1] - Ty*mR1to0.m[2][2]; t2 = ea.y*mAR.m[2][2] + ea.z*mAR.m[2][1] + eb.x*mAR.m[1][0] + eb.y*mAR.m[0][0]; if(GREATER(t, t2)) return FALSE; // L = A0 x B2
+ t = Tx*mR1to0.m[0][2] - Tz*mR1to0.m[0][0]; t2 = ea.x*mAR.m[0][2] + ea.z*mAR.m[0][0] + eb.y*mAR.m[2][1] + eb.z*mAR.m[1][1]; if(GREATER(t, t2)) return FALSE; // L = A1 x B0
+ t = Tx*mR1to0.m[1][2] - Tz*mR1to0.m[1][0]; t2 = ea.x*mAR.m[1][2] + ea.z*mAR.m[1][0] + eb.x*mAR.m[2][1] + eb.z*mAR.m[0][1]; if(GREATER(t, t2)) return FALSE; // L = A1 x B1
+ t = Tx*mR1to0.m[2][2] - Tz*mR1to0.m[2][0]; t2 = ea.x*mAR.m[2][2] + ea.z*mAR.m[2][0] + eb.x*mAR.m[1][1] + eb.y*mAR.m[0][1]; if(GREATER(t, t2)) return FALSE; // L = A1 x B2
+ t = Ty*mR1to0.m[0][0] - Tx*mR1to0.m[0][1]; t2 = ea.x*mAR.m[0][1] + ea.y*mAR.m[0][0] + eb.y*mAR.m[2][2] + eb.z*mAR.m[1][2]; if(GREATER(t, t2)) return FALSE; // L = A2 x B0
+ t = Ty*mR1to0.m[1][0] - Tx*mR1to0.m[1][1]; t2 = ea.x*mAR.m[1][1] + ea.y*mAR.m[1][0] + eb.x*mAR.m[2][2] + eb.z*mAR.m[0][2]; if(GREATER(t, t2)) return FALSE; // L = A2 x B1
+ t = Ty*mR1to0.m[2][0] - Tx*mR1to0.m[2][1]; t2 = ea.x*mAR.m[2][1] + ea.y*mAR.m[2][0] + eb.x*mAR.m[1][2] + eb.y*mAR.m[0][2]; if(GREATER(t, t2)) return FALSE; // L = A2 x B2
+ }
+ return TRUE;
+}
+
+//! A dedicated version when one box is constant
+inline_ BOOL OBBCollider::BoxBoxOverlap(const Point& extents, const Point& center)
+{
+ // Stats
+ mNbVolumeBVTests++;
+
+ float t,t2;
+
+ // Class I : A's basis vectors
+ float Tx = mTBoxToModel.x - center.x; t = extents.x + mBBx1; if(GREATER(Tx, t)) return FALSE;
+ float Ty = mTBoxToModel.y - center.y; t = extents.y + mBBy1; if(GREATER(Ty, t)) return FALSE;
+ float Tz = mTBoxToModel.z - center.z; t = extents.z + mBBz1; if(GREATER(Tz, t)) return FALSE;
+
+ // Class II : B's basis vectors
+ t = Tx*mRBoxToModel.m[0][0] + Ty*mRBoxToModel.m[0][1] + Tz*mRBoxToModel.m[0][2];
+ t2 = extents.x*mAR.m[0][0] + extents.y*mAR.m[0][1] + extents.z*mAR.m[0][2] + mBoxExtents.x;
+ if(GREATER(t, t2)) return FALSE;
+
+ t = Tx*mRBoxToModel.m[1][0] + Ty*mRBoxToModel.m[1][1] + Tz*mRBoxToModel.m[1][2];
+ t2 = extents.x*mAR.m[1][0] + extents.y*mAR.m[1][1] + extents.z*mAR.m[1][2] + mBoxExtents.y;
+ if(GREATER(t, t2)) return FALSE;
+
+ t = Tx*mRBoxToModel.m[2][0] + Ty*mRBoxToModel.m[2][1] + Tz*mRBoxToModel.m[2][2];
+ t2 = extents.x*mAR.m[2][0] + extents.y*mAR.m[2][1] + extents.z*mAR.m[2][2] + mBoxExtents.z;
+ if(GREATER(t, t2)) return FALSE;
+
+ // Class III : 9 cross products
+ // Cool trick: always perform the full test for first level, regardless of settings.
+ // That way pathological cases (such as the pencils scene) are quickly rejected anyway !
+ if(mFullBoxBoxTest || mNbVolumeBVTests==1)
+ {
+ t = Tz*mRBoxToModel.m[0][1] - Ty*mRBoxToModel.m[0][2]; t2 = extents.y*mAR.m[0][2] + extents.z*mAR.m[0][1] + mBB_1; if(GREATER(t, t2)) return FALSE; // L = A0 x B0
+ t = Tz*mRBoxToModel.m[1][1] - Ty*mRBoxToModel.m[1][2]; t2 = extents.y*mAR.m[1][2] + extents.z*mAR.m[1][1] + mBB_2; if(GREATER(t, t2)) return FALSE; // L = A0 x B1
+ t = Tz*mRBoxToModel.m[2][1] - Ty*mRBoxToModel.m[2][2]; t2 = extents.y*mAR.m[2][2] + extents.z*mAR.m[2][1] + mBB_3; if(GREATER(t, t2)) return FALSE; // L = A0 x B2
+ t = Tx*mRBoxToModel.m[0][2] - Tz*mRBoxToModel.m[0][0]; t2 = extents.x*mAR.m[0][2] + extents.z*mAR.m[0][0] + mBB_4; if(GREATER(t, t2)) return FALSE; // L = A1 x B0
+ t = Tx*mRBoxToModel.m[1][2] - Tz*mRBoxToModel.m[1][0]; t2 = extents.x*mAR.m[1][2] + extents.z*mAR.m[1][0] + mBB_5; if(GREATER(t, t2)) return FALSE; // L = A1 x B1
+ t = Tx*mRBoxToModel.m[2][2] - Tz*mRBoxToModel.m[2][0]; t2 = extents.x*mAR.m[2][2] + extents.z*mAR.m[2][0] + mBB_6; if(GREATER(t, t2)) return FALSE; // L = A1 x B2
+ t = Ty*mRBoxToModel.m[0][0] - Tx*mRBoxToModel.m[0][1]; t2 = extents.x*mAR.m[0][1] + extents.y*mAR.m[0][0] + mBB_7; if(GREATER(t, t2)) return FALSE; // L = A2 x B0
+ t = Ty*mRBoxToModel.m[1][0] - Tx*mRBoxToModel.m[1][1]; t2 = extents.x*mAR.m[1][1] + extents.y*mAR.m[1][0] + mBB_8; if(GREATER(t, t2)) return FALSE; // L = A2 x B1
+ t = Ty*mRBoxToModel.m[2][0] - Tx*mRBoxToModel.m[2][1]; t2 = extents.x*mAR.m[2][1] + extents.y*mAR.m[2][0] + mBB_9; if(GREATER(t, t2)) return FALSE; // L = A2 x B2
+ }
+ return TRUE;
+}
+
+//! A special version for 2 axis-aligned boxes
+inline_ BOOL AABBCollider::AABBAABBOverlap(const Point& extents, const Point& center)
+{
+ // Stats
+ mNbVolumeBVTests++;
+
+ float tx = mBox.mCenter.x - center.x; float ex = extents.x + mBox.mExtents.x; if(GREATER(tx, ex)) return FALSE;
+ float ty = mBox.mCenter.y - center.y; float ey = extents.y + mBox.mExtents.y; if(GREATER(ty, ey)) return FALSE;
+ float tz = mBox.mCenter.z - center.z; float ez = extents.z + mBox.mExtents.z; if(GREATER(tz, ez)) return FALSE;
+
+ return TRUE;
+}
diff --git a/Opcode/OPC_BoxPruning.cpp b/Opcode/OPC_BoxPruning.cpp
new file mode 100644
index 0000000..adc2d24
--- /dev/null
+++ b/Opcode/OPC_BoxPruning.cpp
@@ -0,0 +1,367 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for box pruning.
+ * \file IceBoxPruning.cpp
+ * \author Pierre Terdiman
+ * \date January, 29, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ You could use a complex sweep-and-prune as implemented in I-Collide.
+ You could use a complex hashing scheme as implemented in V-Clip or recently in ODE it seems.
+ You could use a "Recursive Dimensional Clustering" algorithm as implemented in GPG2.
+
+ Or you could use this.
+ Faster ? I don't know. Probably not. It would be a shame. But who knows ?
+ Easier ? Definitely. Enjoy the sheer simplicity.
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+*/
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+ inline_ void FindRunningIndex(udword& index, float* array, udword* sorted, int last, float max)
+ {
+ int First=index;
+ while(First<=last)
+ {
+ index = (First+last)>>1;
+
+ if(max>array[sorted[index]]) First = index+1;
+ else last = index-1;
+ }
+ }
+// ### could be log(n) !
+// and maybe use cmp integers
+
+// InsertionSort has better coherence, RadixSort is better for one-shot queries.
+#define PRUNING_SORTER RadixSort
+//#define PRUNING_SORTER InsertionSort
+
+// Static for coherence
+static PRUNING_SORTER* gCompletePruningSorter = null;
+static PRUNING_SORTER* gBipartitePruningSorter0 = null;
+static PRUNING_SORTER* gBipartitePruningSorter1 = null;
+inline_ PRUNING_SORTER* GetCompletePruningSorter()
+{
+ if(!gCompletePruningSorter) gCompletePruningSorter = new PRUNING_SORTER;
+ return gCompletePruningSorter;
+}
+inline_ PRUNING_SORTER* GetBipartitePruningSorter0()
+{
+ if(!gBipartitePruningSorter0) gBipartitePruningSorter0 = new PRUNING_SORTER;
+ return gBipartitePruningSorter0;
+}
+inline_ PRUNING_SORTER* GetBipartitePruningSorter1()
+{
+ if(!gBipartitePruningSorter1) gBipartitePruningSorter1 = new PRUNING_SORTER;
+ return gBipartitePruningSorter1;
+}
+void ReleasePruningSorters()
+{
+ DELETESINGLE(gBipartitePruningSorter1);
+ DELETESINGLE(gBipartitePruningSorter0);
+ DELETESINGLE(gCompletePruningSorter);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Bipartite box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set.
+ * \param nb0 [in] number of boxes in the first set
+ * \param array0 [in] array of boxes for the first set
+ * \param nb1 [in] number of boxes in the second set
+ * \param array1 [in] array of boxes for the second set
+ * \param pairs [out] array of overlapping pairs
+ * \param axes [in] projection order (0,2,1 is often best)
+ * \return true if success.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Opcode::BipartiteBoxPruning(udword nb0, const AABB** array0, udword nb1, const AABB** array1, Pairs& pairs, const Axes& axes)
+{
+ // Checkings
+ if(!nb0 || !array0 || !nb1 || !array1) return false;
+
+ // Catch axes
+ udword Axis0 = axes.mAxis0;
+ udword Axis1 = axes.mAxis1;
+ udword Axis2 = axes.mAxis2;
+
+ // Allocate some temporary data
+ float* MinPosList0 = new float[nb0];
+ float* MinPosList1 = new float[nb1];
+
+ // 1) Build main lists using the primary axis
+ for(udword i=0;i<nb0;i++) MinPosList0[i] = array0[i]->GetMin(Axis0);
+ for(udword i=0;i<nb1;i++) MinPosList1[i] = array1[i]->GetMin(Axis0);
+
+ // 2) Sort the lists
+ PRUNING_SORTER* RS0 = GetBipartitePruningSorter0();
+ PRUNING_SORTER* RS1 = GetBipartitePruningSorter1();
+ const udword* Sorted0 = RS0->Sort(MinPosList0, nb0).GetRanks();
+ const udword* Sorted1 = RS1->Sort(MinPosList1, nb1).GetRanks();
+
+ // 3) Prune the lists
+ udword Index0, Index1;
+
+ const udword* const LastSorted0 = &Sorted0[nb0];
+ const udword* const LastSorted1 = &Sorted1[nb1];
+ const udword* RunningAddress0 = Sorted0;
+ const udword* RunningAddress1 = Sorted1;
+
+ while(RunningAddress1<LastSorted1 && Sorted0<LastSorted0)
+ {
+ Index0 = *Sorted0++;
+
+ while(RunningAddress1<LastSorted1 && MinPosList1[*RunningAddress1]<MinPosList0[Index0]) RunningAddress1++;
+
+ const udword* RunningAddress2_1 = RunningAddress1;
+
+ while(RunningAddress2_1<LastSorted1 && MinPosList1[Index1 = *RunningAddress2_1++]<=array0[Index0]->GetMax(Axis0))
+ {
+ if(array0[Index0]->Intersect(*array1[Index1], Axis1))
+ {
+ if(array0[Index0]->Intersect(*array1[Index1], Axis2))
+ {
+ pairs.AddPair(Index0, Index1);
+ }
+ }
+ }
+ }
+
+ ////
+
+ while(RunningAddress0<LastSorted0 && Sorted1<LastSorted1)
+ {
+ Index0 = *Sorted1++;
+
+ while(RunningAddress0<LastSorted0 && MinPosList0[*RunningAddress0]<=MinPosList1[Index0]) RunningAddress0++;
+
+ const udword* RunningAddress2_0 = RunningAddress0;
+
+ while(RunningAddress2_0<LastSorted0 && MinPosList0[Index1 = *RunningAddress2_0++]<=array1[Index0]->GetMax(Axis0))
+ {
+ if(array0[Index1]->Intersect(*array1[Index0], Axis1))
+ {
+ if(array0[Index1]->Intersect(*array1[Index0], Axis2))
+ {
+ pairs.AddPair(Index1, Index0);
+ }
+ }
+
+ }
+ }
+
+ DELETEARRAY(MinPosList1);
+ DELETEARRAY(MinPosList0);
+
+ return true;
+}
+
+#define ORIGINAL_VERSION
+//#define JOAKIM
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set.
+ * \param nb [in] number of boxes
+ * \param array [in] array of boxes
+ * \param pairs [out] array of overlapping pairs
+ * \param axes [in] projection order (0,2,1 is often best)
+ * \return true if success.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Opcode::CompleteBoxPruning(udword nb, const AABB** array, Pairs& pairs, const Axes& axes)
+{
+ // Checkings
+ if(!nb || !array) return false;
+
+ // Catch axes
+ udword Axis0 = axes.mAxis0;
+ udword Axis1 = axes.mAxis1;
+ udword Axis2 = axes.mAxis2;
+
+#ifdef ORIGINAL_VERSION
+ // Allocate some temporary data
+// float* PosList = new float[nb];
+ float* PosList = new float[nb+1];
+
+ // 1) Build main list using the primary axis
+ for(udword i=0;i<nb;i++) PosList[i] = array[i]->GetMin(Axis0);
+PosList[nb++] = MAX_FLOAT;
+
+ // 2) Sort the list
+ PRUNING_SORTER* RS = GetCompletePruningSorter();
+ const udword* Sorted = RS->Sort(PosList, nb).GetRanks();
+
+ // 3) Prune the list
+ const udword* const LastSorted = &Sorted[nb];
+ const udword* RunningAddress = Sorted;
+ udword Index0, Index1;
+ while(RunningAddress<LastSorted && Sorted<LastSorted)
+ {
+ Index0 = *Sorted++;
+
+// while(RunningAddress<LastSorted && PosList[*RunningAddress++]<PosList[Index0]);
+ while(PosList[*RunningAddress++]<PosList[Index0]);
+
+ if(RunningAddress<LastSorted)
+ {
+ const udword* RunningAddress2 = RunningAddress;
+
+// while(RunningAddress2<LastSorted && PosList[Index1 = *RunningAddress2++]<=array[Index0]->GetMax(Axis0))
+ while(PosList[Index1 = *RunningAddress2++]<=array[Index0]->GetMax(Axis0))
+ {
+// if(Index0!=Index1)
+// {
+ if(array[Index0]->Intersect(*array[Index1], Axis1))
+ {
+ if(array[Index0]->Intersect(*array[Index1], Axis2))
+ {
+ pairs.AddPair(Index0, Index1);
+ }
+ }
+// }
+ }
+ }
+ }
+
+ DELETEARRAY(PosList);
+#endif
+
+#ifdef JOAKIM
+ // Allocate some temporary data
+// float* PosList = new float[nb];
+ float* MinList = new float[nb+1];
+
+ // 1) Build main list using the primary axis
+ for(udword i=0;i<nb;i++) MinList[i] = array[i]->GetMin(Axis0);
+ MinList[nb] = MAX_FLOAT;
+
+ // 2) Sort the list
+ PRUNING_SORTER* RS = GetCompletePruningSorter();
+ udword* Sorted = RS->Sort(MinList, nb+1).GetRanks();
+
+ // 3) Prune the list
+// const udword* const LastSorted = &Sorted[nb];
+// const udword* const LastSorted = &Sorted[nb-1];
+ const udword* RunningAddress = Sorted;
+ udword Index0, Index1;
+
+// while(RunningAddress<LastSorted && Sorted<LastSorted)
+// while(RunningAddress<LastSorted)
+ while(RunningAddress<&Sorted[nb])
+// while(Sorted<LastSorted)
+ {
+// Index0 = *Sorted++;
+ Index0 = *RunningAddress++;
+
+// while(RunningAddress<LastSorted && PosList[*RunningAddress++]<PosList[Index0]);
+// while(PosList[*RunningAddress++]<PosList[Index0]);
+//RunningAddress = Sorted;
+// if(RunningAddress<LastSorted)
+ {
+ const udword* RunningAddress2 = RunningAddress;
+
+// while(RunningAddress2<LastSorted && PosList[Index1 = *RunningAddress2++]<=array[Index0]->GetMax(Axis0))
+
+// float CurrentMin = array[Index0]->GetMin(Axis0);
+ float CurrentMax = array[Index0]->GetMax(Axis0);
+
+ while(MinList[Index1 = *RunningAddress2] <= CurrentMax)
+// while(PosList[Index1 = *RunningAddress] <= CurrentMax)
+ {
+// if(Index0!=Index1)
+// {
+ if(array[Index0]->Intersect(*array[Index1], Axis1))
+ {
+ if(array[Index0]->Intersect(*array[Index1], Axis2))
+ {
+ pairs.AddPair(Index0, Index1);
+ }
+ }
+// }
+
+ RunningAddress2++;
+// RunningAddress++;
+ }
+ }
+ }
+
+ DELETEARRAY(MinList);
+#endif
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Brute-force versions are kept:
+// - to check the optimized versions return the correct list of intersections
+// - to check the speed of the optimized code against the brute-force one
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Brute-force bipartite box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set.
+ * \param nb0 [in] number of boxes in the first set
+ * \param array0 [in] array of boxes for the first set
+ * \param nb1 [in] number of boxes in the second set
+ * \param array1 [in] array of boxes for the second set
+ * \param pairs [out] array of overlapping pairs
+ * \return true if success.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Opcode::BruteForceBipartiteBoxTest(udword nb0, const AABB** array0, udword nb1, const AABB** array1, Pairs& pairs)
+{
+ // Checkings
+ if(!nb0 || !array0 || !nb1 || !array1) return false;
+
+ // Brute-force nb0*nb1 overlap tests
+ for(udword i=0;i<nb0;i++)
+ {
+ for(udword j=0;j<nb1;j++)
+ {
+ if(array0[i]->Intersect(*array1[j])) pairs.AddPair(i, j);
+ }
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set.
+ * \param nb [in] number of boxes
+ * \param array [in] array of boxes
+ * \param pairs [out] array of overlapping pairs
+ * \return true if success.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Opcode::BruteForceCompleteBoxTest(udword nb, const AABB** array, Pairs& pairs)
+{
+ // Checkings
+ if(!nb || !array) return false;
+
+ // Brute-force n(n-1)/2 overlap tests
+ for(udword i=0;i<nb;i++)
+ {
+ for(udword j=i+1;j<nb;j++)
+ {
+ if(array[i]->Intersect(*array[j])) pairs.AddPair(i, j);
+ }
+ }
+ return true;
+}
diff --git a/Opcode/OPC_BoxPruning.h b/Opcode/OPC_BoxPruning.h
new file mode 100644
index 0000000..ef65c80
--- /dev/null
+++ b/Opcode/OPC_BoxPruning.h
@@ -0,0 +1,31 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for box pruning.
+ * \file IceBoxPruning.h
+ * \author Pierre Terdiman
+ * \date January, 29, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_BOXPRUNING_H__
+#define __OPC_BOXPRUNING_H__
+
+ // Optimized versions
+ FUNCTION OPCODE_API bool CompleteBoxPruning(udword nb, const AABB** array, Pairs& pairs, const Axes& axes);
+ FUNCTION OPCODE_API bool BipartiteBoxPruning(udword nb0, const AABB** array0, udword nb1, const AABB** array1, Pairs& pairs, const Axes& axes);
+
+ // Brute-force versions
+ FUNCTION OPCODE_API bool BruteForceCompleteBoxTest(udword nb, const AABB** array, Pairs& pairs);
+ FUNCTION OPCODE_API bool BruteForceBipartiteBoxTest(udword nb0, const AABB** array0, udword nb1, const AABB** array1, Pairs& pairs);
+
+#endif //__OPC_BOXPRUNING_H__ \ No newline at end of file
diff --git a/Opcode/OPC_Collider.cpp b/Opcode/OPC_Collider.cpp
new file mode 100644
index 0000000..bb9663d
--- /dev/null
+++ b/Opcode/OPC_Collider.cpp
@@ -0,0 +1,54 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains base collider class.
+ * \file OPC_Collider.cpp
+ * \author Pierre Terdiman
+ * \date June, 2, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains the abstract class for colliders.
+ *
+ * \class Collider
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date June, 2, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Collider::Collider() :
+ mFlags (0),
+ mCurrentModel (null),
+ mIMesh (null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Collider::~Collider()
+{
+}
diff --git a/Opcode/OPC_Collider.h b/Opcode/OPC_Collider.h
new file mode 100644
index 0000000..4495093
--- /dev/null
+++ b/Opcode/OPC_Collider.h
@@ -0,0 +1,176 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains base collider class.
+ * \file OPC_Collider.h
+ * \author Pierre Terdiman
+ * \date June, 2, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_COLLIDER_H__
+#define __OPC_COLLIDER_H__
+
+ enum CollisionFlag
+ {
+ OPC_FIRST_CONTACT = (1<<0), //!< Report all contacts (false) or only first one (true)
+ OPC_TEMPORAL_COHERENCE = (1<<1), //!< Use temporal coherence or not
+ OPC_CONTACT = (1<<2), //!< Final contact status after a collision query
+ OPC_TEMPORAL_HIT = (1<<3), //!< There has been an early exit due to temporal coherence
+ OPC_NO_PRIMITIVE_TESTS = (1<<4), //!< Keep or discard primitive-bv tests in leaf nodes (volume-mesh queries)
+
+ OPC_CONTACT_FOUND = OPC_FIRST_CONTACT | OPC_CONTACT,
+ OPC_TEMPORAL_CONTACT = OPC_TEMPORAL_HIT | OPC_CONTACT,
+
+ OPC_FORCE_DWORD = 0x7fffffff
+ };
+
+ class OPCODE_API Collider
+ {
+ public:
+ // Constructor / Destructor
+ Collider();
+ virtual ~Collider();
+
+ // Collision report
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the last collision status after a collision query.
+ * \return true if a collision occured
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL GetContactStatus() const { return mFlags & OPC_CONTACT; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the "first contact" mode.
+ * \return true if "first contact" mode is on
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL FirstContactEnabled() const { return mFlags & OPC_FIRST_CONTACT; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the temporal coherence mode.
+ * \return true if temporal coherence is on
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL TemporalCoherenceEnabled() const { return mFlags & OPC_TEMPORAL_COHERENCE; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks a first contact has already been found.
+ * \return true if a first contact has been found and we can stop a query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL ContactFound() const { return (mFlags&OPC_CONTACT_FOUND)==OPC_CONTACT_FOUND; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks there's been an early exit due to temporal coherence;
+ * \return true if a temporal hit has occured
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL TemporalHit() const { return mFlags & OPC_TEMPORAL_HIT; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks primitive tests are enabled;
+ * \return true if primitive tests must be skipped
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL SkipPrimitiveTests() const { return mFlags & OPC_NO_PRIMITIVE_TESTS; }
+
+ // Settings
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Reports all contacts (false) or first contact only (true)
+ * \param flag [in] true for first contact, false for all contacts
+ * \see SetTemporalCoherence(bool flag)
+ * \see ValidateSettings()
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetFirstContact(bool flag)
+ {
+ if(flag) mFlags |= OPC_FIRST_CONTACT;
+ else mFlags &= ~OPC_FIRST_CONTACT;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Enable/disable temporal coherence.
+ * \param flag [in] true to enable temporal coherence, false to discard it
+ * \see SetFirstContact(bool flag)
+ * \see ValidateSettings()
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetTemporalCoherence(bool flag)
+ {
+ if(flag) mFlags |= OPC_TEMPORAL_COHERENCE;
+ else mFlags &= ~OPC_TEMPORAL_COHERENCE;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Enable/disable primitive tests.
+ * \param flag [in] true to enable primitive tests, false to discard them
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetPrimitiveTests(bool flag)
+ {
+ if(!flag) mFlags |= OPC_NO_PRIMITIVE_TESTS;
+ else mFlags &= ~OPC_NO_PRIMITIVE_TESTS;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Validates current settings. You should call this method after all the settings / callbacks have been defined for a collider.
+ * \return null if everything is ok, else a string describing the problem
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual const char* ValidateSettings() = 0;
+
+ protected:
+ udword mFlags; //!< Bit flags
+ const BaseModel* mCurrentModel; //!< Current model for collision query (owner of touched faces)
+ // User mesh interface
+ const MeshInterface* mIMesh; //!< User-defined mesh interface
+
+ // Internal methods
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups current collision model
+ * \param model [in] current collision model
+ * \return TRUE if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL Setup(const BaseModel* model)
+ {
+ // Keep track of current model
+ mCurrentModel = model;
+ if(!mCurrentModel) return FALSE;
+
+ mIMesh = model->GetMeshInterface();
+ return mIMesh!=null;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Initializes a query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual inline_ void InitQuery() { mFlags &= ~OPC_TEMPORAL_CONTACT; }
+ };
+
+#endif // __OPC_COLLIDER_H__
diff --git a/Opcode/OPC_Common.cpp b/Opcode/OPC_Common.cpp
new file mode 100644
index 0000000..839186b
--- /dev/null
+++ b/Opcode/OPC_Common.cpp
@@ -0,0 +1,48 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains common classes & defs used in OPCODE.
+ * \file OPC_Common.cpp
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * An AABB dedicated to collision detection.
+ * We don't use the generic AABB class included in ICE, since it can be a Min/Max or a Center/Extents one (depends
+ * on compilation flags). Since the Center/Extents model is more efficient in collision detection, it was worth
+ * using an extra special class.
+ *
+ * \class CollisionAABB
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A quantized AABB.
+ * Center/Extent model, using 16-bits integers.
+ *
+ * \class QuantizedAABB
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
diff --git a/Opcode/OPC_Common.h b/Opcode/OPC_Common.h
new file mode 100644
index 0000000..58c6253
--- /dev/null
+++ b/Opcode/OPC_Common.h
@@ -0,0 +1,101 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains common classes & defs used in OPCODE.
+ * \file OPC_Common.h
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_COMMON_H__
+#define __OPC_COMMON_H__
+
+// [GOTTFRIED]: Just a small change for readability.
+#ifdef OPC_CPU_COMPARE
+ #define GREATER(x, y) AIR(x) > IR(y)
+#else
+ #define GREATER(x, y) fabsf(x) > (y)
+#endif
+
+ class OPCODE_API CollisionAABB
+ {
+ public:
+ //! Constructor
+ inline_ CollisionAABB() {}
+ //! Constructor
+ inline_ CollisionAABB(const AABB& b) { b.GetCenter(mCenter); b.GetExtents(mExtents); }
+ //! Destructor
+ inline_ ~CollisionAABB() {}
+
+ //! Get min point of the box
+ inline_ void GetMin(Point& min) const { min = mCenter - mExtents; }
+ //! Get max point of the box
+ inline_ void GetMax(Point& max) const { max = mCenter + mExtents; }
+
+ //! Get component of the box's min point along a given axis
+ inline_ float GetMin(udword axis) const { return mCenter[axis] - mExtents[axis]; }
+ //! Get component of the box's max point along a given axis
+ inline_ float GetMax(udword axis) const { return mCenter[axis] + mExtents[axis]; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups an AABB from min & max vectors.
+ * \param min [in] the min point
+ * \param max [in] the max point
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetMinMax(const Point& min, const Point& max) { mCenter = (max + min)*0.5f; mExtents = (max - min)*0.5f; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks a box is inside another box.
+ * \param box [in] the other box
+ * \return true if current box is inside input box
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL IsInside(const CollisionAABB& box) const
+ {
+ if(box.GetMin(0)>GetMin(0)) return FALSE;
+ if(box.GetMin(1)>GetMin(1)) return FALSE;
+ if(box.GetMin(2)>GetMin(2)) return FALSE;
+ if(box.GetMax(0)<GetMax(0)) return FALSE;
+ if(box.GetMax(1)<GetMax(1)) return FALSE;
+ if(box.GetMax(2)<GetMax(2)) return FALSE;
+ return TRUE;
+ }
+
+ Point mCenter; //!< Box center
+ Point mExtents; //!< Box extents
+ };
+
+ class OPCODE_API QuantizedAABB
+ {
+ public:
+ //! Constructor
+ inline_ QuantizedAABB() {}
+ //! Destructor
+ inline_ ~QuantizedAABB() {}
+
+ sword mCenter[3]; //!< Quantized center
+ uword mExtents[3]; //!< Quantized extents
+ };
+
+ //! Quickly rotates & translates a vector
+ inline_ void TransformPoint(Point& dest, const Point& source, const Matrix3x3& rot, const Point& trans)
+ {
+ dest.x = trans.x + source.x * rot.m[0][0] + source.y * rot.m[1][0] + source.z * rot.m[2][0];
+ dest.y = trans.y + source.x * rot.m[0][1] + source.y * rot.m[1][1] + source.z * rot.m[2][1];
+ dest.z = trans.z + source.x * rot.m[0][2] + source.y * rot.m[1][2] + source.z * rot.m[2][2];
+ }
+
+#endif //__OPC_COMMON_H__ \ No newline at end of file
diff --git a/Opcode/OPC_HybridModel.cpp b/Opcode/OPC_HybridModel.cpp
new file mode 100644
index 0000000..f922f6d
--- /dev/null
+++ b/Opcode/OPC_HybridModel.cpp
@@ -0,0 +1,466 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for hybrid models.
+ * \file OPC_HybridModel.cpp
+ * \author Pierre Terdiman
+ * \date May, 18, 2003
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * An hybrid collision model.
+ *
+ * The problem :
+ *
+ * Opcode really shines for mesh-mesh collision, especially when meshes are deeply overlapping
+ * (it typically outperforms RAPID in those cases).
+ *
+ * Unfortunately this is not the typical scenario in games.
+ *
+ * For close-proximity cases, especially for volume-mesh queries, it's relatively easy to run faster
+ * than Opcode, that suffers from a relatively high setup time.
+ *
+ * In particular, Opcode's "vanilla" trees in those cases -can- run faster. They can also use -less-
+ * memory than the optimized ones, when you let the system stop at ~10 triangles / leaf for example
+ * (i.e. when you don't use "complete" trees). However, those trees tend to fragment memory quite a
+ * lot, increasing cache misses : since they're not "complete", we can't predict the final number of
+ * nodes and we have to allocate nodes on-the-fly. For the same reasons we can't use Opcode's "optimized"
+ * trees here, since they rely on a known layout to perform the "optimization".
+ *
+ * Hybrid trees :
+ *
+ * Hybrid trees try to combine best of both worlds :
+ *
+ * - they use a maximum limit of 16 triangles/leaf. "16" is used so that we'll be able to save the
+ * number of triangles using 4 bits only.
+ *
+ * - they're still "complete" trees thanks to a two-passes building phase. First we create a "vanilla"
+ * AABB-tree with Opcode, limited to 16 triangles/leaf. Then we create a *second* vanilla tree, this
+ * time using the leaves of the first one. The trick is : this second tree is now "complete"... so we
+ * can further transform it into an Opcode's optimized tree.
+ *
+ * - then we run the collision queries on that standard Opcode tree. The only difference is that leaf
+ * nodes contain indices to leaf nodes of another tree. Also, we have to skip all primitive tests in
+ * Opcode optimized trees, since our leaves don't contain triangles anymore.
+ *
+ * - finally, for each collided leaf, we simply loop through 16 triangles max, and collide them with
+ * the bounding volume used in the query (we only support volume-vs-mesh queries here, not mesh-vs-mesh)
+ *
+ * All of that is wrapped in this "hybrid model" that contains the minimal data required for this to work.
+ * It's a mix between old "vanilla" trees, and old "optimized" trees.
+ *
+ * Extra advantages:
+ *
+ * - If we use them for dynamic models, we're left with a very small number of leaf nodes to refit. It
+ * might be a bit faster since we have less nodes to write back.
+ *
+ * - In rigid body simulation, using temporal coherence and sleeping objects greatly reduce the actual
+ * influence of one tree over another (i.e. the speed difference is often invisible). So memory is really
+ * the key element to consider, and in this regard hybrid trees are just better.
+ *
+ * Information to take home:
+ * - they use less ram
+ * - they're not slower (they're faster or slower depending on cases, overall there's no significant
+ * difference *as long as objects don't interpenetrate too much* - in which case Opcode's optimized trees
+ * are still notably faster)
+ *
+ * \class HybridModel
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date May, 18, 2003
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridModel::HybridModel() :
+ mNbLeaves (0),
+ mNbPrimitives (0),
+ mTriangles (null),
+ mIndices (null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridModel::~HybridModel()
+{
+ Release();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Releases everything.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void HybridModel::Release()
+{
+ ReleaseBase();
+ DELETEARRAY(mIndices);
+ DELETEARRAY(mTriangles);
+ mNbLeaves = 0;
+ mNbPrimitives = 0;
+}
+
+ struct Internal
+ {
+ Internal()
+ {
+ mNbLeaves = 0;
+ mLeaves = null;
+ mTriangles = null;
+ mBase = null;
+ }
+ ~Internal()
+ {
+ DELETEARRAY(mLeaves);
+ }
+
+ udword mNbLeaves;
+ AABB* mLeaves;
+ LeafTriangles* mTriangles;
+ const udword* mBase;
+ };
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds a collision model.
+ * \param create [in] model creation structure
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool HybridModel::Build(const OPCODECREATE& create)
+{
+ // 1) Checkings
+ if(!create.mIMesh || !create.mIMesh->IsValid()) return false;
+
+ // Look for degenerate faces.
+ udword NbDegenerate = create.mIMesh->CheckTopology();
+ if(NbDegenerate) Log("OPCODE WARNING: found %d degenerate faces in model! Collision might report wrong results!\n", NbDegenerate);
+ // We continue nonetheless....
+
+ Release(); // Make sure previous tree has been discarded
+
+ // 1-1) Setup mesh interface automatically
+ SetMeshInterface(create.mIMesh);
+
+ bool Status = false;
+ AABBTree* LeafTree = null;
+ Internal Data;
+
+ // 2) Build a generic AABB Tree.
+ mSource = new AABBTree;
+ CHECKALLOC(mSource);
+
+ // 2-1) Setup a builder. Our primitives here are triangles from input mesh,
+ // so we use an AABBTreeOfTrianglesBuilder.....
+ {
+ AABBTreeOfTrianglesBuilder TB;
+ TB.mIMesh = create.mIMesh;
+ TB.mNbPrimitives = create.mIMesh->GetNbTriangles();
+ TB.mSettings = create.mSettings;
+ TB.mSettings.mLimit = 16; // ### Hardcoded, but maybe we could let the user choose 8 / 16 / 32 ...
+ if(!mSource->Build(&TB)) goto FreeAndExit;
+ }
+
+ // 2-2) Here's the trick : create *another* AABB tree using the leaves of the first one (which are boxes, this time)
+ struct Local
+ {
+ // A callback to count leaf nodes
+ static bool CountLeaves(const AABBTreeNode* current, udword depth, void* user_data)
+ {
+ if(current->IsLeaf())
+ {
+ Internal* Data = (Internal*)user_data;
+ Data->mNbLeaves++;
+ }
+ return true;
+ }
+
+ // A callback to setup leaf nodes in our internal structures
+ static bool SetupLeafData(const AABBTreeNode* current, udword depth, void* user_data)
+ {
+ if(current->IsLeaf())
+ {
+ Internal* Data = (Internal*)user_data;
+
+ // Get current leaf's box
+ Data->mLeaves[Data->mNbLeaves] = *current->GetAABB();
+
+ // Setup leaf data
+ udword Index = (udword(current->GetPrimitives()) - udword(Data->mBase))/sizeof(udword);
+ Data->mTriangles[Data->mNbLeaves].SetData(current->GetNbPrimitives(), Index);
+
+ Data->mNbLeaves++;
+ }
+ return true;
+ }
+ };
+
+ // Walk the tree & count number of leaves
+ Data.mNbLeaves = 0;
+ mSource->Walk(Local::CountLeaves, &Data);
+ mNbLeaves = Data.mNbLeaves; // Keep track of it
+
+ // Special case for 1-leaf meshes
+ if(mNbLeaves==1)
+ {
+ mModelCode |= OPC_SINGLE_NODE;
+ Status = true;
+ goto FreeAndExit;
+ }
+
+ // Allocate our structures
+ Data.mLeaves = new AABB[Data.mNbLeaves]; CHECKALLOC(Data.mLeaves);
+ mTriangles = new LeafTriangles[Data.mNbLeaves]; CHECKALLOC(mTriangles);
+
+ // Walk the tree again & setup leaf data
+ Data.mTriangles = mTriangles;
+ Data.mBase = mSource->GetIndices();
+ Data.mNbLeaves = 0; // Reset for incoming walk
+ mSource->Walk(Local::SetupLeafData, &Data);
+
+ // Handle source indices
+ {
+ bool MustKeepIndices = true;
+ if(create.mCanRemap)
+ {
+ // We try to get rid of source indices (saving more ram!) by reorganizing triangle arrays...
+ // Remap can fail when we use callbacks => keep track of indices in that case (it still
+ // works, only using more memory)
+ if(create.mIMesh->RemapClient(mSource->GetNbPrimitives(), mSource->GetIndices()))
+ {
+ MustKeepIndices = false;
+ }
+ }
+
+ if(MustKeepIndices)
+ {
+ // Keep track of source indices (from vanilla tree)
+ mNbPrimitives = mSource->GetNbPrimitives();
+ mIndices = new udword[mNbPrimitives];
+ CopyMemory(mIndices, mSource->GetIndices(), mNbPrimitives*sizeof(udword));
+ }
+ }
+
+ // Now, create our optimized tree using previous leaf nodes
+ LeafTree = new AABBTree;
+ CHECKALLOC(LeafTree);
+ {
+ AABBTreeOfAABBsBuilder TB; // Now using boxes !
+ TB.mSettings = create.mSettings;
+ TB.mSettings.mLimit = 1; // We now want a complete tree so that we can "optimize" it
+ TB.mNbPrimitives = Data.mNbLeaves;
+ TB.mAABBArray = Data.mLeaves;
+ if(!LeafTree->Build(&TB)) goto FreeAndExit;
+ }
+
+ // 3) Create an optimized tree according to user-settings
+ if(!CreateTree(create.mNoLeaf, create.mQuantized)) goto FreeAndExit;
+
+ // 3-2) Create optimized tree
+ if(!mTree->Build(LeafTree)) goto FreeAndExit;
+
+ // Finally ok...
+ Status = true;
+
+FreeAndExit: // Allow me this one...
+ DELETESINGLE(LeafTree);
+
+ // 3-3) Delete generic tree if needed
+ if(!create.mKeepOriginal) DELETESINGLE(mSource);
+
+ return Status;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the number of bytes used by the tree.
+ * \return amount of bytes used
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword HybridModel::GetUsedBytes() const
+{
+ udword UsedBytes = 0;
+ if(mTree) UsedBytes += mTree->GetUsedBytes();
+ if(mIndices) UsedBytes += mNbPrimitives * sizeof(udword); // mIndices
+ if(mTriangles) UsedBytes += mNbLeaves * sizeof(LeafTriangles); // mTriangles
+ return UsedBytes;
+}
+
+inline_ void ComputeMinMax(Point& min, Point& max, const VertexPointers& vp)
+{
+ // Compute triangle's AABB = a leaf box
+#ifdef OPC_USE_FCOMI // a 15% speedup on my machine, not much
+ min.x = FCMin3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x);
+ max.x = FCMax3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x);
+
+ min.y = FCMin3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y);
+ max.y = FCMax3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y);
+
+ min.z = FCMin3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z);
+ max.z = FCMax3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z);
+#else
+ min = *vp.Vertex[0];
+ max = *vp.Vertex[0];
+ min.Min(*vp.Vertex[1]);
+ max.Max(*vp.Vertex[1]);
+ min.Min(*vp.Vertex[2]);
+ max.Max(*vp.Vertex[2]);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the collision model. This can be used to handle dynamic meshes. Usage is:
+ * 1. modify your mesh vertices (keep the topology constant!)
+ * 2. refit the tree (call this method)
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool HybridModel::Refit()
+{
+ if(!mIMesh) return false;
+ if(!mTree) return false;
+
+ if(IsQuantized()) return false;
+ if(HasLeafNodes()) return false;
+
+ const LeafTriangles* LT = GetLeafTriangles();
+ const udword* Indices = GetIndices();
+
+ // Bottom-up update
+ VertexPointers VP;
+ Point Min,Max;
+ Point Min_,Max_;
+ udword Index = mTree->GetNbNodes();
+ AABBNoLeafNode* Nodes = (AABBNoLeafNode*)((AABBNoLeafTree*)mTree)->GetNodes();
+ while(Index--)
+ {
+ AABBNoLeafNode& Current = Nodes[Index];
+
+ if(Current.HasPosLeaf())
+ {
+ const LeafTriangles& CurrentLeaf = LT[Current.GetPosPrimitive()];
+
+ Min.SetPlusInfinity();
+ Max.SetMinusInfinity();
+
+ Point TmpMin, TmpMax;
+
+ // Each leaf box has a set of triangles
+ udword NbTris = CurrentLeaf.GetNbTriangles();
+ if(Indices)
+ {
+ const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ mIMesh->GetTriangle(VP, *T++);
+ ComputeMinMax(TmpMin, TmpMax, VP);
+ Min.Min(TmpMin);
+ Max.Max(TmpMax);
+ }
+ }
+ else
+ {
+ udword BaseIndex = CurrentLeaf.GetTriangleIndex();
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ mIMesh->GetTriangle(VP, BaseIndex++);
+ ComputeMinMax(TmpMin, TmpMax, VP);
+ Min.Min(TmpMin);
+ Max.Max(TmpMax);
+ }
+ }
+ }
+ else
+ {
+ const CollisionAABB& CurrentBox = Current.GetPos()->mAABB;
+ CurrentBox.GetMin(Min);
+ CurrentBox.GetMax(Max);
+ }
+
+ if(Current.HasNegLeaf())
+ {
+ const LeafTriangles& CurrentLeaf = LT[Current.GetNegPrimitive()];
+
+ Min_.SetPlusInfinity();
+ Max_.SetMinusInfinity();
+
+ Point TmpMin, TmpMax;
+
+ // Each leaf box has a set of triangles
+ udword NbTris = CurrentLeaf.GetNbTriangles();
+ if(Indices)
+ {
+ const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ mIMesh->GetTriangle(VP, *T++);
+ ComputeMinMax(TmpMin, TmpMax, VP);
+ Min_.Min(TmpMin);
+ Max_.Max(TmpMax);
+ }
+ }
+ else
+ {
+ udword BaseIndex = CurrentLeaf.GetTriangleIndex();
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ mIMesh->GetTriangle(VP, BaseIndex++);
+ ComputeMinMax(TmpMin, TmpMax, VP);
+ Min_.Min(TmpMin);
+ Max_.Max(TmpMax);
+ }
+ }
+ }
+ else
+ {
+ const CollisionAABB& CurrentBox = Current.GetNeg()->mAABB;
+ CurrentBox.GetMin(Min_);
+ CurrentBox.GetMax(Max_);
+ }
+#ifdef OPC_USE_FCOMI
+ Min.x = FCMin2(Min.x, Min_.x);
+ Max.x = FCMax2(Max.x, Max_.x);
+ Min.y = FCMin2(Min.y, Min_.y);
+ Max.y = FCMax2(Max.y, Max_.y);
+ Min.z = FCMin2(Min.z, Min_.z);
+ Max.z = FCMax2(Max.z, Max_.z);
+#else
+ Min.Min(Min_);
+ Max.Max(Max_);
+#endif
+ Current.mAABB.SetMinMax(Min, Max);
+ }
+ return true;
+}
diff --git a/Opcode/OPC_HybridModel.h b/Opcode/OPC_HybridModel.h
new file mode 100644
index 0000000..7833a94
--- /dev/null
+++ b/Opcode/OPC_HybridModel.h
@@ -0,0 +1,106 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for hybrid models.
+ * \file OPC_HybridModel.h
+ * \author Pierre Terdiman
+ * \date May, 18, 2003
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_HYBRIDMODEL_H__
+#define __OPC_HYBRIDMODEL_H__
+
+ //! Leaf descriptor
+ struct LeafTriangles
+ {
+ udword Data; //!< Packed data
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets number of triangles in the leaf.
+ * \return number of triangles N, with 0 < N <= 16
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbTriangles() const { return (Data & 15)+1; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets triangle index for this leaf. Indexed model's array of indices retrieved with HybridModel::GetIndices()
+ * \return triangle index
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetTriangleIndex() const { return Data>>4; }
+ inline_ void SetData(udword nb, udword index) { ASSERT(nb>0 && nb<=16); nb--; Data = (index<<4)|(nb&15); }
+ };
+
+ class OPCODE_API HybridModel : public BaseModel
+ {
+ public:
+ // Constructor/Destructor
+ HybridModel();
+ virtual ~HybridModel();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Builds a collision model.
+ * \param create [in] model creation structure
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(BaseModel) bool Build(const OPCODECREATE& create);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the number of bytes used by the tree.
+ * \return amount of bytes used
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(BaseModel) udword GetUsedBytes() const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Refits the collision model. This can be used to handle dynamic meshes. Usage is:
+ * 1. modify your mesh vertices (keep the topology constant!)
+ * 2. refit the tree (call this method)
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(BaseModel) bool Refit();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets array of triangles.
+ * \return array of triangles
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ const LeafTriangles* GetLeafTriangles() const { return mTriangles; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets array of indices.
+ * \return array of indices
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ const udword* GetIndices() const { return mIndices; }
+
+ private:
+ udword mNbLeaves; //!< Number of leaf nodes in the model
+ LeafTriangles* mTriangles; //!< Array of mNbLeaves leaf descriptors
+ udword mNbPrimitives; //!< Number of primitives in the model
+ udword* mIndices; //!< Array of primitive indices
+
+ // Internal methods
+ void Release();
+ };
+
+#endif // __OPC_HYBRIDMODEL_H__
diff --git a/Opcode/OPC_IceHook.h b/Opcode/OPC_IceHook.h
new file mode 100644
index 0000000..8b97eaa
--- /dev/null
+++ b/Opcode/OPC_IceHook.h
@@ -0,0 +1,70 @@
+
+// Should be included by Opcode.h if needed
+
+ #define ICE_DONT_CHECK_COMPILER_OPTIONS
+
+ // From Windows...
+ typedef int BOOL;
+ #ifndef FALSE
+ #define FALSE 0
+ #endif
+
+ #ifndef TRUE
+ #define TRUE 1
+ #endif
+
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <assert.h>
+ #include <string.h>
+ #include <float.h>
+ #include <Math.h>
+
+ #ifndef ASSERT
+ #define ASSERT(exp) {}
+ #endif
+ #define ICE_COMPILE_TIME_ASSERT(exp) extern char ICE_Dummy[ (exp) ? 1 : -1 ]
+
+ #define Log {}
+ #define SetIceError false
+ #define EC_OUTOFMEMORY "Out of memory"
+
+ #include ".\Ice\IcePreprocessor.h"
+
+ #undef ICECORE_API
+ #define ICECORE_API OPCODE_API
+
+ #include ".\Ice\IceTypes.h"
+ #include ".\Ice\IceFPU.h"
+ #include ".\Ice\IceMemoryMacros.h"
+
+ namespace IceCore
+ {
+ #include ".\Ice\IceUtils.h"
+ #include ".\Ice\IceContainer.h"
+ #include ".\Ice\IcePairs.h"
+ #include ".\Ice\IceRevisitedRadix.h"
+ #include ".\Ice\IceRandom.h"
+ }
+ using namespace IceCore;
+
+ #define ICEMATHS_API OPCODE_API
+ namespace IceMaths
+ {
+ #include ".\Ice\IceAxes.h"
+ #include ".\Ice\IcePoint.h"
+ #include ".\Ice\IceHPoint.h"
+ #include ".\Ice\IceMatrix3x3.h"
+ #include ".\Ice\IceMatrix4x4.h"
+ #include ".\Ice\IcePlane.h"
+ #include ".\Ice\IceRay.h"
+ #include ".\Ice\IceIndexedTriangle.h"
+ #include ".\Ice\IceTriangle.h"
+ #include ".\Ice\IceTriList.h"
+ #include ".\Ice\IceAABB.h"
+ #include ".\Ice\IceOBB.h"
+ #include ".\Ice\IceBoundingSphere.h"
+ #include ".\Ice\IceSegment.h"
+ #include ".\Ice\IceLSS.h"
+ }
+ using namespace IceMaths;
diff --git a/Opcode/OPC_LSSAABBOverlap.h b/Opcode/OPC_LSSAABBOverlap.h
new file mode 100644
index 0000000..c36b250
--- /dev/null
+++ b/Opcode/OPC_LSSAABBOverlap.h
@@ -0,0 +1,523 @@
+
+// Following code from Magic-Software (http://www.magic-software.com/)
+// A bit modified for Opcode
+
+inline_ float OPC_PointAABBSqrDist(const Point& point, const Point& center, const Point& extents)
+{
+ // Compute coordinates of point in box coordinate system
+ Point Closest = point - center;
+
+ float SqrDistance = 0.0f;
+
+ if(Closest.x < -extents.x)
+ {
+ float Delta = Closest.x + extents.x;
+ SqrDistance += Delta*Delta;
+ }
+ else if(Closest.x > extents.x)
+ {
+ float Delta = Closest.x - extents.x;
+ SqrDistance += Delta*Delta;
+ }
+
+ if(Closest.y < -extents.y)
+ {
+ float Delta = Closest.y + extents.y;
+ SqrDistance += Delta*Delta;
+ }
+ else if(Closest.y > extents.y)
+ {
+ float Delta = Closest.y - extents.y;
+ SqrDistance += Delta*Delta;
+ }
+
+ if(Closest.z < -extents.z)
+ {
+ float Delta = Closest.z + extents.z;
+ SqrDistance += Delta*Delta;
+ }
+ else if(Closest.z > extents.z)
+ {
+ float Delta = Closest.z - extents.z;
+ SqrDistance += Delta*Delta;
+ }
+ return SqrDistance;
+}
+
+static void Face(int i0, int i1, int i2, Point& rkPnt, const Point& rkDir, const Point& extents, const Point& rkPmE, float* pfLParam, float& rfSqrDistance)
+{
+ Point kPpE;
+ float fLSqr, fInv, fTmp, fParam, fT, fDelta;
+
+ kPpE[i1] = rkPnt[i1] + extents[i1];
+ kPpE[i2] = rkPnt[i2] + extents[i2];
+ if(rkDir[i0]*kPpE[i1] >= rkDir[i1]*rkPmE[i0])
+ {
+ if(rkDir[i0]*kPpE[i2] >= rkDir[i2]*rkPmE[i0])
+ {
+ // v[i1] >= -e[i1], v[i2] >= -e[i2] (distance = 0)
+ if(pfLParam)
+ {
+ rkPnt[i0] = extents[i0];
+ fInv = 1.0f/rkDir[i0];
+ rkPnt[i1] -= rkDir[i1]*rkPmE[i0]*fInv;
+ rkPnt[i2] -= rkDir[i2]*rkPmE[i0]*fInv;
+ *pfLParam = -rkPmE[i0]*fInv;
+ }
+ }
+ else
+ {
+ // v[i1] >= -e[i1], v[i2] < -e[i2]
+ fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i2]*rkDir[i2];
+ fTmp = fLSqr*kPpE[i1] - rkDir[i1]*(rkDir[i0]*rkPmE[i0] + rkDir[i2]*kPpE[i2]);
+ if(fTmp <= 2.0f*fLSqr*extents[i1])
+ {
+ fT = fTmp/fLSqr;
+ fLSqr += rkDir[i1]*rkDir[i1];
+ fTmp = kPpE[i1] - fT;
+ fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*fTmp + rkDir[i2]*kPpE[i2];
+ fParam = -fDelta/fLSqr;
+ rfSqrDistance += rkPmE[i0]*rkPmE[i0] + fTmp*fTmp + kPpE[i2]*kPpE[i2] + fDelta*fParam;
+
+ if(pfLParam)
+ {
+ *pfLParam = fParam;
+ rkPnt[i0] = extents[i0];
+ rkPnt[i1] = fT - extents[i1];
+ rkPnt[i2] = -extents[i2];
+ }
+ }
+ else
+ {
+ fLSqr += rkDir[i1]*rkDir[i1];
+ fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*rkPmE[i1] + rkDir[i2]*kPpE[i2];
+ fParam = -fDelta/fLSqr;
+ rfSqrDistance += rkPmE[i0]*rkPmE[i0] + rkPmE[i1]*rkPmE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam;
+
+ if(pfLParam)
+ {
+ *pfLParam = fParam;
+ rkPnt[i0] = extents[i0];
+ rkPnt[i1] = extents[i1];
+ rkPnt[i2] = -extents[i2];
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( rkDir[i0]*kPpE[i2] >= rkDir[i2]*rkPmE[i0] )
+ {
+ // v[i1] < -e[i1], v[i2] >= -e[i2]
+ fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1];
+ fTmp = fLSqr*kPpE[i2] - rkDir[i2]*(rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1]);
+ if(fTmp <= 2.0f*fLSqr*extents[i2])
+ {
+ fT = fTmp/fLSqr;
+ fLSqr += rkDir[i2]*rkDir[i2];
+ fTmp = kPpE[i2] - fT;
+ fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*fTmp;
+ fParam = -fDelta/fLSqr;
+ rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + fTmp*fTmp + fDelta*fParam;
+
+ if(pfLParam)
+ {
+ *pfLParam = fParam;
+ rkPnt[i0] = extents[i0];
+ rkPnt[i1] = -extents[i1];
+ rkPnt[i2] = fT - extents[i2];
+ }
+ }
+ else
+ {
+ fLSqr += rkDir[i2]*rkDir[i2];
+ fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*rkPmE[i2];
+ fParam = -fDelta/fLSqr;
+ rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + rkPmE[i2]*rkPmE[i2] + fDelta*fParam;
+
+ if(pfLParam)
+ {
+ *pfLParam = fParam;
+ rkPnt[i0] = extents[i0];
+ rkPnt[i1] = -extents[i1];
+ rkPnt[i2] = extents[i2];
+ }
+ }
+ }
+ else
+ {
+ // v[i1] < -e[i1], v[i2] < -e[i2]
+ fLSqr = rkDir[i0]*rkDir[i0]+rkDir[i2]*rkDir[i2];
+ fTmp = fLSqr*kPpE[i1] - rkDir[i1]*(rkDir[i0]*rkPmE[i0] + rkDir[i2]*kPpE[i2]);
+ if(fTmp >= 0.0f)
+ {
+ // v[i1]-edge is closest
+ if ( fTmp <= 2.0f*fLSqr*extents[i1] )
+ {
+ fT = fTmp/fLSqr;
+ fLSqr += rkDir[i1]*rkDir[i1];
+ fTmp = kPpE[i1] - fT;
+ fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*fTmp + rkDir[i2]*kPpE[i2];
+ fParam = -fDelta/fLSqr;
+ rfSqrDistance += rkPmE[i0]*rkPmE[i0] + fTmp*fTmp + kPpE[i2]*kPpE[i2] + fDelta*fParam;
+
+ if(pfLParam)
+ {
+ *pfLParam = fParam;
+ rkPnt[i0] = extents[i0];
+ rkPnt[i1] = fT - extents[i1];
+ rkPnt[i2] = -extents[i2];
+ }
+ }
+ else
+ {
+ fLSqr += rkDir[i1]*rkDir[i1];
+ fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*rkPmE[i1] + rkDir[i2]*kPpE[i2];
+ fParam = -fDelta/fLSqr;
+ rfSqrDistance += rkPmE[i0]*rkPmE[i0] + rkPmE[i1]*rkPmE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam;
+
+ if(pfLParam)
+ {
+ *pfLParam = fParam;
+ rkPnt[i0] = extents[i0];
+ rkPnt[i1] = extents[i1];
+ rkPnt[i2] = -extents[i2];
+ }
+ }
+ return;
+ }
+
+ fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1];
+ fTmp = fLSqr*kPpE[i2] - rkDir[i2]*(rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1]);
+ if(fTmp >= 0.0f)
+ {
+ // v[i2]-edge is closest
+ if(fTmp <= 2.0f*fLSqr*extents[i2])
+ {
+ fT = fTmp/fLSqr;
+ fLSqr += rkDir[i2]*rkDir[i2];
+ fTmp = kPpE[i2] - fT;
+ fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*fTmp;
+ fParam = -fDelta/fLSqr;
+ rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + fTmp*fTmp + fDelta*fParam;
+
+ if(pfLParam)
+ {
+ *pfLParam = fParam;
+ rkPnt[i0] = extents[i0];
+ rkPnt[i1] = -extents[i1];
+ rkPnt[i2] = fT - extents[i2];
+ }
+ }
+ else
+ {
+ fLSqr += rkDir[i2]*rkDir[i2];
+ fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*rkPmE[i2];
+ fParam = -fDelta/fLSqr;
+ rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + rkPmE[i2]*rkPmE[i2] + fDelta*fParam;
+
+ if(pfLParam)
+ {
+ *pfLParam = fParam;
+ rkPnt[i0] = extents[i0];
+ rkPnt[i1] = -extents[i1];
+ rkPnt[i2] = extents[i2];
+ }
+ }
+ return;
+ }
+
+ // (v[i1],v[i2])-corner is closest
+ fLSqr += rkDir[i2]*rkDir[i2];
+ fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*kPpE[i2];
+ fParam = -fDelta/fLSqr;
+ rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam;
+
+ if(pfLParam)
+ {
+ *pfLParam = fParam;
+ rkPnt[i0] = extents[i0];
+ rkPnt[i1] = -extents[i1];
+ rkPnt[i2] = -extents[i2];
+ }
+ }
+ }
+}
+
+static void CaseNoZeros(Point& rkPnt, const Point& rkDir, const Point& extents, float* pfLParam, float& rfSqrDistance)
+{
+ Point kPmE(rkPnt.x - extents.x, rkPnt.y - extents.y, rkPnt.z - extents.z);
+
+ float fProdDxPy, fProdDyPx, fProdDzPx, fProdDxPz, fProdDzPy, fProdDyPz;
+
+ fProdDxPy = rkDir.x*kPmE.y;
+ fProdDyPx = rkDir.y*kPmE.x;
+ if(fProdDyPx >= fProdDxPy)
+ {
+ fProdDzPx = rkDir.z*kPmE.x;
+ fProdDxPz = rkDir.x*kPmE.z;
+ if(fProdDzPx >= fProdDxPz)
+ {
+ // line intersects x = e0
+ Face(0, 1, 2, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance);
+ }
+ else
+ {
+ // line intersects z = e2
+ Face(2, 0, 1, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance);
+ }
+ }
+ else
+ {
+ fProdDzPy = rkDir.z*kPmE.y;
+ fProdDyPz = rkDir.y*kPmE.z;
+ if(fProdDzPy >= fProdDyPz)
+ {
+ // line intersects y = e1
+ Face(1, 2, 0, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance);
+ }
+ else
+ {
+ // line intersects z = e2
+ Face(2, 0, 1, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance);
+ }
+ }
+}
+
+static void Case0(int i0, int i1, int i2, Point& rkPnt, const Point& rkDir, const Point& extents, float* pfLParam, float& rfSqrDistance)
+{
+ float fPmE0 = rkPnt[i0] - extents[i0];
+ float fPmE1 = rkPnt[i1] - extents[i1];
+ float fProd0 = rkDir[i1]*fPmE0;
+ float fProd1 = rkDir[i0]*fPmE1;
+ float fDelta, fInvLSqr, fInv;
+
+ if(fProd0 >= fProd1)
+ {
+ // line intersects P[i0] = e[i0]
+ rkPnt[i0] = extents[i0];
+
+ float fPpE1 = rkPnt[i1] + extents[i1];
+ fDelta = fProd0 - rkDir[i0]*fPpE1;
+ if(fDelta >= 0.0f)
+ {
+ fInvLSqr = 1.0f/(rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]);
+ rfSqrDistance += fDelta*fDelta*fInvLSqr;
+ if(pfLParam)
+ {
+ rkPnt[i1] = -extents[i1];
+ *pfLParam = -(rkDir[i0]*fPmE0+rkDir[i1]*fPpE1)*fInvLSqr;
+ }
+ }
+ else
+ {
+ if(pfLParam)
+ {
+ fInv = 1.0f/rkDir[i0];
+ rkPnt[i1] -= fProd0*fInv;
+ *pfLParam = -fPmE0*fInv;
+ }
+ }
+ }
+ else
+ {
+ // line intersects P[i1] = e[i1]
+ rkPnt[i1] = extents[i1];
+
+ float fPpE0 = rkPnt[i0] + extents[i0];
+ fDelta = fProd1 - rkDir[i1]*fPpE0;
+ if(fDelta >= 0.0f)
+ {
+ fInvLSqr = 1.0f/(rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]);
+ rfSqrDistance += fDelta*fDelta*fInvLSqr;
+ if(pfLParam)
+ {
+ rkPnt[i0] = -extents[i0];
+ *pfLParam = -(rkDir[i0]*fPpE0+rkDir[i1]*fPmE1)*fInvLSqr;
+ }
+ }
+ else
+ {
+ if(pfLParam)
+ {
+ fInv = 1.0f/rkDir[i1];
+ rkPnt[i0] -= fProd1*fInv;
+ *pfLParam = -fPmE1*fInv;
+ }
+ }
+ }
+
+ if(rkPnt[i2] < -extents[i2])
+ {
+ fDelta = rkPnt[i2] + extents[i2];
+ rfSqrDistance += fDelta*fDelta;
+ rkPnt[i2] = -extents[i2];
+ }
+ else if ( rkPnt[i2] > extents[i2] )
+ {
+ fDelta = rkPnt[i2] - extents[i2];
+ rfSqrDistance += fDelta*fDelta;
+ rkPnt[i2] = extents[i2];
+ }
+}
+
+static void Case00(int i0, int i1, int i2, Point& rkPnt, const Point& rkDir, const Point& extents, float* pfLParam, float& rfSqrDistance)
+{
+ float fDelta;
+
+ if(pfLParam)
+ *pfLParam = (extents[i0] - rkPnt[i0])/rkDir[i0];
+
+ rkPnt[i0] = extents[i0];
+
+ if(rkPnt[i1] < -extents[i1])
+ {
+ fDelta = rkPnt[i1] + extents[i1];
+ rfSqrDistance += fDelta*fDelta;
+ rkPnt[i1] = -extents[i1];
+ }
+ else if(rkPnt[i1] > extents[i1])
+ {
+ fDelta = rkPnt[i1] - extents[i1];
+ rfSqrDistance += fDelta*fDelta;
+ rkPnt[i1] = extents[i1];
+ }
+
+ if(rkPnt[i2] < -extents[i2])
+ {
+ fDelta = rkPnt[i2] + extents[i2];
+ rfSqrDistance += fDelta*fDelta;
+ rkPnt[i1] = -extents[i2];
+ }
+ else if(rkPnt[i2] > extents[i2])
+ {
+ fDelta = rkPnt[i2] - extents[i2];
+ rfSqrDistance += fDelta*fDelta;
+ rkPnt[i2] = extents[i2];
+ }
+}
+
+static void Case000(Point& rkPnt, const Point& extents, float& rfSqrDistance)
+{
+ float fDelta;
+
+ if(rkPnt.x < -extents.x)
+ {
+ fDelta = rkPnt.x + extents.x;
+ rfSqrDistance += fDelta*fDelta;
+ rkPnt.x = -extents.x;
+ }
+ else if(rkPnt.x > extents.x)
+ {
+ fDelta = rkPnt.x - extents.x;
+ rfSqrDistance += fDelta*fDelta;
+ rkPnt.x = extents.x;
+ }
+
+ if(rkPnt.y < -extents.y)
+ {
+ fDelta = rkPnt.y + extents.y;
+ rfSqrDistance += fDelta*fDelta;
+ rkPnt.y = -extents.y;
+ }
+ else if(rkPnt.y > extents.y)
+ {
+ fDelta = rkPnt.y - extents.y;
+ rfSqrDistance += fDelta*fDelta;
+ rkPnt.y = extents.y;
+ }
+
+ if(rkPnt.z < -extents.z)
+ {
+ fDelta = rkPnt.z + extents.z;
+ rfSqrDistance += fDelta*fDelta;
+ rkPnt.z = -extents.z;
+ }
+ else if(rkPnt.z > extents.z)
+ {
+ fDelta = rkPnt.z - extents.z;
+ rfSqrDistance += fDelta*fDelta;
+ rkPnt.z = extents.z;
+ }
+}
+
+static float SqrDistance(const Ray& rkLine, const Point& center, const Point& extents, float* pfLParam)
+{
+ // compute coordinates of line in box coordinate system
+ Point kDiff = rkLine.mOrig - center;
+ Point kPnt = kDiff;
+ Point kDir = rkLine.mDir;
+
+ // Apply reflections so that direction vector has nonnegative components.
+ bool bReflect[3];
+ for(int i=0;i<3;i++)
+ {
+ if(kDir[i]<0.0f)
+ {
+ kPnt[i] = -kPnt[i];
+ kDir[i] = -kDir[i];
+ bReflect[i] = true;
+ }
+ else
+ {
+ bReflect[i] = false;
+ }
+ }
+
+ float fSqrDistance = 0.0f;
+
+ if(kDir.x>0.0f)
+ {
+ if(kDir.y>0.0f)
+ {
+ if(kDir.z>0.0f) CaseNoZeros(kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,+,+)
+ else Case0(0, 1, 2, kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,+,0)
+ }
+ else
+ {
+ if(kDir.z>0.0f) Case0(0, 2, 1, kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,0,+)
+ else Case00(0, 1, 2, kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,0,0)
+ }
+ }
+ else
+ {
+ if(kDir.y>0.0f)
+ {
+ if(kDir.z>0.0f) Case0(1, 2, 0, kPnt, kDir, extents, pfLParam, fSqrDistance); // (0,+,+)
+ else Case00(1, 0, 2, kPnt, kDir, extents, pfLParam, fSqrDistance); // (0,+,0)
+ }
+ else
+ {
+ if(kDir.z>0.0f) Case00(2, 0, 1, kPnt, kDir, extents, pfLParam, fSqrDistance); // (0,0,+)
+ else
+ {
+ Case000(kPnt, extents, fSqrDistance); // (0,0,0)
+ if(pfLParam) *pfLParam = 0.0f;
+ }
+ }
+ }
+ return fSqrDistance;
+}
+
+inline_ float OPC_SegmentOBBSqrDist(const Segment& segment, const Point& c0, const Point& e0)
+{
+ float fLP;
+ float fSqrDistance = SqrDistance(Ray(segment.GetOrigin(), segment.ComputeDirection()), c0, e0, &fLP);
+ if(fLP>=0.0f)
+ {
+ if(fLP<=1.0f) return fSqrDistance;
+ else return OPC_PointAABBSqrDist(segment.mP1, c0, e0);
+ }
+ else return OPC_PointAABBSqrDist(segment.mP0, c0, e0);
+}
+
+inline_ BOOL LSSCollider::LSSAABBOverlap(const Point& center, const Point& extents)
+{
+ // Stats
+ mNbVolumeBVTests++;
+
+ float s2 = OPC_SegmentOBBSqrDist(mSeg, center, extents);
+ if(s2<mRadius2) return TRUE;
+
+ return FALSE;
+}
diff --git a/Opcode/OPC_LSSCollider.cpp b/Opcode/OPC_LSSCollider.cpp
new file mode 100644
index 0000000..c1f21a8
--- /dev/null
+++ b/Opcode/OPC_LSSCollider.cpp
@@ -0,0 +1,725 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for an LSS collider.
+ * \file OPC_LSSCollider.cpp
+ * \author Pierre Terdiman
+ * \date December, 28, 2002
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a lss-vs-tree collider.
+ *
+ * \class LSSCollider
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date December, 28, 2002
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+#include "OPC_LSSAABBOverlap.h"
+#include "OPC_LSSTriOverlap.h"
+
+#define SET_CONTACT(prim_index, flag) \
+ /* Set contact status */ \
+ mFlags |= flag; \
+ mTouchedPrimitives->Add(prim_index);
+
+//! LSS-triangle overlap test
+#define LSS_PRIM(prim_index, flag) \
+ /* Request vertices from the app */ \
+ VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
+ \
+ /* Perform LSS-tri overlap test */ \
+ if(LSSTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \
+ { \
+ SET_CONTACT(prim_index, flag) \
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+LSSCollider::LSSCollider()
+{
+// mCenter.Zero();
+// mRadius2 = 0.0f;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+LSSCollider::~LSSCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Generic collision query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - with GetNbTouchedPrimitives()
+ * - with GetTouchedPrimitives()
+ *
+ * \param cache [in/out] an lss cache
+ * \param lss [in] collision lss in local space
+ * \param model [in] Opcode model to collide with
+ * \param worldl [in] lss world matrix, or null
+ * \param worldm [in] model's world matrix, or null
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool LSSCollider::Collide(LSSCache& cache, const LSS& lss, const Model& model, const Matrix4x4* worldl, const Matrix4x4* worldm)
+{
+ // Checkings
+ if(!Setup(&model)) return false;
+
+ // Init collision query
+ if(InitQuery(cache, lss, worldl, worldm)) return true;
+
+ if(!model.HasLeafNodes())
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ }
+ else
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ }
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Initializes a collision query :
+ * - reset stats & contact status
+ * - setup matrices
+ * - check temporal coherence
+ *
+ * \param cache [in/out] an lss cache
+ * \param lss [in] lss in local space
+ * \param worldl [in] lss world matrix, or null
+ * \param worldm [in] model's world matrix, or null
+ * \return TRUE if we can return immediately
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+BOOL LSSCollider::InitQuery(LSSCache& cache, const LSS& lss, const Matrix4x4* worldl, const Matrix4x4* worldm)
+{
+ // 1) Call the base method
+ VolumeCollider::InitQuery();
+
+ // 2) Compute LSS in model space:
+ // - Precompute R^2
+ mRadius2 = lss.mRadius * lss.mRadius;
+ // - Compute segment
+ mSeg.mP0 = lss.mP0;
+ mSeg.mP1 = lss.mP1;
+ // -> to world space
+ if(worldl)
+ {
+ mSeg.mP0 *= *worldl;
+ mSeg.mP1 *= *worldl;
+ }
+ // -> to model space
+ if(worldm)
+ {
+ // Invert model matrix
+ Matrix4x4 InvWorldM;
+ InvertPRMatrix(InvWorldM, *worldm);
+
+ mSeg.mP0 *= InvWorldM;
+ mSeg.mP1 *= InvWorldM;
+ }
+
+ // 3) Setup destination pointer
+ mTouchedPrimitives = &cache.TouchedPrimitives;
+
+ // 4) Special case: 1-triangle meshes [Opcode 1.3]
+ if(mCurrentModel && mCurrentModel->HasSingleNode())
+ {
+ if(!SkipPrimitiveTests())
+ {
+ // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
+ mTouchedPrimitives->Reset();
+
+ // Perform overlap test between the unique triangle and the LSS (and set contact status if needed)
+ LSS_PRIM(udword(0), OPC_CONTACT)
+
+ // Return immediately regardless of status
+ return TRUE;
+ }
+ }
+
+ // 5) Check temporal coherence :
+ if(TemporalCoherenceEnabled())
+ {
+ // Here we use temporal coherence
+ // => check results from previous frame before performing the collision query
+ if(FirstContactEnabled())
+ {
+ // We're only interested in the first contact found => test the unique previously touched face
+ if(mTouchedPrimitives->GetNbEntries())
+ {
+ // Get index of previously touched face = the first entry in the array
+ udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0);
+
+ // Then reset the array:
+ // - if the overlap test below is successful, the index we'll get added back anyway
+ // - if it isn't, then the array should be reset anyway for the normal query
+ mTouchedPrimitives->Reset();
+
+ // Perform overlap test between the cached triangle and the LSS (and set contact status if needed)
+ LSS_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT)
+
+ // Return immediately if possible
+ if(GetContactStatus()) return TRUE;
+ }
+ // else no face has been touched during previous query
+ // => we'll have to perform a normal query
+ }
+ else
+ {
+ // We're interested in all contacts =>test the new real LSS N(ew) against the previous fat LSS P(revious):
+
+ // ### rewrite this
+
+ LSS Test(mSeg, lss.mRadius); // in model space
+ LSS Previous(cache.Previous, sqrtf(cache.Previous.mRadius));
+
+// if(cache.Previous.Contains(Test))
+ if(IsCacheValid(cache) && Previous.Contains(Test))
+ {
+ // - if N is included in P, return previous list
+ // => we simply leave the list (mTouchedFaces) unchanged
+
+ // Set contact status if needed
+ if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT;
+
+ // In any case we don't need to do a query
+ return TRUE;
+ }
+ else
+ {
+ // - else do the query using a fat N
+
+ // Reset cache since we'll about to perform a real query
+ mTouchedPrimitives->Reset();
+
+ // Make a fat sphere so that coherence will work for subsequent frames
+ mRadius2 *= cache.FatCoeff;
+// mRadius2 = (lss.mRadius * cache.FatCoeff)*(lss.mRadius * cache.FatCoeff);
+
+
+ // Update cache with query data (signature for cached faces)
+ cache.Previous.mP0 = mSeg.mP0;
+ cache.Previous.mP1 = mSeg.mP1;
+ cache.Previous.mRadius = mRadius2;
+ }
+ }
+ }
+ else
+ {
+ // Here we don't use temporal coherence => do a normal query
+ mTouchedPrimitives->Reset();
+ }
+
+ return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Collision query for vanilla AABB trees.
+ * \param cache [in/out] an lss cache
+ * \param lss [in] collision lss in world space
+ * \param tree [in] AABB tree
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool LSSCollider::Collide(LSSCache& cache, const LSS& lss, const AABBTree* tree)
+{
+ // This is typically called for a scene tree, full of -AABBs-, not full of triangles.
+ // So we don't really have "primitives" to deal with. Hence it doesn't work with
+ // "FirstContact" + "TemporalCoherence".
+ ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) );
+
+ // Checkings
+ if(!tree) return false;
+
+ // Init collision query
+ if(InitQuery(cache, lss)) return true;
+
+ // Perform collision query
+ _Collide(tree);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks the LSS completely contains the box. In which case we can end the query sooner.
+ * \param bc [in] box center
+ * \param be [in] box extents
+ * \return true if the LSS contains the whole box
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL LSSCollider::LSSContainsBox(const Point& bc, const Point& be)
+{
+ // Not implemented
+ return FALSE;
+}
+
+#define TEST_BOX_IN_LSS(center, extents) \
+ if(LSSContainsBox(center, extents)) \
+ { \
+ /* Set contact status */ \
+ mFlags |= OPC_CONTACT; \
+ _Dump(node); \
+ return; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void LSSCollider::_Collide(const AABBCollisionNode* node)
+{
+ // Perform LSS-AABB overlap test
+ if(!LSSAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
+
+ TEST_BOX_IN_LSS(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->IsLeaf())
+ {
+ LSS_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _Collide(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void LSSCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node)
+{
+ // Perform LSS-AABB overlap test
+ if(!LSSAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
+
+ TEST_BOX_IN_LSS(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->IsLeaf())
+ {
+ SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _CollideNoPrimitiveTest(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void LSSCollider::_Collide(const AABBQuantizedNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform LSS-AABB overlap test
+ if(!LSSAABBOverlap(Center, Extents)) return;
+
+ TEST_BOX_IN_LSS(Center, Extents)
+
+ if(node->IsLeaf())
+ {
+ LSS_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _Collide(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void LSSCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform LSS-AABB overlap test
+ if(!LSSAABBOverlap(Center, Extents)) return;
+
+ TEST_BOX_IN_LSS(Center, Extents)
+
+ if(node->IsLeaf())
+ {
+ SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _CollideNoPrimitiveTest(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void LSSCollider::_Collide(const AABBNoLeafNode* node)
+{
+ // Perform LSS-AABB overlap test
+ if(!LSSAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
+
+ TEST_BOX_IN_LSS(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->HasPosLeaf()) { LSS_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { LSS_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for no-leaf AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void LSSCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node)
+{
+ // Perform LSS-AABB overlap test
+ if(!LSSAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
+
+ TEST_BOX_IN_LSS(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void LSSCollider::_Collide(const AABBQuantizedNoLeafNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform LSS-AABB overlap test
+ if(!LSSAABBOverlap(Center, Extents)) return;
+
+ TEST_BOX_IN_LSS(Center, Extents)
+
+ if(node->HasPosLeaf()) { LSS_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { LSS_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized no-leaf AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void LSSCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform LSS-AABB overlap test
+ if(!LSSAABBOverlap(Center, Extents)) return;
+
+ TEST_BOX_IN_LSS(Center, Extents)
+
+ if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for vanilla AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void LSSCollider::_Collide(const AABBTreeNode* node)
+{
+ // Perform LSS-AABB overlap test
+ Point Center, Extents;
+ node->GetAABB()->GetCenter(Center);
+ node->GetAABB()->GetExtents(Extents);
+ if(!LSSAABBOverlap(Center, Extents)) return;
+
+ if(node->IsLeaf() || LSSContainsBox(Center, Extents))
+ {
+ mFlags |= OPC_CONTACT;
+ mTouchedPrimitives->Add(node->GetPrimitives(), node->GetNbPrimitives());
+ }
+ else
+ {
+ _Collide(node->GetPos());
+ _Collide(node->GetNeg());
+ }
+}
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridLSSCollider::HybridLSSCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridLSSCollider::~HybridLSSCollider()
+{
+}
+
+bool HybridLSSCollider::Collide(LSSCache& cache, const LSS& lss, const HybridModel& model, const Matrix4x4* worldl, const Matrix4x4* worldm)
+{
+ // We don't want primitive tests here!
+ mFlags |= OPC_NO_PRIMITIVE_TESTS;
+
+ // Checkings
+ if(!Setup(&model)) return false;
+
+ // Init collision query
+ if(InitQuery(cache, lss, worldl, worldm)) return true;
+
+ // Special case for 1-leaf trees
+ if(mCurrentModel && mCurrentModel->HasSingleNode())
+ {
+ // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles
+ udword Nb = mIMesh->GetNbTriangles();
+
+ // Loop through all triangles
+ for(udword i=0;i<Nb;i++)
+ {
+ LSS_PRIM(i, OPC_CONTACT)
+ }
+ return true;
+ }
+
+ // Override destination array since we're only going to get leaf boxes here
+ mTouchedBoxes.Reset();
+ mTouchedPrimitives = &mTouchedBoxes;
+
+ // Now, do the actual query against leaf boxes
+ if(!model.HasLeafNodes())
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ }
+ else
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ }
+
+ // We only have a list of boxes so far
+ if(GetContactStatus())
+ {
+ // Reset contact status, since it currently only reflects collisions with leaf boxes
+ Collider::InitQuery();
+
+ // Change dest container so that we can use built-in overlap tests and get collided primitives
+ cache.TouchedPrimitives.Reset();
+ mTouchedPrimitives = &cache.TouchedPrimitives;
+
+ // Read touched leaf boxes
+ udword Nb = mTouchedBoxes.GetNbEntries();
+ const udword* Touched = mTouchedBoxes.GetEntries();
+
+ const LeafTriangles* LT = model.GetLeafTriangles();
+ const udword* Indices = model.GetIndices();
+
+ // Loop through touched leaves
+ while(Nb--)
+ {
+ const LeafTriangles& CurrentLeaf = LT[*Touched++];
+
+ // Each leaf box has a set of triangles
+ udword NbTris = CurrentLeaf.GetNbTriangles();
+ if(Indices)
+ {
+ const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ udword TriangleIndex = *T++;
+ LSS_PRIM(TriangleIndex, OPC_CONTACT)
+ }
+ }
+ else
+ {
+ udword BaseIndex = CurrentLeaf.GetTriangleIndex();
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ udword TriangleIndex = BaseIndex++;
+ LSS_PRIM(TriangleIndex, OPC_CONTACT)
+ }
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/Opcode/OPC_LSSCollider.h b/Opcode/OPC_LSSCollider.h
new file mode 100644
index 0000000..7b69448
--- /dev/null
+++ b/Opcode/OPC_LSSCollider.h
@@ -0,0 +1,99 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for an LSS collider.
+ * \file OPC_LSSCollider.h
+ * \author Pierre Terdiman
+ * \date December, 28, 2002
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_LSSCOLLIDER_H__
+#define __OPC_LSSCOLLIDER_H__
+
+ struct OPCODE_API LSSCache : VolumeCache
+ {
+ LSSCache()
+ {
+ Previous.mP0 = Point(0.0f, 0.0f, 0.0f);
+ Previous.mP1 = Point(0.0f, 0.0f, 0.0f);
+ Previous.mRadius = 0.0f;
+ FatCoeff = 1.1f;
+ }
+
+ // Cached faces signature
+ LSS Previous; //!< LSS used when performing the query resulting in cached faces
+ // User settings
+ float FatCoeff; //!< mRadius2 multiplier used to create a fat LSS
+ };
+
+ class OPCODE_API LSSCollider : public VolumeCollider
+ {
+ public:
+ // Constructor / Destructor
+ LSSCollider();
+ virtual ~LSSCollider();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Generic collision query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - with GetNbTouchedPrimitives()
+ * - with GetTouchedPrimitives()
+ *
+ * \param cache [in/out] an lss cache
+ * \param lss [in] collision lss in local space
+ * \param model [in] Opcode model to collide with
+ * \param worldl [in] lss world matrix, or null
+ * \param worldm [in] model's world matrix, or null
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool Collide(LSSCache& cache, const LSS& lss, const Model& model, const Matrix4x4* worldl=null, const Matrix4x4* worldm=null);
+ //
+ bool Collide(LSSCache& cache, const LSS& lss, const AABBTree* tree);
+ protected:
+ // LSS in model space
+ Segment mSeg; //!< Segment
+ float mRadius2; //!< LSS radius squared
+ // Internal methods
+ void _Collide(const AABBCollisionNode* node);
+ void _Collide(const AABBNoLeafNode* node);
+ void _Collide(const AABBQuantizedNode* node);
+ void _Collide(const AABBQuantizedNoLeafNode* node);
+ void _Collide(const AABBTreeNode* node);
+ void _CollideNoPrimitiveTest(const AABBCollisionNode* node);
+ void _CollideNoPrimitiveTest(const AABBNoLeafNode* node);
+ void _CollideNoPrimitiveTest(const AABBQuantizedNode* node);
+ void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node);
+ // Overlap tests
+ inline_ BOOL LSSContainsBox(const Point& bc, const Point& be);
+ inline_ BOOL LSSAABBOverlap(const Point& center, const Point& extents);
+ inline_ BOOL LSSTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2);
+ // Init methods
+ BOOL InitQuery(LSSCache& cache, const LSS& lss, const Matrix4x4* worldl=null, const Matrix4x4* worldm=null);
+ };
+
+ class OPCODE_API HybridLSSCollider : public LSSCollider
+ {
+ public:
+ // Constructor / Destructor
+ HybridLSSCollider();
+ virtual ~HybridLSSCollider();
+
+ bool Collide(LSSCache& cache, const LSS& lss, const HybridModel& model, const Matrix4x4* worldl=null, const Matrix4x4* worldm=null);
+ protected:
+ Container mTouchedBoxes;
+ };
+
+#endif // __OPC_LSSCOLLIDER_H__
diff --git a/Opcode/OPC_LSSTriOverlap.h b/Opcode/OPC_LSSTriOverlap.h
new file mode 100644
index 0000000..728ef87
--- /dev/null
+++ b/Opcode/OPC_LSSTriOverlap.h
@@ -0,0 +1,679 @@
+// Following code from Magic-Software (http://www.magic-software.com/)
+// A bit modified for Opcode
+
+static const float gs_fTolerance = 1e-05f;
+
+static float OPC_PointTriangleSqrDist(const Point& point, const Point& p0, const Point& p1, const Point& p2)
+{
+ // Hook
+ Point TriEdge0 = p1 - p0;
+ Point TriEdge1 = p2 - p0;
+
+ Point kDiff = p0 - point;
+ float fA00 = TriEdge0.SquareMagnitude();
+ float fA01 = TriEdge0 | TriEdge1;
+ float fA11 = TriEdge1.SquareMagnitude();
+ float fB0 = kDiff | TriEdge0;
+ float fB1 = kDiff | TriEdge1;
+ float fC = kDiff.SquareMagnitude();
+ float fDet = fabsf(fA00*fA11 - fA01*fA01);
+ float fS = fA01*fB1-fA11*fB0;
+ float fT = fA01*fB0-fA00*fB1;
+ float fSqrDist;
+
+ if(fS + fT <= fDet)
+ {
+ if(fS < 0.0f)
+ {
+ if(fT < 0.0f) // region 4
+ {
+ if(fB0 < 0.0f)
+ {
+ if(-fB0 >= fA00) fSqrDist = fA00+2.0f*fB0+fC;
+ else fSqrDist = fB0*(-fB0/fA00)+fC;
+ }
+ else
+ {
+ if(fB1 >= 0.0f) fSqrDist = fC;
+ else if(-fB1 >= fA11) fSqrDist = fA11+2.0f*fB1+fC;
+ else fSqrDist = fB1*(-fB1/fA11)+fC;
+ }
+ }
+ else // region 3
+ {
+ if(fB1 >= 0.0f) fSqrDist = fC;
+ else if(-fB1 >= fA11) fSqrDist = fA11+2.0f*fB1+fC;
+ else fSqrDist = fB1*(-fB1/fA11)+fC;
+ }
+ }
+ else if(fT < 0.0f) // region 5
+ {
+ if(fB0 >= 0.0f) fSqrDist = fC;
+ else if(-fB0 >= fA00) fSqrDist = fA00+2.0f*fB0+fC;
+ else fSqrDist = fB0*(-fB0/fA00)+fC;
+ }
+ else // region 0
+ {
+ // minimum at interior point
+ if(fDet==0.0f)
+ {
+ fSqrDist = MAX_FLOAT;
+ }
+ else
+ {
+ float fInvDet = 1.0f/fDet;
+ fS *= fInvDet;
+ fT *= fInvDet;
+ fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC;
+ }
+ }
+ }
+ else
+ {
+ float fTmp0, fTmp1, fNumer, fDenom;
+
+ if(fS < 0.0f) // region 2
+ {
+ fTmp0 = fA01 + fB0;
+ fTmp1 = fA11 + fB1;
+ if(fTmp1 > fTmp0)
+ {
+ fNumer = fTmp1 - fTmp0;
+ fDenom = fA00-2.0f*fA01+fA11;
+ if(fNumer >= fDenom)
+ {
+ fSqrDist = fA00+2.0f*fB0+fC;
+ }
+ else
+ {
+ fS = fNumer/fDenom;
+ fT = 1.0f - fS;
+ fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC;
+ }
+ }
+ else
+ {
+ if(fTmp1 <= 0.0f) fSqrDist = fA11+2.0f*fB1+fC;
+ else if(fB1 >= 0.0f) fSqrDist = fC;
+ else fSqrDist = fB1*(-fB1/fA11)+fC;
+ }
+ }
+ else if(fT < 0.0f) // region 6
+ {
+ fTmp0 = fA01 + fB1;
+ fTmp1 = fA00 + fB0;
+ if(fTmp1 > fTmp0)
+ {
+ fNumer = fTmp1 - fTmp0;
+ fDenom = fA00-2.0f*fA01+fA11;
+ if(fNumer >= fDenom)
+ {
+ fSqrDist = fA11+2.0f*fB1+fC;
+ }
+ else
+ {
+ fT = fNumer/fDenom;
+ fS = 1.0f - fT;
+ fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC;
+ }
+ }
+ else
+ {
+ if(fTmp1 <= 0.0f) fSqrDist = fA00+2.0f*fB0+fC;
+ else if(fB0 >= 0.0f) fSqrDist = fC;
+ else fSqrDist = fB0*(-fB0/fA00)+fC;
+ }
+ }
+ else // region 1
+ {
+ fNumer = fA11 + fB1 - fA01 - fB0;
+ if(fNumer <= 0.0f)
+ {
+ fSqrDist = fA11+2.0f*fB1+fC;
+ }
+ else
+ {
+ fDenom = fA00-2.0f*fA01+fA11;
+ if(fNumer >= fDenom)
+ {
+ fSqrDist = fA00+2.0f*fB0+fC;
+ }
+ else
+ {
+ fS = fNumer/fDenom;
+ fT = 1.0f - fS;
+ fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC;
+ }
+ }
+ }
+ }
+ return fabsf(fSqrDist);
+}
+
+static float OPC_SegmentSegmentSqrDist(const Segment& rkSeg0, const Segment& rkSeg1)
+{
+ // Hook
+ Point rkSeg0Direction = rkSeg0.ComputeDirection();
+ Point rkSeg1Direction = rkSeg1.ComputeDirection();
+
+ Point kDiff = rkSeg0.mP0 - rkSeg1.mP0;
+ float fA00 = rkSeg0Direction.SquareMagnitude();
+ float fA01 = -rkSeg0Direction.Dot(rkSeg1Direction);
+ float fA11 = rkSeg1Direction.SquareMagnitude();
+ float fB0 = kDiff.Dot(rkSeg0Direction);
+ float fC = kDiff.SquareMagnitude();
+ float fDet = fabsf(fA00*fA11-fA01*fA01);
+
+ float fB1, fS, fT, fSqrDist, fTmp;
+
+ if(fDet>=gs_fTolerance)
+ {
+ // line segments are not parallel
+ fB1 = -kDiff.Dot(rkSeg1Direction);
+ fS = fA01*fB1-fA11*fB0;
+ fT = fA01*fB0-fA00*fB1;
+
+ if(fS >= 0.0f)
+ {
+ if(fS <= fDet)
+ {
+ if(fT >= 0.0f)
+ {
+ if(fT <= fDet) // region 0 (interior)
+ {
+ // minimum at two interior points of 3D lines
+ float fInvDet = 1.0f/fDet;
+ fS *= fInvDet;
+ fT *= fInvDet;
+ fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC;
+ }
+ else // region 3 (side)
+ {
+ fTmp = fA01+fB0;
+ if(fTmp>=0.0f) fSqrDist = fA11+2.0f*fB1+fC;
+ else if(-fTmp>=fA00) fSqrDist = fA00+fA11+fC+2.0f*(fB1+fTmp);
+ else fSqrDist = fTmp*(-fTmp/fA00)+fA11+2.0f*fB1+fC;
+ }
+ }
+ else // region 7 (side)
+ {
+ if(fB0>=0.0f) fSqrDist = fC;
+ else if(-fB0>=fA00) fSqrDist = fA00+2.0f*fB0+fC;
+ else fSqrDist = fB0*(-fB0/fA00)+fC;
+ }
+ }
+ else
+ {
+ if ( fT >= 0.0 )
+ {
+ if ( fT <= fDet ) // region 1 (side)
+ {
+ fTmp = fA01+fB1;
+ if(fTmp>=0.0f) fSqrDist = fA00+2.0f*fB0+fC;
+ else if(-fTmp>=fA11) fSqrDist = fA00+fA11+fC+2.0f*(fB0+fTmp);
+ else fSqrDist = fTmp*(-fTmp/fA11)+fA00+2.0f*fB0+fC;
+ }
+ else // region 2 (corner)
+ {
+ fTmp = fA01+fB0;
+ if ( -fTmp <= fA00 )
+ {
+ if(fTmp>=0.0f) fSqrDist = fA11+2.0f*fB1+fC;
+ else fSqrDist = fTmp*(-fTmp/fA00)+fA11+2.0f*fB1+fC;
+ }
+ else
+ {
+ fTmp = fA01+fB1;
+ if(fTmp>=0.0f) fSqrDist = fA00+2.0f*fB0+fC;
+ else if(-fTmp>=fA11) fSqrDist = fA00+fA11+fC+2.0f*(fB0+fTmp);
+ else fSqrDist = fTmp*(-fTmp/fA11)+fA00+2.0f*fB0+fC;
+ }
+ }
+ }
+ else // region 8 (corner)
+ {
+ if ( -fB0 < fA00 )
+ {
+ if(fB0>=0.0f) fSqrDist = fC;
+ else fSqrDist = fB0*(-fB0/fA00)+fC;
+ }
+ else
+ {
+ fTmp = fA01+fB1;
+ if(fTmp>=0.0f) fSqrDist = fA00+2.0f*fB0+fC;
+ else if(-fTmp>=fA11) fSqrDist = fA00+fA11+fC+2.0f*(fB0+fTmp);
+ else fSqrDist = fTmp*(-fTmp/fA11)+fA00+2.0f*fB0+fC;
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( fT >= 0.0f )
+ {
+ if ( fT <= fDet ) // region 5 (side)
+ {
+ if(fB1>=0.0f) fSqrDist = fC;
+ else if(-fB1>=fA11) fSqrDist = fA11+2.0f*fB1+fC;
+ else fSqrDist = fB1*(-fB1/fA11)+fC;
+ }
+ else // region 4 (corner)
+ {
+ fTmp = fA01+fB0;
+ if ( fTmp < 0.0f )
+ {
+ if(-fTmp>=fA00) fSqrDist = fA00+fA11+fC+2.0f*(fB1+fTmp);
+ else fSqrDist = fTmp*(-fTmp/fA00)+fA11+2.0f*fB1+fC;
+ }
+ else
+ {
+ if(fB1>=0.0f) fSqrDist = fC;
+ else if(-fB1>=fA11) fSqrDist = fA11+2.0f*fB1+fC;
+ else fSqrDist = fB1*(-fB1/fA11)+fC;
+ }
+ }
+ }
+ else // region 6 (corner)
+ {
+ if ( fB0 < 0.0f )
+ {
+ if(-fB0>=fA00) fSqrDist = fA00+2.0f*fB0+fC;
+ else fSqrDist = fB0*(-fB0/fA00)+fC;
+ }
+ else
+ {
+ if(fB1>=0.0f) fSqrDist = fC;
+ else if(-fB1>=fA11) fSqrDist = fA11+2.0f*fB1+fC;
+ else fSqrDist = fB1*(-fB1/fA11)+fC;
+ }
+ }
+ }
+ }
+ else
+ {
+ // line segments are parallel
+ if ( fA01 > 0.0f )
+ {
+ // direction vectors form an obtuse angle
+ if ( fB0 >= 0.0f )
+ {
+ fSqrDist = fC;
+ }
+ else if ( -fB0 <= fA00 )
+ {
+ fSqrDist = fB0*(-fB0/fA00)+fC;
+ }
+ else
+ {
+ fB1 = -kDiff.Dot(rkSeg1Direction);
+ fTmp = fA00+fB0;
+ if ( -fTmp >= fA01 )
+ {
+ fSqrDist = fA00+fA11+fC+2.0f*(fA01+fB0+fB1);
+ }
+ else
+ {
+ fT = -fTmp/fA01;
+ fSqrDist = fA00+2.0f*fB0+fC+fT*(fA11*fT+2.0f*(fA01+fB1));
+ }
+ }
+ }
+ else
+ {
+ // direction vectors form an acute angle
+ if ( -fB0 >= fA00 )
+ {
+ fSqrDist = fA00+2.0f*fB0+fC;
+ }
+ else if ( fB0 <= 0.0f )
+ {
+ fSqrDist = fB0*(-fB0/fA00)+fC;
+ }
+ else
+ {
+ fB1 = -kDiff.Dot(rkSeg1Direction);
+ if ( fB0 >= -fA01 )
+ {
+ fSqrDist = fA11+2.0f*fB1+fC;
+ }
+ else
+ {
+ fT = -fB0/fA01;
+ fSqrDist = fC+fT*(2.0f*fB1+fA11*fT);
+ }
+ }
+ }
+ }
+ return fabsf(fSqrDist);
+}
+
+inline_ float OPC_SegmentRaySqrDist(const Segment& rkSeg0, const Ray& rkSeg1)
+{
+ return OPC_SegmentSegmentSqrDist(rkSeg0, Segment(rkSeg1.mOrig, rkSeg1.mOrig + rkSeg1.mDir));
+}
+
+static float OPC_SegmentTriangleSqrDist(const Segment& segment, const Point& p0, const Point& p1, const Point& p2)
+{
+ // Hook
+ const Point TriEdge0 = p1 - p0;
+ const Point TriEdge1 = p2 - p0;
+
+ const Point& rkSegOrigin = segment.GetOrigin();
+ Point rkSegDirection = segment.ComputeDirection();
+
+ Point kDiff = p0 - rkSegOrigin;
+ float fA00 = rkSegDirection.SquareMagnitude();
+ float fA01 = -rkSegDirection.Dot(TriEdge0);
+ float fA02 = -rkSegDirection.Dot(TriEdge1);
+ float fA11 = TriEdge0.SquareMagnitude();
+ float fA12 = TriEdge0.Dot(TriEdge1);
+ float fA22 = TriEdge1.Dot(TriEdge1);
+ float fB0 = -kDiff.Dot(rkSegDirection);
+ float fB1 = kDiff.Dot(TriEdge0);
+ float fB2 = kDiff.Dot(TriEdge1);
+ float fCof00 = fA11*fA22-fA12*fA12;
+ float fCof01 = fA02*fA12-fA01*fA22;
+ float fCof02 = fA01*fA12-fA02*fA11;
+ float fDet = fA00*fCof00+fA01*fCof01+fA02*fCof02;
+
+ Ray kTriSeg;
+ Point kPt;
+ float fSqrDist, fSqrDist0;
+
+ if(fabsf(fDet)>=gs_fTolerance)
+ {
+ float fCof11 = fA00*fA22-fA02*fA02;
+ float fCof12 = fA02*fA01-fA00*fA12;
+ float fCof22 = fA00*fA11-fA01*fA01;
+ float fInvDet = 1.0f/fDet;
+ float fRhs0 = -fB0*fInvDet;
+ float fRhs1 = -fB1*fInvDet;
+ float fRhs2 = -fB2*fInvDet;
+
+ float fR = fCof00*fRhs0+fCof01*fRhs1+fCof02*fRhs2;
+ float fS = fCof01*fRhs0+fCof11*fRhs1+fCof12*fRhs2;
+ float fT = fCof02*fRhs0+fCof12*fRhs1+fCof22*fRhs2;
+
+ if ( fR < 0.0f )
+ {
+ if ( fS+fT <= 1.0f )
+ {
+ if ( fS < 0.0f )
+ {
+ if ( fT < 0.0f ) // region 4m
+ {
+ // min on face s=0 or t=0 or r=0
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge1;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge0;
+ fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ fSqrDist0 = OPC_PointTriangleSqrDist(rkSegOrigin, p0, p1, p2);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ }
+ else // region 3m
+ {
+ // min on face s=0 or r=0
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge1;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ fSqrDist0 = OPC_PointTriangleSqrDist(rkSegOrigin, p0, p1, p2);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ }
+ }
+ else if ( fT < 0.0f ) // region 5m
+ {
+ // min on face t=0 or r=0
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge0;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ fSqrDist0 = OPC_PointTriangleSqrDist(rkSegOrigin, p0, p1, p2);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ }
+ else // region 0m
+ {
+ // min on face r=0
+ fSqrDist = OPC_PointTriangleSqrDist(rkSegOrigin, p0, p1, p2);
+ }
+ }
+ else
+ {
+ if ( fS < 0.0f ) // region 2m
+ {
+ // min on face s=0 or s+t=1 or r=0
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge1;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ kTriSeg.mOrig = p1;
+ kTriSeg.mDir = TriEdge1-TriEdge0;
+ fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ fSqrDist0 = OPC_PointTriangleSqrDist(rkSegOrigin, p0, p1, p2);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ }
+ else if ( fT < 0.0f ) // region 6m
+ {
+ // min on face t=0 or s+t=1 or r=0
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge0;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ kTriSeg.mOrig = p1;
+ kTriSeg.mDir = TriEdge1-TriEdge0;
+ fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ fSqrDist0 = OPC_PointTriangleSqrDist(rkSegOrigin, p0, p1, p2);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ }
+ else // region 1m
+ {
+ // min on face s+t=1 or r=0
+ kTriSeg.mOrig = p1;
+ kTriSeg.mDir = TriEdge1-TriEdge0;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ fSqrDist0 = OPC_PointTriangleSqrDist(rkSegOrigin, p0, p1, p2);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ }
+ }
+ }
+ else if ( fR <= 1.0f )
+ {
+ if ( fS+fT <= 1.0f )
+ {
+ if ( fS < 0.0f )
+ {
+ if ( fT < 0.0f ) // region 4
+ {
+ // min on face s=0 or t=0
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge1;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge0;
+ fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ }
+ else // region 3
+ {
+ // min on face s=0
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge1;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ }
+ }
+ else if ( fT < 0.0f ) // region 5
+ {
+ // min on face t=0
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge0;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ }
+ else // region 0
+ {
+ // global minimum is interior, done
+ fSqrDist = fR*(fA00*fR+fA01*fS+fA02*fT+2.0f*fB0)
+ +fS*(fA01*fR+fA11*fS+fA12*fT+2.0f*fB1)
+ +fT*(fA02*fR+fA12*fS+fA22*fT+2.0f*fB2)
+ +kDiff.SquareMagnitude();
+ }
+ }
+ else
+ {
+ if ( fS < 0.0f ) // region 2
+ {
+ // min on face s=0 or s+t=1
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge1;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ kTriSeg.mOrig = p1;
+ kTriSeg.mDir = TriEdge1-TriEdge0;
+ fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ }
+ else if ( fT < 0.0f ) // region 6
+ {
+ // min on face t=0 or s+t=1
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge0;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ kTriSeg.mOrig = p1;
+ kTriSeg.mDir = TriEdge1-TriEdge0;
+ fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ }
+ else // region 1
+ {
+ // min on face s+t=1
+ kTriSeg.mOrig = p1;
+ kTriSeg.mDir = TriEdge1-TriEdge0;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ }
+ }
+ }
+ else // fR > 1
+ {
+ if ( fS+fT <= 1.0f )
+ {
+ if ( fS < 0.0f )
+ {
+ if ( fT < 0.0f ) // region 4p
+ {
+ // min on face s=0 or t=0 or r=1
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge1;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge0;
+ fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ kPt = rkSegOrigin+rkSegDirection;
+ fSqrDist0 = OPC_PointTriangleSqrDist(kPt, p0, p1, p2);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ }
+ else // region 3p
+ {
+ // min on face s=0 or r=1
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge1;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ kPt = rkSegOrigin+rkSegDirection;
+ fSqrDist0 = OPC_PointTriangleSqrDist(kPt, p0, p1, p2);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ }
+ }
+ else if ( fT < 0.0f ) // region 5p
+ {
+ // min on face t=0 or r=1
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge0;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ kPt = rkSegOrigin+rkSegDirection;
+ fSqrDist0 = OPC_PointTriangleSqrDist(kPt, p0, p1, p2);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ }
+ else // region 0p
+ {
+ // min face on r=1
+ kPt = rkSegOrigin+rkSegDirection;
+ fSqrDist = OPC_PointTriangleSqrDist(kPt, p0, p1, p2);
+ }
+ }
+ else
+ {
+ if ( fS < 0.0f ) // region 2p
+ {
+ // min on face s=0 or s+t=1 or r=1
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge1;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ kTriSeg.mOrig = p1;
+ kTriSeg.mDir = TriEdge1-TriEdge0;
+ fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ kPt = rkSegOrigin+rkSegDirection;
+ fSqrDist0 = OPC_PointTriangleSqrDist(kPt, p0, p1, p2);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ }
+ else if ( fT < 0.0f ) // region 6p
+ {
+ // min on face t=0 or s+t=1 or r=1
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge0;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ kTriSeg.mOrig = p1;
+ kTriSeg.mDir = TriEdge1-TriEdge0;
+ fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ kPt = rkSegOrigin+rkSegDirection;
+ fSqrDist0 = OPC_PointTriangleSqrDist(kPt, p0, p1, p2);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ }
+ else // region 1p
+ {
+ // min on face s+t=1 or r=1
+ kTriSeg.mOrig = p1;
+ kTriSeg.mDir = TriEdge1-TriEdge0;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ kPt = rkSegOrigin+rkSegDirection;
+ fSqrDist0 = OPC_PointTriangleSqrDist(kPt, p0, p1, p2);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ }
+ }
+ }
+ }
+ else
+ {
+ // segment and triangle are parallel
+ kTriSeg.mOrig = p0;
+ kTriSeg.mDir = TriEdge0;
+ fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg);
+
+ kTriSeg.mDir = TriEdge1;
+ fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+
+ kTriSeg.mOrig = p1;
+ kTriSeg.mDir = TriEdge1 - TriEdge0;
+ fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+
+ fSqrDist0 = OPC_PointTriangleSqrDist(rkSegOrigin, p0, p1, p2);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+
+ kPt = rkSegOrigin+rkSegDirection;
+ fSqrDist0 = OPC_PointTriangleSqrDist(kPt, p0, p1, p2);
+ if(fSqrDist0<fSqrDist) fSqrDist = fSqrDist0;
+ }
+ return fabsf(fSqrDist);
+}
+
+inline_ BOOL LSSCollider::LSSTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2)
+{
+ // Stats
+ mNbVolumePrimTests++;
+
+ float s2 = OPC_SegmentTriangleSqrDist(mSeg, vert0, vert1, vert2);
+ if(s2<mRadius2) return TRUE;
+ return FALSE;
+}
diff --git a/Opcode/OPC_MeshInterface.cpp b/Opcode/OPC_MeshInterface.cpp
new file mode 100644
index 0000000..3a1d653
--- /dev/null
+++ b/Opcode/OPC_MeshInterface.cpp
@@ -0,0 +1,299 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a mesh interface.
+ * \file OPC_MeshInterface.cpp
+ * \author Pierre Terdiman
+ * \date November, 27, 2002
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * This structure holds 3 vertex-pointers. It's mainly used by collision callbacks so that the app doesn't have
+ * to return 3 vertices to OPCODE (36 bytes) but only 3 pointers (12 bytes). It seems better but I never profiled
+ * the alternative.
+ *
+ * \class VertexPointers
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * This class is an interface between us and user-defined meshes. Meshes can be defined in a lot of ways, and here we
+ * try to support most of them.
+ *
+ * Basically you have two options:
+ * - callbacks, if OPC_USE_CALLBACKS is defined in OPC_Settings.h.
+ * - else pointers.
+ *
+ * If using pointers, you can also use strides or not. Strides are used when OPC_USE_STRIDE is defined.
+ *
+ *
+ * CALLBACKS:
+ *
+ * Using callbacks is the most generic way to feed OPCODE with your meshes. Indeed, you just have to give
+ * access to three vertices at the end of the day. It's up to you to fetch them from your database, using
+ * whatever method you want. Hence your meshes can lie in system memory or AGP, be indexed or not, use 16
+ * or 32-bits indices, you can decompress them on-the-fly if needed, etc. On the other hand, a callback is
+ * called each time OPCODE needs access to a particular triangle, so there might be a slight overhead.
+ *
+ * To make things clear: geometry & topology are NOT stored in the collision system,
+ * in order to save some ram. So, when the system needs them to perform accurate intersection
+ * tests, you're requested to provide the triangle-vertices corresponding to a given face index.
+ *
+ * Ex:
+ *
+ * \code
+ * static void ColCallback(udword triangle_index, VertexPointers& triangle, udword user_data)
+ * {
+ * // Get back Mesh0 or Mesh1 (you also can use 2 different callbacks)
+ * Mesh* MyMesh = (Mesh*)user_data;
+ * // Get correct triangle in the app-controlled database
+ * const Triangle* Tri = MyMesh->GetTriangle(triangle_index);
+ * // Setup pointers to vertices for the collision system
+ * triangle.Vertex[0] = MyMesh->GetVertex(Tri->mVRef[0]);
+ * triangle.Vertex[1] = MyMesh->GetVertex(Tri->mVRef[1]);
+ * triangle.Vertex[2] = MyMesh->GetVertex(Tri->mVRef[2]);
+ * }
+ *
+ * // Setup callbacks
+ * MeshInterface0->SetCallback(ColCallback, udword(Mesh0));
+ * MeshInterface1->SetCallback(ColCallback, udword(Mesh1));
+ * \endcode
+ *
+ * Of course, you should make this callback as fast as possible. And you're also not supposed
+ * to modify the geometry *after* the collision trees have been built. The alternative was to
+ * store the geometry & topology in the collision system as well (as in RAPID) but we have found
+ * this approach to waste a lot of ram in many cases.
+ *
+ *
+ * POINTERS:
+ *
+ * If you're internally using the following canonical structures:
+ * - a vertex made of three 32-bits floating point values
+ * - a triangle made of three 32-bits integer vertex references
+ * ...then you may want to use pointers instead of callbacks. This is the same, except OPCODE will directly
+ * use provided pointers to access the topology and geometry, without using a callback. It might be faster,
+ * but probably not as safe. Pointers have been introduced in OPCODE 1.2.
+ *
+ * Ex:
+ *
+ * \code
+ * // Setup pointers
+ * MeshInterface0->SetPointers(Mesh0->GetFaces(), Mesh0->GetVerts());
+ * MeshInterface1->SetPointers(Mesh1->GetFaces(), Mesh1->GetVerts());
+ * \endcode
+ *
+ *
+ * STRIDES:
+ *
+ * If your vertices are D3D-like entities interleaving a position, a normal and/or texture coordinates
+ * (i.e. if your vertices are FVFs), you might want to use a vertex stride to skip extra data OPCODE
+ * doesn't need. Using a stride shouldn't be notably slower than not using it, but it might increase
+ * cache misses. Please also note that you *shouldn't* read from AGP or video-memory buffers !
+ *
+ *
+ * In any case, compilation flags are here to select callbacks/pointers/strides at compile time, so
+ * choose what's best for your application. All of this has been wrapped into this MeshInterface.
+ *
+ * \class MeshInterface
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date November, 27, 2002
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+MeshInterface::MeshInterface() :
+#ifdef OPC_USE_CALLBACKS
+ mUserData (null),
+ mObjCallback (null),
+#else
+ mTris (null),
+ mVerts (null),
+ #ifdef OPC_USE_STRIDE
+ mTriStride (sizeof(IndexedTriangle)),
+ mVertexStride (sizeof(Point)),
+ #endif
+#endif
+ mNbTris (0),
+ mNbVerts (0)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+MeshInterface::~MeshInterface()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks the mesh interface is valid, i.e. things have been setup correctly.
+ * \return true if valid
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool MeshInterface::IsValid() const
+{
+ if(!mNbTris || !mNbVerts) return false;
+#ifdef OPC_USE_CALLBACKS
+ if(!mObjCallback) return false;
+#else
+ if(!mTris || !mVerts) return false;
+#endif
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks the mesh itself is valid.
+ * Currently we only look for degenerate faces.
+ * \return number of degenerate faces
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword MeshInterface::CheckTopology() const
+{
+ // Check topology. If the model contains degenerate faces, collision report can be wrong in some cases.
+ // e.g. it happens with the standard MAX teapot. So clean your meshes first... If you don't have a mesh cleaner
+ // you can try this: www.codercorner.com/Consolidation.zip
+
+ udword NbDegenerate = 0;
+
+ VertexPointers VP;
+
+ // Using callbacks, we don't have access to vertex indices. Nevertheless we still can check for
+ // redundant vertex pointers, which cover all possibilities (callbacks/pointers/strides).
+ for(udword i=0;i<mNbTris;i++)
+ {
+ GetTriangle(VP, i);
+
+ if( (VP.Vertex[0]==VP.Vertex[1])
+ || (VP.Vertex[1]==VP.Vertex[2])
+ || (VP.Vertex[2]==VP.Vertex[0])) NbDegenerate++;
+ }
+
+ return NbDegenerate;
+}
+
+#ifdef OPC_USE_CALLBACKS
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Callback control: setups object callback. Must provide triangle-vertices for a given triangle index.
+ * \param callback [in] user-defined callback
+ * \param user_data [in] user-defined data
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool MeshInterface::SetCallback(RequestCallback callback, void* user_data)
+{
+ if(!callback) return SetIceError("MeshInterface::SetCallback: callback pointer is null");
+
+ mObjCallback = callback;
+ mUserData = user_data;
+ return true;
+}
+#else
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Pointers control: setups object pointers. Must provide access to faces and vertices for a given object.
+ * \param tris [in] pointer to triangles
+ * \param verts [in] pointer to vertices
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool MeshInterface::SetPointers(const IndexedTriangle* tris, const Point* verts)
+{
+ if(!tris || !verts) return SetIceError("MeshInterface::SetPointers: pointer is null", null);
+
+ mTris = tris;
+ mVerts = verts;
+ return true;
+}
+#ifdef OPC_USE_STRIDE
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Strides control
+ * \param tri_stride [in] size of a triangle in bytes. The first sizeof(IndexedTriangle) bytes are used to get vertex indices.
+ * \param vertex_stride [in] size of a vertex in bytes. The first sizeof(Point) bytes are used to get vertex position.
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool MeshInterface::SetStrides(udword tri_stride, udword vertex_stride)
+{
+ if(tri_stride<sizeof(IndexedTriangle)) return SetIceError("MeshInterface::SetStrides: invalid triangle stride", null);
+ if(vertex_stride<sizeof(Point)) return SetIceError("MeshInterface::SetStrides: invalid vertex stride", null);
+
+ mTriStride = tri_stride;
+ mVertexStride = vertex_stride;
+ return true;
+}
+#endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Remaps client's mesh according to a permutation.
+ * \param nb_indices [in] number of indices in the permutation (will be checked against number of triangles)
+ * \param permutation [in] list of triangle indices
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool MeshInterface::RemapClient(udword nb_indices, const udword* permutation) const
+{
+ // Checkings
+ if(!nb_indices || !permutation) return false;
+ if(nb_indices!=mNbTris) return false;
+
+#ifdef OPC_USE_CALLBACKS
+ // We can't really do that using callbacks
+ return false;
+#else
+ IndexedTriangle* Tmp = new IndexedTriangle[mNbTris];
+ CHECKALLOC(Tmp);
+
+ #ifdef OPC_USE_STRIDE
+ udword Stride = mTriStride;
+ #else
+ udword Stride = sizeof(IndexedTriangle);
+ #endif
+
+ for(udword i=0;i<mNbTris;i++)
+ {
+ const IndexedTriangle* T = (const IndexedTriangle*)(((ubyte*)mTris) + i * Stride);
+ Tmp[i] = *T;
+ }
+
+ for(udword i=0;i<mNbTris;i++)
+ {
+ IndexedTriangle* T = (IndexedTriangle*)(((ubyte*)mTris) + i * Stride);
+ *T = Tmp[permutation[i]];
+ }
+
+ DELETEARRAY(Tmp);
+#endif
+ return true;
+}
diff --git a/Opcode/OPC_MeshInterface.h b/Opcode/OPC_MeshInterface.h
new file mode 100644
index 0000000..1318f7a
--- /dev/null
+++ b/Opcode/OPC_MeshInterface.h
@@ -0,0 +1,182 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a mesh interface.
+ * \file OPC_MeshInterface.h
+ * \author Pierre Terdiman
+ * \date November, 27, 2002
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_MESHINTERFACE_H__
+#define __OPC_MESHINTERFACE_H__
+
+ struct VertexPointers
+ {
+ const Point* Vertex[3];
+
+ bool BackfaceCulling(const Point& source)
+ {
+ const Point& p0 = *Vertex[0];
+ const Point& p1 = *Vertex[1];
+ const Point& p2 = *Vertex[2];
+
+ // Compute normal direction
+ Point Normal = (p2 - p1)^(p0 - p1);
+
+ // Backface culling
+ return (Normal | (source - p0)) >= 0.0f;
+ }
+ };
+
+#ifdef OPC_USE_CALLBACKS
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * User-callback, called by OPCODE to request vertices from the app.
+ * \param triangle_index [in] face index for which the system is requesting the vertices
+ * \param triangle [out] triangle's vertices (must be provided by the user)
+ * \param user_data [in] user-defined data from SetCallback()
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ typedef void (*RequestCallback) (udword triangle_index, VertexPointers& triangle, void* user_data);
+#endif
+
+ class OPCODE_API MeshInterface
+ {
+ public:
+ // Constructor / Destructor
+ MeshInterface();
+ ~MeshInterface();
+ // Common settings
+ inline_ udword GetNbTriangles() const { return mNbTris; }
+ inline_ udword GetNbVertices() const { return mNbVerts; }
+ inline_ void SetNbTriangles(udword nb) { mNbTris = nb; }
+ inline_ void SetNbVertices(udword nb) { mNbVerts = nb; }
+
+#ifdef OPC_USE_CALLBACKS
+ // Callback settings
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Callback control: setups object callback. Must provide triangle-vertices for a given triangle index.
+ * \param callback [in] user-defined callback
+ * \param user_data [in] user-defined data
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool SetCallback(RequestCallback callback, void* user_data);
+ inline_ void* GetUserData() const { return mUserData; }
+ inline_ RequestCallback GetCallback() const { return mObjCallback; }
+#else
+ // Pointers settings
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Pointers control: setups object pointers. Must provide access to faces and vertices for a given object.
+ * \param tris [in] pointer to triangles
+ * \param verts [in] pointer to vertices
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool SetPointers(const IndexedTriangle* tris, const Point* verts);
+ inline_ const IndexedTriangle* GetTris() const { return mTris; }
+ inline_ const Point* GetVerts() const { return mVerts; }
+
+ #ifdef OPC_USE_STRIDE
+ // Strides settings
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Strides control
+ * \param tri_stride [in] size of a triangle in bytes. The first sizeof(IndexedTriangle) bytes are used to get vertex indices.
+ * \param vertex_stride [in] size of a vertex in bytes. The first sizeof(Point) bytes are used to get vertex position.
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool SetStrides(udword tri_stride=sizeof(IndexedTriangle), udword vertex_stride=sizeof(Point));
+ inline_ udword GetTriStride() const { return mTriStride; }
+ inline_ udword GetVertexStride() const { return mVertexStride; }
+ #endif
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Fetches a triangle given a triangle index.
+ * \param vp [out] required triangle's vertex pointers
+ * \param index [in] triangle index
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void GetTriangle(VertexPointers& vp, udword index) const
+ {
+#ifdef OPC_USE_CALLBACKS
+ (mObjCallback)(index, vp, mUserData);
+#else
+ #ifdef OPC_USE_STRIDE
+ const IndexedTriangle* T = (const IndexedTriangle*)(((ubyte*)mTris) + index * mTriStride);
+ vp.Vertex[0] = (const Point*)(((ubyte*)mVerts) + T->mVRef[0] * mVertexStride);
+ vp.Vertex[1] = (const Point*)(((ubyte*)mVerts) + T->mVRef[1] * mVertexStride);
+ vp.Vertex[2] = (const Point*)(((ubyte*)mVerts) + T->mVRef[2] * mVertexStride);
+ #else
+ const IndexedTriangle* T = &mTris[index];
+ vp.Vertex[0] = &mVerts[T->mVRef[0]];
+ vp.Vertex[1] = &mVerts[T->mVRef[1]];
+ vp.Vertex[2] = &mVerts[T->mVRef[2]];
+ #endif
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Remaps client's mesh according to a permutation.
+ * \param nb_indices [in] number of indices in the permutation (will be checked against number of triangles)
+ * \param permutation [in] list of triangle indices
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ bool RemapClient(udword nb_indices, const udword* permutation) const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks the mesh interface is valid, i.e. things have been setup correctly.
+ * \return true if valid
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool IsValid() const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks the mesh itself is valid.
+ * Currently we only look for degenerate faces.
+ * \return number of degenerate faces
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ udword CheckTopology() const;
+ private:
+
+ udword mNbTris; //!< Number of triangles in the input model
+ udword mNbVerts; //!< Number of vertices in the input model
+#ifdef OPC_USE_CALLBACKS
+ // User callback
+ void* mUserData; //!< User-defined data sent to callback
+ RequestCallback mObjCallback; //!< Object callback
+#else
+ // User pointers
+ const IndexedTriangle* mTris; //!< Array of indexed triangles
+ const Point* mVerts; //!< Array of vertices
+ #ifdef OPC_USE_STRIDE
+ udword mTriStride; //!< Possible triangle stride in bytes [Opcode 1.3]
+ udword mVertexStride; //!< Possible vertex stride in bytes [Opcode 1.3]
+ #endif
+#endif
+ };
+
+#endif //__OPC_MESHINTERFACE_H__ \ No newline at end of file
diff --git a/Opcode/OPC_Model.cpp b/Opcode/OPC_Model.cpp
new file mode 100644
index 0000000..0616c4d
--- /dev/null
+++ b/Opcode/OPC_Model.cpp
@@ -0,0 +1,222 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for OPCODE models.
+ * \file OPC_Model.cpp
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * The main collision wrapper, for all trees. Supported trees are:
+ * - Normal trees (2*N-1 nodes, full size)
+ * - No-leaf trees (N-1 nodes, full size)
+ * - Quantized trees (2*N-1 nodes, half size)
+ * - Quantized no-leaf trees (N-1 nodes, half size)
+ *
+ * Usage:
+ *
+ * 1) Create a static mesh interface using callbacks or pointers. (see OPC_MeshInterface.cpp).
+ * Keep it around in your app, since a pointer to this interface is saved internally and
+ * used until you release the collision structures.
+ *
+ * 2) Build a Model using a creation structure:
+ *
+ * \code
+ * Model Sample;
+ *
+ * OPCODECREATE OPCC;
+ * OPCC.IMesh = ...;
+ * OPCC.Rules = ...;
+ * OPCC.NoLeaf = ...;
+ * OPCC.Quantized = ...;
+ * OPCC.KeepOriginal = ...;
+ * bool Status = Sample.Build(OPCC);
+ * \endcode
+ *
+ * 3) Create a tree collider and set it up:
+ *
+ * \code
+ * AABBTreeCollider TC;
+ * TC.SetFirstContact(...);
+ * TC.SetFullBoxBoxTest(...);
+ * TC.SetFullPrimBoxTest(...);
+ * TC.SetTemporalCoherence(...);
+ * \endcode
+ *
+ * 4) Perform a collision query
+ *
+ * \code
+ * // Setup cache
+ * static BVTCache ColCache;
+ * ColCache.Model0 = &Model0;
+ * ColCache.Model1 = &Model1;
+ *
+ * // Collision query
+ * bool IsOk = TC.Collide(ColCache, World0, World1);
+ *
+ * // Get collision status => if true, objects overlap
+ * BOOL Status = TC.GetContactStatus();
+ *
+ * // Number of colliding pairs and list of pairs
+ * udword NbPairs = TC.GetNbPairs();
+ * const Pair* p = TC.GetPairs()
+ * \endcode
+ *
+ * 5) Stats
+ *
+ * \code
+ * Model0.GetUsedBytes() = number of bytes used for this collision tree
+ * TC.GetNbBVBVTests() = number of BV-BV overlap tests performed during last query
+ * TC.GetNbPrimPrimTests() = number of Triangle-Triangle overlap tests performed during last query
+ * TC.GetNbBVPrimTests() = number of Triangle-BV overlap tests performed during last query
+ * \endcode
+ *
+ * \class Model
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Model::Model()
+{
+#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE !
+ mHull = null;
+#endif // __MESHMERIZER_H__
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Model::~Model()
+{
+ Release();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Releases the model.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Model::Release()
+{
+ ReleaseBase();
+#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE !
+ DELETESINGLE(mHull);
+#endif // __MESHMERIZER_H__
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds a collision model.
+ * \param create [in] model creation structure
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Model::Build(const OPCODECREATE& create)
+{
+ // 1) Checkings
+ if(!create.mIMesh || !create.mIMesh->IsValid()) return false;
+
+ // For this model, we only support complete trees
+ if(create.mSettings.mLimit!=1) return SetIceError("OPCODE WARNING: supports complete trees only! Use mLimit = 1.\n", null);
+
+ // Look for degenerate faces.
+ udword NbDegenerate = create.mIMesh->CheckTopology();
+ if(NbDegenerate) Log("OPCODE WARNING: found %d degenerate faces in model! Collision might report wrong results!\n", NbDegenerate);
+ // We continue nonetheless....
+
+ Release(); // Make sure previous tree has been discarded [Opcode 1.3, thanks Adam]
+
+ // 1-1) Setup mesh interface automatically [Opcode 1.3]
+ SetMeshInterface(create.mIMesh);
+
+ // Special case for 1-triangle meshes [Opcode 1.3]
+ udword NbTris = create.mIMesh->GetNbTriangles();
+ if(NbTris==1)
+ {
+ // We don't need to actually create a tree here, since we'll only have a single triangle to deal with anyway.
+ // It's a waste to use a "model" for this but at least it will work.
+ mModelCode |= OPC_SINGLE_NODE;
+ return true;
+ }
+
+ // 2) Build a generic AABB Tree.
+ mSource = new AABBTree;
+ CHECKALLOC(mSource);
+
+ // 2-1) Setup a builder. Our primitives here are triangles from input mesh,
+ // so we use an AABBTreeOfTrianglesBuilder.....
+ {
+ AABBTreeOfTrianglesBuilder TB;
+ TB.mIMesh = create.mIMesh;
+ TB.mSettings = create.mSettings;
+ TB.mNbPrimitives = NbTris;
+ if(!mSource->Build(&TB)) return false;
+ }
+
+ // 3) Create an optimized tree according to user-settings
+ if(!CreateTree(create.mNoLeaf, create.mQuantized)) return false;
+
+ // 3-2) Create optimized tree
+ if(!mTree->Build(mSource)) return false;
+
+ // 3-3) Delete generic tree if needed
+ if(!create.mKeepOriginal) DELETESINGLE(mSource);
+
+#ifdef __MESHMERIZER_H__
+ // 4) Convex hull
+ if(create.mCollisionHull)
+ {
+ // Create hull
+ mHull = new CollisionHull;
+ CHECKALLOC(mHull);
+
+ CONVEXHULLCREATE CHC;
+ // ### doesn't work with strides
+ CHC.NbVerts = create.mIMesh->GetNbVertices();
+ CHC.Vertices = create.mIMesh->GetVerts();
+ CHC.UnifyNormals = true;
+ CHC.ReduceVertices = true;
+ CHC.WordFaces = false;
+ mHull->Compute(CHC);
+ }
+#endif // __MESHMERIZER_H__
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the number of bytes used by the tree.
+ * \return amount of bytes used
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword Model::GetUsedBytes() const
+{
+ if(!mTree) return 0;
+ return mTree->GetUsedBytes();
+}
diff --git a/Opcode/OPC_Model.h b/Opcode/OPC_Model.h
new file mode 100644
index 0000000..1d7e1e4
--- /dev/null
+++ b/Opcode/OPC_Model.h
@@ -0,0 +1,65 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for OPCODE models.
+ * \file OPC_Model.h
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_MODEL_H__
+#define __OPC_MODEL_H__
+
+ class OPCODE_API Model : public BaseModel
+ {
+ public:
+ // Constructor/Destructor
+ Model();
+ virtual ~Model();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Builds a collision model.
+ * \param create [in] model creation structure
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(BaseModel) bool Build(const OPCODECREATE& create);
+
+#ifdef __MESHMERIZER_H__
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the collision hull.
+ * \return the collision hull if it exists
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ const CollisionHull* GetHull() const { return mHull; }
+#endif // __MESHMERIZER_H__
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the number of bytes used by the tree.
+ * \return amount of bytes used
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(BaseModel) udword GetUsedBytes() const;
+
+ private:
+#ifdef __MESHMERIZER_H__
+ CollisionHull* mHull; //!< Possible convex hull
+#endif // __MESHMERIZER_H__
+ // Internal methods
+ void Release();
+ };
+
+#endif //__OPC_MODEL_H__ \ No newline at end of file
diff --git a/Opcode/OPC_OBBCollider.cpp b/Opcode/OPC_OBBCollider.cpp
new file mode 100644
index 0000000..a784339
--- /dev/null
+++ b/Opcode/OPC_OBBCollider.cpp
@@ -0,0 +1,767 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for an OBB collider.
+ * \file OPC_OBBCollider.cpp
+ * \author Pierre Terdiman
+ * \date January, 1st, 2002
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains an OBB-vs-tree collider.
+ *
+ * \class OBBCollider
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date January, 1st, 2002
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+#include "OPC_BoxBoxOverlap.h"
+#include "OPC_TriBoxOverlap.h"
+
+#define SET_CONTACT(prim_index, flag) \
+ /* Set contact status */ \
+ mFlags |= flag; \
+ mTouchedPrimitives->Add(prim_index);
+
+//! OBB-triangle test
+#define OBB_PRIM(prim_index, flag) \
+ /* Request vertices from the app */ \
+ VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
+ /* Transform them in a common space */ \
+ TransformPoint(mLeafVerts[0], *VP.Vertex[0], mRModelToBox, mTModelToBox); \
+ TransformPoint(mLeafVerts[1], *VP.Vertex[1], mRModelToBox, mTModelToBox); \
+ TransformPoint(mLeafVerts[2], *VP.Vertex[2], mRModelToBox, mTModelToBox); \
+ /* Perform triangle-box overlap test */ \
+ if(TriBoxOverlap()) \
+ { \
+ SET_CONTACT(prim_index, flag) \
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OBBCollider::OBBCollider() : mFullBoxBoxTest(true)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OBBCollider::~OBBCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Validates current settings. You should call this method after all the settings and callbacks have been defined.
+ * \return null if everything is ok, else a string describing the problem
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const char* OBBCollider::ValidateSettings()
+{
+ if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!";
+
+ return VolumeCollider::ValidateSettings();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Generic collision query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - with GetNbTouchedPrimitives()
+ * - with GetTouchedPrimitives()
+ *
+ * \param cache [in/out] a box cache
+ * \param box [in] collision OBB in local space
+ * \param model [in] Opcode model to collide with
+ * \param worldb [in] OBB's world matrix, or null
+ * \param worldm [in] model's world matrix, or null
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool OBBCollider::Collide(OBBCache& cache, const OBB& box, const Model& model, const Matrix4x4* worldb, const Matrix4x4* worldm)
+{
+ // Checkings
+ if(!Setup(&model)) return false;
+
+ // Init collision query
+ if(InitQuery(cache, box, worldb, worldm)) return true;
+
+ if(!model.HasLeafNodes())
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ }
+ else
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ }
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Initializes a collision query :
+ * - reset stats & contact status
+ * - setup matrices
+ * - check temporal coherence
+ *
+ * \param cache [in/out] a box cache
+ * \param box [in] obb in local space
+ * \param worldb [in] obb's world matrix, or null
+ * \param worldm [in] model's world matrix, or null
+ * \return TRUE if we can return immediately
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+BOOL OBBCollider::InitQuery(OBBCache& cache, const OBB& box, const Matrix4x4* worldb, const Matrix4x4* worldm)
+{
+ // 1) Call the base method
+ VolumeCollider::InitQuery();
+
+ // 2) Compute obb in world space
+ mBoxExtents = box.mExtents;
+
+ Matrix4x4 WorldB;
+
+ if(worldb)
+ {
+ WorldB = Matrix4x4( box.mRot * Matrix3x3(*worldb) );
+ WorldB.SetTrans(box.mCenter * *worldb);
+ }
+ else
+ {
+ WorldB = box.mRot;
+ WorldB.SetTrans(box.mCenter);
+ }
+
+ // Setup matrices
+ Matrix4x4 InvWorldB;
+ InvertPRMatrix(InvWorldB, WorldB);
+
+ if(worldm)
+ {
+ Matrix4x4 InvWorldM;
+ InvertPRMatrix(InvWorldM, *worldm);
+
+ Matrix4x4 WorldBtoM = WorldB * InvWorldM;
+ Matrix4x4 WorldMtoB = *worldm * InvWorldB;
+
+ mRModelToBox = WorldMtoB; WorldMtoB.GetTrans(mTModelToBox);
+ mRBoxToModel = WorldBtoM; WorldBtoM.GetTrans(mTBoxToModel);
+ }
+ else
+ {
+ mRModelToBox = InvWorldB; InvWorldB.GetTrans(mTModelToBox);
+ mRBoxToModel = WorldB; WorldB.GetTrans(mTBoxToModel);
+ }
+
+ // 3) Setup destination pointer
+ mTouchedPrimitives = &cache.TouchedPrimitives;
+
+ // 4) Special case: 1-triangle meshes [Opcode 1.3]
+ if(mCurrentModel && mCurrentModel->HasSingleNode())
+ {
+ if(!SkipPrimitiveTests())
+ {
+ // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
+ mTouchedPrimitives->Reset();
+
+ // Perform overlap test between the unique triangle and the box (and set contact status if needed)
+ OBB_PRIM(udword(0), OPC_CONTACT)
+
+ // Return immediately regardless of status
+ return TRUE;
+ }
+ }
+
+ // 5) Check temporal coherence:
+ if(TemporalCoherenceEnabled())
+ {
+ // Here we use temporal coherence
+ // => check results from previous frame before performing the collision query
+ if(FirstContactEnabled())
+ {
+ // We're only interested in the first contact found => test the unique previously touched face
+ if(mTouchedPrimitives->GetNbEntries())
+ {
+ // Get index of previously touched face = the first entry in the array
+ udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0);
+
+ // Then reset the array:
+ // - if the overlap test below is successful, the index we'll get added back anyway
+ // - if it isn't, then the array should be reset anyway for the normal query
+ mTouchedPrimitives->Reset();
+
+ // Perform overlap test between the cached triangle and the box (and set contact status if needed)
+ OBB_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT)
+
+ // Return immediately if possible
+ if(GetContactStatus()) return TRUE;
+ }
+ // else no face has been touched during previous query
+ // => we'll have to perform a normal query
+ }
+ else
+ {
+ // ### rewrite this
+ OBB TestBox(mTBoxToModel, mBoxExtents, mRBoxToModel);
+
+ // We're interested in all contacts =>test the new real box N(ew) against the previous fat box P(revious):
+ if(IsCacheValid(cache) && TestBox.IsInside(cache.FatBox))
+ {
+ // - if N is included in P, return previous list
+ // => we simply leave the list (mTouchedFaces) unchanged
+
+ // Set contact status if needed
+ if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT;
+
+ // In any case we don't need to do a query
+ return TRUE;
+ }
+ else
+ {
+ // - else do the query using a fat N
+
+ // Reset cache since we'll about to perform a real query
+ mTouchedPrimitives->Reset();
+
+ // Make a fat box so that coherence will work for subsequent frames
+ TestBox.mExtents *= cache.FatCoeff;
+ mBoxExtents *= cache.FatCoeff;
+
+ // Update cache with query data (signature for cached faces)
+ cache.FatBox = TestBox;
+ }
+ }
+ }
+ else
+ {
+ // Here we don't use temporal coherence => do a normal query
+ mTouchedPrimitives->Reset();
+ }
+
+ // Now we can precompute box-box data
+
+ // Precompute absolute box-to-model rotation matrix
+ for(udword i=0;i<3;i++)
+ {
+ for(udword j=0;j<3;j++)
+ {
+ // Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID)
+ mAR.m[i][j] = 1e-6f + fabsf(mRBoxToModel.m[i][j]);
+ }
+ }
+
+ // Precompute bounds for box-in-box test
+ mB0 = mBoxExtents - mTModelToBox;
+ mB1 = - mBoxExtents - mTModelToBox;
+
+ // Precompute box-box data - Courtesy of Erwin de Vries
+ mBBx1 = mBoxExtents.x*mAR.m[0][0] + mBoxExtents.y*mAR.m[1][0] + mBoxExtents.z*mAR.m[2][0];
+ mBBy1 = mBoxExtents.x*mAR.m[0][1] + mBoxExtents.y*mAR.m[1][1] + mBoxExtents.z*mAR.m[2][1];
+ mBBz1 = mBoxExtents.x*mAR.m[0][2] + mBoxExtents.y*mAR.m[1][2] + mBoxExtents.z*mAR.m[2][2];
+
+ mBB_1 = mBoxExtents.y*mAR.m[2][0] + mBoxExtents.z*mAR.m[1][0];
+ mBB_2 = mBoxExtents.x*mAR.m[2][0] + mBoxExtents.z*mAR.m[0][0];
+ mBB_3 = mBoxExtents.x*mAR.m[1][0] + mBoxExtents.y*mAR.m[0][0];
+ mBB_4 = mBoxExtents.y*mAR.m[2][1] + mBoxExtents.z*mAR.m[1][1];
+ mBB_5 = mBoxExtents.x*mAR.m[2][1] + mBoxExtents.z*mAR.m[0][1];
+ mBB_6 = mBoxExtents.x*mAR.m[1][1] + mBoxExtents.y*mAR.m[0][1];
+ mBB_7 = mBoxExtents.y*mAR.m[2][2] + mBoxExtents.z*mAR.m[1][2];
+ mBB_8 = mBoxExtents.x*mAR.m[2][2] + mBoxExtents.z*mAR.m[0][2];
+ mBB_9 = mBoxExtents.x*mAR.m[1][2] + mBoxExtents.y*mAR.m[0][2];
+
+ return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks the OBB completely contains the box. In which case we can end the query sooner.
+ * \param bc [in] box center
+ * \param be [in] box extents
+ * \return true if the OBB contains the whole box
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL OBBCollider::OBBContainsBox(const Point& bc, const Point& be)
+{
+ // I assume if all 8 box vertices are inside the OBB, so does the whole box.
+ // Sounds ok but maybe there's a better way?
+/*
+#define TEST_PT(a,b,c) \
+ p.x=a; p.y=b; p.z=c; p+=bc; \
+ f = p.x * mRModelToBox.m[0][0] + p.y * mRModelToBox.m[1][0] + p.z * mRModelToBox.m[2][0]; if(f>mB0.x || f<mB1.x) return FALSE;\
+ f = p.x * mRModelToBox.m[0][1] + p.y * mRModelToBox.m[1][1] + p.z * mRModelToBox.m[2][1]; if(f>mB0.y || f<mB1.y) return FALSE;\
+ f = p.x * mRModelToBox.m[0][2] + p.y * mRModelToBox.m[1][2] + p.z * mRModelToBox.m[2][2]; if(f>mB0.z || f<mB1.z) return FALSE;
+
+ Point p;
+ float f;
+
+ TEST_PT(be.x, be.y, be.z)
+ TEST_PT(-be.x, be.y, be.z)
+ TEST_PT(be.x, -be.y, be.z)
+ TEST_PT(-be.x, -be.y, be.z)
+ TEST_PT(be.x, be.y, -be.z)
+ TEST_PT(-be.x, be.y, -be.z)
+ TEST_PT(be.x, -be.y, -be.z)
+ TEST_PT(-be.x, -be.y, -be.z)
+
+ return TRUE;
+*/
+
+ // Yes there is:
+ // - compute model-box's AABB in OBB space
+ // - test AABB-in-AABB
+ float NCx = bc.x * mRModelToBox.m[0][0] + bc.y * mRModelToBox.m[1][0] + bc.z * mRModelToBox.m[2][0];
+ float NEx = fabsf(mRModelToBox.m[0][0] * be.x) + fabsf(mRModelToBox.m[1][0] * be.y) + fabsf(mRModelToBox.m[2][0] * be.z);
+
+ if(mB0.x < NCx+NEx) return FALSE;
+ if(mB1.x > NCx-NEx) return FALSE;
+
+ float NCy = bc.x * mRModelToBox.m[0][1] + bc.y * mRModelToBox.m[1][1] + bc.z * mRModelToBox.m[2][1];
+ float NEy = fabsf(mRModelToBox.m[0][1] * be.x) + fabsf(mRModelToBox.m[1][1] * be.y) + fabsf(mRModelToBox.m[2][1] * be.z);
+
+ if(mB0.y < NCy+NEy) return FALSE;
+ if(mB1.y > NCy-NEy) return FALSE;
+
+ float NCz = bc.x * mRModelToBox.m[0][2] + bc.y * mRModelToBox.m[1][2] + bc.z * mRModelToBox.m[2][2];
+ float NEz = fabsf(mRModelToBox.m[0][2] * be.x) + fabsf(mRModelToBox.m[1][2] * be.y) + fabsf(mRModelToBox.m[2][2] * be.z);
+
+ if(mB0.z < NCz+NEz) return FALSE;
+ if(mB1.z > NCz-NEz) return FALSE;
+
+ return TRUE;
+}
+
+#define TEST_BOX_IN_OBB(center, extents) \
+ if(OBBContainsBox(center, extents)) \
+ { \
+ /* Set contact status */ \
+ mFlags |= OPC_CONTACT; \
+ _Dump(node); \
+ return; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBBCollider::_Collide(const AABBCollisionNode* node)
+{
+ // Perform OBB-AABB overlap test
+ if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
+
+ TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->IsLeaf())
+ {
+ OBB_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _Collide(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBBCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node)
+{
+ // Perform OBB-AABB overlap test
+ if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
+
+ TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->IsLeaf())
+ {
+ SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _CollideNoPrimitiveTest(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBBCollider::_Collide(const AABBQuantizedNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform OBB-AABB overlap test
+ if(!BoxBoxOverlap(Extents, Center)) return;
+
+ TEST_BOX_IN_OBB(Center, Extents)
+
+ if(node->IsLeaf())
+ {
+ OBB_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _Collide(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform OBB-AABB overlap test
+ if(!BoxBoxOverlap(Extents, Center)) return;
+
+ TEST_BOX_IN_OBB(Center, Extents)
+
+ if(node->IsLeaf())
+ {
+ SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _CollideNoPrimitiveTest(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBBCollider::_Collide(const AABBNoLeafNode* node)
+{
+ // Perform OBB-AABB overlap test
+ if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
+
+ TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for no-leaf AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBBCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node)
+{
+ // Perform OBB-AABB overlap test
+ if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
+
+ TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBBCollider::_Collide(const AABBQuantizedNoLeafNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform OBB-AABB overlap test
+ if(!BoxBoxOverlap(Extents, Center)) return;
+
+ TEST_BOX_IN_OBB(Center, Extents)
+
+ if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized no-leaf AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform OBB-AABB overlap test
+ if(!BoxBoxOverlap(Extents, Center)) return;
+
+ TEST_BOX_IN_OBB(Center, Extents)
+
+ if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetNeg());
+}
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridOBBCollider::HybridOBBCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridOBBCollider::~HybridOBBCollider()
+{
+}
+
+bool HybridOBBCollider::Collide(OBBCache& cache, const OBB& box, const HybridModel& model, const Matrix4x4* worldb, const Matrix4x4* worldm)
+{
+ // We don't want primitive tests here!
+ mFlags |= OPC_NO_PRIMITIVE_TESTS;
+
+ // Checkings
+ if(!Setup(&model)) return false;
+
+ // Init collision query
+ if(InitQuery(cache, box, worldb, worldm)) return true;
+
+ // Special case for 1-leaf trees
+ if(mCurrentModel && mCurrentModel->HasSingleNode())
+ {
+ // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles
+ udword Nb = mIMesh->GetNbTriangles();
+
+ // Loop through all triangles
+ for(udword i=0;i<Nb;i++)
+ {
+ OBB_PRIM(i, OPC_CONTACT)
+ }
+ return true;
+ }
+
+ // Override destination array since we're only going to get leaf boxes here
+ mTouchedBoxes.Reset();
+ mTouchedPrimitives = &mTouchedBoxes;
+
+ // Now, do the actual query against leaf boxes
+ if(!model.HasLeafNodes())
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ }
+ else
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ }
+
+ // We only have a list of boxes so far
+ if(GetContactStatus())
+ {
+ // Reset contact status, since it currently only reflects collisions with leaf boxes
+ Collider::InitQuery();
+
+ // Change dest container so that we can use built-in overlap tests and get collided primitives
+ cache.TouchedPrimitives.Reset();
+ mTouchedPrimitives = &cache.TouchedPrimitives;
+
+ // Read touched leaf boxes
+ udword Nb = mTouchedBoxes.GetNbEntries();
+ const udword* Touched = mTouchedBoxes.GetEntries();
+
+ const LeafTriangles* LT = model.GetLeafTriangles();
+ const udword* Indices = model.GetIndices();
+
+ // Loop through touched leaves
+ while(Nb--)
+ {
+ const LeafTriangles& CurrentLeaf = LT[*Touched++];
+
+ // Each leaf box has a set of triangles
+ udword NbTris = CurrentLeaf.GetNbTriangles();
+ if(Indices)
+ {
+ const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ udword TriangleIndex = *T++;
+ OBB_PRIM(TriangleIndex, OPC_CONTACT)
+ }
+ }
+ else
+ {
+ udword BaseIndex = CurrentLeaf.GetTriangleIndex();
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ udword TriangleIndex = BaseIndex++;
+ OBB_PRIM(TriangleIndex, OPC_CONTACT)
+ }
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/Opcode/OPC_OBBCollider.h b/Opcode/OPC_OBBCollider.h
new file mode 100644
index 0000000..b5c1de2
--- /dev/null
+++ b/Opcode/OPC_OBBCollider.h
@@ -0,0 +1,142 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for an OBB collider.
+ * \file OPC_OBBCollider.h
+ * \author Pierre Terdiman
+ * \date January, 1st, 2002
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_OBBCOLLIDER_H__
+#define __OPC_OBBCOLLIDER_H__
+
+ struct OPCODE_API OBBCache : VolumeCache
+ {
+ OBBCache() : FatCoeff(1.1f)
+ {
+ FatBox.mCenter.Zero();
+ FatBox.mExtents.Zero();
+ FatBox.mRot.Identity();
+ }
+
+ // Cached faces signature
+ OBB FatBox; //!< Box used when performing the query resulting in cached faces
+ // User settings
+ float FatCoeff; //!< extents multiplier used to create a fat box
+ };
+
+ class OPCODE_API OBBCollider : public VolumeCollider
+ {
+ public:
+ // Constructor / Destructor
+ OBBCollider();
+ virtual ~OBBCollider();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Generic collision query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - with GetNbTouchedPrimitives()
+ * - with GetTouchedPrimitives()
+ *
+ * \param cache [in/out] a box cache
+ * \param box [in] collision OBB in local space
+ * \param model [in] Opcode model to collide with
+ * \param worldb [in] OBB's world matrix, or null
+ * \param worldm [in] model's world matrix, or null
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool Collide(OBBCache& cache, const OBB& box, const Model& model, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null);
+
+ // Settings
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Settings: select between full box-box tests or "SAT-lite" tests (where Class III axes are discarded)
+ * \param flag [in] true for full tests, false for coarse tests
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetFullBoxBoxTest(bool flag) { mFullBoxBoxTest = flag; }
+
+ // Settings
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Validates current settings. You should call this method after all the settings and callbacks have been defined for a collider.
+ * \return null if everything is ok, else a string describing the problem
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(Collider) const char* ValidateSettings();
+
+ protected:
+ // Precomputed data
+ Matrix3x3 mAR; //!< Absolute rotation matrix
+ Matrix3x3 mRModelToBox; //!< Rotation from model space to obb space
+ Matrix3x3 mRBoxToModel; //!< Rotation from obb space to model space
+ Point mTModelToBox; //!< Translation from model space to obb space
+ Point mTBoxToModel; //!< Translation from obb space to model space
+
+ Point mBoxExtents;
+ Point mB0; //!< - mTModelToBox + mBoxExtents
+ Point mB1; //!< - mTModelToBox - mBoxExtents
+
+ float mBBx1;
+ float mBBy1;
+ float mBBz1;
+
+ float mBB_1;
+ float mBB_2;
+ float mBB_3;
+ float mBB_4;
+ float mBB_5;
+ float mBB_6;
+ float mBB_7;
+ float mBB_8;
+ float mBB_9;
+
+ // Leaf description
+ Point mLeafVerts[3]; //!< Triangle vertices
+ // Settings
+ bool mFullBoxBoxTest; //!< Perform full BV-BV tests (true) or SAT-lite tests (false)
+ // Internal methods
+ void _Collide(const AABBCollisionNode* node);
+ void _Collide(const AABBNoLeafNode* node);
+ void _Collide(const AABBQuantizedNode* node);
+ void _Collide(const AABBQuantizedNoLeafNode* node);
+ void _CollideNoPrimitiveTest(const AABBCollisionNode* node);
+ void _CollideNoPrimitiveTest(const AABBNoLeafNode* node);
+ void _CollideNoPrimitiveTest(const AABBQuantizedNode* node);
+ void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node);
+ // Overlap tests
+ inline_ BOOL OBBContainsBox(const Point& bc, const Point& be);
+ inline_ BOOL BoxBoxOverlap(const Point& extents, const Point& center);
+ inline_ BOOL TriBoxOverlap();
+ // Init methods
+ BOOL InitQuery(OBBCache& cache, const OBB& box, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null);
+ };
+
+ class OPCODE_API HybridOBBCollider : public OBBCollider
+ {
+ public:
+ // Constructor / Destructor
+ HybridOBBCollider();
+ virtual ~HybridOBBCollider();
+
+ bool Collide(OBBCache& cache, const OBB& box, const HybridModel& model, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null);
+ protected:
+ Container mTouchedBoxes;
+ };
+
+#endif // __OPC_OBBCOLLIDER_H__
diff --git a/Opcode/OPC_OptimizedTree.cpp b/Opcode/OPC_OptimizedTree.cpp
new file mode 100644
index 0000000..aed4f64
--- /dev/null
+++ b/Opcode/OPC_OptimizedTree.cpp
@@ -0,0 +1,782 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for optimized trees. Implements 4 trees:
+ * - normal
+ * - no leaf
+ * - quantized
+ * - no leaf / quantized
+ *
+ * \file OPC_OptimizedTree.cpp
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A standard AABB tree.
+ *
+ * \class AABBCollisionTree
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A no-leaf AABB tree.
+ *
+ * \class AABBNoLeafTree
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A quantized AABB tree.
+ *
+ * \class AABBQuantizedTree
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A quantized no-leaf AABB tree.
+ *
+ * \class AABBQuantizedNoLeafTree
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+//! Compilation flag:
+//! - true to fix quantized boxes (i.e. make sure they enclose the original ones)
+//! - false to see the effects of quantization errors (faster, but wrong results in some cases)
+static bool gFixQuantized = true;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds an implicit tree from a standard one. An implicit tree is a complete tree (2*N-1 nodes) whose negative
+ * box pointers and primitive pointers have been made implicit, hence packing 3 pointers in one.
+ *
+ * Layout for implicit trees:
+ * Node:
+ * - box
+ * - data (32-bits value)
+ *
+ * if data's LSB = 1 => remaining bits are a primitive pointer
+ * else remaining bits are a P-node pointer, and N = P + 1
+ *
+ * \relates AABBCollisionNode
+ * \fn _BuildCollisionTree(AABBCollisionNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node)
+ * \param linear [in] base address of destination nodes
+ * \param box_id [in] index of destination node
+ * \param current_id [in] current running index
+ * \param current_node [in] current node from input tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static void _BuildCollisionTree(AABBCollisionNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node)
+{
+ // Current node from input tree is "current_node". Must be flattened into "linear[boxid]".
+
+ // Store the AABB
+ current_node->GetAABB()->GetCenter(linear[box_id].mAABB.mCenter);
+ current_node->GetAABB()->GetExtents(linear[box_id].mAABB.mExtents);
+ // Store remaining info
+ if(current_node->IsLeaf())
+ {
+ // The input tree must be complete => i.e. one primitive/leaf
+ ASSERT(current_node->GetNbPrimitives()==1);
+ // Get the primitive index from the input tree
+ udword PrimitiveIndex = current_node->GetPrimitives()[0];
+ // Setup box data as the primitive index, marked as leaf
+ linear[box_id].mData = (PrimitiveIndex<<1)|1;
+ }
+ else
+ {
+ // To make the negative one implicit, we must store P and N in successive order
+ udword PosID = current_id++; // Get a new id for positive child
+ udword NegID = current_id++; // Get a new id for negative child
+ // Setup box data as the forthcoming new P pointer
+ linear[box_id].mData = (udword)&linear[PosID];
+ // Make sure it's not marked as leaf
+ ASSERT(!(linear[box_id].mData&1));
+ // Recurse with new IDs
+ _BuildCollisionTree(linear, PosID, current_id, current_node->GetPos());
+ _BuildCollisionTree(linear, NegID, current_id, current_node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds a "no-leaf" tree from a standard one. This is a tree whose leaf nodes have been removed.
+ *
+ * Layout for no-leaf trees:
+ *
+ * Node:
+ * - box
+ * - P pointer => a node (LSB=0) or a primitive (LSB=1)
+ * - N pointer => a node (LSB=0) or a primitive (LSB=1)
+ *
+ * \relates AABBNoLeafNode
+ * \fn _BuildNoLeafTree(AABBNoLeafNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node)
+ * \param linear [in] base address of destination nodes
+ * \param box_id [in] index of destination node
+ * \param current_id [in] current running index
+ * \param current_node [in] current node from input tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static void _BuildNoLeafTree(AABBNoLeafNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node)
+{
+ const AABBTreeNode* P = current_node->GetPos();
+ const AABBTreeNode* N = current_node->GetNeg();
+ // Leaf nodes here?!
+ ASSERT(P);
+ ASSERT(N);
+ // Internal node => keep the box
+ current_node->GetAABB()->GetCenter(linear[box_id].mAABB.mCenter);
+ current_node->GetAABB()->GetExtents(linear[box_id].mAABB.mExtents);
+
+ if(P->IsLeaf())
+ {
+ // The input tree must be complete => i.e. one primitive/leaf
+ ASSERT(P->GetNbPrimitives()==1);
+ // Get the primitive index from the input tree
+ udword PrimitiveIndex = P->GetPrimitives()[0];
+ // Setup prev box data as the primitive index, marked as leaf
+ linear[box_id].mPosData = (PrimitiveIndex<<1)|1;
+ }
+ else
+ {
+ // Get a new id for positive child
+ udword PosID = current_id++;
+ // Setup box data
+ linear[box_id].mPosData = (udword)&linear[PosID];
+ // Make sure it's not marked as leaf
+ ASSERT(!(linear[box_id].mPosData&1));
+ // Recurse
+ _BuildNoLeafTree(linear, PosID, current_id, P);
+ }
+
+ if(N->IsLeaf())
+ {
+ // The input tree must be complete => i.e. one primitive/leaf
+ ASSERT(N->GetNbPrimitives()==1);
+ // Get the primitive index from the input tree
+ udword PrimitiveIndex = N->GetPrimitives()[0];
+ // Setup prev box data as the primitive index, marked as leaf
+ linear[box_id].mNegData = (PrimitiveIndex<<1)|1;
+ }
+ else
+ {
+ // Get a new id for negative child
+ udword NegID = current_id++;
+ // Setup box data
+ linear[box_id].mNegData = (udword)&linear[NegID];
+ // Make sure it's not marked as leaf
+ ASSERT(!(linear[box_id].mNegData&1));
+ // Recurse
+ _BuildNoLeafTree(linear, NegID, current_id, N);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBCollisionTree::AABBCollisionTree() : mNodes(null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBCollisionTree::~AABBCollisionTree()
+{
+ DELETEARRAY(mNodes);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds the collision tree from a generic AABB tree.
+ * \param tree [in] generic AABB tree
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBCollisionTree::Build(AABBTree* tree)
+{
+ // Checkings
+ if(!tree) return false;
+ // Check the input tree is complete
+ udword NbTriangles = tree->GetNbPrimitives();
+ udword NbNodes = tree->GetNbNodes();
+ if(NbNodes!=NbTriangles*2-1) return false;
+
+ // Get nodes
+ if(mNbNodes!=NbNodes) // Same number of nodes => keep moving
+ {
+ mNbNodes = NbNodes;
+ DELETEARRAY(mNodes);
+ mNodes = new AABBCollisionNode[mNbNodes];
+ CHECKALLOC(mNodes);
+ }
+
+ // Build the tree
+ udword CurID = 1;
+ _BuildCollisionTree(mNodes, 0, CurID, tree);
+ ASSERT(CurID==mNbNodes);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the collision tree after vertices have been modified.
+ * \param mesh_interface [in] mesh interface for current model
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBCollisionTree::Refit(const MeshInterface* mesh_interface)
+{
+ ASSERT(!"Not implemented since AABBCollisionTrees have twice as more nodes to refit as AABBNoLeafTrees!");
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Walks the tree and call the user back for each node.
+ * \param callback [in] walking callback
+ * \param user_data [in] callback's user data
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBCollisionTree::Walk(GenericWalkingCallback callback, void* user_data) const
+{
+ if(!callback) return false;
+
+ struct Local
+ {
+ static void _Walk(const AABBCollisionNode* current_node, GenericWalkingCallback callback, void* user_data)
+ {
+ if(!current_node || !(callback)(current_node, user_data)) return;
+
+ if(!current_node->IsLeaf())
+ {
+ _Walk(current_node->GetPos(), callback, user_data);
+ _Walk(current_node->GetNeg(), callback, user_data);
+ }
+ }
+ };
+ Local::_Walk(mNodes, callback, user_data);
+ return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBNoLeafTree::AABBNoLeafTree() : mNodes(null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBNoLeafTree::~AABBNoLeafTree()
+{
+ DELETEARRAY(mNodes);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds the collision tree from a generic AABB tree.
+ * \param tree [in] generic AABB tree
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBNoLeafTree::Build(AABBTree* tree)
+{
+ // Checkings
+ if(!tree) return false;
+ // Check the input tree is complete
+ udword NbTriangles = tree->GetNbPrimitives();
+ udword NbNodes = tree->GetNbNodes();
+ if(NbNodes!=NbTriangles*2-1) return false;
+
+ // Get nodes
+ if(mNbNodes!=NbTriangles-1) // Same number of nodes => keep moving
+ {
+ mNbNodes = NbTriangles-1;
+ DELETEARRAY(mNodes);
+ mNodes = new AABBNoLeafNode[mNbNodes];
+ CHECKALLOC(mNodes);
+ }
+
+ // Build the tree
+ udword CurID = 1;
+ _BuildNoLeafTree(mNodes, 0, CurID, tree);
+ ASSERT(CurID==mNbNodes);
+
+ return true;
+}
+
+inline_ void ComputeMinMax(Point& min, Point& max, const VertexPointers& vp)
+{
+ // Compute triangle's AABB = a leaf box
+#ifdef OPC_USE_FCOMI // a 15% speedup on my machine, not much
+ min.x = FCMin3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x);
+ max.x = FCMax3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x);
+
+ min.y = FCMin3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y);
+ max.y = FCMax3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y);
+
+ min.z = FCMin3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z);
+ max.z = FCMax3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z);
+#else
+ min = *vp.Vertex[0];
+ max = *vp.Vertex[0];
+ min.Min(*vp.Vertex[1]);
+ max.Max(*vp.Vertex[1]);
+ min.Min(*vp.Vertex[2]);
+ max.Max(*vp.Vertex[2]);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the collision tree after vertices have been modified.
+ * \param mesh_interface [in] mesh interface for current model
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBNoLeafTree::Refit(const MeshInterface* mesh_interface)
+{
+ // Checkings
+ if(!mesh_interface) return false;
+
+ // Bottom-up update
+ VertexPointers VP;
+ Point Min,Max;
+ Point Min_,Max_;
+ udword Index = mNbNodes;
+ while(Index--)
+ {
+ AABBNoLeafNode& Current = mNodes[Index];
+
+ if(Current.HasPosLeaf())
+ {
+ mesh_interface->GetTriangle(VP, Current.GetPosPrimitive());
+ ComputeMinMax(Min, Max, VP);
+ }
+ else
+ {
+ const CollisionAABB& CurrentBox = Current.GetPos()->mAABB;
+ CurrentBox.GetMin(Min);
+ CurrentBox.GetMax(Max);
+ }
+
+ if(Current.HasNegLeaf())
+ {
+ mesh_interface->GetTriangle(VP, Current.GetNegPrimitive());
+ ComputeMinMax(Min_, Max_, VP);
+ }
+ else
+ {
+ const CollisionAABB& CurrentBox = Current.GetNeg()->mAABB;
+ CurrentBox.GetMin(Min_);
+ CurrentBox.GetMax(Max_);
+ }
+#ifdef OPC_USE_FCOMI
+ Min.x = FCMin2(Min.x, Min_.x);
+ Max.x = FCMax2(Max.x, Max_.x);
+ Min.y = FCMin2(Min.y, Min_.y);
+ Max.y = FCMax2(Max.y, Max_.y);
+ Min.z = FCMin2(Min.z, Min_.z);
+ Max.z = FCMax2(Max.z, Max_.z);
+#else
+ Min.Min(Min_);
+ Max.Max(Max_);
+#endif
+ Current.mAABB.SetMinMax(Min, Max);
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Walks the tree and call the user back for each node.
+ * \param callback [in] walking callback
+ * \param user_data [in] callback's user data
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBNoLeafTree::Walk(GenericWalkingCallback callback, void* user_data) const
+{
+ if(!callback) return false;
+
+ struct Local
+ {
+ static void _Walk(const AABBNoLeafNode* current_node, GenericWalkingCallback callback, void* user_data)
+ {
+ if(!current_node || !(callback)(current_node, user_data)) return;
+
+ if(!current_node->HasPosLeaf()) _Walk(current_node->GetPos(), callback, user_data);
+ if(!current_node->HasNegLeaf()) _Walk(current_node->GetNeg(), callback, user_data);
+ }
+ };
+ Local::_Walk(mNodes, callback, user_data);
+ return true;
+}
+
+// Quantization notes:
+// - We could use the highest bits of mData to store some more quantized bits. Dequantization code
+// would be slightly more complex, but number of overlap tests would be reduced (and anyhow those
+// bits are currently wasted). Of course it's not possible if we move to 16 bits mData.
+// - Something like "16 bits floats" could be tested, to bypass the int-to-float conversion.
+// - A dedicated BV-BV test could be used, dequantizing while testing for overlap. (i.e. it's some
+// lazy-dequantization which may save some work in case of early exits). At the very least some
+// muls could be saved by precomputing several more matrices. But maybe not worth the pain.
+// - Do we need to dequantize anyway? Not doing the extents-related muls only implies the box has
+// been scaled, for example.
+// - The deeper we move into the hierarchy, the smaller the extents should be. May not need a fixed
+// number of quantization bits. Even better, could probably be best delta-encoded.
+
+
+// Find max values. Some people asked why I wasn't simply using the first node. Well, I can't.
+// I'm not looking for (min, max) values like in a standard AABB, I'm looking for the extremal
+// centers/extents in order to quantize them. The first node would only give a single center and
+// a single extents. While extents would be the biggest, the center wouldn't.
+#define FIND_MAX_VALUES \
+ /* Get max values */ \
+ Point CMax(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); \
+ Point EMax(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); \
+ for(udword i=0;i<mNbNodes;i++) \
+ { \
+ if(fabsf(Nodes[i].mAABB.mCenter.x)>CMax.x) CMax.x = fabsf(Nodes[i].mAABB.mCenter.x); \
+ if(fabsf(Nodes[i].mAABB.mCenter.y)>CMax.y) CMax.y = fabsf(Nodes[i].mAABB.mCenter.y); \
+ if(fabsf(Nodes[i].mAABB.mCenter.z)>CMax.z) CMax.z = fabsf(Nodes[i].mAABB.mCenter.z); \
+ if(fabsf(Nodes[i].mAABB.mExtents.x)>EMax.x) EMax.x = fabsf(Nodes[i].mAABB.mExtents.x); \
+ if(fabsf(Nodes[i].mAABB.mExtents.y)>EMax.y) EMax.y = fabsf(Nodes[i].mAABB.mExtents.y); \
+ if(fabsf(Nodes[i].mAABB.mExtents.z)>EMax.z) EMax.z = fabsf(Nodes[i].mAABB.mExtents.z); \
+ }
+
+#define INIT_QUANTIZATION \
+ udword nbc=15; /* Keep one bit for sign */ \
+ udword nbe=15; /* Keep one bit for fix */ \
+ if(!gFixQuantized) nbe++; \
+ \
+ /* Compute quantization coeffs */ \
+ Point CQuantCoeff, EQuantCoeff; \
+ CQuantCoeff.x = CMax.x!=0.0f ? float((1<<nbc)-1)/CMax.x : 0.0f; \
+ CQuantCoeff.y = CMax.y!=0.0f ? float((1<<nbc)-1)/CMax.y : 0.0f; \
+ CQuantCoeff.z = CMax.z!=0.0f ? float((1<<nbc)-1)/CMax.z : 0.0f; \
+ EQuantCoeff.x = EMax.x!=0.0f ? float((1<<nbe)-1)/EMax.x : 0.0f; \
+ EQuantCoeff.y = EMax.y!=0.0f ? float((1<<nbe)-1)/EMax.y : 0.0f; \
+ EQuantCoeff.z = EMax.z!=0.0f ? float((1<<nbe)-1)/EMax.z : 0.0f; \
+ /* Compute and save dequantization coeffs */ \
+ mCenterCoeff.x = CQuantCoeff.x!=0.0f ? 1.0f / CQuantCoeff.x : 0.0f; \
+ mCenterCoeff.y = CQuantCoeff.y!=0.0f ? 1.0f / CQuantCoeff.y : 0.0f; \
+ mCenterCoeff.z = CQuantCoeff.z!=0.0f ? 1.0f / CQuantCoeff.z : 0.0f; \
+ mExtentsCoeff.x = EQuantCoeff.x!=0.0f ? 1.0f / EQuantCoeff.x : 0.0f; \
+ mExtentsCoeff.y = EQuantCoeff.y!=0.0f ? 1.0f / EQuantCoeff.y : 0.0f; \
+ mExtentsCoeff.z = EQuantCoeff.z!=0.0f ? 1.0f / EQuantCoeff.z : 0.0f; \
+
+#define PERFORM_QUANTIZATION \
+ /* Quantize */ \
+ mNodes[i].mAABB.mCenter[0] = sword(Nodes[i].mAABB.mCenter.x * CQuantCoeff.x); \
+ mNodes[i].mAABB.mCenter[1] = sword(Nodes[i].mAABB.mCenter.y * CQuantCoeff.y); \
+ mNodes[i].mAABB.mCenter[2] = sword(Nodes[i].mAABB.mCenter.z * CQuantCoeff.z); \
+ mNodes[i].mAABB.mExtents[0] = uword(Nodes[i].mAABB.mExtents.x * EQuantCoeff.x); \
+ mNodes[i].mAABB.mExtents[1] = uword(Nodes[i].mAABB.mExtents.y * EQuantCoeff.y); \
+ mNodes[i].mAABB.mExtents[2] = uword(Nodes[i].mAABB.mExtents.z * EQuantCoeff.z); \
+ /* Fix quantized boxes */ \
+ if(gFixQuantized) \
+ { \
+ /* Make sure the quantized box is still valid */ \
+ Point Max = Nodes[i].mAABB.mCenter + Nodes[i].mAABB.mExtents; \
+ Point Min = Nodes[i].mAABB.mCenter - Nodes[i].mAABB.mExtents; \
+ /* For each axis */ \
+ for(udword j=0;j<3;j++) \
+ { /* Dequantize the box center */ \
+ float qc = float(mNodes[i].mAABB.mCenter[j]) * mCenterCoeff[j]; \
+ bool FixMe=true; \
+ do \
+ { /* Dequantize the box extent */ \
+ float qe = float(mNodes[i].mAABB.mExtents[j]) * mExtentsCoeff[j]; \
+ /* Compare real & dequantized values */ \
+ if(qc+qe<Max[j] || qc-qe>Min[j]) mNodes[i].mAABB.mExtents[j]++; \
+ else FixMe=false; \
+ /* Prevent wrapping */ \
+ if(!mNodes[i].mAABB.mExtents[j]) \
+ { \
+ mNodes[i].mAABB.mExtents[j]=0xffff; \
+ FixMe=false; \
+ } \
+ }while(FixMe); \
+ } \
+ }
+
+#define REMAP_DATA(member) \
+ /* Fix data */ \
+ Data = Nodes[i].member; \
+ if(!(Data&1)) \
+ { \
+ /* Compute box number */ \
+ udword Nb = (Data - udword(Nodes))/Nodes[i].GetNodeSize(); \
+ Data = udword(&mNodes[Nb]); \
+ } \
+ /* ...remapped */ \
+ mNodes[i].member = Data;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBQuantizedTree::AABBQuantizedTree() : mNodes(null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBQuantizedTree::~AABBQuantizedTree()
+{
+ DELETEARRAY(mNodes);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds the collision tree from a generic AABB tree.
+ * \param tree [in] generic AABB tree
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBQuantizedTree::Build(AABBTree* tree)
+{
+ // Checkings
+ if(!tree) return false;
+ // Check the input tree is complete
+ udword NbTriangles = tree->GetNbPrimitives();
+ udword NbNodes = tree->GetNbNodes();
+ if(NbNodes!=NbTriangles*2-1) return false;
+
+ // Get nodes
+ mNbNodes = NbNodes;
+ DELETEARRAY(mNodes);
+ AABBCollisionNode* Nodes = new AABBCollisionNode[mNbNodes];
+ CHECKALLOC(Nodes);
+
+ // Build the tree
+ udword CurID = 1;
+ _BuildCollisionTree(Nodes, 0, CurID, tree);
+
+ // Quantize
+ {
+ mNodes = new AABBQuantizedNode[mNbNodes];
+ CHECKALLOC(mNodes);
+
+ // Get max values
+ FIND_MAX_VALUES
+
+ // Quantization
+ INIT_QUANTIZATION
+
+ // Quantize
+ udword Data;
+ for(udword i=0;i<mNbNodes;i++)
+ {
+ PERFORM_QUANTIZATION
+ REMAP_DATA(mData)
+ }
+
+ DELETEARRAY(Nodes);
+ }
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the collision tree after vertices have been modified.
+ * \param mesh_interface [in] mesh interface for current model
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBQuantizedTree::Refit(const MeshInterface* mesh_interface)
+{
+ ASSERT(!"Not implemented since requantizing is painful !");
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Walks the tree and call the user back for each node.
+ * \param callback [in] walking callback
+ * \param user_data [in] callback's user data
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBQuantizedTree::Walk(GenericWalkingCallback callback, void* user_data) const
+{
+ if(!callback) return false;
+
+ struct Local
+ {
+ static void _Walk(const AABBQuantizedNode* current_node, GenericWalkingCallback callback, void* user_data)
+ {
+ if(!current_node || !(callback)(current_node, user_data)) return;
+
+ if(!current_node->IsLeaf())
+ {
+ _Walk(current_node->GetPos(), callback, user_data);
+ _Walk(current_node->GetNeg(), callback, user_data);
+ }
+ }
+ };
+ Local::_Walk(mNodes, callback, user_data);
+ return true;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBQuantizedNoLeafTree::AABBQuantizedNoLeafTree() : mNodes(null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBQuantizedNoLeafTree::~AABBQuantizedNoLeafTree()
+{
+ DELETEARRAY(mNodes);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds the collision tree from a generic AABB tree.
+ * \param tree [in] generic AABB tree
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBQuantizedNoLeafTree::Build(AABBTree* tree)
+{
+ // Checkings
+ if(!tree) return false;
+ // Check the input tree is complete
+ udword NbTriangles = tree->GetNbPrimitives();
+ udword NbNodes = tree->GetNbNodes();
+ if(NbNodes!=NbTriangles*2-1) return false;
+
+ // Get nodes
+ mNbNodes = NbTriangles-1;
+ DELETEARRAY(mNodes);
+ AABBNoLeafNode* Nodes = new AABBNoLeafNode[mNbNodes];
+ CHECKALLOC(Nodes);
+
+ // Build the tree
+ udword CurID = 1;
+ _BuildNoLeafTree(Nodes, 0, CurID, tree);
+ ASSERT(CurID==mNbNodes);
+
+ // Quantize
+ {
+ mNodes = new AABBQuantizedNoLeafNode[mNbNodes];
+ CHECKALLOC(mNodes);
+
+ // Get max values
+ FIND_MAX_VALUES
+
+ // Quantization
+ INIT_QUANTIZATION
+
+ // Quantize
+ udword Data;
+ for(udword i=0;i<mNbNodes;i++)
+ {
+ PERFORM_QUANTIZATION
+ REMAP_DATA(mPosData)
+ REMAP_DATA(mNegData)
+ }
+
+ DELETEARRAY(Nodes);
+ }
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the collision tree after vertices have been modified.
+ * \param mesh_interface [in] mesh interface for current model
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBQuantizedNoLeafTree::Refit(const MeshInterface* mesh_interface)
+{
+ ASSERT(!"Not implemented since requantizing is painful !");
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Walks the tree and call the user back for each node.
+ * \param callback [in] walking callback
+ * \param user_data [in] callback's user data
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBQuantizedNoLeafTree::Walk(GenericWalkingCallback callback, void* user_data) const
+{
+ if(!callback) return false;
+
+ struct Local
+ {
+ static void _Walk(const AABBQuantizedNoLeafNode* current_node, GenericWalkingCallback callback, void* user_data)
+ {
+ if(!current_node || !(callback)(current_node, user_data)) return;
+
+ if(!current_node->HasPosLeaf()) _Walk(current_node->GetPos(), callback, user_data);
+ if(!current_node->HasNegLeaf()) _Walk(current_node->GetNeg(), callback, user_data);
+ }
+ };
+ Local::_Walk(mNodes, callback, user_data);
+ return true;
+}
diff --git a/Opcode/OPC_OptimizedTree.h b/Opcode/OPC_OptimizedTree.h
new file mode 100644
index 0000000..7bfe06f
--- /dev/null
+++ b/Opcode/OPC_OptimizedTree.h
@@ -0,0 +1,206 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for optimized trees.
+ * \file OPC_OptimizedTree.h
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_OPTIMIZEDTREE_H__
+#define __OPC_OPTIMIZEDTREE_H__
+
+ //! Common interface for a node of an implicit tree
+ #define IMPLEMENT_IMPLICIT_NODE(base_class, volume) \
+ public: \
+ /* Constructor / Destructor */ \
+ inline_ base_class() : mData(0) {} \
+ inline_ ~base_class() {} \
+ /* Leaf test */ \
+ inline_ BOOL IsLeaf() const { return mData&1; } \
+ /* Data access */ \
+ inline_ const base_class* GetPos() const { return (base_class*)mData; } \
+ inline_ const base_class* GetNeg() const { return ((base_class*)mData)+1; } \
+ inline_ udword GetPrimitive() const { return (mData>>1); } \
+ /* Stats */ \
+ inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \
+ \
+ volume mAABB; \
+ udword mData;
+
+ //! Common interface for a node of a no-leaf tree
+ #define IMPLEMENT_NOLEAF_NODE(base_class, volume) \
+ public: \
+ /* Constructor / Destructor */ \
+ inline_ base_class() : mPosData(0), mNegData(0) {} \
+ inline_ ~base_class() {} \
+ /* Leaf tests */ \
+ inline_ BOOL HasPosLeaf() const { return mPosData&1; } \
+ inline_ BOOL HasNegLeaf() const { return mNegData&1; } \
+ /* Data access */ \
+ inline_ const base_class* GetPos() const { return (base_class*)mPosData; } \
+ inline_ const base_class* GetNeg() const { return (base_class*)mNegData; } \
+ inline_ udword GetPosPrimitive() const { return (mPosData>>1); } \
+ inline_ udword GetNegPrimitive() const { return (mNegData>>1); } \
+ /* Stats */ \
+ inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \
+ \
+ volume mAABB; \
+ udword mPosData; \
+ udword mNegData;
+
+ class OPCODE_API AABBCollisionNode
+ {
+ IMPLEMENT_IMPLICIT_NODE(AABBCollisionNode, CollisionAABB)
+
+ inline_ float GetVolume() const { return mAABB.mExtents.x * mAABB.mExtents.y * mAABB.mExtents.z; }
+ inline_ float GetSize() const { return mAABB.mExtents.SquareMagnitude(); }
+ inline_ udword GetRadius() const
+ {
+ udword* Bits = (udword*)&mAABB.mExtents.x;
+ udword Max = Bits[0];
+ if(Bits[1]>Max) Max = Bits[1];
+ if(Bits[2]>Max) Max = Bits[2];
+ return Max;
+ }
+
+ // NB: using the square-magnitude or the true volume of the box, seems to yield better results
+ // (assuming UNC-like informed traversal methods). I borrowed this idea from PQP. The usual "size"
+ // otherwise, is the largest box extent. In SOLID that extent is computed on-the-fly each time it's
+ // needed (the best approach IMHO). In RAPID the rotation matrix is permuted so that Extent[0] is
+ // always the greatest, which saves looking for it at runtime. On the other hand, it yields matrices
+ // whose determinant is not 1, i.e. you can't encode them anymore as unit quaternions. Not a very
+ // good strategy.
+ };
+
+ class OPCODE_API AABBQuantizedNode
+ {
+ IMPLEMENT_IMPLICIT_NODE(AABBQuantizedNode, QuantizedAABB)
+
+ inline_ uword GetSize() const
+ {
+ const uword* Bits = mAABB.mExtents;
+ uword Max = Bits[0];
+ if(Bits[1]>Max) Max = Bits[1];
+ if(Bits[2]>Max) Max = Bits[2];
+ return Max;
+ }
+ // NB: for quantized nodes I don't feel like computing a square-magnitude with integers all
+ // over the place.......!
+ };
+
+ class OPCODE_API AABBNoLeafNode
+ {
+ IMPLEMENT_NOLEAF_NODE(AABBNoLeafNode, CollisionAABB)
+ };
+
+ class OPCODE_API AABBQuantizedNoLeafNode
+ {
+ IMPLEMENT_NOLEAF_NODE(AABBQuantizedNoLeafNode, QuantizedAABB)
+ };
+
+ //! Common interface for a collision tree
+ #define IMPLEMENT_COLLISION_TREE(base_class, node) \
+ public: \
+ /* Constructor / Destructor */ \
+ base_class(); \
+ virtual ~base_class(); \
+ /* Builds from a standard tree */ \
+ override(AABBOptimizedTree) bool Build(AABBTree* tree); \
+ /* Refits the tree */ \
+ override(AABBOptimizedTree) bool Refit(const MeshInterface* mesh_interface); \
+ /* Walks the tree */ \
+ override(AABBOptimizedTree) bool Walk(GenericWalkingCallback callback, void* user_data) const; \
+ /* Data access */ \
+ inline_ const node* GetNodes() const { return mNodes; } \
+ /* Stats */ \
+ override(AABBOptimizedTree) udword GetUsedBytes() const { return mNbNodes*sizeof(node); } \
+ private: \
+ node* mNodes;
+
+ typedef bool (*GenericWalkingCallback) (const void* current, void* user_data);
+
+ class OPCODE_API AABBOptimizedTree
+ {
+ public:
+ // Constructor / Destructor
+ AABBOptimizedTree() :
+ mNbNodes (0)
+ {}
+ virtual ~AABBOptimizedTree() {}
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Builds the collision tree from a generic AABB tree.
+ * \param tree [in] generic AABB tree
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual bool Build(AABBTree* tree) = 0;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Refits the collision tree after vertices have been modified.
+ * \param mesh_interface [in] mesh interface for current model
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual bool Refit(const MeshInterface* mesh_interface) = 0;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Walks the tree and call the user back for each node.
+ * \param callback [in] walking callback
+ * \param user_data [in] callback's user data
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual bool Walk(GenericWalkingCallback callback, void* user_data) const = 0;
+
+ // Data access
+ virtual udword GetUsedBytes() const = 0;
+ inline_ udword GetNbNodes() const { return mNbNodes; }
+
+ protected:
+ udword mNbNodes;
+ };
+
+ class OPCODE_API AABBCollisionTree : public AABBOptimizedTree
+ {
+ IMPLEMENT_COLLISION_TREE(AABBCollisionTree, AABBCollisionNode)
+ };
+
+ class OPCODE_API AABBNoLeafTree : public AABBOptimizedTree
+ {
+ IMPLEMENT_COLLISION_TREE(AABBNoLeafTree, AABBNoLeafNode)
+ };
+
+ class OPCODE_API AABBQuantizedTree : public AABBOptimizedTree
+ {
+ IMPLEMENT_COLLISION_TREE(AABBQuantizedTree, AABBQuantizedNode)
+
+ public:
+ Point mCenterCoeff;
+ Point mExtentsCoeff;
+ };
+
+ class OPCODE_API AABBQuantizedNoLeafTree : public AABBOptimizedTree
+ {
+ IMPLEMENT_COLLISION_TREE(AABBQuantizedNoLeafTree, AABBQuantizedNoLeafNode)
+
+ public:
+ Point mCenterCoeff;
+ Point mExtentsCoeff;
+ };
+
+#endif // __OPC_OPTIMIZEDTREE_H__
diff --git a/Opcode/OPC_Picking.cpp b/Opcode/OPC_Picking.cpp
new file mode 100644
index 0000000..1d6319f
--- /dev/null
+++ b/Opcode/OPC_Picking.cpp
@@ -0,0 +1,182 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code to perform "picking".
+ * \file OPC_Picking.cpp
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+#ifdef OPC_RAYHIT_CALLBACK
+
+/*
+ Possible RayCollider usages:
+ - boolean query (shadow feeler)
+ - closest hit
+ - all hits
+ - number of intersection (boolean)
+
+*/
+
+bool Opcode::SetupAllHits(RayCollider& collider, CollisionFaces& contacts)
+{
+ struct Local
+ {
+ static void AllContacts(const CollisionFace& hit, void* user_data)
+ {
+ CollisionFaces* CF = (CollisionFaces*)user_data;
+ CF->AddFace(hit);
+ }
+ };
+
+ collider.SetFirstContact(false);
+ collider.SetHitCallback(Local::AllContacts);
+ collider.SetUserData(&contacts);
+ return true;
+}
+
+bool Opcode::SetupClosestHit(RayCollider& collider, CollisionFace& closest_contact)
+{
+ struct Local
+ {
+ static void ClosestContact(const CollisionFace& hit, void* user_data)
+ {
+ CollisionFace* CF = (CollisionFace*)user_data;
+ if(hit.mDistance<CF->mDistance) *CF = hit;
+ }
+ };
+
+ collider.SetFirstContact(false);
+ collider.SetHitCallback(Local::ClosestContact);
+ collider.SetUserData(&closest_contact);
+ closest_contact.mDistance = MAX_FLOAT;
+ return true;
+}
+
+bool Opcode::SetupShadowFeeler(RayCollider& collider)
+{
+ collider.SetFirstContact(true);
+ collider.SetHitCallback(null);
+ return true;
+}
+
+bool Opcode::SetupInOutTest(RayCollider& collider)
+{
+ collider.SetFirstContact(false);
+ collider.SetHitCallback(null);
+ // Results with collider.GetNbIntersections()
+ return true;
+}
+
+bool Opcode::Picking(
+CollisionFace& picked_face,
+const Ray& world_ray, const Model& model, const Matrix4x4* world,
+float min_dist, float max_dist, const Point& view_point, CullModeCallback callback, void* user_data)
+{
+ struct Local
+ {
+ struct CullData
+ {
+ CollisionFace* Closest;
+ float MinLimit;
+ CullModeCallback Callback;
+ void* UserData;
+ Point ViewPoint;
+ const MeshInterface* IMesh;
+ };
+
+ // Called for each stabbed face
+ static void RenderCullingCallback(const CollisionFace& hit, void* user_data)
+ {
+ CullData* Data = (CullData*)user_data;
+
+ // Discard face if we already have a closer hit
+ if(hit.mDistance>=Data->Closest->mDistance) return;
+
+ // Discard face if hit point is smaller than min limit. This mainly happens when the face is in front
+ // of the near clip plane (or straddles it). If we keep the face nonetheless, the user can select an
+ // object that he may not even be able to see, which is very annoying.
+ if(hit.mDistance<=Data->MinLimit) return;
+
+ // This is the index of currently stabbed triangle.
+ udword StabbedFaceIndex = hit.mFaceID;
+
+ // We may keep it or not, depending on backface culling
+ bool KeepIt = true;
+
+ // Catch *render* cull mode for this face
+ CullMode CM = (Data->Callback)(StabbedFaceIndex, Data->UserData);
+
+ if(CM!=CULLMODE_NONE) // Don't even compute culling for double-sided triangles
+ {
+ // Compute backface culling for current face
+
+ VertexPointers VP;
+ Data->IMesh->GetTriangle(VP, StabbedFaceIndex);
+ if(VP.BackfaceCulling(Data->ViewPoint))
+ {
+ if(CM==CULLMODE_CW) KeepIt = false;
+ }
+ else
+ {
+ if(CM==CULLMODE_CCW) KeepIt = false;
+ }
+ }
+
+ if(KeepIt) *Data->Closest = hit;
+ }
+ };
+
+ RayCollider RC;
+ RC.SetMaxDist(max_dist);
+ RC.SetTemporalCoherence(false);
+ RC.SetCulling(false); // We need all faces since some of them can be double-sided
+ RC.SetFirstContact(false);
+ RC.SetHitCallback(Local::RenderCullingCallback);
+
+ picked_face.mFaceID = INVALID_ID;
+ picked_face.mDistance = MAX_FLOAT;
+ picked_face.mU = 0.0f;
+ picked_face.mV = 0.0f;
+
+ Local::CullData Data;
+ Data.Closest = &picked_face;
+ Data.MinLimit = min_dist;
+ Data.Callback = callback;
+ Data.UserData = user_data;
+ Data.ViewPoint = view_point;
+ Data.IMesh = model.GetMeshInterface();
+
+ if(world)
+ {
+ // Get matrices
+ Matrix4x4 InvWorld;
+ InvertPRMatrix(InvWorld, *world);
+
+ // Compute camera position in mesh space
+ Data.ViewPoint *= InvWorld;
+ }
+
+ RC.SetUserData(&Data);
+ if(RC.Collide(world_ray, model, world))
+ {
+ return picked_face.mFaceID!=INVALID_ID;
+ }
+ return false;
+}
+
+#endif
diff --git a/Opcode/OPC_Picking.h b/Opcode/OPC_Picking.h
new file mode 100644
index 0000000..74a87b2
--- /dev/null
+++ b/Opcode/OPC_Picking.h
@@ -0,0 +1,45 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code to perform "picking".
+ * \file OPC_Picking.h
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_PICKING_H__
+#define __OPC_PICKING_H__
+
+#ifdef OPC_RAYHIT_CALLBACK
+
+ enum CullMode
+ {
+ CULLMODE_NONE = 0,
+ CULLMODE_CW = 1,
+ CULLMODE_CCW = 2
+ };
+
+ typedef CullMode (*CullModeCallback)(udword triangle_index, void* user_data);
+
+ OPCODE_API bool SetupAllHits (RayCollider& collider, CollisionFaces& contacts);
+ OPCODE_API bool SetupClosestHit (RayCollider& collider, CollisionFace& closest_contact);
+ OPCODE_API bool SetupShadowFeeler (RayCollider& collider);
+ OPCODE_API bool SetupInOutTest (RayCollider& collider);
+
+ OPCODE_API bool Picking(
+ CollisionFace& picked_face,
+ const Ray& world_ray, const Model& model, const Matrix4x4* world,
+ float min_dist, float max_dist, const Point& view_point, CullModeCallback callback, void* user_data);
+#endif
+
+#endif //__OPC_PICKING_H__ \ No newline at end of file
diff --git a/Opcode/OPC_PlanesAABBOverlap.h b/Opcode/OPC_PlanesAABBOverlap.h
new file mode 100644
index 0000000..010b82f
--- /dev/null
+++ b/Opcode/OPC_PlanesAABBOverlap.h
@@ -0,0 +1,50 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Planes-AABB overlap test.
+ * - original code by Ville Miettinen, from Umbra/dPVS (released on the GD-Algorithms mailing list)
+ * - almost used "as-is", I even left the comments (hence the frustum-related notes)
+ *
+ * \param center [in] box center
+ * \param extents [in] box extents
+ * \param out_clip_mask [out] bitmask for active planes
+ * \param in_clip_mask [in] bitmask for active planes
+ * \return TRUE if boxes overlap planes
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL PlanesCollider::PlanesAABBOverlap(const Point& center, const Point& extents, udword& out_clip_mask, udword in_clip_mask)
+{
+ // Stats
+ mNbVolumeBVTests++;
+
+ const Plane* p = mPlanes;
+
+ // Evaluate through all active frustum planes. We determine the relation
+ // between the AABB and a plane by using the concept of "near" and "far"
+ // vertices originally described by Zhang (and later by Möller). Our
+ // variant here uses 3 fabs ops, 6 muls, 7 adds and two floating point
+ // comparisons per plane. The routine early-exits if the AABB is found
+ // to be outside any of the planes. The loop also constructs a new output
+ // clip mask. Most FPUs have a native single-cycle fabsf() operation.
+
+ udword Mask = 1; // current mask index (1,2,4,8,..)
+ udword TmpOutClipMask = 0; // initialize output clip mask into empty.
+
+ while(Mask<=in_clip_mask) // keep looping while we have active planes left...
+ {
+ if(in_clip_mask & Mask) // if clip plane is active, process it..
+ {
+ float NP = extents.x*fabsf(p->n.x) + extents.y*fabsf(p->n.y) + extents.z*fabsf(p->n.z); // ### fabsf could be precomputed
+ float MP = center.x*p->n.x + center.y*p->n.y + center.z*p->n.z + p->d;
+
+ if(NP < MP) // near vertex behind the clip plane...
+ return FALSE; // .. so there is no intersection..
+ if((-NP) < MP) // near and far vertices on different sides of plane..
+ TmpOutClipMask |= Mask; // .. so update the clip mask...
+ }
+ Mask+=Mask; // mk = (1<<plane)
+ p++; // advance to next plane
+ }
+
+ out_clip_mask = TmpOutClipMask; // copy output value (temp used to resolve aliasing!)
+ return TRUE; // indicate that AABB intersects frustum
+}
diff --git a/Opcode/OPC_PlanesCollider.cpp b/Opcode/OPC_PlanesCollider.cpp
new file mode 100644
index 0000000..89b507c
--- /dev/null
+++ b/Opcode/OPC_PlanesCollider.cpp
@@ -0,0 +1,653 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a planes collider.
+ * \file OPC_PlanesCollider.cpp
+ * \author Pierre Terdiman
+ * \date January, 1st, 2002
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a Planes-vs-tree collider.
+ *
+ * \class PlanesCollider
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date January, 1st, 2002
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+#include "OPC_PlanesAABBOverlap.h"
+#include "OPC_PlanesTriOverlap.h"
+
+#define SET_CONTACT(prim_index, flag) \
+ /* Set contact status */ \
+ mFlags |= flag; \
+ mTouchedPrimitives->Add(prim_index);
+
+//! Planes-triangle test
+#define PLANES_PRIM(prim_index, flag) \
+ /* Request vertices from the app */ \
+ mIMesh->GetTriangle(mVP, prim_index); \
+ /* Perform triangle-box overlap test */ \
+ if(PlanesTriOverlap(clip_mask)) \
+ { \
+ SET_CONTACT(prim_index, flag) \
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+PlanesCollider::PlanesCollider() :
+ mPlanes (null),
+ mNbPlanes (0)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+PlanesCollider::~PlanesCollider()
+{
+ DELETEARRAY(mPlanes);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Validates current settings. You should call this method after all the settings and callbacks have been defined.
+ * \return null if everything is ok, else a string describing the problem
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const char* PlanesCollider::ValidateSettings()
+{
+ if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!";
+
+ return VolumeCollider::ValidateSettings();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Generic collision query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - with GetNbTouchedPrimitives()
+ * - with GetTouchedPrimitives()
+ *
+ * \param cache [in/out] a planes cache
+ * \param planes [in] list of planes in world space
+ * \param nb_planes [in] number of planes
+ * \param model [in] Opcode model to collide with
+ * \param worldm [in] model's world matrix, or null
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool PlanesCollider::Collide(PlanesCache& cache, const Plane* planes, udword nb_planes, const Model& model, const Matrix4x4* worldm)
+{
+ // Checkings
+ if(!Setup(&model)) return false;
+
+ // Init collision query
+ if(InitQuery(cache, planes, nb_planes, worldm)) return true;
+
+ udword PlaneMask = (1<<nb_planes)-1;
+
+ if(!model.HasLeafNodes())
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask);
+ else _Collide(Tree->GetNodes(), PlaneMask);
+ }
+ else
+ {
+ const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask);
+ else _Collide(Tree->GetNodes(), PlaneMask);
+ }
+ }
+ else
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask);
+ else _Collide(Tree->GetNodes(), PlaneMask);
+ }
+ else
+ {
+ const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask);
+ else _Collide(Tree->GetNodes(), PlaneMask);
+ }
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Initializes a collision query :
+ * - reset stats & contact status
+ * - compute planes in model space
+ * - check temporal coherence
+ *
+ * \param cache [in/out] a planes cache
+ * \param planes [in] list of planes
+ * \param nb_planes [in] number of planes
+ * \param worldm [in] model's world matrix, or null
+ * \return TRUE if we can return immediately
+ * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+BOOL PlanesCollider::InitQuery(PlanesCache& cache, const Plane* planes, udword nb_planes, const Matrix4x4* worldm)
+{
+ // 1) Call the base method
+ VolumeCollider::InitQuery();
+
+ // 2) Compute planes in model space
+ if(nb_planes>mNbPlanes)
+ {
+ DELETEARRAY(mPlanes);
+ mPlanes = new Plane[nb_planes];
+ }
+ mNbPlanes = nb_planes;
+
+ if(worldm)
+ {
+ Matrix4x4 InvWorldM;
+ InvertPRMatrix(InvWorldM, *worldm);
+
+// for(udword i=0;i<nb_planes;i++) mPlanes[i] = planes[i] * InvWorldM;
+ for(udword i=0;i<nb_planes;i++) TransformPlane(mPlanes[i], planes[i], InvWorldM);
+ }
+ else CopyMemory(mPlanes, planes, nb_planes*sizeof(Plane));
+
+ // 3) Setup destination pointer
+ mTouchedPrimitives = &cache.TouchedPrimitives;
+
+ // 4) Special case: 1-triangle meshes [Opcode 1.3]
+ if(mCurrentModel && mCurrentModel->HasSingleNode())
+ {
+ if(!SkipPrimitiveTests())
+ {
+ // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
+ mTouchedPrimitives->Reset();
+
+ // Perform overlap test between the unique triangle and the planes (and set contact status if needed)
+ udword clip_mask = (1<<mNbPlanes)-1;
+ PLANES_PRIM(udword(0), OPC_CONTACT)
+
+ // Return immediately regardless of status
+ return TRUE;
+ }
+ }
+
+ // 4) Check temporal coherence:
+ if(TemporalCoherenceEnabled())
+ {
+ // Here we use temporal coherence
+ // => check results from previous frame before performing the collision query
+ if(FirstContactEnabled())
+ {
+ // We're only interested in the first contact found => test the unique previously touched face
+ if(mTouchedPrimitives->GetNbEntries())
+ {
+ // Get index of previously touched face = the first entry in the array
+ udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0);
+
+ // Then reset the array:
+ // - if the overlap test below is successful, the index we'll get added back anyway
+ // - if it isn't, then the array should be reset anyway for the normal query
+ mTouchedPrimitives->Reset();
+
+ // Perform overlap test between the cached triangle and the planes (and set contact status if needed)
+ udword clip_mask = (1<<mNbPlanes)-1;
+ PLANES_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT)
+
+ // Return immediately if possible
+ if(GetContactStatus()) return TRUE;
+ }
+ // else no face has been touched during previous query
+ // => we'll have to perform a normal query
+ }
+ else mTouchedPrimitives->Reset();
+ }
+ else
+ {
+ // Here we don't use temporal coherence => do a normal query
+ mTouchedPrimitives->Reset();
+ }
+
+ return FALSE;
+}
+
+#define TEST_CLIP_MASK \
+ /* If the box is completely included, so are its children. We don't need to do extra tests, we */ \
+ /* can immediately output a list of visible children. Those ones won't need to be clipped. */ \
+ if(!OutClipMask) \
+ { \
+ /* Set contact status */ \
+ mFlags |= OPC_CONTACT; \
+ _Dump(node); \
+ return; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void PlanesCollider::_Collide(const AABBCollisionNode* node, udword clip_mask)
+{
+ // Test the box against the planes. If the box is completely culled, so are its children, hence we exit.
+ udword OutClipMask;
+ if(!PlanesAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clip_mask)) return;
+
+ TEST_CLIP_MASK
+
+ // Else the box straddles one or several planes, so we need to recurse down the tree.
+ if(node->IsLeaf())
+ {
+ PLANES_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _Collide(node->GetPos(), OutClipMask);
+
+ if(ContactFound()) return;
+
+ _Collide(node->GetNeg(), OutClipMask);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void PlanesCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node, udword clip_mask)
+{
+ // Test the box against the planes. If the box is completely culled, so are its children, hence we exit.
+ udword OutClipMask;
+ if(!PlanesAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clip_mask)) return;
+
+ TEST_CLIP_MASK
+
+ // Else the box straddles one or several planes, so we need to recurse down the tree.
+ if(node->IsLeaf())
+ {
+ SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _CollideNoPrimitiveTest(node->GetPos(), OutClipMask);
+
+ if(ContactFound()) return;
+
+ _CollideNoPrimitiveTest(node->GetNeg(), OutClipMask);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void PlanesCollider::_Collide(const AABBQuantizedNode* node, udword clip_mask)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Test the box against the planes. If the box is completely culled, so are its children, hence we exit.
+ udword OutClipMask;
+ if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clip_mask)) return;
+
+ TEST_CLIP_MASK
+
+ // Else the box straddles one or several planes, so we need to recurse down the tree.
+ if(node->IsLeaf())
+ {
+ PLANES_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _Collide(node->GetPos(), OutClipMask);
+
+ if(ContactFound()) return;
+
+ _Collide(node->GetNeg(), OutClipMask);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void PlanesCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node, udword clip_mask)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Test the box against the planes. If the box is completely culled, so are its children, hence we exit.
+ udword OutClipMask;
+ if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clip_mask)) return;
+
+ TEST_CLIP_MASK
+
+ // Else the box straddles one or several planes, so we need to recurse down the tree.
+ if(node->IsLeaf())
+ {
+ SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _CollideNoPrimitiveTest(node->GetPos(), OutClipMask);
+
+ if(ContactFound()) return;
+
+ _CollideNoPrimitiveTest(node->GetNeg(), OutClipMask);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void PlanesCollider::_Collide(const AABBNoLeafNode* node, udword clip_mask)
+{
+ // Test the box against the planes. If the box is completely culled, so are its children, hence we exit.
+ udword OutClipMask;
+ if(!PlanesAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clip_mask)) return;
+
+ TEST_CLIP_MASK
+
+ // Else the box straddles one or several planes, so we need to recurse down the tree.
+ if(node->HasPosLeaf()) { PLANES_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetPos(), OutClipMask);
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { PLANES_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetNeg(), OutClipMask);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void PlanesCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node, udword clip_mask)
+{
+ // Test the box against the planes. If the box is completely culled, so are its children, hence we exit.
+ udword OutClipMask;
+ if(!PlanesAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clip_mask)) return;
+
+ TEST_CLIP_MASK
+
+ // Else the box straddles one or several planes, so we need to recurse down the tree.
+ if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetPos(), OutClipMask);
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetNeg(), OutClipMask);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void PlanesCollider::_Collide(const AABBQuantizedNoLeafNode* node, udword clip_mask)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Test the box against the planes. If the box is completely culled, so are its children, hence we exit.
+ udword OutClipMask;
+ if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clip_mask)) return;
+
+ TEST_CLIP_MASK
+
+ // Else the box straddles one or several planes, so we need to recurse down the tree.
+ if(node->HasPosLeaf()) { PLANES_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetPos(), OutClipMask);
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { PLANES_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetNeg(), OutClipMask);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void PlanesCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node, udword clip_mask)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Test the box against the planes. If the box is completely culled, so are its children, hence we exit.
+ udword OutClipMask;
+ if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clip_mask)) return;
+
+ TEST_CLIP_MASK
+
+ // Else the box straddles one or several planes, so we need to recurse down the tree.
+ if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetPos(), OutClipMask);
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetNeg(), OutClipMask);
+}
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridPlanesCollider::HybridPlanesCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridPlanesCollider::~HybridPlanesCollider()
+{
+}
+
+bool HybridPlanesCollider::Collide(PlanesCache& cache, const Plane* planes, udword nb_planes, const HybridModel& model, const Matrix4x4* worldm)
+{
+ // We don't want primitive tests here!
+ mFlags |= OPC_NO_PRIMITIVE_TESTS;
+
+ // Checkings
+ if(!Setup(&model)) return false;
+
+ // Init collision query
+ if(InitQuery(cache, planes, nb_planes, worldm)) return true;
+
+ // Special case for 1-leaf trees
+ if(mCurrentModel && mCurrentModel->HasSingleNode())
+ {
+ // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles
+ udword Nb = mIMesh->GetNbTriangles();
+
+ // Loop through all triangles
+ udword clip_mask = (1<<mNbPlanes)-1;
+ for(udword i=0;i<Nb;i++)
+ {
+ PLANES_PRIM(i, OPC_CONTACT)
+ }
+ return true;
+ }
+
+ // Override destination array since we're only going to get leaf boxes here
+ mTouchedBoxes.Reset();
+ mTouchedPrimitives = &mTouchedBoxes;
+
+ udword PlaneMask = (1<<nb_planes)-1;
+
+ // Now, do the actual query against leaf boxes
+ if(!model.HasLeafNodes())
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask);
+ }
+ else
+ {
+ const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask);
+ }
+ }
+ else
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask);
+ }
+ else
+ {
+ const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask);
+ }
+ }
+
+ // We only have a list of boxes so far
+ if(GetContactStatus())
+ {
+ // Reset contact status, since it currently only reflects collisions with leaf boxes
+ Collider::InitQuery();
+
+ // Change dest container so that we can use built-in overlap tests and get collided primitives
+ cache.TouchedPrimitives.Reset();
+ mTouchedPrimitives = &cache.TouchedPrimitives;
+
+ // Read touched leaf boxes
+ udword Nb = mTouchedBoxes.GetNbEntries();
+ const udword* Touched = mTouchedBoxes.GetEntries();
+
+ const LeafTriangles* LT = model.GetLeafTriangles();
+ const udword* Indices = model.GetIndices();
+
+ // Loop through touched leaves
+ udword clip_mask = (1<<mNbPlanes)-1;
+ while(Nb--)
+ {
+ const LeafTriangles& CurrentLeaf = LT[*Touched++];
+
+ // Each leaf box has a set of triangles
+ udword NbTris = CurrentLeaf.GetNbTriangles();
+ if(Indices)
+ {
+ const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ udword TriangleIndex = *T++;
+ PLANES_PRIM(TriangleIndex, OPC_CONTACT)
+ }
+ }
+ else
+ {
+ udword BaseIndex = CurrentLeaf.GetTriangleIndex();
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ udword TriangleIndex = BaseIndex++;
+ PLANES_PRIM(TriangleIndex, OPC_CONTACT)
+ }
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/Opcode/OPC_PlanesCollider.h b/Opcode/OPC_PlanesCollider.h
new file mode 100644
index 0000000..8ac63e8
--- /dev/null
+++ b/Opcode/OPC_PlanesCollider.h
@@ -0,0 +1,121 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a planes collider.
+ * \file OPC_PlanesCollider.h
+ * \author Pierre Terdiman
+ * \date January, 1st, 2002
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_PLANESCOLLIDER_H__
+#define __OPC_PLANESCOLLIDER_H__
+
+ struct OPCODE_API PlanesCache : VolumeCache
+ {
+ PlanesCache()
+ {
+ }
+ };
+
+ class OPCODE_API PlanesCollider : public VolumeCollider
+ {
+ public:
+ // Constructor / Destructor
+ PlanesCollider();
+ virtual ~PlanesCollider();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Generic collision query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - with GetNbTouchedPrimitives()
+ * - with GetTouchedPrimitives()
+ *
+ * \param cache [in/out] a planes cache
+ * \param planes [in] list of planes in world space
+ * \param nb_planes [in] number of planes
+ * \param model [in] Opcode model to collide with
+ * \param worldm [in] model's world matrix, or null
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool Collide(PlanesCache& cache, const Plane* planes, udword nb_planes, const Model& model, const Matrix4x4* worldm=null);
+
+ // Mutant box-with-planes collision queries
+ inline_ bool Collide(PlanesCache& cache, const OBB& box, const Model& model, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null)
+ {
+ Plane PL[6];
+
+ if(worldb)
+ {
+ // Create a new OBB in world space
+ OBB WorldBox;
+ box.Rotate(*worldb, WorldBox);
+ // Compute planes from the sides of the box
+ WorldBox.ComputePlanes(PL);
+ }
+ else
+ {
+ // Compute planes from the sides of the box
+ box.ComputePlanes(PL);
+ }
+
+ // Collide with box planes
+ return Collide(cache, PL, 6, model, worldm);
+ }
+ // Settings
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Validates current settings. You should call this method after all the settings and callbacks have been defined for a collider.
+ * \return null if everything is ok, else a string describing the problem
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(Collider) const char* ValidateSettings();
+
+ protected:
+ // Planes in model space
+ udword mNbPlanes;
+ Plane* mPlanes;
+ // Leaf description
+ VertexPointers mVP;
+ // Internal methods
+ void _Collide(const AABBCollisionNode* node, udword clip_mask);
+ void _Collide(const AABBNoLeafNode* node, udword clip_mask);
+ void _Collide(const AABBQuantizedNode* node, udword clip_mask);
+ void _Collide(const AABBQuantizedNoLeafNode* node, udword clip_mask);
+ void _CollideNoPrimitiveTest(const AABBCollisionNode* node, udword clip_mask);
+ void _CollideNoPrimitiveTest(const AABBNoLeafNode* node, udword clip_mask);
+ void _CollideNoPrimitiveTest(const AABBQuantizedNode* node, udword clip_mask);
+ void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node, udword clip_mask);
+ // Overlap tests
+ inline_ BOOL PlanesAABBOverlap(const Point& center, const Point& extents, udword& out_clip_mask, udword in_clip_mask);
+ inline_ BOOL PlanesTriOverlap(udword in_clip_mask);
+ // Init methods
+ BOOL InitQuery(PlanesCache& cache, const Plane* planes, udword nb_planes, const Matrix4x4* worldm=null);
+ };
+
+ class OPCODE_API HybridPlanesCollider : public PlanesCollider
+ {
+ public:
+ // Constructor / Destructor
+ HybridPlanesCollider();
+ virtual ~HybridPlanesCollider();
+
+ bool Collide(PlanesCache& cache, const Plane* planes, udword nb_planes, const HybridModel& model, const Matrix4x4* worldm=null);
+ protected:
+ Container mTouchedBoxes;
+ };
+
+#endif // __OPC_PLANESCOLLIDER_H__
diff --git a/Opcode/OPC_PlanesTriOverlap.h b/Opcode/OPC_PlanesTriOverlap.h
new file mode 100644
index 0000000..7667cbc
--- /dev/null
+++ b/Opcode/OPC_PlanesTriOverlap.h
@@ -0,0 +1,40 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Planes-triangle overlap test.
+ * \param in_clip_mask [in] bitmask for active planes
+ * \return TRUE if triangle overlap planes
+ * \warning THIS IS A CONSERVATIVE TEST !! Some triangles will be returned as intersecting, while they're not!
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL PlanesCollider::PlanesTriOverlap(udword in_clip_mask)
+{
+ // Stats
+ mNbVolumePrimTests++;
+
+ const Plane* p = mPlanes;
+ udword Mask = 1;
+
+ while(Mask<=in_clip_mask)
+ {
+ if(in_clip_mask & Mask)
+ {
+ float d0 = p->Distance(*mVP.Vertex[0]);
+ float d1 = p->Distance(*mVP.Vertex[1]);
+ float d2 = p->Distance(*mVP.Vertex[2]);
+ if(d0>0.0f && d1>0.0f && d2>0.0f) return FALSE;
+// if(!(IR(d0)&SIGN_BITMASK) && !(IR(d1)&SIGN_BITMASK) && !(IR(d2)&SIGN_BITMASK)) return FALSE;
+ }
+ Mask+=Mask;
+ p++;
+ }
+/*
+ for(udword i=0;i<6;i++)
+ {
+ float d0 = p[i].Distance(mLeafVerts[0]);
+ float d1 = p[i].Distance(mLeafVerts[1]);
+ float d2 = p[i].Distance(mLeafVerts[2]);
+ if(d0>0.0f && d1>0.0f && d2>0.0f) return false;
+ }
+*/
+ return TRUE;
+}
diff --git a/Opcode/OPC_RayAABBOverlap.h b/Opcode/OPC_RayAABBOverlap.h
new file mode 100644
index 0000000..4330652
--- /dev/null
+++ b/Opcode/OPC_RayAABBOverlap.h
@@ -0,0 +1,63 @@
+// Opcode 1.1: ray-AABB overlap tests based on Woo's code
+// Opcode 1.2: ray-AABB overlap tests based on the separating axis theorem
+//
+// The point of intersection is not computed anymore. The distance to impact is not needed anymore
+// since we now have two different queries for segments or rays.
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes a segment-AABB overlap test using the separating axis theorem. Segment is cached within the class.
+ * \param center [in] AABB center
+ * \param extents [in] AABB extents
+ * \return true on overlap
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL RayCollider::SegmentAABBOverlap(const Point& center, const Point& extents)
+{
+ // Stats
+ mNbRayBVTests++;
+
+ float Dx = mData2.x - center.x; if(fabsf(Dx) > extents.x + mFDir.x) return FALSE;
+ float Dy = mData2.y - center.y; if(fabsf(Dy) > extents.y + mFDir.y) return FALSE;
+ float Dz = mData2.z - center.z; if(fabsf(Dz) > extents.z + mFDir.z) return FALSE;
+
+ float f;
+ f = mData.y * Dz - mData.z * Dy; if(fabsf(f) > extents.y*mFDir.z + extents.z*mFDir.y) return FALSE;
+ f = mData.z * Dx - mData.x * Dz; if(fabsf(f) > extents.x*mFDir.z + extents.z*mFDir.x) return FALSE;
+ f = mData.x * Dy - mData.y * Dx; if(fabsf(f) > extents.x*mFDir.y + extents.y*mFDir.x) return FALSE;
+
+ return TRUE;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes a ray-AABB overlap test using the separating axis theorem. Ray is cached within the class.
+ * \param center [in] AABB center
+ * \param extents [in] AABB extents
+ * \return true on overlap
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL RayCollider::RayAABBOverlap(const Point& center, const Point& extents)
+{
+ // Stats
+ mNbRayBVTests++;
+
+// float Dx = mOrigin.x - center.x; if(fabsf(Dx) > extents.x && Dx*mDir.x>=0.0f) return FALSE;
+// float Dy = mOrigin.y - center.y; if(fabsf(Dy) > extents.y && Dy*mDir.y>=0.0f) return FALSE;
+// float Dz = mOrigin.z - center.z; if(fabsf(Dz) > extents.z && Dz*mDir.z>=0.0f) return FALSE;
+
+ float Dx = mOrigin.x - center.x; if(GREATER(Dx, extents.x) && Dx*mDir.x>=0.0f) return FALSE;
+ float Dy = mOrigin.y - center.y; if(GREATER(Dy, extents.y) && Dy*mDir.y>=0.0f) return FALSE;
+ float Dz = mOrigin.z - center.z; if(GREATER(Dz, extents.z) && Dz*mDir.z>=0.0f) return FALSE;
+
+// float Dx = mOrigin.x - center.x; if(GREATER(Dx, extents.x) && ((SIR(Dx)-1)^SIR(mDir.x))>=0.0f) return FALSE;
+// float Dy = mOrigin.y - center.y; if(GREATER(Dy, extents.y) && ((SIR(Dy)-1)^SIR(mDir.y))>=0.0f) return FALSE;
+// float Dz = mOrigin.z - center.z; if(GREATER(Dz, extents.z) && ((SIR(Dz)-1)^SIR(mDir.z))>=0.0f) return FALSE;
+
+ float f;
+ f = mDir.y * Dz - mDir.z * Dy; if(fabsf(f) > extents.y*mFDir.z + extents.z*mFDir.y) return FALSE;
+ f = mDir.z * Dx - mDir.x * Dz; if(fabsf(f) > extents.x*mFDir.z + extents.z*mFDir.x) return FALSE;
+ f = mDir.x * Dy - mDir.y * Dx; if(fabsf(f) > extents.x*mFDir.y + extents.y*mFDir.x) return FALSE;
+
+ return TRUE;
+}
diff --git a/Opcode/OPC_RayCollider.cpp b/Opcode/OPC_RayCollider.cpp
new file mode 100644
index 0000000..92c5a9b
--- /dev/null
+++ b/Opcode/OPC_RayCollider.cpp
@@ -0,0 +1,762 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a ray collider.
+ * \file OPC_RayCollider.cpp
+ * \author Pierre Terdiman
+ * \date June, 2, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a ray-vs-tree collider.
+ * This class performs a stabbing query on an AABB tree, i.e. does a ray-mesh collision.
+ *
+ * HIGHER DISTANCE BOUND:
+ *
+ * If P0 and P1 are two 3D points, let's define:
+ * - d = distance between P0 and P1
+ * - Origin = P0
+ * - Direction = (P1 - P0) / d = normalized direction vector
+ * - A parameter t such as a point P on the line (P0,P1) is P = Origin + t * Direction
+ * - t = 0 --> P = P0
+ * - t = d --> P = P1
+ *
+ * Then we can define a general "ray" as:
+ *
+ * struct Ray
+ * {
+ * Point Origin;
+ * Point Direction;
+ * };
+ *
+ * But it actually maps three different things:
+ * - a segment, when 0 <= t <= d
+ * - a half-line, when 0 <= t < +infinity, or -infinity < t <= d
+ * - a line, when -infinity < t < +infinity
+ *
+ * In Opcode, we support segment queries, which yield half-line queries by setting d = +infinity.
+ * We don't support line-queries. If you need them, shift the origin along the ray by an appropriate margin.
+ *
+ * In short, the lower bound is always 0, and you can setup the higher bound "d" with RayCollider::SetMaxDist().
+ *
+ * Query |segment |half-line |line
+ * --------|-------------------|---------------|----------------
+ * Usages |-shadow feelers |-raytracing |-
+ * |-sweep tests |-in/out tests |
+ *
+ * FIRST CONTACT:
+ *
+ * - You can setup "first contact" mode or "all contacts" mode with RayCollider::SetFirstContact().
+ * - In "first contact" mode we return as soon as the ray hits one face. If can be useful e.g. for shadow feelers, where
+ * you want to know whether the path to the light is free or not (a boolean answer is enough).
+ * - In "all contacts" mode we return all faces hit by the ray.
+ *
+ * TEMPORAL COHERENCE:
+ *
+ * - You can enable or disable temporal coherence with RayCollider::SetTemporalCoherence().
+ * - It currently only works in "first contact" mode.
+ * - If temporal coherence is enabled, the previously hit triangle is cached during the first query. Then, next queries
+ * start by colliding the ray against the cached triangle. If they still collide, we return immediately.
+ *
+ * CLOSEST HIT:
+ *
+ * - You can enable or disable "closest hit" with RayCollider::SetClosestHit().
+ * - It currently only works in "all contacts" mode.
+ * - If closest hit is enabled, faces are sorted by distance on-the-fly and the closest one only is reported.
+ *
+ * BACKFACE CULLING:
+ *
+ * - You can enable or disable backface culling with RayCollider::SetCulling().
+ * - If culling is enabled, ray will not hit back faces (only front faces).
+ *
+ *
+ *
+ * \class RayCollider
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date June, 2, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * This class describes a face hit by a ray or segment.
+ * This is a particular class dedicated to stabbing queries.
+ *
+ * \class CollisionFace
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * This class is a dedicated collection of CollisionFace.
+ *
+ * \class CollisionFaces
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+#include "OPC_RayAABBOverlap.h"
+#include "OPC_RayTriOverlap.h"
+
+#define SET_CONTACT(prim_index, flag) \
+ mNbIntersections++; \
+ /* Set contact status */ \
+ mFlags |= flag; \
+ /* In any case the contact has been found and recorded in mStabbedFace */ \
+ mStabbedFace.mFaceID = prim_index;
+
+#ifdef OPC_RAYHIT_CALLBACK
+
+ #define HANDLE_CONTACT(prim_index, flag) \
+ SET_CONTACT(prim_index, flag) \
+ \
+ if(mHitCallback) (mHitCallback)(mStabbedFace, mUserData);
+
+ #define UPDATE_CACHE \
+ if(cache && GetContactStatus()) \
+ { \
+ *cache = mStabbedFace.mFaceID; \
+ }
+#else
+
+ #define HANDLE_CONTACT(prim_index, flag) \
+ SET_CONTACT(prim_index, flag) \
+ \
+ /* Now we can also record it in mStabbedFaces if available */ \
+ if(mStabbedFaces) \
+ { \
+ /* If we want all faces or if that's the first one we hit */ \
+ if(!mClosestHit || !mStabbedFaces->GetNbFaces()) \
+ { \
+ mStabbedFaces->AddFace(mStabbedFace); \
+ } \
+ else \
+ { \
+ /* We only keep closest hit */ \
+ CollisionFace* Current = const_cast<CollisionFace*>(mStabbedFaces->GetFaces()); \
+ if(Current && mStabbedFace.mDistance<Current->mDistance) \
+ { \
+ *Current = mStabbedFace; \
+ } \
+ } \
+ }
+
+ #define UPDATE_CACHE \
+ if(cache && GetContactStatus() && mStabbedFaces) \
+ { \
+ const CollisionFace* Current = mStabbedFaces->GetFaces(); \
+ if(Current) *cache = Current->mFaceID; \
+ else *cache = INVALID_ID; \
+ }
+#endif
+
+#define SEGMENT_PRIM(prim_index, flag) \
+ /* Request vertices from the app */ \
+ VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
+ \
+ /* Perform ray-tri overlap test and return */ \
+ if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \
+ { \
+ /* Intersection point is valid if dist < segment's length */ \
+ /* We know dist>0 so we can use integers */ \
+ if(IR(mStabbedFace.mDistance)<IR(mMaxDist)) \
+ { \
+ HANDLE_CONTACT(prim_index, flag) \
+ } \
+ }
+
+#define RAY_PRIM(prim_index, flag) \
+ /* Request vertices from the app */ \
+ VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
+ \
+ /* Perform ray-tri overlap test and return */ \
+ if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \
+ { \
+ HANDLE_CONTACT(prim_index, flag) \
+ }
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+RayCollider::RayCollider() :
+ mNbRayBVTests (0),
+ mNbRayPrimTests (0),
+ mNbIntersections (0),
+ mCulling (true),
+#ifdef OPC_RAYHIT_CALLBACK
+ mHitCallback (null),
+ mUserData (0),
+#else
+ mClosestHit (false),
+ mStabbedFaces (null),
+#endif
+ mMaxDist (MAX_FLOAT)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+RayCollider::~RayCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Validates current settings. You should call this method after all the settings and callbacks have been defined.
+ * \return null if everything is ok, else a string describing the problem
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const char* RayCollider::ValidateSettings()
+{
+ if(mMaxDist<0.0f) return "Higher distance bound must be positive!";
+ if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!";
+#ifndef OPC_RAYHIT_CALLBACK
+ if(mClosestHit && FirstContactEnabled()) return "Closest hit doesn't work with ""First contact"" mode!";
+ if(TemporalCoherenceEnabled() && mClosestHit) return "Temporal coherence can't guarantee to report closest hit!";
+#endif
+ if(SkipPrimitiveTests()) return "SkipPrimitiveTests not possible for RayCollider ! (not implemented)";
+ return null;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Generic stabbing query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - in the user-provided destination array
+ *
+ * \param world_ray [in] stabbing ray in world space
+ * \param model [in] Opcode model to collide with
+ * \param world [in] model's world matrix, or null
+ * \param cache [in] a possibly cached face index, or null
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool RayCollider::Collide(const Ray& world_ray, const Model& model, const Matrix4x4* world, udword* cache)
+{
+ // Checkings
+ if(!Setup(&model)) return false;
+
+ // Init collision query
+ if(InitQuery(world_ray, world, cache)) return true;
+
+ if(!model.HasLeafNodes())
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform stabbing query
+ if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes());
+ else _RayStab(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
+
+ // Perform stabbing query
+ if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes());
+ else _RayStab(Tree->GetNodes());
+ }
+ }
+ else
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform stabbing query
+ if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes());
+ else _RayStab(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
+
+ // Perform stabbing query
+ if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes());
+ else _RayStab(Tree->GetNodes());
+ }
+ }
+
+ // Update cache if needed
+ UPDATE_CACHE
+ return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Initializes a stabbing query :
+ * - reset stats & contact status
+ * - compute ray in local space
+ * - check temporal coherence
+ *
+ * \param world_ray [in] stabbing ray in world space
+ * \param world [in] object's world matrix, or null
+ * \param face_id [in] index of previously stabbed triangle
+ * \return TRUE if we can return immediately
+ * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+BOOL RayCollider::InitQuery(const Ray& world_ray, const Matrix4x4* world, udword* face_id)
+{
+ // Reset stats & contact status
+ Collider::InitQuery();
+ mNbRayBVTests = 0;
+ mNbRayPrimTests = 0;
+ mNbIntersections = 0;
+#ifndef OPC_RAYHIT_CALLBACK
+ if(mStabbedFaces) mStabbedFaces->Reset();
+#endif
+
+ // Compute ray in local space
+ // The (Origin/Dir) form is needed for the ray-triangle test anyway (even for segment tests)
+ if(world)
+ {
+ Matrix3x3 InvWorld = *world;
+ mDir = InvWorld * world_ray.mDir;
+
+ Matrix4x4 World;
+ InvertPRMatrix(World, *world);
+ mOrigin = world_ray.mOrig * World;
+ }
+ else
+ {
+ mDir = world_ray.mDir;
+ mOrigin = world_ray.mOrig;
+ }
+
+ // 4) Special case: 1-triangle meshes [Opcode 1.3]
+ if(mCurrentModel && mCurrentModel->HasSingleNode())
+ {
+ // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
+ if(!SkipPrimitiveTests())
+ {
+ // Perform overlap test between the unique triangle and the ray (and set contact status if needed)
+ SEGMENT_PRIM(udword(0), OPC_CONTACT)
+
+ // Return immediately regardless of status
+ return TRUE;
+ }
+ }
+
+ // Check temporal coherence :
+
+ // Test previously colliding primitives first
+ if(TemporalCoherenceEnabled() && FirstContactEnabled() && face_id && *face_id!=INVALID_ID)
+ {
+#ifdef OLD_CODE
+#ifndef OPC_RAYHIT_CALLBACK
+ if(!mClosestHit)
+#endif
+ {
+ // Request vertices from the app
+ VertexPointers VP;
+ mIMesh->GetTriangle(VP, *face_id);
+ // Perform ray-cached tri overlap test
+ if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2]))
+ {
+ // Intersection point is valid if:
+ // - distance is positive (else it can just be a face behind the orig point)
+ // - distance is smaller than a given max distance (useful for shadow feelers)
+// if(mStabbedFace.mDistance>0.0f && mStabbedFace.mDistance<mMaxDist)
+ if(IR(mStabbedFace.mDistance)<IR(mMaxDist)) // The other test is already performed in RayTriOverlap
+ {
+ // Set contact status
+ mFlags |= OPC_TEMPORAL_CONTACT;
+
+ mStabbedFace.mFaceID = *face_id;
+
+#ifndef OPC_RAYHIT_CALLBACK
+ if(mStabbedFaces) mStabbedFaces->AddFace(mStabbedFace);
+#endif
+ return TRUE;
+ }
+ }
+ }
+#else
+ // New code
+ // We handle both Segment/ray queries with the same segment code, and a possible infinite limit
+ SEGMENT_PRIM(*face_id, OPC_TEMPORAL_CONTACT)
+
+ // Return immediately if possible
+ if(GetContactStatus()) return TRUE;
+#endif
+ }
+
+ // Precompute data (moved after temporal coherence since only needed for ray-AABB)
+ if(IR(mMaxDist)!=IEEE_MAX_FLOAT)
+ {
+ // For Segment-AABB overlap
+ mData = 0.5f * mDir * mMaxDist;
+ mData2 = mOrigin + mData;
+
+ // Precompute mFDir;
+ mFDir.x = fabsf(mData.x);
+ mFDir.y = fabsf(mData.y);
+ mFDir.z = fabsf(mData.z);
+ }
+ else
+ {
+ // For Ray-AABB overlap
+// udword x = SIR(mDir.x)-1;
+// udword y = SIR(mDir.y)-1;
+// udword z = SIR(mDir.z)-1;
+// mData.x = FR(x);
+// mData.y = FR(y);
+// mData.z = FR(z);
+
+ // Precompute mFDir;
+ mFDir.x = fabsf(mDir.x);
+ mFDir.y = fabsf(mDir.y);
+ mFDir.z = fabsf(mDir.z);
+ }
+
+ return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Stabbing query for vanilla AABB trees.
+ * \param world_ray [in] stabbing ray in world space
+ * \param tree [in] AABB tree
+ * \param box_indices [out] indices of stabbed boxes
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool RayCollider::Collide(const Ray& world_ray, const AABBTree* tree, Container& box_indices)
+{
+ // ### bad design here
+
+ // This is typically called for a scene tree, full of -AABBs-, not full of triangles.
+ // So we don't really have "primitives" to deal with. Hence it doesn't work with
+ // "FirstContact" + "TemporalCoherence".
+ ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) );
+
+ // Checkings
+ if(!tree) return false;
+
+ // Init collision query
+ // Basically this is only called to initialize precomputed data
+ if(InitQuery(world_ray)) return true;
+
+ // Perform stabbing query
+ if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(tree, box_indices);
+ else _RayStab(tree, box_indices);
+
+ return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for normal AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_SegmentStab(const AABBCollisionNode* node)
+{
+ // Perform Segment-AABB overlap test
+ if(!SegmentAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
+
+ if(node->IsLeaf())
+ {
+ SEGMENT_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _SegmentStab(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _SegmentStab(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for quantized AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_SegmentStab(const AABBQuantizedNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform Segment-AABB overlap test
+ if(!SegmentAABBOverlap(Center, Extents)) return;
+
+ if(node->IsLeaf())
+ {
+ SEGMENT_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _SegmentStab(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _SegmentStab(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_SegmentStab(const AABBNoLeafNode* node)
+{
+ // Perform Segment-AABB overlap test
+ if(!SegmentAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
+
+ if(node->HasPosLeaf())
+ {
+ SEGMENT_PRIM(node->GetPosPrimitive(), OPC_CONTACT)
+ }
+ else _SegmentStab(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf())
+ {
+ SEGMENT_PRIM(node->GetNegPrimitive(), OPC_CONTACT)
+ }
+ else _SegmentStab(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for quantized no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_SegmentStab(const AABBQuantizedNoLeafNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform Segment-AABB overlap test
+ if(!SegmentAABBOverlap(Center, Extents)) return;
+
+ if(node->HasPosLeaf())
+ {
+ SEGMENT_PRIM(node->GetPosPrimitive(), OPC_CONTACT)
+ }
+ else _SegmentStab(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf())
+ {
+ SEGMENT_PRIM(node->GetNegPrimitive(), OPC_CONTACT)
+ }
+ else _SegmentStab(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for vanilla AABB trees.
+ * \param node [in] current collision node
+ * \param box_indices [out] indices of stabbed boxes
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_SegmentStab(const AABBTreeNode* node, Container& box_indices)
+{
+ // Test the box against the segment
+ Point Center, Extents;
+ node->GetAABB()->GetCenter(Center);
+ node->GetAABB()->GetExtents(Extents);
+ if(!SegmentAABBOverlap(Center, Extents)) return;
+
+ if(node->IsLeaf())
+ {
+ box_indices.Add(node->GetPrimitives(), node->GetNbPrimitives());
+ }
+ else
+ {
+ _SegmentStab(node->GetPos(), box_indices);
+ _SegmentStab(node->GetNeg(), box_indices);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for normal AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_RayStab(const AABBCollisionNode* node)
+{
+ // Perform Ray-AABB overlap test
+ if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
+
+ if(node->IsLeaf())
+ {
+ RAY_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _RayStab(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _RayStab(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for quantized AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_RayStab(const AABBQuantizedNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform Ray-AABB overlap test
+ if(!RayAABBOverlap(Center, Extents)) return;
+
+ if(node->IsLeaf())
+ {
+ RAY_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _RayStab(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _RayStab(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_RayStab(const AABBNoLeafNode* node)
+{
+ // Perform Ray-AABB overlap test
+ if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
+
+ if(node->HasPosLeaf())
+ {
+ RAY_PRIM(node->GetPosPrimitive(), OPC_CONTACT)
+ }
+ else _RayStab(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf())
+ {
+ RAY_PRIM(node->GetNegPrimitive(), OPC_CONTACT)
+ }
+ else _RayStab(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for quantized no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_RayStab(const AABBQuantizedNoLeafNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform Ray-AABB overlap test
+ if(!RayAABBOverlap(Center, Extents)) return;
+
+ if(node->HasPosLeaf())
+ {
+ RAY_PRIM(node->GetPosPrimitive(), OPC_CONTACT)
+ }
+ else _RayStab(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf())
+ {
+ RAY_PRIM(node->GetNegPrimitive(), OPC_CONTACT)
+ }
+ else _RayStab(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for vanilla AABB trees.
+ * \param node [in] current collision node
+ * \param box_indices [out] indices of stabbed boxes
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_RayStab(const AABBTreeNode* node, Container& box_indices)
+{
+ // Test the box against the ray
+ Point Center, Extents;
+ node->GetAABB()->GetCenter(Center);
+ node->GetAABB()->GetExtents(Extents);
+ if(!RayAABBOverlap(Center, Extents)) return;
+
+ if(node->IsLeaf())
+ {
+ mFlags |= OPC_CONTACT;
+ box_indices.Add(node->GetPrimitives(), node->GetNbPrimitives());
+ }
+ else
+ {
+ _RayStab(node->GetPos(), box_indices);
+ _RayStab(node->GetNeg(), box_indices);
+ }
+}
diff --git a/Opcode/OPC_RayCollider.h b/Opcode/OPC_RayCollider.h
new file mode 100644
index 0000000..f3b0065
--- /dev/null
+++ b/Opcode/OPC_RayCollider.h
@@ -0,0 +1,225 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a ray collider.
+ * \file OPC_RayCollider.h
+ * \author Pierre Terdiman
+ * \date June, 2, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_RAYCOLLIDER_H__
+#define __OPC_RAYCOLLIDER_H__
+
+ class OPCODE_API CollisionFace
+ {
+ public:
+ //! Constructor
+ inline_ CollisionFace() {}
+ //! Destructor
+ inline_ ~CollisionFace() {}
+
+ udword mFaceID; //!< Index of touched face
+ float mDistance; //!< Distance from collider to hitpoint
+ float mU, mV; //!< Impact barycentric coordinates
+ };
+
+ class OPCODE_API CollisionFaces : private Container
+ {
+ public:
+ //! Constructor
+ CollisionFaces() {}
+ //! Destructor
+ ~CollisionFaces() {}
+
+ inline_ udword GetNbFaces() const { return GetNbEntries()>>2; }
+ inline_ const CollisionFace* GetFaces() const { return (const CollisionFace*)GetEntries(); }
+
+ inline_ void Reset() { Container::Reset(); }
+
+ inline_ void AddFace(const CollisionFace& face) { Add(face.mFaceID).Add(face.mDistance).Add(face.mU).Add(face.mV); }
+ };
+
+#ifdef OPC_RAYHIT_CALLBACK
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * User-callback, called by OPCODE to record a hit.
+ * \param hit [in] current hit
+ * \param user_data [in] user-defined data from SetCallback()
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ typedef void (*HitCallback) (const CollisionFace& hit, void* user_data);
+#endif
+
+ class OPCODE_API RayCollider : public Collider
+ {
+ public:
+ // Constructor / Destructor
+ RayCollider();
+ virtual ~RayCollider();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Generic stabbing query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - in the user-provided destination array
+ *
+ * \param world_ray [in] stabbing ray in world space
+ * \param model [in] Opcode model to collide with
+ * \param world [in] model's world matrix, or null
+ * \param cache [in] a possibly cached face index, or null
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool Collide(const Ray& world_ray, const Model& model, const Matrix4x4* world=null, udword* cache=null);
+ //
+ bool Collide(const Ray& world_ray, const AABBTree* tree, Container& box_indices);
+ // Settings
+
+#ifndef OPC_RAYHIT_CALLBACK
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Settings: enable or disable "closest hit" mode.
+ * \param flag [in] true to report closest hit only
+ * \see SetCulling(bool flag)
+ * \see SetMaxDist(float max_dist)
+ * \see SetDestination(StabbedFaces* sf)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetClosestHit(bool flag) { mClosestHit = flag; }
+#endif
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Settings: enable or disable backface culling.
+ * \param flag [in] true to enable backface culling
+ * \see SetClosestHit(bool flag)
+ * \see SetMaxDist(float max_dist)
+ * \see SetDestination(StabbedFaces* sf)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetCulling(bool flag) { mCulling = flag; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Settings: sets the higher distance bound.
+ * \param max_dist [in] higher distance bound. Default = maximal value, for ray queries (else segment)
+ * \see SetClosestHit(bool flag)
+ * \see SetCulling(bool flag)
+ * \see SetDestination(StabbedFaces* sf)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetMaxDist(float max_dist=MAX_FLOAT) { mMaxDist = max_dist; }
+
+#ifdef OPC_RAYHIT_CALLBACK
+ inline_ void SetHitCallback(HitCallback cb) { mHitCallback = cb; }
+ inline_ void SetUserData(void* user_data) { mUserData = user_data; }
+#else
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Settings: sets the destination array for stabbed faces.
+ * \param cf [in] destination array, filled during queries
+ * \see SetClosestHit(bool flag)
+ * \see SetCulling(bool flag)
+ * \see SetMaxDist(float max_dist)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetDestination(CollisionFaces* cf) { mStabbedFaces = cf; }
+#endif
+ // Stats
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Stats: gets the number of Ray-BV overlap tests after a collision query.
+ * \see GetNbRayPrimTests()
+ * \see GetNbIntersections()
+ * \return the number of Ray-BV tests performed during last query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbRayBVTests() const { return mNbRayBVTests; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Stats: gets the number of Ray-Triangle overlap tests after a collision query.
+ * \see GetNbRayBVTests()
+ * \see GetNbIntersections()
+ * \return the number of Ray-Triangle tests performed during last query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbRayPrimTests() const { return mNbRayPrimTests; }
+
+ // In-out test
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Stats: gets the number of intersection found after a collision query. Can be used for in/out tests.
+ * \see GetNbRayBVTests()
+ * \see GetNbRayPrimTests()
+ * \return the number of valid intersections during last query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbIntersections() const { return mNbIntersections; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Validates current settings. You should call this method after all the settings and callbacks have been defined for a collider.
+ * \return null if everything is ok, else a string describing the problem
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(Collider) const char* ValidateSettings();
+
+ protected:
+ // Ray in local space
+ Point mOrigin; //!< Ray origin
+ Point mDir; //!< Ray direction (normalized)
+ Point mFDir; //!< fabsf(mDir)
+ Point mData, mData2;
+ // Stabbed faces
+ CollisionFace mStabbedFace; //!< Current stabbed face
+#ifdef OPC_RAYHIT_CALLBACK
+ HitCallback mHitCallback; //!< Callback used to record a hit
+ void* mUserData; //!< User-defined data
+#else
+ CollisionFaces* mStabbedFaces; //!< List of stabbed faces
+#endif
+ // Stats
+ udword mNbRayBVTests; //!< Number of Ray-BV tests
+ udword mNbRayPrimTests; //!< Number of Ray-Primitive tests
+ // In-out test
+ udword mNbIntersections; //!< Number of valid intersections
+ // Dequantization coeffs
+ Point mCenterCoeff;
+ Point mExtentsCoeff;
+ // Settings
+ float mMaxDist; //!< Valid segment on the ray
+#ifndef OPC_RAYHIT_CALLBACK
+ bool mClosestHit; //!< Report closest hit only
+#endif
+ bool mCulling; //!< Stab culled faces or not
+ // Internal methods
+ void _SegmentStab(const AABBCollisionNode* node);
+ void _SegmentStab(const AABBNoLeafNode* node);
+ void _SegmentStab(const AABBQuantizedNode* node);
+ void _SegmentStab(const AABBQuantizedNoLeafNode* node);
+ void _SegmentStab(const AABBTreeNode* node, Container& box_indices);
+ void _RayStab(const AABBCollisionNode* node);
+ void _RayStab(const AABBNoLeafNode* node);
+ void _RayStab(const AABBQuantizedNode* node);
+ void _RayStab(const AABBQuantizedNoLeafNode* node);
+ void _RayStab(const AABBTreeNode* node, Container& box_indices);
+ // Overlap tests
+ inline_ BOOL RayAABBOverlap(const Point& center, const Point& extents);
+ inline_ BOOL SegmentAABBOverlap(const Point& center, const Point& extents);
+ inline_ BOOL RayTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2);
+ // Init methods
+ BOOL InitQuery(const Ray& world_ray, const Matrix4x4* world=null, udword* face_id=null);
+ };
+
+#endif // __OPC_RAYCOLLIDER_H__
diff --git a/Opcode/OPC_RayTriOverlap.h b/Opcode/OPC_RayTriOverlap.h
new file mode 100644
index 0000000..550effc
--- /dev/null
+++ b/Opcode/OPC_RayTriOverlap.h
@@ -0,0 +1,89 @@
+#define LOCAL_EPSILON 0.000001f
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes a ray-triangle intersection test.
+ * Original code from Tomas Möller's "Fast Minimum Storage Ray-Triangle Intersection".
+ * It's been optimized a bit with integer code, and modified to return a non-intersection if distance from
+ * ray origin to triangle is negative.
+ *
+ * \param vert0 [in] triangle vertex
+ * \param vert1 [in] triangle vertex
+ * \param vert2 [in] triangle vertex
+ * \return true on overlap. mStabbedFace is filled with relevant info.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL RayCollider::RayTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2)
+{
+ // Stats
+ mNbRayPrimTests++;
+
+ // Find vectors for two edges sharing vert0
+ Point edge1 = vert1 - vert0;
+ Point edge2 = vert2 - vert0;
+
+ // Begin calculating determinant - also used to calculate U parameter
+ Point pvec = mDir^edge2;
+
+ // If determinant is near zero, ray lies in plane of triangle
+ float det = edge1|pvec;
+
+ if(mCulling)
+ {
+ if(det<LOCAL_EPSILON) return FALSE;
+ // From here, det is > 0. So we can use integer cmp.
+
+ // Calculate distance from vert0 to ray origin
+ Point tvec = mOrigin - vert0;
+
+ // Calculate U parameter and test bounds
+ mStabbedFace.mU = tvec|pvec;
+// if(IR(u)&0x80000000 || u>det) return FALSE;
+ if(IS_NEGATIVE_FLOAT(mStabbedFace.mU) || IR(mStabbedFace.mU)>IR(det)) return FALSE;
+
+ // Prepare to test V parameter
+ Point qvec = tvec^edge1;
+
+ // Calculate V parameter and test bounds
+ mStabbedFace.mV = mDir|qvec;
+ if(IS_NEGATIVE_FLOAT(mStabbedFace.mV) || mStabbedFace.mU+mStabbedFace.mV>det) return FALSE;
+
+ // Calculate t, scale parameters, ray intersects triangle
+ mStabbedFace.mDistance = edge2|qvec;
+ // Det > 0 so we can early exit here
+ // Intersection point is valid if distance is positive (else it can just be a face behind the orig point)
+ if(IS_NEGATIVE_FLOAT(mStabbedFace.mDistance)) return FALSE;
+ // Else go on
+ float OneOverDet = 1.0f / det;
+ mStabbedFace.mDistance *= OneOverDet;
+ mStabbedFace.mU *= OneOverDet;
+ mStabbedFace.mV *= OneOverDet;
+ }
+ else
+ {
+ // the non-culling branch
+ if(det>-LOCAL_EPSILON && det<LOCAL_EPSILON) return FALSE;
+ float OneOverDet = 1.0f / det;
+
+ // Calculate distance from vert0 to ray origin
+ Point tvec = mOrigin - vert0;
+
+ // Calculate U parameter and test bounds
+ mStabbedFace.mU = (tvec|pvec) * OneOverDet;
+// if(IR(u)&0x80000000 || u>1.0f) return FALSE;
+ if(IS_NEGATIVE_FLOAT(mStabbedFace.mU) || IR(mStabbedFace.mU)>IEEE_1_0) return FALSE;
+
+ // prepare to test V parameter
+ Point qvec = tvec^edge1;
+
+ // Calculate V parameter and test bounds
+ mStabbedFace.mV = (mDir|qvec) * OneOverDet;
+ if(IS_NEGATIVE_FLOAT(mStabbedFace.mV) || mStabbedFace.mU+mStabbedFace.mV>1.0f) return FALSE;
+
+ // Calculate t, ray intersects triangle
+ mStabbedFace.mDistance = (edge2|qvec) * OneOverDet;
+ // Intersection point is valid if distance is positive (else it can just be a face behind the orig point)
+ if(IS_NEGATIVE_FLOAT(mStabbedFace.mDistance)) return FALSE;
+ }
+ return TRUE;
+}
diff --git a/Opcode/OPC_Settings.h b/Opcode/OPC_Settings.h
new file mode 100644
index 0000000..8232d9b
--- /dev/null
+++ b/Opcode/OPC_Settings.h
@@ -0,0 +1,49 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains compilation flags.
+ * \file OPC_Settings.h
+ * \author Pierre Terdiman
+ * \date May, 12, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_SETTINGS_H__
+#define __OPC_SETTINGS_H__
+
+ //! Use CPU comparisons (comment that line to use standard FPU compares)
+ #define OPC_CPU_COMPARE
+
+ //! Use FCOMI / FCMOV on Pentium-Pro based processors (comment that line to use plain C++)
+ #define OPC_USE_FCOMI
+
+ //! Use epsilon value in tri-tri overlap test
+ #define OPC_TRITRI_EPSILON_TEST
+
+ //! Use tree-coherence or not [not implemented yet]
+// #define OPC_USE_TREE_COHERENCE
+
+ //! Use callbacks or direct pointers. Using callbacks might be a bit slower (but probably not much)
+// #define OPC_USE_CALLBACKS
+
+ //! Support triangle and vertex strides or not. Using strides might be a bit slower (but probably not much)
+// #define OPC_USE_STRIDE
+
+ //! Discard negative pointer in vanilla trees
+ #define OPC_NO_NEG_VANILLA_TREE
+
+ //! Use a callback in the ray collider
+ #define OPC_RAYHIT_CALLBACK
+
+ // NB: no compilation flag to enable/disable stats since they're actually needed in the box/box overlap test
+
+#endif //__OPC_SETTINGS_H__ \ No newline at end of file
diff --git a/Opcode/OPC_SphereAABBOverlap.h b/Opcode/OPC_SphereAABBOverlap.h
new file mode 100644
index 0000000..8a50aa9
--- /dev/null
+++ b/Opcode/OPC_SphereAABBOverlap.h
@@ -0,0 +1,128 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sphere-AABB overlap test, based on Jim Arvo's code.
+ * \param center [in] box center
+ * \param extents [in] box extents
+ * \return TRUE on overlap
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL SphereCollider::SphereAABBOverlap(const Point& center, const Point& extents)
+{
+ // Stats
+ mNbVolumeBVTests++;
+
+ float d = 0.0f;
+
+ //find the square of the distance
+ //from the sphere to the box
+#ifdef OLDIES
+ for(udword i=0;i<3;i++)
+ {
+ float tmp = mCenter[i] - center[i];
+ float s = tmp + extents[i];
+
+ if(s<0.0f) d += s*s;
+ else
+ {
+ s = tmp - extents[i];
+ if(s>0.0f) d += s*s;
+ }
+ }
+#endif
+
+//#ifdef NEW_TEST
+
+// float tmp = mCenter.x - center.x;
+// float s = tmp + extents.x;
+
+ float tmp,s;
+
+ tmp = mCenter.x - center.x;
+ s = tmp + extents.x;
+
+ if(s<0.0f)
+ {
+ d += s*s;
+ if(d>mRadius2) return FALSE;
+ }
+ else
+ {
+ s = tmp - extents.x;
+ if(s>0.0f)
+ {
+ d += s*s;
+ if(d>mRadius2) return FALSE;
+ }
+ }
+
+ tmp = mCenter.y - center.y;
+ s = tmp + extents.y;
+
+ if(s<0.0f)
+ {
+ d += s*s;
+ if(d>mRadius2) return FALSE;
+ }
+ else
+ {
+ s = tmp - extents.y;
+ if(s>0.0f)
+ {
+ d += s*s;
+ if(d>mRadius2) return FALSE;
+ }
+ }
+
+ tmp = mCenter.z - center.z;
+ s = tmp + extents.z;
+
+ if(s<0.0f)
+ {
+ d += s*s;
+ if(d>mRadius2) return FALSE;
+ }
+ else
+ {
+ s = tmp - extents.z;
+ if(s>0.0f)
+ {
+ d += s*s;
+ if(d>mRadius2) return FALSE;
+ }
+ }
+//#endif
+
+#ifdef OLDIES
+// Point Min = center - extents;
+// Point Max = center + extents;
+
+ float d = 0.0f;
+
+ //find the square of the distance
+ //from the sphere to the box
+ for(udword i=0;i<3;i++)
+ {
+float Min = center[i] - extents[i];
+
+// if(mCenter[i]<Min[i])
+ if(mCenter[i]<Min)
+ {
+// float s = mCenter[i] - Min[i];
+ float s = mCenter[i] - Min;
+ d += s*s;
+ }
+ else
+ {
+float Max = center[i] + extents[i];
+
+// if(mCenter[i]>Max[i])
+ if(mCenter[i]>Max)
+ {
+ float s = mCenter[i] - Max;
+ d += s*s;
+ }
+ }
+ }
+#endif
+ return d <= mRadius2;
+}
diff --git a/Opcode/OPC_SphereCollider.cpp b/Opcode/OPC_SphereCollider.cpp
new file mode 100644
index 0000000..4f2de83
--- /dev/null
+++ b/Opcode/OPC_SphereCollider.cpp
@@ -0,0 +1,726 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a sphere collider.
+ * \file OPC_SphereCollider.cpp
+ * \author Pierre Terdiman
+ * \date June, 2, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a sphere-vs-tree collider.
+ * This class performs a collision test between a sphere and an AABB tree. You can use this to do a standard player vs world collision,
+ * in a Nettle/Telemachos way. It doesn't suffer from all reported bugs in those two classic codes - the "new" one by Paul Nettle is a
+ * debuggued version I think. Collision response can be driven by reported collision data - it works extremely well for me. In sake of
+ * efficiency, all meshes (that is, all AABB trees) should of course also be kept in an extra hierarchical structure (octree, whatever).
+ *
+ * \class SphereCollider
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date June, 2, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+#include "OPC_SphereAABBOverlap.h"
+#include "OPC_SphereTriOverlap.h"
+
+#define SET_CONTACT(prim_index, flag) \
+ /* Set contact status */ \
+ mFlags |= flag; \
+ mTouchedPrimitives->Add(prim_index);
+
+//! Sphere-triangle overlap test
+#define SPHERE_PRIM(prim_index, flag) \
+ /* Request vertices from the app */ \
+ VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
+ \
+ /* Perform sphere-tri overlap test */ \
+ if(SphereTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \
+ { \
+ SET_CONTACT(prim_index, flag) \
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+SphereCollider::SphereCollider()
+{
+ mCenter.Zero();
+ mRadius2 = 0.0f;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+SphereCollider::~SphereCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Generic collision query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - with GetNbTouchedPrimitives()
+ * - with GetTouchedPrimitives()
+ *
+ * \param cache [in/out] a sphere cache
+ * \param sphere [in] collision sphere in local space
+ * \param model [in] Opcode model to collide with
+ * \param worlds [in] sphere's world matrix, or null
+ * \param worldm [in] model's world matrix, or null
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const Model& model, const Matrix4x4* worlds, const Matrix4x4* worldm)
+{
+ // Checkings
+ if(!Setup(&model)) return false;
+
+ // Init collision query
+ if(InitQuery(cache, sphere, worlds, worldm)) return true;
+
+ if(!model.HasLeafNodes())
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ }
+ else
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Initializes a collision query :
+ * - reset stats & contact status
+ * - setup matrices
+ * - check temporal coherence
+ *
+ * \param cache [in/out] a sphere cache
+ * \param sphere [in] sphere in local space
+ * \param worlds [in] sphere's world matrix, or null
+ * \param worldm [in] model's world matrix, or null
+ * \return TRUE if we can return immediately
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+BOOL SphereCollider::InitQuery(SphereCache& cache, const Sphere& sphere, const Matrix4x4* worlds, const Matrix4x4* worldm)
+{
+ // 1) Call the base method
+ VolumeCollider::InitQuery();
+
+ // 2) Compute sphere in model space:
+ // - Precompute R^2
+ mRadius2 = sphere.mRadius * sphere.mRadius;
+ // - Compute center position
+ mCenter = sphere.mCenter;
+ // -> to world space
+ if(worlds) mCenter *= *worlds;
+ // -> to model space
+ if(worldm)
+ {
+ // Invert model matrix
+ Matrix4x4 InvWorldM;
+ InvertPRMatrix(InvWorldM, *worldm);
+
+ mCenter *= InvWorldM;
+ }
+
+ // 3) Setup destination pointer
+ mTouchedPrimitives = &cache.TouchedPrimitives;
+
+ // 4) Special case: 1-triangle meshes [Opcode 1.3]
+ if(mCurrentModel && mCurrentModel->HasSingleNode())
+ {
+ if(!SkipPrimitiveTests())
+ {
+ // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
+ mTouchedPrimitives->Reset();
+
+ // Perform overlap test between the unique triangle and the sphere (and set contact status if needed)
+ SPHERE_PRIM(udword(0), OPC_CONTACT)
+
+ // Return immediately regardless of status
+ return TRUE;
+ }
+ }
+
+ // 5) Check temporal coherence :
+ if(TemporalCoherenceEnabled())
+ {
+ // Here we use temporal coherence
+ // => check results from previous frame before performing the collision query
+ if(FirstContactEnabled())
+ {
+ // We're only interested in the first contact found => test the unique previously touched face
+ if(mTouchedPrimitives->GetNbEntries())
+ {
+ // Get index of previously touched face = the first entry in the array
+ udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0);
+
+ // Then reset the array:
+ // - if the overlap test below is successful, the index we'll get added back anyway
+ // - if it isn't, then the array should be reset anyway for the normal query
+ mTouchedPrimitives->Reset();
+
+ // Perform overlap test between the cached triangle and the sphere (and set contact status if needed)
+ SPHERE_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT)
+
+ // Return immediately if possible
+ if(GetContactStatus()) return TRUE;
+ }
+ // else no face has been touched during previous query
+ // => we'll have to perform a normal query
+ }
+ else
+ {
+ // We're interested in all contacts =>test the new real sphere N(ew) against the previous fat sphere P(revious):
+ float r = sqrtf(cache.FatRadius2) - sphere.mRadius;
+ if(IsCacheValid(cache) && cache.Center.SquareDistance(mCenter) < r*r)
+ {
+ // - if N is included in P, return previous list
+ // => we simply leave the list (mTouchedFaces) unchanged
+
+ // Set contact status if needed
+ if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT;
+
+ // In any case we don't need to do a query
+ return TRUE;
+ }
+ else
+ {
+ // - else do the query using a fat N
+
+ // Reset cache since we'll about to perform a real query
+ mTouchedPrimitives->Reset();
+
+ // Make a fat sphere so that coherence will work for subsequent frames
+ mRadius2 *= cache.FatCoeff;
+// mRadius2 = (sphere.mRadius * cache.FatCoeff)*(sphere.mRadius * cache.FatCoeff);
+
+ // Update cache with query data (signature for cached faces)
+ cache.Center = mCenter;
+ cache.FatRadius2 = mRadius2;
+ }
+ }
+ }
+ else
+ {
+ // Here we don't use temporal coherence => do a normal query
+ mTouchedPrimitives->Reset();
+ }
+
+ return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Collision query for vanilla AABB trees.
+ * \param cache [in/out] a sphere cache
+ * \param sphere [in] collision sphere in world space
+ * \param tree [in] AABB tree
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const AABBTree* tree)
+{
+ // This is typically called for a scene tree, full of -AABBs-, not full of triangles.
+ // So we don't really have "primitives" to deal with. Hence it doesn't work with
+ // "FirstContact" + "TemporalCoherence".
+ ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) );
+
+ // Checkings
+ if(!tree) return false;
+
+ // Init collision query
+ if(InitQuery(cache, sphere)) return true;
+
+ // Perform collision query
+ _Collide(tree);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks the sphere completely contains the box. In which case we can end the query sooner.
+ * \param bc [in] box center
+ * \param be [in] box extents
+ * \return true if the sphere contains the whole box
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL SphereCollider::SphereContainsBox(const Point& bc, const Point& be)
+{
+ // I assume if all 8 box vertices are inside the sphere, so does the whole box.
+ // Sounds ok but maybe there's a better way?
+ Point p;
+ p.x=bc.x+be.x; p.y=bc.y+be.y; p.z=bc.z+be.z; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
+ p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
+ p.x=bc.x+be.x; p.y=bc.y-be.y; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
+ p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
+ p.x=bc.x+be.x; p.y=bc.y+be.y; p.z=bc.z-be.z; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
+ p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
+ p.x=bc.x+be.x; p.y=bc.y-be.y; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
+ p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
+
+ return TRUE;
+}
+
+#define TEST_BOX_IN_SPHERE(center, extents) \
+ if(SphereContainsBox(center, extents)) \
+ { \
+ /* Set contact status */ \
+ mFlags |= OPC_CONTACT; \
+ _Dump(node); \
+ return; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void SphereCollider::_Collide(const AABBCollisionNode* node)
+{
+ // Perform Sphere-AABB overlap test
+ if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
+
+ TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->IsLeaf())
+ {
+ SPHERE_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _Collide(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void SphereCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node)
+{
+ // Perform Sphere-AABB overlap test
+ if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
+
+ TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->IsLeaf())
+ {
+ SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _CollideNoPrimitiveTest(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void SphereCollider::_Collide(const AABBQuantizedNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform Sphere-AABB overlap test
+ if(!SphereAABBOverlap(Center, Extents)) return;
+
+ TEST_BOX_IN_SPHERE(Center, Extents)
+
+ if(node->IsLeaf())
+ {
+ SPHERE_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _Collide(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void SphereCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform Sphere-AABB overlap test
+ if(!SphereAABBOverlap(Center, Extents)) return;
+
+ TEST_BOX_IN_SPHERE(Center, Extents)
+
+ if(node->IsLeaf())
+ {
+ SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _CollideNoPrimitiveTest(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void SphereCollider::_Collide(const AABBNoLeafNode* node)
+{
+ // Perform Sphere-AABB overlap test
+ if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
+
+ TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->HasPosLeaf()) { SPHERE_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { SPHERE_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for no-leaf AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void SphereCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node)
+{
+ // Perform Sphere-AABB overlap test
+ if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
+
+ TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void SphereCollider::_Collide(const AABBQuantizedNoLeafNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform Sphere-AABB overlap test
+ if(!SphereAABBOverlap(Center, Extents)) return;
+
+ TEST_BOX_IN_SPHERE(Center, Extents)
+
+ if(node->HasPosLeaf()) { SPHERE_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { SPHERE_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized no-leaf AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void SphereCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform Sphere-AABB overlap test
+ if(!SphereAABBOverlap(Center, Extents)) return;
+
+ TEST_BOX_IN_SPHERE(Center, Extents)
+
+ if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for vanilla AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void SphereCollider::_Collide(const AABBTreeNode* node)
+{
+ // Perform Sphere-AABB overlap test
+ Point Center, Extents;
+ node->GetAABB()->GetCenter(Center);
+ node->GetAABB()->GetExtents(Extents);
+ if(!SphereAABBOverlap(Center, Extents)) return;
+
+ if(node->IsLeaf() || SphereContainsBox(Center, Extents))
+ {
+ mFlags |= OPC_CONTACT;
+ mTouchedPrimitives->Add(node->GetPrimitives(), node->GetNbPrimitives());
+ }
+ else
+ {
+ _Collide(node->GetPos());
+ _Collide(node->GetNeg());
+ }
+}
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridSphereCollider::HybridSphereCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridSphereCollider::~HybridSphereCollider()
+{
+}
+
+bool HybridSphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const HybridModel& model, const Matrix4x4* worlds, const Matrix4x4* worldm)
+{
+ // We don't want primitive tests here!
+ mFlags |= OPC_NO_PRIMITIVE_TESTS;
+
+ // Checkings
+ if(!Setup(&model)) return false;
+
+ // Init collision query
+ if(InitQuery(cache, sphere, worlds, worldm)) return true;
+
+ // Special case for 1-leaf trees
+ if(mCurrentModel && mCurrentModel->HasSingleNode())
+ {
+ // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles
+ udword Nb = mIMesh->GetNbTriangles();
+
+ // Loop through all triangles
+ for(udword i=0;i<Nb;i++)
+ {
+ SPHERE_PRIM(i, OPC_CONTACT)
+ }
+ return true;
+ }
+
+ // Override destination array since we're only going to get leaf boxes here
+ mTouchedBoxes.Reset();
+ mTouchedPrimitives = &mTouchedBoxes;
+
+ // Now, do the actual query against leaf boxes
+ if(!model.HasLeafNodes())
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ }
+ else
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ }
+
+ // We only have a list of boxes so far
+ if(GetContactStatus())
+ {
+ // Reset contact status, since it currently only reflects collisions with leaf boxes
+ Collider::InitQuery();
+
+ // Change dest container so that we can use built-in overlap tests and get collided primitives
+ cache.TouchedPrimitives.Reset();
+ mTouchedPrimitives = &cache.TouchedPrimitives;
+
+ // Read touched leaf boxes
+ udword Nb = mTouchedBoxes.GetNbEntries();
+ const udword* Touched = mTouchedBoxes.GetEntries();
+
+ const LeafTriangles* LT = model.GetLeafTriangles();
+ const udword* Indices = model.GetIndices();
+
+ // Loop through touched leaves
+ while(Nb--)
+ {
+ const LeafTriangles& CurrentLeaf = LT[*Touched++];
+
+ // Each leaf box has a set of triangles
+ udword NbTris = CurrentLeaf.GetNbTriangles();
+ if(Indices)
+ {
+ const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ udword TriangleIndex = *T++;
+ SPHERE_PRIM(TriangleIndex, OPC_CONTACT)
+ }
+ }
+ else
+ {
+ udword BaseIndex = CurrentLeaf.GetTriangleIndex();
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ udword TriangleIndex = BaseIndex++;
+ SPHERE_PRIM(TriangleIndex, OPC_CONTACT)
+ }
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/Opcode/OPC_SphereCollider.h b/Opcode/OPC_SphereCollider.h
new file mode 100644
index 0000000..aa53fdf
--- /dev/null
+++ b/Opcode/OPC_SphereCollider.h
@@ -0,0 +1,96 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a sphere collider.
+ * \file OPC_SphereCollider.h
+ * \author Pierre Terdiman
+ * \date June, 2, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_SPHERECOLLIDER_H__
+#define __OPC_SPHERECOLLIDER_H__
+
+ struct OPCODE_API SphereCache : VolumeCache
+ {
+ SphereCache() : Center(0.0f,0.0f,0.0f), FatRadius2(0.0f), FatCoeff(1.1f) {}
+ ~SphereCache() {}
+
+ // Cached faces signature
+ Point Center; //!< Sphere used when performing the query resulting in cached faces
+ float FatRadius2; //!< Sphere used when performing the query resulting in cached faces
+ // User settings
+ float FatCoeff; //!< mRadius2 multiplier used to create a fat sphere
+ };
+
+ class OPCODE_API SphereCollider : public VolumeCollider
+ {
+ public:
+ // Constructor / Destructor
+ SphereCollider();
+ virtual ~SphereCollider();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Generic collision query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - with GetNbTouchedPrimitives()
+ * - with GetTouchedPrimitives()
+ *
+ * \param cache [in/out] a sphere cache
+ * \param sphere [in] collision sphere in local space
+ * \param model [in] Opcode model to collide with
+ * \param worlds [in] sphere's world matrix, or null
+ * \param worldm [in] model's world matrix, or null
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool Collide(SphereCache& cache, const Sphere& sphere, const Model& model, const Matrix4x4* worlds=null, const Matrix4x4* worldm=null);
+
+ //
+ bool Collide(SphereCache& cache, const Sphere& sphere, const AABBTree* tree);
+ protected:
+ // Sphere in model space
+ Point mCenter; //!< Sphere center
+ float mRadius2; //!< Sphere radius squared
+ // Internal methods
+ void _Collide(const AABBCollisionNode* node);
+ void _Collide(const AABBNoLeafNode* node);
+ void _Collide(const AABBQuantizedNode* node);
+ void _Collide(const AABBQuantizedNoLeafNode* node);
+ void _Collide(const AABBTreeNode* node);
+ void _CollideNoPrimitiveTest(const AABBCollisionNode* node);
+ void _CollideNoPrimitiveTest(const AABBNoLeafNode* node);
+ void _CollideNoPrimitiveTest(const AABBQuantizedNode* node);
+ void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node);
+ // Overlap tests
+ inline_ BOOL SphereContainsBox(const Point& bc, const Point& be);
+ inline_ BOOL SphereAABBOverlap(const Point& center, const Point& extents);
+ BOOL SphereTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2);
+ // Init methods
+ BOOL InitQuery(SphereCache& cache, const Sphere& sphere, const Matrix4x4* worlds=null, const Matrix4x4* worldm=null);
+ };
+
+ class OPCODE_API HybridSphereCollider : public SphereCollider
+ {
+ public:
+ // Constructor / Destructor
+ HybridSphereCollider();
+ virtual ~HybridSphereCollider();
+
+ bool Collide(SphereCache& cache, const Sphere& sphere, const HybridModel& model, const Matrix4x4* worlds=null, const Matrix4x4* worldm=null);
+ protected:
+ Container mTouchedBoxes;
+ };
+
+#endif // __OPC_SPHERECOLLIDER_H__
diff --git a/Opcode/OPC_SphereTriOverlap.h b/Opcode/OPC_SphereTriOverlap.h
new file mode 100644
index 0000000..f1452ec
--- /dev/null
+++ b/Opcode/OPC_SphereTriOverlap.h
@@ -0,0 +1,187 @@
+
+// This is collision detection. If you do another distance test for collision *response*,
+// if might be useful to simply *skip* the test below completely, and report a collision.
+// - if sphere-triangle overlap, result is ok
+// - if they don't, we'll discard them during collision response with a similar test anyway
+// Overall this approach should run faster.
+
+// Original code by David Eberly in Magic.
+BOOL SphereCollider::SphereTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2)
+{
+ // Stats
+ mNbVolumePrimTests++;
+
+ // Early exit if one of the vertices is inside the sphere
+ Point kDiff = vert2 - mCenter;
+ float fC = kDiff.SquareMagnitude();
+ if(fC <= mRadius2) return TRUE;
+
+ kDiff = vert1 - mCenter;
+ fC = kDiff.SquareMagnitude();
+ if(fC <= mRadius2) return TRUE;
+
+ kDiff = vert0 - mCenter;
+ fC = kDiff.SquareMagnitude();
+ if(fC <= mRadius2) return TRUE;
+
+ // Else do the full distance test
+ Point TriEdge0 = vert1 - vert0;
+ Point TriEdge1 = vert2 - vert0;
+
+//Point kDiff = vert0 - mCenter;
+ float fA00 = TriEdge0.SquareMagnitude();
+ float fA01 = TriEdge0 | TriEdge1;
+ float fA11 = TriEdge1.SquareMagnitude();
+ float fB0 = kDiff | TriEdge0;
+ float fB1 = kDiff | TriEdge1;
+//float fC = kDiff.SquareMagnitude();
+ float fDet = fabsf(fA00*fA11 - fA01*fA01);
+ float u = fA01*fB1-fA11*fB0;
+ float v = fA01*fB0-fA00*fB1;
+ float SqrDist;
+
+ if(u + v <= fDet)
+ {
+ if(u < 0.0f)
+ {
+ if(v < 0.0f) // region 4
+ {
+ if(fB0 < 0.0f)
+ {
+// v = 0.0f;
+ if(-fB0>=fA00) { /*u = 1.0f;*/ SqrDist = fA00+2.0f*fB0+fC; }
+ else { u = -fB0/fA00; SqrDist = fB0*u+fC; }
+ }
+ else
+ {
+// u = 0.0f;
+ if(fB1>=0.0f) { /*v = 0.0f;*/ SqrDist = fC; }
+ else if(-fB1>=fA11) { /*v = 1.0f;*/ SqrDist = fA11+2.0f*fB1+fC; }
+ else { v = -fB1/fA11; SqrDist = fB1*v+fC; }
+ }
+ }
+ else // region 3
+ {
+// u = 0.0f;
+ if(fB1>=0.0f) { /*v = 0.0f;*/ SqrDist = fC; }
+ else if(-fB1>=fA11) { /*v = 1.0f;*/ SqrDist = fA11+2.0f*fB1+fC; }
+ else { v = -fB1/fA11; SqrDist = fB1*v+fC; }
+ }
+ }
+ else if(v < 0.0f) // region 5
+ {
+// v = 0.0f;
+ if(fB0>=0.0f) { /*u = 0.0f;*/ SqrDist = fC; }
+ else if(-fB0>=fA00) { /*u = 1.0f;*/ SqrDist = fA00+2.0f*fB0+fC; }
+ else { u = -fB0/fA00; SqrDist = fB0*u+fC; }
+ }
+ else // region 0
+ {
+ // minimum at interior point
+ if(fDet==0.0f)
+ {
+// u = 0.0f;
+// v = 0.0f;
+ SqrDist = MAX_FLOAT;
+ }
+ else
+ {
+ float fInvDet = 1.0f/fDet;
+ u *= fInvDet;
+ v *= fInvDet;
+ SqrDist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC;
+ }
+ }
+ }
+ else
+ {
+ float fTmp0, fTmp1, fNumer, fDenom;
+
+ if(u < 0.0f) // region 2
+ {
+ fTmp0 = fA01 + fB0;
+ fTmp1 = fA11 + fB1;
+ if(fTmp1 > fTmp0)
+ {
+ fNumer = fTmp1 - fTmp0;
+ fDenom = fA00-2.0f*fA01+fA11;
+ if(fNumer >= fDenom)
+ {
+// u = 1.0f;
+// v = 0.0f;
+ SqrDist = fA00+2.0f*fB0+fC;
+ }
+ else
+ {
+ u = fNumer/fDenom;
+ v = 1.0f - u;
+ SqrDist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC;
+ }
+ }
+ else
+ {
+// u = 0.0f;
+ if(fTmp1 <= 0.0f) { /*v = 1.0f;*/ SqrDist = fA11+2.0f*fB1+fC; }
+ else if(fB1 >= 0.0f) { /*v = 0.0f;*/ SqrDist = fC; }
+ else { v = -fB1/fA11; SqrDist = fB1*v+fC; }
+ }
+ }
+ else if(v < 0.0f) // region 6
+ {
+ fTmp0 = fA01 + fB1;
+ fTmp1 = fA00 + fB0;
+ if(fTmp1 > fTmp0)
+ {
+ fNumer = fTmp1 - fTmp0;
+ fDenom = fA00-2.0f*fA01+fA11;
+ if(fNumer >= fDenom)
+ {
+// v = 1.0f;
+// u = 0.0f;
+ SqrDist = fA11+2.0f*fB1+fC;
+ }
+ else
+ {
+ v = fNumer/fDenom;
+ u = 1.0f - v;
+ SqrDist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC;
+ }
+ }
+ else
+ {
+// v = 0.0f;
+ if(fTmp1 <= 0.0f) { /*u = 1.0f;*/ SqrDist = fA00+2.0f*fB0+fC; }
+ else if(fB0 >= 0.0f) { /*u = 0.0f;*/ SqrDist = fC; }
+ else { u = -fB0/fA00; SqrDist = fB0*u+fC; }
+ }
+ }
+ else // region 1
+ {
+ fNumer = fA11 + fB1 - fA01 - fB0;
+ if(fNumer <= 0.0f)
+ {
+// u = 0.0f;
+// v = 1.0f;
+ SqrDist = fA11+2.0f*fB1+fC;
+ }
+ else
+ {
+ fDenom = fA00-2.0f*fA01+fA11;
+ if(fNumer >= fDenom)
+ {
+// u = 1.0f;
+// v = 0.0f;
+ SqrDist = fA00+2.0f*fB0+fC;
+ }
+ else
+ {
+ u = fNumer/fDenom;
+ v = 1.0f - u;
+ SqrDist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC;
+ }
+ }
+ }
+ }
+
+ return fabsf(SqrDist) < mRadius2;
+}
diff --git a/Opcode/OPC_SweepAndPrune.cpp b/Opcode/OPC_SweepAndPrune.cpp
new file mode 100644
index 0000000..b593e8b
--- /dev/null
+++ b/Opcode/OPC_SweepAndPrune.cpp
@@ -0,0 +1,664 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains an implementation of the sweep-and-prune algorithm (moved from Z-Collide)
+ * \file OPC_SweepAndPrune.cpp
+ * \author Pierre Terdiman
+ * \date January, 29, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+inline_ void Sort(udword& id0, udword& id1)
+{
+ if(id0>id1) Swap(id0, id1);
+}
+
+ class Opcode::SAP_Element
+ {
+ public:
+ inline_ SAP_Element() {}
+ inline_ SAP_Element(udword id, SAP_Element* next) : mID(id), mNext(next) {}
+ inline_ ~SAP_Element() {}
+
+ udword mID;
+ SAP_Element* mNext;
+ };
+
+ class Opcode::SAP_Box
+ {
+ public:
+ SAP_EndPoint* Min[3];
+ SAP_EndPoint* Max[3];
+ };
+
+ class Opcode::SAP_EndPoint
+ {
+ public:
+ float Value; // Min or Max value
+ SAP_EndPoint* Previous; // Previous EndPoint whose Value is smaller than ours (or null)
+ SAP_EndPoint* Next; // Next EndPoint whose Value is greater than ours (or null)
+ udword Data; // Parent box ID *2 | MinMax flag
+
+ inline_ void SetData(udword box_id, BOOL is_max) { Data = (box_id<<1)|is_max; }
+ inline_ BOOL IsMax() const { return Data & 1; }
+ inline_ udword GetBoxID() const { return Data>>1; }
+
+ inline_ void InsertAfter(SAP_EndPoint* element)
+ {
+ if(this!=element && this!=element->Next)
+ {
+ // Remove
+ if(Previous) Previous->Next = Next;
+ if(Next) Next->Previous = Previous;
+
+ // Insert
+ Next = element->Next;
+ if(Next) Next->Previous = this;
+
+ element->Next = this;
+ Previous = element;
+ }
+ }
+
+ inline_ void InsertBefore(SAP_EndPoint* element)
+ {
+ if(this!=element && this!=element->Previous)
+ {
+ // Remove
+ if(Previous) Previous->Next = Next;
+ if(Next) Next->Previous = Previous;
+
+ // Insert
+ Previous = element->Previous;
+ element->Previous = this;
+
+ Next = element;
+ if(Previous) Previous->Next = this;
+ }
+ }
+ };
+
+
+
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+SAP_PairData::SAP_PairData() :
+ mNbElements (0),
+ mNbUsedElements (0),
+ mElementPool (null),
+ mFirstFree (null),
+ mNbObjects (0),
+ mArray (null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+SAP_PairData::~SAP_PairData()
+{
+ Release();
+}
+
+void SAP_PairData::Release()
+{
+ mNbElements = 0;
+ mNbUsedElements = 0;
+ mNbObjects = 0;
+ DELETEARRAY(mElementPool);
+ DELETEARRAY(mArray);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Initializes.
+ * \param nb_objects [in]
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool SAP_PairData::Init(udword nb_objects)
+{
+ // Make sure everything has been released
+ Release();
+ if(!nb_objects) return false;
+
+ mArray = new SAP_Element*[nb_objects];
+ CHECKALLOC(mArray);
+ ZeroMemory(mArray, nb_objects*sizeof(SAP_Element*));
+ mNbObjects = nb_objects;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Remaps a pointer when pool gets resized.
+ * \param element [in/out] remapped element
+ * \param delta [in] offset in bytes
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ void Remap(SAP_Element*& element, udword delta)
+{
+ if(element) element = (SAP_Element*)(udword(element) + delta);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets a free element in the pool.
+ * \param id [in] element id
+ * \param next [in] next element
+ * \param remap [out] possible remapping offset
+ * \return the new element
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+SAP_Element* SAP_PairData::GetFreeElem(udword id, SAP_Element* next, udword* remap)
+{
+ if(remap) *remap = 0;
+
+ SAP_Element* FreeElem;
+ if(mFirstFree)
+ {
+ // Recycle
+ FreeElem = mFirstFree;
+ mFirstFree = mFirstFree->mNext; // First free = next free (or null)
+ }
+ else
+ {
+ if(mNbUsedElements==mNbElements)
+ {
+ // Resize
+ mNbElements = mNbElements ? (mNbElements<<1) : 2;
+
+ SAP_Element* NewElems = new SAP_Element[mNbElements];
+
+ if(mNbUsedElements) CopyMemory(NewElems, mElementPool, mNbUsedElements*sizeof(SAP_Element));
+
+ // Remap everything
+ {
+ udword Delta = udword(NewElems) - udword(mElementPool);
+
+ for(udword i=0;i<mNbUsedElements;i++) Remap(NewElems[i].mNext, Delta);
+ for(udword i=0;i<mNbObjects;i++) Remap(mArray[i], Delta);
+
+ Remap(mFirstFree, Delta);
+ Remap(next, Delta);
+
+ if(remap) *remap = Delta;
+ }
+
+ DELETEARRAY(mElementPool);
+ mElementPool = NewElems;
+ }
+
+ FreeElem = &mElementPool[mNbUsedElements++];
+ }
+
+ FreeElem->mID = id;
+ FreeElem->mNext = next;
+
+ return FreeElem;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Frees an element of the pool.
+ * \param elem [in] element to free/recycle
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ void SAP_PairData::FreeElem(SAP_Element* elem)
+{
+ elem->mNext = mFirstFree; // Next free
+ mFirstFree = elem;
+}
+
+// Add a pair to the set.
+void SAP_PairData::AddPair(udword id1, udword id2)
+{
+ // Order the ids
+ Sort(id1, id2);
+
+ ASSERT(id1<mNbObjects);
+ if(id1>=mNbObjects) return;
+
+ // Select the right list from "mArray".
+ SAP_Element* Current = mArray[id1];
+
+ if(!Current)
+ {
+ // Empty slot => create new element
+ mArray[id1] = GetFreeElem(id2, null);
+ }
+ else if(Current->mID>id2)
+ {
+ // The list is not empty but all elements are greater than id2 => insert id2 in the front.
+ mArray[id1] = GetFreeElem(id2, mArray[id1]);
+ }
+ else
+ {
+ // Else find the correct location in the sorted list (ascending order) and insert id2 there.
+ while(Current->mNext)
+ {
+ if(Current->mNext->mID > id2) break;
+
+ Current = Current->mNext;
+ }
+
+ if(Current->mID==id2) return; // The pair already exists
+
+// Current->mNext = GetFreeElem(id2, Current->mNext);
+ udword Delta;
+ SAP_Element* E = GetFreeElem(id2, Current->mNext, &Delta);
+ if(Delta) Remap(Current, Delta);
+ Current->mNext = E;
+ }
+}
+
+// Delete a pair from the set.
+void SAP_PairData::RemovePair(udword id1, udword id2)
+{
+ // Order the ids.
+ Sort(id1, id2);
+
+ // Exit if the pair doesn't exist in the set
+ if(id1>=mNbObjects) return;
+
+ // Otherwise, select the correct list.
+ SAP_Element* Current = mArray[id1];
+
+ // If this list is empty, the pair doesn't exist.
+ if(!Current) return;
+
+ // Otherwise, if id2 is the first element, delete it.
+ if(Current->mID==id2)
+ {
+ mArray[id1] = Current->mNext;
+ FreeElem(Current);
+ }
+ else
+ {
+ // If id2 is not the first element, start traversing the sorted list.
+ while(Current->mNext)
+ {
+ // If we have moved too far away without hitting id2, then the pair doesn't exist
+ if(Current->mNext->mID > id2) return;
+
+ // Otherwise, delete id2.
+ if(Current->mNext->mID == id2)
+ {
+ SAP_Element* Temp = Current->mNext;
+ Current->mNext = Temp->mNext;
+ FreeElem(Temp);
+ return;
+ }
+ Current = Current->mNext;
+ }
+ }
+}
+
+void SAP_PairData::DumpPairs(Pairs& pairs) const
+{
+ // ### Ugly and slow
+ for(udword i=0;i<mNbObjects;i++)
+ {
+ SAP_Element* Current = mArray[i];
+ while(Current)
+ {
+ ASSERT(Current->mID<mNbObjects);
+
+ pairs.AddPair(i, Current->mID);
+ Current = Current->mNext;
+ }
+ }
+}
+
+void SAP_PairData::DumpPairs(PairCallback callback, void* user_data) const
+{
+ if(!callback) return;
+
+ // ### Ugly and slow
+ for(udword i=0;i<mNbObjects;i++)
+ {
+ SAP_Element* Current = mArray[i];
+ while(Current)
+ {
+ ASSERT(Current->mID<mNbObjects);
+
+ if(!(callback)(i, Current->mID, user_data)) return;
+ Current = Current->mNext;
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+SweepAndPrune::SweepAndPrune()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+SweepAndPrune::~SweepAndPrune()
+{
+}
+
+void SweepAndPrune::GetPairs(Pairs& pairs) const
+{
+ mPairs.DumpPairs(pairs);
+}
+
+void SweepAndPrune::GetPairs(PairCallback callback, void* user_data) const
+{
+ mPairs.DumpPairs(callback, user_data);
+}
+
+bool SweepAndPrune::Init(udword nb_objects, const AABB** boxes)
+{
+ // 1) Create sorted lists
+ mNbObjects = nb_objects;
+
+ mBoxes = new SAP_Box[nb_objects];
+// for(udword i=0;i<nb_objects;i++) mBoxes[i].Box = *boxes[i];
+
+ float* Data = new float[nb_objects*2];
+
+ for(udword Axis=0;Axis<3;Axis++)
+ {
+ mList[Axis] = new SAP_EndPoint[nb_objects*2];
+
+ for(udword i=0;i<nb_objects;i++)
+ {
+ Data[i*2+0] = boxes[i]->GetMin(Axis);
+ Data[i*2+1] = boxes[i]->GetMax(Axis);
+ }
+ RadixSort RS;
+ const udword* Sorted = RS.Sort(Data, nb_objects*2).GetRanks();
+
+ SAP_EndPoint* PreviousEndPoint = null;
+
+ for(udword i=0;i<nb_objects*2;i++)
+ {
+ udword SortedIndex = *Sorted++;
+ float SortedCoord = Data[SortedIndex];
+ udword BoxIndex = SortedIndex>>1;
+
+ ASSERT(BoxIndex<nb_objects);
+
+ SAP_EndPoint* CurrentEndPoint = &mList[Axis][SortedIndex];
+ CurrentEndPoint->Value = SortedCoord;
+// CurrentEndPoint->IsMax = SortedIndex&1; // ### could be implicit ?
+// CurrentEndPoint->ID = BoxIndex; // ### could be implicit ?
+ CurrentEndPoint->SetData(BoxIndex, SortedIndex&1); // ### could be implicit ?
+ CurrentEndPoint->Previous = PreviousEndPoint;
+ CurrentEndPoint->Next = null;
+ if(PreviousEndPoint) PreviousEndPoint->Next = CurrentEndPoint;
+
+ if(CurrentEndPoint->IsMax()) mBoxes[BoxIndex].Max[Axis] = CurrentEndPoint;
+ else mBoxes[BoxIndex].Min[Axis] = CurrentEndPoint;
+
+ PreviousEndPoint = CurrentEndPoint;
+ }
+ }
+
+ DELETEARRAY(Data);
+
+ CheckListsIntegrity();
+
+ // 2) Quickly find starting pairs
+
+ mPairs.Init(nb_objects);
+
+ {
+ Pairs P;
+ CompleteBoxPruning(nb_objects, boxes, P, Axes(AXES_XZY));
+ for(udword i=0;i<P.GetNbPairs();i++)
+ {
+ const Pair* PP = P.GetPair(i);
+
+ udword id0 = PP->id0;
+ udword id1 = PP->id1;
+
+ if(id0!=id1 && boxes[id0]->Intersect(*boxes[id1]))
+ {
+ mPairs.AddPair(id0, id1);
+ }
+ else ASSERT(0);
+ }
+ }
+
+ return true;
+}
+
+bool SweepAndPrune::CheckListsIntegrity()
+{
+ for(udword Axis=0;Axis<3;Axis++)
+ {
+ // Find list head
+ SAP_EndPoint* Current = mList[Axis];
+ while(Current->Previous) Current = Current->Previous;
+
+ udword Nb = 0;
+
+ SAP_EndPoint* Previous = null;
+ while(Current)
+ {
+ Nb++;
+
+ if(Previous)
+ {
+ ASSERT(Previous->Value <= Current->Value);
+ if(Previous->Value > Current->Value) return false;
+ }
+
+ ASSERT(Current->Previous==Previous);
+ if(Current->Previous!=Previous) return false;
+
+ Previous = Current;
+ Current = Current->Next;
+ }
+
+ ASSERT(Nb==mNbObjects*2);
+ }
+ return true;
+}
+
+inline_ BOOL Intersect(const AABB& a, const SAP_Box& b)
+{
+ if(b.Max[0]->Value < a.GetMin(0) || a.GetMax(0) < b.Min[0]->Value
+ || b.Max[1]->Value < a.GetMin(1) || a.GetMax(1) < b.Min[1]->Value
+ || b.Max[2]->Value < a.GetMin(2) || a.GetMax(2) < b.Min[2]->Value) return FALSE;
+
+ return TRUE;
+}
+
+
+
+bool SweepAndPrune::UpdateObject(udword i, const AABB& box)
+{
+ for(udword Axis=0;Axis<3;Axis++)
+ {
+// udword Base = (udword)&mList[Axis][0];
+
+ // Update min
+ {
+ SAP_EndPoint* const CurrentMin = mBoxes[i].Min[Axis];
+ ASSERT(!CurrentMin->IsMax());
+
+ const float Limit = box.GetMin(Axis);
+ if(Limit == CurrentMin->Value)
+ {
+ }
+ else if(Limit < CurrentMin->Value)
+ {
+ CurrentMin->Value = Limit;
+
+ // Min is moving left:
+ SAP_EndPoint* NewPos = CurrentMin;
+ ASSERT(NewPos);
+
+ SAP_EndPoint* tmp;
+ while((tmp = NewPos->Previous) && tmp->Value > Limit)
+ {
+ NewPos = tmp;
+
+ if(NewPos->IsMax())
+ {
+ // Our min passed a max => start overlap
+ //udword SortedIndex = (udword(CurrentMin) - Base)/sizeof(NS_EndPoint);
+ const udword id0 = CurrentMin->GetBoxID();
+ const udword id1 = NewPos->GetBoxID();
+
+ if(id0!=id1 && Intersect(box, mBoxes[id1])) mPairs.AddPair(id0, id1);
+ }
+ }
+
+ CurrentMin->InsertBefore(NewPos);
+ }
+ else// if(Limit > CurrentMin->Value)
+ {
+ CurrentMin->Value = Limit;
+
+ // Min is moving right:
+ SAP_EndPoint* NewPos = CurrentMin;
+ ASSERT(NewPos);
+
+ SAP_EndPoint* tmp;
+ while((tmp = NewPos->Next) && tmp->Value < Limit)
+ {
+ NewPos = tmp;
+
+ if(NewPos->IsMax())
+ {
+ // Our min passed a max => stop overlap
+ const udword id0 = CurrentMin->GetBoxID();
+ const udword id1 = NewPos->GetBoxID();
+
+ if(id0!=id1) mPairs.RemovePair(id0, id1);
+ }
+ }
+
+ CurrentMin->InsertAfter(NewPos);
+ }
+ }
+
+ // Update max
+ {
+ SAP_EndPoint* const CurrentMax = mBoxes[i].Max[Axis];
+ ASSERT(CurrentMax->IsMax());
+
+ const float Limit = box.GetMax(Axis);
+ if(Limit == CurrentMax->Value)
+ {
+ }
+ else if(Limit > CurrentMax->Value)
+ {
+ CurrentMax->Value = Limit;
+
+ // Max is moving right:
+ SAP_EndPoint* NewPos = CurrentMax;
+ ASSERT(NewPos);
+
+ SAP_EndPoint* tmp;
+ while((tmp = NewPos->Next) && tmp->Value < Limit)
+ {
+ NewPos = tmp;
+
+ if(!NewPos->IsMax())
+ {
+ // Our max passed a min => start overlap
+ const udword id0 = CurrentMax->GetBoxID();
+ const udword id1 = NewPos->GetBoxID();
+
+ if(id0!=id1 && Intersect(box, mBoxes[id1])) mPairs.AddPair(id0, id1);
+ }
+ }
+
+ CurrentMax->InsertAfter(NewPos);
+ }
+ else// if(Limit < CurrentMax->Value)
+ {
+ CurrentMax->Value = Limit;
+
+ // Max is moving left:
+ SAP_EndPoint* NewPos = CurrentMax;
+ ASSERT(NewPos);
+
+ SAP_EndPoint* tmp;
+ while((tmp = NewPos->Previous) && tmp->Value > Limit)
+ {
+ NewPos = tmp;
+
+ if(!NewPos->IsMax())
+ {
+ // Our max passed a min => stop overlap
+ const udword id0 = CurrentMax->GetBoxID();
+ const udword id1 = NewPos->GetBoxID();
+
+ if(id0!=id1) mPairs.RemovePair(id0, id1);
+ }
+ }
+
+ CurrentMax->InsertBefore(NewPos);
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/Opcode/OPC_SweepAndPrune.h b/Opcode/OPC_SweepAndPrune.h
new file mode 100644
index 0000000..5cf7956
--- /dev/null
+++ b/Opcode/OPC_SweepAndPrune.h
@@ -0,0 +1,86 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains an implementation of the sweep-and-prune algorithm (moved from Z-Collide)
+ * \file OPC_SweepAndPrune.h
+ * \author Pierre Terdiman
+ * \date January, 29, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_SWEEPANDPRUNE_H__
+#define __OPC_SWEEPANDPRUNE_H__
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * User-callback, called by OPCODE for each colliding pairs.
+ * \param id0 [in] id of colliding object
+ * \param id1 [in] id of colliding object
+ * \param user_data [in] user-defined data
+ * \return TRUE to continue enumeration
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ typedef BOOL (*PairCallback) (udword id0, udword id1, void* user_data);
+
+ class SAP_Element;
+ class SAP_EndPoint;
+ class SAP_Box;
+
+ class OPCODE_API SAP_PairData
+ {
+ public:
+ SAP_PairData();
+ ~SAP_PairData();
+
+ bool Init(udword nb_objects);
+
+ void AddPair(udword id1, udword id2);
+ void RemovePair(udword id1, udword id2);
+
+ void DumpPairs(Pairs& pairs) const;
+ void DumpPairs(PairCallback callback, void* user_data) const;
+ private:
+ udword mNbElements; //!< Total number of elements in the pool
+ udword mNbUsedElements; //!< Number of used elements
+ SAP_Element* mElementPool; //!< Array of mNbElements elements
+ SAP_Element* mFirstFree; //!< First free element in the pool
+
+ udword mNbObjects; //!< Max number of objects we can handle
+ SAP_Element** mArray; //!< Pointers to pool
+ // Internal methods
+ SAP_Element* GetFreeElem(udword id, SAP_Element* next, udword* remap=null);
+ inline_ void FreeElem(SAP_Element* elem);
+ void Release();
+ };
+
+ class OPCODE_API SweepAndPrune
+ {
+ public:
+ SweepAndPrune();
+ ~SweepAndPrune();
+
+ bool Init(udword nb_objects, const AABB** boxes);
+ bool UpdateObject(udword i, const AABB& box);
+
+ void GetPairs(Pairs& pairs) const;
+ void GetPairs(PairCallback callback, void* user_data) const;
+ private:
+ SAP_PairData mPairs;
+
+ udword mNbObjects;
+ SAP_Box* mBoxes;
+ SAP_EndPoint* mList[3];
+ // Internal methods
+ bool CheckListsIntegrity();
+ };
+
+#endif //__OPC_SWEEPANDPRUNE_H__ \ No newline at end of file
diff --git a/Opcode/OPC_TreeBuilders.cpp b/Opcode/OPC_TreeBuilders.cpp
new file mode 100644
index 0000000..73e89c7
--- /dev/null
+++ b/Opcode/OPC_TreeBuilders.cpp
@@ -0,0 +1,255 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for tree builders.
+ * \file OPC_TreeBuilders.cpp
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A builder for AABB-trees of vertices.
+ *
+ * \class AABBTreeOfVerticesBuilder
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A builder for AABB-trees of AABBs.
+ *
+ * \class AABBTreeOfAABBsBuilder
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A builder for AABB-trees of triangles.
+ *
+ * \class AABBTreeOfTrianglesBuilder
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the AABB of a set of primitives.
+ * \param primitives [in] list of indices of primitives
+ * \param nb_prims [in] number of indices
+ * \param global_box [out] global AABB enclosing the set of input primitives
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeOfAABBsBuilder::ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const
+{
+ // Checkings
+ if(!primitives || !nb_prims) return false;
+
+ // Initialize global box
+ global_box = mAABBArray[primitives[0]];
+
+ // Loop through boxes
+ for(udword i=1;i<nb_prims;i++)
+ {
+ // Update global box
+ global_box.Add(mAABBArray[primitives[i]]);
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the splitting value along a given axis for a given primitive.
+ * \param index [in] index of the primitive to split
+ * \param axis [in] axis index (0,1,2)
+ * \return splitting value
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float AABBTreeOfAABBsBuilder::GetSplittingValue(udword index, udword axis) const
+{
+ // For an AABB, the splitting value is the middle of the given axis,
+ // i.e. the corresponding component of the center point
+ return mAABBArray[index].GetCenter(axis);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the AABB of a set of primitives.
+ * \param primitives [in] list of indices of primitives
+ * \param nb_prims [in] number of indices
+ * \param global_box [out] global AABB enclosing the set of input primitives
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeOfTrianglesBuilder::ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const
+{
+ // Checkings
+ if(!primitives || !nb_prims) return false;
+
+ // Initialize global box
+ Point Min(MAX_FLOAT, MAX_FLOAT, MAX_FLOAT);
+ Point Max(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT);
+
+ // Loop through triangles
+ VertexPointers VP;
+ while(nb_prims--)
+ {
+ // Get current triangle-vertices
+ mIMesh->GetTriangle(VP, *primitives++);
+ // Update global box
+ Min.Min(*VP.Vertex[0]).Min(*VP.Vertex[1]).Min(*VP.Vertex[2]);
+ Max.Max(*VP.Vertex[0]).Max(*VP.Vertex[1]).Max(*VP.Vertex[2]);
+ }
+ global_box.SetMinMax(Min, Max);
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the splitting value along a given axis for a given primitive.
+ * \param index [in] index of the primitive to split
+ * \param axis [in] axis index (0,1,2)
+ * \return splitting value
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float AABBTreeOfTrianglesBuilder::GetSplittingValue(udword index, udword axis) const
+{
+/* // Compute center of triangle
+ Point Center;
+ mTriList[index].Center(mVerts, Center);
+ // Return value
+ return Center[axis];*/
+
+ // Compute correct component from center of triangle
+// return (mVerts[mTriList[index].mVRef[0]][axis]
+// +mVerts[mTriList[index].mVRef[1]][axis]
+// +mVerts[mTriList[index].mVRef[2]][axis])*INV3;
+
+ VertexPointers VP;
+ mIMesh->GetTriangle(VP, index);
+
+ // Compute correct component from center of triangle
+ return ((*VP.Vertex[0])[axis]
+ +(*VP.Vertex[1])[axis]
+ +(*VP.Vertex[2])[axis])*INV3;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the splitting value along a given axis for a given node.
+ * \param primitives [in] list of indices of primitives
+ * \param nb_prims [in] number of indices
+ * \param global_box [in] global AABB enclosing the set of input primitives
+ * \param axis [in] axis index (0,1,2)
+ * \return splitting value
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float AABBTreeOfTrianglesBuilder::GetSplittingValue(const udword* primitives, udword nb_prims, const AABB& global_box, udword axis) const
+{
+ if(mSettings.mRules&SPLIT_GEOM_CENTER)
+ {
+ // Loop through triangles
+ float SplitValue = 0.0f;
+ VertexPointers VP;
+ for(udword i=0;i<nb_prims;i++)
+ {
+ // Get current triangle-vertices
+ mIMesh->GetTriangle(VP, primitives[i]);
+ // Update split value
+ SplitValue += (*VP.Vertex[0])[axis];
+ SplitValue += (*VP.Vertex[1])[axis];
+ SplitValue += (*VP.Vertex[2])[axis];
+ }
+ return SplitValue / float(nb_prims*3);
+ }
+ else return AABBTreeBuilder::GetSplittingValue(primitives, nb_prims, global_box, axis);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the AABB of a set of primitives.
+ * \param primitives [in] list of indices of primitives
+ * \param nb_prims [in] number of indices
+ * \param global_box [out] global AABB enclosing the set of input primitives
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeOfVerticesBuilder::ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const
+{
+ // Checkings
+ if(!primitives || !nb_prims) return false;
+
+ // Initialize global box
+ global_box.SetEmpty();
+
+ // Loop through vertices
+ for(udword i=0;i<nb_prims;i++)
+ {
+ // Update global box
+ global_box.Extend(mVertexArray[primitives[i]]);
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the splitting value along a given axis for a given primitive.
+ * \param index [in] index of the primitive to split
+ * \param axis [in] axis index (0,1,2)
+ * \return splitting value
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float AABBTreeOfVerticesBuilder::GetSplittingValue(udword index, udword axis) const
+{
+ // For a vertex, the splitting value is simply the vertex coordinate.
+ return mVertexArray[index][axis];
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the splitting value along a given axis for a given node.
+ * \param primitives [in] list of indices of primitives
+ * \param nb_prims [in] number of indices
+ * \param global_box [in] global AABB enclosing the set of input primitives
+ * \param axis [in] axis index (0,1,2)
+ * \return splitting value
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float AABBTreeOfVerticesBuilder::GetSplittingValue(const udword* primitives, udword nb_prims, const AABB& global_box, udword axis) const
+{
+ if(mSettings.mRules&SPLIT_GEOM_CENTER)
+ {
+ // Loop through vertices
+ float SplitValue = 0.0f;
+ for(udword i=0;i<nb_prims;i++)
+ {
+ // Update split value
+ SplitValue += mVertexArray[primitives[i]][axis];
+ }
+ return SplitValue / float(nb_prims);
+ }
+ else return AABBTreeBuilder::GetSplittingValue(primitives, nb_prims, global_box, axis);
+}
diff --git a/Opcode/OPC_TreeBuilders.h b/Opcode/OPC_TreeBuilders.h
new file mode 100644
index 0000000..32486aa
--- /dev/null
+++ b/Opcode/OPC_TreeBuilders.h
@@ -0,0 +1,173 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for tree builders.
+ * \file OPC_TreeBuilders.h
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_TREEBUILDERS_H__
+#define __OPC_TREEBUILDERS_H__
+
+ //! Tree splitting rules
+ enum SplittingRules
+ {
+ // Primitive split
+ SPLIT_LARGEST_AXIS = (1<<0), //!< Split along the largest axis
+ SPLIT_SPLATTER_POINTS = (1<<1), //!< Splatter primitive centers (QuickCD-style)
+ SPLIT_BEST_AXIS = (1<<2), //!< Try largest axis, then second, then last
+ SPLIT_BALANCED = (1<<3), //!< Try to keep a well-balanced tree
+ SPLIT_FIFTY = (1<<4), //!< Arbitrary 50-50 split
+ // Node split
+ SPLIT_GEOM_CENTER = (1<<5), //!< Split at geometric center (else split in the middle)
+ //
+ SPLIT_FORCE_DWORD = 0x7fffffff
+ };
+
+ //! Simple wrapper around build-related settings [Opcode 1.3]
+ struct OPCODE_API BuildSettings
+ {
+ inline_ BuildSettings() : mLimit(1), mRules(SPLIT_FORCE_DWORD) {}
+
+ udword mLimit; //!< Limit number of primitives / node. If limit is 1, build a complete tree (2*N-1 nodes)
+ udword mRules; //!< Building/Splitting rules (a combination of SplittingRules flags)
+ };
+
+ class OPCODE_API AABBTreeBuilder
+ {
+ public:
+ //! Constructor
+ AABBTreeBuilder() :
+ mNbPrimitives(0),
+ mNodeBase(null),
+ mCount(0),
+ mNbInvalidSplits(0) {}
+ //! Destructor
+ virtual ~AABBTreeBuilder() {}
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the AABB of a set of primitives.
+ * \param primitives [in] list of indices of primitives
+ * \param nb_prims [in] number of indices
+ * \param global_box [out] global AABB enclosing the set of input primitives
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual bool ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const = 0;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the splitting value along a given axis for a given primitive.
+ * \param index [in] index of the primitive to split
+ * \param axis [in] axis index (0,1,2)
+ * \return splitting value
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual float GetSplittingValue(udword index, udword axis) const = 0;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the splitting value along a given axis for a given node.
+ * \param primitives [in] list of indices of primitives
+ * \param nb_prims [in] number of indices
+ * \param global_box [in] global AABB enclosing the set of input primitives
+ * \param axis [in] axis index (0,1,2)
+ * \return splitting value
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual float GetSplittingValue(const udword* primitives, udword nb_prims, const AABB& global_box, udword axis) const
+ {
+ // Default split value = middle of the axis (using only the box)
+ return global_box.GetCenter(axis);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Validates node subdivision. This is called each time a node is considered for subdivision, during tree building.
+ * \param primitives [in] list of indices of primitives
+ * \param nb_prims [in] number of indices
+ * \param global_box [in] global AABB enclosing the set of input primitives
+ * \return TRUE if the node should be subdivised
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual BOOL ValidateSubdivision(const udword* primitives, udword nb_prims, const AABB& global_box)
+ {
+ // Check the user-defined limit
+ if(nb_prims<=mSettings.mLimit) return FALSE;
+
+ return TRUE;
+ }
+
+ BuildSettings mSettings; //!< Splitting rules & split limit [Opcode 1.3]
+ udword mNbPrimitives; //!< Total number of primitives.
+ void* mNodeBase; //!< Address of node pool [Opcode 1.3]
+ // Stats
+ inline_ void SetCount(udword nb) { mCount=nb; }
+ inline_ void IncreaseCount(udword nb) { mCount+=nb; }
+ inline_ udword GetCount() const { return mCount; }
+ inline_ void SetNbInvalidSplits(udword nb) { mNbInvalidSplits=nb; }
+ inline_ void IncreaseNbInvalidSplits() { mNbInvalidSplits++; }
+ inline_ udword GetNbInvalidSplits() const { return mNbInvalidSplits; }
+
+ private:
+ udword mCount; //!< Stats: number of nodes created
+ udword mNbInvalidSplits; //!< Stats: number of invalid splits
+ };
+
+ class OPCODE_API AABBTreeOfVerticesBuilder : public AABBTreeBuilder
+ {
+ public:
+ //! Constructor
+ AABBTreeOfVerticesBuilder() : mVertexArray(null) {}
+ //! Destructor
+ virtual ~AABBTreeOfVerticesBuilder() {}
+
+ override(AABBTreeBuilder) bool ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const;
+ override(AABBTreeBuilder) float GetSplittingValue(udword index, udword axis) const;
+ override(AABBTreeBuilder) float GetSplittingValue(const udword* primitives, udword nb_prims, const AABB& global_box, udword axis) const;
+
+ const Point* mVertexArray; //!< Shortcut to an app-controlled array of vertices.
+ };
+
+ class OPCODE_API AABBTreeOfAABBsBuilder : public AABBTreeBuilder
+ {
+ public:
+ //! Constructor
+ AABBTreeOfAABBsBuilder() : mAABBArray(null) {}
+ //! Destructor
+ virtual ~AABBTreeOfAABBsBuilder() {}
+
+ override(AABBTreeBuilder) bool ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const;
+ override(AABBTreeBuilder) float GetSplittingValue(udword index, udword axis) const;
+
+ const AABB* mAABBArray; //!< Shortcut to an app-controlled array of AABBs.
+ };
+
+ class OPCODE_API AABBTreeOfTrianglesBuilder : public AABBTreeBuilder
+ {
+ public:
+ //! Constructor
+ AABBTreeOfTrianglesBuilder() : mIMesh(null) {}
+ //! Destructor
+ virtual ~AABBTreeOfTrianglesBuilder() {}
+
+ override(AABBTreeBuilder) bool ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const;
+ override(AABBTreeBuilder) float GetSplittingValue(udword index, udword axis) const;
+ override(AABBTreeBuilder) float GetSplittingValue(const udword* primitives, udword nb_prims, const AABB& global_box, udword axis) const;
+
+ const MeshInterface* mIMesh; //!< Shortcut to an app-controlled mesh interface
+ };
+
+#endif // __OPC_TREEBUILDERS_H__
diff --git a/Opcode/OPC_TreeCollider.cpp b/Opcode/OPC_TreeCollider.cpp
new file mode 100644
index 0000000..00be03c
--- /dev/null
+++ b/Opcode/OPC_TreeCollider.cpp
@@ -0,0 +1,943 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a tree collider.
+ * \file OPC_TreeCollider.cpp
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains an AABB tree collider.
+ * This class performs a collision test between two AABB trees.
+ *
+ * \class AABBTreeCollider
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+#include "OPC_BoxBoxOverlap.h"
+#include "OPC_TriBoxOverlap.h"
+#include "OPC_TriTriOverlap.h"
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBTreeCollider::AABBTreeCollider() :
+ mNbBVBVTests (0),
+ mNbPrimPrimTests (0),
+ mNbBVPrimTests (0),
+ mFullBoxBoxTest (true),
+ mFullPrimBoxTest (true),
+ mIMesh0 (null),
+ mIMesh1 (null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBTreeCollider::~AABBTreeCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Validates current settings. You should call this method after all the settings and callbacks have been defined.
+ * \return null if everything is ok, else a string describing the problem
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const char* AABBTreeCollider::ValidateSettings()
+{
+ if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!";
+ return null;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Generic collision query for generic OPCODE models. After the call, access the results with:
+ * - GetContactStatus()
+ * - GetNbPairs()
+ * - GetPairs()
+ *
+ * \param cache [in] collision cache for model pointers and a colliding pair of primitives
+ * \param world0 [in] world matrix for first object
+ * \param world1 [in] world matrix for second object
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeCollider::Collide(BVTCache& cache, const Matrix4x4* world0, const Matrix4x4* world1)
+{
+ // Checkings
+ if(!cache.Model0 || !cache.Model1) return false;
+ if(cache.Model0->HasLeafNodes()!=cache.Model1->HasLeafNodes()) return false;
+ if(cache.Model0->IsQuantized()!=cache.Model1->IsQuantized()) return false;
+
+ /*
+
+ Rules:
+ - perform hull test
+ - when hulls collide, disable hull test
+ - if meshes overlap, reset countdown
+ - if countdown reaches 0, enable hull test
+
+ */
+
+#ifdef __MESHMERIZER_H__
+ // Handle hulls
+ if(cache.HullTest)
+ {
+ if(cache.Model0->GetHull() && cache.Model1->GetHull())
+ {
+ struct Local
+ {
+ static Point* SVCallback(const Point& sv, udword& previndex, udword user_data)
+ {
+ CollisionHull* Hull = (CollisionHull*)user_data;
+ previndex = Hull->ComputeSupportingVertex(sv, previndex);
+ return (Point*)&Hull->GetVerts()[previndex];
+ }
+ };
+
+ bool Collide;
+
+ if(0)
+ {
+ static GJKEngine GJK;
+ static bool GJKInitDone=false;
+ if(!GJKInitDone)
+ {
+ GJK.Enable(GJK_BACKUP_PROCEDURE);
+ GJK.Enable(GJK_DEGENERATE);
+ GJK.Enable(GJK_HILLCLIMBING);
+ GJKInitDone = true;
+ }
+ GJK.SetCallbackObj0(Local::SVCallback);
+ GJK.SetCallbackObj1(Local::SVCallback);
+ GJK.SetUserData0(udword(cache.Model0->GetHull()));
+ GJK.SetUserData1(udword(cache.Model1->GetHull()));
+ Collide = GJK.Collide(*world0, *world1, &cache.SepVector);
+ }
+ else
+ {
+ static SVEngine SVE;
+ SVE.SetCallbackObj0(Local::SVCallback);
+ SVE.SetCallbackObj1(Local::SVCallback);
+ SVE.SetUserData0(udword(cache.Model0->GetHull()));
+ SVE.SetUserData1(udword(cache.Model1->GetHull()));
+ Collide = SVE.Collide(*world0, *world1, &cache.SepVector);
+ }
+
+ if(!Collide)
+ {
+ // Reset stats & contact status
+ mFlags &= ~OPC_CONTACT;
+ mNbBVBVTests = 0;
+ mNbPrimPrimTests = 0;
+ mNbBVPrimTests = 0;
+ mPairs.Reset();
+ return true;
+ }
+ }
+ }
+
+ // Here, hulls collide
+ cache.HullTest = false;
+#endif // __MESHMERIZER_H__
+
+ // Checkings
+ if(!Setup(cache.Model0->GetMeshInterface(), cache.Model1->GetMeshInterface())) return false;
+
+ // Simple double-dispatch
+ bool Status;
+ if(!cache.Model0->HasLeafNodes())
+ {
+ if(cache.Model0->IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* T0 = (const AABBQuantizedNoLeafTree*)cache.Model0->GetTree();
+ const AABBQuantizedNoLeafTree* T1 = (const AABBQuantizedNoLeafTree*)cache.Model1->GetTree();
+ Status = Collide(T0, T1, world0, world1, &cache);
+ }
+ else
+ {
+ const AABBNoLeafTree* T0 = (const AABBNoLeafTree*)cache.Model0->GetTree();
+ const AABBNoLeafTree* T1 = (const AABBNoLeafTree*)cache.Model1->GetTree();
+ Status = Collide(T0, T1, world0, world1, &cache);
+ }
+ }
+ else
+ {
+ if(cache.Model0->IsQuantized())
+ {
+ const AABBQuantizedTree* T0 = (const AABBQuantizedTree*)cache.Model0->GetTree();
+ const AABBQuantizedTree* T1 = (const AABBQuantizedTree*)cache.Model1->GetTree();
+ Status = Collide(T0, T1, world0, world1, &cache);
+ }
+ else
+ {
+ const AABBCollisionTree* T0 = (const AABBCollisionTree*)cache.Model0->GetTree();
+ const AABBCollisionTree* T1 = (const AABBCollisionTree*)cache.Model1->GetTree();
+ Status = Collide(T0, T1, world0, world1, &cache);
+ }
+ }
+
+#ifdef __MESHMERIZER_H__
+ if(Status)
+ {
+ // Reset counter as long as overlap occurs
+ if(GetContactStatus()) cache.ResetCountDown();
+
+ // Enable hull test again when counter reaches zero
+ cache.CountDown--;
+ if(!cache.CountDown)
+ {
+ cache.ResetCountDown();
+ cache.HullTest = true;
+ }
+ }
+#endif
+ return Status;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Initializes a collision query :
+ * - reset stats & contact status
+ * - setup matrices
+ *
+ * \param world0 [in] world matrix for first object
+ * \param world1 [in] world matrix for second object
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::InitQuery(const Matrix4x4* world0, const Matrix4x4* world1)
+{
+ // Reset stats & contact status
+ Collider::InitQuery();
+ mNbBVBVTests = 0;
+ mNbPrimPrimTests = 0;
+ mNbBVPrimTests = 0;
+ mPairs.Reset();
+
+ // Setup matrices
+ Matrix4x4 InvWorld0, InvWorld1;
+ if(world0) InvertPRMatrix(InvWorld0, *world0);
+ else InvWorld0.Identity();
+
+ if(world1) InvertPRMatrix(InvWorld1, *world1);
+ else InvWorld1.Identity();
+
+ Matrix4x4 World0to1 = world0 ? (*world0 * InvWorld1) : InvWorld1;
+ Matrix4x4 World1to0 = world1 ? (*world1 * InvWorld0) : InvWorld0;
+
+ mR0to1 = World0to1; World0to1.GetTrans(mT0to1);
+ mR1to0 = World1to0; World1to0.GetTrans(mT1to0);
+
+ // Precompute absolute 1-to-0 rotation matrix
+ for(udword i=0;i<3;i++)
+ {
+ for(udword j=0;j<3;j++)
+ {
+ // Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID)
+ mAR.m[i][j] = 1e-6f + fabsf(mR1to0.m[i][j]);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Takes advantage of temporal coherence.
+ * \param cache [in] cache for a pair of previously colliding primitives
+ * \return true if we can return immediately
+ * \warning only works for "First Contact" mode
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeCollider::CheckTemporalCoherence(Pair* cache)
+{
+ // Checkings
+ if(!cache) return false;
+
+ // Test previously colliding primitives first
+ if(TemporalCoherenceEnabled() && FirstContactEnabled())
+ {
+ PrimTest(cache->id0, cache->id1);
+ if(GetContactStatus()) return true;
+ }
+ return false;
+}
+
+#define UPDATE_CACHE \
+ if(cache && GetContactStatus()) \
+ { \
+ cache->id0 = mPairs.GetEntry(0); \
+ cache->id1 = mPairs.GetEntry(1); \
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Collision query for normal AABB trees.
+ * \param tree0 [in] AABB tree from first object
+ * \param tree1 [in] AABB tree from second object
+ * \param world0 [in] world matrix for first object
+ * \param world1 [in] world matrix for second object
+ * \param cache [in/out] cache for a pair of previously colliding primitives
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeCollider::Collide(const AABBCollisionTree* tree0, const AABBCollisionTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache)
+{
+ // Init collision query
+ InitQuery(world0, world1);
+
+ // Check previous state
+ if(CheckTemporalCoherence(cache)) return true;
+
+ // Perform collision query
+ _Collide(tree0->GetNodes(), tree1->GetNodes());
+
+ UPDATE_CACHE
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Collision query for no-leaf AABB trees.
+ * \param tree0 [in] AABB tree from first object
+ * \param tree1 [in] AABB tree from second object
+ * \param world0 [in] world matrix for first object
+ * \param world1 [in] world matrix for second object
+ * \param cache [in/out] cache for a pair of previously colliding primitives
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeCollider::Collide(const AABBNoLeafTree* tree0, const AABBNoLeafTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache)
+{
+ // Init collision query
+ InitQuery(world0, world1);
+
+ // Check previous state
+ if(CheckTemporalCoherence(cache)) return true;
+
+ // Perform collision query
+ _Collide(tree0->GetNodes(), tree1->GetNodes());
+
+ UPDATE_CACHE
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Collision query for quantized AABB trees.
+ * \param tree0 [in] AABB tree from first object
+ * \param tree1 [in] AABB tree from second object
+ * \param world0 [in] world matrix for first object
+ * \param world1 [in] world matrix for second object
+ * \param cache [in/out] cache for a pair of previously colliding primitives
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeCollider::Collide(const AABBQuantizedTree* tree0, const AABBQuantizedTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache)
+{
+ // Init collision query
+ InitQuery(world0, world1);
+
+ // Check previous state
+ if(CheckTemporalCoherence(cache)) return true;
+
+ // Setup dequantization coeffs
+ mCenterCoeff0 = tree0->mCenterCoeff;
+ mExtentsCoeff0 = tree0->mExtentsCoeff;
+ mCenterCoeff1 = tree1->mCenterCoeff;
+ mExtentsCoeff1 = tree1->mExtentsCoeff;
+
+ // Dequantize box A
+ const AABBQuantizedNode* N0 = tree0->GetNodes();
+ const Point a(float(N0->mAABB.mExtents[0]) * mExtentsCoeff0.x, float(N0->mAABB.mExtents[1]) * mExtentsCoeff0.y, float(N0->mAABB.mExtents[2]) * mExtentsCoeff0.z);
+ const Point Pa(float(N0->mAABB.mCenter[0]) * mCenterCoeff0.x, float(N0->mAABB.mCenter[1]) * mCenterCoeff0.y, float(N0->mAABB.mCenter[2]) * mCenterCoeff0.z);
+ // Dequantize box B
+ const AABBQuantizedNode* N1 = tree1->GetNodes();
+ const Point b(float(N1->mAABB.mExtents[0]) * mExtentsCoeff1.x, float(N1->mAABB.mExtents[1]) * mExtentsCoeff1.y, float(N1->mAABB.mExtents[2]) * mExtentsCoeff1.z);
+ const Point Pb(float(N1->mAABB.mCenter[0]) * mCenterCoeff1.x, float(N1->mAABB.mCenter[1]) * mCenterCoeff1.y, float(N1->mAABB.mCenter[2]) * mCenterCoeff1.z);
+
+ // Perform collision query
+ _Collide(N0, N1, a, Pa, b, Pb);
+
+ UPDATE_CACHE
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Collision query for quantized no-leaf AABB trees.
+ * \param tree0 [in] AABB tree from first object
+ * \param tree1 [in] AABB tree from second object
+ * \param world0 [in] world matrix for first object
+ * \param world1 [in] world matrix for second object
+ * \param cache [in/out] cache for a pair of previously colliding primitives
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeCollider::Collide(const AABBQuantizedNoLeafTree* tree0, const AABBQuantizedNoLeafTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache)
+{
+ // Init collision query
+ InitQuery(world0, world1);
+
+ // Check previous state
+ if(CheckTemporalCoherence(cache)) return true;
+
+ // Setup dequantization coeffs
+ mCenterCoeff0 = tree0->mCenterCoeff;
+ mExtentsCoeff0 = tree0->mExtentsCoeff;
+ mCenterCoeff1 = tree1->mCenterCoeff;
+ mExtentsCoeff1 = tree1->mExtentsCoeff;
+
+ // Perform collision query
+ _Collide(tree0->GetNodes(), tree1->GetNodes());
+
+ UPDATE_CACHE
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Standard trees
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// The normal AABB tree can use 2 different descent rules (with different performances)
+//#define ORIGINAL_CODE //!< UNC-like descent rules
+#define ALTERNATIVE_CODE //!< Alternative descent rules
+
+#ifdef ORIGINAL_CODE
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees.
+ * \param b0 [in] collision node from first tree
+ * \param b1 [in] collision node from second tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_Collide(const AABBCollisionNode* b0, const AABBCollisionNode* b1)
+{
+ // Perform BV-BV overlap test
+ if(!BoxBoxOverlap(b0->mAABB.mExtents, b0->mAABB.mCenter, b1->mAABB.mExtents, b1->mAABB.mCenter)) return;
+
+ if(b0->IsLeaf() && b1->IsLeaf()) { PrimTest(b0->GetPrimitive(), b1->GetPrimitive()); return; }
+
+ if(b1->IsLeaf() || (!b0->IsLeaf() && (b0->GetSize() > b1->GetSize())))
+ {
+ _Collide(b0->GetNeg(), b1);
+ if(ContactFound()) return;
+ _Collide(b0->GetPos(), b1);
+ }
+ else
+ {
+ _Collide(b0, b1->GetNeg());
+ if(ContactFound()) return;
+ _Collide(b0, b1->GetPos());
+ }
+}
+#endif
+
+#ifdef ALTERNATIVE_CODE
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees.
+ * \param b0 [in] collision node from first tree
+ * \param b1 [in] collision node from second tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_Collide(const AABBCollisionNode* b0, const AABBCollisionNode* b1)
+{
+ // Perform BV-BV overlap test
+ if(!BoxBoxOverlap(b0->mAABB.mExtents, b0->mAABB.mCenter, b1->mAABB.mExtents, b1->mAABB.mCenter))
+ {
+ return;
+ }
+
+ if(b0->IsLeaf())
+ {
+ if(b1->IsLeaf())
+ {
+ PrimTest(b0->GetPrimitive(), b1->GetPrimitive());
+ }
+ else
+ {
+ _Collide(b0, b1->GetNeg());
+ if(ContactFound()) return;
+ _Collide(b0, b1->GetPos());
+ }
+ }
+ else if(b1->IsLeaf())
+ {
+ _Collide(b0->GetNeg(), b1);
+ if(ContactFound()) return;
+ _Collide(b0->GetPos(), b1);
+ }
+ else
+ {
+ _Collide(b0->GetNeg(), b1->GetNeg());
+ if(ContactFound()) return;
+ _Collide(b0->GetNeg(), b1->GetPos());
+ if(ContactFound()) return;
+ _Collide(b0->GetPos(), b1->GetNeg());
+ if(ContactFound()) return;
+ _Collide(b0->GetPos(), b1->GetPos());
+ }
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// No-leaf trees
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Leaf-leaf test for two primitive indices.
+ * \param id0 [in] index from first leaf-triangle
+ * \param id1 [in] index from second leaf-triangle
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::PrimTest(udword id0, udword id1)
+{
+ // Request vertices from the app
+ VertexPointers VP0;
+ VertexPointers VP1;
+ mIMesh0->GetTriangle(VP0, id0);
+ mIMesh1->GetTriangle(VP1, id1);
+
+ // Transform from space 1 to space 0
+ Point u0,u1,u2;
+ TransformPoint(u0, *VP1.Vertex[0], mR1to0, mT1to0);
+ TransformPoint(u1, *VP1.Vertex[1], mR1to0, mT1to0);
+ TransformPoint(u2, *VP1.Vertex[2], mR1to0, mT1to0);
+
+ // Perform triangle-triangle overlap test
+ if(TriTriOverlap(*VP0.Vertex[0], *VP0.Vertex[1], *VP0.Vertex[2], u0, u1, u2))
+ {
+ // Keep track of colliding pairs
+ mPairs.Add(id0).Add(id1);
+ // Set contact status
+ mFlags |= OPC_CONTACT;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Leaf-leaf test for a previously fetched triangle from tree A (in B's space) and a new leaf from B.
+ * \param id1 [in] leaf-triangle index from tree B
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ void AABBTreeCollider::PrimTestTriIndex(udword id1)
+{
+ // Request vertices from the app
+ VertexPointers VP;
+ mIMesh1->GetTriangle(VP, id1);
+
+ // Perform triangle-triangle overlap test
+ if(TriTriOverlap(mLeafVerts[0], mLeafVerts[1], mLeafVerts[2], *VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2]))
+ {
+ // Keep track of colliding pairs
+ mPairs.Add(mLeafIndex).Add(id1);
+ // Set contact status
+ mFlags |= OPC_CONTACT;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Leaf-leaf test for a previously fetched triangle from tree B (in A's space) and a new leaf from A.
+ * \param id0 [in] leaf-triangle index from tree A
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ void AABBTreeCollider::PrimTestIndexTri(udword id0)
+{
+ // Request vertices from the app
+ VertexPointers VP;
+ mIMesh0->GetTriangle(VP, id0);
+
+ // Perform triangle-triangle overlap test
+ if(TriTriOverlap(mLeafVerts[0], mLeafVerts[1], mLeafVerts[2], *VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2]))
+ {
+ // Keep track of colliding pairs
+ mPairs.Add(id0).Add(mLeafIndex);
+ // Set contact status
+ mFlags |= OPC_CONTACT;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision of a leaf node from A and a branch from B.
+ * \param b [in] collision node from second tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_CollideTriBox(const AABBNoLeafNode* b)
+{
+ // Perform triangle-box overlap test
+ if(!TriBoxOverlap(b->mAABB.mCenter, b->mAABB.mExtents)) return;
+
+ // Keep same triangle, deal with first child
+ if(b->HasPosLeaf()) PrimTestTriIndex(b->GetPosPrimitive());
+ else _CollideTriBox(b->GetPos());
+
+ if(ContactFound()) return;
+
+ // Keep same triangle, deal with second child
+ if(b->HasNegLeaf()) PrimTestTriIndex(b->GetNegPrimitive());
+ else _CollideTriBox(b->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision of a leaf node from B and a branch from A.
+ * \param b [in] collision node from first tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_CollideBoxTri(const AABBNoLeafNode* b)
+{
+ // Perform triangle-box overlap test
+ if(!TriBoxOverlap(b->mAABB.mCenter, b->mAABB.mExtents)) return;
+
+ // Keep same triangle, deal with first child
+ if(b->HasPosLeaf()) PrimTestIndexTri(b->GetPosPrimitive());
+ else _CollideBoxTri(b->GetPos());
+
+ if(ContactFound()) return;
+
+ // Keep same triangle, deal with second child
+ if(b->HasNegLeaf()) PrimTestIndexTri(b->GetNegPrimitive());
+ else _CollideBoxTri(b->GetNeg());
+}
+
+//! Request triangle vertices from the app and transform them
+#define FETCH_LEAF(prim_index, imesh, rot, trans) \
+ mLeafIndex = prim_index; \
+ /* Request vertices from the app */ \
+ VertexPointers VP; imesh->GetTriangle(VP, prim_index); \
+ /* Transform them in a common space */ \
+ TransformPoint(mLeafVerts[0], *VP.Vertex[0], rot, trans); \
+ TransformPoint(mLeafVerts[1], *VP.Vertex[1], rot, trans); \
+ TransformPoint(mLeafVerts[2], *VP.Vertex[2], rot, trans);
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for no-leaf AABB trees.
+ * \param a [in] collision node from first tree
+ * \param b [in] collision node from second tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_Collide(const AABBNoLeafNode* a, const AABBNoLeafNode* b)
+{
+ // Perform BV-BV overlap test
+ if(!BoxBoxOverlap(a->mAABB.mExtents, a->mAABB.mCenter, b->mAABB.mExtents, b->mAABB.mCenter)) return;
+
+ // Catch leaf status
+ BOOL BHasPosLeaf = b->HasPosLeaf();
+ BOOL BHasNegLeaf = b->HasNegLeaf();
+
+ if(a->HasPosLeaf())
+ {
+ FETCH_LEAF(a->GetPosPrimitive(), mIMesh0, mR0to1, mT0to1)
+
+ if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive());
+ else _CollideTriBox(b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive());
+ else _CollideTriBox(b->GetNeg());
+ }
+ else
+ {
+ if(BHasPosLeaf)
+ {
+ FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0)
+
+ _CollideBoxTri(a->GetPos());
+ }
+ else _Collide(a->GetPos(), b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(BHasNegLeaf)
+ {
+ FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0)
+
+ _CollideBoxTri(a->GetPos());
+ }
+ else _Collide(a->GetPos(), b->GetNeg());
+ }
+
+ if(ContactFound()) return;
+
+ if(a->HasNegLeaf())
+ {
+ FETCH_LEAF(a->GetNegPrimitive(), mIMesh0, mR0to1, mT0to1)
+
+ if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive());
+ else _CollideTriBox(b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive());
+ else _CollideTriBox(b->GetNeg());
+ }
+ else
+ {
+ if(BHasPosLeaf)
+ {
+ // ### That leaf has possibly already been fetched
+ FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0)
+
+ _CollideBoxTri(a->GetNeg());
+ }
+ else _Collide(a->GetNeg(), b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(BHasNegLeaf)
+ {
+ // ### That leaf has possibly already been fetched
+ FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0)
+
+ _CollideBoxTri(a->GetNeg());
+ }
+ else _Collide(a->GetNeg(), b->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Quantized trees
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized AABB trees.
+ * \param b0 [in] collision node from first tree
+ * \param b1 [in] collision node from second tree
+ * \param a [in] extent from box A
+ * \param Pa [in] center from box A
+ * \param b [in] extent from box B
+ * \param Pb [in] center from box B
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_Collide(const AABBQuantizedNode* b0, const AABBQuantizedNode* b1, const Point& a, const Point& Pa, const Point& b, const Point& Pb)
+{
+ // Perform BV-BV overlap test
+ if(!BoxBoxOverlap(a, Pa, b, Pb)) return;
+
+ if(b0->IsLeaf() && b1->IsLeaf()) { PrimTest(b0->GetPrimitive(), b1->GetPrimitive()); return; }
+
+ if(b1->IsLeaf() || (!b0->IsLeaf() && (b0->GetSize() > b1->GetSize())))
+ {
+ // Dequantize box
+ const QuantizedAABB* Box = &b0->GetNeg()->mAABB;
+ const Point negPa(float(Box->mCenter[0]) * mCenterCoeff0.x, float(Box->mCenter[1]) * mCenterCoeff0.y, float(Box->mCenter[2]) * mCenterCoeff0.z);
+ const Point nega(float(Box->mExtents[0]) * mExtentsCoeff0.x, float(Box->mExtents[1]) * mExtentsCoeff0.y, float(Box->mExtents[2]) * mExtentsCoeff0.z);
+ _Collide(b0->GetNeg(), b1, nega, negPa, b, Pb);
+
+ if(ContactFound()) return;
+
+ // Dequantize box
+ Box = &b0->GetPos()->mAABB;
+ const Point posPa(float(Box->mCenter[0]) * mCenterCoeff0.x, float(Box->mCenter[1]) * mCenterCoeff0.y, float(Box->mCenter[2]) * mCenterCoeff0.z);
+ const Point posa(float(Box->mExtents[0]) * mExtentsCoeff0.x, float(Box->mExtents[1]) * mExtentsCoeff0.y, float(Box->mExtents[2]) * mExtentsCoeff0.z);
+ _Collide(b0->GetPos(), b1, posa, posPa, b, Pb);
+ }
+ else
+ {
+ // Dequantize box
+ const QuantizedAABB* Box = &b1->GetNeg()->mAABB;
+ const Point negPb(float(Box->mCenter[0]) * mCenterCoeff1.x, float(Box->mCenter[1]) * mCenterCoeff1.y, float(Box->mCenter[2]) * mCenterCoeff1.z);
+ const Point negb(float(Box->mExtents[0]) * mExtentsCoeff1.x, float(Box->mExtents[1]) * mExtentsCoeff1.y, float(Box->mExtents[2]) * mExtentsCoeff1.z);
+ _Collide(b0, b1->GetNeg(), a, Pa, negb, negPb);
+
+ if(ContactFound()) return;
+
+ // Dequantize box
+ Box = &b1->GetPos()->mAABB;
+ const Point posPb(float(Box->mCenter[0]) * mCenterCoeff1.x, float(Box->mCenter[1]) * mCenterCoeff1.y, float(Box->mCenter[2]) * mCenterCoeff1.z);
+ const Point posb(float(Box->mExtents[0]) * mExtentsCoeff1.x, float(Box->mExtents[1]) * mExtentsCoeff1.y, float(Box->mExtents[2]) * mExtentsCoeff1.z);
+ _Collide(b0, b1->GetPos(), a, Pa, posb, posPb);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Quantized no-leaf trees
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision of a leaf node from A and a quantized branch from B.
+ * \param leaf [in] leaf triangle from first tree
+ * \param b [in] collision node from second tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_CollideTriBox(const AABBQuantizedNoLeafNode* b)
+{
+ // Dequantize box
+ const QuantizedAABB* bb = &b->mAABB;
+ const Point Pb(float(bb->mCenter[0]) * mCenterCoeff1.x, float(bb->mCenter[1]) * mCenterCoeff1.y, float(bb->mCenter[2]) * mCenterCoeff1.z);
+ const Point eb(float(bb->mExtents[0]) * mExtentsCoeff1.x, float(bb->mExtents[1]) * mExtentsCoeff1.y, float(bb->mExtents[2]) * mExtentsCoeff1.z);
+
+ // Perform triangle-box overlap test
+ if(!TriBoxOverlap(Pb, eb)) return;
+
+ if(b->HasPosLeaf()) PrimTestTriIndex(b->GetPosPrimitive());
+ else _CollideTriBox(b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(b->HasNegLeaf()) PrimTestTriIndex(b->GetNegPrimitive());
+ else _CollideTriBox(b->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision of a leaf node from B and a quantized branch from A.
+ * \param b [in] collision node from first tree
+ * \param leaf [in] leaf triangle from second tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_CollideBoxTri(const AABBQuantizedNoLeafNode* b)
+{
+ // Dequantize box
+ const QuantizedAABB* bb = &b->mAABB;
+ const Point Pa(float(bb->mCenter[0]) * mCenterCoeff0.x, float(bb->mCenter[1]) * mCenterCoeff0.y, float(bb->mCenter[2]) * mCenterCoeff0.z);
+ const Point ea(float(bb->mExtents[0]) * mExtentsCoeff0.x, float(bb->mExtents[1]) * mExtentsCoeff0.y, float(bb->mExtents[2]) * mExtentsCoeff0.z);
+
+ // Perform triangle-box overlap test
+ if(!TriBoxOverlap(Pa, ea)) return;
+
+ if(b->HasPosLeaf()) PrimTestIndexTri(b->GetPosPrimitive());
+ else _CollideBoxTri(b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(b->HasNegLeaf()) PrimTestIndexTri(b->GetNegPrimitive());
+ else _CollideBoxTri(b->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized no-leaf AABB trees.
+ * \param a [in] collision node from first tree
+ * \param b [in] collision node from second tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_Collide(const AABBQuantizedNoLeafNode* a, const AABBQuantizedNoLeafNode* b)
+{
+ // Dequantize box A
+ const QuantizedAABB* ab = &a->mAABB;
+ const Point Pa(float(ab->mCenter[0]) * mCenterCoeff0.x, float(ab->mCenter[1]) * mCenterCoeff0.y, float(ab->mCenter[2]) * mCenterCoeff0.z);
+ const Point ea(float(ab->mExtents[0]) * mExtentsCoeff0.x, float(ab->mExtents[1]) * mExtentsCoeff0.y, float(ab->mExtents[2]) * mExtentsCoeff0.z);
+ // Dequantize box B
+ const QuantizedAABB* bb = &b->mAABB;
+ const Point Pb(float(bb->mCenter[0]) * mCenterCoeff1.x, float(bb->mCenter[1]) * mCenterCoeff1.y, float(bb->mCenter[2]) * mCenterCoeff1.z);
+ const Point eb(float(bb->mExtents[0]) * mExtentsCoeff1.x, float(bb->mExtents[1]) * mExtentsCoeff1.y, float(bb->mExtents[2]) * mExtentsCoeff1.z);
+
+ // Perform BV-BV overlap test
+ if(!BoxBoxOverlap(ea, Pa, eb, Pb)) return;
+
+ // Catch leaf status
+ BOOL BHasPosLeaf = b->HasPosLeaf();
+ BOOL BHasNegLeaf = b->HasNegLeaf();
+
+ if(a->HasPosLeaf())
+ {
+ FETCH_LEAF(a->GetPosPrimitive(), mIMesh0, mR0to1, mT0to1)
+
+ if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive());
+ else _CollideTriBox(b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive());
+ else _CollideTriBox(b->GetNeg());
+ }
+ else
+ {
+ if(BHasPosLeaf)
+ {
+ FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0)
+
+ _CollideBoxTri(a->GetPos());
+ }
+ else _Collide(a->GetPos(), b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(BHasNegLeaf)
+ {
+ FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0)
+
+ _CollideBoxTri(a->GetPos());
+ }
+ else _Collide(a->GetPos(), b->GetNeg());
+ }
+
+ if(ContactFound()) return;
+
+ if(a->HasNegLeaf())
+ {
+ FETCH_LEAF(a->GetNegPrimitive(), mIMesh0, mR0to1, mT0to1)
+
+ if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive());
+ else _CollideTriBox(b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive());
+ else _CollideTriBox(b->GetNeg());
+ }
+ else
+ {
+ if(BHasPosLeaf)
+ {
+ // ### That leaf has possibly already been fetched
+ FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0)
+
+ _CollideBoxTri(a->GetNeg());
+ }
+ else _Collide(a->GetNeg(), b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(BHasNegLeaf)
+ {
+ // ### That leaf has possibly already been fetched
+ FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0)
+
+ _CollideBoxTri(a->GetNeg());
+ }
+ else _Collide(a->GetNeg(), b->GetNeg());
+ }
+}
diff --git a/Opcode/OPC_TreeCollider.h b/Opcode/OPC_TreeCollider.h
new file mode 100644
index 0000000..bb8d1b6
--- /dev/null
+++ b/Opcode/OPC_TreeCollider.h
@@ -0,0 +1,244 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a tree collider.
+ * \file OPC_TreeCollider.h
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_TREECOLLIDER_H__
+#define __OPC_TREECOLLIDER_H__
+
+ //! This structure holds cached information used by the algorithm.
+ //! Two model pointers and two colliding primitives are cached. Model pointers are assigned
+ //! to their respective meshes, and the pair of colliding primitives is used for temporal
+ //! coherence. That is, in case temporal coherence is enabled, those two primitives are
+ //! tested for overlap before everything else. If they still collide, we're done before
+ //! even entering the recursive collision code.
+ struct OPCODE_API BVTCache : Pair
+ {
+ //! Constructor
+ inline_ BVTCache()
+ {
+ ResetCache();
+ ResetCountDown();
+ }
+
+ void ResetCache()
+ {
+ Model0 = null;
+ Model1 = null;
+ id0 = 0;
+ id1 = 1;
+#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE !
+ HullTest = true;
+ SepVector.pid = 0;
+ SepVector.qid = 0;
+ SepVector.SV = Point(1.0f, 0.0f, 0.0f);
+#endif // __MESHMERIZER_H__
+ }
+
+ inline_ void ResetCountDown()
+ {
+#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE !
+ CountDown = 50;
+#endif // __MESHMERIZER_H__
+ }
+
+ const Model* Model0; //!< Model for first object
+ const Model* Model1; //!< Model for second object
+
+#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE !
+ SVCache SepVector;
+ udword CountDown;
+ bool HullTest;
+#endif // __MESHMERIZER_H__
+ };
+
+ class OPCODE_API AABBTreeCollider : public Collider
+ {
+ public:
+ // Constructor / Destructor
+ AABBTreeCollider();
+ virtual ~AABBTreeCollider();
+ // Generic collision query
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Generic collision query for generic OPCODE models. After the call, access the results with:
+ * - GetContactStatus()
+ * - GetNbPairs()
+ * - GetPairs()
+ *
+ * \param cache [in] collision cache for model pointers and a colliding pair of primitives
+ * \param world0 [in] world matrix for first object, or null
+ * \param world1 [in] world matrix for second object, or null
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool Collide(BVTCache& cache, const Matrix4x4* world0=null, const Matrix4x4* world1=null);
+
+ // Collision queries
+ bool Collide(const AABBCollisionTree* tree0, const AABBCollisionTree* tree1, const Matrix4x4* world0=null, const Matrix4x4* world1=null, Pair* cache=null);
+ bool Collide(const AABBNoLeafTree* tree0, const AABBNoLeafTree* tree1, const Matrix4x4* world0=null, const Matrix4x4* world1=null, Pair* cache=null);
+ bool Collide(const AABBQuantizedTree* tree0, const AABBQuantizedTree* tree1, const Matrix4x4* world0=null, const Matrix4x4* world1=null, Pair* cache=null);
+ bool Collide(const AABBQuantizedNoLeafTree* tree0, const AABBQuantizedNoLeafTree* tree1, const Matrix4x4* world0=null, const Matrix4x4* world1=null, Pair* cache=null);
+ // Settings
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Settings: selects between full box-box tests or "SAT-lite" tests (where Class III axes are discarded)
+ * \param flag [in] true for full tests, false for coarse tests
+ * \see SetFullPrimBoxTest(bool flag)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetFullBoxBoxTest(bool flag) { mFullBoxBoxTest = flag; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Settings: selects between full triangle-box tests or "SAT-lite" tests (where Class III axes are discarded)
+ * \param flag [in] true for full tests, false for coarse tests
+ * \see SetFullBoxBoxTest(bool flag)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetFullPrimBoxTest(bool flag) { mFullPrimBoxTest = flag; }
+
+ // Stats
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Stats: gets the number of BV-BV overlap tests after a collision query.
+ * \see GetNbPrimPrimTests()
+ * \see GetNbBVPrimTests()
+ * \return the number of BV-BV tests performed during last query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbBVBVTests() const { return mNbBVBVTests; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Stats: gets the number of Triangle-Triangle overlap tests after a collision query.
+ * \see GetNbBVBVTests()
+ * \see GetNbBVPrimTests()
+ * \return the number of Triangle-Triangle tests performed during last query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbPrimPrimTests() const { return mNbPrimPrimTests; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Stats: gets the number of BV-Triangle overlap tests after a collision query.
+ * \see GetNbBVBVTests()
+ * \see GetNbPrimPrimTests()
+ * \return the number of BV-Triangle tests performed during last query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbBVPrimTests() const { return mNbBVPrimTests; }
+
+ // Data access
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the number of contacts after a collision query.
+ * \see GetContactStatus()
+ * \see GetPairs()
+ * \return the number of contacts / colliding pairs.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbPairs() const { return mPairs.GetNbEntries()>>1; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the pairs of colliding triangles after a collision query.
+ * \see GetContactStatus()
+ * \see GetNbPairs()
+ * \return the list of colliding pairs (triangle indices)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ const Pair* GetPairs() const { return (const Pair*)mPairs.GetEntries(); }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Validates current settings. You should call this method after all the settings and callbacks have been defined for a collider.
+ * \return null if everything is ok, else a string describing the problem
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(Collider) const char* ValidateSettings();
+
+ protected:
+ // Colliding pairs
+ Container mPairs; //!< Pairs of colliding primitives
+ // User mesh interfaces
+ const MeshInterface* mIMesh0; //!< User-defined mesh interface for object0
+ const MeshInterface* mIMesh1; //!< User-defined mesh interface for object1
+ // Stats
+ udword mNbBVBVTests; //!< Number of BV-BV tests
+ udword mNbPrimPrimTests; //!< Number of Primitive-Primitive tests
+ udword mNbBVPrimTests; //!< Number of BV-Primitive tests
+ // Precomputed data
+ Matrix3x3 mAR; //!< Absolute rotation matrix
+ Matrix3x3 mR0to1; //!< Rotation from object0 to object1
+ Matrix3x3 mR1to0; //!< Rotation from object1 to object0
+ Point mT0to1; //!< Translation from object0 to object1
+ Point mT1to0; //!< Translation from object1 to object0
+ // Dequantization coeffs
+ Point mCenterCoeff0;
+ Point mExtentsCoeff0;
+ Point mCenterCoeff1;
+ Point mExtentsCoeff1;
+ // Leaf description
+ Point mLeafVerts[3]; //!< Triangle vertices
+ udword mLeafIndex; //!< Triangle index
+ // Settings
+ bool mFullBoxBoxTest; //!< Perform full BV-BV tests (true) or SAT-lite tests (false)
+ bool mFullPrimBoxTest; //!< Perform full Primitive-BV tests (true) or SAT-lite tests (false)
+ // Internal methods
+
+ // Standard AABB trees
+ void _Collide(const AABBCollisionNode* b0, const AABBCollisionNode* b1);
+ // Quantized AABB trees
+ void _Collide(const AABBQuantizedNode* b0, const AABBQuantizedNode* b1, const Point& a, const Point& Pa, const Point& b, const Point& Pb);
+ // No-leaf AABB trees
+ void _CollideTriBox(const AABBNoLeafNode* b);
+ void _CollideBoxTri(const AABBNoLeafNode* b);
+ void _Collide(const AABBNoLeafNode* a, const AABBNoLeafNode* b);
+ // Quantized no-leaf AABB trees
+ void _CollideTriBox(const AABBQuantizedNoLeafNode* b);
+ void _CollideBoxTri(const AABBQuantizedNoLeafNode* b);
+ void _Collide(const AABBQuantizedNoLeafNode* a, const AABBQuantizedNoLeafNode* b);
+ // Overlap tests
+ void PrimTest(udword id0, udword id1);
+ inline_ void PrimTestTriIndex(udword id1);
+ inline_ void PrimTestIndexTri(udword id0);
+
+ inline_ BOOL BoxBoxOverlap(const Point& ea, const Point& ca, const Point& eb, const Point& cb);
+ inline_ BOOL TriBoxOverlap(const Point& center, const Point& extents);
+ inline_ BOOL TriTriOverlap(const Point& V0, const Point& V1, const Point& V2, const Point& U0, const Point& U1, const Point& U2);
+ // Init methods
+ void InitQuery(const Matrix4x4* world0=null, const Matrix4x4* world1=null);
+ bool CheckTemporalCoherence(Pair* cache);
+
+ inline_ BOOL Setup(const MeshInterface* mi0, const MeshInterface* mi1)
+ {
+ mIMesh0 = mi0;
+ mIMesh1 = mi1;
+
+ if(!mIMesh0 || !mIMesh1) return FALSE;
+
+ return TRUE;
+ }
+ };
+
+#endif // __OPC_TREECOLLIDER_H__
diff --git a/Opcode/OPC_TriBoxOverlap.h b/Opcode/OPC_TriBoxOverlap.h
new file mode 100644
index 0000000..923e324
--- /dev/null
+++ b/Opcode/OPC_TriBoxOverlap.h
@@ -0,0 +1,339 @@
+
+//! This macro quickly finds the min & max values among 3 variables
+#define FINDMINMAX(x0, x1, x2, min, max) \
+ min = max = x0; \
+ if(x1<min) min=x1; \
+ if(x1>max) max=x1; \
+ if(x2<min) min=x2; \
+ if(x2>max) max=x2;
+
+//! TO BE DOCUMENTED
+inline_ BOOL planeBoxOverlap(const Point& normal, const float d, const Point& maxbox)
+{
+ Point vmin, vmax;
+ for(udword q=0;q<=2;q++)
+ {
+ if(normal[q]>0.0f) { vmin[q]=-maxbox[q]; vmax[q]=maxbox[q]; }
+ else { vmin[q]=maxbox[q]; vmax[q]=-maxbox[q]; }
+ }
+ if((normal|vmin)+d>0.0f) return FALSE;
+ if((normal|vmax)+d>=0.0f) return TRUE;
+
+ return FALSE;
+}
+
+//! TO BE DOCUMENTED
+#define AXISTEST_X01(a, b, fa, fb) \
+ min = a*v0.y - b*v0.z; \
+ max = a*v2.y - b*v2.z; \
+ if(min>max) {const float tmp=max; max=min; min=tmp; } \
+ rad = fa * extents.y + fb * extents.z; \
+ if(min>rad || max<-rad) return FALSE;
+
+//! TO BE DOCUMENTED
+#define AXISTEST_X2(a, b, fa, fb) \
+ min = a*v0.y - b*v0.z; \
+ max = a*v1.y - b*v1.z; \
+ if(min>max) {const float tmp=max; max=min; min=tmp; } \
+ rad = fa * extents.y + fb * extents.z; \
+ if(min>rad || max<-rad) return FALSE;
+
+//! TO BE DOCUMENTED
+#define AXISTEST_Y02(a, b, fa, fb) \
+ min = b*v0.z - a*v0.x; \
+ max = b*v2.z - a*v2.x; \
+ if(min>max) {const float tmp=max; max=min; min=tmp; } \
+ rad = fa * extents.x + fb * extents.z; \
+ if(min>rad || max<-rad) return FALSE;
+
+//! TO BE DOCUMENTED
+#define AXISTEST_Y1(a, b, fa, fb) \
+ min = b*v0.z - a*v0.x; \
+ max = b*v1.z - a*v1.x; \
+ if(min>max) {const float tmp=max; max=min; min=tmp; } \
+ rad = fa * extents.x + fb * extents.z; \
+ if(min>rad || max<-rad) return FALSE;
+
+//! TO BE DOCUMENTED
+#define AXISTEST_Z12(a, b, fa, fb) \
+ min = a*v1.x - b*v1.y; \
+ max = a*v2.x - b*v2.y; \
+ if(min>max) {const float tmp=max; max=min; min=tmp; } \
+ rad = fa * extents.x + fb * extents.y; \
+ if(min>rad || max<-rad) return FALSE;
+
+//! TO BE DOCUMENTED
+#define AXISTEST_Z0(a, b, fa, fb) \
+ min = a*v0.x - b*v0.y; \
+ max = a*v1.x - b*v1.y; \
+ if(min>max) {const float tmp=max; max=min; min=tmp; } \
+ rad = fa * extents.x + fb * extents.y; \
+ if(min>rad || max<-rad) return FALSE;
+
+// compute triangle edges
+// - edges lazy evaluated to take advantage of early exits
+// - fabs precomputed (half less work, possible since extents are always >0)
+// - customized macros to take advantage of the null component
+// - axis vector discarded, possibly saves useless movs
+#define IMPLEMENT_CLASS3_TESTS \
+ float rad; \
+ float min, max; \
+ \
+ const float fey0 = fabsf(e0.y); \
+ const float fez0 = fabsf(e0.z); \
+ AXISTEST_X01(e0.z, e0.y, fez0, fey0); \
+ const float fex0 = fabsf(e0.x); \
+ AXISTEST_Y02(e0.z, e0.x, fez0, fex0); \
+ AXISTEST_Z12(e0.y, e0.x, fey0, fex0); \
+ \
+ const float fey1 = fabsf(e1.y); \
+ const float fez1 = fabsf(e1.z); \
+ AXISTEST_X01(e1.z, e1.y, fez1, fey1); \
+ const float fex1 = fabsf(e1.x); \
+ AXISTEST_Y02(e1.z, e1.x, fez1, fex1); \
+ AXISTEST_Z0(e1.y, e1.x, fey1, fex1); \
+ \
+ const Point e2 = mLeafVerts[0] - mLeafVerts[2]; \
+ const float fey2 = fabsf(e2.y); \
+ const float fez2 = fabsf(e2.z); \
+ AXISTEST_X2(e2.z, e2.y, fez2, fey2); \
+ const float fex2 = fabsf(e2.x); \
+ AXISTEST_Y1(e2.z, e2.x, fez2, fex2); \
+ AXISTEST_Z12(e2.y, e2.x, fey2, fex2);
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Triangle-Box overlap test using the separating axis theorem.
+ * This is the code from Tomas Möller, a bit optimized:
+ * - with some more lazy evaluation (faster path on PC)
+ * - with a tiny bit of assembly
+ * - with "SAT-lite" applied if needed
+ * - and perhaps with some more minor modifs...
+ *
+ * \param center [in] box center
+ * \param extents [in] box extents
+ * \return true if triangle & box overlap
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL AABBTreeCollider::TriBoxOverlap(const Point& center, const Point& extents)
+{
+ // Stats
+ mNbBVPrimTests++;
+
+ // use separating axis theorem to test overlap between triangle and box
+ // need to test for overlap in these directions:
+ // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle
+ // we do not even need to test these)
+ // 2) normal of the triangle
+ // 3) crossproduct(edge from tri, {x,y,z}-directin)
+ // this gives 3x3=9 more tests
+
+ // move everything so that the boxcenter is in (0,0,0)
+ Point v0, v1, v2;
+ v0.x = mLeafVerts[0].x - center.x;
+ v1.x = mLeafVerts[1].x - center.x;
+ v2.x = mLeafVerts[2].x - center.x;
+
+ // First, test overlap in the {x,y,z}-directions
+#ifdef OPC_USE_FCOMI
+ // find min, max of the triangle in x-direction, and test for overlap in X
+ if(FCMin3(v0.x, v1.x, v2.x)>extents.x) return FALSE;
+ if(FCMax3(v0.x, v1.x, v2.x)<-extents.x) return FALSE;
+
+ // same for Y
+ v0.y = mLeafVerts[0].y - center.y;
+ v1.y = mLeafVerts[1].y - center.y;
+ v2.y = mLeafVerts[2].y - center.y;
+
+ if(FCMin3(v0.y, v1.y, v2.y)>extents.y) return FALSE;
+ if(FCMax3(v0.y, v1.y, v2.y)<-extents.y) return FALSE;
+
+ // same for Z
+ v0.z = mLeafVerts[0].z - center.z;
+ v1.z = mLeafVerts[1].z - center.z;
+ v2.z = mLeafVerts[2].z - center.z;
+
+ if(FCMin3(v0.z, v1.z, v2.z)>extents.z) return FALSE;
+ if(FCMax3(v0.z, v1.z, v2.z)<-extents.z) return FALSE;
+#else
+ float min,max;
+ // Find min, max of the triangle in x-direction, and test for overlap in X
+ FINDMINMAX(v0.x, v1.x, v2.x, min, max);
+ if(min>extents.x || max<-extents.x) return FALSE;
+
+ // Same for Y
+ v0.y = mLeafVerts[0].y - center.y;
+ v1.y = mLeafVerts[1].y - center.y;
+ v2.y = mLeafVerts[2].y - center.y;
+
+ FINDMINMAX(v0.y, v1.y, v2.y, min, max);
+ if(min>extents.y || max<-extents.y) return FALSE;
+
+ // Same for Z
+ v0.z = mLeafVerts[0].z - center.z;
+ v1.z = mLeafVerts[1].z - center.z;
+ v2.z = mLeafVerts[2].z - center.z;
+
+ FINDMINMAX(v0.z, v1.z, v2.z, min, max);
+ if(min>extents.z || max<-extents.z) return FALSE;
+#endif
+ // 2) Test if the box intersects the plane of the triangle
+ // compute plane equation of triangle: normal*x+d=0
+ // ### could be precomputed since we use the same leaf triangle several times
+ const Point e0 = v1 - v0;
+ const Point e1 = v2 - v1;
+ const Point normal = e0 ^ e1;
+ const float d = -normal|v0;
+ if(!planeBoxOverlap(normal, d, extents)) return FALSE;
+
+ // 3) "Class III" tests
+ if(mFullPrimBoxTest)
+ {
+ IMPLEMENT_CLASS3_TESTS
+ }
+ return TRUE;
+}
+
+//! A dedicated version where the box is constant
+inline_ BOOL OBBCollider::TriBoxOverlap()
+{
+ // Stats
+ mNbVolumePrimTests++;
+
+ // Hook
+ const Point& extents = mBoxExtents;
+ const Point& v0 = mLeafVerts[0];
+ const Point& v1 = mLeafVerts[1];
+ const Point& v2 = mLeafVerts[2];
+
+ // use separating axis theorem to test overlap between triangle and box
+ // need to test for overlap in these directions:
+ // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle
+ // we do not even need to test these)
+ // 2) normal of the triangle
+ // 3) crossproduct(edge from tri, {x,y,z}-directin)
+ // this gives 3x3=9 more tests
+
+ // Box center is already in (0,0,0)
+
+ // First, test overlap in the {x,y,z}-directions
+#ifdef OPC_USE_FCOMI
+ // find min, max of the triangle in x-direction, and test for overlap in X
+ if(FCMin3(v0.x, v1.x, v2.x)>mBoxExtents.x) return FALSE;
+ if(FCMax3(v0.x, v1.x, v2.x)<-mBoxExtents.x) return FALSE;
+
+ if(FCMin3(v0.y, v1.y, v2.y)>mBoxExtents.y) return FALSE;
+ if(FCMax3(v0.y, v1.y, v2.y)<-mBoxExtents.y) return FALSE;
+
+ if(FCMin3(v0.z, v1.z, v2.z)>mBoxExtents.z) return FALSE;
+ if(FCMax3(v0.z, v1.z, v2.z)<-mBoxExtents.z) return FALSE;
+#else
+ float min,max;
+ // Find min, max of the triangle in x-direction, and test for overlap in X
+ FINDMINMAX(v0.x, v1.x, v2.x, min, max);
+ if(min>mBoxExtents.x || max<-mBoxExtents.x) return FALSE;
+
+ FINDMINMAX(v0.y, v1.y, v2.y, min, max);
+ if(min>mBoxExtents.y || max<-mBoxExtents.y) return FALSE;
+
+ FINDMINMAX(v0.z, v1.z, v2.z, min, max);
+ if(min>mBoxExtents.z || max<-mBoxExtents.z) return FALSE;
+#endif
+ // 2) Test if the box intersects the plane of the triangle
+ // compute plane equation of triangle: normal*x+d=0
+ // ### could be precomputed since we use the same leaf triangle several times
+ const Point e0 = v1 - v0;
+ const Point e1 = v2 - v1;
+ const Point normal = e0 ^ e1;
+ const float d = -normal|v0;
+ if(!planeBoxOverlap(normal, d, mBoxExtents)) return FALSE;
+
+ // 3) "Class III" tests - here we always do full tests since the box is a primitive (not a BV)
+ {
+ IMPLEMENT_CLASS3_TESTS
+ }
+ return TRUE;
+}
+
+//! ...and another one, jeez
+inline_ BOOL AABBCollider::TriBoxOverlap()
+{
+ // Stats
+ mNbVolumePrimTests++;
+
+ // Hook
+ const Point& center = mBox.mCenter;
+ const Point& extents = mBox.mExtents;
+
+ // use separating axis theorem to test overlap between triangle and box
+ // need to test for overlap in these directions:
+ // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle
+ // we do not even need to test these)
+ // 2) normal of the triangle
+ // 3) crossproduct(edge from tri, {x,y,z}-directin)
+ // this gives 3x3=9 more tests
+
+ // move everything so that the boxcenter is in (0,0,0)
+ Point v0, v1, v2;
+ v0.x = mLeafVerts[0].x - center.x;
+ v1.x = mLeafVerts[1].x - center.x;
+ v2.x = mLeafVerts[2].x - center.x;
+
+ // First, test overlap in the {x,y,z}-directions
+#ifdef OPC_USE_FCOMI
+ // find min, max of the triangle in x-direction, and test for overlap in X
+ if(FCMin3(v0.x, v1.x, v2.x)>extents.x) return FALSE;
+ if(FCMax3(v0.x, v1.x, v2.x)<-extents.x) return FALSE;
+
+ // same for Y
+ v0.y = mLeafVerts[0].y - center.y;
+ v1.y = mLeafVerts[1].y - center.y;
+ v2.y = mLeafVerts[2].y - center.y;
+
+ if(FCMin3(v0.y, v1.y, v2.y)>extents.y) return FALSE;
+ if(FCMax3(v0.y, v1.y, v2.y)<-extents.y) return FALSE;
+
+ // same for Z
+ v0.z = mLeafVerts[0].z - center.z;
+ v1.z = mLeafVerts[1].z - center.z;
+ v2.z = mLeafVerts[2].z - center.z;
+
+ if(FCMin3(v0.z, v1.z, v2.z)>extents.z) return FALSE;
+ if(FCMax3(v0.z, v1.z, v2.z)<-extents.z) return FALSE;
+#else
+ float min,max;
+ // Find min, max of the triangle in x-direction, and test for overlap in X
+ FINDMINMAX(v0.x, v1.x, v2.x, min, max);
+ if(min>extents.x || max<-extents.x) return FALSE;
+
+ // Same for Y
+ v0.y = mLeafVerts[0].y - center.y;
+ v1.y = mLeafVerts[1].y - center.y;
+ v2.y = mLeafVerts[2].y - center.y;
+
+ FINDMINMAX(v0.y, v1.y, v2.y, min, max);
+ if(min>extents.y || max<-extents.y) return FALSE;
+
+ // Same for Z
+ v0.z = mLeafVerts[0].z - center.z;
+ v1.z = mLeafVerts[1].z - center.z;
+ v2.z = mLeafVerts[2].z - center.z;
+
+ FINDMINMAX(v0.z, v1.z, v2.z, min, max);
+ if(min>extents.z || max<-extents.z) return FALSE;
+#endif
+ // 2) Test if the box intersects the plane of the triangle
+ // compute plane equation of triangle: normal*x+d=0
+ // ### could be precomputed since we use the same leaf triangle several times
+ const Point e0 = v1 - v0;
+ const Point e1 = v2 - v1;
+ const Point normal = e0 ^ e1;
+ const float d = -normal|v0;
+ if(!planeBoxOverlap(normal, d, extents)) return FALSE;
+
+ // 3) "Class III" tests - here we always do full tests since the box is a primitive (not a BV)
+ {
+ IMPLEMENT_CLASS3_TESTS
+ }
+ return TRUE;
+}
diff --git a/Opcode/OPC_TriTriOverlap.h b/Opcode/OPC_TriTriOverlap.h
new file mode 100644
index 0000000..45de8ba
--- /dev/null
+++ b/Opcode/OPC_TriTriOverlap.h
@@ -0,0 +1,279 @@
+
+//! if OPC_TRITRI_EPSILON_TEST is true then we do a check (if |dv|<EPSILON then dv=0.0;) else no check is done (which is less robust, but faster)
+#define LOCAL_EPSILON 0.000001f
+
+//! sort so that a<=b
+#define SORT(a,b) \
+ if(a>b) \
+ { \
+ const float c=a; \
+ a=b; \
+ b=c; \
+ }
+
+//! Edge to edge test based on Franlin Antonio's gem: "Faster Line Segment Intersection", in Graphics Gems III, pp. 199-202
+#define EDGE_EDGE_TEST(V0, U0, U1) \
+ Bx = U0[i0] - U1[i0]; \
+ By = U0[i1] - U1[i1]; \
+ Cx = V0[i0] - U0[i0]; \
+ Cy = V0[i1] - U0[i1]; \
+ f = Ay*Bx - Ax*By; \
+ d = By*Cx - Bx*Cy; \
+ if((f>0.0f && d>=0.0f && d<=f) || (f<0.0f && d<=0.0f && d>=f)) \
+ { \
+ const float e=Ax*Cy - Ay*Cx; \
+ if(f>0.0f) \
+ { \
+ if(e>=0.0f && e<=f) return TRUE; \
+ } \
+ else \
+ { \
+ if(e<=0.0f && e>=f) return TRUE; \
+ } \
+ }
+
+//! TO BE DOCUMENTED
+#define EDGE_AGAINST_TRI_EDGES(V0, V1, U0, U1, U2) \
+{ \
+ float Bx,By,Cx,Cy,d,f; \
+ const float Ax = V1[i0] - V0[i0]; \
+ const float Ay = V1[i1] - V0[i1]; \
+ /* test edge U0,U1 against V0,V1 */ \
+ EDGE_EDGE_TEST(V0, U0, U1); \
+ /* test edge U1,U2 against V0,V1 */ \
+ EDGE_EDGE_TEST(V0, U1, U2); \
+ /* test edge U2,U1 against V0,V1 */ \
+ EDGE_EDGE_TEST(V0, U2, U0); \
+}
+
+//! TO BE DOCUMENTED
+#define POINT_IN_TRI(V0, U0, U1, U2) \
+{ \
+ /* is T1 completly inside T2? */ \
+ /* check if V0 is inside tri(U0,U1,U2) */ \
+ float a = U1[i1] - U0[i1]; \
+ float b = -(U1[i0] - U0[i0]); \
+ float c = -a*U0[i0] - b*U0[i1]; \
+ float d0 = a*V0[i0] + b*V0[i1] + c; \
+ \
+ a = U2[i1] - U1[i1]; \
+ b = -(U2[i0] - U1[i0]); \
+ c = -a*U1[i0] - b*U1[i1]; \
+ const float d1 = a*V0[i0] + b*V0[i1] + c; \
+ \
+ a = U0[i1] - U2[i1]; \
+ b = -(U0[i0] - U2[i0]); \
+ c = -a*U2[i0] - b*U2[i1]; \
+ const float d2 = a*V0[i0] + b*V0[i1] + c; \
+ if(d0*d1>0.0f) \
+ { \
+ if(d0*d2>0.0f) return TRUE; \
+ } \
+}
+
+//! TO BE DOCUMENTED
+BOOL CoplanarTriTri(const Point& n, const Point& v0, const Point& v1, const Point& v2, const Point& u0, const Point& u1, const Point& u2)
+{
+ float A[3];
+ short i0,i1;
+ /* first project onto an axis-aligned plane, that maximizes the area */
+ /* of the triangles, compute indices: i0,i1. */
+ A[0] = fabsf(n[0]);
+ A[1] = fabsf(n[1]);
+ A[2] = fabsf(n[2]);
+ if(A[0]>A[1])
+ {
+ if(A[0]>A[2])
+ {
+ i0=1; /* A[0] is greatest */
+ i1=2;
+ }
+ else
+ {
+ i0=0; /* A[2] is greatest */
+ i1=1;
+ }
+ }
+ else /* A[0]<=A[1] */
+ {
+ if(A[2]>A[1])
+ {
+ i0=0; /* A[2] is greatest */
+ i1=1;
+ }
+ else
+ {
+ i0=0; /* A[1] is greatest */
+ i1=2;
+ }
+ }
+
+ /* test all edges of triangle 1 against the edges of triangle 2 */
+ EDGE_AGAINST_TRI_EDGES(v0, v1, u0, u1, u2);
+ EDGE_AGAINST_TRI_EDGES(v1, v2, u0, u1, u2);
+ EDGE_AGAINST_TRI_EDGES(v2, v0, u0, u1, u2);
+
+ /* finally, test if tri1 is totally contained in tri2 or vice versa */
+ POINT_IN_TRI(v0, u0, u1, u2);
+ POINT_IN_TRI(u0, v0, v1, v2);
+
+ return FALSE;
+}
+
+//! TO BE DOCUMENTED
+#define NEWCOMPUTE_INTERVALS(VV0, VV1, VV2, D0, D1, D2, D0D1, D0D2, A, B, C, X0, X1) \
+{ \
+ if(D0D1>0.0f) \
+ { \
+ /* here we know that D0D2<=0.0 */ \
+ /* that is D0, D1 are on the same side, D2 on the other or on the plane */ \
+ A=VV2; B=(VV0 - VV2)*D2; C=(VV1 - VV2)*D2; X0=D2 - D0; X1=D2 - D1; \
+ } \
+ else if(D0D2>0.0f) \
+ { \
+ /* here we know that d0d1<=0.0 */ \
+ A=VV1; B=(VV0 - VV1)*D1; C=(VV2 - VV1)*D1; X0=D1 - D0; X1=D1 - D2; \
+ } \
+ else if(D1*D2>0.0f || D0!=0.0f) \
+ { \
+ /* here we know that d0d1<=0.0 or that D0!=0.0 */ \
+ A=VV0; B=(VV1 - VV0)*D0; C=(VV2 - VV0)*D0; X0=D0 - D1; X1=D0 - D2; \
+ } \
+ else if(D1!=0.0f) \
+ { \
+ A=VV1; B=(VV0 - VV1)*D1; C=(VV2 - VV1)*D1; X0=D1 - D0; X1=D1 - D2; \
+ } \
+ else if(D2!=0.0f) \
+ { \
+ A=VV2; B=(VV0 - VV2)*D2; C=(VV1 - VV2)*D2; X0=D2 - D0; X1=D2 - D1; \
+ } \
+ else \
+ { \
+ /* triangles are coplanar */ \
+ return CoplanarTriTri(N1, V0, V1, V2, U0, U1, U2); \
+ } \
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Triangle/triangle intersection test routine,
+ * by Tomas Moller, 1997.
+ * See article "A Fast Triangle-Triangle Intersection Test",
+ * Journal of Graphics Tools, 2(2), 1997
+ *
+ * Updated June 1999: removed the divisions -- a little faster now!
+ * Updated October 1999: added {} to CROSS and SUB macros
+ *
+ * int NoDivTriTriIsect(float V0[3],float V1[3],float V2[3],
+ * float U0[3],float U1[3],float U2[3])
+ *
+ * \param V0 [in] triangle 0, vertex 0
+ * \param V1 [in] triangle 0, vertex 1
+ * \param V2 [in] triangle 0, vertex 2
+ * \param U0 [in] triangle 1, vertex 0
+ * \param U1 [in] triangle 1, vertex 1
+ * \param U2 [in] triangle 1, vertex 2
+ * \return true if triangles overlap
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL AABBTreeCollider::TriTriOverlap(const Point& V0, const Point& V1, const Point& V2, const Point& U0, const Point& U1, const Point& U2)
+{
+ // Stats
+ mNbPrimPrimTests++;
+
+ // Compute plane equation of triangle(V0,V1,V2)
+ Point E1 = V1 - V0;
+ Point E2 = V2 - V0;
+ const Point N1 = E1 ^ E2;
+ const float d1 =-N1 | V0;
+ // Plane equation 1: N1.X+d1=0
+
+ // Put U0,U1,U2 into plane equation 1 to compute signed distances to the plane
+ float du0 = (N1|U0) + d1;
+ float du1 = (N1|U1) + d1;
+ float du2 = (N1|U2) + d1;
+
+ // Coplanarity robustness check
+#ifdef OPC_TRITRI_EPSILON_TEST
+ if(fabsf(du0)<LOCAL_EPSILON) du0 = 0.0f;
+ if(fabsf(du1)<LOCAL_EPSILON) du1 = 0.0f;
+ if(fabsf(du2)<LOCAL_EPSILON) du2 = 0.0f;
+#endif
+ const float du0du1 = du0 * du1;
+ const float du0du2 = du0 * du2;
+
+ if(du0du1>0.0f && du0du2>0.0f) // same sign on all of them + not equal 0 ?
+ return FALSE; // no intersection occurs
+
+ // Compute plane of triangle (U0,U1,U2)
+ E1 = U1 - U0;
+ E2 = U2 - U0;
+ const Point N2 = E1 ^ E2;
+ const float d2=-N2 | U0;
+ // plane equation 2: N2.X+d2=0
+
+ // put V0,V1,V2 into plane equation 2
+ float dv0 = (N2|V0) + d2;
+ float dv1 = (N2|V1) + d2;
+ float dv2 = (N2|V2) + d2;
+
+#ifdef OPC_TRITRI_EPSILON_TEST
+ if(fabsf(dv0)<LOCAL_EPSILON) dv0 = 0.0f;
+ if(fabsf(dv1)<LOCAL_EPSILON) dv1 = 0.0f;
+ if(fabsf(dv2)<LOCAL_EPSILON) dv2 = 0.0f;
+#endif
+
+ const float dv0dv1 = dv0 * dv1;
+ const float dv0dv2 = dv0 * dv2;
+
+ if(dv0dv1>0.0f && dv0dv2>0.0f) // same sign on all of them + not equal 0 ?
+ return FALSE; // no intersection occurs
+
+ // Compute direction of intersection line
+ const Point D = N1^N2;
+
+ // Compute and index to the largest component of D
+ float max=fabsf(D[0]);
+ short index=0;
+ float bb=fabsf(D[1]);
+ float cc=fabsf(D[2]);
+ if(bb>max) max=bb,index=1;
+ if(cc>max) max=cc,index=2;
+
+ // This is the simplified projection onto L
+ const float vp0 = V0[index];
+ const float vp1 = V1[index];
+ const float vp2 = V2[index];
+
+ const float up0 = U0[index];
+ const float up1 = U1[index];
+ const float up2 = U2[index];
+
+ // Compute interval for triangle 1
+ float a,b,c,x0,x1;
+ NEWCOMPUTE_INTERVALS(vp0,vp1,vp2,dv0,dv1,dv2,dv0dv1,dv0dv2,a,b,c,x0,x1);
+
+ // Compute interval for triangle 2
+ float d,e,f,y0,y1;
+ NEWCOMPUTE_INTERVALS(up0,up1,up2,du0,du1,du2,du0du1,du0du2,d,e,f,y0,y1);
+
+ const float xx=x0*x1;
+ const float yy=y0*y1;
+ const float xxyy=xx*yy;
+
+ float isect1[2], isect2[2];
+
+ float tmp=a*xxyy;
+ isect1[0]=tmp+b*x1*yy;
+ isect1[1]=tmp+c*x0*yy;
+
+ tmp=d*xxyy;
+ isect2[0]=tmp+e*xx*y1;
+ isect2[1]=tmp+f*xx*y0;
+
+ SORT(isect1[0],isect1[1]);
+ SORT(isect2[0],isect2[1]);
+
+ if(isect1[1]<isect2[0] || isect2[1]<isect1[0]) return FALSE;
+ return TRUE;
+}
diff --git a/Opcode/OPC_VolumeCollider.cpp b/Opcode/OPC_VolumeCollider.cpp
new file mode 100644
index 0000000..70fc292
--- /dev/null
+++ b/Opcode/OPC_VolumeCollider.cpp
@@ -0,0 +1,103 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains base volume collider class.
+ * \file OPC_VolumeCollider.cpp
+ * \author Pierre Terdiman
+ * \date June, 2, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains the abstract class for volume colliders.
+ *
+ * \class VolumeCollider
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date June, 2, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+VolumeCollider::VolumeCollider() :
+ mTouchedPrimitives (null),
+ mNbVolumeBVTests (0),
+ mNbVolumePrimTests (0)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+VolumeCollider::~VolumeCollider()
+{
+ mTouchedPrimitives = null;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Validates current settings. You should call this method after all the settings / callbacks have been defined for a collider.
+ * \return null if everything is ok, else a string describing the problem
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const char* VolumeCollider::ValidateSettings()
+{
+ return null;
+}
+
+// Pretty dumb way to dump - to do better - one day...
+
+#define IMPLEMENT_NOLEAFDUMP(type) \
+void VolumeCollider::_Dump(const type* node) \
+{ \
+ if(node->HasPosLeaf()) mTouchedPrimitives->Add(node->GetPosPrimitive()); \
+ else _Dump(node->GetPos()); \
+ \
+ if(ContactFound()) return; \
+ \
+ if(node->HasNegLeaf()) mTouchedPrimitives->Add(node->GetNegPrimitive()); \
+ else _Dump(node->GetNeg()); \
+}
+
+#define IMPLEMENT_LEAFDUMP(type) \
+void VolumeCollider::_Dump(const type* node) \
+{ \
+ if(node->IsLeaf()) \
+ { \
+ mTouchedPrimitives->Add(node->GetPrimitive()); \
+ } \
+ else \
+ { \
+ _Dump(node->GetPos()); \
+ \
+ if(ContactFound()) return; \
+ \
+ _Dump(node->GetNeg()); \
+ } \
+}
+
+IMPLEMENT_NOLEAFDUMP(AABBNoLeafNode)
+IMPLEMENT_NOLEAFDUMP(AABBQuantizedNoLeafNode)
+
+IMPLEMENT_LEAFDUMP(AABBCollisionNode)
+IMPLEMENT_LEAFDUMP(AABBQuantizedNode)
diff --git a/Opcode/OPC_VolumeCollider.h b/Opcode/OPC_VolumeCollider.h
new file mode 100644
index 0000000..05ea693
--- /dev/null
+++ b/Opcode/OPC_VolumeCollider.h
@@ -0,0 +1,138 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains base volume collider class.
+ * \file OPC_VolumeCollider.h
+ * \author Pierre Terdiman
+ * \date June, 2, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_VOLUMECOLLIDER_H__
+#define __OPC_VOLUMECOLLIDER_H__
+
+ struct OPCODE_API VolumeCache
+ {
+ VolumeCache() : Model(null) {}
+ ~VolumeCache() {}
+
+ Container TouchedPrimitives; //!< Indices of touched primitives
+ const BaseModel* Model; //!< Owner
+ };
+
+ class OPCODE_API VolumeCollider : public Collider
+ {
+ public:
+ // Constructor / Destructor
+ VolumeCollider();
+ virtual ~VolumeCollider() = 0;
+
+ // Collision report
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the number of touched primitives after a collision query.
+ * \see GetContactStatus()
+ * \see GetTouchedPrimitives()
+ * \return the number of touched primitives
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbTouchedPrimitives() const { return mTouchedPrimitives ? mTouchedPrimitives->GetNbEntries() : 0; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the list of touched primitives after a collision query.
+ * \see GetContactStatus()
+ * \see GetNbTouchedPrimitives()
+ * \return the list of touched primitives (primitive indices)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ const udword* GetTouchedPrimitives() const { return mTouchedPrimitives ? mTouchedPrimitives->GetEntries() : null; }
+
+ // Stats
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Stats: gets the number of Volume-BV overlap tests after a collision query.
+ * \see GetNbVolumePrimTests()
+ * \return the number of Volume-BV tests performed during last query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbVolumeBVTests() const { return mNbVolumeBVTests; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Stats: gets the number of Volume-Triangle overlap tests after a collision query.
+ * \see GetNbVolumeBVTests()
+ * \return the number of Volume-Triangle tests performed during last query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbVolumePrimTests() const { return mNbVolumePrimTests; }
+
+ // Settings
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Validates current settings. You should call this method after all the settings / callbacks have been defined for a collider.
+ * \return null if everything is ok, else a string describing the problem
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(Collider) const char* ValidateSettings();
+
+ protected:
+ // Touched primitives
+ Container* mTouchedPrimitives; //!< List of touched primitives
+
+ // Dequantization coeffs
+ Point mCenterCoeff;
+ Point mExtentsCoeff;
+ // Stats
+ udword mNbVolumeBVTests; //!< Number of Volume-BV tests
+ udword mNbVolumePrimTests; //!< Number of Volume-Primitive tests
+ // Internal methods
+ void _Dump(const AABBCollisionNode* node);
+ void _Dump(const AABBNoLeafNode* node);
+ void _Dump(const AABBQuantizedNode* node);
+ void _Dump(const AABBQuantizedNoLeafNode* node);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Initializes a query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(Collider) inline_ void InitQuery()
+ {
+ // Reset stats & contact status
+ mNbVolumeBVTests = 0;
+ mNbVolumePrimTests = 0;
+ Collider::InitQuery();
+ }
+
+ inline_ BOOL IsCacheValid(VolumeCache& cache)
+ {
+ // We're going to do a volume-vs-model query.
+ if(cache.Model!=mCurrentModel)
+ {
+ // Cached list was for another model so we can't keep it
+ // Keep track of new owner and reset cache
+ cache.Model = mCurrentModel;
+ return FALSE;
+ }
+ else
+ {
+ // Same models, no problem
+ return TRUE;
+ }
+ }
+ };
+
+#endif // __OPC_VOLUMECOLLIDER_H__
diff --git a/Opcode/Opcode.cpp b/Opcode/Opcode.cpp
new file mode 100644
index 0000000..72d6b47
--- /dev/null
+++ b/Opcode/Opcode.cpp
@@ -0,0 +1,65 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Main file for Opcode.dll.
+ * \file Opcode.cpp
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+ Finding a good name is difficult!
+ Here's the draft for this lib.... Spooky, uh?
+
+ VOID? Very Optimized Interference Detection
+ ZOID? Zappy's Optimized Interference Detection
+ CID? Custom/Clever Interference Detection
+ AID / ACID! Accurate Interference Detection
+ QUID? Quick Interference Detection
+ RIDE? Realtime Interference DEtection
+ WIDE? Wicked Interference DEtection (....)
+ GUID!
+ KID ! k-dop interference detection :)
+ OPCODE! OPtimized COllision DEtection
+*/
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+bool Opcode::InitOpcode()
+{
+ Log("// Initializing OPCODE\n\n");
+// LogAPIInfo();
+ return true;
+}
+
+void ReleasePruningSorters();
+bool Opcode::CloseOpcode()
+{
+ Log("// Closing OPCODE\n\n");
+
+ ReleasePruningSorters();
+
+ return true;
+}
+
+#ifdef ICE_MAIN
+
+void ModuleAttach(HINSTANCE hinstance)
+{
+}
+
+void ModuleDetach()
+{
+}
+
+#endif \ No newline at end of file
diff --git a/Opcode/Opcode.dsp b/Opcode/Opcode.dsp
new file mode 100644
index 0000000..74d3b65
--- /dev/null
+++ b/Opcode/Opcode.dsp
@@ -0,0 +1,517 @@
+# Microsoft Developer Studio Project File - Name="Opcode" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=Opcode - 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 "Opcode.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 "Opcode.mak" CFG="Opcode - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "Opcode - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "Opcode - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "Opcode - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODE_EXPORTS" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODE_EXPORTS" /Yu"stdafx.h" /FD /QIfist /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40c /d "NDEBUG"
+# ADD RSC /l 0x40c /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 /nologo /dll /machine:I386
+# SUBTRACT LINK32 /debug
+
+!ELSEIF "$(CFG)" == "Opcode - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODE_EXPORTS" /Yu"stdafx.h" /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODE_EXPORTS" /FR /Yu"stdafx.h" /FD /GZ /QIfist /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40c /d "_DEBUG"
+# ADD RSC /l 0x40c /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 /nologo /dll /incremental:no /debug /machine:I386 /out:"Debug\Opcode_D.dll" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "Opcode - Win32 Release"
+# Name "Opcode - Win32 Debug"
+# Begin Group "API"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\OPC_BaseModel.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_BaseModel.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_HybridModel.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_HybridModel.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_IceHook.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_Model.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_Model.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_Settings.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Opcode.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Opcode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.cpp
+# ADD CPP /Yc"stdafx.h"
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.h
+# End Source File
+# End Group
+# Begin Group "Trees"
+
+# PROP Default_Filter ""
+# Begin Group "Collision queries"
+
+# PROP Default_Filter ""
+# Begin Group "Base colliders"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\OPC_Collider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_Collider.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_VolumeCollider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_VolumeCollider.h
+# End Source File
+# End Group
+# Begin Group "Standard colliders"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\OPC_AABBCollider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_AABBCollider.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_LSSCollider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_LSSCollider.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_OBBCollider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_OBBCollider.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_PlanesCollider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_PlanesCollider.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_RayCollider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_RayCollider.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_SphereCollider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_SphereCollider.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_TreeCollider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_TreeCollider.h
+# End Source File
+# End Group
+# End Group
+# Begin Source File
+
+SOURCE=.\OPC_AABBTree.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_AABBTree.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_Common.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_Common.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_MeshInterface.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_MeshInterface.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_OptimizedTree.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_OptimizedTree.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_TreeBuilders.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_TreeBuilders.h
+# End Source File
+# End Group
+# Begin Group "Overlap tests"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\OPC_BoxBoxOverlap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_LSSAABBOverlap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_LSSTriOverlap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_PlanesAABBOverlap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_PlanesTriOverlap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_RayAABBOverlap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_RayTriOverlap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_SphereAABBOverlap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_SphereTriOverlap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_TriBoxOverlap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_TriTriOverlap.h
+# End Source File
+# End Group
+# Begin Group "SweepAndPrune"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\OPC_BoxPruning.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_BoxPruning.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_SweepAndPrune.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_SweepAndPrune.h
+# End Source File
+# End Group
+# Begin Group "Usages"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\OPC_Picking.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_Picking.h
+# End Source File
+# End Group
+# Begin Group "Ice"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Ice\IceAABB.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceAABB.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceAxes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceBoundingSphere.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceContainer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceContainer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceFPU.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceHPoint.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceHPoint.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceIndexedTriangle.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceIndexedTriangle.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceLSS.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceMatrix3x3.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceMatrix3x3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceMatrix4x4.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceMatrix4x4.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceMemoryMacros.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceOBB.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceOBB.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IcePairs.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IcePlane.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IcePlane.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IcePoint.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IcePoint.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IcePreprocessor.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceRandom.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceRandom.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceRay.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceRay.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceRevisitedRadix.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceRevisitedRadix.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceSegment.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceSegment.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceTriangle.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceTriangle.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceTrilist.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceTypes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceUtils.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceUtils.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\ReadMe.txt
+# End Source File
+# End Target
+# End Project
diff --git a/Opcode/Opcode.dsw b/Opcode/Opcode.dsw
new file mode 100644
index 0000000..fffc3df
--- /dev/null
+++ b/Opcode/Opcode.dsw
@@ -0,0 +1,41 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Opcode"=.\Opcode.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "OpcodeLib"=.\OpcodeLib\OpcodeLib.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Opcode/Opcode.h b/Opcode/Opcode.h
new file mode 100644
index 0000000..ca29a91
--- /dev/null
+++ b/Opcode/Opcode.h
@@ -0,0 +1,89 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Main file for Opcode.dll.
+ * \file Opcode.h
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPCODE_H__
+#define __OPCODE_H__
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Compilation messages
+#if defined(OPCODE_EXPORTS)
+ #pragma message("Compiling OPCODE")
+#elif !defined(OPCODE_EXPORTS)
+ #pragma message("Using OPCODE")
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Automatic linking
+ #ifndef BAN_OPCODE_AUTOLINK
+ #ifdef _DEBUG
+ #pragma comment(lib, "Opcode_D.lib")
+ #else
+ #pragma comment(lib, "Opcode.lib")
+ #endif
+ #endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Preprocessor
+#ifndef ICE_NO_DLL
+ #ifdef OPCODE_EXPORTS
+ #define OPCODE_API __declspec(dllexport)
+ #else
+ #define OPCODE_API __declspec(dllimport)
+ #endif
+#else
+ #define OPCODE_API
+#endif
+
+ #include "OPC_IceHook.h"
+
+ namespace Opcode
+ {
+ // Bulk-of-the-work
+ #include "OPC_Settings.h"
+ #include "OPC_Common.h"
+ #include "OPC_MeshInterface.h"
+ // Builders
+ #include "OPC_TreeBuilders.h"
+ // Trees
+ #include "OPC_AABBTree.h"
+ #include "OPC_OptimizedTree.h"
+ // Models
+ #include "OPC_BaseModel.h"
+ #include "OPC_Model.h"
+ #include "OPC_HybridModel.h"
+ // Colliders
+ #include "OPC_Collider.h"
+ #include "OPC_VolumeCollider.h"
+ #include "OPC_TreeCollider.h"
+ #include "OPC_RayCollider.h"
+ #include "OPC_SphereCollider.h"
+ #include "OPC_OBBCollider.h"
+ #include "OPC_AABBCollider.h"
+ #include "OPC_LSSCollider.h"
+ #include "OPC_PlanesCollider.h"
+ // Usages
+ #include "OPC_Picking.h"
+ // Sweep-and-prune
+ #include "OPC_BoxPruning.h"
+ #include "OPC_SweepAndPrune.h"
+
+ FUNCTION OPCODE_API bool InitOpcode();
+ FUNCTION OPCODE_API bool CloseOpcode();
+ }
+
+#endif // __OPCODE_H__
diff --git a/Opcode/Opcode.ncb b/Opcode/Opcode.ncb
new file mode 100644
index 0000000..1adcbbb
--- /dev/null
+++ b/Opcode/Opcode.ncb
Binary files differ
diff --git a/Opcode/Opcode.opt b/Opcode/Opcode.opt
new file mode 100644
index 0000000..92017f5
--- /dev/null
+++ b/Opcode/Opcode.opt
Binary files differ
diff --git a/Opcode/Opcode.plg b/Opcode/Opcode.plg
new file mode 100644
index 0000000..60eb06c
--- /dev/null
+++ b/Opcode/Opcode.plg
@@ -0,0 +1,299 @@
+<html>
+<body>
+<pre>
+<h1>Build Log</h1>
+<h3>
+--------------------Configuration: Opcode - Win32 Release--------------------
+</h3>
+<h3>Command Lines</h3>
+Creating temporary file "C:\DOCUME~1\milo\LOCALS~1\Temp\RSP17B.tmp" with contents
+[
+/nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODE_EXPORTS" /Fp"Release/Opcode.pch" /Yu"stdafx.h" /Fo"Release/" /Fd"Release/" /FD /QIfist /c
+"C:\GameDev\Opcode\OPC_BaseModel.cpp"
+"C:\GameDev\Opcode\OPC_HybridModel.cpp"
+"C:\GameDev\Opcode\OPC_Model.cpp"
+"C:\GameDev\Opcode\Opcode.cpp"
+"C:\GameDev\Opcode\OPC_Collider.cpp"
+"C:\GameDev\Opcode\OPC_VolumeCollider.cpp"
+"C:\GameDev\Opcode\OPC_AABBCollider.cpp"
+"C:\GameDev\Opcode\OPC_LSSCollider.cpp"
+"C:\GameDev\Opcode\OPC_OBBCollider.cpp"
+"C:\GameDev\Opcode\OPC_PlanesCollider.cpp"
+"C:\GameDev\Opcode\OPC_RayCollider.cpp"
+"C:\GameDev\Opcode\OPC_SphereCollider.cpp"
+"C:\GameDev\Opcode\OPC_TreeCollider.cpp"
+"C:\GameDev\Opcode\OPC_AABBTree.cpp"
+"C:\GameDev\Opcode\OPC_Common.cpp"
+"C:\GameDev\Opcode\OPC_MeshInterface.cpp"
+"C:\GameDev\Opcode\OPC_OptimizedTree.cpp"
+"C:\GameDev\Opcode\OPC_TreeBuilders.cpp"
+"C:\GameDev\Opcode\OPC_BoxPruning.cpp"
+"C:\GameDev\Opcode\OPC_SweepAndPrune.cpp"
+"C:\GameDev\Opcode\OPC_Picking.cpp"
+"C:\GameDev\Opcode\Ice\IceAABB.cpp"
+"C:\GameDev\Opcode\Ice\IceContainer.cpp"
+"C:\GameDev\Opcode\Ice\IceHPoint.cpp"
+"C:\GameDev\Opcode\Ice\IceIndexedTriangle.cpp"
+"C:\GameDev\Opcode\Ice\IceMatrix3x3.cpp"
+"C:\GameDev\Opcode\Ice\IceMatrix4x4.cpp"
+"C:\GameDev\Opcode\Ice\IceOBB.cpp"
+"C:\GameDev\Opcode\Ice\IcePlane.cpp"
+"C:\GameDev\Opcode\Ice\IcePoint.cpp"
+"C:\GameDev\Opcode\Ice\IceRandom.cpp"
+"C:\GameDev\Opcode\Ice\IceRay.cpp"
+"C:\GameDev\Opcode\Ice\IceRevisitedRadix.cpp"
+"C:\GameDev\Opcode\Ice\IceSegment.cpp"
+"C:\GameDev\Opcode\Ice\IceTriangle.cpp"
+"C:\GameDev\Opcode\Ice\IceUtils.cpp"
+]
+Creating command line "cl.exe @C:\DOCUME~1\milo\LOCALS~1\Temp\RSP17B.tmp"
+Creating temporary file "C:\DOCUME~1\milo\LOCALS~1\Temp\RSP17C.tmp" with contents
+[
+/nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODE_EXPORTS" /Fp"Release/Opcode.pch" /Yc"stdafx.h" /Fo"Release/" /Fd"Release/" /FD /QIfist /c
+"C:\GameDev\Opcode\StdAfx.cpp"
+]
+Creating command line "cl.exe @C:\DOCUME~1\milo\LOCALS~1\Temp\RSP17C.tmp"
+Creating temporary file "C:\DOCUME~1\milo\LOCALS~1\Temp\RSP17D.tmp" with contents
+[
+/nologo /dll /incremental:no /pdb:"Release/Opcode.pdb" /machine:I386 /out:"Release/Opcode.dll" /implib:"Release/Opcode.lib"
+.\Release\OPC_BaseModel.obj
+.\Release\OPC_HybridModel.obj
+.\Release\OPC_Model.obj
+.\Release\Opcode.obj
+.\Release\StdAfx.obj
+.\Release\OPC_Collider.obj
+.\Release\OPC_VolumeCollider.obj
+.\Release\OPC_AABBCollider.obj
+.\Release\OPC_LSSCollider.obj
+.\Release\OPC_OBBCollider.obj
+.\Release\OPC_PlanesCollider.obj
+.\Release\OPC_RayCollider.obj
+.\Release\OPC_SphereCollider.obj
+.\Release\OPC_TreeCollider.obj
+.\Release\OPC_AABBTree.obj
+.\Release\OPC_Common.obj
+.\Release\OPC_MeshInterface.obj
+.\Release\OPC_OptimizedTree.obj
+.\Release\OPC_TreeBuilders.obj
+.\Release\OPC_BoxPruning.obj
+.\Release\OPC_SweepAndPrune.obj
+.\Release\OPC_Picking.obj
+.\Release\IceAABB.obj
+.\Release\IceContainer.obj
+.\Release\IceHPoint.obj
+.\Release\IceIndexedTriangle.obj
+.\Release\IceMatrix3x3.obj
+.\Release\IceMatrix4x4.obj
+.\Release\IceOBB.obj
+.\Release\IcePlane.obj
+.\Release\IcePoint.obj
+.\Release\IceRandom.obj
+.\Release\IceRay.obj
+.\Release\IceRevisitedRadix.obj
+.\Release\IceSegment.obj
+.\Release\IceTriangle.obj
+.\Release\IceUtils.obj
+]
+Creating command line "link.exe @C:\DOCUME~1\milo\LOCALS~1\Temp\RSP17D.tmp"
+<h3>Output Window</h3>
+Compiling...
+StdAfx.cpp
+Compiling OPCODE
+Compiling on Windows...
+Compiling with VC++...
+Compiling...
+OPC_BaseModel.cpp
+OPC_HybridModel.cpp
+OPC_Model.cpp
+Opcode.cpp
+OPC_Collider.cpp
+OPC_VolumeCollider.cpp
+OPC_AABBCollider.cpp
+OPC_LSSCollider.cpp
+OPC_OBBCollider.cpp
+OPC_PlanesCollider.cpp
+OPC_RayCollider.cpp
+OPC_SphereCollider.cpp
+OPC_TreeCollider.cpp
+OPC_AABBTree.cpp
+OPC_Common.cpp
+OPC_MeshInterface.cpp
+OPC_OptimizedTree.cpp
+OPC_TreeBuilders.cpp
+OPC_BoxPruning.cpp
+OPC_SweepAndPrune.cpp
+Generating Code...
+Compiling...
+OPC_Picking.cpp
+IceAABB.cpp
+IceContainer.cpp
+IceHPoint.cpp
+IceIndexedTriangle.cpp
+IceMatrix3x3.cpp
+IceMatrix4x4.cpp
+IceOBB.cpp
+IcePlane.cpp
+IcePoint.cpp
+IceRandom.cpp
+IceRay.cpp
+IceRevisitedRadix.cpp
+IceSegment.cpp
+IceTriangle.cpp
+IceUtils.cpp
+Generating Code...
+Linking...
+ Creating library Release/Opcode.lib and object Release/Opcode.exp
+
+
+
+<h3>Results</h3>
+Opcode.dll - 0 error(s), 0 warning(s)
+<h3>
+--------------------Configuration: Opcode - Win32 Debug--------------------
+</h3>
+<h3>Command Lines</h3>
+Creating temporary file "C:\DOCUME~1\milo\LOCALS~1\Temp\RSP181.tmp" with contents
+[
+/nologo /MTd /W3 /Gm /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODE_EXPORTS" /FR"Debug/" /Fp"Debug/Opcode.pch" /Yu"stdafx.h" /Fo"Debug/" /Fd"Debug/" /FD /GZ /QIfist /c
+"C:\GameDev\Opcode\OPC_BaseModel.cpp"
+"C:\GameDev\Opcode\OPC_HybridModel.cpp"
+"C:\GameDev\Opcode\OPC_Model.cpp"
+"C:\GameDev\Opcode\Opcode.cpp"
+"C:\GameDev\Opcode\OPC_Collider.cpp"
+"C:\GameDev\Opcode\OPC_VolumeCollider.cpp"
+"C:\GameDev\Opcode\OPC_AABBCollider.cpp"
+"C:\GameDev\Opcode\OPC_LSSCollider.cpp"
+"C:\GameDev\Opcode\OPC_OBBCollider.cpp"
+"C:\GameDev\Opcode\OPC_PlanesCollider.cpp"
+"C:\GameDev\Opcode\OPC_RayCollider.cpp"
+"C:\GameDev\Opcode\OPC_SphereCollider.cpp"
+"C:\GameDev\Opcode\OPC_TreeCollider.cpp"
+"C:\GameDev\Opcode\OPC_AABBTree.cpp"
+"C:\GameDev\Opcode\OPC_Common.cpp"
+"C:\GameDev\Opcode\OPC_MeshInterface.cpp"
+"C:\GameDev\Opcode\OPC_OptimizedTree.cpp"
+"C:\GameDev\Opcode\OPC_TreeBuilders.cpp"
+"C:\GameDev\Opcode\OPC_BoxPruning.cpp"
+"C:\GameDev\Opcode\OPC_SweepAndPrune.cpp"
+"C:\GameDev\Opcode\OPC_Picking.cpp"
+"C:\GameDev\Opcode\Ice\IceAABB.cpp"
+"C:\GameDev\Opcode\Ice\IceContainer.cpp"
+"C:\GameDev\Opcode\Ice\IceHPoint.cpp"
+"C:\GameDev\Opcode\Ice\IceIndexedTriangle.cpp"
+"C:\GameDev\Opcode\Ice\IceMatrix3x3.cpp"
+"C:\GameDev\Opcode\Ice\IceMatrix4x4.cpp"
+"C:\GameDev\Opcode\Ice\IceOBB.cpp"
+"C:\GameDev\Opcode\Ice\IcePlane.cpp"
+"C:\GameDev\Opcode\Ice\IcePoint.cpp"
+"C:\GameDev\Opcode\Ice\IceRandom.cpp"
+"C:\GameDev\Opcode\Ice\IceRay.cpp"
+"C:\GameDev\Opcode\Ice\IceRevisitedRadix.cpp"
+"C:\GameDev\Opcode\Ice\IceSegment.cpp"
+"C:\GameDev\Opcode\Ice\IceTriangle.cpp"
+"C:\GameDev\Opcode\Ice\IceUtils.cpp"
+]
+Creating command line "cl.exe @C:\DOCUME~1\milo\LOCALS~1\Temp\RSP181.tmp"
+Creating temporary file "C:\DOCUME~1\milo\LOCALS~1\Temp\RSP182.tmp" with contents
+[
+/nologo /MTd /W3 /Gm /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OPCODE_EXPORTS" /FR"Debug/" /Fp"Debug/Opcode.pch" /Yc"stdafx.h" /Fo"Debug/" /Fd"Debug/" /FD /GZ /QIfist /c
+"C:\GameDev\Opcode\StdAfx.cpp"
+]
+Creating command line "cl.exe @C:\DOCUME~1\milo\LOCALS~1\Temp\RSP182.tmp"
+Creating temporary file "C:\DOCUME~1\milo\LOCALS~1\Temp\RSP183.tmp" with contents
+[
+/nologo /dll /incremental:no /pdb:"Debug/Opcode_D.pdb" /debug /machine:I386 /out:"Debug\Opcode_D.dll" /implib:"Debug/Opcode_D.lib" /pdbtype:sept
+.\Debug\OPC_BaseModel.obj
+.\Debug\OPC_HybridModel.obj
+.\Debug\OPC_Model.obj
+.\Debug\Opcode.obj
+.\Debug\StdAfx.obj
+.\Debug\OPC_Collider.obj
+.\Debug\OPC_VolumeCollider.obj
+.\Debug\OPC_AABBCollider.obj
+.\Debug\OPC_LSSCollider.obj
+.\Debug\OPC_OBBCollider.obj
+.\Debug\OPC_PlanesCollider.obj
+.\Debug\OPC_RayCollider.obj
+.\Debug\OPC_SphereCollider.obj
+.\Debug\OPC_TreeCollider.obj
+.\Debug\OPC_AABBTree.obj
+.\Debug\OPC_Common.obj
+.\Debug\OPC_MeshInterface.obj
+.\Debug\OPC_OptimizedTree.obj
+.\Debug\OPC_TreeBuilders.obj
+.\Debug\OPC_BoxPruning.obj
+.\Debug\OPC_SweepAndPrune.obj
+.\Debug\OPC_Picking.obj
+.\Debug\IceAABB.obj
+.\Debug\IceContainer.obj
+.\Debug\IceHPoint.obj
+.\Debug\IceIndexedTriangle.obj
+.\Debug\IceMatrix3x3.obj
+.\Debug\IceMatrix4x4.obj
+.\Debug\IceOBB.obj
+.\Debug\IcePlane.obj
+.\Debug\IcePoint.obj
+.\Debug\IceRandom.obj
+.\Debug\IceRay.obj
+.\Debug\IceRevisitedRadix.obj
+.\Debug\IceSegment.obj
+.\Debug\IceTriangle.obj
+.\Debug\IceUtils.obj
+]
+Creating command line "link.exe @C:\DOCUME~1\milo\LOCALS~1\Temp\RSP183.tmp"
+<h3>Output Window</h3>
+Compiling...
+StdAfx.cpp
+Compiling OPCODE
+Compiling on Windows...
+Compiling with VC++...
+Compiling...
+OPC_BaseModel.cpp
+OPC_HybridModel.cpp
+OPC_Model.cpp
+Opcode.cpp
+OPC_Collider.cpp
+OPC_VolumeCollider.cpp
+OPC_AABBCollider.cpp
+OPC_LSSCollider.cpp
+OPC_OBBCollider.cpp
+OPC_PlanesCollider.cpp
+OPC_RayCollider.cpp
+OPC_SphereCollider.cpp
+OPC_TreeCollider.cpp
+OPC_AABBTree.cpp
+OPC_Common.cpp
+OPC_MeshInterface.cpp
+OPC_OptimizedTree.cpp
+OPC_TreeBuilders.cpp
+OPC_BoxPruning.cpp
+OPC_SweepAndPrune.cpp
+Generating Code...
+Compiling...
+OPC_Picking.cpp
+IceAABB.cpp
+IceContainer.cpp
+IceHPoint.cpp
+IceIndexedTriangle.cpp
+IceMatrix3x3.cpp
+IceMatrix4x4.cpp
+IceOBB.cpp
+IcePlane.cpp
+IcePoint.cpp
+IceRandom.cpp
+IceRay.cpp
+IceRevisitedRadix.cpp
+IceSegment.cpp
+IceTriangle.cpp
+IceUtils.cpp
+Generating Code...
+Linking...
+ Creating library Debug/Opcode_D.lib and object Debug/Opcode_D.exp
+
+
+
+<h3>Results</h3>
+Opcode_D.dll - 0 error(s), 0 warning(s)
+</pre>
+</body>
+</html>
diff --git a/Opcode/Opcode.vcxproj b/Opcode/Opcode.vcxproj
new file mode 100644
index 0000000..86aebed
--- /dev/null
+++ b/Opcode/Opcode.vcxproj
@@ -0,0 +1,367 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Template|Win32">
+ <Configuration>Template</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <SccProjectName />
+ <SccLocalPath />
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Template|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Template|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>.\Debug\</OutDir>
+ <IntDir>.\Debug\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>.\Release\</OutDir>
+ <IntDir>.\Release\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+ <FunctionLevelLinking>false</FunctionLevelLinking>
+ <Optimization>Disabled</Optimization>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <WarningLevel>Level3</WarningLevel>
+ <MinimalRebuild>true</MinimalRebuild>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;OPCODE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AssemblerListingLocation>.\Debug\</AssemblerListingLocation>
+ <BrowseInformation>true</BrowseInformation>
+ <PrecompiledHeaderOutputFile>.\Debug\Opcode.pch</PrecompiledHeaderOutputFile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
+ <ObjectFileName>.\Debug\</ObjectFileName>
+ <ProgramDataBaseFileName>.\Debug\</ProgramDataBaseFileName>
+ <AdditionalOptions> /QIfist </AdditionalOptions>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ </ClCompile>
+ <Midl>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <TypeLibraryName>.\Debug\Opcode.tlb</TypeLibraryName>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ </Midl>
+ <ResourceCompile>
+ <Culture>0x040c</Culture>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug\Opcode.bsc</OutputFile>
+ </Bscmake>
+ <Link>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <LinkDLL>true</LinkDLL>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OutputFile>Debug\Opcode_D.dll</OutputFile>
+ <ImportLibrary>.\Debug\Opcode_D.lib</ImportLibrary>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <StringPooling>true</StringPooling>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <Optimization>MaxSpeed</Optimization>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <WarningLevel>Level3</WarningLevel>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;OPCODE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AssemblerListingLocation>.\Release\</AssemblerListingLocation>
+ <PrecompiledHeaderOutputFile>.\Release\Opcode.pch</PrecompiledHeaderOutputFile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
+ <ObjectFileName>.\Release\</ObjectFileName>
+ <ProgramDataBaseFileName>.\Release\</ProgramDataBaseFileName>
+ <AdditionalOptions> /QIfist </AdditionalOptions>
+ </ClCompile>
+ <Midl>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <TypeLibraryName>.\Release\Opcode.tlb</TypeLibraryName>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ </Midl>
+ <ResourceCompile>
+ <Culture>0x040c</Culture>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release\Opcode.bsc</OutputFile>
+ </Bscmake>
+ <Link>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <LinkDLL>true</LinkDLL>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OutputFile>.\Release\Opcode.dll</OutputFile>
+ <ImportLibrary>.\Release\Opcode.lib</ImportLibrary>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="OPC_BaseModel.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_HybridModel.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_Model.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="Opcode.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="StdAfx.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">stdafx.h</PrecompiledHeaderFile>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">stdafx.h</PrecompiledHeaderFile>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_Collider.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_VolumeCollider.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_AABBCollider.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_LSSCollider.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_OBBCollider.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_PlanesCollider.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_RayCollider.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_SphereCollider.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_TreeCollider.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_AABBTree.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_Common.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_MeshInterface.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_OptimizedTree.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_TreeBuilders.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_BoxPruning.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_SweepAndPrune.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="OPC_Picking.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="Ice\IceAABB.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="Ice\IceContainer.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="Ice\IceHPoint.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="Ice\IceIndexedTriangle.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="Ice\IceMatrix3x3.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="Ice\IceMatrix4x4.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="Ice\IceOBB.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="Ice\IcePlane.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="Ice\IcePoint.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="Ice\IceRandom.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="Ice\IceRay.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="Ice\IceRevisitedRadix.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="Ice\IceSegment.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="Ice\IceTriangle.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ <ClCompile Include="Ice\IceUtils.cpp">
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> /QIfist /QIfist </AdditionalOptions>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="OPC_BaseModel.h" />
+ <ClInclude Include="OPC_HybridModel.h" />
+ <ClInclude Include="OPC_IceHook.h" />
+ <ClInclude Include="OPC_Model.h" />
+ <ClInclude Include="OPC_Settings.h" />
+ <ClInclude Include="Opcode.h" />
+ <ClInclude Include="StdAfx.h" />
+ <ClInclude Include="OPC_Collider.h" />
+ <ClInclude Include="OPC_VolumeCollider.h" />
+ <ClInclude Include="OPC_AABBCollider.h" />
+ <ClInclude Include="OPC_LSSCollider.h" />
+ <ClInclude Include="OPC_OBBCollider.h" />
+ <ClInclude Include="OPC_PlanesCollider.h" />
+ <ClInclude Include="OPC_RayCollider.h" />
+ <ClInclude Include="OPC_SphereCollider.h" />
+ <ClInclude Include="OPC_TreeCollider.h" />
+ <ClInclude Include="OPC_AABBTree.h" />
+ <ClInclude Include="OPC_Common.h" />
+ <ClInclude Include="OPC_MeshInterface.h" />
+ <ClInclude Include="OPC_OptimizedTree.h" />
+ <ClInclude Include="OPC_TreeBuilders.h" />
+ <ClInclude Include="OPC_BoxBoxOverlap.h" />
+ <ClInclude Include="OPC_LSSAABBOverlap.h" />
+ <ClInclude Include="OPC_LSSTriOverlap.h" />
+ <ClInclude Include="OPC_PlanesAABBOverlap.h" />
+ <ClInclude Include="OPC_PlanesTriOverlap.h" />
+ <ClInclude Include="OPC_RayAABBOverlap.h" />
+ <ClInclude Include="OPC_RayTriOverlap.h" />
+ <ClInclude Include="OPC_SphereAABBOverlap.h" />
+ <ClInclude Include="OPC_SphereTriOverlap.h" />
+ <ClInclude Include="OPC_TriBoxOverlap.h" />
+ <ClInclude Include="OPC_TriTriOverlap.h" />
+ <ClInclude Include="OPC_BoxPruning.h" />
+ <ClInclude Include="OPC_SweepAndPrune.h" />
+ <ClInclude Include="OPC_Picking.h" />
+ <ClInclude Include="Ice\IceAABB.h" />
+ <ClInclude Include="Ice\IceAxes.h" />
+ <ClInclude Include="Ice\IceBoundingSphere.h" />
+ <ClInclude Include="Ice\IceContainer.h" />
+ <ClInclude Include="Ice\IceFPU.h" />
+ <ClInclude Include="Ice\IceHPoint.h" />
+ <ClInclude Include="Ice\IceIndexedTriangle.h" />
+ <ClInclude Include="Ice\IceLSS.h" />
+ <ClInclude Include="Ice\IceMatrix3x3.h" />
+ <ClInclude Include="Ice\IceMatrix4x4.h" />
+ <ClInclude Include="Ice\IceMemoryMacros.h" />
+ <ClInclude Include="Ice\IceOBB.h" />
+ <ClInclude Include="Ice\IcePairs.h" />
+ <ClInclude Include="Ice\IcePlane.h" />
+ <ClInclude Include="Ice\IcePoint.h" />
+ <ClInclude Include="Ice\IcePreprocessor.h" />
+ <ClInclude Include="Ice\IceRandom.h" />
+ <ClInclude Include="Ice\IceRay.h" />
+ <ClInclude Include="Ice\IceRevisitedRadix.h" />
+ <ClInclude Include="Ice\IceSegment.h" />
+ <ClInclude Include="Ice\IceTriangle.h" />
+ <ClInclude Include="Ice\IceTrilist.h" />
+ <ClInclude Include="Ice\IceTypes.h" />
+ <ClInclude Include="Ice\IceUtils.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="ReadMe.txt" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Opcode/Opcode.vcxproj.filters b/Opcode/Opcode.vcxproj.filters
new file mode 100644
index 0000000..be0c241
--- /dev/null
+++ b/Opcode/Opcode.vcxproj.filters
@@ -0,0 +1,327 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="API">
+ <UniqueIdentifier>{6c24b0c1-fd62-41c8-99dc-9db1df8ee68b}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Trees">
+ <UniqueIdentifier>{39af0cb0-f266-4e16-bb83-e68a0730cb89}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Trees\Collision queries">
+ <UniqueIdentifier>{35f8ea64-1de7-4e93-b530-99c5966311b8}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Trees\Collision queries\Base colliders">
+ <UniqueIdentifier>{76d1ec49-eb79-4ee7-bee9-362490af6f51}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Trees\Collision queries\Standard colliders">
+ <UniqueIdentifier>{a6a367d4-6027-47cd-9cdd-1e714c09db19}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Overlap tests">
+ <UniqueIdentifier>{8ed5026e-bd57-4d9f-8e1b-a5a94e89a486}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="SweepAndPrune">
+ <UniqueIdentifier>{0c7b2ad1-679e-4869-8292-4bc14f1a79fb}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Usages">
+ <UniqueIdentifier>{6656c6e0-b75e-41ed-9ddf-cf3b8f213b0b}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ice">
+ <UniqueIdentifier>{f9ebe0cc-7a07-4345-83e7-25f75f517391}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="OPC_BaseModel.cpp">
+ <Filter>API</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_HybridModel.cpp">
+ <Filter>API</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_Model.cpp">
+ <Filter>API</Filter>
+ </ClCompile>
+ <ClCompile Include="Opcode.cpp">
+ <Filter>API</Filter>
+ </ClCompile>
+ <ClCompile Include="StdAfx.cpp">
+ <Filter>API</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_Collider.cpp">
+ <Filter>Trees\Collision queries\Base colliders</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_VolumeCollider.cpp">
+ <Filter>Trees\Collision queries\Base colliders</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_AABBCollider.cpp">
+ <Filter>Trees\Collision queries\Standard colliders</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_LSSCollider.cpp">
+ <Filter>Trees\Collision queries\Standard colliders</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_OBBCollider.cpp">
+ <Filter>Trees\Collision queries\Standard colliders</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_PlanesCollider.cpp">
+ <Filter>Trees\Collision queries\Standard colliders</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_RayCollider.cpp">
+ <Filter>Trees\Collision queries\Standard colliders</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_SphereCollider.cpp">
+ <Filter>Trees\Collision queries\Standard colliders</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_TreeCollider.cpp">
+ <Filter>Trees\Collision queries\Standard colliders</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_AABBTree.cpp">
+ <Filter>Trees</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_Common.cpp">
+ <Filter>Trees</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_MeshInterface.cpp">
+ <Filter>Trees</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_OptimizedTree.cpp">
+ <Filter>Trees</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_TreeBuilders.cpp">
+ <Filter>Trees</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_BoxPruning.cpp">
+ <Filter>SweepAndPrune</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_SweepAndPrune.cpp">
+ <Filter>SweepAndPrune</Filter>
+ </ClCompile>
+ <ClCompile Include="OPC_Picking.cpp">
+ <Filter>Usages</Filter>
+ </ClCompile>
+ <ClCompile Include="Ice\IceAABB.cpp">
+ <Filter>Ice</Filter>
+ </ClCompile>
+ <ClCompile Include="Ice\IceContainer.cpp">
+ <Filter>Ice</Filter>
+ </ClCompile>
+ <ClCompile Include="Ice\IceHPoint.cpp">
+ <Filter>Ice</Filter>
+ </ClCompile>
+ <ClCompile Include="Ice\IceIndexedTriangle.cpp">
+ <Filter>Ice</Filter>
+ </ClCompile>
+ <ClCompile Include="Ice\IceMatrix3x3.cpp">
+ <Filter>Ice</Filter>
+ </ClCompile>
+ <ClCompile Include="Ice\IceMatrix4x4.cpp">
+ <Filter>Ice</Filter>
+ </ClCompile>
+ <ClCompile Include="Ice\IceOBB.cpp">
+ <Filter>Ice</Filter>
+ </ClCompile>
+ <ClCompile Include="Ice\IcePlane.cpp">
+ <Filter>Ice</Filter>
+ </ClCompile>
+ <ClCompile Include="Ice\IcePoint.cpp">
+ <Filter>Ice</Filter>
+ </ClCompile>
+ <ClCompile Include="Ice\IceRandom.cpp">
+ <Filter>Ice</Filter>
+ </ClCompile>
+ <ClCompile Include="Ice\IceRay.cpp">
+ <Filter>Ice</Filter>
+ </ClCompile>
+ <ClCompile Include="Ice\IceRevisitedRadix.cpp">
+ <Filter>Ice</Filter>
+ </ClCompile>
+ <ClCompile Include="Ice\IceSegment.cpp">
+ <Filter>Ice</Filter>
+ </ClCompile>
+ <ClCompile Include="Ice\IceTriangle.cpp">
+ <Filter>Ice</Filter>
+ </ClCompile>
+ <ClCompile Include="Ice\IceUtils.cpp">
+ <Filter>Ice</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="OPC_BaseModel.h">
+ <Filter>API</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_HybridModel.h">
+ <Filter>API</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_IceHook.h">
+ <Filter>API</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_Model.h">
+ <Filter>API</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_Settings.h">
+ <Filter>API</Filter>
+ </ClInclude>
+ <ClInclude Include="Opcode.h">
+ <Filter>API</Filter>
+ </ClInclude>
+ <ClInclude Include="StdAfx.h">
+ <Filter>API</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_Collider.h">
+ <Filter>Trees\Collision queries\Base colliders</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_VolumeCollider.h">
+ <Filter>Trees\Collision queries\Base colliders</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_AABBCollider.h">
+ <Filter>Trees\Collision queries\Standard colliders</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_LSSCollider.h">
+ <Filter>Trees\Collision queries\Standard colliders</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_OBBCollider.h">
+ <Filter>Trees\Collision queries\Standard colliders</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_PlanesCollider.h">
+ <Filter>Trees\Collision queries\Standard colliders</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_RayCollider.h">
+ <Filter>Trees\Collision queries\Standard colliders</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_SphereCollider.h">
+ <Filter>Trees\Collision queries\Standard colliders</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_TreeCollider.h">
+ <Filter>Trees\Collision queries\Standard colliders</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_AABBTree.h">
+ <Filter>Trees</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_Common.h">
+ <Filter>Trees</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_MeshInterface.h">
+ <Filter>Trees</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_OptimizedTree.h">
+ <Filter>Trees</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_TreeBuilders.h">
+ <Filter>Trees</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_BoxBoxOverlap.h">
+ <Filter>Overlap tests</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_LSSAABBOverlap.h">
+ <Filter>Overlap tests</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_LSSTriOverlap.h">
+ <Filter>Overlap tests</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_PlanesAABBOverlap.h">
+ <Filter>Overlap tests</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_PlanesTriOverlap.h">
+ <Filter>Overlap tests</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_RayAABBOverlap.h">
+ <Filter>Overlap tests</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_RayTriOverlap.h">
+ <Filter>Overlap tests</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_SphereAABBOverlap.h">
+ <Filter>Overlap tests</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_SphereTriOverlap.h">
+ <Filter>Overlap tests</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_TriBoxOverlap.h">
+ <Filter>Overlap tests</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_TriTriOverlap.h">
+ <Filter>Overlap tests</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_BoxPruning.h">
+ <Filter>SweepAndPrune</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_SweepAndPrune.h">
+ <Filter>SweepAndPrune</Filter>
+ </ClInclude>
+ <ClInclude Include="OPC_Picking.h">
+ <Filter>Usages</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceAABB.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceAxes.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceBoundingSphere.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceContainer.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceFPU.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceHPoint.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceIndexedTriangle.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceLSS.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceMatrix3x3.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceMatrix4x4.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceMemoryMacros.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceOBB.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IcePairs.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IcePlane.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IcePoint.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IcePreprocessor.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceRandom.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceRay.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceRevisitedRadix.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceSegment.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceTriangle.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceTrilist.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceTypes.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ <ClInclude Include="Ice\IceUtils.h">
+ <Filter>Ice</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="ReadMe.txt" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Opcode/OpcodeLib/Ice/IceAABB.cpp b/Opcode/OpcodeLib/Ice/IceAABB.cpp
new file mode 100644
index 0000000..2e3288b
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceAABB.cpp
@@ -0,0 +1,405 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains AABB-related code.
+ * \file IceAABB.cpp
+ * \author Pierre Terdiman
+ * \date January, 29, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * AABB class.
+ * \class AABB
+ * \author Pierre Terdiman
+ * \version 1.0
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the sum of two AABBs.
+ * \param aabb [in] the other AABB
+ * \return Self-Reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABB& AABB::Add(const AABB& aabb)
+{
+ // Compute new min & max values
+ IcePoint Min; GetMin(Min);
+ IcePoint Tmp; aabb.GetMin(Tmp);
+ Min.Min(Tmp);
+
+ IcePoint Max; GetMax(Max);
+ aabb.GetMax(Tmp);
+ Max.Max(Tmp);
+
+ // Update this
+ SetMinMax(Min, Max);
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Makes a cube from the AABB.
+ * \param cube [out] the cube AABB
+ * \return cube edge length
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float AABB::MakeCube(AABB& cube) const
+{
+ IcePoint Ext; GetExtents(Ext);
+ float Max = Ext.Max();
+
+ IcePoint Cnt; GetCenter(Cnt);
+ cube.SetCenterExtents(Cnt, IcePoint(Max, Max, Max));
+ return Max;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Makes a sphere from the AABB.
+ * \param sphere [out] sphere containing the AABB
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABB::MakeSphere(Sphere& sphere) const
+{
+ GetExtents(sphere.mCenter);
+ sphere.mRadius = sphere.mCenter.Magnitude() * 1.00001f; // To make sure sphere::Contains(*this) succeeds
+ GetCenter(sphere.mCenter);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks a box is inside another box.
+ * \param box [in] the other AABB
+ * \return true if current box is inside input box
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABB::IsInside(const AABB& box) const
+{
+ if(box.GetMin(0)>GetMin(0)) return false;
+ if(box.GetMin(1)>GetMin(1)) return false;
+ if(box.GetMin(2)>GetMin(2)) return false;
+ if(box.GetMax(0)<GetMax(0)) return false;
+ if(box.GetMax(1)<GetMax(1)) return false;
+ if(box.GetMax(2)<GetMax(2)) return false;
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the AABB planes.
+ * \param planes [out] 6 planes surrounding the box
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABB::ComputePlanes(IcePlane* planes) const
+{
+ // Checkings
+ if(!planes) return false;
+
+ IcePoint Center, Extents;
+ GetCenter(Center);
+ GetExtents(Extents);
+
+ // Writes normals
+ planes[0].n = IcePoint(1.0f, 0.0f, 0.0f);
+ planes[1].n = IcePoint(-1.0f, 0.0f, 0.0f);
+ planes[2].n = IcePoint(0.0f, 1.0f, 0.0f);
+ planes[3].n = IcePoint(0.0f, -1.0f, 0.0f);
+ planes[4].n = IcePoint(0.0f, 0.0f, 1.0f);
+ planes[5].n = IcePoint(0.0f, 0.0f, -1.0f);
+
+ // Compute a point on each plane
+ IcePoint p0 = IcePoint(Center.x+Extents.x, Center.y, Center.z);
+ IcePoint p1 = IcePoint(Center.x-Extents.x, Center.y, Center.z);
+ IcePoint p2 = IcePoint(Center.x, Center.y+Extents.y, Center.z);
+ IcePoint p3 = IcePoint(Center.x, Center.y-Extents.y, Center.z);
+ IcePoint p4 = IcePoint(Center.x, Center.y, Center.z+Extents.z);
+ IcePoint p5 = IcePoint(Center.x, Center.y, Center.z-Extents.z);
+
+ // Compute d
+ planes[0].d = -(planes[0].n|p0);
+ planes[1].d = -(planes[1].n|p1);
+ planes[2].d = -(planes[2].n|p2);
+ planes[3].d = -(planes[3].n|p3);
+ planes[4].d = -(planes[4].n|p4);
+ planes[5].d = -(planes[5].n|p5);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the aabb points.
+ * \param pts [out] 8 box points
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABB::ComputePoints(IcePoint* pts) const
+{
+ // Checkings
+ if(!pts) return false;
+
+ // Get box corners
+ IcePoint min; GetMin(min);
+ IcePoint max; GetMax(max);
+
+ // 7+------+6 0 = ---
+ // /| /| 1 = +--
+ // / | / | 2 = ++-
+ // / 4+---/--+5 3 = -+-
+ // 3+------+2 / y z 4 = --+
+ // | / | / | / 5 = +-+
+ // |/ |/ |/ 6 = +++
+ // 0+------+1 *---x 7 = -++
+
+ // Generate 8 corners of the bbox
+ pts[0] = IcePoint(min.x, min.y, min.z);
+ pts[1] = IcePoint(max.x, min.y, min.z);
+ pts[2] = IcePoint(max.x, max.y, min.z);
+ pts[3] = IcePoint(min.x, max.y, min.z);
+ pts[4] = IcePoint(min.x, min.y, max.z);
+ pts[5] = IcePoint(max.x, min.y, max.z);
+ pts[6] = IcePoint(max.x, max.y, max.z);
+ pts[7] = IcePoint(min.x, max.y, max.z);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets vertex normals.
+ * \param pts [out] 8 box points
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const IcePoint* AABB::GetVertexNormals() const
+{
+ static float VertexNormals[] =
+ {
+ -INVSQRT3, -INVSQRT3, -INVSQRT3,
+ INVSQRT3, -INVSQRT3, -INVSQRT3,
+ INVSQRT3, INVSQRT3, -INVSQRT3,
+ -INVSQRT3, INVSQRT3, -INVSQRT3,
+ -INVSQRT3, -INVSQRT3, INVSQRT3,
+ INVSQRT3, -INVSQRT3, INVSQRT3,
+ INVSQRT3, INVSQRT3, INVSQRT3,
+ -INVSQRT3, INVSQRT3, INVSQRT3
+ };
+ return (const IcePoint*)VertexNormals;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns edges.
+ * \return 24 indices (12 edges) indexing the list returned by ComputePoints()
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const udword* AABB::GetEdges() const
+{
+ static udword Indices[] = {
+ 0, 1, 1, 2, 2, 3, 3, 0,
+ 7, 6, 6, 5, 5, 4, 4, 7,
+ 1, 5, 6, 2,
+ 3, 7, 4, 0
+ };
+ return Indices;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns edge normals.
+ * \return edge normals in local space
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const IcePoint* AABB::GetEdgeNormals() const
+{
+ static float EdgeNormals[] =
+ {
+ 0, -INVSQRT2, -INVSQRT2, // 0-1
+ INVSQRT2, 0, -INVSQRT2, // 1-2
+ 0, INVSQRT2, -INVSQRT2, // 2-3
+ -INVSQRT2, 0, -INVSQRT2, // 3-0
+
+ 0, INVSQRT2, INVSQRT2, // 7-6
+ INVSQRT2, 0, INVSQRT2, // 6-5
+ 0, -INVSQRT2, INVSQRT2, // 5-4
+ -INVSQRT2, 0, INVSQRT2, // 4-7
+
+ INVSQRT2, -INVSQRT2, 0, // 1-5
+ INVSQRT2, INVSQRT2, 0, // 6-2
+ -INVSQRT2, INVSQRT2, 0, // 3-7
+ -INVSQRT2, -INVSQRT2, 0 // 4-0
+ };
+ return (const IcePoint*)EdgeNormals;
+}
+
+// ===========================================================================
+// (C) 1996-98 Vienna University of Technology
+// ===========================================================================
+// NAME: bboxarea
+// TYPE: c++ code
+// PROJECT: Bounding Box Area
+// CONTENT: Computes area of 2D projection of 3D oriented bounding box
+// VERSION: 1.0
+// ===========================================================================
+// AUTHORS: ds Dieter Schmalstieg
+// ep Erik Pojar
+// ===========================================================================
+// HISTORY:
+//
+// 19-sep-99 15:23:03 ds last modification
+// 01-dec-98 15:23:03 ep created
+// ===========================================================================
+
+//----------------------------------------------------------------------------
+// SAMPLE CODE STARTS HERE
+//----------------------------------------------------------------------------
+
+// NOTE: This sample program requires OPEN INVENTOR!
+
+//indexlist: this table stores the 64 possible cases of classification of
+//the eyepoint with respect to the 6 defining planes of the bbox (2^6=64)
+//only 26 (3^3-1, where 1 is "inside" cube) of these cases are valid.
+//the first 6 numbers in each row are the indices of the bbox vertices that
+//form the outline of which we want to compute the area (counterclockwise
+//ordering), the 7th entry means the number of vertices in the outline.
+//there are 6 cases with a single face and and a 4-vertex outline, and
+//20 cases with 2 or 3 faces and a 6-vertex outline. a value of 0 indicates
+//an invalid case.
+
+
+// Original list was made of 7 items, I added an 8th element:
+// - to padd on a cache line
+// - to repeat the first entry to avoid modulos
+//
+// I also replaced original ints with sbytes.
+
+static const sbyte gIndexList[64][8] =
+{
+ {-1,-1,-1,-1,-1,-1,-1, 0}, // 0 inside
+ { 0, 4, 7, 3, 0,-1,-1, 4}, // 1 left
+ { 1, 2, 6, 5, 1,-1,-1, 4}, // 2 right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, // 3 -
+ { 0, 1, 5, 4, 0,-1,-1, 4}, // 4 bottom
+ { 0, 1, 5, 4, 7, 3, 0, 6}, // 5 bottom, left
+ { 0, 1, 2, 6, 5, 4, 0, 6}, // 6 bottom, right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, // 7 -
+ { 2, 3, 7, 6, 2,-1,-1, 4}, // 8 top
+ { 0, 4, 7, 6, 2, 3, 0, 6}, // 9 top, left
+ { 1, 2, 3, 7, 6, 5, 1, 6}, //10 top, right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //11 -
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //12 -
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //13 -
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //14 -
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //15 -
+ { 0, 3, 2, 1, 0,-1,-1, 4}, //16 front
+ { 0, 4, 7, 3, 2, 1, 0, 6}, //17 front, left
+ { 0, 3, 2, 6, 5, 1, 0, 6}, //18 front, right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //19 -
+ { 0, 3, 2, 1, 5, 4, 0, 6}, //20 front, bottom
+ { 1, 5, 4, 7, 3, 2, 1, 6}, //21 front, bottom, left
+ { 0, 3, 2, 6, 5, 4, 0, 6}, //22 front, bottom, right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //23 -
+ { 0, 3, 7, 6, 2, 1, 0, 6}, //24 front, top
+ { 0, 4, 7, 6, 2, 1, 0, 6}, //25 front, top, left
+ { 0, 3, 7, 6, 5, 1, 0, 6}, //26 front, top, right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //27 -
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //28 -
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //29 -
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //30 -
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //31 -
+ { 4, 5, 6, 7, 4,-1,-1, 4}, //32 back
+ { 0, 4, 5, 6, 7, 3, 0, 6}, //33 back, left
+ { 1, 2, 6, 7, 4, 5, 1, 6}, //34 back, right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //35 -
+ { 0, 1, 5, 6, 7, 4, 0, 6}, //36 back, bottom
+ { 0, 1, 5, 6, 7, 3, 0, 6}, //37 back, bottom, left
+ { 0, 1, 2, 6, 7, 4, 0, 6}, //38 back, bottom, right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //39 -
+ { 2, 3, 7, 4, 5, 6, 2, 6}, //40 back, top
+ { 0, 4, 5, 6, 2, 3, 0, 6}, //41 back, top, left
+ { 1, 2, 3, 7, 4, 5, 1, 6}, //42 back, top, right
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //43 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //44 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //45 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //46 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //47 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //48 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //49 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //50 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //51 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //52 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //53 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //54 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //55 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //56 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //57 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //58 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //59 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //60 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //61 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0}, //62 invalid
+ {-1,-1,-1,-1,-1,-1,-1, 0} //63 invalid
+};
+
+const sbyte* AABB::ComputeOutline(const IcePoint& local_eye, sdword& num) const
+{
+ // Get box corners
+ IcePoint min; GetMin(min);
+ IcePoint max; GetMax(max);
+
+ // Compute 6-bit code to classify eye with respect to the 6 defining planes of the bbox
+ int pos = ((local_eye.x < min.x) ? 1 : 0) // 1 = left
+ + ((local_eye.x > max.x) ? 2 : 0) // 2 = right
+ + ((local_eye.y < min.y) ? 4 : 0) // 4 = bottom
+ + ((local_eye.y > max.y) ? 8 : 0) // 8 = top
+ + ((local_eye.z < min.z) ? 16 : 0) // 16 = front
+ + ((local_eye.z > max.z) ? 32 : 0); // 32 = back
+
+ // Look up number of vertices in outline
+ num = (sdword)gIndexList[pos][7];
+ // Zero indicates invalid case
+ if(!num) return null;
+
+ return &gIndexList[pos][0];
+}
+
+// calculateBoxArea: computes the screen-projected 2D area of an oriented 3D bounding box
+
+//const IcePoint& eye, //eye point (in bbox object coordinates)
+//const AABB& box, //3d bbox
+//const Matrix4x4& mat, //free transformation for bbox
+//float width, float height, int& num)
+float AABB::ComputeBoxArea(const IcePoint& eye, const Matrix4x4& mat, float width, float height, sdword& num) const
+{
+ const sbyte* Outline = ComputeOutline(eye, num);
+ if(!Outline) return -1.0f;
+
+ // Compute box vertices
+ IcePoint vertexBox[8], dst[8];
+ ComputePoints(vertexBox);
+
+ // Transform all outline corners into 2D screen space
+ for(sdword i=0;i<num;i++)
+ {
+ HPoint Projected;
+ vertexBox[Outline[i]].ProjectToScreen(width, height, mat, Projected);
+ dst[i] = Projected;
+ }
+
+ float Sum = (dst[num-1][0] - dst[0][0]) * (dst[num-1][1] + dst[0][1]);
+
+ for(int i=0; i<num-1; i++)
+ Sum += (dst[i][0] - dst[i+1][0]) * (dst[i][1] + dst[i+1][1]);
+
+ return Sum * 0.5f; //return computed value corrected by 0.5
+}
diff --git a/Opcode/OpcodeLib/Ice/IceAABB.h b/Opcode/OpcodeLib/Ice/IceAABB.h
new file mode 100644
index 0000000..caf8a80
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceAABB.h
@@ -0,0 +1,505 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains AABB-related code. (axis-aligned bounding box)
+ * \file IceAABB.h
+ * \author Pierre Terdiman
+ * \date January, 13, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEAABB_H__
+#define __ICEAABB_H__
+
+ // Forward declarations
+ class Sphere;
+
+//! Declarations of type-independent methods (most of them implemented in the .cpp)
+#define AABB_COMMON_METHODS \
+ AABB& Add(const AABB& aabb); \
+ float MakeCube(AABB& cube) const; \
+ void MakeSphere(Sphere& sphere) const; \
+ const sbyte* ComputeOutline(const IcePoint& local_eye, sdword& num) const; \
+ float ComputeBoxArea(const IcePoint& eye, const Matrix4x4& mat, float width, float height, sdword& num) const; \
+ bool IsInside(const AABB& box) const; \
+ bool ComputePlanes(IcePlane* planes) const; \
+ bool ComputePoints(IcePoint* pts) const; \
+ const IcePoint* GetVertexNormals() const; \
+ const udword* GetEdges() const; \
+ const IcePoint* GetEdgeNormals() const; \
+ inline_ BOOL ContainsPoint(const IcePoint& p) const \
+ { \
+ if(p.x > GetMax(0) || p.x < GetMin(0)) return FALSE; \
+ if(p.y > GetMax(1) || p.y < GetMin(1)) return FALSE; \
+ if(p.z > GetMax(2) || p.z < GetMin(2)) return FALSE; \
+ return TRUE; \
+ }
+
+ enum AABBType
+ {
+ AABB_RENDER = 0, //!< AABB used for rendering. Not visible == not rendered.
+ AABB_UPDATE = 1, //!< AABB used for dynamic updates. Not visible == not updated.
+
+ AABB_FORCE_DWORD = 0x7fffffff,
+ };
+
+#ifdef USE_MINMAX
+
+ struct ICEMATHS_API ShadowAABB
+ {
+ IcePoint mMin;
+ IcePoint mMax;
+ };
+
+ class ICEMATHS_API AABB
+ {
+ public:
+ //! Constructor
+ inline_ AABB() {}
+ //! Destructor
+ inline_ ~AABB() {}
+
+ //! Type-independent methods
+ AABB_COMMON_METHODS;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups an AABB from min & max vectors.
+ * \param min [in] the min point
+ * \param max [in] the max point
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetMinMax(const IcePoint& min, const IcePoint& max) { mMin = min; mMax = max; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups an AABB from center & extents vectors.
+ * \param c [in] the center point
+ * \param e [in] the extents vector
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetCenterExtents(const IcePoint& c, const IcePoint& e) { mMin = c - e; mMax = c + e; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups an empty AABB.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetEmpty() { IcePoint p(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); mMin = -p; mMax = p;}
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups a point AABB.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetPoint(const IcePoint& pt) { mMin = mMax = pt; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the size of the AABB. The size is defined as the longest extent.
+ * \return the size of the AABB
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ float GetSize() const { IcePoint e; GetExtents(e); return e.Max(); }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Extends the AABB.
+ * \param p [in] the next point
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void Extend(const IcePoint& p)
+ {
+ if(p.x > mMax.x) mMax.x = p.x;
+ if(p.x < mMin.x) mMin.x = p.x;
+
+ if(p.y > mMax.y) mMax.y = p.y;
+ if(p.y < mMin.y) mMin.y = p.y;
+
+ if(p.z > mMax.z) mMax.z = p.z;
+ if(p.z < mMin.z) mMin.z = p.z;
+ }
+ // Data access
+
+ //! Get min point of the box
+ inline_ void GetMin(IcePoint& min) const { min = mMin; }
+ //! Get max point of the box
+ inline_ void GetMax(IcePoint& max) const { max = mMax; }
+
+ //! Get component of the box's min point along a given axis
+ inline_ float GetMin(udword axis) const { return mMin[axis]; }
+ //! Get component of the box's max point along a given axis
+ inline_ float GetMax(udword axis) const { return mMax[axis]; }
+
+ //! Get box center
+ inline_ void GetCenter(IcePoint& center) const { center = (mMax + mMin)*0.5f; }
+ //! Get box extents
+ inline_ void GetExtents(IcePoint& extents) const { extents = (mMax - mMin)*0.5f; }
+
+ //! Get component of the box's center along a given axis
+ inline_ float GetCenter(udword axis) const { return (mMax[axis] + mMin[axis])*0.5f; }
+ //! Get component of the box's extents along a given axis
+ inline_ float GetExtents(udword axis) const { return (mMax[axis] - mMin[axis])*0.5f; }
+
+ //! Get box diagonal
+ inline_ void GetDiagonal(IcePoint& diagonal) const { diagonal = mMax - mMin; }
+ inline_ float GetWidth() const { return mMax.x - mMin.x; }
+ inline_ float GetHeight() const { return mMax.y - mMin.y; }
+ inline_ float GetDepth() const { return mMax.z - mMin.z; }
+
+ //! Volume
+ inline_ float GetVolume() const { return GetWidth() * GetHeight() * GetDepth(); }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the intersection between two AABBs.
+ * \param a [in] the other AABB
+ * \return true on intersection
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL Intersect(const AABB& a) const
+ {
+ if(mMax.x < a.mMin.x
+ || a.mMax.x < mMin.x
+ || mMax.y < a.mMin.y
+ || a.mMax.y < mMin.y
+ || mMax.z < a.mMin.z
+ || a.mMax.z < mMin.z) return FALSE;
+
+ return TRUE;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the 1D-intersection between two AABBs, on a given axis.
+ * \param a [in] the other AABB
+ * \param axis [in] the axis (0, 1, 2)
+ * \return true on intersection
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL Intersect(const AABB& a, udword axis) const
+ {
+ if(mMax[axis] < a.mMin[axis] || a.mMax[axis] < mMin[axis]) return FALSE;
+ return TRUE;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Recomputes the AABB after an arbitrary transform by a 4x4 matrix.
+ * Original code by Charles Bloom on the GD-Algorithm list. (I slightly modified it)
+ * \param mtx [in] the transform matrix
+ * \param aabb [out] the transformed AABB [can be *this]
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void Rotate(const Matrix4x4& mtx, AABB& aabb) const
+ {
+ // The three edges transformed: you can efficiently transform an X-only vector
+ // by just getting the "X" column of the matrix
+ IcePoint vx,vy,vz;
+ mtx.GetRow(0, vx); vx *= (mMax.x - mMin.x);
+ mtx.GetRow(1, vy); vy *= (mMax.y - mMin.y);
+ mtx.GetRow(2, vz); vz *= (mMax.z - mMin.z);
+
+ // Transform the min point
+ aabb.mMin = aabb.mMax = mMin * mtx;
+
+ // Take the transformed min & axes and find new extents
+ // Using CPU code in the right place is faster...
+ if(IS_NEGATIVE_FLOAT(vx.x)) aabb.mMin.x += vx.x; else aabb.mMax.x += vx.x;
+ if(IS_NEGATIVE_FLOAT(vx.y)) aabb.mMin.y += vx.y; else aabb.mMax.y += vx.y;
+ if(IS_NEGATIVE_FLOAT(vx.z)) aabb.mMin.z += vx.z; else aabb.mMax.z += vx.z;
+ if(IS_NEGATIVE_FLOAT(vy.x)) aabb.mMin.x += vy.x; else aabb.mMax.x += vy.x;
+ if(IS_NEGATIVE_FLOAT(vy.y)) aabb.mMin.y += vy.y; else aabb.mMax.y += vy.y;
+ if(IS_NEGATIVE_FLOAT(vy.z)) aabb.mMin.z += vy.z; else aabb.mMax.z += vy.z;
+ if(IS_NEGATIVE_FLOAT(vz.x)) aabb.mMin.x += vz.x; else aabb.mMax.x += vz.x;
+ if(IS_NEGATIVE_FLOAT(vz.y)) aabb.mMin.y += vz.y; else aabb.mMax.y += vz.y;
+ if(IS_NEGATIVE_FLOAT(vz.z)) aabb.mMin.z += vz.z; else aabb.mMax.z += vz.z;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks the AABB is valid.
+ * \return true if the box is valid
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL IsValid() const
+ {
+ // Consistency condition for (Min, Max) boxes: min < max
+ if(mMin.x > mMax.x) return FALSE;
+ if(mMin.y > mMax.y) return FALSE;
+ if(mMin.z > mMax.z) return FALSE;
+ return TRUE;
+ }
+
+ //! Operator for AABB *= float. Scales the extents, keeps same center.
+ inline_ AABB& operator*=(float s)
+ {
+ IcePoint Center; GetCenter(Center);
+ IcePoint Extents; GetExtents(Extents);
+ SetCenterExtents(Center, Extents * s);
+ return *this;
+ }
+
+ //! Operator for AABB /= float. Scales the extents, keeps same center.
+ inline_ AABB& operator/=(float s)
+ {
+ IcePoint Center; GetCenter(Center);
+ IcePoint Extents; GetExtents(Extents);
+ SetCenterExtents(Center, Extents / s);
+ return *this;
+ }
+
+ //! Operator for AABB += IcePoint. Translates the box.
+ inline_ AABB& operator+=(const IcePoint& trans)
+ {
+ mMin+=trans;
+ mMax+=trans;
+ return *this;
+ }
+ private:
+ IcePoint mMin; //!< Min point
+ IcePoint mMax; //!< Max point
+ };
+
+#else
+
+ class ICEMATHS_API AABB
+ {
+ public:
+ //! Constructor
+ inline_ AABB() {}
+ //! Destructor
+ inline_ ~AABB() {}
+
+ //! Type-independent methods
+ AABB_COMMON_METHODS;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups an AABB from min & max vectors.
+ * \param min [in] the min point
+ * \param max [in] the max point
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetMinMax(const IcePoint& min, const IcePoint& max) { mCenter = (max + min)*0.5f; mExtents = (max - min)*0.5f; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups an AABB from center & extents vectors.
+ * \param c [in] the center point
+ * \param e [in] the extents vector
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetCenterExtents(const IcePoint& c, const IcePoint& e) { mCenter = c; mExtents = e; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups an empty AABB.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetEmpty() { mCenter.Zero(); mExtents.Set(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT);}
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups a point AABB.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetPoint(const IcePoint& pt) { mCenter = pt; mExtents.Zero(); }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the size of the AABB. The size is defined as the longest extent.
+ * \return the size of the AABB
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ float GetSize() const { return mExtents.Max(); }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Extends the AABB.
+ * \param p [in] the next point
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void Extend(const IcePoint& p)
+ {
+ IcePoint Max = mCenter + mExtents;
+ IcePoint Min = mCenter - mExtents;
+
+ if(p.x > Max.x) Max.x = p.x;
+ if(p.x < Min.x) Min.x = p.x;
+
+ if(p.y > Max.y) Max.y = p.y;
+ if(p.y < Min.y) Min.y = p.y;
+
+ if(p.z > Max.z) Max.z = p.z;
+ if(p.z < Min.z) Min.z = p.z;
+
+ SetMinMax(Min, Max);
+ }
+ // Data access
+
+ //! Get min point of the box
+ inline_ void GetMin(IcePoint& min) const { min = mCenter - mExtents; }
+ //! Get max point of the box
+ inline_ void GetMax(IcePoint& max) const { max = mCenter + mExtents; }
+
+ //! Get component of the box's min point along a given axis
+ inline_ float GetMin(udword axis) const { return mCenter[axis] - mExtents[axis]; }
+ //! Get component of the box's max point along a given axis
+ inline_ float GetMax(udword axis) const { return mCenter[axis] + mExtents[axis]; }
+
+ //! Get box center
+ inline_ void GetCenter(IcePoint& center) const { center = mCenter; }
+ //! Get box extents
+ inline_ void GetExtents(IcePoint& extents) const { extents = mExtents; }
+
+ //! Get component of the box's center along a given axis
+ inline_ float GetCenter(udword axis) const { return mCenter[axis]; }
+ //! Get component of the box's extents along a given axis
+ inline_ float GetExtents(udword axis) const { return mExtents[axis]; }
+
+ //! Get box diagonal
+ inline_ void GetDiagonal(IcePoint& diagonal) const { diagonal = mExtents * 2.0f; }
+ inline_ float GetWidth() const { return mExtents.x * 2.0f; }
+ inline_ float GetHeight() const { return mExtents.y * 2.0f; }
+ inline_ float GetDepth() const { return mExtents.z * 2.0f; }
+
+ //! Volume
+ inline_ float GetVolume() const { return mExtents.x * mExtents.y * mExtents.z * 8.0f; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the intersection between two AABBs.
+ * \param a [in] the other AABB
+ * \return true on intersection
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL Intersect(const AABB& a) const
+ {
+ float tx = mCenter.x - a.mCenter.x; float ex = a.mExtents.x + mExtents.x; if(AIR(tx) > IR(ex)) return FALSE;
+ float ty = mCenter.y - a.mCenter.y; float ey = a.mExtents.y + mExtents.y; if(AIR(ty) > IR(ey)) return FALSE;
+ float tz = mCenter.z - a.mCenter.z; float ez = a.mExtents.z + mExtents.z; if(AIR(tz) > IR(ez)) return FALSE;
+ return TRUE;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * The standard intersection method from Gamasutra. Just here to check its speed against the one above.
+ * \param a [in] the other AABB
+ * \return true on intersection
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ bool GomezIntersect(const AABB& a)
+ {
+ IcePoint T = mCenter - a.mCenter; // Vector from A to B
+ return ((fabsf(T.x) <= (a.mExtents.x + mExtents.x))
+ && (fabsf(T.y) <= (a.mExtents.y + mExtents.y))
+ && (fabsf(T.z) <= (a.mExtents.z + mExtents.z)));
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the 1D-intersection between two AABBs, on a given axis.
+ * \param a [in] the other AABB
+ * \param axis [in] the axis (0, 1, 2)
+ * \return true on intersection
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL Intersect(const AABB& a, udword axis) const
+ {
+ float t = mCenter[axis] - a.mCenter[axis];
+ float e = a.mExtents[axis] + mExtents[axis];
+ if(AIR(t) > IR(e)) return FALSE;
+ return TRUE;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Recomputes the AABB after an arbitrary transform by a 4x4 matrix.
+ * \param mtx [in] the transform matrix
+ * \param aabb [out] the transformed AABB [can be *this]
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void Rotate(const Matrix4x4& mtx, AABB& aabb) const
+ {
+ // Compute new center
+ aabb.mCenter = mCenter * mtx;
+
+ // Compute new extents. FPU code & CPU code have been interleaved for improved performance.
+ IcePoint Ex(mtx.m[0][0] * mExtents.x, mtx.m[0][1] * mExtents.x, mtx.m[0][2] * mExtents.x);
+ IR(Ex.x)&=0x7fffffff; IR(Ex.y)&=0x7fffffff; IR(Ex.z)&=0x7fffffff;
+
+ IcePoint Ey(mtx.m[1][0] * mExtents.y, mtx.m[1][1] * mExtents.y, mtx.m[1][2] * mExtents.y);
+ IR(Ey.x)&=0x7fffffff; IR(Ey.y)&=0x7fffffff; IR(Ey.z)&=0x7fffffff;
+
+ IcePoint Ez(mtx.m[2][0] * mExtents.z, mtx.m[2][1] * mExtents.z, mtx.m[2][2] * mExtents.z);
+ IR(Ez.x)&=0x7fffffff; IR(Ez.y)&=0x7fffffff; IR(Ez.z)&=0x7fffffff;
+
+ aabb.mExtents.x = Ex.x + Ey.x + Ez.x;
+ aabb.mExtents.y = Ex.y + Ey.y + Ez.y;
+ aabb.mExtents.z = Ex.z + Ey.z + Ez.z;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks the AABB is valid.
+ * \return true if the box is valid
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL IsValid() const
+ {
+ // Consistency condition for (Center, Extents) boxes: Extents >= 0
+ if(IS_NEGATIVE_FLOAT(mExtents.x)) return FALSE;
+ if(IS_NEGATIVE_FLOAT(mExtents.y)) return FALSE;
+ if(IS_NEGATIVE_FLOAT(mExtents.z)) return FALSE;
+ return TRUE;
+ }
+
+ //! Operator for AABB *= float. Scales the extents, keeps same center.
+ inline_ AABB& operator*=(float s) { mExtents*=s; return *this; }
+
+ //! Operator for AABB /= float. Scales the extents, keeps same center.
+ inline_ AABB& operator/=(float s) { mExtents/=s; return *this; }
+
+ //! Operator for AABB += IcePoint. Translates the box.
+ inline_ AABB& operator+=(const IcePoint& trans)
+ {
+ mCenter+=trans;
+ return *this;
+ }
+ private:
+ IcePoint mCenter; //!< AABB Center
+ IcePoint mExtents; //!< x, y and z extents
+ };
+
+#endif
+
+ inline_ void ComputeMinMax(const IcePoint& p, IcePoint& min, IcePoint& max)
+ {
+ if(p.x > max.x) max.x = p.x;
+ if(p.x < min.x) min.x = p.x;
+
+ if(p.y > max.y) max.y = p.y;
+ if(p.y < min.y) min.y = p.y;
+
+ if(p.z > max.z) max.z = p.z;
+ if(p.z < min.z) min.z = p.z;
+ }
+
+ inline_ void ComputeAABB(AABB& aabb, const IcePoint* list, udword nb_pts)
+ {
+ if(list)
+ {
+ IcePoint Maxi(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT);
+ IcePoint Mini(MAX_FLOAT, MAX_FLOAT, MAX_FLOAT);
+ while(nb_pts--)
+ {
+// _prefetch(list+1); // off by one ?
+ ComputeMinMax(*list++, Mini, Maxi);
+ }
+ aabb.SetMinMax(Mini, Maxi);
+ }
+ }
+
+#endif // __ICEAABB_H__
diff --git a/Opcode/OpcodeLib/Ice/IceAxes.h b/Opcode/OpcodeLib/Ice/IceAxes.h
new file mode 100644
index 0000000..39004a9
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceAxes.h
@@ -0,0 +1,54 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains axes definition.
+ * \file IceAxes.h
+ * \author Pierre Terdiman
+ * \date January, 29, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEAXES_H__
+#define __ICEAXES_H__
+
+ enum PointComponent
+ {
+ _X = 0,
+ _Y = 1,
+ _Z = 2,
+ _W = 3,
+
+ _FORCE_DWORD = 0x7fffffff
+ };
+
+ enum AxisOrder
+ {
+ AXES_XYZ = (_X)|(_Y<<2)|(_Z<<4),
+ AXES_XZY = (_X)|(_Z<<2)|(_Y<<4),
+ AXES_YXZ = (_Y)|(_X<<2)|(_Z<<4),
+ AXES_YZX = (_Y)|(_Z<<2)|(_X<<4),
+ AXES_ZXY = (_Z)|(_X<<2)|(_Y<<4),
+ AXES_ZYX = (_Z)|(_Y<<2)|(_X<<4),
+
+ AXES_FORCE_DWORD = 0x7fffffff
+ };
+
+ class ICEMATHS_API Axes
+ {
+ public:
+
+ inline_ Axes(AxisOrder order)
+ {
+ mAxis0 = (order ) & 3;
+ mAxis1 = (order>>2) & 3;
+ mAxis2 = (order>>4) & 3;
+ }
+ inline_ ~Axes() {}
+
+ udword mAxis0;
+ udword mAxis1;
+ udword mAxis2;
+ };
+
+#endif // __ICEAXES_H__
diff --git a/Opcode/OpcodeLib/Ice/IceBoundingSphere.h b/Opcode/OpcodeLib/Ice/IceBoundingSphere.h
new file mode 100644
index 0000000..43329e6
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceBoundingSphere.h
@@ -0,0 +1,142 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code to compute the minimal bounding sphere.
+ * \file IceBoundingSphere.h
+ * \author Pierre Terdiman
+ * \date January, 29, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEBOUNDINGSPHERE_H__
+#define __ICEBOUNDINGSPHERE_H__
+
+ enum BSphereMethod
+ {
+ BS_NONE,
+ BS_GEMS,
+ BS_MINIBALL,
+
+ BS_FORCE_DWORD = 0x7fffffff
+ };
+
+ class ICEMATHS_API Sphere
+ {
+ public:
+ //! Constructor
+ inline_ Sphere() {}
+ //! Constructor
+ inline_ Sphere(const IcePoint& center, float radius) : mCenter(center), mRadius(radius) {}
+ //! Constructor
+ Sphere(udword nb_verts, const IcePoint* verts);
+ //! Copy constructor
+ inline_ Sphere(const Sphere& sphere) : mCenter(sphere.mCenter), mRadius(sphere.mRadius) {}
+ //! Destructor
+ inline_ ~Sphere() {}
+
+ BSphereMethod Compute(udword nb_verts, const IcePoint* verts);
+ bool FastCompute(udword nb_verts, const IcePoint* verts);
+
+ // Access methods
+ inline_ const IcePoint& GetCenter() const { return mCenter; }
+ inline_ float GetRadius() const { return mRadius; }
+
+ inline_ const IcePoint& Center() const { return mCenter; }
+ inline_ float Radius() const { return mRadius; }
+
+ inline_ Sphere& Set(const IcePoint& center, float radius) { mCenter = center; mRadius = radius; return *this; }
+ inline_ Sphere& SetCenter(const IcePoint& center) { mCenter = center; return *this; }
+ inline_ Sphere& SetRadius(float radius) { mRadius = radius; return *this; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Tests if a point is contained within the sphere.
+ * \param p [in] the point to test
+ * \return true if inside the sphere
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ bool Contains(const IcePoint& p) const
+ {
+ return mCenter.SquareDistance(p) <= mRadius*mRadius;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Tests if a sphere is contained within the sphere.
+ * \param sphere [in] the sphere to test
+ * \return true if inside the sphere
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ bool Contains(const Sphere& sphere) const
+ {
+ // If our radius is the smallest, we can't possibly contain the other sphere
+ if(mRadius < sphere.mRadius) return false;
+ // So r is always positive or null now
+ float r = mRadius - sphere.mRadius;
+ return mCenter.SquareDistance(sphere.mCenter) <= r*r;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Tests if a box is contained within the sphere.
+ * \param aabb [in] the box to test
+ * \return true if inside the sphere
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL Contains(const AABB& aabb) const
+ {
+ // I assume if all 8 box vertices are inside the sphere, so does the whole box.
+ // Sounds ok but maybe there's a better way?
+ float R2 = mRadius * mRadius;
+#ifdef USE_MIN_MAX
+ const IcePoint& Max = ((ShadowAABB&)&aabb).mMax;
+ const IcePoint& Min = ((ShadowAABB&)&aabb).mMin;
+#else
+ IcePoint Max; aabb.GetMax(Max);
+ IcePoint Min; aabb.GetMin(Min);
+#endif
+ IcePoint p;
+ p.x=Max.x; p.y=Max.y; p.z=Max.z; if(mCenter.SquareDistance(p)>=R2) return FALSE;
+ p.x=Min.x; if(mCenter.SquareDistance(p)>=R2) return FALSE;
+ p.x=Max.x; p.y=Min.y; if(mCenter.SquareDistance(p)>=R2) return FALSE;
+ p.x=Min.x; if(mCenter.SquareDistance(p)>=R2) return FALSE;
+ p.x=Max.x; p.y=Max.y; p.z=Min.z; if(mCenter.SquareDistance(p)>=R2) return FALSE;
+ p.x=Min.x; if(mCenter.SquareDistance(p)>=R2) return FALSE;
+ p.x=Max.x; p.y=Min.y; if(mCenter.SquareDistance(p)>=R2) return FALSE;
+ p.x=Min.x; if(mCenter.SquareDistance(p)>=R2) return FALSE;
+
+ return TRUE;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Tests if the sphere intersects another sphere
+ * \param sphere [in] the other sphere
+ * \return true if spheres overlap
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ bool Intersect(const Sphere& sphere) const
+ {
+ float r = mRadius + sphere.mRadius;
+ return mCenter.SquareDistance(sphere.mCenter) <= r*r;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks the sphere is valid.
+ * \return true if the box is valid
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL IsValid() const
+ {
+ // Consistency condition for spheres: Radius >= 0.0f
+ if(mRadius < 0.0f) return FALSE;
+ return TRUE;
+ }
+ public:
+ IcePoint mCenter; //!< Sphere center
+ float mRadius; //!< Sphere radius
+ };
+
+#endif // __ICEBOUNDINGSPHERE_H__
diff --git a/Opcode/OpcodeLib/Ice/IceContainer.cpp b/Opcode/OpcodeLib/Ice/IceContainer.cpp
new file mode 100644
index 0000000..e2c42d1
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceContainer.cpp
@@ -0,0 +1,345 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a simple container class.
+ * \file IceContainer.cpp
+ * \author Pierre Terdiman
+ * \date February, 5, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a list of 32-bits values.
+ * Use this class when you need to store an unknown number of values. The list is automatically
+ * resized and can contains 32-bits entities (dwords or floats)
+ *
+ * \class Container
+ * \author Pierre Terdiman
+ * \version 1.0
+ * \date 08.15.98
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceCore;
+
+// Static members
+#ifdef CONTAINER_STATS
+udword Container::mNbContainers = 0;
+udword Container::mUsedRam = 0;
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor. No entries allocated there.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Container::Container() : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(2.0f)
+{
+#ifdef CONTAINER_STATS
+ mNbContainers++;
+ mUsedRam+=sizeof(Container);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor. Also allocates a given number of entries.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Container::Container(udword size, float growth_factor) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(growth_factor)
+{
+#ifdef CONTAINER_STATS
+ mNbContainers++;
+ mUsedRam+=sizeof(Container);
+#endif
+ SetSize(size);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Copy constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Container::Container(const Container& object) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(2.0f)
+{
+#ifdef CONTAINER_STATS
+ mNbContainers++;
+ mUsedRam+=sizeof(Container);
+#endif
+ *this = object;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor. Frees everything and leaves.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Container::~Container()
+{
+ Empty();
+#ifdef CONTAINER_STATS
+ mNbContainers--;
+ mUsedRam-=GetUsedRam();
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Clears the container. All stored values are deleted, and it frees used ram.
+ * \see Reset()
+ * \return Self-Reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Container& Container::Empty()
+{
+#ifdef CONTAINER_STATS
+ mUsedRam-=mMaxNbEntries*sizeof(udword);
+#endif
+ DELETEARRAY(mEntries);
+ mCurNbEntries = mMaxNbEntries = 0;
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resizes the container.
+ * \param needed [in] assume the container can be added at least "needed" values
+ * \return true if success.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Container::Resize(udword needed)
+{
+#ifdef CONTAINER_STATS
+ // Subtract previous amount of bytes
+ mUsedRam-=mMaxNbEntries*sizeof(udword);
+#endif
+
+ // Get more entries
+ mMaxNbEntries = mMaxNbEntries ? udword(float(mMaxNbEntries)*mGrowthFactor) : 2; // Default nb Entries = 2
+ if(mMaxNbEntries<mCurNbEntries + needed) mMaxNbEntries = mCurNbEntries + needed;
+
+ // Get some bytes for new entries
+ udword* NewEntries = new udword[mMaxNbEntries];
+ CHECKALLOC(NewEntries);
+
+#ifdef CONTAINER_STATS
+ // Add current amount of bytes
+ mUsedRam+=mMaxNbEntries*sizeof(udword);
+#endif
+
+ // Copy old data if needed
+ if(mCurNbEntries) CopyMemory(NewEntries, mEntries, mCurNbEntries*sizeof(udword));
+
+ // Delete old data
+ DELETEARRAY(mEntries);
+
+ // Assign new pointer
+ mEntries = NewEntries;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets the initial size of the container. If it already contains something, it's discarded.
+ * \param nb [in] Number of entries
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Container::SetSize(udword nb)
+{
+ // Make sure it's empty
+ Empty();
+
+ // Checkings
+ if(!nb) return false;
+
+ // Initialize for nb entries
+ mMaxNbEntries = nb;
+
+ // Get some bytes for new entries
+ mEntries = new udword[mMaxNbEntries];
+ CHECKALLOC(mEntries);
+
+#ifdef CONTAINER_STATS
+ // Add current amount of bytes
+ mUsedRam+=mMaxNbEntries*sizeof(udword);
+#endif
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the container and get rid of unused bytes.
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Container::Refit()
+{
+#ifdef CONTAINER_STATS
+ // Subtract previous amount of bytes
+ mUsedRam-=mMaxNbEntries*sizeof(udword);
+#endif
+
+ // Get just enough entries
+ mMaxNbEntries = mCurNbEntries;
+ if(!mMaxNbEntries) return false;
+
+ // Get just enough bytes
+ udword* NewEntries = new udword[mMaxNbEntries];
+ CHECKALLOC(NewEntries);
+
+#ifdef CONTAINER_STATS
+ // Add current amount of bytes
+ mUsedRam+=mMaxNbEntries*sizeof(udword);
+#endif
+
+ // Copy old data
+ CopyMemory(NewEntries, mEntries, mCurNbEntries*sizeof(udword));
+
+ // Delete old data
+ DELETEARRAY(mEntries);
+
+ // Assign new pointer
+ mEntries = NewEntries;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks whether the container already contains a given value.
+ * \param entry [in] the value to look for in the container
+ * \param location [out] a possible pointer to store the entry location
+ * \see Add(udword entry)
+ * \see Add(float entry)
+ * \see Empty()
+ * \return true if the value has been found in the container, else false.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Container::Contains(udword entry, udword* location) const
+{
+ // Look for the entry
+ for(udword i=0;i<mCurNbEntries;i++)
+ {
+ if(mEntries[i]==entry)
+ {
+ if(location) *location = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Deletes an entry. If the container contains such an entry, it's removed.
+ * \param entry [in] the value to delete.
+ * \return true if the value has been found in the container, else false.
+ * \warning This method is arbitrary slow (O(n)) and should be used carefully. Insertion order is not preserved.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Container::Delete(udword entry)
+{
+ // Look for the entry
+ for(udword i=0;i<mCurNbEntries;i++)
+ {
+ if(mEntries[i]==entry)
+ {
+ // Entry has been found at index i. The strategy is to copy the last current entry at index i, and decrement the current number of entries.
+ DeleteIndex(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Deletes an entry, preserving the insertion order. If the container contains such an entry, it's removed.
+ * \param entry [in] the value to delete.
+ * \return true if the value has been found in the container, else false.
+ * \warning This method is arbitrary slow (O(n)) and should be used carefully.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Container::DeleteKeepingOrder(udword entry)
+{
+ // Look for the entry
+ for(udword i=0;i<mCurNbEntries;i++)
+ {
+ if(mEntries[i]==entry)
+ {
+ // Entry has been found at index i.
+ // Shift entries to preserve order. You really should use a linked list instead.
+ mCurNbEntries--;
+ for(udword j=i;j<mCurNbEntries;j++)
+ {
+ mEntries[j] = mEntries[j+1];
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the next entry, starting from input one.
+ * \param entry [in/out] On input, the entry to look for. On output, the next entry
+ * \param find_mode [in] wrap/clamp
+ * \return Self-Reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Container& Container::FindNext(udword& entry, FindMode find_mode)
+{
+ udword Location;
+ if(Contains(entry, &Location))
+ {
+ Location++;
+ if(Location==mCurNbEntries) Location = find_mode==FIND_WRAP ? 0 : mCurNbEntries-1;
+ entry = mEntries[Location];
+ }
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the previous entry, starting from input one.
+ * \param entry [in/out] On input, the entry to look for. On output, the previous entry
+ * \param find_mode [in] wrap/clamp
+ * \return Self-Reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Container& Container::FindPrev(udword& entry, FindMode find_mode)
+{
+ udword Location;
+ if(Contains(entry, &Location))
+ {
+ Location--;
+ if(Location==0xffffffff) Location = find_mode==FIND_WRAP ? mCurNbEntries-1 : 0;
+ entry = mEntries[Location];
+ }
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the ram used by the container.
+ * \return the ram used in bytes.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword Container::GetUsedRam() const
+{
+ return sizeof(Container) + mMaxNbEntries * sizeof(udword);
+}
+
+void Container::operator=(const Container& object)
+{
+ SetSize(object.GetNbEntries());
+ CopyMemory(mEntries, object.GetEntries(), mMaxNbEntries*sizeof(udword));
+ mCurNbEntries = mMaxNbEntries;
+}
diff --git a/Opcode/OpcodeLib/Ice/IceContainer.h b/Opcode/OpcodeLib/Ice/IceContainer.h
new file mode 100644
index 0000000..9f06ada
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceContainer.h
@@ -0,0 +1,212 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a simple container class.
+ * \file IceContainer.h
+ * \author Pierre Terdiman
+ * \date February, 5, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICECONTAINER_H__
+#define __ICECONTAINER_H__
+
+ #define CONTAINER_STATS
+
+ enum FindMode
+ {
+ FIND_CLAMP,
+ FIND_WRAP,
+
+ FIND_FORCE_DWORD = 0x7fffffff
+ };
+
+ class ICECORE_API Container
+ {
+ public:
+ // Constructor / Destructor
+ Container();
+ Container(const Container& object);
+ Container(udword size, float growth_factor);
+ ~Container();
+ // Management
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * A O(1) method to add a value in the container. The container is automatically resized if needed.
+ * The method is inline, not the resize. The call overhead happens on resizes only, which is not a problem since the resizing operation
+ * costs a lot more than the call overhead...
+ *
+ * \param entry [in] a udword to store in the container
+ * \see Add(float entry)
+ * \see Empty()
+ * \see Contains(udword entry)
+ * \return Self-Reference
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ Container& Add(udword entry)
+ {
+ // Resize if needed
+ if(mCurNbEntries==mMaxNbEntries) Resize();
+
+ // Add new entry
+ mEntries[mCurNbEntries++] = entry;
+ return *this;
+ }
+
+ inline_ Container& Add(const udword* entries, udword nb)
+ {
+ // Resize if needed
+ if(mCurNbEntries+nb>mMaxNbEntries) Resize(nb);
+
+ // Add new entry
+ CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(udword));
+ mCurNbEntries+=nb;
+ return *this;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * A O(1) method to add a value in the container. The container is automatically resized if needed.
+ * The method is inline, not the resize. The call overhead happens on resizes only, which is not a problem since the resizing operation
+ * costs a lot more than the call overhead...
+ *
+ * \param entry [in] a float to store in the container
+ * \see Add(udword entry)
+ * \see Empty()
+ * \see Contains(udword entry)
+ * \return Self-Reference
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ Container& Add(float entry)
+ {
+ // Resize if needed
+ if(mCurNbEntries==mMaxNbEntries) Resize();
+
+ // Add new entry
+ mEntries[mCurNbEntries++] = IR(entry);
+ return *this;
+ }
+
+ inline_ Container& Add(const float* entries, udword nb)
+ {
+ // Resize if needed
+ if(mCurNbEntries+nb>mMaxNbEntries) Resize(nb);
+
+ // Add new entry
+ CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(float));
+ mCurNbEntries+=nb;
+ return *this;
+ }
+
+ //! Add unique [slow]
+ inline_ Container& AddUnique(udword entry)
+ {
+ if(!Contains(entry)) Add(entry);
+ return *this;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Clears the container. All stored values are deleted, and it frees used ram.
+ * \see Reset()
+ * \return Self-Reference
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ Container& Empty();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Resets the container. Stored values are discarded but the buffer is kept so that further calls don't need resizing again.
+ * That's a kind of temporal coherence.
+ * \see Empty()
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void Reset()
+ {
+ // Avoid the write if possible
+ // ### CMOV
+ if(mCurNbEntries) mCurNbEntries = 0;
+ }
+
+ // HANDLE WITH CARE
+ inline_ void ForceSize(udword size)
+ {
+ mCurNbEntries = size;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Sets the initial size of the container. If it already contains something, it's discarded.
+ * \param nb [in] Number of entries
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool SetSize(udword nb);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Refits the container and get rid of unused bytes.
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool Refit();
+
+ // Checks whether the container already contains a given value.
+ bool Contains(udword entry, udword* location=null) const;
+ // Deletes an entry - doesn't preserve insertion order.
+ bool Delete(udword entry);
+ // Deletes an entry - does preserve insertion order.
+ bool DeleteKeepingOrder(udword entry);
+ //! Deletes the very last entry.
+ inline_ void DeleteLastEntry() { if(mCurNbEntries) mCurNbEntries--; }
+ //! Deletes the entry whose index is given
+ inline_ void DeleteIndex(udword index) { mEntries[index] = mEntries[--mCurNbEntries]; }
+
+ // Helpers
+ Container& FindNext(udword& entry, FindMode find_mode=FIND_CLAMP);
+ Container& FindPrev(udword& entry, FindMode find_mode=FIND_CLAMP);
+ // Data access.
+ inline_ udword GetNbEntries() const { return mCurNbEntries; } //!< Returns the current number of entries.
+ inline_ udword GetEntry(udword i) const { return mEntries[i]; } //!< Returns ith entry
+ inline_ udword* GetEntries() const { return mEntries; } //!< Returns the list of entries.
+
+ inline_ udword GetFirst() const { return mEntries[0]; }
+ inline_ udword GetLast() const { return mEntries[mCurNbEntries-1]; }
+
+ // Growth control
+ inline_ float GetGrowthFactor() const { return mGrowthFactor; } //!< Returns the growth factor
+ inline_ void SetGrowthFactor(float growth) { mGrowthFactor = growth; } //!< Sets the growth factor
+ inline_ bool IsFull() const { return mCurNbEntries==mMaxNbEntries; } //!< Checks the container is full
+ inline_ BOOL IsNotEmpty() const { return mCurNbEntries; } //!< Checks the container is empty
+
+ //! Read-access as an array
+ inline_ udword operator[](udword i) const { ASSERT(i>=0 && i<mCurNbEntries); return mEntries[i]; }
+ //! Write-access as an array
+ inline_ udword& operator[](udword i) { ASSERT(i>=0 && i<mCurNbEntries); return mEntries[i]; }
+
+ // Stats
+ udword GetUsedRam() const;
+
+ //! Operator for "Container A = Container B"
+ void operator = (const Container& object);
+
+#ifdef CONTAINER_STATS
+ inline_ udword GetNbContainers() const { return mNbContainers; }
+ inline_ udword GetTotalBytes() const { return mUsedRam; }
+ private:
+
+ static udword mNbContainers; //!< Number of containers around
+ static udword mUsedRam; //!< Amount of bytes used by containers in the system
+#endif
+ private:
+ // Resizing
+ bool Resize(udword needed=1);
+ // Data
+ udword mMaxNbEntries; //!< Maximum possible number of entries
+ udword mCurNbEntries; //!< Current number of entries
+ udword* mEntries; //!< List of entries
+ float mGrowthFactor; //!< Resize: new number of entries = old number * mGrowthFactor
+ };
+
+#endif // __ICECONTAINER_H__
diff --git a/Opcode/OpcodeLib/Ice/IceFPU.h b/Opcode/OpcodeLib/Ice/IceFPU.h
new file mode 100644
index 0000000..9e57960
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceFPU.h
@@ -0,0 +1,317 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains FPU related code.
+ * \file IceFPU.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEFPU_H__
+#define __ICEFPU_H__
+
+ #define SIGN_BITMASK 0x80000000
+
+ //! Integer representation of a floating-point value.
+ #define IR(x) ((udword&)(x))
+
+ //! Signed integer representation of a floating-point value.
+ #define SIR(x) ((sdword&)(x))
+
+ //! Absolute integer representation of a floating-point value
+ #define AIR(x) (IR(x)&0x7fffffff)
+
+ //! Floating-point representation of an integer value.
+ #define FR(x) ((float&)(x))
+
+ //! Integer-based comparison of a floating point value.
+ //! Don't use it blindly, it can be faster or slower than the FPU comparison, depends on the context.
+ #define IS_NEGATIVE_FLOAT(x) (IR(x)&0x80000000)
+
+ //! Fast fabs for floating-point values. It just clears the sign bit.
+ //! Don't use it blindy, it can be faster or slower than the FPU comparison, depends on the context.
+ inline_ float FastFabs(float x)
+ {
+ udword FloatBits = IR(x)&0x7fffffff;
+ return FR(FloatBits);
+ }
+
+ //! Fast square root for floating-point values.
+ inline_ float FastSqrt(float square)
+ {
+ float retval;
+
+ __asm {
+ mov eax, square
+ sub eax, 0x3F800000
+ sar eax, 1
+ add eax, 0x3F800000
+ mov [retval], eax
+ }
+ return retval;
+ }
+
+ //! Saturates positive to zero.
+ inline_ float fsat(float f)
+ {
+ udword y = (udword&)f & ~((sdword&)f >>31);
+ return (float&)y;
+ }
+
+ //! Computes 1.0f / sqrtf(x).
+ inline_ float frsqrt(float f)
+ {
+ float x = f * 0.5f;
+ udword y = 0x5f3759df - ((udword&)f >> 1);
+ // Iteration...
+ (float&)y = (float&)y * ( 1.5f - ( x * (float&)y * (float&)y ) );
+ // Result
+ return (float&)y;
+ }
+
+ //! Computes 1.0f / sqrtf(x). Comes from NVIDIA.
+ inline_ float InvSqrt(const float& x)
+ {
+ udword tmp = (udword(IEEE_1_0 << 1) + IEEE_1_0 - *(udword*)&x) >> 1;
+ float y = *(float*)&tmp;
+ return y * (1.47f - 0.47f * x * y * y);
+ }
+
+ //! Computes 1.0f / sqrtf(x). Comes from Quake3. Looks like the first one I had above.
+ //! See http://www.magic-software.com/3DGEDInvSqrt.html
+ inline_ float RSqrt(float number)
+ {
+ long i;
+ float x2, y;
+ const float threehalfs = 1.5f;
+
+ x2 = number * 0.5f;
+ y = number;
+ i = * (long *) &y;
+ i = 0x5f3759df - (i >> 1);
+ y = * (float *) &i;
+ y = y * (threehalfs - (x2 * y * y));
+
+ return y;
+ }
+
+ //! TO BE DOCUMENTED
+ inline_ float fsqrt(float f)
+ {
+ udword y = ( ( (sdword&)f - 0x3f800000 ) >> 1 ) + 0x3f800000;
+ // Iteration...?
+ // (float&)y = (3.0f - ((float&)y * (float&)y) / f) * (float&)y * 0.5f;
+ // Result
+ return (float&)y;
+ }
+
+ //! Returns the float ranged espilon value.
+ inline_ float fepsilon(float f)
+ {
+ udword b = (udword&)f & 0xff800000;
+ udword a = b | 0x00000001;
+ (float&)a -= (float&)b;
+ // Result
+ return (float&)a;
+ }
+
+ //! Is the float valid ?
+ inline_ bool IsNAN(float value) { return (IR(value)&0x7f800000) == 0x7f800000; }
+ inline_ bool IsIndeterminate(float value) { return IR(value) == 0xffc00000; }
+ inline_ bool IsPlusInf(float value) { return IR(value) == 0x7f800000; }
+ inline_ bool IsMinusInf(float value) { return IR(value) == 0xff800000; }
+
+ inline_ bool IsValidFloat(float value)
+ {
+ if(IsNAN(value)) return false;
+ if(IsIndeterminate(value)) return false;
+ if(IsPlusInf(value)) return false;
+ if(IsMinusInf(value)) return false;
+ return true;
+ }
+
+ #define CHECK_VALID_FLOAT(x) ASSERT(IsValidFloat(x));
+
+/*
+ //! FPU precision setting function.
+ inline_ void SetFPU()
+ {
+ // This function evaluates whether the floating-point
+ // control word is set to single precision/round to nearest/
+ // exceptions disabled. If these conditions don't hold, the
+ // function changes the control word to set them and returns
+ // TRUE, putting the old control word value in the passback
+ // location pointed to by pwOldCW.
+ {
+ uword wTemp, wSave;
+
+ __asm fstcw wSave
+ if (wSave & 0x300 || // Not single mode
+ 0x3f != (wSave & 0x3f) || // Exceptions enabled
+ wSave & 0xC00) // Not round to nearest mode
+ {
+ __asm
+ {
+ mov ax, wSave
+ and ax, not 300h ;; single mode
+ or ax, 3fh ;; disable all exceptions
+ and ax, not 0xC00 ;; round to nearest mode
+ mov wTemp, ax
+ fldcw wTemp
+ }
+ }
+ }
+ }
+*/
+ //! This function computes the slowest possible floating-point value (you can also directly use FLT_EPSILON)
+ inline_ float ComputeFloatEpsilon()
+ {
+ float f = 1.0f;
+ ((udword&)f)^=1;
+ return f - 1.0f; // You can check it's the same as FLT_EPSILON
+ }
+
+ inline_ bool IsFloatZero(float x, float epsilon=1e-6f)
+ {
+ return x*x < epsilon;
+ }
+
+ #define FCOMI_ST0 _asm _emit 0xdb _asm _emit 0xf0
+ #define FCOMIP_ST0 _asm _emit 0xdf _asm _emit 0xf0
+ #define FCMOVB_ST0 _asm _emit 0xda _asm _emit 0xc0
+ #define FCMOVNB_ST0 _asm _emit 0xdb _asm _emit 0xc0
+
+ #define FCOMI_ST1 _asm _emit 0xdb _asm _emit 0xf1
+ #define FCOMIP_ST1 _asm _emit 0xdf _asm _emit 0xf1
+ #define FCMOVB_ST1 _asm _emit 0xda _asm _emit 0xc1
+ #define FCMOVNB_ST1 _asm _emit 0xdb _asm _emit 0xc1
+
+ #define FCOMI_ST2 _asm _emit 0xdb _asm _emit 0xf2
+ #define FCOMIP_ST2 _asm _emit 0xdf _asm _emit 0xf2
+ #define FCMOVB_ST2 _asm _emit 0xda _asm _emit 0xc2
+ #define FCMOVNB_ST2 _asm _emit 0xdb _asm _emit 0xc2
+
+ #define FCOMI_ST3 _asm _emit 0xdb _asm _emit 0xf3
+ #define FCOMIP_ST3 _asm _emit 0xdf _asm _emit 0xf3
+ #define FCMOVB_ST3 _asm _emit 0xda _asm _emit 0xc3
+ #define FCMOVNB_ST3 _asm _emit 0xdb _asm _emit 0xc3
+
+ #define FCOMI_ST4 _asm _emit 0xdb _asm _emit 0xf4
+ #define FCOMIP_ST4 _asm _emit 0xdf _asm _emit 0xf4
+ #define FCMOVB_ST4 _asm _emit 0xda _asm _emit 0xc4
+ #define FCMOVNB_ST4 _asm _emit 0xdb _asm _emit 0xc4
+
+ #define FCOMI_ST5 _asm _emit 0xdb _asm _emit 0xf5
+ #define FCOMIP_ST5 _asm _emit 0xdf _asm _emit 0xf5
+ #define FCMOVB_ST5 _asm _emit 0xda _asm _emit 0xc5
+ #define FCMOVNB_ST5 _asm _emit 0xdb _asm _emit 0xc5
+
+ #define FCOMI_ST6 _asm _emit 0xdb _asm _emit 0xf6
+ #define FCOMIP_ST6 _asm _emit 0xdf _asm _emit 0xf6
+ #define FCMOVB_ST6 _asm _emit 0xda _asm _emit 0xc6
+ #define FCMOVNB_ST6 _asm _emit 0xdb _asm _emit 0xc6
+
+ #define FCOMI_ST7 _asm _emit 0xdb _asm _emit 0xf7
+ #define FCOMIP_ST7 _asm _emit 0xdf _asm _emit 0xf7
+ #define FCMOVB_ST7 _asm _emit 0xda _asm _emit 0xc7
+ #define FCMOVNB_ST7 _asm _emit 0xdb _asm _emit 0xc7
+
+ //! A global function to find MAX(a,b) using FCOMI/FCMOV
+ inline_ float FCMax2(float a, float b)
+ {
+ float Res;
+ _asm fld [a]
+ _asm fld [b]
+ FCOMI_ST1
+ FCMOVB_ST1
+ _asm fstp [Res]
+ _asm fcomp
+ return Res;
+ }
+
+ //! A global function to find MIN(a,b) using FCOMI/FCMOV
+ inline_ float FCMin2(float a, float b)
+ {
+ float Res;
+ _asm fld [a]
+ _asm fld [b]
+ FCOMI_ST1
+ FCMOVNB_ST1
+ _asm fstp [Res]
+ _asm fcomp
+ return Res;
+ }
+
+ //! A global function to find MAX(a,b,c) using FCOMI/FCMOV
+ inline_ float FCMax3(float a, float b, float c)
+ {
+ float Res;
+ _asm fld [a]
+ _asm fld [b]
+ _asm fld [c]
+ FCOMI_ST1
+ FCMOVB_ST1
+ FCOMI_ST2
+ FCMOVB_ST2
+ _asm fstp [Res]
+ _asm fcompp
+ return Res;
+ }
+
+ //! A global function to find MIN(a,b,c) using FCOMI/FCMOV
+ inline_ float FCMin3(float a, float b, float c)
+ {
+ float Res;
+ _asm fld [a]
+ _asm fld [b]
+ _asm fld [c]
+ FCOMI_ST1
+ FCMOVNB_ST1
+ FCOMI_ST2
+ FCMOVNB_ST2
+ _asm fstp [Res]
+ _asm fcompp
+ return Res;
+ }
+
+ inline_ int ConvertToSortable(float f)
+ {
+ int& Fi = (int&)f;
+ int Fmask = (Fi>>31);
+ Fi ^= Fmask;
+ Fmask &= ~(1<<31);
+ Fi -= Fmask;
+ return Fi;
+ }
+
+ enum FPUMode
+ {
+ FPU_FLOOR = 0,
+ FPU_CEIL = 1,
+ FPU_BEST = 2,
+
+ FPU_FORCE_DWORD = 0x7fffffff
+ };
+
+ FUNCTION ICECORE_API FPUMode GetFPUMode();
+ FUNCTION ICECORE_API void SaveFPU();
+ FUNCTION ICECORE_API void RestoreFPU();
+ FUNCTION ICECORE_API void SetFPUFloorMode();
+ FUNCTION ICECORE_API void SetFPUCeilMode();
+ FUNCTION ICECORE_API void SetFPUBestMode();
+
+ FUNCTION ICECORE_API void SetFPUPrecision24();
+ FUNCTION ICECORE_API void SetFPUPrecision53();
+ FUNCTION ICECORE_API void SetFPUPrecision64();
+ FUNCTION ICECORE_API void SetFPURoundingChop();
+ FUNCTION ICECORE_API void SetFPURoundingUp();
+ FUNCTION ICECORE_API void SetFPURoundingDown();
+ FUNCTION ICECORE_API void SetFPURoundingNear();
+
+ FUNCTION ICECORE_API int intChop(const float& f);
+ FUNCTION ICECORE_API int intFloor(const float& f);
+ FUNCTION ICECORE_API int intCeil(const float& f);
+
+#endif // __ICEFPU_H__
diff --git a/Opcode/OpcodeLib/Ice/IceHPoint.cpp b/Opcode/OpcodeLib/Ice/IceHPoint.cpp
new file mode 100644
index 0000000..2074543
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceHPoint.cpp
@@ -0,0 +1,70 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for homogeneous points.
+ * \file IceHPoint.cpp
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Homogeneous point.
+ *
+ * Use it:
+ * - for clipping in homogeneous space (standard way)
+ * - to differentiate between points (w=1) and vectors (w=0).
+ * - in some cases you can also use it instead of IcePoint for padding reasons.
+ *
+ * \class HPoint
+ * \author Pierre Terdiman
+ * \version 1.0
+ * \warning No cross-product in 4D.
+ * \warning HPoint *= Matrix3x3 doesn't exist, the matrix is first casted to a 4x4
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// IcePoint Mul = HPoint * Matrix3x3;
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+IcePoint HPoint::operator*(const Matrix3x3& mat) const
+{
+ return IcePoint(
+ x * mat.m[0][0] + y * mat.m[1][0] + z * mat.m[2][0],
+ x * mat.m[0][1] + y * mat.m[1][1] + z * mat.m[2][1],
+ x * mat.m[0][2] + y * mat.m[1][2] + z * mat.m[2][2] );
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// HPoint Mul = HPoint * Matrix4x4;
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HPoint HPoint::operator*(const Matrix4x4& mat) const
+{
+ return HPoint(
+ x * mat.m[0][0] + y * mat.m[1][0] + z * mat.m[2][0] + w * mat.m[3][0],
+ x * mat.m[0][1] + y * mat.m[1][1] + z * mat.m[2][1] + w * mat.m[3][1],
+ x * mat.m[0][2] + y * mat.m[1][2] + z * mat.m[2][2] + w * mat.m[3][2],
+ x * mat.m[0][3] + y * mat.m[1][3] + z * mat.m[2][3] + w * mat.m[3][3]);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// HPoint *= Matrix4x4
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HPoint& HPoint::operator*=(const Matrix4x4& mat)
+{
+ float xp = x * mat.m[0][0] + y * mat.m[1][0] + z * mat.m[2][0] + w * mat.m[3][0];
+ float yp = x * mat.m[0][1] + y * mat.m[1][1] + z * mat.m[2][1] + w * mat.m[3][1];
+ float zp = x * mat.m[0][2] + y * mat.m[1][2] + z * mat.m[2][2] + w * mat.m[3][2];
+ float wp = x * mat.m[0][3] + y * mat.m[1][3] + z * mat.m[2][3] + w * mat.m[3][3];
+
+ x = xp; y = yp; z = zp; w = wp;
+
+ return *this;
+}
+
diff --git a/Opcode/OpcodeLib/Ice/IceHPoint.h b/Opcode/OpcodeLib/Ice/IceHPoint.h
new file mode 100644
index 0000000..461c39b
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceHPoint.h
@@ -0,0 +1,157 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for homogeneous points.
+ * \file IceHPoint.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEHPOINT_H__
+#define __ICEHPOINT_H__
+
+ class ICEMATHS_API HPoint : public IcePoint
+ {
+ public:
+
+ //! Empty constructor
+ inline_ HPoint() {}
+ //! Constructor from floats
+ inline_ HPoint(float _x, float _y, float _z, float _w=0.0f) : IcePoint(_x, _y, _z), w(_w) {}
+ //! Constructor from array
+ inline_ HPoint(const float f[4]) : IcePoint(f), w(f[3]) {}
+ //! Constructor from a IcePoint
+ inline_ HPoint(const IcePoint& p, float _w=0.0f) : IcePoint(p), w(_w) {}
+ //! Destructor
+ inline_ ~HPoint() {}
+
+ //! Clear the point
+ inline_ HPoint& Zero() { x = y = z = w = 0.0f; return *this; }
+
+ //! Assignment from values
+ inline_ HPoint& Set(float _x, float _y, float _z, float _w ) { x = _x; y = _y; z = _z; w = _w; return *this; }
+ //! Assignment from array
+ inline_ HPoint& Set(const float f[4]) { x = f[_X]; y = f[_Y]; z = f[_Z]; w = f[_W]; return *this; }
+ //! Assignment from another h-point
+ inline_ HPoint& Set(const HPoint& src) { x = src.x; y = src.y; z = src.z; w = src.w; return *this; }
+
+ //! Add a vector
+ inline_ HPoint& Add(float _x, float _y, float _z, float _w ) { x += _x; y += _y; z += _z; w += _w; return *this; }
+ //! Add a vector
+ inline_ HPoint& Add(const float f[4]) { x += f[_X]; y += f[_Y]; z += f[_Z]; w += f[_W]; return *this; }
+
+ //! Subtract a vector
+ inline_ HPoint& Sub(float _x, float _y, float _z, float _w ) { x -= _x; y -= _y; z -= _z; w -= _w; return *this; }
+ //! Subtract a vector
+ inline_ HPoint& Sub(const float f[4]) { x -= f[_X]; y -= f[_Y]; z -= f[_Z]; w -= f[_W]; return *this; }
+
+ //! Multiplies by a scalar
+ inline_ HPoint& Mul(float s) { x *= s; y *= s; z *= s; w *= s; return *this; }
+
+ //! Returns MIN(x, y, z, w);
+ float Min() const { return MIN(x, MIN(y, MIN(z, w))); }
+ //! Returns MAX(x, y, z, w);
+ float Max() const { return MAX(x, MAX(y, MAX(z, w))); }
+ //! Sets each element to be componentwise minimum
+ HPoint& Min(const HPoint& p) { x = MIN(x, p.x); y = MIN(y, p.y); z = MIN(z, p.z); w = MIN(w, p.w); return *this; }
+ //! Sets each element to be componentwise maximum
+ HPoint& Max(const HPoint& p) { x = MAX(x, p.x); y = MAX(y, p.y); z = MAX(z, p.z); w = MAX(w, p.w); return *this; }
+
+ //! Computes square magnitude
+ inline_ float SquareMagnitude() const { return x*x + y*y + z*z + w*w; }
+ //! Computes magnitude
+ inline_ float Magnitude() const { return sqrtf(x*x + y*y + z*z + w*w); }
+
+ //! Normalize the vector
+ inline_ HPoint& Normalize()
+ {
+ float M = Magnitude();
+ if(M)
+ {
+ M = 1.0f / M;
+ x *= M;
+ y *= M;
+ z *= M;
+ w *= M;
+ }
+ return *this;
+ }
+
+ // Arithmetic operators
+ //! Operator for HPoint Negate = - HPoint;
+ inline_ HPoint operator-() const { return HPoint(-x, -y, -z, -w); }
+
+ //! Operator for HPoint Plus = HPoint + HPoint;
+ inline_ HPoint operator+(const HPoint& p) const { return HPoint(x + p.x, y + p.y, z + p.z, w + p.w); }
+ //! Operator for HPoint Minus = HPoint - HPoint;
+ inline_ HPoint operator-(const HPoint& p) const { return HPoint(x - p.x, y - p.y, z - p.z, w - p.w); }
+
+ //! Operator for HPoint Mul = HPoint * HPoint;
+ inline_ HPoint operator*(const HPoint& p) const { return HPoint(x * p.x, y * p.y, z * p.z, w * p.w); }
+ //! Operator for HPoint Scale = HPoint * float;
+ inline_ HPoint operator*(float s) const { return HPoint(x * s, y * s, z * s, w * s); }
+ //! Operator for HPoint Scale = float * HPoint;
+ inline_ friend HPoint operator*(float s, const HPoint& p) { return HPoint(s * p.x, s * p.y, s * p.z, s * p.w); }
+
+ //! Operator for HPoint Div = HPoint / HPoint;
+ inline_ HPoint operator/(const HPoint& p) const { return HPoint(x / p.x, y / p.y, z / p.z, w / p.w); }
+ //! Operator for HPoint Scale = HPoint / float;
+ inline_ HPoint operator/(float s) const { s = 1.0f / s; return HPoint(x * s, y * s, z * s, w * s); }
+ //! Operator for HPoint Scale = float / HPoint;
+ inline_ friend HPoint operator/(float s, const HPoint& p) { return HPoint(s / p.x, s / p.y, s / p.z, s / p.w); }
+
+ //! Operator for float DotProd = HPoint | HPoint;
+ inline_ float operator|(const HPoint& p) const { return x*p.x + y*p.y + z*p.z + w*p.w; }
+ // No cross-product in 4D
+
+ //! Operator for HPoint += HPoint;
+ inline_ HPoint& operator+=(const HPoint& p) { x += p.x; y += p.y; z += p.z; w += p.w; return *this; }
+ //! Operator for HPoint += float;
+ inline_ HPoint& operator+=(float s) { x += s; y += s; z += s; w += s; return *this; }
+
+ //! Operator for HPoint -= HPoint;
+ inline_ HPoint& operator-=(const HPoint& p) { x -= p.x; y -= p.y; z -= p.z; w -= p.w; return *this; }
+ //! Operator for HPoint -= float;
+ inline_ HPoint& operator-=(float s) { x -= s; y -= s; z -= s; w -= s; return *this; }
+
+ //! Operator for HPoint *= HPoint;
+ inline_ HPoint& operator*=(const HPoint& p) { x *= p.x; y *= p.y; z *= p.z; w *= p.w; return *this; }
+ //! Operator for HPoint *= float;
+ inline_ HPoint& operator*=(float s) { x*=s; y*=s; z*=s; w*=s; return *this; }
+
+ //! Operator for HPoint /= HPoint;
+ inline_ HPoint& operator/=(const HPoint& p) { x /= p.x; y /= p.y; z /= p.z; w /= p.w; return *this; }
+ //! Operator for HPoint /= float;
+ inline_ HPoint& operator/=(float s) { s = 1.0f / s; x*=s; y*=s; z*=s; w*=s; return *this; }
+
+ // Arithmetic operators
+
+ //! Operator for IcePoint Mul = HPoint * Matrix3x3;
+ IcePoint operator*(const Matrix3x3& mat) const;
+ //! Operator for HPoint Mul = HPoint * Matrix4x4;
+ HPoint operator*(const Matrix4x4& mat) const;
+
+ // HPoint *= Matrix3x3 doesn't exist, the matrix is first casted to a 4x4
+ //! Operator for HPoint *= Matrix4x4
+ HPoint& operator*=(const Matrix4x4& mat);
+
+ // Logical operators
+
+ //! Operator for "if(HPoint==HPoint)"
+ inline_ bool operator==(const HPoint& p) const { return ( (x==p.x)&&(y==p.y)&&(z==p.z)&&(w==p.w)); }
+ //! Operator for "if(HPoint!=HPoint)"
+ inline_ bool operator!=(const HPoint& p) const { return ( (x!=p.x)||(y!=p.y)||(z!=p.z)||(w!=p.w)); }
+
+ // Cast operators
+
+ //! Cast a HPoint to a IcePoint. w is discarded.
+ inline_ operator IcePoint() const { return IcePoint(x, y, z); }
+
+ public:
+ float w;
+ };
+
+#endif // __ICEHPOINT_H__
+
diff --git a/Opcode/OpcodeLib/Ice/IceIndexedTriangle.cpp b/Opcode/OpcodeLib/Ice/IceIndexedTriangle.cpp
new file mode 100644
index 0000000..ea32362
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceIndexedTriangle.cpp
@@ -0,0 +1,548 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a handy indexed triangle class.
+ * \file IceIndexedTriangle.cpp
+ * \author Pierre Terdiman
+ * \date January, 17, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains an indexed triangle class.
+ *
+ * \class Triangle
+ * \author Pierre Terdiman
+ * \version 1.0
+ * \date 08.15.98
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Flips the winding order.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void IndexedTriangle::Flip()
+{
+ Swap(mVRef[1], mVRef[2]);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle area.
+ * \param verts [in] the list of indexed vertices
+ * \return the area
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float IndexedTriangle::Area(const IcePoint* verts) const
+{
+ if(!verts) return 0.0f;
+ const IcePoint& p0 = verts[0];
+ const IcePoint& p1 = verts[1];
+ const IcePoint& p2 = verts[2];
+ return ((p0-p1)^(p0-p2)).Magnitude() * 0.5f;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle perimeter.
+ * \param verts [in] the list of indexed vertices
+ * \return the perimeter
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float IndexedTriangle::Perimeter(const IcePoint* verts) const
+{
+ if(!verts) return 0.0f;
+ const IcePoint& p0 = verts[0];
+ const IcePoint& p1 = verts[1];
+ const IcePoint& p2 = verts[2];
+ return p0.Distance(p1)
+ + p0.Distance(p2)
+ + p1.Distance(p2);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle compacity.
+ * \param verts [in] the list of indexed vertices
+ * \return the compacity
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float IndexedTriangle::Compacity(const IcePoint* verts) const
+{
+ if(!verts) return 0.0f;
+ float P = Perimeter(verts);
+ if(P==0.0f) return 0.0f;
+ return (4.0f*PI*Area(verts)/(P*P));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle normal.
+ * \param verts [in] the list of indexed vertices
+ * \param normal [out] the computed normal
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void IndexedTriangle::Normal(const IcePoint* verts, IcePoint& normal) const
+{
+ if(!verts) return;
+
+ const IcePoint& p0 = verts[mVRef[0]];
+ const IcePoint& p1 = verts[mVRef[1]];
+ const IcePoint& p2 = verts[mVRef[2]];
+ normal = ((p2-p1)^(p0-p1)).Normalize();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle denormalized normal.
+ * \param verts [in] the list of indexed vertices
+ * \param normal [out] the computed normal
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void IndexedTriangle::DenormalizedNormal(const IcePoint* verts, IcePoint& normal) const
+{
+ if(!verts) return;
+
+ const IcePoint& p0 = verts[mVRef[0]];
+ const IcePoint& p1 = verts[mVRef[1]];
+ const IcePoint& p2 = verts[mVRef[2]];
+ normal = ((p2-p1)^(p0-p1));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle center.
+ * \param verts [in] the list of indexed vertices
+ * \param center [out] the computed center
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void IndexedTriangle::Center(const IcePoint* verts, IcePoint& center) const
+{
+ if(!verts) return;
+
+ const IcePoint& p0 = verts[mVRef[0]];
+ const IcePoint& p1 = verts[mVRef[1]];
+ const IcePoint& p2 = verts[mVRef[2]];
+ center = (p0+p1+p2)*INV3;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the centered normal
+ * \param verts [in] the list of indexed vertices
+ * \param normal [out] the computed centered normal
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void IndexedTriangle::CenteredNormal(const IcePoint* verts, IcePoint& normal) const
+{
+ if(!verts) return;
+
+ const IcePoint& p0 = verts[mVRef[0]];
+ const IcePoint& p1 = verts[mVRef[1]];
+ const IcePoint& p2 = verts[mVRef[2]];
+ IcePoint Center = (p0+p1+p2)*INV3;
+ normal = Center + ((p2-p1)^(p0-p1)).Normalize();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes a random point within the triangle.
+ * \param verts [in] the list of indexed vertices
+ * \param normal [out] the computed centered normal
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void IndexedTriangle::RandomPoint(const IcePoint* verts, IcePoint& random) const
+{
+ if(!verts) return;
+
+ // Random barycentric coords
+ float Alpha = UnitRandomFloat();
+ float Beta = UnitRandomFloat();
+ float Gamma = UnitRandomFloat();
+ float OneOverTotal = 1.0f / (Alpha + Beta + Gamma);
+ Alpha *= OneOverTotal;
+ Beta *= OneOverTotal;
+ Gamma *= OneOverTotal;
+
+ const IcePoint& p0 = verts[mVRef[0]];
+ const IcePoint& p1 = verts[mVRef[1]];
+ const IcePoint& p2 = verts[mVRef[2]];
+ random = Alpha*p0 + Beta*p1 + Gamma*p2;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes backface culling.
+ * \param verts [in] the list of indexed vertices
+ * \param source [in] source point (in local space) from which culling must be computed
+ * \return true if the triangle is visible from the source point
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool IndexedTriangle::IsVisible(const IcePoint* verts, const IcePoint& source) const
+{
+ // Checkings
+ if(!verts) return false;
+
+ const IcePoint& p0 = verts[mVRef[0]];
+ const IcePoint& p1 = verts[mVRef[1]];
+ const IcePoint& p2 = verts[mVRef[2]];
+
+ // Compute denormalized normal
+ IcePoint Normal = (p2 - p1)^(p0 - p1);
+
+ // Backface culling
+ return (Normal | source) >= 0.0f;
+
+// Same as:
+// IcePlane PL(verts[mVRef[0]], verts[mVRef[1]], verts[mVRef[2]]);
+// return PL.Distance(source) > PL.d;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes backface culling.
+ * \param verts [in] the list of indexed vertices
+ * \param source [in] source point (in local space) from which culling must be computed
+ * \return true if the triangle is visible from the source point
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool IndexedTriangle::BackfaceCulling(const IcePoint* verts, const IcePoint& source) const
+{
+ // Checkings
+ if(!verts) return false;
+
+ const IcePoint& p0 = verts[mVRef[0]];
+ const IcePoint& p1 = verts[mVRef[1]];
+ const IcePoint& p2 = verts[mVRef[2]];
+
+ // Compute base
+// IcePoint Base = (p0 + p1 + p2)*INV3;
+
+ // Compute denormalized normal
+ IcePoint Normal = (p2 - p1)^(p0 - p1);
+
+ // Backface culling
+// return (Normal | (source - Base)) >= 0.0f;
+ return (Normal | (source - p0)) >= 0.0f;
+
+// Same as: (but a bit faster)
+// IcePlane PL(verts[mVRef[0]], verts[mVRef[1]], verts[mVRef[2]]);
+// return PL.Distance(source)>0.0f;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the occlusion potential of the triangle.
+ * \param verts [in] the list of indexed vertices
+ * \param source [in] source point (in local space) from which occlusion potential must be computed
+ * \return the occlusion potential
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float IndexedTriangle::ComputeOcclusionPotential(const IcePoint* verts, const IcePoint& view) const
+{
+ if(!verts) return 0.0f;
+ // Occlusion potential: -(A * (N|V) / d^2)
+ // A = polygon area
+ // N = polygon normal
+ // V = view vector
+ // d = distance viewpoint-center of polygon
+
+ float A = Area(verts);
+ IcePoint N; Normal(verts, N);
+ IcePoint C; Center(verts, C);
+ float d = view.Distance(C);
+ return -(A*(N|view))/(d*d);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Replaces a vertex reference with another one.
+ * \param oldref [in] the vertex reference to replace
+ * \param newref [in] the new vertex reference
+ * \return true if success, else false if the input vertex reference doesn't belong to the triangle
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool IndexedTriangle::ReplaceVertex(udword oldref, udword newref)
+{
+ if(mVRef[0]==oldref) { mVRef[0] = newref; return true; }
+ else if(mVRef[1]==oldref) { mVRef[1] = newref; return true; }
+ else if(mVRef[2]==oldref) { mVRef[2] = newref; return true; }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks whether the triangle is degenerate or not. A degenerate triangle has two common vertex references. This is a zero-area triangle.
+ * \return true if the triangle is degenerate
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool IndexedTriangle::IsDegenerate() const
+{
+ if(mVRef[0]==mVRef[1]) return true;
+ if(mVRef[1]==mVRef[2]) return true;
+ if(mVRef[2]==mVRef[0]) return true;
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks whether the input vertex reference belongs to the triangle or not.
+ * \param ref [in] the vertex reference to look for
+ * \return true if the triangle contains the vertex reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool IndexedTriangle::HasVertex(udword ref) const
+{
+ if(mVRef[0]==ref) return true;
+ if(mVRef[1]==ref) return true;
+ if(mVRef[2]==ref) return true;
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks whether the input vertex reference belongs to the triangle or not.
+ * \param ref [in] the vertex reference to look for
+ * \param index [out] the corresponding index in the triangle
+ * \return true if the triangle contains the vertex reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool IndexedTriangle::HasVertex(udword ref, udword* index) const
+{
+ if(mVRef[0]==ref) { *index = 0; return true; }
+ if(mVRef[1]==ref) { *index = 1; return true; }
+ if(mVRef[2]==ref) { *index = 2; return true; }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Finds an edge in a tri, given two vertex references.
+ * \param vref0 [in] the edge's first vertex reference
+ * \param vref1 [in] the edge's second vertex reference
+ * \return the edge number between 0 and 2, or 0xff if input refs are wrong.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ubyte IndexedTriangle::FindEdge(udword vref0, udword vref1) const
+{
+ if(mVRef[0]==vref0 && mVRef[1]==vref1) return 0;
+ else if(mVRef[0]==vref1 && mVRef[1]==vref0) return 0;
+ else if(mVRef[0]==vref0 && mVRef[2]==vref1) return 1;
+ else if(mVRef[0]==vref1 && mVRef[2]==vref0) return 1;
+ else if(mVRef[1]==vref0 && mVRef[2]==vref1) return 2;
+ else if(mVRef[1]==vref1 && mVRef[2]==vref0) return 2;
+ return 0xff;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the last reference given the first two.
+ * \param vref0 [in] the first vertex reference
+ * \param vref1 [in] the second vertex reference
+ * \return the last reference, or INVALID_ID if input refs are wrong.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword IndexedTriangle::OppositeVertex(udword vref0, udword vref1) const
+{
+ if(mVRef[0]==vref0 && mVRef[1]==vref1) return mVRef[2];
+ else if(mVRef[0]==vref1 && mVRef[1]==vref0) return mVRef[2];
+ else if(mVRef[0]==vref0 && mVRef[2]==vref1) return mVRef[1];
+ else if(mVRef[0]==vref1 && mVRef[2]==vref0) return mVRef[1];
+ else if(mVRef[1]==vref0 && mVRef[2]==vref1) return mVRef[0];
+ else if(mVRef[1]==vref1 && mVRef[2]==vref0) return mVRef[0];
+ return INVALID_ID;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the three sorted vertex references according to an edge number.
+ * edgenb = 0 => edge 0-1, returns references 0, 1, 2
+ * edgenb = 1 => edge 0-2, returns references 0, 2, 1
+ * edgenb = 2 => edge 1-2, returns references 1, 2, 0
+ *
+ * \param edgenb [in] the edge number, 0, 1 or 2
+ * \param vref0 [out] the returned first vertex reference
+ * \param vref1 [out] the returned second vertex reference
+ * \param vref2 [out] the returned third vertex reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void IndexedTriangle::GetVRefs(ubyte edgenb, udword& vref0, udword& vref1, udword& vref2) const
+{
+ if(edgenb==0)
+ {
+ vref0 = mVRef[0];
+ vref1 = mVRef[1];
+ vref2 = mVRef[2];
+ }
+ else if(edgenb==1)
+ {
+ vref0 = mVRef[0];
+ vref1 = mVRef[2];
+ vref2 = mVRef[1];
+ }
+ else if(edgenb==2)
+ {
+ vref0 = mVRef[1];
+ vref1 = mVRef[2];
+ vref2 = mVRef[0];
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle's smallest edge length.
+ * \param verts [in] the list of indexed vertices
+ * \return the smallest edge length
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float IndexedTriangle::MinEdgeLength(const IcePoint* verts) const
+{
+ if(!verts) return 0.0f;
+
+ float Min = MAX_FLOAT;
+ float Length01 = verts[0].Distance(verts[1]);
+ float Length02 = verts[0].Distance(verts[2]);
+ float Length12 = verts[1].Distance(verts[2]);
+ if(Length01 < Min) Min = Length01;
+ if(Length02 < Min) Min = Length02;
+ if(Length12 < Min) Min = Length12;
+ return Min;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle's largest edge length.
+ * \param verts [in] the list of indexed vertices
+ * \return the largest edge length
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float IndexedTriangle::MaxEdgeLength(const IcePoint* verts) const
+{
+ if(!verts) return 0.0f;
+
+ float Max = MIN_FLOAT;
+ float Length01 = verts[0].Distance(verts[1]);
+ float Length02 = verts[0].Distance(verts[2]);
+ float Length12 = verts[1].Distance(verts[2]);
+ if(Length01 > Max) Max = Length01;
+ if(Length02 > Max) Max = Length02;
+ if(Length12 > Max) Max = Length12;
+ return Max;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes a point on the triangle according to the stabbing information.
+ * \param verts [in] the list of indexed vertices
+ * \param u,v [in] point's barycentric coordinates
+ * \param pt [out] point on triangle
+ * \param nearvtx [out] index of nearest vertex
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void IndexedTriangle::ComputePoint(const IcePoint* verts, float u, float v, IcePoint& pt, udword* nearvtx) const
+{
+ // Checkings
+ if(!verts) return;
+
+ // Get face in local or global space
+ const IcePoint& p0 = verts[mVRef[0]];
+ const IcePoint& p1 = verts[mVRef[1]];
+ const IcePoint& p2 = verts[mVRef[2]];
+
+ // Compute point coordinates
+ pt = (1.0f - u - v)*p0 + u*p1 + v*p2;
+
+ // Compute nearest vertex if needed
+ if(nearvtx)
+ {
+ // Compute distance vector
+ IcePoint d(p0.SquareDistance(pt), // Distance^2 from vertex 0 to point on the face
+ p1.SquareDistance(pt), // Distance^2 from vertex 1 to point on the face
+ p2.SquareDistance(pt)); // Distance^2 from vertex 2 to point on the face
+
+ // Get smallest distance
+ *nearvtx = mVRef[d.SmallestAxis()];
+ }
+}
+
+ //**************************************
+ // Angle between two vectors (in radians)
+ // we use this formula
+ // uv = |u||v| cos(u,v)
+ // u ^ v = w
+ // |w| = |u||v| |sin(u,v)|
+ //**************************************
+ float Angle(const IcePoint& u, const IcePoint& v)
+ {
+ float NormU = u.Magnitude(); // |u|
+ float NormV = v.Magnitude(); // |v|
+ float Product = NormU*NormV; // |u||v|
+ if(Product==0.0f) return 0.0f;
+ float OneOverProduct = 1.0f / Product;
+
+ // Cosinus
+ float Cosinus = (u|v) * OneOverProduct;
+
+ // Sinus
+ IcePoint w = u^v;
+ float NormW = w.Magnitude();
+
+ float AbsSinus = NormW * OneOverProduct;
+
+ // Remove degeneracy
+ if(AbsSinus > 1.0f) AbsSinus = 1.0f;
+ if(AbsSinus < -1.0f) AbsSinus = -1.0f;
+
+ if(Cosinus>=0.0f) return asinf(AbsSinus);
+ else return (PI-asinf(AbsSinus));
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the angle between two triangles.
+ * \param tri [in] the other triangle
+ * \param verts [in] the list of indexed vertices
+ * \return the angle in radians
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float IndexedTriangle::Angle(const IndexedTriangle& tri, const IcePoint* verts) const
+{
+ // Checkings
+ if(!verts) return 0.0f;
+
+ // Compute face normals
+ IcePoint n0, n1;
+ Normal(verts, n0);
+ tri.Normal(verts, n1);
+
+ // Compute angle
+ float dp = n0|n1;
+ if(dp>1.0f) return 0.0f;
+ if(dp<-1.0f) return PI;
+ return acosf(dp);
+
+// return ::Angle(n0,n1);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks a triangle is the same as another one.
+ * \param tri [in] the other triangle
+ * \return true if same triangle
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool IndexedTriangle::Equal(const IndexedTriangle& tri) const
+{
+ // Test all vertex references
+ return (HasVertex(tri.mVRef[0]) &&
+ HasVertex(tri.mVRef[1]) &&
+ HasVertex(tri.mVRef[2]));
+}
diff --git a/Opcode/OpcodeLib/Ice/IceIndexedTriangle.h b/Opcode/OpcodeLib/Ice/IceIndexedTriangle.h
new file mode 100644
index 0000000..2cffa92
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceIndexedTriangle.h
@@ -0,0 +1,68 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a handy indexed triangle class.
+ * \file IceIndexedTriangle.h
+ * \author Pierre Terdiman
+ * \date January, 17, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEINDEXEDTRIANGLE_H__
+#define __ICEINDEXEDTRIANGLE_H__
+
+ // Forward declarations
+ enum CubeIndex;
+
+ // An indexed triangle class.
+ class ICEMATHS_API IndexedTriangle
+ {
+ public:
+ //! Constructor
+ inline_ IndexedTriangle() {}
+ //! Constructor
+ inline_ IndexedTriangle(udword r0, udword r1, udword r2) { mVRef[0]=r0; mVRef[1]=r1; mVRef[2]=r2; }
+ //! Copy constructor
+ inline_ IndexedTriangle(const IndexedTriangle& triangle)
+ {
+ mVRef[0] = triangle.mVRef[0];
+ mVRef[1] = triangle.mVRef[1];
+ mVRef[2] = triangle.mVRef[2];
+ }
+ //! Destructor
+ inline_ ~IndexedTriangle() {}
+ //! Vertex-references
+ udword mVRef[3];
+
+ // Methods
+ void Flip();
+ float Area(const IcePoint* verts) const;
+ float Perimeter(const IcePoint* verts) const;
+ float Compacity(const IcePoint* verts) const;
+ void Normal(const IcePoint* verts, IcePoint& normal) const;
+ void DenormalizedNormal(const IcePoint* verts, IcePoint& normal) const;
+ void Center(const IcePoint* verts, IcePoint& center) const;
+ void CenteredNormal(const IcePoint* verts, IcePoint& normal) const;
+ void RandomPoint(const IcePoint* verts, IcePoint& random) const;
+ bool IsVisible(const IcePoint* verts, const IcePoint& source) const;
+ bool BackfaceCulling(const IcePoint* verts, const IcePoint& source) const;
+ float ComputeOcclusionPotential(const IcePoint* verts, const IcePoint& view) const;
+ bool ReplaceVertex(udword oldref, udword newref);
+ bool IsDegenerate() const;
+ bool HasVertex(udword ref) const;
+ bool HasVertex(udword ref, udword* index) const;
+ ubyte FindEdge(udword vref0, udword vref1) const;
+ udword OppositeVertex(udword vref0, udword vref1) const;
+ inline_ udword OppositeVertex(ubyte edgenb) const { return mVRef[2-edgenb]; }
+ void GetVRefs(ubyte edgenb, udword& vref0, udword& vref1, udword& vref2) const;
+ float MinEdgeLength(const IcePoint* verts) const;
+ float MaxEdgeLength(const IcePoint* verts) const;
+ void ComputePoint(const IcePoint* verts, float u, float v, IcePoint& pt, udword* nearvtx=null) const;
+ float Angle(const IndexedTriangle& tri, const IcePoint* verts) const;
+ inline_ IcePlane PlaneEquation(const IcePoint* verts) const { return IcePlane(verts[mVRef[0]], verts[mVRef[1]], verts[mVRef[2]]); }
+ bool Equal(const IndexedTriangle& tri) const;
+ CubeIndex ComputeCubeIndex(const IcePoint* verts) const;
+ };
+
+#endif // __ICEINDEXEDTRIANGLE_H__
diff --git a/Opcode/OpcodeLib/Ice/IceLSS.h b/Opcode/OpcodeLib/Ice/IceLSS.h
new file mode 100644
index 0000000..7fe5b59
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceLSS.h
@@ -0,0 +1,75 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for line-swept spheres.
+ * \file IceLSS.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICELSS_H__
+#define __ICELSS_H__
+
+ class ICEMATHS_API LSS : public IceSegment
+ {
+ public:
+ //! Constructor
+ inline_ LSS() {}
+ //! Constructor
+ inline_ LSS(const IceSegment& seg, float radius) : IceSegment(seg), mRadius(radius) {}
+ //! Destructor
+ inline_ ~LSS() {}
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes an OBB surrounding the LSS.
+ * \param box [out] the OBB
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void ComputeOBB(OBB& box);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Tests if a point is contained within the LSS.
+ * \param pt [in] the point to test
+ * \return true if inside the LSS
+ * \warning point and LSS must be in same space
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ bool Contains(const IcePoint& pt) const { return SquareDistance(pt) <= mRadius*mRadius; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Tests if a sphere is contained within the LSS.
+ * \param sphere [in] the sphere to test
+ * \return true if inside the LSS
+ * \warning sphere and LSS must be in same space
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ bool Contains(const Sphere& sphere)
+ {
+ float d = mRadius - sphere.mRadius;
+ if(d>=0.0f) return SquareDistance(sphere.mCenter) <= d*d;
+ else return false;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Tests if an LSS is contained within the LSS.
+ * \param lss [in] the LSS to test
+ * \return true if inside the LSS
+ * \warning both LSS must be in same space
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ bool Contains(const LSS& lss)
+ {
+ // We check the LSS contains the two spheres at the start and end of the sweep
+ return Contains(Sphere(lss.mP0, lss.mRadius)) && Contains(Sphere(lss.mP0, lss.mRadius));
+ }
+
+ float mRadius; //!< Sphere radius
+ };
+
+#endif // __ICELSS_H__
diff --git a/Opcode/OpcodeLib/Ice/IceMatrix3x3.cpp b/Opcode/OpcodeLib/Ice/IceMatrix3x3.cpp
new file mode 100644
index 0000000..189d39d
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceMatrix3x3.cpp
@@ -0,0 +1,48 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for 3x3 matrices.
+ * \file IceMatrix3x3.cpp
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 3x3 matrix.
+ * DirectX-compliant, ie row-column order, ie m[Row][Col].
+ * Same as:
+ * m11 m12 m13 first row.
+ * m21 m22 m23 second row.
+ * m31 m32 m33 third row.
+ * Stored in memory as m11 m12 m13 m21...
+ *
+ * Multiplication rules:
+ *
+ * [x'y'z'] = [xyz][M]
+ *
+ * x' = x*m11 + y*m21 + z*m31
+ * y' = x*m12 + y*m22 + z*m32
+ * z' = x*m13 + y*m23 + z*m33
+ *
+ * \class Matrix3x3
+ * \author Pierre Terdiman
+ * \version 1.0
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+// Cast operator
+Matrix3x3::operator Matrix4x4() const
+{
+ return Matrix4x4(
+ m[0][0], m[0][1], m[0][2], 0.0f,
+ m[1][0], m[1][1], m[1][2], 0.0f,
+ m[2][0], m[2][1], m[2][2], 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+}
diff --git a/Opcode/OpcodeLib/Ice/IceMatrix3x3.h b/Opcode/OpcodeLib/Ice/IceMatrix3x3.h
new file mode 100644
index 0000000..07f5759
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceMatrix3x3.h
@@ -0,0 +1,496 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for 3x3 matrices.
+ * \file IceMatrix3x3.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEMATRIX3X3_H__
+#define __ICEMATRIX3X3_H__
+
+ // Forward declarations
+ class Quat;
+
+ #define MATRIX3X3_EPSILON (1.0e-7f)
+
+ class ICEMATHS_API Matrix3x3
+ {
+ public:
+ //! Empty constructor
+ inline_ Matrix3x3() {}
+ //! Constructor from 9 values
+ inline_ Matrix3x3(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22)
+ {
+ m[0][0] = m00; m[0][1] = m01; m[0][2] = m02;
+ m[1][0] = m10; m[1][1] = m11; m[1][2] = m12;
+ m[2][0] = m20; m[2][1] = m21; m[2][2] = m22;
+ }
+ //! Copy constructor
+ inline_ Matrix3x3(const Matrix3x3& mat) { CopyMemory(m, &mat.m, 9*sizeof(float)); }
+ //! Destructor
+ inline_ ~Matrix3x3() {}
+
+ //! Assign values
+ inline_ void Set(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22)
+ {
+ m[0][0] = m00; m[0][1] = m01; m[0][2] = m02;
+ m[1][0] = m10; m[1][1] = m11; m[1][2] = m12;
+ m[2][0] = m20; m[2][1] = m21; m[2][2] = m22;
+ }
+
+ //! Sets the scale from a IcePoint. The point is put on the diagonal.
+ inline_ void SetScale(const IcePoint& p) { m[0][0] = p.x; m[1][1] = p.y; m[2][2] = p.z; }
+
+ //! Sets the scale from floats. Values are put on the diagonal.
+ inline_ void SetScale(float sx, float sy, float sz) { m[0][0] = sx; m[1][1] = sy; m[2][2] = sz; }
+
+ //! Scales from a IcePoint. Each row is multiplied by a component.
+ inline_ void Scale(const IcePoint& p)
+ {
+ m[0][0] *= p.x; m[0][1] *= p.x; m[0][2] *= p.x;
+ m[1][0] *= p.y; m[1][1] *= p.y; m[1][2] *= p.y;
+ m[2][0] *= p.z; m[2][1] *= p.z; m[2][2] *= p.z;
+ }
+
+ //! Scales from floats. Each row is multiplied by a value.
+ inline_ void Scale(float sx, float sy, float sz)
+ {
+ m[0][0] *= sx; m[0][1] *= sx; m[0][2] *= sx;
+ m[1][0] *= sy; m[1][1] *= sy; m[1][2] *= sy;
+ m[2][0] *= sz; m[2][1] *= sz; m[2][2] *= sz;
+ }
+
+ //! Copy from a Matrix3x3
+ inline_ void Copy(const Matrix3x3& source) { CopyMemory(m, source.m, 9*sizeof(float)); }
+
+ // Row-column access
+ //! Returns a row.
+ inline_ void GetRow(const udword r, IcePoint& p) const { p.x = m[r][0]; p.y = m[r][1]; p.z = m[r][2]; }
+ //! Returns a row.
+ inline_ const IcePoint& GetRow(const udword r) const { return *(const IcePoint*)&m[r][0]; }
+ //! Returns a row.
+ inline_ IcePoint& GetRow(const udword r) { return *(IcePoint*)&m[r][0]; }
+ //! Sets a row.
+ inline_ void SetRow(const udword r, const IcePoint& p) { m[r][0] = p.x; m[r][1] = p.y; m[r][2] = p.z; }
+ //! Returns a column.
+ inline_ void GetCol(const udword c, IcePoint& p) const { p.x = m[0][c]; p.y = m[1][c]; p.z = m[2][c]; }
+ //! Sets a column.
+ inline_ void SetCol(const udword c, const IcePoint& p) { m[0][c] = p.x; m[1][c] = p.y; m[2][c] = p.z; }
+
+ //! Computes the trace. The trace is the sum of the 3 diagonal components.
+ inline_ float Trace() const { return m[0][0] + m[1][1] + m[2][2]; }
+ //! Clears the matrix.
+ inline_ void Zero() { ZeroMemory(&m, sizeof(m)); }
+ //! Sets the identity matrix.
+ inline_ void Identity() { Zero(); m[0][0] = m[1][1] = m[2][2] = 1.0f; }
+ //! Checks for identity
+ inline_ bool IsIdentity() const
+ {
+ if(IR(m[0][0])!=IEEE_1_0) return false;
+ if(IR(m[0][1])!=0) return false;
+ if(IR(m[0][2])!=0) return false;
+
+ if(IR(m[1][0])!=0) return false;
+ if(IR(m[1][1])!=IEEE_1_0) return false;
+ if(IR(m[1][2])!=0) return false;
+
+ if(IR(m[2][0])!=0) return false;
+ if(IR(m[2][1])!=0) return false;
+ if(IR(m[2][2])!=IEEE_1_0) return false;
+
+ return true;
+ }
+
+ //! Checks matrix validity
+ inline_ BOOL IsValid() const
+ {
+ for(udword j=0;j<3;j++)
+ {
+ for(udword i=0;i<3;i++)
+ {
+ if(!IsValidFloat(m[j][i])) return FALSE;
+ }
+ }
+ return TRUE;
+ }
+
+ //! Makes a skew-symmetric matrix (a.k.a. Star(*) Matrix)
+ //! [ 0.0 -a.z a.y ]
+ //! [ a.z 0.0 -a.x ]
+ //! [ -a.y a.x 0.0 ]
+ //! This is also called a "cross matrix" since for any vectors A and B,
+ //! A^B = Skew(A) * B = - B * Skew(A);
+ inline_ void SkewSymmetric(const IcePoint& a)
+ {
+ m[0][0] = 0.0f;
+ m[0][1] = -a.z;
+ m[0][2] = a.y;
+
+ m[1][0] = a.z;
+ m[1][1] = 0.0f;
+ m[1][2] = -a.x;
+
+ m[2][0] = -a.y;
+ m[2][1] = a.x;
+ m[2][2] = 0.0f;
+ }
+
+ //! Negates the matrix
+ inline_ void Neg()
+ {
+ m[0][0] = -m[0][0]; m[0][1] = -m[0][1]; m[0][2] = -m[0][2];
+ m[1][0] = -m[1][0]; m[1][1] = -m[1][1]; m[1][2] = -m[1][2];
+ m[2][0] = -m[2][0]; m[2][1] = -m[2][1]; m[2][2] = -m[2][2];
+ }
+
+ //! Neg from another matrix
+ inline_ void Neg(const Matrix3x3& mat)
+ {
+ m[0][0] = -mat.m[0][0]; m[0][1] = -mat.m[0][1]; m[0][2] = -mat.m[0][2];
+ m[1][0] = -mat.m[1][0]; m[1][1] = -mat.m[1][1]; m[1][2] = -mat.m[1][2];
+ m[2][0] = -mat.m[2][0]; m[2][1] = -mat.m[2][1]; m[2][2] = -mat.m[2][2];
+ }
+
+ //! Add another matrix
+ inline_ void Add(const Matrix3x3& mat)
+ {
+ m[0][0] += mat.m[0][0]; m[0][1] += mat.m[0][1]; m[0][2] += mat.m[0][2];
+ m[1][0] += mat.m[1][0]; m[1][1] += mat.m[1][1]; m[1][2] += mat.m[1][2];
+ m[2][0] += mat.m[2][0]; m[2][1] += mat.m[2][1]; m[2][2] += mat.m[2][2];
+ }
+
+ //! Sub another matrix
+ inline_ void Sub(const Matrix3x3& mat)
+ {
+ m[0][0] -= mat.m[0][0]; m[0][1] -= mat.m[0][1]; m[0][2] -= mat.m[0][2];
+ m[1][0] -= mat.m[1][0]; m[1][1] -= mat.m[1][1]; m[1][2] -= mat.m[1][2];
+ m[2][0] -= mat.m[2][0]; m[2][1] -= mat.m[2][1]; m[2][2] -= mat.m[2][2];
+ }
+ //! Mac
+ inline_ void Mac(const Matrix3x3& a, const Matrix3x3& b, float s)
+ {
+ m[0][0] = a.m[0][0] + b.m[0][0] * s;
+ m[0][1] = a.m[0][1] + b.m[0][1] * s;
+ m[0][2] = a.m[0][2] + b.m[0][2] * s;
+
+ m[1][0] = a.m[1][0] + b.m[1][0] * s;
+ m[1][1] = a.m[1][1] + b.m[1][1] * s;
+ m[1][2] = a.m[1][2] + b.m[1][2] * s;
+
+ m[2][0] = a.m[2][0] + b.m[2][0] * s;
+ m[2][1] = a.m[2][1] + b.m[2][1] * s;
+ m[2][2] = a.m[2][2] + b.m[2][2] * s;
+ }
+ //! Mac
+ inline_ void Mac(const Matrix3x3& a, float s)
+ {
+ m[0][0] += a.m[0][0] * s; m[0][1] += a.m[0][1] * s; m[0][2] += a.m[0][2] * s;
+ m[1][0] += a.m[1][0] * s; m[1][1] += a.m[1][1] * s; m[1][2] += a.m[1][2] * s;
+ m[2][0] += a.m[2][0] * s; m[2][1] += a.m[2][1] * s; m[2][2] += a.m[2][2] * s;
+ }
+
+ //! this = A * s
+ inline_ void Mult(const Matrix3x3& a, float s)
+ {
+ m[0][0] = a.m[0][0] * s; m[0][1] = a.m[0][1] * s; m[0][2] = a.m[0][2] * s;
+ m[1][0] = a.m[1][0] * s; m[1][1] = a.m[1][1] * s; m[1][2] = a.m[1][2] * s;
+ m[2][0] = a.m[2][0] * s; m[2][1] = a.m[2][1] * s; m[2][2] = a.m[2][2] * s;
+ }
+
+ inline_ void Add(const Matrix3x3& a, const Matrix3x3& b)
+ {
+ m[0][0] = a.m[0][0] + b.m[0][0]; m[0][1] = a.m[0][1] + b.m[0][1]; m[0][2] = a.m[0][2] + b.m[0][2];
+ m[1][0] = a.m[1][0] + b.m[1][0]; m[1][1] = a.m[1][1] + b.m[1][1]; m[1][2] = a.m[1][2] + b.m[1][2];
+ m[2][0] = a.m[2][0] + b.m[2][0]; m[2][1] = a.m[2][1] + b.m[2][1]; m[2][2] = a.m[2][2] + b.m[2][2];
+ }
+
+ inline_ void Sub(const Matrix3x3& a, const Matrix3x3& b)
+ {
+ m[0][0] = a.m[0][0] - b.m[0][0]; m[0][1] = a.m[0][1] - b.m[0][1]; m[0][2] = a.m[0][2] - b.m[0][2];
+ m[1][0] = a.m[1][0] - b.m[1][0]; m[1][1] = a.m[1][1] - b.m[1][1]; m[1][2] = a.m[1][2] - b.m[1][2];
+ m[2][0] = a.m[2][0] - b.m[2][0]; m[2][1] = a.m[2][1] - b.m[2][1]; m[2][2] = a.m[2][2] - b.m[2][2];
+ }
+
+ //! this = a * b
+ inline_ void Mult(const Matrix3x3& a, const Matrix3x3& b)
+ {
+ m[0][0] = a.m[0][0] * b.m[0][0] + a.m[0][1] * b.m[1][0] + a.m[0][2] * b.m[2][0];
+ m[0][1] = a.m[0][0] * b.m[0][1] + a.m[0][1] * b.m[1][1] + a.m[0][2] * b.m[2][1];
+ m[0][2] = a.m[0][0] * b.m[0][2] + a.m[0][1] * b.m[1][2] + a.m[0][2] * b.m[2][2];
+ m[1][0] = a.m[1][0] * b.m[0][0] + a.m[1][1] * b.m[1][0] + a.m[1][2] * b.m[2][0];
+ m[1][1] = a.m[1][0] * b.m[0][1] + a.m[1][1] * b.m[1][1] + a.m[1][2] * b.m[2][1];
+ m[1][2] = a.m[1][0] * b.m[0][2] + a.m[1][1] * b.m[1][2] + a.m[1][2] * b.m[2][2];
+ m[2][0] = a.m[2][0] * b.m[0][0] + a.m[2][1] * b.m[1][0] + a.m[2][2] * b.m[2][0];
+ m[2][1] = a.m[2][0] * b.m[0][1] + a.m[2][1] * b.m[1][1] + a.m[2][2] * b.m[2][1];
+ m[2][2] = a.m[2][0] * b.m[0][2] + a.m[2][1] * b.m[1][2] + a.m[2][2] * b.m[2][2];
+ }
+
+ //! this = transpose(a) * b
+ inline_ void MultAtB(const Matrix3x3& a, const Matrix3x3& b)
+ {
+ m[0][0] = a.m[0][0] * b.m[0][0] + a.m[1][0] * b.m[1][0] + a.m[2][0] * b.m[2][0];
+ m[0][1] = a.m[0][0] * b.m[0][1] + a.m[1][0] * b.m[1][1] + a.m[2][0] * b.m[2][1];
+ m[0][2] = a.m[0][0] * b.m[0][2] + a.m[1][0] * b.m[1][2] + a.m[2][0] * b.m[2][2];
+ m[1][0] = a.m[0][1] * b.m[0][0] + a.m[1][1] * b.m[1][0] + a.m[2][1] * b.m[2][0];
+ m[1][1] = a.m[0][1] * b.m[0][1] + a.m[1][1] * b.m[1][1] + a.m[2][1] * b.m[2][1];
+ m[1][2] = a.m[0][1] * b.m[0][2] + a.m[1][1] * b.m[1][2] + a.m[2][1] * b.m[2][2];
+ m[2][0] = a.m[0][2] * b.m[0][0] + a.m[1][2] * b.m[1][0] + a.m[2][2] * b.m[2][0];
+ m[2][1] = a.m[0][2] * b.m[0][1] + a.m[1][2] * b.m[1][1] + a.m[2][2] * b.m[2][1];
+ m[2][2] = a.m[0][2] * b.m[0][2] + a.m[1][2] * b.m[1][2] + a.m[2][2] * b.m[2][2];
+ }
+
+ //! this = a * transpose(b)
+ inline_ void MultABt(const Matrix3x3& a, const Matrix3x3& b)
+ {
+ m[0][0] = a.m[0][0] * b.m[0][0] + a.m[0][1] * b.m[0][1] + a.m[0][2] * b.m[0][2];
+ m[0][1] = a.m[0][0] * b.m[1][0] + a.m[0][1] * b.m[1][1] + a.m[0][2] * b.m[1][2];
+ m[0][2] = a.m[0][0] * b.m[2][0] + a.m[0][1] * b.m[2][1] + a.m[0][2] * b.m[2][2];
+ m[1][0] = a.m[1][0] * b.m[0][0] + a.m[1][1] * b.m[0][1] + a.m[1][2] * b.m[0][2];
+ m[1][1] = a.m[1][0] * b.m[1][0] + a.m[1][1] * b.m[1][1] + a.m[1][2] * b.m[1][2];
+ m[1][2] = a.m[1][0] * b.m[2][0] + a.m[1][1] * b.m[2][1] + a.m[1][2] * b.m[2][2];
+ m[2][0] = a.m[2][0] * b.m[0][0] + a.m[2][1] * b.m[0][1] + a.m[2][2] * b.m[0][2];
+ m[2][1] = a.m[2][0] * b.m[1][0] + a.m[2][1] * b.m[1][1] + a.m[2][2] * b.m[1][2];
+ m[2][2] = a.m[2][0] * b.m[2][0] + a.m[2][1] * b.m[2][1] + a.m[2][2] * b.m[2][2];
+ }
+
+ //! Makes a rotation matrix mapping vector "from" to vector "to".
+ Matrix3x3& FromTo(const IcePoint& from, const IcePoint& to);
+
+ //! Set a rotation matrix around the X axis.
+ //! 1 0 0
+ //! RX = 0 cx sx
+ //! 0 -sx cx
+ void RotX(float angle);
+ //! Set a rotation matrix around the Y axis.
+ //! cy 0 -sy
+ //! RY = 0 1 0
+ //! sy 0 cy
+ void RotY(float angle);
+ //! Set a rotation matrix around the Z axis.
+ //! cz sz 0
+ //! RZ = -sz cz 0
+ //! 0 0 1
+ void RotZ(float angle);
+ //! cy sx.sy -sy.cx
+ //! RY.RX 0 cx sx
+ //! sy -sx.cy cx.cy
+ void RotYX(float y, float x);
+
+ //! Make a rotation matrix about an arbitrary axis
+ Matrix3x3& Rot(float angle, const IcePoint& axis);
+
+ //! Transpose the matrix.
+ void Transpose()
+ {
+ IR(m[1][0]) ^= IR(m[0][1]); IR(m[0][1]) ^= IR(m[1][0]); IR(m[1][0]) ^= IR(m[0][1]);
+ IR(m[2][0]) ^= IR(m[0][2]); IR(m[0][2]) ^= IR(m[2][0]); IR(m[2][0]) ^= IR(m[0][2]);
+ IR(m[2][1]) ^= IR(m[1][2]); IR(m[1][2]) ^= IR(m[2][1]); IR(m[2][1]) ^= IR(m[1][2]);
+ }
+
+ //! this = Transpose(a)
+ void Transpose(const Matrix3x3& a)
+ {
+ m[0][0] = a.m[0][0]; m[0][1] = a.m[1][0]; m[0][2] = a.m[2][0];
+ m[1][0] = a.m[0][1]; m[1][1] = a.m[1][1]; m[1][2] = a.m[2][1];
+ m[2][0] = a.m[0][2]; m[2][1] = a.m[1][2]; m[2][2] = a.m[2][2];
+ }
+
+ //! Compute the determinant of the matrix. We use the rule of Sarrus.
+ float Determinant() const
+ {
+ return (m[0][0]*m[1][1]*m[2][2] + m[0][1]*m[1][2]*m[2][0] + m[0][2]*m[1][0]*m[2][1])
+ - (m[2][0]*m[1][1]*m[0][2] + m[2][1]*m[1][2]*m[0][0] + m[2][2]*m[1][0]*m[0][1]);
+ }
+/*
+ //! Compute a cofactor. Used for matrix inversion.
+ float CoFactor(ubyte row, ubyte column) const
+ {
+ static sdword gIndex[3+2] = { 0, 1, 2, 0, 1 };
+ return (m[gIndex[row+1]][gIndex[column+1]]*m[gIndex[row+2]][gIndex[column+2]] - m[gIndex[row+2]][gIndex[column+1]]*m[gIndex[row+1]][gIndex[column+2]]);
+ }
+*/
+ //! Invert the matrix. Determinant must be different from zero, else matrix can't be inverted.
+ Matrix3x3& Invert()
+ {
+ float Det = Determinant(); // Must be !=0
+ float OneOverDet = 1.0f / Det;
+
+ Matrix3x3 Temp;
+ Temp.m[0][0] = +(m[1][1] * m[2][2] - m[2][1] * m[1][2]) * OneOverDet;
+ Temp.m[1][0] = -(m[1][0] * m[2][2] - m[2][0] * m[1][2]) * OneOverDet;
+ Temp.m[2][0] = +(m[1][0] * m[2][1] - m[2][0] * m[1][1]) * OneOverDet;
+ Temp.m[0][1] = -(m[0][1] * m[2][2] - m[2][1] * m[0][2]) * OneOverDet;
+ Temp.m[1][1] = +(m[0][0] * m[2][2] - m[2][0] * m[0][2]) * OneOverDet;
+ Temp.m[2][1] = -(m[0][0] * m[2][1] - m[2][0] * m[0][1]) * OneOverDet;
+ Temp.m[0][2] = +(m[0][1] * m[1][2] - m[1][1] * m[0][2]) * OneOverDet;
+ Temp.m[1][2] = -(m[0][0] * m[1][2] - m[1][0] * m[0][2]) * OneOverDet;
+ Temp.m[2][2] = +(m[0][0] * m[1][1] - m[1][0] * m[0][1]) * OneOverDet;
+
+ *this = Temp;
+
+ return *this;
+ }
+
+ Matrix3x3& Normalize();
+
+ //! this = exp(a)
+ Matrix3x3& Exp(const Matrix3x3& a);
+
+void FromQuat(const Quat &q);
+void FromQuatL2(const Quat &q, float l2);
+
+ // Arithmetic operators
+ //! Operator for Matrix3x3 Plus = Matrix3x3 + Matrix3x3;
+ inline_ Matrix3x3 operator+(const Matrix3x3& mat) const
+ {
+ return Matrix3x3(
+ m[0][0] + mat.m[0][0], m[0][1] + mat.m[0][1], m[0][2] + mat.m[0][2],
+ m[1][0] + mat.m[1][0], m[1][1] + mat.m[1][1], m[1][2] + mat.m[1][2],
+ m[2][0] + mat.m[2][0], m[2][1] + mat.m[2][1], m[2][2] + mat.m[2][2]);
+ }
+
+ //! Operator for Matrix3x3 Minus = Matrix3x3 - Matrix3x3;
+ inline_ Matrix3x3 operator-(const Matrix3x3& mat) const
+ {
+ return Matrix3x3(
+ m[0][0] - mat.m[0][0], m[0][1] - mat.m[0][1], m[0][2] - mat.m[0][2],
+ m[1][0] - mat.m[1][0], m[1][1] - mat.m[1][1], m[1][2] - mat.m[1][2],
+ m[2][0] - mat.m[2][0], m[2][1] - mat.m[2][1], m[2][2] - mat.m[2][2]);
+ }
+
+ //! Operator for Matrix3x3 Mul = Matrix3x3 * Matrix3x3;
+ inline_ Matrix3x3 operator*(const Matrix3x3& mat) const
+ {
+ return Matrix3x3(
+ m[0][0]*mat.m[0][0] + m[0][1]*mat.m[1][0] + m[0][2]*mat.m[2][0],
+ m[0][0]*mat.m[0][1] + m[0][1]*mat.m[1][1] + m[0][2]*mat.m[2][1],
+ m[0][0]*mat.m[0][2] + m[0][1]*mat.m[1][2] + m[0][2]*mat.m[2][2],
+
+ m[1][0]*mat.m[0][0] + m[1][1]*mat.m[1][0] + m[1][2]*mat.m[2][0],
+ m[1][0]*mat.m[0][1] + m[1][1]*mat.m[1][1] + m[1][2]*mat.m[2][1],
+ m[1][0]*mat.m[0][2] + m[1][1]*mat.m[1][2] + m[1][2]*mat.m[2][2],
+
+ m[2][0]*mat.m[0][0] + m[2][1]*mat.m[1][0] + m[2][2]*mat.m[2][0],
+ m[2][0]*mat.m[0][1] + m[2][1]*mat.m[1][1] + m[2][2]*mat.m[2][1],
+ m[2][0]*mat.m[0][2] + m[2][1]*mat.m[1][2] + m[2][2]*mat.m[2][2]);
+ }
+
+ //! Operator for IcePoint Mul = Matrix3x3 * IcePoint;
+ inline_ IcePoint operator*(const IcePoint& v) const { return IcePoint(GetRow(0)|v, GetRow(1)|v, GetRow(2)|v); }
+
+ //! Operator for Matrix3x3 Mul = Matrix3x3 * float;
+ inline_ Matrix3x3 operator*(float s) const
+ {
+ return Matrix3x3(
+ m[0][0]*s, m[0][1]*s, m[0][2]*s,
+ m[1][0]*s, m[1][1]*s, m[1][2]*s,
+ m[2][0]*s, m[2][1]*s, m[2][2]*s);
+ }
+
+ //! Operator for Matrix3x3 Mul = float * Matrix3x3;
+ inline_ friend Matrix3x3 operator*(float s, const Matrix3x3& mat)
+ {
+ return Matrix3x3(
+ s*mat.m[0][0], s*mat.m[0][1], s*mat.m[0][2],
+ s*mat.m[1][0], s*mat.m[1][1], s*mat.m[1][2],
+ s*mat.m[2][0], s*mat.m[2][1], s*mat.m[2][2]);
+ }
+
+ //! Operator for Matrix3x3 Div = Matrix3x3 / float;
+ inline_ Matrix3x3 operator/(float s) const
+ {
+ if (s) s = 1.0f / s;
+ return Matrix3x3(
+ m[0][0]*s, m[0][1]*s, m[0][2]*s,
+ m[1][0]*s, m[1][1]*s, m[1][2]*s,
+ m[2][0]*s, m[2][1]*s, m[2][2]*s);
+ }
+
+ //! Operator for Matrix3x3 Div = float / Matrix3x3;
+ inline_ friend Matrix3x3 operator/(float s, const Matrix3x3& mat)
+ {
+ return Matrix3x3(
+ s/mat.m[0][0], s/mat.m[0][1], s/mat.m[0][2],
+ s/mat.m[1][0], s/mat.m[1][1], s/mat.m[1][2],
+ s/mat.m[2][0], s/mat.m[2][1], s/mat.m[2][2]);
+ }
+
+ //! Operator for Matrix3x3 += Matrix3x3
+ inline_ Matrix3x3& operator+=(const Matrix3x3& mat)
+ {
+ m[0][0] += mat.m[0][0]; m[0][1] += mat.m[0][1]; m[0][2] += mat.m[0][2];
+ m[1][0] += mat.m[1][0]; m[1][1] += mat.m[1][1]; m[1][2] += mat.m[1][2];
+ m[2][0] += mat.m[2][0]; m[2][1] += mat.m[2][1]; m[2][2] += mat.m[2][2];
+ return *this;
+ }
+
+ //! Operator for Matrix3x3 -= Matrix3x3
+ inline_ Matrix3x3& operator-=(const Matrix3x3& mat)
+ {
+ m[0][0] -= mat.m[0][0]; m[0][1] -= mat.m[0][1]; m[0][2] -= mat.m[0][2];
+ m[1][0] -= mat.m[1][0]; m[1][1] -= mat.m[1][1]; m[1][2] -= mat.m[1][2];
+ m[2][0] -= mat.m[2][0]; m[2][1] -= mat.m[2][1]; m[2][2] -= mat.m[2][2];
+ return *this;
+ }
+
+ //! Operator for Matrix3x3 *= Matrix3x3
+ inline_ Matrix3x3& operator*=(const Matrix3x3& mat)
+ {
+ IcePoint TempRow;
+
+ GetRow(0, TempRow);
+ m[0][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0];
+ m[0][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1];
+ m[0][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2];
+
+ GetRow(1, TempRow);
+ m[1][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0];
+ m[1][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1];
+ m[1][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2];
+
+ GetRow(2, TempRow);
+ m[2][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0];
+ m[2][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1];
+ m[2][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2];
+ return *this;
+ }
+
+ //! Operator for Matrix3x3 *= float
+ inline_ Matrix3x3& operator*=(float s)
+ {
+ m[0][0] *= s; m[0][1] *= s; m[0][2] *= s;
+ m[1][0] *= s; m[1][1] *= s; m[1][2] *= s;
+ m[2][0] *= s; m[2][1] *= s; m[2][2] *= s;
+ return *this;
+ }
+
+ //! Operator for Matrix3x3 /= float
+ inline_ Matrix3x3& operator/=(float s)
+ {
+ if (s) s = 1.0f / s;
+ m[0][0] *= s; m[0][1] *= s; m[0][2] *= s;
+ m[1][0] *= s; m[1][1] *= s; m[1][2] *= s;
+ m[2][0] *= s; m[2][1] *= s; m[2][2] *= s;
+ return *this;
+ }
+
+ // Cast operators
+ //! Cast a Matrix3x3 to a Matrix4x4.
+ operator Matrix4x4() const;
+ //! Cast a Matrix3x3 to a Quat.
+ operator Quat() const;
+
+ inline_ const IcePoint& operator[](int row) const { return *(const IcePoint*)&m[row][0]; }
+ inline_ IcePoint& operator[](int row) { return *(IcePoint*)&m[row][0]; }
+
+ public:
+
+ float m[3][3];
+ };
+
+#endif // __ICEMATRIX3X3_H__
+
diff --git a/Opcode/OpcodeLib/Ice/IceMatrix4x4.cpp b/Opcode/OpcodeLib/Ice/IceMatrix4x4.cpp
new file mode 100644
index 0000000..4c539c9
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceMatrix4x4.cpp
@@ -0,0 +1,135 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for 4x4 matrices.
+ * \file IceMatrix4x4.cpp
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 4x4 matrix.
+ * DirectX-compliant, ie row-column order, ie m[Row][Col].
+ * Same as:
+ * m11 m12 m13 m14 first row.
+ * m21 m22 m23 m24 second row.
+ * m31 m32 m33 m34 third row.
+ * m41 m42 m43 m44 fourth row.
+ * Translation is (m41, m42, m43), (m14, m24, m34, m44) = (0, 0, 0, 1).
+ * Stored in memory as m11 m12 m13 m14 m21...
+ *
+ * Multiplication rules:
+ *
+ * [x'y'z'1] = [xyz1][M]
+ *
+ * x' = x*m11 + y*m21 + z*m31 + m41
+ * y' = x*m12 + y*m22 + z*m32 + m42
+ * z' = x*m13 + y*m23 + z*m33 + m43
+ * 1' = 0 + 0 + 0 + m44
+ *
+ * \class Matrix4x4
+ * \author Pierre Terdiman
+ * \version 1.0
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Inverts a PR matrix. (which only contains a rotation and a translation)
+ * This is faster and less subject to FPU errors than the generic inversion code.
+ *
+ * \relates Matrix4x4
+ * \fn InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src)
+ * \param dest [out] destination matrix
+ * \param src [in] source matrix
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ICEMATHS_API void IceMaths::InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src)
+{
+ dest.m[0][0] = src.m[0][0];
+ dest.m[1][0] = src.m[0][1];
+ dest.m[2][0] = src.m[0][2];
+ dest.m[3][0] = -(src.m[3][0]*src.m[0][0] + src.m[3][1]*src.m[0][1] + src.m[3][2]*src.m[0][2]);
+
+ dest.m[0][1] = src.m[1][0];
+ dest.m[1][1] = src.m[1][1];
+ dest.m[2][1] = src.m[1][2];
+ dest.m[3][1] = -(src.m[3][0]*src.m[1][0] + src.m[3][1]*src.m[1][1] + src.m[3][2]*src.m[1][2]);
+
+ dest.m[0][2] = src.m[2][0];
+ dest.m[1][2] = src.m[2][1];
+ dest.m[2][2] = src.m[2][2];
+ dest.m[3][2] = -(src.m[3][0]*src.m[2][0] + src.m[3][1]*src.m[2][1] + src.m[3][2]*src.m[2][2]);
+
+ dest.m[0][3] = 0.0f;
+ dest.m[1][3] = 0.0f;
+ dest.m[2][3] = 0.0f;
+ dest.m[3][3] = 1.0f;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Compute the cofactor of the Matrix at a specified location
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float Matrix4x4::CoFactor(udword row, udword col) const
+{
+ return (( m[(row+1)&3][(col+1)&3]*m[(row+2)&3][(col+2)&3]*m[(row+3)&3][(col+3)&3] +
+ m[(row+1)&3][(col+2)&3]*m[(row+2)&3][(col+3)&3]*m[(row+3)&3][(col+1)&3] +
+ m[(row+1)&3][(col+3)&3]*m[(row+2)&3][(col+1)&3]*m[(row+3)&3][(col+2)&3])
+ - (m[(row+3)&3][(col+1)&3]*m[(row+2)&3][(col+2)&3]*m[(row+1)&3][(col+3)&3] +
+ m[(row+3)&3][(col+2)&3]*m[(row+2)&3][(col+3)&3]*m[(row+1)&3][(col+1)&3] +
+ m[(row+3)&3][(col+3)&3]*m[(row+2)&3][(col+1)&3]*m[(row+1)&3][(col+2)&3])) * ((row + col) & 1 ? -1.0f : +1.0f);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Compute the determinant of the Matrix
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float Matrix4x4::Determinant() const
+{
+ return m[0][0] * CoFactor(0, 0) +
+ m[0][1] * CoFactor(0, 1) +
+ m[0][2] * CoFactor(0, 2) +
+ m[0][3] * CoFactor(0, 3);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Compute the inverse of the matrix
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Matrix4x4& Matrix4x4::Invert()
+{
+ float Det = Determinant();
+ Matrix4x4 Temp;
+
+ if(fabsf(Det) < MATRIX4X4_EPSILON)
+ return *this; // The matrix is not invertible! Singular case!
+
+ float IDet = 1.0f / Det;
+
+ Temp.m[0][0] = CoFactor(0,0) * IDet;
+ Temp.m[1][0] = CoFactor(0,1) * IDet;
+ Temp.m[2][0] = CoFactor(0,2) * IDet;
+ Temp.m[3][0] = CoFactor(0,3) * IDet;
+ Temp.m[0][1] = CoFactor(1,0) * IDet;
+ Temp.m[1][1] = CoFactor(1,1) * IDet;
+ Temp.m[2][1] = CoFactor(1,2) * IDet;
+ Temp.m[3][1] = CoFactor(1,3) * IDet;
+ Temp.m[0][2] = CoFactor(2,0) * IDet;
+ Temp.m[1][2] = CoFactor(2,1) * IDet;
+ Temp.m[2][2] = CoFactor(2,2) * IDet;
+ Temp.m[3][2] = CoFactor(2,3) * IDet;
+ Temp.m[0][3] = CoFactor(3,0) * IDet;
+ Temp.m[1][3] = CoFactor(3,1) * IDet;
+ Temp.m[2][3] = CoFactor(3,2) * IDet;
+ Temp.m[3][3] = CoFactor(3,3) * IDet;
+
+ *this = Temp;
+
+ return *this;
+}
+
diff --git a/Opcode/OpcodeLib/Ice/IceMatrix4x4.h b/Opcode/OpcodeLib/Ice/IceMatrix4x4.h
new file mode 100644
index 0000000..f7658da
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceMatrix4x4.h
@@ -0,0 +1,455 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for 4x4 matrices.
+ * \file IceMatrix4x4.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEMATRIX4X4_H__
+#define __ICEMATRIX4X4_H__
+
+ // Forward declarations
+ class PRS;
+ class PR;
+
+ #define MATRIX4X4_EPSILON (1.0e-7f)
+
+ class ICEMATHS_API Matrix4x4
+ {
+// void LUBackwardSubstitution( sdword *indx, float* b );
+// void LUDecomposition( sdword* indx, float* d );
+
+ public:
+ //! Empty constructor.
+ inline_ Matrix4x4() {}
+ //! Constructor from 16 values
+ inline_ Matrix4x4( float m00, float m01, float m02, float m03,
+ float m10, float m11, float m12, float m13,
+ float m20, float m21, float m22, float m23,
+ float m30, float m31, float m32, float m33)
+ {
+ m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; m[0][3] = m03;
+ m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; m[1][3] = m13;
+ m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; m[2][3] = m23;
+ m[3][0] = m30; m[3][1] = m31; m[3][2] = m32; m[3][3] = m33;
+ }
+ //! Copy constructor
+ inline_ Matrix4x4(const Matrix4x4& mat) { CopyMemory(m, &mat.m, 16*sizeof(float)); }
+ //! Destructor.
+ inline_ ~Matrix4x4() {}
+
+ //! Assign values (rotation only)
+ inline_ Matrix4x4& Set( float m00, float m01, float m02,
+ float m10, float m11, float m12,
+ float m20, float m21, float m22)
+ {
+ m[0][0] = m00; m[0][1] = m01; m[0][2] = m02;
+ m[1][0] = m10; m[1][1] = m11; m[1][2] = m12;
+ m[2][0] = m20; m[2][1] = m21; m[2][2] = m22;
+ return *this;
+ }
+ //! Assign values
+ inline_ Matrix4x4& Set( float m00, float m01, float m02, float m03,
+ float m10, float m11, float m12, float m13,
+ float m20, float m21, float m22, float m23,
+ float m30, float m31, float m32, float m33)
+ {
+ m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; m[0][3] = m03;
+ m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; m[1][3] = m13;
+ m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; m[2][3] = m23;
+ m[3][0] = m30; m[3][1] = m31; m[3][2] = m32; m[3][3] = m33;
+ return *this;
+ }
+
+ //! Copy from a Matrix4x4
+ inline_ void Copy(const Matrix4x4& source) { CopyMemory(m, source.m, 16*sizeof(float)); }
+
+ // Row-column access
+ //! Returns a row.
+ inline_ void GetRow(const udword r, HPoint& p) const { p.x=m[r][0]; p.y=m[r][1]; p.z=m[r][2]; p.w=m[r][3]; }
+ //! Returns a row.
+ inline_ void GetRow(const udword r, IcePoint& p) const { p.x=m[r][0]; p.y=m[r][1]; p.z=m[r][2]; }
+ //! Returns a row.
+ inline_ const HPoint& GetRow(const udword r) const { return *(const HPoint*)&m[r][0]; }
+ //! Returns a row.
+ inline_ HPoint& GetRow(const udword r) { return *(HPoint*)&m[r][0]; }
+ //! Sets a row.
+ inline_ void SetRow(const udword r, const HPoint& p) { m[r][0]=p.x; m[r][1]=p.y; m[r][2]=p.z; m[r][3]=p.w; }
+ //! Sets a row.
+ inline_ void SetRow(const udword r, const IcePoint& p) { m[r][0]=p.x; m[r][1]=p.y; m[r][2]=p.z; m[r][3]= (r!=3) ? 0.0f : 1.0f; }
+ //! Returns a column.
+ inline_ void GetCol(const udword c, HPoint& p) const { p.x=m[0][c]; p.y=m[1][c]; p.z=m[2][c]; p.w=m[3][c]; }
+ //! Returns a column.
+ inline_ void GetCol(const udword c, IcePoint& p) const { p.x=m[0][c]; p.y=m[1][c]; p.z=m[2][c]; }
+ //! Sets a column.
+ inline_ void SetCol(const udword c, const HPoint& p) { m[0][c]=p.x; m[1][c]=p.y; m[2][c]=p.z; m[3][c]=p.w; }
+ //! Sets a column.
+ inline_ void SetCol(const udword c, const IcePoint& p) { m[0][c]=p.x; m[1][c]=p.y; m[2][c]=p.z; m[3][c]= (c!=3) ? 0.0f : 1.0f; }
+
+ // Translation
+ //! Returns the translation part of the matrix.
+ inline_ const HPoint& GetTrans() const { return GetRow(3); }
+ //! Gets the translation part of the matrix
+ inline_ void GetTrans(IcePoint& p) const { p.x=m[3][0]; p.y=m[3][1]; p.z=m[3][2]; }
+ //! Sets the translation part of the matrix, from a IcePoint.
+ inline_ void SetTrans(const IcePoint& p) { m[3][0]=p.x; m[3][1]=p.y; m[3][2]=p.z; }
+ //! Sets the translation part of the matrix, from a HPoint.
+ inline_ void SetTrans(const HPoint& p) { m[3][0]=p.x; m[3][1]=p.y; m[3][2]=p.z; m[3][3]=p.w; }
+ //! Sets the translation part of the matrix, from floats.
+ inline_ void SetTrans(float tx, float ty, float tz) { m[3][0]=tx; m[3][1]=ty; m[3][2]=tz; }
+
+ // Scale
+ //! Sets the scale from a IcePoint. The point is put on the diagonal.
+ inline_ void SetScale(const IcePoint& p) { m[0][0]=p.x; m[1][1]=p.y; m[2][2]=p.z; }
+ //! Sets the scale from floats. Values are put on the diagonal.
+ inline_ void SetScale(float sx, float sy, float sz) { m[0][0]=sx; m[1][1]=sy; m[2][2]=sz; }
+ //! Scales from a IcePoint. Each row is multiplied by a component.
+ void Scale(const IcePoint& p)
+ {
+ m[0][0] *= p.x; m[1][0] *= p.y; m[2][0] *= p.z;
+ m[0][1] *= p.x; m[1][1] *= p.y; m[2][1] *= p.z;
+ m[0][2] *= p.x; m[1][2] *= p.y; m[2][2] *= p.z;
+ }
+ //! Scales from floats. Each row is multiplied by a value.
+ void Scale(float sx, float sy, float sz)
+ {
+ m[0][0] *= sx; m[1][0] *= sy; m[2][0] *= sz;
+ m[0][1] *= sx; m[1][1] *= sy; m[2][1] *= sz;
+ m[0][2] *= sx; m[1][2] *= sy; m[2][2] *= sz;
+ }
+/*
+ //! Returns a row.
+ inline_ HPoint GetRow(const udword row) const { return mRow[row]; }
+ //! Sets a row.
+ inline_ Matrix4x4& SetRow(const udword row, const HPoint& p) { mRow[row] = p; return *this; }
+ //! Sets a row.
+ Matrix4x4& SetRow(const udword row, const IcePoint& p)
+ {
+ m[row][0] = p.x;
+ m[row][1] = p.y;
+ m[row][2] = p.z;
+ m[row][3] = (row != 3) ? 0.0f : 1.0f;
+ return *this;
+ }
+ //! Returns a column.
+ HPoint GetCol(const udword col) const
+ {
+ HPoint Res;
+ Res.x = m[0][col];
+ Res.y = m[1][col];
+ Res.z = m[2][col];
+ Res.w = m[3][col];
+ return Res;
+ }
+ //! Sets a column.
+ Matrix4x4& SetCol(const udword col, const HPoint& p)
+ {
+ m[0][col] = p.x;
+ m[1][col] = p.y;
+ m[2][col] = p.z;
+ m[3][col] = p.w;
+ return *this;
+ }
+ //! Sets a column.
+ Matrix4x4& SetCol(const udword col, const IcePoint& p)
+ {
+ m[0][col] = p.x;
+ m[1][col] = p.y;
+ m[2][col] = p.z;
+ m[3][col] = (col != 3) ? 0.0f : 1.0f;
+ return *this;
+ }
+*/
+ //! Computes the trace. The trace is the sum of the 4 diagonal components.
+ inline_ float Trace() const { return m[0][0] + m[1][1] + m[2][2] + m[3][3]; }
+ //! Computes the trace of the upper 3x3 matrix.
+ inline_ float Trace3x3() const { return m[0][0] + m[1][1] + m[2][2]; }
+ //! Clears the matrix.
+ inline_ void Zero() { ZeroMemory(&m, sizeof(m)); }
+ //! Sets the identity matrix.
+ inline_ void Identity() { Zero(); m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1.0f; }
+ //! Checks for identity
+ inline_ bool IsIdentity() const
+ {
+ if(IR(m[0][0])!=IEEE_1_0) return false;
+ if(IR(m[0][1])!=0) return false;
+ if(IR(m[0][2])!=0) return false;
+ if(IR(m[0][3])!=0) return false;
+
+ if(IR(m[1][0])!=0) return false;
+ if(IR(m[1][1])!=IEEE_1_0) return false;
+ if(IR(m[1][2])!=0) return false;
+ if(IR(m[1][3])!=0) return false;
+
+ if(IR(m[2][0])!=0) return false;
+ if(IR(m[2][1])!=0) return false;
+ if(IR(m[2][2])!=IEEE_1_0) return false;
+ if(IR(m[2][3])!=0) return false;
+
+ if(IR(m[3][0])!=0) return false;
+ if(IR(m[3][1])!=0) return false;
+ if(IR(m[3][2])!=0) return false;
+ if(IR(m[3][3])!=IEEE_1_0) return false;
+ return true;
+ }
+
+ //! Checks matrix validity
+ inline_ BOOL IsValid() const
+ {
+ for(udword j=0;j<4;j++)
+ {
+ for(udword i=0;i<4;i++)
+ {
+ if(!IsValidFloat(m[j][i])) return FALSE;
+ }
+ }
+ return TRUE;
+ }
+
+ //! Sets a rotation matrix around the X axis.
+ void RotX(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[1][1] = m[2][2] = Cos; m[2][1] = -Sin; m[1][2] = Sin; }
+ //! Sets a rotation matrix around the Y axis.
+ void RotY(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[0][0] = m[2][2] = Cos; m[2][0] = Sin; m[0][2] = -Sin; }
+ //! Sets a rotation matrix around the Z axis.
+ void RotZ(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[0][0] = m[1][1] = Cos; m[1][0] = -Sin; m[0][1] = Sin; }
+
+ //! Makes a rotation matrix about an arbitrary axis
+ Matrix4x4& Rot(float angle, IcePoint& p1, IcePoint& p2);
+
+ //! Transposes the matrix.
+ void Transpose()
+ {
+ IR(m[1][0]) ^= IR(m[0][1]); IR(m[0][1]) ^= IR(m[1][0]); IR(m[1][0]) ^= IR(m[0][1]);
+ IR(m[2][0]) ^= IR(m[0][2]); IR(m[0][2]) ^= IR(m[2][0]); IR(m[2][0]) ^= IR(m[0][2]);
+ IR(m[3][0]) ^= IR(m[0][3]); IR(m[0][3]) ^= IR(m[3][0]); IR(m[3][0]) ^= IR(m[0][3]);
+ IR(m[1][2]) ^= IR(m[2][1]); IR(m[2][1]) ^= IR(m[1][2]); IR(m[1][2]) ^= IR(m[2][1]);
+ IR(m[1][3]) ^= IR(m[3][1]); IR(m[3][1]) ^= IR(m[1][3]); IR(m[1][3]) ^= IR(m[3][1]);
+ IR(m[2][3]) ^= IR(m[3][2]); IR(m[3][2]) ^= IR(m[2][3]); IR(m[2][3]) ^= IR(m[3][2]);
+ }
+
+ //! Computes a cofactor. Used for matrix inversion.
+ float CoFactor(udword row, udword col) const;
+ //! Computes the determinant of the matrix.
+ float Determinant() const;
+ //! Inverts the matrix. Determinant must be different from zero, else matrix can't be inverted.
+ Matrix4x4& Invert();
+// Matrix& ComputeAxisMatrix(IcePoint& axis, float angle);
+
+ // Cast operators
+ //! Casts a Matrix4x4 to a Matrix3x3.
+ inline_ operator Matrix3x3() const
+ {
+ return Matrix3x3(
+ m[0][0], m[0][1], m[0][2],
+ m[1][0], m[1][1], m[1][2],
+ m[2][0], m[2][1], m[2][2]);
+ }
+ //! Casts a Matrix4x4 to a Quat.
+ operator Quat() const;
+ //! Casts a Matrix4x4 to a PR.
+ operator PR() const;
+
+ // Arithmetic operators
+ //! Operator for Matrix4x4 Plus = Matrix4x4 + Matrix4x4;
+ inline_ Matrix4x4 operator+(const Matrix4x4& mat) const
+ {
+ return Matrix4x4(
+ m[0][0]+mat.m[0][0], m[0][1]+mat.m[0][1], m[0][2]+mat.m[0][2], m[0][3]+mat.m[0][3],
+ m[1][0]+mat.m[1][0], m[1][1]+mat.m[1][1], m[1][2]+mat.m[1][2], m[1][3]+mat.m[1][3],
+ m[2][0]+mat.m[2][0], m[2][1]+mat.m[2][1], m[2][2]+mat.m[2][2], m[2][3]+mat.m[2][3],
+ m[3][0]+mat.m[3][0], m[3][1]+mat.m[3][1], m[3][2]+mat.m[3][2], m[3][3]+mat.m[3][3]);
+ }
+
+ //! Operator for Matrix4x4 Minus = Matrix4x4 - Matrix4x4;
+ inline_ Matrix4x4 operator-(const Matrix4x4& mat) const
+ {
+ return Matrix4x4(
+ m[0][0]-mat.m[0][0], m[0][1]-mat.m[0][1], m[0][2]-mat.m[0][2], m[0][3]-mat.m[0][3],
+ m[1][0]-mat.m[1][0], m[1][1]-mat.m[1][1], m[1][2]-mat.m[1][2], m[1][3]-mat.m[1][3],
+ m[2][0]-mat.m[2][0], m[2][1]-mat.m[2][1], m[2][2]-mat.m[2][2], m[2][3]-mat.m[2][3],
+ m[3][0]-mat.m[3][0], m[3][1]-mat.m[3][1], m[3][2]-mat.m[3][2], m[3][3]-mat.m[3][3]);
+ }
+
+ //! Operator for Matrix4x4 Mul = Matrix4x4 * Matrix4x4;
+ inline_ Matrix4x4 operator*(const Matrix4x4& mat) const
+ {
+ return Matrix4x4(
+ m[0][0]*mat.m[0][0] + m[0][1]*mat.m[1][0] + m[0][2]*mat.m[2][0] + m[0][3]*mat.m[3][0],
+ m[0][0]*mat.m[0][1] + m[0][1]*mat.m[1][1] + m[0][2]*mat.m[2][1] + m[0][3]*mat.m[3][1],
+ m[0][0]*mat.m[0][2] + m[0][1]*mat.m[1][2] + m[0][2]*mat.m[2][2] + m[0][3]*mat.m[3][2],
+ m[0][0]*mat.m[0][3] + m[0][1]*mat.m[1][3] + m[0][2]*mat.m[2][3] + m[0][3]*mat.m[3][3],
+
+ m[1][0]*mat.m[0][0] + m[1][1]*mat.m[1][0] + m[1][2]*mat.m[2][0] + m[1][3]*mat.m[3][0],
+ m[1][0]*mat.m[0][1] + m[1][1]*mat.m[1][1] + m[1][2]*mat.m[2][1] + m[1][3]*mat.m[3][1],
+ m[1][0]*mat.m[0][2] + m[1][1]*mat.m[1][2] + m[1][2]*mat.m[2][2] + m[1][3]*mat.m[3][2],
+ m[1][0]*mat.m[0][3] + m[1][1]*mat.m[1][3] + m[1][2]*mat.m[2][3] + m[1][3]*mat.m[3][3],
+
+ m[2][0]*mat.m[0][0] + m[2][1]*mat.m[1][0] + m[2][2]*mat.m[2][0] + m[2][3]*mat.m[3][0],
+ m[2][0]*mat.m[0][1] + m[2][1]*mat.m[1][1] + m[2][2]*mat.m[2][1] + m[2][3]*mat.m[3][1],
+ m[2][0]*mat.m[0][2] + m[2][1]*mat.m[1][2] + m[2][2]*mat.m[2][2] + m[2][3]*mat.m[3][2],
+ m[2][0]*mat.m[0][3] + m[2][1]*mat.m[1][3] + m[2][2]*mat.m[2][3] + m[2][3]*mat.m[3][3],
+
+ m[3][0]*mat.m[0][0] + m[3][1]*mat.m[1][0] + m[3][2]*mat.m[2][0] + m[3][3]*mat.m[3][0],
+ m[3][0]*mat.m[0][1] + m[3][1]*mat.m[1][1] + m[3][2]*mat.m[2][1] + m[3][3]*mat.m[3][1],
+ m[3][0]*mat.m[0][2] + m[3][1]*mat.m[1][2] + m[3][2]*mat.m[2][2] + m[3][3]*mat.m[3][2],
+ m[3][0]*mat.m[0][3] + m[3][1]*mat.m[1][3] + m[3][2]*mat.m[2][3] + m[3][3]*mat.m[3][3]);
+ }
+
+ //! Operator for HPoint Mul = Matrix4x4 * HPoint;
+ inline_ HPoint operator*(const HPoint& v) const { return HPoint(GetRow(0)|v, GetRow(1)|v, GetRow(2)|v, GetRow(3)|v); }
+
+ //! Operator for IcePoint Mul = Matrix4x4 * IcePoint;
+ inline_ IcePoint operator*(const IcePoint& v) const
+ {
+ return IcePoint( m[0][0]*v.x + m[0][1]*v.y + m[0][2]*v.z + m[0][3],
+ m[1][0]*v.x + m[1][1]*v.y + m[1][2]*v.z + m[1][3],
+ m[2][0]*v.x + m[2][1]*v.y + m[2][2]*v.z + m[2][3] );
+ }
+
+ //! Operator for Matrix4x4 Scale = Matrix4x4 * float;
+ inline_ Matrix4x4 operator*(float s) const
+ {
+ return Matrix4x4(
+ m[0][0]*s, m[0][1]*s, m[0][2]*s, m[0][3]*s,
+ m[1][0]*s, m[1][1]*s, m[1][2]*s, m[1][3]*s,
+ m[2][0]*s, m[2][1]*s, m[2][2]*s, m[2][3]*s,
+ m[3][0]*s, m[3][1]*s, m[3][2]*s, m[3][3]*s);
+ }
+
+ //! Operator for Matrix4x4 Scale = float * Matrix4x4;
+ inline_ friend Matrix4x4 operator*(float s, const Matrix4x4& mat)
+ {
+ return Matrix4x4(
+ s*mat.m[0][0], s*mat.m[0][1], s*mat.m[0][2], s*mat.m[0][3],
+ s*mat.m[1][0], s*mat.m[1][1], s*mat.m[1][2], s*mat.m[1][3],
+ s*mat.m[2][0], s*mat.m[2][1], s*mat.m[2][2], s*mat.m[2][3],
+ s*mat.m[3][0], s*mat.m[3][1], s*mat.m[3][2], s*mat.m[3][3]);
+ }
+
+ //! Operator for Matrix4x4 Div = Matrix4x4 / float;
+ inline_ Matrix4x4 operator/(float s) const
+ {
+ if(s) s = 1.0f / s;
+
+ return Matrix4x4(
+ m[0][0]*s, m[0][1]*s, m[0][2]*s, m[0][3]*s,
+ m[1][0]*s, m[1][1]*s, m[1][2]*s, m[1][3]*s,
+ m[2][0]*s, m[2][1]*s, m[2][2]*s, m[2][3]*s,
+ m[3][0]*s, m[3][1]*s, m[3][2]*s, m[3][3]*s);
+ }
+
+ //! Operator for Matrix4x4 Div = float / Matrix4x4;
+ inline_ friend Matrix4x4 operator/(float s, const Matrix4x4& mat)
+ {
+ return Matrix4x4(
+ s/mat.m[0][0], s/mat.m[0][1], s/mat.m[0][2], s/mat.m[0][3],
+ s/mat.m[1][0], s/mat.m[1][1], s/mat.m[1][2], s/mat.m[1][3],
+ s/mat.m[2][0], s/mat.m[2][1], s/mat.m[2][2], s/mat.m[2][3],
+ s/mat.m[3][0], s/mat.m[3][1], s/mat.m[3][2], s/mat.m[3][3]);
+ }
+
+ //! Operator for Matrix4x4 += Matrix4x4;
+ inline_ Matrix4x4& operator+=(const Matrix4x4& mat)
+ {
+ m[0][0]+=mat.m[0][0]; m[0][1]+=mat.m[0][1]; m[0][2]+=mat.m[0][2]; m[0][3]+=mat.m[0][3];
+ m[1][0]+=mat.m[1][0]; m[1][1]+=mat.m[1][1]; m[1][2]+=mat.m[1][2]; m[1][3]+=mat.m[1][3];
+ m[2][0]+=mat.m[2][0]; m[2][1]+=mat.m[2][1]; m[2][2]+=mat.m[2][2]; m[2][3]+=mat.m[2][3];
+ m[3][0]+=mat.m[3][0]; m[3][1]+=mat.m[3][1]; m[3][2]+=mat.m[3][2]; m[3][3]+=mat.m[3][3];
+ return *this;
+ }
+
+ //! Operator for Matrix4x4 -= Matrix4x4;
+ inline_ Matrix4x4& operator-=(const Matrix4x4& mat)
+ {
+ m[0][0]-=mat.m[0][0]; m[0][1]-=mat.m[0][1]; m[0][2]-=mat.m[0][2]; m[0][3]-=mat.m[0][3];
+ m[1][0]-=mat.m[1][0]; m[1][1]-=mat.m[1][1]; m[1][2]-=mat.m[1][2]; m[1][3]-=mat.m[1][3];
+ m[2][0]-=mat.m[2][0]; m[2][1]-=mat.m[2][1]; m[2][2]-=mat.m[2][2]; m[2][3]-=mat.m[2][3];
+ m[3][0]-=mat.m[3][0]; m[3][1]-=mat.m[3][1]; m[3][2]-=mat.m[3][2]; m[3][3]-=mat.m[3][3];
+ return *this;
+ }
+
+ //! Operator for Matrix4x4 *= Matrix4x4;
+ Matrix4x4& operator*=(const Matrix4x4& mat)
+ {
+ HPoint TempRow;
+
+ GetRow(0, TempRow);
+ m[0][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0];
+ m[0][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1];
+ m[0][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2];
+ m[0][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3];
+
+ GetRow(1, TempRow);
+ m[1][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0];
+ m[1][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1];
+ m[1][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2];
+ m[1][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3];
+
+ GetRow(2, TempRow);
+ m[2][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0];
+ m[2][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1];
+ m[2][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2];
+ m[2][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3];
+
+ GetRow(3, TempRow);
+ m[3][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0];
+ m[3][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1];
+ m[3][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2];
+ m[3][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3];
+
+ return *this;
+ }
+
+ //! Operator for Matrix4x4 *= float;
+ inline_ Matrix4x4& operator*=(float s)
+ {
+ m[0][0]*=s; m[0][1]*=s; m[0][2]*=s; m[0][3]*=s;
+ m[1][0]*=s; m[1][1]*=s; m[1][2]*=s; m[1][3]*=s;
+ m[2][0]*=s; m[2][1]*=s; m[2][2]*=s; m[2][3]*=s;
+ m[3][0]*=s; m[3][1]*=s; m[3][2]*=s; m[3][3]*=s;
+ return *this;
+ }
+
+ //! Operator for Matrix4x4 /= float;
+ inline_ Matrix4x4& operator/=(float s)
+ {
+ if(s) s = 1.0f / s;
+ m[0][0]*=s; m[0][1]*=s; m[0][2]*=s; m[0][3]*=s;
+ m[1][0]*=s; m[1][1]*=s; m[1][2]*=s; m[1][3]*=s;
+ m[2][0]*=s; m[2][1]*=s; m[2][2]*=s; m[2][3]*=s;
+ m[3][0]*=s; m[3][1]*=s; m[3][2]*=s; m[3][3]*=s;
+ return *this;
+ }
+
+ inline_ const HPoint& operator[](int row) const { return *(const HPoint*)&m[row][0]; }
+ inline_ HPoint& operator[](int row) { return *(HPoint*)&m[row][0]; }
+
+ public:
+
+ float m[4][4];
+ };
+
+ //! Quickly rotates & translates a vector, using the 4x3 part of a 4x4 matrix
+ inline_ void TransformPoint4x3(IcePoint& dest, const IcePoint& source, const Matrix4x4& rot)
+ {
+ dest.x = rot.m[3][0] + source.x * rot.m[0][0] + source.y * rot.m[1][0] + source.z * rot.m[2][0];
+ dest.y = rot.m[3][1] + source.x * rot.m[0][1] + source.y * rot.m[1][1] + source.z * rot.m[2][1];
+ dest.z = rot.m[3][2] + source.x * rot.m[0][2] + source.y * rot.m[1][2] + source.z * rot.m[2][2];
+ }
+
+ //! Quickly rotates a vector, using the 3x3 part of a 4x4 matrix
+ inline_ void TransformPoint3x3(IcePoint& dest, const IcePoint& source, const Matrix4x4& rot)
+ {
+ dest.x = source.x * rot.m[0][0] + source.y * rot.m[1][0] + source.z * rot.m[2][0];
+ dest.y = source.x * rot.m[0][1] + source.y * rot.m[1][1] + source.z * rot.m[2][1];
+ dest.z = source.x * rot.m[0][2] + source.y * rot.m[1][2] + source.z * rot.m[2][2];
+ }
+
+ ICEMATHS_API void InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src);
+
+#endif // __ICEMATRIX4X4_H__
+
diff --git a/Opcode/OpcodeLib/Ice/IceMemoryMacros.h b/Opcode/OpcodeLib/Ice/IceMemoryMacros.h
new file mode 100644
index 0000000..490ecd1
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceMemoryMacros.h
@@ -0,0 +1,105 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains all memory macros.
+ * \file IceMemoryMacros.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEMEMORYMACROS_H__
+#define __ICEMEMORYMACROS_H__
+
+#undef ZeroMemory
+#undef CopyMemory
+#undef MoveMemory
+#undef FillMemory
+
+ //! Clears a buffer.
+ //! \param addr [in] buffer address
+ //! \param size [in] buffer length
+ //! \see FillMemory
+ //! \see StoreDwords
+ //! \see CopyMemory
+ //! \see MoveMemory
+ inline_ void ZeroMemory(void* addr, udword size) { memset(addr, 0, size); }
+
+ //! Fills a buffer with a given byte.
+ //! \param addr [in] buffer address
+ //! \param size [in] buffer length
+ //! \param val [in] the byte value
+ //! \see StoreDwords
+ //! \see ZeroMemory
+ //! \see CopyMemory
+ //! \see MoveMemory
+ inline_ void FillMemory(void* dest, udword size, ubyte val) { memset(dest, val, size); }
+
+ //! Fills a buffer with a given dword.
+ //! \param addr [in] buffer address
+ //! \param nb [in] number of dwords to write
+ //! \param value [in] the dword value
+ //! \see FillMemory
+ //! \see ZeroMemory
+ //! \see CopyMemory
+ //! \see MoveMemory
+ //! \warning writes nb*4 bytes !
+ inline_ void StoreDwords(udword* dest, udword nb, udword value)
+ {
+ // The asm code below **SHOULD** be equivalent to one of those C versions
+ // or the other if your compiled is good: (checked on VC++ 6.0)
+ //
+ // 1) while(nb--) *dest++ = value;
+ //
+ // 2) for(udword i=0;i<nb;i++) dest[i] = value;
+ //
+ _asm push eax
+ _asm push ecx
+ _asm push edi
+ _asm mov edi, dest
+ _asm mov ecx, nb
+ _asm mov eax, value
+ _asm rep stosd
+ _asm pop edi
+ _asm pop ecx
+ _asm pop eax
+ }
+
+ //! Copies a buffer.
+ //! \param addr [in] destination buffer address
+ //! \param addr [in] source buffer address
+ //! \param size [in] buffer length
+ //! \see ZeroMemory
+ //! \see FillMemory
+ //! \see StoreDwords
+ //! \see MoveMemory
+ inline_ void CopyMemory(void* dest, const void* src, udword size) { memcpy(dest, src, size); }
+
+ //! Moves a buffer.
+ //! \param addr [in] destination buffer address
+ //! \param addr [in] source buffer address
+ //! \param size [in] buffer length
+ //! \see ZeroMemory
+ //! \see FillMemory
+ //! \see StoreDwords
+ //! \see CopyMemory
+ inline_ void MoveMemory(void* dest, const void* src, udword size) { memmove(dest, src, size); }
+
+ #define SIZEOFOBJECT sizeof(*this) //!< Gives the size of current object. Avoid some mistakes (e.g. "sizeof(this)").
+ //#define CLEAROBJECT { memset(this, 0, SIZEOFOBJECT); } //!< Clears current object. Laziness is my business. HANDLE WITH CARE.
+ #define DELETESINGLE(x) if (x) { delete x; x = null; } //!< Deletes an instance of a class.
+ #define DELETEARRAY(x) if (x) { delete []x; x = null; } //!< Deletes an array.
+ #define SAFE_RELEASE(x) if (x) { (x)->Release(); (x) = null; } //!< Safe D3D-style release
+ #define SAFE_DESTRUCT(x) if (x) { (x)->SelfDestruct(); (x) = null; } //!< Safe ICE-style release
+
+#ifdef __ICEERROR_H__
+ #define CHECKALLOC(x) if(!x) return SetIceError("Out of memory.", EC_OUT_OF_MEMORY); //!< Standard alloc checking. HANDLE WITH CARE.
+#else
+ #define CHECKALLOC(x) if(!x) return false;
+#endif
+
+ //! Standard allocation cycle
+ #define SAFE_ALLOC(ptr, type, count) DELETEARRAY(ptr); ptr = new type[count]; CHECKALLOC(ptr);
+
+#endif // __ICEMEMORYMACROS_H__
diff --git a/Opcode/OpcodeLib/Ice/IceOBB.cpp b/Opcode/OpcodeLib/Ice/IceOBB.cpp
new file mode 100644
index 0000000..5a986e8
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceOBB.cpp
@@ -0,0 +1,323 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains OBB-related code.
+ * \file IceOBB.cpp
+ * \author Pierre Terdiman
+ * \date January, 29, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * An Oriented Bounding Box (OBB).
+ * \class OBB
+ * \author Pierre Terdiman
+ * \version 1.0
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Tests if a point is contained within the OBB.
+ * \param p [in] the world point to test
+ * \return true if inside the OBB
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool OBB::ContainsPoint(const IcePoint& p) const
+{
+ // IcePoint in OBB test using lazy evaluation and early exits
+
+ // Translate to box space
+ IcePoint RelPoint = p - mCenter;
+
+ // IcePoint * mRot maps from box space to world space
+ // mRot * IcePoint maps from world space to box space (what we need here)
+
+ float f = mRot.m[0][0] * RelPoint.x + mRot.m[0][1] * RelPoint.y + mRot.m[0][2] * RelPoint.z;
+ if(f >= mExtents.x || f <= -mExtents.x) return false;
+
+ f = mRot.m[1][0] * RelPoint.x + mRot.m[1][1] * RelPoint.y + mRot.m[1][2] * RelPoint.z;
+ if(f >= mExtents.y || f <= -mExtents.y) return false;
+
+ f = mRot.m[2][0] * RelPoint.x + mRot.m[2][1] * RelPoint.y + mRot.m[2][2] * RelPoint.z;
+ if(f >= mExtents.z || f <= -mExtents.z) return false;
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds an OBB from an AABB and a world transform.
+ * \param aabb [in] the aabb
+ * \param mat [in] the world transform
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBB::Create(const AABB& aabb, const Matrix4x4& mat)
+{
+ // Note: must be coherent with Rotate()
+
+ aabb.GetCenter(mCenter);
+ aabb.GetExtents(mExtents);
+ // Here we have the same as OBB::Rotate(mat) where the obb is (mCenter, mExtents, Identity).
+
+ // So following what's done in Rotate:
+ // - x-form the center
+ mCenter *= mat;
+ // - combine rotation with identity, i.e. just use given matrix
+ mRot = mat;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the obb planes.
+ * \param planes [out] 6 box planes
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool OBB::ComputePlanes(IcePlane* planes) const
+{
+ // Checkings
+ if(!planes) return false;
+
+ IcePoint Axis0 = mRot[0];
+ IcePoint Axis1 = mRot[1];
+ IcePoint Axis2 = mRot[2];
+
+ // Writes normals
+ planes[0].n = Axis0;
+ planes[1].n = -Axis0;
+ planes[2].n = Axis1;
+ planes[3].n = -Axis1;
+ planes[4].n = Axis2;
+ planes[5].n = -Axis2;
+
+ // Compute a point on each plane
+ IcePoint p0 = mCenter + Axis0 * mExtents.x;
+ IcePoint p1 = mCenter - Axis0 * mExtents.x;
+ IcePoint p2 = mCenter + Axis1 * mExtents.y;
+ IcePoint p3 = mCenter - Axis1 * mExtents.y;
+ IcePoint p4 = mCenter + Axis2 * mExtents.z;
+ IcePoint p5 = mCenter - Axis2 * mExtents.z;
+
+ // Compute d
+ planes[0].d = -(planes[0].n|p0);
+ planes[1].d = -(planes[1].n|p1);
+ planes[2].d = -(planes[2].n|p2);
+ planes[3].d = -(planes[3].n|p3);
+ planes[4].d = -(planes[4].n|p4);
+ planes[5].d = -(planes[5].n|p5);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the obb points.
+ * \param pts [out] 8 box points
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool OBB::ComputePoints(IcePoint* pts) const
+{
+ // Checkings
+ if(!pts) return false;
+
+ IcePoint Axis0 = mRot[0];
+ IcePoint Axis1 = mRot[1];
+ IcePoint Axis2 = mRot[2];
+
+ Axis0 *= mExtents.x;
+ Axis1 *= mExtents.y;
+ Axis2 *= mExtents.z;
+
+ // 7+------+6 0 = ---
+ // /| /| 1 = +--
+ // / | / | 2 = ++-
+ // / 4+---/--+5 3 = -+-
+ // 3+------+2 / y z 4 = --+
+ // | / | / | / 5 = +-+
+ // |/ |/ |/ 6 = +++
+ // 0+------+1 *---x 7 = -++
+
+ pts[0] = mCenter - Axis0 - Axis1 - Axis2;
+ pts[1] = mCenter + Axis0 - Axis1 - Axis2;
+ pts[2] = mCenter + Axis0 + Axis1 - Axis2;
+ pts[3] = mCenter - Axis0 + Axis1 - Axis2;
+ pts[4] = mCenter - Axis0 - Axis1 + Axis2;
+ pts[5] = mCenter + Axis0 - Axis1 + Axis2;
+ pts[6] = mCenter + Axis0 + Axis1 + Axis2;
+ pts[7] = mCenter - Axis0 + Axis1 + Axis2;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes vertex normals.
+ * \param pts [out] 8 box points
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool OBB::ComputeVertexNormals(IcePoint* pts) const
+{
+ static float VertexNormals[] =
+ {
+ -INVSQRT3, -INVSQRT3, -INVSQRT3,
+ INVSQRT3, -INVSQRT3, -INVSQRT3,
+ INVSQRT3, INVSQRT3, -INVSQRT3,
+ -INVSQRT3, INVSQRT3, -INVSQRT3,
+ -INVSQRT3, -INVSQRT3, INVSQRT3,
+ INVSQRT3, -INVSQRT3, INVSQRT3,
+ INVSQRT3, INVSQRT3, INVSQRT3,
+ -INVSQRT3, INVSQRT3, INVSQRT3
+ };
+
+ if(!pts) return false;
+
+ const IcePoint* VN = (const IcePoint*)VertexNormals;
+ for(udword i=0;i<8;i++)
+ {
+ pts[i] = VN[i] * mRot;
+ }
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns edges.
+ * \return 24 indices (12 edges) indexing the list returned by ComputePoints()
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const udword* OBB::GetEdges() const
+{
+ static udword Indices[] = {
+ 0, 1, 1, 2, 2, 3, 3, 0,
+ 7, 6, 6, 5, 5, 4, 4, 7,
+ 1, 5, 6, 2,
+ 3, 7, 4, 0
+ };
+ return Indices;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns local edge normals.
+ * \return edge normals in local space
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const IcePoint* OBB::GetLocalEdgeNormals() const
+{
+ static float EdgeNormals[] =
+ {
+ 0, -INVSQRT2, -INVSQRT2, // 0-1
+ INVSQRT2, 0, -INVSQRT2, // 1-2
+ 0, INVSQRT2, -INVSQRT2, // 2-3
+ -INVSQRT2, 0, -INVSQRT2, // 3-0
+
+ 0, INVSQRT2, INVSQRT2, // 7-6
+ INVSQRT2, 0, INVSQRT2, // 6-5
+ 0, -INVSQRT2, INVSQRT2, // 5-4
+ -INVSQRT2, 0, INVSQRT2, // 4-7
+
+ INVSQRT2, -INVSQRT2, 0, // 1-5
+ INVSQRT2, INVSQRT2, 0, // 6-2
+ -INVSQRT2, INVSQRT2, 0, // 3-7
+ -INVSQRT2, -INVSQRT2, 0 // 4-0
+ };
+ return (const IcePoint*)EdgeNormals;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns world edge normal
+ * \param edge_index [in] 0 <= edge index < 12
+ * \param world_normal [out] edge normal in world space
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBB::ComputeWorldEdgeNormal(udword edge_index, IcePoint& world_normal) const
+{
+ ASSERT(edge_index<12);
+ world_normal = GetLocalEdgeNormals()[edge_index] * mRot;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes an LSS surrounding the OBB.
+ * \param lss [out] the LSS
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBB::ComputeLSS(LSS& lss) const
+{
+ IcePoint Axis0 = mRot[0];
+ IcePoint Axis1 = mRot[1];
+ IcePoint Axis2 = mRot[2];
+
+ switch(mExtents.LargestAxis())
+ {
+ case 0:
+ lss.mRadius = (mExtents.y + mExtents.z)*0.5f;
+ lss.mP0 = mCenter + Axis0 * (mExtents.x - lss.mRadius);
+ lss.mP1 = mCenter - Axis0 * (mExtents.x - lss.mRadius);
+ break;
+ case 1:
+ lss.mRadius = (mExtents.x + mExtents.z)*0.5f;
+ lss.mP0 = mCenter + Axis1 * (mExtents.y - lss.mRadius);
+ lss.mP1 = mCenter - Axis1 * (mExtents.y - lss.mRadius);
+ break;
+ case 2:
+ lss.mRadius = (mExtents.x + mExtents.y)*0.5f;
+ lss.mP0 = mCenter + Axis2 * (mExtents.z - lss.mRadius);
+ lss.mP1 = mCenter - Axis2 * (mExtents.z - lss.mRadius);
+ break;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks the OBB is inside another OBB.
+ * \param box [in] the other OBB
+ * \return TRUE if we're inside the other box
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+BOOL OBB::IsInside(const OBB& box) const
+{
+ // Make a 4x4 from the box & inverse it
+ Matrix4x4 M0Inv;
+ {
+ Matrix4x4 M0 = box.mRot;
+ M0.SetTrans(box.mCenter);
+ InvertPRMatrix(M0Inv, M0);
+ }
+
+ // With our inversed 4x4, create box1 in space of box0
+ OBB _1in0;
+ Rotate(M0Inv, _1in0);
+
+ // This should cancel out box0's rotation, i.e. it's now an AABB.
+ // => Center(0,0,0), Rot(identity)
+
+ // The two boxes are in the same space so now we can compare them.
+
+ // Create the AABB of (box1 in space of box0)
+ const Matrix3x3& mtx = _1in0.mRot;
+
+ float f = fabsf(mtx.m[0][0] * mExtents.x) + fabsf(mtx.m[1][0] * mExtents.y) + fabsf(mtx.m[2][0] * mExtents.z) - box.mExtents.x;
+ if(f > _1in0.mCenter.x) return FALSE;
+ if(-f < _1in0.mCenter.x) return FALSE;
+
+ f = fabsf(mtx.m[0][1] * mExtents.x) + fabsf(mtx.m[1][1] * mExtents.y) + fabsf(mtx.m[2][1] * mExtents.z) - box.mExtents.y;
+ if(f > _1in0.mCenter.y) return FALSE;
+ if(-f < _1in0.mCenter.y) return FALSE;
+
+ f = fabsf(mtx.m[0][2] * mExtents.x) + fabsf(mtx.m[1][2] * mExtents.y) + fabsf(mtx.m[2][2] * mExtents.z) - box.mExtents.z;
+ if(f > _1in0.mCenter.z) return FALSE;
+ if(-f < _1in0.mCenter.z) return FALSE;
+
+ return TRUE;
+}
diff --git a/Opcode/OpcodeLib/Ice/IceOBB.h b/Opcode/OpcodeLib/Ice/IceOBB.h
new file mode 100644
index 0000000..249e76a
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceOBB.h
@@ -0,0 +1,177 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains OBB-related code. (oriented bounding box)
+ * \file IceOBB.h
+ * \author Pierre Terdiman
+ * \date January, 13, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEOBB_H__
+#define __ICEOBB_H__
+
+ // Forward declarations
+ class LSS;
+
+ class ICEMATHS_API OBB
+ {
+ public:
+ //! Constructor
+ inline_ OBB() {}
+ //! Constructor
+ inline_ OBB(const IcePoint& center, const IcePoint& extents, const Matrix3x3& rot) : mCenter(center), mExtents(extents), mRot(rot) {}
+ //! Destructor
+ inline_ ~OBB() {}
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups an empty OBB.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void SetEmpty()
+ {
+ mCenter.Zero();
+ mExtents.Set(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT);
+ mRot.Identity();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Tests if a point is contained within the OBB.
+ * \param p [in] the world point to test
+ * \return true if inside the OBB
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool ContainsPoint(const IcePoint& p) const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Builds an OBB from an AABB and a world transform.
+ * \param aabb [in] the aabb
+ * \param mat [in] the world transform
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void Create(const AABB& aabb, const Matrix4x4& mat);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Recomputes the OBB after an arbitrary transform by a 4x4 matrix.
+ * \param mtx [in] the transform matrix
+ * \param obb [out] the transformed OBB
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void Rotate(const Matrix4x4& mtx, OBB& obb) const
+ {
+ // The extents remain constant
+ obb.mExtents = mExtents;
+ // The center gets x-formed
+ obb.mCenter = mCenter * mtx;
+ // Combine rotations
+ obb.mRot = mRot * Matrix3x3(mtx);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks the OBB is valid.
+ * \return true if the box is valid
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL IsValid() const
+ {
+ // Consistency condition for (Center, Extents) boxes: Extents >= 0.0f
+ if(mExtents.x < 0.0f) return FALSE;
+ if(mExtents.y < 0.0f) return FALSE;
+ if(mExtents.z < 0.0f) return FALSE;
+ return TRUE;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the obb planes.
+ * \param planes [out] 6 box planes
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool ComputePlanes(IcePlane* planes) const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the obb points.
+ * \param pts [out] 8 box points
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool ComputePoints(IcePoint* pts) const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes vertex normals.
+ * \param pts [out] 8 box points
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool ComputeVertexNormals(IcePoint* pts) const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Returns edges.
+ * \return 24 indices (12 edges) indexing the list returned by ComputePoints()
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ const udword* GetEdges() const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Returns local edge normals.
+ * \return edge normals in local space
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ const IcePoint* GetLocalEdgeNormals() const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Returns world edge normal
+ * \param edge_index [in] 0 <= edge index < 12
+ * \param world_normal [out] edge normal in world space
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void ComputeWorldEdgeNormal(udword edge_index, IcePoint& world_normal) const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes an LSS surrounding the OBB.
+ * \param lss [out] the LSS
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ void ComputeLSS(LSS& lss) const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks the OBB is inside another OBB.
+ * \param box [in] the other OBB
+ * \return TRUE if we're inside the other box
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ BOOL IsInside(const OBB& box) const;
+
+ inline_ const IcePoint& GetCenter() const { return mCenter; }
+ inline_ const IcePoint& GetExtents() const { return mExtents; }
+ inline_ const Matrix3x3& GetRot() const { return mRot; }
+
+ inline_ void GetRotatedExtents(Matrix3x3& extents) const
+ {
+ extents = mRot;
+ extents.Scale(mExtents);
+ }
+
+ IcePoint mCenter; //!< B for Box
+ IcePoint mExtents; //!< B for Bounding
+ Matrix3x3 mRot; //!< O for Oriented
+
+ // Orientation is stored in row-major format,
+ // i.e. rows = eigen vectors of the covariance matrix
+ };
+
+#endif // __ICEOBB_H__
diff --git a/Opcode/OpcodeLib/Ice/IcePairs.h b/Opcode/OpcodeLib/Ice/IcePairs.h
new file mode 100644
index 0000000..35e3a07
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IcePairs.h
@@ -0,0 +1,45 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a simple pair class.
+ * \file IcePairs.h
+ * \author Pierre Terdiman
+ * \date January, 13, 2003
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEPAIRS_H__
+#define __ICEPAIRS_H__
+
+ //! A generic couple structure
+ struct ICECORE_API Pair
+ {
+ inline_ Pair() {}
+ inline_ Pair(udword i0, udword i1) : id0(i0), id1(i1) {}
+
+ udword id0; //!< First index of the pair
+ udword id1; //!< Second index of the pair
+ };
+
+ class ICECORE_API Pairs : private Container
+ {
+ public:
+ // Constructor / Destructor
+ Pairs() {}
+ ~Pairs() {}
+
+ inline_ udword GetNbPairs() const { return GetNbEntries()>>1; }
+ inline_ const Pair* GetPairs() const { return (const Pair*)GetEntries(); }
+ inline_ const Pair* GetPair(udword i) const { return (const Pair*)&GetEntries()[i+i]; }
+
+ inline_ BOOL HasPairs() const { return IsNotEmpty(); }
+
+ inline_ void ResetPairs() { Reset(); }
+ inline_ void DeleteLastPair() { DeleteLastEntry(); DeleteLastEntry(); }
+
+ inline_ void AddPair(const Pair& p) { Add(p.id0).Add(p.id1); }
+ inline_ void AddPair(udword id0, udword id1) { Add(id0).Add(id1); }
+ };
+
+#endif // __ICEPAIRS_H__
diff --git a/Opcode/OpcodeLib/Ice/IcePlane.cpp b/Opcode/OpcodeLib/Ice/IcePlane.cpp
new file mode 100644
index 0000000..1890f1c
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IcePlane.cpp
@@ -0,0 +1,45 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for planes.
+ * \file IcePlane.cpp
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * IcePlane class.
+ * \class IcePlane
+ * \author Pierre Terdiman
+ * \version 1.0
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the plane equation from 3 points.
+ * \param p0 [in] first point
+ * \param p1 [in] second point
+ * \param p2 [in] third point
+ * \return Self-reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+IcePlane& IcePlane::Set(const IcePoint& p0, const IcePoint& p1, const IcePoint& p2)
+{
+ IcePoint Edge0 = p1 - p0;
+ IcePoint Edge1 = p2 - p0;
+
+ n = Edge0 ^ Edge1;
+ n.Normalize();
+
+ d = -(p0 | n);
+
+ return *this;
+}
diff --git a/Opcode/OpcodeLib/Ice/IcePlane.h b/Opcode/OpcodeLib/Ice/IcePlane.h
new file mode 100644
index 0000000..1a447ce
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IcePlane.h
@@ -0,0 +1,113 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for planes.
+ * \file IcePlane.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEPLANE_H__
+#define __ICEPLANE_H__
+
+ #define PLANE_EPSILON (1.0e-7f)
+
+ class ICEMATHS_API IcePlane
+ {
+ public:
+ //! Constructor
+ inline_ IcePlane() { }
+ //! Constructor from a normal and a distance
+ inline_ IcePlane(float nx, float ny, float nz, float d) { Set(nx, ny, nz, d); }
+ //! Constructor from a point on the plane and a normal
+ inline_ IcePlane(const IcePoint& p, const IcePoint& n) { Set(p, n); }
+ //! Constructor from three points
+ inline_ IcePlane(const IcePoint& p0, const IcePoint& p1, const IcePoint& p2) { Set(p0, p1, p2); }
+ //! Constructor from a normal and a distance
+ inline_ IcePlane(const IcePoint& _n, float _d) { n = _n; d = _d; }
+ //! Copy constructor
+ inline_ IcePlane(const IcePlane& plane) : n(plane.n), d(plane.d) { }
+ //! Destructor
+ inline_ ~IcePlane() { }
+
+ inline_ IcePlane& Zero() { n.Zero(); d = 0.0f; return *this; }
+ inline_ IcePlane& Set(float nx, float ny, float nz, float _d) { n.Set(nx, ny, nz); d = _d; return *this; }
+ inline_ IcePlane& Set(const IcePoint& p, const IcePoint& _n) { n = _n; d = - p | _n; return *this; }
+ IcePlane& Set(const IcePoint& p0, const IcePoint& p1, const IcePoint& p2);
+
+ inline_ float Distance(const IcePoint& p) const { return (p | n) + d; }
+ inline_ bool Belongs(const IcePoint& p) const { return fabsf(Distance(p)) < PLANE_EPSILON; }
+
+ inline_ void Normalize()
+ {
+ float Denom = 1.0f / n.Magnitude();
+ n.x *= Denom;
+ n.y *= Denom;
+ n.z *= Denom;
+ d *= Denom;
+ }
+ public:
+ // Members
+ IcePoint n; //!< The normal to the plane
+ float d; //!< The distance from the origin
+
+ // Cast operators
+ inline_ operator IcePoint() const { return n; }
+ inline_ operator HPoint() const { return HPoint(n, d); }
+
+ // Arithmetic operators
+ inline_ IcePlane operator*(const Matrix4x4& m) const
+ {
+ // Old code from Irion. Kept for reference.
+ IcePlane Ret(*this);
+ return Ret *= m;
+ }
+
+ inline_ IcePlane& operator*=(const Matrix4x4& m)
+ {
+ // Old code from Irion. Kept for reference.
+ IcePoint n2 = HPoint(n, 0.0f) * m;
+ d = -((IcePoint) (HPoint( -d*n, 1.0f ) * m) | n2);
+ n = n2;
+ return *this;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Transforms a plane by a 4x4 matrix. Same as IcePlane * Matrix4x4 operator, but faster.
+ * \param transformed [out] transformed plane
+ * \param plane [in] source plane
+ * \param transform [in] transform matrix
+ * \warning the plane normal must be unit-length
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void TransformPlane(IcePlane& transformed, const IcePlane& plane, const Matrix4x4& transform)
+ {
+ // Rotate the normal using the rotation part of the 4x4 matrix
+ transformed.n = plane.n * Matrix3x3(transform);
+
+ // Compute new d
+ transformed.d = plane.d - (IcePoint(transform.GetTrans())|transformed.n);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Transforms a plane by a 4x4 matrix. Same as IcePlane * Matrix4x4 operator, but faster.
+ * \param plane [in/out] source plane (transformed on return)
+ * \param transform [in] transform matrix
+ * \warning the plane normal must be unit-length
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void TransformPlane(IcePlane& plane, const Matrix4x4& transform)
+ {
+ // Rotate the normal using the rotation part of the 4x4 matrix
+ plane.n *= Matrix3x3(transform);
+
+ // Compute new d
+ plane.d -= IcePoint(transform.GetTrans())|plane.n;
+ }
+
+#endif // __ICEPLANE_H__
diff --git a/Opcode/OpcodeLib/Ice/IcePoint.cpp b/Opcode/OpcodeLib/Ice/IcePoint.cpp
new file mode 100644
index 0000000..8615f4e
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IcePoint.cpp
@@ -0,0 +1,193 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for 3D vectors.
+ * \file IcePoint.cpp
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * 3D point.
+ *
+ * The name is "IcePoint" instead of "Vector" since a vector is N-dimensional, whereas a point is an implicit "vector of dimension 3".
+ * So the choice was between "IcePoint" and "Vector3", the first one looked better (IMHO).
+ *
+ * Some people, then, use a typedef to handle both points & vectors using the same class: typedef IcePoint Vector3;
+ * This is bad since it opens the door to a lot of confusion while reading the code. I know it may sounds weird but check this out:
+ *
+ * \code
+ * IcePoint P0,P1 = some 3D points;
+ * IcePoint Delta = P1 - P0;
+ * \endcode
+ *
+ * This compiles fine, although you should have written:
+ *
+ * \code
+ * IcePoint P0,P1 = some 3D points;
+ * Vector3 Delta = P1 - P0;
+ * \endcode
+ *
+ * Subtle things like this are not caught at compile-time, and when you find one in the code, you never know whether it's a mistake
+ * from the author or something you don't get.
+ *
+ * One way to handle it at compile-time would be to use different classes for IcePoint & Vector3, only overloading operator "-" for vectors.
+ * But then, you get a lot of redundant code in thoses classes, and basically it's really a lot of useless work.
+ *
+ * Another way would be to use homogeneous points: w=1 for points, w=0 for vectors. That's why the HPoint class exists. Now, to store
+ * your model's vertices and in most cases, you really want to use Points to save ram.
+ *
+ * \class IcePoint
+ * \author Pierre Terdiman
+ * \version 1.0
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Creates a positive unit random vector.
+ * \return Self-reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+IcePoint& IcePoint::PositiveUnitRandomVector()
+{
+ x = UnitRandomFloat();
+ y = UnitRandomFloat();
+ z = UnitRandomFloat();
+ Normalize();
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Creates a unit random vector.
+ * \return Self-reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+IcePoint& IcePoint::UnitRandomVector()
+{
+ x = UnitRandomFloat() - 0.5f;
+ y = UnitRandomFloat() - 0.5f;
+ z = UnitRandomFloat() - 0.5f;
+ Normalize();
+ return *this;
+}
+
+// Cast operator
+// WARNING: not inlined
+IcePoint::operator HPoint() const { return HPoint(x, y, z, 0.0f); }
+
+IcePoint& IcePoint::Refract(const IcePoint& eye, const IcePoint& n, float refractindex, IcePoint& refracted)
+{
+ // IcePoint EyePt = eye position
+ // IcePoint p = current vertex
+ // IcePoint n = vertex normal
+ // IcePoint rv = refracted vector
+ // Eye vector - doesn't need to be normalized
+ IcePoint Env;
+ Env.x = eye.x - x;
+ Env.y = eye.y - y;
+ Env.z = eye.z - z;
+
+ float NDotE = n|Env;
+ float NDotN = n|n;
+ NDotE /= refractindex;
+
+ // Refracted vector
+ refracted = n*NDotE - Env*NDotN;
+
+ return *this;
+}
+
+IcePoint& IcePoint::ProjectToPlane(const IcePlane& p)
+{
+ *this-= (p.d + (*this|p.n))*p.n;
+ return *this;
+}
+
+void IcePoint::ProjectToScreen(float halfrenderwidth, float halfrenderheight, const Matrix4x4& mat, HPoint& projected) const
+{
+ projected = HPoint(x, y, z, 1.0f) * mat;
+ projected.w = 1.0f / projected.w;
+
+ projected.x*=projected.w;
+ projected.y*=projected.w;
+ projected.z*=projected.w;
+
+ projected.x *= halfrenderwidth; projected.x += halfrenderwidth;
+ projected.y *= -halfrenderheight; projected.y += halfrenderheight;
+}
+
+void IcePoint::SetNotUsed()
+{
+ // We use a particular integer pattern : 0xffffffff everywhere. This is a NAN.
+ IR(x) = 0xffffffff;
+ IR(y) = 0xffffffff;
+ IR(z) = 0xffffffff;
+}
+
+BOOL IcePoint::IsNotUsed() const
+{
+ if(IR(x)!=0xffffffff) return FALSE;
+ if(IR(y)!=0xffffffff) return FALSE;
+ if(IR(z)!=0xffffffff) return FALSE;
+ return TRUE;
+}
+
+IcePoint& IcePoint::Mult(const Matrix3x3& mat, const IcePoint& a)
+{
+ x = a.x * mat.m[0][0] + a.y * mat.m[0][1] + a.z * mat.m[0][2];
+ y = a.x * mat.m[1][0] + a.y * mat.m[1][1] + a.z * mat.m[1][2];
+ z = a.x * mat.m[2][0] + a.y * mat.m[2][1] + a.z * mat.m[2][2];
+ return *this;
+}
+
+IcePoint& IcePoint::Mult2(const Matrix3x3& mat1, const IcePoint& a1, const Matrix3x3& mat2, const IcePoint& a2)
+{
+ x = a1.x * mat1.m[0][0] + a1.y * mat1.m[0][1] + a1.z * mat1.m[0][2] + a2.x * mat2.m[0][0] + a2.y * mat2.m[0][1] + a2.z * mat2.m[0][2];
+ y = a1.x * mat1.m[1][0] + a1.y * mat1.m[1][1] + a1.z * mat1.m[1][2] + a2.x * mat2.m[1][0] + a2.y * mat2.m[1][1] + a2.z * mat2.m[1][2];
+ z = a1.x * mat1.m[2][0] + a1.y * mat1.m[2][1] + a1.z * mat1.m[2][2] + a2.x * mat2.m[2][0] + a2.y * mat2.m[2][1] + a2.z * mat2.m[2][2];
+ return *this;
+}
+
+IcePoint& IcePoint::Mac(const Matrix3x3& mat, const IcePoint& a)
+{
+ x += a.x * mat.m[0][0] + a.y * mat.m[0][1] + a.z * mat.m[0][2];
+ y += a.x * mat.m[1][0] + a.y * mat.m[1][1] + a.z * mat.m[1][2];
+ z += a.x * mat.m[2][0] + a.y * mat.m[2][1] + a.z * mat.m[2][2];
+ return *this;
+}
+
+IcePoint& IcePoint::TransMult(const Matrix3x3& mat, const IcePoint& a)
+{
+ x = a.x * mat.m[0][0] + a.y * mat.m[1][0] + a.z * mat.m[2][0];
+ y = a.x * mat.m[0][1] + a.y * mat.m[1][1] + a.z * mat.m[2][1];
+ z = a.x * mat.m[0][2] + a.y * mat.m[1][2] + a.z * mat.m[2][2];
+ return *this;
+}
+
+IcePoint& IcePoint::Transform(const IcePoint& r, const Matrix3x3& rotpos, const IcePoint& linpos)
+{
+ x = r.x * rotpos.m[0][0] + r.y * rotpos.m[0][1] + r.z * rotpos.m[0][2] + linpos.x;
+ y = r.x * rotpos.m[1][0] + r.y * rotpos.m[1][1] + r.z * rotpos.m[1][2] + linpos.y;
+ z = r.x * rotpos.m[2][0] + r.y * rotpos.m[2][1] + r.z * rotpos.m[2][2] + linpos.z;
+ return *this;
+}
+
+IcePoint& IcePoint::InvTransform(const IcePoint& r, const Matrix3x3& rotpos, const IcePoint& linpos)
+{
+ float sx = r.x - linpos.x;
+ float sy = r.y - linpos.y;
+ float sz = r.z - linpos.z;
+ x = sx * rotpos.m[0][0] + sy * rotpos.m[1][0] + sz * rotpos.m[2][0];
+ y = sx * rotpos.m[0][1] + sy * rotpos.m[1][1] + sz * rotpos.m[2][1];
+ z = sx * rotpos.m[0][2] + sy * rotpos.m[1][2] + sz * rotpos.m[2][2];
+ return *this;
+}
diff --git a/Opcode/OpcodeLib/Ice/IcePoint.h b/Opcode/OpcodeLib/Ice/IcePoint.h
new file mode 100644
index 0000000..6b67409
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IcePoint.h
@@ -0,0 +1,528 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for 3D vectors.
+ * \file IcePoint.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEPOINT_H__
+#define __ICEPOINT_H__
+
+ // Forward declarations
+ class HPoint;
+ class IcePlane;
+ class Matrix3x3;
+ class Matrix4x4;
+
+ #define CROSS2D(a, b) (a.x*b.y - b.x*a.y)
+
+ const float EPSILON2 = 1.0e-20f;
+
+ class ICEMATHS_API IcePoint
+ {
+ public:
+
+ //! Empty constructor
+ inline_ IcePoint() {}
+ //! Constructor from a single float
+// inline_ IcePoint(float val) : x(val), y(val), z(val) {}
+// Removed since it introduced the nasty "IcePoint T = *Matrix4x4.GetTrans();" bug.......
+ //! Constructor from floats
+ inline_ IcePoint(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
+ //! Constructor from array
+ inline_ IcePoint(const float f[3]) : x(f[_X]), y(f[_Y]), z(f[_Z]) {}
+ //! Copy constructor
+ inline_ IcePoint(const IcePoint& p) : x(p.x), y(p.y), z(p.z) {}
+ //! Destructor
+ inline_ ~IcePoint() {}
+
+ //! Clears the vector
+ inline_ IcePoint& Zero() { x = y = z = 0.0f; return *this; }
+
+ //! + infinity
+ inline_ IcePoint& SetPlusInfinity() { x = y = z = MAX_FLOAT; return *this; }
+ //! - infinity
+ inline_ IcePoint& SetMinusInfinity() { x = y = z = MIN_FLOAT; return *this; }
+
+ //! Sets positive unit random vector
+ IcePoint& PositiveUnitRandomVector();
+ //! Sets unit random vector
+ IcePoint& UnitRandomVector();
+
+ //! Assignment from values
+ inline_ IcePoint& Set(float _x, float _y, float _z) { x = _x; y = _y; z = _z; return *this; }
+ //! Assignment from array
+ inline_ IcePoint& Set(const float f[3]) { x = f[_X]; y = f[_Y]; z = f[_Z]; return *this; }
+ //! Assignment from another point
+ inline_ IcePoint& Set(const IcePoint& src) { x = src.x; y = src.y; z = src.z; return *this; }
+
+ //! Adds a vector
+ inline_ IcePoint& Add(const IcePoint& p) { x += p.x; y += p.y; z += p.z; return *this; }
+ //! Adds a vector
+ inline_ IcePoint& Add(float _x, float _y, float _z) { x += _x; y += _y; z += _z; return *this; }
+ //! Adds a vector
+ inline_ IcePoint& Add(const float f[3]) { x += f[_X]; y += f[_Y]; z += f[_Z]; return *this; }
+ //! Adds vectors
+ inline_ IcePoint& Add(const IcePoint& p, const IcePoint& q) { x = p.x+q.x; y = p.y+q.y; z = p.z+q.z; return *this; }
+
+ //! Subtracts a vector
+ inline_ IcePoint& Sub(const IcePoint& p) { x -= p.x; y -= p.y; z -= p.z; return *this; }
+ //! Subtracts a vector
+ inline_ IcePoint& Sub(float _x, float _y, float _z) { x -= _x; y -= _y; z -= _z; return *this; }
+ //! Subtracts a vector
+ inline_ IcePoint& Sub(const float f[3]) { x -= f[_X]; y -= f[_Y]; z -= f[_Z]; return *this; }
+ //! Subtracts vectors
+ inline_ IcePoint& Sub(const IcePoint& p, const IcePoint& q) { x = p.x-q.x; y = p.y-q.y; z = p.z-q.z; return *this; }
+
+ //! this = -this
+ inline_ IcePoint& Neg() { x = -x; y = -y; z = -z; return *this; }
+ //! this = -a
+ inline_ IcePoint& Neg(const IcePoint& a) { x = -a.x; y = -a.y; z = -a.z; return *this; }
+
+ //! Multiplies by a scalar
+ inline_ IcePoint& Mult(float s) { x *= s; y *= s; z *= s; return *this; }
+
+ //! this = a * scalar
+ inline_ IcePoint& Mult(const IcePoint& a, float scalar)
+ {
+ x = a.x * scalar;
+ y = a.y * scalar;
+ z = a.z * scalar;
+ return *this;
+ }
+
+ //! this = a + b * scalar
+ inline_ IcePoint& Mac(const IcePoint& a, const IcePoint& b, float scalar)
+ {
+ x = a.x + b.x * scalar;
+ y = a.y + b.y * scalar;
+ z = a.z + b.z * scalar;
+ return *this;
+ }
+
+ //! this = this + a * scalar
+ inline_ IcePoint& Mac(const IcePoint& a, float scalar)
+ {
+ x += a.x * scalar;
+ y += a.y * scalar;
+ z += a.z * scalar;
+ return *this;
+ }
+
+ //! this = a - b * scalar
+ inline_ IcePoint& Msc(const IcePoint& a, const IcePoint& b, float scalar)
+ {
+ x = a.x - b.x * scalar;
+ y = a.y - b.y * scalar;
+ z = a.z - b.z * scalar;
+ return *this;
+ }
+
+ //! this = this - a * scalar
+ inline_ IcePoint& Msc(const IcePoint& a, float scalar)
+ {
+ x -= a.x * scalar;
+ y -= a.y * scalar;
+ z -= a.z * scalar;
+ return *this;
+ }
+
+ //! this = a + b * scalarb + c * scalarc
+ inline_ IcePoint& Mac2(const IcePoint& a, const IcePoint& b, float scalarb, const IcePoint& c, float scalarc)
+ {
+ x = a.x + b.x * scalarb + c.x * scalarc;
+ y = a.y + b.y * scalarb + c.y * scalarc;
+ z = a.z + b.z * scalarb + c.z * scalarc;
+ return *this;
+ }
+
+ //! this = a - b * scalarb - c * scalarc
+ inline_ IcePoint& Msc2(const IcePoint& a, const IcePoint& b, float scalarb, const IcePoint& c, float scalarc)
+ {
+ x = a.x - b.x * scalarb - c.x * scalarc;
+ y = a.y - b.y * scalarb - c.y * scalarc;
+ z = a.z - b.z * scalarb - c.z * scalarc;
+ return *this;
+ }
+
+ //! this = mat * a
+ inline_ IcePoint& Mult(const Matrix3x3& mat, const IcePoint& a);
+
+ //! this = mat1 * a1 + mat2 * a2
+ inline_ IcePoint& Mult2(const Matrix3x3& mat1, const IcePoint& a1, const Matrix3x3& mat2, const IcePoint& a2);
+
+ //! this = this + mat * a
+ inline_ IcePoint& Mac(const Matrix3x3& mat, const IcePoint& a);
+
+ //! this = transpose(mat) * a
+ inline_ IcePoint& TransMult(const Matrix3x3& mat, const IcePoint& a);
+
+ //! Linear interpolate between two vectors: this = a + t * (b - a)
+ inline_ IcePoint& Lerp(const IcePoint& a, const IcePoint& b, float t)
+ {
+ x = a.x + t * (b.x - a.x);
+ y = a.y + t * (b.y - a.y);
+ z = a.z + t * (b.z - a.z);
+ return *this;
+ }
+
+ //! Hermite interpolate between p1 and p2. p0 and p3 are used for finding gradient at p1 and p2.
+ //! this = p0 * (2t^2 - t^3 - t)/2
+ //! + p1 * (3t^3 - 5t^2 + 2)/2
+ //! + p2 * (4t^2 - 3t^3 + t)/2
+ //! + p3 * (t^3 - t^2)/2
+ inline_ IcePoint& Herp(const IcePoint& p0, const IcePoint& p1, const IcePoint& p2, const IcePoint& p3, float t)
+ {
+ float t2 = t * t;
+ float t3 = t2 * t;
+ float kp0 = (2.0f * t2 - t3 - t) * 0.5f;
+ float kp1 = (3.0f * t3 - 5.0f * t2 + 2.0f) * 0.5f;
+ float kp2 = (4.0f * t2 - 3.0f * t3 + t) * 0.5f;
+ float kp3 = (t3 - t2) * 0.5f;
+ x = p0.x * kp0 + p1.x * kp1 + p2.x * kp2 + p3.x * kp3;
+ y = p0.y * kp0 + p1.y * kp1 + p2.y * kp2 + p3.y * kp3;
+ z = p0.z * kp0 + p1.z * kp1 + p2.z * kp2 + p3.z * kp3;
+ return *this;
+ }
+
+ //! this = rotpos * r + linpos
+ inline_ IcePoint& Transform(const IcePoint& r, const Matrix3x3& rotpos, const IcePoint& linpos);
+
+ //! this = trans(rotpos) * (r - linpos)
+ inline_ IcePoint& InvTransform(const IcePoint& r, const Matrix3x3& rotpos, const IcePoint& linpos);
+
+ //! Returns MIN(x, y, z);
+ inline_ float Min() const { return MIN(x, MIN(y, z)); }
+ //! Returns MAX(x, y, z);
+ inline_ float Max() const { return MAX(x, MAX(y, z)); }
+ //! Sets each element to be componentwise minimum
+ inline_ IcePoint& Min(const IcePoint& p) { x = MIN(x, p.x); y = MIN(y, p.y); z = MIN(z, p.z); return *this; }
+ //! Sets each element to be componentwise maximum
+ inline_ IcePoint& Max(const IcePoint& p) { x = MAX(x, p.x); y = MAX(y, p.y); z = MAX(z, p.z); return *this; }
+
+ //! Clamps each element
+ inline_ IcePoint& Clamp(float min, float max)
+ {
+ if(x<min) x=min; if(x>max) x=max;
+ if(y<min) y=min; if(y>max) y=max;
+ if(z<min) z=min; if(z>max) z=max;
+ return *this;
+ }
+
+ //! Computes square magnitude
+ inline_ float SquareMagnitude() const { return x*x + y*y + z*z; }
+ //! Computes magnitude
+ inline_ float Magnitude() const { return sqrtf(x*x + y*y + z*z); }
+ //! Computes volume
+ inline_ float Volume() const { return x * y * z; }
+
+ //! Checks the point is near zero
+ inline_ bool ApproxZero() const { return SquareMagnitude() < EPSILON2; }
+
+ //! Tests for exact zero vector
+ inline_ BOOL IsZero() const
+ {
+ if(IR(x) || IR(y) || IR(z)) return FALSE;
+ return TRUE;
+ }
+
+ //! Checks point validity
+ inline_ BOOL IsValid() const
+ {
+ if(!IsValidFloat(x)) return FALSE;
+ if(!IsValidFloat(y)) return FALSE;
+ if(!IsValidFloat(z)) return FALSE;
+ return TRUE;
+ }
+
+ //! Slighty moves the point
+ void Tweak(udword coord_mask, udword tweak_mask)
+ {
+ if(coord_mask&1) { udword Dummy = IR(x); Dummy^=tweak_mask; x = FR(Dummy); }
+ if(coord_mask&2) { udword Dummy = IR(y); Dummy^=tweak_mask; y = FR(Dummy); }
+ if(coord_mask&4) { udword Dummy = IR(z); Dummy^=tweak_mask; z = FR(Dummy); }
+ }
+
+ #define TWEAKMASK 0x3fffff
+ #define TWEAKNOTMASK ~TWEAKMASK
+ //! Slighty moves the point out
+ inline_ void TweakBigger()
+ {
+ udword Dummy = (IR(x)&TWEAKNOTMASK); if(!IS_NEGATIVE_FLOAT(x)) Dummy+=TWEAKMASK+1; x = FR(Dummy);
+ Dummy = (IR(y)&TWEAKNOTMASK); if(!IS_NEGATIVE_FLOAT(y)) Dummy+=TWEAKMASK+1; y = FR(Dummy);
+ Dummy = (IR(z)&TWEAKNOTMASK); if(!IS_NEGATIVE_FLOAT(z)) Dummy+=TWEAKMASK+1; z = FR(Dummy);
+ }
+
+ //! Slighty moves the point in
+ inline_ void TweakSmaller()
+ {
+ udword Dummy = (IR(x)&TWEAKNOTMASK); if(IS_NEGATIVE_FLOAT(x)) Dummy+=TWEAKMASK+1; x = FR(Dummy);
+ Dummy = (IR(y)&TWEAKNOTMASK); if(IS_NEGATIVE_FLOAT(y)) Dummy+=TWEAKMASK+1; y = FR(Dummy);
+ Dummy = (IR(z)&TWEAKNOTMASK); if(IS_NEGATIVE_FLOAT(z)) Dummy+=TWEAKMASK+1; z = FR(Dummy);
+ }
+
+ //! Normalizes the vector
+ inline_ IcePoint& Normalize()
+ {
+ float M = x*x + y*y + z*z;
+ if(M)
+ {
+ M = 1.0f / sqrtf(M);
+ x *= M;
+ y *= M;
+ z *= M;
+ }
+ return *this;
+ }
+
+ //! Sets vector length
+ inline_ IcePoint& SetLength(float length)
+ {
+ float NewLength = length / Magnitude();
+ x *= NewLength;
+ y *= NewLength;
+ z *= NewLength;
+ return *this;
+ }
+
+ //! Clamps vector length
+ inline_ IcePoint& ClampLength(float limit_length)
+ {
+ if(limit_length>=0.0f) // Magnitude must be positive
+ {
+ float CurrentSquareLength = SquareMagnitude();
+
+ if(CurrentSquareLength > limit_length * limit_length)
+ {
+ float Coeff = limit_length / sqrtf(CurrentSquareLength);
+ x *= Coeff;
+ y *= Coeff;
+ z *= Coeff;
+ }
+ }
+ return *this;
+ }
+
+ //! Computes distance to another point
+ inline_ float Distance(const IcePoint& b) const
+ {
+ return sqrtf((x - b.x)*(x - b.x) + (y - b.y)*(y - b.y) + (z - b.z)*(z - b.z));
+ }
+
+ //! Computes square distance to another point
+ inline_ float SquareDistance(const IcePoint& b) const
+ {
+ return ((x - b.x)*(x - b.x) + (y - b.y)*(y - b.y) + (z - b.z)*(z - b.z));
+ }
+
+ //! Dot product dp = this|a
+ inline_ float Dot(const IcePoint& p) const { return p.x * x + p.y * y + p.z * z; }
+
+ //! Cross product this = a x b
+ inline_ IcePoint& Cross(const IcePoint& a, const IcePoint& b)
+ {
+ x = a.y * b.z - a.z * b.y;
+ y = a.z * b.x - a.x * b.z;
+ z = a.x * b.y - a.y * b.x;
+ return *this;
+ }
+
+ //! Vector code ( bitmask = sign(z) | sign(y) | sign(x) )
+ inline_ udword VectorCode() const
+ {
+ return (IR(x)>>31) | ((IR(y)&SIGN_BITMASK)>>30) | ((IR(z)&SIGN_BITMASK)>>29);
+ }
+
+ //! Returns largest axis
+ inline_ PointComponent LargestAxis() const
+ {
+ const float* Vals = &x;
+ PointComponent m = _X;
+ if(Vals[_Y] > Vals[m]) m = _Y;
+ if(Vals[_Z] > Vals[m]) m = _Z;
+ return m;
+ }
+
+ //! Returns closest axis
+ inline_ PointComponent ClosestAxis() const
+ {
+ const float* Vals = &x;
+ PointComponent m = _X;
+ if(AIR(Vals[_Y]) > AIR(Vals[m])) m = _Y;
+ if(AIR(Vals[_Z]) > AIR(Vals[m])) m = _Z;
+ return m;
+ }
+
+ //! Returns smallest axis
+ inline_ PointComponent SmallestAxis() const
+ {
+ const float* Vals = &x;
+ PointComponent m = _X;
+ if(Vals[_Y] < Vals[m]) m = _Y;
+ if(Vals[_Z] < Vals[m]) m = _Z;
+ return m;
+ }
+
+ //! Refracts the point
+ IcePoint& Refract(const IcePoint& eye, const IcePoint& n, float refractindex, IcePoint& refracted);
+
+ //! Projects the point onto a plane
+ IcePoint& ProjectToPlane(const IcePlane& p);
+
+ //! Projects the point onto the screen
+ void ProjectToScreen(float halfrenderwidth, float halfrenderheight, const Matrix4x4& mat, HPoint& projected) const;
+
+ //! Unfolds the point onto a plane according to edge(a,b)
+ IcePoint& Unfold(IcePlane& p, IcePoint& a, IcePoint& b);
+
+ //! Hash function from Ville Miettinen
+ inline_ udword GetHashValue() const
+ {
+ const udword* h = (const udword*)(this);
+ udword f = (h[0]+h[1]*11-(h[2]*17)) & 0x7fffffff; // avoid problems with +-0
+ return (f>>22)^(f>>12)^(f);
+ }
+
+ //! Stuff magic values in the point, marking it as explicitely not used.
+ void SetNotUsed();
+ //! Checks the point is marked as not used
+ BOOL IsNotUsed() const;
+
+ // Arithmetic operators
+
+ //! Unary operator for IcePoint Negate = - IcePoint
+ inline_ IcePoint operator-() const { return IcePoint(-x, -y, -z); }
+
+ //! Operator for IcePoint Plus = IcePoint + IcePoint.
+ inline_ IcePoint operator+(const IcePoint& p) const { return IcePoint(x + p.x, y + p.y, z + p.z); }
+ //! Operator for IcePoint Minus = IcePoint - IcePoint.
+ inline_ IcePoint operator-(const IcePoint& p) const { return IcePoint(x - p.x, y - p.y, z - p.z); }
+
+ //! Operator for IcePoint Mul = IcePoint * IcePoint.
+ inline_ IcePoint operator*(const IcePoint& p) const { return IcePoint(x * p.x, y * p.y, z * p.z); }
+ //! Operator for IcePoint Scale = IcePoint * float.
+ inline_ IcePoint operator*(float s) const { return IcePoint(x * s, y * s, z * s ); }
+ //! Operator for IcePoint Scale = float * IcePoint.
+ inline_ friend IcePoint operator*(float s, const IcePoint& p) { return IcePoint(s * p.x, s * p.y, s * p.z); }
+
+ //! Operator for IcePoint Div = IcePoint / IcePoint.
+ inline_ IcePoint operator/(const IcePoint& p) const { return IcePoint(x / p.x, y / p.y, z / p.z); }
+ //! Operator for IcePoint Scale = IcePoint / float.
+ inline_ IcePoint operator/(float s) const { s = 1.0f / s; return IcePoint(x * s, y * s, z * s); }
+ //! Operator for IcePoint Scale = float / IcePoint.
+ inline_ friend IcePoint operator/(float s, const IcePoint& p) { return IcePoint(s / p.x, s / p.y, s / p.z); }
+
+ //! Operator for float DotProd = IcePoint | IcePoint.
+ inline_ float operator|(const IcePoint& p) const { return x*p.x + y*p.y + z*p.z; }
+ //! Operator for IcePoint VecProd = IcePoint ^ IcePoint.
+ inline_ IcePoint operator^(const IcePoint& p) const
+ {
+ return IcePoint(
+ y * p.z - z * p.y,
+ z * p.x - x * p.z,
+ x * p.y - y * p.x );
+ }
+
+ //! Operator for IcePoint += IcePoint.
+ inline_ IcePoint& operator+=(const IcePoint& p) { x += p.x; y += p.y; z += p.z; return *this; }
+ //! Operator for IcePoint += float.
+ inline_ IcePoint& operator+=(float s) { x += s; y += s; z += s; return *this; }
+
+ //! Operator for IcePoint -= IcePoint.
+ inline_ IcePoint& operator-=(const IcePoint& p) { x -= p.x; y -= p.y; z -= p.z; return *this; }
+ //! Operator for IcePoint -= float.
+ inline_ IcePoint& operator-=(float s) { x -= s; y -= s; z -= s; return *this; }
+
+ //! Operator for IcePoint *= IcePoint.
+ inline_ IcePoint& operator*=(const IcePoint& p) { x *= p.x; y *= p.y; z *= p.z; return *this; }
+ //! Operator for IcePoint *= float.
+ inline_ IcePoint& operator*=(float s) { x *= s; y *= s; z *= s; return *this; }
+
+ //! Operator for IcePoint /= IcePoint.
+ inline_ IcePoint& operator/=(const IcePoint& p) { x /= p.x; y /= p.y; z /= p.z; return *this; }
+ //! Operator for IcePoint /= float.
+ inline_ IcePoint& operator/=(float s) { s = 1.0f/s; x *= s; y *= s; z *= s; return *this; }
+
+ // Logical operators
+
+ //! Operator for "if(IcePoint==IcePoint)"
+ inline_ bool operator==(const IcePoint& p) const { return ( (IR(x)==IR(p.x))&&(IR(y)==IR(p.y))&&(IR(z)==IR(p.z))); }
+ //! Operator for "if(IcePoint!=IcePoint)"
+ inline_ bool operator!=(const IcePoint& p) const { return ( (IR(x)!=IR(p.x))||(IR(y)!=IR(p.y))||(IR(z)!=IR(p.z))); }
+
+ // Arithmetic operators
+
+ //! Operator for IcePoint Mul = IcePoint * Matrix3x3.
+ inline_ IcePoint operator*(const Matrix3x3& mat) const
+ {
+ class ShadowMatrix3x3{ public: float m[3][3]; }; // To allow inlining
+ const ShadowMatrix3x3* Mat = (const ShadowMatrix3x3*)&mat;
+
+ return IcePoint(
+ x * Mat->m[0][0] + y * Mat->m[1][0] + z * Mat->m[2][0],
+ x * Mat->m[0][1] + y * Mat->m[1][1] + z * Mat->m[2][1],
+ x * Mat->m[0][2] + y * Mat->m[1][2] + z * Mat->m[2][2] );
+ }
+
+ //! Operator for IcePoint Mul = IcePoint * Matrix4x4.
+ inline_ IcePoint operator*(const Matrix4x4& mat) const
+ {
+ class ShadowMatrix4x4{ public: float m[4][4]; }; // To allow inlining
+ const ShadowMatrix4x4* Mat = (const ShadowMatrix4x4*)&mat;
+
+ return IcePoint(
+ x * Mat->m[0][0] + y * Mat->m[1][0] + z * Mat->m[2][0] + Mat->m[3][0],
+ x * Mat->m[0][1] + y * Mat->m[1][1] + z * Mat->m[2][1] + Mat->m[3][1],
+ x * Mat->m[0][2] + y * Mat->m[1][2] + z * Mat->m[2][2] + Mat->m[3][2]);
+ }
+
+ //! Operator for IcePoint *= Matrix3x3.
+ inline_ IcePoint& operator*=(const Matrix3x3& mat)
+ {
+ class ShadowMatrix3x3{ public: float m[3][3]; }; // To allow inlining
+ const ShadowMatrix3x3* Mat = (const ShadowMatrix3x3*)&mat;
+
+ float xp = x * Mat->m[0][0] + y * Mat->m[1][0] + z * Mat->m[2][0];
+ float yp = x * Mat->m[0][1] + y * Mat->m[1][1] + z * Mat->m[2][1];
+ float zp = x * Mat->m[0][2] + y * Mat->m[1][2] + z * Mat->m[2][2];
+
+ x = xp; y = yp; z = zp;
+
+ return *this;
+ }
+
+ //! Operator for IcePoint *= Matrix4x4.
+ inline_ IcePoint& operator*=(const Matrix4x4& mat)
+ {
+ class ShadowMatrix4x4{ public: float m[4][4]; }; // To allow inlining
+ const ShadowMatrix4x4* Mat = (const ShadowMatrix4x4*)&mat;
+
+ float xp = x * Mat->m[0][0] + y * Mat->m[1][0] + z * Mat->m[2][0] + Mat->m[3][0];
+ float yp = x * Mat->m[0][1] + y * Mat->m[1][1] + z * Mat->m[2][1] + Mat->m[3][1];
+ float zp = x * Mat->m[0][2] + y * Mat->m[1][2] + z * Mat->m[2][2] + Mat->m[3][2];
+
+ x = xp; y = yp; z = zp;
+
+ return *this;
+ }
+
+ // Cast operators
+
+ //! Cast a IcePoint to a HPoint. w is set to zero.
+ operator HPoint() const;
+
+ inline_ operator const float*() const { return &x; }
+ inline_ operator float*() { return &x; }
+
+ public:
+ float x, y, z;
+ };
+
+ FUNCTION ICEMATHS_API void Normalize1(IcePoint& a);
+ FUNCTION ICEMATHS_API void Normalize2(IcePoint& a);
+
+#endif //__ICEPOINT_H__
diff --git a/Opcode/OpcodeLib/Ice/IcePreprocessor.h b/Opcode/OpcodeLib/Ice/IcePreprocessor.h
new file mode 100644
index 0000000..bb0ef7b
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IcePreprocessor.h
@@ -0,0 +1,128 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains preprocessor stuff. This should be the first included header.
+ * \file IcePreprocessor.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEPREPROCESSOR_H__
+#define __ICEPREPROCESSOR_H__
+
+ // Check platform
+ #if defined( _WIN32 ) || defined( WIN32 )
+ #pragma message("Compiling on Windows...")
+ #define PLATFORM_WINDOWS
+ #else
+ #pragma message("Compiling on unknown platform...")
+ #endif
+
+ // Check compiler
+ #if defined(_MSC_VER)
+ #pragma message("Compiling with VC++...")
+ #define COMPILER_VISUAL_CPP
+ #else
+ #pragma message("Compiling with unknown compiler...")
+ #endif
+
+ // Check compiler options. If this file is included in user-apps, this
+ // shouldn't be needed, so that they can use what they like best.
+ #ifndef ICE_DONT_CHECK_COMPILER_OPTIONS
+ #ifdef COMPILER_VISUAL_CPP
+ #if defined(_CHAR_UNSIGNED)
+ #endif
+
+ #if defined(_CPPRTTI)
+ #error Please disable RTTI...
+ #endif
+
+ #if defined(_CPPUNWIND)
+ #error Please disable exceptions...
+ #endif
+
+ #if defined(_MT)
+ // Multithreading
+ #endif
+ #endif
+ #endif
+
+ // Check debug mode
+ #ifdef DEBUG // May be defined instead of _DEBUG. Let's fix it.
+ #ifndef _DEBUG
+ #define _DEBUG
+ #endif
+ #endif
+
+ #ifdef _DEBUG
+ // Here you may define items for debug builds
+ #endif
+
+ #ifndef THIS_FILE
+ #define THIS_FILE __FILE__
+ #endif
+
+ #ifndef ICE_NO_DLL
+ #ifdef ICECORE_EXPORTS
+ #define ICECORE_API __declspec(dllexport)
+ #else
+ #define ICECORE_API __declspec(dllimport)
+ #endif
+ #else
+ #define ICECORE_API
+ #endif
+
+ // Don't override new/delete
+// #define DEFAULT_NEWDELETE
+ #define DONT_TRACK_MEMORY_LEAKS
+
+ #define FUNCTION extern "C"
+
+ // Cosmetic stuff [mainly useful with multiple inheritance]
+ #define override(base_class) virtual
+
+ // Our own inline keyword, so that:
+ // - we can switch to __forceinline to check it's really better or not
+ // - we can remove __forceinline if the compiler doesn't support it
+// #define inline_ __forceinline
+// #define inline_ inline
+
+ // Contributed by Bruce Mitchener
+ #if defined(COMPILER_VISUAL_CPP)
+ #define inline_ __forceinline
+// #define inline_ inline
+ #elif defined(__GNUC__) && __GNUC__ < 3
+ #define inline_ inline
+ #elif defined(__GNUC__)
+ #define inline_ inline __attribute__ ((always_inline))
+ #else
+ #define inline_ inline
+ #endif
+
+ // Down the hatch
+ #pragma inline_depth( 255 )
+
+ #ifdef COMPILER_VISUAL_CPP
+ #pragma intrinsic(memcmp)
+ #pragma intrinsic(memcpy)
+ #pragma intrinsic(memset)
+ #pragma intrinsic(strcat)
+ #pragma intrinsic(strcmp)
+ #pragma intrinsic(strcpy)
+ #pragma intrinsic(strlen)
+ #pragma intrinsic(abs)
+ #pragma intrinsic(labs)
+ #endif
+
+ // ANSI compliance
+ #ifdef _DEBUG
+ // Remove painful warning in debug
+ inline_ bool __False__(){ return false; }
+ #define for if(__False__()){} else for
+ #else
+ #define for if(0){} else for
+ #endif
+
+#endif // __ICEPREPROCESSOR_H__
diff --git a/Opcode/OpcodeLib/Ice/IceRandom.cpp b/Opcode/OpcodeLib/Ice/IceRandom.cpp
new file mode 100644
index 0000000..0139be6
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceRandom.cpp
@@ -0,0 +1,35 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for random generators.
+ * \file IceRandom.cpp
+ * \author Pierre Terdiman
+ * \date August, 9, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceCore;
+
+void IceCore:: SRand(udword seed)
+{
+ srand(seed);
+}
+
+udword IceCore::Rand()
+{
+ return rand();
+}
+
+
+static BasicRandom gRandomGenerator(42);
+
+udword IceCore::GetRandomIndex(udword max_index)
+{
+ // We don't use rand() since it's limited to RAND_MAX
+ udword Index = gRandomGenerator.Randomize();
+ return Index % max_index;
+}
+
diff --git a/Opcode/OpcodeLib/Ice/IceRandom.h b/Opcode/OpcodeLib/Ice/IceRandom.h
new file mode 100644
index 0000000..3584769
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceRandom.h
@@ -0,0 +1,42 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for random generators.
+ * \file IceRandom.h
+ * \author Pierre Terdiman
+ * \date August, 9, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICERANDOM_H__
+#define __ICERANDOM_H__
+
+ FUNCTION ICECORE_API void SRand(udword seed);
+ FUNCTION ICECORE_API udword Rand();
+
+ //! Returns a unit random floating-point value
+ inline_ float UnitRandomFloat() { return float(Rand()) * ONE_OVER_RAND_MAX; }
+
+ //! Returns a random index so that 0<= index < max_index
+ ICECORE_API udword GetRandomIndex(udword max_index);
+
+ class ICECORE_API BasicRandom
+ {
+ public:
+
+ //! Constructor
+ inline_ BasicRandom(udword seed=0) : mRnd(seed) {}
+ //! Destructor
+ inline_ ~BasicRandom() {}
+
+ inline_ void SetSeed(udword seed) { mRnd = seed; }
+ inline_ udword GetCurrentValue() const { return mRnd; }
+ inline_ udword Randomize() { mRnd = mRnd * 2147001325 + 715136305; return mRnd; }
+
+ private:
+ udword mRnd;
+ };
+
+#endif // __ICERANDOM_H__
+
diff --git a/Opcode/OpcodeLib/Ice/IceRay.cpp b/Opcode/OpcodeLib/Ice/IceRay.cpp
new file mode 100644
index 0000000..20970ac
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceRay.cpp
@@ -0,0 +1,84 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for rays.
+ * \file IceRay.cpp
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Ray class.
+ * A ray is a half-line P(t) = mOrig + mDir * t, with 0 <= t <= +infinity
+ * \class Ray
+ * \author Pierre Terdiman
+ * \version 1.0
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+ O = Origin = impact point
+ i = normalized vector along the x axis
+ j = normalized vector along the y axis = actually the normal vector in O
+ D = Direction vector, norm |D| = 1
+ N = Projection of D on y axis, norm |N| = normal reaction
+ T = Projection of D on x axis, norm |T| = tangential reaction
+ R = Reflexion vector
+
+ ^y
+ |
+ |
+ |
+ _ _ _| _ _ _
+ * * *|
+ \ | /
+ \ |N / |
+ R\ | /D
+ \ | / |
+ \ | /
+ _________\|/______*_______>x
+ O T
+
+ Let define theta = angle between D and N. Then cos(theta) = |N| / |D| = |N| since D is normalized.
+
+ j|D = |j|*|D|*cos(theta) => |N| = j|D
+
+ Then we simply have:
+
+ D = N + T
+
+ To compute tangential reaction :
+
+ T = D - N
+
+ To compute reflexion vector :
+
+ R = N - T = N - (D-N) = 2*N - D
+*/
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+float Ray::SquareDistance(const IcePoint& point, float* t) const
+{
+ IcePoint Diff = point - mOrig;
+ float fT = Diff | mDir;
+
+ if(fT<=0.0f)
+ {
+ fT = 0.0f;
+ }
+ else
+ {
+ fT /= mDir.SquareMagnitude();
+ Diff -= fT*mDir;
+ }
+
+ if(t) *t = fT;
+
+ return Diff.SquareMagnitude();
+}
diff --git a/Opcode/OpcodeLib/Ice/IceRay.h b/Opcode/OpcodeLib/Ice/IceRay.h
new file mode 100644
index 0000000..c40552b
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceRay.h
@@ -0,0 +1,98 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for rays.
+ * \file IceRay.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICERAY_H__
+#define __ICERAY_H__
+
+ class ICEMATHS_API Ray
+ {
+ public:
+ //! Constructor
+ inline_ Ray() {}
+ //! Constructor
+ inline_ Ray(const IcePoint& orig, const IcePoint& dir) : mOrig(orig), mDir(dir) {}
+ //! Copy constructor
+ inline_ Ray(const Ray& ray) : mOrig(ray.mOrig), mDir(ray.mDir) {}
+ //! Destructor
+ inline_ ~Ray() {}
+
+ float SquareDistance(const IcePoint& point, float* t=null) const;
+ inline_ float Distance(const IcePoint& point, float* t=null) const { return sqrtf(SquareDistance(point, t)); }
+
+ IcePoint mOrig; //!< Ray origin
+ IcePoint mDir; //!< Normalized direction
+ };
+
+ inline_ void ComputeReflexionVector(IcePoint& reflected, const IcePoint& incoming_dir, const IcePoint& outward_normal)
+ {
+ reflected = incoming_dir - outward_normal * 2.0f * (incoming_dir|outward_normal);
+ }
+
+ inline_ void ComputeReflexionVector(IcePoint& reflected, const IcePoint& source, const IcePoint& impact, const IcePoint& normal)
+ {
+ IcePoint V = impact - source;
+ reflected = V - normal * 2.0f * (V|normal);
+ }
+
+ inline_ void DecomposeVector(IcePoint& normal_compo, IcePoint& tangent_compo, const IcePoint& outward_dir, const IcePoint& outward_normal)
+ {
+ normal_compo = outward_normal * (outward_dir|outward_normal);
+ tangent_compo = outward_dir - normal_compo;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Transforms a direction vector from world space to local space
+ * \param local_dir [out] direction vector in local space
+ * \param world_dir [in] direction vector in world space
+ * \param world [in] world transform
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void ComputeLocalDirection(IcePoint& local_dir, const IcePoint& world_dir, const Matrix4x4& world)
+ {
+ // Get world direction back in local space
+// Matrix3x3 InvWorld = world;
+// local_dir = InvWorld * world_dir;
+ local_dir = Matrix3x3(world) * world_dir;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Transforms a position vector from world space to local space
+ * \param local_pt [out] position vector in local space
+ * \param world_pt [in] position vector in world space
+ * \param world [in] world transform
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void ComputeLocalPoint(IcePoint& local_pt, const IcePoint& world_pt, const Matrix4x4& world)
+ {
+ // Get world vertex back in local space
+ Matrix4x4 InvWorld = world;
+ InvWorld.Invert();
+ local_pt = world_pt * InvWorld;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Transforms a ray from world space to local space
+ * \param local_ray [out] ray in local space
+ * \param world_ray [in] ray in world space
+ * \param world [in] world transform
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void ComputeLocalRay(Ray& local_ray, const Ray& world_ray, const Matrix4x4& world)
+ {
+ // Get world ray back in local space
+ ComputeLocalDirection(local_ray.mDir, world_ray.mDir, world);
+ ComputeLocalPoint(local_ray.mOrig, world_ray.mOrig, world);
+ }
+
+#endif // __ICERAY_H__
diff --git a/Opcode/OpcodeLib/Ice/IceRevisitedRadix.cpp b/Opcode/OpcodeLib/Ice/IceRevisitedRadix.cpp
new file mode 100644
index 0000000..f55b0cf
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceRevisitedRadix.cpp
@@ -0,0 +1,520 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains source code from the article "Radix Sort Revisited".
+ * \file IceRevisitedRadix.cpp
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Revisited Radix Sort.
+ * This is my new radix routine:
+ * - it uses indices and doesn't recopy the values anymore, hence wasting less ram
+ * - it creates all the histograms in one run instead of four
+ * - it sorts words faster than dwords and bytes faster than words
+ * - it correctly sorts negative floating-point values by patching the offsets
+ * - it automatically takes advantage of temporal coherence
+ * - multiple keys support is a side effect of temporal coherence
+ * - it may be worth recoding in asm... (mainly to use FCOMI, FCMOV, etc) [it's probably memory-bound anyway]
+ *
+ * History:
+ * - 08.15.98: very first version
+ * - 04.04.00: recoded for the radix article
+ * - 12.xx.00: code lifting
+ * - 09.18.01: faster CHECK_PASS_VALIDITY thanks to Mark D. Shattuck (who provided other tips, not included here)
+ * - 10.11.01: added local ram support
+ * - 01.20.02: bugfix! In very particular cases the last pass was skipped in the float code-path, leading to incorrect sorting......
+ * - 01.02.02: - "mIndices" renamed => "mRanks". That's a rank sorter after all.
+ * - ranks are not "reset" anymore, but implicit on first calls
+ * - 07.05.02: - offsets rewritten with one less indirection.
+ * - 11.03.02: - "bool" replaced with RadixHint enum
+ *
+ * \class RadixSort
+ * \author Pierre Terdiman
+ * \version 1.4
+ * \date August, 15, 1998
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+To do:
+ - add an offset parameter between two input values (avoid some data recopy sometimes)
+ - unroll ? asm ?
+ - 11 bits trick & 3 passes as Michael did
+ - prefetch stuff the day I have a P3
+ - make a version with 16-bits indices ?
+*/
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceCore;
+
+#define INVALIDATE_RANKS mCurrentSize|=0x80000000
+#define VALIDATE_RANKS mCurrentSize&=0x7fffffff
+#define CURRENT_SIZE (mCurrentSize&0x7fffffff)
+#define INVALID_RANKS (mCurrentSize&0x80000000)
+
+#define CHECK_RESIZE(n) \
+ if(n!=mPreviousSize) \
+ { \
+ if(n>mCurrentSize) Resize(n); \
+ else ResetRanks(); \
+ mPreviousSize = n; \
+ }
+
+#define CREATE_HISTOGRAMS(type, buffer) \
+ /* Clear counters/histograms */ \
+ ZeroMemory(mHistogram, 256*4*sizeof(udword)); \
+ \
+ /* Prepare to count */ \
+ ubyte* p = (ubyte*)input; \
+ ubyte* pe = &p[nb*4]; \
+ udword* h0= &mHistogram[0]; /* Histogram for first pass (LSB) */ \
+ udword* h1= &mHistogram[256]; /* Histogram for second pass */ \
+ udword* h2= &mHistogram[512]; /* Histogram for third pass */ \
+ udword* h3= &mHistogram[768]; /* Histogram for last pass (MSB) */ \
+ \
+ bool AlreadySorted = true; /* Optimism... */ \
+ \
+ if(INVALID_RANKS) \
+ { \
+ /* Prepare for temporal coherence */ \
+ type* Running = (type*)buffer; \
+ type PrevVal = *Running; \
+ \
+ while(p!=pe) \
+ { \
+ /* Read input buffer in previous sorted order */ \
+ type Val = *Running++; \
+ /* Check whether already sorted or not */ \
+ if(Val<PrevVal) { AlreadySorted = false; break; } /* Early out */ \
+ /* Update for next iteration */ \
+ PrevVal = Val; \
+ \
+ /* Create histograms */ \
+ h0[*p++]++; h1[*p++]++; h2[*p++]++; h3[*p++]++; \
+ } \
+ \
+ /* If all input values are already sorted, we just have to return and leave the */ \
+ /* previous list unchanged. That way the routine may take advantage of temporal */ \
+ /* coherence, for example when used to sort transparent faces. */ \
+ if(AlreadySorted) \
+ { \
+ mNbHits++; \
+ for(udword i=0;i<nb;i++) mRanks[i] = i; \
+ return *this; \
+ } \
+ } \
+ else \
+ { \
+ /* Prepare for temporal coherence */ \
+ udword* Indices = mRanks; \
+ type PrevVal = (type)buffer[*Indices]; \
+ \
+ while(p!=pe) \
+ { \
+ /* Read input buffer in previous sorted order */ \
+ type Val = (type)buffer[*Indices++]; \
+ /* Check whether already sorted or not */ \
+ if(Val<PrevVal) { AlreadySorted = false; break; } /* Early out */ \
+ /* Update for next iteration */ \
+ PrevVal = Val; \
+ \
+ /* Create histograms */ \
+ h0[*p++]++; h1[*p++]++; h2[*p++]++; h3[*p++]++; \
+ } \
+ \
+ /* If all input values are already sorted, we just have to return and leave the */ \
+ /* previous list unchanged. That way the routine may take advantage of temporal */ \
+ /* coherence, for example when used to sort transparent faces. */ \
+ if(AlreadySorted) { mNbHits++; return *this; } \
+ } \
+ \
+ /* Else there has been an early out and we must finish computing the histograms */ \
+ while(p!=pe) \
+ { \
+ /* Create histograms without the previous overhead */ \
+ h0[*p++]++; h1[*p++]++; h2[*p++]++; h3[*p++]++; \
+ }
+
+#define CHECK_PASS_VALIDITY(pass) \
+ /* Shortcut to current counters */ \
+ udword* CurCount = &mHistogram[pass<<8]; \
+ \
+ /* Reset flag. The sorting pass is supposed to be performed. (default) */ \
+ bool PerformPass = true; \
+ \
+ /* Check pass validity */ \
+ \
+ /* If all values have the same byte, sorting is useless. */ \
+ /* It may happen when sorting bytes or words instead of dwords. */ \
+ /* This routine actually sorts words faster than dwords, and bytes */ \
+ /* faster than words. Standard running time (O(4*n))is reduced to O(2*n) */ \
+ /* for words and O(n) for bytes. Running time for floats depends on actual values... */ \
+ \
+ /* Get first byte */ \
+ ubyte UniqueVal = *(((ubyte*)input)+pass); \
+ \
+ /* Check that byte's counter */ \
+ if(CurCount[UniqueVal]==nb) PerformPass=false;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+RadixSort::RadixSort() : mRanks(null), mRanks2(null), mCurrentSize(0), mTotalCalls(0), mNbHits(0)
+{
+#ifndef RADIX_LOCAL_RAM
+ // Allocate input-independent ram
+ mHistogram = new udword[256*4];
+ mOffset = new udword[256];
+#endif
+ // Initialize indices
+ INVALIDATE_RANKS;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+RadixSort::~RadixSort()
+{
+ // Release everything
+#ifndef RADIX_LOCAL_RAM
+ DELETEARRAY(mOffset);
+ DELETEARRAY(mHistogram);
+#endif
+ DELETEARRAY(mRanks2);
+ DELETEARRAY(mRanks);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Resizes the inner lists.
+ * \param nb [in] new size (number of dwords)
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool RadixSort::Resize(udword nb)
+{
+ // Free previously used ram
+ DELETEARRAY(mRanks2);
+ DELETEARRAY(mRanks);
+
+ // Get some fresh one
+ mRanks = new udword[nb]; CHECKALLOC(mRanks);
+ mRanks2 = new udword[nb]; CHECKALLOC(mRanks2);
+
+ return true;
+}
+
+inline_ void RadixSort::CheckResize(udword nb)
+{
+ udword CurSize = CURRENT_SIZE;
+ if(nb!=CurSize)
+ {
+ if(nb>CurSize) Resize(nb);
+ mCurrentSize = nb;
+ INVALIDATE_RANKS;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Main sort routine.
+ * This one is for integer values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data.
+ * \param input [in] a list of integer values to sort
+ * \param nb [in] number of values to sort, must be < 2^31
+ * \param hint [in] RADIX_SIGNED to handle negative values, RADIX_UNSIGNED if you know your input buffer only contains positive values
+ * \return Self-Reference
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+RadixSort& RadixSort::Sort(const udword* input, udword nb, RadixHint hint)
+{
+ // Checkings
+ if(!input || !nb || nb&0x80000000) return *this;
+
+ // Stats
+ mTotalCalls++;
+
+ // Resize lists if needed
+ CheckResize(nb);
+
+#ifdef RADIX_LOCAL_RAM
+ // Allocate histograms & offsets on the stack
+ udword mHistogram[256*4];
+// udword mOffset[256];
+ udword* mLink[256];
+#endif
+
+ // Create histograms (counters). Counters for all passes are created in one run.
+ // Pros: read input buffer once instead of four times
+ // Cons: mHistogram is 4Kb instead of 1Kb
+ // We must take care of signed/unsigned values for temporal coherence.... I just
+ // have 2 code paths even if just a single opcode changes. Self-modifying code, someone?
+ if(hint==RADIX_UNSIGNED) { CREATE_HISTOGRAMS(udword, input); }
+ else { CREATE_HISTOGRAMS(sdword, input); }
+
+ // Compute #negative values involved if needed
+ udword NbNegativeValues = 0;
+ if(hint==RADIX_SIGNED)
+ {
+ // An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128
+ // last values of the last histogram. Last histogram because that's the one for the Most Significant Byte,
+ // responsible for the sign. 128 last values because the 128 first ones are related to positive numbers.
+ udword* h3= &mHistogram[768];
+ for(udword i=128;i<256;i++) NbNegativeValues += h3[i]; // 768 for last histogram, 128 for negative part
+ }
+
+ // Radix sort, j is the pass number (0=LSB, 3=MSB)
+ for(udword j=0;j<4;j++)
+ {
+ CHECK_PASS_VALIDITY(j);
+
+ // Sometimes the fourth (negative) pass is skipped because all numbers are negative and the MSB is 0xFF (for example). This is
+ // not a problem, numbers are correctly sorted anyway.
+ if(PerformPass)
+ {
+ // Should we care about negative values?
+ if(j!=3 || hint==RADIX_UNSIGNED)
+ {
+ // Here we deal with positive values only
+
+ // Create offsets
+// mOffset[0] = 0;
+// for(udword i=1;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1];
+ mLink[0] = mRanks2;
+ for(udword i=1;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1];
+ }
+ else
+ {
+ // This is a special case to correctly handle negative integers. They're sorted in the right order but at the wrong place.
+
+ // Create biased offsets, in order for negative numbers to be sorted as well
+// mOffset[0] = NbNegativeValues; // First positive number takes place after the negative ones
+ mLink[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones
+// for(udword i=1;i<128;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers
+ for(udword i=1;i<128;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers
+
+ // Fixing the wrong place for negative values
+// mOffset[128] = 0;
+ mLink[128] = mRanks2;
+// for(i=129;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1];
+ for(udword i=129;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1];
+ }
+
+ // Perform Radix Sort
+ ubyte* InputBytes = (ubyte*)input;
+ InputBytes += j;
+ if(INVALID_RANKS)
+ {
+// for(udword i=0;i<nb;i++) mRanks2[mOffset[InputBytes[i<<2]]++] = i;
+ for(udword i=0;i<nb;i++) *mLink[InputBytes[i<<2]]++ = i;
+ VALIDATE_RANKS;
+ }
+ else
+ {
+ udword* Indices = mRanks;
+ udword* IndicesEnd = &mRanks[nb];
+ while(Indices!=IndicesEnd)
+ {
+ udword id = *Indices++;
+// mRanks2[mOffset[InputBytes[id<<2]]++] = id;
+ *mLink[InputBytes[id<<2]]++ = id;
+ }
+ }
+
+ // Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
+ udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
+ }
+ }
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Main sort routine.
+ * This one is for floating-point values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data.
+ * \param input [in] a list of floating-point values to sort
+ * \param nb [in] number of values to sort, must be < 2^31
+ * \return Self-Reference
+ * \warning only sorts IEEE floating-point values
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+RadixSort& RadixSort::Sort(const float* input2, udword nb)
+{
+ // Checkings
+ if(!input2 || !nb || nb&0x80000000) return *this;
+
+ // Stats
+ mTotalCalls++;
+
+ udword* input = (udword*)input2;
+
+ // Resize lists if needed
+ CheckResize(nb);
+
+#ifdef RADIX_LOCAL_RAM
+ // Allocate histograms & offsets on the stack
+ udword mHistogram[256*4];
+// udword mOffset[256];
+ udword* mLink[256];
+#endif
+
+ // Create histograms (counters). Counters for all passes are created in one run.
+ // Pros: read input buffer once instead of four times
+ // Cons: mHistogram is 4Kb instead of 1Kb
+ // Floating-point values are always supposed to be signed values, so there's only one code path there.
+ // Please note the floating point comparison needed for temporal coherence! Although the resulting asm code
+ // is dreadful, this is surprisingly not such a performance hit - well, I suppose that's a big one on first
+ // generation Pentiums....We can't make comparison on integer representations because, as Chris said, it just
+ // wouldn't work with mixed positive/negative values....
+ { CREATE_HISTOGRAMS(float, input2); }
+
+ // Compute #negative values involved if needed
+ udword NbNegativeValues = 0;
+ // An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128
+ // last values of the last histogram. Last histogram because that's the one for the Most Significant Byte,
+ // responsible for the sign. 128 last values because the 128 first ones are related to positive numbers.
+ udword* h3= &mHistogram[768];
+ for(udword i=128;i<256;i++) NbNegativeValues += h3[i]; // 768 for last histogram, 128 for negative part
+
+ // Radix sort, j is the pass number (0=LSB, 3=MSB)
+ for(udword j=0;j<4;j++)
+ {
+ // Should we care about negative values?
+ if(j!=3)
+ {
+ // Here we deal with positive values only
+ CHECK_PASS_VALIDITY(j);
+
+ if(PerformPass)
+ {
+ // Create offsets
+// mOffset[0] = 0;
+ mLink[0] = mRanks2;
+// for(udword i=1;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1];
+ for(udword i=1;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1];
+
+ // Perform Radix Sort
+ ubyte* InputBytes = (ubyte*)input;
+ InputBytes += j;
+ if(INVALID_RANKS)
+ {
+// for(i=0;i<nb;i++) mRanks2[mOffset[InputBytes[i<<2]]++] = i;
+ for(udword i=0;i<nb;i++) *mLink[InputBytes[i<<2]]++ = i;
+ VALIDATE_RANKS;
+ }
+ else
+ {
+ udword* Indices = mRanks;
+ udword* IndicesEnd = &mRanks[nb];
+ while(Indices!=IndicesEnd)
+ {
+ udword id = *Indices++;
+// mRanks2[mOffset[InputBytes[id<<2]]++] = id;
+ *mLink[InputBytes[id<<2]]++ = id;
+ }
+ }
+
+ // Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
+ udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
+ }
+ }
+ else
+ {
+ // This is a special case to correctly handle negative values
+ CHECK_PASS_VALIDITY(j);
+
+ if(PerformPass)
+ {
+ // Create biased offsets, in order for negative numbers to be sorted as well
+// mOffset[0] = NbNegativeValues; // First positive number takes place after the negative ones
+ mLink[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones
+// for(udword i=1;i<128;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers
+ for(udword i=1;i<128;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers
+
+ // We must reverse the sorting order for negative numbers!
+// mOffset[255] = 0;
+ mLink[255] = mRanks2;
+// for(i=0;i<127;i++) mOffset[254-i] = mOffset[255-i] + CurCount[255-i]; // Fixing the wrong order for negative values
+ for(udword i=0;i<127;i++) mLink[254-i] = mLink[255-i] + CurCount[255-i]; // Fixing the wrong order for negative values
+// for(i=128;i<256;i++) mOffset[i] += CurCount[i]; // Fixing the wrong place for negative values
+ for(udword i=128;i<256;i++) mLink[i] += CurCount[i]; // Fixing the wrong place for negative values
+
+ // Perform Radix Sort
+ if(INVALID_RANKS)
+ {
+ for(udword i=0;i<nb;i++)
+ {
+ udword Radix = input[i]>>24; // Radix byte, same as above. AND is useless here (udword).
+ // ### cmp to be killed. Not good. Later.
+// if(Radix<128) mRanks2[mOffset[Radix]++] = i; // Number is positive, same as above
+// else mRanks2[--mOffset[Radix]] = i; // Number is negative, flip the sorting order
+ if(Radix<128) *mLink[Radix]++ = i; // Number is positive, same as above
+ else *(--mLink[Radix]) = i; // Number is negative, flip the sorting order
+ }
+ VALIDATE_RANKS;
+ }
+ else
+ {
+ for(udword i=0;i<nb;i++)
+ {
+ udword Radix = input[mRanks[i]]>>24; // Radix byte, same as above. AND is useless here (udword).
+ // ### cmp to be killed. Not good. Later.
+// if(Radix<128) mRanks2[mOffset[Radix]++] = mRanks[i]; // Number is positive, same as above
+// else mRanks2[--mOffset[Radix]] = mRanks[i]; // Number is negative, flip the sorting order
+ if(Radix<128) *mLink[Radix]++ = mRanks[i]; // Number is positive, same as above
+ else *(--mLink[Radix]) = mRanks[i]; // Number is negative, flip the sorting order
+ }
+ }
+ // Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
+ udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
+ }
+ else
+ {
+ // The pass is useless, yet we still have to reverse the order of current list if all values are negative.
+ if(UniqueVal>=128)
+ {
+ if(INVALID_RANKS)
+ {
+ // ###Possible?
+ for(udword i=0;i<nb;i++) mRanks2[i] = nb-i-1;
+ VALIDATE_RANKS;
+ }
+ else
+ {
+ for(udword i=0;i<nb;i++) mRanks2[i] = mRanks[nb-i-1];
+ }
+
+ // Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
+ udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
+ }
+ }
+ }
+ }
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the ram used.
+ * \return memory used in bytes
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword RadixSort::GetUsedRam() const
+{
+ udword UsedRam = sizeof(RadixSort);
+#ifndef RADIX_LOCAL_RAM
+ UsedRam += 256*4*sizeof(udword); // Histograms
+ UsedRam += 256*sizeof(udword); // Offsets
+#endif
+ UsedRam += 2*CURRENT_SIZE*sizeof(udword); // 2 lists of indices
+ return UsedRam;
+}
diff --git a/Opcode/OpcodeLib/Ice/IceRevisitedRadix.h b/Opcode/OpcodeLib/Ice/IceRevisitedRadix.h
new file mode 100644
index 0000000..ec2f6b1
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceRevisitedRadix.h
@@ -0,0 +1,65 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains source code from the article "Radix Sort Revisited".
+ * \file IceRevisitedRadix.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICERADIXSORT_H__
+#define __ICERADIXSORT_H__
+
+ //! Allocate histograms & offsets locally
+ #define RADIX_LOCAL_RAM
+
+ enum RadixHint
+ {
+ RADIX_SIGNED, //!< Input values are signed
+ RADIX_UNSIGNED, //!< Input values are unsigned
+
+ RADIX_FORCE_DWORD = 0x7fffffff
+ };
+
+ class ICECORE_API RadixSort
+ {
+ public:
+ // Constructor/Destructor
+ RadixSort();
+ ~RadixSort();
+ // Sorting methods
+ RadixSort& Sort(const udword* input, udword nb, RadixHint hint=RADIX_SIGNED);
+ RadixSort& Sort(const float* input, udword nb);
+
+ //! Access to results. mRanks is a list of indices in sorted order, i.e. in the order you may further process your data
+ inline_ const udword* GetRanks() const { return mRanks; }
+
+ //! mIndices2 gets trashed on calling the sort routine, but otherwise you can recycle it the way you want.
+ inline_ udword* GetRecyclable() const { return mRanks2; }
+
+ // Stats
+ udword GetUsedRam() const;
+ //! Returns the total number of calls to the radix sorter.
+ inline_ udword GetNbTotalCalls() const { return mTotalCalls; }
+ //! Returns the number of eraly exits due to temporal coherence.
+ inline_ udword GetNbHits() const { return mNbHits; }
+
+ private:
+#ifndef RADIX_LOCAL_RAM
+ udword* mHistogram; //!< Counters for each byte
+ udword* mOffset; //!< Offsets (nearly a cumulative distribution function)
+#endif
+ udword mCurrentSize; //!< Current size of the indices list
+ udword* mRanks; //!< Two lists, swapped each pass
+ udword* mRanks2;
+ // Stats
+ udword mTotalCalls; //!< Total number of calls to the sort routine
+ udword mNbHits; //!< Number of early exits due to coherence
+ // Internal methods
+ void CheckResize(udword nb);
+ bool Resize(udword nb);
+ };
+
+#endif // __ICERADIXSORT_H__
diff --git a/Opcode/OpcodeLib/Ice/IceSegment.cpp b/Opcode/OpcodeLib/Ice/IceSegment.cpp
new file mode 100644
index 0000000..ae99384
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceSegment.cpp
@@ -0,0 +1,57 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for segments.
+ * \file IceSegment.cpp
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * IceSegment class.
+ * A segment is defined by S(t) = mP0 * (1 - t) + mP1 * t, with 0 <= t <= 1
+ * Alternatively, a segment is S(t) = Origin + t * Direction for 0 <= t <= 1.
+ * Direction is not necessarily unit length. The end points are Origin = mP0 and Origin + Direction = mP1.
+ *
+ * \class IceSegment
+ * \author Pierre Terdiman
+ * \version 1.0
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+float IceSegment::SquareDistance(const IcePoint& point, float* t) const
+{
+ IcePoint Diff = point - mP0;
+ IcePoint Dir = mP1 - mP0;
+ float fT = Diff | Dir;
+
+ if(fT<=0.0f)
+ {
+ fT = 0.0f;
+ }
+ else
+ {
+ float SqrLen= Dir.SquareMagnitude();
+ if(fT>=SqrLen)
+ {
+ fT = 1.0f;
+ Diff -= Dir;
+ }
+ else
+ {
+ fT /= SqrLen;
+ Diff -= fT*Dir;
+ }
+ }
+
+ if(t) *t = fT;
+
+ return Diff.SquareMagnitude();
+}
diff --git a/Opcode/OpcodeLib/Ice/IceSegment.h b/Opcode/OpcodeLib/Ice/IceSegment.h
new file mode 100644
index 0000000..1ddaa1a
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceSegment.h
@@ -0,0 +1,55 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for segments.
+ * \file IceSegment.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICESEGMENT_H__
+#define __ICESEGMENT_H__
+
+ class ICEMATHS_API IceSegment
+ {
+ public:
+ //! Constructor
+ inline_ IceSegment() {}
+ //! Constructor
+ inline_ IceSegment(const IcePoint& p0, const IcePoint& p1) : mP0(p0), mP1(p1) {}
+ //! Copy constructor
+ inline_ IceSegment(const IceSegment& seg) : mP0(seg.mP0), mP1(seg.mP1) {}
+ //! Destructor
+ inline_ ~IceSegment() {}
+
+ inline_ const IcePoint& GetOrigin() const { return mP0; }
+ inline_ IcePoint ComputeDirection() const { return mP1 - mP0; }
+ inline_ void ComputeDirection(IcePoint& dir) const { dir = mP1 - mP0; }
+ inline_ float ComputeLength() const { return mP1.Distance(mP0); }
+ inline_ float ComputeSquareLength() const { return mP1.SquareDistance(mP0); }
+
+ inline_ void SetOriginDirection(const IcePoint& origin, const IcePoint& direction)
+ {
+ mP0 = mP1 = origin;
+ mP1 += direction;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes a point on the segment
+ * \param pt [out] point on segment
+ * \param t [in] point's parameter [t=0 => pt = mP0, t=1 => pt = mP1]
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void ComputePoint(IcePoint& pt, float t) const { pt = mP0 + t * (mP1 - mP0); }
+
+ float SquareDistance(const IcePoint& point, float* t=null) const;
+ inline_ float Distance(const IcePoint& point, float* t=null) const { return sqrtf(SquareDistance(point, t)); }
+
+ IcePoint mP0; //!< Start of segment
+ IcePoint mP1; //!< End of segment
+ };
+
+#endif // __ICESEGMENT_H__
diff --git a/Opcode/OpcodeLib/Ice/IceTriangle.cpp b/Opcode/OpcodeLib/Ice/IceTriangle.cpp
new file mode 100644
index 0000000..0b8c6d5
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceTriangle.cpp
@@ -0,0 +1,286 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a handy triangle class.
+ * \file IceTriangle.cpp
+ * \author Pierre Terdiman
+ * \date January, 17, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceMaths;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a triangle class.
+ *
+ * \class Tri
+ * \author Pierre Terdiman
+ * \version 1.0
+ * \date 08.15.98
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static sdword VPlaneSideEps(const IcePoint& v, const IcePlane& plane, float epsilon)
+{
+ // Compute distance from current vertex to the plane
+ float Dist = plane.Distance(v);
+ // Compute side:
+ // 1 = the vertex is on the positive side of the plane
+ // -1 = the vertex is on the negative side of the plane
+ // 0 = the vertex is on the plane (within epsilon)
+ return Dist > epsilon ? 1 : Dist < -epsilon ? -1 : 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Flips the winding order.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Triangle::Flip()
+{
+ IcePoint Tmp = mVerts[1];
+ mVerts[1] = mVerts[2];
+ mVerts[2] = Tmp;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle area.
+ * \return the area
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float Triangle::Area() const
+{
+ const IcePoint& p0 = mVerts[0];
+ const IcePoint& p1 = mVerts[1];
+ const IcePoint& p2 = mVerts[2];
+ return ((p0 - p1)^(p0 - p2)).Magnitude() * 0.5f;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle perimeter.
+ * \return the perimeter
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float Triangle::Perimeter() const
+{
+ const IcePoint& p0 = mVerts[0];
+ const IcePoint& p1 = mVerts[1];
+ const IcePoint& p2 = mVerts[2];
+ return p0.Distance(p1)
+ + p0.Distance(p2)
+ + p1.Distance(p2);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle compacity.
+ * \return the compacity
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float Triangle::Compacity() const
+{
+ float P = Perimeter();
+ if(P==0.0f) return 0.0f;
+ return (4.0f*PI*Area()/(P*P));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle normal.
+ * \param normal [out] the computed normal
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Triangle::Normal(IcePoint& normal) const
+{
+ const IcePoint& p0 = mVerts[0];
+ const IcePoint& p1 = mVerts[1];
+ const IcePoint& p2 = mVerts[2];
+ normal = ((p0 - p1)^(p0 - p2)).Normalize();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle denormalized normal.
+ * \param normal [out] the computed normal
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Triangle::DenormalizedNormal(IcePoint& normal) const
+{
+ const IcePoint& p0 = mVerts[0];
+ const IcePoint& p1 = mVerts[1];
+ const IcePoint& p2 = mVerts[2];
+ normal = ((p0 - p1)^(p0 - p2));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle center.
+ * \param center [out] the computed center
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Triangle::Center(IcePoint& center) const
+{
+ const IcePoint& p0 = mVerts[0];
+ const IcePoint& p1 = mVerts[1];
+ const IcePoint& p2 = mVerts[2];
+ center = (p0 + p1 + p2)*INV3;
+}
+
+PartVal Triangle::TestAgainstPlane(const IcePlane& plane, float epsilon) const
+{
+ bool Pos = false, Neg = false;
+
+ // Loop through all vertices
+ for(udword i=0;i<3;i++)
+ {
+ // Compute side:
+ sdword Side = VPlaneSideEps(mVerts[i], plane, epsilon);
+
+ if (Side < 0) Neg = true;
+ else if (Side > 0) Pos = true;
+ }
+
+ if (!Pos && !Neg) return TRI_ON_PLANE;
+ else if (Pos && Neg) return TRI_INTERSECT;
+ else if (Pos && !Neg) return TRI_PLUS_SPACE;
+ else if (!Pos && Neg) return TRI_MINUS_SPACE;
+
+ // What?!
+ return TRI_FORCEDWORD;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle moment.
+ * \param m [out] the moment
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+void Triangle::ComputeMoment(Moment& m)
+{
+ // Compute the area of the triangle
+ m.mArea = Area();
+
+ // Compute the centroid
+ Center(m.mCentroid);
+
+ // Second-order components. Handle zero-area faces.
+ IcePoint& p = mVerts[0];
+ IcePoint& q = mVerts[1];
+ IcePoint& r = mVerts[2];
+ if(m.mArea==0.0f)
+ {
+ // This triangle has zero area. The second order components would be eliminated with the usual formula, so, for the
+ // sake of robustness we use an alternative form. These are the centroid and second-order components of the triangle's vertices.
+ m.mCovariance.m[0][0] = (p.x*p.x + q.x*q.x + r.x*r.x);
+ m.mCovariance.m[0][1] = (p.x*p.y + q.x*q.y + r.x*r.y);
+ m.mCovariance.m[0][2] = (p.x*p.z + q.x*q.z + r.x*r.z);
+ m.mCovariance.m[1][1] = (p.y*p.y + q.y*q.y + r.y*r.y);
+ m.mCovariance.m[1][2] = (p.y*p.z + q.y*q.z + r.y*r.z);
+ m.mCovariance.m[2][2] = (p.z*p.z + q.z*q.z + r.z*r.z);
+ m.mCovariance.m[2][1] = m.mCovariance.m[1][2];
+ m.mCovariance.m[1][0] = m.mCovariance.m[0][1];
+ m.mCovariance.m[2][0] = m.mCovariance.m[0][2];
+ }
+ else
+ {
+ const float OneOverTwelve = 1.0f / 12.0f;
+ m.mCovariance.m[0][0] = m.mArea * (9.0f * m.mCentroid.x*m.mCentroid.x + p.x*p.x + q.x*q.x + r.x*r.x) * OneOverTwelve;
+ m.mCovariance.m[0][1] = m.mArea * (9.0f * m.mCentroid.x*m.mCentroid.y + p.x*p.y + q.x*q.y + r.x*r.y) * OneOverTwelve;
+ m.mCovariance.m[1][1] = m.mArea * (9.0f * m.mCentroid.y*m.mCentroid.y + p.y*p.y + q.y*q.y + r.y*r.y) * OneOverTwelve;
+ m.mCovariance.m[0][2] = m.mArea * (9.0f * m.mCentroid.x*m.mCentroid.z + p.x*p.z + q.x*q.z + r.x*r.z) * OneOverTwelve;
+ m.mCovariance.m[1][2] = m.mArea * (9.0f * m.mCentroid.y*m.mCentroid.z + p.y*p.z + q.y*q.z + r.y*r.z) * OneOverTwelve;
+ m.mCovariance.m[2][2] = m.mArea * (9.0f * m.mCentroid.z*m.mCentroid.z + p.z*p.z + q.z*q.z + r.z*r.z) * OneOverTwelve;
+ m.mCovariance.m[2][1] = m.mCovariance.m[1][2];
+ m.mCovariance.m[1][0] = m.mCovariance.m[0][1];
+ m.mCovariance.m[2][0] = m.mCovariance.m[0][2];
+ }
+}
+*/
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle's smallest edge length.
+ * \return the smallest edge length
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float Triangle::MinEdgeLength() const
+{
+ float Min = MAX_FLOAT;
+ float Length01 = mVerts[0].Distance(mVerts[1]);
+ float Length02 = mVerts[0].Distance(mVerts[2]);
+ float Length12 = mVerts[1].Distance(mVerts[2]);
+ if(Length01 < Min) Min = Length01;
+ if(Length02 < Min) Min = Length02;
+ if(Length12 < Min) Min = Length12;
+ return Min;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the triangle's largest edge length.
+ * \return the largest edge length
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float Triangle::MaxEdgeLength() const
+{
+ float Max = MIN_FLOAT;
+ float Length01 = mVerts[0].Distance(mVerts[1]);
+ float Length02 = mVerts[0].Distance(mVerts[2]);
+ float Length12 = mVerts[1].Distance(mVerts[2]);
+ if(Length01 > Max) Max = Length01;
+ if(Length02 > Max) Max = Length02;
+ if(Length12 > Max) Max = Length12;
+ return Max;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes a point on the triangle according to the stabbing information.
+ * \param u,v [in] point's barycentric coordinates
+ * \param pt [out] point on triangle
+ * \param nearvtx [out] index of nearest vertex
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Triangle::ComputePoint(float u, float v, IcePoint& pt, udword* nearvtx) const
+{
+ // Compute point coordinates
+ pt = (1.0f - u - v)*mVerts[0] + u*mVerts[1] + v*mVerts[2];
+
+ // Compute nearest vertex if needed
+ if(nearvtx)
+ {
+ // Compute distance vector
+ IcePoint d(mVerts[0].SquareDistance(pt), // Distance^2 from vertex 0 to point on the face
+ mVerts[1].SquareDistance(pt), // Distance^2 from vertex 1 to point on the face
+ mVerts[2].SquareDistance(pt)); // Distance^2 from vertex 2 to point on the face
+
+ // Get smallest distance
+ *nearvtx = d.SmallestAxis();
+ }
+}
+
+void Triangle::Inflate(float fat_coeff, bool constant_border)
+{
+ // Compute triangle center
+ IcePoint TriangleCenter;
+ Center(TriangleCenter);
+
+ // Don't normalize?
+ // Normalize => add a constant border, regardless of triangle size
+ // Don't => add more to big triangles
+ for(udword i=0;i<3;i++)
+ {
+ IcePoint v = mVerts[i] - TriangleCenter;
+
+ if(constant_border) v.Normalize();
+
+ mVerts[i] += v * fat_coeff;
+ }
+}
diff --git a/Opcode/OpcodeLib/Ice/IceTriangle.h b/Opcode/OpcodeLib/Ice/IceTriangle.h
new file mode 100644
index 0000000..c6a9a87
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceTriangle.h
@@ -0,0 +1,68 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a handy triangle class.
+ * \file IceTriangle.h
+ * \author Pierre Terdiman
+ * \date January, 17, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICETRIANGLE_H__
+#define __ICETRIANGLE_H__
+
+ // Forward declarations
+ class Moment;
+
+ // Partitioning values
+ enum PartVal
+ {
+ TRI_MINUS_SPACE = 0, //!< Triangle is in the negative space
+ TRI_PLUS_SPACE = 1, //!< Triangle is in the positive space
+ TRI_INTERSECT = 2, //!< Triangle intersects plane
+ TRI_ON_PLANE = 3, //!< Triangle and plane are coplanar
+
+ TRI_FORCEDWORD = 0x7fffffff
+ };
+
+ // A triangle class.
+ class ICEMATHS_API Triangle
+ {
+ public:
+ //! Constructor
+ inline_ Triangle() {}
+ //! Constructor
+ inline_ Triangle(const IcePoint& p0, const IcePoint& p1, const IcePoint& p2) { mVerts[0]=p0; mVerts[1]=p1; mVerts[2]=p2; }
+ //! Copy constructor
+ inline_ Triangle(const Triangle& triangle)
+ {
+ mVerts[0] = triangle.mVerts[0];
+ mVerts[1] = triangle.mVerts[1];
+ mVerts[2] = triangle.mVerts[2];
+ }
+ //! Destructor
+ inline_ ~Triangle() {}
+ //! Vertices
+ IcePoint mVerts[3];
+
+ // Methods
+ void Flip();
+ float Area() const;
+ float Perimeter() const;
+ float Compacity() const;
+ void Normal(IcePoint& normal) const;
+ void DenormalizedNormal(IcePoint& normal) const;
+ void Center(IcePoint& center) const;
+ inline_ IcePlane PlaneEquation() const { return IcePlane(mVerts[0], mVerts[1], mVerts[2]); }
+
+ PartVal TestAgainstPlane(const IcePlane& plane, float epsilon) const;
+// float Distance(IcePoint& cp, IcePoint& cq, Tri& tri);
+ void ComputeMoment(Moment& m);
+ float MinEdgeLength() const;
+ float MaxEdgeLength() const;
+ void ComputePoint(float u, float v, IcePoint& pt, udword* nearvtx=null) const;
+ void Inflate(float fat_coeff, bool constant_border);
+ };
+
+#endif // __ICETRIANGLE_H__
diff --git a/Opcode/OpcodeLib/Ice/IceTrilist.h b/Opcode/OpcodeLib/Ice/IceTrilist.h
new file mode 100644
index 0000000..d5f7c70
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceTrilist.h
@@ -0,0 +1,61 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a triangle container.
+ * \file IceTrilist.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICETRILIST_H__
+#define __ICETRILIST_H__
+
+ class ICEMATHS_API TriList : public Container
+ {
+ public:
+ // Constructor / Destructor
+ TriList() {}
+ ~TriList() {}
+
+ inline_ udword GetNbTriangles() const { return GetNbEntries()/9; }
+ inline_ Triangle* GetTriangles() const { return (Triangle*)GetEntries(); }
+
+ void AddTri(const Triangle& tri)
+ {
+ Add(tri.mVerts[0].x).Add(tri.mVerts[0].y).Add(tri.mVerts[0].z);
+ Add(tri.mVerts[1].x).Add(tri.mVerts[1].y).Add(tri.mVerts[1].z);
+ Add(tri.mVerts[2].x).Add(tri.mVerts[2].y).Add(tri.mVerts[2].z);
+ }
+
+ void AddTri(const IcePoint& p0, const IcePoint& p1, const IcePoint& p2)
+ {
+ Add(p0.x).Add(p0.y).Add(p0.z);
+ Add(p1.x).Add(p1.y).Add(p1.z);
+ Add(p2.x).Add(p2.y).Add(p2.z);
+ }
+ };
+
+ class ICEMATHS_API TriangleList : public Container
+ {
+ public:
+ // Constructor / Destructor
+ TriangleList() {}
+ ~TriangleList() {}
+
+ inline_ udword GetNbTriangles() const { return GetNbEntries()/3; }
+ inline_ IndexedTriangle* GetTriangles() const { return (IndexedTriangle*)GetEntries();}
+
+ void AddTriangle(const IndexedTriangle& tri)
+ {
+ Add(tri.mVRef[0]).Add(tri.mVRef[1]).Add(tri.mVRef[2]);
+ }
+
+ void AddTriangle(udword vref0, udword vref1, udword vref2)
+ {
+ Add(vref0).Add(vref1).Add(vref2);
+ }
+ };
+
+#endif //__ICETRILIST_H__
diff --git a/Opcode/OpcodeLib/Ice/IceTypes.h b/Opcode/OpcodeLib/Ice/IceTypes.h
new file mode 100644
index 0000000..dac0a71
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceTypes.h
@@ -0,0 +1,157 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains custom types.
+ * \file IceTypes.h
+ * \author Pierre Terdiman
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICETYPES_H__
+#define __ICETYPES_H__
+
+ #define USE_HANDLE_MANAGER
+
+ // Constants
+ #define PI 3.1415926535897932384626433832795028841971693993751f //!< PI
+ #define HALFPI 1.57079632679489661923f //!< 0.5 * PI
+ #define TWOPI 6.28318530717958647692f //!< 2.0 * PI
+ #define INVPI 0.31830988618379067154f //!< 1.0 / PI
+
+ #define RADTODEG 57.2957795130823208768f //!< 180.0 / PI, convert radians to degrees
+ #define DEGTORAD 0.01745329251994329577f //!< PI / 180.0, convert degrees to radians
+
+ #define EXP 2.71828182845904523536f //!< e
+ #define INVLOG2 3.32192809488736234787f //!< 1.0 / log10(2)
+ #define LN2 0.693147180559945f //!< ln(2)
+ #define INVLN2 1.44269504089f //!< 1.0f / ln(2)
+
+ #define INV3 0.33333333333333333333f //!< 1/3
+ #define INV6 0.16666666666666666666f //!< 1/6
+ #define INV7 0.14285714285714285714f //!< 1/7
+ #define INV9 0.11111111111111111111f //!< 1/9
+ #define INV255 0.00392156862745098039f //!< 1/255
+
+ #define SQRT2 1.41421356237f //!< sqrt(2)
+ #define INVSQRT2 0.707106781188f //!< 1 / sqrt(2)
+
+ #define SQRT3 1.73205080757f //!< sqrt(3)
+ #define INVSQRT3 0.577350269189f //!< 1 / sqrt(3)
+
+ #define null 0 //!< our own NULL pointer
+
+ // Custom types used in ICE
+ typedef signed char sbyte; //!< sizeof(sbyte) must be 1
+ typedef unsigned char ubyte; //!< sizeof(ubyte) must be 1
+ typedef signed short sword; //!< sizeof(sword) must be 2
+ typedef unsigned short uword; //!< sizeof(uword) must be 2
+ typedef signed int sdword; //!< sizeof(sdword) must be 4
+ typedef unsigned int udword; //!< sizeof(udword) must be 4
+ typedef signed __int64 sqword; //!< sizeof(sqword) must be 8
+ typedef unsigned __int64 uqword; //!< sizeof(uqword) must be 8
+ typedef float float32; //!< sizeof(float32) must be 4
+ typedef double float64; //!< sizeof(float64) must be 4
+
+ ICE_COMPILE_TIME_ASSERT(sizeof(bool)==1); // ...otherwise things might fail with VC++ 4.2 !
+ ICE_COMPILE_TIME_ASSERT(sizeof(ubyte)==1);
+ ICE_COMPILE_TIME_ASSERT(sizeof(sbyte)==1);
+ ICE_COMPILE_TIME_ASSERT(sizeof(sword)==2);
+ ICE_COMPILE_TIME_ASSERT(sizeof(uword)==2);
+ ICE_COMPILE_TIME_ASSERT(sizeof(udword)==4);
+ ICE_COMPILE_TIME_ASSERT(sizeof(sdword)==4);
+ ICE_COMPILE_TIME_ASSERT(sizeof(uqword)==8);
+ ICE_COMPILE_TIME_ASSERT(sizeof(sqword)==8);
+
+ //! TO BE DOCUMENTED
+ #define DECLARE_ICE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name
+
+ typedef udword DynID; //!< Dynamic identifier
+#ifdef USE_HANDLE_MANAGER
+ typedef udword KID; //!< Kernel ID
+// DECLARE_ICE_HANDLE(KID);
+#else
+ typedef uword KID; //!< Kernel ID
+#endif
+ typedef udword RTYPE; //!< Relationship-type (!) between owners and references
+ #define INVALID_ID 0xffffffff //!< Invalid dword ID (counterpart of null pointers)
+#ifdef USE_HANDLE_MANAGER
+ #define INVALID_KID 0xffffffff //!< Invalid Kernel ID
+#else
+ #define INVALID_KID 0xffff //!< Invalid Kernel ID
+#endif
+ #define INVALID_NUMBER 0xDEADBEEF //!< Standard junk value
+
+ // Define BOOL if needed
+ #ifndef BOOL
+ typedef int BOOL; //!< Another boolean type.
+ #endif
+
+ //! Union of a float and a sdword
+ typedef union {
+ float f; //!< The float
+ sdword d; //!< The integer
+ }scell;
+
+ //! Union of a float and a udword
+ typedef union {
+ float f; //!< The float
+ udword d; //!< The integer
+ }ucell;
+
+ // Type ranges
+ #define MAX_SBYTE 0x7f //!< max possible sbyte value
+ #define MIN_SBYTE 0x80 //!< min possible sbyte value
+ #define MAX_UBYTE 0xff //!< max possible ubyte value
+ #define MIN_UBYTE 0x00 //!< min possible ubyte value
+ #define MAX_SWORD 0x7fff //!< max possible sword value
+ #define MIN_SWORD 0x8000 //!< min possible sword value
+ #define MAX_UWORD 0xffff //!< max possible uword value
+ #define MIN_UWORD 0x0000 //!< min possible uword value
+ #define MAX_SDWORD 0x7fffffff //!< max possible sdword value
+ #define MIN_SDWORD 0x80000000 //!< min possible sdword value
+ #define MAX_UDWORD 0xffffffff //!< max possible udword value
+ #define MIN_UDWORD 0x00000000 //!< min possible udword value
+ #define MAX_FLOAT FLT_MAX //!< max possible float value
+ #define MIN_FLOAT (-FLT_MAX) //!< min possible loat value
+ #define IEEE_1_0 0x3f800000 //!< integer representation of 1.0
+ #define IEEE_255_0 0x437f0000 //!< integer representation of 255.0
+ #define IEEE_MAX_FLOAT 0x7f7fffff //!< integer representation of MAX_FLOAT
+ #define IEEE_MIN_FLOAT 0xff7fffff //!< integer representation of MIN_FLOAT
+ #define IEEE_UNDERFLOW_LIMIT 0x1a000000
+
+ #define ONE_OVER_RAND_MAX (1.0f / float(RAND_MAX)) //!< Inverse of the max possible value returned by rand()
+
+ typedef int (__stdcall* PROC)(); //!< A standard procedure call.
+ typedef bool (*ENUMERATION)(udword value, udword param, udword context); //!< ICE standard enumeration call
+ typedef void** VTABLE; //!< A V-Table.
+
+ #undef MIN
+ #undef MAX
+ #define MIN(a, b) ((a) < (b) ? (a) : (b)) //!< Returns the min value between a and b
+ #define MAX(a, b) ((a) > (b) ? (a) : (b)) //!< Returns the max value between a and b
+ #define MAXMAX(a,b,c) ((a) > (b) ? MAX (a,c) : MAX (b,c)) //!< Returns the max value between a, b and c
+
+ template<class T> inline_ const T& TMin (const T& a, const T& b) { return b < a ? b : a; }
+ template<class T> inline_ const T& TMax (const T& a, const T& b) { return a < b ? b : a; }
+ template<class T> inline_ void TSetMin (T& a, const T& b) { if(a>b) a = b; }
+ template<class T> inline_ void TSetMax (T& a, const T& b) { if(a<b) a = b; }
+
+ #define SQR(x) ((x)*(x)) //!< Returns x square
+ #define CUBE(x) ((x)*(x)*(x)) //!< Returns x cube
+
+ #define AND & //!< ...
+ #define OR | //!< ...
+ #define XOR ^ //!< ...
+
+ #define QUADRAT(x) ((x)*(x)) //!< Returns x square
+
+#ifdef _WIN32
+# define srand48(x) srand((unsigned int) (x))
+# define srandom(x) srand((unsigned int) (x))
+# define random() ((double) rand())
+# define drand48() ((double) (((double) rand()) / ((double) RAND_MAX)))
+#endif
+
+#endif // __ICETYPES_H__
diff --git a/Opcode/OpcodeLib/Ice/IceUtils.cpp b/Opcode/OpcodeLib/Ice/IceUtils.cpp
new file mode 100644
index 0000000..d877203
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceUtils.cpp
@@ -0,0 +1,39 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains misc. useful macros & defines.
+ * \file IceUtils.cpp
+ * \author Pierre Terdiman (collected from various sources)
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace IceCore;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the alignment of the input address.
+ * \fn Alignment()
+ * \param address [in] address to check
+ * \return the best alignment (e.g. 1 for odd addresses, etc)
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword IceCore::Alignment(udword address)
+{
+ // Returns 0 for null addresses
+ if(!address) return 0;
+
+ // Test all bits
+ udword Align = 1;
+ for(udword i=1;i<32;i++)
+ {
+ // Returns as soon as the alignment is broken
+ if(address&Align) return Align;
+ Align<<=1;
+ }
+ // Here all bits are null, except the highest one (else the address would be null)
+ return Align;
+}
diff --git a/Opcode/OpcodeLib/Ice/IceUtils.h b/Opcode/OpcodeLib/Ice/IceUtils.h
new file mode 100644
index 0000000..0e6161e
--- /dev/null
+++ b/Opcode/OpcodeLib/Ice/IceUtils.h
@@ -0,0 +1,256 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains misc. useful macros & defines.
+ * \file IceUtils.h
+ * \author Pierre Terdiman (collected from various sources)
+ * \date April, 4, 2000
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __ICEUTILS_H__
+#define __ICEUTILS_H__
+
+ #define START_RUNONCE { static bool __RunOnce__ = false; if(!__RunOnce__){
+ #define END_RUNONCE __RunOnce__ = true;}}
+
+ //! Reverse all the bits in a 32 bit word (from Steve Baker's Cute Code Collection)
+ //! (each line can be done in any order.
+ inline_ void ReverseBits(udword& n)
+ {
+ n = ((n >> 1) & 0x55555555) | ((n << 1) & 0xaaaaaaaa);
+ n = ((n >> 2) & 0x33333333) | ((n << 2) & 0xcccccccc);
+ n = ((n >> 4) & 0x0f0f0f0f) | ((n << 4) & 0xf0f0f0f0);
+ n = ((n >> 8) & 0x00ff00ff) | ((n << 8) & 0xff00ff00);
+ n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000);
+ // Etc for larger intergers (64 bits in Java)
+ // NOTE: the >> operation must be unsigned! (>>> in java)
+ }
+
+ //! Count the number of '1' bits in a 32 bit word (from Steve Baker's Cute Code Collection)
+ inline_ udword CountBits(udword n)
+ {
+ // This relies of the fact that the count of n bits can NOT overflow
+ // an n bit interger. EG: 1 bit count takes a 1 bit interger, 2 bit counts
+ // 2 bit interger, 3 bit count requires only a 2 bit interger.
+ // So we add all bit pairs, then each nible, then each byte etc...
+ n = (n & 0x55555555) + ((n & 0xaaaaaaaa) >> 1);
+ n = (n & 0x33333333) + ((n & 0xcccccccc) >> 2);
+ n = (n & 0x0f0f0f0f) + ((n & 0xf0f0f0f0) >> 4);
+ n = (n & 0x00ff00ff) + ((n & 0xff00ff00) >> 8);
+ n = (n & 0x0000ffff) + ((n & 0xffff0000) >> 16);
+ // Etc for larger intergers (64 bits in Java)
+ // NOTE: the >> operation must be unsigned! (>>> in java)
+ return n;
+ }
+
+ //! Even faster?
+ inline_ udword CountBits2(udword bits)
+ {
+ bits = bits - ((bits >> 1) & 0x55555555);
+ bits = ((bits >> 2) & 0x33333333) + (bits & 0x33333333);
+ bits = ((bits >> 4) + bits) & 0x0F0F0F0F;
+ return (bits * 0x01010101) >> 24;
+ }
+
+ //! Spread out bits. EG 00001111 -> 0101010101
+ //! 00001010 -> 0100010000
+ //! This is used to interleve to intergers to produce a `Morten Key'
+ //! used in Space Filling Curves (See DrDobbs Journal, July 1999)
+ //! Order is important.
+ inline_ void SpreadBits(udword& n)
+ {
+ n = ( n & 0x0000ffff) | (( n & 0xffff0000) << 16);
+ n = ( n & 0x000000ff) | (( n & 0x0000ff00) << 8);
+ n = ( n & 0x000f000f) | (( n & 0x00f000f0) << 4);
+ n = ( n & 0x03030303) | (( n & 0x0c0c0c0c) << 2);
+ n = ( n & 0x11111111) | (( n & 0x22222222) << 1);
+ }
+
+ // Next Largest Power of 2
+ // Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm
+ // that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with
+ // the same most significant 1 as x, but all 1's below it. Adding 1 to that value yields the next
+ // largest power of 2. For a 32-bit value:
+ inline_ udword nlpo2(udword x)
+ {
+ x |= (x >> 1);
+ x |= (x >> 2);
+ x |= (x >> 4);
+ x |= (x >> 8);
+ x |= (x >> 16);
+ return x+1;
+ }
+
+ //! Test to see if a number is an exact power of two (from Steve Baker's Cute Code Collection)
+ inline_ bool IsPowerOfTwo(udword n) { return ((n&(n-1))==0); }
+
+ //! Zero the least significant '1' bit in a word. (from Steve Baker's Cute Code Collection)
+ inline_ void ZeroLeastSetBit(udword& n) { n&=(n-1); }
+
+ //! Set the least significant N bits in a word. (from Steve Baker's Cute Code Collection)
+ inline_ void SetLeastNBits(udword& x, udword n) { x|=~(~0<<n); }
+
+ //! Classic XOR swap (from Steve Baker's Cute Code Collection)
+ //! x ^= y; /* x' = (x^y) */
+ //! y ^= x; /* y' = (y^(x^y)) = x */
+ //! x ^= y; /* x' = (x^y)^x = y */
+ inline_ void Swap(udword& x, udword& y) { x ^= y; y ^= x; x ^= y; }
+
+ //! Little/Big endian (from Steve Baker's Cute Code Collection)
+ //!
+ //! Extra comments by Kenny Hoff:
+ //! Determines the byte-ordering of the current machine (little or big endian)
+ //! by setting an integer value to 1 (so least significant bit is now 1); take
+ //! the address of the int and cast to a byte pointer (treat integer as an
+ //! array of four bytes); check the value of the first byte (must be 0 or 1).
+ //! If the value is 1, then the first byte least significant byte and this
+ //! implies LITTLE endian. If the value is 0, the first byte is the most
+ //! significant byte, BIG endian. Examples:
+ //! integer 1 on BIG endian: 00000000 00000000 00000000 00000001
+ //! integer 1 on LITTLE endian: 00000001 00000000 00000000 00000000
+ //!---------------------------------------------------------------------------
+ //! int IsLittleEndian() { int x=1; return ( ((char*)(&x))[0] ); }
+ inline_ char LittleEndian() { int i = 1; return *((char*)&i); }
+
+ //!< Alternative abs function
+ inline_ udword abs_(sdword x) { sdword y= x >> 31; return (x^y)-y; }
+
+ //!< Alternative min function
+ inline_ sdword min_(sdword a, sdword b) { sdword delta = b-a; return a + (delta&(delta>>31)); }
+
+ // Determine if one of the bytes in a 4 byte word is zero
+ inline_ BOOL HasNullByte(udword x) { return ((x + 0xfefefeff) & (~x) & 0x80808080); }
+
+ // To find the smallest 1 bit in a word EG: ~~~~~~10---0 => 0----010---0
+ inline_ udword LowestOneBit(udword w) { return ((w) & (~(w)+1)); }
+// inline_ udword LowestOneBit_(udword w) { return ((w) & (-(w))); }
+
+ // Most Significant 1 Bit
+ // Given a binary integer value x, the most significant 1 bit (highest numbered element of a bit set)
+ // can be computed using a SWAR algorithm that recursively "folds" the upper bits into the lower bits.
+ // This process yields a bit vector with the same most significant 1 as x, but all 1's below it.
+ // Bitwise AND of the original value with the complement of the "folded" value shifted down by one
+ // yields the most significant bit. For a 32-bit value:
+ inline_ udword msb32(udword x)
+ {
+ x |= (x >> 1);
+ x |= (x >> 2);
+ x |= (x >> 4);
+ x |= (x >> 8);
+ x |= (x >> 16);
+ return (x & ~(x >> 1));
+ }
+
+ /*
+ "Just call it repeatedly with various input values and always with the same variable as "memory".
+ The sharpness determines the degree of filtering, where 0 completely filters out the input, and 1
+ does no filtering at all.
+
+ I seem to recall from college that this is called an IIR (Infinite Impulse Response) filter. As opposed
+ to the more typical FIR (Finite Impulse Response).
+
+ Also, I'd say that you can make more intelligent and interesting filters than this, for example filters
+ that remove wrong responses from the mouse because it's being moved too fast. You'd want such a filter
+ to be applied before this one, of course."
+
+ (JCAB on Flipcode)
+ */
+ inline_ float FeedbackFilter(float val, float& memory, float sharpness)
+ {
+ ASSERT(sharpness>=0.0f && sharpness<=1.0f && "Invalid sharpness value in feedback filter");
+ if(sharpness<0.0f) sharpness = 0.0f;
+ else if(sharpness>1.0f) sharpness = 1.0f;
+ return memory = val * sharpness + memory * (1.0f - sharpness);
+ }
+
+ //! If you can guarantee that your input domain (i.e. value of x) is slightly
+ //! limited (abs(x) must be < ((1<<31u)-32767)), then you can use the
+ //! following code to clamp the resulting value into [-32768,+32767] range:
+ inline_ int ClampToInt16(int x)
+ {
+// ASSERT(abs(x) < (int)((1<<31u)-32767));
+
+ int delta = 32767 - x;
+ x += (delta>>31) & delta;
+ delta = x + 32768;
+ x -= (delta>>31) & delta;
+ return x;
+ }
+
+ // Generic functions
+ template<class Type> inline_ void TSwap(Type& a, Type& b) { const Type c = a; a = b; b = c; }
+ template<class Type> inline_ Type TClamp(const Type& x, const Type& lo, const Type& hi) { return ((x<lo) ? lo : (x>hi) ? hi : x); }
+
+ template<class Type> inline_ void TSort(Type& a, Type& b)
+ {
+ if(a>b) TSwap(a, b);
+ }
+
+ template<class Type> inline_ void TSort(Type& a, Type& b, Type& c)
+ {
+ if(a>b) TSwap(a, b);
+ if(b>c) TSwap(b, c);
+ if(a>b) TSwap(a, b);
+ if(b>c) TSwap(b, c);
+ }
+
+ // Prevent nasty user-manipulations (strategy borrowed from Charles Bloom)
+// #define PREVENT_COPY(curclass) void operator = (const curclass& object) { ASSERT(!"Bad use of operator ="); }
+ // ... actually this is better !
+ #define PREVENT_COPY(cur_class) private: cur_class(const cur_class& object); cur_class& operator=(const cur_class& object);
+
+ //! TO BE DOCUMENTED
+ #define OFFSET_OF(Class, Member) (size_t)&(((Class*)0)->Member)
+ //! TO BE DOCUMENTED
+ #define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0]))
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Returns the alignment of the input address.
+ * \fn Alignment()
+ * \param address [in] address to check
+ * \return the best alignment (e.g. 1 for odd addresses, etc)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ FUNCTION ICECORE_API udword Alignment(udword address);
+
+ #define IS_ALIGNED_2(x) ((x&1)==0)
+ #define IS_ALIGNED_4(x) ((x&3)==0)
+ #define IS_ALIGNED_8(x) ((x&7)==0)
+
+ inline_ void _prefetch(void const* ptr) { (void)*(char const volatile *)ptr; }
+
+ // Compute implicit coords from an index:
+ // The idea is to get back 2D coords from a 1D index.
+ // For example:
+ //
+ // 0 1 2 ... nbu-1
+ // nbu nbu+1 i ...
+ //
+ // We have i, we're looking for the equivalent (u=2, v=1) location.
+ // i = u + v*nbu
+ // <=> i/nbu = u/nbu + v
+ // Since 0 <= u < nbu, u/nbu = 0 (integer)
+ // Hence: v = i/nbu
+ // Then we simply put it back in the original equation to compute u = i - v*nbu
+ inline_ void Compute2DCoords(udword& u, udword& v, udword i, udword nbu)
+ {
+ v = i / nbu;
+ u = i - (v * nbu);
+ }
+
+ // In 3D: i = u + v*nbu + w*nbu*nbv
+ // <=> i/(nbu*nbv) = u/(nbu*nbv) + v/nbv + w
+ // u/(nbu*nbv) is null since u/nbu was null already.
+ // v/nbv is null as well for the same reason.
+ // Hence w = i/(nbu*nbv)
+ // Then we're left with a 2D problem: i' = i - w*nbu*nbv = u + v*nbu
+ inline_ void Compute3DCoords(udword& u, udword& v, udword& w, udword i, udword nbu, udword nbu_nbv)
+ {
+ w = i / (nbu_nbv);
+ Compute2DCoords(u, v, i - (w * nbu_nbv), nbu);
+ }
+
+#endif // __ICEUTILS_H__
diff --git a/Opcode/OpcodeLib/OPC_AABBCollider.cpp b/Opcode/OpcodeLib/OPC_AABBCollider.cpp
new file mode 100644
index 0000000..fe87d24
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_AABBCollider.cpp
@@ -0,0 +1,696 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for an AABB collider.
+ * \file OPC_AABBCollider.cpp
+ * \author Pierre Terdiman
+ * \date January, 1st, 2002
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains an AABB-vs-tree collider.
+ *
+ * \class AABBCollider
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date January, 1st, 2002
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+#include "OPC_BoxBoxOverlap.h"
+#include "OPC_TriBoxOverlap.h"
+
+#define SET_CONTACT(prim_index, flag) \
+ /* Set contact status */ \
+ mFlags |= flag; \
+ mTouchedPrimitives->Add(prim_index);
+
+//! AABB-triangle test
+#define AABB_PRIM(prim_index, flag) \
+ /* Request vertices from the app */ \
+ VertexPointers VP; mIMesh->GetTriangle(VP, prim_index);\
+ mLeafVerts[0] = *VP.Vertex[0]; \
+ mLeafVerts[1] = *VP.Vertex[1]; \
+ mLeafVerts[2] = *VP.Vertex[2]; \
+ /* Perform triangle-box overlap test */ \
+ if(TriBoxOverlap()) \
+ { \
+ SET_CONTACT(prim_index, flag) \
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBCollider::AABBCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBCollider::~AABBCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Generic collision query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - with GetNbTouchedPrimitives()
+ * - with GetTouchedPrimitives()
+ *
+ * \param cache [in/out] a box cache
+ * \param box [in] collision AABB in world space
+ * \param model [in] Opcode model to collide with
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const Model& model)
+{
+ // Checkings
+ if(!Setup(&model)) return false;
+
+ // Init collision query
+ if(InitQuery(cache, box)) return true;
+
+ if(!model.HasLeafNodes())
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ }
+ else
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Initializes a collision query :
+ * - reset stats & contact status
+ * - check temporal coherence
+ *
+ * \param cache [in/out] a box cache
+ * \param box [in] AABB in world space
+ * \return TRUE if we can return immediately
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+BOOL AABBCollider::InitQuery(AABBCache& cache, const CollisionAABB& box)
+{
+ // 1) Call the base method
+ VolumeCollider::InitQuery();
+
+ // 2) Keep track of the query box
+ mBox = box;
+
+ // 3) Setup destination pointer
+ mTouchedPrimitives = &cache.TouchedPrimitives;
+
+ // 4) Special case: 1-triangle meshes [Opcode 1.3]
+ if(mCurrentModel && mCurrentModel->HasSingleNode())
+ {
+ if(!SkipPrimitiveTests())
+ {
+ // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
+ mTouchedPrimitives->Reset();
+
+ // Perform overlap test between the unique triangle and the box (and set contact status if needed)
+ AABB_PRIM(udword(0), OPC_CONTACT)
+
+ // Return immediately regardless of status
+ return TRUE;
+ }
+ }
+
+ // 5) Check temporal coherence :
+ if(TemporalCoherenceEnabled())
+ {
+ // Here we use temporal coherence
+ // => check results from previous frame before performing the collision query
+ if(FirstContactEnabled())
+ {
+ // We're only interested in the first contact found => test the unique previously touched face
+ if(mTouchedPrimitives->GetNbEntries())
+ {
+ // Get index of previously touched face = the first entry in the array
+ udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0);
+
+ // Then reset the array:
+ // - if the overlap test below is successful, the index we'll get added back anyway
+ // - if it isn't, then the array should be reset anyway for the normal query
+ mTouchedPrimitives->Reset();
+
+ // Perform overlap test between the cached triangle and the box (and set contact status if needed)
+ AABB_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT)
+
+ // Return immediately if possible
+ if(GetContactStatus()) return TRUE;
+ }
+ // else no face has been touched during previous query
+ // => we'll have to perform a normal query
+ }
+ else
+ {
+ // We're interested in all contacts =>test the new real box N(ew) against the previous fat box P(revious):
+ if(IsCacheValid(cache) && mBox.IsInside(cache.FatBox))
+ {
+ // - if N is included in P, return previous list
+ // => we simply leave the list (mTouchedFaces) unchanged
+
+ // Set contact status if needed
+ if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT;
+
+ // In any case we don't need to do a query
+ return TRUE;
+ }
+ else
+ {
+ // - else do the query using a fat N
+
+ // Reset cache since we'll about to perform a real query
+ mTouchedPrimitives->Reset();
+
+ // Make a fat box so that coherence will work for subsequent frames
+ mBox.mExtents *= cache.FatCoeff;
+
+ // Update cache with query data (signature for cached faces)
+ cache.FatBox = mBox;
+ }
+ }
+ }
+ else
+ {
+ // Here we don't use temporal coherence => do a normal query
+ mTouchedPrimitives->Reset();
+ }
+
+ // 5) Precompute min & max bounds if needed
+ mMin = box.mCenter - box.mExtents;
+ mMax = box.mCenter + box.mExtents;
+
+ return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Collision query for vanilla AABB trees.
+ * \param cache [in/out] a box cache
+ * \param box [in] collision AABB in world space
+ * \param tree [in] AABB tree
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const AABBTree* tree)
+{
+ // This is typically called for a scene tree, full of -AABBs-, not full of triangles.
+ // So we don't really have "primitives" to deal with. Hence it doesn't work with
+ // "FirstContact" + "TemporalCoherence".
+ ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) );
+
+ // Checkings
+ if(!tree) return false;
+
+ // Init collision query
+ if(InitQuery(cache, box)) return true;
+
+ // Perform collision query
+ _Collide(tree);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks the AABB completely contains the box. In which case we can end the query sooner.
+ * \param bc [in] box center
+ * \param be [in] box extents
+ * \return true if the AABB contains the whole box
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL AABBCollider::AABBContainsBox(const IcePoint& bc, const IcePoint& be)
+{
+ if(mMin.x > bc.x - be.x) return FALSE;
+ if(mMin.y > bc.y - be.y) return FALSE;
+ if(mMin.z > bc.z - be.z) return FALSE;
+
+ if(mMax.x < bc.x + be.x) return FALSE;
+ if(mMax.y < bc.y + be.y) return FALSE;
+ if(mMax.z < bc.z + be.z) return FALSE;
+
+ return TRUE;
+}
+
+#define TEST_BOX_IN_AABB(center, extents) \
+ if(AABBContainsBox(center, extents)) \
+ { \
+ /* Set contact status */ \
+ mFlags |= OPC_CONTACT; \
+ _Dump(node); \
+ return; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_Collide(const AABBCollisionNode* node)
+{
+ // Perform AABB-AABB overlap test
+ if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
+
+ TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->IsLeaf())
+ {
+ AABB_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _Collide(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node)
+{
+ // Perform AABB-AABB overlap test
+ if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
+
+ TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->IsLeaf())
+ {
+ SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _CollideNoPrimitiveTest(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_Collide(const AABBQuantizedNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const IcePoint Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const IcePoint Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform AABB-AABB overlap test
+ if(!AABBAABBOverlap(Extents, Center)) return;
+
+ TEST_BOX_IN_AABB(Center, Extents)
+
+ if(node->IsLeaf())
+ {
+ AABB_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _Collide(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const IcePoint Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const IcePoint Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform AABB-AABB overlap test
+ if(!AABBAABBOverlap(Extents, Center)) return;
+
+ TEST_BOX_IN_AABB(Center, Extents)
+
+ if(node->IsLeaf())
+ {
+ SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _CollideNoPrimitiveTest(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_Collide(const AABBNoLeafNode* node)
+{
+ // Perform AABB-AABB overlap test
+ if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
+
+ TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->HasPosLeaf()) { AABB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { AABB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for no-leaf AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node)
+{
+ // Perform AABB-AABB overlap test
+ if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
+
+ TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_Collide(const AABBQuantizedNoLeafNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const IcePoint Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const IcePoint Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform AABB-AABB overlap test
+ if(!AABBAABBOverlap(Extents, Center)) return;
+
+ TEST_BOX_IN_AABB(Center, Extents)
+
+ if(node->HasPosLeaf()) { AABB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { AABB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized no-leaf AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const IcePoint Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const IcePoint Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform AABB-AABB overlap test
+ if(!AABBAABBOverlap(Extents, Center)) return;
+
+ TEST_BOX_IN_AABB(Center, Extents)
+
+ if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for vanilla AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBCollider::_Collide(const AABBTreeNode* node)
+{
+ // Perform AABB-AABB overlap test
+ IcePoint Center, Extents;
+ node->GetAABB()->GetCenter(Center);
+ node->GetAABB()->GetExtents(Extents);
+ if(!AABBAABBOverlap(Center, Extents)) return;
+
+ if(node->IsLeaf() || AABBContainsBox(Center, Extents))
+ {
+ mFlags |= OPC_CONTACT;
+ mTouchedPrimitives->Add(node->GetPrimitives(), node->GetNbPrimitives());
+ }
+ else
+ {
+ _Collide(node->GetPos());
+ _Collide(node->GetNeg());
+ }
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridAABBCollider::HybridAABBCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridAABBCollider::~HybridAABBCollider()
+{
+}
+
+bool HybridAABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const HybridModel& model)
+{
+ // We don't want primitive tests here!
+ mFlags |= OPC_NO_PRIMITIVE_TESTS;
+
+ // Checkings
+ if(!Setup(&model)) return false;
+
+ // Init collision query
+ if(InitQuery(cache, box)) return true;
+
+ // Special case for 1-leaf trees
+ if(mCurrentModel && mCurrentModel->HasSingleNode())
+ {
+ // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles
+ udword Nb = mIMesh->GetNbTriangles();
+
+ // Loop through all triangles
+ for(udword i=0;i<Nb;i++)
+ {
+ AABB_PRIM(i, OPC_CONTACT)
+ }
+ return true;
+ }
+
+ // Override destination array since we're only going to get leaf boxes here
+ mTouchedBoxes.Reset();
+ mTouchedPrimitives = &mTouchedBoxes;
+
+ // Now, do the actual query against leaf boxes
+ if(!model.HasLeafNodes())
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ }
+ else
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ }
+
+ // We only have a list of boxes so far
+ if(GetContactStatus())
+ {
+ // Reset contact status, since it currently only reflects collisions with leaf boxes
+ Collider::InitQuery();
+
+ // Change dest container so that we can use built-in overlap tests and get collided primitives
+ cache.TouchedPrimitives.Reset();
+ mTouchedPrimitives = &cache.TouchedPrimitives;
+
+ // Read touched leaf boxes
+ udword Nb = mTouchedBoxes.GetNbEntries();
+ const udword* Touched = mTouchedBoxes.GetEntries();
+
+ const LeafTriangles* LT = model.GetLeafTriangles();
+ const udword* Indices = model.GetIndices();
+
+ // Loop through touched leaves
+ while(Nb--)
+ {
+ const LeafTriangles& CurrentLeaf = LT[*Touched++];
+
+ // Each leaf box has a set of triangles
+ udword NbTris = CurrentLeaf.GetNbTriangles();
+ if(Indices)
+ {
+ const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ udword TriangleIndex = *T++;
+ AABB_PRIM(TriangleIndex, OPC_CONTACT)
+ }
+ }
+ else
+ {
+ udword BaseIndex = CurrentLeaf.GetTriangleIndex();
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ udword TriangleIndex = BaseIndex++;
+ AABB_PRIM(TriangleIndex, OPC_CONTACT)
+ }
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/Opcode/OpcodeLib/OPC_AABBCollider.h b/Opcode/OpcodeLib/OPC_AABBCollider.h
new file mode 100644
index 0000000..9a86948
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_AABBCollider.h
@@ -0,0 +1,97 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for an AABB collider.
+ * \file OPC_AABBCollider.h
+ * \author Pierre Terdiman
+ * \date January, 1st, 2002
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_AABBCOLLIDER_H__
+#define __OPC_AABBCOLLIDER_H__
+
+ struct OPCODE_API AABBCache : VolumeCache
+ {
+ AABBCache() : FatCoeff(1.1f)
+ {
+ FatBox.mCenter.Zero();
+ FatBox.mExtents.Zero();
+ }
+
+ // Cached faces signature
+ CollisionAABB FatBox; //!< Box used when performing the query resulting in cached faces
+ // User settings
+ float FatCoeff; //!< mRadius2 multiplier used to create a fat sphere
+ };
+
+ class OPCODE_API AABBCollider : public VolumeCollider
+ {
+ public:
+ // Constructor / Destructor
+ AABBCollider();
+ virtual ~AABBCollider();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Generic collision query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - with GetNbTouchedPrimitives()
+ * - with GetTouchedPrimitives()
+ *
+ * \param cache [in/out] a box cache
+ * \param box [in] collision AABB in world space
+ * \param model [in] Opcode model to collide with
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool Collide(AABBCache& cache, const CollisionAABB& box, const Model& model);
+ //
+ bool Collide(AABBCache& cache, const CollisionAABB& box, const AABBTree* tree);
+ protected:
+ CollisionAABB mBox; //!< Query box in (center, extents) form
+ IcePoint mMin; //!< Query box min point
+ IcePoint mMax; //!< Query box max point
+ // Leaf description
+ IcePoint mLeafVerts[3]; //!< Triangle vertices
+ // Internal methods
+ void _Collide(const AABBCollisionNode* node);
+ void _Collide(const AABBNoLeafNode* node);
+ void _Collide(const AABBQuantizedNode* node);
+ void _Collide(const AABBQuantizedNoLeafNode* node);
+ void _Collide(const AABBTreeNode* node);
+ void _CollideNoPrimitiveTest(const AABBCollisionNode* node);
+ void _CollideNoPrimitiveTest(const AABBNoLeafNode* node);
+ void _CollideNoPrimitiveTest(const AABBQuantizedNode* node);
+ void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node);
+ // Overlap tests
+ inline_ BOOL AABBContainsBox(const IcePoint& bc, const IcePoint& be);
+ inline_ BOOL AABBAABBOverlap(const IcePoint& b, const IcePoint& Pb);
+ inline_ BOOL TriBoxOverlap();
+ // Init methods
+ BOOL InitQuery(AABBCache& cache, const CollisionAABB& box);
+ };
+
+ class OPCODE_API HybridAABBCollider : public AABBCollider
+ {
+ public:
+ // Constructor / Destructor
+ HybridAABBCollider();
+ virtual ~HybridAABBCollider();
+
+ bool Collide(AABBCache& cache, const CollisionAABB& box, const HybridModel& model);
+ protected:
+ Container mTouchedBoxes;
+ };
+
+#endif // __OPC_AABBCOLLIDER_H__
diff --git a/Opcode/OpcodeLib/OPC_AABBTree.cpp b/Opcode/OpcodeLib/OPC_AABBTree.cpp
new file mode 100644
index 0000000..e7ac631
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_AABBTree.cpp
@@ -0,0 +1,573 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a versatile AABB tree.
+ * \file OPC_AABBTree.cpp
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a generic AABB tree node.
+ *
+ * \class AABBTreeNode
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a generic AABB tree.
+ * This is a vanilla AABB tree, without any particular optimization. It contains anonymous references to
+ * user-provided primitives, which can theoretically be anything - triangles, boxes, etc. Each primitive
+ * is surrounded by an AABB, regardless of the primitive's nature. When the primitive is a triangle, the
+ * resulting tree can be converted into an optimized tree. If the primitive is a box, the resulting tree
+ * can be used for culling - VFC or occlusion -, assuming you cull on a mesh-by-mesh basis (modern way).
+ *
+ * \class AABBTree
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBTreeNode::AABBTreeNode() :
+ mPos (null),
+#ifndef OPC_NO_NEG_VANILLA_TREE
+ mNeg (null),
+#endif
+ mNbPrimitives (0),
+ mNodePrimitives (null)
+{
+#ifdef OPC_USE_TREE_COHERENCE
+ mBitmask = 0;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBTreeNode::~AABBTreeNode()
+{
+ // Opcode 1.3:
+ const AABBTreeNode* Pos = GetPos();
+ const AABBTreeNode* Neg = GetNeg();
+#ifndef OPC_NO_NEG_VANILLA_TREE
+ if(!(mPos&1)) DELETESINGLE(Pos);
+ if(!(mNeg&1)) DELETESINGLE(Neg);
+#else
+ if(!(mPos&1)) DELETEARRAY(Pos);
+#endif
+ mNodePrimitives = null; // This was just a shortcut to the global list => no release
+ mNbPrimitives = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Splits the node along a given axis.
+ * The list of indices is reorganized according to the split values.
+ * \param axis [in] splitting axis index
+ * \param builder [in] the tree builder
+ * \return the number of primitives assigned to the first child
+ * \warning this method reorganizes the internal list of primitives
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword AABBTreeNode::Split(udword axis, AABBTreeBuilder* builder)
+{
+ // Get node split value
+ float SplitValue = builder->GetSplittingValue(mNodePrimitives, mNbPrimitives, mBV, axis);
+
+ udword NbPos = 0;
+ // Loop through all node-related primitives. Their indices range from mNodePrimitives[0] to mNodePrimitives[mNbPrimitives-1].
+ // Those indices map the global list in the tree builder.
+ for(udword i=0;i<mNbPrimitives;i++)
+ {
+ // Get index in global list
+ udword Index = mNodePrimitives[i];
+
+ // Test against the splitting value. The primitive value is tested against the enclosing-box center.
+ // [We only need an approximate partition of the enclosing box here.]
+ float PrimitiveValue = builder->GetSplittingValue(Index, axis);
+
+ // Reorganize the list of indices in this order: positive - negative.
+ if(PrimitiveValue > SplitValue)
+ {
+ // Swap entries
+ udword Tmp = mNodePrimitives[i];
+ mNodePrimitives[i] = mNodePrimitives[NbPos];
+ mNodePrimitives[NbPos] = Tmp;
+ // Count primitives assigned to positive space
+ NbPos++;
+ }
+ }
+ return NbPos;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Subdivides the node.
+ *
+ * N
+ * / \
+ * / \
+ * N/2 N/2
+ * / \ / \
+ * N/4 N/4 N/4 N/4
+ * (etc)
+ *
+ * A well-balanced tree should have a O(log n) depth.
+ * A degenerate tree would have a O(n) depth.
+ * Note a perfectly-balanced tree is not well-suited to collision detection anyway.
+ *
+ * \param builder [in] the tree builder
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeNode::Subdivide(AABBTreeBuilder* builder)
+{
+ // Checkings
+ if(!builder) return false;
+
+ // Stop subdividing if we reach a leaf node. This is always performed here,
+ // else we could end in trouble if user overrides this.
+ if(mNbPrimitives==1) return true;
+
+ // Let the user validate the subdivision
+ if(!builder->ValidateSubdivision(mNodePrimitives, mNbPrimitives, mBV)) return true;
+
+ bool ValidSplit = true; // Optimism...
+ udword NbPos;
+ if(builder->mSettings.mRules & SPLIT_LARGEST_AXIS)
+ {
+ // Find the largest axis to split along
+ IcePoint Extents; mBV.GetExtents(Extents); // Box extents
+ udword Axis = Extents.LargestAxis(); // Index of largest axis
+
+ // Split along the axis
+ NbPos = Split(Axis, builder);
+
+ // Check split validity
+ if(!NbPos || NbPos==mNbPrimitives) ValidSplit = false;
+ }
+ else if(builder->mSettings.mRules & SPLIT_SPLATTER_POINTS)
+ {
+ // Compute the means
+ IcePoint Means(0.0f, 0.0f, 0.0f);
+ for(udword i=0;i<mNbPrimitives;i++)
+ {
+ udword Index = mNodePrimitives[i];
+ Means.x+=builder->GetSplittingValue(Index, 0);
+ Means.y+=builder->GetSplittingValue(Index, 1);
+ Means.z+=builder->GetSplittingValue(Index, 2);
+ }
+ Means/=float(mNbPrimitives);
+
+ // Compute variances
+ IcePoint Vars(0.0f, 0.0f, 0.0f);
+ for(udword i=0;i<mNbPrimitives;i++)
+ {
+ udword Index = mNodePrimitives[i];
+ float Cx = builder->GetSplittingValue(Index, 0);
+ float Cy = builder->GetSplittingValue(Index, 1);
+ float Cz = builder->GetSplittingValue(Index, 2);
+ Vars.x += (Cx - Means.x)*(Cx - Means.x);
+ Vars.y += (Cy - Means.y)*(Cy - Means.y);
+ Vars.z += (Cz - Means.z)*(Cz - Means.z);
+ }
+ Vars/=float(mNbPrimitives-1);
+
+ // Choose axis with greatest variance
+ udword Axis = Vars.LargestAxis();
+
+ // Split along the axis
+ NbPos = Split(Axis, builder);
+
+ // Check split validity
+ if(!NbPos || NbPos==mNbPrimitives) ValidSplit = false;
+ }
+ else if(builder->mSettings.mRules & SPLIT_BALANCED)
+ {
+ // Test 3 axis, take the best
+ float Results[3];
+ NbPos = Split(0, builder); Results[0] = float(NbPos)/float(mNbPrimitives);
+ NbPos = Split(1, builder); Results[1] = float(NbPos)/float(mNbPrimitives);
+ NbPos = Split(2, builder); Results[2] = float(NbPos)/float(mNbPrimitives);
+ Results[0]-=0.5f; Results[0]*=Results[0];
+ Results[1]-=0.5f; Results[1]*=Results[1];
+ Results[2]-=0.5f; Results[2]*=Results[2];
+ udword Min=0;
+ if(Results[1]<Results[Min]) Min = 1;
+ if(Results[2]<Results[Min]) Min = 2;
+
+ // Split along the axis
+ NbPos = Split(Min, builder);
+
+ // Check split validity
+ if(!NbPos || NbPos==mNbPrimitives) ValidSplit = false;
+ }
+ else if(builder->mSettings.mRules & SPLIT_BEST_AXIS)
+ {
+ // Test largest, then middle, then smallest axis...
+
+ // Sort axis
+ IcePoint Extents; mBV.GetExtents(Extents); // Box extents
+ udword SortedAxis[] = { 0, 1, 2 };
+ float* Keys = (float*)&Extents.x;
+ for(udword j=0;j<3;j++)
+ {
+ for(udword i=0;i<2;i++)
+ {
+ if(Keys[SortedAxis[i]]<Keys[SortedAxis[i+1]])
+ {
+ udword Tmp = SortedAxis[i];
+ SortedAxis[i] = SortedAxis[i+1];
+ SortedAxis[i+1] = Tmp;
+ }
+ }
+ }
+
+ // Find the largest axis to split along
+ udword CurAxis = 0;
+ ValidSplit = false;
+ while(!ValidSplit && CurAxis!=3)
+ {
+ NbPos = Split(SortedAxis[CurAxis], builder);
+ // Check the subdivision has been successful
+ if(!NbPos || NbPos==mNbPrimitives) CurAxis++;
+ else ValidSplit = true;
+ }
+ }
+ else if(builder->mSettings.mRules & SPLIT_FIFTY)
+ {
+ // Don't even bother splitting (mainly a performance test)
+ NbPos = mNbPrimitives>>1;
+ }
+ else return false; // Unknown splitting rules
+
+ // Check the subdivision has been successful
+ if(!ValidSplit)
+ {
+ // Here, all boxes lie in the same sub-space. Two strategies:
+ // - if the tree *must* be complete, make an arbitrary 50-50 split
+ // - else stop subdividing
+// if(builder->mSettings.mRules&SPLIT_COMPLETE)
+ if(builder->mSettings.mLimit==1)
+ {
+ builder->IncreaseNbInvalidSplits();
+ NbPos = mNbPrimitives>>1;
+ }
+ else return true;
+ }
+
+ // Now create children and assign their pointers.
+ if(builder->mNodeBase)
+ {
+ // We use a pre-allocated linear pool for complete trees [Opcode 1.3]
+ AABBTreeNode* Pool = (AABBTreeNode*)builder->mNodeBase;
+ udword Count = builder->GetCount() - 1; // Count begins to 1...
+ // Set last bit to tell it shouldn't be freed ### pretty ugly, find a better way. Maybe one bit in mNbPrimitives
+ ASSERT(!(udword(&Pool[Count+0])&1));
+ ASSERT(!(udword(&Pool[Count+1])&1));
+ mPos = udword(&Pool[Count+0])|1;
+#ifndef OPC_NO_NEG_VANILLA_TREE
+ mNeg = udword(&Pool[Count+1])|1;
+#endif
+ }
+ else
+ {
+ // Non-complete trees and/or Opcode 1.2 allocate nodes on-the-fly
+#ifndef OPC_NO_NEG_VANILLA_TREE
+ mPos = (udword)new AABBTreeNode; CHECKALLOC(mPos);
+ mNeg = (udword)new AABBTreeNode; CHECKALLOC(mNeg);
+#else
+ AABBTreeNode* PosNeg = new AABBTreeNode[2];
+ CHECKALLOC(PosNeg);
+ mPos = (udword)PosNeg;
+#endif
+ }
+
+ // Update stats
+ builder->IncreaseCount(2);
+
+ // Assign children
+ AABBTreeNode* Pos = (AABBTreeNode*)GetPos();
+ AABBTreeNode* Neg = (AABBTreeNode*)GetNeg();
+ Pos->mNodePrimitives = &mNodePrimitives[0];
+ Pos->mNbPrimitives = NbPos;
+ Neg->mNodePrimitives = &mNodePrimitives[NbPos];
+ Neg->mNbPrimitives = mNbPrimitives - NbPos;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive hierarchy building in a top-down fashion.
+ * \param builder [in] the tree builder
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeNode::_BuildHierarchy(AABBTreeBuilder* builder)
+{
+ // 1) Compute the global box for current node. The box is stored in mBV.
+ builder->ComputeGlobalBox(mNodePrimitives, mNbPrimitives, mBV);
+
+ // 2) Subdivide current node
+ Subdivide(builder);
+
+ // 3) Recurse
+ AABBTreeNode* Pos = (AABBTreeNode*)GetPos();
+ AABBTreeNode* Neg = (AABBTreeNode*)GetNeg();
+ if(Pos) Pos->_BuildHierarchy(builder);
+ if(Neg) Neg->_BuildHierarchy(builder);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the tree (top-down).
+ * \param builder [in] the tree builder
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeNode::_Refit(AABBTreeBuilder* builder)
+{
+ // 1) Recompute the new global box for current node
+ builder->ComputeGlobalBox(mNodePrimitives, mNbPrimitives, mBV);
+
+ // 2) Recurse
+ AABBTreeNode* Pos = (AABBTreeNode*)GetPos();
+ AABBTreeNode* Neg = (AABBTreeNode*)GetNeg();
+ if(Pos) Pos->_Refit(builder);
+ if(Neg) Neg->_Refit(builder);
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBTree::AABBTree() : mIndices(null), mTotalNbNodes(0), mPool(null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBTree::~AABBTree()
+{
+ Release();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Releases the tree.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTree::Release()
+{
+ DELETEARRAY(mPool);
+ DELETEARRAY(mIndices);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds a generic AABB tree from a tree builder.
+ * \param builder [in] the tree builder
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTree::Build(AABBTreeBuilder* builder)
+{
+ // Checkings
+ if(!builder || !builder->mNbPrimitives) return false;
+
+ // Release previous tree
+ Release();
+
+ // Init stats
+ builder->SetCount(1);
+ builder->SetNbInvalidSplits(0);
+
+ // Initialize indices. This list will be modified during build.
+ mIndices = new udword[builder->mNbPrimitives];
+ CHECKALLOC(mIndices);
+ // Identity permutation
+ for(udword i=0;i<builder->mNbPrimitives;i++) mIndices[i] = i;
+
+ // Setup initial node. Here we have a complete permutation of the app's primitives.
+ mNodePrimitives = mIndices;
+ mNbPrimitives = builder->mNbPrimitives;
+
+ // Use a linear array for complete trees (since we can predict the final number of nodes) [Opcode 1.3]
+// if(builder->mRules&SPLIT_COMPLETE)
+ if(builder->mSettings.mLimit==1)
+ {
+ // Allocate a pool of nodes
+ mPool = new AABBTreeNode[builder->mNbPrimitives*2 - 1];
+
+ builder->mNodeBase = mPool; // ### ugly !
+ }
+
+ // Build the hierarchy
+ _BuildHierarchy(builder);
+
+ // Get back total number of nodes
+ mTotalNbNodes = builder->GetCount();
+
+ // For complete trees, check the correct number of nodes has been created [Opcode 1.3]
+ if(mPool) ASSERT(mTotalNbNodes==builder->mNbPrimitives*2 - 1);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the depth of the tree.
+ * A well-balanced tree should have a log(n) depth. A degenerate tree O(n) depth.
+ * \return depth of the tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword AABBTree::ComputeDepth() const
+{
+ return Walk(null, null); // Use the walking code without callback
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Walks the tree, calling the user back for each node.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword AABBTree::Walk(WalkingCallback callback, void* user_data) const
+{
+ // Call it without callback to compute max depth
+ udword MaxDepth = 0;
+ udword CurrentDepth = 0;
+
+ struct Local
+ {
+ static void _Walk(const AABBTreeNode* current_node, udword& max_depth, udword& current_depth, WalkingCallback callback, void* user_data)
+ {
+ // Checkings
+ if(!current_node) return;
+ // Entering a new node => increase depth
+ current_depth++;
+ // Keep track of max depth
+ if(current_depth>max_depth) max_depth = current_depth;
+
+ // Callback
+ if(callback && !(callback)(current_node, current_depth, user_data)) return;
+
+ // Recurse
+ if(current_node->GetPos()) { _Walk(current_node->GetPos(), max_depth, current_depth, callback, user_data); current_depth--; }
+ if(current_node->GetNeg()) { _Walk(current_node->GetNeg(), max_depth, current_depth, callback, user_data); current_depth--; }
+ }
+ };
+ Local::_Walk(this, MaxDepth, CurrentDepth, callback, user_data);
+ return MaxDepth;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the tree in a top-down way.
+ * \param builder [in] the tree builder
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTree::Refit(AABBTreeBuilder* builder)
+{
+ if(!builder) return false;
+ _Refit(builder);
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the tree in a bottom-up way.
+ * \param builder [in] the tree builder
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTree::Refit2(AABBTreeBuilder* builder)
+{
+ // Checkings
+ if(!builder) return false;
+
+ ASSERT(mPool);
+
+ // Bottom-up update
+ IcePoint Min,Max;
+ IcePoint Min_,Max_;
+ udword Index = mTotalNbNodes;
+ while(Index--)
+ {
+ AABBTreeNode& Current = mPool[Index];
+
+ if(Current.IsLeaf())
+ {
+ builder->ComputeGlobalBox(Current.GetPrimitives(), Current.GetNbPrimitives(), *(AABB*)Current.GetAABB());
+ }
+ else
+ {
+ Current.GetPos()->GetAABB()->GetMin(Min);
+ Current.GetPos()->GetAABB()->GetMax(Max);
+
+ Current.GetNeg()->GetAABB()->GetMin(Min_);
+ Current.GetNeg()->GetAABB()->GetMax(Max_);
+
+ Min.Min(Min_);
+ Max.Max(Max_);
+
+ ((AABB*)Current.GetAABB())->SetMinMax(Min, Max);
+ }
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the number of bytes used by the tree.
+ * \return number of bytes used
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword AABBTree::GetUsedBytes() const
+{
+ udword TotalSize = mTotalNbNodes*GetNodeSize();
+ if(mIndices) TotalSize+=mNbPrimitives*sizeof(udword);
+ return TotalSize;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks the tree is a complete tree or not.
+ * A complete tree is made of 2*N-1 nodes, where N is the number of primitives in the tree.
+ * \return true for complete trees
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTree::IsComplete() const
+{
+ return (GetNbNodes()==GetNbPrimitives()*2-1);
+}
diff --git a/Opcode/OpcodeLib/OPC_AABBTree.h b/Opcode/OpcodeLib/OPC_AABBTree.h
new file mode 100644
index 0000000..377fbcb
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_AABBTree.h
@@ -0,0 +1,137 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a versatile AABB tree.
+ * \file OPC_AABBTree.h
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_AABBTREE_H__
+#define __OPC_AABBTREE_H__
+
+#ifdef OPC_NO_NEG_VANILLA_TREE
+ //! TO BE DOCUMENTED
+ #define IMPLEMENT_TREE(base_class, volume) \
+ public: \
+ /* Constructor / Destructor */ \
+ base_class(); \
+ ~base_class(); \
+ /* Data access */ \
+ inline_ const volume* Get##volume() const { return &mBV; } \
+ /* Clear the last bit */ \
+ inline_ const base_class* GetPos() const { return (const base_class*)(mPos&~1); } \
+ inline_ const base_class* GetNeg() const { const base_class* P = GetPos(); return P ? P+1 : null;} \
+ \
+ /* We don't need to test both nodes since we can't have one without the other */ \
+ inline_ bool IsLeaf() const { return !GetPos(); } \
+ \
+ /* Stats */ \
+ inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \
+ protected: \
+ /* Tree-independent data */ \
+ /* Following data always belong to the BV-tree, regardless of what the tree actually contains.*/ \
+ /* Whatever happens we need the two children and the enclosing volume.*/ \
+ volume mBV; /* Global bounding-volume enclosing all the node-related primitives */ \
+ udword mPos; /* "Positive" & "Negative" children */
+#else
+ //! TO BE DOCUMENTED
+ #define IMPLEMENT_TREE(base_class, volume) \
+ public: \
+ /* Constructor / Destructor */ \
+ base_class(); \
+ ~base_class(); \
+ /* Data access */ \
+ inline_ const volume* Get##volume() const { return &mBV; } \
+ /* Clear the last bit */ \
+ inline_ const base_class* GetPos() const { return (const base_class*)(mPos&~1); } \
+ inline_ const base_class* GetNeg() const { return (const base_class*)(mNeg&~1); } \
+ \
+/* inline_ bool IsLeaf() const { return (!GetPos() && !GetNeg()); } */ \
+ /* We don't need to test both nodes since we can't have one without the other */ \
+ inline_ bool IsLeaf() const { return !GetPos(); } \
+ \
+ /* Stats */ \
+ inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \
+ protected: \
+ /* Tree-independent data */ \
+ /* Following data always belong to the BV-tree, regardless of what the tree actually contains.*/ \
+ /* Whatever happens we need the two children and the enclosing volume.*/ \
+ volume mBV; /* Global bounding-volume enclosing all the node-related primitives */ \
+ udword mPos; /* "Positive" child */ \
+ udword mNeg; /* "Negative" child */
+#endif
+
+ typedef void (*CullingCallback) (udword nb_primitives, udword* node_primitives, BOOL need_clipping, void* user_data);
+
+ class OPCODE_API AABBTreeNode
+ {
+ IMPLEMENT_TREE(AABBTreeNode, AABB)
+ public:
+ // Data access
+ inline_ const udword* GetPrimitives() const { return mNodePrimitives; }
+ inline_ udword GetNbPrimitives() const { return mNbPrimitives; }
+
+ protected:
+ // Tree-dependent data
+ udword* mNodePrimitives; //!< Node-related primitives (shortcut to a position in mIndices below)
+ udword mNbPrimitives; //!< Number of primitives for this node
+ // Internal methods
+ udword Split(udword axis, AABBTreeBuilder* builder);
+ bool Subdivide(AABBTreeBuilder* builder);
+ void _BuildHierarchy(AABBTreeBuilder* builder);
+ void _Refit(AABBTreeBuilder* builder);
+ };
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * User-callback, called for each node by the walking code.
+ * \param current [in] current node
+ * \param depth [in] current node's depth
+ * \param user_data [in] user-defined data
+ * \return true to recurse through children, else false to bypass them
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ typedef bool (*WalkingCallback) (const AABBTreeNode* current, udword depth, void* user_data);
+
+ class OPCODE_API AABBTree : public AABBTreeNode
+ {
+ public:
+ // Constructor / Destructor
+ AABBTree();
+ ~AABBTree();
+ // Build
+ bool Build(AABBTreeBuilder* builder);
+ void Release();
+
+ // Data access
+ inline_ const udword* GetIndices() const { return mIndices; } //!< Catch the indices
+ inline_ udword GetNbNodes() const { return mTotalNbNodes; } //!< Catch the number of nodes
+
+ // Infos
+ bool IsComplete() const;
+ // Stats
+ udword ComputeDepth() const;
+ udword GetUsedBytes() const;
+ udword Walk(WalkingCallback callback, void* user_data) const;
+
+ bool Refit(AABBTreeBuilder* builder);
+ bool Refit2(AABBTreeBuilder* builder);
+ private:
+ udword* mIndices; //!< Indices in the app list. Indices are reorganized during build (permutation).
+ AABBTreeNode* mPool; //!< Linear pool of nodes for complete trees. Null otherwise. [Opcode 1.3]
+ // Stats
+ udword mTotalNbNodes; //!< Number of nodes in the tree.
+ };
+
+#endif // __OPC_AABBTREE_H__
diff --git a/Opcode/OpcodeLib/OPC_BaseModel.cpp b/Opcode/OpcodeLib/OPC_BaseModel.cpp
new file mode 100644
index 0000000..4e15809
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_BaseModel.cpp
@@ -0,0 +1,138 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains base model interface.
+ * \file OPC_BaseModel.cpp
+ * \author Pierre Terdiman
+ * \date May, 18, 2003
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * The base class for collision models.
+ *
+ * \class BaseModel
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date May, 18, 2003
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPCODECREATE::OPCODECREATE()
+{
+ mIMesh = null;
+ mSettings.mRules = SPLIT_SPLATTER_POINTS | SPLIT_GEOM_CENTER;
+ mSettings.mLimit = 1; // Mandatory for complete trees
+ mNoLeaf = true;
+ mQuantized = true;
+#ifdef __MESHMERIZER_H__
+ mCollisionHull = false;
+#endif // __MESHMERIZER_H__
+ mKeepOriginal = false;
+ mCanRemap = false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+BaseModel::BaseModel() : mIMesh(null), mModelCode(0), mSource(null), mTree(null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+BaseModel::~BaseModel()
+{
+ ReleaseBase();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Releases everything.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void BaseModel::ReleaseBase()
+{
+ DELETESINGLE(mSource);
+ DELETESINGLE(mTree);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Creates an optimized tree according to user-settings, and setups mModelCode.
+ * \param no_leaf [in] true for "no leaf" tree
+ * \param quantized [in] true for quantized tree
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool BaseModel::CreateTree(bool no_leaf, bool quantized)
+{
+ DELETESINGLE(mTree);
+
+ // Setup model code
+ if(no_leaf) mModelCode |= OPC_NO_LEAF;
+ else mModelCode &= ~OPC_NO_LEAF;
+
+ if(quantized) mModelCode |= OPC_QUANTIZED;
+ else mModelCode &= ~OPC_QUANTIZED;
+
+ // Create the correct class
+ if(mModelCode & OPC_NO_LEAF)
+ {
+ if(mModelCode & OPC_QUANTIZED) mTree = new AABBQuantizedNoLeafTree;
+ else mTree = new AABBNoLeafTree;
+ }
+ else
+ {
+ if(mModelCode & OPC_QUANTIZED) mTree = new AABBQuantizedTree;
+ else mTree = new AABBCollisionTree;
+ }
+ CHECKALLOC(mTree);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the collision model. This can be used to handle dynamic meshes. Usage is:
+ * 1. modify your mesh vertices (keep the topology constant!)
+ * 2. refit the tree (call this method)
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool BaseModel::Refit()
+{
+ // Refit the optimized tree
+ return mTree->Refit(mIMesh);
+
+// Old code kept for reference : refit the source tree then rebuild !
+// if(!mSource) return false;
+// // Ouch...
+// mSource->Refit(&mTB);
+// // Ouch...
+// return mTree->Build(mSource);
+}
diff --git a/Opcode/OpcodeLib/OPC_BaseModel.h b/Opcode/OpcodeLib/OPC_BaseModel.h
new file mode 100644
index 0000000..15fc423
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_BaseModel.h
@@ -0,0 +1,175 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains base model interface.
+ * \file OPC_BaseModel.h
+ * \author Pierre Terdiman
+ * \date May, 18, 2003
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_BASEMODEL_H__
+#define __OPC_BASEMODEL_H__
+
+ //! Model creation structure
+ struct OPCODE_API OPCODECREATE
+ {
+ //! Constructor
+ OPCODECREATE();
+
+ MeshInterface* mIMesh; //!< Mesh interface (access to triangles & vertices) (*)
+ BuildSettings mSettings; //!< Builder's settings
+ bool mNoLeaf; //!< true => discard leaf nodes (else use a normal tree)
+ bool mQuantized; //!< true => quantize the tree (else use a normal tree)
+#ifdef __MESHMERIZER_H__
+ bool mCollisionHull; //!< true => use convex hull + GJK
+#endif // __MESHMERIZER_H__
+ bool mKeepOriginal; //!< true => keep a copy of the original tree (debug purpose)
+ bool mCanRemap; //!< true => allows OPCODE to reorganize client arrays
+
+ // (*) This pointer is saved internally and used by OPCODE until collision structures are released,
+ // so beware of the object's lifetime.
+ };
+
+ enum ModelFlag
+ {
+ OPC_QUANTIZED = (1<<0), //!< Compressed/uncompressed tree
+ OPC_NO_LEAF = (1<<1), //!< Leaf/NoLeaf tree
+ OPC_SINGLE_NODE = (1<<2) //!< Special case for 1-node models
+ };
+
+ class OPCODE_API BaseModel
+ {
+ public:
+ // Constructor/Destructor
+ BaseModel();
+ virtual ~BaseModel();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Builds a collision model.
+ * \param create [in] model creation structure
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual bool Build(const OPCODECREATE& create) = 0;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the number of bytes used by the tree.
+ * \return amount of bytes used
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual udword GetUsedBytes() const = 0;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Refits the collision model. This can be used to handle dynamic meshes. Usage is:
+ * 1. modify your mesh vertices (keep the topology constant!)
+ * 2. refit the tree (call this method)
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual bool Refit();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the source tree.
+ * \return generic tree
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ const AABBTree* GetSourceTree() const { return mSource; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the tree.
+ * \return the collision tree
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ const AABBOptimizedTree* GetTree() const { return mTree; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the tree.
+ * \return the collision tree
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ AABBOptimizedTree* GetTree() { return mTree; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the number of nodes in the tree.
+ * Should be 2*N-1 for normal trees and N-1 for optimized ones.
+ * \return number of nodes
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbNodes() const { return mTree->GetNbNodes(); }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks whether the tree has leaf nodes or not.
+ * \return true if the tree has leaf nodes (normal tree), else false (optimized tree)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL HasLeafNodes() const { return !(mModelCode & OPC_NO_LEAF); }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks whether the tree is quantized or not.
+ * \return true if the tree is quantized
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL IsQuantized() const { return mModelCode & OPC_QUANTIZED; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks whether the model has a single node or not. This special case must be handled separately.
+ * \return true if the model has only 1 node
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL HasSingleNode() const { return mModelCode & OPC_SINGLE_NODE; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the model's code.
+ * \return model's code
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetModelCode() const { return mModelCode; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the mesh interface.
+ * \return mesh interface
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ const MeshInterface* GetMeshInterface() const { return mIMesh; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Sets the mesh interface.
+ * \param imesh [in] mesh interface
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetMeshInterface(const MeshInterface* imesh) { mIMesh = imesh; }
+
+ protected:
+ const MeshInterface* mIMesh; //!< User-defined mesh interface
+ udword mModelCode; //!< Model code = combination of ModelFlag(s)
+ AABBTree* mSource; //!< Original source tree
+ AABBOptimizedTree* mTree; //!< Optimized tree owned by the model
+ // Internal methods
+ void ReleaseBase();
+ bool CreateTree(bool no_leaf, bool quantized);
+ };
+
+#endif //__OPC_BASEMODEL_H__ \ No newline at end of file
diff --git a/Opcode/OpcodeLib/OPC_BoxBoxOverlap.h b/Opcode/OpcodeLib/OPC_BoxBoxOverlap.h
new file mode 100644
index 0000000..fd39dbb
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_BoxBoxOverlap.h
@@ -0,0 +1,122 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * OBB-OBB overlap test using the separating axis theorem.
+ * - original code by Gomez / Gamasutra (similar to Gottschalk's one in RAPID)
+ * - optimized for AABB trees by computing the rotation matrix once (SOLID-fashion)
+ * - the fabs matrix is precomputed as well and epsilon-tweaked (RAPID-style, we found this almost mandatory)
+ * - Class III axes can be disabled... (SOLID & Intel fashion)
+ * - ...or enabled to perform some profiling
+ * - CPU comparisons used when appropriate
+ * - lazy evaluation sometimes saves some work in case of early exits (unlike SOLID)
+ *
+ * \param ea [in] extents from box A
+ * \param ca [in] center from box A
+ * \param eb [in] extents from box B
+ * \param cb [in] center from box B
+ * \return true if boxes overlap
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL AABBTreeCollider::BoxBoxOverlap(const IcePoint& ea, const IcePoint& ca, const IcePoint& eb, const IcePoint& cb)
+{
+ // Stats
+ mNbBVBVTests++;
+
+ float t,t2;
+
+ // Class I : A's basis vectors
+ float Tx = (mR1to0.m[0][0]*cb.x + mR1to0.m[1][0]*cb.y + mR1to0.m[2][0]*cb.z) + mT1to0.x - ca.x;
+ t = ea.x + eb.x*mAR.m[0][0] + eb.y*mAR.m[1][0] + eb.z*mAR.m[2][0];
+ if(GREATER(Tx, t)) return FALSE;
+
+ float Ty = (mR1to0.m[0][1]*cb.x + mR1to0.m[1][1]*cb.y + mR1to0.m[2][1]*cb.z) + mT1to0.y - ca.y;
+ t = ea.y + eb.x*mAR.m[0][1] + eb.y*mAR.m[1][1] + eb.z*mAR.m[2][1];
+ if(GREATER(Ty, t)) return FALSE;
+
+ float Tz = (mR1to0.m[0][2]*cb.x + mR1to0.m[1][2]*cb.y + mR1to0.m[2][2]*cb.z) + mT1to0.z - ca.z;
+ t = ea.z + eb.x*mAR.m[0][2] + eb.y*mAR.m[1][2] + eb.z*mAR.m[2][2];
+ if(GREATER(Tz, t)) return FALSE;
+
+ // Class II : B's basis vectors
+ t = Tx*mR1to0.m[0][0] + Ty*mR1to0.m[0][1] + Tz*mR1to0.m[0][2]; t2 = ea.x*mAR.m[0][0] + ea.y*mAR.m[0][1] + ea.z*mAR.m[0][2] + eb.x;
+ if(GREATER(t, t2)) return FALSE;
+
+ t = Tx*mR1to0.m[1][0] + Ty*mR1to0.m[1][1] + Tz*mR1to0.m[1][2]; t2 = ea.x*mAR.m[1][0] + ea.y*mAR.m[1][1] + ea.z*mAR.m[1][2] + eb.y;
+ if(GREATER(t, t2)) return FALSE;
+
+ t = Tx*mR1to0.m[2][0] + Ty*mR1to0.m[2][1] + Tz*mR1to0.m[2][2]; t2 = ea.x*mAR.m[2][0] + ea.y*mAR.m[2][1] + ea.z*mAR.m[2][2] + eb.z;
+ if(GREATER(t, t2)) return FALSE;
+
+ // Class III : 9 cross products
+ // Cool trick: always perform the full test for first level, regardless of settings.
+ // That way pathological cases (such as the pencils scene) are quickly rejected anyway !
+ if(mFullBoxBoxTest || mNbBVBVTests==1)
+ {
+ t = Tz*mR1to0.m[0][1] - Ty*mR1to0.m[0][2]; t2 = ea.y*mAR.m[0][2] + ea.z*mAR.m[0][1] + eb.y*mAR.m[2][0] + eb.z*mAR.m[1][0]; if(GREATER(t, t2)) return FALSE; // L = A0 x B0
+ t = Tz*mR1to0.m[1][1] - Ty*mR1to0.m[1][2]; t2 = ea.y*mAR.m[1][2] + ea.z*mAR.m[1][1] + eb.x*mAR.m[2][0] + eb.z*mAR.m[0][0]; if(GREATER(t, t2)) return FALSE; // L = A0 x B1
+ t = Tz*mR1to0.m[2][1] - Ty*mR1to0.m[2][2]; t2 = ea.y*mAR.m[2][2] + ea.z*mAR.m[2][1] + eb.x*mAR.m[1][0] + eb.y*mAR.m[0][0]; if(GREATER(t, t2)) return FALSE; // L = A0 x B2
+ t = Tx*mR1to0.m[0][2] - Tz*mR1to0.m[0][0]; t2 = ea.x*mAR.m[0][2] + ea.z*mAR.m[0][0] + eb.y*mAR.m[2][1] + eb.z*mAR.m[1][1]; if(GREATER(t, t2)) return FALSE; // L = A1 x B0
+ t = Tx*mR1to0.m[1][2] - Tz*mR1to0.m[1][0]; t2 = ea.x*mAR.m[1][2] + ea.z*mAR.m[1][0] + eb.x*mAR.m[2][1] + eb.z*mAR.m[0][1]; if(GREATER(t, t2)) return FALSE; // L = A1 x B1
+ t = Tx*mR1to0.m[2][2] - Tz*mR1to0.m[2][0]; t2 = ea.x*mAR.m[2][2] + ea.z*mAR.m[2][0] + eb.x*mAR.m[1][1] + eb.y*mAR.m[0][1]; if(GREATER(t, t2)) return FALSE; // L = A1 x B2
+ t = Ty*mR1to0.m[0][0] - Tx*mR1to0.m[0][1]; t2 = ea.x*mAR.m[0][1] + ea.y*mAR.m[0][0] + eb.y*mAR.m[2][2] + eb.z*mAR.m[1][2]; if(GREATER(t, t2)) return FALSE; // L = A2 x B0
+ t = Ty*mR1to0.m[1][0] - Tx*mR1to0.m[1][1]; t2 = ea.x*mAR.m[1][1] + ea.y*mAR.m[1][0] + eb.x*mAR.m[2][2] + eb.z*mAR.m[0][2]; if(GREATER(t, t2)) return FALSE; // L = A2 x B1
+ t = Ty*mR1to0.m[2][0] - Tx*mR1to0.m[2][1]; t2 = ea.x*mAR.m[2][1] + ea.y*mAR.m[2][0] + eb.x*mAR.m[1][2] + eb.y*mAR.m[0][2]; if(GREATER(t, t2)) return FALSE; // L = A2 x B2
+ }
+ return TRUE;
+}
+
+//! A dedicated version when one box is constant
+inline_ BOOL OBBCollider::BoxBoxOverlap(const IcePoint& extents, const IcePoint& center)
+{
+ // Stats
+ mNbVolumeBVTests++;
+
+ float t,t2;
+
+ // Class I : A's basis vectors
+ float Tx = mTBoxToModel.x - center.x; t = extents.x + mBBx1; if(GREATER(Tx, t)) return FALSE;
+ float Ty = mTBoxToModel.y - center.y; t = extents.y + mBBy1; if(GREATER(Ty, t)) return FALSE;
+ float Tz = mTBoxToModel.z - center.z; t = extents.z + mBBz1; if(GREATER(Tz, t)) return FALSE;
+
+ // Class II : B's basis vectors
+ t = Tx*mRBoxToModel.m[0][0] + Ty*mRBoxToModel.m[0][1] + Tz*mRBoxToModel.m[0][2];
+ t2 = extents.x*mAR.m[0][0] + extents.y*mAR.m[0][1] + extents.z*mAR.m[0][2] + mBoxExtents.x;
+ if(GREATER(t, t2)) return FALSE;
+
+ t = Tx*mRBoxToModel.m[1][0] + Ty*mRBoxToModel.m[1][1] + Tz*mRBoxToModel.m[1][2];
+ t2 = extents.x*mAR.m[1][0] + extents.y*mAR.m[1][1] + extents.z*mAR.m[1][2] + mBoxExtents.y;
+ if(GREATER(t, t2)) return FALSE;
+
+ t = Tx*mRBoxToModel.m[2][0] + Ty*mRBoxToModel.m[2][1] + Tz*mRBoxToModel.m[2][2];
+ t2 = extents.x*mAR.m[2][0] + extents.y*mAR.m[2][1] + extents.z*mAR.m[2][2] + mBoxExtents.z;
+ if(GREATER(t, t2)) return FALSE;
+
+ // Class III : 9 cross products
+ // Cool trick: always perform the full test for first level, regardless of settings.
+ // That way pathological cases (such as the pencils scene) are quickly rejected anyway !
+ if(mFullBoxBoxTest || mNbVolumeBVTests==1)
+ {
+ t = Tz*mRBoxToModel.m[0][1] - Ty*mRBoxToModel.m[0][2]; t2 = extents.y*mAR.m[0][2] + extents.z*mAR.m[0][1] + mBB_1; if(GREATER(t, t2)) return FALSE; // L = A0 x B0
+ t = Tz*mRBoxToModel.m[1][1] - Ty*mRBoxToModel.m[1][2]; t2 = extents.y*mAR.m[1][2] + extents.z*mAR.m[1][1] + mBB_2; if(GREATER(t, t2)) return FALSE; // L = A0 x B1
+ t = Tz*mRBoxToModel.m[2][1] - Ty*mRBoxToModel.m[2][2]; t2 = extents.y*mAR.m[2][2] + extents.z*mAR.m[2][1] + mBB_3; if(GREATER(t, t2)) return FALSE; // L = A0 x B2
+ t = Tx*mRBoxToModel.m[0][2] - Tz*mRBoxToModel.m[0][0]; t2 = extents.x*mAR.m[0][2] + extents.z*mAR.m[0][0] + mBB_4; if(GREATER(t, t2)) return FALSE; // L = A1 x B0
+ t = Tx*mRBoxToModel.m[1][2] - Tz*mRBoxToModel.m[1][0]; t2 = extents.x*mAR.m[1][2] + extents.z*mAR.m[1][0] + mBB_5; if(GREATER(t, t2)) return FALSE; // L = A1 x B1
+ t = Tx*mRBoxToModel.m[2][2] - Tz*mRBoxToModel.m[2][0]; t2 = extents.x*mAR.m[2][2] + extents.z*mAR.m[2][0] + mBB_6; if(GREATER(t, t2)) return FALSE; // L = A1 x B2
+ t = Ty*mRBoxToModel.m[0][0] - Tx*mRBoxToModel.m[0][1]; t2 = extents.x*mAR.m[0][1] + extents.y*mAR.m[0][0] + mBB_7; if(GREATER(t, t2)) return FALSE; // L = A2 x B0
+ t = Ty*mRBoxToModel.m[1][0] - Tx*mRBoxToModel.m[1][1]; t2 = extents.x*mAR.m[1][1] + extents.y*mAR.m[1][0] + mBB_8; if(GREATER(t, t2)) return FALSE; // L = A2 x B1
+ t = Ty*mRBoxToModel.m[2][0] - Tx*mRBoxToModel.m[2][1]; t2 = extents.x*mAR.m[2][1] + extents.y*mAR.m[2][0] + mBB_9; if(GREATER(t, t2)) return FALSE; // L = A2 x B2
+ }
+ return TRUE;
+}
+
+//! A special version for 2 axis-aligned boxes
+inline_ BOOL AABBCollider::AABBAABBOverlap(const IcePoint& extents, const IcePoint& center)
+{
+ // Stats
+ mNbVolumeBVTests++;
+
+ float tx = mBox.mCenter.x - center.x; float ex = extents.x + mBox.mExtents.x; if(GREATER(tx, ex)) return FALSE;
+ float ty = mBox.mCenter.y - center.y; float ey = extents.y + mBox.mExtents.y; if(GREATER(ty, ey)) return FALSE;
+ float tz = mBox.mCenter.z - center.z; float ez = extents.z + mBox.mExtents.z; if(GREATER(tz, ez)) return FALSE;
+
+ return TRUE;
+}
diff --git a/Opcode/OpcodeLib/OPC_Collider.cpp b/Opcode/OpcodeLib/OPC_Collider.cpp
new file mode 100644
index 0000000..bb9663d
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_Collider.cpp
@@ -0,0 +1,54 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains base collider class.
+ * \file OPC_Collider.cpp
+ * \author Pierre Terdiman
+ * \date June, 2, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains the abstract class for colliders.
+ *
+ * \class Collider
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date June, 2, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Collider::Collider() :
+ mFlags (0),
+ mCurrentModel (null),
+ mIMesh (null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Collider::~Collider()
+{
+}
diff --git a/Opcode/OpcodeLib/OPC_Collider.h b/Opcode/OpcodeLib/OPC_Collider.h
new file mode 100644
index 0000000..4495093
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_Collider.h
@@ -0,0 +1,176 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains base collider class.
+ * \file OPC_Collider.h
+ * \author Pierre Terdiman
+ * \date June, 2, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_COLLIDER_H__
+#define __OPC_COLLIDER_H__
+
+ enum CollisionFlag
+ {
+ OPC_FIRST_CONTACT = (1<<0), //!< Report all contacts (false) or only first one (true)
+ OPC_TEMPORAL_COHERENCE = (1<<1), //!< Use temporal coherence or not
+ OPC_CONTACT = (1<<2), //!< Final contact status after a collision query
+ OPC_TEMPORAL_HIT = (1<<3), //!< There has been an early exit due to temporal coherence
+ OPC_NO_PRIMITIVE_TESTS = (1<<4), //!< Keep or discard primitive-bv tests in leaf nodes (volume-mesh queries)
+
+ OPC_CONTACT_FOUND = OPC_FIRST_CONTACT | OPC_CONTACT,
+ OPC_TEMPORAL_CONTACT = OPC_TEMPORAL_HIT | OPC_CONTACT,
+
+ OPC_FORCE_DWORD = 0x7fffffff
+ };
+
+ class OPCODE_API Collider
+ {
+ public:
+ // Constructor / Destructor
+ Collider();
+ virtual ~Collider();
+
+ // Collision report
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the last collision status after a collision query.
+ * \return true if a collision occured
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL GetContactStatus() const { return mFlags & OPC_CONTACT; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the "first contact" mode.
+ * \return true if "first contact" mode is on
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL FirstContactEnabled() const { return mFlags & OPC_FIRST_CONTACT; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the temporal coherence mode.
+ * \return true if temporal coherence is on
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL TemporalCoherenceEnabled() const { return mFlags & OPC_TEMPORAL_COHERENCE; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks a first contact has already been found.
+ * \return true if a first contact has been found and we can stop a query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL ContactFound() const { return (mFlags&OPC_CONTACT_FOUND)==OPC_CONTACT_FOUND; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks there's been an early exit due to temporal coherence;
+ * \return true if a temporal hit has occured
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL TemporalHit() const { return mFlags & OPC_TEMPORAL_HIT; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks primitive tests are enabled;
+ * \return true if primitive tests must be skipped
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL SkipPrimitiveTests() const { return mFlags & OPC_NO_PRIMITIVE_TESTS; }
+
+ // Settings
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Reports all contacts (false) or first contact only (true)
+ * \param flag [in] true for first contact, false for all contacts
+ * \see SetTemporalCoherence(bool flag)
+ * \see ValidateSettings()
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetFirstContact(bool flag)
+ {
+ if(flag) mFlags |= OPC_FIRST_CONTACT;
+ else mFlags &= ~OPC_FIRST_CONTACT;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Enable/disable temporal coherence.
+ * \param flag [in] true to enable temporal coherence, false to discard it
+ * \see SetFirstContact(bool flag)
+ * \see ValidateSettings()
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetTemporalCoherence(bool flag)
+ {
+ if(flag) mFlags |= OPC_TEMPORAL_COHERENCE;
+ else mFlags &= ~OPC_TEMPORAL_COHERENCE;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Enable/disable primitive tests.
+ * \param flag [in] true to enable primitive tests, false to discard them
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetPrimitiveTests(bool flag)
+ {
+ if(!flag) mFlags |= OPC_NO_PRIMITIVE_TESTS;
+ else mFlags &= ~OPC_NO_PRIMITIVE_TESTS;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Validates current settings. You should call this method after all the settings / callbacks have been defined for a collider.
+ * \return null if everything is ok, else a string describing the problem
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual const char* ValidateSettings() = 0;
+
+ protected:
+ udword mFlags; //!< Bit flags
+ const BaseModel* mCurrentModel; //!< Current model for collision query (owner of touched faces)
+ // User mesh interface
+ const MeshInterface* mIMesh; //!< User-defined mesh interface
+
+ // Internal methods
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups current collision model
+ * \param model [in] current collision model
+ * \return TRUE if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL Setup(const BaseModel* model)
+ {
+ // Keep track of current model
+ mCurrentModel = model;
+ if(!mCurrentModel) return FALSE;
+
+ mIMesh = model->GetMeshInterface();
+ return mIMesh!=null;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Initializes a query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual inline_ void InitQuery() { mFlags &= ~OPC_TEMPORAL_CONTACT; }
+ };
+
+#endif // __OPC_COLLIDER_H__
diff --git a/Opcode/OpcodeLib/OPC_Common.cpp b/Opcode/OpcodeLib/OPC_Common.cpp
new file mode 100644
index 0000000..839186b
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_Common.cpp
@@ -0,0 +1,48 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains common classes & defs used in OPCODE.
+ * \file OPC_Common.cpp
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * An AABB dedicated to collision detection.
+ * We don't use the generic AABB class included in ICE, since it can be a Min/Max or a Center/Extents one (depends
+ * on compilation flags). Since the Center/Extents model is more efficient in collision detection, it was worth
+ * using an extra special class.
+ *
+ * \class CollisionAABB
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A quantized AABB.
+ * Center/Extent model, using 16-bits integers.
+ *
+ * \class QuantizedAABB
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
diff --git a/Opcode/OpcodeLib/OPC_Common.h b/Opcode/OpcodeLib/OPC_Common.h
new file mode 100644
index 0000000..3c39ea3
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_Common.h
@@ -0,0 +1,101 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains common classes & defs used in OPCODE.
+ * \file OPC_Common.h
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_COMMON_H__
+#define __OPC_COMMON_H__
+
+// [GOTTFRIED]: Just a small change for readability.
+#ifdef OPC_CPU_COMPARE
+ #define GREATER(x, y) AIR(x) > IR(y)
+#else
+ #define GREATER(x, y) fabsf(x) > (y)
+#endif
+
+ class OPCODE_API CollisionAABB
+ {
+ public:
+ //! Constructor
+ inline_ CollisionAABB() {}
+ //! Constructor
+ inline_ CollisionAABB(const AABB& b) { b.GetCenter(mCenter); b.GetExtents(mExtents); }
+ //! Destructor
+ inline_ ~CollisionAABB() {}
+
+ //! Get min point of the box
+ inline_ void GetMin(IcePoint& min) const { min = mCenter - mExtents; }
+ //! Get max point of the box
+ inline_ void GetMax(IcePoint& max) const { max = mCenter + mExtents; }
+
+ //! Get component of the box's min point along a given axis
+ inline_ float GetMin(udword axis) const { return mCenter[axis] - mExtents[axis]; }
+ //! Get component of the box's max point along a given axis
+ inline_ float GetMax(udword axis) const { return mCenter[axis] + mExtents[axis]; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Setups an AABB from min & max vectors.
+ * \param min [in] the min point
+ * \param max [in] the max point
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetMinMax(const IcePoint& min, const IcePoint& max) { mCenter = (max + min)*0.5f; mExtents = (max - min)*0.5f; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks a box is inside another box.
+ * \param box [in] the other box
+ * \return true if current box is inside input box
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ BOOL IsInside(const CollisionAABB& box) const
+ {
+ if(box.GetMin(0)>GetMin(0)) return FALSE;
+ if(box.GetMin(1)>GetMin(1)) return FALSE;
+ if(box.GetMin(2)>GetMin(2)) return FALSE;
+ if(box.GetMax(0)<GetMax(0)) return FALSE;
+ if(box.GetMax(1)<GetMax(1)) return FALSE;
+ if(box.GetMax(2)<GetMax(2)) return FALSE;
+ return TRUE;
+ }
+
+ IcePoint mCenter; //!< Box center
+ IcePoint mExtents; //!< Box extents
+ };
+
+ class OPCODE_API QuantizedAABB
+ {
+ public:
+ //! Constructor
+ inline_ QuantizedAABB() {}
+ //! Destructor
+ inline_ ~QuantizedAABB() {}
+
+ sword mCenter[3]; //!< Quantized center
+ uword mExtents[3]; //!< Quantized extents
+ };
+
+ //! Quickly rotates & translates a vector
+ inline_ void TransformPoint(IcePoint& dest, const IcePoint& source, const Matrix3x3& rot, const IcePoint& trans)
+ {
+ dest.x = trans.x + source.x * rot.m[0][0] + source.y * rot.m[1][0] + source.z * rot.m[2][0];
+ dest.y = trans.y + source.x * rot.m[0][1] + source.y * rot.m[1][1] + source.z * rot.m[2][1];
+ dest.z = trans.z + source.x * rot.m[0][2] + source.y * rot.m[1][2] + source.z * rot.m[2][2];
+ }
+
+#endif //__OPC_COMMON_H__ \ No newline at end of file
diff --git a/Opcode/OpcodeLib/OPC_HybridModel.cpp b/Opcode/OpcodeLib/OPC_HybridModel.cpp
new file mode 100644
index 0000000..0793e5e
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_HybridModel.cpp
@@ -0,0 +1,466 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for hybrid models.
+ * \file OPC_HybridModel.cpp
+ * \author Pierre Terdiman
+ * \date May, 18, 2003
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * An hybrid collision model.
+ *
+ * The problem :
+ *
+ * Opcode really shines for mesh-mesh collision, especially when meshes are deeply overlapping
+ * (it typically outperforms RAPID in those cases).
+ *
+ * Unfortunately this is not the typical scenario in games.
+ *
+ * For close-proximity cases, especially for volume-mesh queries, it's relatively easy to run faster
+ * than Opcode, that suffers from a relatively high setup time.
+ *
+ * In particular, Opcode's "vanilla" trees in those cases -can- run faster. They can also use -less-
+ * memory than the optimized ones, when you let the system stop at ~10 triangles / leaf for example
+ * (i.e. when you don't use "complete" trees). However, those trees tend to fragment memory quite a
+ * lot, increasing cache misses : since they're not "complete", we can't predict the final number of
+ * nodes and we have to allocate nodes on-the-fly. For the same reasons we can't use Opcode's "optimized"
+ * trees here, since they rely on a known layout to perform the "optimization".
+ *
+ * Hybrid trees :
+ *
+ * Hybrid trees try to combine best of both worlds :
+ *
+ * - they use a maximum limit of 16 triangles/leaf. "16" is used so that we'll be able to save the
+ * number of triangles using 4 bits only.
+ *
+ * - they're still "complete" trees thanks to a two-passes building phase. First we create a "vanilla"
+ * AABB-tree with Opcode, limited to 16 triangles/leaf. Then we create a *second* vanilla tree, this
+ * time using the leaves of the first one. The trick is : this second tree is now "complete"... so we
+ * can further transform it into an Opcode's optimized tree.
+ *
+ * - then we run the collision queries on that standard Opcode tree. The only difference is that leaf
+ * nodes contain indices to leaf nodes of another tree. Also, we have to skip all primitive tests in
+ * Opcode optimized trees, since our leaves don't contain triangles anymore.
+ *
+ * - finally, for each collided leaf, we simply loop through 16 triangles max, and collide them with
+ * the bounding volume used in the query (we only support volume-vs-mesh queries here, not mesh-vs-mesh)
+ *
+ * All of that is wrapped in this "hybrid model" that contains the minimal data required for this to work.
+ * It's a mix between old "vanilla" trees, and old "optimized" trees.
+ *
+ * Extra advantages:
+ *
+ * - If we use them for dynamic models, we're left with a very small number of leaf nodes to refit. It
+ * might be a bit faster since we have less nodes to write back.
+ *
+ * - In rigid body simulation, using temporal coherence and sleeping objects greatly reduce the actual
+ * influence of one tree over another (i.e. the speed difference is often invisible). So memory is really
+ * the key element to consider, and in this regard hybrid trees are just better.
+ *
+ * Information to take home:
+ * - they use less ram
+ * - they're not slower (they're faster or slower depending on cases, overall there's no significant
+ * difference *as long as objects don't interpenetrate too much* - in which case Opcode's optimized trees
+ * are still notably faster)
+ *
+ * \class HybridModel
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date May, 18, 2003
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridModel::HybridModel() :
+ mNbLeaves (0),
+ mNbPrimitives (0),
+ mTriangles (null),
+ mIndices (null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridModel::~HybridModel()
+{
+ Release();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Releases everything.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void HybridModel::Release()
+{
+ ReleaseBase();
+ DELETEARRAY(mIndices);
+ DELETEARRAY(mTriangles);
+ mNbLeaves = 0;
+ mNbPrimitives = 0;
+}
+
+ struct Internal
+ {
+ Internal()
+ {
+ mNbLeaves = 0;
+ mLeaves = null;
+ mTriangles = null;
+ mBase = null;
+ }
+ ~Internal()
+ {
+ DELETEARRAY(mLeaves);
+ }
+
+ udword mNbLeaves;
+ AABB* mLeaves;
+ LeafTriangles* mTriangles;
+ const udword* mBase;
+ };
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds a collision model.
+ * \param create [in] model creation structure
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool HybridModel::Build(const OPCODECREATE& create)
+{
+ // 1) Checkings
+ if(!create.mIMesh || !create.mIMesh->IsValid()) return false;
+
+ // Look for degenerate faces.
+ udword NbDegenerate = create.mIMesh->CheckTopology();
+ if(NbDegenerate) Log("OPCODE WARNING: found %d degenerate faces in model! Collision might report wrong results!\n", NbDegenerate);
+ // We continue nonetheless....
+
+ Release(); // Make sure previous tree has been discarded
+
+ // 1-1) Setup mesh interface automatically
+ SetMeshInterface(create.mIMesh);
+
+ bool Status = false;
+ AABBTree* LeafTree = null;
+ Internal Data;
+
+ // 2) Build a generic AABB Tree.
+ mSource = new AABBTree;
+ CHECKALLOC(mSource);
+
+ // 2-1) Setup a builder. Our primitives here are triangles from input mesh,
+ // so we use an AABBTreeOfTrianglesBuilder.....
+ {
+ AABBTreeOfTrianglesBuilder TB;
+ TB.mIMesh = create.mIMesh;
+ TB.mNbPrimitives = create.mIMesh->GetNbTriangles();
+ TB.mSettings = create.mSettings;
+ TB.mSettings.mLimit = 16; // ### Hardcoded, but maybe we could let the user choose 8 / 16 / 32 ...
+ if(!mSource->Build(&TB)) goto FreeAndExit;
+ }
+
+ // 2-2) Here's the trick : create *another* AABB tree using the leaves of the first one (which are boxes, this time)
+ struct Local
+ {
+ // A callback to count leaf nodes
+ static bool CountLeaves(const AABBTreeNode* current, udword depth, void* user_data)
+ {
+ if(current->IsLeaf())
+ {
+ Internal* Data = (Internal*)user_data;
+ Data->mNbLeaves++;
+ }
+ return true;
+ }
+
+ // A callback to setup leaf nodes in our internal structures
+ static bool SetupLeafData(const AABBTreeNode* current, udword depth, void* user_data)
+ {
+ if(current->IsLeaf())
+ {
+ Internal* Data = (Internal*)user_data;
+
+ // Get current leaf's box
+ Data->mLeaves[Data->mNbLeaves] = *current->GetAABB();
+
+ // Setup leaf data
+ udword Index = (udword(current->GetPrimitives()) - udword(Data->mBase))/sizeof(udword);
+ Data->mTriangles[Data->mNbLeaves].SetData(current->GetNbPrimitives(), Index);
+
+ Data->mNbLeaves++;
+ }
+ return true;
+ }
+ };
+
+ // Walk the tree & count number of leaves
+ Data.mNbLeaves = 0;
+ mSource->Walk(Local::CountLeaves, &Data);
+ mNbLeaves = Data.mNbLeaves; // Keep track of it
+
+ // Special case for 1-leaf meshes
+ if(mNbLeaves==1)
+ {
+ mModelCode |= OPC_SINGLE_NODE;
+ Status = true;
+ goto FreeAndExit;
+ }
+
+ // Allocate our structures
+ Data.mLeaves = new AABB[Data.mNbLeaves]; CHECKALLOC(Data.mLeaves);
+ mTriangles = new LeafTriangles[Data.mNbLeaves]; CHECKALLOC(mTriangles);
+
+ // Walk the tree again & setup leaf data
+ Data.mTriangles = mTriangles;
+ Data.mBase = mSource->GetIndices();
+ Data.mNbLeaves = 0; // Reset for incoming walk
+ mSource->Walk(Local::SetupLeafData, &Data);
+
+ // Handle source indices
+ {
+ bool MustKeepIndices = true;
+ if(create.mCanRemap)
+ {
+ // We try to get rid of source indices (saving more ram!) by reorganizing triangle arrays...
+ // Remap can fail when we use callbacks => keep track of indices in that case (it still
+ // works, only using more memory)
+ if(create.mIMesh->RemapClient(mSource->GetNbPrimitives(), mSource->GetIndices()))
+ {
+ MustKeepIndices = false;
+ }
+ }
+
+ if(MustKeepIndices)
+ {
+ // Keep track of source indices (from vanilla tree)
+ mNbPrimitives = mSource->GetNbPrimitives();
+ mIndices = new udword[mNbPrimitives];
+ CopyMemory(mIndices, mSource->GetIndices(), mNbPrimitives*sizeof(udword));
+ }
+ }
+
+ // Now, create our optimized tree using previous leaf nodes
+ LeafTree = new AABBTree;
+ CHECKALLOC(LeafTree);
+ {
+ AABBTreeOfAABBsBuilder TB; // Now using boxes !
+ TB.mSettings = create.mSettings;
+ TB.mSettings.mLimit = 1; // We now want a complete tree so that we can "optimize" it
+ TB.mNbPrimitives = Data.mNbLeaves;
+ TB.mAABBArray = Data.mLeaves;
+ if(!LeafTree->Build(&TB)) goto FreeAndExit;
+ }
+
+ // 3) Create an optimized tree according to user-settings
+ if(!CreateTree(create.mNoLeaf, create.mQuantized)) goto FreeAndExit;
+
+ // 3-2) Create optimized tree
+ if(!mTree->Build(LeafTree)) goto FreeAndExit;
+
+ // Finally ok...
+ Status = true;
+
+FreeAndExit: // Allow me this one...
+ DELETESINGLE(LeafTree);
+
+ // 3-3) Delete generic tree if needed
+ if(!create.mKeepOriginal) DELETESINGLE(mSource);
+
+ return Status;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the number of bytes used by the tree.
+ * \return amount of bytes used
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword HybridModel::GetUsedBytes() const
+{
+ udword UsedBytes = 0;
+ if(mTree) UsedBytes += mTree->GetUsedBytes();
+ if(mIndices) UsedBytes += mNbPrimitives * sizeof(udword); // mIndices
+ if(mTriangles) UsedBytes += mNbLeaves * sizeof(LeafTriangles); // mTriangles
+ return UsedBytes;
+}
+
+inline_ void ComputeMinMax(IcePoint& min, IcePoint& max, const VertexPointers& vp)
+{
+ // Compute triangle's AABB = a leaf box
+#ifdef OPC_USE_FCOMI // a 15% speedup on my machine, not much
+ min.x = FCMin3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x);
+ max.x = FCMax3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x);
+
+ min.y = FCMin3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y);
+ max.y = FCMax3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y);
+
+ min.z = FCMin3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z);
+ max.z = FCMax3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z);
+#else
+ min = *vp.Vertex[0];
+ max = *vp.Vertex[0];
+ min.Min(*vp.Vertex[1]);
+ max.Max(*vp.Vertex[1]);
+ min.Min(*vp.Vertex[2]);
+ max.Max(*vp.Vertex[2]);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the collision model. This can be used to handle dynamic meshes. Usage is:
+ * 1. modify your mesh vertices (keep the topology constant!)
+ * 2. refit the tree (call this method)
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool HybridModel::Refit()
+{
+ if(!mIMesh) return false;
+ if(!mTree) return false;
+
+ if(IsQuantized()) return false;
+ if(HasLeafNodes()) return false;
+
+ const LeafTriangles* LT = GetLeafTriangles();
+ const udword* Indices = GetIndices();
+
+ // Bottom-up update
+ VertexPointers VP;
+ IcePoint Min,Max;
+ IcePoint Min_,Max_;
+ udword Index = mTree->GetNbNodes();
+ AABBNoLeafNode* Nodes = (AABBNoLeafNode*)((AABBNoLeafTree*)mTree)->GetNodes();
+ while(Index--)
+ {
+ AABBNoLeafNode& Current = Nodes[Index];
+
+ if(Current.HasPosLeaf())
+ {
+ const LeafTriangles& CurrentLeaf = LT[Current.GetPosPrimitive()];
+
+ Min.SetPlusInfinity();
+ Max.SetMinusInfinity();
+
+ IcePoint TmpMin, TmpMax;
+
+ // Each leaf box has a set of triangles
+ udword NbTris = CurrentLeaf.GetNbTriangles();
+ if(Indices)
+ {
+ const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ mIMesh->GetTriangle(VP, *T++);
+ ComputeMinMax(TmpMin, TmpMax, VP);
+ Min.Min(TmpMin);
+ Max.Max(TmpMax);
+ }
+ }
+ else
+ {
+ udword BaseIndex = CurrentLeaf.GetTriangleIndex();
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ mIMesh->GetTriangle(VP, BaseIndex++);
+ ComputeMinMax(TmpMin, TmpMax, VP);
+ Min.Min(TmpMin);
+ Max.Max(TmpMax);
+ }
+ }
+ }
+ else
+ {
+ const CollisionAABB& CurrentBox = Current.GetPos()->mAABB;
+ CurrentBox.GetMin(Min);
+ CurrentBox.GetMax(Max);
+ }
+
+ if(Current.HasNegLeaf())
+ {
+ const LeafTriangles& CurrentLeaf = LT[Current.GetNegPrimitive()];
+
+ Min_.SetPlusInfinity();
+ Max_.SetMinusInfinity();
+
+ IcePoint TmpMin, TmpMax;
+
+ // Each leaf box has a set of triangles
+ udword NbTris = CurrentLeaf.GetNbTriangles();
+ if(Indices)
+ {
+ const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ mIMesh->GetTriangle(VP, *T++);
+ ComputeMinMax(TmpMin, TmpMax, VP);
+ Min_.Min(TmpMin);
+ Max_.Max(TmpMax);
+ }
+ }
+ else
+ {
+ udword BaseIndex = CurrentLeaf.GetTriangleIndex();
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ mIMesh->GetTriangle(VP, BaseIndex++);
+ ComputeMinMax(TmpMin, TmpMax, VP);
+ Min_.Min(TmpMin);
+ Max_.Max(TmpMax);
+ }
+ }
+ }
+ else
+ {
+ const CollisionAABB& CurrentBox = Current.GetNeg()->mAABB;
+ CurrentBox.GetMin(Min_);
+ CurrentBox.GetMax(Max_);
+ }
+#ifdef OPC_USE_FCOMI
+ Min.x = FCMin2(Min.x, Min_.x);
+ Max.x = FCMax2(Max.x, Max_.x);
+ Min.y = FCMin2(Min.y, Min_.y);
+ Max.y = FCMax2(Max.y, Max_.y);
+ Min.z = FCMin2(Min.z, Min_.z);
+ Max.z = FCMax2(Max.z, Max_.z);
+#else
+ Min.Min(Min_);
+ Max.Max(Max_);
+#endif
+ Current.mAABB.SetMinMax(Min, Max);
+ }
+ return true;
+}
diff --git a/Opcode/OpcodeLib/OPC_HybridModel.h b/Opcode/OpcodeLib/OPC_HybridModel.h
new file mode 100644
index 0000000..7833a94
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_HybridModel.h
@@ -0,0 +1,106 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for hybrid models.
+ * \file OPC_HybridModel.h
+ * \author Pierre Terdiman
+ * \date May, 18, 2003
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_HYBRIDMODEL_H__
+#define __OPC_HYBRIDMODEL_H__
+
+ //! Leaf descriptor
+ struct LeafTriangles
+ {
+ udword Data; //!< Packed data
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets number of triangles in the leaf.
+ * \return number of triangles N, with 0 < N <= 16
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbTriangles() const { return (Data & 15)+1; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets triangle index for this leaf. Indexed model's array of indices retrieved with HybridModel::GetIndices()
+ * \return triangle index
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetTriangleIndex() const { return Data>>4; }
+ inline_ void SetData(udword nb, udword index) { ASSERT(nb>0 && nb<=16); nb--; Data = (index<<4)|(nb&15); }
+ };
+
+ class OPCODE_API HybridModel : public BaseModel
+ {
+ public:
+ // Constructor/Destructor
+ HybridModel();
+ virtual ~HybridModel();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Builds a collision model.
+ * \param create [in] model creation structure
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(BaseModel) bool Build(const OPCODECREATE& create);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the number of bytes used by the tree.
+ * \return amount of bytes used
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(BaseModel) udword GetUsedBytes() const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Refits the collision model. This can be used to handle dynamic meshes. Usage is:
+ * 1. modify your mesh vertices (keep the topology constant!)
+ * 2. refit the tree (call this method)
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(BaseModel) bool Refit();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets array of triangles.
+ * \return array of triangles
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ const LeafTriangles* GetLeafTriangles() const { return mTriangles; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets array of indices.
+ * \return array of indices
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ const udword* GetIndices() const { return mIndices; }
+
+ private:
+ udword mNbLeaves; //!< Number of leaf nodes in the model
+ LeafTriangles* mTriangles; //!< Array of mNbLeaves leaf descriptors
+ udword mNbPrimitives; //!< Number of primitives in the model
+ udword* mIndices; //!< Array of primitive indices
+
+ // Internal methods
+ void Release();
+ };
+
+#endif // __OPC_HYBRIDMODEL_H__
diff --git a/Opcode/OpcodeLib/OPC_IceHook.h b/Opcode/OpcodeLib/OPC_IceHook.h
new file mode 100644
index 0000000..8b97eaa
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_IceHook.h
@@ -0,0 +1,70 @@
+
+// Should be included by Opcode.h if needed
+
+ #define ICE_DONT_CHECK_COMPILER_OPTIONS
+
+ // From Windows...
+ typedef int BOOL;
+ #ifndef FALSE
+ #define FALSE 0
+ #endif
+
+ #ifndef TRUE
+ #define TRUE 1
+ #endif
+
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <assert.h>
+ #include <string.h>
+ #include <float.h>
+ #include <Math.h>
+
+ #ifndef ASSERT
+ #define ASSERT(exp) {}
+ #endif
+ #define ICE_COMPILE_TIME_ASSERT(exp) extern char ICE_Dummy[ (exp) ? 1 : -1 ]
+
+ #define Log {}
+ #define SetIceError false
+ #define EC_OUTOFMEMORY "Out of memory"
+
+ #include ".\Ice\IcePreprocessor.h"
+
+ #undef ICECORE_API
+ #define ICECORE_API OPCODE_API
+
+ #include ".\Ice\IceTypes.h"
+ #include ".\Ice\IceFPU.h"
+ #include ".\Ice\IceMemoryMacros.h"
+
+ namespace IceCore
+ {
+ #include ".\Ice\IceUtils.h"
+ #include ".\Ice\IceContainer.h"
+ #include ".\Ice\IcePairs.h"
+ #include ".\Ice\IceRevisitedRadix.h"
+ #include ".\Ice\IceRandom.h"
+ }
+ using namespace IceCore;
+
+ #define ICEMATHS_API OPCODE_API
+ namespace IceMaths
+ {
+ #include ".\Ice\IceAxes.h"
+ #include ".\Ice\IcePoint.h"
+ #include ".\Ice\IceHPoint.h"
+ #include ".\Ice\IceMatrix3x3.h"
+ #include ".\Ice\IceMatrix4x4.h"
+ #include ".\Ice\IcePlane.h"
+ #include ".\Ice\IceRay.h"
+ #include ".\Ice\IceIndexedTriangle.h"
+ #include ".\Ice\IceTriangle.h"
+ #include ".\Ice\IceTriList.h"
+ #include ".\Ice\IceAABB.h"
+ #include ".\Ice\IceOBB.h"
+ #include ".\Ice\IceBoundingSphere.h"
+ #include ".\Ice\IceSegment.h"
+ #include ".\Ice\IceLSS.h"
+ }
+ using namespace IceMaths;
diff --git a/Opcode/OpcodeLib/OPC_MeshInterface.cpp b/Opcode/OpcodeLib/OPC_MeshInterface.cpp
new file mode 100644
index 0000000..9f45c25
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_MeshInterface.cpp
@@ -0,0 +1,299 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a mesh interface.
+ * \file OPC_MeshInterface.cpp
+ * \author Pierre Terdiman
+ * \date November, 27, 2002
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * This structure holds 3 vertex-pointers. It's mainly used by collision callbacks so that the app doesn't have
+ * to return 3 vertices to OPCODE (36 bytes) but only 3 pointers (12 bytes). It seems better but I never profiled
+ * the alternative.
+ *
+ * \class VertexPointers
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * This class is an interface between us and user-defined meshes. Meshes can be defined in a lot of ways, and here we
+ * try to support most of them.
+ *
+ * Basically you have two options:
+ * - callbacks, if OPC_USE_CALLBACKS is defined in OPC_Settings.h.
+ * - else pointers.
+ *
+ * If using pointers, you can also use strides or not. Strides are used when OPC_USE_STRIDE is defined.
+ *
+ *
+ * CALLBACKS:
+ *
+ * Using callbacks is the most generic way to feed OPCODE with your meshes. Indeed, you just have to give
+ * access to three vertices at the end of the day. It's up to you to fetch them from your database, using
+ * whatever method you want. Hence your meshes can lie in system memory or AGP, be indexed or not, use 16
+ * or 32-bits indices, you can decompress them on-the-fly if needed, etc. On the other hand, a callback is
+ * called each time OPCODE needs access to a particular triangle, so there might be a slight overhead.
+ *
+ * To make things clear: geometry & topology are NOT stored in the collision system,
+ * in order to save some ram. So, when the system needs them to perform accurate intersection
+ * tests, you're requested to provide the triangle-vertices corresponding to a given face index.
+ *
+ * Ex:
+ *
+ * \code
+ * static void ColCallback(udword triangle_index, VertexPointers& triangle, udword user_data)
+ * {
+ * // Get back Mesh0 or Mesh1 (you also can use 2 different callbacks)
+ * Mesh* MyMesh = (Mesh*)user_data;
+ * // Get correct triangle in the app-controlled database
+ * const Triangle* Tri = MyMesh->GetTriangle(triangle_index);
+ * // Setup pointers to vertices for the collision system
+ * triangle.Vertex[0] = MyMesh->GetVertex(Tri->mVRef[0]);
+ * triangle.Vertex[1] = MyMesh->GetVertex(Tri->mVRef[1]);
+ * triangle.Vertex[2] = MyMesh->GetVertex(Tri->mVRef[2]);
+ * }
+ *
+ * // Setup callbacks
+ * MeshInterface0->SetCallback(ColCallback, udword(Mesh0));
+ * MeshInterface1->SetCallback(ColCallback, udword(Mesh1));
+ * \endcode
+ *
+ * Of course, you should make this callback as fast as possible. And you're also not supposed
+ * to modify the geometry *after* the collision trees have been built. The alternative was to
+ * store the geometry & topology in the collision system as well (as in RAPID) but we have found
+ * this approach to waste a lot of ram in many cases.
+ *
+ *
+ * POINTERS:
+ *
+ * If you're internally using the following canonical structures:
+ * - a vertex made of three 32-bits floating point values
+ * - a triangle made of three 32-bits integer vertex references
+ * ...then you may want to use pointers instead of callbacks. This is the same, except OPCODE will directly
+ * use provided pointers to access the topology and geometry, without using a callback. It might be faster,
+ * but probably not as safe. Pointers have been introduced in OPCODE 1.2.
+ *
+ * Ex:
+ *
+ * \code
+ * // Setup pointers
+ * MeshInterface0->SetPointers(Mesh0->GetFaces(), Mesh0->GetVerts());
+ * MeshInterface1->SetPointers(Mesh1->GetFaces(), Mesh1->GetVerts());
+ * \endcode
+ *
+ *
+ * STRIDES:
+ *
+ * If your vertices are D3D-like entities interleaving a position, a normal and/or texture coordinates
+ * (i.e. if your vertices are FVFs), you might want to use a vertex stride to skip extra data OPCODE
+ * doesn't need. Using a stride shouldn't be notably slower than not using it, but it might increase
+ * cache misses. Please also note that you *shouldn't* read from AGP or video-memory buffers !
+ *
+ *
+ * In any case, compilation flags are here to select callbacks/pointers/strides at compile time, so
+ * choose what's best for your application. All of this has been wrapped into this MeshInterface.
+ *
+ * \class MeshInterface
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date November, 27, 2002
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+MeshInterface::MeshInterface() :
+#ifdef OPC_USE_CALLBACKS
+ mUserData (null),
+ mObjCallback (null),
+#else
+ mTris (null),
+ mVerts (null),
+ #ifdef OPC_USE_STRIDE
+ mTriStride (sizeof(IndexedTriangle)),
+ mVertexStride (sizeof(IcePoint)),
+ #endif
+#endif
+ mNbTris (0),
+ mNbVerts (0)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+MeshInterface::~MeshInterface()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks the mesh interface is valid, i.e. things have been setup correctly.
+ * \return true if valid
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool MeshInterface::IsValid() const
+{
+ if(!mNbTris || !mNbVerts) return false;
+#ifdef OPC_USE_CALLBACKS
+ if(!mObjCallback) return false;
+#else
+ if(!mTris || !mVerts) return false;
+#endif
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks the mesh itself is valid.
+ * Currently we only look for degenerate faces.
+ * \return number of degenerate faces
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword MeshInterface::CheckTopology() const
+{
+ // Check topology. If the model contains degenerate faces, collision report can be wrong in some cases.
+ // e.g. it happens with the standard MAX teapot. So clean your meshes first... If you don't have a mesh cleaner
+ // you can try this: www.codercorner.com/Consolidation.zip
+
+ udword NbDegenerate = 0;
+
+ VertexPointers VP;
+
+ // Using callbacks, we don't have access to vertex indices. Nevertheless we still can check for
+ // redundant vertex pointers, which cover all possibilities (callbacks/pointers/strides).
+ for(udword i=0;i<mNbTris;i++)
+ {
+ GetTriangle(VP, i);
+
+ if( (VP.Vertex[0]==VP.Vertex[1])
+ || (VP.Vertex[1]==VP.Vertex[2])
+ || (VP.Vertex[2]==VP.Vertex[0])) NbDegenerate++;
+ }
+
+ return NbDegenerate;
+}
+
+#ifdef OPC_USE_CALLBACKS
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Callback control: setups object callback. Must provide triangle-vertices for a given triangle index.
+ * \param callback [in] user-defined callback
+ * \param user_data [in] user-defined data
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool MeshInterface::SetCallback(RequestCallback callback, void* user_data)
+{
+ if(!callback) return SetIceError("MeshInterface::SetCallback: callback pointer is null");
+
+ mObjCallback = callback;
+ mUserData = user_data;
+ return true;
+}
+#else
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Pointers control: setups object pointers. Must provide access to faces and vertices for a given object.
+ * \param tris [in] pointer to triangles
+ * \param verts [in] pointer to vertices
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool MeshInterface::SetPointers(const IndexedTriangle* tris, const IcePoint* verts)
+{
+ if(!tris || !verts) return SetIceError("MeshInterface::SetPointers: pointer is null", null);
+
+ mTris = tris;
+ mVerts = verts;
+ return true;
+}
+#ifdef OPC_USE_STRIDE
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Strides control
+ * \param tri_stride [in] size of a triangle in bytes. The first sizeof(IndexedTriangle) bytes are used to get vertex indices.
+ * \param vertex_stride [in] size of a vertex in bytes. The first sizeof(IcePoint) bytes are used to get vertex position.
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool MeshInterface::SetStrides(udword tri_stride, udword vertex_stride)
+{
+ if(tri_stride<sizeof(IndexedTriangle)) return SetIceError("MeshInterface::SetStrides: invalid triangle stride", null);
+ if(vertex_stride<sizeof(IcePoint)) return SetIceError("MeshInterface::SetStrides: invalid vertex stride", null);
+
+ mTriStride = tri_stride;
+ mVertexStride = vertex_stride;
+ return true;
+}
+#endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Remaps client's mesh according to a permutation.
+ * \param nb_indices [in] number of indices in the permutation (will be checked against number of triangles)
+ * \param permutation [in] list of triangle indices
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool MeshInterface::RemapClient(udword nb_indices, const udword* permutation) const
+{
+ // Checkings
+ if(!nb_indices || !permutation) return false;
+ if(nb_indices!=mNbTris) return false;
+
+#ifdef OPC_USE_CALLBACKS
+ // We can't really do that using callbacks
+ return false;
+#else
+ IndexedTriangle* Tmp = new IndexedTriangle[mNbTris];
+ CHECKALLOC(Tmp);
+
+ #ifdef OPC_USE_STRIDE
+ udword Stride = mTriStride;
+ #else
+ udword Stride = sizeof(IndexedTriangle);
+ #endif
+
+ for(udword i=0;i<mNbTris;i++)
+ {
+ const IndexedTriangle* T = (const IndexedTriangle*)(((ubyte*)mTris) + i * Stride);
+ Tmp[i] = *T;
+ }
+
+ for(udword i=0;i<mNbTris;i++)
+ {
+ IndexedTriangle* T = (IndexedTriangle*)(((ubyte*)mTris) + i * Stride);
+ *T = Tmp[permutation[i]];
+ }
+
+ DELETEARRAY(Tmp);
+#endif
+ return true;
+}
diff --git a/Opcode/OpcodeLib/OPC_MeshInterface.h b/Opcode/OpcodeLib/OPC_MeshInterface.h
new file mode 100644
index 0000000..7770b40
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_MeshInterface.h
@@ -0,0 +1,182 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a mesh interface.
+ * \file OPC_MeshInterface.h
+ * \author Pierre Terdiman
+ * \date November, 27, 2002
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_MESHINTERFACE_H__
+#define __OPC_MESHINTERFACE_H__
+
+ struct VertexPointers
+ {
+ const IcePoint* Vertex[3];
+
+ bool BackfaceCulling(const IcePoint& source)
+ {
+ const IcePoint& p0 = *Vertex[0];
+ const IcePoint& p1 = *Vertex[1];
+ const IcePoint& p2 = *Vertex[2];
+
+ // Compute normal direction
+ IcePoint Normal = (p2 - p1)^(p0 - p1);
+
+ // Backface culling
+ return (Normal | (source - p0)) >= 0.0f;
+ }
+ };
+
+#ifdef OPC_USE_CALLBACKS
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * User-callback, called by OPCODE to request vertices from the app.
+ * \param triangle_index [in] face index for which the system is requesting the vertices
+ * \param triangle [out] triangle's vertices (must be provided by the user)
+ * \param user_data [in] user-defined data from SetCallback()
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ typedef void (*RequestCallback) (udword triangle_index, VertexPointers& triangle, void* user_data);
+#endif
+
+ class OPCODE_API MeshInterface
+ {
+ public:
+ // Constructor / Destructor
+ MeshInterface();
+ ~MeshInterface();
+ // Common settings
+ inline_ udword GetNbTriangles() const { return mNbTris; }
+ inline_ udword GetNbVertices() const { return mNbVerts; }
+ inline_ void SetNbTriangles(udword nb) { mNbTris = nb; }
+ inline_ void SetNbVertices(udword nb) { mNbVerts = nb; }
+
+#ifdef OPC_USE_CALLBACKS
+ // Callback settings
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Callback control: setups object callback. Must provide triangle-vertices for a given triangle index.
+ * \param callback [in] user-defined callback
+ * \param user_data [in] user-defined data
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool SetCallback(RequestCallback callback, void* user_data);
+ inline_ void* GetUserData() const { return mUserData; }
+ inline_ RequestCallback GetCallback() const { return mObjCallback; }
+#else
+ // Pointers settings
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Pointers control: setups object pointers. Must provide access to faces and vertices for a given object.
+ * \param tris [in] pointer to triangles
+ * \param verts [in] pointer to vertices
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool SetPointers(const IndexedTriangle* tris, const IcePoint* verts);
+ inline_ const IndexedTriangle* GetTris() const { return mTris; }
+ inline_ const IcePoint* GetVerts() const { return mVerts; }
+
+ #ifdef OPC_USE_STRIDE
+ // Strides settings
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Strides control
+ * \param tri_stride [in] size of a triangle in bytes. The first sizeof(IndexedTriangle) bytes are used to get vertex indices.
+ * \param vertex_stride [in] size of a vertex in bytes. The first sizeof(IcePoint) bytes are used to get vertex position.
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool SetStrides(udword tri_stride=sizeof(IndexedTriangle), udword vertex_stride=sizeof(IcePoint));
+ inline_ udword GetTriStride() const { return mTriStride; }
+ inline_ udword GetVertexStride() const { return mVertexStride; }
+ #endif
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Fetches a triangle given a triangle index.
+ * \param vp [out] required triangle's vertex pointers
+ * \param index [in] triangle index
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void GetTriangle(VertexPointers& vp, udword index) const
+ {
+#ifdef OPC_USE_CALLBACKS
+ (mObjCallback)(index, vp, mUserData);
+#else
+ #ifdef OPC_USE_STRIDE
+ const IndexedTriangle* T = (const IndexedTriangle*)(((ubyte*)mTris) + index * mTriStride);
+ vp.Vertex[0] = (const IcePoint*)(((ubyte*)mVerts) + T->mVRef[0] * mVertexStride);
+ vp.Vertex[1] = (const IcePoint*)(((ubyte*)mVerts) + T->mVRef[1] * mVertexStride);
+ vp.Vertex[2] = (const IcePoint*)(((ubyte*)mVerts) + T->mVRef[2] * mVertexStride);
+ #else
+ const IndexedTriangle* T = &mTris[index];
+ vp.Vertex[0] = &mVerts[T->mVRef[0]];
+ vp.Vertex[1] = &mVerts[T->mVRef[1]];
+ vp.Vertex[2] = &mVerts[T->mVRef[2]];
+ #endif
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Remaps client's mesh according to a permutation.
+ * \param nb_indices [in] number of indices in the permutation (will be checked against number of triangles)
+ * \param permutation [in] list of triangle indices
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ bool RemapClient(udword nb_indices, const udword* permutation) const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks the mesh interface is valid, i.e. things have been setup correctly.
+ * \return true if valid
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool IsValid() const;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Checks the mesh itself is valid.
+ * Currently we only look for degenerate faces.
+ * \return number of degenerate faces
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ udword CheckTopology() const;
+ private:
+
+ udword mNbTris; //!< Number of triangles in the input model
+ udword mNbVerts; //!< Number of vertices in the input model
+#ifdef OPC_USE_CALLBACKS
+ // User callback
+ void* mUserData; //!< User-defined data sent to callback
+ RequestCallback mObjCallback; //!< Object callback
+#else
+ // User pointers
+ const IndexedTriangle* mTris; //!< Array of indexed triangles
+ const IcePoint* mVerts; //!< Array of vertices
+ #ifdef OPC_USE_STRIDE
+ udword mTriStride; //!< Possible triangle stride in bytes [Opcode 1.3]
+ udword mVertexStride; //!< Possible vertex stride in bytes [Opcode 1.3]
+ #endif
+#endif
+ };
+
+#endif //__OPC_MESHINTERFACE_H__ \ No newline at end of file
diff --git a/Opcode/OpcodeLib/OPC_Model.cpp b/Opcode/OpcodeLib/OPC_Model.cpp
new file mode 100644
index 0000000..0616c4d
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_Model.cpp
@@ -0,0 +1,222 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for OPCODE models.
+ * \file OPC_Model.cpp
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * The main collision wrapper, for all trees. Supported trees are:
+ * - Normal trees (2*N-1 nodes, full size)
+ * - No-leaf trees (N-1 nodes, full size)
+ * - Quantized trees (2*N-1 nodes, half size)
+ * - Quantized no-leaf trees (N-1 nodes, half size)
+ *
+ * Usage:
+ *
+ * 1) Create a static mesh interface using callbacks or pointers. (see OPC_MeshInterface.cpp).
+ * Keep it around in your app, since a pointer to this interface is saved internally and
+ * used until you release the collision structures.
+ *
+ * 2) Build a Model using a creation structure:
+ *
+ * \code
+ * Model Sample;
+ *
+ * OPCODECREATE OPCC;
+ * OPCC.IMesh = ...;
+ * OPCC.Rules = ...;
+ * OPCC.NoLeaf = ...;
+ * OPCC.Quantized = ...;
+ * OPCC.KeepOriginal = ...;
+ * bool Status = Sample.Build(OPCC);
+ * \endcode
+ *
+ * 3) Create a tree collider and set it up:
+ *
+ * \code
+ * AABBTreeCollider TC;
+ * TC.SetFirstContact(...);
+ * TC.SetFullBoxBoxTest(...);
+ * TC.SetFullPrimBoxTest(...);
+ * TC.SetTemporalCoherence(...);
+ * \endcode
+ *
+ * 4) Perform a collision query
+ *
+ * \code
+ * // Setup cache
+ * static BVTCache ColCache;
+ * ColCache.Model0 = &Model0;
+ * ColCache.Model1 = &Model1;
+ *
+ * // Collision query
+ * bool IsOk = TC.Collide(ColCache, World0, World1);
+ *
+ * // Get collision status => if true, objects overlap
+ * BOOL Status = TC.GetContactStatus();
+ *
+ * // Number of colliding pairs and list of pairs
+ * udword NbPairs = TC.GetNbPairs();
+ * const Pair* p = TC.GetPairs()
+ * \endcode
+ *
+ * 5) Stats
+ *
+ * \code
+ * Model0.GetUsedBytes() = number of bytes used for this collision tree
+ * TC.GetNbBVBVTests() = number of BV-BV overlap tests performed during last query
+ * TC.GetNbPrimPrimTests() = number of Triangle-Triangle overlap tests performed during last query
+ * TC.GetNbBVPrimTests() = number of Triangle-BV overlap tests performed during last query
+ * \endcode
+ *
+ * \class Model
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Model::Model()
+{
+#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE !
+ mHull = null;
+#endif // __MESHMERIZER_H__
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Model::~Model()
+{
+ Release();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Releases the model.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Model::Release()
+{
+ ReleaseBase();
+#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE !
+ DELETESINGLE(mHull);
+#endif // __MESHMERIZER_H__
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds a collision model.
+ * \param create [in] model creation structure
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Model::Build(const OPCODECREATE& create)
+{
+ // 1) Checkings
+ if(!create.mIMesh || !create.mIMesh->IsValid()) return false;
+
+ // For this model, we only support complete trees
+ if(create.mSettings.mLimit!=1) return SetIceError("OPCODE WARNING: supports complete trees only! Use mLimit = 1.\n", null);
+
+ // Look for degenerate faces.
+ udword NbDegenerate = create.mIMesh->CheckTopology();
+ if(NbDegenerate) Log("OPCODE WARNING: found %d degenerate faces in model! Collision might report wrong results!\n", NbDegenerate);
+ // We continue nonetheless....
+
+ Release(); // Make sure previous tree has been discarded [Opcode 1.3, thanks Adam]
+
+ // 1-1) Setup mesh interface automatically [Opcode 1.3]
+ SetMeshInterface(create.mIMesh);
+
+ // Special case for 1-triangle meshes [Opcode 1.3]
+ udword NbTris = create.mIMesh->GetNbTriangles();
+ if(NbTris==1)
+ {
+ // We don't need to actually create a tree here, since we'll only have a single triangle to deal with anyway.
+ // It's a waste to use a "model" for this but at least it will work.
+ mModelCode |= OPC_SINGLE_NODE;
+ return true;
+ }
+
+ // 2) Build a generic AABB Tree.
+ mSource = new AABBTree;
+ CHECKALLOC(mSource);
+
+ // 2-1) Setup a builder. Our primitives here are triangles from input mesh,
+ // so we use an AABBTreeOfTrianglesBuilder.....
+ {
+ AABBTreeOfTrianglesBuilder TB;
+ TB.mIMesh = create.mIMesh;
+ TB.mSettings = create.mSettings;
+ TB.mNbPrimitives = NbTris;
+ if(!mSource->Build(&TB)) return false;
+ }
+
+ // 3) Create an optimized tree according to user-settings
+ if(!CreateTree(create.mNoLeaf, create.mQuantized)) return false;
+
+ // 3-2) Create optimized tree
+ if(!mTree->Build(mSource)) return false;
+
+ // 3-3) Delete generic tree if needed
+ if(!create.mKeepOriginal) DELETESINGLE(mSource);
+
+#ifdef __MESHMERIZER_H__
+ // 4) Convex hull
+ if(create.mCollisionHull)
+ {
+ // Create hull
+ mHull = new CollisionHull;
+ CHECKALLOC(mHull);
+
+ CONVEXHULLCREATE CHC;
+ // ### doesn't work with strides
+ CHC.NbVerts = create.mIMesh->GetNbVertices();
+ CHC.Vertices = create.mIMesh->GetVerts();
+ CHC.UnifyNormals = true;
+ CHC.ReduceVertices = true;
+ CHC.WordFaces = false;
+ mHull->Compute(CHC);
+ }
+#endif // __MESHMERIZER_H__
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the number of bytes used by the tree.
+ * \return amount of bytes used
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+udword Model::GetUsedBytes() const
+{
+ if(!mTree) return 0;
+ return mTree->GetUsedBytes();
+}
diff --git a/Opcode/OpcodeLib/OPC_Model.h b/Opcode/OpcodeLib/OPC_Model.h
new file mode 100644
index 0000000..1d7e1e4
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_Model.h
@@ -0,0 +1,65 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for OPCODE models.
+ * \file OPC_Model.h
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_MODEL_H__
+#define __OPC_MODEL_H__
+
+ class OPCODE_API Model : public BaseModel
+ {
+ public:
+ // Constructor/Destructor
+ Model();
+ virtual ~Model();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Builds a collision model.
+ * \param create [in] model creation structure
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(BaseModel) bool Build(const OPCODECREATE& create);
+
+#ifdef __MESHMERIZER_H__
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the collision hull.
+ * \return the collision hull if it exists
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ const CollisionHull* GetHull() const { return mHull; }
+#endif // __MESHMERIZER_H__
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the number of bytes used by the tree.
+ * \return amount of bytes used
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(BaseModel) udword GetUsedBytes() const;
+
+ private:
+#ifdef __MESHMERIZER_H__
+ CollisionHull* mHull; //!< Possible convex hull
+#endif // __MESHMERIZER_H__
+ // Internal methods
+ void Release();
+ };
+
+#endif //__OPC_MODEL_H__ \ No newline at end of file
diff --git a/Opcode/OpcodeLib/OPC_OBBCollider.cpp b/Opcode/OpcodeLib/OPC_OBBCollider.cpp
new file mode 100644
index 0000000..1dc343d
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_OBBCollider.cpp
@@ -0,0 +1,767 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for an OBB collider.
+ * \file OPC_OBBCollider.cpp
+ * \author Pierre Terdiman
+ * \date January, 1st, 2002
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains an OBB-vs-tree collider.
+ *
+ * \class OBBCollider
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date January, 1st, 2002
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+#include "OPC_BoxBoxOverlap.h"
+#include "OPC_TriBoxOverlap.h"
+
+#define SET_CONTACT(prim_index, flag) \
+ /* Set contact status */ \
+ mFlags |= flag; \
+ mTouchedPrimitives->Add(prim_index);
+
+//! OBB-triangle test
+#define OBB_PRIM(prim_index, flag) \
+ /* Request vertices from the app */ \
+ VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
+ /* Transform them in a common space */ \
+ TransformPoint(mLeafVerts[0], *VP.Vertex[0], mRModelToBox, mTModelToBox); \
+ TransformPoint(mLeafVerts[1], *VP.Vertex[1], mRModelToBox, mTModelToBox); \
+ TransformPoint(mLeafVerts[2], *VP.Vertex[2], mRModelToBox, mTModelToBox); \
+ /* Perform triangle-box overlap test */ \
+ if(TriBoxOverlap()) \
+ { \
+ SET_CONTACT(prim_index, flag) \
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OBBCollider::OBBCollider() : mFullBoxBoxTest(true)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OBBCollider::~OBBCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Validates current settings. You should call this method after all the settings and callbacks have been defined.
+ * \return null if everything is ok, else a string describing the problem
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const char* OBBCollider::ValidateSettings()
+{
+ if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!";
+
+ return VolumeCollider::ValidateSettings();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Generic collision query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - with GetNbTouchedPrimitives()
+ * - with GetTouchedPrimitives()
+ *
+ * \param cache [in/out] a box cache
+ * \param box [in] collision OBB in local space
+ * \param model [in] Opcode model to collide with
+ * \param worldb [in] OBB's world matrix, or null
+ * \param worldm [in] model's world matrix, or null
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool OBBCollider::Collide(OBBCache& cache, const OBB& box, const Model& model, const Matrix4x4* worldb, const Matrix4x4* worldm)
+{
+ // Checkings
+ if(!Setup(&model)) return false;
+
+ // Init collision query
+ if(InitQuery(cache, box, worldb, worldm)) return true;
+
+ if(!model.HasLeafNodes())
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ }
+ else
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
+
+ // Perform collision query
+ if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
+ else _Collide(Tree->GetNodes());
+ }
+ }
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Initializes a collision query :
+ * - reset stats & contact status
+ * - setup matrices
+ * - check temporal coherence
+ *
+ * \param cache [in/out] a box cache
+ * \param box [in] obb in local space
+ * \param worldb [in] obb's world matrix, or null
+ * \param worldm [in] model's world matrix, or null
+ * \return TRUE if we can return immediately
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+BOOL OBBCollider::InitQuery(OBBCache& cache, const OBB& box, const Matrix4x4* worldb, const Matrix4x4* worldm)
+{
+ // 1) Call the base method
+ VolumeCollider::InitQuery();
+
+ // 2) Compute obb in world space
+ mBoxExtents = box.mExtents;
+
+ Matrix4x4 WorldB;
+
+ if(worldb)
+ {
+ WorldB = Matrix4x4( box.mRot * Matrix3x3(*worldb) );
+ WorldB.SetTrans(box.mCenter * *worldb);
+ }
+ else
+ {
+ WorldB = box.mRot;
+ WorldB.SetTrans(box.mCenter);
+ }
+
+ // Setup matrices
+ Matrix4x4 InvWorldB;
+ InvertPRMatrix(InvWorldB, WorldB);
+
+ if(worldm)
+ {
+ Matrix4x4 InvWorldM;
+ InvertPRMatrix(InvWorldM, *worldm);
+
+ Matrix4x4 WorldBtoM = WorldB * InvWorldM;
+ Matrix4x4 WorldMtoB = *worldm * InvWorldB;
+
+ mRModelToBox = WorldMtoB; WorldMtoB.GetTrans(mTModelToBox);
+ mRBoxToModel = WorldBtoM; WorldBtoM.GetTrans(mTBoxToModel);
+ }
+ else
+ {
+ mRModelToBox = InvWorldB; InvWorldB.GetTrans(mTModelToBox);
+ mRBoxToModel = WorldB; WorldB.GetTrans(mTBoxToModel);
+ }
+
+ // 3) Setup destination pointer
+ mTouchedPrimitives = &cache.TouchedPrimitives;
+
+ // 4) Special case: 1-triangle meshes [Opcode 1.3]
+ if(mCurrentModel && mCurrentModel->HasSingleNode())
+ {
+ if(!SkipPrimitiveTests())
+ {
+ // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
+ mTouchedPrimitives->Reset();
+
+ // Perform overlap test between the unique triangle and the box (and set contact status if needed)
+ OBB_PRIM(udword(0), OPC_CONTACT)
+
+ // Return immediately regardless of status
+ return TRUE;
+ }
+ }
+
+ // 5) Check temporal coherence:
+ if(TemporalCoherenceEnabled())
+ {
+ // Here we use temporal coherence
+ // => check results from previous frame before performing the collision query
+ if(FirstContactEnabled())
+ {
+ // We're only interested in the first contact found => test the unique previously touched face
+ if(mTouchedPrimitives->GetNbEntries())
+ {
+ // Get index of previously touched face = the first entry in the array
+ udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0);
+
+ // Then reset the array:
+ // - if the overlap test below is successful, the index we'll get added back anyway
+ // - if it isn't, then the array should be reset anyway for the normal query
+ mTouchedPrimitives->Reset();
+
+ // Perform overlap test between the cached triangle and the box (and set contact status if needed)
+ OBB_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT)
+
+ // Return immediately if possible
+ if(GetContactStatus()) return TRUE;
+ }
+ // else no face has been touched during previous query
+ // => we'll have to perform a normal query
+ }
+ else
+ {
+ // ### rewrite this
+ OBB TestBox(mTBoxToModel, mBoxExtents, mRBoxToModel);
+
+ // We're interested in all contacts =>test the new real box N(ew) against the previous fat box P(revious):
+ if(IsCacheValid(cache) && TestBox.IsInside(cache.FatBox))
+ {
+ // - if N is included in P, return previous list
+ // => we simply leave the list (mTouchedFaces) unchanged
+
+ // Set contact status if needed
+ if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT;
+
+ // In any case we don't need to do a query
+ return TRUE;
+ }
+ else
+ {
+ // - else do the query using a fat N
+
+ // Reset cache since we'll about to perform a real query
+ mTouchedPrimitives->Reset();
+
+ // Make a fat box so that coherence will work for subsequent frames
+ TestBox.mExtents *= cache.FatCoeff;
+ mBoxExtents *= cache.FatCoeff;
+
+ // Update cache with query data (signature for cached faces)
+ cache.FatBox = TestBox;
+ }
+ }
+ }
+ else
+ {
+ // Here we don't use temporal coherence => do a normal query
+ mTouchedPrimitives->Reset();
+ }
+
+ // Now we can precompute box-box data
+
+ // Precompute absolute box-to-model rotation matrix
+ for(udword i=0;i<3;i++)
+ {
+ for(udword j=0;j<3;j++)
+ {
+ // Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID)
+ mAR.m[i][j] = 1e-6f + fabsf(mRBoxToModel.m[i][j]);
+ }
+ }
+
+ // Precompute bounds for box-in-box test
+ mB0 = mBoxExtents - mTModelToBox;
+ mB1 = - mBoxExtents - mTModelToBox;
+
+ // Precompute box-box data - Courtesy of Erwin de Vries
+ mBBx1 = mBoxExtents.x*mAR.m[0][0] + mBoxExtents.y*mAR.m[1][0] + mBoxExtents.z*mAR.m[2][0];
+ mBBy1 = mBoxExtents.x*mAR.m[0][1] + mBoxExtents.y*mAR.m[1][1] + mBoxExtents.z*mAR.m[2][1];
+ mBBz1 = mBoxExtents.x*mAR.m[0][2] + mBoxExtents.y*mAR.m[1][2] + mBoxExtents.z*mAR.m[2][2];
+
+ mBB_1 = mBoxExtents.y*mAR.m[2][0] + mBoxExtents.z*mAR.m[1][0];
+ mBB_2 = mBoxExtents.x*mAR.m[2][0] + mBoxExtents.z*mAR.m[0][0];
+ mBB_3 = mBoxExtents.x*mAR.m[1][0] + mBoxExtents.y*mAR.m[0][0];
+ mBB_4 = mBoxExtents.y*mAR.m[2][1] + mBoxExtents.z*mAR.m[1][1];
+ mBB_5 = mBoxExtents.x*mAR.m[2][1] + mBoxExtents.z*mAR.m[0][1];
+ mBB_6 = mBoxExtents.x*mAR.m[1][1] + mBoxExtents.y*mAR.m[0][1];
+ mBB_7 = mBoxExtents.y*mAR.m[2][2] + mBoxExtents.z*mAR.m[1][2];
+ mBB_8 = mBoxExtents.x*mAR.m[2][2] + mBoxExtents.z*mAR.m[0][2];
+ mBB_9 = mBoxExtents.x*mAR.m[1][2] + mBoxExtents.y*mAR.m[0][2];
+
+ return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Checks the OBB completely contains the box. In which case we can end the query sooner.
+ * \param bc [in] box center
+ * \param be [in] box extents
+ * \return true if the OBB contains the whole box
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL OBBCollider::OBBContainsBox(const IcePoint& bc, const IcePoint& be)
+{
+ // I assume if all 8 box vertices are inside the OBB, so does the whole box.
+ // Sounds ok but maybe there's a better way?
+/*
+#define TEST_PT(a,b,c) \
+ p.x=a; p.y=b; p.z=c; p+=bc; \
+ f = p.x * mRModelToBox.m[0][0] + p.y * mRModelToBox.m[1][0] + p.z * mRModelToBox.m[2][0]; if(f>mB0.x || f<mB1.x) return FALSE;\
+ f = p.x * mRModelToBox.m[0][1] + p.y * mRModelToBox.m[1][1] + p.z * mRModelToBox.m[2][1]; if(f>mB0.y || f<mB1.y) return FALSE;\
+ f = p.x * mRModelToBox.m[0][2] + p.y * mRModelToBox.m[1][2] + p.z * mRModelToBox.m[2][2]; if(f>mB0.z || f<mB1.z) return FALSE;
+
+ IcePoint p;
+ float f;
+
+ TEST_PT(be.x, be.y, be.z)
+ TEST_PT(-be.x, be.y, be.z)
+ TEST_PT(be.x, -be.y, be.z)
+ TEST_PT(-be.x, -be.y, be.z)
+ TEST_PT(be.x, be.y, -be.z)
+ TEST_PT(-be.x, be.y, -be.z)
+ TEST_PT(be.x, -be.y, -be.z)
+ TEST_PT(-be.x, -be.y, -be.z)
+
+ return TRUE;
+*/
+
+ // Yes there is:
+ // - compute model-box's AABB in OBB space
+ // - test AABB-in-AABB
+ float NCx = bc.x * mRModelToBox.m[0][0] + bc.y * mRModelToBox.m[1][0] + bc.z * mRModelToBox.m[2][0];
+ float NEx = fabsf(mRModelToBox.m[0][0] * be.x) + fabsf(mRModelToBox.m[1][0] * be.y) + fabsf(mRModelToBox.m[2][0] * be.z);
+
+ if(mB0.x < NCx+NEx) return FALSE;
+ if(mB1.x > NCx-NEx) return FALSE;
+
+ float NCy = bc.x * mRModelToBox.m[0][1] + bc.y * mRModelToBox.m[1][1] + bc.z * mRModelToBox.m[2][1];
+ float NEy = fabsf(mRModelToBox.m[0][1] * be.x) + fabsf(mRModelToBox.m[1][1] * be.y) + fabsf(mRModelToBox.m[2][1] * be.z);
+
+ if(mB0.y < NCy+NEy) return FALSE;
+ if(mB1.y > NCy-NEy) return FALSE;
+
+ float NCz = bc.x * mRModelToBox.m[0][2] + bc.y * mRModelToBox.m[1][2] + bc.z * mRModelToBox.m[2][2];
+ float NEz = fabsf(mRModelToBox.m[0][2] * be.x) + fabsf(mRModelToBox.m[1][2] * be.y) + fabsf(mRModelToBox.m[2][2] * be.z);
+
+ if(mB0.z < NCz+NEz) return FALSE;
+ if(mB1.z > NCz-NEz) return FALSE;
+
+ return TRUE;
+}
+
+#define TEST_BOX_IN_OBB(center, extents) \
+ if(OBBContainsBox(center, extents)) \
+ { \
+ /* Set contact status */ \
+ mFlags |= OPC_CONTACT; \
+ _Dump(node); \
+ return; \
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBBCollider::_Collide(const AABBCollisionNode* node)
+{
+ // Perform OBB-AABB overlap test
+ if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
+
+ TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->IsLeaf())
+ {
+ OBB_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _Collide(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBBCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node)
+{
+ // Perform OBB-AABB overlap test
+ if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
+
+ TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->IsLeaf())
+ {
+ SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _CollideNoPrimitiveTest(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBBCollider::_Collide(const AABBQuantizedNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const IcePoint Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const IcePoint Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform OBB-AABB overlap test
+ if(!BoxBoxOverlap(Extents, Center)) return;
+
+ TEST_BOX_IN_OBB(Center, Extents)
+
+ if(node->IsLeaf())
+ {
+ OBB_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _Collide(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const IcePoint Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const IcePoint Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform OBB-AABB overlap test
+ if(!BoxBoxOverlap(Extents, Center)) return;
+
+ TEST_BOX_IN_OBB(Center, Extents)
+
+ if(node->IsLeaf())
+ {
+ SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _CollideNoPrimitiveTest(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBBCollider::_Collide(const AABBNoLeafNode* node)
+{
+ // Perform OBB-AABB overlap test
+ if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
+
+ TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for no-leaf AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBBCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node)
+{
+ // Perform OBB-AABB overlap test
+ if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
+
+ TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
+
+ if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBBCollider::_Collide(const AABBQuantizedNoLeafNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const IcePoint Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const IcePoint Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform OBB-AABB overlap test
+ if(!BoxBoxOverlap(Extents, Center)) return;
+
+ TEST_BOX_IN_OBB(Center, Extents)
+
+ if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _Collide(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized no-leaf AABB trees, without primitive tests.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OBBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const IcePoint Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const IcePoint Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform OBB-AABB overlap test
+ if(!BoxBoxOverlap(Extents, Center)) return;
+
+ TEST_BOX_IN_OBB(Center, Extents)
+
+ if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
+ else _CollideNoPrimitiveTest(node->GetNeg());
+}
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridOBBCollider::HybridOBBCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+HybridOBBCollider::~HybridOBBCollider()
+{
+}
+
+bool HybridOBBCollider::Collide(OBBCache& cache, const OBB& box, const HybridModel& model, const Matrix4x4* worldb, const Matrix4x4* worldm)
+{
+ // We don't want primitive tests here!
+ mFlags |= OPC_NO_PRIMITIVE_TESTS;
+
+ // Checkings
+ if(!Setup(&model)) return false;
+
+ // Init collision query
+ if(InitQuery(cache, box, worldb, worldm)) return true;
+
+ // Special case for 1-leaf trees
+ if(mCurrentModel && mCurrentModel->HasSingleNode())
+ {
+ // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles
+ udword Nb = mIMesh->GetNbTriangles();
+
+ // Loop through all triangles
+ for(udword i=0;i<Nb;i++)
+ {
+ OBB_PRIM(i, OPC_CONTACT)
+ }
+ return true;
+ }
+
+ // Override destination array since we're only going to get leaf boxes here
+ mTouchedBoxes.Reset();
+ mTouchedPrimitives = &mTouchedBoxes;
+
+ // Now, do the actual query against leaf boxes
+ if(!model.HasLeafNodes())
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ }
+ else
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
+
+ // Perform collision query - we don't want primitive tests here!
+ _CollideNoPrimitiveTest(Tree->GetNodes());
+ }
+ }
+
+ // We only have a list of boxes so far
+ if(GetContactStatus())
+ {
+ // Reset contact status, since it currently only reflects collisions with leaf boxes
+ Collider::InitQuery();
+
+ // Change dest container so that we can use built-in overlap tests and get collided primitives
+ cache.TouchedPrimitives.Reset();
+ mTouchedPrimitives = &cache.TouchedPrimitives;
+
+ // Read touched leaf boxes
+ udword Nb = mTouchedBoxes.GetNbEntries();
+ const udword* Touched = mTouchedBoxes.GetEntries();
+
+ const LeafTriangles* LT = model.GetLeafTriangles();
+ const udword* Indices = model.GetIndices();
+
+ // Loop through touched leaves
+ while(Nb--)
+ {
+ const LeafTriangles& CurrentLeaf = LT[*Touched++];
+
+ // Each leaf box has a set of triangles
+ udword NbTris = CurrentLeaf.GetNbTriangles();
+ if(Indices)
+ {
+ const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ udword TriangleIndex = *T++;
+ OBB_PRIM(TriangleIndex, OPC_CONTACT)
+ }
+ }
+ else
+ {
+ udword BaseIndex = CurrentLeaf.GetTriangleIndex();
+
+ // Loop through triangles and test each of them
+ while(NbTris--)
+ {
+ udword TriangleIndex = BaseIndex++;
+ OBB_PRIM(TriangleIndex, OPC_CONTACT)
+ }
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/Opcode/OpcodeLib/OPC_OBBCollider.h b/Opcode/OpcodeLib/OPC_OBBCollider.h
new file mode 100644
index 0000000..0601b20
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_OBBCollider.h
@@ -0,0 +1,142 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for an OBB collider.
+ * \file OPC_OBBCollider.h
+ * \author Pierre Terdiman
+ * \date January, 1st, 2002
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_OBBCOLLIDER_H__
+#define __OPC_OBBCOLLIDER_H__
+
+ struct OPCODE_API OBBCache : VolumeCache
+ {
+ OBBCache() : FatCoeff(1.1f)
+ {
+ FatBox.mCenter.Zero();
+ FatBox.mExtents.Zero();
+ FatBox.mRot.Identity();
+ }
+
+ // Cached faces signature
+ OBB FatBox; //!< Box used when performing the query resulting in cached faces
+ // User settings
+ float FatCoeff; //!< extents multiplier used to create a fat box
+ };
+
+ class OPCODE_API OBBCollider : public VolumeCollider
+ {
+ public:
+ // Constructor / Destructor
+ OBBCollider();
+ virtual ~OBBCollider();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Generic collision query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - with GetNbTouchedPrimitives()
+ * - with GetTouchedPrimitives()
+ *
+ * \param cache [in/out] a box cache
+ * \param box [in] collision OBB in local space
+ * \param model [in] Opcode model to collide with
+ * \param worldb [in] OBB's world matrix, or null
+ * \param worldm [in] model's world matrix, or null
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool Collide(OBBCache& cache, const OBB& box, const Model& model, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null);
+
+ // Settings
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Settings: select between full box-box tests or "SAT-lite" tests (where Class III axes are discarded)
+ * \param flag [in] true for full tests, false for coarse tests
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetFullBoxBoxTest(bool flag) { mFullBoxBoxTest = flag; }
+
+ // Settings
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Validates current settings. You should call this method after all the settings and callbacks have been defined for a collider.
+ * \return null if everything is ok, else a string describing the problem
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(Collider) const char* ValidateSettings();
+
+ protected:
+ // Precomputed data
+ Matrix3x3 mAR; //!< Absolute rotation matrix
+ Matrix3x3 mRModelToBox; //!< Rotation from model space to obb space
+ Matrix3x3 mRBoxToModel; //!< Rotation from obb space to model space
+ IcePoint mTModelToBox; //!< Translation from model space to obb space
+ IcePoint mTBoxToModel; //!< Translation from obb space to model space
+
+ IcePoint mBoxExtents;
+ IcePoint mB0; //!< - mTModelToBox + mBoxExtents
+ IcePoint mB1; //!< - mTModelToBox - mBoxExtents
+
+ float mBBx1;
+ float mBBy1;
+ float mBBz1;
+
+ float mBB_1;
+ float mBB_2;
+ float mBB_3;
+ float mBB_4;
+ float mBB_5;
+ float mBB_6;
+ float mBB_7;
+ float mBB_8;
+ float mBB_9;
+
+ // Leaf description
+ IcePoint mLeafVerts[3]; //!< Triangle vertices
+ // Settings
+ bool mFullBoxBoxTest; //!< Perform full BV-BV tests (true) or SAT-lite tests (false)
+ // Internal methods
+ void _Collide(const AABBCollisionNode* node);
+ void _Collide(const AABBNoLeafNode* node);
+ void _Collide(const AABBQuantizedNode* node);
+ void _Collide(const AABBQuantizedNoLeafNode* node);
+ void _CollideNoPrimitiveTest(const AABBCollisionNode* node);
+ void _CollideNoPrimitiveTest(const AABBNoLeafNode* node);
+ void _CollideNoPrimitiveTest(const AABBQuantizedNode* node);
+ void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node);
+ // Overlap tests
+ inline_ BOOL OBBContainsBox(const IcePoint& bc, const IcePoint& be);
+ inline_ BOOL BoxBoxOverlap(const IcePoint& extents, const IcePoint& center);
+ inline_ BOOL TriBoxOverlap();
+ // Init methods
+ BOOL InitQuery(OBBCache& cache, const OBB& box, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null);
+ };
+
+ class OPCODE_API HybridOBBCollider : public OBBCollider
+ {
+ public:
+ // Constructor / Destructor
+ HybridOBBCollider();
+ virtual ~HybridOBBCollider();
+
+ bool Collide(OBBCache& cache, const OBB& box, const HybridModel& model, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null);
+ protected:
+ Container mTouchedBoxes;
+ };
+
+#endif // __OPC_OBBCOLLIDER_H__
diff --git a/Opcode/OpcodeLib/OPC_OptimizedTree.cpp b/Opcode/OpcodeLib/OPC_OptimizedTree.cpp
new file mode 100644
index 0000000..e2abe60
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_OptimizedTree.cpp
@@ -0,0 +1,782 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for optimized trees. Implements 4 trees:
+ * - normal
+ * - no leaf
+ * - quantized
+ * - no leaf / quantized
+ *
+ * \file OPC_OptimizedTree.cpp
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A standard AABB tree.
+ *
+ * \class AABBCollisionTree
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A no-leaf AABB tree.
+ *
+ * \class AABBNoLeafTree
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A quantized AABB tree.
+ *
+ * \class AABBQuantizedTree
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A quantized no-leaf AABB tree.
+ *
+ * \class AABBQuantizedNoLeafTree
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+//! Compilation flag:
+//! - true to fix quantized boxes (i.e. make sure they enclose the original ones)
+//! - false to see the effects of quantization errors (faster, but wrong results in some cases)
+static bool gFixQuantized = true;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds an implicit tree from a standard one. An implicit tree is a complete tree (2*N-1 nodes) whose negative
+ * box pointers and primitive pointers have been made implicit, hence packing 3 pointers in one.
+ *
+ * Layout for implicit trees:
+ * Node:
+ * - box
+ * - data (32-bits value)
+ *
+ * if data's LSB = 1 => remaining bits are a primitive pointer
+ * else remaining bits are a P-node pointer, and N = P + 1
+ *
+ * \relates AABBCollisionNode
+ * \fn _BuildCollisionTree(AABBCollisionNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node)
+ * \param linear [in] base address of destination nodes
+ * \param box_id [in] index of destination node
+ * \param current_id [in] current running index
+ * \param current_node [in] current node from input tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static void _BuildCollisionTree(AABBCollisionNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node)
+{
+ // Current node from input tree is "current_node". Must be flattened into "linear[boxid]".
+
+ // Store the AABB
+ current_node->GetAABB()->GetCenter(linear[box_id].mAABB.mCenter);
+ current_node->GetAABB()->GetExtents(linear[box_id].mAABB.mExtents);
+ // Store remaining info
+ if(current_node->IsLeaf())
+ {
+ // The input tree must be complete => i.e. one primitive/leaf
+ ASSERT(current_node->GetNbPrimitives()==1);
+ // Get the primitive index from the input tree
+ udword PrimitiveIndex = current_node->GetPrimitives()[0];
+ // Setup box data as the primitive index, marked as leaf
+ linear[box_id].mData = (PrimitiveIndex<<1)|1;
+ }
+ else
+ {
+ // To make the negative one implicit, we must store P and N in successive order
+ udword PosID = current_id++; // Get a new id for positive child
+ udword NegID = current_id++; // Get a new id for negative child
+ // Setup box data as the forthcoming new P pointer
+ linear[box_id].mData = (udword)&linear[PosID];
+ // Make sure it's not marked as leaf
+ ASSERT(!(linear[box_id].mData&1));
+ // Recurse with new IDs
+ _BuildCollisionTree(linear, PosID, current_id, current_node->GetPos());
+ _BuildCollisionTree(linear, NegID, current_id, current_node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds a "no-leaf" tree from a standard one. This is a tree whose leaf nodes have been removed.
+ *
+ * Layout for no-leaf trees:
+ *
+ * Node:
+ * - box
+ * - P pointer => a node (LSB=0) or a primitive (LSB=1)
+ * - N pointer => a node (LSB=0) or a primitive (LSB=1)
+ *
+ * \relates AABBNoLeafNode
+ * \fn _BuildNoLeafTree(AABBNoLeafNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node)
+ * \param linear [in] base address of destination nodes
+ * \param box_id [in] index of destination node
+ * \param current_id [in] current running index
+ * \param current_node [in] current node from input tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static void _BuildNoLeafTree(AABBNoLeafNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node)
+{
+ const AABBTreeNode* P = current_node->GetPos();
+ const AABBTreeNode* N = current_node->GetNeg();
+ // Leaf nodes here?!
+ ASSERT(P);
+ ASSERT(N);
+ // Internal node => keep the box
+ current_node->GetAABB()->GetCenter(linear[box_id].mAABB.mCenter);
+ current_node->GetAABB()->GetExtents(linear[box_id].mAABB.mExtents);
+
+ if(P->IsLeaf())
+ {
+ // The input tree must be complete => i.e. one primitive/leaf
+ ASSERT(P->GetNbPrimitives()==1);
+ // Get the primitive index from the input tree
+ udword PrimitiveIndex = P->GetPrimitives()[0];
+ // Setup prev box data as the primitive index, marked as leaf
+ linear[box_id].mPosData = (PrimitiveIndex<<1)|1;
+ }
+ else
+ {
+ // Get a new id for positive child
+ udword PosID = current_id++;
+ // Setup box data
+ linear[box_id].mPosData = (udword)&linear[PosID];
+ // Make sure it's not marked as leaf
+ ASSERT(!(linear[box_id].mPosData&1));
+ // Recurse
+ _BuildNoLeafTree(linear, PosID, current_id, P);
+ }
+
+ if(N->IsLeaf())
+ {
+ // The input tree must be complete => i.e. one primitive/leaf
+ ASSERT(N->GetNbPrimitives()==1);
+ // Get the primitive index from the input tree
+ udword PrimitiveIndex = N->GetPrimitives()[0];
+ // Setup prev box data as the primitive index, marked as leaf
+ linear[box_id].mNegData = (PrimitiveIndex<<1)|1;
+ }
+ else
+ {
+ // Get a new id for negative child
+ udword NegID = current_id++;
+ // Setup box data
+ linear[box_id].mNegData = (udword)&linear[NegID];
+ // Make sure it's not marked as leaf
+ ASSERT(!(linear[box_id].mNegData&1));
+ // Recurse
+ _BuildNoLeafTree(linear, NegID, current_id, N);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBCollisionTree::AABBCollisionTree() : mNodes(null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBCollisionTree::~AABBCollisionTree()
+{
+ DELETEARRAY(mNodes);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds the collision tree from a generic AABB tree.
+ * \param tree [in] generic AABB tree
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBCollisionTree::Build(AABBTree* tree)
+{
+ // Checkings
+ if(!tree) return false;
+ // Check the input tree is complete
+ udword NbTriangles = tree->GetNbPrimitives();
+ udword NbNodes = tree->GetNbNodes();
+ if(NbNodes!=NbTriangles*2-1) return false;
+
+ // Get nodes
+ if(mNbNodes!=NbNodes) // Same number of nodes => keep moving
+ {
+ mNbNodes = NbNodes;
+ DELETEARRAY(mNodes);
+ mNodes = new AABBCollisionNode[mNbNodes];
+ CHECKALLOC(mNodes);
+ }
+
+ // Build the tree
+ udword CurID = 1;
+ _BuildCollisionTree(mNodes, 0, CurID, tree);
+ ASSERT(CurID==mNbNodes);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the collision tree after vertices have been modified.
+ * \param mesh_interface [in] mesh interface for current model
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBCollisionTree::Refit(const MeshInterface* mesh_interface)
+{
+ ASSERT(!"Not implemented since AABBCollisionTrees have twice as more nodes to refit as AABBNoLeafTrees!");
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Walks the tree and call the user back for each node.
+ * \param callback [in] walking callback
+ * \param user_data [in] callback's user data
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBCollisionTree::Walk(GenericWalkingCallback callback, void* user_data) const
+{
+ if(!callback) return false;
+
+ struct Local
+ {
+ static void _Walk(const AABBCollisionNode* current_node, GenericWalkingCallback callback, void* user_data)
+ {
+ if(!current_node || !(callback)(current_node, user_data)) return;
+
+ if(!current_node->IsLeaf())
+ {
+ _Walk(current_node->GetPos(), callback, user_data);
+ _Walk(current_node->GetNeg(), callback, user_data);
+ }
+ }
+ };
+ Local::_Walk(mNodes, callback, user_data);
+ return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBNoLeafTree::AABBNoLeafTree() : mNodes(null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBNoLeafTree::~AABBNoLeafTree()
+{
+ DELETEARRAY(mNodes);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds the collision tree from a generic AABB tree.
+ * \param tree [in] generic AABB tree
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBNoLeafTree::Build(AABBTree* tree)
+{
+ // Checkings
+ if(!tree) return false;
+ // Check the input tree is complete
+ udword NbTriangles = tree->GetNbPrimitives();
+ udword NbNodes = tree->GetNbNodes();
+ if(NbNodes!=NbTriangles*2-1) return false;
+
+ // Get nodes
+ if(mNbNodes!=NbTriangles-1) // Same number of nodes => keep moving
+ {
+ mNbNodes = NbTriangles-1;
+ DELETEARRAY(mNodes);
+ mNodes = new AABBNoLeafNode[mNbNodes];
+ CHECKALLOC(mNodes);
+ }
+
+ // Build the tree
+ udword CurID = 1;
+ _BuildNoLeafTree(mNodes, 0, CurID, tree);
+ ASSERT(CurID==mNbNodes);
+
+ return true;
+}
+
+inline_ void ComputeMinMax(IcePoint& min, IcePoint& max, const VertexPointers& vp)
+{
+ // Compute triangle's AABB = a leaf box
+#ifdef OPC_USE_FCOMI // a 15% speedup on my machine, not much
+ min.x = FCMin3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x);
+ max.x = FCMax3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x);
+
+ min.y = FCMin3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y);
+ max.y = FCMax3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y);
+
+ min.z = FCMin3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z);
+ max.z = FCMax3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z);
+#else
+ min = *vp.Vertex[0];
+ max = *vp.Vertex[0];
+ min.Min(*vp.Vertex[1]);
+ max.Max(*vp.Vertex[1]);
+ min.Min(*vp.Vertex[2]);
+ max.Max(*vp.Vertex[2]);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the collision tree after vertices have been modified.
+ * \param mesh_interface [in] mesh interface for current model
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBNoLeafTree::Refit(const MeshInterface* mesh_interface)
+{
+ // Checkings
+ if(!mesh_interface) return false;
+
+ // Bottom-up update
+ VertexPointers VP;
+ IcePoint Min,Max;
+ IcePoint Min_,Max_;
+ udword Index = mNbNodes;
+ while(Index--)
+ {
+ AABBNoLeafNode& Current = mNodes[Index];
+
+ if(Current.HasPosLeaf())
+ {
+ mesh_interface->GetTriangle(VP, Current.GetPosPrimitive());
+ ComputeMinMax(Min, Max, VP);
+ }
+ else
+ {
+ const CollisionAABB& CurrentBox = Current.GetPos()->mAABB;
+ CurrentBox.GetMin(Min);
+ CurrentBox.GetMax(Max);
+ }
+
+ if(Current.HasNegLeaf())
+ {
+ mesh_interface->GetTriangle(VP, Current.GetNegPrimitive());
+ ComputeMinMax(Min_, Max_, VP);
+ }
+ else
+ {
+ const CollisionAABB& CurrentBox = Current.GetNeg()->mAABB;
+ CurrentBox.GetMin(Min_);
+ CurrentBox.GetMax(Max_);
+ }
+#ifdef OPC_USE_FCOMI
+ Min.x = FCMin2(Min.x, Min_.x);
+ Max.x = FCMax2(Max.x, Max_.x);
+ Min.y = FCMin2(Min.y, Min_.y);
+ Max.y = FCMax2(Max.y, Max_.y);
+ Min.z = FCMin2(Min.z, Min_.z);
+ Max.z = FCMax2(Max.z, Max_.z);
+#else
+ Min.Min(Min_);
+ Max.Max(Max_);
+#endif
+ Current.mAABB.SetMinMax(Min, Max);
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Walks the tree and call the user back for each node.
+ * \param callback [in] walking callback
+ * \param user_data [in] callback's user data
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBNoLeafTree::Walk(GenericWalkingCallback callback, void* user_data) const
+{
+ if(!callback) return false;
+
+ struct Local
+ {
+ static void _Walk(const AABBNoLeafNode* current_node, GenericWalkingCallback callback, void* user_data)
+ {
+ if(!current_node || !(callback)(current_node, user_data)) return;
+
+ if(!current_node->HasPosLeaf()) _Walk(current_node->GetPos(), callback, user_data);
+ if(!current_node->HasNegLeaf()) _Walk(current_node->GetNeg(), callback, user_data);
+ }
+ };
+ Local::_Walk(mNodes, callback, user_data);
+ return true;
+}
+
+// Quantization notes:
+// - We could use the highest bits of mData to store some more quantized bits. Dequantization code
+// would be slightly more complex, but number of overlap tests would be reduced (and anyhow those
+// bits are currently wasted). Of course it's not possible if we move to 16 bits mData.
+// - Something like "16 bits floats" could be tested, to bypass the int-to-float conversion.
+// - A dedicated BV-BV test could be used, dequantizing while testing for overlap. (i.e. it's some
+// lazy-dequantization which may save some work in case of early exits). At the very least some
+// muls could be saved by precomputing several more matrices. But maybe not worth the pain.
+// - Do we need to dequantize anyway? Not doing the extents-related muls only implies the box has
+// been scaled, for example.
+// - The deeper we move into the hierarchy, the smaller the extents should be. May not need a fixed
+// number of quantization bits. Even better, could probably be best delta-encoded.
+
+
+// Find max values. Some people asked why I wasn't simply using the first node. Well, I can't.
+// I'm not looking for (min, max) values like in a standard AABB, I'm looking for the extremal
+// centers/extents in order to quantize them. The first node would only give a single center and
+// a single extents. While extents would be the biggest, the center wouldn't.
+#define FIND_MAX_VALUES \
+ /* Get max values */ \
+ IcePoint CMax(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); \
+ IcePoint EMax(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); \
+ for(udword i=0;i<mNbNodes;i++) \
+ { \
+ if(fabsf(Nodes[i].mAABB.mCenter.x)>CMax.x) CMax.x = fabsf(Nodes[i].mAABB.mCenter.x); \
+ if(fabsf(Nodes[i].mAABB.mCenter.y)>CMax.y) CMax.y = fabsf(Nodes[i].mAABB.mCenter.y); \
+ if(fabsf(Nodes[i].mAABB.mCenter.z)>CMax.z) CMax.z = fabsf(Nodes[i].mAABB.mCenter.z); \
+ if(fabsf(Nodes[i].mAABB.mExtents.x)>EMax.x) EMax.x = fabsf(Nodes[i].mAABB.mExtents.x); \
+ if(fabsf(Nodes[i].mAABB.mExtents.y)>EMax.y) EMax.y = fabsf(Nodes[i].mAABB.mExtents.y); \
+ if(fabsf(Nodes[i].mAABB.mExtents.z)>EMax.z) EMax.z = fabsf(Nodes[i].mAABB.mExtents.z); \
+ }
+
+#define INIT_QUANTIZATION \
+ udword nbc=15; /* Keep one bit for sign */ \
+ udword nbe=15; /* Keep one bit for fix */ \
+ if(!gFixQuantized) nbe++; \
+ \
+ /* Compute quantization coeffs */ \
+ IcePoint CQuantCoeff, EQuantCoeff; \
+ CQuantCoeff.x = CMax.x!=0.0f ? float((1<<nbc)-1)/CMax.x : 0.0f; \
+ CQuantCoeff.y = CMax.y!=0.0f ? float((1<<nbc)-1)/CMax.y : 0.0f; \
+ CQuantCoeff.z = CMax.z!=0.0f ? float((1<<nbc)-1)/CMax.z : 0.0f; \
+ EQuantCoeff.x = EMax.x!=0.0f ? float((1<<nbe)-1)/EMax.x : 0.0f; \
+ EQuantCoeff.y = EMax.y!=0.0f ? float((1<<nbe)-1)/EMax.y : 0.0f; \
+ EQuantCoeff.z = EMax.z!=0.0f ? float((1<<nbe)-1)/EMax.z : 0.0f; \
+ /* Compute and save dequantization coeffs */ \
+ mCenterCoeff.x = CQuantCoeff.x!=0.0f ? 1.0f / CQuantCoeff.x : 0.0f; \
+ mCenterCoeff.y = CQuantCoeff.y!=0.0f ? 1.0f / CQuantCoeff.y : 0.0f; \
+ mCenterCoeff.z = CQuantCoeff.z!=0.0f ? 1.0f / CQuantCoeff.z : 0.0f; \
+ mExtentsCoeff.x = EQuantCoeff.x!=0.0f ? 1.0f / EQuantCoeff.x : 0.0f; \
+ mExtentsCoeff.y = EQuantCoeff.y!=0.0f ? 1.0f / EQuantCoeff.y : 0.0f; \
+ mExtentsCoeff.z = EQuantCoeff.z!=0.0f ? 1.0f / EQuantCoeff.z : 0.0f; \
+
+#define PERFORM_QUANTIZATION \
+ /* Quantize */ \
+ mNodes[i].mAABB.mCenter[0] = sword(Nodes[i].mAABB.mCenter.x * CQuantCoeff.x); \
+ mNodes[i].mAABB.mCenter[1] = sword(Nodes[i].mAABB.mCenter.y * CQuantCoeff.y); \
+ mNodes[i].mAABB.mCenter[2] = sword(Nodes[i].mAABB.mCenter.z * CQuantCoeff.z); \
+ mNodes[i].mAABB.mExtents[0] = uword(Nodes[i].mAABB.mExtents.x * EQuantCoeff.x); \
+ mNodes[i].mAABB.mExtents[1] = uword(Nodes[i].mAABB.mExtents.y * EQuantCoeff.y); \
+ mNodes[i].mAABB.mExtents[2] = uword(Nodes[i].mAABB.mExtents.z * EQuantCoeff.z); \
+ /* Fix quantized boxes */ \
+ if(gFixQuantized) \
+ { \
+ /* Make sure the quantized box is still valid */ \
+ IcePoint Max = Nodes[i].mAABB.mCenter + Nodes[i].mAABB.mExtents; \
+ IcePoint Min = Nodes[i].mAABB.mCenter - Nodes[i].mAABB.mExtents; \
+ /* For each axis */ \
+ for(udword j=0;j<3;j++) \
+ { /* Dequantize the box center */ \
+ float qc = float(mNodes[i].mAABB.mCenter[j]) * mCenterCoeff[j]; \
+ bool FixMe=true; \
+ do \
+ { /* Dequantize the box extent */ \
+ float qe = float(mNodes[i].mAABB.mExtents[j]) * mExtentsCoeff[j]; \
+ /* Compare real & dequantized values */ \
+ if(qc+qe<Max[j] || qc-qe>Min[j]) mNodes[i].mAABB.mExtents[j]++; \
+ else FixMe=false; \
+ /* Prevent wrapping */ \
+ if(!mNodes[i].mAABB.mExtents[j]) \
+ { \
+ mNodes[i].mAABB.mExtents[j]=0xffff; \
+ FixMe=false; \
+ } \
+ }while(FixMe); \
+ } \
+ }
+
+#define REMAP_DATA(member) \
+ /* Fix data */ \
+ Data = Nodes[i].member; \
+ if(!(Data&1)) \
+ { \
+ /* Compute box number */ \
+ udword Nb = (Data - udword(Nodes))/Nodes[i].GetNodeSize(); \
+ Data = udword(&mNodes[Nb]); \
+ } \
+ /* ...remapped */ \
+ mNodes[i].member = Data;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBQuantizedTree::AABBQuantizedTree() : mNodes(null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBQuantizedTree::~AABBQuantizedTree()
+{
+ DELETEARRAY(mNodes);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds the collision tree from a generic AABB tree.
+ * \param tree [in] generic AABB tree
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBQuantizedTree::Build(AABBTree* tree)
+{
+ // Checkings
+ if(!tree) return false;
+ // Check the input tree is complete
+ udword NbTriangles = tree->GetNbPrimitives();
+ udword NbNodes = tree->GetNbNodes();
+ if(NbNodes!=NbTriangles*2-1) return false;
+
+ // Get nodes
+ mNbNodes = NbNodes;
+ DELETEARRAY(mNodes);
+ AABBCollisionNode* Nodes = new AABBCollisionNode[mNbNodes];
+ CHECKALLOC(Nodes);
+
+ // Build the tree
+ udword CurID = 1;
+ _BuildCollisionTree(Nodes, 0, CurID, tree);
+
+ // Quantize
+ {
+ mNodes = new AABBQuantizedNode[mNbNodes];
+ CHECKALLOC(mNodes);
+
+ // Get max values
+ FIND_MAX_VALUES
+
+ // Quantization
+ INIT_QUANTIZATION
+
+ // Quantize
+ udword Data;
+ for(udword i=0;i<mNbNodes;i++)
+ {
+ PERFORM_QUANTIZATION
+ REMAP_DATA(mData)
+ }
+
+ DELETEARRAY(Nodes);
+ }
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the collision tree after vertices have been modified.
+ * \param mesh_interface [in] mesh interface for current model
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBQuantizedTree::Refit(const MeshInterface* mesh_interface)
+{
+ ASSERT(!"Not implemented since requantizing is painful !");
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Walks the tree and call the user back for each node.
+ * \param callback [in] walking callback
+ * \param user_data [in] callback's user data
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBQuantizedTree::Walk(GenericWalkingCallback callback, void* user_data) const
+{
+ if(!callback) return false;
+
+ struct Local
+ {
+ static void _Walk(const AABBQuantizedNode* current_node, GenericWalkingCallback callback, void* user_data)
+ {
+ if(!current_node || !(callback)(current_node, user_data)) return;
+
+ if(!current_node->IsLeaf())
+ {
+ _Walk(current_node->GetPos(), callback, user_data);
+ _Walk(current_node->GetNeg(), callback, user_data);
+ }
+ }
+ };
+ Local::_Walk(mNodes, callback, user_data);
+ return true;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBQuantizedNoLeafTree::AABBQuantizedNoLeafTree() : mNodes(null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBQuantizedNoLeafTree::~AABBQuantizedNoLeafTree()
+{
+ DELETEARRAY(mNodes);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Builds the collision tree from a generic AABB tree.
+ * \param tree [in] generic AABB tree
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBQuantizedNoLeafTree::Build(AABBTree* tree)
+{
+ // Checkings
+ if(!tree) return false;
+ // Check the input tree is complete
+ udword NbTriangles = tree->GetNbPrimitives();
+ udword NbNodes = tree->GetNbNodes();
+ if(NbNodes!=NbTriangles*2-1) return false;
+
+ // Get nodes
+ mNbNodes = NbTriangles-1;
+ DELETEARRAY(mNodes);
+ AABBNoLeafNode* Nodes = new AABBNoLeafNode[mNbNodes];
+ CHECKALLOC(Nodes);
+
+ // Build the tree
+ udword CurID = 1;
+ _BuildNoLeafTree(Nodes, 0, CurID, tree);
+ ASSERT(CurID==mNbNodes);
+
+ // Quantize
+ {
+ mNodes = new AABBQuantizedNoLeafNode[mNbNodes];
+ CHECKALLOC(mNodes);
+
+ // Get max values
+ FIND_MAX_VALUES
+
+ // Quantization
+ INIT_QUANTIZATION
+
+ // Quantize
+ udword Data;
+ for(udword i=0;i<mNbNodes;i++)
+ {
+ PERFORM_QUANTIZATION
+ REMAP_DATA(mPosData)
+ REMAP_DATA(mNegData)
+ }
+
+ DELETEARRAY(Nodes);
+ }
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Refits the collision tree after vertices have been modified.
+ * \param mesh_interface [in] mesh interface for current model
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBQuantizedNoLeafTree::Refit(const MeshInterface* mesh_interface)
+{
+ ASSERT(!"Not implemented since requantizing is painful !");
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Walks the tree and call the user back for each node.
+ * \param callback [in] walking callback
+ * \param user_data [in] callback's user data
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBQuantizedNoLeafTree::Walk(GenericWalkingCallback callback, void* user_data) const
+{
+ if(!callback) return false;
+
+ struct Local
+ {
+ static void _Walk(const AABBQuantizedNoLeafNode* current_node, GenericWalkingCallback callback, void* user_data)
+ {
+ if(!current_node || !(callback)(current_node, user_data)) return;
+
+ if(!current_node->HasPosLeaf()) _Walk(current_node->GetPos(), callback, user_data);
+ if(!current_node->HasNegLeaf()) _Walk(current_node->GetNeg(), callback, user_data);
+ }
+ };
+ Local::_Walk(mNodes, callback, user_data);
+ return true;
+}
diff --git a/Opcode/OpcodeLib/OPC_OptimizedTree.h b/Opcode/OpcodeLib/OPC_OptimizedTree.h
new file mode 100644
index 0000000..cda2959
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_OptimizedTree.h
@@ -0,0 +1,206 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for optimized trees.
+ * \file OPC_OptimizedTree.h
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_OPTIMIZEDTREE_H__
+#define __OPC_OPTIMIZEDTREE_H__
+
+ //! Common interface for a node of an implicit tree
+ #define IMPLEMENT_IMPLICIT_NODE(base_class, volume) \
+ public: \
+ /* Constructor / Destructor */ \
+ inline_ base_class() : mData(0) {} \
+ inline_ ~base_class() {} \
+ /* Leaf test */ \
+ inline_ BOOL IsLeaf() const { return mData&1; } \
+ /* Data access */ \
+ inline_ const base_class* GetPos() const { return (base_class*)mData; } \
+ inline_ const base_class* GetNeg() const { return ((base_class*)mData)+1; } \
+ inline_ udword GetPrimitive() const { return (mData>>1); } \
+ /* Stats */ \
+ inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \
+ \
+ volume mAABB; \
+ udword mData;
+
+ //! Common interface for a node of a no-leaf tree
+ #define IMPLEMENT_NOLEAF_NODE(base_class, volume) \
+ public: \
+ /* Constructor / Destructor */ \
+ inline_ base_class() : mPosData(0), mNegData(0) {} \
+ inline_ ~base_class() {} \
+ /* Leaf tests */ \
+ inline_ BOOL HasPosLeaf() const { return mPosData&1; } \
+ inline_ BOOL HasNegLeaf() const { return mNegData&1; } \
+ /* Data access */ \
+ inline_ const base_class* GetPos() const { return (base_class*)mPosData; } \
+ inline_ const base_class* GetNeg() const { return (base_class*)mNegData; } \
+ inline_ udword GetPosPrimitive() const { return (mPosData>>1); } \
+ inline_ udword GetNegPrimitive() const { return (mNegData>>1); } \
+ /* Stats */ \
+ inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \
+ \
+ volume mAABB; \
+ udword mPosData; \
+ udword mNegData;
+
+ class OPCODE_API AABBCollisionNode
+ {
+ IMPLEMENT_IMPLICIT_NODE(AABBCollisionNode, CollisionAABB)
+
+ inline_ float GetVolume() const { return mAABB.mExtents.x * mAABB.mExtents.y * mAABB.mExtents.z; }
+ inline_ float GetSize() const { return mAABB.mExtents.SquareMagnitude(); }
+ inline_ udword GetRadius() const
+ {
+ udword* Bits = (udword*)&mAABB.mExtents.x;
+ udword Max = Bits[0];
+ if(Bits[1]>Max) Max = Bits[1];
+ if(Bits[2]>Max) Max = Bits[2];
+ return Max;
+ }
+
+ // NB: using the square-magnitude or the true volume of the box, seems to yield better results
+ // (assuming UNC-like informed traversal methods). I borrowed this idea from PQP. The usual "size"
+ // otherwise, is the largest box extent. In SOLID that extent is computed on-the-fly each time it's
+ // needed (the best approach IMHO). In RAPID the rotation matrix is permuted so that Extent[0] is
+ // always the greatest, which saves looking for it at runtime. On the other hand, it yields matrices
+ // whose determinant is not 1, i.e. you can't encode them anymore as unit quaternions. Not a very
+ // good strategy.
+ };
+
+ class OPCODE_API AABBQuantizedNode
+ {
+ IMPLEMENT_IMPLICIT_NODE(AABBQuantizedNode, QuantizedAABB)
+
+ inline_ uword GetSize() const
+ {
+ const uword* Bits = mAABB.mExtents;
+ uword Max = Bits[0];
+ if(Bits[1]>Max) Max = Bits[1];
+ if(Bits[2]>Max) Max = Bits[2];
+ return Max;
+ }
+ // NB: for quantized nodes I don't feel like computing a square-magnitude with integers all
+ // over the place.......!
+ };
+
+ class OPCODE_API AABBNoLeafNode
+ {
+ IMPLEMENT_NOLEAF_NODE(AABBNoLeafNode, CollisionAABB)
+ };
+
+ class OPCODE_API AABBQuantizedNoLeafNode
+ {
+ IMPLEMENT_NOLEAF_NODE(AABBQuantizedNoLeafNode, QuantizedAABB)
+ };
+
+ //! Common interface for a collision tree
+ #define IMPLEMENT_COLLISION_TREE(base_class, node) \
+ public: \
+ /* Constructor / Destructor */ \
+ base_class(); \
+ virtual ~base_class(); \
+ /* Builds from a standard tree */ \
+ override(AABBOptimizedTree) bool Build(AABBTree* tree); \
+ /* Refits the tree */ \
+ override(AABBOptimizedTree) bool Refit(const MeshInterface* mesh_interface); \
+ /* Walks the tree */ \
+ override(AABBOptimizedTree) bool Walk(GenericWalkingCallback callback, void* user_data) const; \
+ /* Data access */ \
+ inline_ const node* GetNodes() const { return mNodes; } \
+ /* Stats */ \
+ override(AABBOptimizedTree) udword GetUsedBytes() const { return mNbNodes*sizeof(node); } \
+ private: \
+ node* mNodes;
+
+ typedef bool (*GenericWalkingCallback) (const void* current, void* user_data);
+
+ class OPCODE_API AABBOptimizedTree
+ {
+ public:
+ // Constructor / Destructor
+ AABBOptimizedTree() :
+ mNbNodes (0)
+ {}
+ virtual ~AABBOptimizedTree() {}
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Builds the collision tree from a generic AABB tree.
+ * \param tree [in] generic AABB tree
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual bool Build(AABBTree* tree) = 0;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Refits the collision tree after vertices have been modified.
+ * \param mesh_interface [in] mesh interface for current model
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual bool Refit(const MeshInterface* mesh_interface) = 0;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Walks the tree and call the user back for each node.
+ * \param callback [in] walking callback
+ * \param user_data [in] callback's user data
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual bool Walk(GenericWalkingCallback callback, void* user_data) const = 0;
+
+ // Data access
+ virtual udword GetUsedBytes() const = 0;
+ inline_ udword GetNbNodes() const { return mNbNodes; }
+
+ protected:
+ udword mNbNodes;
+ };
+
+ class OPCODE_API AABBCollisionTree : public AABBOptimizedTree
+ {
+ IMPLEMENT_COLLISION_TREE(AABBCollisionTree, AABBCollisionNode)
+ };
+
+ class OPCODE_API AABBNoLeafTree : public AABBOptimizedTree
+ {
+ IMPLEMENT_COLLISION_TREE(AABBNoLeafTree, AABBNoLeafNode)
+ };
+
+ class OPCODE_API AABBQuantizedTree : public AABBOptimizedTree
+ {
+ IMPLEMENT_COLLISION_TREE(AABBQuantizedTree, AABBQuantizedNode)
+
+ public:
+ IcePoint mCenterCoeff;
+ IcePoint mExtentsCoeff;
+ };
+
+ class OPCODE_API AABBQuantizedNoLeafTree : public AABBOptimizedTree
+ {
+ IMPLEMENT_COLLISION_TREE(AABBQuantizedNoLeafTree, AABBQuantizedNoLeafNode)
+
+ public:
+ IcePoint mCenterCoeff;
+ IcePoint mExtentsCoeff;
+ };
+
+#endif // __OPC_OPTIMIZEDTREE_H__
diff --git a/Opcode/OpcodeLib/OPC_RayAABBOverlap.h b/Opcode/OpcodeLib/OPC_RayAABBOverlap.h
new file mode 100644
index 0000000..3929a1f
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_RayAABBOverlap.h
@@ -0,0 +1,63 @@
+// Opcode 1.1: ray-AABB overlap tests based on Woo's code
+// Opcode 1.2: ray-AABB overlap tests based on the separating axis theorem
+//
+// The point of intersection is not computed anymore. The distance to impact is not needed anymore
+// since we now have two different queries for segments or rays.
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes a segment-AABB overlap test using the separating axis theorem. Segment is cached within the class.
+ * \param center [in] AABB center
+ * \param extents [in] AABB extents
+ * \return true on overlap
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL RayCollider::SegmentAABBOverlap(const IcePoint& center, const IcePoint& extents)
+{
+ // Stats
+ mNbRayBVTests++;
+
+ float Dx = mData2.x - center.x; if(fabsf(Dx) > extents.x + mFDir.x) return FALSE;
+ float Dy = mData2.y - center.y; if(fabsf(Dy) > extents.y + mFDir.y) return FALSE;
+ float Dz = mData2.z - center.z; if(fabsf(Dz) > extents.z + mFDir.z) return FALSE;
+
+ float f;
+ f = mData.y * Dz - mData.z * Dy; if(fabsf(f) > extents.y*mFDir.z + extents.z*mFDir.y) return FALSE;
+ f = mData.z * Dx - mData.x * Dz; if(fabsf(f) > extents.x*mFDir.z + extents.z*mFDir.x) return FALSE;
+ f = mData.x * Dy - mData.y * Dx; if(fabsf(f) > extents.x*mFDir.y + extents.y*mFDir.x) return FALSE;
+
+ return TRUE;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes a ray-AABB overlap test using the separating axis theorem. Ray is cached within the class.
+ * \param center [in] AABB center
+ * \param extents [in] AABB extents
+ * \return true on overlap
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL RayCollider::RayAABBOverlap(const IcePoint& center, const IcePoint& extents)
+{
+ // Stats
+ mNbRayBVTests++;
+
+// float Dx = mOrigin.x - center.x; if(fabsf(Dx) > extents.x && Dx*mDir.x>=0.0f) return FALSE;
+// float Dy = mOrigin.y - center.y; if(fabsf(Dy) > extents.y && Dy*mDir.y>=0.0f) return FALSE;
+// float Dz = mOrigin.z - center.z; if(fabsf(Dz) > extents.z && Dz*mDir.z>=0.0f) return FALSE;
+
+ float Dx = mOrigin.x - center.x; if(GREATER(Dx, extents.x) && Dx*mDir.x>=0.0f) return FALSE;
+ float Dy = mOrigin.y - center.y; if(GREATER(Dy, extents.y) && Dy*mDir.y>=0.0f) return FALSE;
+ float Dz = mOrigin.z - center.z; if(GREATER(Dz, extents.z) && Dz*mDir.z>=0.0f) return FALSE;
+
+// float Dx = mOrigin.x - center.x; if(GREATER(Dx, extents.x) && ((SIR(Dx)-1)^SIR(mDir.x))>=0.0f) return FALSE;
+// float Dy = mOrigin.y - center.y; if(GREATER(Dy, extents.y) && ((SIR(Dy)-1)^SIR(mDir.y))>=0.0f) return FALSE;
+// float Dz = mOrigin.z - center.z; if(GREATER(Dz, extents.z) && ((SIR(Dz)-1)^SIR(mDir.z))>=0.0f) return FALSE;
+
+ float f;
+ f = mDir.y * Dz - mDir.z * Dy; if(fabsf(f) > extents.y*mFDir.z + extents.z*mFDir.y) return FALSE;
+ f = mDir.z * Dx - mDir.x * Dz; if(fabsf(f) > extents.x*mFDir.z + extents.z*mFDir.x) return FALSE;
+ f = mDir.x * Dy - mDir.y * Dx; if(fabsf(f) > extents.x*mFDir.y + extents.y*mFDir.x) return FALSE;
+
+ return TRUE;
+}
diff --git a/Opcode/OpcodeLib/OPC_RayCollider.cpp b/Opcode/OpcodeLib/OPC_RayCollider.cpp
new file mode 100644
index 0000000..b535bb7
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_RayCollider.cpp
@@ -0,0 +1,762 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a ray collider.
+ * \file OPC_RayCollider.cpp
+ * \author Pierre Terdiman
+ * \date June, 2, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains a ray-vs-tree collider.
+ * This class performs a stabbing query on an AABB tree, i.e. does a ray-mesh collision.
+ *
+ * HIGHER DISTANCE BOUND:
+ *
+ * If P0 and P1 are two 3D points, let's define:
+ * - d = distance between P0 and P1
+ * - Origin = P0
+ * - Direction = (P1 - P0) / d = normalized direction vector
+ * - A parameter t such as a point P on the line (P0,P1) is P = Origin + t * Direction
+ * - t = 0 --> P = P0
+ * - t = d --> P = P1
+ *
+ * Then we can define a general "ray" as:
+ *
+ * struct Ray
+ * {
+ * IcePoint Origin;
+ * IcePoint Direction;
+ * };
+ *
+ * But it actually maps three different things:
+ * - a segment, when 0 <= t <= d
+ * - a half-line, when 0 <= t < +infinity, or -infinity < t <= d
+ * - a line, when -infinity < t < +infinity
+ *
+ * In Opcode, we support segment queries, which yield half-line queries by setting d = +infinity.
+ * We don't support line-queries. If you need them, shift the origin along the ray by an appropriate margin.
+ *
+ * In short, the lower bound is always 0, and you can setup the higher bound "d" with RayCollider::SetMaxDist().
+ *
+ * Query |segment |half-line |line
+ * --------|-------------------|---------------|----------------
+ * Usages |-shadow feelers |-raytracing |-
+ * |-sweep tests |-in/out tests |
+ *
+ * FIRST CONTACT:
+ *
+ * - You can setup "first contact" mode or "all contacts" mode with RayCollider::SetFirstContact().
+ * - In "first contact" mode we return as soon as the ray hits one face. If can be useful e.g. for shadow feelers, where
+ * you want to know whether the path to the light is free or not (a boolean answer is enough).
+ * - In "all contacts" mode we return all faces hit by the ray.
+ *
+ * TEMPORAL COHERENCE:
+ *
+ * - You can enable or disable temporal coherence with RayCollider::SetTemporalCoherence().
+ * - It currently only works in "first contact" mode.
+ * - If temporal coherence is enabled, the previously hit triangle is cached during the first query. Then, next queries
+ * start by colliding the ray against the cached triangle. If they still collide, we return immediately.
+ *
+ * CLOSEST HIT:
+ *
+ * - You can enable or disable "closest hit" with RayCollider::SetClosestHit().
+ * - It currently only works in "all contacts" mode.
+ * - If closest hit is enabled, faces are sorted by distance on-the-fly and the closest one only is reported.
+ *
+ * BACKFACE CULLING:
+ *
+ * - You can enable or disable backface culling with RayCollider::SetCulling().
+ * - If culling is enabled, ray will not hit back faces (only front faces).
+ *
+ *
+ *
+ * \class RayCollider
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date June, 2, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * This class describes a face hit by a ray or segment.
+ * This is a particular class dedicated to stabbing queries.
+ *
+ * \class CollisionFace
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * This class is a dedicated collection of CollisionFace.
+ *
+ * \class CollisionFaces
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+#include "OPC_RayAABBOverlap.h"
+#include "OPC_RayTriOverlap.h"
+
+#define SET_CONTACT(prim_index, flag) \
+ mNbIntersections++; \
+ /* Set contact status */ \
+ mFlags |= flag; \
+ /* In any case the contact has been found and recorded in mStabbedFace */ \
+ mStabbedFace.mFaceID = prim_index;
+
+#ifdef OPC_RAYHIT_CALLBACK
+
+ #define HANDLE_CONTACT(prim_index, flag) \
+ SET_CONTACT(prim_index, flag) \
+ \
+ if(mHitCallback) (mHitCallback)(mStabbedFace, mUserData);
+
+ #define UPDATE_CACHE \
+ if(cache && GetContactStatus()) \
+ { \
+ *cache = mStabbedFace.mFaceID; \
+ }
+#else
+
+ #define HANDLE_CONTACT(prim_index, flag) \
+ SET_CONTACT(prim_index, flag) \
+ \
+ /* Now we can also record it in mStabbedFaces if available */ \
+ if(mStabbedFaces) \
+ { \
+ /* If we want all faces or if that's the first one we hit */ \
+ if(!mClosestHit || !mStabbedFaces->GetNbFaces()) \
+ { \
+ mStabbedFaces->AddFace(mStabbedFace); \
+ } \
+ else \
+ { \
+ /* We only keep closest hit */ \
+ CollisionFace* Current = const_cast<CollisionFace*>(mStabbedFaces->GetFaces()); \
+ if(Current && mStabbedFace.mDistance<Current->mDistance) \
+ { \
+ *Current = mStabbedFace; \
+ } \
+ } \
+ }
+
+ #define UPDATE_CACHE \
+ if(cache && GetContactStatus() && mStabbedFaces) \
+ { \
+ const CollisionFace* Current = mStabbedFaces->GetFaces(); \
+ if(Current) *cache = Current->mFaceID; \
+ else *cache = INVALID_ID; \
+ }
+#endif
+
+#define SEGMENT_PRIM(prim_index, flag) \
+ /* Request vertices from the app */ \
+ VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
+ \
+ /* Perform ray-tri overlap test and return */ \
+ if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \
+ { \
+ /* Intersection point is valid if dist < segment's length */ \
+ /* We know dist>0 so we can use integers */ \
+ if(IR(mStabbedFace.mDistance)<IR(mMaxDist)) \
+ { \
+ HANDLE_CONTACT(prim_index, flag) \
+ } \
+ }
+
+#define RAY_PRIM(prim_index, flag) \
+ /* Request vertices from the app */ \
+ VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
+ \
+ /* Perform ray-tri overlap test and return */ \
+ if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \
+ { \
+ HANDLE_CONTACT(prim_index, flag) \
+ }
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+RayCollider::RayCollider() :
+ mNbRayBVTests (0),
+ mNbRayPrimTests (0),
+ mNbIntersections (0),
+ mCulling (true),
+#ifdef OPC_RAYHIT_CALLBACK
+ mHitCallback (null),
+ mUserData (0),
+#else
+ mClosestHit (false),
+ mStabbedFaces (null),
+#endif
+ mMaxDist (MAX_FLOAT)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+RayCollider::~RayCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Validates current settings. You should call this method after all the settings and callbacks have been defined.
+ * \return null if everything is ok, else a string describing the problem
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const char* RayCollider::ValidateSettings()
+{
+ if(mMaxDist<0.0f) return "Higher distance bound must be positive!";
+ if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!";
+#ifndef OPC_RAYHIT_CALLBACK
+ if(mClosestHit && FirstContactEnabled()) return "Closest hit doesn't work with ""First contact"" mode!";
+ if(TemporalCoherenceEnabled() && mClosestHit) return "Temporal coherence can't guarantee to report closest hit!";
+#endif
+ if(SkipPrimitiveTests()) return "SkipPrimitiveTests not possible for RayCollider ! (not implemented)";
+ return null;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Generic stabbing query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - in the user-provided destination array
+ *
+ * \param world_ray [in] stabbing ray in world space
+ * \param model [in] Opcode model to collide with
+ * \param world [in] model's world matrix, or null
+ * \param cache [in] a possibly cached face index, or null
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool RayCollider::Collide(const Ray& world_ray, const Model& model, const Matrix4x4* world, udword* cache)
+{
+ // Checkings
+ if(!Setup(&model)) return false;
+
+ // Init collision query
+ if(InitQuery(world_ray, world, cache)) return true;
+
+ if(!model.HasLeafNodes())
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform stabbing query
+ if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes());
+ else _RayStab(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
+
+ // Perform stabbing query
+ if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes());
+ else _RayStab(Tree->GetNodes());
+ }
+ }
+ else
+ {
+ if(model.IsQuantized())
+ {
+ const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
+
+ // Setup dequantization coeffs
+ mCenterCoeff = Tree->mCenterCoeff;
+ mExtentsCoeff = Tree->mExtentsCoeff;
+
+ // Perform stabbing query
+ if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes());
+ else _RayStab(Tree->GetNodes());
+ }
+ else
+ {
+ const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
+
+ // Perform stabbing query
+ if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes());
+ else _RayStab(Tree->GetNodes());
+ }
+ }
+
+ // Update cache if needed
+ UPDATE_CACHE
+ return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Initializes a stabbing query :
+ * - reset stats & contact status
+ * - compute ray in local space
+ * - check temporal coherence
+ *
+ * \param world_ray [in] stabbing ray in world space
+ * \param world [in] object's world matrix, or null
+ * \param face_id [in] index of previously stabbed triangle
+ * \return TRUE if we can return immediately
+ * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+BOOL RayCollider::InitQuery(const Ray& world_ray, const Matrix4x4* world, udword* face_id)
+{
+ // Reset stats & contact status
+ Collider::InitQuery();
+ mNbRayBVTests = 0;
+ mNbRayPrimTests = 0;
+ mNbIntersections = 0;
+#ifndef OPC_RAYHIT_CALLBACK
+ if(mStabbedFaces) mStabbedFaces->Reset();
+#endif
+
+ // Compute ray in local space
+ // The (Origin/Dir) form is needed for the ray-triangle test anyway (even for segment tests)
+ if(world)
+ {
+ Matrix3x3 InvWorld = *world;
+ mDir = InvWorld * world_ray.mDir;
+
+ Matrix4x4 World;
+ InvertPRMatrix(World, *world);
+ mOrigin = world_ray.mOrig * World;
+ }
+ else
+ {
+ mDir = world_ray.mDir;
+ mOrigin = world_ray.mOrig;
+ }
+
+ // 4) Special case: 1-triangle meshes [Opcode 1.3]
+ if(mCurrentModel && mCurrentModel->HasSingleNode())
+ {
+ // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
+ if(!SkipPrimitiveTests())
+ {
+ // Perform overlap test between the unique triangle and the ray (and set contact status if needed)
+ SEGMENT_PRIM(udword(0), OPC_CONTACT)
+
+ // Return immediately regardless of status
+ return TRUE;
+ }
+ }
+
+ // Check temporal coherence :
+
+ // Test previously colliding primitives first
+ if(TemporalCoherenceEnabled() && FirstContactEnabled() && face_id && *face_id!=INVALID_ID)
+ {
+#ifdef OLD_CODE
+#ifndef OPC_RAYHIT_CALLBACK
+ if(!mClosestHit)
+#endif
+ {
+ // Request vertices from the app
+ VertexPointers VP;
+ mIMesh->GetTriangle(VP, *face_id);
+ // Perform ray-cached tri overlap test
+ if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2]))
+ {
+ // Intersection point is valid if:
+ // - distance is positive (else it can just be a face behind the orig point)
+ // - distance is smaller than a given max distance (useful for shadow feelers)
+// if(mStabbedFace.mDistance>0.0f && mStabbedFace.mDistance<mMaxDist)
+ if(IR(mStabbedFace.mDistance)<IR(mMaxDist)) // The other test is already performed in RayTriOverlap
+ {
+ // Set contact status
+ mFlags |= OPC_TEMPORAL_CONTACT;
+
+ mStabbedFace.mFaceID = *face_id;
+
+#ifndef OPC_RAYHIT_CALLBACK
+ if(mStabbedFaces) mStabbedFaces->AddFace(mStabbedFace);
+#endif
+ return TRUE;
+ }
+ }
+ }
+#else
+ // New code
+ // We handle both Segment/ray queries with the same segment code, and a possible infinite limit
+ SEGMENT_PRIM(*face_id, OPC_TEMPORAL_CONTACT)
+
+ // Return immediately if possible
+ if(GetContactStatus()) return TRUE;
+#endif
+ }
+
+ // Precompute data (moved after temporal coherence since only needed for ray-AABB)
+ if(IR(mMaxDist)!=IEEE_MAX_FLOAT)
+ {
+ // For Segment-AABB overlap
+ mData = 0.5f * mDir * mMaxDist;
+ mData2 = mOrigin + mData;
+
+ // Precompute mFDir;
+ mFDir.x = fabsf(mData.x);
+ mFDir.y = fabsf(mData.y);
+ mFDir.z = fabsf(mData.z);
+ }
+ else
+ {
+ // For Ray-AABB overlap
+// udword x = SIR(mDir.x)-1;
+// udword y = SIR(mDir.y)-1;
+// udword z = SIR(mDir.z)-1;
+// mData.x = FR(x);
+// mData.y = FR(y);
+// mData.z = FR(z);
+
+ // Precompute mFDir;
+ mFDir.x = fabsf(mDir.x);
+ mFDir.y = fabsf(mDir.y);
+ mFDir.z = fabsf(mDir.z);
+ }
+
+ return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Stabbing query for vanilla AABB trees.
+ * \param world_ray [in] stabbing ray in world space
+ * \param tree [in] AABB tree
+ * \param box_indices [out] indices of stabbed boxes
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool RayCollider::Collide(const Ray& world_ray, const AABBTree* tree, Container& box_indices)
+{
+ // ### bad design here
+
+ // This is typically called for a scene tree, full of -AABBs-, not full of triangles.
+ // So we don't really have "primitives" to deal with. Hence it doesn't work with
+ // "FirstContact" + "TemporalCoherence".
+ ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) );
+
+ // Checkings
+ if(!tree) return false;
+
+ // Init collision query
+ // Basically this is only called to initialize precomputed data
+ if(InitQuery(world_ray)) return true;
+
+ // Perform stabbing query
+ if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(tree, box_indices);
+ else _RayStab(tree, box_indices);
+
+ return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for normal AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_SegmentStab(const AABBCollisionNode* node)
+{
+ // Perform Segment-AABB overlap test
+ if(!SegmentAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
+
+ if(node->IsLeaf())
+ {
+ SEGMENT_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _SegmentStab(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _SegmentStab(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for quantized AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_SegmentStab(const AABBQuantizedNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const IcePoint Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const IcePoint Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform Segment-AABB overlap test
+ if(!SegmentAABBOverlap(Center, Extents)) return;
+
+ if(node->IsLeaf())
+ {
+ SEGMENT_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _SegmentStab(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _SegmentStab(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_SegmentStab(const AABBNoLeafNode* node)
+{
+ // Perform Segment-AABB overlap test
+ if(!SegmentAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
+
+ if(node->HasPosLeaf())
+ {
+ SEGMENT_PRIM(node->GetPosPrimitive(), OPC_CONTACT)
+ }
+ else _SegmentStab(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf())
+ {
+ SEGMENT_PRIM(node->GetNegPrimitive(), OPC_CONTACT)
+ }
+ else _SegmentStab(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for quantized no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_SegmentStab(const AABBQuantizedNoLeafNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const IcePoint Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const IcePoint Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform Segment-AABB overlap test
+ if(!SegmentAABBOverlap(Center, Extents)) return;
+
+ if(node->HasPosLeaf())
+ {
+ SEGMENT_PRIM(node->GetPosPrimitive(), OPC_CONTACT)
+ }
+ else _SegmentStab(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf())
+ {
+ SEGMENT_PRIM(node->GetNegPrimitive(), OPC_CONTACT)
+ }
+ else _SegmentStab(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for vanilla AABB trees.
+ * \param node [in] current collision node
+ * \param box_indices [out] indices of stabbed boxes
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_SegmentStab(const AABBTreeNode* node, Container& box_indices)
+{
+ // Test the box against the segment
+ IcePoint Center, Extents;
+ node->GetAABB()->GetCenter(Center);
+ node->GetAABB()->GetExtents(Extents);
+ if(!SegmentAABBOverlap(Center, Extents)) return;
+
+ if(node->IsLeaf())
+ {
+ box_indices.Add(node->GetPrimitives(), node->GetNbPrimitives());
+ }
+ else
+ {
+ _SegmentStab(node->GetPos(), box_indices);
+ _SegmentStab(node->GetNeg(), box_indices);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for normal AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_RayStab(const AABBCollisionNode* node)
+{
+ // Perform Ray-AABB overlap test
+ if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
+
+ if(node->IsLeaf())
+ {
+ RAY_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _RayStab(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _RayStab(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for quantized AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_RayStab(const AABBQuantizedNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const IcePoint Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const IcePoint Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform Ray-AABB overlap test
+ if(!RayAABBOverlap(Center, Extents)) return;
+
+ if(node->IsLeaf())
+ {
+ RAY_PRIM(node->GetPrimitive(), OPC_CONTACT)
+ }
+ else
+ {
+ _RayStab(node->GetPos());
+
+ if(ContactFound()) return;
+
+ _RayStab(node->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_RayStab(const AABBNoLeafNode* node)
+{
+ // Perform Ray-AABB overlap test
+ if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
+
+ if(node->HasPosLeaf())
+ {
+ RAY_PRIM(node->GetPosPrimitive(), OPC_CONTACT)
+ }
+ else _RayStab(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf())
+ {
+ RAY_PRIM(node->GetNegPrimitive(), OPC_CONTACT)
+ }
+ else _RayStab(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for quantized no-leaf AABB trees.
+ * \param node [in] current collision node
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_RayStab(const AABBQuantizedNoLeafNode* node)
+{
+ // Dequantize box
+ const QuantizedAABB& Box = node->mAABB;
+ const IcePoint Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
+ const IcePoint Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
+
+ // Perform Ray-AABB overlap test
+ if(!RayAABBOverlap(Center, Extents)) return;
+
+ if(node->HasPosLeaf())
+ {
+ RAY_PRIM(node->GetPosPrimitive(), OPC_CONTACT)
+ }
+ else _RayStab(node->GetPos());
+
+ if(ContactFound()) return;
+
+ if(node->HasNegLeaf())
+ {
+ RAY_PRIM(node->GetNegPrimitive(), OPC_CONTACT)
+ }
+ else _RayStab(node->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive stabbing query for vanilla AABB trees.
+ * \param node [in] current collision node
+ * \param box_indices [out] indices of stabbed boxes
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void RayCollider::_RayStab(const AABBTreeNode* node, Container& box_indices)
+{
+ // Test the box against the ray
+ IcePoint Center, Extents;
+ node->GetAABB()->GetCenter(Center);
+ node->GetAABB()->GetExtents(Extents);
+ if(!RayAABBOverlap(Center, Extents)) return;
+
+ if(node->IsLeaf())
+ {
+ mFlags |= OPC_CONTACT;
+ box_indices.Add(node->GetPrimitives(), node->GetNbPrimitives());
+ }
+ else
+ {
+ _RayStab(node->GetPos(), box_indices);
+ _RayStab(node->GetNeg(), box_indices);
+ }
+}
diff --git a/Opcode/OpcodeLib/OPC_RayCollider.h b/Opcode/OpcodeLib/OPC_RayCollider.h
new file mode 100644
index 0000000..64dc2b4
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_RayCollider.h
@@ -0,0 +1,225 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a ray collider.
+ * \file OPC_RayCollider.h
+ * \author Pierre Terdiman
+ * \date June, 2, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_RAYCOLLIDER_H__
+#define __OPC_RAYCOLLIDER_H__
+
+ class OPCODE_API CollisionFace
+ {
+ public:
+ //! Constructor
+ inline_ CollisionFace() {}
+ //! Destructor
+ inline_ ~CollisionFace() {}
+
+ udword mFaceID; //!< Index of touched face
+ float mDistance; //!< Distance from collider to hitpoint
+ float mU, mV; //!< Impact barycentric coordinates
+ };
+
+ class OPCODE_API CollisionFaces : private Container
+ {
+ public:
+ //! Constructor
+ CollisionFaces() {}
+ //! Destructor
+ ~CollisionFaces() {}
+
+ inline_ udword GetNbFaces() const { return GetNbEntries()>>2; }
+ inline_ const CollisionFace* GetFaces() const { return (const CollisionFace*)GetEntries(); }
+
+ inline_ void Reset() { Container::Reset(); }
+
+ inline_ void AddFace(const CollisionFace& face) { Add(face.mFaceID).Add(face.mDistance).Add(face.mU).Add(face.mV); }
+ };
+
+#ifdef OPC_RAYHIT_CALLBACK
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * User-callback, called by OPCODE to record a hit.
+ * \param hit [in] current hit
+ * \param user_data [in] user-defined data from SetCallback()
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ typedef void (*HitCallback) (const CollisionFace& hit, void* user_data);
+#endif
+
+ class OPCODE_API RayCollider : public Collider
+ {
+ public:
+ // Constructor / Destructor
+ RayCollider();
+ virtual ~RayCollider();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Generic stabbing query for generic OPCODE models. After the call, access the results:
+ * - with GetContactStatus()
+ * - in the user-provided destination array
+ *
+ * \param world_ray [in] stabbing ray in world space
+ * \param model [in] Opcode model to collide with
+ * \param world [in] model's world matrix, or null
+ * \param cache [in] a possibly cached face index, or null
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool Collide(const Ray& world_ray, const Model& model, const Matrix4x4* world=null, udword* cache=null);
+ //
+ bool Collide(const Ray& world_ray, const AABBTree* tree, Container& box_indices);
+ // Settings
+
+#ifndef OPC_RAYHIT_CALLBACK
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Settings: enable or disable "closest hit" mode.
+ * \param flag [in] true to report closest hit only
+ * \see SetCulling(bool flag)
+ * \see SetMaxDist(float max_dist)
+ * \see SetDestination(StabbedFaces* sf)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetClosestHit(bool flag) { mClosestHit = flag; }
+#endif
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Settings: enable or disable backface culling.
+ * \param flag [in] true to enable backface culling
+ * \see SetClosestHit(bool flag)
+ * \see SetMaxDist(float max_dist)
+ * \see SetDestination(StabbedFaces* sf)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetCulling(bool flag) { mCulling = flag; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Settings: sets the higher distance bound.
+ * \param max_dist [in] higher distance bound. Default = maximal value, for ray queries (else segment)
+ * \see SetClosestHit(bool flag)
+ * \see SetCulling(bool flag)
+ * \see SetDestination(StabbedFaces* sf)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetMaxDist(float max_dist=MAX_FLOAT) { mMaxDist = max_dist; }
+
+#ifdef OPC_RAYHIT_CALLBACK
+ inline_ void SetHitCallback(HitCallback cb) { mHitCallback = cb; }
+ inline_ void SetUserData(void* user_data) { mUserData = user_data; }
+#else
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Settings: sets the destination array for stabbed faces.
+ * \param cf [in] destination array, filled during queries
+ * \see SetClosestHit(bool flag)
+ * \see SetCulling(bool flag)
+ * \see SetMaxDist(float max_dist)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetDestination(CollisionFaces* cf) { mStabbedFaces = cf; }
+#endif
+ // Stats
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Stats: gets the number of Ray-BV overlap tests after a collision query.
+ * \see GetNbRayPrimTests()
+ * \see GetNbIntersections()
+ * \return the number of Ray-BV tests performed during last query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbRayBVTests() const { return mNbRayBVTests; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Stats: gets the number of Ray-Triangle overlap tests after a collision query.
+ * \see GetNbRayBVTests()
+ * \see GetNbIntersections()
+ * \return the number of Ray-Triangle tests performed during last query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbRayPrimTests() const { return mNbRayPrimTests; }
+
+ // In-out test
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Stats: gets the number of intersection found after a collision query. Can be used for in/out tests.
+ * \see GetNbRayBVTests()
+ * \see GetNbRayPrimTests()
+ * \return the number of valid intersections during last query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbIntersections() const { return mNbIntersections; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Validates current settings. You should call this method after all the settings and callbacks have been defined for a collider.
+ * \return null if everything is ok, else a string describing the problem
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(Collider) const char* ValidateSettings();
+
+ protected:
+ // Ray in local space
+ IcePoint mOrigin; //!< Ray origin
+ IcePoint mDir; //!< Ray direction (normalized)
+ IcePoint mFDir; //!< fabsf(mDir)
+ IcePoint mData, mData2;
+ // Stabbed faces
+ CollisionFace mStabbedFace; //!< Current stabbed face
+#ifdef OPC_RAYHIT_CALLBACK
+ HitCallback mHitCallback; //!< Callback used to record a hit
+ void* mUserData; //!< User-defined data
+#else
+ CollisionFaces* mStabbedFaces; //!< List of stabbed faces
+#endif
+ // Stats
+ udword mNbRayBVTests; //!< Number of Ray-BV tests
+ udword mNbRayPrimTests; //!< Number of Ray-Primitive tests
+ // In-out test
+ udword mNbIntersections; //!< Number of valid intersections
+ // Dequantization coeffs
+ IcePoint mCenterCoeff;
+ IcePoint mExtentsCoeff;
+ // Settings
+ float mMaxDist; //!< Valid segment on the ray
+#ifndef OPC_RAYHIT_CALLBACK
+ bool mClosestHit; //!< Report closest hit only
+#endif
+ bool mCulling; //!< Stab culled faces or not
+ // Internal methods
+ void _SegmentStab(const AABBCollisionNode* node);
+ void _SegmentStab(const AABBNoLeafNode* node);
+ void _SegmentStab(const AABBQuantizedNode* node);
+ void _SegmentStab(const AABBQuantizedNoLeafNode* node);
+ void _SegmentStab(const AABBTreeNode* node, Container& box_indices);
+ void _RayStab(const AABBCollisionNode* node);
+ void _RayStab(const AABBNoLeafNode* node);
+ void _RayStab(const AABBQuantizedNode* node);
+ void _RayStab(const AABBQuantizedNoLeafNode* node);
+ void _RayStab(const AABBTreeNode* node, Container& box_indices);
+ // Overlap tests
+ inline_ BOOL RayAABBOverlap(const IcePoint& center, const IcePoint& extents);
+ inline_ BOOL SegmentAABBOverlap(const IcePoint& center, const IcePoint& extents);
+ inline_ BOOL RayTriOverlap(const IcePoint& vert0, const IcePoint& vert1, const IcePoint& vert2);
+ // Init methods
+ BOOL InitQuery(const Ray& world_ray, const Matrix4x4* world=null, udword* face_id=null);
+ };
+
+#endif // __OPC_RAYCOLLIDER_H__
diff --git a/Opcode/OpcodeLib/OPC_RayTriOverlap.h b/Opcode/OpcodeLib/OPC_RayTriOverlap.h
new file mode 100644
index 0000000..6c65df1
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_RayTriOverlap.h
@@ -0,0 +1,89 @@
+#define LOCAL_EPSILON 0.000001f
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes a ray-triangle intersection test.
+ * Original code from Tomas Möller's "Fast Minimum Storage Ray-Triangle Intersection".
+ * It's been optimized a bit with integer code, and modified to return a non-intersection if distance from
+ * ray origin to triangle is negative.
+ *
+ * \param vert0 [in] triangle vertex
+ * \param vert1 [in] triangle vertex
+ * \param vert2 [in] triangle vertex
+ * \return true on overlap. mStabbedFace is filled with relevant info.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL RayCollider::RayTriOverlap(const IcePoint& vert0, const IcePoint& vert1, const IcePoint& vert2)
+{
+ // Stats
+ mNbRayPrimTests++;
+
+ // Find vectors for two edges sharing vert0
+ IcePoint edge1 = vert1 - vert0;
+ IcePoint edge2 = vert2 - vert0;
+
+ // Begin calculating determinant - also used to calculate U parameter
+ IcePoint pvec = mDir^edge2;
+
+ // If determinant is near zero, ray lies in plane of triangle
+ float det = edge1|pvec;
+
+ if(mCulling)
+ {
+ if(det<LOCAL_EPSILON) return FALSE;
+ // From here, det is > 0. So we can use integer cmp.
+
+ // Calculate distance from vert0 to ray origin
+ IcePoint tvec = mOrigin - vert0;
+
+ // Calculate U parameter and test bounds
+ mStabbedFace.mU = tvec|pvec;
+// if(IR(u)&0x80000000 || u>det) return FALSE;
+ if(IS_NEGATIVE_FLOAT(mStabbedFace.mU) || IR(mStabbedFace.mU)>IR(det)) return FALSE;
+
+ // Prepare to test V parameter
+ IcePoint qvec = tvec^edge1;
+
+ // Calculate V parameter and test bounds
+ mStabbedFace.mV = mDir|qvec;
+ if(IS_NEGATIVE_FLOAT(mStabbedFace.mV) || mStabbedFace.mU+mStabbedFace.mV>det) return FALSE;
+
+ // Calculate t, scale parameters, ray intersects triangle
+ mStabbedFace.mDistance = edge2|qvec;
+ // Det > 0 so we can early exit here
+ // Intersection point is valid if distance is positive (else it can just be a face behind the orig point)
+ if(IS_NEGATIVE_FLOAT(mStabbedFace.mDistance)) return FALSE;
+ // Else go on
+ float OneOverDet = 1.0f / det;
+ mStabbedFace.mDistance *= OneOverDet;
+ mStabbedFace.mU *= OneOverDet;
+ mStabbedFace.mV *= OneOverDet;
+ }
+ else
+ {
+ // the non-culling branch
+ if(det>-LOCAL_EPSILON && det<LOCAL_EPSILON) return FALSE;
+ float OneOverDet = 1.0f / det;
+
+ // Calculate distance from vert0 to ray origin
+ IcePoint tvec = mOrigin - vert0;
+
+ // Calculate U parameter and test bounds
+ mStabbedFace.mU = (tvec|pvec) * OneOverDet;
+// if(IR(u)&0x80000000 || u>1.0f) return FALSE;
+ if(IS_NEGATIVE_FLOAT(mStabbedFace.mU) || IR(mStabbedFace.mU)>IEEE_1_0) return FALSE;
+
+ // prepare to test V parameter
+ IcePoint qvec = tvec^edge1;
+
+ // Calculate V parameter and test bounds
+ mStabbedFace.mV = (mDir|qvec) * OneOverDet;
+ if(IS_NEGATIVE_FLOAT(mStabbedFace.mV) || mStabbedFace.mU+mStabbedFace.mV>1.0f) return FALSE;
+
+ // Calculate t, ray intersects triangle
+ mStabbedFace.mDistance = (edge2|qvec) * OneOverDet;
+ // Intersection point is valid if distance is positive (else it can just be a face behind the orig point)
+ if(IS_NEGATIVE_FLOAT(mStabbedFace.mDistance)) return FALSE;
+ }
+ return TRUE;
+}
diff --git a/Opcode/OpcodeLib/OPC_Settings.h b/Opcode/OpcodeLib/OPC_Settings.h
new file mode 100644
index 0000000..8232d9b
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_Settings.h
@@ -0,0 +1,49 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains compilation flags.
+ * \file OPC_Settings.h
+ * \author Pierre Terdiman
+ * \date May, 12, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_SETTINGS_H__
+#define __OPC_SETTINGS_H__
+
+ //! Use CPU comparisons (comment that line to use standard FPU compares)
+ #define OPC_CPU_COMPARE
+
+ //! Use FCOMI / FCMOV on Pentium-Pro based processors (comment that line to use plain C++)
+ #define OPC_USE_FCOMI
+
+ //! Use epsilon value in tri-tri overlap test
+ #define OPC_TRITRI_EPSILON_TEST
+
+ //! Use tree-coherence or not [not implemented yet]
+// #define OPC_USE_TREE_COHERENCE
+
+ //! Use callbacks or direct pointers. Using callbacks might be a bit slower (but probably not much)
+// #define OPC_USE_CALLBACKS
+
+ //! Support triangle and vertex strides or not. Using strides might be a bit slower (but probably not much)
+// #define OPC_USE_STRIDE
+
+ //! Discard negative pointer in vanilla trees
+ #define OPC_NO_NEG_VANILLA_TREE
+
+ //! Use a callback in the ray collider
+ #define OPC_RAYHIT_CALLBACK
+
+ // NB: no compilation flag to enable/disable stats since they're actually needed in the box/box overlap test
+
+#endif //__OPC_SETTINGS_H__ \ No newline at end of file
diff --git a/Opcode/OpcodeLib/OPC_TreeBuilders.cpp b/Opcode/OpcodeLib/OPC_TreeBuilders.cpp
new file mode 100644
index 0000000..fa415db
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_TreeBuilders.cpp
@@ -0,0 +1,255 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for tree builders.
+ * \file OPC_TreeBuilders.cpp
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A builder for AABB-trees of vertices.
+ *
+ * \class AABBTreeOfVerticesBuilder
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A builder for AABB-trees of AABBs.
+ *
+ * \class AABBTreeOfAABBsBuilder
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A builder for AABB-trees of triangles.
+ *
+ * \class AABBTreeOfTrianglesBuilder
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the AABB of a set of primitives.
+ * \param primitives [in] list of indices of primitives
+ * \param nb_prims [in] number of indices
+ * \param global_box [out] global AABB enclosing the set of input primitives
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeOfAABBsBuilder::ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const
+{
+ // Checkings
+ if(!primitives || !nb_prims) return false;
+
+ // Initialize global box
+ global_box = mAABBArray[primitives[0]];
+
+ // Loop through boxes
+ for(udword i=1;i<nb_prims;i++)
+ {
+ // Update global box
+ global_box.Add(mAABBArray[primitives[i]]);
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the splitting value along a given axis for a given primitive.
+ * \param index [in] index of the primitive to split
+ * \param axis [in] axis index (0,1,2)
+ * \return splitting value
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float AABBTreeOfAABBsBuilder::GetSplittingValue(udword index, udword axis) const
+{
+ // For an AABB, the splitting value is the middle of the given axis,
+ // i.e. the corresponding component of the center point
+ return mAABBArray[index].GetCenter(axis);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the AABB of a set of primitives.
+ * \param primitives [in] list of indices of primitives
+ * \param nb_prims [in] number of indices
+ * \param global_box [out] global AABB enclosing the set of input primitives
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeOfTrianglesBuilder::ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const
+{
+ // Checkings
+ if(!primitives || !nb_prims) return false;
+
+ // Initialize global box
+ IcePoint Min(MAX_FLOAT, MAX_FLOAT, MAX_FLOAT);
+ IcePoint Max(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT);
+
+ // Loop through triangles
+ VertexPointers VP;
+ while(nb_prims--)
+ {
+ // Get current triangle-vertices
+ mIMesh->GetTriangle(VP, *primitives++);
+ // Update global box
+ Min.Min(*VP.Vertex[0]).Min(*VP.Vertex[1]).Min(*VP.Vertex[2]);
+ Max.Max(*VP.Vertex[0]).Max(*VP.Vertex[1]).Max(*VP.Vertex[2]);
+ }
+ global_box.SetMinMax(Min, Max);
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the splitting value along a given axis for a given primitive.
+ * \param index [in] index of the primitive to split
+ * \param axis [in] axis index (0,1,2)
+ * \return splitting value
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float AABBTreeOfTrianglesBuilder::GetSplittingValue(udword index, udword axis) const
+{
+/* // Compute center of triangle
+ IcePoint Center;
+ mTriList[index].Center(mVerts, Center);
+ // Return value
+ return Center[axis];*/
+
+ // Compute correct component from center of triangle
+// return (mVerts[mTriList[index].mVRef[0]][axis]
+// +mVerts[mTriList[index].mVRef[1]][axis]
+// +mVerts[mTriList[index].mVRef[2]][axis])*INV3;
+
+ VertexPointers VP;
+ mIMesh->GetTriangle(VP, index);
+
+ // Compute correct component from center of triangle
+ return ((*VP.Vertex[0])[axis]
+ +(*VP.Vertex[1])[axis]
+ +(*VP.Vertex[2])[axis])*INV3;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the splitting value along a given axis for a given node.
+ * \param primitives [in] list of indices of primitives
+ * \param nb_prims [in] number of indices
+ * \param global_box [in] global AABB enclosing the set of input primitives
+ * \param axis [in] axis index (0,1,2)
+ * \return splitting value
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float AABBTreeOfTrianglesBuilder::GetSplittingValue(const udword* primitives, udword nb_prims, const AABB& global_box, udword axis) const
+{
+ if(mSettings.mRules&SPLIT_GEOM_CENTER)
+ {
+ // Loop through triangles
+ float SplitValue = 0.0f;
+ VertexPointers VP;
+ for(udword i=0;i<nb_prims;i++)
+ {
+ // Get current triangle-vertices
+ mIMesh->GetTriangle(VP, primitives[i]);
+ // Update split value
+ SplitValue += (*VP.Vertex[0])[axis];
+ SplitValue += (*VP.Vertex[1])[axis];
+ SplitValue += (*VP.Vertex[2])[axis];
+ }
+ return SplitValue / float(nb_prims*3);
+ }
+ else return AABBTreeBuilder::GetSplittingValue(primitives, nb_prims, global_box, axis);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the AABB of a set of primitives.
+ * \param primitives [in] list of indices of primitives
+ * \param nb_prims [in] number of indices
+ * \param global_box [out] global AABB enclosing the set of input primitives
+ * \return true if success
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeOfVerticesBuilder::ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const
+{
+ // Checkings
+ if(!primitives || !nb_prims) return false;
+
+ // Initialize global box
+ global_box.SetEmpty();
+
+ // Loop through vertices
+ for(udword i=0;i<nb_prims;i++)
+ {
+ // Update global box
+ global_box.Extend(mVertexArray[primitives[i]]);
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the splitting value along a given axis for a given primitive.
+ * \param index [in] index of the primitive to split
+ * \param axis [in] axis index (0,1,2)
+ * \return splitting value
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float AABBTreeOfVerticesBuilder::GetSplittingValue(udword index, udword axis) const
+{
+ // For a vertex, the splitting value is simply the vertex coordinate.
+ return mVertexArray[index][axis];
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Computes the splitting value along a given axis for a given node.
+ * \param primitives [in] list of indices of primitives
+ * \param nb_prims [in] number of indices
+ * \param global_box [in] global AABB enclosing the set of input primitives
+ * \param axis [in] axis index (0,1,2)
+ * \return splitting value
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+float AABBTreeOfVerticesBuilder::GetSplittingValue(const udword* primitives, udword nb_prims, const AABB& global_box, udword axis) const
+{
+ if(mSettings.mRules&SPLIT_GEOM_CENTER)
+ {
+ // Loop through vertices
+ float SplitValue = 0.0f;
+ for(udword i=0;i<nb_prims;i++)
+ {
+ // Update split value
+ SplitValue += mVertexArray[primitives[i]][axis];
+ }
+ return SplitValue / float(nb_prims);
+ }
+ else return AABBTreeBuilder::GetSplittingValue(primitives, nb_prims, global_box, axis);
+}
diff --git a/Opcode/OpcodeLib/OPC_TreeBuilders.h b/Opcode/OpcodeLib/OPC_TreeBuilders.h
new file mode 100644
index 0000000..bfff16a
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_TreeBuilders.h
@@ -0,0 +1,173 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for tree builders.
+ * \file OPC_TreeBuilders.h
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_TREEBUILDERS_H__
+#define __OPC_TREEBUILDERS_H__
+
+ //! Tree splitting rules
+ enum SplittingRules
+ {
+ // Primitive split
+ SPLIT_LARGEST_AXIS = (1<<0), //!< Split along the largest axis
+ SPLIT_SPLATTER_POINTS = (1<<1), //!< Splatter primitive centers (QuickCD-style)
+ SPLIT_BEST_AXIS = (1<<2), //!< Try largest axis, then second, then last
+ SPLIT_BALANCED = (1<<3), //!< Try to keep a well-balanced tree
+ SPLIT_FIFTY = (1<<4), //!< Arbitrary 50-50 split
+ // Node split
+ SPLIT_GEOM_CENTER = (1<<5), //!< Split at geometric center (else split in the middle)
+ //
+ SPLIT_FORCE_DWORD = 0x7fffffff
+ };
+
+ //! Simple wrapper around build-related settings [Opcode 1.3]
+ struct OPCODE_API BuildSettings
+ {
+ inline_ BuildSettings() : mLimit(1), mRules(SPLIT_FORCE_DWORD) {}
+
+ udword mLimit; //!< Limit number of primitives / node. If limit is 1, build a complete tree (2*N-1 nodes)
+ udword mRules; //!< Building/Splitting rules (a combination of SplittingRules flags)
+ };
+
+ class OPCODE_API AABBTreeBuilder
+ {
+ public:
+ //! Constructor
+ AABBTreeBuilder() :
+ mNbPrimitives(0),
+ mNodeBase(null),
+ mCount(0),
+ mNbInvalidSplits(0) {}
+ //! Destructor
+ virtual ~AABBTreeBuilder() {}
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the AABB of a set of primitives.
+ * \param primitives [in] list of indices of primitives
+ * \param nb_prims [in] number of indices
+ * \param global_box [out] global AABB enclosing the set of input primitives
+ * \return true if success
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual bool ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const = 0;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the splitting value along a given axis for a given primitive.
+ * \param index [in] index of the primitive to split
+ * \param axis [in] axis index (0,1,2)
+ * \return splitting value
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual float GetSplittingValue(udword index, udword axis) const = 0;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Computes the splitting value along a given axis for a given node.
+ * \param primitives [in] list of indices of primitives
+ * \param nb_prims [in] number of indices
+ * \param global_box [in] global AABB enclosing the set of input primitives
+ * \param axis [in] axis index (0,1,2)
+ * \return splitting value
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual float GetSplittingValue(const udword* primitives, udword nb_prims, const AABB& global_box, udword axis) const
+ {
+ // Default split value = middle of the axis (using only the box)
+ return global_box.GetCenter(axis);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Validates node subdivision. This is called each time a node is considered for subdivision, during tree building.
+ * \param primitives [in] list of indices of primitives
+ * \param nb_prims [in] number of indices
+ * \param global_box [in] global AABB enclosing the set of input primitives
+ * \return TRUE if the node should be subdivised
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ virtual BOOL ValidateSubdivision(const udword* primitives, udword nb_prims, const AABB& global_box)
+ {
+ // Check the user-defined limit
+ if(nb_prims<=mSettings.mLimit) return FALSE;
+
+ return TRUE;
+ }
+
+ BuildSettings mSettings; //!< Splitting rules & split limit [Opcode 1.3]
+ udword mNbPrimitives; //!< Total number of primitives.
+ void* mNodeBase; //!< Address of node pool [Opcode 1.3]
+ // Stats
+ inline_ void SetCount(udword nb) { mCount=nb; }
+ inline_ void IncreaseCount(udword nb) { mCount+=nb; }
+ inline_ udword GetCount() const { return mCount; }
+ inline_ void SetNbInvalidSplits(udword nb) { mNbInvalidSplits=nb; }
+ inline_ void IncreaseNbInvalidSplits() { mNbInvalidSplits++; }
+ inline_ udword GetNbInvalidSplits() const { return mNbInvalidSplits; }
+
+ private:
+ udword mCount; //!< Stats: number of nodes created
+ udword mNbInvalidSplits; //!< Stats: number of invalid splits
+ };
+
+ class OPCODE_API AABBTreeOfVerticesBuilder : public AABBTreeBuilder
+ {
+ public:
+ //! Constructor
+ AABBTreeOfVerticesBuilder() : mVertexArray(null) {}
+ //! Destructor
+ virtual ~AABBTreeOfVerticesBuilder() {}
+
+ override(AABBTreeBuilder) bool ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const;
+ override(AABBTreeBuilder) float GetSplittingValue(udword index, udword axis) const;
+ override(AABBTreeBuilder) float GetSplittingValue(const udword* primitives, udword nb_prims, const AABB& global_box, udword axis) const;
+
+ const IcePoint* mVertexArray; //!< Shortcut to an app-controlled array of vertices.
+ };
+
+ class OPCODE_API AABBTreeOfAABBsBuilder : public AABBTreeBuilder
+ {
+ public:
+ //! Constructor
+ AABBTreeOfAABBsBuilder() : mAABBArray(null) {}
+ //! Destructor
+ virtual ~AABBTreeOfAABBsBuilder() {}
+
+ override(AABBTreeBuilder) bool ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const;
+ override(AABBTreeBuilder) float GetSplittingValue(udword index, udword axis) const;
+
+ const AABB* mAABBArray; //!< Shortcut to an app-controlled array of AABBs.
+ };
+
+ class OPCODE_API AABBTreeOfTrianglesBuilder : public AABBTreeBuilder
+ {
+ public:
+ //! Constructor
+ AABBTreeOfTrianglesBuilder() : mIMesh(null) {}
+ //! Destructor
+ virtual ~AABBTreeOfTrianglesBuilder() {}
+
+ override(AABBTreeBuilder) bool ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const;
+ override(AABBTreeBuilder) float GetSplittingValue(udword index, udword axis) const;
+ override(AABBTreeBuilder) float GetSplittingValue(const udword* primitives, udword nb_prims, const AABB& global_box, udword axis) const;
+
+ const MeshInterface* mIMesh; //!< Shortcut to an app-controlled mesh interface
+ };
+
+#endif // __OPC_TREEBUILDERS_H__
diff --git a/Opcode/OpcodeLib/OPC_TreeCollider.cpp b/Opcode/OpcodeLib/OPC_TreeCollider.cpp
new file mode 100644
index 0000000..f8c0ca3
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_TreeCollider.cpp
@@ -0,0 +1,943 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a tree collider.
+ * \file OPC_TreeCollider.cpp
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains an AABB tree collider.
+ * This class performs a collision test between two AABB trees.
+ *
+ * \class AABBTreeCollider
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date March, 20, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+#include "OPC_BoxBoxOverlap.h"
+#include "OPC_TriBoxOverlap.h"
+#include "OPC_TriTriOverlap.h"
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBTreeCollider::AABBTreeCollider() :
+ mNbBVBVTests (0),
+ mNbPrimPrimTests (0),
+ mNbBVPrimTests (0),
+ mFullBoxBoxTest (true),
+ mFullPrimBoxTest (true),
+ mIMesh0 (null),
+ mIMesh1 (null)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+AABBTreeCollider::~AABBTreeCollider()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Validates current settings. You should call this method after all the settings and callbacks have been defined.
+ * \return null if everything is ok, else a string describing the problem
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const char* AABBTreeCollider::ValidateSettings()
+{
+ if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!";
+ return null;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Generic collision query for generic OPCODE models. After the call, access the results with:
+ * - GetContactStatus()
+ * - GetNbPairs()
+ * - GetPairs()
+ *
+ * \param cache [in] collision cache for model pointers and a colliding pair of primitives
+ * \param world0 [in] world matrix for first object
+ * \param world1 [in] world matrix for second object
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeCollider::Collide(BVTCache& cache, const Matrix4x4* world0, const Matrix4x4* world1)
+{
+ // Checkings
+ if(!cache.Model0 || !cache.Model1) return false;
+ if(cache.Model0->HasLeafNodes()!=cache.Model1->HasLeafNodes()) return false;
+ if(cache.Model0->IsQuantized()!=cache.Model1->IsQuantized()) return false;
+
+ /*
+
+ Rules:
+ - perform hull test
+ - when hulls collide, disable hull test
+ - if meshes overlap, reset countdown
+ - if countdown reaches 0, enable hull test
+
+ */
+
+#ifdef __MESHMERIZER_H__
+ // Handle hulls
+ if(cache.HullTest)
+ {
+ if(cache.Model0->GetHull() && cache.Model1->GetHull())
+ {
+ struct Local
+ {
+ static IcePoint* SVCallback(const IcePoint& sv, udword& previndex, udword user_data)
+ {
+ CollisionHull* Hull = (CollisionHull*)user_data;
+ previndex = Hull->ComputeSupportingVertex(sv, previndex);
+ return (IcePoint*)&Hull->GetVerts()[previndex];
+ }
+ };
+
+ bool Collide;
+
+ if(0)
+ {
+ static GJKEngine GJK;
+ static bool GJKInitDone=false;
+ if(!GJKInitDone)
+ {
+ GJK.Enable(GJK_BACKUP_PROCEDURE);
+ GJK.Enable(GJK_DEGENERATE);
+ GJK.Enable(GJK_HILLCLIMBING);
+ GJKInitDone = true;
+ }
+ GJK.SetCallbackObj0(Local::SVCallback);
+ GJK.SetCallbackObj1(Local::SVCallback);
+ GJK.SetUserData0(udword(cache.Model0->GetHull()));
+ GJK.SetUserData1(udword(cache.Model1->GetHull()));
+ Collide = GJK.Collide(*world0, *world1, &cache.SepVector);
+ }
+ else
+ {
+ static SVEngine SVE;
+ SVE.SetCallbackObj0(Local::SVCallback);
+ SVE.SetCallbackObj1(Local::SVCallback);
+ SVE.SetUserData0(udword(cache.Model0->GetHull()));
+ SVE.SetUserData1(udword(cache.Model1->GetHull()));
+ Collide = SVE.Collide(*world0, *world1, &cache.SepVector);
+ }
+
+ if(!Collide)
+ {
+ // Reset stats & contact status
+ mFlags &= ~OPC_CONTACT;
+ mNbBVBVTests = 0;
+ mNbPrimPrimTests = 0;
+ mNbBVPrimTests = 0;
+ mPairs.Reset();
+ return true;
+ }
+ }
+ }
+
+ // Here, hulls collide
+ cache.HullTest = false;
+#endif // __MESHMERIZER_H__
+
+ // Checkings
+ if(!Setup(cache.Model0->GetMeshInterface(), cache.Model1->GetMeshInterface())) return false;
+
+ // Simple double-dispatch
+ bool Status;
+ if(!cache.Model0->HasLeafNodes())
+ {
+ if(cache.Model0->IsQuantized())
+ {
+ const AABBQuantizedNoLeafTree* T0 = (const AABBQuantizedNoLeafTree*)cache.Model0->GetTree();
+ const AABBQuantizedNoLeafTree* T1 = (const AABBQuantizedNoLeafTree*)cache.Model1->GetTree();
+ Status = Collide(T0, T1, world0, world1, &cache);
+ }
+ else
+ {
+ const AABBNoLeafTree* T0 = (const AABBNoLeafTree*)cache.Model0->GetTree();
+ const AABBNoLeafTree* T1 = (const AABBNoLeafTree*)cache.Model1->GetTree();
+ Status = Collide(T0, T1, world0, world1, &cache);
+ }
+ }
+ else
+ {
+ if(cache.Model0->IsQuantized())
+ {
+ const AABBQuantizedTree* T0 = (const AABBQuantizedTree*)cache.Model0->GetTree();
+ const AABBQuantizedTree* T1 = (const AABBQuantizedTree*)cache.Model1->GetTree();
+ Status = Collide(T0, T1, world0, world1, &cache);
+ }
+ else
+ {
+ const AABBCollisionTree* T0 = (const AABBCollisionTree*)cache.Model0->GetTree();
+ const AABBCollisionTree* T1 = (const AABBCollisionTree*)cache.Model1->GetTree();
+ Status = Collide(T0, T1, world0, world1, &cache);
+ }
+ }
+
+#ifdef __MESHMERIZER_H__
+ if(Status)
+ {
+ // Reset counter as long as overlap occurs
+ if(GetContactStatus()) cache.ResetCountDown();
+
+ // Enable hull test again when counter reaches zero
+ cache.CountDown--;
+ if(!cache.CountDown)
+ {
+ cache.ResetCountDown();
+ cache.HullTest = true;
+ }
+ }
+#endif
+ return Status;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Initializes a collision query :
+ * - reset stats & contact status
+ * - setup matrices
+ *
+ * \param world0 [in] world matrix for first object
+ * \param world1 [in] world matrix for second object
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::InitQuery(const Matrix4x4* world0, const Matrix4x4* world1)
+{
+ // Reset stats & contact status
+ Collider::InitQuery();
+ mNbBVBVTests = 0;
+ mNbPrimPrimTests = 0;
+ mNbBVPrimTests = 0;
+ mPairs.Reset();
+
+ // Setup matrices
+ Matrix4x4 InvWorld0, InvWorld1;
+ if(world0) InvertPRMatrix(InvWorld0, *world0);
+ else InvWorld0.Identity();
+
+ if(world1) InvertPRMatrix(InvWorld1, *world1);
+ else InvWorld1.Identity();
+
+ Matrix4x4 World0to1 = world0 ? (*world0 * InvWorld1) : InvWorld1;
+ Matrix4x4 World1to0 = world1 ? (*world1 * InvWorld0) : InvWorld0;
+
+ mR0to1 = World0to1; World0to1.GetTrans(mT0to1);
+ mR1to0 = World1to0; World1to0.GetTrans(mT1to0);
+
+ // Precompute absolute 1-to-0 rotation matrix
+ for(udword i=0;i<3;i++)
+ {
+ for(udword j=0;j<3;j++)
+ {
+ // Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID)
+ mAR.m[i][j] = 1e-6f + fabsf(mR1to0.m[i][j]);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Takes advantage of temporal coherence.
+ * \param cache [in] cache for a pair of previously colliding primitives
+ * \return true if we can return immediately
+ * \warning only works for "First Contact" mode
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeCollider::CheckTemporalCoherence(Pair* cache)
+{
+ // Checkings
+ if(!cache) return false;
+
+ // Test previously colliding primitives first
+ if(TemporalCoherenceEnabled() && FirstContactEnabled())
+ {
+ PrimTest(cache->id0, cache->id1);
+ if(GetContactStatus()) return true;
+ }
+ return false;
+}
+
+#define UPDATE_CACHE \
+ if(cache && GetContactStatus()) \
+ { \
+ cache->id0 = mPairs.GetEntry(0); \
+ cache->id1 = mPairs.GetEntry(1); \
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Collision query for normal AABB trees.
+ * \param tree0 [in] AABB tree from first object
+ * \param tree1 [in] AABB tree from second object
+ * \param world0 [in] world matrix for first object
+ * \param world1 [in] world matrix for second object
+ * \param cache [in/out] cache for a pair of previously colliding primitives
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeCollider::Collide(const AABBCollisionTree* tree0, const AABBCollisionTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache)
+{
+ // Init collision query
+ InitQuery(world0, world1);
+
+ // Check previous state
+ if(CheckTemporalCoherence(cache)) return true;
+
+ // Perform collision query
+ _Collide(tree0->GetNodes(), tree1->GetNodes());
+
+ UPDATE_CACHE
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Collision query for no-leaf AABB trees.
+ * \param tree0 [in] AABB tree from first object
+ * \param tree1 [in] AABB tree from second object
+ * \param world0 [in] world matrix for first object
+ * \param world1 [in] world matrix for second object
+ * \param cache [in/out] cache for a pair of previously colliding primitives
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeCollider::Collide(const AABBNoLeafTree* tree0, const AABBNoLeafTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache)
+{
+ // Init collision query
+ InitQuery(world0, world1);
+
+ // Check previous state
+ if(CheckTemporalCoherence(cache)) return true;
+
+ // Perform collision query
+ _Collide(tree0->GetNodes(), tree1->GetNodes());
+
+ UPDATE_CACHE
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Collision query for quantized AABB trees.
+ * \param tree0 [in] AABB tree from first object
+ * \param tree1 [in] AABB tree from second object
+ * \param world0 [in] world matrix for first object
+ * \param world1 [in] world matrix for second object
+ * \param cache [in/out] cache for a pair of previously colliding primitives
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeCollider::Collide(const AABBQuantizedTree* tree0, const AABBQuantizedTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache)
+{
+ // Init collision query
+ InitQuery(world0, world1);
+
+ // Check previous state
+ if(CheckTemporalCoherence(cache)) return true;
+
+ // Setup dequantization coeffs
+ mCenterCoeff0 = tree0->mCenterCoeff;
+ mExtentsCoeff0 = tree0->mExtentsCoeff;
+ mCenterCoeff1 = tree1->mCenterCoeff;
+ mExtentsCoeff1 = tree1->mExtentsCoeff;
+
+ // Dequantize box A
+ const AABBQuantizedNode* N0 = tree0->GetNodes();
+ const IcePoint a(float(N0->mAABB.mExtents[0]) * mExtentsCoeff0.x, float(N0->mAABB.mExtents[1]) * mExtentsCoeff0.y, float(N0->mAABB.mExtents[2]) * mExtentsCoeff0.z);
+ const IcePoint Pa(float(N0->mAABB.mCenter[0]) * mCenterCoeff0.x, float(N0->mAABB.mCenter[1]) * mCenterCoeff0.y, float(N0->mAABB.mCenter[2]) * mCenterCoeff0.z);
+ // Dequantize box B
+ const AABBQuantizedNode* N1 = tree1->GetNodes();
+ const IcePoint b(float(N1->mAABB.mExtents[0]) * mExtentsCoeff1.x, float(N1->mAABB.mExtents[1]) * mExtentsCoeff1.y, float(N1->mAABB.mExtents[2]) * mExtentsCoeff1.z);
+ const IcePoint Pb(float(N1->mAABB.mCenter[0]) * mCenterCoeff1.x, float(N1->mAABB.mCenter[1]) * mCenterCoeff1.y, float(N1->mAABB.mCenter[2]) * mCenterCoeff1.z);
+
+ // Perform collision query
+ _Collide(N0, N1, a, Pa, b, Pb);
+
+ UPDATE_CACHE
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Collision query for quantized no-leaf AABB trees.
+ * \param tree0 [in] AABB tree from first object
+ * \param tree1 [in] AABB tree from second object
+ * \param world0 [in] world matrix for first object
+ * \param world1 [in] world matrix for second object
+ * \param cache [in/out] cache for a pair of previously colliding primitives
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool AABBTreeCollider::Collide(const AABBQuantizedNoLeafTree* tree0, const AABBQuantizedNoLeafTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache)
+{
+ // Init collision query
+ InitQuery(world0, world1);
+
+ // Check previous state
+ if(CheckTemporalCoherence(cache)) return true;
+
+ // Setup dequantization coeffs
+ mCenterCoeff0 = tree0->mCenterCoeff;
+ mExtentsCoeff0 = tree0->mExtentsCoeff;
+ mCenterCoeff1 = tree1->mCenterCoeff;
+ mExtentsCoeff1 = tree1->mExtentsCoeff;
+
+ // Perform collision query
+ _Collide(tree0->GetNodes(), tree1->GetNodes());
+
+ UPDATE_CACHE
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Standard trees
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// The normal AABB tree can use 2 different descent rules (with different performances)
+//#define ORIGINAL_CODE //!< UNC-like descent rules
+#define ALTERNATIVE_CODE //!< Alternative descent rules
+
+#ifdef ORIGINAL_CODE
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees.
+ * \param b0 [in] collision node from first tree
+ * \param b1 [in] collision node from second tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_Collide(const AABBCollisionNode* b0, const AABBCollisionNode* b1)
+{
+ // Perform BV-BV overlap test
+ if(!BoxBoxOverlap(b0->mAABB.mExtents, b0->mAABB.mCenter, b1->mAABB.mExtents, b1->mAABB.mCenter)) return;
+
+ if(b0->IsLeaf() && b1->IsLeaf()) { PrimTest(b0->GetPrimitive(), b1->GetPrimitive()); return; }
+
+ if(b1->IsLeaf() || (!b0->IsLeaf() && (b0->GetSize() > b1->GetSize())))
+ {
+ _Collide(b0->GetNeg(), b1);
+ if(ContactFound()) return;
+ _Collide(b0->GetPos(), b1);
+ }
+ else
+ {
+ _Collide(b0, b1->GetNeg());
+ if(ContactFound()) return;
+ _Collide(b0, b1->GetPos());
+ }
+}
+#endif
+
+#ifdef ALTERNATIVE_CODE
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for normal AABB trees.
+ * \param b0 [in] collision node from first tree
+ * \param b1 [in] collision node from second tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_Collide(const AABBCollisionNode* b0, const AABBCollisionNode* b1)
+{
+ // Perform BV-BV overlap test
+ if(!BoxBoxOverlap(b0->mAABB.mExtents, b0->mAABB.mCenter, b1->mAABB.mExtents, b1->mAABB.mCenter))
+ {
+ return;
+ }
+
+ if(b0->IsLeaf())
+ {
+ if(b1->IsLeaf())
+ {
+ PrimTest(b0->GetPrimitive(), b1->GetPrimitive());
+ }
+ else
+ {
+ _Collide(b0, b1->GetNeg());
+ if(ContactFound()) return;
+ _Collide(b0, b1->GetPos());
+ }
+ }
+ else if(b1->IsLeaf())
+ {
+ _Collide(b0->GetNeg(), b1);
+ if(ContactFound()) return;
+ _Collide(b0->GetPos(), b1);
+ }
+ else
+ {
+ _Collide(b0->GetNeg(), b1->GetNeg());
+ if(ContactFound()) return;
+ _Collide(b0->GetNeg(), b1->GetPos());
+ if(ContactFound()) return;
+ _Collide(b0->GetPos(), b1->GetNeg());
+ if(ContactFound()) return;
+ _Collide(b0->GetPos(), b1->GetPos());
+ }
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// No-leaf trees
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Leaf-leaf test for two primitive indices.
+ * \param id0 [in] index from first leaf-triangle
+ * \param id1 [in] index from second leaf-triangle
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::PrimTest(udword id0, udword id1)
+{
+ // Request vertices from the app
+ VertexPointers VP0;
+ VertexPointers VP1;
+ mIMesh0->GetTriangle(VP0, id0);
+ mIMesh1->GetTriangle(VP1, id1);
+
+ // Transform from space 1 to space 0
+ IcePoint u0,u1,u2;
+ TransformPoint(u0, *VP1.Vertex[0], mR1to0, mT1to0);
+ TransformPoint(u1, *VP1.Vertex[1], mR1to0, mT1to0);
+ TransformPoint(u2, *VP1.Vertex[2], mR1to0, mT1to0);
+
+ // Perform triangle-triangle overlap test
+ if(TriTriOverlap(*VP0.Vertex[0], *VP0.Vertex[1], *VP0.Vertex[2], u0, u1, u2))
+ {
+ // Keep track of colliding pairs
+ mPairs.Add(id0).Add(id1);
+ // Set contact status
+ mFlags |= OPC_CONTACT;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Leaf-leaf test for a previously fetched triangle from tree A (in B's space) and a new leaf from B.
+ * \param id1 [in] leaf-triangle index from tree B
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ void AABBTreeCollider::PrimTestTriIndex(udword id1)
+{
+ // Request vertices from the app
+ VertexPointers VP;
+ mIMesh1->GetTriangle(VP, id1);
+
+ // Perform triangle-triangle overlap test
+ if(TriTriOverlap(mLeafVerts[0], mLeafVerts[1], mLeafVerts[2], *VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2]))
+ {
+ // Keep track of colliding pairs
+ mPairs.Add(mLeafIndex).Add(id1);
+ // Set contact status
+ mFlags |= OPC_CONTACT;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Leaf-leaf test for a previously fetched triangle from tree B (in A's space) and a new leaf from A.
+ * \param id0 [in] leaf-triangle index from tree A
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ void AABBTreeCollider::PrimTestIndexTri(udword id0)
+{
+ // Request vertices from the app
+ VertexPointers VP;
+ mIMesh0->GetTriangle(VP, id0);
+
+ // Perform triangle-triangle overlap test
+ if(TriTriOverlap(mLeafVerts[0], mLeafVerts[1], mLeafVerts[2], *VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2]))
+ {
+ // Keep track of colliding pairs
+ mPairs.Add(id0).Add(mLeafIndex);
+ // Set contact status
+ mFlags |= OPC_CONTACT;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision of a leaf node from A and a branch from B.
+ * \param b [in] collision node from second tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_CollideTriBox(const AABBNoLeafNode* b)
+{
+ // Perform triangle-box overlap test
+ if(!TriBoxOverlap(b->mAABB.mCenter, b->mAABB.mExtents)) return;
+
+ // Keep same triangle, deal with first child
+ if(b->HasPosLeaf()) PrimTestTriIndex(b->GetPosPrimitive());
+ else _CollideTriBox(b->GetPos());
+
+ if(ContactFound()) return;
+
+ // Keep same triangle, deal with second child
+ if(b->HasNegLeaf()) PrimTestTriIndex(b->GetNegPrimitive());
+ else _CollideTriBox(b->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision of a leaf node from B and a branch from A.
+ * \param b [in] collision node from first tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_CollideBoxTri(const AABBNoLeafNode* b)
+{
+ // Perform triangle-box overlap test
+ if(!TriBoxOverlap(b->mAABB.mCenter, b->mAABB.mExtents)) return;
+
+ // Keep same triangle, deal with first child
+ if(b->HasPosLeaf()) PrimTestIndexTri(b->GetPosPrimitive());
+ else _CollideBoxTri(b->GetPos());
+
+ if(ContactFound()) return;
+
+ // Keep same triangle, deal with second child
+ if(b->HasNegLeaf()) PrimTestIndexTri(b->GetNegPrimitive());
+ else _CollideBoxTri(b->GetNeg());
+}
+
+//! Request triangle vertices from the app and transform them
+#define FETCH_LEAF(prim_index, imesh, rot, trans) \
+ mLeafIndex = prim_index; \
+ /* Request vertices from the app */ \
+ VertexPointers VP; imesh->GetTriangle(VP, mLeafIndex); \
+ /* Transform them in a common space */ \
+ TransformPoint(mLeafVerts[0], *VP.Vertex[0], rot, trans); \
+ TransformPoint(mLeafVerts[1], *VP.Vertex[1], rot, trans); \
+ TransformPoint(mLeafVerts[2], *VP.Vertex[2], rot, trans);
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for no-leaf AABB trees.
+ * \param a [in] collision node from first tree
+ * \param b [in] collision node from second tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_Collide(const AABBNoLeafNode* a, const AABBNoLeafNode* b)
+{
+ // Perform BV-BV overlap test
+ if(!BoxBoxOverlap(a->mAABB.mExtents, a->mAABB.mCenter, b->mAABB.mExtents, b->mAABB.mCenter)) return;
+
+ // Catch leaf status
+ BOOL BHasPosLeaf = b->HasPosLeaf();
+ BOOL BHasNegLeaf = b->HasNegLeaf();
+
+ if(a->HasPosLeaf())
+ {
+ FETCH_LEAF(a->GetPosPrimitive(), mIMesh0, mR0to1, mT0to1)
+
+ if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive());
+ else _CollideTriBox(b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive());
+ else _CollideTriBox(b->GetNeg());
+ }
+ else
+ {
+ if(BHasPosLeaf)
+ {
+ FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0)
+
+ _CollideBoxTri(a->GetPos());
+ }
+ else _Collide(a->GetPos(), b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(BHasNegLeaf)
+ {
+ FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0)
+
+ _CollideBoxTri(a->GetPos());
+ }
+ else _Collide(a->GetPos(), b->GetNeg());
+ }
+
+ if(ContactFound()) return;
+
+ if(a->HasNegLeaf())
+ {
+ FETCH_LEAF(a->GetNegPrimitive(), mIMesh0, mR0to1, mT0to1)
+
+ if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive());
+ else _CollideTriBox(b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive());
+ else _CollideTriBox(b->GetNeg());
+ }
+ else
+ {
+ if(BHasPosLeaf)
+ {
+ // ### That leaf has possibly already been fetched
+ FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0)
+
+ _CollideBoxTri(a->GetNeg());
+ }
+ else _Collide(a->GetNeg(), b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(BHasNegLeaf)
+ {
+ // ### That leaf has possibly already been fetched
+ FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0)
+
+ _CollideBoxTri(a->GetNeg());
+ }
+ else _Collide(a->GetNeg(), b->GetNeg());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Quantized trees
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized AABB trees.
+ * \param b0 [in] collision node from first tree
+ * \param b1 [in] collision node from second tree
+ * \param a [in] extent from box A
+ * \param Pa [in] center from box A
+ * \param b [in] extent from box B
+ * \param Pb [in] center from box B
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_Collide(const AABBQuantizedNode* b0, const AABBQuantizedNode* b1, const IcePoint& a, const IcePoint& Pa, const IcePoint& b, const IcePoint& Pb)
+{
+ // Perform BV-BV overlap test
+ if(!BoxBoxOverlap(a, Pa, b, Pb)) return;
+
+ if(b0->IsLeaf() && b1->IsLeaf()) { PrimTest(b0->GetPrimitive(), b1->GetPrimitive()); return; }
+
+ if(b1->IsLeaf() || (!b0->IsLeaf() && (b0->GetSize() > b1->GetSize())))
+ {
+ // Dequantize box
+ const QuantizedAABB* Box = &b0->GetNeg()->mAABB;
+ const IcePoint negPa(float(Box->mCenter[0]) * mCenterCoeff0.x, float(Box->mCenter[1]) * mCenterCoeff0.y, float(Box->mCenter[2]) * mCenterCoeff0.z);
+ const IcePoint nega(float(Box->mExtents[0]) * mExtentsCoeff0.x, float(Box->mExtents[1]) * mExtentsCoeff0.y, float(Box->mExtents[2]) * mExtentsCoeff0.z);
+ _Collide(b0->GetNeg(), b1, nega, negPa, b, Pb);
+
+ if(ContactFound()) return;
+
+ // Dequantize box
+ Box = &b0->GetPos()->mAABB;
+ const IcePoint posPa(float(Box->mCenter[0]) * mCenterCoeff0.x, float(Box->mCenter[1]) * mCenterCoeff0.y, float(Box->mCenter[2]) * mCenterCoeff0.z);
+ const IcePoint posa(float(Box->mExtents[0]) * mExtentsCoeff0.x, float(Box->mExtents[1]) * mExtentsCoeff0.y, float(Box->mExtents[2]) * mExtentsCoeff0.z);
+ _Collide(b0->GetPos(), b1, posa, posPa, b, Pb);
+ }
+ else
+ {
+ // Dequantize box
+ const QuantizedAABB* Box = &b1->GetNeg()->mAABB;
+ const IcePoint negPb(float(Box->mCenter[0]) * mCenterCoeff1.x, float(Box->mCenter[1]) * mCenterCoeff1.y, float(Box->mCenter[2]) * mCenterCoeff1.z);
+ const IcePoint negb(float(Box->mExtents[0]) * mExtentsCoeff1.x, float(Box->mExtents[1]) * mExtentsCoeff1.y, float(Box->mExtents[2]) * mExtentsCoeff1.z);
+ _Collide(b0, b1->GetNeg(), a, Pa, negb, negPb);
+
+ if(ContactFound()) return;
+
+ // Dequantize box
+ Box = &b1->GetPos()->mAABB;
+ const IcePoint posPb(float(Box->mCenter[0]) * mCenterCoeff1.x, float(Box->mCenter[1]) * mCenterCoeff1.y, float(Box->mCenter[2]) * mCenterCoeff1.z);
+ const IcePoint posb(float(Box->mExtents[0]) * mExtentsCoeff1.x, float(Box->mExtents[1]) * mExtentsCoeff1.y, float(Box->mExtents[2]) * mExtentsCoeff1.z);
+ _Collide(b0, b1->GetPos(), a, Pa, posb, posPb);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Quantized no-leaf trees
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision of a leaf node from A and a quantized branch from B.
+ * \param leaf [in] leaf triangle from first tree
+ * \param b [in] collision node from second tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_CollideTriBox(const AABBQuantizedNoLeafNode* b)
+{
+ // Dequantize box
+ const QuantizedAABB* bb = &b->mAABB;
+ const IcePoint Pb(float(bb->mCenter[0]) * mCenterCoeff1.x, float(bb->mCenter[1]) * mCenterCoeff1.y, float(bb->mCenter[2]) * mCenterCoeff1.z);
+ const IcePoint eb(float(bb->mExtents[0]) * mExtentsCoeff1.x, float(bb->mExtents[1]) * mExtentsCoeff1.y, float(bb->mExtents[2]) * mExtentsCoeff1.z);
+
+ // Perform triangle-box overlap test
+ if(!TriBoxOverlap(Pb, eb)) return;
+
+ if(b->HasPosLeaf()) PrimTestTriIndex(b->GetPosPrimitive());
+ else _CollideTriBox(b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(b->HasNegLeaf()) PrimTestTriIndex(b->GetNegPrimitive());
+ else _CollideTriBox(b->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision of a leaf node from B and a quantized branch from A.
+ * \param b [in] collision node from first tree
+ * \param leaf [in] leaf triangle from second tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_CollideBoxTri(const AABBQuantizedNoLeafNode* b)
+{
+ // Dequantize box
+ const QuantizedAABB* bb = &b->mAABB;
+ const IcePoint Pa(float(bb->mCenter[0]) * mCenterCoeff0.x, float(bb->mCenter[1]) * mCenterCoeff0.y, float(bb->mCenter[2]) * mCenterCoeff0.z);
+ const IcePoint ea(float(bb->mExtents[0]) * mExtentsCoeff0.x, float(bb->mExtents[1]) * mExtentsCoeff0.y, float(bb->mExtents[2]) * mExtentsCoeff0.z);
+
+ // Perform triangle-box overlap test
+ if(!TriBoxOverlap(Pa, ea)) return;
+
+ if(b->HasPosLeaf()) PrimTestIndexTri(b->GetPosPrimitive());
+ else _CollideBoxTri(b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(b->HasNegLeaf()) PrimTestIndexTri(b->GetNegPrimitive());
+ else _CollideBoxTri(b->GetNeg());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Recursive collision query for quantized no-leaf AABB trees.
+ * \param a [in] collision node from first tree
+ * \param b [in] collision node from second tree
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void AABBTreeCollider::_Collide(const AABBQuantizedNoLeafNode* a, const AABBQuantizedNoLeafNode* b)
+{
+ // Dequantize box A
+ const QuantizedAABB* ab = &a->mAABB;
+ const IcePoint Pa(float(ab->mCenter[0]) * mCenterCoeff0.x, float(ab->mCenter[1]) * mCenterCoeff0.y, float(ab->mCenter[2]) * mCenterCoeff0.z);
+ const IcePoint ea(float(ab->mExtents[0]) * mExtentsCoeff0.x, float(ab->mExtents[1]) * mExtentsCoeff0.y, float(ab->mExtents[2]) * mExtentsCoeff0.z);
+ // Dequantize box B
+ const QuantizedAABB* bb = &b->mAABB;
+ const IcePoint Pb(float(bb->mCenter[0]) * mCenterCoeff1.x, float(bb->mCenter[1]) * mCenterCoeff1.y, float(bb->mCenter[2]) * mCenterCoeff1.z);
+ const IcePoint eb(float(bb->mExtents[0]) * mExtentsCoeff1.x, float(bb->mExtents[1]) * mExtentsCoeff1.y, float(bb->mExtents[2]) * mExtentsCoeff1.z);
+
+ // Perform BV-BV overlap test
+ if(!BoxBoxOverlap(ea, Pa, eb, Pb)) return;
+
+ // Catch leaf status
+ BOOL BHasPosLeaf = b->HasPosLeaf();
+ BOOL BHasNegLeaf = b->HasNegLeaf();
+
+ if(a->HasPosLeaf())
+ {
+ FETCH_LEAF(a->GetPosPrimitive(), mIMesh0, mR0to1, mT0to1)
+
+ if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive());
+ else _CollideTriBox(b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive());
+ else _CollideTriBox(b->GetNeg());
+ }
+ else
+ {
+ if(BHasPosLeaf)
+ {
+ FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0)
+
+ _CollideBoxTri(a->GetPos());
+ }
+ else _Collide(a->GetPos(), b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(BHasNegLeaf)
+ {
+ FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0)
+
+ _CollideBoxTri(a->GetPos());
+ }
+ else _Collide(a->GetPos(), b->GetNeg());
+ }
+
+ if(ContactFound()) return;
+
+ if(a->HasNegLeaf())
+ {
+ FETCH_LEAF(a->GetNegPrimitive(), mIMesh0, mR0to1, mT0to1)
+
+ if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive());
+ else _CollideTriBox(b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive());
+ else _CollideTriBox(b->GetNeg());
+ }
+ else
+ {
+ if(BHasPosLeaf)
+ {
+ // ### That leaf has possibly already been fetched
+ FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0)
+
+ _CollideBoxTri(a->GetNeg());
+ }
+ else _Collide(a->GetNeg(), b->GetPos());
+
+ if(ContactFound()) return;
+
+ if(BHasNegLeaf)
+ {
+ // ### That leaf has possibly already been fetched
+ FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0)
+
+ _CollideBoxTri(a->GetNeg());
+ }
+ else _Collide(a->GetNeg(), b->GetNeg());
+ }
+}
diff --git a/Opcode/OpcodeLib/OPC_TreeCollider.h b/Opcode/OpcodeLib/OPC_TreeCollider.h
new file mode 100644
index 0000000..ce58ff8
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_TreeCollider.h
@@ -0,0 +1,244 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains code for a tree collider.
+ * \file OPC_TreeCollider.h
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_TREECOLLIDER_H__
+#define __OPC_TREECOLLIDER_H__
+
+ //! This structure holds cached information used by the algorithm.
+ //! Two model pointers and two colliding primitives are cached. Model pointers are assigned
+ //! to their respective meshes, and the pair of colliding primitives is used for temporal
+ //! coherence. That is, in case temporal coherence is enabled, those two primitives are
+ //! tested for overlap before everything else. If they still collide, we're done before
+ //! even entering the recursive collision code.
+ struct OPCODE_API BVTCache : Pair
+ {
+ //! Constructor
+ inline_ BVTCache()
+ {
+ ResetCache();
+ ResetCountDown();
+ }
+
+ void ResetCache()
+ {
+ Model0 = null;
+ Model1 = null;
+ id0 = 0;
+ id1 = 1;
+#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE !
+ HullTest = true;
+ SepVector.pid = 0;
+ SepVector.qid = 0;
+ SepVector.SV = IcePoint(1.0f, 0.0f, 0.0f);
+#endif // __MESHMERIZER_H__
+ }
+
+ inline_ void ResetCountDown()
+ {
+#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE !
+ CountDown = 50;
+#endif // __MESHMERIZER_H__
+ }
+
+ const Model* Model0; //!< Model for first object
+ const Model* Model1; //!< Model for second object
+
+#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE !
+ SVCache SepVector;
+ udword CountDown;
+ bool HullTest;
+#endif // __MESHMERIZER_H__
+ };
+
+ class OPCODE_API AABBTreeCollider : public Collider
+ {
+ public:
+ // Constructor / Destructor
+ AABBTreeCollider();
+ virtual ~AABBTreeCollider();
+ // Generic collision query
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Generic collision query for generic OPCODE models. After the call, access the results with:
+ * - GetContactStatus()
+ * - GetNbPairs()
+ * - GetPairs()
+ *
+ * \param cache [in] collision cache for model pointers and a colliding pair of primitives
+ * \param world0 [in] world matrix for first object, or null
+ * \param world1 [in] world matrix for second object, or null
+ * \return true if success
+ * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ bool Collide(BVTCache& cache, const Matrix4x4* world0=null, const Matrix4x4* world1=null);
+
+ // Collision queries
+ bool Collide(const AABBCollisionTree* tree0, const AABBCollisionTree* tree1, const Matrix4x4* world0=null, const Matrix4x4* world1=null, Pair* cache=null);
+ bool Collide(const AABBNoLeafTree* tree0, const AABBNoLeafTree* tree1, const Matrix4x4* world0=null, const Matrix4x4* world1=null, Pair* cache=null);
+ bool Collide(const AABBQuantizedTree* tree0, const AABBQuantizedTree* tree1, const Matrix4x4* world0=null, const Matrix4x4* world1=null, Pair* cache=null);
+ bool Collide(const AABBQuantizedNoLeafTree* tree0, const AABBQuantizedNoLeafTree* tree1, const Matrix4x4* world0=null, const Matrix4x4* world1=null, Pair* cache=null);
+ // Settings
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Settings: selects between full box-box tests or "SAT-lite" tests (where Class III axes are discarded)
+ * \param flag [in] true for full tests, false for coarse tests
+ * \see SetFullPrimBoxTest(bool flag)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetFullBoxBoxTest(bool flag) { mFullBoxBoxTest = flag; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Settings: selects between full triangle-box tests or "SAT-lite" tests (where Class III axes are discarded)
+ * \param flag [in] true for full tests, false for coarse tests
+ * \see SetFullBoxBoxTest(bool flag)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ void SetFullPrimBoxTest(bool flag) { mFullPrimBoxTest = flag; }
+
+ // Stats
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Stats: gets the number of BV-BV overlap tests after a collision query.
+ * \see GetNbPrimPrimTests()
+ * \see GetNbBVPrimTests()
+ * \return the number of BV-BV tests performed during last query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbBVBVTests() const { return mNbBVBVTests; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Stats: gets the number of Triangle-Triangle overlap tests after a collision query.
+ * \see GetNbBVBVTests()
+ * \see GetNbBVPrimTests()
+ * \return the number of Triangle-Triangle tests performed during last query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbPrimPrimTests() const { return mNbPrimPrimTests; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Stats: gets the number of BV-Triangle overlap tests after a collision query.
+ * \see GetNbBVBVTests()
+ * \see GetNbPrimPrimTests()
+ * \return the number of BV-Triangle tests performed during last query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbBVPrimTests() const { return mNbBVPrimTests; }
+
+ // Data access
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the number of contacts after a collision query.
+ * \see GetContactStatus()
+ * \see GetPairs()
+ * \return the number of contacts / colliding pairs.
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbPairs() const { return mPairs.GetNbEntries()>>1; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the pairs of colliding triangles after a collision query.
+ * \see GetContactStatus()
+ * \see GetNbPairs()
+ * \return the list of colliding pairs (triangle indices)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ const Pair* GetPairs() const { return (const Pair*)mPairs.GetEntries(); }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Validates current settings. You should call this method after all the settings and callbacks have been defined for a collider.
+ * \return null if everything is ok, else a string describing the problem
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(Collider) const char* ValidateSettings();
+
+ protected:
+ // Colliding pairs
+ Container mPairs; //!< Pairs of colliding primitives
+ // User mesh interfaces
+ const MeshInterface* mIMesh0; //!< User-defined mesh interface for object0
+ const MeshInterface* mIMesh1; //!< User-defined mesh interface for object1
+ // Stats
+ udword mNbBVBVTests; //!< Number of BV-BV tests
+ udword mNbPrimPrimTests; //!< Number of Primitive-Primitive tests
+ udword mNbBVPrimTests; //!< Number of BV-Primitive tests
+ // Precomputed data
+ Matrix3x3 mAR; //!< Absolute rotation matrix
+ Matrix3x3 mR0to1; //!< Rotation from object0 to object1
+ Matrix3x3 mR1to0; //!< Rotation from object1 to object0
+ IcePoint mT0to1; //!< Translation from object0 to object1
+ IcePoint mT1to0; //!< Translation from object1 to object0
+ // Dequantization coeffs
+ IcePoint mCenterCoeff0;
+ IcePoint mExtentsCoeff0;
+ IcePoint mCenterCoeff1;
+ IcePoint mExtentsCoeff1;
+ // Leaf description
+ IcePoint mLeafVerts[3]; //!< Triangle vertices
+ udword mLeafIndex; //!< Triangle index
+ // Settings
+ bool mFullBoxBoxTest; //!< Perform full BV-BV tests (true) or SAT-lite tests (false)
+ bool mFullPrimBoxTest; //!< Perform full Primitive-BV tests (true) or SAT-lite tests (false)
+ // Internal methods
+
+ // Standard AABB trees
+ void _Collide(const AABBCollisionNode* b0, const AABBCollisionNode* b1);
+ // Quantized AABB trees
+ void _Collide(const AABBQuantizedNode* b0, const AABBQuantizedNode* b1, const IcePoint& a, const IcePoint& Pa, const IcePoint& b, const IcePoint& Pb);
+ // No-leaf AABB trees
+ void _CollideTriBox(const AABBNoLeafNode* b);
+ void _CollideBoxTri(const AABBNoLeafNode* b);
+ void _Collide(const AABBNoLeafNode* a, const AABBNoLeafNode* b);
+ // Quantized no-leaf AABB trees
+ void _CollideTriBox(const AABBQuantizedNoLeafNode* b);
+ void _CollideBoxTri(const AABBQuantizedNoLeafNode* b);
+ void _Collide(const AABBQuantizedNoLeafNode* a, const AABBQuantizedNoLeafNode* b);
+ // Overlap tests
+ void PrimTest(udword id0, udword id1);
+ inline_ void PrimTestTriIndex(udword id1);
+ inline_ void PrimTestIndexTri(udword id0);
+
+ inline_ BOOL BoxBoxOverlap(const IcePoint& ea, const IcePoint& ca, const IcePoint& eb, const IcePoint& cb);
+ inline_ BOOL TriBoxOverlap(const IcePoint& center, const IcePoint& extents);
+ inline_ BOOL TriTriOverlap(const IcePoint& V0, const IcePoint& V1, const IcePoint& V2, const IcePoint& U0, const IcePoint& U1, const IcePoint& U2);
+ // Init methods
+ void InitQuery(const Matrix4x4* world0=null, const Matrix4x4* world1=null);
+ bool CheckTemporalCoherence(Pair* cache);
+
+ inline_ BOOL Setup(const MeshInterface* mi0, const MeshInterface* mi1)
+ {
+ mIMesh0 = mi0;
+ mIMesh1 = mi1;
+
+ if(!mIMesh0 || !mIMesh1) return FALSE;
+
+ return TRUE;
+ }
+ };
+
+#endif // __OPC_TREECOLLIDER_H__
diff --git a/Opcode/OpcodeLib/OPC_TriBoxOverlap.h b/Opcode/OpcodeLib/OPC_TriBoxOverlap.h
new file mode 100644
index 0000000..662a127
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_TriBoxOverlap.h
@@ -0,0 +1,339 @@
+
+//! This macro quickly finds the min & max values among 3 variables
+#define FINDMINMAX(x0, x1, x2, min, max) \
+ min = max = x0; \
+ if(x1<min) min=x1; \
+ if(x1>max) max=x1; \
+ if(x2<min) min=x2; \
+ if(x2>max) max=x2;
+
+//! TO BE DOCUMENTED
+inline_ BOOL planeBoxOverlap(const IcePoint& normal, const float d, const IcePoint& maxbox)
+{
+ IcePoint vmin, vmax;
+ for(udword q=0;q<=2;q++)
+ {
+ if(normal[q]>0.0f) { vmin[q]=-maxbox[q]; vmax[q]=maxbox[q]; }
+ else { vmin[q]=maxbox[q]; vmax[q]=-maxbox[q]; }
+ }
+ if((normal|vmin)+d>0.0f) return FALSE;
+ if((normal|vmax)+d>=0.0f) return TRUE;
+
+ return FALSE;
+}
+
+//! TO BE DOCUMENTED
+#define AXISTEST_X01(a, b, fa, fb) \
+ min = a*v0.y - b*v0.z; \
+ max = a*v2.y - b*v2.z; \
+ if(min>max) {const float tmp=max; max=min; min=tmp; } \
+ rad = fa * extents.y + fb * extents.z; \
+ if(min>rad || max<-rad) return FALSE;
+
+//! TO BE DOCUMENTED
+#define AXISTEST_X2(a, b, fa, fb) \
+ min = a*v0.y - b*v0.z; \
+ max = a*v1.y - b*v1.z; \
+ if(min>max) {const float tmp=max; max=min; min=tmp; } \
+ rad = fa * extents.y + fb * extents.z; \
+ if(min>rad || max<-rad) return FALSE;
+
+//! TO BE DOCUMENTED
+#define AXISTEST_Y02(a, b, fa, fb) \
+ min = b*v0.z - a*v0.x; \
+ max = b*v2.z - a*v2.x; \
+ if(min>max) {const float tmp=max; max=min; min=tmp; } \
+ rad = fa * extents.x + fb * extents.z; \
+ if(min>rad || max<-rad) return FALSE;
+
+//! TO BE DOCUMENTED
+#define AXISTEST_Y1(a, b, fa, fb) \
+ min = b*v0.z - a*v0.x; \
+ max = b*v1.z - a*v1.x; \
+ if(min>max) {const float tmp=max; max=min; min=tmp; } \
+ rad = fa * extents.x + fb * extents.z; \
+ if(min>rad || max<-rad) return FALSE;
+
+//! TO BE DOCUMENTED
+#define AXISTEST_Z12(a, b, fa, fb) \
+ min = a*v1.x - b*v1.y; \
+ max = a*v2.x - b*v2.y; \
+ if(min>max) {const float tmp=max; max=min; min=tmp; } \
+ rad = fa * extents.x + fb * extents.y; \
+ if(min>rad || max<-rad) return FALSE;
+
+//! TO BE DOCUMENTED
+#define AXISTEST_Z0(a, b, fa, fb) \
+ min = a*v0.x - b*v0.y; \
+ max = a*v1.x - b*v1.y; \
+ if(min>max) {const float tmp=max; max=min; min=tmp; } \
+ rad = fa * extents.x + fb * extents.y; \
+ if(min>rad || max<-rad) return FALSE;
+
+// compute triangle edges
+// - edges lazy evaluated to take advantage of early exits
+// - fabs precomputed (half less work, possible since extents are always >0)
+// - customized macros to take advantage of the null component
+// - axis vector discarded, possibly saves useless movs
+#define IMPLEMENT_CLASS3_TESTS \
+ float rad; \
+ float min, max; \
+ \
+ const float fey0 = fabsf(e0.y); \
+ const float fez0 = fabsf(e0.z); \
+ AXISTEST_X01(e0.z, e0.y, fez0, fey0); \
+ const float fex0 = fabsf(e0.x); \
+ AXISTEST_Y02(e0.z, e0.x, fez0, fex0); \
+ AXISTEST_Z12(e0.y, e0.x, fey0, fex0); \
+ \
+ const float fey1 = fabsf(e1.y); \
+ const float fez1 = fabsf(e1.z); \
+ AXISTEST_X01(e1.z, e1.y, fez1, fey1); \
+ const float fex1 = fabsf(e1.x); \
+ AXISTEST_Y02(e1.z, e1.x, fez1, fex1); \
+ AXISTEST_Z0(e1.y, e1.x, fey1, fex1); \
+ \
+ const IcePoint e2 = mLeafVerts[0] - mLeafVerts[2]; \
+ const float fey2 = fabsf(e2.y); \
+ const float fez2 = fabsf(e2.z); \
+ AXISTEST_X2(e2.z, e2.y, fez2, fey2); \
+ const float fex2 = fabsf(e2.x); \
+ AXISTEST_Y1(e2.z, e2.x, fez2, fex2); \
+ AXISTEST_Z12(e2.y, e2.x, fey2, fex2);
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Triangle-Box overlap test using the separating axis theorem.
+ * This is the code from Tomas Möller, a bit optimized:
+ * - with some more lazy evaluation (faster path on PC)
+ * - with a tiny bit of assembly
+ * - with "SAT-lite" applied if needed
+ * - and perhaps with some more minor modifs...
+ *
+ * \param center [in] box center
+ * \param extents [in] box extents
+ * \return true if triangle & box overlap
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL AABBTreeCollider::TriBoxOverlap(const IcePoint& center, const IcePoint& extents)
+{
+ // Stats
+ mNbBVPrimTests++;
+
+ // use separating axis theorem to test overlap between triangle and box
+ // need to test for overlap in these directions:
+ // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle
+ // we do not even need to test these)
+ // 2) normal of the triangle
+ // 3) crossproduct(edge from tri, {x,y,z}-directin)
+ // this gives 3x3=9 more tests
+
+ // move everything so that the boxcenter is in (0,0,0)
+ IcePoint v0, v1, v2;
+ v0.x = mLeafVerts[0].x - center.x;
+ v1.x = mLeafVerts[1].x - center.x;
+ v2.x = mLeafVerts[2].x - center.x;
+
+ // First, test overlap in the {x,y,z}-directions
+#ifdef OPC_USE_FCOMI
+ // find min, max of the triangle in x-direction, and test for overlap in X
+ if(FCMin3(v0.x, v1.x, v2.x)>extents.x) return FALSE;
+ if(FCMax3(v0.x, v1.x, v2.x)<-extents.x) return FALSE;
+
+ // same for Y
+ v0.y = mLeafVerts[0].y - center.y;
+ v1.y = mLeafVerts[1].y - center.y;
+ v2.y = mLeafVerts[2].y - center.y;
+
+ if(FCMin3(v0.y, v1.y, v2.y)>extents.y) return FALSE;
+ if(FCMax3(v0.y, v1.y, v2.y)<-extents.y) return FALSE;
+
+ // same for Z
+ v0.z = mLeafVerts[0].z - center.z;
+ v1.z = mLeafVerts[1].z - center.z;
+ v2.z = mLeafVerts[2].z - center.z;
+
+ if(FCMin3(v0.z, v1.z, v2.z)>extents.z) return FALSE;
+ if(FCMax3(v0.z, v1.z, v2.z)<-extents.z) return FALSE;
+#else
+ float min,max;
+ // Find min, max of the triangle in x-direction, and test for overlap in X
+ FINDMINMAX(v0.x, v1.x, v2.x, min, max);
+ if(min>extents.x || max<-extents.x) return FALSE;
+
+ // Same for Y
+ v0.y = mLeafVerts[0].y - center.y;
+ v1.y = mLeafVerts[1].y - center.y;
+ v2.y = mLeafVerts[2].y - center.y;
+
+ FINDMINMAX(v0.y, v1.y, v2.y, min, max);
+ if(min>extents.y || max<-extents.y) return FALSE;
+
+ // Same for Z
+ v0.z = mLeafVerts[0].z - center.z;
+ v1.z = mLeafVerts[1].z - center.z;
+ v2.z = mLeafVerts[2].z - center.z;
+
+ FINDMINMAX(v0.z, v1.z, v2.z, min, max);
+ if(min>extents.z || max<-extents.z) return FALSE;
+#endif
+ // 2) Test if the box intersects the plane of the triangle
+ // compute plane equation of triangle: normal*x+d=0
+ // ### could be precomputed since we use the same leaf triangle several times
+ const IcePoint e0 = v1 - v0;
+ const IcePoint e1 = v2 - v1;
+ const IcePoint normal = e0 ^ e1;
+ const float d = -normal|v0;
+ if(!planeBoxOverlap(normal, d, extents)) return FALSE;
+
+ // 3) "Class III" tests
+ if(mFullPrimBoxTest)
+ {
+ IMPLEMENT_CLASS3_TESTS
+ }
+ return TRUE;
+}
+
+//! A dedicated version where the box is constant
+inline_ BOOL OBBCollider::TriBoxOverlap()
+{
+ // Stats
+ mNbVolumePrimTests++;
+
+ // Hook
+ const IcePoint& extents = mBoxExtents;
+ const IcePoint& v0 = mLeafVerts[0];
+ const IcePoint& v1 = mLeafVerts[1];
+ const IcePoint& v2 = mLeafVerts[2];
+
+ // use separating axis theorem to test overlap between triangle and box
+ // need to test for overlap in these directions:
+ // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle
+ // we do not even need to test these)
+ // 2) normal of the triangle
+ // 3) crossproduct(edge from tri, {x,y,z}-directin)
+ // this gives 3x3=9 more tests
+
+ // Box center is already in (0,0,0)
+
+ // First, test overlap in the {x,y,z}-directions
+#ifdef OPC_USE_FCOMI
+ // find min, max of the triangle in x-direction, and test for overlap in X
+ if(FCMin3(v0.x, v1.x, v2.x)>mBoxExtents.x) return FALSE;
+ if(FCMax3(v0.x, v1.x, v2.x)<-mBoxExtents.x) return FALSE;
+
+ if(FCMin3(v0.y, v1.y, v2.y)>mBoxExtents.y) return FALSE;
+ if(FCMax3(v0.y, v1.y, v2.y)<-mBoxExtents.y) return FALSE;
+
+ if(FCMin3(v0.z, v1.z, v2.z)>mBoxExtents.z) return FALSE;
+ if(FCMax3(v0.z, v1.z, v2.z)<-mBoxExtents.z) return FALSE;
+#else
+ float min,max;
+ // Find min, max of the triangle in x-direction, and test for overlap in X
+ FINDMINMAX(v0.x, v1.x, v2.x, min, max);
+ if(min>mBoxExtents.x || max<-mBoxExtents.x) return FALSE;
+
+ FINDMINMAX(v0.y, v1.y, v2.y, min, max);
+ if(min>mBoxExtents.y || max<-mBoxExtents.y) return FALSE;
+
+ FINDMINMAX(v0.z, v1.z, v2.z, min, max);
+ if(min>mBoxExtents.z || max<-mBoxExtents.z) return FALSE;
+#endif
+ // 2) Test if the box intersects the plane of the triangle
+ // compute plane equation of triangle: normal*x+d=0
+ // ### could be precomputed since we use the same leaf triangle several times
+ const IcePoint e0 = v1 - v0;
+ const IcePoint e1 = v2 - v1;
+ const IcePoint normal = e0 ^ e1;
+ const float d = -normal|v0;
+ if(!planeBoxOverlap(normal, d, mBoxExtents)) return FALSE;
+
+ // 3) "Class III" tests - here we always do full tests since the box is a primitive (not a BV)
+ {
+ IMPLEMENT_CLASS3_TESTS
+ }
+ return TRUE;
+}
+
+//! ...and another one, jeez
+inline_ BOOL AABBCollider::TriBoxOverlap()
+{
+ // Stats
+ mNbVolumePrimTests++;
+
+ // Hook
+ const IcePoint& center = mBox.mCenter;
+ const IcePoint& extents = mBox.mExtents;
+
+ // use separating axis theorem to test overlap between triangle and box
+ // need to test for overlap in these directions:
+ // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle
+ // we do not even need to test these)
+ // 2) normal of the triangle
+ // 3) crossproduct(edge from tri, {x,y,z}-directin)
+ // this gives 3x3=9 more tests
+
+ // move everything so that the boxcenter is in (0,0,0)
+ IcePoint v0, v1, v2;
+ v0.x = mLeafVerts[0].x - center.x;
+ v1.x = mLeafVerts[1].x - center.x;
+ v2.x = mLeafVerts[2].x - center.x;
+
+ // First, test overlap in the {x,y,z}-directions
+#ifdef OPC_USE_FCOMI
+ // find min, max of the triangle in x-direction, and test for overlap in X
+ if(FCMin3(v0.x, v1.x, v2.x)>extents.x) return FALSE;
+ if(FCMax3(v0.x, v1.x, v2.x)<-extents.x) return FALSE;
+
+ // same for Y
+ v0.y = mLeafVerts[0].y - center.y;
+ v1.y = mLeafVerts[1].y - center.y;
+ v2.y = mLeafVerts[2].y - center.y;
+
+ if(FCMin3(v0.y, v1.y, v2.y)>extents.y) return FALSE;
+ if(FCMax3(v0.y, v1.y, v2.y)<-extents.y) return FALSE;
+
+ // same for Z
+ v0.z = mLeafVerts[0].z - center.z;
+ v1.z = mLeafVerts[1].z - center.z;
+ v2.z = mLeafVerts[2].z - center.z;
+
+ if(FCMin3(v0.z, v1.z, v2.z)>extents.z) return FALSE;
+ if(FCMax3(v0.z, v1.z, v2.z)<-extents.z) return FALSE;
+#else
+ float min,max;
+ // Find min, max of the triangle in x-direction, and test for overlap in X
+ FINDMINMAX(v0.x, v1.x, v2.x, min, max);
+ if(min>extents.x || max<-extents.x) return FALSE;
+
+ // Same for Y
+ v0.y = mLeafVerts[0].y - center.y;
+ v1.y = mLeafVerts[1].y - center.y;
+ v2.y = mLeafVerts[2].y - center.y;
+
+ FINDMINMAX(v0.y, v1.y, v2.y, min, max);
+ if(min>extents.y || max<-extents.y) return FALSE;
+
+ // Same for Z
+ v0.z = mLeafVerts[0].z - center.z;
+ v1.z = mLeafVerts[1].z - center.z;
+ v2.z = mLeafVerts[2].z - center.z;
+
+ FINDMINMAX(v0.z, v1.z, v2.z, min, max);
+ if(min>extents.z || max<-extents.z) return FALSE;
+#endif
+ // 2) Test if the box intersects the plane of the triangle
+ // compute plane equation of triangle: normal*x+d=0
+ // ### could be precomputed since we use the same leaf triangle several times
+ const IcePoint e0 = v1 - v0;
+ const IcePoint e1 = v2 - v1;
+ const IcePoint normal = e0 ^ e1;
+ const float d = -normal|v0;
+ if(!planeBoxOverlap(normal, d, extents)) return FALSE;
+
+ // 3) "Class III" tests - here we always do full tests since the box is a primitive (not a BV)
+ {
+ IMPLEMENT_CLASS3_TESTS
+ }
+ return TRUE;
+}
diff --git a/Opcode/OpcodeLib/OPC_TriTriOverlap.h b/Opcode/OpcodeLib/OPC_TriTriOverlap.h
new file mode 100644
index 0000000..ccc8161
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_TriTriOverlap.h
@@ -0,0 +1,279 @@
+
+//! if OPC_TRITRI_EPSILON_TEST is true then we do a check (if |dv|<EPSILON then dv=0.0;) else no check is done (which is less robust, but faster)
+#define LOCAL_EPSILON 0.000001f
+
+//! sort so that a<=b
+#define SORT(a,b) \
+ if(a>b) \
+ { \
+ const float c=a; \
+ a=b; \
+ b=c; \
+ }
+
+//! Edge to edge test based on Franlin Antonio's gem: "Faster Line Segment Intersection", in Graphics Gems III, pp. 199-202
+#define EDGE_EDGE_TEST(V0, U0, U1) \
+ Bx = U0[i0] - U1[i0]; \
+ By = U0[i1] - U1[i1]; \
+ Cx = V0[i0] - U0[i0]; \
+ Cy = V0[i1] - U0[i1]; \
+ f = Ay*Bx - Ax*By; \
+ d = By*Cx - Bx*Cy; \
+ if((f>0.0f && d>=0.0f && d<=f) || (f<0.0f && d<=0.0f && d>=f)) \
+ { \
+ const float e=Ax*Cy - Ay*Cx; \
+ if(f>0.0f) \
+ { \
+ if(e>=0.0f && e<=f) return TRUE; \
+ } \
+ else \
+ { \
+ if(e<=0.0f && e>=f) return TRUE; \
+ } \
+ }
+
+//! TO BE DOCUMENTED
+#define EDGE_AGAINST_TRI_EDGES(V0, V1, U0, U1, U2) \
+{ \
+ float Bx,By,Cx,Cy,d,f; \
+ const float Ax = V1[i0] - V0[i0]; \
+ const float Ay = V1[i1] - V0[i1]; \
+ /* test edge U0,U1 against V0,V1 */ \
+ EDGE_EDGE_TEST(V0, U0, U1); \
+ /* test edge U1,U2 against V0,V1 */ \
+ EDGE_EDGE_TEST(V0, U1, U2); \
+ /* test edge U2,U1 against V0,V1 */ \
+ EDGE_EDGE_TEST(V0, U2, U0); \
+}
+
+//! TO BE DOCUMENTED
+#define POINT_IN_TRI(V0, U0, U1, U2) \
+{ \
+ /* is T1 completly inside T2? */ \
+ /* check if V0 is inside tri(U0,U1,U2) */ \
+ float a = U1[i1] - U0[i1]; \
+ float b = -(U1[i0] - U0[i0]); \
+ float c = -a*U0[i0] - b*U0[i1]; \
+ float d0 = a*V0[i0] + b*V0[i1] + c; \
+ \
+ a = U2[i1] - U1[i1]; \
+ b = -(U2[i0] - U1[i0]); \
+ c = -a*U1[i0] - b*U1[i1]; \
+ const float d1 = a*V0[i0] + b*V0[i1] + c; \
+ \
+ a = U0[i1] - U2[i1]; \
+ b = -(U0[i0] - U2[i0]); \
+ c = -a*U2[i0] - b*U2[i1]; \
+ const float d2 = a*V0[i0] + b*V0[i1] + c; \
+ if(d0*d1>0.0f) \
+ { \
+ if(d0*d2>0.0f) return TRUE; \
+ } \
+}
+
+//! TO BE DOCUMENTED
+BOOL CoplanarTriTri(const IcePoint& n, const IcePoint& v0, const IcePoint& v1, const IcePoint& v2, const IcePoint& u0, const IcePoint& u1, const IcePoint& u2)
+{
+ float A[3];
+ short i0,i1;
+ /* first project onto an axis-aligned plane, that maximizes the area */
+ /* of the triangles, compute indices: i0,i1. */
+ A[0] = fabsf(n[0]);
+ A[1] = fabsf(n[1]);
+ A[2] = fabsf(n[2]);
+ if(A[0]>A[1])
+ {
+ if(A[0]>A[2])
+ {
+ i0=1; /* A[0] is greatest */
+ i1=2;
+ }
+ else
+ {
+ i0=0; /* A[2] is greatest */
+ i1=1;
+ }
+ }
+ else /* A[0]<=A[1] */
+ {
+ if(A[2]>A[1])
+ {
+ i0=0; /* A[2] is greatest */
+ i1=1;
+ }
+ else
+ {
+ i0=0; /* A[1] is greatest */
+ i1=2;
+ }
+ }
+
+ /* test all edges of triangle 1 against the edges of triangle 2 */
+ EDGE_AGAINST_TRI_EDGES(v0, v1, u0, u1, u2);
+ EDGE_AGAINST_TRI_EDGES(v1, v2, u0, u1, u2);
+ EDGE_AGAINST_TRI_EDGES(v2, v0, u0, u1, u2);
+
+ /* finally, test if tri1 is totally contained in tri2 or vice versa */
+ POINT_IN_TRI(v0, u0, u1, u2);
+ POINT_IN_TRI(u0, v0, v1, v2);
+
+ return FALSE;
+}
+
+//! TO BE DOCUMENTED
+#define NEWCOMPUTE_INTERVALS(VV0, VV1, VV2, D0, D1, D2, D0D1, D0D2, A, B, C, X0, X1) \
+{ \
+ if(D0D1>0.0f) \
+ { \
+ /* here we know that D0D2<=0.0 */ \
+ /* that is D0, D1 are on the same side, D2 on the other or on the plane */ \
+ A=VV2; B=(VV0 - VV2)*D2; C=(VV1 - VV2)*D2; X0=D2 - D0; X1=D2 - D1; \
+ } \
+ else if(D0D2>0.0f) \
+ { \
+ /* here we know that d0d1<=0.0 */ \
+ A=VV1; B=(VV0 - VV1)*D1; C=(VV2 - VV1)*D1; X0=D1 - D0; X1=D1 - D2; \
+ } \
+ else if(D1*D2>0.0f || D0!=0.0f) \
+ { \
+ /* here we know that d0d1<=0.0 or that D0!=0.0 */ \
+ A=VV0; B=(VV1 - VV0)*D0; C=(VV2 - VV0)*D0; X0=D0 - D1; X1=D0 - D2; \
+ } \
+ else if(D1!=0.0f) \
+ { \
+ A=VV1; B=(VV0 - VV1)*D1; C=(VV2 - VV1)*D1; X0=D1 - D0; X1=D1 - D2; \
+ } \
+ else if(D2!=0.0f) \
+ { \
+ A=VV2; B=(VV0 - VV2)*D2; C=(VV1 - VV2)*D2; X0=D2 - D0; X1=D2 - D1; \
+ } \
+ else \
+ { \
+ /* triangles are coplanar */ \
+ return CoplanarTriTri(N1, V0, V1, V2, U0, U1, U2); \
+ } \
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Triangle/triangle intersection test routine,
+ * by Tomas Moller, 1997.
+ * See article "A Fast Triangle-Triangle Intersection Test",
+ * Journal of Graphics Tools, 2(2), 1997
+ *
+ * Updated June 1999: removed the divisions -- a little faster now!
+ * Updated October 1999: added {} to CROSS and SUB macros
+ *
+ * int NoDivTriTriIsect(float V0[3],float V1[3],float V2[3],
+ * float U0[3],float U1[3],float U2[3])
+ *
+ * \param V0 [in] triangle 0, vertex 0
+ * \param V1 [in] triangle 0, vertex 1
+ * \param V2 [in] triangle 0, vertex 2
+ * \param U0 [in] triangle 1, vertex 0
+ * \param U1 [in] triangle 1, vertex 1
+ * \param U2 [in] triangle 1, vertex 2
+ * \return true if triangles overlap
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+inline_ BOOL AABBTreeCollider::TriTriOverlap(const IcePoint& V0, const IcePoint& V1, const IcePoint& V2, const IcePoint& U0, const IcePoint& U1, const IcePoint& U2)
+{
+ // Stats
+ mNbPrimPrimTests++;
+
+ // Compute plane equation of triangle(V0,V1,V2)
+ IcePoint E1 = V1 - V0;
+ IcePoint E2 = V2 - V0;
+ const IcePoint N1 = E1 ^ E2;
+ const float d1 =-N1 | V0;
+ // Plane equation 1: N1.X+d1=0
+
+ // Put U0,U1,U2 into plane equation 1 to compute signed distances to the plane
+ float du0 = (N1|U0) + d1;
+ float du1 = (N1|U1) + d1;
+ float du2 = (N1|U2) + d1;
+
+ // Coplanarity robustness check
+#ifdef OPC_TRITRI_EPSILON_TEST
+ if(fabsf(du0)<LOCAL_EPSILON) du0 = 0.0f;
+ if(fabsf(du1)<LOCAL_EPSILON) du1 = 0.0f;
+ if(fabsf(du2)<LOCAL_EPSILON) du2 = 0.0f;
+#endif
+ const float du0du1 = du0 * du1;
+ const float du0du2 = du0 * du2;
+
+ if(du0du1>0.0f && du0du2>0.0f) // same sign on all of them + not equal 0 ?
+ return FALSE; // no intersection occurs
+
+ // Compute plane of triangle (U0,U1,U2)
+ E1 = U1 - U0;
+ E2 = U2 - U0;
+ const IcePoint N2 = E1 ^ E2;
+ const float d2=-N2 | U0;
+ // plane equation 2: N2.X+d2=0
+
+ // put V0,V1,V2 into plane equation 2
+ float dv0 = (N2|V0) + d2;
+ float dv1 = (N2|V1) + d2;
+ float dv2 = (N2|V2) + d2;
+
+#ifdef OPC_TRITRI_EPSILON_TEST
+ if(fabsf(dv0)<LOCAL_EPSILON) dv0 = 0.0f;
+ if(fabsf(dv1)<LOCAL_EPSILON) dv1 = 0.0f;
+ if(fabsf(dv2)<LOCAL_EPSILON) dv2 = 0.0f;
+#endif
+
+ const float dv0dv1 = dv0 * dv1;
+ const float dv0dv2 = dv0 * dv2;
+
+ if(dv0dv1>0.0f && dv0dv2>0.0f) // same sign on all of them + not equal 0 ?
+ return FALSE; // no intersection occurs
+
+ // Compute direction of intersection line
+ const IcePoint D = N1^N2;
+
+ // Compute and index to the largest component of D
+ float max=fabsf(D[0]);
+ short index=0;
+ float bb=fabsf(D[1]);
+ float cc=fabsf(D[2]);
+ if(bb>max) max=bb,index=1;
+ if(cc>max) max=cc,index=2;
+
+ // This is the simplified projection onto L
+ const float vp0 = V0[index];
+ const float vp1 = V1[index];
+ const float vp2 = V2[index];
+
+ const float up0 = U0[index];
+ const float up1 = U1[index];
+ const float up2 = U2[index];
+
+ // Compute interval for triangle 1
+ float a,b,c,x0,x1;
+ NEWCOMPUTE_INTERVALS(vp0,vp1,vp2,dv0,dv1,dv2,dv0dv1,dv0dv2,a,b,c,x0,x1);
+
+ // Compute interval for triangle 2
+ float d,e,f,y0,y1;
+ NEWCOMPUTE_INTERVALS(up0,up1,up2,du0,du1,du2,du0du1,du0du2,d,e,f,y0,y1);
+
+ const float xx=x0*x1;
+ const float yy=y0*y1;
+ const float xxyy=xx*yy;
+
+ float isect1[2], isect2[2];
+
+ float tmp=a*xxyy;
+ isect1[0]=tmp+b*x1*yy;
+ isect1[1]=tmp+c*x0*yy;
+
+ tmp=d*xxyy;
+ isect2[0]=tmp+e*xx*y1;
+ isect2[1]=tmp+f*xx*y0;
+
+ SORT(isect1[0],isect1[1]);
+ SORT(isect2[0],isect2[1]);
+
+ if(isect1[1]<isect2[0] || isect2[1]<isect1[0]) return FALSE;
+ return TRUE;
+}
diff --git a/Opcode/OpcodeLib/OPC_VolumeCollider.cpp b/Opcode/OpcodeLib/OPC_VolumeCollider.cpp
new file mode 100644
index 0000000..70fc292
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_VolumeCollider.cpp
@@ -0,0 +1,103 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains base volume collider class.
+ * \file OPC_VolumeCollider.cpp
+ * \author Pierre Terdiman
+ * \date June, 2, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains the abstract class for volume colliders.
+ *
+ * \class VolumeCollider
+ * \author Pierre Terdiman
+ * \version 1.3
+ * \date June, 2, 2001
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+using namespace Opcode;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+VolumeCollider::VolumeCollider() :
+ mTouchedPrimitives (null),
+ mNbVolumeBVTests (0),
+ mNbVolumePrimTests (0)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Destructor.
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+VolumeCollider::~VolumeCollider()
+{
+ mTouchedPrimitives = null;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Validates current settings. You should call this method after all the settings / callbacks have been defined for a collider.
+ * \return null if everything is ok, else a string describing the problem
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const char* VolumeCollider::ValidateSettings()
+{
+ return null;
+}
+
+// Pretty dumb way to dump - to do better - one day...
+
+#define IMPLEMENT_NOLEAFDUMP(type) \
+void VolumeCollider::_Dump(const type* node) \
+{ \
+ if(node->HasPosLeaf()) mTouchedPrimitives->Add(node->GetPosPrimitive()); \
+ else _Dump(node->GetPos()); \
+ \
+ if(ContactFound()) return; \
+ \
+ if(node->HasNegLeaf()) mTouchedPrimitives->Add(node->GetNegPrimitive()); \
+ else _Dump(node->GetNeg()); \
+}
+
+#define IMPLEMENT_LEAFDUMP(type) \
+void VolumeCollider::_Dump(const type* node) \
+{ \
+ if(node->IsLeaf()) \
+ { \
+ mTouchedPrimitives->Add(node->GetPrimitive()); \
+ } \
+ else \
+ { \
+ _Dump(node->GetPos()); \
+ \
+ if(ContactFound()) return; \
+ \
+ _Dump(node->GetNeg()); \
+ } \
+}
+
+IMPLEMENT_NOLEAFDUMP(AABBNoLeafNode)
+IMPLEMENT_NOLEAFDUMP(AABBQuantizedNoLeafNode)
+
+IMPLEMENT_LEAFDUMP(AABBCollisionNode)
+IMPLEMENT_LEAFDUMP(AABBQuantizedNode)
diff --git a/Opcode/OpcodeLib/OPC_VolumeCollider.h b/Opcode/OpcodeLib/OPC_VolumeCollider.h
new file mode 100644
index 0000000..5c39ea3
--- /dev/null
+++ b/Opcode/OpcodeLib/OPC_VolumeCollider.h
@@ -0,0 +1,138 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Contains base volume collider class.
+ * \file OPC_VolumeCollider.h
+ * \author Pierre Terdiman
+ * \date June, 2, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPC_VOLUMECOLLIDER_H__
+#define __OPC_VOLUMECOLLIDER_H__
+
+ struct OPCODE_API VolumeCache
+ {
+ VolumeCache() : Model(null) {}
+ ~VolumeCache() {}
+
+ Container TouchedPrimitives; //!< Indices of touched primitives
+ const BaseModel* Model; //!< Owner
+ };
+
+ class OPCODE_API VolumeCollider : public Collider
+ {
+ public:
+ // Constructor / Destructor
+ VolumeCollider();
+ virtual ~VolumeCollider() = 0;
+
+ // Collision report
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the number of touched primitives after a collision query.
+ * \see GetContactStatus()
+ * \see GetTouchedPrimitives()
+ * \return the number of touched primitives
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbTouchedPrimitives() const { return mTouchedPrimitives ? mTouchedPrimitives->GetNbEntries() : 0; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Gets the list of touched primitives after a collision query.
+ * \see GetContactStatus()
+ * \see GetNbTouchedPrimitives()
+ * \return the list of touched primitives (primitive indices)
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ const udword* GetTouchedPrimitives() const { return mTouchedPrimitives ? mTouchedPrimitives->GetEntries() : null; }
+
+ // Stats
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Stats: gets the number of Volume-BV overlap tests after a collision query.
+ * \see GetNbVolumePrimTests()
+ * \return the number of Volume-BV tests performed during last query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbVolumeBVTests() const { return mNbVolumeBVTests; }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Stats: gets the number of Volume-Triangle overlap tests after a collision query.
+ * \see GetNbVolumeBVTests()
+ * \return the number of Volume-Triangle tests performed during last query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ inline_ udword GetNbVolumePrimTests() const { return mNbVolumePrimTests; }
+
+ // Settings
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Validates current settings. You should call this method after all the settings / callbacks have been defined for a collider.
+ * \return null if everything is ok, else a string describing the problem
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(Collider) const char* ValidateSettings();
+
+ protected:
+ // Touched primitives
+ Container* mTouchedPrimitives; //!< List of touched primitives
+
+ // Dequantization coeffs
+ IcePoint mCenterCoeff;
+ IcePoint mExtentsCoeff;
+ // Stats
+ udword mNbVolumeBVTests; //!< Number of Volume-BV tests
+ udword mNbVolumePrimTests; //!< Number of Volume-Primitive tests
+ // Internal methods
+ void _Dump(const AABBCollisionNode* node);
+ void _Dump(const AABBNoLeafNode* node);
+ void _Dump(const AABBQuantizedNode* node);
+ void _Dump(const AABBQuantizedNoLeafNode* node);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * Initializes a query
+ */
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ override(Collider) inline_ void InitQuery()
+ {
+ // Reset stats & contact status
+ mNbVolumeBVTests = 0;
+ mNbVolumePrimTests = 0;
+ Collider::InitQuery();
+ }
+
+ inline_ BOOL IsCacheValid(VolumeCache& cache)
+ {
+ // We're going to do a volume-vs-model query.
+ if(cache.Model!=mCurrentModel)
+ {
+ // Cached list was for another model so we can't keep it
+ // Keep track of new owner and reset cache
+ cache.Model = mCurrentModel;
+ return FALSE;
+ }
+ else
+ {
+ // Same models, no problem
+ return TRUE;
+ }
+ }
+ };
+
+#endif // __OPC_VOLUMECOLLIDER_H__
diff --git a/Opcode/OpcodeLib/Opcode.cpp b/Opcode/OpcodeLib/Opcode.cpp
new file mode 100644
index 0000000..72d6b47
--- /dev/null
+++ b/Opcode/OpcodeLib/Opcode.cpp
@@ -0,0 +1,65 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Main file for Opcode.dll.
+ * \file Opcode.cpp
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+ Finding a good name is difficult!
+ Here's the draft for this lib.... Spooky, uh?
+
+ VOID? Very Optimized Interference Detection
+ ZOID? Zappy's Optimized Interference Detection
+ CID? Custom/Clever Interference Detection
+ AID / ACID! Accurate Interference Detection
+ QUID? Quick Interference Detection
+ RIDE? Realtime Interference DEtection
+ WIDE? Wicked Interference DEtection (....)
+ GUID!
+ KID ! k-dop interference detection :)
+ OPCODE! OPtimized COllision DEtection
+*/
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Precompiled Header
+#include "Stdafx.h"
+
+bool Opcode::InitOpcode()
+{
+ Log("// Initializing OPCODE\n\n");
+// LogAPIInfo();
+ return true;
+}
+
+void ReleasePruningSorters();
+bool Opcode::CloseOpcode()
+{
+ Log("// Closing OPCODE\n\n");
+
+ ReleasePruningSorters();
+
+ return true;
+}
+
+#ifdef ICE_MAIN
+
+void ModuleAttach(HINSTANCE hinstance)
+{
+}
+
+void ModuleDetach()
+{
+}
+
+#endif \ No newline at end of file
diff --git a/Opcode/OpcodeLib/Opcode.h b/Opcode/OpcodeLib/Opcode.h
new file mode 100644
index 0000000..6078565
--- /dev/null
+++ b/Opcode/OpcodeLib/Opcode.h
@@ -0,0 +1,64 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Main file for Opcode.dll.
+ * \file Opcode.h
+ * \author Pierre Terdiman
+ * \date March, 20, 2001
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Include Guard
+#ifndef __OPCODE_H__
+#define __OPCODE_H__
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Compilation messages
+#define OPCODE_API
+
+ #include "OPC_IceHook.h"
+
+ namespace Opcode
+ {
+ // Bulk-of-the-work
+ #include "OPC_Settings.h"
+ #include "OPC_Common.h"
+ #include "OPC_MeshInterface.h"
+ // Builders
+ #include "OPC_TreeBuilders.h"
+ // Trees
+ #include "OPC_AABBTree.h"
+ #include "OPC_OptimizedTree.h"
+ // Models
+ #include "OPC_BaseModel.h"
+ #include "OPC_Model.h"
+ #include "OPC_HybridModel.h"
+ // Colliders
+ #include "OPC_Collider.h"
+ #include "OPC_VolumeCollider.h"
+ #include "OPC_TreeCollider.h"
+ #include "OPC_RayCollider.h"
+ //#include "OPC_SphereCollider.h"
+ #include "OPC_OBBCollider.h"
+ #include "OPC_AABBCollider.h"
+ //#include "OPC_LSSCollider.h"
+ //#include "OPC_PlanesCollider.h"
+ // Usages
+ //#include "OPC_Picking.h"
+ // Sweep-and-prune
+ //#include "OPC_BoxPruning.h"
+ //#include "OPC_SweepAndPrune.h"
+
+ FUNCTION OPCODE_API bool InitOpcode();
+ FUNCTION OPCODE_API bool CloseOpcode();
+ }
+
+#endif // __OPCODE_H__
diff --git a/Opcode/OpcodeLib/OpcodeLib.dsp b/Opcode/OpcodeLib/OpcodeLib.dsp
new file mode 100644
index 0000000..c91baac
--- /dev/null
+++ b/Opcode/OpcodeLib/OpcodeLib.dsp
@@ -0,0 +1,405 @@
+# Microsoft Developer Studio Project File - Name="OpcodeLib" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=OpcodeLib - 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 "OpcodeLib.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 "OpcodeLib.mak" CFG="OpcodeLib - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "OpcodeLib - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "OpcodeLib - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "OpcodeLib - 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 "_MBCS" /D "_LIB" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Yu"stdafx.h" /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
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF "$(CFG)" == "OpcodeLib - 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 "_MBCS" /D "_LIB" /Yu"stdafx.h" /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Yu"stdafx.h" /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
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF
+
+# Begin Target
+
+# Name "OpcodeLib - Win32 Release"
+# Name "OpcodeLib - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\Ice\IceAABB.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceContainer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceHPoint.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceIndexedTriangle.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceMatrix3x3.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceMatrix4x4.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceOBB.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IcePlane.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IcePoint.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceRandom.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceRay.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceRevisitedRadix.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceSegment.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceTriangle.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceUtils.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_AABBCollider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_AABBTree.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_BaseModel.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_Collider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_Common.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_HybridModel.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_MeshInterface.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_Model.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_OBBCollider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_OptimizedTree.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_RayCollider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_TreeBuilders.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_TreeCollider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_VolumeCollider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Opcode.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.cpp
+# ADD CPP /Yc"stdafx.h"
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\Ice\IceAABB.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceAxes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceBoundingSphere.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceContainer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceFPU.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceHPoint.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceIndexedTriangle.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceLSS.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceMatrix3x3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceMatrix4x4.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceMemoryMacros.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceOBB.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IcePairs.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IcePlane.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IcePoint.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IcePreprocessor.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceRandom.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceRay.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceRevisitedRadix.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceSegment.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceTriangle.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceTrilist.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceTypes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ice\IceUtils.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_AABBCollider.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_AABBTree.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_BaseModel.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_BoxBoxOverlap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_Collider.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_Common.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_HybridModel.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_MeshInterface.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_Model.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_OBBCollider.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_OptimizedTree.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_RayAABBOverlap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_RayCollider.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_RayTriOverlap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_Settings.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_TreeBuilders.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_TreeCollider.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_TriBoxOverlap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_TriTriOverlap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OPC_VolumeCollider.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Opcode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\Readme.txt
+# End Source File
+# End Target
+# End Project
diff --git a/Opcode/OpcodeLib/OpcodeLib.plg b/Opcode/OpcodeLib/OpcodeLib.plg
new file mode 100644
index 0000000..f782739
--- /dev/null
+++ b/Opcode/OpcodeLib/OpcodeLib.plg
@@ -0,0 +1,259 @@
+<html>
+<body>
+<pre>
+<h1>Build Log</h1>
+<h3>
+--------------------Configuration: OpcodeLib - Win32 Release--------------------
+</h3>
+<h3>Command Lines</h3>
+Creating temporary file "C:\DOCUME~1\milo\LOCALS~1\Temp\RSP68A9.tmp" with contents
+[
+/nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fp"Release/OpcodeLib.pch" /Yu"stdafx.h" /Fo"Release/" /Fd"Release/" /FD /c
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceAABB.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceContainer.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceHPoint.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceIndexedTriangle.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceMatrix3x3.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceMatrix4x4.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceOBB.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IcePlane.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IcePoint.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceRandom.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceRay.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceRevisitedRadix.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceSegment.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceTriangle.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceUtils.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_AABBCollider.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_AABBTree.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_BaseModel.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_Collider.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_Common.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_HybridModel.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_MeshInterface.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_Model.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_OBBCollider.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_OptimizedTree.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_RayCollider.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_TreeBuilders.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_TreeCollider.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_VolumeCollider.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Opcode.cpp"
+]
+Creating command line "cl.exe @C:\DOCUME~1\milo\LOCALS~1\Temp\RSP68A9.tmp"
+Creating temporary file "C:\DOCUME~1\milo\LOCALS~1\Temp\RSP68AA.tmp" with contents
+[
+/nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fp"Release/OpcodeLib.pch" /Yc"stdafx.h" /Fo"Release/" /Fd"Release/" /FD /c
+"C:\GameDev\Opcode\OpcodeLib\StdAfx.cpp"
+]
+Creating command line "cl.exe @C:\DOCUME~1\milo\LOCALS~1\Temp\RSP68AA.tmp"
+Creating temporary file "C:\DOCUME~1\milo\LOCALS~1\Temp\RSP68AB.tmp" with contents
+[
+/nologo /out:"Release\OpcodeLib.lib"
+.\Release\IceAABB.obj
+.\Release\IceContainer.obj
+.\Release\IceHPoint.obj
+.\Release\IceIndexedTriangle.obj
+.\Release\IceMatrix3x3.obj
+.\Release\IceMatrix4x4.obj
+.\Release\IceOBB.obj
+.\Release\IcePlane.obj
+.\Release\IcePoint.obj
+.\Release\IceRandom.obj
+.\Release\IceRay.obj
+.\Release\IceRevisitedRadix.obj
+.\Release\IceSegment.obj
+.\Release\IceTriangle.obj
+.\Release\IceUtils.obj
+.\Release\OPC_AABBCollider.obj
+.\Release\OPC_AABBTree.obj
+.\Release\OPC_BaseModel.obj
+.\Release\OPC_Collider.obj
+.\Release\OPC_Common.obj
+.\Release\OPC_HybridModel.obj
+.\Release\OPC_MeshInterface.obj
+.\Release\OPC_Model.obj
+.\Release\OPC_OBBCollider.obj
+.\Release\OPC_OptimizedTree.obj
+.\Release\OPC_RayCollider.obj
+.\Release\OPC_TreeBuilders.obj
+.\Release\OPC_TreeCollider.obj
+.\Release\OPC_VolumeCollider.obj
+.\Release\Opcode.obj
+.\Release\StdAfx.obj
+]
+Creating command line "link.exe -lib @C:\DOCUME~1\milo\LOCALS~1\Temp\RSP68AB.tmp"
+<h3>Output Window</h3>
+Compiling...
+StdAfx.cpp
+Compiling on Windows...
+Compiling with VC++...
+Compiling...
+IceAABB.cpp
+IceContainer.cpp
+IceHPoint.cpp
+IceIndexedTriangle.cpp
+IceMatrix3x3.cpp
+IceMatrix4x4.cpp
+IceOBB.cpp
+IcePlane.cpp
+IcePoint.cpp
+IceRandom.cpp
+IceRay.cpp
+IceRevisitedRadix.cpp
+IceSegment.cpp
+IceTriangle.cpp
+IceUtils.cpp
+OPC_AABBCollider.cpp
+OPC_AABBTree.cpp
+OPC_BaseModel.cpp
+OPC_Collider.cpp
+OPC_Common.cpp
+Generating Code...
+Compiling...
+OPC_HybridModel.cpp
+OPC_MeshInterface.cpp
+OPC_Model.cpp
+OPC_OBBCollider.cpp
+OPC_OptimizedTree.cpp
+OPC_RayCollider.cpp
+OPC_TreeBuilders.cpp
+OPC_TreeCollider.cpp
+OPC_VolumeCollider.cpp
+Opcode.cpp
+Generating Code...
+Creating library...
+
+
+
+<h3>Results</h3>
+OpcodeLib.lib - 0 error(s), 0 warning(s)
+<h3>
+--------------------Configuration: OpcodeLib - Win32 Debug--------------------
+</h3>
+<h3>Command Lines</h3>
+Creating temporary file "C:\DOCUME~1\milo\LOCALS~1\Temp\RSP68AC.tmp" with contents
+[
+/nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fp"Debug/OpcodeLib.pch" /Yu"stdafx.h" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceAABB.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceContainer.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceHPoint.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceIndexedTriangle.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceMatrix3x3.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceMatrix4x4.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceOBB.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IcePlane.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IcePoint.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceRandom.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceRay.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceRevisitedRadix.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceSegment.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceTriangle.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Ice\IceUtils.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_AABBCollider.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_AABBTree.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_BaseModel.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_Collider.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_Common.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_HybridModel.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_MeshInterface.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_Model.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_OBBCollider.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_OptimizedTree.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_RayCollider.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_TreeBuilders.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_TreeCollider.cpp"
+"C:\GameDev\Opcode\OpcodeLib\OPC_VolumeCollider.cpp"
+"C:\GameDev\Opcode\OpcodeLib\Opcode.cpp"
+]
+Creating command line "cl.exe @C:\DOCUME~1\milo\LOCALS~1\Temp\RSP68AC.tmp"
+Creating temporary file "C:\DOCUME~1\milo\LOCALS~1\Temp\RSP68AD.tmp" with contents
+[
+/nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fp"Debug/OpcodeLib.pch" /Yc"stdafx.h" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c
+"C:\GameDev\Opcode\OpcodeLib\StdAfx.cpp"
+]
+Creating command line "cl.exe @C:\DOCUME~1\milo\LOCALS~1\Temp\RSP68AD.tmp"
+Creating temporary file "C:\DOCUME~1\milo\LOCALS~1\Temp\RSP68AE.tmp" with contents
+[
+/nologo /out:"Debug\OpcodeLib.lib"
+.\Debug\IceAABB.obj
+.\Debug\IceContainer.obj
+.\Debug\IceHPoint.obj
+.\Debug\IceIndexedTriangle.obj
+.\Debug\IceMatrix3x3.obj
+.\Debug\IceMatrix4x4.obj
+.\Debug\IceOBB.obj
+.\Debug\IcePlane.obj
+.\Debug\IcePoint.obj
+.\Debug\IceRandom.obj
+.\Debug\IceRay.obj
+.\Debug\IceRevisitedRadix.obj
+.\Debug\IceSegment.obj
+.\Debug\IceTriangle.obj
+.\Debug\IceUtils.obj
+.\Debug\OPC_AABBCollider.obj
+.\Debug\OPC_AABBTree.obj
+.\Debug\OPC_BaseModel.obj
+.\Debug\OPC_Collider.obj
+.\Debug\OPC_Common.obj
+.\Debug\OPC_HybridModel.obj
+.\Debug\OPC_MeshInterface.obj
+.\Debug\OPC_Model.obj
+.\Debug\OPC_OBBCollider.obj
+.\Debug\OPC_OptimizedTree.obj
+.\Debug\OPC_RayCollider.obj
+.\Debug\OPC_TreeBuilders.obj
+.\Debug\OPC_TreeCollider.obj
+.\Debug\OPC_VolumeCollider.obj
+.\Debug\Opcode.obj
+.\Debug\StdAfx.obj
+]
+Creating command line "link.exe -lib @C:\DOCUME~1\milo\LOCALS~1\Temp\RSP68AE.tmp"
+<h3>Output Window</h3>
+Compiling...
+StdAfx.cpp
+Compiling on Windows...
+Compiling with VC++...
+Compiling...
+IceAABB.cpp
+IceContainer.cpp
+IceHPoint.cpp
+IceIndexedTriangle.cpp
+IceMatrix3x3.cpp
+IceMatrix4x4.cpp
+IceOBB.cpp
+IcePlane.cpp
+IcePoint.cpp
+IceRandom.cpp
+IceRay.cpp
+IceRevisitedRadix.cpp
+IceSegment.cpp
+IceTriangle.cpp
+IceUtils.cpp
+OPC_AABBCollider.cpp
+OPC_AABBTree.cpp
+OPC_BaseModel.cpp
+OPC_Collider.cpp
+OPC_Common.cpp
+Generating Code...
+Compiling...
+OPC_HybridModel.cpp
+OPC_MeshInterface.cpp
+OPC_Model.cpp
+OPC_OBBCollider.cpp
+OPC_OptimizedTree.cpp
+OPC_RayCollider.cpp
+OPC_TreeBuilders.cpp
+OPC_TreeCollider.cpp
+OPC_VolumeCollider.cpp
+Opcode.cpp
+Generating Code...
+Creating library...
+
+
+
+<h3>Results</h3>
+OpcodeLib.lib - 0 error(s), 0 warning(s)
+</pre>
+</body>
+</html>
diff --git a/Opcode/OpcodeLib/Readme.txt b/Opcode/OpcodeLib/Readme.txt
new file mode 100644
index 0000000..c18bef1
--- /dev/null
+++ b/Opcode/OpcodeLib/Readme.txt
@@ -0,0 +1,24 @@
+========================================================================
+ STATIC LIBRARY : OpcodeLib
+========================================================================
+
+
+AppWizard has created this OpcodeLib library for you.
+
+This file contains a summary of what you will find in each of the files that
+make up your OpcodeLib application.
+
+/////////////////////////////////////////////////////////////////////////////
+
+StdAfx.h, StdAfx.cpp
+ These files are used to build a precompiled header (PCH) file
+ named OpcodeLib.pch and a precompiled types file named StdAfx.obj.
+
+/////////////////////////////////////////////////////////////////////////////
+Other notes:
+
+AppWizard uses "TODO:" to indicate parts of the source code you
+should add to or customize.
+
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/Opcode/OpcodeLib/StdAfx.cpp b/Opcode/OpcodeLib/StdAfx.cpp
new file mode 100644
index 0000000..c9b75bb
--- /dev/null
+++ b/Opcode/OpcodeLib/StdAfx.cpp
@@ -0,0 +1,10 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+//#define ICE_MAIN
+#include "Stdafx.h"
diff --git a/Opcode/OpcodeLib/StdAfx.h b/Opcode/OpcodeLib/StdAfx.h
new file mode 100644
index 0000000..9988c25
--- /dev/null
+++ b/Opcode/OpcodeLib/StdAfx.h
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_STDAFX_H__EFB95044_1D31_11D5_8B0F_0050BAC83302__INCLUDED_)
+#define AFX_STDAFX_H__EFB95044_1D31_11D5_8B0F_0050BAC83302__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+// Insert your headers here
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+
+#include "Opcode.h"
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__EFB95044_1D31_11D5_8B0F_0050BAC83302__INCLUDED_)
diff --git a/Opcode/ReadMe.txt b/Opcode/ReadMe.txt
new file mode 100644
index 0000000..ee16382
--- /dev/null
+++ b/Opcode/ReadMe.txt
@@ -0,0 +1,171 @@
+
+ OPCODE distribution 1.3 (june 2003)
+ -----------------------
+
+ New in Opcode 1.3:
+ - fixed the divide by 0 bug that was happening when all centers where located on a coordinate axis (thanks to Jorrit T)
+ - linearized "complete" vanilla AABB trees
+ - ANSI-compliant "for" loops (for the ones porting it to Linux...)
+ - callbacks & pointers moved to mesh interface
+ - support for triangle & vertex strides
+ - optimized the sphere-triangle overlap code a bit
+ - dynamic trees (refit)
+ - more builders
+ - ValidateSubdivision in builders
+ - LSS collider
+ - primitive-bv tests can now be skipped in most volume queries
+ - temporal coherence now also works for airborne objects
+ - temporal coherence completed for boxes / all contacts, LSS, etc
+ - ray-collider now uses a callback
+ - some common "usages" have been introduced (only picking for now)
+ - SPLIT_COMPLETE removed (now implicitely using mLimit = 1)
+ - hybrid collision models
+ - sweep-and-prune code added, moved from my old Z-Collide lib
+ - it now works with meshes made of only 1 triangle (except in mesh-mesh case!)
+
+ Disclaimer:
+
+ - I forced myself to actually *do* the release today no matter what. Else it would never have been done. That's
+ why the code may not be very polished. I also removed a *lot* of things (more usages, distance queries, etc...)
+ that weren't ready for prime-time (or that were linked to too many of my supporting libs)
+
+ - Some comments may also be obsolete here and there. The old User Manual for Opcode 1.2 may not fit version 1.3
+ either, since there's a new "mesh interface" to support strides, etc.
+
+ - Everything in the "Ice" directory has been hacked out of my engine and edited until everything compiled. Don't
+ expect anything out there to be cute or something. In particular, some CPP files are not even included when not
+ needed, so you can expect some linker errors if you try messing around with them...
+
+ Otherwise, it should be just like previous version, only better. In particular, hybrid models can be very
+ memory-friendly (sometimes using like 10 times less ram than the best trees from version 1.2). The possible
+ speed hit is often invisible (if it even exists), especially using temporal coherence in "all contacts" mode.
+ (Admittedly, this depends on your particular usage pattern / what you do on collided triangles).
+
+ The sweep-and-prune code is similar to the "vanilla" version found in V-Collide (but that one's better IMHO...)
+ The simple "radix" version is often just as good, see for yourself.
+
+ OPCODE distribution 1.2 (august 2002)
+ -----------------------
+
+ New in Opcode 1.2:
+ - new VolumeCollider base class
+ - simplified callback setup
+ - you can now use callbacks or pointers (setup at compile time)
+ - destination array not needed anymore in the RayCollider (faster in-out tests)
+ - renamed classes: AABBRayCollider => RayCollider, AABBSphereCollider => SphereCollider
+ - the sphere query now only returns a list of faces (extra info discarded). On the other hand it's a lot faster.
+ - OBB, AABB and planes queries. Original OBB and AABB queries contributed by Erwin de Vries.
+ - cosmetic changes in OPC_BoxBoxOverlap.h contributed by Gottfried Chen
+ - some inlining problems fixed
+ - faster ray-mesh tests using the separating axis theorem
+ - new split value in AABB tree construction (contributed by Igor Kravtchenko). Provides faster queries most of the time.
+ - improved temporal coherence for sphere & AABB queries (works in "All contacts" mode)
+
+ Notes:
+
+ - Everything in the "Ice code" directory (in VC++) is basically copy-pasted from my engine, with a lot
+ of code removed until there was no link error anymore. Don't expect those files to be cute or anything,
+ they've never been meant to be released and they're often updated/modified/messy.
+ - Some experimental features have been removed as well. Else I would never have released the 1.2...
+ - Not as polished/optimal as I would like it to be, but that's life. I promised myself to release it
+ before october 2002 (one YEAR later ?!).... That's the only reason why it's there.
+ - Some people reported ColDet was faster. Uh, come on. They were using Opcode in
+ "All contacts" mode whereas ColDet was doing "first contact"...
+
+ OPCODE distribution 1.1 (october 2001)
+ -----------------------
+
+ New in Opcode 1.1:
+ - stabbing queries
+ - sphere queries
+ - abtract base class for colliders
+ - settings validation methods
+ - compilation flags now grouped in OPC_Settings.h
+ - smaller files, new VC++ virtual dirs (cleaner)
+
+ Notes:
+
+ - "override(baseclass)" is a personal cosmetic thing. It's the same as "virtual", but provides more info.
+ - I code in 1600*1200, so some lines may look a bit long..
+ - This version is not as polished as the previous one due to lack of time. The stabbing & sphere queries
+ can still be optimized: for example by trying other atomic overlap tests. I'm using my first ray-AABB
+ code, but the newer one seems better. Tim Schröder's one is good as well. See: www.codercorner.com/RayAABB.cpp
+ - The trees can easily be compressed even more, I save this for later (lack of time, lack of time!)
+ - I removed various tests before releasing this one:
+ - a separation line, a.k.a. "front" in QuickCD, because gains were unclear
+ - distance queries in a PQP style, because it was way too slow
+ - support for deformable models, too slow as well
+ - You can easily use Opcode to do your player-vs-world collision detection, in a Nettle/Telemachos way.
+ If someone out there wants to donate some art / level for the cause, I'd be glad to release a demo. (current
+ demo uses copyrighted art I'm not allowed to spread)
+ - Sorry for the lack of real docs and/or solid examples. I just don't have enough time.
+
+ OPCODE distribution 1.0 (march 2001)
+ -----------------------
+
+ - First release
+
+ ===============================================================================
+
+ WHAT ?
+
+ OPCODE means OPtimized COllision DEtection.
+ So this is a collision detection package similar to RAPID. Here's a
+ quick list of features:
+
+ - C++ interface, developed for Windows systems using VC++ 6.0
+ - Works on arbitrary meshes (convex or non-convex), even polygon soups
+ - Current implementation uses AABB-trees
+ - Introduces Primitive-BV overlap tests during recursive collision queries (whereas
+ standard libraries only rely on Primitive-Primitive and BV-BV tests)
+ - Introduces no-leaf trees, i.e. collision trees whose leaf nodes have been removed
+ - Supports collision queries on quantized trees (decompressed on-the-fly)
+ - Supports "first contact" or "all contacts" modes (à la RAPID)
+ - Uses temporal coherence for "first contact" mode (~10 to 20 times faster, useful
+ in rigid body simulation during bisection)
+ - Memory footprint is 7.2 times smaller than RAPID's one, which is ideal for console
+ games with limited ram (actually, if you use the unmodified RAPID code using double
+ precision, it's more like 13 times smaller...)
+ - And yet it often runs faster than RAPID (according to RDTSC, sometimes more than 5
+ times faster when objects are deeply overlapping)
+ - Performance is usually close to RAPID's one in close-proximity situations
+ - Stabbing, planes & volume queries (sphere, AABB, OBB, LSS)
+ - Sweep-and-prune
+ - Now works with deformable meshes
+ - Hybrid trees
+
+
+ What it can be used for:
+ - standard mesh-mesh collision detection (similar to RAPID, SOLID, QuickCD, PQP, ColDet...)
+ - N-body collisions (similar to V-Collide)
+ - camera-vs-world collisions (similar to Telemachos/Paul Nettle/Stan Melax articles)
+ - shadow feelers to speed up lightmap computations
+ - in-out tests to speed up voxelization processes
+ - picking
+ - rigid body simulation
+ - view frustum culling
+ - etc
+
+ WHY ?
+
+ - Because RAPID uses too many bytes.
+ - Because the idea was nice...
+
+ WHEN ?
+
+ It's been coded in march 2001 following a thread on the GD-Algorithms list.
+
+ GDAlgorithms-list mailing list
+ GDAlgorithms-list@lists.sourceforge.net
+ http://lists.sourceforge.net/lists/listinfo/gdalgorithms-list
+
+ WHO ?
+
+ Pierre Terdiman
+ June, 1, 2003
+
+ p.terdiman@wanadoo.fr
+ p.terdiman@codercorner.com
+
+ http://www.codercorner.com
+ http://www.codercorner.com/Opcode.htm
diff --git a/Opcode/StdAfx.cpp b/Opcode/StdAfx.cpp
new file mode 100644
index 0000000..c9b75bb
--- /dev/null
+++ b/Opcode/StdAfx.cpp
@@ -0,0 +1,10 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+//#define ICE_MAIN
+#include "Stdafx.h"
diff --git a/Opcode/StdAfx.h b/Opcode/StdAfx.h
new file mode 100644
index 0000000..9988c25
--- /dev/null
+++ b/Opcode/StdAfx.h
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * OPCODE - Optimized Collision Detection
+ * Copyright (C) 2001 Pierre Terdiman
+ * Homepage: http://www.codercorner.com/Opcode.htm
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_STDAFX_H__EFB95044_1D31_11D5_8B0F_0050BAC83302__INCLUDED_)
+#define AFX_STDAFX_H__EFB95044_1D31_11D5_8B0F_0050BAC83302__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+// Insert your headers here
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+
+#include "Opcode.h"
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__EFB95044_1D31_11D5_8B0F_0050BAC83302__INCLUDED_)
diff --git a/Opcode/TemporalCoherence.txt b/Opcode/TemporalCoherence.txt
new file mode 100644
index 0000000..8fde158
--- /dev/null
+++ b/Opcode/TemporalCoherence.txt
@@ -0,0 +1,32 @@
+
+> Hi John,
+>
+> I know I'll forget to tell you this if I don't write it right now....
+>
+> >(2) How is the receiving geometry for the shadow decided?
+>
+> I wrote about an LSS-test but actually performing a new VFC test (from the
+> light's view) is the same. In both cases, here's a trick to take advantage
+> of temporal coherence : test the world against a slightly larger than
+> necessary LSS or frustum. Keep the list of touched surfaces. Then next
+> frame, if the new volume is still contained within the previous one used
+for
+> the query, you can reuse the same list immediately. Actually it's a bit
+> similar to what you did in your sphere-tree, I think. Anyway, now the
+O(log
+> N) VFC is O(1) for some frames. It's not worth it for the "real" VFC, but
+> when you have N virtual frustum to test to drop N shadows, that's another
+> story.
+>
+> Two downsides:
+> - You need more ram to keep track of one list of meshes / shadow, but
+> usually it's not a lot.
+> - By using a larger volume for the query you possibly touch more
+> faces/surfaces, which will be rendered in the shadow pass. Usually it's
+not
+> a problem either since rendering is simply faster than geometric queries
+> those days. But of course, "your mileage may vary".
+>
+> Happy new year !
+>
+> Pierre
diff --git a/Parser/Parser.cpp b/Parser/Parser.cpp
new file mode 100644
index 0000000..f8dea6f
--- /dev/null
+++ b/Parser/Parser.cpp
@@ -0,0 +1,309 @@
+/* Project STARS
+ John DiCamillo Software Consulting
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: parser
+ FILE: parser.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Implementation of the generic Parser class
+*/
+
+#include "MemDebug.h"
+#include "reader.h"
+#include "token.h"
+#include "parser.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+enum KEYS { KEY_TRUE, KEY_FALSE, KEY_DEF, KEY_MINUS };
+
+void Print(const char* fmt, ...);
+
+static int dump_tokens = 0;
+
+// +-------------------------------------------------------------------+
+
+Term* error(char* msg, const Token& token)
+{
+ static char buf[1024];
+ sprintf(buf, " near '%s' in line %d.", (const char*) token.symbol(), token.line());
+
+ return error(msg, buf);
+}
+
+// +-------------------------------------------------------------------+
+
+Parser::Parser(Reader* r)
+{
+ reader = r ? r : new(__FILE__, __LINE__) ConsoleReader;
+ lexer = new(__FILE__, __LINE__) Scanner(reader);
+
+ Token::addKey("true", KEY_TRUE);
+ Token::addKey("false", KEY_FALSE);
+ Token::addKey(":", KEY_DEF);
+ Token::addKey("-", KEY_MINUS);
+}
+
+Parser::~Parser()
+{
+ delete lexer;
+ delete reader;
+ //Token::close();
+}
+
+Term*
+Parser::ParseTerm()
+{
+ Term* t = ParseTermBase();
+ if (t == 0) return t;
+
+ Term* t2 = ParseTermRest(t);
+
+ return t2;
+}
+
+Term*
+Parser::ParseTermRest(Term* base)
+{
+ Token t = lexer->Get();
+
+ switch (t.type()) {
+ default:
+ lexer->PutBack();
+ return base;
+
+ case Token::StringLiteral: {
+ // concatenate adjacent string literal tokens:
+ TermText* text = base->isText();
+ if (text) {
+ TermText* base2 = new(__FILE__, __LINE__) TermText(text->value() + t.symbol()(1, t.symbol().length()-2));
+ delete base;
+ return ParseTermRest(base2);
+ }
+ else {
+ lexer->PutBack();
+ }
+ }
+ break;
+
+ case Token::Keyword:
+ switch (t.key()) {
+ case KEY_DEF:
+ if (base->isText())
+ return new(__FILE__, __LINE__) TermDef(base->isText(), ParseTerm());
+ else
+ return error("(Parse) illegal lhs in def", t);
+
+ default:
+ lexer->PutBack();
+ return base;
+ }
+ break;
+ }
+
+ return base;
+}
+
+static int xtol(const char* p)
+{
+ int n = 0;
+
+ while (*p) {
+ char digit = *p++;
+ n *= 16;
+
+ if (digit >= '0' && digit <= '9')
+ n += digit - '0';
+
+ else if (digit >= 'a' && digit <= 'f')
+ n += digit - 'a' + 10;
+
+ else if (digit >= 'A' && digit <= 'F')
+ n += digit - 'A' + 10;
+ }
+
+ return n;
+}
+
+Term*
+Parser::ParseTermBase()
+{
+ Token t = lexer->Get();
+ int n = 0;
+ double d = 0.0;
+
+ switch (t.type()) {
+ case Token::IntLiteral: {
+ if (dump_tokens)
+ Print("%s", t.symbol().data());
+
+ char nstr[256], *p = nstr;
+ for (int i = 0; i < (int) t.symbol().length(); i++)
+ if (t.symbol()[i] != '_')
+ *p++ = t.symbol()[i];
+ *p++ = '\0';
+
+ // handle hex notation:
+ if (nstr[1] == 'x')
+ n = xtol(nstr+2);
+
+ else
+ n = atol(nstr);
+
+ return new(__FILE__, __LINE__) TermNumber(n);
+ }
+
+ case Token::FloatLiteral: {
+ if (dump_tokens)
+ Print("%s", t.symbol().data());
+
+ char nstr[256], *p = nstr;
+ for (int i = 0; i < (int) t.symbol().length(); i++)
+ if (t.symbol()[i] != '_')
+ *p++ = t.symbol()[i];
+ *p++ = '\0';
+
+ d = atof(nstr);
+ return new(__FILE__, __LINE__) TermNumber(d);
+ }
+
+ case Token::StringLiteral:
+ if (dump_tokens)
+ Print("%s", t.symbol().data());
+
+ return new(__FILE__, __LINE__) TermText(t.symbol()(1, t.symbol().length()-2));
+
+ case Token::AlphaIdent:
+ if (dump_tokens)
+ Print("%s", t.symbol().data());
+
+ return new(__FILE__, __LINE__) TermText(t.symbol());
+
+ case Token::Keyword:
+ if (dump_tokens)
+ Print("%s", t.symbol().data());
+
+ switch (t.key()) {
+ case KEY_FALSE: return new(__FILE__, __LINE__) TermBool(0);
+ case KEY_TRUE: return new(__FILE__, __LINE__) TermBool(1);
+
+ case KEY_MINUS: {
+ Token next = lexer->Get();
+ if (next.type() == Token::IntLiteral) {
+ if (dump_tokens)
+ Print("%s", next.symbol().data());
+
+ char nstr[256], *p = nstr;
+ for (int i = 0; i < (int) next.symbol().length(); i++)
+ if (next.symbol()[i] != '_')
+ *p++ = next.symbol()[i];
+ *p++ = '\0';
+
+ n = -1 * atol(nstr);
+ return new(__FILE__, __LINE__) TermNumber(n);
+ }
+ else if (next.type() == Token::FloatLiteral) {
+ if (dump_tokens)
+ Print("%s", next.symbol().data());
+
+ char nstr[256], *p = nstr;
+ for (int i = 0; i < (int) next.symbol().length(); i++)
+ if (next.symbol()[i] != '_')
+ *p++ = next.symbol()[i];
+ *p++ = '\0';
+
+ d = -1.0 * atof(nstr);
+ return new(__FILE__, __LINE__) TermNumber(d);
+ }
+ else {
+ lexer->PutBack();
+ return error("(Parse) illegal token '-': number expected", next);
+ }
+ }
+ break;
+
+ default:
+ lexer->PutBack();
+ return 0;
+ }
+
+ case Token::LParen: return ParseArray();
+
+ case Token::LBrace: return ParseStruct();
+
+ case Token::CharLiteral:
+ return error("(Parse) illegal token ", t);
+
+ default:
+ lexer->PutBack();
+ return 0;
+ }
+}
+
+TermArray*
+Parser::ParseArray()
+{
+ TermList* elems = ParseTermList(0);
+ Token end = lexer->Get();
+
+ if (end.type() != Token::RParen)
+ return (TermArray*) error("(Parse) ')' missing in array-decl", end);
+
+ return new(__FILE__, __LINE__) TermArray(elems);
+}
+
+TermStruct*
+Parser::ParseStruct()
+{
+ TermList* elems = ParseTermList(1);
+ Token end = lexer->Get();
+
+ if (end.type() != Token::RBrace)
+ return (TermStruct*) error("(Parse) '}' missing in struct", end);
+
+ return new(__FILE__, __LINE__) TermStruct(elems);
+}
+
+TermList*
+Parser::ParseTermList(int for_struct)
+{
+ TermList* tlist = new(__FILE__, __LINE__) TermList;
+
+ Term* term = ParseTerm();
+ while (term) {
+ if (for_struct && !term->isDef()) {
+ return (TermList*) error("(Parse) non-definition term in struct");
+ }
+ else if (!for_struct && term->isDef()) {
+ return (TermList*) error("(Parse) illegal definition in array");
+ }
+
+ tlist->append(term);
+ Token t = lexer->Get();
+
+ /*** OLD WAY: COMMA SEPARATORS REQUIRED ***
+ if (t.type() != Token::Comma) {
+ lexer->PutBack();
+ term = 0;
+ }
+ else
+ term = ParseTerm();
+ /*******************************************/
+
+ // NEW WAY: COMMA SEPARATORS OPTIONAL:
+ if (t.type() != Token::Comma) {
+ lexer->PutBack();
+ }
+
+ term = ParseTerm();
+ }
+
+ return tlist;
+}
+
+
+
diff --git a/Parser/Parser.h b/Parser/Parser.h
new file mode 100644
index 0000000..652c975
--- /dev/null
+++ b/Parser/Parser.h
@@ -0,0 +1,46 @@
+/* Project nGen
+ John DiCamillo Software Consulting
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: parser
+ FILE: parser.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Declaration of the generic Parser class
+*/
+
+#ifndef PARSER_H
+#define PARSER_H
+
+#include "text.h"
+#include "term.h"
+
+// +-------------------------------------------------------------------+
+
+class Reader;
+class Scanner;
+
+// +-------------------------------------------------------------------+
+
+class Parser
+{
+public:
+ Parser(Reader* r = 0);
+ ~Parser();
+
+ Term* ParseTerm();
+ Term* ParseTermBase();
+ Term* ParseTermRest(Term* base);
+ TermList* ParseTermList(int for_struct);
+ TermArray* ParseArray();
+ TermStruct* ParseStruct();
+
+private:
+ Reader* reader;
+ Scanner* lexer;
+};
+
+#endif
diff --git a/Parser/Reader.cpp b/Parser/Reader.cpp
new file mode 100644
index 0000000..14b1126
--- /dev/null
+++ b/Parser/Reader.cpp
@@ -0,0 +1,114 @@
+/* Project STARS
+ John DiCamillo Software Consulting
+ Copyright © 1997-2000. All Rights Reserved.
+
+ SUBSYSTEM: obelisk
+ FILE: reader.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Implementation of the Reader class
+*/
+
+#include "MemDebug.h"
+#include "reader.h"
+#include <stdio.h>
+#include <fstream.h>
+#include <ctype.h>
+
+
+void Print(const char* fmt, ...);
+
+// +-------------------------------------------------------------------+
+
+Text
+ConsoleReader::more()
+{
+ // loop until the user types something
+ do {
+ printPrimaryPrompt();
+ fillInputBuffer();
+ } while (! *p);
+
+ return Text(p);
+}
+
+void
+ConsoleReader::printPrimaryPrompt()
+{
+ printf("- ");
+}
+
+void
+ConsoleReader::fillInputBuffer()
+{
+ fgets(buffer, 980, stdin);
+ p = buffer;
+ while (isspace(*p)) p++;
+}
+
+// +-------------------------------------------------------------------+
+
+FileReader::FileReader(const char* fname)
+ : filename(fname), done(0)
+{ }
+
+Text
+FileReader::more()
+{
+ if (done) return Text();
+
+ ifstream fin;
+ fin.open(filename, ios::in | ios::nocreate);
+
+ if (!fin) {
+ Print("ERROR(Parse): Could not open file '%s'\n", filename);
+ return Text();
+ }
+
+ Text result;
+ char buf[1000], newline;
+
+ while (fin.get(buf, 1000)) {
+ result.append(buf);
+ fin.get(newline);
+ result.append(newline);
+ }
+
+ done = 1;
+ return result;
+}
+
+// +-------------------------------------------------------------------+
+
+BlockReader::BlockReader(const char* block)
+ : data((char*) block), done(0), length(0)
+{ }
+
+BlockReader::BlockReader(const char* block, int len)
+ : data((char*) block), done(0), length(len)
+{ }
+
+Text
+BlockReader::more()
+{
+ if (done) return Text();
+
+ if (length) {
+ Text result(data, length);
+ done = 1;
+ return result;
+ }
+ else if (data) {
+ Text result(data);
+ done = 1;
+ return result;
+ }
+
+ done = 1;
+ return Text();
+}
+
+
diff --git a/Parser/Reader.h b/Parser/Reader.h
new file mode 100644
index 0000000..4af6992
--- /dev/null
+++ b/Parser/Reader.h
@@ -0,0 +1,68 @@
+/* Project nGen
+ John DiCamillo Software Consulting
+ Copyright © 1997-2000. All Rights Reserved.
+
+ SUBSYSTEM: obelisk
+ FILE: reader.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Declaration of the Reader class
+*/
+
+#ifndef READER_H
+#define READER_H
+
+#include "text.h"
+
+// +-------------------------------------------------------------------+
+
+class Reader
+{
+public:
+ Reader() { }
+ virtual ~Reader() { }
+
+ virtual Text more() = 0;
+};
+
+class ConsoleReader : public Reader
+{
+public:
+ virtual Text more();
+
+ void printPrimaryPrompt();
+ void fillInputBuffer();
+
+private:
+ char buffer[1000];
+ char* p;
+};
+
+class FileReader : public Reader
+{
+public:
+ FileReader(const char* fname);
+ virtual Text more();
+
+private:
+ Text filename;
+ int done;
+};
+
+class BlockReader : public Reader
+{
+public:
+ BlockReader(const char* block);
+ BlockReader(const char* block, int len);
+ virtual Text more();
+
+private:
+ char* data;
+ int done;
+ int length;
+};
+
+#endif
diff --git a/Parser/Term.cpp b/Parser/Term.cpp
new file mode 100644
index 0000000..5c5f9d4
--- /dev/null
+++ b/Parser/Term.cpp
@@ -0,0 +1,122 @@
+/* Project STARS
+ John DiCamillo Software Consulting
+ Copyright © 1997-2000. All Rights Reserved.
+
+ SUBSYSTEM: Stars
+ FILE: Term.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Implementation of the Term class
+*/
+
+
+#include "MemDebug.h"
+#include "Term.h"
+
+void Print(const char* fmt, ...);
+
+// +-------------------------------------------------------------------+
+
+Term*
+error(char* s1, char* s2)
+{
+ Print("ERROR: ");
+ if (s1) Print(s1);
+ if (s2) Print(s2);
+ Print("\n\n");
+ return 0;
+}
+
+// +-------------------------------------------------------------------+
+
+void TermBool::print(int level) { if (level > 0) Print(val? "true" : "false"); else Print("..."); }
+void TermNumber::print(int level){ if (level > 0) Print("%g", val); else Print("..."); }
+void TermText::print(int level) { if (level > 0) Print("\"%s\"", val); else Print("..."); }
+
+// +-------------------------------------------------------------------+
+
+TermArray::TermArray(TermList* elist)
+{
+ elems = elist;
+}
+
+TermArray::~TermArray()
+{
+ if (elems) elems->destroy();
+ delete elems;
+}
+
+void
+TermArray::print(int level)
+{
+ if (level > 1) {
+ Print("(");
+
+ if (elems) {
+ for (int i = 0; i < elems->size(); i++) {
+ elems->at(i)->print(level-1);
+ if (i < elems->size() -1)
+ Print(", ");
+ }
+ }
+
+ Print(") ");
+ }
+ else Print("(...) ");
+}
+
+// +-------------------------------------------------------------------+
+
+TermStruct::TermStruct(TermList* elist)
+{
+ elems = elist;
+}
+
+TermStruct::~TermStruct()
+{
+ if (elems) elems->destroy();
+ delete elems;
+}
+
+void
+TermStruct::print(int level)
+{
+ if (level > 1) {
+ Print("{");
+
+ if (elems) {
+ for (int i = 0; i < elems->size(); i++) {
+ elems->at(i)->print(level-1);
+ if (i < elems->size() -1)
+ Print(", ");
+ }
+ }
+
+ Print("} ");
+ }
+ else Print("{...} ");
+}
+
+// +-------------------------------------------------------------------+
+
+TermDef::~TermDef()
+{
+ delete mname;
+ delete mval;
+}
+
+void
+TermDef::print(int level)
+{
+ if (level >= 0) {
+ mname->print(level);
+ Print(": ");
+ mval->print(level-1);
+ }
+ else Print("...");
+}
+
+// +-------------------------------------------------------------------+
diff --git a/Parser/Term.h b/Parser/Term.h
new file mode 100644
index 0000000..8597b97
--- /dev/null
+++ b/Parser/Term.h
@@ -0,0 +1,172 @@
+/* Project STARS
+ John DiCamillo Software Consulting
+ Copyright © 1997-2000. All Rights Reserved.
+
+ SUBSYSTEM: Stars
+ FILE: term.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Declaration of the Abstract Syntax Tree classes
+*/
+
+
+#ifndef TERM_H
+#define TERM_H
+
+#include "Text.h"
+#include "List.h"
+
+// +-------------------------------------------------------------------+
+
+class Term;
+class TermBool;
+class TermNumber;
+class TermText;
+class TermArray;
+class TermDef;
+class TermStruct;
+
+// +-------------------------------------------------------------------+
+
+class Term
+{
+public:
+ static const char* TYPENAME() { return "Term"; }
+
+ Term() { }
+ virtual ~Term() { }
+
+ virtual int operator==(const Term& rhs) const { return 0; }
+
+ virtual void print(int level=10) { }
+
+ // conversion tests
+ virtual Term* touch() { return this; }
+ virtual TermBool* isBool() { return 0; }
+ virtual TermNumber* isNumber() { return 0; }
+ virtual TermText* isText() { return 0; }
+ virtual TermArray* isArray() { return 0; }
+ virtual TermDef* isDef() { return 0; }
+ virtual TermStruct* isStruct() { return 0; }
+};
+
+Term* error(char*, char* = 0);
+
+// +-------------------------------------------------------------------+
+
+typedef List<Term> TermList;
+typedef ListIter<Term> TermListIter;
+
+// +-------------------------------------------------------------------+
+
+class TermBool : public Term
+{
+public:
+ static const char* TYPENAME() { return "TermBool"; }
+
+ TermBool(bool v) : val(v) { }
+
+ virtual void print(int level=10);
+ virtual TermBool* isBool() { return this; }
+ bool value() const { return val; }
+
+private:
+ bool val;
+};
+
+// +-------------------------------------------------------------------+
+
+class TermNumber : public Term
+{
+public:
+ static const char* TYPENAME() { return "TermNumber"; }
+
+ TermNumber(double v) : val(v) { }
+
+ virtual void print(int level=10);
+ virtual TermNumber* isNumber() { return this; }
+ double value() const { return val; }
+
+private:
+ double val;
+};
+
+// +-------------------------------------------------------------------+
+
+class TermText : public Term
+{
+public:
+ static const char* TYPENAME() { return "TermText"; }
+
+ TermText(const Text& v) : val(v) { }
+
+ virtual void print(int level=10);
+ virtual TermText* isText() { return this; }
+ Text value() const { return val; }
+
+private:
+ Text val;
+};
+
+// +-------------------------------------------------------------------+
+
+class TermArray : public Term
+{
+public:
+ static const char* TYPENAME() { return "TermArray"; }
+
+ TermArray(TermList* elist);
+ virtual ~TermArray();
+
+ virtual void print(int level=10);
+ virtual TermArray* isArray() { return this; }
+ TermList* elements() { return elems; }
+
+private:
+ TermList* elems;
+};
+
+// +-------------------------------------------------------------------+
+
+class TermStruct : public Term
+{
+public:
+ static const char* TYPENAME() { return "TermStruct"; }
+
+ TermStruct(TermList* elist);
+ virtual ~TermStruct();
+
+ virtual void print(int level=10);
+
+ virtual TermStruct* isStruct() { return this; }
+ TermList* elements() { return elems; }
+
+private:
+ TermList* elems;
+};
+
+// +-------------------------------------------------------------------+
+
+class TermDef : public Term
+{
+public:
+ static const char* TYPENAME() { return "TermDef"; }
+
+ TermDef(TermText* n, Term* v) : mname(n), mval(v) { }
+ virtual ~TermDef();
+
+ virtual void print(int level=10);
+ virtual TermDef* isDef() { return this; }
+
+ virtual TermText* name() { return mname; }
+ virtual Term* term() { return mval; }
+
+private:
+ TermText* mname;
+ Term* mval;
+};
+
+#endif
diff --git a/Parser/Token.cpp b/Parser/Token.cpp
new file mode 100644
index 0000000..a7470ee
--- /dev/null
+++ b/Parser/Token.cpp
@@ -0,0 +1,546 @@
+/* Project STARS
+ John DiCamillo Software Consulting
+ Copyright © 1997-2000. All Rights Reserved.
+
+ SUBSYSTEM: Stars
+ FILE: token.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Scanner class implementation
+*/
+
+#include "MemDebug.h"
+#include "Token.h"
+#include "Reader.h"
+#include "Text.h"
+
+#include <ctype.h>
+
+// +-------------------------------------------------------------------+
+
+bool Token::hidecom = true;
+char Token::combeg[3] = "//";
+char Token::comend[3] = "\n";
+char Token::altbeg[3] = "/*";
+char Token::altend[3] = "*/";
+Dictionary<int> Token::keymap;
+
+// +-------------------------------------------------------------------+
+
+Token::Token()
+ : mType(Undefined), mKey(0), mLine(0), mColumn(0)
+{
+ mLength = 0;
+ mSymbol[0] = '\0';
+}
+
+Token::Token(const Token& rhs)
+ : mType(rhs.mType), mKey(rhs.mKey), mLine(rhs.mLine), mColumn(rhs.mColumn)
+{
+ mLength = rhs.mLength;
+ if (mLength < 8) {
+ strcpy(mSymbol, rhs.mSymbol);
+ }
+ else {
+ mFullSymbol = new(__FILE__, __LINE__) char[mLength + 1];
+ strcpy(mFullSymbol, rhs.mFullSymbol);
+ }
+}
+
+Token::Token(int t)
+ : mType(t), mKey(0), mLine(0), mColumn(0)
+{
+ mLength = 0;
+ mSymbol[0] = '\0';
+}
+
+Token::Token(const char* s, int t, int k, int l, int c)
+ : mType(t), mKey(k), mLine(l), mColumn(c)
+{
+ mLength = strlen(s);
+ if (mLength < 8) {
+ strcpy(mSymbol, s);
+ }
+ else {
+ mFullSymbol = new(__FILE__, __LINE__) char[mLength + 1];
+ strcpy(mFullSymbol, s);
+ }
+}
+
+Token::Token(const Text& s, int t, int k, int l, int c)
+ : mType(t), mKey(k), mLine(l), mColumn(c)
+{
+ mLength = s.length();
+ if (mLength < 8) {
+ strcpy(mSymbol, s.data());
+ }
+ else {
+ mFullSymbol = new(__FILE__, __LINE__) char[mLength + 1];
+ strcpy(mFullSymbol, s.data());
+ }
+}
+
+Token::~Token()
+{
+ if (mLength >= 8)
+ delete [] mFullSymbol;
+}
+
+// +-------------------------------------------------------------------+
+
+void
+Token::close()
+{
+ keymap.clear();
+}
+
+// +-------------------------------------------------------------------+
+
+Token&
+Token::operator = (const Token& rhs)
+{
+ if (mLength >= 8)
+ delete [] mFullSymbol;
+
+ mLength = rhs.mLength;
+ if (mLength < 8) {
+ strcpy(mSymbol, rhs.mSymbol);
+ }
+ else {
+ mFullSymbol = new(__FILE__, __LINE__) char[mLength + 1];
+ strcpy(mFullSymbol, rhs.mFullSymbol);
+ }
+
+ mType = rhs.mType;
+ mKey = rhs.mKey;
+ mLine = rhs.mLine;
+ mColumn = rhs.mColumn;
+
+ return *this;
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+Token::match(const Token& ref) const
+{
+ if (mType == ref.mType) { // if types match
+ if (ref.mLength == 0) // if no symbol to match
+ return true; // match!
+
+ else if (mLength == ref.mLength) { // else if symbols match
+ if (mLength < 8) {
+ if (!strcmp(mSymbol, ref.mSymbol))
+ return true; // match!
+ }
+ else {
+ if (!strcmp(mFullSymbol, ref.mFullSymbol))
+ return true; // match!
+ }
+ }
+ }
+
+ return false;
+}
+
+// +-------------------------------------------------------------------+
+
+Text
+Token::symbol() const
+{
+ if (mLength < 8)
+ return Text(mSymbol);
+ else
+ return Text(mFullSymbol);
+}
+
+// +-------------------------------------------------------------------+
+
+void
+Token::addKey(const Text& k, int v)
+{
+ keymap.insert(k, v);
+}
+
+// +-------------------------------------------------------------------+
+
+void
+Token::addKeys(Dictionary<int>& keys)
+{
+ DictionaryIter<int> iter = keys;
+ while (++iter)
+ keymap.insert(iter.key(), iter.value());
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+Token::findKey(const Text& k, int& v)
+{
+ if (keymap.contains(k)) {
+ v = keymap.find(k);
+ return true;
+ }
+ else
+ return false;
+}
+
+// +-------------------------------------------------------------------+
+
+void
+Token::comments(const Text& begin, const Text& end)
+{
+ combeg[0] = begin(0);
+ if (begin.length() > 1) combeg[1] = begin(1);
+ else combeg[1] = '\0';
+
+ comend[0] = end(0);
+ if (end.length() > 1) comend[1] = end(1);
+ else comend[1] = '\0';
+}
+
+// +-------------------------------------------------------------------+
+
+void
+Token::altComments(const Text& begin, const Text& end)
+{
+ altbeg[0] = begin(0);
+ if (begin.length() > 1) altbeg[1] = begin(1);
+ else altbeg[1] = '\0';
+
+ altend[0] = end(0);
+ if (end.length() > 1) altend[1] = end(1);
+ else altend[1] = '\0';
+}
+
+// +-------------------------------------------------------------------+
+
+Text
+Token::typestr() const
+{
+ Text t = "Unknown";
+ switch (type()) {
+ case Undefined: t = "Undefined"; break;
+ case Keyword: t = "Keyword"; break;
+ case AlphaIdent: t = "AlphaIdent"; break;
+ case SymbolicIdent: t = "SymbolicIdent"; break;
+ case Comment: t = "Comment"; break;
+ case IntLiteral: t = "IntLiteral"; break;
+ case FloatLiteral: t = "FloatLiteral"; break;
+ case StringLiteral: t = "StringLiteral"; break;
+ case CharLiteral: t = "CharLiteral"; break;
+ case Dot: t = "Dot"; break;
+ case Comma: t = "Comma"; break;
+ case Colon: t = "Colon"; break;
+ case Semicolon: t = "Semicolon"; break;
+ case LParen: t = "LParen"; break;
+ case RParen: t = "RParen"; break;
+ case LBracket: t = "LBracket"; break;
+ case RBracket: t = "RBracket"; break;
+ case LBrace: t = "LBrace"; break;
+ case RBrace: t = "RBrace"; break;
+ case EOT: t = "EOT"; break;
+ case LastTokenType: t = "LastTokenType"; break;
+ }
+
+ return t;
+}
+
+// +-------------------------------------------------------------------+
+
+Text
+Token::describe(const Text& tok)
+{
+ Text d;
+
+ switch (tok(0)) {
+ case '.' : d = "Token::Dot"; break;
+ case ',' : d = "Token::Comma"; break;
+ case ';' : d = "Token::Semicolon"; break;
+ case '(' : d = "Token::LParen"; break;
+ case ')' : d = "Token::RParen"; break;
+ case '[' : d = "Token::LBracket"; break;
+ case ']' : d = "Token::RBracket"; break;
+ case '{' : d = "Token::LBrace"; break;
+ case '}' : d = "Token::RBrace"; break;
+ default : break;
+ }
+
+ if (d.length() == 0) {
+ if (isalpha(tok(0)))
+ d = "\"" + tok + "\", Token::AlphaIdent";
+ else if (isdigit(tok(0))) {
+ if (tok.contains("."))
+ d = "\"" + tok + "\", Token::FloatLiteral";
+ else
+ d = "\"" + tok + "\", Token::IntLiteral";
+ }
+ else
+ d = "\"" + tok + "\", Token::SymbolicIdent";
+ }
+
+ return d;
+}
+
+// +-------------------------------------------------------------------+
+
+Scanner::Scanner(Reader* r)
+ : reader(r), str(0), index(0), old_index(0),
+ length(0), line(0), old_line(0), lineStart(0)
+{ }
+
+Scanner::Scanner(const Scanner& rhs)
+ : index(rhs.index), old_index(rhs.old_index), length(rhs.length),
+ reader(rhs.reader),
+ line(rhs.line), old_line(0), lineStart(rhs.lineStart)
+{
+ str = new(__FILE__, __LINE__) char [strlen(rhs.str) + 1];
+ strcpy(str, rhs.str);
+}
+
+Scanner::Scanner(const Text& s)
+ : reader(0), index(0), old_index(0), length(s.length()), line(0),
+ old_line(0), lineStart(0)
+{
+ str = new(__FILE__, __LINE__) char [s.length() + 1];
+ strcpy(str, s.data());
+}
+
+Scanner::~Scanner()
+{
+ delete [] str;
+}
+
+// +-------------------------------------------------------------------+
+
+Scanner&
+Scanner::operator = (const Scanner& rhs)
+{
+ delete [] str;
+ str = new(__FILE__, __LINE__) char [strlen(rhs.str) + 1];
+ strcpy(str, rhs.str);
+
+ index = rhs.index;
+ old_index = rhs.old_index;
+ length = rhs.length;
+ line = rhs.line;
+ old_line = rhs.old_line;
+ lineStart = rhs.lineStart;
+
+ return *this;
+}
+
+// +-------------------------------------------------------------------+
+
+void
+Scanner::Load(const Text& s)
+{
+ delete [] str;
+ str = new(__FILE__, __LINE__) char [s.length() + 1];
+ strcpy(str, s.data());
+
+ index = 0;
+ old_index = 0;
+ best = Token();
+ length = s.length();
+ line = 0;
+ old_line = 0;
+ lineStart = 0;
+}
+
+// +-------------------------------------------------------------------+
+
+Token
+Scanner::Get(Need need)
+{
+ int type = Token::EOT;
+ old_index = index;
+ old_line = line;
+
+ eos = str + length;
+ p = str + index;
+
+ if (p >= eos) {
+ if (need == Demand && reader) {
+ Load(reader->more());
+ if (length > 0)
+ return Get(need);
+ }
+ return Token("", type, 0, line, 0);
+ }
+
+ while (isspace(*p) && p < eos) { // skip initial white space
+ if (*p == '\n') {
+ line++;
+ lineStart = p - str;
+ }
+ p++;
+ }
+
+ if (p >= eos) {
+ if (need == Demand && reader) {
+ Load(reader->more());
+ if (length > 0)
+ return Get(need);
+ }
+ return Token("", type, 0, line, 0);
+ }
+
+ Token result;
+ size_t start = p - str;
+
+ if (*p == '"' || *p == '\'') { // special case for quoted tokens
+
+ if (*p == '"') type = Token::StringLiteral;
+ else type = Token::CharLiteral;
+
+ char match = *p;
+ while (++p < eos) {
+ if (*p == match) { // find matching quote
+ if (*(p-1) != '\\') { // if not escaped
+ p++; // token includes matching quote
+ break;
+ }
+ }
+ }
+ }
+
+ // generic delimited comments
+ else if (*p == Token::comBeg(0) &&
+ (!Token::comBeg(1) || *(p+1) == Token::comBeg(1))) {
+ type = Token::Comment;
+ while (++p < eos) {
+ if (*p == Token::comEnd(0) &&
+ (!Token::comEnd(1) || *(p+1) == Token::comEnd(1))) {
+ p++; if (Token::comEnd(1)) p++;
+ break;
+ }
+ }
+ }
+
+ // alternate form delimited comments
+ else if (*p == Token::altBeg(0) &&
+ (!Token::altBeg(1) || *(p+1) == Token::altBeg(1))) {
+ type = Token::Comment;
+ while (++p < eos) {
+ if (*p == Token::altEnd(0) &&
+ (!Token::altEnd(1) || *(p+1) == Token::altEnd(1))) {
+ p++; if (Token::altEnd(1)) p++;
+ break;
+ }
+ }
+ }
+
+ else if (*p == '.') type = Token::Dot;
+ else if (*p == ',') type = Token::Comma;
+ else if (*p == ';') type = Token::Semicolon;
+ else if (*p == '(') type = Token::LParen;
+ else if (*p == ')') type = Token::RParen;
+ else if (*p == '[') type = Token::LBracket;
+ else if (*p == ']') type = Token::RBracket;
+ else if (*p == '{') type = Token::LBrace;
+ else if (*p == '}') type = Token::RBrace;
+
+ // use lexical sub-parser for ints and floats
+ else if (isdigit(*p))
+ type = GetNumeric();
+
+ else if (IsSymbolic(*p)) {
+ type = Token::SymbolicIdent;
+ while (IsSymbolic(*p)) p++;
+ }
+
+ else {
+ type = Token::AlphaIdent;
+ while (IsAlpha(*p)) p++;
+ }
+
+ size_t extent = (p - str) - start;
+
+ if (extent < 1) extent = 1; // always get at least one character
+
+ index = start + extent; // advance the cursor
+ int col = start - lineStart;
+ if (line == 0) col++;
+
+ char* buf = new(__FILE__, __LINE__) char [extent + 1];
+ strncpy(buf, str + start, extent);
+ buf[extent] = '\0';
+
+ if (type == Token::Comment && Token::hidecom) {
+ delete [] buf;
+ if (Token::comEnd(0) == '\n') {
+ line++;
+ lineStart = p - str;
+ }
+ return Get(need);
+ }
+
+ if (type == Token::AlphaIdent || // check for keyword
+ type == Token::SymbolicIdent) {
+ int val;
+ if (Token::findKey(Text(buf), val))
+ result = Token(buf, Token::Keyword, val, line+1, col);
+ }
+
+ if (result.mType != Token::Keyword)
+ result = Token(buf, type, 0, line+1, col);
+
+ if (line+1 > (size_t) best.mLine ||
+ (line+1 == (size_t) best.mLine && col > best.mColumn))
+ best = result;
+
+ delete [] buf;
+ return result;
+}
+
+// +-------------------------------------------------------------------+
+
+int
+Scanner::GetNumeric()
+{
+ int type = Token::IntLiteral; // assume int
+
+ if (*p == '0' && *(p+1) == 'x') { // check for hex:
+ p += 2;
+ while (isxdigit(*p)) p++;
+ return type;
+ }
+
+ while (isdigit(*p) || *p == '_') p++; // whole number part
+
+ if (*p == '.') { p++; // optional fract part
+ type = Token::FloatLiteral; // implies float
+
+ while (isdigit(*p) || *p == '_') p++; // fractional part
+ }
+
+ if (*p == 'E' || *p == 'e') { p++; // optional exponent
+ if (*p == '+' || *p == '-') p++; // which may be signed
+ while (isdigit(*p)) p++;
+
+ type = Token::FloatLiteral; // implies float
+ }
+
+ return type;
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+Scanner::IsAlpha(char c)
+{
+ return (isalpha(*p) || isdigit(*p) || (*p == '_'))?true:false;
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+Scanner::IsSymbolic(char c)
+{
+ const char* s = "+-*/\\<=>~!@#$%^&|:";
+ return strchr(s, c)?true:false;
+}
diff --git a/Parser/Token.h b/Parser/Token.h
new file mode 100644
index 0000000..870fa66
--- /dev/null
+++ b/Parser/Token.h
@@ -0,0 +1,146 @@
+/* Project STARSHATTER
+ John DiCamillo
+ Copyright © 1997-2001. All Rights Reserved.
+
+ SUBSYSTEM: Parser
+ FILE: Token.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Scanner class definition
+*/
+
+#ifndef Token_h
+#define Token_h
+
+#include "Text.h"
+#include "Dictionary.h"
+
+#pragma warning( disable : 4237)
+
+// +-------------------------------------------------------------------+
+
+class Reader;
+class Token;
+class Scanner;
+
+// +-------------------------------------------------------------------+
+
+class Token
+{
+ friend class Scanner;
+
+public:
+ // keywords must be alphanumeric identifiers or symbolic identifiers
+ enum Types { Undefined, Keyword, AlphaIdent, SymbolicIdent, Comment,
+ IntLiteral, FloatLiteral, StringLiteral, CharLiteral,
+ Dot, Comma, Colon, Semicolon,
+ LParen, RParen, LBracket, RBracket, LBrace, RBrace,
+ EOT, LastTokenType };
+
+ enum Alias { CompoundSeparator = Dot,
+ ItemSeparator = Comma,
+ StatementTerminator = Semicolon,
+ TypeIndicator = Colon,
+ Lambda = LastTokenType + 1 };
+
+ Token();
+ Token(const Token& rhs);
+ Token(int t);
+ Token(const char* s, int t, int k=0, int l=0, int c=0);
+ Token(const Text& s, int t, int k=0, int l=0, int c=0);
+ ~Token();
+
+ Token& operator = (const Token& rhs);
+
+ bool match(const Token& ref) const;
+
+ Text symbol() const;
+ int type() const { return mType; }
+ int key() const { return mKey; }
+ int line() const { return mLine; }
+ int column() const { return mColumn; }
+
+ Text typestr() const;
+
+ static Text describe(const Text& tok);
+ static void addKey(const Text& k, int v);
+ static void addKeys(Dictionary<int>& keys);
+ static bool findKey(const Text& k, int& v);
+ static void comments(const Text& begin, const Text& end);
+ static void altComments(const Text& begin, const Text& end);
+ static void hideComments(bool hide = true) { hidecom = hide; }
+
+ static char comBeg(unsigned int i) { return combeg[i]; }
+ static char comEnd(unsigned int i) { return comend[i]; }
+ static char altBeg(unsigned int i) { return altbeg[i]; }
+ static char altEnd(unsigned int i) { return altend[i]; }
+
+ static void close();
+
+protected:
+ int mLength;
+ union {
+ char mSymbol[8];
+ char* mFullSymbol;
+ };
+ int mType;
+ int mKey;
+ int mLine;
+ int mColumn;
+
+ static bool hidecom;
+ static char combeg[3];
+ static char comend[3];
+ static char altbeg[3];
+ static char altend[3];
+
+ static Dictionary<int> keymap;
+};
+
+// +-------------------------------------------------------------------+
+
+class Scanner
+{
+public:
+ Scanner(Reader* r = 0);
+ Scanner(const Text& s);
+ Scanner(const Scanner& rhs);
+ virtual ~Scanner();
+
+ Scanner& operator = (const Scanner& rhs);
+
+ void Load(const Text& s);
+
+ enum Need { Demand, Request };
+ virtual Token Get(Need n = Demand);
+
+ void PutBack() { index = old_index; line = old_line; }
+ int GetCursor() { return index; }
+ int GetLine() { return line; }
+ void Reset(int c, int l) { index = old_index = c; line = old_line = l; }
+ Token Best() const { return best; }
+
+protected:
+ virtual int GetNumeric();
+ virtual bool IsSymbolic(char c);
+ virtual bool IsAlpha(char c);
+
+ Reader* reader;
+ char* str;
+
+ const char* p;
+ const char* eos;
+
+ size_t index;
+ size_t old_index;
+ Token best;
+ size_t length;
+ size_t line;
+ size_t old_line;
+ size_t lineStart;
+};
+
+#endif // TOKEN_H
diff --git a/Parser/stdafx.h b/Parser/stdafx.h
new file mode 100644
index 0000000..071c3ed
--- /dev/null
+++ b/Parser/stdafx.h
@@ -0,0 +1 @@
+#include "MemDebug.h" \ No newline at end of file
diff --git a/Stars45/Asteroid.cpp b/Stars45/Asteroid.cpp
new file mode 100644
index 0000000..a20a0eb
--- /dev/null
+++ b/Stars45/Asteroid.cpp
@@ -0,0 +1,116 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Asteroid.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Asteroid Sprite animation class
+*/
+
+#include "MemDebug.h"
+#include "Asteroid.h"
+#include "Shot.h"
+#include "Explosion.h"
+#include "Sim.h"
+
+#include "Solid.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+
+static Point asteroid_velocity = Point(0,0,0);
+static Model* asteroid_model[32];
+
+// +--------------------------------------------------------------------+
+
+Asteroid::Asteroid(int t, const Vec3& pos, double m)
+ : Debris(asteroid_model[t%6], pos, asteroid_velocity, m)
+{
+ life = -1;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Asteroid::Initialize()
+{
+ ZeroMemory(asteroid_model, sizeof(asteroid_model));
+
+ DataLoader* loader = DataLoader::GetLoader();
+ Text old_path = loader->GetDataPath();
+ loader->SetDataPath("Galaxy/Asteroids/");
+
+ int n = 0;
+
+ Model* a = new(__FILE__,__LINE__) Model;
+ if (a) {
+ a->Load("a1.mag", 100);
+ asteroid_model[n++] = a;
+ }
+
+ a = new(__FILE__,__LINE__) Model;
+ if (a) {
+ a->Load("a2.mag", 50);
+ asteroid_model[n++] = a;
+ }
+
+ a = new(__FILE__,__LINE__) Model;
+ if (a) {
+ a->Load("a1.mag", 8);
+ asteroid_model[n++] = a;
+ }
+
+ a = new(__FILE__,__LINE__) Model;
+ if (a) {
+ a->Load("a2.mag", 10);
+ asteroid_model[n++] = a;
+ }
+
+ a = new(__FILE__,__LINE__) Model;
+ if (a) {
+ a->Load("a3.mag", 30);
+ asteroid_model[n++] = a;
+ }
+
+ a = new(__FILE__,__LINE__) Model;
+ if (a) {
+ a->Load("a4.mag", 20);
+ asteroid_model[n++] = a;
+ }
+
+ List<Text> mod_asteroids;
+ loader->SetDataPath("Mods/Galaxy/Asteroids/");
+ loader->ListFiles("*.mag", mod_asteroids);
+
+ ListIter<Text> iter = mod_asteroids;
+ while (++iter && n < 32) {
+ a = new(__FILE__,__LINE__) Model;
+ if (a) {
+ a->Load(*iter.value(), 50);
+ asteroid_model[n++] = a;
+ }
+ }
+
+ loader->SetDataPath(old_path);
+}
+
+void
+Asteroid::Close()
+{
+ for (int i = 0; i < 32; i++)
+ delete asteroid_model[i];
+
+ ZeroMemory(asteroid_model, sizeof(asteroid_model));
+}
+
+// +--------------------------------------------------------------------+
+
+
+
diff --git a/Stars45/Asteroid.h b/Stars45/Asteroid.h
new file mode 100644
index 0000000..bbba83f
--- /dev/null
+++ b/Stars45/Asteroid.h
@@ -0,0 +1,35 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Asteroid.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Asteroid Sprite class
+*/
+
+#ifndef Asteroid_h
+#define Asteroid_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "SimObject.h"
+#include "Debris.h"
+
+// +--------------------------------------------------------------------+
+
+class Asteroid : public Debris
+{
+public:
+ Asteroid(int type, const Vec3& pos, double mass);
+
+ static void Initialize();
+ static void Close();
+};
+
+#endif Asteroid_h
+
diff --git a/Stars45/AudDlg.cpp b/Stars45/AudDlg.cpp
new file mode 100644
index 0000000..30d3c67
--- /dev/null
+++ b/Stars45/AudDlg.cpp
@@ -0,0 +1,201 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: AudDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "AudDlg.h"
+#include "MenuScreen.h"
+#include "Starshatter.h"
+#include "AudioConfig.h"
+
+#include "DataLoader.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "MachineInfo.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(AudDlg, OnApply);
+DEF_MAP_CLIENT(AudDlg, OnCancel);
+DEF_MAP_CLIENT(AudDlg, OnAudio);
+DEF_MAP_CLIENT(AudDlg, OnVideo);
+DEF_MAP_CLIENT(AudDlg, OnOptions);
+DEF_MAP_CLIENT(AudDlg, OnControls);
+DEF_MAP_CLIENT(AudDlg, OnMod);
+
+// +--------------------------------------------------------------------+
+
+AudDlg::AudDlg(Screen* s, FormDef& def, BaseScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ apply(0), cancel(0), vid_btn(0), aud_btn(0), ctl_btn(0), opt_btn(0), mod_btn(0),
+ closed(true)
+{
+ Init(def);
+}
+
+AudDlg::~AudDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AudDlg::RegisterControls()
+{
+ if (apply)
+ return;
+
+ efx_volume_slider = (Slider*) FindControl(201);
+ gui_volume_slider = (Slider*) FindControl(202);
+ wrn_volume_slider = (Slider*) FindControl(203);
+ vox_volume_slider = (Slider*) FindControl(204);
+
+ menu_music_slider = (Slider*) FindControl(205);
+ game_music_slider = (Slider*) FindControl(206);
+
+ apply = (Button*) FindControl(1);
+ REGISTER_CLIENT(EID_CLICK, apply, AudDlg, OnApply);
+
+ cancel = (Button*) FindControl(2);
+ REGISTER_CLIENT(EID_CLICK, cancel, AudDlg, OnCancel);
+
+ vid_btn = (Button*) FindControl(901);
+ REGISTER_CLIENT(EID_CLICK, vid_btn, AudDlg, OnVideo);
+
+ aud_btn = (Button*) FindControl(902);
+ REGISTER_CLIENT(EID_CLICK, aud_btn, AudDlg, OnAudio);
+
+ ctl_btn = (Button*) FindControl(903);
+ REGISTER_CLIENT(EID_CLICK, ctl_btn, AudDlg, OnControls);
+
+ opt_btn = (Button*) FindControl(904);
+ REGISTER_CLIENT(EID_CLICK, opt_btn, AudDlg, OnOptions);
+
+ mod_btn = (Button*) FindControl(905);
+ if (mod_btn) {
+ REGISTER_CLIENT(EID_CLICK, mod_btn, AudDlg, OnMod);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AudDlg::Show()
+{
+ FormWindow::Show();
+
+ if (closed && AudioConfig::GetInstance()) {
+ AudioConfig* audio = AudioConfig::GetInstance();
+
+ if (efx_volume_slider)
+ efx_volume_slider->SetValue(audio->GetEfxVolume());
+
+ if (gui_volume_slider)
+ gui_volume_slider->SetValue(audio->GetGuiVolume());
+
+ if (wrn_volume_slider)
+ wrn_volume_slider->SetValue(audio->GetWrnVolume());
+
+ if (vox_volume_slider)
+ vox_volume_slider->SetValue(audio->GetVoxVolume());
+
+ if (menu_music_slider)
+ menu_music_slider->SetValue(audio->GetMenuMusic());
+
+ if (game_music_slider)
+ game_music_slider->SetValue(audio->GetGameMusic());
+ }
+
+ if (vid_btn) vid_btn->SetButtonState(0);
+ if (aud_btn) aud_btn->SetButtonState(1);
+ if (ctl_btn) ctl_btn->SetButtonState(0);
+ if (opt_btn) opt_btn->SetButtonState(0);
+ if (mod_btn) mod_btn->SetButtonState(0);
+
+ closed = false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AudDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ OnApply(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void AudDlg::OnAudio(AWEvent* event) { manager->ShowAudDlg(); }
+void AudDlg::OnVideo(AWEvent* event) { manager->ShowVidDlg(); }
+void AudDlg::OnOptions(AWEvent* event) { manager->ShowOptDlg(); }
+void AudDlg::OnControls(AWEvent* event) { manager->ShowCtlDlg(); }
+void AudDlg::OnMod(AWEvent* event) { manager->ShowModDlg(); }
+
+// +--------------------------------------------------------------------+
+
+void
+AudDlg::OnApply(AWEvent* event)
+{
+ if (manager)
+ manager->ApplyOptions();
+}
+
+void
+AudDlg::OnCancel(AWEvent* event)
+{
+ manager->CancelOptions();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AudDlg::Apply()
+{
+ if (!closed && AudioConfig::GetInstance()) {
+ AudioConfig* audio = AudioConfig::GetInstance();
+
+ if (efx_volume_slider)
+ audio->SetEfxVolume(efx_volume_slider->GetValue());
+
+ if (gui_volume_slider)
+ audio->SetGuiVolume(gui_volume_slider->GetValue());
+
+ if (wrn_volume_slider)
+ audio->SetWrnVolume(wrn_volume_slider->GetValue());
+
+ if (vox_volume_slider)
+ audio->SetVoxVolume(vox_volume_slider->GetValue());
+
+ if (menu_music_slider)
+ audio->SetMenuMusic(menu_music_slider->GetValue());
+
+ if (game_music_slider)
+ audio->SetGameMusic(game_music_slider->GetValue());
+
+ audio->Save();
+ }
+
+ closed = true;
+}
+
+void
+AudDlg::Cancel()
+{
+ closed = true;
+}
diff --git a/Stars45/AudDlg.h b/Stars45/AudDlg.h
new file mode 100644
index 0000000..829caf7
--- /dev/null
+++ b/Stars45/AudDlg.h
@@ -0,0 +1,79 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: AudDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#ifndef AudDlg_h
+#define AudDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+
+// +--------------------------------------------------------------------+
+
+class BaseScreen;
+
+// +--------------------------------------------------------------------+
+
+class AudDlg : public FormWindow
+{
+public:
+ AudDlg(Screen* s, FormDef& def, BaseScreen* mgr);
+ virtual ~AudDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void Apply();
+ virtual void Cancel();
+
+ virtual void OnApply(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+
+ virtual void OnAudio(AWEvent* event);
+ virtual void OnVideo(AWEvent* event);
+ virtual void OnOptions(AWEvent* event);
+ virtual void OnControls(AWEvent* event);
+ virtual void OnMod(AWEvent* event);
+
+protected:
+ BaseScreen* manager;
+
+ Slider* efx_volume_slider;
+ Slider* gui_volume_slider;
+ Slider* wrn_volume_slider;
+ Slider* vox_volume_slider;
+
+ Slider* menu_music_slider;
+ Slider* game_music_slider;
+
+ Button* aud_btn;
+ Button* vid_btn;
+ Button* opt_btn;
+ Button* ctl_btn;
+ Button* mod_btn;
+
+ Button* apply;
+ Button* cancel;
+
+ bool closed;
+};
+
+#endif AudDlg_h
+
diff --git a/Stars45/AudioConfig.cpp b/Stars45/AudioConfig.cpp
new file mode 100644
index 0000000..1d6a20a
--- /dev/null
+++ b/Stars45/AudioConfig.cpp
@@ -0,0 +1,382 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: AudioConfig.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Audio Configuration class
+*/
+
+#include "MemDebug.h"
+#include "AudioConfig.h"
+
+#include "DataLoader.h"
+#include "ParseUtil.h"
+#include "Button.h"
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+
+static AudioConfig* audio_config = 0;
+
+// +--------------------------------------------------------------------+
+
+AudioConfig::AudioConfig()
+ : menu_music(90),
+ game_music(90),
+ efx_volume(90),
+ gui_volume(90),
+ wrn_volume(90),
+ vox_volume(90),
+ training(false)
+{
+ if (!audio_config)
+ audio_config = this;
+}
+
+AudioConfig::~AudioConfig()
+{
+ if (audio_config == this)
+ audio_config = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AudioConfig::Initialize()
+{
+ audio_config = new(__FILE__,__LINE__) AudioConfig;
+ if (audio_config)
+ audio_config->Load();
+}
+
+void
+AudioConfig::Close()
+{
+ delete audio_config;
+ audio_config = 0;
+}
+
+AudioConfig*
+AudioConfig::GetInstance()
+{
+ return audio_config;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+AudioConfig::MenuMusic()
+{
+ if (audio_config)
+ return -50 * (100 - audio_config->menu_music);
+
+ return 0;
+}
+
+int
+AudioConfig::GameMusic()
+{
+ int vol = 0;
+
+ if (audio_config) {
+ vol = -50 * (100 - audio_config->game_music);
+
+ if (audio_config->training)
+ vol -= 2000;
+ }
+
+ return vol;
+}
+
+int
+AudioConfig::EfxVolume()
+{
+ int vol = 0;
+
+ if (audio_config) {
+ vol = -50 * (100 - audio_config->efx_volume);
+
+ if (audio_config->training)
+ vol -= 2000;
+ }
+
+ return vol;
+}
+
+int
+AudioConfig::GuiVolume()
+{
+ if (audio_config)
+ return -50 * (100 - audio_config->gui_volume);
+
+ return 0;
+}
+
+int
+AudioConfig::WrnVolume()
+{
+ int vol = 0;
+
+ if (audio_config) {
+ vol = -50 * (100 - audio_config->wrn_volume);
+
+ if (audio_config->training)
+ vol -= 2000;
+ }
+
+ return vol;
+}
+
+int
+AudioConfig::VoxVolume()
+{
+ int vol = 0;
+
+ if (audio_config) {
+ vol = -50 * (100 - audio_config->vox_volume);
+
+ if (audio_config->training && vol < -750)
+ vol = -750;
+ }
+
+ return vol;
+}
+
+int
+AudioConfig::Silence()
+{
+ return -5000;
+}
+
+void
+AudioConfig::SetTraining(bool t)
+{
+ if (audio_config)
+ audio_config->training = t;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AudioConfig::SetMenuMusic(int v)
+{
+ if (v < 0) v = 0;
+ else if (v > 100) v = 100;
+
+ menu_music = v;
+}
+
+void
+AudioConfig::SetGameMusic(int v)
+{
+ if (v < 0) v = 0;
+ else if (v > 100) v = 100;
+
+ game_music = v;
+}
+
+void
+AudioConfig::SetEfxVolume(int v)
+{
+ if (v < 0) v = 0;
+ else if (v > 100) v = 100;
+
+ efx_volume = v;
+}
+
+void
+AudioConfig::SetGuiVolume(int v)
+{
+ if (v < 0) v = 0;
+ else if (v > 100) v = 100;
+
+ gui_volume = v;
+ Button::SetVolume(-50 * (100 - gui_volume));
+}
+
+void
+AudioConfig::SetWrnVolume(int v)
+{
+ if (v < 0) v = 0;
+ else if (v > 100) v = 100;
+
+ wrn_volume = v;
+ Button::SetVolume(-50 * (100 - wrn_volume));
+}
+
+void
+AudioConfig::SetVoxVolume(int v)
+{
+ if (v < 0) v = 0;
+ else if (v > 100) v = 100;
+
+ vox_volume = v;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AudioConfig::Load()
+{
+ DataLoader* loader = DataLoader::GetLoader();
+ Text old_path = loader->GetDataPath();
+ loader->SetDataPath(0);
+
+ // read the config file:
+ BYTE* block = 0;
+ int blocklen = 0;
+ const char* filename = "audio.cfg";
+
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ ::fseek(f, 0, SEEK_END);
+ blocklen = ftell(f);
+ ::fseek(f, 0, SEEK_SET);
+
+ block = new(__FILE__,__LINE__) BYTE[blocklen+1];
+ block[blocklen] = 0;
+
+ ::fread(block, blocklen, 1, f);
+ ::fclose(f);
+ }
+
+ if (blocklen == 0)
+ return;
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'.\n", filename);
+ exit(-3);
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "AUDIO") {
+ Print("WARNING: invalid %s file. Using defaults\n", filename);
+ return;
+ }
+ }
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ int v = 0;
+ TermDef* def = term->isDef();
+
+ if (def) {
+ if (def->name()->value() == "menu_music") {
+ GetDefNumber(v, def, filename);
+
+ if (v < 0 || v > 100) {
+ Print("WARNING: Invalid menu_music (%d) in '%s'\n", v, filename);
+ }
+ else {
+ menu_music = v;
+ }
+ }
+
+ else if (def->name()->value() == "game_music") {
+ GetDefNumber(v, def, filename);
+
+ if (v < 0 || v > 100) {
+ Print("WARNING: Invalid game_music (%d) in '%s'\n", v, filename);
+ }
+ else {
+ game_music = v;
+ }
+ }
+
+ else if (def->name()->value() == "efx_volume") {
+ GetDefNumber(v, def, filename);
+
+ if (v < 0 || v > 100) {
+ Print("WARNING: Invalid efx_volume (%d) in '%s'\n", v, filename);
+ }
+ else {
+ efx_volume = v;
+ }
+ }
+
+ else if (def->name()->value() == "gui_volume") {
+ GetDefNumber(v, def, filename);
+
+ if (v < 0 || v > 100) {
+ Print("WARNING: Invalid gui_volume (%d) in '%s'\n", v, filename);
+ }
+ else {
+ gui_volume = v;
+
+ Button::SetVolume(-50 * (100 - gui_volume));
+ }
+ }
+
+ else if (def->name()->value() == "wrn_volume") {
+ GetDefNumber(v, def, filename);
+
+ if (v < 0 || v > 100) {
+ Print("WARNING: Invalid wrn_volume (%d) in '%s'\n", v, filename);
+ }
+ else {
+ wrn_volume = v;
+ }
+ }
+
+ else if (def->name()->value() == "vox_volume") {
+ GetDefNumber(v, def, filename);
+
+ if (v < 0 || v > 100) {
+ Print("WARNING: Invalid vox_volume (%d) in '%s'\n", v, filename);
+ }
+ else {
+ vox_volume = v;
+ }
+ }
+
+ else
+ Print("WARNING: unknown label '%s' in '%s'\n",
+ def->name()->value().data(), filename);
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ term->print();
+ Print("\n");
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+ loader->SetDataPath(old_path);
+}
+
+void
+AudioConfig::Save()
+{
+ FILE* f = fopen("audio.cfg", "w");
+ if (f) {
+ fprintf(f, "AUDIO\n\n");
+ fprintf(f, "menu_music: %3d\n", menu_music);
+ fprintf(f, "game_music: %3d\n\n", game_music);
+ fprintf(f, "efx_volume: %3d\n", efx_volume);
+ fprintf(f, "gui_volume: %3d\n", gui_volume);
+ fprintf(f, "wrn_volume: %3d\n", wrn_volume);
+ fprintf(f, "vox_volume: %3d\n", vox_volume);
+ fclose(f);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+
+
diff --git a/Stars45/AudioConfig.h b/Stars45/AudioConfig.h
new file mode 100644
index 0000000..5264f41
--- /dev/null
+++ b/Stars45/AudioConfig.h
@@ -0,0 +1,71 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: AudioConfig.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Audio Configuration class
+*/
+
+#ifndef AudioConfig_h
+#define AudioConfig_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class AudioConfig
+{
+public:
+ AudioConfig();
+ ~AudioConfig();
+
+ static void Initialize();
+ static void Close();
+ static AudioConfig* GetInstance();
+
+ void Load();
+ void Save();
+
+ static int MenuMusic();
+ static int GameMusic();
+ static int EfxVolume();
+ static int GuiVolume();
+ static int WrnVolume();
+ static int VoxVolume();
+ static int Silence();
+ static void SetTraining(bool t);
+
+ int GetMenuMusic() const { return menu_music; }
+ int GetGameMusic() const { return game_music; }
+ int GetEfxVolume() const { return efx_volume; }
+ int GetGuiVolume() const { return gui_volume; }
+ int GetWrnVolume() const { return wrn_volume; }
+ int GetVoxVolume() const { return vox_volume; }
+
+ void SetMenuMusic(int v);
+ void SetGameMusic(int v);
+ void SetEfxVolume(int v);
+ void SetGuiVolume(int v);
+ void SetWrnVolume(int v);
+ void SetVoxVolume(int v);
+
+protected:
+ int menu_music;
+ int game_music;
+
+ int efx_volume;
+ int gui_volume;
+ int wrn_volume;
+ int vox_volume;
+
+ bool training;
+};
+
+#endif AudioConfig_h
+
diff --git a/Stars45/Authorization.cpp b/Stars45/Authorization.cpp
new file mode 100644
index 0000000..a0dd411
--- /dev/null
+++ b/Stars45/Authorization.cpp
@@ -0,0 +1,202 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Authorization.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Authorization Sprite animation class
+*/
+
+#include "MemDebug.h"
+#include "Authorization.h"
+
+#include "Game.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+static Text GetCDKeyFromRegistry()
+{
+ Text cdkey;
+ BYTE cdbuf[64];
+ DWORD cdlen = 0;
+ HKEY hkey = 0;
+
+ ZeroMemory(cdbuf, sizeof(cdbuf));
+
+ RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Matrix Games\\Starshatter",
+ 0,
+ KEY_QUERY_VALUE,
+ &hkey);
+
+ if (hkey) {
+ cdlen = 64;
+
+ LONG result =
+ RegQueryValueEx(hkey,
+ "authorized",
+ NULL,
+ NULL,
+ cdbuf,
+ &cdlen);
+
+ if (result == ERROR_SUCCESS && cdlen > 0)
+ cdkey = (const char*) cdbuf;
+
+ RegCloseKey(hkey);
+ }
+
+ return cdkey;
+}
+
+static Text GetCDKeyFromIniFile()
+{
+ Text cdkey;
+ char cdbuf[256];
+
+ ZeroMemory(cdbuf, sizeof(cdbuf));
+
+ FILE* f = fopen("maga.mg", "r");
+ if (f) {
+ bool found_section = false;
+
+ while (fgets(cdbuf, sizeof(cdbuf)-1, f)) {
+ Text line = Text(cdbuf).trim();
+ line.setSensitive(false);
+
+ if (line == "[SerialNumber]")
+ found_section = true;
+
+ if (found_section) {
+ if (line.indexOf("serial") == 0) {
+ // found the proper line in the proper section,
+ // now we need to parse the 'name = value' sentence:
+
+ const char* p = line.data();
+
+ // find the equal sign:
+ while (p && *p && *p != '=')
+ p++;
+
+ // skip the equal sign:
+ p++;
+
+ // find the string after the equal sign:
+ while (p && *p && isspace(*p))
+ p++;
+
+ if (p && *p) {
+ // deal with quoted strings:
+ int cutoff = sizeof(cdbuf)-1;
+
+ if (*p == '"') {
+ char* s = cdbuf;
+ p++;
+
+ while (*p && *p != '"' && cutoff-- > 0) {
+ *s++ = *p++;
+ }
+
+ *s = 0;
+ }
+
+ // and unquoted strings:
+ else {
+ char* s = cdbuf;
+
+ while (*p && cutoff-- > 0) {
+ *s++ = *p++;
+ }
+
+ *s = 0;
+ }
+
+ cdkey = cdbuf;
+ }
+ }
+ }
+ }
+
+ fclose(f);
+ }
+
+ return cdkey;
+}
+
+// +--------------------------------------------------------------------+
+
+static char serial_number[64];
+int execRegistrationProgram();
+
+bool
+Authorization::IsUserAuthorized()
+{
+ // XXX DEBUG ONLY!
+ // return true;
+
+ int authcode = execRegistrationProgram();
+ if (authcode != 1) {
+ ::Print("Authorization failed, code = %d\n", authcode);
+ }
+
+ return (authcode == 1);
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+Authorization::GetSerialNumber()
+{
+ return serial_number;
+}
+
+// +--------------------------------------------------------------------+
+
+int execRegistrationProgram()
+{
+ int result = 999;
+ char cmdline[256];
+ strcpy(cmdline, "SS2rez");
+
+ STARTUPINFO s;
+ ZeroMemory(&s, sizeof(s));
+ s.cb = sizeof(s);
+
+ PROCESS_INFORMATION pi;
+ ZeroMemory(&pi, sizeof(pi));
+
+ if (CreateProcess("SS2rez.exe", cmdline, 0, 0, 0, 0, 0, 0, &s, &pi)) {
+ DWORD exitcode = STILL_ACTIVE;
+
+ WaitForSingleObject(pi.hProcess, 20000);
+ GetExitCodeProcess(pi.hProcess, &exitcode);
+
+ if (exitcode != STILL_ACTIVE)
+ result = exitcode;
+
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+ else {
+ TCHAR message[256];
+ DWORD errcode = GetLastError();
+
+ ::Print(" WARN: Failed to create authorization process: %08X.\n", errcode);
+
+ if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, errcode, 0, message, 256, 0)) {
+ ::Print(" ");
+ ::Print(message);
+ ::Print("\n");
+ }
+ }
+
+ return result;
+}
+
+
diff --git a/Stars45/Authorization.h b/Stars45/Authorization.h
new file mode 100644
index 0000000..107bb5f
--- /dev/null
+++ b/Stars45/Authorization.h
@@ -0,0 +1,30 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Authorization.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Authorization Sprite class
+*/
+
+#ifndef Authorization_h
+#define Authorization_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class Authorization
+{
+public:
+ static bool IsUserAuthorized();
+ static const char* GetSerialNumber();
+};
+
+#endif Authorization_h
+
diff --git a/Stars45/AwardDlg.cpp b/Stars45/AwardDlg.cpp
new file mode 100644
index 0000000..138bdb1
--- /dev/null
+++ b/Stars45/AwardDlg.cpp
@@ -0,0 +1,152 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: AwardDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "AwardDlg.h"
+#include "PlanScreen.h"
+#include "Starshatter.h"
+#include "Ship.h"
+#include "Player.h"
+#include "Campaign.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "EditBox.h"
+#include "ImageBox.h"
+#include "FormatUtil.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "Sound.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(AwardDlg, OnClose);
+
+// +--------------------------------------------------------------------+
+
+AwardDlg::AwardDlg(Screen* s, FormDef& def, PlanScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ lbl_name(0), lbl_info(0), img_rank(0), btn_close(0), exit_latch(true)
+{
+ Init(def);
+}
+
+AwardDlg::~AwardDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AwardDlg::RegisterControls()
+{
+ lbl_name = FindControl(203);
+ lbl_info = FindControl(201);
+ img_rank = (ImageBox*) FindControl(202);
+
+ btn_close = (Button*) FindControl(1);
+ REGISTER_CLIENT(EID_CLICK, btn_close, AwardDlg, OnClose);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AwardDlg::Show()
+{
+ FormWindow::Show();
+ ShowPlayer();
+
+ exit_latch = true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AwardDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ if (!exit_latch)
+ OnClose(0);
+ }
+
+ else if (Keyboard::KeyDown(VK_ESCAPE)) {
+ if (!exit_latch)
+ OnClose(0);
+ }
+
+ else {
+ exit_latch = false;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AwardDlg::ShowPlayer()
+{
+ Player* p = Player::GetCurrentPlayer();
+
+ if (p) {
+ if (lbl_name) {
+ lbl_name->SetText(p->AwardName());
+ }
+
+ if (lbl_info) {
+ lbl_info->SetText(p->AwardDesc());
+ }
+
+ if (img_rank) {
+ img_rank->SetPicture(*p->AwardImage());
+ img_rank->Show();
+ }
+
+ Sound* congrats = p->AwardSound();
+ if (congrats) {
+ congrats->Play();
+ }
+ }
+ else {
+ if (lbl_info) lbl_info->SetText("");
+ if (img_rank) img_rank->Hide();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AwardDlg::OnClose(AWEvent* event)
+{
+ Player* player = Player::GetCurrentPlayer();
+ if (player)
+ player->ClearShowAward();
+
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars) {
+ Mouse::Show(false);
+
+ Campaign* campaign = Campaign::GetCampaign();
+ if (campaign && campaign->GetCampaignId() < Campaign::SINGLE_MISSIONS)
+ stars->SetGameMode(Starshatter::CMPN_MODE);
+ else
+ stars->SetGameMode(Starshatter::MENU_MODE);
+ }
+
+ else
+ Game::Panic("AwardDlg::OnClose() - Game instance not found");
+}
diff --git a/Stars45/AwardDlg.h b/Stars45/AwardDlg.h
new file mode 100644
index 0000000..1b84b94
--- /dev/null
+++ b/Stars45/AwardDlg.h
@@ -0,0 +1,59 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: AwardDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#ifndef AwardDlg_h
+#define AwardDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+
+// +--------------------------------------------------------------------+
+
+class PlanScreen;
+class Player;
+
+// +--------------------------------------------------------------------+
+
+class AwardDlg : public FormWindow
+{
+public:
+ AwardDlg(Screen* s, FormDef& def, PlanScreen* mgr);
+ virtual ~AwardDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnClose(AWEvent* event);
+ virtual void ShowPlayer();
+
+protected:
+ PlanScreen* manager;
+
+ ActiveWindow* lbl_name;
+ ActiveWindow* lbl_info;
+ ImageBox* img_rank;
+ Button* btn_close;
+
+ bool exit_latch;
+};
+
+#endif AwardDlg_h
+
diff --git a/Stars45/AwardShowDlg.cpp b/Stars45/AwardShowDlg.cpp
new file mode 100644
index 0000000..8ba188c
--- /dev/null
+++ b/Stars45/AwardShowDlg.cpp
@@ -0,0 +1,159 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: AwardShowDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "AwardShowDlg.h"
+#include "MenuScreen.h"
+#include "Starshatter.h"
+#include "Ship.h"
+#include "Player.h"
+#include "Campaign.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "EditBox.h"
+#include "ImageBox.h"
+#include "FormatUtil.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(AwardShowDlg, OnClose);
+
+// +--------------------------------------------------------------------+
+
+AwardShowDlg::AwardShowDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ lbl_name(0), lbl_info(0), img_rank(0), btn_close(0), exit_latch(true),
+ rank(-1), medal(-1)
+{
+ Init(def);
+}
+
+AwardShowDlg::~AwardShowDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AwardShowDlg::RegisterControls()
+{
+ lbl_name = FindControl(203);
+ lbl_info = FindControl(201);
+ img_rank = (ImageBox*) FindControl(202);
+
+ btn_close = (Button*) FindControl(1);
+ REGISTER_CLIENT(EID_CLICK, btn_close, AwardShowDlg, OnClose);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AwardShowDlg::Show()
+{
+ FormWindow::Show();
+ ShowAward();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AwardShowDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ if (!exit_latch)
+ OnClose(0);
+ }
+
+ else if (Keyboard::KeyDown(VK_ESCAPE)) {
+ if (!exit_latch)
+ OnClose(0);
+ }
+
+ else {
+ exit_latch = false;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AwardShowDlg::SetRank(int r)
+{
+ rank = r;
+ medal = -1;
+}
+
+void
+AwardShowDlg::SetMedal(int m)
+{
+ rank = -1;
+ medal = m;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AwardShowDlg::ShowAward()
+{
+ if (rank >= 0) {
+ if (lbl_name) {
+ lbl_name->SetText(Text("Rank of ") + Player::RankName(rank));
+ }
+
+ if (lbl_info) {
+ lbl_info->SetText(Player::RankDescription(rank));
+ }
+
+ if (img_rank) {
+ img_rank->SetPicture(*Player::RankInsignia(rank, 1));
+ img_rank->Show();
+ }
+ }
+
+ else if (medal >= 0) {
+ if (lbl_name) {
+ lbl_name->SetText(Player::MedalName(medal));
+ }
+
+ if (lbl_info) {
+ lbl_info->SetText(Player::MedalDescription(medal));
+ }
+
+ if (img_rank) {
+ img_rank->SetPicture(*Player::MedalInsignia(medal, 1));
+ img_rank->Show();
+ }
+ }
+
+ else {
+ if (lbl_name) lbl_name->SetText("");
+ if (lbl_info) lbl_info->SetText("");
+ if (img_rank) img_rank->Hide();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+AwardShowDlg::OnClose(AWEvent* event)
+{
+ manager->ShowPlayerDlg();
+}
diff --git a/Stars45/AwardShowDlg.h b/Stars45/AwardShowDlg.h
new file mode 100644
index 0000000..29c3208
--- /dev/null
+++ b/Stars45/AwardShowDlg.h
@@ -0,0 +1,63 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: AwardShowDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#ifndef AwardShowDlg_h
+#define AwardShowDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+
+// +--------------------------------------------------------------------+
+
+class AwardShowDlg : public FormWindow
+{
+public:
+ AwardShowDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~AwardShowDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnClose(AWEvent* event);
+ virtual void ShowAward();
+ virtual void SetRank(int r);
+ virtual void SetMedal(int r);
+
+protected:
+ MenuScreen* manager;
+
+ ActiveWindow* lbl_name;
+ ActiveWindow* lbl_info;
+ ImageBox* img_rank;
+ Button* btn_close;
+
+ bool exit_latch;
+
+ int rank;
+ int medal;
+};
+
+#endif AwardShowDlg_h
+
diff --git a/Stars45/BaseScreen.h b/Stars45/BaseScreen.h
new file mode 100644
index 0000000..ccc3ede
--- /dev/null
+++ b/Stars45/BaseScreen.h
@@ -0,0 +1,83 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: BaseScreen.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef BaseScreen_h
+#define BaseScreen_h
+
+#include "Types.h"
+#include "Bitmap.h"
+#include "Screen.h"
+
+// +--------------------------------------------------------------------+
+
+class Screen;
+class Sim;
+class Window;
+class Font;
+class NavDlg;
+class MsnElemDlg;
+class AudDlg;
+class VidDlg;
+class ModDlg;
+class ModInfoDlg;
+class OptDlg;
+class CtlDlg;
+class KeyDlg;
+class JoyDlg;
+class MsgDlg;
+
+// +--------------------------------------------------------------------+
+
+class BaseScreen
+{
+public:
+ BaseScreen() { }
+ virtual ~BaseScreen() { }
+
+ virtual void ShowNavDlg() { }
+ virtual void HideNavDlg() { }
+ virtual bool IsNavShown() { return false; }
+ virtual NavDlg* GetNavDlg() { return 0; }
+
+ virtual void ShowMsnElemDlg() { }
+ virtual void HideMsnElemDlg() { }
+ virtual MsnElemDlg* GetMsnElemDlg() { return 0; }
+
+ virtual AudDlg* GetAudDlg() const { return 0; }
+ virtual VidDlg* GetVidDlg() const { return 0; }
+ virtual ModDlg* GetModDlg() const { return 0; }
+ virtual ModInfoDlg* GetModInfoDlg() const { return 0; }
+ virtual OptDlg* GetOptDlg() const { return 0; }
+ virtual CtlDlg* GetCtlDlg() const { return 0; }
+ virtual JoyDlg* GetJoyDlg() const { return 0; }
+ virtual KeyDlg* GetKeyDlg() const { return 0; }
+
+ virtual void ShowAudDlg() { }
+ virtual void ShowVidDlg() { }
+ virtual void ShowModDlg() { }
+ virtual void ShowModInfoDlg() { }
+ virtual void ShowOptDlg() { }
+ virtual void ShowCtlDlg() { }
+ virtual void ShowJoyDlg() { }
+ virtual void ShowKeyDlg() { }
+
+ virtual void ShowMsgDlg() { }
+ virtual void HideMsgDlg() { }
+ virtual bool IsMsgShown() { return false; }
+ virtual MsgDlg* GetMsgDlg() { return 0; }
+
+ virtual void ApplyOptions() { }
+ virtual void CancelOptions() { }
+};
+
+// +--------------------------------------------------------------------+
+
+#endif BaseScreen_h
+
diff --git a/Stars45/Callsign.cpp b/Stars45/Callsign.cpp
new file mode 100644
index 0000000..db4c1d3
--- /dev/null
+++ b/Stars45/Callsign.cpp
@@ -0,0 +1,102 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Callsign.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Package Callsign catalog class
+*/
+
+#include "MemDebug.h"
+#include "Callsign.h"
+
+// +----------------------------------------------------------------------+
+
+static int callsign_index = -1;
+
+static char civilian_catalog[32][16] = {
+ "Aleph", "Vehan", "Galvin", "Caleb",
+ "Mercury", "Lancet", "Hera", "Zeus",
+ "Odin", "Thor", "Nereus", "Klono",
+ "Athena", "Helios", "Leto", "Nivas",
+
+ "Selene", "Proteus", "Triton", "Thetis",
+ "Aurora", "Ceres", "Rana", "Doradus",
+ "Perseus", "Corina", "Cygnus", "Lago",
+ "Andil", "Galen", "Temas", "Dalan"
+};
+
+static char alliance_catalog[32][16] = {
+ "Alpha", "Bravo", "Delta", "Echo",
+ "Ranger", "Magic", "Falcon", "Omega",
+ "Vulcan", "Hammer", "Nomad", "Dragon",
+ "Sierra", "Tango", "Victor", "Zulu",
+
+ "Sentry", "Wolf", "Zeta", "Jackal",
+ "Merlin", "Eagle", "Blade", "Tiger",
+ "Raptor", "Ares", "Condor", "Rogue",
+ "Hornet", "Gold", "Mustang", "Voodoo"
+};
+
+static char hegemony_catalog[32][16] = {
+ "Nagal", "Nalak", "Olkar", "Kalar",
+ "Narom", "Tranor", "Orrin", "Orlak",
+ "Nardik", "Sorrin", "Amnar", "Kizek",
+ "Orten", "Ronar", "Molkar", "Ternal",
+
+ "Martak", "Komar", "Malik", "Morgul",
+ "Kliek", "Torlan", "Arvin", "Artak",
+ "Ralka", "Sernal", "Roten", "Reza",
+ "Tinet", "Aliek", "Salar", "Sona"
+};
+
+static char pirate_catalog[32][16] = {
+ "Raider", "Skull", "Black", "Blood",
+ "Roger", "Hook", "Galleon", "Privateer",
+ "Cutlass", "Sabre", "Pike", "Blackbeard",
+ "Pistol", "Cortez", "Pirate", "Buccaneer",
+
+ "Raider", "Skull", "Black", "Blood",
+ "Morgan", "Redbeard", "Cutlass", "Sabre",
+ "Iron", "Stocks", "Quarter", "Gray",
+ "Ruby", "Cross", "Pirate", "Raider"
+};
+
+static char zolon_catalog[32][16] = {
+ "Cancer", "Krill", "Bluefin", "Scylla",
+ "Charybdis","Finback", "Mantis", "Skate",
+ "Sealion", "Lamprey", "Tarpon", "Hammerhead",
+ "Orca", "Skipjack", "Sculpin", "Thresher",
+
+ "Devilray", "Chinook", "Moray", "Seastar",
+ "Heron", "Puffin", "Rockeye", "Tiburon",
+ "Coho", "Stingray", "Mako", "Conger",
+ "Scad", "Pompano", "Tusk", "Nautilus"
+};
+
+
+// +----------------------------------------------------------------------+
+
+const char*
+Callsign::GetCallsign(int IFF)
+{
+ if (callsign_index < 0)
+ callsign_index = rand()/1000;
+
+ if (callsign_index > 31)
+ callsign_index = 0;
+
+ switch (IFF) {
+ case 0: return civilian_catalog[callsign_index++];
+ default:
+ case 1: return alliance_catalog[callsign_index++];
+ case 2: return hegemony_catalog[callsign_index++];
+ case 3: return pirate_catalog[callsign_index++];
+ case 4: return zolon_catalog[callsign_index++];
+ }
+}
diff --git a/Stars45/Callsign.h b/Stars45/Callsign.h
new file mode 100644
index 0000000..6e1b3d7
--- /dev/null
+++ b/Stars45/Callsign.h
@@ -0,0 +1,29 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Callsign.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Package Callsign catalog class
+*/
+
+#ifndef Callsign_h
+#define Callsign_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class Callsign
+{
+public:
+ static const char* GetCallsign(int IFF=1);
+};
+
+#endif Callsign_h
+
diff --git a/Stars45/CameraDirector.cpp b/Stars45/CameraDirector.cpp
new file mode 100644
index 0000000..dc8ba10
--- /dev/null
+++ b/Stars45/CameraDirector.cpp
@@ -0,0 +1,1196 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CameraDirector.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Camera Director singleton manages the main view camera based on the current ship
+*/
+
+#include "MemDebug.h"
+#include "CameraDirector.h"
+#include "Ship.h"
+#include "FlightDeck.h"
+#include "Contact.h"
+#include "Sim.h"
+#include "StarSystem.h"
+#include "Terrain.h"
+#include "HUDView.h"
+#include "DetailSet.h"
+#include "Starshatter.h"
+
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+CameraDirector* CameraDirector::instance = 0;
+
+static double range_max_limit = 300e3;
+
+// +----------------------------------------------------------------------+
+
+CameraDirector*
+CameraDirector::GetInstance()
+{
+ if (!instance)
+ instance = new(__FILE__,__LINE__) CameraDirector;
+
+ return instance;
+}
+
+// +----------------------------------------------------------------------+
+
+CameraDirector::CameraDirector()
+ : mode(MODE_COCKPIT), requested_mode(MODE_NONE), old_mode(MODE_NONE),
+ sim(0), ship(0), region(0), external_ship(0), external_body(0),
+ virt_az(0), virt_el(0), virt_x(0), virt_y(0), virt_z(0),
+ azimuth(PI/4), elevation(PI/4), az_rate(0), el_rate(0), range_rate(0),
+ range(0), range_min(100), range_max(range_max_limit),
+ base_range(0), transition(0), hud(0)
+{
+ instance = this;
+}
+
+// +--------------------------------------------------------------------+
+
+CameraDirector::~CameraDirector()
+{
+ if (instance == this)
+ instance = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::Reset()
+{
+ mode = MODE_COCKPIT;
+ requested_mode = MODE_NONE;
+ old_mode = MODE_NONE;
+
+ sim = 0;
+ ship = 0;
+ region = 0;
+ external_ship = 0;
+ external_body = 0;
+ transition = 0;
+ hud = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::SetShip(Ship* s)
+{
+ sim = Sim::GetSim();
+ hud = HUDView::GetInstance();
+
+ // can't take control of a dead ship:
+ if (s && (s->Life() == 0 || s->IsDying() || s->IsDead()))
+ return;
+
+ // leaving the old ship, so make sure it is visible:
+ if (ship && ship != s) {
+ ship->ShowRep();
+ ship->HideCockpit();
+ }
+
+ // taking control of the new ship:
+ ship = s;
+
+ if (ship) {
+ Observe(ship);
+ region = ship->GetRegion();
+
+ if (sim && ship->GetRegion() != sim->GetActiveRegion())
+ sim->ActivateRegion(ship->GetRegion());
+
+ range = ship->Radius() * 4;
+
+ if (mode == MODE_COCKPIT)
+ mode = MODE_CHASE;
+
+ SetMode(MODE_COCKPIT);
+ ExecFrame(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::SetMode(int m, double t)
+{
+ if (requested_mode == m)
+ return;
+
+ external_point = Point();
+
+ // save current mode for after transition:
+ if (m == MODE_DROP && mode != MODE_DROP)
+ old_mode = mode;
+
+ // if manually leaving drop mode, forget about
+ // restoring the previous mode when the drop
+ // expires...
+ else if (m != MODE_DROP && mode == MODE_DROP)
+ old_mode = MODE_NONE;
+
+ if (m == MODE_VIRTUAL && ship && !ship->Cockpit())
+ return;
+
+ if (mode == m) {
+ if (mode == MODE_TARGET || mode == MODE_ORBIT)
+ CycleViewObject();
+
+ return;
+ }
+
+ if (m > MODE_NONE && m < MODE_LAST) {
+ if (m <= MODE_VIRTUAL) {
+ requested_mode = m;
+ transition = t;
+ external_ship = 0;
+ ClearGroup();
+
+ // no easy way to do a smooth transition between
+ // certain modes, so just go immediately:
+ if ((mode == MODE_TARGET && m == MODE_CHASE) ||
+ (mode == MODE_COCKPIT && m == MODE_VIRTUAL) ||
+ (mode == MODE_VIRTUAL && m == MODE_COCKPIT))
+ {
+ mode = m;
+ requested_mode = 0;
+ transition = 0;
+ }
+ }
+
+ else if (m == MODE_TRANSLATE || m == MODE_ZOOM) {
+ requested_mode = m;
+ transition = t;
+ base_range = range;
+ }
+
+ else if (m >= MODE_DOCKING) {
+ mode = m;
+ transition = 0;
+ external_ship = 0;
+ ClearGroup();
+ }
+
+ virt_az = 0;
+ virt_el = 0;
+ virt_x = 0;
+ virt_y = 0;
+ virt_z = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static const char* get_camera_mode_name(int m)
+{
+ switch (m) {
+ default:
+ case CameraDirector::MODE_NONE: return ""; break;
+ case CameraDirector::MODE_COCKPIT: return ""; break;
+ case CameraDirector::MODE_CHASE: return "Chase Cam"; break;
+ case CameraDirector::MODE_TARGET: return "Padlock"; break;
+ case CameraDirector::MODE_THREAT: return "Threatlock"; break;
+ case CameraDirector::MODE_VIRTUAL: return "Virtual"; break;
+ case CameraDirector::MODE_ORBIT:
+ case CameraDirector::MODE_TRANSLATE:
+ case CameraDirector::MODE_ZOOM: return "Orbit Cam"; break;
+ case CameraDirector::MODE_DOCKING: return "Dock Cam"; break;
+ case CameraDirector::MODE_DROP: return ""; break;
+ }
+}
+
+const char*
+CameraDirector::GetModeName()
+{
+ int m = GetCameraMode();
+
+ if (m != CameraDirector::MODE_VIRTUAL) {
+ return get_camera_mode_name(m);
+ }
+ else if (instance) {
+ if (instance->virt_az > 30*DEGREES && instance->virt_az < 100*DEGREES)
+ return "RIGHT";
+
+ else if (instance->virt_az >= 100*DEGREES)
+ return "RIGHT-AFT";
+
+ else if (instance->virt_az < -30*DEGREES && instance->virt_az > -100*DEGREES)
+ return "LEFT";
+
+ else if (instance->virt_az <= -100*DEGREES)
+ return "LEFT-AFT";
+
+ else if (instance->virt_el > 15*DEGREES)
+ return "UP";
+
+ else if (instance->virt_el < -15*DEGREES)
+ return "DOWN";
+
+ return get_camera_mode_name(m);
+ }
+
+ return "";
+}
+
+int
+CameraDirector::GetCameraMode()
+{
+ if (instance) {
+ int op_mode = instance->mode;
+ if (instance->requested_mode > instance->mode)
+ op_mode = instance->requested_mode;
+ return op_mode;
+ }
+
+ return 0;
+}
+
+void
+CameraDirector::SetCameraMode(int m, double t)
+{
+ if (instance)
+ instance->SetMode(m, t);
+}
+
+// +--------------------------------------------------------------------+
+
+double
+CameraDirector::GetRangeLimit()
+{
+ return range_max_limit;
+}
+
+void
+CameraDirector::SetRangeLimit(double r)
+{
+ if (r >= 1e3)
+ range_max_limit = r;
+}
+
+void
+CameraDirector::SetRangeLimits(double min, double max)
+{
+ if (instance) {
+ instance->range_min = min;
+ instance->range_max = max;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::ClearGroup()
+{
+ external_group.clear();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::CycleViewObject()
+{
+ if (!ship) return;
+
+ Ship* current = external_ship;
+ external_ship = 0;
+
+ ListIter<Contact> iter = ship->ContactList();
+ while (++iter && !external_ship) {
+ Contact* c = iter.value();
+ Ship* c_ship = c->GetShip();
+
+ if (c_ship && !current) {
+ external_ship = c_ship;
+ }
+
+ else if (current && c_ship == current) {
+ while (++iter && !external_ship) {
+ c = iter.value();
+ if (c->ActLock())
+ external_ship = c->GetShip();
+ }
+ }
+ }
+
+ if (external_ship != current) {
+ if (external_ship) {
+ if (external_ship->Life() == 0 || external_ship->IsDying() || external_ship->IsDead()) {
+ external_point = external_ship->Location();
+ external_ship = 0;
+ }
+ else {
+ Observe(external_ship);
+ }
+ }
+
+ if (mode == MODE_ORBIT) {
+ SetMode(MODE_TRANSLATE);
+ ExternalRange(1);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::SetViewOrbital(Orbital* orb)
+{
+ external_body = orb;
+
+ if (external_body) {
+ range_min = external_body->Radius() * 2.5;
+ ClearGroup();
+ external_ship = 0;
+
+ if (sim) {
+ region = sim->FindNearestSpaceRegion(orb);
+ if (region)
+ sim->ActivateRegion(region);
+ }
+
+ if (ship && !region)
+ region = ship->GetRegion();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::SetViewObject(Ship* obj, bool quick)
+{
+ if (!ship) return;
+ if (!obj) {
+ obj = ship;
+ region = ship->GetRegion();
+ }
+
+ external_body = 0;
+ external_point = Point();
+
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (obj->GetIFF() != ship->GetIFF() && !stars->InCutscene()) {
+ // only view solid contacts:
+ Contact* c = ship->FindContact(obj);
+ if (!c || !c->ActLock())
+ return;
+ }
+
+ if (mode == MODE_TARGET) {
+ ClearGroup();
+ if (external_ship) {
+ external_ship = 0;
+ }
+ }
+ else if (mode >= MODE_ORBIT) {
+ if (quick) {
+ mode = MODE_ORBIT;
+ transition = 0;
+ }
+ else {
+ SetMode(MODE_TRANSLATE);
+ }
+
+ if (external_group.size()) {
+ ClearGroup();
+
+ if (external_ship) {
+ external_ship = 0;
+ }
+ }
+
+ else {
+ if ((obj == external_ship) || (obj==ship && external_ship==0)) {
+ if (!quick)
+ SetMode(MODE_ZOOM);
+ }
+
+ else if (external_ship) {
+ external_ship = 0;
+ }
+ }
+ }
+
+ if (external_ship != obj) {
+ external_ship = obj;
+
+ if (external_ship) {
+ region = external_ship->GetRegion();
+
+ if (external_ship->Life() == 0 || external_ship->IsDying() || external_ship->IsDead()) {
+ external_ship = 0;
+ range_min = 100;
+ }
+ else {
+ Observe(external_ship);
+
+ if (sim)
+ sim->ActivateRegion(external_ship->GetRegion());
+
+ range_min = external_ship->Radius() * 1.5;
+ }
+ }
+
+ Observe(external_ship);
+ ExternalRange(1);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::SetViewObjectGroup(ListIter<Ship> group, bool quick)
+{
+ if (!ship) return;
+
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (!stars->InCutscene()) {
+ // only view solid contacts:
+ while (++group) {
+ Ship* s = group.value();
+
+ if (s->GetIFF() != ship->GetIFF()) {
+ Contact* c = ship->FindContact(s);
+ if (!c || !c->ActLock())
+ return;
+ }
+
+ if (s->Life() == 0 || s->IsDying() || s->IsDead())
+ return;
+ }
+ }
+
+ group.reset();
+
+ if (external_group.size() > 1 &&
+ external_group.size() == group.size()) {
+
+ bool same = true;
+
+ for (int i = 0; same && i < external_group.size(); i++) {
+ if (external_group[i] != group.container()[i])
+ same = false;
+ }
+
+ if (same) {
+ SetMode(MODE_ZOOM);
+ return;
+ }
+ }
+
+ ClearGroup();
+
+ if (quick) {
+ mode = MODE_ORBIT;
+ transition = 0;
+ }
+ else {
+ SetMode(MODE_TRANSLATE);
+ }
+
+ external_group.append(group.container());
+
+ ListIter<Ship> iter = external_group;
+ while (++iter) {
+ Ship* s = iter.value();
+ region = s->GetRegion();
+ Observe(s);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::VirtualHead(double az, double el)
+{
+ if (mode == MODE_VIRTUAL || mode == MODE_TARGET || mode == MODE_COCKPIT) {
+ const double alimit = 3*PI/4;
+ const double e_lo = PI/8;
+ const double e_hi = PI/3;
+ const double escale = e_hi;
+
+ virt_az = az * alimit;
+ virt_el = el * escale;
+
+ if (virt_az > alimit)
+ virt_az = alimit;
+
+ else if (virt_az < -alimit)
+ virt_az = -alimit;
+
+ if (virt_el > e_hi)
+ virt_el = e_hi;
+
+ else if (virt_el < -e_lo)
+ virt_el = -e_lo;
+ }
+}
+
+void
+CameraDirector::VirtualHeadOffset(double x, double y, double z)
+{
+ if (mode == MODE_VIRTUAL || mode == MODE_TARGET || mode == MODE_COCKPIT) {
+ virt_x = x;
+ virt_y = y;
+ virt_z = z;
+ }
+}
+
+void
+CameraDirector::VirtualAzimuth(double delta)
+{
+ if (mode == MODE_VIRTUAL || mode == MODE_TARGET || mode == MODE_COCKPIT) {
+ virt_az += delta;
+
+ const double alimit = 3*PI/4;
+
+ if (virt_az > alimit)
+ virt_az = alimit;
+
+ else if (virt_az < -alimit)
+ virt_az = -alimit;
+ }
+}
+
+void
+CameraDirector::VirtualElevation(double delta)
+{
+ if (mode == MODE_VIRTUAL || mode == MODE_TARGET || mode == MODE_COCKPIT) {
+ virt_el += delta;
+
+ const double e_lo = PI/8;
+ const double e_hi = PI/3;
+
+ if (virt_el > e_hi)
+ virt_el = e_hi;
+
+ else if (virt_el < -e_lo)
+ virt_el = -e_lo;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::ExternalAzimuth(double delta)
+{
+ azimuth += delta;
+
+ if (azimuth > PI)
+ azimuth = -2*PI + azimuth;
+
+ else if (azimuth < -PI)
+ azimuth = 2*PI + azimuth;
+}
+
+void
+CameraDirector::ExternalElevation(double delta)
+{
+ elevation += delta;
+
+ const double limit = (0.43 * PI);
+
+ if (elevation > limit)
+ elevation = limit;
+ else if (elevation < -limit)
+ elevation = -limit;
+}
+
+void
+CameraDirector::ExternalRange(double delta)
+{
+ range *= delta;
+
+ if (ship && ship->IsAirborne())
+ range_max = 30e3;
+ else
+ range_max = range_max_limit;
+
+ if (range < range_min)
+ range = range_min;
+
+ else if (range > range_max)
+ range = range_max;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::SetOrbitPoint(double a, double e, double r)
+{
+ azimuth = a;
+ elevation = e;
+ range = r;
+
+ if (external_body) {
+ if (range < external_body->Radius() * 2)
+ range = external_body->Radius() * 2;
+
+ else if (range > external_body->Radius() * 6)
+ range = external_body->Radius() * 6;
+ }
+
+ else {
+ if (range < range_min)
+ range = range_min;
+
+ else if (range > range_max)
+ range = range_max;
+ }
+}
+
+void
+CameraDirector::SetOrbitRates(double ar, double er, double rr)
+{
+ az_rate = ar;
+ el_rate = er;
+
+ range_rate = rr;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CameraDirector::Update(SimObject* obj)
+{
+ if (obj->Type() == SimObject::SIM_SHIP) {
+ Ship* s = (Ship*) obj;
+ if (ship == s)
+ ship = 0;
+
+ if (external_ship == s) {
+ external_point = s->Location();
+ external_ship = 0;
+ }
+
+ if (external_group.contains(s))
+ external_group.remove(s);
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+CameraDirector::GetObserverName() const
+{
+ return "CameraDirector";
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::ExecFrame(double seconds)
+{
+ if (!ship)
+ return;
+
+ hud = HUDView::GetInstance();
+
+ int flight_phase = ship->GetFlightPhase();
+
+ if (flight_phase < Ship::LOCKED)
+ SetMode(MODE_DOCKING);
+
+ if (ship->IsAirborne()) {
+ if (flight_phase >= Ship::DOCKING)
+ SetMode(MODE_DOCKING);
+ }
+ else {
+ if (flight_phase >= Ship::RECOVERY)
+ SetMode(MODE_DOCKING);
+ }
+
+ if (flight_phase >= Ship::LOCKED && flight_phase < Ship::ACTIVE) {
+ int m = GetMode();
+ if (m != MODE_COCKPIT && m != MODE_VIRTUAL)
+ SetMode(MODE_COCKPIT);
+ }
+
+ if (ship->InTransition()) {
+ SetMode(MODE_DROP);
+ }
+ // automatically restore mode after transition:
+ else if (old_mode != MODE_NONE) {
+ mode = old_mode;
+ requested_mode = old_mode;
+ old_mode = MODE_NONE;
+ }
+
+ int op_mode = mode;
+ if (requested_mode > mode)
+ op_mode = requested_mode;
+
+ Starshatter* stars = Starshatter::GetInstance();
+
+ // if we are in padlock, and have not locked a ship
+ // try to padlock the current target:
+ if (op_mode == MODE_TARGET && !external_ship) {
+ SimObject* tgt = ship->GetTarget();
+ if (tgt && tgt->Type() == SimObject::SIM_SHIP)
+ SetViewObject((Ship*) tgt);
+ }
+
+ // if in an external mode, check the external ship:
+ else if (op_mode >= MODE_TARGET && op_mode <= MODE_ZOOM) {
+ if (external_ship && external_ship != ship && !stars->InCutscene()) {
+ Contact* c = ship->FindContact(external_ship);
+ if (!c || !c->ActLock()) {
+ SetViewObject(ship);
+ }
+ }
+ }
+
+ if (ship->Rep()) {
+ if (op_mode == MODE_COCKPIT) {
+ ship->HideRep();
+ ship->HideCockpit();
+ }
+ else if (op_mode == MODE_VIRTUAL || op_mode == MODE_TARGET) {
+ if (ship->Cockpit()) {
+ ship->HideRep();
+ ship->ShowCockpit();
+ }
+ else {
+ ship->ShowRep();
+ }
+ }
+ else {
+ ship->Rep()->SetForeground(op_mode == MODE_DOCKING);
+ ship->ShowRep();
+ ship->HideCockpit();
+ }
+ }
+
+ if (hud && hud->Ambient() != Color::Black)
+ sim->GetScene()->SetAmbient( hud->Ambient() );
+ else
+ sim->GetScene()->SetAmbient( sim->GetStarSystem()->Ambient() );
+
+ switch (op_mode) {
+ default:
+ case MODE_COCKPIT: Cockpit(seconds); break;
+ case MODE_CHASE: Chase(seconds); break;
+ case MODE_TARGET: Target(seconds); break;
+ case MODE_THREAT: Threat(seconds); break;
+ case MODE_VIRTUAL: Virtual(seconds); break;
+ case MODE_ORBIT:
+ case MODE_TRANSLATE:
+ case MODE_ZOOM: Orbit(seconds); break;
+ case MODE_DOCKING: Docking(seconds); break;
+ case MODE_DROP: Drop(seconds); break;
+ }
+
+ if (ship->Shake() > 0 &&
+ (op_mode < MODE_ORBIT ||
+ (op_mode == MODE_VIRTUAL && ship->Cockpit()))) {
+
+ Point vib = ship->Vibration() * 0.2;
+ camera.MoveBy(vib);
+ camera.Aim(0, vib.y, vib.z);
+ }
+
+ Transition(seconds);
+ DetailSet::SetReference(region, camera.Pos());
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::Transition(double seconds)
+{
+ if (transition > 0)
+ transition -= seconds * 1.5;
+
+ if (transition <= 0) {
+ transition = 0;
+
+ if (requested_mode && requested_mode != mode)
+ mode = requested_mode;
+
+ requested_mode = 0;
+
+ if (mode == MODE_TRANSLATE || mode == MODE_ZOOM) {
+ if (mode == MODE_ZOOM)
+ range = range_min;
+
+ mode = MODE_ORBIT;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CameraDirector::IsViewCentered()
+{
+ if (instance) {
+ double fvaz = fabs(instance->virt_az);
+ double fvel = fabs(instance->virt_el);
+
+ return fvaz < 15*DEGREES && fvel < 15*DEGREES;
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::Cockpit(double seconds)
+{
+ camera.Clone(ship->Cam());
+
+ Point bridge = ship->BridgeLocation();
+ Point cpos = camera.Pos() +
+ camera.vrt() * bridge.x +
+ camera.vpn() * bridge.y +
+ camera.vup() * bridge.z;
+
+ camera.MoveTo(cpos);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::Virtual(double seconds)
+{
+ camera.Clone(ship->Cam());
+
+ Point bridge = ship->BridgeLocation();
+ Point cpos = camera.Pos() +
+ camera.vrt() * (bridge.x + virt_x) +
+ camera.vpn() * (bridge.y + virt_z) +
+ camera.vup() * (bridge.z + virt_y);
+
+ camera.MoveTo(cpos);
+
+ camera.Yaw(virt_az);
+ camera.Pitch(-virt_el);
+
+ double fvaz = fabs(virt_az);
+ double fvel = fabs(virt_el);
+
+ if (fvaz > 0.01*DEGREES && fvaz < 15*DEGREES) {
+ double bleed = fvaz * 2;
+
+ if (virt_az > 0)
+ virt_az -= bleed * seconds;
+ else
+ virt_az += bleed * seconds;
+ }
+
+ if (fvel > 0.01*DEGREES && fvel < 15*DEGREES) {
+ double bleed = fvel;
+
+ if (virt_el > 0)
+ virt_el -= bleed * seconds;
+ else
+ virt_el += bleed * seconds;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::Chase(double seconds)
+{
+ double step = 1;
+
+ if (requested_mode == MODE_COCKPIT)
+ step = transition;
+
+ else if (requested_mode == MODE_CHASE)
+ step = 1 - transition;
+
+ camera.Clone(ship->Cam());
+ Point velocity = camera.vpn();
+
+ if (ship->Velocity().length() > 10) {
+ velocity = ship->Velocity();
+ velocity.Normalize();
+ velocity *= 0.25;
+ velocity += camera.vpn() * 2;
+ velocity.Normalize();
+ }
+
+ Point chase = ship->ChaseLocation();
+ Point bridge = ship->BridgeLocation();
+ Point cpos = camera.Pos() +
+ camera.vrt() * bridge.x * (1-step) +
+ camera.vpn() * bridge.y * (1-step) +
+ camera.vup() * bridge.z * (1-step) +
+ velocity * chase.y * step +
+ camera.vup() * chase.z * step;
+
+ camera.MoveTo(cpos);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::Target(double seconds)
+{
+ Point target_loc = external_point;
+
+ if (external_ship)
+ target_loc = external_ship->Location();
+
+ if (!external_ship || external_ship == ship) {
+ if (!external_point) {
+ if (ship->Cockpit())
+ Virtual(seconds);
+ else
+ Orbit(seconds);
+
+ return;
+ }
+ }
+
+ double step = 1;
+
+ if (requested_mode == MODE_COCKPIT)
+ step = transition;
+
+ else if (requested_mode == MODE_TARGET)
+ step = 1 - transition;
+
+ if (ship->Cockpit()) {
+ // internal padlock:
+ Cockpit(seconds);
+ camera.Padlock(target_loc, 3*PI/4, PI/8, PI/3);
+ }
+ else {
+ // external padlock:
+ Point delta = target_loc - ship->Location();
+ delta.Normalize();
+ delta *= -5 * ship->Radius() * step;
+ delta.y += ship->Radius() * step;
+
+ camera.MoveTo(ship->Location() + delta);
+ camera.LookAt(target_loc);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::Threat(double seconds)
+{
+ Chase(seconds);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::Orbit(double seconds)
+{
+ Point cpos = ship->Location();
+ int op_mode = GetCameraMode();
+
+ if (seconds < 0) seconds = 0;
+ else if (seconds > 0.2) seconds = 0.2;
+
+ // auto rotate
+ azimuth += az_rate * seconds;
+ elevation += el_rate * seconds;
+ range *= 1 + range_rate * seconds;
+
+ if (external_body && external_body->Rep()) {
+ range_min = external_body->Radius() * 2.5;
+ cpos = external_body->Rep()->Location();
+ }
+
+ else if (external_group.size()) {
+ Point neg(1e9, 1e9, 1e9);
+ Point pos(-1e9, -1e9, -1e9);
+
+ ListIter<Ship> iter(external_group);
+ while (++iter) {
+ Point loc = iter->Location();
+
+ if (loc.x < neg.x) neg.x = loc.x;
+ if (loc.x > pos.x) pos.x = loc.x;
+ if (loc.y < neg.y) neg.y = loc.y;
+ if (loc.y > pos.y) pos.y = loc.y;
+ if (loc.z < neg.z) neg.z = loc.z;
+ if (loc.z > pos.z) pos.z = loc.z;
+ }
+
+ double dx = pos.x - neg.x;
+ double dy = pos.y - neg.y;
+ double dz = pos.z - neg.z;
+
+ if (dx > dy) {
+ if (dx > dz) range_min = dx * 1.2;
+ else range_min = dz * 1.2;
+ }
+ else {
+ if (dy > dz) range_min = dy * 1.2;
+ else range_min = dz * 1.2;
+ }
+
+ // focus on median location:
+ cpos = neg + Point(dx/2, dy/2, dz/2);
+ }
+ else if (external_ship) {
+ range_min = external_ship->Radius() * 1.5;
+ cpos = external_ship->Location();
+ }
+ else {
+ range_min = ship->Radius() * 1.5;
+ cpos = ship->Location();
+ }
+
+ if (range < range_min)
+ range = range_min;
+
+ double er = range;
+ double az = azimuth;
+ double el = elevation;
+
+ if (requested_mode == MODE_TRANSLATE) {
+ cpos = cpos*(1-transition) + base_loc*(transition);
+ }
+
+ else if (requested_mode == MODE_ZOOM) {
+ er = base_range * transition;
+
+ if (er < range_min) {
+ er = range_min;
+ range = range_min;
+ transition = 0;
+ requested_mode = 0;
+ }
+ }
+
+ // transitions:
+ else if (mode < MODE_ORBIT || requested_mode > 0 && requested_mode < MODE_ORBIT) {
+ double az0 = ship->CompassHeading();
+ if (fabs(az-az0) > PI) az0 -= 2*PI;
+
+ double r0 = 0;
+ double z0 = 0;
+
+ if (mode == MODE_CHASE || requested_mode == MODE_CHASE ||
+ mode == MODE_TARGET || requested_mode == MODE_TARGET) {
+ r0 = ship->ChaseLocation().length();
+ z0 = 20 * DEGREES;
+ }
+
+ // pull out:
+ if (mode < MODE_ORBIT) {
+ er *= (1-transition);
+ az *= (1-transition);
+ el *= (1-transition);
+
+ er += r0 * transition;
+ az += az0 * transition;
+ el += z0 * transition;
+ }
+
+ // push in:
+ else {
+ er *= transition;
+ az *= transition;
+ el *= transition;
+
+ er += r0 * (1-transition);
+ az += az0 * (1-transition);
+ el += z0 * (1-transition);
+ }
+ }
+
+ else {
+ // save base location for next time we re-focus
+ base_loc = cpos;
+ }
+
+ double dx = er * sin(az) * cos(el);
+ double dy = er * cos(az) * cos(el);
+ double dz = er * sin(el);
+
+ Point cloc = cpos + Point(dx,dz,dy);
+
+ Terrain* terrain = ship->GetRegion()->GetTerrain();
+
+ if (terrain) {
+ double cam_agl = cloc.y - terrain->Height(cloc.x, cloc.z);
+
+ if (cam_agl < 100)
+ cloc.y = terrain->Height(cloc.x, cloc.z) + 100;
+ }
+
+ if (external_ship == 0 && er < 0.5 * ship->Radius())
+ ship->Rep()->Hide();
+
+ camera.MoveTo(cloc.x, cloc.y, cloc.z);
+ camera.LookAt(cpos);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::Docking(double seconds)
+{
+ FlightDeck* dock = ship->GetDock();
+
+ if (!dock) {
+ Cockpit(seconds);
+ return;
+ }
+
+ if (!ship->IsAirborne())
+ sim->GetScene()->SetAmbient(Color(120,130,140));
+ int flight_phase = ship->GetFlightPhase();
+
+ Point bridge = ship->BridgeLocation();
+ Point cloc = ship->Location() +
+ ship->Cam().vrt() * bridge.x +
+ ship->Cam().vpn() * bridge.y +
+ ship->Cam().vup() * bridge.z;
+
+ Point cpos = dock->CamLoc();
+
+ // preflight:
+ if (flight_phase < Ship::LOCKED) {
+ base_loc = cpos;
+ }
+
+ else if (flight_phase == Ship::LOCKED) {
+ if (hud)
+ hud->SetHUDMode(HUDView::HUD_MODE_TAC);
+ cpos = base_loc * transition +
+ cloc * (1-transition);
+ }
+
+ // recovery:
+ else if (flight_phase > Ship::APPROACH) {
+ if (hud)
+ hud->SetTacticalMode(1);
+ }
+
+ camera.MoveTo(cpos);
+ camera.LookAt(cloc);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraDirector::Drop(double seconds)
+{
+ // orbital transitions use "drop cam" at transition_loc
+ camera.MoveTo(ship->TransitionLocation());
+ camera.LookAt(ship->Location());
+}
diff --git a/Stars45/CameraDirector.h b/Stars45/CameraDirector.h
new file mode 100644
index 0000000..7cafb53
--- /dev/null
+++ b/Stars45/CameraDirector.h
@@ -0,0 +1,156 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CameraDirector.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Camera Director singleton manages the main view camera based on the current ship
+*/
+
+#ifndef CameraDirector_h
+#define CameraDirector_h
+
+#include "Types.h"
+#include "Camera.h"
+#include "SimObject.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class HUDView;
+class Orbital;
+class Ship;
+class Sim;
+
+// +--------------------------------------------------------------------+
+
+class CameraDirector : public SimObserver
+{
+public:
+ static const char* TYPENAME() { return "CameraDirector"; }
+
+ enum CAM_MODE {
+ MODE_NONE,
+
+ MODE_COCKPIT,
+ MODE_CHASE,
+ MODE_TARGET,
+ MODE_THREAT,
+ MODE_ORBIT,
+ MODE_VIRTUAL,
+ MODE_TRANSLATE,
+ MODE_ZOOM,
+ MODE_DOCKING,
+ MODE_DROP,
+
+ MODE_LAST
+ };
+
+ // CONSTRUCTORS:
+ CameraDirector();
+ virtual ~CameraDirector();
+
+ int operator == (const CameraDirector& that) const { return this == &that; }
+
+ static CameraDirector* GetInstance();
+
+ // CAMERA:
+ static int GetCameraMode();
+ static void SetCameraMode(int m, double trans_time=1);
+ static const char* GetModeName();
+
+ static double GetRangeLimit();
+ static void SetRangeLimit(double r);
+ static void SetRangeLimits(double min, double max);
+
+ virtual void Reset();
+ virtual void SetMode(int m, double trans_time=1);
+ virtual int GetMode() const { return requested_mode > MODE_NONE ? requested_mode : mode; }
+ virtual Camera* GetCamera() { return &camera; }
+ virtual Ship* GetShip() { return ship; }
+ virtual void SetShip(Ship* s);
+
+ virtual void VirtualHead(double az, double el);
+ virtual void VirtualHeadOffset(double x, double y, double z);
+ virtual void VirtualAzimuth(double delta);
+ virtual void VirtualElevation(double delta);
+ virtual void ExternalAzimuth(double delta);
+ virtual void ExternalElevation(double delta);
+ virtual void ExternalRange(double delta);
+ virtual void SetOrbitPoint(double az, double el, double range);
+ virtual void SetOrbitRates(double az_rate, double el_rate, double r_rate);
+
+ static bool IsViewCentered();
+
+ virtual void CycleViewObject();
+
+ virtual Orbital* GetViewOrbital() const { return external_body; }
+ virtual Ship* GetViewObject() const { return external_ship; }
+
+ virtual void SetViewOrbital(Orbital* orb);
+ virtual void SetViewObject(Ship* obj, bool quick=false);
+ virtual void SetViewObjectGroup(ListIter<Ship> group, bool quick=false);
+ virtual void ClearGroup();
+
+ // BEHAVIORS:
+ virtual void ExecFrame(double s);
+ virtual void Transition(double s);
+
+ // SimObserver:
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+protected:
+ virtual void Cockpit(double s);
+ virtual void Chase(double s);
+ virtual void Target(double s);
+ virtual void Threat(double s);
+ virtual void Virtual(double s);
+ virtual void Orbit(double s);
+ virtual void Docking(double s);
+ virtual void Drop(double s);
+
+ int mode;
+ int requested_mode;
+ int old_mode;
+ Camera camera;
+
+ Ship* ship;
+
+ SimRegion* region;
+ Orbital* external_body;
+ Ship* external_ship;
+ List<Ship> external_group;
+ Point external_point;
+
+ Point base_loc;
+
+ double virt_az;
+ double virt_el;
+ double virt_x;
+ double virt_y;
+ double virt_z;
+ double azimuth;
+ double elevation;
+ double az_rate;
+ double el_rate;
+ double range_rate;
+ double range;
+ double range_min;
+ double range_max;
+ double base_range;
+ double transition;
+
+ Sim* sim;
+ HUDView* hud;
+
+ static CameraDirector* instance;
+};
+
+#endif CameraDirector_h
+
diff --git a/Stars45/Campaign.cpp b/Stars45/Campaign.cpp
new file mode 100644
index 0000000..bfa368d
--- /dev/null
+++ b/Stars45/Campaign.cpp
@@ -0,0 +1,2347 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Campaign.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Campaign defines a strategic military scenario.
+*/
+
+#include "MemDebug.h"
+#include "Campaign.h"
+#include "CampaignPlanStrategic.h"
+#include "CampaignPlanAssignment.h"
+#include "CampaignPlanEvent.h"
+#include "CampaignPlanMission.h"
+#include "CampaignPlanMovement.h"
+#include "CampaignSituationReport.h"
+#include "CampaignSaveGame.h"
+#include "Combatant.h"
+#include "CombatAction.h"
+#include "CombatEvent.h"
+#include "CombatGroup.h"
+#include "CombatRoster.h"
+#include "CombatUnit.h"
+#include "CombatZone.h"
+#include "Galaxy.h"
+#include "Mission.h"
+#include "StarSystem.h"
+#include "Starshatter.h"
+#include "Player.h"
+
+#include "Game.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "ParseUtil.h"
+#include "Random.h"
+#include "FormatUtil.h"
+
+
+const int TIME_NEVER = (int) 1e9;
+const int ONE_DAY = (int) 24 * 3600;
+
+// +====================================================================+
+
+MissionInfo::MissionInfo()
+ : mission(0), start(0), type(0), id(0), min_rank(0), max_rank(0),
+ action_id(0), action_status(0), exec_once(0),
+ start_before(TIME_NEVER), start_after(0)
+{ }
+
+MissionInfo::~MissionInfo()
+{
+ delete mission;
+}
+
+bool
+MissionInfo::IsAvailable()
+{
+ Campaign* campaign = Campaign::GetCampaign();
+ Player* player = Player::GetCurrentPlayer();
+ CombatGroup* player_group = campaign->GetPlayerGroup();
+
+ if (campaign->GetTime() < start_after)
+ return false;
+
+ if (campaign->GetTime() > start_before)
+ return false;
+
+ if (region.length() && player_group->GetRegion() != region)
+ return false;
+
+ if (min_rank && player->Rank() < min_rank)
+ return false;
+
+ if (max_rank && player->Rank() > max_rank)
+ return false;
+
+ if (exec_once < 0)
+ return false;
+
+ if (exec_once > 0)
+ exec_once = -1;
+
+ return true;
+}
+
+// +====================================================================+
+
+TemplateList::TemplateList()
+ : mission_type(0), group_type(0), index(0)
+{ }
+
+TemplateList::~TemplateList()
+{
+ missions.destroy();
+}
+
+// +====================================================================+
+
+static List<Campaign> campaigns;
+static Campaign* current_campaign = 0;
+
+// +--------------------------------------------------------------------+
+
+Campaign::Campaign(int id, const char* n)
+ : campaign_id(id), name(n), mission_id(-1), mission(0), net_mission(0),
+ scripted(false), sequential(false), time(0), startTime(0), loadTime(0),
+ player_group(0), player_unit(0), status(CAMPAIGN_INIT), lockout(0),
+ loaded_from_savegame(false)
+{
+ ZeroMemory(path, sizeof(path));
+ Load();
+}
+
+Campaign::Campaign(int id, const char* n, const char* p)
+ : campaign_id(id), name(n), mission_id(-1), mission(0), net_mission(0),
+ scripted(false), sequential(false), time(0), startTime(0), loadTime(0),
+ player_group(0), player_unit(0), status(CAMPAIGN_INIT), lockout(0),
+ loaded_from_savegame(false)
+{
+ ZeroMemory(path, sizeof(path));
+ strncpy(path, p, sizeof(path));
+ Load();
+}
+
+// +--------------------------------------------------------------------+
+
+Campaign::~Campaign()
+{
+ for (int i = 0; i < NUM_IMAGES; i++)
+ image[i].ClearImage();
+
+ delete net_mission;
+
+ actions.destroy();
+ events.destroy();
+ missions.destroy();
+ templates.destroy();
+ planners.destroy();
+ zones.destroy();
+ combatants.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Campaign::Initialize()
+{
+ Campaign* c = 0;
+ DataLoader* loader = DataLoader::GetLoader();
+
+ for (int i = 1; i < 100; i++) {
+ char path[256];
+ sprintf(path, "Campaigns/%02d/", i);
+
+ loader->UseFileSystem(true);
+ loader->SetDataPath(path);
+
+ if (loader->FindFile("campaign.def")) {
+ char txt[256];
+ sprintf(txt, "Dynamic Campaign %02d", i);
+ c = new(__FILE__,__LINE__) Campaign(i, txt);
+
+ if (c) {
+ campaigns.insertSort(c);
+ }
+ }
+ }
+
+ c = new(__FILE__,__LINE__) Campaign(SINGLE_MISSIONS, "Single Missions");
+ if (c) {
+ campaigns.insertSort(c);
+ current_campaign = c;
+ }
+
+ c = new(__FILE__,__LINE__) Campaign(MULTIPLAYER_MISSIONS, "Multiplayer Missions");
+ if (c) {
+ campaigns.insertSort(c);
+ }
+
+ c = new(__FILE__,__LINE__) Campaign(CUSTOM_MISSIONS, "Custom Missions");
+ if (c) {
+ campaigns.insertSort(c);
+ }
+}
+
+void
+Campaign::Close()
+{
+ Print("Campaign::Close() - destroying all campaigns\n");
+ current_campaign = 0;
+ campaigns.destroy();
+}
+
+Campaign*
+Campaign::GetCampaign()
+{
+ return current_campaign;
+}
+
+Campaign*
+Campaign::SelectCampaign(const char* name)
+{
+ Campaign* c = 0;
+ ListIter<Campaign> iter = campaigns;
+
+ while (++iter && !c) {
+ if (!stricmp(iter->Name(), name))
+ c = iter.value();
+ }
+
+ if (c) {
+ Print("Campaign: Selected '%s'\n", c->Name());
+ current_campaign = c;
+ }
+ else {
+ Print("Campaign: could not find '%s'\n", name);
+ }
+
+ return c;
+}
+
+Campaign*
+Campaign::CreateCustomCampaign(const char* name, const char* path)
+{
+ int id = 0;
+
+ if (name && *name && path && *path) {
+ ListIter<Campaign> iter = campaigns;
+
+ while (++iter) {
+ Campaign* c = iter.value();
+ if (c->GetCampaignId() >= id)
+ id = c->GetCampaignId() + 1;
+
+ if (!strcmp(c->Name(), name)) {
+ Print("Campaign: custom campaign '%s' already exists.\n", name);
+ return 0;
+ }
+ }
+ }
+
+ if (id == 0)
+ id = CUSTOM_MISSIONS + 1;
+
+ Campaign* c = new(__FILE__,__LINE__) Campaign(id, name, path);
+ Print("Campaign: created custom campaign %d '%s'\n", id, name);
+ campaigns.append(c);
+
+ return c;
+}
+
+List<Campaign>&
+Campaign::GetAllCampaigns()
+{
+ return campaigns;
+}
+
+int
+Campaign::GetLastCampaignId()
+{
+ int result = 0;
+
+ for (int i = 0; i < campaigns.size(); i++) {
+ Campaign* c = campaigns.at(i);
+
+ if (c->IsDynamic() && c->GetCampaignId() > result) {
+ result = c->GetCampaignId();
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+CombatEvent*
+Campaign::GetLastEvent()
+{
+ CombatEvent* result = 0;
+
+ if (!events.isEmpty())
+ result = events.last();
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Campaign::CountNewEvents() const
+{
+ int result = 0;
+
+ for (int i = 0; i < events.size(); i++)
+ if (/*events[i]->Source() != CombatEvent::TACNET &&*/ !events[i]->Visited())
+ result++;
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Campaign::Clear()
+{
+ missions.destroy();
+ planners.destroy();
+ combatants.destroy();
+ events.destroy();
+ actions.destroy();
+
+ player_group = 0;
+ player_unit = 0;
+
+ updateTime = time;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Campaign::Load()
+{
+ // first, unload any existing data:
+ Unload();
+
+ if (!path[0]) {
+ // then load the campaign from files:
+ switch (campaign_id) {
+ case SINGLE_MISSIONS: strcpy(path, "Missions/"); break;
+ case CUSTOM_MISSIONS: strcpy(path, "Mods/Missions/"); break;
+ case MULTIPLAYER_MISSIONS: strcpy(path, "Multiplayer/"); break;
+ default: sprintf(path, "Campaigns/%02d/",
+ campaign_id); break;
+ }
+ }
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->UseFileSystem(true);
+ loader->SetDataPath(path);
+ systems.clear();
+
+ if (loader->FindFile("zones.def"))
+ zones.append(CombatZone::Load("zones.def"));
+
+ for (int i = 0; i < zones.size(); i++) {
+ Text s = zones[i]->System();
+ bool found = false;
+
+ for (int n = 0; !found && n < systems.size(); n++) {
+ if (s == systems[n]->Name())
+ found = true;
+ }
+
+ if (!found)
+ systems.append(Galaxy::GetInstance()->GetSystem(s));
+ }
+
+ loader->UseFileSystem(Starshatter::UseFileSystem());
+
+ if (loader->FindFile("campaign.def"))
+ LoadCampaign(loader);
+
+ if (campaign_id == CUSTOM_MISSIONS) {
+ loader->SetDataPath(path);
+ LoadCustomMissions(loader);
+ }
+ else {
+ bool found = false;
+
+ if (loader->FindFile("missions.def")) {
+ loader->SetDataPath(path);
+ LoadMissionList(loader);
+ found = true;
+ }
+
+ if (loader->FindFile("templates.def")) {
+ loader->SetDataPath(path);
+ LoadTemplateList(loader);
+ found = true;
+ }
+
+ if (!found) {
+ loader->SetDataPath(path);
+ LoadCustomMissions(loader);
+ }
+ }
+
+ loader->UseFileSystem(true);
+ loader->SetDataPath(path);
+
+ if (loader->FindFile("image.pcx")) {
+ loader->LoadBitmap("image.pcx", image[ 0]);
+ loader->LoadBitmap("selected.pcx", image[ 1]);
+ loader->LoadBitmap("unavail.pcx", image[ 2]);
+ loader->LoadBitmap("banner.pcx", image[ 3]);
+ }
+
+ loader->SetDataPath(0);
+ loader->UseFileSystem(Starshatter::UseFileSystem());
+}
+
+void
+Campaign::Unload()
+{
+ SetStatus(CAMPAIGN_INIT);
+
+ Game::ResetGameTime();
+ StarSystem::SetBaseTime(0);
+
+ startTime = Stardate();
+ loadTime = startTime;
+ lockout = 0;
+
+ for (int i = 0; i < NUM_IMAGES; i++)
+ image[i].ClearImage();
+
+ Clear();
+
+ zones.destroy();
+}
+
+void
+Campaign::LoadCampaign(DataLoader* loader, bool full)
+{
+ BYTE* block = 0;
+ const char* filename = "campaign.def";
+
+ loader->UseFileSystem(true);
+ loader->LoadBuffer(filename, block, true);
+ loader->UseFileSystem(Starshatter::UseFileSystem());
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
+
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "CAMPAIGN") {
+ return;
+ }
+ }
+
+ do {
+ delete term; term = 0;
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "name") {
+ if (!def->term() || !def->term()->isText()) {
+ ::Print("WARNING: name missing in '%s/%s'\n", loader->GetDataPath(), filename);
+ }
+ else {
+ name = def->term()->isText()->value();
+ name = Game::GetText(name);
+ }
+ }
+ else if (def->name()->value() == "desc") {
+ if (!def->term() || !def->term()->isText()) {
+ ::Print("WARNING: description missing in '%s/%s'\n", loader->GetDataPath(), filename);
+ }
+ else {
+ description = def->term()->isText()->value();
+ description = Game::GetText(description);
+ }
+ }
+ else if (def->name()->value() == "situation") {
+ if (!def->term() || !def->term()->isText()) {
+ ::Print("WARNING: situation missing in '%s/%s'\n", loader->GetDataPath(), filename);
+ }
+ else {
+ situation = def->term()->isText()->value();
+ situation = Game::GetText(situation);
+ }
+ }
+ else if (def->name()->value() == "orders") {
+ if (!def->term() || !def->term()->isText()) {
+ ::Print("WARNING: orders missing in '%s/%s'\n", loader->GetDataPath(), filename);
+ }
+ else {
+ orders = def->term()->isText()->value();
+ orders = Game::GetText(orders);
+ }
+ }
+ else if (def->name()->value() == "scripted") {
+ if (def->term() && def->term()->isBool()) {
+ scripted = def->term()->isBool()->value();
+ }
+ }
+ else if (def->name()->value() == "sequential") {
+ if (def->term() && def->term()->isBool()) {
+ sequential = def->term()->isBool()->value();
+ }
+ }
+ else if (full && def->name()->value() == "combatant") {
+ if (!def->term() || !def->term()->isStruct()) {
+ ::Print("WARNING: combatant struct missing in '%s/%s'\n", loader->GetDataPath(), filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+
+ char name[64];
+ int iff = 0;
+ CombatGroup* force = 0;
+ CombatGroup* clone = 0;
+
+ ZeroMemory(name, sizeof(name));
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "name") {
+ GetDefText(name, pdef, filename);
+
+ force = CombatRoster::GetInstance()->GetForce(name);
+
+ if (force)
+ clone = force->Clone(false); // shallow copy
+ }
+
+ else if (pdef->name()->value() == "group") {
+ ParseGroup(pdef->term()->isStruct(), force, clone, filename);
+ }
+ }
+ }
+
+ loader->SetDataPath(path);
+ Combatant* c = new(__FILE__,__LINE__) Combatant(name, clone);
+ if (c) {
+ combatants.append(c);
+ }
+ else {
+ Unload();
+ return;
+ }
+ }
+ }
+ else if (full && def->name()->value() == "action") {
+ if (!def->term() || !def->term()->isStruct()) {
+ ::Print("WARNING: action struct missing in '%s/%s'\n", loader->GetDataPath(), filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseAction(val, filename);
+ }
+ }
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Campaign::ParseGroup(TermStruct* val,
+ CombatGroup* force,
+ CombatGroup* clone,
+ const char* filename)
+{
+ if (!val) {
+ ::Print("invalid combat group in campaign %s\n", name.data());
+ return;
+ }
+
+ int type = 0;
+ int id = 0;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "type") {
+ char type_name[64];
+ GetDefText(type_name, pdef, filename);
+ type = CombatGroup::TypeFromName(type_name);
+ }
+
+ else if (pdef->name()->value() == "id") {
+ GetDefNumber(id, pdef, filename);
+ }
+ }
+ }
+
+ if (type && id) {
+ CombatGroup* g = force->FindGroup(type, id);
+
+ // found original group, now clone it over
+ if (g && g->GetParent()) {
+ CombatGroup* parent = CloneOver(force, clone, g->GetParent());
+ parent->AddComponent(g->Clone());
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Campaign::ParseAction(TermStruct* val, const char* filename)
+{
+ if (!val) {
+ ::Print("invalid action in campaign %s\n", name.data());
+ return;
+ }
+
+ int id = 0;
+ int type = 0;
+ int subtype = 0;
+ int opp_type = -1;
+ int team = 0;
+ int source = 0;
+ Vec3 loc(0.0f, 0.0f, 0.0f);
+ Text system;
+ Text region;
+ Text file;
+ Text image;
+ Text scene;
+ Text text;
+ int count = 1;
+ int start_before = TIME_NEVER;
+ int start_after = 0;
+ int min_rank = 0;
+ int max_rank = 100;
+ int delay = 0;
+ int probability = 100;
+
+ int asset_type = 0;
+ int asset_id = 0;
+ int target_type = 0;
+ int target_id = 0;
+ int target_iff = 0;
+
+ CombatAction* action = 0;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "id") {
+ GetDefNumber(id, pdef, filename);
+ }
+ else if (pdef->name()->value() == "type") {
+ char txt[64];
+ GetDefText(txt, pdef, filename);
+ type = CombatAction::TypeFromName(txt);
+ }
+ else if (pdef->name()->value() == "subtype") {
+ if (pdef->term()->isNumber()) {
+ GetDefNumber(subtype, pdef, filename);
+ }
+
+ else if (pdef->term()->isText()) {
+ char txt[64];
+ GetDefText(txt, pdef, filename);
+
+ if (type == CombatAction::MISSION_TEMPLATE) {
+ subtype = Mission::TypeFromName(txt);
+ }
+ else if (type == CombatAction::COMBAT_EVENT) {
+ subtype = CombatEvent::TypeFromName(txt);
+ }
+ else if (type == CombatAction::INTEL_EVENT) {
+ subtype = Intel::IntelFromName(txt);
+ }
+ }
+ }
+ else if (pdef->name()->value() == "opp_type") {
+ if (pdef->term()->isNumber()) {
+ GetDefNumber(opp_type, pdef, filename);
+ }
+
+ else if (pdef->term()->isText()) {
+ char txt[64];
+ GetDefText(txt, pdef, filename);
+
+ if (type == CombatAction::MISSION_TEMPLATE) {
+ opp_type = Mission::TypeFromName(txt);
+ }
+ }
+ }
+ else if (pdef->name()->value() == "source") {
+ char txt[64];
+ GetDefText(txt, pdef, filename);
+ source = CombatEvent::SourceFromName(txt);
+ }
+ else if (pdef->name()->value() == "team") {
+ GetDefNumber(team, pdef, filename);
+ }
+ else if (pdef->name()->value() == "iff") {
+ GetDefNumber(team, pdef, filename);
+ }
+ else if (pdef->name()->value() == "count") {
+ GetDefNumber(count, pdef, filename);
+ }
+ else if (pdef->name()->value().contains("before")) {
+ if (pdef->term()->isNumber()) {
+ GetDefNumber(start_before, pdef, filename);
+ }
+ else {
+ GetDefTime(start_before, pdef, filename);
+ start_before -= ONE_DAY;
+ }
+ }
+ else if (pdef->name()->value().contains("after")) {
+ if (pdef->term()->isNumber()) {
+ GetDefNumber(start_after, pdef, filename);
+ }
+ else {
+ GetDefTime(start_after, pdef, filename);
+ start_after -= ONE_DAY;
+ }
+ }
+ else if (pdef->name()->value() == "min_rank") {
+ if (pdef->term()->isNumber()) {
+ GetDefNumber(min_rank, pdef, filename);
+ }
+ else {
+ char rank_name[64];
+ GetDefText(rank_name, pdef, filename);
+ min_rank = Player::RankFromName(rank_name);
+ }
+ }
+ else if (pdef->name()->value() == "max_rank") {
+ if (pdef->term()->isNumber()) {
+ GetDefNumber(max_rank, pdef, filename);
+ }
+ else {
+ char rank_name[64];
+ GetDefText(rank_name, pdef, filename);
+ max_rank = Player::RankFromName(rank_name);
+ }
+ }
+ else if (pdef->name()->value() == "delay") {
+ GetDefNumber(delay, pdef, filename);
+ }
+ else if (pdef->name()->value() == "probability") {
+ GetDefNumber(probability, pdef, filename);
+ }
+ else if (pdef->name()->value() == "asset_type") {
+ char type_name[64];
+ GetDefText(type_name, pdef, filename);
+ asset_type = CombatGroup::TypeFromName(type_name);
+ }
+ else if (pdef->name()->value() == "target_type") {
+ char type_name[64];
+ GetDefText(type_name, pdef, filename);
+ target_type = CombatGroup::TypeFromName(type_name);
+ }
+ else if (pdef->name()->value() == "location" ||
+ pdef->name()->value() == "loc") {
+ GetDefVec(loc, pdef, filename);
+ }
+ else if (pdef->name()->value() == "system" ||
+ pdef->name()->value() == "sys") {
+ GetDefText(system, pdef, filename);
+ }
+ else if (pdef->name()->value() == "region" ||
+ pdef->name()->value() == "rgn" ||
+ pdef->name()->value() == "zone") {
+ GetDefText(region, pdef, filename);
+ }
+ else if (pdef->name()->value() == "file") {
+ GetDefText(file, pdef, filename);
+ }
+ else if (pdef->name()->value() == "image") {
+ GetDefText(image, pdef, filename);
+ }
+ else if (pdef->name()->value() == "scene") {
+ GetDefText(scene, pdef, filename);
+ }
+ else if (pdef->name()->value() == "text") {
+ GetDefText(text, pdef, filename);
+ text = Game::GetText(text);
+ }
+ else if (pdef->name()->value() == "asset_id") {
+ GetDefNumber(asset_id, pdef, filename);
+ }
+ else if (pdef->name()->value() == "target_id") {
+ GetDefNumber(target_id, pdef, filename);
+ }
+ else if (pdef->name()->value() == "target_iff") {
+ GetDefNumber(target_iff, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "asset_kill") {
+ if (!action)
+ action = new(__FILE__,__LINE__) CombatAction(id, type, subtype, team);
+
+ if (action) {
+ char txt[64];
+ GetDefText(txt, pdef, filename);
+ action->AssetKills().append(new (__FILE__,__LINE__) Text(txt));
+ }
+ }
+
+ else if (pdef->name()->value() == "target_kill") {
+ if (!action)
+ action = new(__FILE__,__LINE__) CombatAction(id, type, subtype, team);
+
+ if (action) {
+ char txt[64];
+ GetDefText(txt, pdef, filename);
+ action->TargetKills().append(new (__FILE__,__LINE__) Text(txt));
+ }
+ }
+
+ else if (pdef->name()->value() == "req") {
+ if (!action)
+ action = new(__FILE__,__LINE__) CombatAction(id, type, subtype, team);
+
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ ::Print("WARNING: action req struct missing in '%s'\n", filename);
+ }
+ else if (action) {
+ TermStruct* val2 = pdef->term()->isStruct();
+
+ int act = 0;
+ int stat = CombatAction::COMPLETE;
+ bool not = false;
+
+ Combatant* c1 = 0;
+ Combatant* c2 = 0;
+ int comp = 0;
+ int score = 0;
+ int intel = 0;
+ int gtype = 0;
+ int gid = 0;
+
+ for (int i = 0; i < val2->elements()->size(); i++) {
+ TermDef* pdef2 = val2->elements()->at(i)->isDef();
+ if (pdef2) {
+ if (pdef2->name()->value() == "action") {
+ GetDefNumber(act, pdef2, filename);
+ }
+ else if (pdef2->name()->value() == "status") {
+ char txt[64];
+ GetDefText(txt, pdef2, filename);
+ stat = CombatAction::StatusFromName(txt);
+ }
+ else if (pdef2->name()->value() == "not") {
+ GetDefBool(not, pdef2, filename);
+ }
+
+ else if (pdef2->name()->value() == "c1") {
+ char txt[64];
+ GetDefText(txt, pdef2, filename);
+ c1 = GetCombatant(txt);
+ }
+ else if (pdef2->name()->value() == "c2") {
+ char txt[64];
+ GetDefText(txt, pdef2, filename);
+ c2 = GetCombatant(txt);
+ }
+ else if (pdef2->name()->value() == "comp") {
+ char txt[64];
+ GetDefText(txt, pdef2, filename);
+ comp = CombatActionReq::CompFromName(txt);
+ }
+ else if (pdef2->name()->value() == "score") {
+ GetDefNumber(score, pdef2, filename);
+ }
+ else if (pdef2->name()->value() == "intel") {
+ if (pdef2->term()->isNumber()) {
+ GetDefNumber(intel, pdef2, filename);
+ }
+ else if (pdef2->term()->isText()) {
+ char txt[64];
+ GetDefText(txt, pdef2, filename);
+ intel = Intel::IntelFromName(txt);
+ }
+ }
+ else if (pdef2->name()->value() == "group_type") {
+ char type_name[64];
+ GetDefText(type_name, pdef2, filename);
+ gtype = CombatGroup::TypeFromName(type_name);
+ }
+ else if (pdef2->name()->value() == "group_id") {
+ GetDefNumber(gid, pdef2, filename);
+ }
+ }
+ }
+
+ if (act)
+ action->AddRequirement(act, stat, not);
+
+ else if (gtype)
+ action->AddRequirement(c1, gtype, gid, comp, score, intel);
+
+ else
+ action->AddRequirement(c1, c2, comp, score);
+ }
+ }
+ }
+ }
+
+ if (!action)
+ action = new(__FILE__,__LINE__) CombatAction(id, type, subtype, team);
+
+ if (action) {
+ action->SetSource(source);
+ action->SetOpposingType(opp_type);
+ action->SetLocation(loc);
+ action->SetSystem(system);
+ action->SetRegion(region);
+ action->SetFilename(file);
+ action->SetImageFile(image);
+ action->SetSceneFile(scene);
+ action->SetCount(count);
+ action->SetStartAfter(start_after);
+ action->SetStartBefore(start_before);
+ action->SetMinRank(min_rank);
+ action->SetMaxRank(max_rank);
+ action->SetDelay(delay);
+ action->SetProbability(probability);
+
+ action->SetAssetId(asset_id);
+ action->SetAssetType(asset_type);
+ action->SetTargetId(target_id);
+ action->SetTargetIFF(target_iff);
+ action->SetTargetType(target_type);
+ action->SetText(text);
+
+ actions.append(action);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+CombatGroup*
+Campaign::CloneOver(CombatGroup* force, CombatGroup* clone, CombatGroup* group)
+{
+ CombatGroup* orig_parent = group->GetParent();
+
+ if (orig_parent) {
+ CombatGroup* clone_parent = clone->FindGroup(orig_parent->Type(), orig_parent->GetID());
+
+ if (!clone_parent)
+ clone_parent = CloneOver(force, clone, orig_parent);
+
+ CombatGroup* new_clone = clone->FindGroup(group->Type(), group->GetID());
+
+ if (!new_clone) {
+ new_clone = group->Clone(false);
+ clone_parent->AddComponent(new_clone);
+ }
+
+ return new_clone;
+ }
+ else {
+ return clone;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Campaign::LoadMissionList(DataLoader* loader)
+{
+ bool ok = true;
+ BYTE* block = 0;
+ const char* filename = "Missions.def";
+
+ loader->UseFileSystem(true);
+ loader->LoadBuffer(filename, block, true);
+ loader->UseFileSystem(Starshatter::UseFileSystem());
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
+
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "MISSIONLIST") {
+ ::Print("WARNING: invalid mission list file '%s'\n", filename);
+ term->print(10);
+ return;
+ }
+ }
+
+ do {
+ delete term; term = 0;
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "mission") {
+ if (!def->term() || !def->term()->isStruct()) {
+ ::Print("WARNING: mission struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+
+ int id = 0;
+ Text name;
+ Text desc;
+ char script[256];
+ char system[256];
+ char region[256];
+ int start = 0;
+ int type = 0;
+
+ ZeroMemory(script, sizeof(script));
+
+ strcpy(system, "Unknown");
+ strcpy(region, "Unknown");
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "id")
+ GetDefNumber(id, pdef, filename);
+
+ else if (pdef->name()->value() == "name") {
+ GetDefText(name, pdef, filename);
+ name = Game::GetText(name);
+ }
+
+ else if (pdef->name()->value() == "desc") {
+ GetDefText(desc, pdef, filename);
+ if (desc.length() > 0 && desc.length() < 32)
+ desc = Game::GetText(desc);
+ }
+
+ else if (pdef->name()->value() == "start")
+ GetDefTime(start, pdef, filename);
+
+ else if (pdef->name()->value() == "system")
+ GetDefText(system, pdef, filename);
+
+ else if (pdef->name()->value() == "region")
+ GetDefText(region, pdef, filename);
+
+ else if (pdef->name()->value() == "script")
+ GetDefText(script, pdef, filename);
+
+ else if (pdef->name()->value() == "type") {
+ char typestr[64];
+ GetDefText(typestr, pdef, filename);
+ type = Mission::TypeFromName(typestr);
+ }
+ }
+ }
+
+ MissionInfo* info = new(__FILE__,__LINE__) MissionInfo;
+ if (info) {
+ info->id = id;
+ info->name = name;
+ info->description = desc;
+ info->system = system;
+ info->region = region;
+ info->script = script;
+ info->start = start;
+ info->type = type;
+ info->mission = 0;
+
+ info->script.setSensitive(false);
+
+ missions.append(info);
+ }
+ else {
+ ok = false;
+ }
+ }
+ }
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+
+ if (!ok)
+ Unload();
+}
+
+void
+Campaign::LoadCustomMissions(DataLoader* loader)
+{
+ bool ok = true;
+ List<Text> files;
+ loader->UseFileSystem(true);
+ loader->ListFiles("*.*", files);
+
+ for (int i = 0; i < files.size(); i++) {
+ Text file = *files[i];
+ file.setSensitive(false);
+
+ if (file.contains(".def")) {
+ BYTE* block = 0;
+ const char* filename = file.data();
+
+ loader->UseFileSystem(true);
+ loader->LoadBuffer(filename, block, true);
+ loader->UseFileSystem(Starshatter::UseFileSystem());
+
+ if (strstr((const char*) block, "MISSION") == (const char*) block) {
+ Text name;
+ Text desc;
+ Text system = "Unknown";
+ Text region = "Unknown";
+ int start = 0;
+ int type = 0;
+ int msn_id = 0;
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'\n", filename);
+ continue;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "MISSION") {
+ Print("ERROR: invalid mission file '%s'\n", filename);
+ term->print(10);
+ continue;
+ }
+ }
+
+ do {
+ delete term; term = 0;
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "name") {
+ GetDefText(name, def, filename);
+ name = Game::GetText(name);
+ }
+
+ else if (def->name()->value() == "type") {
+ char typestr[64];
+ GetDefText(typestr, def, filename);
+ type = Mission::TypeFromName(typestr);
+ }
+
+ else if (def->name()->value() == "id")
+ GetDefNumber(msn_id, def, filename);
+
+ else if (def->name()->value() == "desc") {
+ GetDefText(desc, def, filename);
+ if (desc.length() > 0 && desc.length() < 32)
+ desc = Game::GetText(desc);
+ }
+
+ else if (def->name()->value() == "system")
+ GetDefText(system, def, filename);
+
+ else if (def->name()->value() == "region")
+ GetDefText(region, def, filename);
+
+ else if (def->name()->value() == "start")
+ GetDefTime(start, def, filename);
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+
+ if (strstr(filename, "custom") == filename) {
+ sscanf(filename+6, "%d", &msn_id);
+
+ if (msn_id <= i)
+ msn_id = i+1;
+ }
+ else if (msn_id < 1) {
+ msn_id = i+1;
+ }
+
+ MissionInfo* info = new(__FILE__,__LINE__) MissionInfo;
+ if (info) {
+ info->id = msn_id;
+ info->name = name;
+ info->type = type;
+ info->description = desc;
+ info->system = system;
+ info->region = region;
+ info->script = filename;
+ info->start = start;
+ info->mission = 0;
+
+ info->script.setSensitive(false);
+
+ missions.append(info);
+ }
+ else {
+ ok = false;
+ }
+ }
+ else {
+ Print("Invalid Custom Mission File: '%s'\n", filename);
+ }
+
+ loader->ReleaseBuffer(block);
+ }
+ }
+
+ files.destroy();
+
+ if (!ok)
+ Unload();
+ else
+ missions.sort();
+}
+
+void
+Campaign::LoadTemplateList(DataLoader* loader)
+{
+ BYTE* block = 0;
+ const char* filename = "Templates.def";
+
+ loader->UseFileSystem(true);
+ loader->LoadBuffer(filename, block, true);
+ loader->UseFileSystem(Starshatter::UseFileSystem());
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
+
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "TEMPLATELIST") {
+ ::Print("WARNING: invalid template list file '%s'\n", filename);
+ term->print(10);
+ return;
+ }
+ }
+
+ do {
+ delete term; term = 0;
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "mission") {
+ if (!def->term() || !def->term()->isStruct()) {
+ ::Print("WARNING: mission struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+
+ char name[256];
+ char script[256];
+ char region[256];
+ int id = 0;
+ int msn_type = 0;
+ int grp_type = 0;
+
+ int min_rank = 0;
+ int max_rank = 0;
+ int action_id = 0;
+ int action_status = 0;
+ int exec_once = 0;
+ int start_before = TIME_NEVER;
+ int start_after = 0;
+
+ name[0] = 0;
+ script[0] = 0;
+ region[0] = 0;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "id")
+ GetDefNumber(id, pdef, filename);
+
+ else if (pdef->name()->value() == "name")
+ GetDefText(name, pdef, filename);
+
+ else if (pdef->name()->value() == "script")
+ GetDefText(script, pdef, filename);
+
+ else if (pdef->name()->value() == "rgn" || pdef->name()->value() == "region")
+ GetDefText(region, pdef, filename);
+
+ else if (pdef->name()->value() == "type") {
+ char typestr[64];
+ GetDefText(typestr, pdef, filename);
+ msn_type = Mission::TypeFromName(typestr);
+ }
+
+ else if (pdef->name()->value() == "group") {
+ char typestr[64];
+ GetDefText(typestr, pdef, filename);
+ grp_type = CombatGroup::TypeFromName(typestr);
+ }
+
+ else if (pdef->name()->value() == "min_rank")
+ GetDefNumber(min_rank, pdef, filename);
+
+ else if (pdef->name()->value() == "max_rank")
+ GetDefNumber(max_rank, pdef, filename);
+
+ else if (pdef->name()->value() == "action_id")
+ GetDefNumber(action_id, pdef, filename);
+
+ else if (pdef->name()->value() == "action_status")
+ GetDefNumber(action_status, pdef, filename);
+
+ else if (pdef->name()->value() == "exec_once")
+ GetDefNumber(exec_once, pdef, filename);
+
+ else if (pdef->name()->value().contains("before")) {
+ if (pdef->term()->isNumber()) {
+ GetDefNumber(start_before, pdef, filename);
+ }
+ else {
+ GetDefTime(start_before, pdef, filename);
+ start_before -= ONE_DAY;
+ }
+ }
+ else if (pdef->name()->value().contains("after")) {
+ if (pdef->term()->isNumber()) {
+ GetDefNumber(start_after, pdef, filename);
+ }
+ else {
+ GetDefTime(start_after, pdef, filename);
+ start_after -= ONE_DAY;
+ }
+ }
+ }
+ }
+
+ MissionInfo* info = new(__FILE__,__LINE__) MissionInfo;
+ if (info) {
+ info->id = id;
+ info->name = name;
+ info->script = script;
+ info->region = region;
+ info->type = msn_type;
+ info->min_rank = min_rank;
+ info->max_rank = max_rank;
+ info->action_id = action_id;
+ info->action_status = action_status;
+ info->exec_once = exec_once;
+ info->start_before = start_before;
+ info->start_after = start_after;
+
+ info->script.setSensitive(false);
+
+ TemplateList* templist = GetTemplateList(msn_type, grp_type);
+
+ if (!templist) {
+ templist = new(__FILE__,__LINE__) TemplateList;
+ templist->mission_type = msn_type;
+ templist->group_type = grp_type;
+ templates.append(templist);
+ }
+
+ templist->missions.append(info);
+ }
+ }
+ }
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Campaign::CreatePlanners()
+{
+ if (planners.size() > 0)
+ planners.destroy();
+
+ CampaignPlan* p;
+
+ // PLAN EVENT MUST BE FIRST PLANNER:
+ p = new(__FILE__,__LINE__) CampaignPlanEvent(this);
+ if (p)
+ planners.append(p);
+
+ p = new(__FILE__,__LINE__) CampaignPlanStrategic(this);
+ if (p)
+ planners.append(p);
+
+ p = new(__FILE__,__LINE__) CampaignPlanAssignment(this);
+ if (p)
+ planners.append(p);
+
+ p = new(__FILE__,__LINE__) CampaignPlanMovement(this);
+ if (p)
+ planners.append(p);
+
+ p = new(__FILE__,__LINE__) CampaignPlanMission(this);
+ if (p)
+ planners.append(p);
+
+ if (lockout > 0 && planners.size()) {
+ ListIter<CampaignPlan> plan = planners;
+ while (++plan)
+ plan->SetLockout(lockout);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Campaign::GetPlayerIFF()
+{
+ int iff = 1;
+
+ if (player_group)
+ iff = player_group->GetIFF();
+
+ return iff;
+}
+
+void
+Campaign::SetPlayerGroup(CombatGroup* pg)
+{
+ if (player_group != pg) {
+ ::Print("Campaign::SetPlayerGroup(%s)\n", pg ? pg->Name().data() : "0");
+
+ // should verify that the player group is
+ // actually part of this campaign, first!
+
+ player_group = pg;
+ player_unit = 0;
+
+ // need to regenerate missions when changing
+ // player combat group:
+ if (IsDynamic()) {
+ ::Print(" destroying mission list...\n");
+ missions.destroy();
+ }
+ }
+}
+
+void
+Campaign::SetPlayerUnit(CombatUnit* unit)
+{
+ if (player_unit != unit) {
+ ::Print("Campaign::SetPlayerUnit(%s)\n", unit ? unit->Name().data() : "0");
+
+ // should verify that the player unit is
+ // actually part of this campaign, first!
+
+ player_unit = unit;
+
+ if (unit)
+ player_group = unit->GetCombatGroup();
+
+ // need to regenerate missions when changing
+ // player combat unit:
+ if (IsDynamic()) {
+ ::Print(" destroying mission list...\n");
+ missions.destroy();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+CombatZone*
+Campaign::GetZone(const char* rgn)
+{
+ ListIter<CombatZone> z = zones;
+ while (++z) {
+ if (z->HasRegion(rgn))
+ return z.value();
+ }
+
+ return 0;
+}
+
+StarSystem*
+Campaign::GetSystem(const char* sys)
+{
+ return Galaxy::GetInstance()->GetSystem(sys);
+}
+
+Combatant*
+Campaign::GetCombatant(const char* cname)
+{
+ ListIter<Combatant> iter = combatants;
+ while (++iter) {
+ Combatant* c = iter.value();
+ if (!strcmp(c->Name(), cname))
+ return c;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+Mission*
+Campaign::GetMission()
+{
+ return GetMission(mission_id);
+}
+
+Mission*
+Campaign::GetMission(int id)
+{
+ if (id < 0) {
+ ::Print("ERROR - Campaign::GetMission(%d) invalid mission id\n", id);
+ return 0;
+ }
+
+ if (mission && mission->Identity() == id) {
+ return mission;
+ }
+
+ MissionInfo* info = 0;
+ for (int i = 0; !info && i < missions.size(); i++)
+ if (missions[i]->id == id)
+ info = missions[i];
+
+ if (info) {
+ if (!info->mission) {
+ ::Print("Campaign::GetMission(%d) loading mission...\n", id);
+ info->mission = new(__FILE__,__LINE__) Mission(id, info->script, path);
+ if (info->mission)
+ info->mission->Load();
+ }
+
+ if (IsDynamic()) {
+ if (info->mission) {
+ if (!stricmp(info->mission->Situation(), "Unknown")) {
+ ::Print("Campaign::GetMission(%d) generating sitrep...\n", id);
+ CampaignSituationReport sitrep(this, info->mission);
+ sitrep.GenerateSituationReport();
+ }
+ }
+ else {
+ ::Print("Campaign::GetMission(%d) could not find/load mission.\n", id);
+ }
+ }
+
+ return info->mission;
+ }
+
+ return 0;
+}
+
+Mission*
+Campaign::GetMissionByFile(const char* filename)
+{
+ if (!filename || !*filename) {
+ ::Print("ERROR - Campaign::GetMissionByFile() invalid filename\n");
+ return 0;
+ }
+
+ int id = 0;
+ int maxid = 0;
+ MissionInfo* info = 0;
+
+ for (int i = 0; !info && i < missions.size(); i++) {
+ MissionInfo* m = missions[i];
+
+ if (m->id > maxid)
+ maxid = m->id;
+
+ if (m->script == filename)
+ info = m;
+ }
+
+ if (info) {
+ id = info->id;
+
+ if (!info->mission) {
+ ::Print("Campaign::GetMission(%d) loading mission...\n", id);
+ info->mission = new(__FILE__,__LINE__) Mission(id, info->script, path);
+ if (info->mission)
+ info->mission->Load();
+ }
+
+ if (IsDynamic()) {
+ if (info->mission) {
+ if (!stricmp(info->mission->Situation(), "Unknown")) {
+ ::Print("Campaign::GetMission(%d) generating sitrep...\n", id);
+ CampaignSituationReport sitrep(this, info->mission);
+ sitrep.GenerateSituationReport();
+ }
+ }
+ else {
+ ::Print("Campaign::GetMission(%d) could not find/load mission.\n", id);
+ }
+ }
+ }
+
+ else {
+ info = new(__FILE__,__LINE__) MissionInfo;
+ if (info) {
+ info->id = maxid+1;
+ info->name = "New Custom Mission";
+ info->script = filename;
+ info->mission = new(__FILE__,__LINE__) Mission(info->id, info->script, "Mods/Missions/");
+ info->mission->SetName(info->name);
+
+ info->script.setSensitive(false);
+
+ missions.append(info);
+ }
+ }
+
+ return info->mission;
+}
+
+MissionInfo*
+Campaign::CreateNewMission()
+{
+ int id = 0;
+ int maxid = 0;
+ MissionInfo* info = 0;
+
+ if (campaign_id == MULTIPLAYER_MISSIONS)
+ maxid = 10;
+
+ for (int i = 0; !info && i < missions.size(); i++) {
+ MissionInfo* m = missions[i];
+
+ if (m->id > maxid)
+ maxid = m->id;
+ }
+
+ char filename[64];
+ sprintf(filename, "custom%03d.def", maxid+1);
+
+ info = new(__FILE__,__LINE__) MissionInfo;
+ if (info) {
+ info->id = maxid+1;
+ info->name = "New Custom Mission";
+ info->script = filename;
+ info->mission = new(__FILE__,__LINE__) Mission(info->id, filename, path);
+ info->mission->SetName(info->name);
+
+ info->script.setSensitive(false);
+
+ missions.append(info);
+ }
+
+ return info;
+}
+
+void
+Campaign::DeleteMission(int id)
+{
+ if (id < 0) {
+ ::Print("ERROR - Campaign::DeleteMission(%d) invalid mission id\n", id);
+ return;
+ }
+
+ MissionInfo* m = 0;
+ int index = -1;
+
+ for (int i = 0; !m && i < missions.size(); i++) {
+ if (missions[i]->id == id) {
+ m = missions[i];
+ index = i;
+ }
+ }
+
+ if (m) {
+ char full_path[256];
+
+ if (path[strlen(path)-1] == '/')
+ sprintf(full_path, "%s%s", path, m->script);
+ else
+ sprintf(full_path, "%s/%s", path, m->script);
+
+ DeleteFile(full_path);
+ Load();
+ }
+
+ else {
+ ::Print("ERROR - Campaign::DeleteMission(%d) could not find mission\n", id);
+ }
+}
+
+MissionInfo*
+Campaign::GetMissionInfo(int id)
+{
+ if (id < 0) {
+ ::Print("ERROR - Campaign::GetMissionInfo(%d) invalid mission id\n", id);
+ return 0;
+ }
+
+ MissionInfo* m = 0;
+ for (int i = 0; !m && i < missions.size(); i++)
+ if (missions[i]->id == id)
+ m = missions[i];
+
+ if (m) {
+ if (!m->mission) {
+ m->mission = new(__FILE__,__LINE__) Mission(id, m->script);
+ if (m->mission)
+ m->mission->Load();
+ }
+
+ return m;
+ }
+
+ else {
+ ::Print("ERROR - Campaign::GetMissionInfo(%d) could not find mission\n", id);
+ }
+
+ return 0;
+}
+
+void
+Campaign::ReloadMission(int id)
+{
+ if (mission && mission == net_mission) {
+ delete net_mission;
+ net_mission = 0;
+ }
+
+ mission = 0;
+
+ if (id >= 0 && id < missions.size()) {
+ MissionInfo* m = missions[id];
+ delete m->mission;
+ m->mission = 0;
+ }
+}
+
+void
+Campaign::LoadNetMission(int id, const char* net_mission_script)
+{
+ if (mission && mission == net_mission) {
+ delete net_mission;
+ net_mission = 0;
+ }
+
+ mission_id = id;
+ mission = new(__FILE__,__LINE__) Mission(id);
+
+ if (mission && mission->ParseMission(net_mission_script))
+ mission->Validate();
+
+ net_mission = mission;
+}
+
+// +--------------------------------------------------------------------+
+
+CombatAction*
+Campaign::FindAction(int action_id)
+{
+ ListIter<CombatAction> iter = actions;
+ while (++iter) {
+ CombatAction* a = iter.value();
+
+ if (a->Identity() == action_id)
+ return a;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+MissionInfo*
+Campaign::FindMissionTemplate(int mission_type, CombatGroup* player_group)
+{
+ MissionInfo* info = 0;
+
+ if (!player_group)
+ return info;
+
+ TemplateList* templates = GetTemplateList(mission_type, player_group->Type());
+
+ if (!templates || !templates->missions.size())
+ return info;
+
+ int tries = 0;
+ int msize = templates->missions.size();
+
+ while (!info && tries < msize) {
+ // get next template:
+ int index = templates->index;
+
+ if (index >= msize)
+ index = 0;
+
+ info = templates->missions[index];
+ templates->index = index + 1;
+ tries++;
+
+ // validate the template:
+ if (info) {
+ if (info->action_id) {
+ CombatAction* a = FindAction(info->action_id);
+
+ if (a && a->Status() != info->action_status)
+ info = 0;
+ }
+
+ if (info && !info->IsAvailable()) {
+ info = 0;
+ }
+ }
+ }
+
+ return info;
+}
+
+// +--------------------------------------------------------------------+
+
+TemplateList*
+Campaign::GetTemplateList(int msn_type, int grp_type)
+{
+ for (int i = 0; i < templates.size(); i++) {
+ if (templates[i]->mission_type == msn_type &&
+ templates[i]->group_type == grp_type)
+ return templates[i];
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Campaign::SetMissionId(int id)
+{
+ ::Print("Campaign::SetMissionId(%d)\n", id);
+
+ if (id > 0)
+ mission_id = id;
+ else
+ ::Print(" retaining mission id = %d\n", mission_id);
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Campaign::Stardate()
+{
+ return StarSystem::Stardate();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Campaign::SelectDefaultPlayerGroup(CombatGroup* g, int type)
+{
+ if (player_group || !g) return;
+
+ if (g->Type() == type && !g->IsReserve() && g->Value() > 0) {
+ player_group = g;
+ player_unit = 0;
+ return;
+ }
+
+ for (int i = 0; i < g->GetComponents().size(); i++)
+ SelectDefaultPlayerGroup(g->GetComponents()[i], type);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Campaign::Prep()
+{
+ // if this is a new campaign,
+ // create combatants from roster and template:
+ if (IsDynamic() && combatants.isEmpty()) {
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath(path);
+ LoadCampaign(loader, true);
+ }
+
+ StarSystem::SetBaseTime(loadTime);
+
+ // load scripted missions:
+ if (IsScripted() && actions.isEmpty()) {
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath(path);
+ LoadCampaign(loader, true);
+
+ ListIter<MissionInfo> m = missions;
+ while (++m) {
+ GetMission(m->id);
+ }
+ }
+
+ CheckPlayerGroup();
+}
+
+void
+Campaign::Start()
+{
+ ::Print("Campaign::Start()\n");
+
+ Prep();
+
+ // create planners:
+ CreatePlanners();
+ SetStatus(CAMPAIGN_ACTIVE);
+}
+
+void
+Campaign::ExecFrame()
+{
+ if (InCutscene())
+ return;
+
+ time = Stardate() - startTime;
+
+ if (status < CAMPAIGN_ACTIVE)
+ return;
+
+ if (IsDynamic()) {
+ bool completed = false;
+ ListIter<MissionInfo> m = missions;
+ while (++m) {
+ if (m->mission && m->mission->IsComplete()) {
+ ::Print("Campaign::ExecFrame() completed mission %d '%s'\n", m->id, m->name.data());
+ completed = true;
+ }
+ }
+
+ if (completed) {
+ ::Print("Campaign::ExecFrame() destroying mission list after completion...\n");
+ missions.destroy();
+
+ if (!player_group || player_group->IsFighterGroup())
+ time += 10 * 3600;
+ else
+ time += 20 * 3600;
+
+ StarSystem::SetBaseTime(startTime + time - Game::GameTime()/1000.0);
+ }
+ else {
+ m.reset();
+
+ while (++m) {
+ if (m->start < time && !m->mission->IsActive()) {
+ MissionInfo* info = m.removeItem();
+
+ if (info)
+ ::Print("Campaign::ExecFrame() deleting expired mission %d start: %d current: %d\n",
+ info->id,
+ info->start,
+ (int) time);
+
+ delete info;
+ }
+ }
+ }
+
+ // PLAN EVENT MUST BE FIRST PLANNER:
+ if (loaded_from_savegame && planners.size() > 0) {
+ CampaignPlanEvent* plan_event = (CampaignPlanEvent*) planners.first();
+ plan_event->ExecScriptedEvents();
+
+ loaded_from_savegame = false;
+ }
+
+ ListIter<CampaignPlan> plan = planners;
+ while (++plan) {
+ CheckPlayerGroup();
+ plan->ExecFrame();
+ }
+
+ CheckPlayerGroup();
+
+ // Auto save game AFTER planners have run!
+ // This is done to ensure that campaign status
+ // is properly recorded after winning or losing
+ // the campaign.
+
+ if (completed) {
+ CampaignSaveGame save(this);
+ save.SaveAuto();
+ }
+ }
+ else {
+ // PLAN EVENT MUST BE FIRST PLANNER:
+ if (planners.size() > 0) {
+ CampaignPlanEvent* plan_event = (CampaignPlanEvent*) planners.first();
+ plan_event->ExecScriptedEvents();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Campaign::LockoutEvents(int seconds)
+{
+ lockout = seconds;
+}
+
+void
+Campaign::CheckPlayerGroup()
+{
+ if (!player_group || player_group->IsReserve() || player_group->CalcValue() < 1) {
+ int player_iff = GetPlayerIFF();
+ player_group = 0;
+
+ CombatGroup* force = 0;
+ for (int i = 0; i < combatants.size() && !force; i++) {
+ if (combatants[i]->GetIFF() == player_iff) {
+ force = combatants[i]->GetForce();
+ }
+ }
+
+ if (force) {
+ force->CalcValue();
+ SelectDefaultPlayerGroup(force, CombatGroup::WING);
+
+ if (!player_group)
+ SelectDefaultPlayerGroup(force, CombatGroup::DESTROYER_SQUADRON);
+ }
+ }
+
+ if (player_unit && player_unit->GetValue() < 1)
+ SetPlayerUnit(0);
+}
+
+// +--------------------------------------------------------------------+
+
+void FPU2Extended();
+void FPURestore();
+
+void
+Campaign::StartMission()
+{
+ Mission* m = GetMission();
+
+ if (m) {
+ ::Print("\n\nCampaign Start Mission - %d. '%s'\n", m->Identity(), m->Name());
+
+ if (!scripted) {
+ FPU2Extended();
+
+ double gtime = (double) Game::GameTime() / 1000.0;
+ double base = startTime + m->Start() - 15 - gtime;
+
+ StarSystem::SetBaseTime(base);
+
+ double current_time = Stardate() - startTime;
+
+ char buffer[32];
+ FormatDayTime(buffer, current_time);
+ ::Print(" current time: %s\n", buffer);
+
+ FormatDayTime(buffer, m->Start());
+ ::Print(" mission start: %s\n", buffer);
+ ::Print("\n");
+ }
+ }
+}
+
+void
+Campaign::RollbackMission()
+{
+ ::Print("Campaign::RollbackMission()\n");
+
+ Mission* m = GetMission();
+
+ if (m) {
+ if (!scripted) {
+ FPU2Extended();
+
+ double gtime = (double) Game::GameTime() / 1000.0;
+ double base = startTime + m->Start() - 60 - gtime;
+
+ StarSystem::SetBaseTime(base);
+
+ double current_time = Stardate() - startTime;
+ ::Print(" mission start: %d\n", m->Start());
+ ::Print(" current time: %d\n", (int) current_time);
+ }
+
+ m->SetActive(false);
+ m->SetComplete(false);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Campaign::InCutscene() const
+{
+ Starshatter* stars = Starshatter::GetInstance();
+ return stars ? stars->InCutscene() : false;
+}
+
+bool
+Campaign::IsDynamic() const
+{
+ return campaign_id >= DYNAMIC_CAMPAIGN &&
+ campaign_id < SINGLE_MISSIONS;
+}
+
+bool
+Campaign::IsTraining() const
+{
+ return campaign_id == TRAINING_CAMPAIGN;
+}
+
+bool
+Campaign::IsScripted() const
+{
+ return scripted;
+}
+
+bool
+Campaign::IsSequential() const
+{
+ return sequential;
+}
+
+// +--------------------------------------------------------------------+
+
+static CombatGroup* FindGroup(CombatGroup* g, int type, int id)
+{
+ if (g->Type() == type && g->GetID() == id)
+ return g;
+
+ CombatGroup* result = 0;
+
+ ListIter<CombatGroup> subgroup = g->GetComponents();
+ while (++subgroup && !result)
+ result = FindGroup(subgroup.value(), type, id);
+
+ return result;
+}
+
+CombatGroup*
+Campaign::FindGroup(int iff, int type, int id)
+{
+ CombatGroup* result = 0;
+
+ ListIter<Combatant> combatant = combatants;
+ while (++combatant && !result) {
+ if (combatant->GetIFF() == iff) {
+ result = ::FindGroup(combatant->GetForce(), type, id);
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+static void FindGroups(CombatGroup* g, int type, CombatGroup* near_group,
+ List<CombatGroup>& groups)
+{
+ if (g->Type() == type && g->IntelLevel() > Intel::RESERVE) {
+ if (!near_group || g->GetAssignedZone() == near_group->GetAssignedZone())
+ groups.append(g);
+ }
+
+ ListIter<CombatGroup> subgroup = g->GetComponents();
+ while (++subgroup)
+ FindGroups(subgroup.value(), type, near_group, groups);
+}
+
+CombatGroup*
+Campaign::FindGroup(int iff, int type, CombatGroup* near_group)
+{
+ CombatGroup* result = 0;
+ List<CombatGroup> groups;
+
+ ListIter<Combatant> combatant = combatants;
+ while (++combatant) {
+ if (combatant->GetIFF() == iff) {
+ FindGroups(combatant->GetForce(), type, near_group, groups);
+ }
+ }
+
+ if (groups.size() > 0) {
+ int index = (int) Random(0, groups.size());
+ if (index >= groups.size()) index = groups.size() - 1;
+ result = groups[index];
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+static void FindStrikeTargets(CombatGroup* g, CombatGroup* strike_group,
+ List<CombatGroup>& groups)
+{
+ if (!strike_group || !strike_group->GetAssignedZone()) return;
+
+ if (g->IsStrikeTarget() && g->IntelLevel() > Intel::RESERVE) {
+ if (strike_group->GetAssignedZone() == g->GetAssignedZone() ||
+ strike_group->GetAssignedZone()->HasRegion(g->GetRegion()))
+ groups.append(g);
+ }
+
+ ListIter<CombatGroup> subgroup = g->GetComponents();
+ while (++subgroup)
+ FindStrikeTargets(subgroup.value(), strike_group, groups);
+}
+
+CombatGroup*
+Campaign::FindStrikeTarget(int iff, CombatGroup* strike_group)
+{
+ CombatGroup* result = 0;
+
+ List<CombatGroup> groups;
+
+ ListIter<Combatant> combatant = GetCombatants();
+ while (++combatant) {
+ if (combatant->GetIFF() != 0 && combatant->GetIFF() != iff) {
+ FindStrikeTargets(combatant->GetForce(), strike_group, groups);
+ }
+ }
+
+ if (groups.size() > 0) {
+ int index = rand() % groups.size();
+ result = groups[index];
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Campaign::CommitExpiredActions()
+{
+ ListIter<CombatAction> iter = actions;
+ while (++iter) {
+ CombatAction* a = iter.value();
+
+ if (a->IsAvailable())
+ a->SetStatus(CombatAction::COMPLETE);
+ }
+
+ updateTime = time;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Campaign::GetPlayerTeamScore()
+{
+ int score_us = 0;
+ int score_them = 0;
+
+ if (player_group) {
+ int iff = player_group->GetIFF();
+
+ ListIter<Combatant> iter = combatants;
+ while (++iter) {
+ Combatant* c = iter.value();
+
+ if (iff <= 1) {
+ if (c->GetIFF() <= 1)
+ score_us += c->Score();
+ else
+ score_them += c->Score();
+ }
+
+ else {
+ if (c->GetIFF() <= 1)
+ score_them += c->Score();
+ else
+ score_us += c->Score();
+ }
+ }
+ }
+
+ return score_us - score_them;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Campaign::SetStatus(int s)
+{
+ status = s;
+
+ // record the win in player profile:
+ if (status == CAMPAIGN_SUCCESS) {
+ Player* player = Player::GetCurrentPlayer();
+
+ if (player)
+ player->SetCampaignComplete(campaign_id);
+ }
+
+ if (status > CAMPAIGN_ACTIVE) {
+ ::Print("Campaign::SetStatus() destroying mission list at campaign end\n");
+ missions.destroy();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static void GetCombatUnits(CombatGroup* g, List<CombatUnit>& units)
+{
+ if (g) {
+ ListIter<CombatUnit> unit = g->GetUnits();
+ while (++unit) {
+ CombatUnit* u = unit.value();
+
+ if (u->Count() - u->DeadCount() > 0)
+ units.append(u);
+ }
+
+ ListIter<CombatGroup> comp = g->GetComponents();
+ while (++comp) {
+ CombatGroup* g2 = comp.value();
+
+ if (!g2->IsReserve())
+ GetCombatUnits(g2, units);
+ }
+ }
+}
+
+int
+Campaign::GetAllCombatUnits(int iff, List<CombatUnit>& units)
+{
+ units.clear();
+
+ ListIter<Combatant> iter = combatants;
+ while (++iter) {
+ Combatant* c = iter.value();
+
+ if (iff < 0 || c->GetIFF() == iff) {
+ GetCombatUnits(c->GetForce(), units);
+ }
+ }
+
+ return units.size();
+}
diff --git a/Stars45/Campaign.h b/Stars45/Campaign.h
new file mode 100644
index 0000000..c844604
--- /dev/null
+++ b/Stars45/Campaign.h
@@ -0,0 +1,282 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Campaign.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Campaign defines a strategic military scenario. This class
+ owns (or generates) the Mission list that defines the action
+ in the campaign.
+*/
+
+#ifndef Campaign_h
+#define Campaign_h
+
+#include "Types.h"
+#include "Bitmap.h"
+#include "Geometry.h"
+#include "Text.h"
+#include "Term.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Campaign;
+class CampaignPlan;
+class Combatant;
+class CombatAction;
+class CombatEvent;
+class CombatGroup;
+class CombatUnit;
+class CombatZone;
+class DataLoader;
+class Mission;
+class MissionTemplate;
+class StarSystem;
+
+// +--------------------------------------------------------------------+
+
+class MissionInfo
+{
+public:
+ static const char* TYPENAME() { return "MissionInfo"; }
+
+ MissionInfo();
+ ~MissionInfo();
+
+ int operator == (const MissionInfo& m) const { return id == m.id; }
+ int operator < (const MissionInfo& m) const { return id < m.id; }
+ int operator <= (const MissionInfo& m) const { return id <= m.id; }
+
+ bool IsAvailable();
+
+ int id;
+ Text name;
+ Text player_info;
+ Text description;
+ Text system;
+ Text region;
+ Text script;
+ int start;
+ int type;
+
+ int min_rank;
+ int max_rank;
+ int action_id;
+ int action_status;
+ int exec_once;
+ int start_before;
+ int start_after;
+
+ Mission* mission;
+};
+
+class TemplateList
+{
+public:
+ static const char* TYPENAME() { return "TemplateList"; }
+
+ TemplateList();
+ ~TemplateList();
+
+ int mission_type;
+ int group_type;
+ int index;
+ List<MissionInfo> missions;
+};
+
+// +--------------------------------------------------------------------+
+
+class Campaign
+{
+public:
+ static const char* TYPENAME() { return "Campaign"; }
+
+ enum CONSTANTS {
+ TRAINING_CAMPAIGN = 1,
+ DYNAMIC_CAMPAIGN,
+ MOD_CAMPAIGN = 100,
+ SINGLE_MISSIONS = 1000,
+ MULTIPLAYER_MISSIONS,
+ CUSTOM_MISSIONS,
+
+ NUM_IMAGES = 6
+ };
+
+ enum STATUS {
+ CAMPAIGN_INIT,
+ CAMPAIGN_ACTIVE,
+ CAMPAIGN_SUCCESS,
+ CAMPAIGN_FAILED
+ };
+
+ Campaign(int id, const char* name=0);
+ Campaign(int id, const char* name, const char* path);
+ virtual ~Campaign();
+
+ int operator == (const Campaign& s) const { return name == s.name; }
+ int operator < (const Campaign& s) const { return campaign_id < s.campaign_id; }
+
+ // operations:
+ virtual void Load();
+ virtual void Prep();
+ virtual void Start();
+ virtual void ExecFrame();
+ virtual void Unload();
+
+ virtual void Clear();
+ virtual void CommitExpiredActions();
+ virtual void LockoutEvents(int seconds);
+ virtual void CheckPlayerGroup();
+ void CreatePlanners();
+
+ // accessors:
+ const char* Name() const { return name; }
+ const char* Description() const { return description; }
+ const char* Path() const { return path; }
+
+ const char* Situation() const { return situation; }
+ const char* Orders() const { return orders; }
+
+ void SetSituation(const char* s) { situation = s; }
+ void SetOrders(const char* o) { orders = o; }
+
+ int GetPlayerTeamScore();
+ List<MissionInfo>& GetMissionList() { return missions; }
+ List<Combatant>& GetCombatants() { return combatants; }
+ List<CombatZone>& GetZones() { return zones; }
+ List<StarSystem>& GetSystemList() { return systems; }
+ List<CombatAction>& GetActions() { return actions; }
+ List<CombatEvent>& GetEvents() { return events; }
+ CombatEvent* GetLastEvent();
+
+ CombatAction* FindAction(int id);
+
+ int CountNewEvents() const;
+
+ int GetPlayerIFF();
+ CombatGroup* GetPlayerGroup() { return player_group; }
+ void SetPlayerGroup(CombatGroup* pg);
+ CombatUnit* GetPlayerUnit() { return player_unit; }
+ void SetPlayerUnit(CombatUnit* pu);
+
+ Combatant* GetCombatant(const char* name);
+ CombatGroup* FindGroup(int iff, int type, int id);
+ CombatGroup* FindGroup(int iff, int type, CombatGroup* near_group=0);
+ CombatGroup* FindStrikeTarget(int iff, CombatGroup* strike_group);
+
+ StarSystem* GetSystem(const char* sys);
+ CombatZone* GetZone(const char* rgn);
+ MissionInfo* CreateNewMission();
+ void DeleteMission(int id);
+ Mission* GetMission();
+ Mission* GetMission(int id);
+ Mission* GetMissionByFile(const char* filename);
+ MissionInfo* GetMissionInfo(int id);
+ MissionInfo* FindMissionTemplate(int msn_type, CombatGroup* player_group);
+ void ReloadMission(int id);
+ void LoadNetMission(int id, const char* net_mission);
+ void StartMission();
+ void RollbackMission();
+
+ void SetCampaignId(int id);
+ int GetCampaignId() const { return campaign_id; }
+ void SetMissionId(int id);
+ int GetMissionId() const { return mission_id; }
+ Bitmap* GetImage(int n) { return &image[n]; }
+ double GetTime() const { return time; }
+ double GetStartTime() const { return startTime; }
+ void SetStartTime(double t) { startTime = t; }
+ double GetLoadTime() const { return loadTime; }
+ void SetLoadTime(double t) { loadTime = t; }
+ double GetUpdateTime() const { return updateTime; }
+ void SetUpdateTime(double t) { updateTime = t; }
+
+ bool InCutscene() const;
+ bool IsDynamic() const;
+ bool IsTraining() const;
+ bool IsScripted() const;
+ bool IsSequential() const;
+ bool IsSaveGame() const { return loaded_from_savegame; }
+ void SetSaveGame(bool s) { loaded_from_savegame = s; }
+
+ bool IsActive() const { return status == CAMPAIGN_ACTIVE; }
+ bool IsComplete() const { return status == CAMPAIGN_SUCCESS; }
+ bool IsFailed() const { return status == CAMPAIGN_FAILED; }
+ void SetStatus(int s);
+ int GetStatus() const { return status; }
+
+ int GetAllCombatUnits(int iff, List<CombatUnit>& units);
+
+ static void Initialize();
+ static void Close();
+ static Campaign* GetCampaign();
+ static List<Campaign>&
+ GetAllCampaigns();
+ static int GetLastCampaignId();
+ static Campaign* SelectCampaign(const char* name);
+ static Campaign* CreateCustomCampaign(const char* name, const char* path);
+
+ static double Stardate();
+
+protected:
+ void LoadCampaign(DataLoader* loader, bool full=false);
+ void LoadTemplateList(DataLoader* loader);
+ void LoadMissionList(DataLoader* loader);
+ void LoadCustomMissions(DataLoader* loader);
+ void ParseGroup(TermStruct* val,
+ CombatGroup* force,
+ CombatGroup* clone,
+ const char* filename);
+ void ParseAction(TermStruct* val,
+ const char* filename);
+ CombatGroup* CloneOver(CombatGroup* force,
+ CombatGroup* clone,
+ CombatGroup* group);
+ void SelectDefaultPlayerGroup(CombatGroup* g, int type);
+ TemplateList* GetTemplateList(int msn_type, int grp_type);
+
+ // attributes:
+ int campaign_id;
+ int status;
+ char filename[64];
+ char path[64];
+ Text name;
+ Text description;
+ Text situation;
+ Text orders;
+ Bitmap image[NUM_IMAGES];
+
+ bool scripted;
+ bool sequential;
+ bool loaded_from_savegame;
+
+ List<Combatant> combatants;
+ List<StarSystem> systems;
+ List<CombatZone> zones;
+ List<CampaignPlan> planners;
+ List<MissionInfo> missions;
+ List<TemplateList> templates;
+ List<CombatAction> actions;
+ List<CombatEvent> events;
+ CombatGroup* player_group;
+ CombatUnit* player_unit;
+
+ int mission_id;
+ Mission* mission;
+ Mission* net_mission;
+
+ double time;
+ double loadTime;
+ double startTime;
+ double updateTime;
+ int lockout;
+};
+
+#endif Campaign_h
+
diff --git a/Stars45/CampaignMissionFighter.cpp b/Stars45/CampaignMissionFighter.cpp
new file mode 100644
index 0000000..f859ad8
--- /dev/null
+++ b/Stars45/CampaignMissionFighter.cpp
@@ -0,0 +1,2152 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignMissionFighter.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignMissionFighter generates missions and mission
+ info for the player's FIGHTER SQUADRON as part of a
+ dynamic campaign.
+*/
+
+#include "MemDebug.h"
+#include "CampaignMissionFighter.h"
+#include "CampaignMissionRequest.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatAssignment.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "CombatZone.h"
+#include "Callsign.h"
+#include "Galaxy.h"
+#include "Mission.h"
+#include "MissionTemplate.h"
+#include "Instruction.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Starshatter.h"
+#include "StarSystem.h"
+#include "Player.h"
+
+#include "Random.h"
+
+static int pkg_id = 1000;
+extern int dump_missions;
+
+// +--------------------------------------------------------------------+
+
+CampaignMissionFighter::CampaignMissionFighter(Campaign* c)
+ : campaign(c), squadron(0), mission(0), player_elem(0),
+ strike_group(0), strike_target(0), prime_target(0),
+ carrier_elem(0), ward(0), escort(0), airborne(false), airbase(false),
+ ownside(0), enemy(-1), mission_type(0), mission_info(0)
+{
+ if (!campaign || !campaign->GetPlayerGroup()) {
+ ::Print("ERROR - CMF campaign=0x%08x player_group=0x%08x\n",
+ campaign, (DWORD) campaign?campaign->GetPlayerGroup():0);
+ return;
+ }
+
+ CombatGroup* player_group = campaign->GetPlayerGroup();
+
+ switch (player_group->Type()) {
+ case CombatGroup::WING: {
+ CombatGroup* wing = player_group;
+ ListIter<CombatGroup> iter = wing->GetComponents();
+
+ while (++iter) {
+ if (iter->Type() == CombatGroup::FIGHTER_SQUADRON) {
+ squadron = iter.value();
+ }
+ }
+ }
+ break;
+
+ case CombatGroup::FIGHTER_SQUADRON:
+ case CombatGroup::INTERCEPT_SQUADRON:
+ case CombatGroup::ATTACK_SQUADRON:
+ squadron = player_group;
+ break;
+
+ default:
+ ::Print("ERROR - CMF invalid player group: %s IFF %d\n",
+ player_group->GetDescription(),
+ player_group->GetIFF());
+ break;
+ }
+
+ if (squadron) {
+ CombatGroup* carrier = squadron->FindCarrier();
+
+ if (carrier && carrier->Type() == CombatGroup::STARBASE) {
+ airbase = true;
+ }
+ }
+}
+
+CampaignMissionFighter::~CampaignMissionFighter() {}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionFighter::CreateMission(CampaignMissionRequest* req)
+{
+ if (!campaign || !squadron || !req)
+ return;
+
+ ::Print("\n-----------------------------------------------\n");
+ if (req->Script().length())
+ ::Print("CMF CreateMission() request: %s '%s'\n",
+ Mission::RoleName(req->Type()),
+ (const char*) req->Script());
+
+ else
+ ::Print("CMF CreateMission() request: %s %s\n",
+ Mission::RoleName(req->Type()),
+ req->GetObjective() ? req->GetObjective()->Name() : "(no target)");
+
+ request = req;
+ mission_info = 0;
+
+ if (request->GetPrimaryGroup()) {
+ switch (request->GetPrimaryGroup()->Type()) {
+ case CombatGroup::FIGHTER_SQUADRON:
+ case CombatGroup::INTERCEPT_SQUADRON:
+ case CombatGroup::ATTACK_SQUADRON:
+ squadron = request->GetPrimaryGroup();
+ break;
+ }
+ }
+
+ ownside = squadron->GetIFF();
+
+ for (int i = 0; i < campaign->GetCombatants().size(); i++) {
+ int iff = campaign->GetCombatants().at(i)->GetIFF();
+ if (iff > 0 && iff != ownside) {
+ enemy = iff;
+ break;
+ }
+ }
+
+ static int id_key = 1;
+ GenerateMission(id_key++);
+ DefineMissionObjectives();
+
+ MissionInfo* info = DescribeMission();
+
+ if (info) {
+ campaign->GetMissionList().append(info);
+
+ ::Print("CMF Created %03d '%s' %s\n\n",
+ info->id,
+ (const char*) info->name,
+ Mission::RoleName(mission->Type()));
+
+ if (dump_missions) {
+ Text script = mission->Serialize();
+ char fname[32];
+
+ sprintf(fname, "msn%03d.def", info->id);
+ FILE* f = fopen(fname, "w");
+ if (f) {
+ fprintf(f, "%s\n", script.data());
+ fclose(f);
+ }
+ }
+ }
+ else {
+ ::Print("CMF failed to create mission.\n");
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Mission*
+CampaignMissionFighter::GenerateMission(int id)
+{
+ bool found = false;
+
+ SelectType();
+
+ if (request && request->Script().length()) {
+ MissionTemplate* mt = new(__FILE__,__LINE__) MissionTemplate(id, request->Script(), campaign->Path());
+ if (mt)
+ mt->SetPlayerSquadron(squadron);
+ mission = mt;
+ found = true;
+ }
+
+ else {
+ mission_info = campaign->FindMissionTemplate(mission_type, squadron);
+ found = mission_info != 0;
+
+ if (found) {
+ MissionTemplate* mt = new(__FILE__,__LINE__) MissionTemplate(id, mission_info->script, campaign->Path());
+ if (mt)
+ mt->SetPlayerSquadron(squadron);
+ mission = mt;
+ }
+ else {
+ mission = new(__FILE__,__LINE__) Mission(id);
+ if (mission)
+ mission->SetType(mission_type);
+ }
+ }
+
+ if (!mission) {
+ Exit();
+ return 0;
+ }
+
+ char name[64];
+ sprintf(name, "Fighter Mission %d", id);
+
+ mission->SetName(name);
+ mission->SetTeam(squadron->GetIFF());
+ mission->SetStart(request->StartTime());
+
+ SelectRegion();
+ GenerateStandardElements();
+ CreatePatrols();
+
+ if (!found) {
+ GenerateMissionElements();
+ mission->SetOK(true);
+ mission->Validate();
+ }
+
+ else {
+ mission->Load();
+
+ if (mission->IsOK()) {
+ player_elem = mission->GetPlayer();
+ prime_target = mission->GetTarget();
+ ward = mission->GetWard();
+
+ Player* p = Player::GetCurrentPlayer();
+
+ if (player_elem && p)
+ player_elem->SetAlert(!p->FlyingStart());
+ }
+
+ // if there was a problem, scrap the mission
+ // and start over:
+ else {
+ delete mission;
+
+ mission = new(__FILE__,__LINE__) Mission(id);
+ mission->SetType(mission_type);
+ mission->SetName(name);
+ mission->SetTeam(squadron->GetIFF());
+ mission->SetStart(request->StartTime());
+
+ SelectRegion();
+ GenerateStandardElements();
+ GenerateMissionElements();
+
+ mission->SetOK(true);
+ mission->Validate();
+ }
+ }
+
+ return mission;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CampaignMissionFighter::IsGroundObjective(CombatGroup* obj)
+{
+ bool ground = false;
+
+ if (obj) {
+ CombatGroup* pgroup = campaign->GetPlayerGroup();
+
+ if (pgroup) {
+ CombatZone* zone = pgroup->GetAssignedZone();
+
+ if (zone) {
+ StarSystem* system = campaign->GetSystem(zone->System());
+
+ if (system) {
+ OrbitalRegion* region = system->FindRegion(obj->GetRegion());
+
+ if (region && region->Type() == Orbital::TERRAIN) {
+ ground = true;
+ }
+ }
+ }
+ }
+ }
+
+ return ground;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionFighter::SelectType()
+{
+ int type = Mission::PATROL;
+
+ if (request) {
+ type = request->Type();
+ if (type == Mission::STRIKE) {
+ strike_group = request->GetPrimaryGroup();
+
+ // verify that objective is a ground target:
+ if (!IsGroundObjective(request->GetObjective())) {
+ type = Mission::ASSAULT;
+ }
+ }
+
+ else if (type == Mission::ESCORT_STRIKE) {
+ strike_group = request->GetSecondaryGroup();
+ if (!strike_group || strike_group->CalcValue() < 1) {
+ type = Mission::SWEEP;
+ strike_group = 0;
+ }
+ }
+ }
+
+ mission_type = type;
+}
+
+void
+CampaignMissionFighter::SelectRegion()
+{
+ CombatZone* zone = squadron->GetAssignedZone();
+
+ if (!zone)
+ zone = squadron->GetCurrentZone();
+
+ if (zone) {
+ mission->SetStarSystem(campaign->GetSystem(zone->System()));
+ mission->SetRegion(*zone->GetRegions().at(0));
+
+ orb_region = mission->GetRegion();
+
+ if (zone->GetRegions().size() > 1) {
+ air_region = *zone->GetRegions().at(1);
+
+ StarSystem* system = mission->GetStarSystem();
+ OrbitalRegion* rgn = 0;
+
+ if (system)
+ rgn = system->FindRegion(air_region);
+
+ if (!rgn || rgn->Type() != Orbital::TERRAIN)
+ air_region = "";
+ }
+
+ if (air_region.length() > 0) {
+ if (request && IsGroundObjective(request->GetObjective())) {
+ airborne = true;
+ }
+
+ else if (mission->Type() >= Mission::AIR_PATROL &&
+ mission->Type() <= Mission::AIR_INTERCEPT) {
+ airborne = true;
+ }
+
+ else if (mission->Type() == Mission::STRIKE ||
+ mission->Type() == Mission::ESCORT_STRIKE) {
+ if (strike_group) {
+ strike_target = campaign->FindStrikeTarget(ownside, strike_group);
+
+ if (strike_target && strike_target->GetRegion() == air_region)
+ airborne = true;
+ }
+ }
+
+ if (airbase) {
+ mission->SetRegion(air_region);
+ }
+ }
+ }
+
+ else {
+ ::Print("WARNING: CMF - No zone for '%s'\n", squadron->Name().data());
+
+ StarSystem* s = campaign->GetSystemList()[0];
+
+ mission->SetStarSystem(s);
+ mission->SetRegion(s->Regions()[0]->Name());
+ }
+
+ if (!airborne) {
+ switch (mission->Type()) {
+ case Mission::AIR_PATROL: mission->SetType(Mission::PATROL); break;
+ case Mission::AIR_SWEEP: mission->SetType(Mission::SWEEP); break;
+ case Mission::AIR_INTERCEPT: mission->SetType(Mission::INTERCEPT); break;
+ default: break;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionFighter::GenerateStandardElements()
+{
+ ListIter<CombatZone> z_iter = campaign->GetZones();
+ while (++z_iter) {
+ CombatZone* z = z_iter.value();
+
+ ListIter<ZoneForce> iter = z->GetForces();
+ while (++iter) {
+ ZoneForce* force = iter.value();
+ ListIter<CombatGroup> group = force->GetGroups();
+
+ while (++group) {
+ CombatGroup* g = group.value();
+
+ switch (g->Type()) {
+ case CombatGroup::INTERCEPT_SQUADRON:
+ case CombatGroup::FIGHTER_SQUADRON:
+ case CombatGroup::ATTACK_SQUADRON:
+ case CombatGroup::LCA_SQUADRON:
+ CreateSquadron(g);
+ break;
+
+ case CombatGroup::DESTROYER_SQUADRON:
+ case CombatGroup::BATTLE_GROUP:
+ case CombatGroup::CARRIER_GROUP:
+ CreateElements(g);
+ break;
+
+ case CombatGroup::MINEFIELD:
+ case CombatGroup::BATTERY:
+ case CombatGroup::MISSILE:
+ case CombatGroup::STATION:
+ case CombatGroup::STARBASE:
+ case CombatGroup::SUPPORT:
+ case CombatGroup::COURIER:
+ case CombatGroup::MEDICAL:
+ case CombatGroup::SUPPLY:
+ case CombatGroup::REPAIR:
+ CreateElements(g);
+ break;
+
+ case CombatGroup::CIVILIAN:
+ case CombatGroup::WAR_PRODUCTION:
+ case CombatGroup::FACTORY:
+ case CombatGroup::REFINERY:
+ case CombatGroup::RESOURCE:
+ case CombatGroup::INFRASTRUCTURE:
+ case CombatGroup::TRANSPORT:
+ case CombatGroup::NETWORK:
+ case CombatGroup::HABITAT:
+ case CombatGroup::STORAGE:
+ case CombatGroup::NON_COM:
+ CreateElements(g);
+ break;
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionFighter::GenerateMissionElements()
+{
+ CreateWards();
+ CreatePlayer(squadron);
+ CreateTargets();
+ CreateEscorts();
+
+ if (player_elem) {
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(mission->GetRegion(),
+ Point(0,0,0),
+ Instruction::RTB);
+
+ if (obj)
+ player_elem->AddObjective(obj);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionFighter::CreateElements(CombatGroup* g)
+{
+ MissionElement* elem = 0;
+ List<CombatUnit>& units = g->GetUnits();
+
+ CombatUnit* cmdr = 0;
+
+ for (int i = 0; i < units.size(); i++) {
+ elem = CreateSingleElement(g, units[i]);
+
+ if (elem) {
+ if (!cmdr) {
+ cmdr = units[i];
+ }
+ else {
+ elem->SetCommander(cmdr->Name());
+
+ if (g->Type() == CombatGroup::CARRIER_GROUP &&
+ elem->MissionRole() == Mission::ESCORT) {
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::ESCORT, cmdr->Name());
+ if (obj)
+ elem->AddObjective(obj);
+ }
+ }
+
+ mission->AddElement(elem);
+ }
+ }
+}
+
+MissionElement*
+CampaignMissionFighter::CreateSingleElement(CombatGroup* g, CombatUnit* u)
+{
+ if (!g || g->IsReserve()) return 0;
+ if (!u || u->LiveCount() < 1) return 0;
+
+ // make sure this unit is actually in the right star system:
+ Galaxy* galaxy = Galaxy::GetInstance();
+ if (galaxy) {
+ if (galaxy->FindSystemByRegion(u->GetRegion()) !=
+ galaxy->FindSystemByRegion(squadron->GetRegion())) {
+ return 0;
+ }
+ }
+
+ // make sure this unit isn't already in the mission:
+ ListIter<MissionElement> e_iter = mission->GetElements();
+ while (++e_iter) {
+ MissionElement* elem = e_iter.value();
+
+ if (elem->GetCombatUnit() == u)
+ return 0;
+ }
+
+ MissionElement* elem = new(__FILE__,__LINE__) MissionElement;
+ if (!elem) {
+ Exit();
+ return 0;
+ }
+
+ if (u->Name().length())
+ elem->SetName(u->Name());
+ else
+ elem->SetName(u->DesignName());
+
+ elem->SetElementID(pkg_id++);
+
+ elem->SetDesign(u->GetDesign());
+ elem->SetCount(u->LiveCount());
+ elem->SetIFF(u->GetIFF());
+ elem->SetIntelLevel(g->IntelLevel());
+ elem->SetRegion(u->GetRegion());
+ elem->SetHeading(u->GetHeading());
+
+ int unit_index = g->GetUnits().index(u);
+ Point base_loc = u->Location();
+ bool exact = u->IsStatic(); // exact unit-level placement
+
+ if (base_loc.length() < 1) {
+ base_loc = g->Location();
+ exact = false;
+ }
+
+ if (unit_index < 0 || unit_index > 0 && !exact) {
+ Point loc = RandomDirection();
+
+ if (!u->IsStatic()) {
+ while (fabs(loc.y) > fabs(loc.x))
+ loc = RandomDirection();
+
+ loc *= 10e3 + 9e3 * unit_index;
+ }
+ else {
+ loc *= 2e3 + 2e3 * unit_index;
+ }
+
+ elem->SetLocation(base_loc + loc);
+ }
+ else {
+ elem->SetLocation(base_loc);
+ }
+
+ if (g->Type() == CombatGroup::CARRIER_GROUP) {
+ if (u->Type() == Ship::CARRIER) {
+ elem->SetMissionRole(Mission::FLIGHT_OPS);
+
+ if (squadron && elem->GetCombatGroup() == squadron->FindCarrier())
+ carrier_elem = elem;
+
+ else if (!carrier_elem && u->GetIFF() == squadron->GetIFF())
+ carrier_elem = elem;
+ }
+ else {
+ elem->SetMissionRole(Mission::ESCORT);
+ }
+ }
+ else if (u->Type() == Ship::STATION ||
+ u->Type() == Ship::STARBASE) {
+ elem->SetMissionRole(Mission::FLIGHT_OPS);
+
+ if (squadron && elem->GetCombatGroup() == squadron->FindCarrier()) {
+ carrier_elem = elem;
+
+ if (u->Type() == Ship::STARBASE)
+ airbase = true;
+ }
+ }
+ else if (u->Type() == Ship::FARCASTER) {
+ elem->SetMissionRole(Mission::OTHER);
+
+ // link farcaster to other terminus:
+ Text name = u->Name();
+ int dash = -1;
+
+ for (int i = 0; i < (int) name.length(); i++)
+ if (name[i] == '-')
+ dash = i;
+
+ Text src = name.substring(0, dash);
+ Text dst = name.substring(dash+1, name.length() - (dash+1));
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::VECTOR, dst + "-" + src);
+ elem->AddObjective(obj);
+ }
+ else if (u->Type() & Ship::STARSHIPS != 0) {
+ elem->SetMissionRole(Mission::FLEET);
+ }
+
+ elem->SetCombatGroup(g);
+ elem->SetCombatUnit(u);
+
+ return elem;
+}
+
+CombatUnit*
+CampaignMissionFighter::FindCarrier(CombatGroup* g)
+{
+ CombatGroup* carrier = g->FindCarrier();
+
+ if (carrier && carrier->GetUnits().size()) {
+ MissionElement* carrier_elem = mission->FindElement(carrier->Name());
+
+ if (carrier_elem)
+ return carrier->GetUnits().at(0);
+ }
+
+ return 0;
+}
+
+void
+CampaignMissionFighter::CreateSquadron(CombatGroup* g)
+{
+ if (!g || g->IsReserve()) return;
+
+ CombatUnit* fighter = g->GetUnits().at(0);
+ CombatUnit* carrier = FindCarrier(g);
+
+ if (!fighter || !carrier) return;
+
+ int live_count = fighter->LiveCount();
+ int maint_count = (live_count > 4) ? live_count / 2 : 0;
+
+ MissionElement* elem = new(__FILE__,__LINE__) MissionElement;
+
+ if (!elem) {
+ Exit();
+ return;
+ }
+
+ elem->SetName(g->Name());
+ elem->SetElementID(pkg_id++);
+
+ elem->SetDesign(fighter->GetDesign());
+ elem->SetCount(fighter->Count());
+ elem->SetDeadCount(fighter->DeadCount());
+ elem->SetMaintCount(maint_count);
+ elem->SetIFF(fighter->GetIFF());
+ elem->SetIntelLevel(g->IntelLevel());
+ elem->SetRegion(fighter->GetRegion());
+
+ elem->SetCarrier(carrier->Name());
+ elem->SetCommander(carrier->Name());
+ elem->SetLocation(carrier->Location() + RandomPoint());
+
+ elem->SetCombatGroup(g);
+ elem->SetCombatUnit(fighter);
+
+ mission->AddElement(elem);
+}
+
+void
+CampaignMissionFighter::CreatePlayer(CombatGroup* g)
+{
+ int pkg_size = 2;
+
+ if (mission->Type() == Mission::STRIKE || mission->Type() == Mission::ASSAULT) {
+ if (request && request->GetObjective()) {
+ int tgt_type = request->GetObjective()->Type();
+
+ if (tgt_type >= CombatGroup::FLEET && tgt_type <= CombatGroup::CARRIER_GROUP)
+ pkg_size = 4;
+
+ if (tgt_type == CombatGroup::STATION || tgt_type == CombatGroup::STARBASE)
+ pkg_size = 4;
+ }
+ }
+
+ MissionElement* elem = CreateFighterPackage(g, pkg_size, mission->Type());
+
+ if (elem) {
+ Player* p = Player::GetCurrentPlayer();
+ elem->SetAlert(p ? !p->FlyingStart() : true);
+ elem->SetPlayer(1);
+
+ if (ward) {
+ Point approach = elem->Location() - ward->Location();
+ approach.Normalize();
+
+ Point pickup = ward->Location() + approach * 50e3;
+ double delta = (pickup - elem->Location()).length();
+
+ if (delta > 30e3) {
+ Instruction* n = new(__FILE__,__LINE__) Instruction(elem->Region(), pickup, Instruction::ESCORT);
+ n->SetTarget(ward->Name());
+ n->SetSpeed(750);
+ elem->AddNavPoint(n);
+ }
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::ESCORT, ward->Name());
+
+ switch (mission->Type()) {
+ case Mission::ESCORT_FREIGHT:
+ obj->SetTargetDesc(Text("the star freighter ") + ward->Name());
+ break;
+
+ case Mission::ESCORT_SHUTTLE:
+ obj->SetTargetDesc(Text("the shuttle ") + ward->Name());
+ break;
+
+ case Mission::ESCORT_STRIKE:
+ obj->SetTargetDesc(Text("the ") + ward->Name() + Text(" strike package"));
+ break;
+
+ case Mission::DEFEND:
+ obj->SetTargetDesc(Text("the ") + ward->Name());
+ break;
+
+ default:
+ if (ward->GetCombatGroup()) {
+ obj->SetTargetDesc(Text("the ") + ward->GetCombatGroup()->GetDescription());
+ }
+ else {
+ obj->SetTargetDesc(Text("the ") + ward->Name());
+ }
+ break;
+ }
+
+ elem->AddObjective(obj);
+ }
+
+ mission->AddElement(elem);
+
+ player_elem = elem;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionFighter::CreatePatrols()
+{
+ List<MissionElement> patrols;
+
+ ListIter<MissionElement> iter = mission->GetElements();
+ while (++iter) {
+ MissionElement* squad_elem = iter.value();
+ CombatGroup* squadron = squad_elem->GetCombatGroup();
+ CombatUnit* unit = squad_elem->GetCombatUnit();
+
+ if (!squad_elem->IsSquadron() || !squadron || !unit || unit->LiveCount() < 4)
+ continue;
+
+ if (squadron->Type() == CombatGroup::INTERCEPT_SQUADRON ||
+ squadron->Type() == CombatGroup::FIGHTER_SQUADRON) {
+
+ StarSystem* system = mission->GetStarSystem();
+ CombatGroup* base = squadron->FindCarrier();
+ OrbitalRegion* region = system->FindRegion(base->GetRegion());
+ int patrol_type = Mission::PATROL;
+ Point base_loc;
+
+ if (!base || !region)
+ continue;
+
+ if (region->Type() == Orbital::TERRAIN) {
+ patrol_type = Mission::AIR_PATROL;
+
+ if (RandomChance(2,3))
+ continue;
+ }
+
+ base_loc = base->Location() + RandomPoint() * 1.5;
+
+ if (region->Type() == Orbital::TERRAIN)
+ base_loc += Point(0, 0, 14.0e3);
+
+ MissionElement* elem = CreateFighterPackage(squadron, 2, patrol_type);
+ if (elem) {
+ elem->SetIntelLevel(Intel::KNOWN);
+ elem->SetRegion(base->GetRegion());
+ elem->SetLocation(base_loc);
+ patrols.append(elem);
+ }
+ }
+ }
+
+ iter.attach(patrols);
+ while (++iter)
+ mission->AddElement(iter.value());
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionFighter::CreateWards()
+{
+ switch (mission->Type()) {
+ case Mission::ESCORT_FREIGHT: CreateWardFreight(); break;
+ case Mission::ESCORT_SHUTTLE: CreateWardShuttle(); break;
+ case Mission::ESCORT_STRIKE: CreateWardStrike(); break;
+ default: break;
+ }
+}
+
+void
+CampaignMissionFighter::CreateWardFreight()
+{
+ if (!mission || !mission->GetStarSystem()) return;
+
+ CombatUnit* carrier = FindCarrier(squadron);
+ CombatGroup* freight = 0;
+
+ if (request)
+ freight = request->GetObjective();
+
+ if (!freight)
+ freight = campaign->FindGroup(ownside, CombatGroup::FREIGHT);
+
+ if (!freight || freight->CalcValue() < 1) return;
+
+ CombatUnit* unit = freight->GetNextUnit();
+ if (!unit) return;
+
+ MissionElement* elem = CreateSingleElement(freight, unit);
+ if (!elem) return;
+
+ elem->SetMissionRole(Mission::CARGO);
+ elem->SetIntelLevel(Intel::KNOWN);
+ elem->SetRegion(squadron->GetRegion());
+
+ if (carrier)
+ elem->SetLocation(carrier->Location() + RandomPoint() * 2);
+
+ ward = elem;
+ mission->AddElement(elem);
+
+
+ StarSystem* system = mission->GetStarSystem();
+ OrbitalRegion* rgn1 = system->FindRegion(elem->Region());
+ Point delta = rgn1->Location() - rgn1->Primary()->Location();
+ Point npt_loc = elem->Location();
+ Instruction* n = 0;
+
+ delta.Normalize();
+ delta *= 200.0e3;
+
+ npt_loc += delta;
+
+ n = new(__FILE__,__LINE__) Instruction(elem->Region(),
+ npt_loc,
+ Instruction::VECTOR);
+
+ if (n) {
+ n->SetSpeed(500);
+ elem->AddNavPoint(n);
+ }
+
+ Text rgn2 = elem->Region();
+ List<CombatZone>& zones = campaign->GetZones();
+ if (zones[zones.size()-1]->HasRegion(rgn2))
+ rgn2 = *zones[0]->GetRegions()[0];
+ else
+ rgn2 = *zones[zones.size()-1]->GetRegions()[0];
+
+ n = new(__FILE__,__LINE__) Instruction(rgn2,
+ Point(0, 0, 0),
+ Instruction::VECTOR);
+
+ if (n) {
+ n->SetSpeed(750);
+ elem->AddNavPoint(n);
+ }
+}
+
+void
+CampaignMissionFighter::CreateWardShuttle()
+{
+ if (!mission || !mission->GetStarSystem()) return;
+
+ CombatUnit* carrier = FindCarrier(squadron);
+ CombatGroup* shuttle = campaign->FindGroup(ownside, CombatGroup::LCA_SQUADRON);
+
+ if (!shuttle || shuttle->CalcValue() < 1) return;
+
+ List<CombatUnit>& units = shuttle->GetUnits();
+
+ MissionElement* elem = CreateFighterPackage(shuttle, 1, Mission::CARGO);
+ if (!elem) return;
+
+ elem->SetIntelLevel(Intel::KNOWN);
+ elem->SetRegion(orb_region);
+ elem->Loadouts().destroy();
+
+ if (carrier)
+ elem->SetLocation(carrier->Location() + RandomPoint() * 2);
+
+ ward = elem;
+ mission->AddElement(elem);
+
+ // if there is terrain nearby, then have the shuttle fly down to it:
+ if (air_region.length() > 0) {
+ StarSystem* system = mission->GetStarSystem();
+ OrbitalRegion* rgn1 = system->FindRegion(elem->Region());
+ Point delta = rgn1->Location() - rgn1->Primary()->Location();
+ Point npt_loc = elem->Location();
+ Instruction* n = 0;
+
+ delta.Normalize();
+ delta *= -200.0e3;
+
+ npt_loc += delta;
+
+ n = new(__FILE__,__LINE__) Instruction(elem->Region(),
+ npt_loc,
+ Instruction::VECTOR);
+
+ if (n) {
+ n->SetSpeed(500);
+ elem->AddNavPoint(n);
+ }
+
+ n = new(__FILE__,__LINE__) Instruction(air_region,
+ Point(0, 0, 10.0e3),
+ Instruction::VECTOR);
+
+ if (n) {
+ n->SetSpeed(500);
+ elem->AddNavPoint(n);
+ }
+ }
+
+ // otherwise, escort the shuttle in for a landing on the carrier:
+ else if (carrier) {
+ Point src = carrier->Location() + RandomDirection() * 150e3;
+ Point dst = carrier->Location() + RandomDirection() * 25e3;
+ Instruction* n = 0;
+
+ elem->SetLocation(src);
+
+ n = new(__FILE__,__LINE__) Instruction(elem->Region(), dst, Instruction::DOCK);
+ if (n) {
+ n->SetTarget(carrier->Name());
+ n->SetSpeed(500);
+ elem->AddNavPoint(n);
+ }
+ }
+}
+
+void
+CampaignMissionFighter::CreateWardStrike()
+{
+ if (!mission || !mission->GetStarSystem()) return;
+
+ CombatUnit* carrier = FindCarrier(squadron);
+ CombatGroup* strike = strike_group;
+
+ if (!strike || strike->CalcValue() < 1) return;
+
+ List<CombatUnit>& units = strike->GetUnits();
+
+ int type = Mission::ASSAULT;
+
+ if (airborne)
+ type = Mission::STRIKE;
+
+ MissionElement* elem = CreateFighterPackage(strike, 2, type);
+ if (!elem) return;
+
+ if (strike->GetParent() == squadron->GetParent()) {
+ Player* p = Player::GetCurrentPlayer();
+ elem->SetAlert(p ? !p->FlyingStart() : true);
+ }
+
+ elem->SetIntelLevel(Intel::KNOWN);
+ elem->SetRegion(squadron->GetRegion());
+
+ if (strike_target) {
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::ASSAULT, strike_target->Name());
+
+ if (obj) {
+ if (airborne)
+ obj->SetAction(Instruction::STRIKE);
+
+ elem->AddObjective(obj);
+ }
+ }
+
+ ward = elem;
+ mission->AddElement(elem);
+
+
+ StarSystem* system = mission->GetStarSystem();
+ OrbitalRegion* rgn1 = system->FindRegion(elem->Region());
+ Point delta = rgn1->Location() - rgn1->Primary()->Location();
+ Point npt_loc = elem->Location();
+ Instruction* n = 0;
+
+ if (airborne) {
+ delta.Normalize();
+ delta *= -30.0e3;
+ npt_loc += delta;
+
+ n = new(__FILE__,__LINE__) Instruction(elem->Region(),
+ npt_loc,
+ Instruction::VECTOR);
+ if (n) {
+ n->SetSpeed(500);
+ elem->AddNavPoint(n);
+ }
+
+ npt_loc = Point(0, 0, 10.0e3);
+
+ n = new(__FILE__,__LINE__) Instruction(air_region,
+ npt_loc,
+ Instruction::VECTOR);
+ if (n) {
+ n->SetSpeed(500);
+ elem->AddNavPoint(n);
+ }
+ }
+
+ // IP:
+ if (strike_target) {
+ delta = strike_target->Location() - npt_loc;
+ delta.Normalize();
+ delta *= 15.0e3;
+
+ npt_loc = strike_target->Location() + delta + Point(0, 0, 8.0e3);
+
+ n = new(__FILE__,__LINE__) Instruction(strike_target->GetRegion(),
+ npt_loc,
+ Instruction::STRIKE);
+ if (n) {
+ n->SetSpeed(500);
+ elem->AddNavPoint(n);
+ }
+ }
+
+ if (airborne) {
+ n = new(__FILE__,__LINE__) Instruction(air_region,
+ Point(0, 0, 30.0e3),
+ Instruction::VECTOR);
+ if (n) {
+ n->SetSpeed(500);
+ elem->AddNavPoint(n);
+ }
+ }
+
+ if (carrier) {
+ n = new(__FILE__,__LINE__) Instruction(elem->Region(),
+ carrier->Location() - Point(0, -20.0e3, 0),
+ Instruction::VECTOR);
+ if (n) {
+ n->SetSpeed(500);
+ elem->AddNavPoint(n);
+ }
+ }
+
+ // find the strike target element:
+ if (strike_target) {
+ prime_target = mission->FindElement(strike_target->Name());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionFighter::CreateEscorts()
+{
+ bool escort_needed = false;
+
+ if (mission->Type() == Mission::STRIKE || mission->Type() == Mission::ASSAULT) {
+ if (request && request->GetObjective()) {
+ int tgt_type = request->GetObjective()->Type();
+
+ if (tgt_type == CombatGroup::CARRIER_GROUP ||
+ tgt_type == CombatGroup::STATION ||
+ tgt_type == CombatGroup::STARBASE)
+
+ escort_needed = true;
+ }
+ }
+
+ if (player_elem && escort_needed) {
+ CombatGroup* s = FindSquadron(ownside, CombatGroup::INTERCEPT_SQUADRON);
+
+ if (s && s->IsAssignable()) {
+ MissionElement* elem = CreateFighterPackage(s, 2, Mission::ESCORT_STRIKE);
+
+ if (elem) {
+ Point offset = Point(2.0e3, 2.0e3, 1.0e3);
+
+ ListIter<Instruction> npt_iter = player_elem->NavList();
+ while (++npt_iter) {
+ Instruction* npt = npt_iter.value();
+ Instruction* n = new(__FILE__,__LINE__) Instruction(npt->RegionName(),
+ npt->Location() + offset,
+ Instruction::ESCORT);
+ if (n) {
+ n->SetSpeed(npt->Speed());
+ elem->AddNavPoint(n);
+ }
+ }
+
+ mission->AddElement(elem);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionFighter::CreateTargets()
+{
+ switch (mission->Type()) {
+ default:
+ case Mission::DEFEND:
+ case Mission::PATROL: CreateTargetsPatrol(); break;
+ case Mission::SWEEP: CreateTargetsSweep(); break;
+ case Mission::INTERCEPT: CreateTargetsIntercept(); break;
+ case Mission::ESCORT_FREIGHT: CreateTargetsFreightEscort(); break;
+
+ case Mission::ESCORT:
+ case Mission::ESCORT_SHUTTLE: CreateTargetsShuttleEscort(); break;
+ case Mission::ESCORT_STRIKE: CreateTargetsStrikeEscort(); break;
+ case Mission::STRIKE: CreateTargetsStrike(); break;
+ case Mission::ASSAULT: CreateTargetsAssault(); break;
+ }
+}
+
+void
+CampaignMissionFighter::CreateTargetsPatrol()
+{
+ if (!squadron || !player_elem) return;
+
+ Text region = squadron->GetRegion();
+ Point base_loc = player_elem->Location();
+ Point patrol_loc;
+
+ if (airborne)
+ base_loc = RandomPoint() * 2 + Point(0, 0, 12.0e3);
+
+ else if (carrier_elem)
+ base_loc = carrier_elem->Location();
+
+ if (airborne) {
+ if (!airbase)
+ PlanetaryInsertion(player_elem);
+ region = air_region;
+ patrol_loc = base_loc +
+ RandomDirection() * Random( 60e3, 100e3);
+ }
+ else {
+ patrol_loc = base_loc +
+ RandomDirection() * Random(110e3, 160e3);
+ }
+
+ Instruction* n = new(__FILE__,__LINE__) Instruction(region,
+ patrol_loc,
+ Instruction::PATROL);
+ if (n)
+ player_elem->AddNavPoint(n);
+
+ int ntargets = (int) Random(2.0,5.1);
+
+ while (ntargets > 0) {
+ int t = CreateRandomTarget(region, patrol_loc);
+ ntargets -= t;
+ if (t < 1) break;
+ }
+
+ if (airborne && !airbase) {
+ OrbitalInsertion(player_elem);
+ }
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(*n);
+ obj->SetTargetDesc("inbound enemy units");
+ player_elem->AddObjective(obj);
+
+ if (carrier_elem && !airborne) {
+ obj = new(__FILE__,__LINE__) Instruction(Instruction::DEFEND, carrier_elem->Name());
+ if (obj) {
+ obj->SetTargetDesc(Text("the ") + carrier_elem->Name() + " battle group");
+ player_elem->AddObjective(obj);
+ }
+ }
+}
+
+void
+CampaignMissionFighter::CreateTargetsSweep()
+{
+ if (!squadron || !player_elem) return;
+
+ double traverse = PI;
+ double a = Random(-PI/2, PI/2);
+ Point base_loc = player_elem->Location();
+ Point sweep_loc = base_loc;
+ Text region = player_elem->Region();
+ Instruction* n = 0;
+
+ if (carrier_elem)
+ base_loc = carrier_elem->Location();
+
+ if (airborne) {
+ PlanetaryInsertion(player_elem);
+ region = air_region;
+ sweep_loc = RandomPoint() + Point(0, 0, 10.0e3); // keep it airborne!
+ }
+
+ sweep_loc += Point(sin(a), -cos(a), 0) * 100.0e3;
+
+ n = new(__FILE__,__LINE__) Instruction(region,
+ sweep_loc,
+ Instruction::VECTOR);
+ if (n) {
+ n->SetSpeed(750);
+ player_elem->AddNavPoint(n);
+ }
+
+ int index = 0;
+ int ntargets = 6;
+
+ while (traverse > 0) {
+ double a1 = Random(PI/4, PI/2);
+ traverse -= a1;
+ a += a1;
+
+ sweep_loc += Point(sin(a), -cos(a), 0) * 80.0e3;
+
+ n = new(__FILE__,__LINE__) Instruction(region,
+ sweep_loc,
+ Instruction::SWEEP);
+ if (n) {
+ n->SetSpeed(750);
+ n->SetFormation(Instruction::SPREAD);
+ player_elem->AddNavPoint(n);
+ }
+
+ if (ntargets && RandomChance()) {
+ ntargets -= CreateRandomTarget(region, sweep_loc);
+ }
+
+ index++;
+ }
+
+ if (ntargets > 0)
+ CreateRandomTarget(region, sweep_loc);
+
+ if (airborne && !airbase) {
+ OrbitalInsertion(player_elem);
+ region = player_elem->Region();
+ }
+
+ sweep_loc = base_loc;
+ sweep_loc.y += 30.0e3;
+
+ n = new(__FILE__,__LINE__) Instruction(region,
+ sweep_loc,
+ Instruction::VECTOR);
+ if (n) {
+ n->SetSpeed(750);
+ player_elem->AddNavPoint(n);
+ }
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(region,
+ sweep_loc,
+ Instruction::SWEEP);
+ if (obj) {
+ obj->SetTargetDesc("enemy patrols");
+ player_elem->AddObjective(obj);
+ }
+
+ if (carrier_elem && !airborne) {
+ obj = new(__FILE__,__LINE__) Instruction(Instruction::DEFEND, carrier_elem->Name());
+ if (obj) {
+ obj->SetTargetDesc(Text("the ") + carrier_elem->Name() + " battle group");
+ player_elem->AddObjective(obj);
+ }
+ }
+}
+
+void
+CampaignMissionFighter::CreateTargetsIntercept()
+{
+ if (!squadron || !player_elem) return;
+
+ CombatUnit* carrier = FindCarrier(squadron);
+ CombatGroup* s = FindSquadron(enemy, CombatGroup::ATTACK_SQUADRON);
+ CombatGroup* s2 = FindSquadron(enemy, CombatGroup::FIGHTER_SQUADRON);
+
+ if (!s || !s2) return;
+
+ int ninbound = 2 + (int) (RandomIndex() < 5);
+ bool second = ninbound > 2;
+ Text attacker;
+
+ while (ninbound--) {
+ MissionElement* elem = CreateFighterPackage(s, 4, Mission::ASSAULT);
+ if (elem) {
+ elem->SetIntelLevel(Intel::KNOWN);
+ elem->Loadouts().destroy();
+ elem->Loadouts().append(new(__FILE__,__LINE__) MissionLoad(-1, "Hvy Ship Strike"));
+
+ if (carrier) {
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::ASSAULT, carrier->Name());
+ if (obj) {
+ elem->AddObjective(obj);
+ elem->SetLocation(carrier->Location() + RandomPoint() * 6);
+ }
+ }
+ else {
+ elem->SetLocation(squadron->Location() + RandomPoint() * 5);
+ }
+
+ mission->AddElement(elem);
+
+ attacker = elem->Name();
+
+ if (!prime_target) {
+ prime_target = elem;
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::INTERCEPT, attacker);
+ if (obj) {
+ obj->SetTargetDesc(Text("inbound strike package '") + elem->Name() + "'");
+ player_elem->AddObjective(obj);
+ }
+ }
+
+ MissionElement* e2 = CreateFighterPackage(s2, 2, Mission::ESCORT);
+ if (e2) {
+ e2->SetIntelLevel(Intel::KNOWN);
+ e2->SetLocation(elem->Location() + RandomPoint() * 0.25);
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::ESCORT, elem->Name());
+ if (obj)
+ e2->AddObjective(obj);
+ mission->AddElement(e2);
+ }
+ }
+ }
+
+ if (second) {
+ // second friendly fighter package
+ CombatGroup* s = FindSquadron(ownside, CombatGroup::FIGHTER_SQUADRON);
+
+ if (s) {
+ MissionElement* elem = CreateFighterPackage(s, 2, Mission::INTERCEPT);
+ if (elem) {
+ Player* p = Player::GetCurrentPlayer();
+ elem->SetAlert(p ? !p->FlyingStart() : true);
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::INTERCEPT, attacker);
+ if (obj)
+ elem->AddObjective(obj);
+ mission->AddElement(elem);
+ }
+ }
+ }
+
+ if (carrier && !airborne) {
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::DEFEND, carrier->Name());
+ if (obj) {
+ obj->SetTargetDesc(Text("the ") + carrier->Name() + " battle group");
+ player_elem->AddObjective(obj);
+ }
+ }
+}
+
+void
+CampaignMissionFighter::CreateTargetsFreightEscort()
+{
+ if (!squadron || !player_elem) return;
+
+ if (!ward) {
+ CreateTargetsPatrol();
+ return;
+ }
+
+ CombatUnit* carrier = FindCarrier(squadron);
+ CombatGroup* s = FindSquadron(enemy, CombatGroup::ATTACK_SQUADRON);
+ CombatGroup* s2 = FindSquadron(enemy, CombatGroup::FIGHTER_SQUADRON);
+
+ if (!s) s = s2;
+
+ if (!s || !s2) return;
+
+ MissionElement* elem = CreateFighterPackage(s, 2, Mission::ASSAULT);
+ if (elem) {
+ elem->SetIntelLevel(Intel::KNOWN);
+
+ elem->SetLocation(ward->Location() + RandomPoint() * 5);
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::ASSAULT, ward->Name());
+ if (obj)
+ elem->AddObjective(obj);
+ mission->AddElement(elem);
+
+ MissionElement* e2 = CreateFighterPackage(s2, 2, Mission::ESCORT);
+ if (e2) {
+ e2->SetIntelLevel(Intel::KNOWN);
+ e2->SetLocation(elem->Location() + RandomPoint() * 0.25);
+
+ Instruction* obj2 = new(__FILE__,__LINE__) Instruction(Instruction::ESCORT, elem->Name());
+ if (obj2)
+ e2->AddObjective(obj2);
+ mission->AddElement(e2);
+ }
+ }
+
+ Instruction* obj3 = new(__FILE__,__LINE__) Instruction(mission->GetRegion(),
+ Point(0,0,0),
+ Instruction::PATROL);
+
+ if (obj3) {
+ obj3->SetTargetDesc("enemy patrols");
+ player_elem->AddObjective(obj3);
+ }
+}
+
+void
+CampaignMissionFighter::CreateTargetsShuttleEscort()
+{
+ CreateTargetsFreightEscort();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionFighter::CreateTargetsStrikeEscort()
+{
+ if (!squadron || !player_elem) return;
+
+ if (ward) {
+ Point offset = Point(2.0e3, 2.0e3, 1.0e3);
+
+ ListIter<Instruction> npt_iter = ward->NavList();
+ while (++npt_iter) {
+ Instruction* npt = npt_iter.value();
+ Instruction* n = new(__FILE__,__LINE__) Instruction(npt->RegionName(),
+ npt->Location() + offset,
+ Instruction::ESCORT);
+ if (n) {
+ n->SetSpeed(npt->Speed());
+ player_elem->AddNavPoint(n);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionFighter::CreateTargetsStrike()
+{
+ if (!squadron || !player_elem) return;
+
+ if (request && request->GetObjective())
+ strike_target = request->GetObjective();
+
+ if (strike_target && strike_group) {
+ CreateElements(strike_target);
+
+ ListIter<MissionElement> e_iter = mission->GetElements();
+ while (++e_iter) {
+ MissionElement* elem = e_iter.value();
+
+ if (elem->GetCombatGroup() == strike_target) {
+ prime_target = elem;
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::STRIKE, elem->Name());
+ if (obj) {
+ obj->SetTargetDesc(Text("preplanned target '") + elem->Name() + "'");
+ player_elem->AddObjective(obj);
+ }
+
+ // create flight plan:
+ RLoc rloc;
+ Point loc = Point(0, 0, 15e3);
+ Instruction* n = 0;
+
+ PlanetaryInsertion(player_elem);
+
+ // target approach and strike:
+ Point delta = prime_target->Location() - loc;
+
+ if (delta.length() >= 100e3) {
+ Point mid = loc + delta * 0.5;
+ mid.z = 10.0e3;
+
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(mid);
+ rloc.SetDistance(20e3);
+ rloc.SetDistanceVar(5e3);
+ rloc.SetAzimuth(90*DEGREES);
+ rloc.SetAzimuthVar(25*DEGREES);
+
+ n = new(__FILE__,__LINE__) Instruction(prime_target->Region(),
+ Point(),
+ Instruction::VECTOR);
+ if (n) {
+ n->SetSpeed(750);
+ n->GetRLoc() = rloc;
+ player_elem->AddNavPoint(n);
+ }
+
+ loc = mid;
+ }
+
+ delta = loc - prime_target->Location();
+ delta.Normalize();
+ delta *= 25.0e3;
+
+ loc = prime_target->Location() + delta;
+ loc.z = 8.0e3;
+
+ n = new(__FILE__,__LINE__) Instruction(prime_target->Region(),
+ loc,
+ Instruction::STRIKE);
+ if (n) {
+ n->SetSpeed(500);
+ player_elem->AddNavPoint(n);
+ }
+
+ // exeunt:
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(Point(0, 0, 30.0e3));
+ rloc.SetDistance(50e3);
+ rloc.SetDistanceVar(5e3);
+ rloc.SetAzimuth(-90*DEGREES);
+ rloc.SetAzimuthVar(25*DEGREES);
+
+ n = new(__FILE__,__LINE__) Instruction(prime_target->Region(),
+ Point(),
+ Instruction::VECTOR);
+ if (n) {
+ n->SetSpeed(750);
+ n->GetRLoc() = rloc;
+ player_elem->AddNavPoint(n);
+ }
+
+ if (carrier_elem) {
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(carrier_elem->Location());
+ rloc.SetDistance(60e3);
+ rloc.SetDistanceVar(10e3);
+ rloc.SetAzimuth(180*DEGREES);
+ rloc.SetAzimuthVar(30*DEGREES);
+
+ n = new(__FILE__,__LINE__) Instruction(carrier_elem->Region(),
+ Point(),
+ Instruction::RTB);
+ if (n) {
+ n->SetSpeed(750);
+ n->GetRLoc() = rloc;
+ player_elem->AddNavPoint(n);
+ }
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionFighter::CreateTargetsAssault()
+{
+ if (!squadron || !player_elem) return;
+
+ CombatGroup* assigned = 0;
+
+ if (request)
+ assigned = request->GetObjective();
+
+ if (assigned) {
+ if (assigned->Type() > CombatGroup::WING && assigned->Type() < CombatGroup::FLEET) {
+ mission->AddElement(CreateFighterPackage(assigned, 2, Mission::CARGO));
+ }
+ else {
+ CreateElements(assigned);
+ }
+
+ // select the prime target element - choose the lowest ranking
+ // unit of a DESRON, CBG, or CVBG:
+
+ ListIter<MissionElement> e_iter = mission->GetElements();
+ while (++e_iter) {
+ MissionElement* elem = e_iter.value();
+
+ if (elem->GetCombatGroup() == assigned) {
+ if (!prime_target || assigned->Type() <= CombatGroup::CARRIER_GROUP) {
+ prime_target = elem;
+ }
+ }
+ }
+
+ if (prime_target) {
+ MissionElement* elem = prime_target;
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::ASSAULT, elem->Name());
+ if (obj) {
+ obj->SetTargetDesc(Text("preplanned target '") + elem->Name() + "'");
+ player_elem->AddObjective(obj);
+ }
+
+ // create flight plan:
+ RLoc rloc;
+ Vec3 dummy(0,0,0);
+ Instruction* instr = 0;
+ Point loc = player_elem->Location();
+ Point tgt = elem->Location();
+ Point mid;
+
+ CombatGroup* tgt_group = elem->GetCombatGroup();
+ if (tgt_group && tgt_group->GetFirstUnit() && tgt_group->IsMovable()) {
+ tgt = tgt_group->GetFirstUnit()->Location();
+ }
+
+ if (carrier_elem)
+ loc = carrier_elem->Location();
+
+ mid = loc + (elem->Location() - loc) * 0.5;
+
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(mid);
+ rloc.SetDistance(40e3);
+ rloc.SetDistanceVar(5e3);
+ rloc.SetAzimuth(90*DEGREES);
+ rloc.SetAzimuthVar(45*DEGREES);
+
+ instr = new(__FILE__,__LINE__) Instruction(elem->Region(), dummy, Instruction::VECTOR);
+ if (instr) {
+ instr->SetSpeed(750);
+ instr->GetRLoc() = rloc;
+
+ player_elem->AddNavPoint(instr);
+
+ if (RandomChance()) {
+ CreateRandomTarget(elem->Region(), rloc.Location());
+ }
+ }
+
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(tgt);
+ rloc.SetDistance(60e3);
+ rloc.SetDistanceVar(5e3);
+ rloc.SetAzimuth(120*DEGREES);
+ rloc.SetAzimuthVar(15*DEGREES);
+
+ instr = new(__FILE__,__LINE__) Instruction(elem->Region(), dummy, Instruction::ASSAULT);
+ if (instr) {
+ instr->SetSpeed(750);
+ instr->GetRLoc() = rloc;
+ instr->SetTarget(elem->Name());
+
+ player_elem->AddNavPoint(instr);
+ }
+
+ if (carrier_elem) {
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(loc);
+ rloc.SetDistance(30e3);
+ rloc.SetDistanceVar(0);
+ rloc.SetAzimuth(180*DEGREES);
+ rloc.SetAzimuthVar(60*DEGREES);
+
+ instr = new(__FILE__,__LINE__) Instruction(carrier_elem->Region(), dummy, Instruction::RTB);
+ if (instr) {
+ instr->SetSpeed(500);
+ instr->GetRLoc() = rloc;
+
+ player_elem->AddNavPoint(instr);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+CampaignMissionFighter::CreateRandomTarget(const char* rgn, Point base_loc)
+{
+ if (!mission) return 0;
+
+ int ntargets = 0;
+ int ttype = RandomIndex();
+ bool oca = (mission->Type() == Mission::SWEEP);
+
+ if (ttype < 8) {
+ CombatGroup* s = 0;
+
+ if (ttype < 4)
+ s = FindSquadron(enemy, CombatGroup::INTERCEPT_SQUADRON);
+ else
+ s = FindSquadron(enemy, CombatGroup::FIGHTER_SQUADRON);
+
+ if (s) {
+ MissionElement* elem = CreateFighterPackage(s, 2, Mission::SWEEP);
+ if (elem) {
+ elem->SetIntelLevel(Intel::KNOWN);
+ elem->SetRegion(rgn);
+ elem->SetLocation(base_loc + RandomPoint() * 1.5);
+ mission->AddElement(elem);
+ ntargets++;
+ }
+ }
+ }
+ else if (ttype < 12) {
+ if (oca) {
+ CombatGroup* s = FindSquadron(enemy, CombatGroup::LCA_SQUADRON);
+
+ if (s) {
+ MissionElement* elem = CreateFighterPackage(s, 1, Mission::CARGO);
+ if (elem) {
+ elem->SetIntelLevel(Intel::KNOWN);
+ elem->SetRegion(rgn);
+ elem->SetLocation(base_loc + RandomPoint() * 2);
+ mission->AddElement(elem);
+ ntargets++;
+
+ CombatGroup* s2 = FindSquadron(enemy, CombatGroup::FIGHTER_SQUADRON);
+
+ if (s2) {
+ MissionElement* e2 = CreateFighterPackage(s2, 2, Mission::ESCORT);
+ if (e2) {
+ e2->SetIntelLevel(Intel::KNOWN);
+ e2->SetRegion(rgn);
+ e2->SetLocation(elem->Location() + RandomPoint() * 0.5);
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::ESCORT, elem->Name());
+ if (obj)
+ e2->AddObjective(obj);
+ mission->AddElement(e2);
+ ntargets++;
+ }
+ }
+ }
+ }
+ }
+ else {
+ CombatGroup* s = FindSquadron(enemy, CombatGroup::ATTACK_SQUADRON);
+
+ if (s) {
+ MissionElement* elem = CreateFighterPackage(s, 2, Mission::ASSAULT);
+ if (elem) {
+ elem->SetIntelLevel(Intel::KNOWN);
+ elem->SetRegion(rgn);
+ elem->SetLocation(base_loc + RandomPoint() * 1.3);
+ mission->AddElement(elem);
+ ntargets++;
+ }
+ }
+ }
+ }
+ else if (ttype < 15) {
+ if (oca) {
+ CombatGroup* s = 0;
+
+ if (airborne)
+ s = FindSquadron(enemy, CombatGroup::LCA_SQUADRON);
+ else
+ s = FindSquadron(enemy, CombatGroup::FREIGHT);
+
+ if (s) {
+ MissionElement* elem = CreateFighterPackage(s, 1, Mission::CARGO);
+ if (elem) {
+ elem->SetIntelLevel(Intel::KNOWN);
+ elem->SetRegion(rgn);
+ elem->SetLocation(base_loc + RandomPoint() * 2);
+ mission->AddElement(elem);
+ ntargets++;
+
+ CombatGroup* s2 = FindSquadron(enemy, CombatGroup::INTERCEPT_SQUADRON);
+
+ if (s2) {
+ MissionElement* e2 = CreateFighterPackage(s2, 2, Mission::ESCORT);
+ if (e2) {
+ e2->SetIntelLevel(Intel::KNOWN);
+ e2->SetRegion(rgn);
+ e2->SetLocation(elem->Location() + RandomPoint() * 0.5);
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::ESCORT, elem->Name());
+ if (obj)
+ e2->AddObjective(obj);
+ mission->AddElement(e2);
+ ntargets++;
+ }
+ }
+ }
+ }
+ }
+ else {
+ CombatGroup* s = FindSquadron(enemy, CombatGroup::ATTACK_SQUADRON);
+
+ if (s) {
+ MissionElement* elem = CreateFighterPackage(s, 2, Mission::ASSAULT);
+ if (elem) {
+ elem->SetIntelLevel(Intel::KNOWN);
+ elem->SetRegion(rgn);
+ elem->SetLocation(base_loc + RandomPoint() * 1.1);
+ mission->AddElement(elem);
+ ntargets++;
+
+ CombatGroup* s2 = FindSquadron(enemy, CombatGroup::FIGHTER_SQUADRON);
+
+ if (s2) {
+ MissionElement* e2 = CreateFighterPackage(s2, 2, Mission::ESCORT);
+ if (e2) {
+ e2->SetIntelLevel(Intel::KNOWN);
+ e2->SetRegion(rgn);
+ e2->SetLocation(elem->Location() + RandomPoint() * 0.5);
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::ESCORT, elem->Name());
+ if (obj)
+ e2->AddObjective(obj);
+ mission->AddElement(e2);
+ ntargets++;
+ }
+ }
+ }
+ }
+ }
+ }
+ else {
+ CombatGroup* s = FindSquadron(enemy, CombatGroup::LCA_SQUADRON);
+
+ if (s) {
+ MissionElement* elem = CreateFighterPackage(s, 2, Mission::CARGO);
+ if (elem) {
+ elem->SetIntelLevel(Intel::KNOWN);
+ elem->SetRegion(rgn);
+ elem->SetLocation(base_loc + RandomPoint() * 2);
+ mission->AddElement(elem);
+ ntargets++;
+ }
+ }
+ }
+
+ return ntargets;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionFighter::PlanetaryInsertion(MissionElement* elem)
+{
+ if (!mission || !elem) return;
+ if (!mission->GetStarSystem()) return;
+
+ MissionElement* carrier = mission->FindElement(elem->Commander());
+ StarSystem* system = mission->GetStarSystem();
+ OrbitalRegion* rgn1 = system->FindRegion(elem->Region());
+ OrbitalRegion* rgn2 = system->FindRegion(air_region);
+ Point npt_loc = elem->Location();
+ Instruction* n = 0;
+ Player* p = Player::GetCurrentPlayer();
+
+ int flying_start = p ? p->FlyingStart() : 0;
+
+ if (carrier && !flying_start) {
+ npt_loc = carrier->Location() + Point(1e3, -5e3, 0);
+ }
+
+ if (rgn1 && rgn2) {
+ double delta_t = mission->Start() - campaign->GetTime();
+ Point r1 = rgn1->PredictLocation(delta_t);
+ Point r2 = rgn2->PredictLocation(delta_t);
+
+ Point delta = r2 - r1;
+
+ delta.y *= -1;
+ delta.Normalize();
+ delta *= 10e3;
+
+ npt_loc += delta;
+
+ n = new(__FILE__,__LINE__) Instruction(elem->Region(),
+ npt_loc,
+ Instruction::VECTOR);
+ if (n) {
+ n->SetSpeed(750);
+ elem->AddNavPoint(n);
+ }
+ }
+
+ n = new(__FILE__,__LINE__) Instruction(air_region,
+ Point(0, 0, 15e3),
+ Instruction::VECTOR);
+ if (n) {
+ n->SetSpeed(750);
+ elem->AddNavPoint(n);
+ }
+}
+
+void
+CampaignMissionFighter::OrbitalInsertion(MissionElement* elem)
+{
+ Instruction* n = new(__FILE__,__LINE__) Instruction(air_region,
+ Point(0, 0, 30.0e3),
+ Instruction::VECTOR);
+ if (n) {
+ n->SetSpeed(750);
+ elem->AddNavPoint(n);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+MissionElement*
+CampaignMissionFighter::CreateFighterPackage(CombatGroup* squadron, int count, int role)
+{
+ if (!squadron) return 0;
+
+ CombatUnit* fighter = squadron->GetUnits().at(0);
+ CombatUnit* carrier = FindCarrier(squadron);
+
+ if (!fighter)
+ return 0;
+
+ int avail = fighter->LiveCount();
+ int actual = count;
+
+ if (avail < actual)
+ actual = avail;
+
+ if (avail < 1) {
+ ::Print("CMF - Insufficient fighters in squadron '%s' - %d required, %d available\n",
+ squadron->Name().data(), count, avail);
+ return 0;
+ }
+
+ MissionElement* elem = new(__FILE__,__LINE__) MissionElement;
+ if (!elem) {
+ Exit();
+ return 0;
+ }
+
+ elem->SetName(Callsign::GetCallsign(fighter->GetIFF()));
+ elem->SetElementID(pkg_id++);
+
+ if (carrier) {
+ elem->SetCommander(carrier->Name());
+ elem->SetHeading(carrier->GetHeading());
+ }
+ else {
+ elem->SetHeading(fighter->GetHeading());
+ }
+
+ elem->SetDesign(fighter->GetDesign());
+ elem->SetCount(actual);
+ elem->SetIFF(fighter->GetIFF());
+ elem->SetIntelLevel(squadron->IntelLevel());
+ elem->SetRegion(fighter->GetRegion());
+ elem->SetSquadron(squadron->Name());
+ elem->SetMissionRole(role);
+
+ switch (role) {
+ case Mission::ASSAULT:
+ if (request->GetObjective() &&
+ request->GetObjective()->Type() == CombatGroup::MINEFIELD)
+ elem->Loadouts().append(new(__FILE__,__LINE__) MissionLoad(-1, "Rockets"));
+ else
+ elem->Loadouts().append(new(__FILE__,__LINE__) MissionLoad(-1, "Ship Strike"));
+ break;
+
+ case Mission::STRIKE:
+ elem->Loadouts().append(new(__FILE__,__LINE__) MissionLoad(-1, "Ground Strike"));
+ break;
+
+ default:
+ elem->Loadouts().append(new(__FILE__,__LINE__) MissionLoad(-1, "ACM Medium Range"));
+ break;
+ }
+
+ if (carrier) {
+ Point offset = RandomPoint() * 0.3;
+ offset.y = fabs(offset.y);
+ offset.z += 2e3;
+ elem->SetLocation(carrier->Location() + offset);
+ }
+ else {
+ elem->SetLocation(fighter->Location() + RandomPoint());
+ }
+
+ elem->SetCombatGroup(squadron);
+ elem->SetCombatUnit(fighter);
+
+ return elem;
+}
+
+// +--------------------------------------------------------------------+
+
+static CombatGroup* FindCombatGroup(CombatGroup* g, int type)
+{
+ if (g->IntelLevel() <= Intel::RESERVE)
+ return 0;
+
+ if (g->GetUnits().size() > 0) {
+ for (int i = 0; i < g->GetUnits().size(); i++) {
+ CombatUnit* u = g->GetUnits().at(i);
+ if (g->Type() == type && u->LiveCount() > 0)
+ return g;
+ }
+ }
+
+ CombatGroup* result = 0;
+
+ ListIter<CombatGroup> subgroup = g->GetComponents();
+ while (++subgroup && !result)
+ result = FindCombatGroup(subgroup.value(), type);
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+CombatGroup*
+CampaignMissionFighter::FindSquadron(int iff, int type)
+{
+ if (!squadron) return 0;
+
+ CombatGroup* result = 0;
+ Campaign* campaign = Campaign::GetCampaign();
+
+ if (campaign) {
+ ListIter<Combatant> combatant = campaign->GetCombatants();
+ while (++combatant && !result) {
+ if (combatant->GetIFF() == iff) {
+ result = ::FindCombatGroup(combatant->GetForce(), type);
+
+ if (result && result->CountUnits() < 1) {
+ result = 0;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionFighter::DefineMissionObjectives()
+{
+ if (!mission || !player_elem) return;
+
+ if (prime_target) mission->SetTarget(prime_target);
+ if (ward) mission->SetWard(ward);
+
+ Text objectives;
+
+ for (int i = 0; i < player_elem->Objectives().size(); i++) {
+ Instruction* obj = player_elem->Objectives().at(i);
+ objectives += "* ";
+ objectives += obj->GetDescription();
+ objectives += ".\n";
+ }
+
+ mission->SetObjective(objectives);
+}
+
+// +--------------------------------------------------------------------+
+
+MissionInfo*
+CampaignMissionFighter::DescribeMission()
+{
+ if (!mission || !player_elem) return 0;
+
+ char name[256];
+ char player_info[256];
+
+ if (mission_info && mission_info->name.length())
+ sprintf(name, "MSN-%03d %s", mission->Identity(), mission_info->name.data());
+
+ else if (ward)
+ sprintf(name, "MSN-%03d %s %s", mission->Identity(), Game::GetText(mission->TypeName()).data(), ward->Name().data());
+
+ else if (prime_target)
+ sprintf(name, "MSN-%03d %s %s %s", mission->Identity(), Game::GetText(mission->TypeName()).data(),
+ Ship::ClassName(prime_target->GetDesign()->type),
+ prime_target->Name().data());
+
+ else
+ sprintf(name, "MSN-%03d %s", mission->Identity(), Game::GetText(mission->TypeName()).data());
+
+ if (player_elem) {
+ sprintf(player_info, "%d x %s %s '%s'",
+ player_elem->Count(),
+ (const char*) player_elem->GetDesign()->abrv,
+ (const char*) player_elem->GetDesign()->name,
+ (const char*) player_elem->Name());
+ }
+
+ MissionInfo* info = new(__FILE__,__LINE__) MissionInfo;
+
+ info->id = mission->Identity();
+ info->mission = mission;
+ info->name = name;
+ info->type = mission->Type();
+ info->player_info = player_info;
+ info->description = mission->Objective();
+ info->start = mission->Start();
+
+ if (mission->GetStarSystem())
+ info->system = mission->GetStarSystem()->Name();
+ info->region = mission->GetRegion();
+
+ mission->SetName(name);
+
+ return info;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionFighter::Exit()
+{
+ Starshatter* stars = Starshatter::GetInstance();
+ if (stars)
+ stars->SetGameMode(Starshatter::MENU_MODE);
+}
diff --git a/Stars45/CampaignMissionFighter.h b/Stars45/CampaignMissionFighter.h
new file mode 100644
index 0000000..3c2bb6f
--- /dev/null
+++ b/Stars45/CampaignMissionFighter.h
@@ -0,0 +1,121 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignMissionFighter.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignMissionFighter generates missions and mission
+ info for the player's FIGHTER SQUADRON as part of a
+ dynamic campaign.
+*/
+
+#ifndef CampaignMissionFighter_h
+#define CampaignMissionFighter_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Campaign;
+class CampaignMissionRequest;
+class CombatGroup;
+class CombatUnit;
+class CombatZone;
+class Mission;
+class MissionElement;
+class MissionInfo;
+class MissionTemplate;
+
+// +--------------------------------------------------------------------+
+
+class CampaignMissionFighter
+{
+public:
+ static const char* TYPENAME() { return "CampaignMissionFighter"; }
+
+ CampaignMissionFighter(Campaign* c);
+ virtual ~CampaignMissionFighter();
+
+ virtual void CreateMission(CampaignMissionRequest* request);
+
+protected:
+ virtual Mission* GenerateMission(int id);
+ virtual void SelectType();
+ virtual void SelectRegion();
+ virtual void GenerateStandardElements();
+ virtual void GenerateMissionElements();
+ virtual void CreateElements(CombatGroup* g);
+ virtual void CreateSquadron(CombatGroup* g);
+ virtual void CreatePlayer(CombatGroup* g);
+
+ virtual void CreatePatrols();
+ virtual void CreateWards();
+ virtual void CreateWardFreight();
+ virtual void CreateWardShuttle();
+ virtual void CreateWardStrike();
+
+ virtual void CreateEscorts();
+
+ virtual void CreateTargets();
+ virtual void CreateTargetsPatrol();
+ virtual void CreateTargetsSweep();
+ virtual void CreateTargetsIntercept();
+ virtual void CreateTargetsFreightEscort();
+ virtual void CreateTargetsShuttleEscort();
+ virtual void CreateTargetsStrikeEscort();
+ virtual void CreateTargetsStrike();
+ virtual void CreateTargetsAssault();
+ virtual int CreateRandomTarget(const char* rgn, Point base_loc);
+
+ virtual bool IsGroundObjective(CombatGroup* obj);
+
+ virtual void PlanetaryInsertion(MissionElement* elem);
+ virtual void OrbitalInsertion(MissionElement* elem);
+
+ virtual MissionElement*
+ CreateSingleElement(CombatGroup* g,
+ CombatUnit* u);
+ virtual MissionElement*
+ CreateFighterPackage(CombatGroup* squadron,
+ int count,
+ int role);
+
+ virtual CombatGroup* FindSquadron(int iff, int type);
+ virtual CombatUnit* FindCarrier(CombatGroup* g);
+
+ virtual void DefineMissionObjectives();
+ virtual MissionInfo* DescribeMission();
+ virtual void Exit();
+
+ Campaign* campaign;
+ CampaignMissionRequest* request;
+ MissionInfo* mission_info;
+
+ CombatGroup* squadron;
+ CombatGroup* strike_group;
+ CombatGroup* strike_target;
+ Mission* mission;
+ MissionElement* player_elem;
+ MissionElement* carrier_elem;
+ MissionElement* ward;
+ MissionElement* prime_target;
+ MissionElement* escort;
+ Text air_region;
+ Text orb_region;
+ bool airborne;
+ bool airbase;
+ int ownside;
+ int enemy;
+ int mission_type;
+};
+
+#endif CampaignMissionFighter_h
+
diff --git a/Stars45/CampaignMissionRequest.cpp b/Stars45/CampaignMissionRequest.cpp
new file mode 100644
index 0000000..a3df9a0
--- /dev/null
+++ b/Stars45/CampaignMissionRequest.cpp
@@ -0,0 +1,39 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignMissionRequest.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignMissionRequest
+*/
+
+#include "MemDebug.h"
+#include "CampaignMissionRequest.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatAssignment.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "CombatZone.h"
+#include "Mission.h"
+#include "Instruction.h"
+#include "ShipDesign.h"
+#include "StarSystem.h"
+#include "Random.h"
+
+// +--------------------------------------------------------------------+
+
+CampaignMissionRequest::CampaignMissionRequest(Campaign* c,
+ int t,
+ int s,
+ CombatGroup* p,
+ CombatGroup* tgt)
+ : campaign(c), type(t), opp_type(-1), start(s),
+ primary_group(p), secondary_group(0),
+ objective(tgt), use_loc(false)
+{ }
diff --git a/Stars45/CampaignMissionRequest.h b/Stars45/CampaignMissionRequest.h
new file mode 100644
index 0000000..5b70f70
--- /dev/null
+++ b/Stars45/CampaignMissionRequest.h
@@ -0,0 +1,84 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignMissionRequest.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignMissionRequest
+*/
+
+#ifndef CampaignMissionRequest_h
+#define CampaignMissionRequest_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Campaign;
+class CombatGroup;
+class CombatUnit;
+class CombatZone;
+class Mission;
+class MissionElement;
+class MissionInfo;
+
+// +--------------------------------------------------------------------+
+
+class CampaignMissionRequest
+{
+public:
+ static const char* TYPENAME() { return "CampaignMissionRequest"; }
+
+ CampaignMissionRequest(Campaign* c, int type, int start,
+ CombatGroup* primary, CombatGroup* tgt=0);
+
+ Campaign* GetCampaign() { return campaign; }
+ int Type() { return type; }
+ int OpposingType() { return opp_type; }
+ int StartTime() { return start; }
+ CombatGroup* GetPrimaryGroup() { return primary_group; }
+ CombatGroup* GetSecondaryGroup() { return secondary_group; }
+ CombatGroup* GetObjective() { return objective; }
+
+ bool IsLocSpecified() { return use_loc; }
+ const Text& RegionName() { return region; }
+ Point Location() { return location; }
+ const Text& Script() { return script; }
+
+ void SetType(int t) { type = t; }
+ void SetOpposingType(int t) { opp_type = t; }
+ void SetStartTime(int s) { start = s; }
+ void SetPrimaryGroup(CombatGroup* g) { primary_group = g; }
+ void SetSecondaryGroup(CombatGroup* g) { secondary_group = g; }
+ void SetObjective(CombatGroup* g) { objective = g; }
+
+ void SetRegionName(const char* rgn) { region = rgn; use_loc = true; }
+ void SetLocation(const Point& loc) { location = loc; use_loc = true; }
+ void SetScript(const char* s) { script = s; }
+
+private:
+ Campaign* campaign;
+
+ int type; // type of mission
+ int opp_type; // opposing mission type
+ int start; // start time
+ CombatGroup* primary_group; // player's group
+ CombatGroup* secondary_group; // optional support group
+ CombatGroup* objective; // target or ward
+
+ bool use_loc; // use the specified location
+ Text region;
+ Point location;
+ Text script;
+};
+
+#endif CampaignMissionRequest_h
+
diff --git a/Stars45/CampaignMissionStarship.cpp b/Stars45/CampaignMissionStarship.cpp
new file mode 100644
index 0000000..f0c935f
--- /dev/null
+++ b/Stars45/CampaignMissionStarship.cpp
@@ -0,0 +1,1405 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignMissionStarship.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignMissionStarship generates missions and mission
+ info for the player's STARSHIP GROUP as part of a
+ dynamic campaign.
+*/
+
+#include "MemDebug.h"
+#include "CampaignMissionStarship.h"
+#include "CampaignMissionRequest.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatAssignment.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "CombatZone.h"
+#include "Callsign.h"
+#include "Mission.h"
+#include "MissionTemplate.h"
+#include "Instruction.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Starshatter.h"
+#include "StarSystem.h"
+#include "Player.h"
+#include "Random.h"
+
+static int pkg_id = 1000;
+extern int dump_missions;
+
+// +--------------------------------------------------------------------+
+
+CampaignMissionStarship::CampaignMissionStarship(Campaign* c)
+ : campaign(c), player_group(0), player_unit(0), mission(0), player(0),
+ strike_group(0), strike_target(0), prime_target(0),
+ ward(0), escort(0), ownside(0), enemy(-1), mission_type(0)
+{
+ if (!campaign || !campaign->GetPlayerGroup()) {
+ ::Print("ERROR - CMS campaign=0x%08x player_group=0x%08x\n",
+ campaign, (DWORD) campaign?campaign->GetPlayerGroup():0);
+ return;
+ }
+
+ player_group = campaign->GetPlayerGroup();
+ player_unit = campaign->GetPlayerUnit();
+}
+
+CampaignMissionStarship::~CampaignMissionStarship() {}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionStarship::CreateMission(CampaignMissionRequest* req)
+{
+ if (!campaign || !req)
+ return;
+
+ ::Print("\n-----------------------------------------------\n");
+ if (req->Script().length())
+ ::Print("CMS CreateMission() request: %s '%s'\n",
+ Mission::RoleName(req->Type()),
+ (const char*) req->Script());
+
+ else
+ ::Print("CMS CreateMission() request: %s %s\n",
+ Mission::RoleName(req->Type()),
+ req->GetObjective() ? req->GetObjective()->Name() : "(no target)");
+
+ request = req;
+
+ if (!player_group)
+ return;
+
+ if (request->GetPrimaryGroup() != player_group) {
+ player_group = request->GetPrimaryGroup();
+ player_unit = 0;
+ }
+
+ ownside = player_group->GetIFF();
+ mission_info = 0;
+
+ for (int i = 0; i < campaign->GetCombatants().size(); i++) {
+ int iff = campaign->GetCombatants().at(i)->GetIFF();
+ if (iff > 0 && iff != ownside) {
+ enemy = iff;
+ break;
+ }
+ }
+
+ static int id_key = 1;
+ GenerateMission(id_key++);
+ DefineMissionObjectives();
+
+ MissionInfo* info = DescribeMission();
+
+ if (info) {
+ campaign->GetMissionList().append(info);
+
+ ::Print("CMS Created %03d '%s' %s\n\n",
+ info->id,
+ (const char*) info->name,
+ Mission::RoleName(mission->Type()));
+
+ if (dump_missions) {
+ Text script = mission->Serialize();
+ char fname[32];
+
+ sprintf(fname, "msn%03d.def", info->id);
+ FILE* f = fopen(fname, "w");
+ if (f) {
+ fprintf(f, "%s\n", script.data());
+ fclose(f);
+ }
+ }
+ }
+
+ else {
+ ::Print("CMS failed to create mission.\n");
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Mission*
+CampaignMissionStarship::GenerateMission(int id)
+{
+ bool found = false;
+
+ SelectType();
+
+ if (request && request->Script().length()) {
+ MissionTemplate* mt = new(__FILE__,__LINE__) MissionTemplate(id, request->Script(), campaign->Path());
+ if (mt)
+ mt->SetPlayerSquadron(player_group);
+ mission = mt;
+ found = true;
+ }
+
+ else {
+ mission_info = campaign->FindMissionTemplate(mission_type, player_group);
+
+ found = mission_info != 0;
+
+ if (found) {
+ MissionTemplate* mt = new(__FILE__,__LINE__) MissionTemplate(id, mission_info->script, campaign->Path());
+ if (mt)
+ mt->SetPlayerSquadron(player_group);
+ mission = mt;
+ }
+ else {
+ mission = new(__FILE__,__LINE__) Mission(id);
+ if (mission)
+ mission->SetType(mission_type);
+ }
+ }
+
+ if (!mission || !player_group) {
+ Exit();
+ return 0;
+ }
+
+ char name[64];
+ sprintf(name, "Starship Mission %d", id);
+
+ mission->SetName(name);
+ mission->SetTeam(player_group->GetIFF());
+ mission->SetStart(request->StartTime());
+
+ SelectRegion();
+ GenerateStandardElements();
+
+ if (!found) {
+ GenerateMissionElements();
+ mission->SetOK(true);
+ mission->Validate();
+ }
+
+ else {
+ CreatePlayer();
+ mission->Load();
+
+ if (mission->IsOK()) {
+ player = mission->GetPlayer();
+ prime_target = mission->GetTarget();
+ ward = mission->GetWard();
+ }
+
+ // if there was a problem, scrap the mission
+ // and start over:
+ else {
+ delete mission;
+
+ mission = new(__FILE__,__LINE__) Mission(id);
+
+ if (!mission) {
+ Exit();
+ return 0;
+ }
+
+ mission->SetType(mission_type);
+ mission->SetName(name);
+ mission->SetTeam(player_group->GetIFF());
+ mission->SetStart(request->StartTime());
+
+ SelectRegion();
+ GenerateStandardElements();
+ GenerateMissionElements();
+
+ mission->SetOK(true);
+ mission->Validate();
+ }
+ }
+
+ return mission;
+}
+
+void
+CampaignMissionStarship::SelectType()
+{
+ if (request)
+ mission_type = request->Type();
+
+ else
+ mission_type = Mission::PATROL;
+
+ if (player_unit && player_unit->GetShipClass() == Ship::CARRIER)
+ mission_type = Mission::FLIGHT_OPS;
+}
+
+void
+CampaignMissionStarship::SelectRegion()
+{
+ if (!player_group) {
+ ::Print("WARNING: CMS - no player group in SelectRegion\n");
+ return;
+ }
+
+ CombatZone* zone = player_group->GetAssignedZone();
+
+ if (!zone)
+ zone = player_group->GetCurrentZone();
+
+ if (zone) {
+ mission->SetStarSystem(campaign->GetSystem(zone->System()));
+
+ if (zone->HasRegion(player_group->GetRegion()))
+ mission->SetRegion(player_group->GetRegion());
+
+ else
+ mission->SetRegion(*zone->GetRegions().at(0));
+ }
+
+ else {
+ ::Print("WARNING: CMS - No zone for '%s'\n", player_group->Name().data());
+
+ StarSystem* s = campaign->GetSystemList()[0];
+
+ mission->SetStarSystem(s);
+ mission->SetRegion(s->Regions()[0]->Name());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionStarship::GenerateStandardElements()
+{
+ ListIter<CombatZone> z = campaign->GetZones();
+ while (++z) {
+ ListIter<ZoneForce> iter = z->GetForces();
+ while (++iter) {
+ ZoneForce* force = iter.value();
+ ListIter<CombatGroup> group = force->GetGroups();
+
+ while (++group) {
+ CombatGroup* g = group.value();
+
+ switch (g->Type()) {
+ case CombatGroup::INTERCEPT_SQUADRON:
+ case CombatGroup::FIGHTER_SQUADRON:
+ case CombatGroup::ATTACK_SQUADRON:
+ case CombatGroup::LCA_SQUADRON:
+ CreateSquadron(g);
+ break;
+
+ case CombatGroup::DESTROYER_SQUADRON:
+ case CombatGroup::BATTLE_GROUP:
+ case CombatGroup::CARRIER_GROUP:
+ CreateElements(g);
+ break;
+
+ case CombatGroup::MINEFIELD:
+ case CombatGroup::BATTERY:
+ case CombatGroup::MISSILE:
+ case CombatGroup::STATION:
+ case CombatGroup::STARBASE:
+ case CombatGroup::SUPPORT:
+ case CombatGroup::COURIER:
+ case CombatGroup::MEDICAL:
+ case CombatGroup::SUPPLY:
+ case CombatGroup::REPAIR:
+ CreateElements(g);
+ break;
+
+ case CombatGroup::CIVILIAN:
+ case CombatGroup::WAR_PRODUCTION:
+ case CombatGroup::FACTORY:
+ case CombatGroup::REFINERY:
+ case CombatGroup::RESOURCE:
+ case CombatGroup::INFRASTRUCTURE:
+ case CombatGroup::TRANSPORT:
+ case CombatGroup::NETWORK:
+ case CombatGroup::HABITAT:
+ case CombatGroup::STORAGE:
+ case CombatGroup::NON_COM:
+ CreateElements(g);
+ break;
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionStarship::GenerateMissionElements()
+{
+ CreatePlayer();
+ CreateWards();
+ CreateTargets();
+
+ if (ward && player) {
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::ESCORT, ward->Name());
+
+ if (obj) {
+ switch (mission->Type()) {
+ case Mission::ESCORT_FREIGHT:
+ obj->SetTargetDesc(Text("the star freighter ") + ward->Name());
+ break;
+
+ case Mission::ESCORT_SHUTTLE:
+ obj->SetTargetDesc(Text("the shuttle ") + ward->Name());
+ break;
+
+ case Mission::ESCORT_STRIKE:
+ obj->SetTargetDesc(Text("the ") + ward->Name() + Text(" strike package"));
+ break;
+
+ default:
+ if (ward->GetCombatGroup()) {
+ obj->SetTargetDesc(Text("the ") + ward->GetCombatGroup()->GetDescription());
+ }
+ else {
+ obj->SetTargetDesc(Text("the ") + ward->Name());
+ }
+ break;
+ }
+
+ player->AddObjective(obj);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionStarship::CreatePlayer()
+{
+ // prepare elements for the player's group
+ MissionElement* elem = 0;
+
+ if (player_group) {
+ ListIter<MissionElement> iter = mission->GetElements();
+ while (++iter) {
+ MissionElement* e = iter.value();
+ if (e->GetCombatGroup() == player_group) {
+ player_group_elements.append(e);
+
+ // match the player to the requested unit, if possible:
+ if ((!player_unit && !elem) || (player_unit == e->GetCombatUnit())) {
+ elem = e;
+ }
+ }
+ }
+ }
+
+ if (elem) {
+ elem->SetPlayer(1);
+ elem->SetCommandAI(0);
+ player = elem;
+ }
+ else if (player_group) {
+ ::Print("CMS GenerateMissionElements() could not find player element '%s'\n",
+ player_group->Name().data());
+ }
+ else {
+ ::Print("CMS GenerateMissionElements() could not find player element (no player group)\n");
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionStarship::CreateElements(CombatGroup* g)
+{
+ MissionElement* elem = 0;
+ List<CombatUnit>& units = g->GetUnits();
+
+ CombatUnit* cmdr = 0;
+
+ for (int i = 0; i < units.size(); i++) {
+ elem = CreateSingleElement(g, units[i]);
+
+ if (elem) {
+ if (!cmdr) {
+ cmdr = units[i];
+
+ if (player_group && player_group->GetIFF() == g->GetIFF()) {
+ // the grand admiral is all powerful!
+ Player* player = Player::GetCurrentPlayer();
+ if (player && player->Rank() >= 10) {
+ elem->SetCommander(player_group->Name());
+ }
+ }
+ }
+ else {
+ elem->SetCommander(cmdr->Name());
+
+ if (g->Type() == CombatGroup::CARRIER_GROUP &&
+ elem->MissionRole() == Mission::ESCORT) {
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::ESCORT, cmdr->Name());
+ if (obj) {
+ obj->SetTargetDesc(Text("the ") + g->GetDescription());
+ elem->AddObjective(obj);
+ }
+ }
+ }
+
+ mission->AddElement(elem);
+ }
+ }
+}
+
+MissionElement*
+CampaignMissionStarship::CreateSingleElement(CombatGroup* g, CombatUnit* u)
+{
+ if (!g || g->IsReserve()) return 0;
+ if (!u || u->LiveCount() < 1) return 0;
+ if (!mission->GetStarSystem()) return 0;
+
+ // no ground units in starship missions:
+ StarSystem* system = mission->GetStarSystem();
+ OrbitalRegion* rgn = system->FindRegion(u->GetRegion());
+
+ if (!rgn || rgn->Type() == Orbital::TERRAIN)
+ return 0;
+
+ // make sure this unit isn't already in the mission:
+ ListIter<MissionElement> e_iter = mission->GetElements();
+ while (++e_iter) {
+ MissionElement* elem = e_iter.value();
+
+ if (elem->GetCombatUnit() == u)
+ return 0;
+ }
+
+ MissionElement* elem = new(__FILE__,__LINE__) MissionElement;
+ if (!elem) {
+ Exit();
+ return 0;
+ }
+
+ if (u->Name().length())
+ elem->SetName(u->Name());
+ else
+ elem->SetName(u->DesignName());
+
+ elem->SetElementID(pkg_id++);
+
+ elem->SetDesign(u->GetDesign());
+ elem->SetCount(u->LiveCount());
+ elem->SetIFF(u->GetIFF());
+ elem->SetIntelLevel(g->IntelLevel());
+ elem->SetRegion(u->GetRegion());
+ elem->SetHeading(u->GetHeading());
+
+ int unit_index = g->GetUnits().index(u);
+ Point base_loc = u->Location();
+ bool exact = u->IsStatic(); // exact unit-level placement
+
+ if (base_loc.length() < 1) {
+ base_loc = g->Location();
+ exact = false;
+ }
+
+ if (unit_index < 0 || unit_index > 0 && !exact) {
+ Point loc = RandomDirection();
+
+ if (!u->IsStatic()) {
+ while (fabs(loc.y) > fabs(loc.x))
+ loc = RandomDirection();
+
+ loc *= 10e3 + 9e3 * unit_index;
+ }
+ else {
+ loc *= 2e3 + 2e3 * unit_index;
+ }
+
+ elem->SetLocation(base_loc + loc);
+ }
+ else {
+ elem->SetLocation(base_loc);
+ }
+
+ if (g->Type() == CombatGroup::CARRIER_GROUP) {
+ if (u->Type() == Ship::CARRIER) {
+ elem->SetMissionRole(Mission::FLIGHT_OPS);
+ }
+ else {
+ elem->SetMissionRole(Mission::ESCORT);
+ }
+ }
+ else if (u->Type() == Ship::STATION || u->Type() == Ship::FARCASTER) {
+ elem->SetMissionRole(Mission::OTHER);
+
+ // link farcaster to other terminus:
+ if (u->Type() == Ship::FARCASTER) {
+ Text name = u->Name();
+ int dash = -1;
+
+ for (int i = 0; i < (int) name.length(); i++)
+ if (name[i] == '-')
+ dash = i;
+
+ Text src = name.substring(0, dash);
+ Text dst = name.substring(dash+1, name.length() - (dash+1));
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::VECTOR, dst + "-" + src);
+ if (obj)
+ elem->AddObjective(obj);
+ }
+ }
+ else if (u->Type() & Ship::STARSHIPS != 0) {
+ elem->SetMissionRole(Mission::FLEET);
+ }
+
+ elem->SetCombatGroup(g);
+ elem->SetCombatUnit(u);
+
+ return elem;
+}
+
+CombatUnit*
+CampaignMissionStarship::FindCarrier(CombatGroup* g)
+{
+ CombatGroup* carrier = g->FindCarrier();
+
+ if (carrier && carrier->GetUnits().size()) {
+ MissionElement* carrier_elem = mission->FindElement(carrier->Name());
+
+ if (carrier_elem)
+ return carrier->GetUnits().at(0);
+ }
+
+ return 0;
+}
+
+void
+CampaignMissionStarship::CreateSquadron(CombatGroup* g)
+{
+ if (!g || g->IsReserve()) return;
+
+ CombatUnit* fighter = g->GetUnits().at(0);
+ CombatUnit* carrier = FindCarrier(g);
+
+ if (!fighter || !carrier) return;
+
+ int live_count = fighter->LiveCount();
+ int maint_count = (live_count > 4) ? live_count / 2 : 0;
+
+ MissionElement* elem = new(__FILE__,__LINE__) MissionElement;
+
+ if (!elem) {
+ Exit();
+ return;
+ }
+
+ elem->SetName(g->Name());
+ elem->SetElementID(pkg_id++);
+
+ elem->SetDesign(fighter->GetDesign());
+ elem->SetCount(fighter->Count());
+ elem->SetDeadCount(fighter->DeadCount());
+ elem->SetMaintCount(maint_count);
+ elem->SetIFF(fighter->GetIFF());
+ elem->SetIntelLevel(g->IntelLevel());
+ elem->SetRegion(fighter->GetRegion());
+
+ elem->SetCarrier(carrier->Name());
+ elem->SetCommander(carrier->Name());
+ elem->SetLocation(carrier->Location() + RandomPoint());
+
+ elem->SetCombatGroup(g);
+ elem->SetCombatUnit(fighter);
+
+ mission->AddElement(elem);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionStarship::CreateWards()
+{
+ switch (mission->Type()) {
+ case Mission::ESCORT_FREIGHT: CreateWardFreight(); break;
+ default: break;
+ }
+}
+
+void
+CampaignMissionStarship::CreateWardFreight()
+{
+ if (!mission || !mission->GetStarSystem() || !player_group) return;
+
+ CombatGroup* freight = 0;
+
+ if (request)
+ freight = request->GetObjective();
+
+ if (!freight)
+ freight = campaign->FindGroup(ownside, CombatGroup::FREIGHT);
+
+ if (!freight || freight->CalcValue() < 1) return;
+
+ CombatUnit* unit = freight->GetNextUnit();
+ if (!unit) return;
+
+ MissionElement* elem = CreateSingleElement(freight, unit);
+ if (!elem) return;
+
+ elem->SetMissionRole(Mission::CARGO);
+ elem->SetIntelLevel(Intel::KNOWN);
+ elem->SetRegion(player_group->GetRegion());
+
+ ward = elem;
+ mission->AddElement(elem);
+
+
+ StarSystem* system = mission->GetStarSystem();
+ OrbitalRegion* rgn1 = system->FindRegion(elem->Region());
+ Point delta = rgn1->Location() - rgn1->Primary()->Location();
+ Point navpt_loc = elem->Location();
+ Instruction* n = 0;
+
+ delta.Normalize();
+ delta *= 200.0e3;
+
+ navpt_loc += delta;
+
+ n = new(__FILE__,__LINE__) Instruction(elem->Region(),
+ navpt_loc,
+ Instruction::VECTOR);
+ if (n) {
+ n->SetSpeed(500);
+ elem->AddNavPoint(n);
+ }
+
+ Text rgn2 = elem->Region();
+ List<CombatZone>& zones = campaign->GetZones();
+ if (zones[zones.size()-1]->HasRegion(rgn2))
+ rgn2 = *zones[0]->GetRegions()[0];
+ else
+ rgn2 = *zones[zones.size()-1]->GetRegions()[0];
+
+ n = new(__FILE__,__LINE__) Instruction(rgn2,
+ Point(0, 0, 0),
+ Instruction::VECTOR);
+ if (n) {
+ n->SetSpeed(750);
+ elem->AddNavPoint(n);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionStarship::CreateEscorts()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionStarship::CreateTargets()
+{
+ if (player_group && player_group->Type() == CombatGroup::CARRIER_GROUP) {
+ CreateTargetsCarrier();
+ }
+
+ else {
+ switch (mission->Type()) {
+ default:
+ case Mission::PATROL: CreateTargetsPatrol(); break;
+ case Mission::ASSAULT:
+ case Mission::STRIKE: CreateTargetsAssault(); break;
+ case Mission::ESCORT_FREIGHT: CreateTargetsFreightEscort(); break;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionStarship::CreateTargetsAssault()
+{
+ if (!player) return;
+
+ CombatGroup* assigned = 0;
+
+ if (request)
+ assigned = request->GetObjective();
+
+ if (assigned) {
+ CreateElements(assigned);
+
+ ListIter<MissionElement> e_iter = mission->GetElements();
+ while (++e_iter) {
+ MissionElement* elem = e_iter.value();
+
+ if (elem->GetCombatGroup() == assigned) {
+ if (!prime_target) {
+ prime_target = elem;
+
+ MissionElement* player_lead = player_group_elements[0];
+
+ if (!player_lead) return;
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::ASSAULT, prime_target->Name());
+ if (obj) {
+ obj->SetTargetDesc(Text("preplanned target '") + prime_target->Name() + "'");
+ player_lead->AddObjective(obj);
+ }
+
+ // create flight plan:
+ RLoc rloc;
+ RLoc* ref = 0;
+ Vec3 dummy(0,0,0);
+ Instruction* instr = 0;
+ Point loc = player_lead->Location();
+ Point tgt = prime_target->Location();
+ Point mid;
+
+ mid = loc + (prime_target->Location() - loc) * 0.35;
+
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(mid);
+ rloc.SetDistance(50e3);
+ rloc.SetDistanceVar(5e3);
+ rloc.SetAzimuth(90*DEGREES);
+ rloc.SetAzimuthVar(45*DEGREES);
+
+ instr = new(__FILE__,__LINE__) Instruction(prime_target->Region(), dummy, Instruction::VECTOR);
+
+ if (!instr)
+ return;
+
+ instr->SetSpeed(750);
+ instr->GetRLoc() = rloc;
+
+ ref = &instr->GetRLoc();
+
+ player_lead->AddNavPoint(instr);
+
+ for (int i = 1; i < player_group_elements.size(); i++) {
+ MissionElement* pge = player_group_elements[i];
+ RLoc rloc2;
+
+ rloc2.SetReferenceLoc(ref);
+ rloc2.SetDistance(50e3);
+ rloc2.SetDistanceVar(5e3);
+
+ instr = new(__FILE__,__LINE__) Instruction(prime_target->Region(), dummy, Instruction::VECTOR);
+
+ if (!instr)
+ return;
+
+ instr->SetSpeed(750);
+ instr->GetRLoc() = rloc2;
+
+ pge->AddNavPoint(instr);
+ }
+
+ double extra = 10e3;
+
+ if (prime_target && prime_target->GetDesign()) {
+ switch (prime_target->GetDesign()->type) {
+ default: extra = 20e3; break;
+ case Ship::FRIGATE: extra = 25e3; break;
+ case Ship::DESTROYER: extra = 30e3; break;
+ case Ship::CRUISER: extra = 50e3; break;
+ case Ship::BATTLESHIP: extra = 70e3; break;
+ case Ship::DREADNAUGHT: extra = 80e3; break;
+ case Ship::CARRIER: extra = 90e3; break;
+ }
+ }
+
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(tgt);
+ rloc.SetDistance(100e3 + extra);
+ rloc.SetDistanceVar(15e3);
+ rloc.SetAzimuth(90*DEGREES);
+ rloc.SetAzimuthVar(45*DEGREES);
+
+ instr = new(__FILE__,__LINE__) Instruction(prime_target->Region(), dummy, Instruction::ASSAULT);
+
+ if (!instr)
+ return;
+
+ instr->SetSpeed(500);
+ instr->GetRLoc() = rloc;
+ instr->SetTarget(prime_target->Name());
+
+ ref = &instr->GetRLoc();
+
+ player_lead->AddNavPoint(instr);
+
+ for (i = 1; i < player_group_elements.size(); i++) {
+ MissionElement* pge = player_group_elements[i];
+ RLoc rloc2;
+
+ rloc2.SetReferenceLoc(ref);
+ rloc2.SetDistance(50e3);
+ rloc2.SetDistanceVar(5e3);
+
+ instr = new(__FILE__,__LINE__) Instruction(prime_target->Region(), dummy, Instruction::ASSAULT);
+
+ if (!instr)
+ return;
+
+ instr->SetSpeed(500);
+ instr->GetRLoc() = rloc2;
+ instr->SetTarget(prime_target->Name());
+
+ pge->AddNavPoint(instr);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionStarship::CreateTargetsCarrier()
+{
+ if (!player_group || !player) return;
+
+ Text region = player_group->GetRegion();
+ Point base_loc = player->Location();
+ Point patrol_loc = base_loc +
+ RandomDirection() * Random( 75e3, 150e3);
+ Point loc2 = patrol_loc +
+ RandomDirection() * Random( 50e3, 100e3);
+
+
+ int ntargets = 2 + RandomChance() ? 1 : 0;
+ int ntries = 8;
+
+ while (ntargets > 0 && ntries > 0) {
+ Point target_loc = RandomChance() ? patrol_loc : loc2;
+ int t = CreateRandomTarget(region, target_loc);
+ ntargets -= t;
+ if (t < 1) ntries--;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionStarship::CreateTargetsPatrol()
+{
+ if (!player_group || !player) return;
+
+ Text region = player_group->GetRegion();
+ Point base_loc = player->Location();
+ Point patrol_loc = base_loc +
+ RandomDirection() * Random(170e3, 250e3);
+
+ Instruction* n = new(__FILE__,__LINE__) Instruction(region,
+ patrol_loc,
+ Instruction::PATROL);
+ player->AddNavPoint(n);
+
+ for (int i = 1; i < player_group_elements.size(); i++) {
+ MissionElement* elem = player_group_elements[i];
+
+ n = new(__FILE__,__LINE__) Instruction(region,
+ patrol_loc + RandomDirection() * Random(20e3, 40e3),
+ Instruction::PATROL);
+ if (n)
+ elem->AddNavPoint(n);
+ }
+
+ Point loc2 = patrol_loc + RandomDirection() * Random(150e3, 200e3);
+
+ n = new(__FILE__,__LINE__) Instruction(region,
+ loc2,
+ Instruction::PATROL);
+ if (n)
+ player->AddNavPoint(n);
+
+ for (i = 1; i < player_group_elements.size(); i++) {
+ MissionElement* elem = player_group_elements[i];
+
+ n = new(__FILE__,__LINE__) Instruction(region,
+ loc2 + RandomDirection() * Random(20e3, 40e3),
+ Instruction::PATROL);
+
+ if (n)
+ elem->AddNavPoint(n);
+ }
+
+ int ntargets = 2 + RandomChance() ? 1 : 0;
+ int ntries = 8;
+
+ while (ntargets > 0 && ntries > 0) {
+ Point target_loc = RandomChance() ? patrol_loc : loc2;
+ int t = CreateRandomTarget(region, target_loc);
+ ntargets -= t;
+ if (t < 1) ntries--;
+ }
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(*n);
+ if (obj) {
+ obj->SetTargetDesc("inbound enemy units");
+ player->AddObjective(obj);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionStarship::CreateTargetsFreightEscort()
+{
+ if (!ward) {
+ CreateTargetsPatrol();
+ return;
+ }
+
+ CombatGroup* s = FindSquadron(enemy, CombatGroup::ATTACK_SQUADRON);
+ CombatGroup* s2 = FindSquadron(enemy, CombatGroup::FIGHTER_SQUADRON);
+
+ if (!s || !s2) return;
+
+ MissionElement* elem = CreateFighterPackage(s, 2, Mission::ASSAULT);
+ if (elem) {
+ elem->SetIntelLevel(Intel::KNOWN);
+
+ elem->SetLocation(ward->Location() + RandomPoint() * 5);
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::ASSAULT, ward->Name());
+ if (obj)
+ elem->AddObjective(obj);
+ mission->AddElement(elem);
+
+ MissionElement* e2 = CreateFighterPackage(s2, 2, Mission::ESCORT);
+ if (e2) {
+ e2->SetIntelLevel(Intel::KNOWN);
+ e2->SetLocation(elem->Location() + RandomPoint() * 0.25);
+
+ Instruction* obj2 = new(__FILE__,__LINE__) Instruction(Instruction::ESCORT, elem->Name());
+ if (obj2)
+ e2->AddObjective(obj2);
+ mission->AddElement(e2);
+ }
+ }
+
+ Instruction* obj3 = new(__FILE__,__LINE__) Instruction(mission->GetRegion(),
+ Point(0,0,0),
+ Instruction::PATROL);
+ if (player && obj3) {
+ obj3->SetTargetDesc("enemy patrols");
+ player->AddObjective(obj3);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+CampaignMissionStarship::CreateRandomTarget(const char* rgn, Point base_loc)
+{
+ int ntargets = 0;
+ int ttype = RandomIndex();
+
+ if (player_group && player_group->Type() == CombatGroup::CARRIER_GROUP) {
+ switch (ttype) {
+ case 0:
+ case 1:
+ case 2:
+ case 3: ttype = 0; break;
+ case 4:
+ case 5: ttype = 1; break;
+ case 6:
+ case 7: ttype = 2; break;
+ case 8:
+ case 9: ttype = 3; break;
+ case 10:
+ case 11: ttype = 4; break;
+ case 12:
+ case 13:
+ case 14:
+ case 15: ttype = 5; break;
+ }
+ }
+ else {
+ switch (ttype) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5: ttype = 0; break;
+ case 6:
+ case 7:
+ case 8: ttype = 1; break;
+ case 9:
+ case 10: ttype = 4; break;
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15: ttype = 5; break;
+ }
+ }
+
+ switch (ttype) {
+ case 0: {
+ CombatGroup* s = 0;
+
+ s = FindSquadron(enemy, CombatGroup::DESTROYER_SQUADRON);
+
+ if (s) {
+ for (int i = 0; i < 2; i++) {
+ CombatUnit* u = s->GetRandomUnit();
+ MissionElement* elem = CreateSingleElement(s, u);
+ if (elem) {
+ elem->SetIntelLevel(Intel::KNOWN);
+ elem->SetRegion(rgn);
+ elem->SetLocation(base_loc + RandomPoint() * 1.5);
+ elem->SetMissionRole(Mission::FLEET);
+ mission->AddElement(elem);
+ ntargets++;
+ }
+ }
+ }
+ }
+ break;
+
+ case 1: {
+ CombatGroup* s = FindSquadron(enemy, CombatGroup::LCA_SQUADRON);
+
+ if (s) {
+ MissionElement* elem = CreateFighterPackage(s, 2, Mission::CARGO);
+ if (elem) {
+ elem->SetIntelLevel(Intel::KNOWN);
+ elem->SetRegion(rgn);
+ elem->SetLocation(base_loc + RandomPoint() * 2);
+ mission->AddElement(elem);
+ ntargets++;
+
+ CombatGroup* s2 = FindSquadron(enemy, CombatGroup::FIGHTER_SQUADRON);
+
+ if (s2) {
+ MissionElement* e2 = CreateFighterPackage(s2, 2, Mission::ESCORT);
+ if (e2) {
+ e2->SetIntelLevel(Intel::KNOWN);
+ e2->SetRegion(rgn);
+ e2->SetLocation(elem->Location() + RandomPoint() * 0.5);
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::ESCORT, elem->Name());
+ if (obj)
+ e2->AddObjective(obj);
+
+ mission->AddElement(e2);
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case 2: {
+ CombatGroup* s = FindSquadron(enemy, CombatGroup::INTERCEPT_SQUADRON);
+
+ if (s) {
+ MissionElement* elem = CreateFighterPackage(s, 4, Mission::PATROL);
+ if (elem) {
+ elem->SetIntelLevel(Intel::SECRET);
+ elem->SetRegion(rgn);
+ elem->SetLocation(base_loc);
+ mission->AddElement(elem);
+ ntargets++;
+ }
+ }
+ }
+ break;
+
+ case 3: {
+ CombatGroup* s = FindSquadron(enemy, CombatGroup::FIGHTER_SQUADRON);
+
+ if (s) {
+ MissionElement* elem = CreateFighterPackage(s, 3, Mission::ASSAULT);
+ if (elem) {
+ elem->SetIntelLevel(Intel::KNOWN);
+ elem->Loadouts().destroy();
+ elem->Loadouts().append(new(__FILE__,__LINE__) MissionLoad(-1, "Ship Strike"));
+ elem->SetRegion(rgn);
+ elem->SetLocation(base_loc + RandomPoint());
+ mission->AddElement(elem);
+
+ if (player) {
+ Instruction* n = new(__FILE__,__LINE__) Instruction(player->Region(),
+ player->Location() + RandomPoint(),
+ Instruction::ASSAULT);
+ n->SetTarget(player->Name());
+ elem->AddNavPoint(n);
+ }
+
+ ntargets++;
+ }
+ }
+ }
+ break;
+
+ case 4: {
+ CombatGroup* s = FindSquadron(enemy, CombatGroup::ATTACK_SQUADRON);
+
+ if (s) {
+ MissionElement* elem = CreateFighterPackage(s, 2, Mission::ASSAULT);
+ if (elem) {
+ elem->SetIntelLevel(Intel::KNOWN);
+ elem->Loadouts().destroy();
+ elem->Loadouts().append(new(__FILE__,__LINE__) MissionLoad(-1, "Hvy Ship Strike"));
+ elem->SetRegion(rgn);
+ elem->SetLocation(base_loc + RandomPoint() * 1.3);
+ mission->AddElement(elem);
+
+ if (player) {
+ Instruction* n = new(__FILE__,__LINE__) Instruction(player->Region(),
+ player->Location() + RandomPoint(),
+ Instruction::ASSAULT);
+ n->SetTarget(player->Name());
+ elem->AddNavPoint(n);
+ }
+
+ ntargets++;
+ }
+ }
+ }
+ break;
+
+ default: {
+ CombatGroup* s = 0;
+
+ s = FindSquadron(enemy, CombatGroup::FREIGHT);
+
+ if (s) {
+ CombatUnit* u = s->GetRandomUnit();
+ MissionElement* elem = CreateSingleElement(s, u);
+ if (elem) {
+ elem->SetIntelLevel(Intel::KNOWN);
+ elem->SetRegion(rgn);
+ elem->SetLocation(base_loc + RandomPoint() * 2);
+ elem->SetMissionRole(Mission::CARGO);
+ mission->AddElement(elem);
+ ntargets++;
+
+ CombatGroup* s2 = FindSquadron(enemy, CombatGroup::INTERCEPT_SQUADRON);
+
+ if (s2) {
+ MissionElement* e2 = CreateFighterPackage(s2, 2, Mission::ESCORT);
+ if (e2) {
+ e2->SetIntelLevel(Intel::KNOWN);
+ e2->SetRegion(rgn);
+ e2->SetLocation(elem->Location() + RandomPoint() * 0.5);
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(Instruction::ESCORT, elem->Name());
+ if (obj)
+ e2->AddObjective(obj);
+ mission->AddElement(e2);
+ ntargets++;
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ return ntargets;
+}
+
+// +--------------------------------------------------------------------+
+
+MissionElement*
+CampaignMissionStarship::CreateFighterPackage(CombatGroup* squadron, int count, int role)
+{
+ if (!squadron || squadron->IsReserve())
+ return 0;
+
+ CombatUnit* fighter = squadron->GetUnits().at(0);
+ CombatUnit* carrier = FindCarrier(squadron);
+
+ if (!fighter)
+ return 0;
+
+ int avail = fighter->LiveCount();
+ int actual = count;
+
+ if (avail < actual)
+ actual = avail;
+
+ if (avail < 1) {
+ ::Print("CMS - Insufficient fighters in squadron '%s' - %d required, %d available\n",
+ squadron->Name().data(), count, avail);
+ return 0;
+ }
+
+ MissionElement* elem = new(__FILE__,__LINE__) MissionElement;
+
+ if (!elem) {
+ Exit();
+ return 0;
+ }
+
+ elem->SetName(Callsign::GetCallsign(fighter->GetIFF()));
+ elem->SetElementID(pkg_id++);
+
+ if (carrier) {
+ elem->SetCommander(carrier->Name());
+ elem->SetHeading(carrier->GetHeading());
+ }
+ else {
+ elem->SetHeading(fighter->GetHeading());
+ }
+
+ elem->SetDesign(fighter->GetDesign());
+ elem->SetCount(actual);
+ elem->SetIFF(fighter->GetIFF());
+ elem->SetIntelLevel(squadron->IntelLevel());
+ elem->SetRegion(fighter->GetRegion());
+ elem->SetSquadron(fighter->Name());
+ elem->SetMissionRole(role);
+ elem->Loadouts().append(new(__FILE__,__LINE__) MissionLoad(-1, "ACM Medium Range"));
+
+ if (carrier)
+ elem->SetLocation(carrier->Location() + RandomPoint() * 0.3);
+ else
+ elem->SetLocation(fighter->Location() + RandomPoint());
+
+ elem->SetCombatGroup(squadron);
+ elem->SetCombatUnit(fighter);
+
+ return elem;
+}
+
+// +--------------------------------------------------------------------+
+
+CombatGroup*
+CampaignMissionStarship::FindSquadron(int iff, int type)
+{
+ if (!player_group) return 0;
+
+ CombatGroup* result = 0;
+ CombatZone* zone = player_group->GetAssignedZone();
+ if (!zone) zone = player_group->GetCurrentZone();
+
+ if (!zone) {
+ ::Print("CMS Warning: no zone for %s\n", player_group->Name().data());
+ return result;
+ }
+
+ ZoneForce* force = zone->FindForce(iff);
+
+ if (force) {
+ List<CombatGroup> groups;
+ ListIter<CombatGroup> group = force->GetGroups();
+ while (++group) {
+ CombatGroup* g = group.value();
+
+ if (g->Type() == type && g->CountUnits() > 0) {
+ result = g;
+ groups.append(g);
+ }
+ }
+
+ if (groups.size() > 1) {
+ int index = (int) Random(0, groups.size());
+ if (index >= groups.size()) index = groups.size() - 1;
+ result = groups[index];
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionStarship::DefineMissionObjectives()
+{
+ if (!mission || !player) return;
+
+ if (prime_target) mission->SetTarget(prime_target);
+ if (ward) mission->SetWard(ward);
+
+ Text objectives;
+
+ if (player->Objectives().size() > 0) {
+ for (int i = 0; i < player->Objectives().size(); i++) {
+ Instruction* obj = player->Objectives().at(i);
+ objectives += "* ";
+ objectives += obj->GetDescription();
+ objectives += ".\n";
+ }
+ }
+ else {
+ objectives += "* Perform standard fleet operations in the ";
+ objectives += mission->GetRegion();
+ objectives += " sector.\n";
+ }
+
+ mission->SetObjective(objectives);
+}
+
+// +--------------------------------------------------------------------+
+
+MissionInfo*
+CampaignMissionStarship::DescribeMission()
+{
+ if (!mission || !player) return 0;
+
+ char name[256];
+ char player_info[256];
+
+ if (mission_info && mission_info->name.length())
+ sprintf(name, "MSN-%03d %s", mission->Identity(), mission_info->name.data());
+
+ else if (ward)
+ sprintf(name, "MSN-%03d %s %s", mission->Identity(), Game::GetText(mission->TypeName()).data(), ward->Name().data());
+
+ else if (prime_target)
+ sprintf(name, "MSN-%03d %s %s %s", mission->Identity(), Game::GetText(mission->TypeName()).data(),
+ Ship::ClassName(prime_target->GetDesign()->type),
+ prime_target->Name().data());
+
+ else
+ sprintf(name, "MSN-%03d %s", mission->Identity(), Game::GetText(mission->TypeName()).data());
+
+ if (player) {
+ strcpy(player_info, player->GetCombatGroup()->GetDescription());
+ }
+
+ MissionInfo* info = new(__FILE__,__LINE__) MissionInfo;
+
+ if (info) {
+ info->id = mission->Identity();
+ info->mission = mission;
+ info->name = name;
+ info->type = mission->Type();
+ info->player_info = player_info;
+ info->description = mission->Objective();
+ info->start = mission->Start();
+
+ if (mission->GetStarSystem())
+ info->system = mission->GetStarSystem()->Name();
+ info->region = mission->GetRegion();
+ }
+
+ mission->SetName(name);
+
+ return info;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignMissionStarship::Exit()
+{
+ Starshatter* stars = Starshatter::GetInstance();
+ if (stars)
+ stars->SetGameMode(Starshatter::MENU_MODE);
+}
diff --git a/Stars45/CampaignMissionStarship.h b/Stars45/CampaignMissionStarship.h
new file mode 100644
index 0000000..708e7a0
--- /dev/null
+++ b/Stars45/CampaignMissionStarship.h
@@ -0,0 +1,107 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignMissionStarship.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignMissionStarship generates missions and mission
+ info for the player's STARSHIP GROUP as part of a
+ dynamic campaign.
+*/
+
+#ifndef CampaignMissionStarship_h
+#define CampaignMissionStarship_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Campaign;
+class CampaignMissionRequest;
+class CombatGroup;
+class CombatUnit;
+class CombatZone;
+class Mission;
+class MissionElement;
+class MissionInfo;
+class MissionTemplate;
+
+// +--------------------------------------------------------------------+
+
+class CampaignMissionStarship
+{
+public:
+ static const char* TYPENAME() { return "CampaignMissionStarship"; }
+
+ CampaignMissionStarship(Campaign* c);
+ virtual ~CampaignMissionStarship();
+
+ virtual void CreateMission(CampaignMissionRequest* request);
+
+protected:
+ virtual Mission* GenerateMission(int id);
+ virtual void SelectType();
+ virtual void SelectRegion();
+ virtual void GenerateStandardElements();
+ virtual void GenerateMissionElements();
+ virtual void CreateElements(CombatGroup* g);
+ virtual void CreateSquadron(CombatGroup* g);
+ virtual void CreatePlayer();
+
+ virtual void CreateWards();
+ virtual void CreateWardFreight();
+
+ virtual void CreateEscorts();
+
+ virtual void CreateTargets();
+ virtual void CreateTargetsAssault();
+ virtual void CreateTargetsPatrol();
+ virtual void CreateTargetsCarrier();
+ virtual void CreateTargetsFreightEscort();
+ virtual int CreateRandomTarget(const char* rgn, Point base_loc);
+
+ virtual MissionElement*
+ CreateSingleElement(CombatGroup* g,
+ CombatUnit* u);
+ virtual MissionElement*
+ CreateFighterPackage(CombatGroup* squadron,
+ int count,
+ int role);
+
+ virtual CombatGroup* FindSquadron(int iff, int type);
+ virtual CombatUnit* FindCarrier(CombatGroup* g);
+
+ virtual void DefineMissionObjectives();
+ virtual MissionInfo* DescribeMission();
+ virtual void Exit();
+
+ Campaign* campaign;
+ CampaignMissionRequest* request;
+ MissionInfo* mission_info;
+
+ CombatUnit* player_unit;
+ CombatGroup* player_group;
+ CombatGroup* strike_group;
+ CombatGroup* strike_target;
+ Mission* mission;
+ List<MissionElement> player_group_elements;
+ MissionElement* player;
+ MissionElement* ward;
+ MissionElement* prime_target;
+ MissionElement* escort;
+
+ int ownside;
+ int enemy;
+ int mission_type;
+};
+
+#endif CampaignMissionStarship_h
+
diff --git a/Stars45/CampaignPlan.h b/Stars45/CampaignPlan.h
new file mode 100644
index 0000000..f823a7f
--- /dev/null
+++ b/Stars45/CampaignPlan.h
@@ -0,0 +1,58 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignPlan.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignPlan defines the interface for all campaign
+ planning algorithms. Known subclasses:
+ CampaignPlanStrategic - strategic planning
+ CampaignPlanAssignment - logistics planning
+ CampaignPlanMission - mission planning
+ CampaignPlanMovement - starship movement
+ CampaignPlanEvent - scripted events
+*/
+
+#ifndef CampaignPlan_h
+#define CampaignPlan_h
+
+#include "Types.h"
+#include "Text.h"
+#include "Term.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Campaign;
+class Combatant;
+class CombatGroup;
+class CombatUnit;
+
+// +--------------------------------------------------------------------+
+
+class CampaignPlan
+{
+public:
+ static const char* TYPENAME() { return "CampaignPlan"; }
+
+ CampaignPlan(Campaign* c) : campaign(c), exec_time(-1e6) { }
+ virtual ~CampaignPlan() { }
+
+ int operator == (const CampaignPlan& p) const { return this == &p; }
+
+ // operations:
+ virtual void ExecFrame() { }
+ virtual void SetLockout(int seconds) { }
+
+protected:
+ Campaign* campaign;
+ double exec_time;
+};
+
+#endif CampaignPlan_h
+
diff --git a/Stars45/CampaignPlanAssignment.cpp b/Stars45/CampaignPlanAssignment.cpp
new file mode 100644
index 0000000..3e2728b
--- /dev/null
+++ b/Stars45/CampaignPlanAssignment.cpp
@@ -0,0 +1,158 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignPlanAssignment.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignPlanAssignment creates combat assignments for
+ assets within each combat zone as the third step in
+ force tasking.
+*/
+
+#include "MemDebug.h"
+#include "CampaignPlanAssignment.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatAssignment.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "CombatZone.h"
+#include "Mission.h"
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanAssignment::ExecFrame()
+{
+ if (campaign && campaign->IsActive()) {
+ // once every few minutes is plenty:
+ if (Campaign::Stardate() - exec_time < 300)
+ return;
+
+ ListIter<Combatant> iter = campaign->GetCombatants();
+ while (++iter) {
+ ProcessCombatant(iter.value());
+ }
+
+ exec_time = Campaign::Stardate();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanAssignment::ProcessCombatant(Combatant* c)
+{
+ CombatGroup* force = c->GetForce();
+ if (force) {
+ force->CalcValue();
+ force->ClearAssignments();
+ }
+
+ ListIter<CombatZone> zone = campaign->GetZones();
+ while (++zone) {
+ ProcessZone(c, zone.value());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanAssignment::BuildZoneList(CombatGroup* g, CombatZone* zone, List<CombatGroup>& groups)
+{
+ if (!g)
+ return;
+
+ if (g->GetAssignedZone() == zone)
+ groups.append(g);
+
+ ListIter<CombatGroup> iter = g->GetComponents();
+ while (++iter)
+ BuildZoneList(iter.value(), zone, groups);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanAssignment::BuildAssetList(const int* pref,
+ List<CombatGroup>& groups,
+ List<CombatGroup>& assets)
+{
+ if (!pref)
+ return;
+
+ while (*pref) {
+ ListIter<CombatGroup> g = groups;
+ while (++g) {
+ if (g->Type() == *pref && g->CountUnits() > 0)
+ assets.append(g.value());
+ }
+
+ pref++;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanAssignment::ProcessZone(Combatant* c, CombatZone* zone)
+{
+ List<CombatGroup> groups;
+ BuildZoneList(c->GetForce(), zone, groups);
+
+ ZoneForce* force = zone->FindForce(c->GetIFF());
+
+ // defensive assignments:
+ ListIter<CombatGroup> def = force->GetDefendList();
+ while (++def) {
+ List<CombatGroup> assets;
+ BuildAssetList(CombatGroup::PreferredDefender(def->Type()), groups, assets);
+
+ ListIter<CombatGroup> g = assets;
+ while (++g) {
+ CombatAssignment* a = new(__FILE__,__LINE__)
+ CombatAssignment(Mission::DEFEND,
+ def.value(),
+ g.value());
+
+ if (a)
+ g->GetAssignments().append(a);
+ }
+ }
+
+ // offensive assignments:
+ ListIter<CombatGroup> tgt = force->GetTargetList();
+ while (++tgt) {
+ CombatGroup* target = tgt.value();
+
+ List<CombatGroup> assets;
+ BuildAssetList(CombatGroup::PreferredAttacker(tgt->Type()), groups, assets);
+
+ ListIter<CombatGroup> g = assets;
+ while (++g) {
+ CombatGroup* asset = g.value();
+ int mtype = Mission::ASSAULT;
+
+ if (target->IsStrikeTarget())
+ mtype = Mission::STRIKE;
+
+ else if (target->IsFighterGroup())
+ mtype = Mission::SWEEP;
+
+ else if (target->Type() == CombatGroup::LCA_SQUADRON)
+ mtype = Mission::INTERCEPT;
+
+ CombatAssignment* a = new(__FILE__,__LINE__)
+ CombatAssignment(mtype, target, asset);
+
+ if (a)
+ g->GetAssignments().append(a);
+ }
+ }
+}
+
diff --git a/Stars45/CampaignPlanAssignment.h b/Stars45/CampaignPlanAssignment.h
new file mode 100644
index 0000000..f73ca41
--- /dev/null
+++ b/Stars45/CampaignPlanAssignment.h
@@ -0,0 +1,50 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignPlanAssignment.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignPlanAssignment creates combat assignments for
+ assets within each combat zone as the third step in
+ force tasking.
+*/
+
+#ifndef CampaignPlanAssignment_h
+#define CampaignPlanAssignment_h
+
+#include "Types.h"
+#include "CampaignPlan.h"
+
+// +--------------------------------------------------------------------+
+
+class CombatGroup;
+class CombatUnit;
+class CombatZone;
+
+// +--------------------------------------------------------------------+
+
+class CampaignPlanAssignment : public CampaignPlan
+{
+public:
+ static const char* TYPENAME() { return "CampaignPlanAssignment"; }
+
+ CampaignPlanAssignment(Campaign* c) : CampaignPlan(c) { }
+ virtual ~CampaignPlanAssignment() { }
+
+ // operations:
+ virtual void ExecFrame();
+
+protected:
+ virtual void ProcessCombatant(Combatant* c);
+ virtual void ProcessZone(Combatant* c, CombatZone* zone);
+ virtual void BuildZoneList(CombatGroup* g, CombatZone* zone, List<CombatGroup>& list);
+ virtual void BuildAssetList(const int* pref, List<CombatGroup>& avail, List<CombatGroup>& assets);
+};
+
+#endif CampaignPlanAssignment_h
+
diff --git a/Stars45/CampaignPlanEvent.cpp b/Stars45/CampaignPlanEvent.cpp
new file mode 100644
index 0000000..bb1480d
--- /dev/null
+++ b/Stars45/CampaignPlanEvent.cpp
@@ -0,0 +1,1315 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignPlanEvent.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignPlanEvent generates simulated combat
+ events based on a statistical analysis of the
+ combatants within the context of a dynamic
+ campaign.
+*/
+
+#include "MemDebug.h"
+#include "CampaignPlanEvent.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatAction.h"
+#include "CombatAssignment.h"
+#include "CombatEvent.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "CombatZone.h"
+#include "Mission.h"
+#include "Random.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+
+CampaignPlanEvent::CampaignPlanEvent(Campaign* c)
+ : CampaignPlan(c), event_time(0)
+{
+ if (campaign) {
+ event_time = (int) campaign->GetTime();
+ }
+}
+
+CampaignPlanEvent::~CampaignPlanEvent()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanEvent::ExecFrame()
+{
+ if (campaign && campaign->IsActive()) {
+ if (!campaign->GetPlayerGroup())
+ return;
+
+ // once every twenty minutes is plenty:
+ if (Campaign::Stardate() - exec_time < 1200)
+ return;
+
+ if (!ExecScriptedEvents())
+ ExecStatisticalEvents();
+
+ exec_time = Campaign::Stardate();
+ event_time = (int) campaign->GetTime();
+ }
+}
+
+void
+CampaignPlanEvent::SetLockout(int seconds)
+{
+ exec_time = Campaign::Stardate() + seconds;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CampaignPlanEvent::ExecScriptedEvents()
+{
+ bool scripted_event = false;
+
+ if (campaign) {
+ ListIter<CombatAction> iter = campaign->GetActions();
+ while (++iter) {
+ CombatAction* action = iter.value();
+
+ if (action->IsAvailable()) {
+
+ switch (action->Type()) {
+ case CombatAction::COMBAT_EVENT:
+ {
+ CombatEvent* event = new(__FILE__,__LINE__)
+ CombatEvent(campaign,
+ action->Subtype(),
+ (int) campaign->GetTime(),
+ action->GetIFF(),
+ action->Source(),
+ action->Region());
+
+ if (!event)
+ return false;
+
+ event->SetTitle(action->GetText());
+
+ if (*action->Filename() != 0)
+ event->SetFilename(action->Filename());
+
+ if (*action->ImageFile() != 0)
+ event->SetImageFile(action->ImageFile());
+
+ if (*action->SceneFile() != 0)
+ event->SetSceneFile(action->SceneFile());
+
+ event->Load();
+
+ ProsecuteKills(action);
+ campaign->GetEvents().append(event);
+
+ action->FireAction();
+ scripted_event = true;
+
+ if (action->Subtype() == CombatEvent::CAMPAIGN_END) {
+ ::Print(">>>>> CAMPAIGN %d END (Action %03d) <<<<<\n", campaign->GetCampaignId(), action->Identity());
+ campaign->SetStatus(Campaign::CAMPAIGN_SUCCESS);
+ }
+
+ else if (action->Subtype() == CombatEvent::CAMPAIGN_FAIL) {
+ ::Print(">>>>> CAMPAIGN %d FAIL (Action %03d) <<<<<\n", campaign->GetCampaignId(), action->Identity());
+ campaign->SetStatus(Campaign::CAMPAIGN_FAILED);
+ }
+ }
+ break;
+
+ case CombatAction::STRATEGIC_DIRECTIVE:
+ {
+ CombatGroup* g = campaign->FindGroup(action->GetIFF(),
+ action->AssetType(),
+ action->AssetId());
+
+ if (g) {
+ g->SetStrategicDirection(action->GetText());
+ action->FireAction();
+ }
+ else {
+ action->FailAction();
+ }
+
+ scripted_event = true;
+ }
+ break;
+
+ case CombatAction::CAMPAIGN_SITUATION:
+ {
+ campaign->SetSituation(action->GetText());
+ action->FireAction();
+ scripted_event = true;
+ }
+ break;
+
+ case CombatAction::CAMPAIGN_ORDERS:
+ {
+ campaign->SetOrders(action->GetText());
+ action->FireAction();
+ scripted_event = true;
+ }
+ break;
+
+ case CombatAction::INTEL_EVENT:
+ {
+ CombatGroup* g = campaign->FindGroup(action->GetIFF(),
+ action->AssetType(),
+ action->AssetId());
+
+ if (g) {
+ g->SetIntelLevel(action->Subtype());
+ action->FireAction();
+ }
+ else {
+ ::Print("WARNING: Action %d (intel level) Could not find group (IFF:%d, type:%d, id:%d)\n",
+ action->Identity(),
+ action->GetIFF(),
+ action->AssetType(),
+ action->AssetId());
+
+ action->FailAction();
+ }
+
+ scripted_event = true;
+ }
+ break;
+
+ case CombatAction::ZONE_ASSIGNMENT:
+ {
+ CombatGroup* g = campaign->FindGroup(action->GetIFF(),
+ action->AssetType(),
+ action->AssetId());
+
+ if (g) {
+ bool found = false;
+
+ if (*action->Region()) {
+ CombatZone* zone = campaign->GetZone(action->Region());
+
+ if (zone) {
+ g->SetAssignedZone(zone);
+ g->SetZoneLock(true);
+ found = true;
+
+ // don't announce the move unless it's for the player's team:
+ if (action->GetIFF() == campaign->GetPlayerIFF() &&
+ stricmp(action->GetText(), "do-not-display")) {
+ CombatEvent* event = new(__FILE__,__LINE__)
+ CombatEvent(campaign,
+ CombatEvent::MOVE_TO,
+ (int) campaign->GetTime(),
+ action->GetIFF(),
+ CombatEvent::FORCOM,
+ action->Region());
+
+ if (!event)
+ return false;
+
+ Text title = Text(g->Name()) + " Orders: Proceed to " + action->Region() + " Sector";
+ event->SetTitle(title);
+
+ double eta = campaign->GetTime() + 3600;
+ eta -= fmod(eta, 1800);
+
+ char text[64];
+ FormatDayTime(text, eta);
+
+ Text info = "ORDERS:\n\nEffective immediately, ";
+ info += g->GetDescription();
+ info += " and all associated units shall proceed to ";
+ info += action->Region();
+ info += " sector and commence spaceborne operations in that area. ETA rendevous point ";
+ info += text;
+ info += ".\n\nFleet Admiral A. Evars FORCOM\nCommanding";
+
+ event->SetInformation(info);
+
+ if (*action->ImageFile() != 0)
+ event->SetImageFile(action->ImageFile());
+
+ if (*action->SceneFile() != 0)
+ event->SetSceneFile(action->SceneFile());
+
+ event->Load();
+ campaign->GetEvents().append(event);
+ }
+ }
+ }
+
+ if (!found) {
+ ::Print("WARNING: Action %d Could not find assigned zone '%s' for '%s'\n",
+ action->Identity(),
+ action->Region() ? action->Region() : "NULL",
+ g->Name().data());
+
+ g->SetAssignedZone(0);
+ }
+
+ action->FireAction();
+ }
+ else {
+ ::Print("WARNING: Action %d (zone assignment) Could not find group (IFF:%d, type:%d, id:%d)\n",
+ action->Identity(),
+ action->GetIFF(),
+ action->AssetType(),
+ action->AssetId());
+
+ action->FailAction();
+ }
+
+ scripted_event = true;
+ }
+ break;
+
+ case CombatAction::SYSTEM_ASSIGNMENT:
+ {
+ CombatGroup* g = campaign->FindGroup(action->GetIFF(),
+ action->AssetType(),
+ action->AssetId());
+
+ if (g) {
+ bool found = false;
+
+ if (*action->System()) {
+ Text system = action->System();
+
+ if (campaign->GetSystem(system)) {
+ g->SetAssignedSystem(system);
+ found = true;
+
+ // don't announce the move unless it's for the player's team:
+ if (action->GetIFF() == campaign->GetPlayerIFF() &&
+ stricmp(action->GetText(), "do-not-display")) {
+ CombatEvent* event = new(__FILE__,__LINE__)
+ CombatEvent(campaign,
+ CombatEvent::MOVE_TO,
+ (int) campaign->GetTime(),
+ action->GetIFF(),
+ CombatEvent::FORCOM,
+ action->Region());
+
+ if (!event)
+ return false;
+
+ Text title = Text(g->Name()) + " Orders: Proceed to " + action->System() + " System";
+ event->SetTitle(title);
+
+ double eta = campaign->GetTime() + 3600;
+ eta -= fmod(eta, 1800);
+
+ char text[64];
+ FormatDayTime(text, eta);
+
+ Text info = "ORDERS:\n\nEffective immediately, ";
+ info += g->GetDescription();
+ info += " and all associated units shall proceed to the ";
+ info += action->System();
+ info += " star system and commence spaceborne operations in that area. ETA rendevous point ";
+ info += text;
+ info += ".\n\nFleet Admiral A. Evars FORCOM\nCommanding";
+
+ event->SetInformation(info);
+
+ if (*action->ImageFile() != 0)
+ event->SetImageFile(action->ImageFile());
+
+ if (*action->SceneFile() != 0)
+ event->SetSceneFile(action->SceneFile());
+
+ event->Load();
+ campaign->GetEvents().append(event);
+ }
+ }
+ }
+
+ if (!found) {
+ ::Print("WARNING: Action %d Could not find assigned system '%s' for '%s'\n",
+ action->Identity(),
+ action->System() ? action->System() : "NULL",
+ g->Name().data());
+
+ g->SetAssignedSystem("");
+ }
+
+ action->FireAction();
+ }
+ else {
+ ::Print("WARNING: Action %d (system assignment) Could not find group (IFF:%d, type:%d, id:%d)\n",
+ action->Identity(),
+ action->GetIFF(),
+ action->AssetType(),
+ action->AssetId());
+
+ action->FailAction();
+ }
+
+ scripted_event = true;
+ }
+ break;
+
+ case CombatAction::NO_ACTION:
+ action->FireAction();
+ scripted_event = true;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ return scripted_event;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanEvent::ProsecuteKills(CombatAction* action)
+{
+ if (action->AssetKills().size() > 0) {
+ CombatGroup* g = campaign->FindGroup(action->GetIFF(),
+ action->AssetType(),
+ action->AssetId());
+
+ if (g) {
+ ListIter<Text> iter = action->AssetKills();
+ while (++iter) {
+ Text* name = iter.value();
+ CombatUnit* asset = g->FindUnit(*name);
+
+ if (asset) {
+ int value_killed = asset->Kill(1);
+
+ ListIter<Combatant> iter = campaign->GetCombatants();
+ while (++iter) {
+ Combatant* c = iter.value();
+ if (c->GetIFF() > 0 && c->GetIFF() != asset->GetIFF()) {
+ // damage to neutral assets must be scored to bad guys:
+ if (asset->GetIFF() > 0 || c->GetIFF() > 1) {
+ c->AddScore(value_killed);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (action->TargetKills().size() > 0) {
+ CombatGroup* g = campaign->FindGroup(action->TargetIFF(),
+ action->TargetType(),
+ action->TargetId());
+
+ if (g) {
+ ListIter<Text> iter = action->TargetKills();
+ while (++iter) {
+ Text* name = iter.value();
+ CombatUnit* target = g->FindUnit(*name);
+
+ if (target) {
+ int value_killed = target->Kill(1);
+
+ ListIter<Combatant> iter = campaign->GetCombatants();
+ while (++iter) {
+ Combatant* c = iter.value();
+ if (c->GetIFF() > 0 && c->GetIFF() != target->GetIFF()) {
+ // damage to neutral assets must be scored to bad guys:
+ if (target->GetIFF() > 0 || c->GetIFF() > 1) {
+ c->AddScore(value_killed);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CampaignPlanEvent::ExecStatisticalEvents()
+{
+ bool result = false;
+
+ if (campaign) {
+ ListIter<Combatant> iter = campaign->GetCombatants();
+ while (++iter && !result) {
+ Combatant* c = iter.value();
+ CombatAssignment* a = ChooseAssignment(c->GetForce());
+
+ // prefer assignments not in player's zone:
+ if (a) {
+ CombatGroup* objective = a->GetObjective();
+ CombatGroup* player = campaign->GetPlayerGroup();
+
+ if (objective && player &&
+ objective->GetCurrentZone() == player->GetCurrentZone())
+ a = ChooseAssignment(c->GetForce());
+ }
+
+ if (a) {
+ result = CreateEvent(a);
+ }
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CampaignPlanEvent::CreateEvent(CombatAssignment* a)
+{
+ CombatEvent* event = 0;
+
+ if (campaign && a && a->GetResource() && RandomChance(1,2)) {
+ event_time = (int) Random(event_time, campaign->GetTime());
+
+ CombatGroup* group = a->GetResource();
+
+ if (group == campaign->GetPlayerGroup()) {
+
+ if (group->Type() == CombatGroup::DESTROYER_SQUADRON ||
+ group->Type() == CombatGroup::BATTLE_GROUP ||
+ group->Type() == CombatGroup::CARRIER_GROUP) {
+
+ return false;
+ }
+ }
+
+ CombatGroup* target = a->GetObjective();
+
+ if (target && target == campaign->GetPlayerGroup()) {
+
+ if (target->Type() == CombatGroup::DESTROYER_SQUADRON ||
+ target->Type() == CombatGroup::BATTLE_GROUP ||
+ target->Type() == CombatGroup::CARRIER_GROUP) {
+
+ return false;
+ }
+ }
+
+ switch (a->Type()) {
+ case Mission::DEFEND:
+ event = CreateEventDefend(a);
+ break;
+
+ case Mission::ASSAULT:
+ if (group->IsStarshipGroup())
+ event = CreateEventStarship(a);
+ else
+ event = CreateEventFighterAssault(a);
+ break;
+
+ case Mission::STRIKE:
+ if (group->IsStarshipGroup())
+ event = CreateEventStarship(a);
+ else
+ event = CreateEventFighterStrike(a);
+ break;
+
+ case Mission::SWEEP:
+ event = CreateEventFighterSweep(a);
+ break;
+ }
+
+ if (event) {
+ campaign->GetEvents().append(event);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+static void FindAssignments(CombatGroup* g, List<CombatAssignment>& alist)
+{
+ if (!g) return;
+
+ alist.append(g->GetAssignments());
+
+ ListIter<CombatGroup> iter = g->GetComponents();
+ while (++iter)
+ FindAssignments(iter.value(), alist);
+}
+
+CombatAssignment*
+CampaignPlanEvent::ChooseAssignment(CombatGroup* g)
+{
+ List<CombatAssignment> alist;
+ FindAssignments(g, alist);
+
+ int tries = 5;
+
+ if (alist.size() > 0) {
+ while (tries-- > 0) {
+ int index = (int) Random(0, alist.size());
+
+ if (index >= alist.size())
+ index = 0;
+
+ CombatAssignment* a = alist[index];
+
+ if (!a) continue;
+
+ CombatGroup* resource = a->GetResource();
+ CombatGroup* objective = a->GetObjective();
+
+ if (!resource || !objective)
+ continue;
+
+ if (resource->IsReserve() || objective->IsReserve())
+ continue;
+
+ if (resource->CalcValue() < 50 || objective->CalcValue() < 50)
+ continue;
+
+ if (resource == campaign->GetPlayerGroup() || objective == campaign->GetPlayerGroup())
+ continue;
+
+ return a;
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+CombatEvent*
+CampaignPlanEvent::CreateEventDefend(CombatAssignment* a)
+{
+ bool friendly = IsFriendlyAssignment(a);
+
+ if (!friendly)
+ return 0;
+
+ CombatEvent* event = 0;
+ CombatGroup* group = a->GetResource();
+ CombatGroup* obj = a->GetObjective();
+ CombatUnit* unit = group->GetRandomUnit();
+ CombatUnit* tgt = obj->GetRandomUnit();
+
+ if (!unit || !tgt)
+ return 0;
+
+ bool success = Success(a);
+ Text rgn = group->GetRegion();
+ Text title = Text(group->Name()) + " in Defensive Engagement";
+ Text info;
+
+ event = new(__FILE__,__LINE__) CombatEvent(campaign,
+ CombatEvent::DEFEND,
+ event_time,
+ group->GetIFF(),
+ CombatEvent::TACNET,
+ rgn);
+
+ if (!event)
+ return 0;
+
+ int tgt_count = 0;
+ int unit_count = 0;
+
+ if (!success) {
+ if (tgt) {
+ if (tgt->Kill(1) > 0)
+ tgt_count++;
+ Combatant* c = group->GetCombatant();
+ if (c) c->AddScore(tgt->GetSingleValue());
+ }
+
+ if (unit && RandomChance(1,5)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+
+ CombatGroup* us = group;
+ CombatGroup* them = obj;
+ int us_count = unit_count;
+ int them_count = tgt_count;
+
+ if (obj->IsStrikeTarget()) {
+ info = Text("EVENT: ") + rgn + " Sector\n\n";
+ }
+
+ else {
+ info = Text("MISSION: Escort ") + obj->Name() + ", " + rgn + " Sector\n\n";
+ }
+
+ info += GetTeamName(group);
+ info += Text(" ") + group->GetDescription();
+
+ if (success)
+ info += " successfully defended ";
+ else
+ info += " was unable to defend ";
+
+ info += GetTeamName(obj);
+ info += Text(" ") + obj->GetDescription() + ".\n\n";
+
+ // need to find an enemy group to do the attacking...
+
+ event->SetTitle(title);
+ event->SetInformation(info);
+ return event;
+}
+
+// +--------------------------------------------------------------------+
+
+CombatEvent*
+CampaignPlanEvent::CreateEventFighterAssault(CombatAssignment* a)
+{
+ CombatEvent* event = 0;
+ CombatGroup* group = a->GetResource();
+ CombatGroup* obj = a->GetObjective();
+ CombatUnit* unit = group->GetRandomUnit();
+ CombatUnit* tgt = obj->GetRandomUnit();
+
+ if (!unit || !tgt)
+ return 0;
+
+ bool success = Success(a);
+ Text rgn = group->GetRegion();
+ Text title = Text(group->Name());
+ Text info;
+
+ event = new(__FILE__,__LINE__) CombatEvent(campaign,
+ CombatEvent::ATTACK,
+ event_time,
+ group->GetIFF(),
+ CombatEvent::TACNET,
+ rgn);
+
+ if (!event)
+ return 0;
+
+ title += Text(" Assault ") + obj->Name();
+
+ int tgt_count = 0;
+ int unit_count = 0;
+
+ if (success) {
+ if (tgt) {
+ int killed = tgt->Kill(1 + tgt->Count()/2);
+ if (killed > 0)
+ tgt_count += killed / tgt->GetSingleValue();
+ Combatant* c = group->GetCombatant();
+ if (c) c->AddScore(tgt->GetSingleValue());
+ }
+
+ if (unit && RandomChance(1,5)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+ else {
+ for (int i = 0; i < 2; i++) {
+ if (unit && RandomChance(1,4)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+ }
+
+ CombatGroup* us = group;
+ CombatGroup* them = obj;
+ int us_count = unit_count;
+ int them_count = tgt_count;
+
+ bool friendly = IsFriendlyAssignment(a);
+
+ if (friendly) {
+ info = Text("MISSION: Strike, ") + rgn + " Sector\n\n";
+ }
+
+ else {
+ info = Text("EVENT: ") + rgn + " Sector\n\n";
+
+ us = obj;
+ them = group;
+ us_count = tgt_count;
+ them_count = unit_count;
+ }
+
+ info += GetTeamName(group);
+ info += Text(" ") + group->GetDescription();
+
+ if (success)
+ info += " successfully assault ";
+ else if (!friendly)
+ info += " assault averted against ";
+ else
+ info += " attempted assault on ";
+
+ info += GetTeamName(obj);
+ info += Text(" ") + obj->GetDescription() + ".\n\n";
+
+ char text[256];
+
+ if (them_count) {
+ if (friendly) {
+ if (them_count > 1)
+ sprintf(text, "ENEMY KILLED:\t %d %s destroyed\n",
+ them_count, tgt->Name().data());
+ else
+ sprintf(text, "ENEMY KILLED:\t %s destroyed\n",
+ tgt->Name().data());
+ }
+ else {
+ sprintf(text, "ENEMY KILLED:\t %d %s destroyed\n",
+ them_count, them->Name().data());
+ }
+
+ info += text;
+ }
+ else {
+ info += "ENEMY KILLED:\t 0\n";
+ }
+
+ if (us_count) {
+ if (!friendly)
+ sprintf(text, "ALLIED LOSSES:\t %s destroyed\n",
+ tgt->Name().data());
+ else
+ sprintf(text, "ALLIED LOSSES:\t %d %s destroyed",
+ us_count,
+ us->Name().data());
+
+ info += text;
+ }
+ else {
+ info += "ALLIED LOSSES:\t 0";
+ }
+
+
+ event->SetTitle(title);
+ event->SetInformation(info);
+ return event;
+}
+
+// +--------------------------------------------------------------------+
+
+CombatEvent*
+CampaignPlanEvent::CreateEventFighterStrike(CombatAssignment* a)
+{
+ CombatEvent* event = 0;
+ CombatGroup* group = a->GetResource();
+ CombatGroup* obj = a->GetObjective();
+ CombatUnit* unit = group->GetRandomUnit();
+ CombatUnit* tgt = obj->GetRandomUnit();
+
+ if (!unit || !tgt)
+ return 0;
+
+ bool success = Success(a);
+ Text rgn = group->GetRegion();
+ Text title = Text(group->Name());
+ Text info;
+
+ event = new(__FILE__,__LINE__) CombatEvent(campaign,
+ CombatEvent::ATTACK,
+ event_time,
+ group->GetIFF(),
+ CombatEvent::TACNET,
+ rgn);
+
+ if (!event)
+ return 0;
+
+ if (unit)
+ title += Text(" ") + unit->GetDesign()->abrv + "s";
+
+ if (success) {
+ title += " Successfully Strike " + obj->Name();
+ }
+ else {
+ title += " Attempt Strike on " + obj->Name();
+ }
+
+ int tgt_count = 0;
+ int unit_count = 0;
+
+ if (success) {
+ if (tgt) {
+ int killed = tgt->Kill(1 + tgt->Count()/2);
+ if (killed > 0)
+ tgt_count += killed / tgt->GetSingleValue();
+ Combatant* c = group->GetCombatant();
+ if (c) c->AddScore(tgt->GetSingleValue());
+ }
+
+ if (unit && RandomChance(1,5)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+ else {
+ for (int i = 0; i < 2; i++) {
+ if (unit && RandomChance(1,4)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+ }
+
+ CombatGroup* us = group;
+ CombatGroup* them = obj;
+ int us_count = unit_count;
+ int them_count = tgt_count;
+
+ bool friendly = IsFriendlyAssignment(a);
+
+ if (friendly) {
+ info = Text("MISSION: Strike, ") + rgn + " Sector\n\n";
+ }
+
+ else {
+ info = Text("EVENT: ") + rgn + " Sector\n\n";
+
+ us = obj;
+ them = group;
+ us_count = tgt_count;
+ them_count = unit_count;
+ }
+
+ info += GetTeamName(group);
+ info += Text(" ") + group->GetDescription();
+
+ if (success)
+ info += " successfully strike ";
+ else if (!friendly)
+ info += " strike against ";
+ else
+ info += " attempted strike on ";
+
+ info += GetTeamName(obj);
+ info += Text(" ") + obj->GetDescription();
+
+ if (!success && !friendly)
+ info += " averted.\n\n";
+ else
+ info += ".\n\n";
+
+ char text[256];
+
+ if (them_count) {
+ if (friendly) {
+ if (them_count > 1)
+ sprintf(text, "ENEMY KILLED:\t %d %s destroyed\n",
+ them_count, tgt->Name().data());
+ else
+ sprintf(text, "ENEMY KILLED:\t %s destroyed\n",
+ tgt->Name().data());
+ }
+ else {
+ sprintf(text, "ENEMY KILLED:\t %d %s destroyed\n",
+ them_count,
+ them->Name().data());
+ }
+
+ info += text;
+ }
+ else {
+ info += "ENEMY KILLED:\t 0\n";
+ }
+
+ if (us_count) {
+ if (!friendly)
+ sprintf(text, "ALLIED LOSSES:\t %s destroyed\n",
+ tgt->Name().data());
+ else
+ sprintf(text, "ALLIED LOSSES:\t %d %s destroyed",
+ us_count,
+ us->Name().data());
+
+ info += text;
+ }
+ else {
+ info += "ALLIED LOSSES:\t 0";
+ }
+
+ event->SetTitle(title);
+ event->SetInformation(info);
+ return event;
+}
+
+// +--------------------------------------------------------------------+
+
+CombatEvent*
+CampaignPlanEvent::CreateEventFighterSweep(CombatAssignment* a)
+{
+ CombatEvent* event = 0;
+ CombatGroup* group = a->GetResource();
+ CombatGroup* obj = a->GetObjective();
+ CombatUnit* unit = group->GetRandomUnit();
+ CombatUnit* tgt = obj->GetRandomUnit();
+
+ if (!unit || !tgt)
+ return 0;
+
+ bool success = Success(a);
+ Text rgn = group->GetRegion();
+ Text title = Text(group->Name());
+ Text info;
+
+ event = new(__FILE__,__LINE__) CombatEvent(campaign,
+ CombatEvent::ATTACK,
+ event_time,
+ group->GetIFF(),
+ CombatEvent::TACNET,
+ rgn);
+
+ if (!event)
+ return 0;
+
+ if (unit)
+ title += Text(" ") + unit->GetDesign()->abrv + "s";
+ else
+ title += " Fighters";
+
+ if (RandomChance(1, 4)) title += " Clash with ";
+ else if (RandomChance(1, 4)) title += " Engage ";
+ else if (RandomChance(1, 4)) title += " Intercept ";
+ else title += " Encounter ";
+
+ title += obj->Name();
+
+ int tgt_count = 0;
+ int unit_count = 0;
+
+ if (success) {
+ for (int i = 0; i < 2; i++) {
+ if (tgt && RandomChance(3,4)) {
+ if (tgt->Kill(1) > 0)
+ tgt_count++;
+ Combatant* c = group->GetCombatant();
+ if (c) c->AddScore(tgt->GetSingleValue());
+ }
+ }
+
+ if (tgt_count > 1) {
+ if (tgt && RandomChance(1,4)) {
+ if (tgt->Kill(1) > 0)
+ tgt_count++;
+ Combatant* c = group->GetCombatant();
+ if (c) c->AddScore(tgt->GetSingleValue());
+ }
+ }
+
+ else {
+ if (unit && RandomChance(1,5)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < 2; i++) {
+ if (unit && RandomChance(3,4)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+
+ if (tgt && RandomChance(1,4)) {
+ if (tgt->Kill(1) > 0)
+ tgt_count++;
+ Combatant* c = group->GetCombatant();
+ if (c) c->AddScore(tgt->GetSingleValue());
+ }
+ }
+
+ CombatGroup* us = group;
+ CombatGroup* them = obj;
+ int us_count = unit_count;
+ int them_count = tgt_count;
+
+ bool friendly = IsFriendlyAssignment(a);
+
+ if (!friendly) {
+ us = obj;
+ them = group;
+ us_count = tgt_count;
+ them_count = unit_count;
+ }
+
+ if (friendly) {
+ if (RandomChance())
+ info = Text("MISSION: OCA Sweep, ") + rgn + " Sector\n\n";
+ else
+ info = Text("MISSION: FORCAP, ") + rgn + " Sector\n\n";
+
+ info += GetTeamName(group);
+ info += Text(" ") + group->GetDescription();
+ info += Text(" engaged ") + GetTeamName(obj);
+ info += Text(" ") + obj->GetDescription() + ".\n\n";
+ }
+ else {
+ info = Text("MISSION: Patrol, ") + rgn + " Sector\n\n";
+
+ info += GetTeamName(obj);
+ info += Text(" ") + obj->GetDescription();
+ info += Text(" engaged ") + GetTeamName(group);
+ info += Text(" ") + group->GetDescription() + ".\n\n";
+ }
+
+ char text[256];
+
+ if (them_count) {
+ sprintf(text, "ENEMY KILLED:\t %d %s destroyed\n",
+ them_count,
+ them->Name().data());
+
+ info += text;
+ }
+ else {
+ info += "ENEMY KILLED:\t 0\n";
+ }
+
+ if (us_count) {
+ sprintf(text, "ALLIED LOSSES:\t %d %s destroyed",
+ us_count,
+ us->Name().data());
+ info += text;
+ }
+ else {
+ info += "ALLIED LOSSES:\t 0";
+ }
+
+ event->SetTitle(title);
+ event->SetInformation(info);
+ return event;
+}
+
+// +--------------------------------------------------------------------+
+
+CombatEvent*
+CampaignPlanEvent::CreateEventStarship(CombatAssignment* a)
+{
+ CombatEvent* event = 0;
+ CombatGroup* group = a->GetResource();
+ CombatGroup* obj = a->GetObjective();
+ CombatUnit* unit = group->GetRandomUnit();
+ CombatUnit* tgt = obj->GetRandomUnit();
+
+ if (!unit || !tgt)
+ return 0;
+
+ bool success = Success(a);
+ Text rgn = group->GetRegion();
+ Text title = Text(group->Name());
+ Text info;
+
+ event = new(__FILE__,__LINE__) CombatEvent(campaign,
+ CombatEvent::ATTACK,
+ event_time,
+ group->GetIFF(),
+ CombatEvent::TACNET,
+ group->GetRegion());
+
+ if (!event)
+ return 0;
+
+ title += Text(" Assaults ") + a->GetObjective()->Name();
+
+ int tgt_count = 0;
+ int unit_count = 0;
+
+ if (success) {
+ if (tgt) {
+ if (tgt->Kill(1) > 0)
+ tgt_count++;
+ Combatant* c = group->GetCombatant();
+ if (c) c->AddScore(tgt->GetSingleValue());
+ }
+
+ if (unit && RandomChance(1,5)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+ else {
+ for (int i = 0; i < 2; i++) {
+ if (unit && RandomChance(1,4)) {
+ if (unit->Kill(1) > 0)
+ unit_count++;
+ Combatant* c = obj->GetCombatant();
+ if (c) c->AddScore(unit->GetSingleValue());
+ }
+ }
+ }
+
+ CombatGroup* us = group;
+ CombatGroup* them = obj;
+ int us_count = unit_count;
+ int them_count = tgt_count;
+
+ bool friendly = IsFriendlyAssignment(a);
+
+ if (friendly) {
+ info = Text("MISSION: Fleet Action, ") + rgn + " Sector\n\n";
+ }
+
+ else {
+ info = Text("EVENT: ") + rgn + " Sector\n\n";
+
+ us = obj;
+ them = group;
+ us_count = tgt_count;
+ them_count = unit_count;
+ }
+
+ info += GetTeamName(group);
+ info += Text(" ") + group->GetDescription();
+
+ if (success)
+ info += " successfully assaulted ";
+ else if (!friendly)
+ info += " assault against ";
+ else
+ info += " attempted assault on ";
+
+ info += GetTeamName(obj);
+ info += Text(" ") + obj->GetDescription();
+
+ if (!success && !friendly)
+ info += " failed.\n\n";
+ else
+ info += ".\n\n";
+
+ char text[256];
+
+ if (them_count) {
+ if (friendly) {
+ if (tgt->Count() > 1) {
+ sprintf(text, "ENEMY KILLED:\t %d %s destroyed\n",
+ them_count,
+ tgt->Name().data());
+ }
+ else {
+ sprintf(text, "ENEMY KILLED:\t %s destroyed\n",
+ tgt->Name().data());
+ }
+ }
+ else {
+ if (unit->Count() > 1) {
+ sprintf(text, "ENEMY KILLED:\t %d %s destroyed\n",
+ them_count,
+ unit->Name().data());
+ }
+ else {
+ sprintf(text, "ENEMY KILLED:\t %s destroyed\n",
+ unit->Name().data());
+ }
+ }
+
+ info += text;
+ }
+ else {
+ info += "ENEMY KILLED:\t 0\n";
+ }
+
+ if (us_count) {
+ if (!friendly)
+ sprintf(text, "ALLIED LOSSES:\t %s destroyed\n",
+ tgt->Name().data());
+ else
+ sprintf(text, "ALLIED LOSSES:\t %s destroyed",
+ unit->Name().data());
+
+ info += text;
+ }
+ else {
+ info += "ALLIED LOSSES:\t 0";
+ }
+
+ event->SetTitle(title);
+ event->SetInformation(info);
+ return event;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CampaignPlanEvent::IsFriendlyAssignment(CombatAssignment* a)
+{
+ if (!campaign || !a || !a->GetResource())
+ return false;
+
+ int a_team = a->GetResource()->GetIFF();
+ CombatGroup* player = campaign->GetPlayerGroup();
+
+ if (player && (player->GetIFF() == a_team))
+ return true;
+
+ return false;
+}
+
+bool
+CampaignPlanEvent::Success(CombatAssignment* a)
+{
+ if (!campaign || !a || !a->GetResource())
+ return false;
+
+ int odds = 6 - campaign->GetCampaignId();
+
+ if (odds < 1)
+ odds = 1;
+
+ bool success = RandomChance(odds, 5);
+
+ if (!IsFriendlyAssignment(a))
+ success = !success;
+
+ return success;
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+CampaignPlanEvent::GetTeamName(CombatGroup* g)
+{
+ while (g->GetParent())
+ g = g->GetParent();
+
+ return g->Name();
+}
diff --git a/Stars45/CampaignPlanEvent.h b/Stars45/CampaignPlanEvent.h
new file mode 100644
index 0000000..74bfea9
--- /dev/null
+++ b/Stars45/CampaignPlanEvent.h
@@ -0,0 +1,72 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignPlanEvent.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignPlanEvent generates simulated combat
+ events based on a statistical analysis of the
+ combatants within the context of a dynamic
+ campaign.
+*/
+
+#ifndef CampaignPlanEvent_h
+#define CampaignPlanEvent_h
+
+#include "Types.h"
+#include "CampaignPlan.h"
+
+// +--------------------------------------------------------------------+
+
+class CombatAction;
+class CombatAssignment;
+class CombatEvent;
+class CombatGroup;
+class CombatUnit;
+class CombatZone;
+
+// +--------------------------------------------------------------------+
+
+class CampaignPlanEvent : public CampaignPlan
+{
+public:
+ static const char* TYPENAME() { return "CampaignPlanEvent"; }
+
+ CampaignPlanEvent(Campaign* c);
+ virtual ~CampaignPlanEvent();
+
+ // operations:
+ virtual void ExecFrame();
+ virtual void SetLockout(int seconds);
+
+ virtual bool ExecScriptedEvents();
+ virtual bool ExecStatisticalEvents();
+
+protected:
+ virtual void ProsecuteKills(CombatAction* action);
+
+ virtual CombatAssignment*
+ ChooseAssignment(CombatGroup* c);
+ virtual bool CreateEvent(CombatAssignment* a);
+
+ virtual CombatEvent* CreateEventDefend(CombatAssignment* a);
+ virtual CombatEvent* CreateEventFighterAssault(CombatAssignment* a);
+ virtual CombatEvent* CreateEventFighterStrike(CombatAssignment* a);
+ virtual CombatEvent* CreateEventFighterSweep(CombatAssignment* a);
+ virtual CombatEvent* CreateEventStarship(CombatAssignment* a);
+
+ virtual bool IsFriendlyAssignment(CombatAssignment* a);
+ virtual bool Success(CombatAssignment* a);
+ virtual Text GetTeamName(CombatGroup* g);
+
+ // attributes:
+ int event_time;
+};
+
+#endif CampaignPlanEvent_h
+
diff --git a/Stars45/CampaignPlanMission.cpp b/Stars45/CampaignPlanMission.cpp
new file mode 100644
index 0000000..6cc228a
--- /dev/null
+++ b/Stars45/CampaignPlanMission.cpp
@@ -0,0 +1,374 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignPlanMission.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignPlanMission generates missions and mission
+ info for the player's combat group as part of a
+ dynamic campaign.
+*/
+
+#include "MemDebug.h"
+#include "CampaignPlanMission.h"
+#include "CampaignMissionRequest.h"
+#include "CampaignMissionFighter.h"
+#include "CampaignMissionStarship.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatAction.h"
+#include "CombatAssignment.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "CombatZone.h"
+#include "Mission.h"
+#include "StarSystem.h"
+#include "Random.h"
+
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanMission::ExecFrame()
+{
+ if (campaign && campaign->IsActive()) {
+ player_group = campaign->GetPlayerGroup();
+ if (!player_group) return;
+
+ int missionCount = campaign->GetMissionList().size();
+
+ if (missionCount > 0) {
+ // starships only get one mission to pick from:
+ if (player_group->IsStarshipGroup())
+ return;
+
+ // fighters get a maximum of five missions:
+ if (missionCount >= 5)
+ return;
+
+ // otherwise, once every few seconds is plenty:
+ if (Campaign::Stardate() - exec_time < 1)
+ return;
+ }
+
+ SelectStartTime();
+
+ if (player_group->IsFighterGroup()) {
+ slot++;
+ if (slot > 2) slot = 0;
+
+ CampaignMissionRequest* request = PlanFighterMission();
+ CampaignMissionFighter generator(campaign);
+ generator.CreateMission(request);
+ delete request;
+ }
+
+ else if (player_group->IsStarshipGroup()) {
+ // starships should always check for campaign and strategic missions
+ slot = 0;
+
+ CampaignMissionRequest* request = PlanStarshipMission();
+ CampaignMissionStarship generator(campaign);
+ generator.CreateMission(request);
+ delete request;
+ }
+
+ exec_time = Campaign::Stardate();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanMission::SelectStartTime()
+{
+ const int HOUR = 3600; // 60 minutes
+ const int MISSION_DELAY = 1800; // 30 minutes
+ double base_time = 0;
+
+ List<MissionInfo>& info_list = campaign->GetMissionList();
+
+ if (info_list.size() > 0) {
+ MissionInfo* info = info_list[info_list.size()-1];
+ base_time = info->start;
+ }
+
+ if (base_time == 0)
+ base_time = campaign->GetTime() + MISSION_DELAY;
+
+ start = (int) base_time + MISSION_DELAY;
+ start -= start % MISSION_DELAY;
+}
+
+// +--------------------------------------------------------------------+
+
+CampaignMissionRequest*
+CampaignPlanMission::PlanStarshipMission()
+{
+ CampaignMissionRequest* request = 0;
+
+ if (!request) request = PlanCampaignMission();
+ if (!request) request = PlanStrategicMission();
+ if (!request) request = PlanRandomStarshipMission();
+
+ return request;
+}
+
+// +--------------------------------------------------------------------+
+
+CampaignMissionRequest*
+CampaignPlanMission::PlanFighterMission()
+{
+ CampaignMissionRequest* request = 0;
+
+ if (!request) request = PlanCampaignMission();
+ if (!request) request = PlanStrategicMission();
+ if (!request) request = PlanRandomFighterMission();
+
+ return request;
+}
+
+// +--------------------------------------------------------------------+
+
+CampaignMissionRequest*
+CampaignPlanMission::PlanCampaignMission()
+{
+ CampaignMissionRequest* request = 0;
+
+ ListIter<CombatAction> iter = campaign->GetActions();
+ while (++iter && !request) {
+ CombatAction* action = iter.value();
+
+ if (action->Type() != CombatAction::MISSION_TEMPLATE)
+ continue;
+
+ if (action->IsAvailable()) {
+
+ // only fire each action once every two hours:
+ if (action->ExecTime() > 0 && campaign->GetTime() - action->ExecTime() < 7200)
+ continue;
+
+ CombatGroup* g = campaign->FindGroup(action->GetIFF(),
+ action->AssetType(),
+ action->AssetId());
+
+ if (g && (g == player_group ||
+ (player_group->Type() == CombatGroup::WING &&
+ player_group->FindGroup(g->Type(), g->GetID())))) {
+
+ request = new(__FILE__,__LINE__)
+ CampaignMissionRequest(campaign,
+ action->Subtype(),
+ start,
+ g);
+
+ if (request) {
+ request->SetOpposingType(action->OpposingType());
+ request->SetScript(action->GetText());
+ }
+
+ action->FireAction();
+ }
+ }
+ }
+
+ return request;
+}
+
+// +--------------------------------------------------------------------+
+
+CampaignMissionRequest*
+CampaignPlanMission::PlanStrategicMission()
+{
+ CampaignMissionRequest* request = 0;
+
+ if (slot > 1)
+ return request;
+
+ // build list of assignments:
+ List<CombatAssignment> assignments;
+ assignments.append(player_group->GetAssignments());
+
+ if (player_group->Type() == CombatGroup::WING) {
+ ListIter<CombatGroup> iter = player_group->GetComponents();
+ while (++iter) {
+ CombatGroup* g = iter.value();
+ assignments.append(g->GetAssignments());
+ }
+ }
+
+ // pick next assignment as basis for mission:
+ static int assignment_index = 0;
+
+ if (assignments.size()) {
+ if (assignment_index >= assignments.size())
+ assignment_index = 0;
+
+ CombatAssignment* a = assignments[assignment_index++];
+
+ request = new(__FILE__,__LINE__)
+ CampaignMissionRequest(campaign,
+ a->Type(),
+ start,
+ a->GetResource());
+
+ if (request)
+ request->SetObjective(a->GetObjective());
+ }
+
+ return request;
+}
+
+// +--------------------------------------------------------------------+
+
+static int mission_type_index = -1;
+static int mission_types[16] = {
+ Mission::PATROL,
+ Mission::PATROL,
+ Mission::ESCORT_FREIGHT,
+ Mission::PATROL,
+ Mission::ESCORT_FREIGHT,
+ Mission::PATROL,
+ Mission::ESCORT_FREIGHT,
+ Mission::ESCORT_FREIGHT,
+ Mission::PATROL,
+ Mission::ESCORT_FREIGHT,
+ Mission::PATROL,
+ Mission::ESCORT_FREIGHT,
+ Mission::PATROL,
+ Mission::PATROL,
+ Mission::ESCORT_FREIGHT,
+ Mission::PATROL
+};
+
+// +--------------------------------------------------------------------+
+
+CampaignMissionRequest*
+CampaignPlanMission::PlanRandomStarshipMission()
+{
+ int type = Mission::PATROL;
+ int r = RandomIndex();
+ int ownside = player_group->GetIFF();
+
+ if (mission_type_index < 0)
+ mission_type_index = r;
+
+ else if (mission_type_index >= 16)
+ mission_type_index = 0;
+
+ type = mission_types[mission_type_index++];
+
+ if (type == Mission::ESCORT_FREIGHT) {
+ CombatGroup* freight = campaign->FindGroup(ownside, CombatGroup::FREIGHT);
+ if (!freight || freight->CountUnits() < 1)
+ type = Mission::PATROL;
+ }
+
+ CampaignMissionRequest* request = 0;
+ request = new(__FILE__,__LINE__)
+ CampaignMissionRequest(campaign, type, start, player_group);
+
+ return request;
+}
+
+// +--------------------------------------------------------------------+
+
+static int fighter_mission_index = 0;
+static int fighter_mission_types[16] = {
+ Mission::PATROL,
+ Mission::SWEEP,
+ Mission::ESCORT_SHUTTLE,
+ Mission::AIR_PATROL,
+ Mission::SWEEP,
+ Mission::ESCORT_SHUTTLE,
+ Mission::PATROL,
+ Mission::PATROL,
+ Mission::AIR_SWEEP,
+ Mission::PATROL,
+ Mission::AIR_PATROL,
+ Mission::ESCORT_SHUTTLE,
+ Mission::PATROL,
+ Mission::SWEEP,
+ Mission::PATROL,
+ Mission::AIR_SWEEP
+};
+
+CampaignMissionRequest*
+CampaignPlanMission::PlanRandomFighterMission()
+{
+ CampaignMissionRequest* request = 0;
+ int type = fighter_mission_types[fighter_mission_index++];
+ int ownside = player_group->GetIFF();
+ CombatGroup* primary = player_group;
+ CombatGroup* obj = 0;
+
+ if (fighter_mission_index > 15)
+ fighter_mission_index = 0;
+
+ if (type == Mission::ESCORT_FREIGHT) {
+ CombatGroup* freight = campaign->FindGroup(ownside, CombatGroup::FREIGHT);
+ if (!freight || freight->CalcValue() < 1)
+ type = Mission::PATROL;
+ else
+ obj = freight;
+ }
+
+ else if (type == Mission::ESCORT_SHUTTLE) {
+ CombatGroup* shuttle = campaign->FindGroup(ownside, CombatGroup::LCA_SQUADRON);
+ if (!shuttle || shuttle->CalcValue() < 1)
+ type = Mission::PATROL;
+ else
+ obj = shuttle;
+ }
+
+ else if (primary->Type() == CombatGroup::WING) {
+ if (RandomChance())
+ primary = primary->FindGroup(CombatGroup::INTERCEPT_SQUADRON);
+ else
+ primary = primary->FindGroup(CombatGroup::FIGHTER_SQUADRON);
+ }
+
+ if (type >= Mission::AIR_PATROL && type <= Mission::AIR_INTERCEPT) {
+ CombatZone* zone = 0;
+ bool airborne = false;
+
+ if (primary)
+ zone = primary->GetAssignedZone();
+
+ if (zone && zone->GetRegions().size() > 1) {
+ Text air_region = *zone->GetRegions().at(1);
+ StarSystem* system = campaign->GetSystem(zone->System());
+
+ if (system) {
+ OrbitalRegion* rgn = system->FindRegion(air_region);
+
+ if (rgn && rgn->Type() == Orbital::TERRAIN)
+ airborne = true;
+ }
+ }
+
+ if (!airborne) {
+ if (type == Mission::AIR_INTERCEPT)
+ type = Mission::INTERCEPT;
+
+ else if (type == Mission::AIR_SWEEP)
+ type = Mission::SWEEP;
+
+ else
+ type = Mission::PATROL;
+ }
+ }
+
+ request = new(__FILE__,__LINE__)
+ CampaignMissionRequest(campaign, type, start, primary);
+
+ if (request)
+ request->SetObjective(obj);
+
+ return request;
+}
diff --git a/Stars45/CampaignPlanMission.h b/Stars45/CampaignPlanMission.h
new file mode 100644
index 0000000..4496ce6
--- /dev/null
+++ b/Stars45/CampaignPlanMission.h
@@ -0,0 +1,58 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignPlanMission.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignPlanMission generates missions and mission
+ info for the player's combat group as part of a
+ dynamic campaign.
+*/
+
+#ifndef CampaignPlanMission_h
+#define CampaignPlanMission_h
+
+#include "Types.h"
+#include "CampaignPlan.h"
+
+// +--------------------------------------------------------------------+
+
+class CampaignMissionRequest;
+class CombatGroup;
+class CombatUnit;
+class CombatZone;
+
+// +--------------------------------------------------------------------+
+
+class CampaignPlanMission : public CampaignPlan
+{
+public:
+ static const char* TYPENAME() { return "CampaignPlanMission"; }
+
+ CampaignPlanMission(Campaign* c) : CampaignPlan(c), start(0), slot(0) { }
+ virtual ~CampaignPlanMission() { }
+
+ // operations:
+ virtual void ExecFrame();
+
+protected:
+ virtual void SelectStartTime();
+ virtual CampaignMissionRequest* PlanCampaignMission();
+ virtual CampaignMissionRequest* PlanStrategicMission();
+ virtual CampaignMissionRequest* PlanRandomStarshipMission();
+ virtual CampaignMissionRequest* PlanRandomFighterMission();
+ virtual CampaignMissionRequest* PlanStarshipMission();
+ virtual CampaignMissionRequest* PlanFighterMission();
+
+ CombatGroup* player_group;
+ int start;
+ int slot;
+};
+
+#endif CampaignPlanMission_h
+
diff --git a/Stars45/CampaignPlanMovement.cpp b/Stars45/CampaignPlanMovement.cpp
new file mode 100644
index 0000000..dc08798
--- /dev/null
+++ b/Stars45/CampaignPlanMovement.cpp
@@ -0,0 +1,168 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignPlanMovement.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignPlanMovement simulates random patrol movements
+ of starship groups between missions.
+*/
+
+#include "MemDebug.h"
+#include "CampaignPlanMovement.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "CombatZone.h"
+#include "Random.h"
+#include "ShipDesign.h"
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanMovement::ExecFrame()
+{
+ if (campaign && campaign->IsActive()) {
+ if (Campaign::Stardate() - exec_time < 7200)
+ return;
+
+ campaign->GetAllCombatUnits(-1, all_units);
+
+ ListIter<CombatUnit> iter = all_units;
+ while (++iter) {
+ CombatUnit* u = iter.value();
+
+ if (u->IsStarship() && !u->IsStatic())
+ MoveUnit(u);
+ }
+
+ all_units.clear();
+
+ exec_time = Campaign::Stardate();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanMovement::MoveUnit(CombatUnit* u)
+{
+ if (u) {
+ // starship repair:
+ double damage = u->GetSustainedDamage();
+
+ if (damage > 0 && u->GetDesign()) {
+ int percent = (int) (100 * damage / u->GetDesign()->integrity);
+
+ if (percent > 50) {
+ u->SetSustainedDamage(0.90 * damage);
+ }
+ }
+
+ Point loc = u->Location();
+ Point dir = loc;
+ double dist = dir.Normalize();
+
+ const double MAX_RAD = 320e3;
+ const double MIN_DIST = 150e3;
+
+ if (dist < MAX_RAD) {
+ double scale = 1 - dist/MAX_RAD;
+
+ loc += dir * (Random(30e3, 90e3) * scale) + RandomDirection() * 10e3;
+
+ if (fabs(loc.z) > 20e3)
+ loc.z *= 0.1;
+
+ u->MoveTo(loc);
+
+ CombatGroup* g = u->GetCombatGroup();
+ if (g && g->Type() > CombatGroup::FLEET && g->GetFirstUnit() == u) {
+ g->MoveTo(loc);
+
+ if (g->IntelLevel() > Intel::KNOWN)
+ g->SetIntelLevel(Intel::KNOWN);
+ }
+ }
+
+ else if (dist > 1.25 * MAX_RAD) {
+ double scale = 1 - dist/MAX_RAD;
+
+ loc += dir * (Random(80e3, 120e3) * scale) + RandomDirection() * 3e3;
+
+ if (fabs(loc.z) > 20e3)
+ loc.z *= 0.1;
+
+ u->MoveTo(loc);
+
+ CombatGroup* g = u->GetCombatGroup();
+ if (g && g->Type() > CombatGroup::FLEET && g->GetFirstUnit() == u) {
+ g->MoveTo(loc);
+
+ if (g->IntelLevel() > Intel::KNOWN)
+ g->SetIntelLevel(Intel::KNOWN);
+ }
+ }
+
+ else {
+ loc += RandomDirection() * 30e3;
+
+ if (fabs(loc.z) > 20e3)
+ loc.z *= 0.1;
+
+ u->MoveTo(loc);
+
+ CombatGroup* g = u->GetCombatGroup();
+ if (g && g->Type() > CombatGroup::FLEET && g->GetFirstUnit() == u) {
+ g->MoveTo(loc);
+
+ if (g->IntelLevel() > Intel::KNOWN)
+ g->SetIntelLevel(Intel::KNOWN);
+ }
+ }
+
+ CombatUnit* closest_unit = 0;
+ double closest_dist = 1e6;
+
+ ListIter<CombatUnit> iter = all_units;
+ while (++iter) {
+ CombatUnit* unit = iter.value();
+
+ if (unit->GetCombatGroup() != u->GetCombatGroup() && unit->GetRegion() == u->GetRegion() && !unit->IsDropship()) {
+ Point delta = loc - unit->Location();
+ double dist = delta.Normalize();
+
+ if (dist < closest_dist) {
+ closest_unit = unit;
+ closest_dist = dist;
+ }
+ }
+ }
+
+ if (closest_unit && closest_dist < MIN_DIST) {
+ Point delta = loc - closest_unit->Location();
+ double dist = delta.Normalize();
+
+ loc += delta * 1.1 * (MIN_DIST - closest_dist);
+
+ if (fabs(loc.z) > 20e3)
+ loc.z *= 0.1;
+
+ u->MoveTo(loc);
+
+ CombatGroup* g = u->GetCombatGroup();
+ if (g && g->Type() > CombatGroup::FLEET && g->GetFirstUnit() == u) {
+ g->MoveTo(loc);
+
+ if (g->IntelLevel() > Intel::KNOWN)
+ g->SetIntelLevel(Intel::KNOWN);
+ }
+ }
+ }
+}
diff --git a/Stars45/CampaignPlanMovement.h b/Stars45/CampaignPlanMovement.h
new file mode 100644
index 0000000..e2d94ad
--- /dev/null
+++ b/Stars45/CampaignPlanMovement.h
@@ -0,0 +1,44 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignPlanMovement.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignPlanMovement simulates random patrol movements
+ of starship groups between missions. This agitation
+ keeps the ships from bunching up in the middle of a
+ sector.
+*/
+
+#ifndef CampaignPlanMovement_h
+#define CampaignPlanMovement_h
+
+#include "Types.h"
+#include "CampaignPlan.h"
+
+// +--------------------------------------------------------------------+
+
+class CampaignPlanMovement : public CampaignPlan
+{
+public:
+ static const char* TYPENAME() { return "CampaignPlanMovement"; }
+
+ CampaignPlanMovement(Campaign* c) : CampaignPlan(c) { }
+ virtual ~CampaignPlanMovement() { }
+
+ // operations:
+ virtual void ExecFrame();
+
+protected:
+ void MoveUnit(CombatUnit* u);
+
+ List<CombatUnit> all_units;
+};
+
+#endif CampaignPlanMovement_h
+
diff --git a/Stars45/CampaignPlanStrategic.cpp b/Stars45/CampaignPlanStrategic.cpp
new file mode 100644
index 0000000..34c1922
--- /dev/null
+++ b/Stars45/CampaignPlanStrategic.cpp
@@ -0,0 +1,483 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignPlanStrategic.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignPlanStrategic prioritizes targets and defensible
+ allied forces as the first step in force tasking.
+*/
+
+#include "MemDebug.h"
+#include "CampaignPlanStrategic.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "CombatZone.h"
+#include "Random.h"
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanStrategic::ExecFrame()
+{
+ if (campaign && campaign->IsActive()) {
+ if (Campaign::Stardate() - exec_time < 300)
+ return;
+
+ ListIter<CombatZone> zone = campaign->GetZones();
+ while (++zone)
+ zone->Clear();
+
+ ListIter<Combatant> iter = campaign->GetCombatants();
+ while (++iter) {
+ Combatant* c = iter.value();
+ CombatGroup* force = c->GetForce();
+
+ force->CalcValue();
+
+ PlaceGroup(force);
+ ScoreCombatant(c);
+ ScoreNeeds(c);
+
+ force->ClearUnlockedZones();
+ AssignZones(c);
+ ResolveZoneMovement(force);
+ }
+
+ exec_time = Campaign::Stardate();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanStrategic::PlaceGroup(CombatGroup* g)
+{
+ if (!g)
+ return;
+
+ Text rgn = g->GetRegion();
+ CombatZone* zone = campaign->GetZone(rgn);
+
+ // if we couldn't find anything suitable,
+ // just pick a zone at random:
+ if (!zone && g->IsMovable()) {
+ int nzones = campaign->GetZones().size();
+ int n = RandomIndex() % nzones;
+ zone = campaign->GetZones().at(n);
+
+ Text assigned_rgn;
+ if (!campaign->GetZone(rgn)) {
+ assigned_rgn = *zone->GetRegions().at(0);
+ g->AssignRegion(assigned_rgn);
+ }
+ }
+
+ if (zone && !zone->HasGroup(g))
+ zone->AddGroup(g);
+
+ ListIter<CombatGroup> iter = g->GetComponents();
+ while (++iter)
+ PlaceGroup(iter.value());
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanStrategic::ScoreCombatant(Combatant* c)
+{
+ // prep lists:
+ c->GetDefendList().clear();
+ c->GetTargetList().clear();
+
+ ScoreDefensible(c);
+
+ ListIter<Combatant> iter = campaign->GetCombatants();
+ while (++iter) {
+ if (iter->GetIFF() > 0 && iter->GetIFF() != c->GetIFF())
+ ScoreTargets(c, iter.value());
+ }
+
+ // sort lists:
+ c->GetDefendList().sort();
+ c->GetTargetList().sort();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanStrategic::ScoreDefensible(Combatant* c)
+{
+ if (c->GetForce())
+ ScoreDefend(c, c->GetForce());
+}
+
+void
+CampaignPlanStrategic::ScoreDefend(Combatant* c, CombatGroup* g)
+{
+ if (!g || g->IsReserve())
+ return;
+
+ if (g->IsDefensible()) {
+ g->SetPlanValue(g->Value());
+ c->GetDefendList().append(g);
+
+ CombatZone* zone = campaign->GetZone(g->GetRegion());
+ ZoneForce* force = 0;
+
+ if (zone)
+ force = zone->FindForce(c->GetIFF());
+
+ if (force)
+ force->GetDefendList().append(g);
+ }
+
+ ListIter<CombatGroup> iter = g->GetComponents();
+ while (++iter) {
+ ScoreDefend(c, iter.value());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanStrategic::ScoreTargets(Combatant* c, Combatant* t)
+{
+ if (t->GetForce())
+ ScoreTarget(c, t->GetForce());
+}
+
+void
+CampaignPlanStrategic::ScoreTarget(Combatant* c, CombatGroup* g)
+{
+ if (!g || g->IntelLevel() <= Intel::SECRET)
+ return;
+
+ if (g->IsTargetable()) {
+ g->SetPlanValue(g->Value() * c->GetTargetStratFactor(g->Type()));
+ c->GetTargetList().append(g);
+
+ CombatZone* zone = campaign->GetZone(g->GetRegion());
+ ZoneForce* force = 0;
+
+ if (zone)
+ force = zone->FindForce(c->GetIFF());
+
+ if (force)
+ force->GetTargetList().append(g);
+ }
+
+ ListIter<CombatGroup> iter = g->GetComponents();
+ while (++iter) {
+ ScoreTarget(c, iter.value());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanStrategic::ScoreNeeds(Combatant* c)
+{
+ ListIter<CombatZone> zone = campaign->GetZones();
+ while (++zone) {
+ ZoneForce* force = zone->FindForce(c->GetIFF());
+
+ // clear needs:
+ force->SetNeed(CombatGroup::CARRIER_GROUP, 0);
+ force->SetNeed(CombatGroup::BATTLE_GROUP, 0);
+ force->SetNeed(CombatGroup::DESTROYER_SQUADRON, 0);
+ force->SetNeed(CombatGroup::ATTACK_SQUADRON, 0);
+ force->SetNeed(CombatGroup::FIGHTER_SQUADRON, 0);
+ force->SetNeed(CombatGroup::INTERCEPT_SQUADRON, 0);
+
+ // what defensive assets are needed in this zone?
+ ListIter<CombatGroup> def = force->GetDefendList();
+ while (++def) {
+ int defender_type = *CombatGroup::PreferredDefender(def->Type());
+ force->AddNeed(defender_type, def->Value());
+ }
+
+ // what offensive assets are needed in this zone?
+ ListIter<CombatGroup> tgt = force->GetTargetList();
+ while (++tgt) {
+ int attacker_type = *CombatGroup::PreferredAttacker(tgt->Type());
+ force->AddNeed(attacker_type, tgt->Value());
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanStrategic::BuildGroupList(CombatGroup* g, List<CombatGroup>& groups)
+{
+ if (!g || g->IsReserve())
+ return;
+
+ if (g->IsAssignable())
+ groups.append(g);
+
+ ListIter<CombatGroup> iter = g->GetComponents();
+ while (++iter)
+ BuildGroupList(iter.value(), groups);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanStrategic::AssignZones(Combatant* c)
+{
+ // find the list of assignable groups, in priority order:
+ List<CombatGroup> groups;
+ BuildGroupList(c->GetForce(), groups);
+ groups.sort();
+
+ // for each group, assign a zone:
+ ListIter<CombatGroup> g_iter = groups;
+
+ // first pass: fighter and attack squadrons assigned to star bases
+ while (++g_iter) {
+ CombatGroup* g = g_iter.value();
+ int gtype = g->Type();
+
+ if (gtype == CombatGroup::ATTACK_SQUADRON ||
+ gtype == CombatGroup::FIGHTER_SQUADRON ||
+ gtype == CombatGroup::INTERCEPT_SQUADRON) {
+ CombatGroup* parent = g->GetParent();
+
+ if (parent && parent->Type() == CombatGroup::WING)
+ parent = parent->GetParent();
+
+ if (!parent || parent->Type() == CombatGroup::CARRIER_GROUP)
+ continue;
+
+ // these groups are attached to fixed resources,
+ // so they must be assigned to the parent's zone:
+ CombatZone* parent_zone = campaign->GetZone(parent->GetRegion());
+
+ if (parent_zone) {
+ ZoneForce* parent_force = parent_zone->FindForce(g->GetIFF());
+
+ if (parent_force) {
+ g->SetAssignedZone(parent_zone);
+ parent_force->AddNeed(g->Type(), -(g->Value()));
+ }
+ }
+ }
+ }
+
+ // second pass: carrier groups
+ g_iter.reset();
+ while (++g_iter) {
+ CombatGroup* g = g_iter.value();
+ int gtype = g->Type();
+
+ if (gtype == CombatGroup::CARRIER_GROUP) {
+ int current_zone_need = 0;
+ int highest_zone_need = 0;
+ CombatZone* highest_zone = 0;
+ ZoneForce* highest_force = 0;
+ CombatZone* current_zone = 0;
+ ZoneForce* current_force = 0;
+
+ List<CombatZone> possible_zones;
+
+ if (g->IsZoneLocked()) {
+ current_zone = g->GetAssignedZone();
+ current_force = current_zone->FindForce(g->GetIFF());
+ }
+
+ else {
+ ListIter<CombatZone> z_iter = campaign->GetZones();
+ while (++z_iter) {
+ CombatZone* zone = z_iter.value();
+ ZoneForce* force = zone->FindForce(g->GetIFF());
+ int need = force->GetNeed(CombatGroup::CARRIER_GROUP) +
+ force->GetNeed(CombatGroup::ATTACK_SQUADRON) +
+ force->GetNeed(CombatGroup::FIGHTER_SQUADRON) +
+ force->GetNeed(CombatGroup::INTERCEPT_SQUADRON);
+
+ if (g->IsSystemLocked() && zone->System() != g->GetAssignedSystem())
+ continue;
+
+ possible_zones.append(zone);
+
+ if (zone->HasRegion(g->GetRegion())) {
+ current_zone_need = need;
+ current_zone = zone;
+ current_force = force;
+ }
+
+ if (need > highest_zone_need) {
+ highest_zone_need = need;
+ highest_zone = zone;
+ highest_force = force;
+ }
+ }
+ }
+
+ CombatZone* assigned_zone = current_zone;
+ ZoneForce* assigned_force = current_force;
+
+ if (highest_zone_need > current_zone_need) {
+ assigned_zone = highest_zone;
+ assigned_force = highest_force;
+ }
+
+ // if we couldn't find anything suitable,
+ // just pick a zone at random:
+ if (!assigned_zone) {
+ if (possible_zones.isEmpty())
+ possible_zones.append(campaign->GetZones());
+
+ int nzones = possible_zones.size();
+ int n = RandomIndex() % nzones;
+
+ assigned_zone = possible_zones.at(n);
+ assigned_force = assigned_zone->FindForce(g->GetIFF());
+ }
+
+ if (assigned_force && assigned_zone) {
+ Text assigned_rgn;
+ if (!campaign->GetZone(g->GetRegion())) {
+ assigned_rgn = *assigned_zone->GetRegions().at(0);
+ g->AssignRegion(assigned_rgn);
+ }
+
+ g->SetAssignedZone(assigned_zone);
+ assigned_force->AddNeed(g->Type(), -(g->Value()));
+
+ // also assign the carrier's wing and squadrons to the same zone:
+ ListIter<CombatGroup> squadron = g->GetComponents();
+ while (++squadron) {
+ squadron->SetAssignedZone(assigned_zone);
+ assigned_force->AddNeed(squadron->Type(), -(squadron->Value()));
+
+ if (squadron->Type() == CombatGroup::WING) {
+ ListIter<CombatGroup> s = squadron->GetComponents();
+ while (++s) {
+ s->SetAssignedZone(assigned_zone);
+ assigned_force->AddNeed(s->Type(), -(s->Value()));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // third pass: everything else
+ g_iter.reset();
+ while (++g_iter) {
+ CombatGroup* g = g_iter.value();
+ int gtype = g->Type();
+
+ if (gtype == CombatGroup::BATTLE_GROUP || gtype == CombatGroup::DESTROYER_SQUADRON) {
+ int current_zone_need = 0;
+ int highest_zone_need = 0;
+ CombatZone* highest_zone = 0;
+ ZoneForce* highest_force = 0;
+ CombatZone* current_zone = 0;
+ ZoneForce* current_force = 0;
+
+ List<CombatZone> possible_zones;
+
+ if (g->IsZoneLocked()) {
+ current_zone = g->GetAssignedZone();
+ current_force = current_zone->FindForce(g->GetIFF());
+ }
+
+ else {
+ ListIter<CombatZone> z_iter = campaign->GetZones();
+ while (++z_iter) {
+ CombatZone* zone = z_iter.value();
+ ZoneForce* force = zone->FindForce(g->GetIFF());
+ int need = force->GetNeed(g->Type());
+
+ if (g->IsSystemLocked() && zone->System() != g->GetAssignedSystem())
+ continue;
+
+ possible_zones.append(zone);
+
+ // battle groups can do double-duty:
+ if (gtype == CombatGroup::BATTLE_GROUP)
+ need += force->GetNeed(CombatGroup::DESTROYER_SQUADRON);
+
+ if (zone->HasRegion(g->GetRegion())) {
+ current_zone_need = need;
+ current_zone = zone;
+ current_force = force;
+ }
+
+ if (need > highest_zone_need) {
+ highest_zone_need = need;
+ highest_zone = zone;
+ highest_force = force;
+ }
+ }
+ }
+
+ if (highest_zone_need > current_zone_need) {
+ g->SetAssignedZone(highest_zone);
+
+ if (highest_force)
+ highest_force->AddNeed(g->Type(), -(g->Value()));
+ }
+ else {
+ if (!current_zone) {
+ if (possible_zones.isEmpty())
+ possible_zones.append(campaign->GetZones());
+
+ int nzones = possible_zones.size();
+ int n = RandomIndex() % nzones;
+
+ current_zone = possible_zones.at(n);
+ current_force = current_zone->FindForce(g->GetIFF());
+ }
+
+ g->SetAssignedZone(current_zone);
+
+ if (current_force)
+ current_force->AddNeed(g->Type(), -(g->Value()));
+
+ Text assigned_rgn;
+ if (!campaign->GetZone(g->GetRegion())) {
+ assigned_rgn = *current_zone->GetRegions().at(0);
+ g->AssignRegion(assigned_rgn);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignPlanStrategic::ResolveZoneMovement(CombatGroup* g)
+{
+ CombatZone* zone = g->GetAssignedZone();
+ bool move = false;
+
+ if (zone && !zone->HasRegion(g->GetRegion())) {
+ move = true;
+ CombatZone* old_zone = g->GetCurrentZone();
+ if (old_zone)
+ old_zone->RemoveGroup(g);
+ zone->AddGroup(g);
+ }
+
+ ListIter<CombatGroup> comp = g->GetComponents();
+ while (++comp)
+ ResolveZoneMovement(comp.value());
+
+ // assign region last, to allow components to
+ // resolve their zones:
+ if (zone && move)
+ g->AssignRegion(*zone->GetRegions().at(0));
+}
diff --git a/Stars45/CampaignPlanStrategic.h b/Stars45/CampaignPlanStrategic.h
new file mode 100644
index 0000000..b5376f8
--- /dev/null
+++ b/Stars45/CampaignPlanStrategic.h
@@ -0,0 +1,59 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignPlanStrategic.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignPlanStrategic prioritizes targets and defensible
+ allied forces as the first step in force tasking. This
+ algorithm computes which enemy resources are most important
+ to attack, based on the AI value of each combat group, and
+ strategic weighting factors that help shape the strategy
+ to the objectives for the current campaign.
+*/
+
+#ifndef CampaignPlanStrategic_h
+#define CampaignPlanStrategic_h
+
+#include "Types.h"
+#include "CampaignPlan.h"
+
+// +--------------------------------------------------------------------+
+
+class CampaignPlanStrategic : public CampaignPlan
+{
+public:
+ static const char* TYPENAME() { return "CampaignPlanStrategic"; }
+
+ CampaignPlanStrategic(Campaign* c) : CampaignPlan(c) { }
+ virtual ~CampaignPlanStrategic() { }
+
+ // operations:
+ virtual void ExecFrame();
+
+protected:
+ void PlaceGroup(CombatGroup* g);
+
+ void ScoreCombatant(Combatant* c);
+
+ void ScoreDefensible(Combatant* c);
+ void ScoreDefend(Combatant* c, CombatGroup* g);
+
+ void ScoreTargets(Combatant* c, Combatant* t);
+ void ScoreTarget(Combatant* c, CombatGroup* g);
+
+ void ScoreNeeds(Combatant* c);
+
+ // zone alocation:
+ void BuildGroupList(CombatGroup* g, List<CombatGroup>& groups);
+ void AssignZones(Combatant* c);
+ void ResolveZoneMovement(CombatGroup* g);
+};
+
+#endif CampaignPlanStrategic_h
+
diff --git a/Stars45/CampaignSaveGame.cpp b/Stars45/CampaignSaveGame.cpp
new file mode 100644
index 0000000..5584f8f
--- /dev/null
+++ b/Stars45/CampaignSaveGame.cpp
@@ -0,0 +1,729 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignSaveGame.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignSaveGame contains the logic needed to save and load
+ campaign games in progress.
+*/
+
+#include "MemDebug.h"
+#include "CampaignSaveGame.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatAction.h"
+#include "CombatEvent.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "CombatZone.h"
+#include "Galaxy.h"
+#include "Mission.h"
+#include "StarSystem.h"
+#include "Player.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+
+static const char* SAVE_DIR = "SaveGame";
+
+// +--------------------------------------------------------------------+
+
+CampaignSaveGame::CampaignSaveGame(Campaign* c)
+ : campaign(c)
+{ }
+
+// +--------------------------------------------------------------------+
+
+CampaignSaveGame::~CampaignSaveGame()
+{ }
+
+// +--------------------------------------------------------------------+
+
+Text
+CampaignSaveGame::GetSaveDirectory()
+{
+ return GetSaveDirectory(Player::GetCurrentPlayer());
+}
+
+Text
+CampaignSaveGame::GetSaveDirectory(Player* player)
+{
+ if (player) {
+ char save_dir[32];
+ sprintf(save_dir, "%s/%02d", SAVE_DIR, player->Identity());
+
+ return save_dir;
+ }
+
+ return SAVE_DIR;
+}
+
+void
+CampaignSaveGame::CreateSaveDirectory()
+{
+ HANDLE hDir = CreateFile(SAVE_DIR, 0, 0, 0, OPEN_EXISTING, 0, 0);
+
+ if (hDir == INVALID_HANDLE_VALUE)
+ CreateDirectory(SAVE_DIR, NULL);
+ else
+ CloseHandle(hDir);
+
+ hDir = CreateFile(GetSaveDirectory(), 0, 0, 0, OPEN_EXISTING, 0, 0);
+
+ if (hDir == INVALID_HANDLE_VALUE)
+ CreateDirectory(GetSaveDirectory(), NULL);
+ else
+ CloseHandle(hDir);
+}
+
+// +--------------------------------------------------------------------+
+
+static char multiline[4096];
+static char* FormatMultiLine(const char* s)
+{
+ int i = 4095;
+ char* p = multiline;
+
+ while (*s && i > 0) {
+ if (*s == '\n') {
+ *p++ = '\\';
+ *p++ = 'n';
+ s++;
+ i -= 2;
+ }
+ else if (*s == '"') {
+ *p++ = '\\';
+ *p++ = '"';
+ s++;
+ i -= 2;
+ }
+ else {
+ *p++ = *s++;
+ i--;
+ }
+ }
+
+ *p = 0;
+ return multiline;
+}
+
+static char* ParseMultiLine(const char* s)
+{
+ int i = 4095;
+ char* p = multiline;
+
+ while (*s && i > 0) {
+ if (*s == '\\') {
+ s++;
+ if (*s == 'n') {
+ *p++ = '\n';
+ *s++;
+ i--;
+ }
+ else if (*s == '"') {
+ *p++ = '"';
+ *s++;
+ i--;
+ }
+ else {
+ *p++ = *s++;
+ i--;
+ }
+ }
+ else {
+ *p++ = *s++;
+ i--;
+ }
+ }
+
+ *p = 0;
+ return multiline;
+}
+
+void
+CampaignSaveGame::Load(const char* filename)
+{
+ Print("-------------------------\nLOADING SAVEGAME (%s).\n", filename);
+ campaign = 0;
+
+ if (!filename || !filename[0]) return;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ bool use_file_sys = loader->IsFileSystemEnabled();
+ loader->UseFileSystem(true);
+ loader->SetDataPath(GetSaveDirectory() + "/");
+
+ BYTE* block;
+ loader->LoadBuffer(filename, block, true);
+ loader->UseFileSystem(use_file_sys);
+
+ Sleep(10);
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse save game '%s'\n", filename);
+ loader->SetDataPath(0);
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "SAVEGAME") {
+ Print("ERROR: invalid save game file '%s'\n", filename);
+ term->print(10);
+ loader->SetDataPath(0);
+ delete term;
+ return;
+ }
+ }
+
+ int grp_iff = 0;
+ int grp_type = 0;
+ int grp_id = 0;
+ int status = 0;
+ double baseTime = 0;
+ double time = 0;
+ Text unit;
+ Text sitrep;
+ Text orders;
+
+ do {
+ Sleep(5);
+ delete term; term = 0;
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "campaign") {
+ Text cname;
+ int cid=0;
+
+ if (def->term()) {
+ if (def->term()->isText())
+ cname = def->term()->isText()->value();
+ else if (def->term()->isNumber())
+ cid = (int) def->term()->isNumber()->value();
+ }
+
+ if (!campaign) {
+ List<Campaign>& list = Campaign::GetAllCampaigns();
+
+ for (int i = 0; i < list.size() && !campaign; i++) {
+ Campaign* c = list.at(i);
+
+ if (cname == c->Name() || cid == c->GetCampaignId()) {
+ campaign = c;
+ campaign->Load();
+ campaign->Prep(); // restore campaign to pristine state
+
+ loader->SetDataPath(GetSaveDirectory() + "/");
+ }
+ }
+ }
+ }
+
+ else if (def->name()->value() == "grp_iff") {
+ GetDefNumber(grp_iff, def, filename);
+ }
+
+ else if (def->name()->value() == "grp_type") {
+ GetDefNumber(grp_type, def, filename);
+ }
+
+ else if (def->name()->value() == "grp_id") {
+ GetDefNumber(grp_id, def, filename);
+ }
+
+ else if (def->name()->value() == "unit") {
+ GetDefText(unit, def, filename);
+ }
+
+ else if (def->name()->value() == "status") {
+ GetDefNumber(status, def, filename);
+ }
+
+ else if (def->name()->value() == "basetime") {
+ GetDefNumber(baseTime, def, filename);
+ }
+
+ else if (def->name()->value() == "time") {
+ GetDefNumber(time, def, filename);
+ }
+
+ else if (def->name()->value() == "sitrep") {
+ GetDefText(sitrep, def, filename);
+ }
+
+ else if (def->name()->value() == "orders") {
+ GetDefText(orders, def, filename);
+ }
+
+ else if (def->name()->value() == "combatant") {
+ if (!def->term() || !def->term()->isStruct()) {
+ ::Print("WARNING: combatant struct missing in '%s/%s'\n", loader->GetDataPath(), filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+
+ char name[64];
+ int iff = 0;
+ int score = 0;
+
+ ZeroMemory(name, sizeof(name));
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "name")
+ GetDefText(name, pdef, filename);
+
+ else if (pdef->name()->value() == "iff") {
+ GetDefNumber(iff, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "score") {
+ GetDefNumber(score, pdef, filename);
+ }
+ }
+ }
+
+ if (campaign && name[0]) {
+ Combatant* combatant = campaign->GetCombatant(name);
+
+ if (combatant) {
+ CombatGroup::MergeOrderOfBattle(block, filename, iff, combatant, campaign);
+ combatant->SetScore(score);
+ }
+ else {
+ ::Print("WARNING: could not find combatant '%s' in campaign.\n", name);
+ }
+ }
+ }
+ }
+ else if (def->name()->value() == "event") {
+ if (!def->term() || !def->term()->isStruct()) {
+ ::Print("WARNING: event struct missing in '%s/%s'\n", loader->GetDataPath(), filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+
+ char type[64];
+ char source[64];
+ char region[64];
+ char title[256];
+ char file[256];
+ char image[256];
+ char scene[256];
+ char info[4096];
+ int time = 0;
+ int team = 0;
+ int points = 0;
+
+ type[0] = 0;
+ info[0] = 0;
+ file[0] = 0;
+ image[0] = 0;
+ scene[0] = 0;
+ title[0] = 0;
+ region[0] = 0;
+ source[0] = 0;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "type")
+ GetDefText(type, pdef, filename);
+
+ else if (pdef->name()->value() == "source")
+ GetDefText(source, pdef, filename);
+
+ else if (pdef->name()->value() == "region")
+ GetDefText(region, pdef, filename);
+
+ else if (pdef->name()->value() == "title")
+ GetDefText(title, pdef, filename);
+
+ else if (pdef->name()->value() == "file")
+ GetDefText(file, pdef, filename);
+
+ else if (pdef->name()->value() == "image")
+ GetDefText(image, pdef, filename);
+
+ else if (pdef->name()->value() == "scene")
+ GetDefText(scene, pdef, filename);
+
+ else if (pdef->name()->value() == "info")
+ GetDefText(info, pdef, filename);
+
+ else if (pdef->name()->value() == "time")
+ GetDefNumber(time, pdef, filename);
+
+ else if (pdef->name()->value() == "team")
+ GetDefNumber(team, pdef, filename);
+
+ else if (pdef->name()->value() == "points")
+ GetDefNumber(points, pdef, filename);
+ }
+ }
+
+ if (campaign && type[0]) {
+ loader->SetDataPath(campaign->Path());
+
+ CombatEvent* event = new(__FILE__,__LINE__)
+ CombatEvent(campaign,
+ CombatEvent::TypeFromName(type),
+ time,
+ team,
+ CombatEvent::SourceFromName(source),
+ region);
+
+ if (event) {
+ event->SetTitle(title);
+ event->SetFilename(file);
+ event->SetImageFile(image);
+ event->SetSceneFile(scene);
+ event->Load();
+
+ if (info[0])
+ event->SetInformation(ParseMultiLine(info));
+
+ event->SetVisited(true);
+ campaign->GetEvents().append(event);
+ }
+ }
+ }
+ }
+ else if (def->name()->value() == "action") {
+ if (!def->term() || !def->term()->isStruct()) {
+ ::Print("WARNING: action struct missing in '%s/%s'\n", loader->GetDataPath(), filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+
+ int id = -1;
+ int stat = 0;
+ int count = 0;
+ int after = 0;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "id")
+ GetDefNumber(id, pdef, filename);
+
+ else if (pdef->name()->value() == "stat")
+ GetDefNumber(stat, pdef, filename);
+
+ else if (pdef->name()->value() == "count")
+ GetDefNumber(count, pdef, filename);
+
+ else if (pdef->name()->value().contains("after"))
+ GetDefNumber(after, pdef, filename);
+ }
+ }
+
+ if (campaign && id >= 0) {
+ ListIter<CombatAction> a_iter = campaign->GetActions();
+ while (++a_iter) {
+ CombatAction* a = a_iter.value();
+ if (a->Identity() == id) {
+ a->SetStatus(stat);
+
+ if (count)
+ a->SetCount(count);
+
+ if (after)
+ a->SetStartAfter(after);
+
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ while (term);
+
+ if (term) {
+ delete term;
+ term = 0;
+ }
+
+ if (campaign) {
+ campaign->SetSaveGame(true);
+
+ List<Campaign>& list = Campaign::GetAllCampaigns();
+
+ if (status < Campaign::CAMPAIGN_SUCCESS) {
+ campaign->SetStatus(status);
+ if (sitrep.length()) campaign->SetSituation(sitrep);
+ if (orders.length()) campaign->SetOrders(orders);
+ campaign->SetStartTime(baseTime);
+ campaign->SetLoadTime(baseTime + time);
+ campaign->LockoutEvents(3600);
+ campaign->Start();
+
+ if (grp_type >= CombatGroup::FLEET && grp_type <= CombatGroup::PRIVATE) {
+ CombatGroup* player_group = campaign->FindGroup(grp_iff, grp_type, grp_id);
+ if (player_group) {
+ CombatUnit* player_unit = 0;
+
+ if (unit.length())
+ player_unit = player_group->FindUnit(unit);
+
+ if (player_unit)
+ campaign->SetPlayerUnit(player_unit);
+ else
+ campaign->SetPlayerGroup(player_group);
+ }
+ }
+ }
+
+ // failed - restart current campaign:
+ else if (status == Campaign::CAMPAIGN_FAILED) {
+ Print("CampaignSaveGame: Loading FAILED campaign, restarting '%s'\n",
+ campaign->Name());
+
+ campaign->Load();
+ campaign->Prep(); // restore campaign to pristine state
+ campaign->SetSaveGame(false);
+
+ loader->SetDataPath(GetSaveDirectory() + "/");
+ }
+
+ // start next campaign:
+ else if (status == Campaign::CAMPAIGN_SUCCESS) {
+ Print("CampaignSaveGame: Loading COMPLETED campaign '%s', searching for next campaign...\n",
+ campaign->Name());
+
+ bool found = false;
+
+ for (int i = 0; i < list.size() && !found; i++) {
+ Campaign* c = list.at(i);
+
+ if (c->GetCampaignId() == campaign->GetCampaignId()+1) {
+ campaign = c;
+ campaign->Load();
+ campaign->Prep(); // restore campaign to pristine state
+
+ Print("Advanced to campaign %d '%s'\n",
+ campaign->GetCampaignId(),
+ campaign->Name());
+
+ loader->SetDataPath(GetSaveDirectory() + "/");
+ found = true;
+ }
+ }
+
+ // if no next campaign found, start over from the beginning:
+ for (i = 0; i < list.size() && !found; i++) {
+ Campaign* c = list.at(i);
+
+ if (c->IsDynamic()) {
+ campaign = c;
+ campaign->Load();
+ campaign->Prep(); // restore campaign to pristine state
+
+ Print("Completed full series, restarting at %d '%s'\n",
+ campaign->GetCampaignId(),
+ campaign->Name());
+
+ loader->SetDataPath(GetSaveDirectory() + "/");
+ found = true;
+ }
+ }
+ }
+ }
+
+ loader->ReleaseBuffer(block);
+ loader->SetDataPath(0);
+ Print("SAVEGAME LOADED (%s).\n\n", filename);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignSaveGame::Save(const char* name)
+{
+ if (!campaign) return;
+
+ CreateSaveDirectory();
+
+ Text s = GetSaveDirectory() + Text("/") + Text(name);
+
+ FILE* f = fopen(s, "w");
+ if (f) {
+ char timestr[32];
+ FormatDayTime(timestr, campaign->GetTime());
+
+ CombatGroup* player_group = campaign->GetPlayerGroup();
+ CombatUnit* player_unit = campaign->GetPlayerUnit();
+
+ fprintf(f, "SAVEGAME\n\n");
+ fprintf(f, "campaign: \"%s\"\n\n", campaign->Name());
+ fprintf(f, "grp_iff: %d\n", (int) player_group->GetIFF());
+ fprintf(f, "grp_type: %d\n", (int) player_group->Type());
+ fprintf(f, "grp_id: %d\n", (int) player_group->GetID());
+ if (player_unit)
+ fprintf(f, "unit: \"%s\"\n", player_unit->Name().data());
+
+ fprintf(f, "status: %d\n", (int) campaign->GetStatus());
+ fprintf(f, "basetime: %f\n", campaign->GetStartTime());
+ fprintf(f, "time: %f // %s\n\n",
+ campaign->GetTime(),
+ timestr);
+
+ fprintf(f, "sitrep: \"%s\"\n", campaign->Situation());
+ fprintf(f, "orders: \"%s\"\n\n", campaign->Orders());
+
+ ListIter<Combatant> c_iter = campaign->GetCombatants();
+ while (++c_iter) {
+ Combatant* c = c_iter.value();
+
+ fprintf(f, "combatant: {");
+ fprintf(f, " name:\"%s\",", c->Name());
+ fprintf(f, " iff:%d,", c->GetIFF());
+ fprintf(f, " score:%d,", c->Score());
+ fprintf(f, " }\n");
+ }
+
+ fprintf(f, "\n");
+
+ ListIter<CombatAction> a_iter = campaign->GetActions();
+ while (++a_iter) {
+ CombatAction* a = a_iter.value();
+ fprintf(f, "action: { id:%4d, stat:%d", a->Identity(), a->Status());
+
+ if (a->Status() == CombatAction::PENDING) {
+ if (a->Count())
+ fprintf(f, ", count:%d", a->Count());
+
+ if (a->StartAfter())
+ fprintf(f, ", after:%d", a->StartAfter());
+ }
+
+ fprintf(f, " }\n");
+ }
+
+ fprintf(f, "\n");
+
+ ListIter<CombatEvent> e_iter = campaign->GetEvents();
+ while (++e_iter) {
+ CombatEvent* e = e_iter.value();
+
+ fprintf(f, "event: {");
+ fprintf(f, " type:%-18s,", e->TypeName());
+ fprintf(f, " time:0x%08x,", e->Time());
+ fprintf(f, " team:%d,", e->GetIFF());
+ fprintf(f, " points:%d,", e->Points());
+ fprintf(f, " source:\"%s\",", e->SourceName());
+ fprintf(f, " region:\"%s\",", e->Region());
+ fprintf(f, " title:\"%s\",", e->Title());
+ if (e->Filename())
+ fprintf(f, " file:\"%s\",", e->Filename());
+ if (e->ImageFile())
+ fprintf(f, " image:\"%s\",", e->ImageFile());
+ if (e->SceneFile())
+ fprintf(f, " scene:\"%s\",", e->SceneFile());
+ if (!e->Filename() || *e->Filename() == 0)
+ fprintf(f, " info:\"%s\"", FormatMultiLine(e->Information()));
+ fprintf(f, " }\n");
+ }
+
+ fprintf(f, "\n// ORDER OF BATTLE:\n\n");
+ fclose(f);
+
+ c_iter.reset();
+ while (++c_iter) {
+ Combatant* c = c_iter.value();
+ CombatGroup* g = c->GetForce();
+ CombatGroup::SaveOrderOfBattle(s, g);
+ }
+ }
+}
+
+void
+CampaignSaveGame::Delete(const char* name)
+{
+ DeleteFile(GetSaveDirectory() + "/" + name);
+}
+
+void
+CampaignSaveGame::RemovePlayer(Player* p)
+{
+ List<Text> save_list;
+ Text save_dir = GetSaveDirectory(p) + "/";
+
+ DataLoader* loader = DataLoader::GetLoader();
+ bool use_file_sys = loader->IsFileSystemEnabled();
+ loader->UseFileSystem(true);
+ loader->SetDataPath(save_dir);
+ loader->ListFiles("*.*", save_list);
+ loader->SetDataPath(0);
+ loader->UseFileSystem(use_file_sys);
+
+ for (int i = 0; i < save_list.size(); i++) {
+ Text* filename = save_list[i];
+ DeleteFile(save_dir + filename->data());
+ }
+
+ save_list.destroy();
+
+ RemoveDirectory(GetSaveDirectory(p));
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignSaveGame::SaveAuto()
+{
+ Save("AutoSave");
+}
+
+void
+CampaignSaveGame::LoadAuto()
+{
+ Load("AutoSave");
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+CampaignSaveGame::GetResumeFile()
+{
+ // check for auto save game:
+ FILE* f = ::fopen(GetSaveDirectory() + "/AutoSave", "r");
+ if (f) {
+ ::fclose(f);
+
+ return "AutoSave";
+ }
+
+ return Text();
+}
+
+int
+CampaignSaveGame::GetSaveGameList(List<Text>& save_list)
+{
+ DataLoader* loader = DataLoader::GetLoader();
+ bool use_file_sys = loader->IsFileSystemEnabled();
+ loader->UseFileSystem(true);
+ loader->SetDataPath(GetSaveDirectory() + "/");
+ loader->ListFiles("*.*", save_list);
+ loader->SetDataPath(0);
+ loader->UseFileSystem(use_file_sys);
+
+ return save_list.size();
+} \ No newline at end of file
diff --git a/Stars45/CampaignSaveGame.h b/Stars45/CampaignSaveGame.h
new file mode 100644
index 0000000..896fbb3
--- /dev/null
+++ b/Stars45/CampaignSaveGame.h
@@ -0,0 +1,69 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignSaveGame.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignSaveGame contains the logic needed to save and load
+ campaign games in progress.
+*/
+
+#ifndef CampaignSaveGame_h
+#define CampaignSaveGame_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "text.h"
+#include "term.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Campaign;
+class CampaignPlan;
+class Combatant;
+class CombatGroup;
+class CombatZone;
+class DataLoader;
+class Mission;
+class Player;
+class StarSystem;
+
+// +--------------------------------------------------------------------+
+
+class CampaignSaveGame
+{
+public:
+ static const char* TYPENAME() { return "CampaignSaveGame"; }
+
+ CampaignSaveGame(Campaign* c=0);
+ virtual ~CampaignSaveGame();
+
+ virtual Campaign* GetCampaign() { return campaign; }
+
+ virtual void Load(const char* name);
+ virtual void Save(const char* name);
+ static void Delete(const char* name);
+ static void RemovePlayer(Player* p);
+
+ virtual void LoadAuto();
+ virtual void SaveAuto();
+
+ static Text GetResumeFile();
+ static int GetSaveGameList(List<Text>& save_list);
+
+private:
+ static Text GetSaveDirectory();
+ static Text GetSaveDirectory(Player* p);
+ static void CreateSaveDirectory();
+
+ Campaign* campaign;
+};
+
+#endif CampaignSaveGame_h
+
diff --git a/Stars45/CampaignSituationReport.cpp b/Stars45/CampaignSituationReport.cpp
new file mode 100644
index 0000000..8f53cc0
--- /dev/null
+++ b/Stars45/CampaignSituationReport.cpp
@@ -0,0 +1,416 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignSituationReport.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignSituationReport generates the situation report
+ portion of the briefing for a dynamically generated
+ mission in a dynamic campaign.
+*/
+
+#include "MemDebug.h"
+#include "CampaignSituationReport.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatAssignment.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "CombatZone.h"
+#include "Callsign.h"
+#include "Mission.h"
+#include "Instruction.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "StarSystem.h"
+#include "Random.h"
+#include "Player.h"
+
+// +--------------------------------------------------------------------+
+
+CampaignSituationReport::CampaignSituationReport(Campaign* c, Mission* m)
+ : campaign(c), mission(m)
+{}
+
+CampaignSituationReport::~CampaignSituationReport() {}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignSituationReport::GenerateSituationReport()
+{
+ if (!campaign || !mission)
+ return;
+
+ sitrep = Text();
+
+ GlobalSituation();
+ MissionSituation();
+
+ mission->SetSituation(sitrep);
+}
+
+// +--------------------------------------------------------------------+
+
+static const char* outlooks[4] = { "good", "fluid", "poor", "bleak" };
+
+void
+CampaignSituationReport::GlobalSituation()
+{
+ if (campaign->GetTime() < 40 * 3600)
+ sitrep = Text(campaign->Name()) + Text(" is still in its early stages and the situation is ");
+ else
+ sitrep = Text("The overall outlook for ") + Text(campaign->Name()) + Text(" is ");
+
+ int score = campaign->GetPlayerTeamScore();
+
+ if (score > 1000)
+ sitrep += outlooks[0];
+ else if (score > -1000)
+ sitrep += outlooks[1];
+ else if (score > -2000)
+ sitrep += outlooks[2];
+ else
+ sitrep += outlooks[3];
+
+ sitrep += ". ";
+
+ Text strat_dir;
+
+ CombatGroup* pg = campaign->GetPlayerGroup();
+
+ if (pg)
+ strat_dir = pg->GetStrategicDirection();
+
+ if (strat_dir.length())
+ sitrep += strat_dir;
+ else
+ sitrep += Text("Establishing and maintaining military control of the ") +
+ mission->GetStarSystem()->Name() + " System remains a key priority.";
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CampaignSituationReport::MissionSituation()
+{
+ if (mission) {
+ MissionElement* player = mission->GetPlayer();
+ MissionElement* target = mission->GetTarget();
+ MissionElement* ward = mission->GetWard();
+ MissionElement* escort = FindEscort(player);
+ Text threat = GetThreatInfo();
+ Text sector = mission->GetRegion();
+
+ sector += " sector.";
+
+ switch (mission->Type()) {
+ case Mission::PATROL:
+ case Mission::AIR_PATROL:
+ sitrep += "\n\nThis mission is a routine patrol of the ";
+ sitrep += sector;
+ break;
+
+ case Mission::SWEEP:
+ case Mission::AIR_SWEEP:
+ sitrep += "\n\nFor this mission, you will be performing a fighter sweep of the ";
+ sitrep += sector;
+ break;
+
+ case Mission::INTERCEPT:
+ case Mission::AIR_INTERCEPT:
+ sitrep += "\n\nWe have detected hostile elements inbound. ";
+ sitrep += "Your mission is to intercept them before they are able to engage their targets.";
+ break;
+
+
+ case Mission::STRIKE:
+ sitrep += "\n\nThe goal of this mission is to perform a strike on preplanned targets in the ";
+ sitrep += sector;
+
+ if (target) {
+ sitrep += " Your package has been assigned to strike the ";
+
+ if (target->GetCombatGroup())
+ sitrep += target->GetCombatGroup()->GetDescription();
+ else
+ sitrep += target->Name();
+
+ sitrep += ".";
+ }
+ break;
+
+ case Mission::ASSAULT:
+ sitrep += "\n\nThis mission is to assault preplanned targets in the ";
+ sitrep += sector;
+
+ if (target) {
+ sitrep += " Your package has been assigned to strike the ";
+
+ if (target->GetCombatGroup())
+ sitrep += target->GetCombatGroup()->GetDescription();
+ else
+ sitrep += target->Name();
+
+ sitrep += ".";
+ }
+ break;
+
+ case Mission::DEFEND:
+ if (ward) {
+ sitrep += "\n\nFor this mission, you will need to defend ";
+ sitrep += ward->Name();
+ sitrep += " in the ";
+ sitrep += sector;
+ }
+ else {
+ sitrep += "\n\nThis is a defensive patrol mission in the ";
+ sitrep += sector;
+ }
+ break;
+
+ case Mission::ESCORT:
+ if (ward) {
+ sitrep += "\n\nFor this mission, you will need to escort the ";
+ sitrep += ward->Name();
+ sitrep += " in the ";
+ sitrep += sector;
+ }
+ else {
+ sitrep += "\n\nThis is an escort mission in the ";
+ sitrep += sector;
+ }
+ break;
+
+ case Mission::ESCORT_FREIGHT:
+ if (ward) {
+ sitrep += "\n\nFor this mission, you will need to escort the freighter ";
+ sitrep += ward->Name();
+ sitrep += ".";
+ }
+ else {
+ sitrep += "\n\nThis is a freight escort mission in the ";
+ sitrep += sector;
+ }
+ break;
+
+ case Mission::ESCORT_SHUTTLE:
+ if (ward) {
+ sitrep += "\n\nFor this mission, you will need to escort the shuttle ";
+ sitrep += ward->Name();
+ sitrep += ".";
+ }
+ else {
+ sitrep += "\n\nThis is a shuttle escort mission in the ";
+ sitrep += sector;
+ }
+ break;
+
+ case Mission::ESCORT_STRIKE:
+ if (ward) {
+ sitrep += "\n\nFor this mission, you will need to protect the ";
+ sitrep += ward->Name();
+ sitrep += " strike package from hostile interceptors.";
+ }
+ else {
+ sitrep += "\n\nFor this mission, you will be responsible for strike escort duty.";
+ }
+ break;
+
+ case Mission::INTEL:
+ case Mission::SCOUT:
+ case Mission::RECON:
+ sitrep += "\n\nThis is an intelligence gathering mission in the ";
+ sitrep += sector;
+ break;
+
+ case Mission::BLOCKADE:
+ sitrep += "\n\nThis mission is part of the blockade operation in the ";
+ sitrep += sector;
+ break;
+
+ case Mission::FLEET:
+ sitrep += "\n\nThis mission is a routine fleet patrol of the ";
+ sitrep += sector;
+ break;
+
+ case Mission::BOMBARDMENT:
+ sitrep += "\n\nOur goal for this mission is to engage and destroy preplanned targets in the ";
+ sitrep += sector;
+ break;
+
+ case Mission::FLIGHT_OPS:
+ sitrep += "\n\nFor this mission, the ";
+ sitrep += player->Name();
+ sitrep += " will be conducting combat flight operations in the ";
+ sitrep += sector;
+ break;
+
+ case Mission::TRAINING:
+ sitrep += "\n\nThis will be a training mission.";
+ break;
+
+ case Mission::TRANSPORT:
+ case Mission::CARGO:
+ case Mission::OTHER:
+ default:
+ break;
+ }
+
+ if (threat.length()) {
+ sitrep += " ";
+ sitrep += threat;
+ sitrep += "\n\n";
+ }
+ }
+ else {
+ sitrep += "\n\n";
+ }
+
+ Text rank;
+ Text name;
+
+ Player* p = Player::GetCurrentPlayer();
+
+ if (p) {
+ if (p->Rank() > 6)
+ rank = ", Admiral";
+ else
+ rank = Text(", ") + Player::RankName(p->Rank());
+
+ name = Text(", ") + p->Name();
+ }
+
+ sitrep += "You have a mission to perform. ";
+
+ switch (RandomIndex()) {
+ case 0: sitrep += "You'd better go get to it!"; break;
+ case 1: sitrep += "And let's be careful out there!"; break;
+ case 2: sitrep += "Good luck, sir!"; break;
+ case 3: sitrep += "Let's keep up the good work out there."; break;
+ case 4: sitrep += "Don't lose your focus."; break;
+ case 5: sitrep += "Good luck out there."; break;
+ case 6: sitrep += "What are you waiting for, cocktail hour?"; break;
+ case 7: sitrep += Text("Godspeed") + rank + "!"; break;
+ case 8: sitrep += Text("Good luck") + rank + "!"; break;
+ case 9: sitrep += Text("Good luck") + name + "!"; break;
+ case 10: sitrep += "If everything is clear, get your team ready and get underway.";
+ break;
+ case 11: sitrep += Text("Go get to it") + rank + "!"; break;
+ case 12: sitrep += "The clock is ticking, so let's move it!"; break;
+ case 13: sitrep += "Stay sharp out there!"; break;
+ case 14: sitrep += Text("Go get 'em") + rank + "!"; break;
+ case 15: sitrep += "Now get out of here and get to work!"; break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+MissionElement*
+CampaignSituationReport::FindEscort(MissionElement* player)
+{
+ MissionElement* escort = 0;
+
+ if (!mission || !player) return escort;
+
+ ListIter<MissionElement> iter = mission->GetElements();
+ while (++iter) {
+ MissionElement* elem = iter.value();
+
+ }
+
+ return escort;
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+CampaignSituationReport::GetThreatInfo()
+{
+ Text threat_info;
+
+ int enemy_fighters = 0;
+ int enemy_starships = 0;
+ int enemy_sites = 0;
+
+ if (mission && mission->GetPlayer()) {
+ MissionElement* player = mission->GetPlayer();
+ Text rgn0 = player->Region();
+ Text rgn1;
+ int iff = player->GetIFF();
+
+ ListIter<Instruction> nav = player->NavList();
+ while (++nav) {
+ if (rgn0 != nav->RegionName())
+ rgn1 = nav->RegionName();
+ }
+
+ ListIter<MissionElement> elem = mission->GetElements();
+ while (++elem) {
+ if (elem->GetIFF() > 0 && elem->GetIFF() != iff && elem->IntelLevel() > Intel::SECRET) {
+ if (elem->IsGroundUnit()) {
+ if (!elem->GetDesign() ||
+ elem->GetDesign()->type != Ship::SAM)
+ continue;
+
+ if (elem->Region() != rgn0 &&
+ elem->Region() != rgn1)
+ continue;
+ }
+
+ int mission_role = elem->MissionRole();
+
+ if (mission_role == Mission::STRIKE ||
+ mission_role == Mission::INTEL ||
+ mission_role == Mission::CARGO ||
+ mission_role == Mission::TRANSPORT)
+ continue;
+
+ if (elem->GetDesign()->type >= Ship::MINE && elem->GetDesign()->type <= Ship::SWACS)
+ enemy_sites += elem->Count();
+
+ else if (elem->IsDropship())
+ enemy_fighters += elem->Count();
+
+ else if (elem->IsStarship())
+ enemy_starships += elem->Count();
+
+ else if (elem->IsGroundUnit())
+ enemy_sites += elem->Count();
+ }
+ }
+ }
+
+ if (enemy_starships > 0) {
+ threat_info = "We have reports of several enemy starships in the vicinity.";
+
+ if (enemy_fighters > 0) {
+ threat_info += " Also be advised that enemy fighters may be operating nearby.";
+ }
+
+ else if (enemy_sites > 0) {
+ threat_info += " And be on the lookout for mines and defense satellites.";
+ }
+ }
+
+ else if (enemy_fighters > 0) {
+ threat_info = "We have reports of several enemy fighters in your operating area.";
+ }
+
+ else if (enemy_sites > 0) {
+ if (mission->Type() >= Mission::AIR_PATROL && mission->Type() <= Mission::STRIKE)
+ threat_info = "Remember to check air-to-ground sensors for SAM and AAA sites.";
+ else
+ threat_info = "Be on the lookout for mines and defense satellites.";
+ }
+ else {
+ threat_info = "We have no reliable information on threats in your operating area.";
+ }
+
+ return threat_info;
+}
diff --git a/Stars45/CampaignSituationReport.h b/Stars45/CampaignSituationReport.h
new file mode 100644
index 0000000..118e7eb
--- /dev/null
+++ b/Stars45/CampaignSituationReport.h
@@ -0,0 +1,59 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CampaignSituationReport.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CampaignSituationReport generates the situation report
+ portion of the briefing for a dynamically generated
+ mission in a dynamic campaign.
+*/
+
+#ifndef CampaignSituationReport_h
+#define CampaignSituationReport_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Campaign;
+class CombatGroup;
+class CombatUnit;
+class CombatZone;
+class Mission;
+class MissionElement;
+
+// +--------------------------------------------------------------------+
+
+class CampaignSituationReport
+{
+public:
+ static const char* TYPENAME() { return "CampaignSituationReport"; }
+
+ CampaignSituationReport(Campaign* c, Mission* m);
+ virtual ~CampaignSituationReport();
+
+ virtual void GenerateSituationReport();
+
+protected:
+ virtual void GlobalSituation();
+ virtual void MissionSituation();
+ virtual MissionElement*
+ FindEscort(MissionElement* elem);
+ virtual Text GetThreatInfo();
+
+ Campaign* campaign;
+ Mission* mission;
+ Text sitrep;
+};
+
+#endif CampaignSituationReport_h
+
diff --git a/Stars45/CarrierAI.cpp b/Stars45/CarrierAI.cpp
new file mode 100644
index 0000000..8f584b9
--- /dev/null
+++ b/Stars45/CarrierAI.cpp
@@ -0,0 +1,400 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CarrierAI.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ "Air Boss" AI class for managing carrier fighter squadrons
+*/
+
+#include "MemDebug.h"
+#include "CarrierAI.h"
+#include "ShipAI.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Element.h"
+#include "FlightPlanner.h"
+#include "Instruction.h"
+#include "RadioMessage.h"
+#include "RadioTraffic.h"
+#include "Hangar.h"
+#include "FlightDeck.h"
+#include "Mission.h"
+#include "Contact.h"
+#include "Sim.h"
+#include "StarSystem.h"
+#include "Callsign.h"
+#include "NetUtil.h"
+
+#include "Game.h"
+#include "Random.h"
+
+// +----------------------------------------------------------------------+
+
+CarrierAI::CarrierAI(Ship* s, int level)
+ : sim(0), ship(s), hangar(0), exec_time(0), flight_planner(0),
+ hold_time(0), ai_level(level)
+{
+ if (ship) {
+ sim = Sim::GetSim();
+ hangar = ship->GetHangar();
+
+ for (int i = 0; i < 4; i++)
+ patrol_elem[i] = 0;
+
+ if (ship)
+ flight_planner = new(__FILE__,__LINE__) FlightPlanner(ship);
+
+ hold_time = (int) Game::GameTime();
+ }
+}
+
+CarrierAI::~CarrierAI()
+{
+ delete flight_planner;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CarrierAI::ExecFrame(double secs)
+{
+ const int INIT_HOLD = 15000;
+ const int EXEC_PERIOD = 3000;
+
+ if (!sim || !ship || !hangar)
+ return;
+
+ if (((int) Game::GameTime() - hold_time >= INIT_HOLD) &&
+ ((int) Game::GameTime() - exec_time > EXEC_PERIOD)) {
+
+ CheckHostileElements();
+ CheckPatrolCoverage();
+
+ exec_time = (int) Game::GameTime();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CarrierAI::CheckPatrolCoverage()
+{
+ const DWORD PATROL_PERIOD = 900 * 1000;
+
+ // pick up existing patrol elements:
+
+ ListIter<Element> iter = sim->GetElements();
+ while (++iter) {
+ Element* elem = iter.value();
+
+ if (elem->GetCarrier() == ship &&
+ (elem->Type() == Mission::PATROL ||
+ elem->Type() == Mission::SWEEP ||
+ elem->Type() == Mission::AIR_PATROL ||
+ elem->Type() == Mission::AIR_SWEEP) &&
+ !elem->IsSquadron() &&
+ !elem->IsFinished()) {
+
+ bool found = false;
+ int open = -1;
+
+ for (int i = 0; i < 4; i++) {
+ if (patrol_elem[i] == elem)
+ found = true;
+
+ else if (patrol_elem[i] == 0 && open < 0)
+ open = i;
+ }
+
+ if (!found && open >= 0) {
+ patrol_elem[open] = elem;
+ }
+ }
+ }
+
+ // manage the four screening patrols:
+
+ for (int i = 0; i < 4; i++) {
+ Element* elem = patrol_elem[i];
+
+ if (elem) {
+ if (elem->IsFinished()) {
+ patrol_elem[i] = 0;
+ }
+
+ else {
+ LaunchElement(elem);
+ }
+ }
+
+ else if (Game::GameTime() - hangar->GetLastPatrolLaunch() > PATROL_PERIOD ||
+ hangar->GetLastPatrolLaunch() == 0) {
+ Element* patrol = CreatePackage(0, 2, Mission::PATROL, 0, "ACM Medium Range");
+ if (patrol) {
+ patrol_elem[i] = patrol;
+
+ if (flight_planner)
+ flight_planner->CreatePatrolRoute(patrol, i);
+
+ hangar->SetLastPatrolLaunch(Game::GameTime());
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CarrierAI::CheckHostileElements()
+{
+ List<Element> assigned;
+ ListIter<Element> iter = sim->GetElements();
+ while (++iter) {
+ Element* elem = iter.value();
+
+ // if this element is hostile to us
+ // or if the element is a target objective
+ // of the carrier, or is hostile to any
+ // of our squadrons...
+
+ bool hostile = false;
+
+ if (elem->IsHostileTo(ship) || elem->IsObjectiveTargetOf(ship)) {
+ hostile = true;
+ }
+ else {
+ for (int i = 0; i < hangar->NumSquadrons() && !hostile; i++) {
+ int squadron_iff = hangar->SquadronIFF(i);
+
+ if (elem->IsHostileTo(squadron_iff))
+ hostile = true;
+ }
+ }
+
+ if (hostile) {
+ sim->GetAssignedElements(elem, assigned);
+
+ // is one of our fighter elements already assigned to this target?
+ bool found = false;
+ ListIter<Element> a_iter = assigned;
+ while (++a_iter && !found) {
+ Element* a = a_iter.value();
+
+ if (a->GetCarrier() == ship)
+ found = true;
+ }
+
+ // nobody is assigned yet, create an attack package
+ if (!found && CreateStrike(elem)) {
+ hold_time = (int) Game::GameTime() + 30000;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool
+CarrierAI::CreateStrike(Element* elem)
+{
+ Element* strike = 0;
+ Ship* target = elem->GetShip(1);
+
+ if (target && !target->IsGroundUnit()) {
+ Contact* contact = ship->FindContact(target);
+ if (contact && contact->GetIFF(ship) > 0) {
+
+ // fighter intercept
+ if (target->IsDropship()) {
+ int squadron = 0;
+ if (hangar->NumShipsReady(1) >= hangar->NumShipsReady(0))
+ squadron = 1;
+
+ int count = 2;
+
+ if (count < elem->NumShips())
+ count = elem->NumShips();
+
+ strike = CreatePackage(squadron, count, Mission::INTERCEPT, elem->Name(), "ACM Medium Range");
+
+ if (strike) {
+ strike->SetAssignment(elem);
+
+ if (flight_planner)
+ flight_planner->CreateStrikeRoute(strike, elem);
+ }
+ }
+
+ // starship or station assault
+ else {
+ int squadron = 0;
+ if (hangar->NumSquadrons() > 1)
+ squadron = 1;
+ if (hangar->NumSquadrons() > 2)
+ squadron = 2;
+
+ int count = 2;
+
+ if (target->Class() > Ship::FRIGATE) {
+ count = 4;
+ strike = CreatePackage(squadron, count, Mission::ASSAULT, elem->Name(), "Hvy Ship Strike");
+ }
+ else {
+ count = 2;
+ strike = CreatePackage(squadron, count, Mission::ASSAULT, elem->Name(), "Ship Strike");
+ }
+
+ if (strike) {
+ strike->SetAssignment(elem);
+
+ if (flight_planner)
+ flight_planner->CreateStrikeRoute(strike, elem);
+
+ // strike escort if target has fighter protection:
+ if (target->GetHangar()) {
+ if (squadron > 1) squadron--;
+ Element* escort = CreatePackage(squadron, 2, Mission::ESCORT_STRIKE, strike->Name(), "ACM Short Range");
+
+ if (escort && flight_planner)
+ flight_planner->CreateEscortRoute(escort, strike);
+ }
+ }
+ }
+ }
+ }
+
+ return strike != 0;
+}
+
+// +--------------------------------------------------------------------+
+
+Element*
+CarrierAI::CreatePackage(int squadron, int size, int code, const char* target, const char* loadname)
+{
+ if (squadron < 0 || size < 1 || code < Mission::PATROL || hangar->NumShipsReady(squadron) < size)
+ return 0;
+
+ Sim* sim = Sim::GetSim();
+ const char* call = sim->FindAvailCallsign(ship->GetIFF());
+ Element* elem = sim->CreateElement(call, ship->GetIFF(), code);
+ FlightDeck* deck = 0;
+ int queue = 1000;
+ int* load = 0;
+ const ShipDesign*
+ design = hangar->SquadronDesign(squadron);
+
+ elem->SetSquadron(hangar->SquadronName(squadron));
+ elem->SetCarrier(ship);
+
+ if (target) {
+ int i_code = 0;
+
+ switch (code) {
+ case Mission::ASSAULT: i_code = Instruction::ASSAULT; break;
+ case Mission::STRIKE: i_code = Instruction::STRIKE; break;
+
+ case Mission::AIR_INTERCEPT:
+ case Mission::INTERCEPT: i_code = Instruction::INTERCEPT; break;
+
+ case Mission::ESCORT:
+ case Mission::ESCORT_STRIKE:
+ case Mission::ESCORT_FREIGHT:
+ i_code = Instruction::ESCORT; break;
+
+ case Mission::DEFEND: i_code = Instruction::DEFEND; break;
+ }
+
+ Instruction* objective = new(__FILE__,__LINE__) Instruction(i_code, target);
+ if (objective)
+ elem->AddObjective(objective);
+ }
+
+ if (design && loadname) {
+ Text name = loadname;
+ name.setSensitive(false);
+
+ ListIter<ShipLoad> sl = (List<ShipLoad>&) design->loadouts;
+ while (++sl) {
+ if (name == sl->name) {
+ load = sl->load;
+ elem->SetLoadout(load);
+ }
+ }
+ }
+
+ for (int i = 0; i < ship->NumFlightDecks(); i++) {
+ FlightDeck* d = ship->GetFlightDeck(i);
+
+ if (d && d->IsLaunchDeck()) {
+ int dq = hangar->PreflightQueue(d);
+
+ if (dq < queue) {
+ queue = dq;
+ deck = d;
+ }
+ }
+ }
+
+ int npackage = 0;
+ int slots[4];
+
+ for (i = 0; i < 4; i++)
+ slots[i] = -1;
+
+ for (int slot = 0; slot < hangar->SquadronSize(squadron); slot++) {
+ const HangarSlot* s = hangar->GetSlot(squadron, slot);
+
+ if (hangar->GetState(s) == Hangar::STORAGE) {
+ if (npackage < 4)
+ slots[npackage] = slot;
+
+ hangar->GotoAlert(squadron, slot, deck, elem, load, code > Mission::SWEEP);
+ npackage++;
+
+ if (npackage >= size)
+ break;
+ }
+ }
+
+ NetUtil::SendElemCreate(elem, squadron, slots, code <= Mission::SWEEP);
+
+ return elem;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CarrierAI::LaunchElement(Element* elem)
+{
+ bool result = false;
+
+ if (!elem)
+ return result;
+
+ for (int squadron = 0; squadron < hangar->NumSquadrons(); squadron++) {
+ for (int slot = 0; slot < hangar->SquadronSize(squadron); slot++) {
+ const HangarSlot* s = hangar->GetSlot(squadron, slot);
+
+ if (hangar->GetState(s) == Hangar::ALERT &&
+ hangar->GetPackageElement(s) == elem) {
+
+ hangar->Launch(squadron, slot);
+ NetUtil::SendShipLaunch(ship, squadron, slot);
+
+ result = true;
+ }
+ }
+ }
+
+ return result;
+}
diff --git a/Stars45/CarrierAI.h b/Stars45/CarrierAI.h
new file mode 100644
index 0000000..651c8f5
--- /dev/null
+++ b/Stars45/CarrierAI.h
@@ -0,0 +1,64 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CarrierAI.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ "Air Boss" AI class for managing carrier fighter squadrons
+*/
+
+#ifndef CarrierAI_h
+#define CarrierAI_h
+
+#include "Types.h"
+#include "Director.h"
+
+// +--------------------------------------------------------------------+
+
+class Sim;
+class Ship;
+class ShipAI;
+class Instruction;
+class Hangar;
+class Element;
+class FlightPlanner;
+
+// +--------------------------------------------------------------------+
+
+class CarrierAI : public Director
+{
+public:
+ CarrierAI(Ship* s, int level);
+ virtual ~CarrierAI();
+
+ virtual void ExecFrame(double seconds);
+
+protected:
+ virtual bool CheckPatrolCoverage();
+ virtual bool CheckHostileElements();
+
+ virtual bool CreateStrike(Element* elem);
+
+ virtual Element* CreatePackage(int squad, int size, int code, const char* target=0, const char* loadname=0);
+ virtual bool LaunchElement(Element* elem);
+
+ Sim* sim;
+ Ship* ship;
+ Hangar* hangar;
+ FlightPlanner* flight_planner;
+ int exec_time;
+ int hold_time;
+ int ai_level;
+
+ Element* patrol_elem[4];
+};
+
+// +--------------------------------------------------------------------+
+
+#endif CarrierAI_h
+
diff --git a/Stars45/CmdDlg.cpp b/Stars45/CmdDlg.cpp
new file mode 100644
index 0000000..c6f5433
--- /dev/null
+++ b/Stars45/CmdDlg.cpp
@@ -0,0 +1,186 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmdDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Operational Command Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "CmdDlg.h"
+#include "CmpFileDlg.h"
+#include "CmpnScreen.h"
+#include "Starshatter.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "ShipDesign.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+
+CmdDlg::CmdDlg(CmpnScreen* mgr)
+ : cmpn_screen(mgr),
+ txt_group(0), txt_score(0), txt_name(0), txt_time(0),
+ btn_save(0), btn_exit(0), stars(0), campaign(0), mode(0)
+{
+ stars = Starshatter::GetInstance();
+ campaign = Campaign::GetCampaign();
+
+ for (int i = 0; i < 5; i++)
+ btn_mode[i] = 0;
+}
+
+CmdDlg::~CmdDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdDlg::RegisterCmdControls(FormWindow* win)
+{
+ btn_save = (Button*) win->FindControl( 1);
+ btn_exit = (Button*) win->FindControl( 2);
+
+ for (int i = 0; i < 5; i++) {
+ btn_mode[i] = (Button*) win->FindControl(100 + i);
+ }
+
+ txt_group = win->FindControl(200);
+ txt_score = win->FindControl(201);
+ txt_name = win->FindControl(300);
+ txt_time = win->FindControl(301);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdDlg::ShowCmdDlg()
+{
+ campaign = Campaign::GetCampaign();
+
+ if (txt_name) {
+ if (campaign)
+ txt_name->SetText(campaign->Name());
+ else
+ txt_name->SetText("No Campaign Selected");
+ }
+
+ ShowMode();
+
+ if (btn_save)
+ btn_save->SetEnabled(!campaign->IsTraining());
+
+ if (btn_mode[MODE_FORCES]) btn_mode[MODE_FORCES]->SetEnabled(!campaign->IsTraining());
+ if (btn_mode[MODE_INTEL]) btn_mode[MODE_INTEL]->SetEnabled(!campaign->IsTraining());
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdDlg::ExecFrame()
+{
+ CombatGroup* g = campaign->GetPlayerGroup();
+ if (g && txt_group)
+ txt_group->SetText(g->GetDescription());
+
+ if (txt_score) {
+ char score[32];
+ sprintf(score, "Team Score: %d", campaign->GetPlayerTeamScore());
+ txt_score->SetText(score);
+ txt_score->SetTextAlign(DT_RIGHT);
+ }
+
+ if (txt_time) {
+ double t = campaign->GetTime();
+ char daytime[32];
+ FormatDayTime(daytime, t);
+ txt_time->SetText(daytime);
+ }
+
+ int unread = campaign->CountNewEvents();
+
+ if (btn_mode[MODE_INTEL]) {
+ if (unread > 0) {
+ char text[64];
+ sprintf(text, "INTEL (%d)", unread);
+ btn_mode[MODE_INTEL]->SetText(text);
+ }
+ else {
+ btn_mode[MODE_INTEL]->SetText("INTEL");
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdDlg::ShowMode()
+{
+ for (int i = 0; i < 5; i++) {
+ if (btn_mode[i]) btn_mode[i]->SetButtonState(0);
+ }
+
+ if (mode < 0 || mode > 4)
+ mode = 0;
+
+ if (btn_mode[mode]) btn_mode[mode]->SetButtonState(1);
+}
+
+void
+CmdDlg::OnMode(AWEvent* event)
+{
+ for (int i = MODE_ORDERS; i < NUM_MODES; i++) {
+ Button* btn = btn_mode[i];
+
+ if (event->window == btn) {
+ mode = i;
+ }
+ }
+
+ switch (mode) {
+ case MODE_ORDERS: cmpn_screen->ShowCmdOrdersDlg(); break;
+ case MODE_THEATER: cmpn_screen->ShowCmdTheaterDlg(); break;
+ case MODE_FORCES: cmpn_screen->ShowCmdForceDlg(); break;
+ case MODE_INTEL: cmpn_screen->ShowCmdIntelDlg(); break;
+ case MODE_MISSIONS: cmpn_screen->ShowCmdMissionsDlg(); break;
+ default: cmpn_screen->ShowCmdOrdersDlg(); break;
+ }
+}
+
+void
+CmdDlg::OnSave(AWEvent* event)
+{
+ if (campaign && cmpn_screen) {
+ CmpFileDlg* fdlg = cmpn_screen->GetCmpFileDlg();
+
+ cmpn_screen->ShowCmpFileDlg();
+ }
+}
+
+void
+CmdDlg::OnExit(AWEvent* event)
+{
+ if (stars) {
+ Mouse::Show(false);
+ stars->SetGameMode(Starshatter::MENU_MODE);
+ }
+}
+
+// +--------------------------------------------------------------------+
diff --git a/Stars45/CmdDlg.h b/Stars45/CmdDlg.h
new file mode 100644
index 0000000..5d98f8a
--- /dev/null
+++ b/Stars45/CmdDlg.h
@@ -0,0 +1,83 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmdDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Operational Command Dialog Active Window class
+*/
+
+#ifndef CmdDlg_h
+#define CmdDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class CmpnScreen;
+class Campaign;
+class Combatant;
+class CombatGroup;
+class CombatUnit;
+class Starshatter;
+
+// +--------------------------------------------------------------------+
+
+class CmdDlg
+{
+public:
+ enum MODE {
+ MODE_ORDERS,
+ MODE_THEATER,
+ MODE_FORCES,
+ MODE_INTEL,
+ MODE_MISSIONS,
+ NUM_MODES
+ };
+
+ CmdDlg(CmpnScreen* mgr);
+ virtual ~CmdDlg();
+
+ virtual void RegisterCmdControls(FormWindow* win);
+ virtual void ShowCmdDlg();
+
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnMode(AWEvent* event);
+ virtual void OnSave(AWEvent* event);
+ virtual void OnExit(AWEvent* event);
+
+protected:
+ virtual void ShowMode();
+
+ CmpnScreen* cmpn_screen;
+
+ ActiveWindow* txt_group;
+ ActiveWindow* txt_score;
+ ActiveWindow* txt_name;
+ ActiveWindow* txt_time;
+ Button* btn_mode[NUM_MODES];
+ Button* btn_save;
+ Button* btn_exit;
+
+ Starshatter* stars;
+ Campaign* campaign;
+
+ int mode;
+};
+
+#endif CmdDlg_h
+
diff --git a/Stars45/CmdForceDlg.cpp b/Stars45/CmdForceDlg.cpp
new file mode 100644
index 0000000..205817d
--- /dev/null
+++ b/Stars45/CmdForceDlg.cpp
@@ -0,0 +1,718 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmdForceDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Operational Command Dialog (Order of Battle Tab)
+*/
+
+#include "MemDebug.h"
+#include "CmdForceDlg.h"
+#include "CmdMsgDlg.h"
+#include "CmpnScreen.h"
+#include "Starshatter.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatAssignment.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "CombatZone.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Player.h"
+#include "Weapon.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(CmdForceDlg, OnMode);
+DEF_MAP_CLIENT(CmdForceDlg, OnSave);
+DEF_MAP_CLIENT(CmdForceDlg, OnExit);
+DEF_MAP_CLIENT(CmdForceDlg, OnForces);
+DEF_MAP_CLIENT(CmdForceDlg, OnCombat);
+DEF_MAP_CLIENT(CmdForceDlg, OnTransfer);
+
+// +--------------------------------------------------------------------+
+
+CmdForceDlg::CmdForceDlg(Screen* s, FormDef& def, CmpnScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ CmdDlg(mgr),
+ cmb_forces(0), lst_combat(0), lst_desc(0),
+ stars(0), campaign(0), current_group(0), current_unit(0),
+ btn_transfer(0)
+{
+ stars = Starshatter::GetInstance();
+ campaign = Campaign::GetCampaign();
+
+ Init(def);
+}
+
+CmdForceDlg::~CmdForceDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdForceDlg::RegisterControls()
+{
+ cmb_forces = (ComboBox*) FindControl(400);
+ lst_combat = (ListBox*) FindControl(401);
+ lst_desc = (ListBox*) FindControl(402);
+ btn_transfer = (Button*) FindControl(403);
+
+ RegisterCmdControls(this);
+
+ if (btn_save)
+ REGISTER_CLIENT(EID_CLICK, btn_save, CmdForceDlg, OnSave);
+
+ if (btn_exit)
+ REGISTER_CLIENT(EID_CLICK, btn_exit, CmdForceDlg, OnExit);
+
+ for (int i = 0; i < 5; i++) {
+ if (btn_mode[i])
+ REGISTER_CLIENT(EID_CLICK, btn_mode[i], CmdForceDlg, OnMode);
+ }
+
+ if (cmb_forces)
+ REGISTER_CLIENT(EID_SELECT, cmb_forces, CmdForceDlg, OnForces);
+
+ if (lst_combat) {
+ lst_combat->AddColumn("datatype", 0);
+ REGISTER_CLIENT(EID_SELECT, lst_combat, CmdForceDlg, OnCombat);
+ }
+
+ if (btn_transfer) {
+ btn_transfer->SetEnabled(false);
+ REGISTER_CLIENT(EID_CLICK, btn_transfer, CmdForceDlg, OnTransfer);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdForceDlg::ExecFrame()
+{
+ CmdDlg::ExecFrame();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdForceDlg::Show()
+{
+ mode = MODE_FORCES;
+
+ FormWindow::Show();
+ ShowCmdDlg();
+
+ cmb_forces->ClearItems();
+
+ campaign = Campaign::GetCampaign();
+
+ if (campaign) {
+ List<Combatant>& combatants = campaign->GetCombatants();
+
+ if (combatants.size() > 0) {
+ for (int i = 0; i < combatants.size(); i++) {
+ if (IsVisible(combatants[i])) {
+ cmb_forces->AddItem(combatants[i]->Name());
+ }
+ }
+
+ cmb_forces->SetSelection(0);
+ Combatant* c = combatants[0];
+ ShowCombatant(c);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CmdForceDlg::IsVisible(Combatant* c)
+{
+ int nvis = 0;
+
+ if (c) {
+ CombatGroup* force = c->GetForce();
+
+ if (force) {
+ List<CombatGroup>& groups = force->GetComponents();
+ for (int i = 0; i < groups.size(); i++) {
+ CombatGroup* g = groups[i];
+
+ if (g->Type() < CombatGroup::CIVILIAN &&
+ g->CountUnits() > 0 &&
+ g->IntelLevel() >= Intel::KNOWN)
+
+ nvis++;
+ }
+ }
+ }
+
+ return nvis > 0;
+}
+
+// +--------------------------------------------------------------------+
+
+static char pipe_stack[32];
+static bool blank_line = false;
+
+void
+CmdForceDlg::ShowCombatant(Combatant* c)
+{
+ if (!lst_combat || !c) return;
+
+ lst_combat->ClearItems();
+ ZeroMemory(pipe_stack, 32);
+
+ CombatGroup* force = c->GetForce();
+
+ if (force) {
+ List<CombatGroup>& groups = force->GetComponents();
+ for (int i = 0; i < groups.size(); i++) {
+ CombatGroup* g = groups[i];
+ if (g->Type() < CombatGroup::CIVILIAN && g->CountUnits() > 0)
+ AddCombatGroup(g);
+ }
+ }
+
+ current_group = 0;
+ current_unit = 0;
+
+ if (lst_desc) lst_desc->ClearItems();
+ if (btn_transfer) btn_transfer->SetEnabled(false);
+}
+
+void
+CmdForceDlg::AddCombatGroup(CombatGroup* grp, bool last_child)
+{
+ if (!lst_combat || !grp || grp->IntelLevel() < Intel::KNOWN) return;
+
+ char prefix[4];
+
+ if (!grp->GetParent() || grp->GetParent()->Type() == CombatGroup::FORCE) {
+ if (!grp->IsExpanded()) {
+ prefix[0] = Font::PIPE_PLUS;
+ prefix[1] = 0;
+ }
+ else {
+ prefix[0] = Font::PIPE_MINUS;
+ prefix[1] = 0;
+ }
+ }
+ else {
+ prefix[0] = last_child ? Font::PIPE_LL : Font::PIPE_LT;
+ prefix[1] = Font::PIPE_HORZ;
+ prefix[2] = 0;
+ prefix[3] = 0;
+
+ if (grp->GetLiveComponents().size() > 0 || grp->GetUnits().size() > 0) {
+ if (grp->IsExpanded())
+ prefix[1] = Font::PIPE_MINUS;
+ else
+ prefix[1] = Font::PIPE_PLUS;
+ }
+ }
+
+ int index = lst_combat->AddItemWithData(
+ Text(pipe_stack) +
+ Text(prefix) +
+ grp->GetDescription(),
+ (DWORD) grp);
+ lst_combat->SetItemData(index-1, 1, 0);
+ blank_line = false;
+
+ int stacklen = strlen(pipe_stack);
+
+ if (!grp->GetParent() || grp->GetParent()->Type() == CombatGroup::FORCE)
+ ; // no stack after first entry
+ else if (prefix[0] == Font::PIPE_LT)
+ pipe_stack[stacklen++] = Font::PIPE_VERT;
+ else
+ pipe_stack[stacklen++] = Font::PIPE_NBSP;
+ pipe_stack[stacklen] = 0;
+
+ if (grp->IsExpanded() && grp->GetUnits().size() > 0) {
+ prefix[0] = (grp->GetLiveComponents().size() > 0) ? Font::PIPE_VERT : Font::PIPE_NBSP;
+ prefix[1] = Font::PIPE_NBSP;
+ prefix[2] = 0;
+ prefix[3] = 0;
+
+ ListIter<CombatUnit> unit_iter = grp->GetUnits();
+ while (++unit_iter) {
+ CombatUnit* unit = unit_iter.value();
+ char info[512];
+ int damage = (int) (100 * unit->GetSustainedDamage() / unit->GetDesign()->integrity);
+
+ if (damage < 1 || unit->DeadCount() >= unit->Count()) {
+ sprintf(info, "%s%s%s", pipe_stack, prefix, unit->GetDescription());
+ }
+ else {
+ sprintf(info, "%s%s%s %d%% damage", pipe_stack, prefix, unit->GetDescription(),
+ damage);
+ }
+
+ int index = lst_combat->AddItemWithData(info, (DWORD) unit);
+
+ lst_combat->SetItemData(index-1, 1, 1);
+ lst_combat->SetItemColor(index-1, lst_combat->GetForeColor() * 0.65);
+ }
+
+ // blank line after last unit in group:
+ lst_combat->AddItem(Text(pipe_stack) + Text(prefix));
+ blank_line = true;
+ }
+
+ if (grp->IsExpanded() && grp->GetLiveComponents().size() > 0) {
+ List<CombatGroup>& groups = grp->GetLiveComponents();
+ for (int i = 0; i < groups.size(); i++) {
+ AddCombatGroup(groups[i], i == groups.size()-1);
+ }
+
+ // blank line after last group in group:
+ if (!blank_line) {
+ lst_combat->AddItem(pipe_stack);
+ blank_line = true;
+ }
+ }
+
+ pipe_stack[stacklen-1] = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdForceDlg::OnForces(AWEvent* event)
+{
+ Text name = cmb_forces->GetSelectedItem();
+
+ campaign = Campaign::GetCampaign();
+
+ if (campaign) {
+ ListIter<Combatant> iter = campaign->GetCombatants();
+
+ while (++iter) {
+ Combatant* c = iter.value();
+
+ if (name == c->Name()) {
+ ShowCombatant(c);
+ break;
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+struct WepGroup {
+ Text name;
+ int count;
+
+ WepGroup() : count(0) { }
+};
+
+WepGroup* FindWepGroup(WepGroup* weapons, const char* name)
+{
+ WepGroup* group = 0;
+ WepGroup* iter = weapons;
+ int w = 0;
+ int first = -1;
+
+ while (!group && w < 8) {
+ if (first < 0 && iter->name.length() == 0)
+ first = w;
+
+ if (!stricmp(iter->name, name))
+ group = iter;
+
+ iter++;
+ w++;
+ }
+
+ if (!group && first >= 0) {
+ group = weapons + first;
+ group->name = name;
+ }
+
+ return group;
+}
+
+void
+CmdForceDlg::OnCombat(AWEvent* event)
+{
+ static int old_index = -1;
+
+ int top_index = 0;
+ bool expand = false;
+ DWORD data = 0;
+ DWORD type = 0;
+
+ current_group = 0;
+ current_unit = 0;
+
+ if (lst_combat) {
+ top_index = lst_combat->GetTopIndex();
+
+ int index = lst_combat->GetListIndex();
+ data = lst_combat->GetItemData(index);
+ type = lst_combat->GetItemData(index, 1);
+ Text item = lst_combat->GetItemText(index);
+ int nplus = item.indexOf(Font::PIPE_PLUS);
+ int xplus = -1;
+ int dx = 10000;
+
+ if (nplus < 0)
+ nplus = item.indexOf(Font::PIPE_MINUS);
+
+ if (nplus >= 0 && nplus < 64) {
+ char pipe[64];
+ strncpy(pipe, item.data(), nplus+1);
+ pipe[nplus+1] = 0;
+
+ Rect rect(0, 0, 1000, 20);
+ lst_combat->DrawText(pipe, 0, rect, DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_CALCRECT);
+
+ xplus = rect.w;
+ }
+
+ if (xplus > 0) {
+ dx = Mouse::X() - (lst_combat->GetRect().x + xplus - 16);
+ }
+
+ // look for click on plus/minus in pipe
+ if (dx >= 0 && dx < 16) {
+ if (data && type == 0) {
+ CombatGroup* grp = (CombatGroup*) data;
+ grp->SetExpanded(!grp->IsExpanded());
+ expand = true;
+
+ current_group = grp;
+ current_unit = 0;
+ }
+ }
+
+ old_index = index;
+ }
+
+ if (campaign && data) {
+ if (!expand) {
+ lst_desc->ClearItems();
+
+ // combat group
+ if (type == 0) {
+ CombatGroup* grp = (CombatGroup*) data;
+
+ current_group = grp;
+ current_unit = 0;
+
+ // if has units, show location and assignment
+ if (grp->GetUnits().size() > 0) {
+ char txt[64];
+ int n;
+
+ n = lst_desc->AddItem("Group:") - 1;
+ lst_desc->SetItemText(n, 1, grp->GetDescription());
+ n = lst_desc->AddItem("Sector:") - 1;
+ lst_desc->SetItemText(n, 1, grp->GetRegion());
+
+ lst_desc->AddItem(" ");
+ n = lst_desc->AddItem("Sorties:") - 1;
+
+ if (grp->Sorties() >= 0)
+ sprintf(txt, "%d", grp->Sorties());
+ else
+ strcpy(txt, "Unavail");
+
+ lst_desc->SetItemText(n, 1, txt);
+
+ n = lst_desc->AddItem("Kills:") - 1;
+
+ if (grp->Kills() >= 0)
+ sprintf(txt, "%d", grp->Kills());
+ else
+ strcpy(txt, "Unavail");
+
+ lst_desc->SetItemText(n, 1, txt);
+
+ n = lst_desc->AddItem("Eff Rating:") - 1;
+
+ if (grp->Points() >= 0) {
+ if (grp->Sorties() > 0)
+ sprintf(txt, "%.1f", (double) grp->Points() / grp->Sorties());
+ else
+ sprintf(txt, "%.1f", (double) grp->Points());
+ }
+ else {
+ strcpy(txt, "Unavail");
+ }
+
+ lst_desc->SetItemText(n, 1, txt);
+ }
+
+ // else (if high-level) show components
+ else {
+ int n;
+
+ n = lst_desc->AddItem("Group:") - 1;
+ lst_desc->SetItemText(n, 1, grp->GetDescription());
+
+ ListIter<CombatGroup> c = grp->GetLiveComponents();
+ while (++c) {
+ n = lst_desc->AddItem("-") - 1;
+ lst_desc->SetItemText(n, 1, c->GetDescription());
+ }
+ }
+ }
+ // combat unit
+ else {
+ CombatUnit* unit = (CombatUnit*) data;
+ current_group = unit->GetCombatGroup();
+ current_unit = unit;
+
+ int n;
+ char txt[64];
+
+ n = lst_desc->AddItem("Unit:") - 1;
+ lst_desc->SetItemText(n, 1, unit->GetDescription());
+ n = lst_desc->AddItem("Sector:") - 1;
+ lst_desc->SetItemText(n, 1, unit->GetRegion());
+
+ const ShipDesign* design = unit->GetDesign();
+ if (design) {
+ lst_desc->AddItem(" ");
+ n = lst_desc->AddItem("Type:") - 1;
+ lst_desc->SetItemText(n, 1, Ship::ClassName(design->type));
+ n = lst_desc->AddItem("Class:") - 1;
+ lst_desc->SetItemText(n, 1, design->DisplayName());
+
+ if (design->type < Ship::STATION)
+ FormatNumber(txt, design->radius/2);
+ else
+ FormatNumber(txt, design->radius*2);
+
+ strcat(txt, " m");
+
+ n = lst_desc->AddItem("Length:") - 1;
+ lst_desc->SetItemText(n, 1, txt);
+
+ FormatNumber(txt, design->mass);
+ strcat(txt, " T");
+
+ n = lst_desc->AddItem("Mass:") - 1;
+ lst_desc->SetItemText(n, 1, txt);
+
+ FormatNumber(txt, design->integrity);
+ n = lst_desc->AddItem("Hull:") - 1;
+ lst_desc->SetItemText(n, 1, txt);
+
+ if (design->weapons.size()) {
+ lst_desc->AddItem(" ");
+ n = lst_desc->AddItem("Weapons:") - 1;
+
+ WepGroup groups[8];
+ for (int w = 0; w < design->weapons.size(); w++) {
+ Weapon* gun = design->weapons[w];
+ WepGroup* group = FindWepGroup(groups, gun->Group());
+
+ if (group)
+ group->count++;
+ }
+
+ for (int g = 0; g < 8; g++) {
+ WepGroup* group = &groups[g];
+ if (group && group->count) {
+ sprintf(txt, "%s (%d)", group->name.data(), group->count);
+ if (g > 0) n = lst_desc->AddItem(" ") - 1;
+ lst_desc->SetItemText(n, 1, txt);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ else {
+ List<Combatant>& combatants = campaign->GetCombatants();
+ Combatant* c = combatants[0];
+
+ if (cmb_forces) {
+ Text name = cmb_forces->GetSelectedItem();
+
+ for (int i = 0; i < combatants.size(); i++) {
+ c = combatants[i];
+
+ if (name == c->Name()) {
+ break;
+ }
+ }
+ }
+
+ if (c) {
+ ShowCombatant(c);
+
+ lst_combat->ScrollTo(top_index);
+ lst_combat->SetSelected(old_index);
+ }
+ }
+ }
+
+ if (btn_transfer && campaign && current_group)
+ btn_transfer->SetEnabled( campaign->IsActive() &&
+ CanTransfer(current_group) );
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CmdForceDlg::CanTransfer(CombatGroup* grp)
+{
+ if (!grp || !campaign)
+ return false;
+
+ if (grp->Type() < CombatGroup::WING)
+ return false;
+
+ if (grp->Type() > CombatGroup::CARRIER_GROUP)
+ return false;
+
+ if (grp->Type() == CombatGroup::FLEET ||
+ grp->Type() == CombatGroup::LCA_SQUADRON)
+ return false;
+
+ CombatGroup* player_group = campaign->GetPlayerGroup();
+ if (player_group->GetIFF() != grp->GetIFF())
+ return false;
+
+ return true;
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+CmdForceDlg::OnSave(AWEvent* event)
+{
+ CmdDlg::OnSave(event);
+}
+
+void
+CmdForceDlg::OnExit(AWEvent* event)
+{
+ CmdDlg::OnExit(event);
+}
+
+void
+CmdForceDlg::OnMode(AWEvent* event)
+{
+ CmdDlg::OnMode(event);
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+CmdForceDlg::OnTransfer(AWEvent* event)
+{
+ if (campaign && current_group) {
+
+ // check player rank/responsibility:
+ Player* player = Player::GetCurrentPlayer();
+ int cmd_class = Ship::FIGHTER;
+
+ switch (current_group->Type()) {
+ case CombatGroup::WING:
+ case CombatGroup::INTERCEPT_SQUADRON:
+ case CombatGroup::FIGHTER_SQUADRON:
+ cmd_class = Ship::FIGHTER;
+ break;
+
+ case CombatGroup::ATTACK_SQUADRON:
+ cmd_class = Ship::ATTACK;
+ break;
+
+ case CombatGroup::LCA_SQUADRON:
+ cmd_class = Ship::LCA;
+ break;
+
+ case CombatGroup::DESTROYER_SQUADRON:
+ cmd_class = Ship::DESTROYER;
+ break;
+
+ case CombatGroup::BATTLE_GROUP:
+ cmd_class = Ship::CRUISER;
+ break;
+
+ case CombatGroup::CARRIER_GROUP:
+ case CombatGroup::FLEET:
+ cmd_class = Ship::CARRIER;
+ break;
+ }
+
+ char transfer_info[512];
+
+ if (player->CanCommand(cmd_class)) {
+ if (current_unit) {
+ campaign->SetPlayerUnit(current_unit);
+
+ sprintf(transfer_info, "Your transfer request has been approved, %s %s. You are now assigned to the %s. Good luck.\n\nFleet Admiral A. Evars FORCOM\nCommanding",
+ Player::RankName(player->Rank()),
+ player->Name().data(),
+ current_unit->GetDescription());
+ }
+ else {
+ campaign->SetPlayerGroup(current_group);
+
+ sprintf(transfer_info, "Your transfer request has been approved, %s %s. You are now assigned to the %s. Good luck.\n\nFleet Admiral A. Evars FORCOM\nCommanding",
+ Player::RankName(player->Rank()),
+ player->Name().data(),
+ current_group->GetDescription());
+ }
+
+ Button::PlaySound(Button::SND_ACCEPT);
+
+ CmdMsgDlg* msgdlg = manager->GetCmdMsgDlg();
+ msgdlg->Title()->SetText("Transfer Approved");
+ msgdlg->Message()->SetText(transfer_info);
+ msgdlg->Message()->SetTextAlign(DT_LEFT);
+
+ manager->ShowCmdMsgDlg();
+ }
+
+ else {
+ Button::PlaySound(Button::SND_REJECT);
+
+ sprintf(transfer_info, "Your transfer request has been denied, %s %s. The %s requires a command rank of %s. Please return to your unit and your duties.\n\nFleet Admiral A. Evars FORCOM\nCommanding",
+ Player::RankName(player->Rank()),
+ player->Name().data(),
+ current_group->GetDescription(),
+ Player::RankName(Player::CommandRankRequired(cmd_class)));
+
+ CmdMsgDlg* msgdlg = manager->GetCmdMsgDlg();
+ msgdlg->Title()->SetText("Transfer Denied");
+ msgdlg->Message()->SetText(transfer_info);
+ msgdlg->Message()->SetTextAlign(DT_LEFT);
+
+ manager->ShowCmdMsgDlg();
+ }
+ }
+}
diff --git a/Stars45/CmdForceDlg.h b/Stars45/CmdForceDlg.h
new file mode 100644
index 0000000..86fb348
--- /dev/null
+++ b/Stars45/CmdForceDlg.h
@@ -0,0 +1,69 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmdForceDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Operational Command Dialog (Order of Battle Tab)
+*/
+
+#ifndef CmdForceDlg_h
+#define CmdForceDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "CmdDlg.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class CmdForceDlg : public FormWindow,
+ public CmdDlg
+{
+public:
+ CmdForceDlg(Screen* s, FormDef& def, CmpnScreen* mgr);
+ virtual ~CmdForceDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnMode(AWEvent* event);
+ virtual void OnSave(AWEvent* event);
+ virtual void OnExit(AWEvent* event);
+ virtual void OnForces(AWEvent* event);
+ virtual void OnCombat(AWEvent* event);
+ virtual void OnTransfer(AWEvent* event);
+
+protected:
+ void ShowCombatant(Combatant* c);
+ void AddCombatGroup(CombatGroup* grp, bool last_child=false);
+ bool CanTransfer(CombatGroup* grp);
+ bool IsVisible(Combatant* c);
+
+ CmpnScreen* manager;
+
+ ComboBox* cmb_forces;
+ ListBox* lst_combat;
+ ListBox* lst_desc;
+ Button* btn_transfer;
+
+ Starshatter* stars;
+ Campaign* campaign;
+ CombatGroup* current_group;
+ CombatUnit* current_unit;
+};
+
+#endif CmdForceDlg_h
+
diff --git a/Stars45/CmdIntelDlg.cpp b/Stars45/CmdIntelDlg.cpp
new file mode 100644
index 0000000..a08e816
--- /dev/null
+++ b/Stars45/CmdIntelDlg.cpp
@@ -0,0 +1,393 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmdIntelDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Operational Command Dialog (Intel/Newsfeed Tab)
+*/
+
+#include "MemDebug.h"
+#include "CmdIntelDlg.h"
+#include "CmpnScreen.h"
+#include "Starshatter.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatEvent.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "ShipDesign.h"
+#include "Starshatter.h"
+#include "Sim.h"
+#include "CameraDirector.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(CmdIntelDlg, OnMode);
+DEF_MAP_CLIENT(CmdIntelDlg, OnSave);
+DEF_MAP_CLIENT(CmdIntelDlg, OnExit);
+DEF_MAP_CLIENT(CmdIntelDlg, OnNews);
+DEF_MAP_CLIENT(CmdIntelDlg, OnPlay);
+
+// +--------------------------------------------------------------------+
+
+CmdIntelDlg::CmdIntelDlg(Screen* s, FormDef& def, CmpnScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), CmdDlg(mgr), manager(mgr),
+ stars(0), campaign(0), update_time(0), start_scene(0),
+ cam_view(0), dsp_view(0)
+{
+ stars = Starshatter::GetInstance();
+ campaign = Campaign::GetCampaign();
+
+ if (campaign)
+ update_time = campaign->GetUpdateTime();
+
+ Init(def);
+}
+
+CmdIntelDlg::~CmdIntelDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdIntelDlg::RegisterControls()
+{
+ lst_news = (ListBox*) FindControl(401);
+ txt_news = (RichTextBox*) FindControl(402);
+ img_news = (ImageBox*) FindControl(403);
+ mov_news = FindControl(404);
+ btn_play = (Button*) FindControl(405);
+
+ RegisterCmdControls(this);
+
+ if (btn_save)
+ REGISTER_CLIENT(EID_CLICK, btn_save, CmdIntelDlg, OnSave);
+
+ if (btn_exit)
+ REGISTER_CLIENT(EID_CLICK, btn_exit, CmdIntelDlg, OnExit);
+
+ if (btn_play)
+ REGISTER_CLIENT(EID_CLICK, btn_play, CmdIntelDlg, OnPlay);
+
+ for (int i = 0; i < 5; i++) {
+ if (btn_mode[i])
+ REGISTER_CLIENT(EID_CLICK, btn_mode[i], CmdIntelDlg, OnMode);
+ }
+
+ if (lst_news) {
+ REGISTER_CLIENT(EID_SELECT, lst_news, CmdIntelDlg, OnNews);
+ }
+
+ if (img_news) {
+ img_news->GetPicture(bmp_default);
+ }
+
+ if (mov_news) {
+ CameraDirector* cam_dir = CameraDirector::GetInstance();
+ cam_view = new(__FILE__,__LINE__) CameraView(mov_news, cam_dir->GetCamera(), 0);
+ if (cam_view)
+ mov_news->AddView(cam_view);
+
+ dsp_view = DisplayView::GetInstance();
+ if (dsp_view) {
+ dsp_view->SetWindow(mov_news);
+ mov_news->AddView(dsp_view);
+ }
+
+ mov_news->Hide();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdIntelDlg::Show()
+{
+ mode = MODE_INTEL;
+
+ FormWindow::Show();
+ ShowCmdDlg();
+
+ if (btn_play)
+ btn_play->Hide();
+
+ if (mov_news)
+ mov_news->Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdIntelDlg::ExecFrame()
+{
+ CmdDlg::ExecFrame();
+
+ if (campaign != Campaign::GetCampaign() || campaign->GetUpdateTime() != update_time) {
+ campaign = Campaign::GetCampaign();
+ update_time = campaign->GetUpdateTime();
+
+ lst_news->ClearItems();
+ txt_news->SetText("");
+
+ if (img_news)
+ img_news->SetPicture(bmp_default);
+ }
+
+ if (campaign) {
+ List<CombatEvent>& events = campaign->GetEvents();
+ bool auto_scroll = false;
+
+ if (events.size() > lst_news->NumItems()) {
+ while (events.size() > lst_news->NumItems()) {
+ CombatEvent* info = events[lst_news->NumItems()];
+
+ const char* unread = info->Visited() ? " " : "*";
+ int i = lst_news->AddItemWithData(unread, (DWORD) info) - 1;
+
+ char dateline[32];
+ FormatDayTime(dateline, info->Time());
+ lst_news->SetItemText(i, 1, dateline);
+ lst_news->SetItemText(i, 2, info->Title());
+ lst_news->SetItemText(i, 3, info->Region());
+ lst_news->SetItemText(i, 4, Game::GetText(info->SourceName()));
+
+ if (!info->Visited())
+ auto_scroll = true;
+ }
+
+ if (lst_news->GetSortColumn() > 0)
+ lst_news->SortItems();
+ }
+
+ else if (events.size() < lst_news->NumItems()) {
+ lst_news->ClearItems();
+
+ for (int i = 0; i < events.size(); i++) {
+ CombatEvent* info = events[i];
+
+ const char* unread = info->Visited() ? " " : "*";
+ int i = lst_news->AddItemWithData(unread, (DWORD) info) - 1;
+
+ char dateline[32];
+ FormatDayTime(dateline, info->Time());
+ lst_news->SetItemText(i, 1, dateline);
+ lst_news->SetItemText(i, 2, info->Title());
+ lst_news->SetItemText(i, 3, info->Region());
+ lst_news->SetItemText(i, 4, Game::GetText(info->SourceName()));
+
+ if (!info->Visited())
+ auto_scroll = true;
+ }
+
+ if (lst_news->GetSortColumn() > 0)
+ lst_news->SortItems();
+
+ txt_news->SetText("");
+
+ if (img_news)
+ img_news->SetPicture(bmp_default);
+ }
+
+ if (auto_scroll) {
+ int first_unread = -1;
+
+ for (int i = 0; i < lst_news->NumItems(); i++) {
+ if (lst_news->GetItemText(i, 0) == "*") {
+ first_unread = i;
+ break;
+ }
+ }
+
+ if (first_unread >= 0)
+ lst_news->ScrollTo(first_unread);
+ }
+ }
+
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (start_scene > 0) {
+ ShowMovie();
+
+ start_scene--;
+
+ if (start_scene == 0) {
+ if (stars && campaign) {
+ stars->ExecCutscene(event_scene, campaign->Path());
+
+ if (stars->InCutscene()) {
+ Sim* sim = Sim::GetSim();
+
+ if (sim) {
+ cam_view->UseCamera(CameraDirector::GetInstance()->GetCamera());
+ cam_view->UseScene(sim->GetScene());
+ }
+ }
+ }
+
+ event_scene = "";
+ }
+ }
+ else {
+ if (dsp_view)
+ dsp_view->ExecFrame();
+
+ if (stars->InCutscene())
+ ShowMovie();
+ else
+ HideMovie();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdIntelDlg::OnSave(AWEvent* event)
+{
+ CmdDlg::OnSave(event);
+}
+
+void
+CmdIntelDlg::OnExit(AWEvent* event)
+{
+ CmdDlg::OnExit(event);
+}
+
+void
+CmdIntelDlg::OnMode(AWEvent* event)
+{
+ CmdDlg::OnMode(event);
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+CmdIntelDlg::OnNews(AWEvent* e)
+{
+ CombatEvent* event = 0;
+ int index = lst_news->GetSelection();
+
+ if (index >= 0) {
+ event = (CombatEvent*) lst_news->GetItemData(index, 0);
+ }
+
+ if (event) {
+ Text info("<font Limerick12><color ffff80>");
+ info += event->Title();
+ info += "<font Verdana><color ffffff>\n\n";
+ info += event->Information();
+
+ txt_news->SetText(info);
+ txt_news->EnsureVisible(0);
+
+ lst_news->SetItemText(index, 0, " ");
+
+ if (img_news) {
+ if (event->Image().Width() >= 64)
+ img_news->SetPicture(event->Image());
+ else
+ img_news->SetPicture(bmp_default);
+ }
+
+ if (btn_play) {
+ if (event->SceneFile() && *event->SceneFile())
+ btn_play->Show();
+ else
+ btn_play->Hide();
+ }
+
+ if (!event->Visited() && btn_play->IsEnabled())
+ OnPlay(0);
+
+ event->SetVisited(true);
+ }
+ else {
+ txt_news->SetText("");
+
+ if (img_news)
+ img_news->SetPicture(bmp_default);
+
+ if (btn_play)
+ btn_play->Hide();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdIntelDlg::OnPlay(AWEvent* e)
+{
+ CombatEvent* event = 0;
+ int index = lst_news->GetSelection();
+
+ if (index >= 0) {
+ event = (CombatEvent*) lst_news->GetItemData(index, 0);
+ }
+
+ if (mov_news && cam_view && event && event->SceneFile() && *event->SceneFile()) {
+ event_scene = event->SceneFile();
+ start_scene = 2;
+ ShowMovie();
+ }
+}
+
+void
+CmdIntelDlg::ShowMovie()
+{
+ if (mov_news) {
+ mov_news->Show();
+ dsp_view->SetWindow(mov_news);
+
+ if (img_news) img_news->Hide();
+ if (txt_news) txt_news->Hide();
+ if (btn_play) btn_play->Hide();
+ }
+}
+
+void
+CmdIntelDlg::HideMovie()
+{
+ CombatEvent* event = 0;
+ int index = lst_news->GetSelection();
+ bool play = false;
+
+ if (index >= 0) {
+ event = (CombatEvent*) lst_news->GetItemData(index, 0);
+
+ if (event && event->SceneFile() && *event->SceneFile())
+ play = true;
+ }
+
+ if (mov_news) {
+ mov_news->Hide();
+
+ if (img_news) img_news->Show();
+ if (txt_news) txt_news->Show();
+ if (btn_play) {
+ if (play)
+ btn_play->Show();
+ else
+ btn_play->Hide();
+ }
+ }
+}
diff --git a/Stars45/CmdIntelDlg.h b/Stars45/CmdIntelDlg.h
new file mode 100644
index 0000000..ee86d60
--- /dev/null
+++ b/Stars45/CmdIntelDlg.h
@@ -0,0 +1,76 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmdIntelDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Operational Command Dialog (Intel/Newsfeed Tab)
+*/
+
+#ifndef CmdIntelDlg_h
+#define CmdIntelDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "CmdDlg.h"
+#include "CameraView.h"
+#include "DisplayView.h"
+
+#include "Bitmap.h"
+#include "Button.h"
+#include "ImageBox.h"
+#include "ListBox.h"
+#include "RichTextBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class CmdIntelDlg : public FormWindow,
+ public CmdDlg
+{
+public:
+ CmdIntelDlg(Screen* s, FormDef& def, CmpnScreen* mgr);
+ virtual ~CmdIntelDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnMode(AWEvent* event);
+ virtual void OnSave(AWEvent* event);
+ virtual void OnExit(AWEvent* event);
+ virtual void OnNews(AWEvent* event);
+ virtual void OnPlay(AWEvent* event);
+
+protected:
+ virtual void ShowMovie();
+ virtual void HideMovie();
+
+ CmpnScreen* manager;
+
+ ListBox* lst_news;
+ RichTextBox* txt_news;
+ ImageBox* img_news;
+ Button* btn_play;
+ ActiveWindow* mov_news;
+ CameraView* cam_view;
+ DisplayView* dsp_view;
+
+ Bitmap bmp_default;
+
+ Starshatter* stars;
+ Campaign* campaign;
+ double update_time;
+ int start_scene;
+ Text event_scene;
+};
+
+#endif CmdIntelDlg_h
+
diff --git a/Stars45/CmdMissionsDlg.cpp b/Stars45/CmdMissionsDlg.cpp
new file mode 100644
index 0000000..2df5ebd
--- /dev/null
+++ b/Stars45/CmdMissionsDlg.cpp
@@ -0,0 +1,325 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmdMissionsDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Operational Command Dialog (Mission List Tab)
+*/
+
+#include "MemDebug.h"
+#include "CmdMissionsDlg.h"
+#include "CmpnScreen.h"
+#include "Starshatter.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatAssignment.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "CombatZone.h"
+#include "Mission.h"
+#include "ShipDesign.h"
+#include "Player.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(CmdMissionsDlg, OnMode);
+DEF_MAP_CLIENT(CmdMissionsDlg, OnSave);
+DEF_MAP_CLIENT(CmdMissionsDlg, OnExit);
+DEF_MAP_CLIENT(CmdMissionsDlg, OnMission);
+DEF_MAP_CLIENT(CmdMissionsDlg, OnAccept);
+
+// +--------------------------------------------------------------------+
+
+CmdMissionsDlg::CmdMissionsDlg(Screen* s, FormDef& def, CmpnScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), CmdDlg(mgr), manager(mgr),
+ lst_missions(0), txt_desc(0), btn_accept(0),
+ stars(0), campaign(0), mission(0)
+{
+ stars = Starshatter::GetInstance();
+ campaign = Campaign::GetCampaign();
+
+ Init(def);
+}
+
+CmdMissionsDlg::~CmdMissionsDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdMissionsDlg::RegisterControls()
+{
+ lst_missions = (ListBox*) FindControl(401);
+ txt_desc = FindControl(402);
+ btn_accept = (Button*) FindControl(403);
+
+ RegisterCmdControls(this);
+
+ if (btn_save)
+ REGISTER_CLIENT(EID_CLICK, btn_save, CmdMissionsDlg, OnSave);
+
+ if (btn_exit)
+ REGISTER_CLIENT(EID_CLICK, btn_exit, CmdMissionsDlg, OnExit);
+
+ for (int i = 0; i < 5; i++) {
+ if (btn_mode[i])
+ REGISTER_CLIENT(EID_CLICK, btn_mode[i], CmdMissionsDlg, OnMode);
+ }
+
+ if (lst_missions) {
+ REGISTER_CLIENT(EID_SELECT, lst_missions, CmdMissionsDlg, OnMission);
+ }
+
+ if (btn_accept) {
+ btn_accept->SetEnabled(false);
+ REGISTER_CLIENT(EID_CLICK, btn_accept, CmdMissionsDlg, OnAccept);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdMissionsDlg::Show()
+{
+ mode = MODE_MISSIONS;
+
+ FormWindow::Show();
+ ShowCmdDlg();
+
+ campaign = Campaign::GetCampaign();
+
+ if (campaign) {
+ if (lst_missions) {
+ lst_missions->ClearItems();
+
+ Player* player = Player::GetCurrentPlayer();
+ List<MissionInfo>& missions = campaign->GetMissionList();
+ for (int i = 0; i < missions.size(); i++) {
+ MissionInfo* info = missions[i];
+ lst_missions->AddItemWithData(info->name, info->id);
+
+ Mission* m = info->mission;
+ if (m) {
+ if (m->Type() == Mission::TRAINING && player->HasTrained(m->Identity())) {
+ lst_missions->SetItemText(i, 1, Game::GetText("CmdMissionsDlg.training"));
+ }
+ else {
+ lst_missions->SetItemText(i, 1, m->TypeName());
+ }
+ }
+
+ char start_time[64];
+ FormatDayTime(start_time, info->start);
+ lst_missions->SetItemText(i, 2, start_time);
+ }
+ }
+ }
+}
+
+void
+CmdMissionsDlg::ExecFrame()
+{
+ CmdDlg::ExecFrame();
+
+ if (campaign) {
+ List<MissionInfo>& missions = campaign->GetMissionList();
+ Player* player = Player::GetCurrentPlayer();
+
+ if (missions.size() > lst_missions->NumItems()) {
+ while (missions.size() > lst_missions->NumItems()) {
+ MissionInfo* info = missions[lst_missions->NumItems()];
+ int i = lst_missions->AddItemWithData(info->name, info->id) - 1;
+
+ Mission* m = info->mission;
+ if (m) {
+ if (m->Type() == Mission::TRAINING && player->HasTrained(m->Identity())) {
+ lst_missions->SetItemText(i, 1, Game::GetText("CmdMissionsDlg.training"));
+ }
+ else {
+ lst_missions->SetItemText(i, 1, m->TypeName());
+ }
+ }
+
+ char start_time[64];
+ FormatDayTime(start_time, info->start);
+ lst_missions->SetItemText(i, 2, start_time);
+ }
+ }
+
+ else if (missions.size() < lst_missions->NumItems()) {
+ lst_missions->ClearItems();
+
+ for (int i = 0; i < missions.size(); i++) {
+ MissionInfo* info = missions[i];
+ lst_missions->AddItemWithData(info->name, info->id);
+
+ Mission* m = info->mission;
+ if (m) {
+ if (m->Type() == Mission::TRAINING && player->HasTrained(m->Identity())) {
+ lst_missions->SetItemText(i, 1, Game::GetText("CmdMissionsDlg.training"));
+ }
+ else {
+ lst_missions->SetItemText(i, 1, m->TypeName());
+ }
+ }
+
+ char start_time[64];
+ FormatDayTime(start_time, info->start);
+ lst_missions->SetItemText(i, 2, start_time);
+ }
+ }
+
+ else if (missions.size() > 0 && lst_missions->NumItems() > 0) {
+ int id = lst_missions->GetItemData(0);
+ MissionInfo* info = campaign->GetMissionInfo(id);
+
+ if (!info) {
+ int seln = -1;
+ int seln_id = 0;
+
+ for (int i = 0; i < lst_missions->NumItems(); i++)
+ if (lst_missions->IsSelected(i))
+ seln = i;
+
+ if (seln >= 0)
+ seln_id = lst_missions->GetItemData(seln);
+
+ lst_missions->ClearItems();
+ seln = -1;
+
+ for (i = 0; i < missions.size(); i++) {
+ MissionInfo* info = missions[i];
+ lst_missions->AddItemWithData(info->name, info->id);
+
+ Mission* m = info->mission;
+ if (m) {
+ if (m->Type() == Mission::TRAINING && player->HasTrained(m->Identity())) {
+ lst_missions->SetItemText(i, 1, Game::GetText("CmdMissionsDlg.training"));
+ }
+ else {
+ lst_missions->SetItemText(i, 1, m->TypeName());
+ }
+ }
+
+ char start_time[64];
+ FormatDayTime(start_time, info->start);
+ lst_missions->SetItemText(i, 2, start_time);
+
+ if (info->id == seln_id)
+ seln = i;
+ }
+
+ if (seln >= 0)
+ lst_missions->SetSelected(seln);
+ }
+ }
+
+ bool found = false;
+
+ for (int i = 0; i < missions.size() && !found; i++) {
+ MissionInfo* info = missions[i];
+ if (info->mission == mission)
+ found = true;
+ }
+
+ if (!found) {
+ mission = 0;
+ txt_desc->SetText("");
+ btn_accept->SetEnabled(false);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdMissionsDlg::OnSave(AWEvent* event)
+{
+ CmdDlg::OnSave(event);
+}
+
+void
+CmdMissionsDlg::OnExit(AWEvent* event)
+{
+ CmdDlg::OnExit(event);
+}
+
+void
+CmdMissionsDlg::OnMode(AWEvent* event)
+{
+ CmdDlg::OnMode(event);
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+CmdMissionsDlg::OnMission(AWEvent* event)
+{
+ if (campaign && lst_missions) {
+ MissionInfo* info = 0;
+ mission = 0;
+
+ for (int i = 0; i < lst_missions->NumItems(); i++) {
+ if (lst_missions->IsSelected(i)) {
+ int id = lst_missions->GetItemData(i);
+ info = campaign->GetMissionInfo(id);
+ }
+ }
+
+ btn_accept->SetEnabled((info != 0) ? true : false);
+
+ if (info) {
+ Text desc("<font Limerick12><color ffff80>");
+ desc += info->name;
+ desc += "<font Verdana><color ffffff>\n\n";
+ desc += info->player_info;
+ desc += "\n\n";
+ desc += info->description;
+
+ txt_desc->SetText(desc);
+ mission = info->mission;
+ }
+ else {
+ txt_desc->SetText(" ");
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdMissionsDlg::OnAccept(AWEvent* event)
+{
+ if (!campaign || !mission) {
+ ::Print(" ERROR CMD::Accept campaign=0x%08x mission=0x%08x\n", campaign, mission);
+ return;
+ }
+
+ ::Print(" CMD::Accept Mission %d\n", mission->Identity());
+
+ Mouse::Show(false);
+ campaign->SetMissionId(mission->Identity());
+ campaign->StartMission();
+ stars->SetGameMode(Starshatter::PREP_MODE);
+}
diff --git a/Stars45/CmdMissionsDlg.h b/Stars45/CmdMissionsDlg.h
new file mode 100644
index 0000000..b2a060a
--- /dev/null
+++ b/Stars45/CmdMissionsDlg.h
@@ -0,0 +1,65 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmdMissionsDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Operational Command Dialog (Mission List Tab)
+*/
+
+#ifndef CmdMissionsDlg_h
+#define CmdMissionsDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "CmdDlg.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Mission;
+
+// +--------------------------------------------------------------------+
+
+class CmdMissionsDlg : public FormWindow,
+ public CmdDlg
+{
+public:
+ CmdMissionsDlg(Screen* s, FormDef& def, CmpnScreen* mgr);
+ virtual ~CmdMissionsDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnMode(AWEvent* event);
+ virtual void OnSave(AWEvent* event);
+ virtual void OnExit(AWEvent* event);
+ virtual void OnMission(AWEvent* event);
+ virtual void OnAccept(AWEvent* event);
+
+protected:
+ CmpnScreen* manager;
+
+ ListBox* lst_missions;
+ ActiveWindow* txt_desc;
+ Button* btn_accept;
+
+ Starshatter* stars;
+ Campaign* campaign;
+ Mission* mission;
+};
+
+#endif CmdMissionsDlg_h
+
diff --git a/Stars45/CmdMsgDlg.cpp b/Stars45/CmdMsgDlg.cpp
new file mode 100644
index 0000000..d151954
--- /dev/null
+++ b/Stars45/CmdMsgDlg.cpp
@@ -0,0 +1,92 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmdMsgDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "CmdMsgDlg.h"
+#include "CmpnScreen.h"
+#include "Starshatter.h"
+
+#include "Game.h"
+#include "ListBox.h"
+#include "ComboBox.h"
+#include "Button.h"
+#include "Keyboard.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(CmdMsgDlg, OnApply);
+
+// +--------------------------------------------------------------------+
+
+CmdMsgDlg::CmdMsgDlg(Screen* s, FormDef& def, CmpnScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ exit_latch(false)
+{
+ Init(def);
+}
+
+CmdMsgDlg::~CmdMsgDlg()
+{
+}
+
+void
+CmdMsgDlg::RegisterControls()
+{
+ title = FindControl(100);
+ message = FindControl(101);
+
+ apply = (Button*) FindControl(1);
+ REGISTER_CLIENT(EID_CLICK, apply, CmdMsgDlg, OnApply);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdMsgDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ OnApply(0);
+ }
+
+ if (Keyboard::KeyDown(VK_ESCAPE)) {
+ if (!exit_latch)
+ OnApply(0);
+
+ exit_latch = true;
+ }
+ else {
+ exit_latch = false;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdMsgDlg::Show()
+{
+ FormWindow::Show();
+ SetFocus();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdMsgDlg::OnApply(AWEvent* event)
+{
+ if (manager)
+ manager->CloseTopmost();
+}
+
+// +--------------------------------------------------------------------+
diff --git a/Stars45/CmdMsgDlg.h b/Stars45/CmdMsgDlg.h
new file mode 100644
index 0000000..e089097
--- /dev/null
+++ b/Stars45/CmdMsgDlg.h
@@ -0,0 +1,54 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmdMsgDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Navigation Active Window class
+*/
+
+#ifndef CmdMsgDlg_h
+#define CmdMsgDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+
+// +--------------------------------------------------------------------+
+
+class CmpnScreen;
+
+// +--------------------------------------------------------------------+
+
+class CmdMsgDlg : public FormWindow
+{
+public:
+ CmdMsgDlg(Screen* s, FormDef& def, CmpnScreen* mgr);
+ virtual ~CmdMsgDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+
+ // Operations:
+ virtual void ExecFrame();
+ virtual void OnApply(AWEvent* event);
+
+ ActiveWindow* Title() { return title; }
+ ActiveWindow* Message() { return message; }
+
+protected:
+ CmpnScreen* manager;
+
+ ActiveWindow* title;
+ ActiveWindow* message;
+
+ Button* apply;
+ bool exit_latch;
+};
+
+#endif CmdMsgDlg_h
+
diff --git a/Stars45/CmdOrdersDlg.cpp b/Stars45/CmdOrdersDlg.cpp
new file mode 100644
index 0000000..c874d22
--- /dev/null
+++ b/Stars45/CmdOrdersDlg.cpp
@@ -0,0 +1,139 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmdOrdersDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Operational Command Dialog (Campaign Orders Tab)
+*/
+
+#include "MemDebug.h"
+#include "CmdOrdersDlg.h"
+#include "CmdDlg.h"
+#include "CmpnScreen.h"
+#include "Starshatter.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "ShipDesign.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(CmdOrdersDlg, OnMode);
+DEF_MAP_CLIENT(CmdOrdersDlg, OnSave);
+DEF_MAP_CLIENT(CmdOrdersDlg, OnExit);
+
+// +--------------------------------------------------------------------+
+
+CmdOrdersDlg::CmdOrdersDlg(Screen* s, FormDef& def, CmpnScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), CmdDlg(mgr), manager(mgr),
+ lbl_orders(0), stars(0), campaign(0)
+{
+ stars = Starshatter::GetInstance();
+ campaign = Campaign::GetCampaign();
+
+ Init(def);
+}
+
+CmdOrdersDlg::~CmdOrdersDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdOrdersDlg::RegisterControls()
+{
+ lbl_orders = FindControl(400);
+
+ RegisterCmdControls(this);
+
+ if (btn_save)
+ REGISTER_CLIENT(EID_CLICK, btn_save, CmdOrdersDlg, OnSave);
+
+ if (btn_exit)
+ REGISTER_CLIENT(EID_CLICK, btn_exit, CmdOrdersDlg, OnExit);
+
+ for (int i = 0; i < 5; i++) {
+ if (btn_mode[i])
+ REGISTER_CLIENT(EID_CLICK, btn_mode[i], CmdOrdersDlg, OnMode);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdOrdersDlg::Show()
+{
+ mode = MODE_ORDERS;
+
+ FormWindow::Show();
+ ShowCmdDlg();
+
+ campaign = Campaign::GetCampaign();
+
+ if (campaign && lbl_orders) {
+ Text orders("<font Limerick12><color ffff80>");
+ orders += Game::GetText("CmdOrdersDlg.situation");
+ orders += "\n<font Verdana><color ffffff>";
+ if (*campaign->Situation())
+ orders += campaign->Situation();
+ else
+ orders += campaign->Description();
+
+ orders += "\n\n<font Limerick12><color ffff80>";
+ orders += Game::GetText("CmdOrdersDlg.orders");
+ orders += "\n<font Verdana><color ffffff>";
+ orders += campaign->Orders();
+
+ lbl_orders->SetText(orders);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdOrdersDlg::ExecFrame()
+{
+ CmdDlg::ExecFrame();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdOrdersDlg::OnSave(AWEvent* event)
+{
+ CmdDlg::OnSave(event);
+}
+
+void
+CmdOrdersDlg::OnExit(AWEvent* event)
+{
+ CmdDlg::OnExit(event);
+}
+
+void
+CmdOrdersDlg::OnMode(AWEvent* event)
+{
+ CmdDlg::OnMode(event);
+}
+
diff --git a/Stars45/CmdOrdersDlg.h b/Stars45/CmdOrdersDlg.h
new file mode 100644
index 0000000..41ff736
--- /dev/null
+++ b/Stars45/CmdOrdersDlg.h
@@ -0,0 +1,56 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmdOrdersDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Operational Command Dialog (Campaign Orders Tab)
+*/
+
+#ifndef CmdOrdersDlg_h
+#define CmdOrdersDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "CmdDlg.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class CmdOrdersDlg : public FormWindow,
+ public CmdDlg
+{
+public:
+ CmdOrdersDlg(Screen* s, FormDef& def, CmpnScreen* mgr);
+ virtual ~CmdOrdersDlg();
+
+ virtual void RegisterControls();
+ virtual void ExecFrame();
+ virtual void Show();
+
+ // Operations:
+ virtual void OnMode(AWEvent* event);
+ virtual void OnSave(AWEvent* event);
+ virtual void OnExit(AWEvent* event);
+
+protected:
+ CmpnScreen* manager;
+
+ ActiveWindow* lbl_orders;
+
+ Starshatter* stars;
+ Campaign* campaign;
+};
+
+#endif CmdOrdersDlg_h
+
diff --git a/Stars45/CmdTheaterDlg.cpp b/Stars45/CmdTheaterDlg.cpp
new file mode 100644
index 0000000..74d351d
--- /dev/null
+++ b/Stars45/CmdTheaterDlg.cpp
@@ -0,0 +1,216 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmdTheaterDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Operational Command Dialog (Theater Map Tab)
+*/
+
+#include "MemDebug.h"
+#include "CmdTheaterDlg.h"
+#include "CmdDlg.h"
+#include "CmpnScreen.h"
+#include "Galaxy.h"
+#include "Starshatter.h"
+#include "StarSystem.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "ShipDesign.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(CmdTheaterDlg, OnMode);
+DEF_MAP_CLIENT(CmdTheaterDlg, OnSave);
+DEF_MAP_CLIENT(CmdTheaterDlg, OnExit);
+DEF_MAP_CLIENT(CmdTheaterDlg, OnView);
+
+// Supported Selection Modes:
+
+const int SELECT_NONE = -1;
+const int SELECT_SYSTEM = 0;
+const int SELECT_PLANET = 1;
+const int SELECT_REGION = 2;
+const int SELECT_STATION = 3;
+const int SELECT_STARSHIP = 4;
+const int SELECT_FIGHTER = 5;
+
+const int VIEW_GALAXY = 0;
+const int VIEW_SYSTEM = 1;
+const int VIEW_REGION = 2;
+
+// +--------------------------------------------------------------------+
+
+CmdTheaterDlg::CmdTheaterDlg(Screen* s, FormDef& def, CmpnScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), CmdDlg(mgr), manager(mgr),
+ map_theater(0), map_view(0), stars(0), campaign(0)
+{
+ stars = Starshatter::GetInstance();
+ campaign = Campaign::GetCampaign();
+
+ Init(def);
+}
+
+CmdTheaterDlg::~CmdTheaterDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdTheaterDlg::RegisterControls()
+{
+ map_theater = FindControl(400);
+
+ RegisterCmdControls(this);
+
+ if (btn_save)
+ REGISTER_CLIENT(EID_CLICK, btn_save, CmdTheaterDlg, OnSave);
+
+ if (btn_exit)
+ REGISTER_CLIENT(EID_CLICK, btn_exit, CmdTheaterDlg, OnExit);
+
+ for (int i = 0; i < 5; i++) {
+ if (btn_mode[i])
+ REGISTER_CLIENT(EID_CLICK, btn_mode[i], CmdTheaterDlg, OnMode);
+ }
+
+ if (map_theater)
+ map_view = new(__FILE__,__LINE__) MapView(map_theater);
+
+ for (i = 0; i < 3; i++) {
+ view_btn[i] = (Button*) FindControl(401 + i);
+ REGISTER_CLIENT(EID_CLICK, view_btn[i], CmdTheaterDlg, OnView);
+ }
+
+ zoom_in_btn = (Button*) FindControl(410);
+ zoom_out_btn = (Button*) FindControl(411);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdTheaterDlg::Show()
+{
+ mode = MODE_THEATER;
+
+ FormWindow::Show();
+ ShowCmdDlg();
+
+ campaign = Campaign::GetCampaign();
+
+ if (campaign && map_theater) {
+ map_view->SetCampaign(campaign);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdTheaterDlg::ExecFrame()
+{
+ CmdDlg::ExecFrame();
+
+ if (!map_view)
+ return;
+
+ if (Keyboard::KeyDown(VK_ADD) ||
+ (zoom_in_btn && zoom_in_btn->GetButtonState() > 0)) {
+ map_view->ZoomIn();
+ }
+ else if (Keyboard::KeyDown(VK_SUBTRACT) ||
+ (zoom_out_btn && zoom_out_btn->GetButtonState() > 0)) {
+ map_view->ZoomOut();
+ }
+
+ else if (Mouse::Wheel() > 0) {
+ map_view->ZoomIn();
+ map_view->ZoomIn();
+ map_view->ZoomIn();
+ }
+
+ else if (Mouse::Wheel() < 0) {
+ map_view->ZoomOut();
+ map_view->ZoomOut();
+ map_view->ZoomOut();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdTheaterDlg::OnSave(AWEvent* event)
+{
+ CmdDlg::OnSave(event);
+}
+
+void
+CmdTheaterDlg::OnExit(AWEvent* event)
+{
+ CmdDlg::OnExit(event);
+}
+
+void
+CmdTheaterDlg::OnMode(AWEvent* event)
+{
+ CmdDlg::OnMode(event);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdTheaterDlg::OnView(AWEvent* event)
+{
+ int use_filter_mode = -1;
+
+ view_btn[VIEW_GALAXY]->SetButtonState(0);
+ view_btn[VIEW_SYSTEM]->SetButtonState(0);
+ view_btn[VIEW_REGION]->SetButtonState(0);
+
+ if (view_btn[0] == event->window) {
+ if (map_view) map_view->SetViewMode(VIEW_GALAXY);
+ view_btn[VIEW_GALAXY]->SetButtonState(1);
+ use_filter_mode = SELECT_SYSTEM;
+ }
+
+ else if (view_btn[VIEW_SYSTEM] == event->window) {
+ if (map_view) map_view->SetViewMode(VIEW_SYSTEM);
+ view_btn[VIEW_SYSTEM]->SetButtonState(1);
+ use_filter_mode = SELECT_REGION;
+ }
+
+ else if (view_btn[VIEW_REGION] == event->window) {
+ if (map_view) map_view->SetViewMode(VIEW_REGION);
+ view_btn[VIEW_REGION]->SetButtonState(1);
+ use_filter_mode = SELECT_STARSHIP;
+ }
+
+ if (use_filter_mode >= 0) {
+ if (map_view) map_view->SetSelectionMode(use_filter_mode);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+
+
diff --git a/Stars45/CmdTheaterDlg.h b/Stars45/CmdTheaterDlg.h
new file mode 100644
index 0000000..d549ca4
--- /dev/null
+++ b/Stars45/CmdTheaterDlg.h
@@ -0,0 +1,62 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmdTheaterDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Operational Command Dialog (Order of Battle Tab)
+*/
+
+#ifndef CmdTheaterDlg_h
+#define CmdTheaterDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "CmdDlg.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "MapView.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class CmdTheaterDlg : public FormWindow,
+ public CmdDlg
+{
+public:
+ CmdTheaterDlg(Screen* s, FormDef& def, CmpnScreen* mgr);
+ virtual ~CmdTheaterDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnMode(AWEvent* event);
+ virtual void OnSave(AWEvent* event);
+ virtual void OnExit(AWEvent* event);
+ virtual void OnView(AWEvent* event);
+
+protected:
+ CmpnScreen* manager;
+
+ ActiveWindow* map_theater;
+ MapView* map_view;
+ Button* view_btn[3];
+ Button* zoom_in_btn;
+ Button* zoom_out_btn;
+
+ Starshatter* stars;
+ Campaign* campaign;
+};
+
+#endif CmdTheaterDlg_h
+
diff --git a/Stars45/CmdTitleDlg.cpp b/Stars45/CmdTitleDlg.cpp
new file mode 100644
index 0000000..58ae5fc
--- /dev/null
+++ b/Stars45/CmdTitleDlg.cpp
@@ -0,0 +1,78 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmdTitleDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Operational Command Dialog (Intel/Newsfeed Tab)
+*/
+
+#include "MemDebug.h"
+#include "CmdTitleDlg.h"
+#include "CmpnScreen.h"
+#include "Starshatter.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatEvent.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "ShipDesign.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+// +--------------------------------------------------------------------+
+
+CmdTitleDlg::CmdTitleDlg(Screen* s, FormDef& def, CmpnScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ stars(0), campaign(0), showTime(0)
+{
+ stars = Starshatter::GetInstance();
+ campaign = Campaign::GetCampaign();
+
+ Init(def);
+}
+
+CmdTitleDlg::~CmdTitleDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdTitleDlg::RegisterControls()
+{
+ img_title = (ImageBox*) FindControl(200);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdTitleDlg::Show()
+{
+ FormWindow::Show();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmdTitleDlg::ExecFrame()
+{
+}
diff --git a/Stars45/CmdTitleDlg.h b/Stars45/CmdTitleDlg.h
new file mode 100644
index 0000000..70a151d
--- /dev/null
+++ b/Stars45/CmdTitleDlg.h
@@ -0,0 +1,56 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmdTitleDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Campaign Title Card
+*/
+
+#ifndef CmdTitleDlg_h
+#define CmdTitleDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ImageBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class CmpnScreen;
+class Campaign;
+class Starshatter;
+
+// +--------------------------------------------------------------------+
+
+class CmdTitleDlg : public FormWindow
+{
+public:
+ CmdTitleDlg(Screen* s, FormDef& def, CmpnScreen* mgr);
+ virtual ~CmdTitleDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+protected:
+ CmpnScreen* manager;
+
+ ImageBox* img_title;
+
+ Starshatter* stars;
+ Campaign* campaign;
+ double showTime;
+};
+
+#endif CmdTitleDlg_h
+
diff --git a/Stars45/CmpCompleteDlg.cpp b/Stars45/CmpCompleteDlg.cpp
new file mode 100644
index 0000000..0e47a89
--- /dev/null
+++ b/Stars45/CmpCompleteDlg.cpp
@@ -0,0 +1,108 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmpCompleteDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "CmpCompleteDlg.h"
+#include "CmpnScreen.h"
+#include "Campaign.h"
+#include "CombatEvent.h"
+#include "Starshatter.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ImageBox.h"
+#include "Button.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(CmpCompleteDlg, OnClose);
+
+// +--------------------------------------------------------------------+
+
+CmpCompleteDlg::CmpCompleteDlg(Screen* s, FormDef& def, CmpnScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ lbl_info(0), img_title(0), btn_close(0)
+{
+ Init(def);
+}
+
+CmpCompleteDlg::~CmpCompleteDlg()
+{
+}
+
+void
+CmpCompleteDlg::RegisterControls()
+{
+ img_title = (ImageBox*) FindControl(100);
+ lbl_info = FindControl(101);
+ btn_close = (Button*) FindControl(1);
+
+ REGISTER_CLIENT(EID_CLICK, btn_close, CmpCompleteDlg, OnClose);
+}
+
+void
+CmpCompleteDlg::Show()
+{
+ FormWindow::Show();
+
+ Campaign* c = Campaign::GetCampaign();
+
+ if (img_title && c) {
+ DataLoader* loader = DataLoader::GetLoader();
+ Starshatter* stars = Starshatter::GetInstance();
+ CombatEvent* event = c->GetLastEvent();
+ char img_name[256];
+
+ if (event) {
+ strcpy(img_name, event->ImageFile());
+
+ if (!strstr(img_name, ".pcx")) {
+ strcat(img_name, ".pcx");
+ }
+
+ if (loader) {
+ loader->SetDataPath(c->Path());
+ loader->LoadBitmap(img_name, banner);
+ loader->SetDataPath(0);
+
+ Rect tgt_rect;
+ tgt_rect.w = img_title->Width();
+ tgt_rect.h = img_title->Height();
+
+ img_title->SetTargetRect(tgt_rect);
+ img_title->SetPicture(banner);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpCompleteDlg::ExecFrame()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpCompleteDlg::OnClose(AWEvent* event)
+{
+ if (manager)
+ manager->ShowCmdDlg();
+}
diff --git a/Stars45/CmpCompleteDlg.h b/Stars45/CmpCompleteDlg.h
new file mode 100644
index 0000000..a32a712
--- /dev/null
+++ b/Stars45/CmpCompleteDlg.h
@@ -0,0 +1,50 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmpCompleteDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Campaign title card and load progress dialog
+*/
+
+#ifndef CmpCompleteDlg_h
+#define CmpCompleteDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+
+// +--------------------------------------------------------------------+
+
+class CmpnScreen;
+
+// +--------------------------------------------------------------------+
+
+class CmpCompleteDlg : public FormWindow
+{
+public:
+ CmpCompleteDlg(Screen* s, FormDef& def, CmpnScreen* mgr);
+ virtual ~CmpCompleteDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+
+ // Operations:
+ virtual void ExecFrame();
+ virtual void OnClose(AWEvent* event);
+
+protected:
+ ImageBox* img_title;
+ ActiveWindow* lbl_info;
+ Button* btn_close;
+ Bitmap banner;
+
+ CmpnScreen* manager;
+};
+
+#endif CmpCompleteDlg_h
+
diff --git a/Stars45/CmpFileDlg.cpp b/Stars45/CmpFileDlg.cpp
new file mode 100644
index 0000000..22a093d
--- /dev/null
+++ b/Stars45/CmpFileDlg.cpp
@@ -0,0 +1,191 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmpFileDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Select Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "CmpFileDlg.h"
+#include "CmpnScreen.h"
+#include "Starshatter.h"
+#include "Campaign.h"
+#include "CampaignSaveGame.h"
+#include "CombatGroup.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "EditBox.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "FormatUtil.h"
+#include "ParseUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(CmpFileDlg, OnSave);
+DEF_MAP_CLIENT(CmpFileDlg, OnCancel);
+DEF_MAP_CLIENT(CmpFileDlg, OnCampaign);
+
+// +--------------------------------------------------------------------+
+
+CmpFileDlg::CmpFileDlg(Screen* s, FormDef& def, CmpnScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ exit_latch(false), btn_save(0), btn_cancel(0), edt_name(0), lst_campaigns(0)
+{
+ Init(def);
+}
+
+CmpFileDlg::~CmpFileDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpFileDlg::RegisterControls()
+{
+ btn_save = (Button*) FindControl(1);
+ btn_cancel = (Button*) FindControl(2);
+
+ if (btn_save)
+ REGISTER_CLIENT(EID_CLICK, btn_save, CmpFileDlg, OnSave);
+
+ if (btn_cancel)
+ REGISTER_CLIENT(EID_CLICK, btn_cancel, CmpFileDlg, OnCancel);
+
+ edt_name = (EditBox*) FindControl(200);
+
+ if (edt_name)
+ edt_name->SetText("");
+
+ lst_campaigns = (ListBox*) FindControl(201);
+
+ if (lst_campaigns)
+ REGISTER_CLIENT(EID_SELECT, lst_campaigns, CmpFileDlg, OnCampaign);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpFileDlg::Show()
+{
+ FormWindow::Show();
+
+ if (lst_campaigns) {
+ lst_campaigns->ClearItems();
+ lst_campaigns->SetLineHeight(12);
+
+ List<Text> save_list;
+
+ CampaignSaveGame::GetSaveGameList(save_list);
+ save_list.sort();
+
+ for (int i = 0; i < save_list.size(); i++)
+ lst_campaigns->AddItem(*save_list[i]);
+
+ save_list.destroy();
+ }
+
+ if (edt_name) {
+ char save_name[256];
+ save_name[0] = 0;
+
+ campaign = Campaign::GetCampaign();
+ if (campaign && campaign->GetPlayerGroup()) {
+ const char* op_name = campaign->Name();
+ char day[32];
+ CombatGroup* group = campaign->GetPlayerGroup();
+
+ if (strstr(op_name, "Operation "))
+ op_name += 10;
+
+ FormatDay(day, campaign->GetTime());
+
+ sprintf(save_name, "%s %s (%s)",
+ op_name,
+ day,
+ group->GetRegion().data());
+ }
+
+ edt_name->SetText(save_name);
+ edt_name->SetFocus();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpFileDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ OnSave(0);
+ }
+
+ if (Keyboard::KeyDown(VK_ESCAPE)) {
+ if (!exit_latch)
+ OnCancel(0);
+
+ exit_latch = true;
+ }
+ else {
+ exit_latch = false;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpFileDlg::OnSave(AWEvent* event)
+{
+ if (edt_name && edt_name->GetText().length() > 0) {
+ campaign = Campaign::GetCampaign();
+ CampaignSaveGame save(campaign);
+
+ char filename[256];
+ strcpy(filename, edt_name->GetText());
+ char* newline = strchr(filename, '\n');
+ if (newline)
+ *newline = 0;
+
+ save.Save(filename);
+ save.SaveAuto();
+
+ if (manager)
+ manager->HideCmpFileDlg();
+ }
+}
+
+void
+CmpFileDlg::OnCancel(AWEvent* event)
+{
+ if (manager)
+ manager->HideCmpFileDlg();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpFileDlg::OnCampaign(AWEvent* event)
+{
+ int n = lst_campaigns->GetSelection();
+
+ if (n >= 0) {
+ Text cmpn = lst_campaigns->GetItemText(n);
+
+ if (edt_name)
+ edt_name->SetText(cmpn);
+ }
+} \ No newline at end of file
diff --git a/Stars45/CmpFileDlg.h b/Stars45/CmpFileDlg.h
new file mode 100644
index 0000000..f3b19cd
--- /dev/null
+++ b/Stars45/CmpFileDlg.h
@@ -0,0 +1,64 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmpFileDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Select Dialog Active Window class
+*/
+
+#ifndef CmpFileDlg_h
+#define CmpFileDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class CmpnScreen;
+class Campaign;
+class Starshatter;
+
+// +--------------------------------------------------------------------+
+
+class CmpFileDlg : public FormWindow
+{
+public:
+ CmpFileDlg(Screen* s, FormDef& def, CmpnScreen* mgr);
+ virtual ~CmpFileDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+
+ // Operations:
+ virtual void ExecFrame();
+
+ virtual void OnSave(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+ virtual void OnCampaign(AWEvent* event);
+
+protected:
+ CmpnScreen* manager;
+ Campaign* campaign;
+
+ Button* btn_save;
+ Button* btn_cancel;
+ EditBox* edt_name;
+ ListBox* lst_campaigns;
+
+ bool exit_latch;
+};
+
+#endif CmpFileDlg_h
+
diff --git a/Stars45/CmpLoadDlg.cpp b/Stars45/CmpLoadDlg.cpp
new file mode 100644
index 0000000..8bcf422
--- /dev/null
+++ b/Stars45/CmpLoadDlg.cpp
@@ -0,0 +1,118 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmpLoadDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "CmpLoadDlg.h"
+#include "Campaign.h"
+#include "Starshatter.h"
+#include "FormatUtil.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ImageBox.h"
+#include "Slider.h"
+
+// +--------------------------------------------------------------------+
+
+CmpLoadDlg::CmpLoadDlg(Screen* s, FormDef& def)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()),
+ lbl_progress(0), lbl_activity(0), lbl_title(0), img_title(0), show_time(0)
+{
+ Init(def);
+}
+
+CmpLoadDlg::~CmpLoadDlg()
+{
+}
+
+void
+CmpLoadDlg::RegisterControls()
+{
+ img_title = (ImageBox*) FindControl(100);
+ lbl_title = FindControl(200);
+ lbl_activity = FindControl(101);
+ lbl_progress = (Slider*) FindControl(102);
+}
+
+void
+CmpLoadDlg::Show()
+{
+ FormWindow::Show();
+
+ Campaign* campaign = Campaign::GetCampaign();
+
+ if (campaign) {
+ Bitmap* bmp = campaign->GetImage(3);
+ if (img_title && bmp) {
+ Rect tgt_rect;
+ tgt_rect.w = img_title->Width();
+ tgt_rect.h = img_title->Height();
+
+ img_title->SetTargetRect(tgt_rect);
+ img_title->SetPicture(*bmp);
+ }
+
+ if (lbl_title)
+ lbl_title->SetText(campaign->Name());
+ }
+
+ show_time = Game::RealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpLoadDlg::ExecFrame()
+{
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars) {
+ if (lbl_activity) lbl_activity->SetText(stars->GetLoadActivity());
+ if (lbl_progress) lbl_progress->SetValue(stars->GetLoadProgress());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpLoadDlg::MoveTo(const Rect& r)
+{
+ FormWindow::MoveTo(r);
+
+ Campaign* campaign = Campaign::GetCampaign();
+
+ if (campaign && img_title && campaign->GetImage(3)) {
+ Bitmap* bmp = campaign->GetImage(3);
+
+ Rect tgt_rect;
+ tgt_rect.w = img_title->Width();
+ tgt_rect.h = img_title->Height();
+
+ img_title->SetTargetRect(tgt_rect);
+ img_title->SetPicture(*bmp);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CmpLoadDlg::IsDone()
+{
+ if (Game::RealTime() - show_time < 5000)
+ return false;
+
+ return true;
+}
diff --git a/Stars45/CmpLoadDlg.h b/Stars45/CmpLoadDlg.h
new file mode 100644
index 0000000..64dd538
--- /dev/null
+++ b/Stars45/CmpLoadDlg.h
@@ -0,0 +1,46 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmpLoadDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Campaign title card and load progress dialog
+*/
+
+#ifndef CmpLoadDlg_h
+#define CmpLoadDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+
+// +--------------------------------------------------------------------+
+
+class CmpLoadDlg : public FormWindow
+{
+public:
+ CmpLoadDlg(Screen* s, FormDef& def);
+ virtual ~CmpLoadDlg();
+
+ // Operations:
+ virtual void ExecFrame();
+ virtual void MoveTo(const Rect& r);
+ virtual void RegisterControls();
+ virtual void Show();
+
+ virtual bool IsDone();
+
+protected:
+ ActiveWindow* lbl_activity;
+ Slider* lbl_progress;
+ ActiveWindow* lbl_title;
+ ImageBox* img_title;
+ DWORD show_time;
+};
+
+#endif CmpLoadDlg_h
+
diff --git a/Stars45/CmpSceneDlg.cpp b/Stars45/CmpSceneDlg.cpp
new file mode 100644
index 0000000..ccc8a0d
--- /dev/null
+++ b/Stars45/CmpSceneDlg.cpp
@@ -0,0 +1,188 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmpSceneDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "CmpSceneDlg.h"
+#include "CmpnScreen.h"
+#include "GameScreen.h"
+#include "Campaign.h"
+#include "CombatEvent.h"
+#include "Starshatter.h"
+#include "Sim.h"
+#include "CameraDirector.h"
+#include "Mission.h"
+#include "MissionEvent.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ImageBox.h"
+#include "RichTextBox.h"
+#include "Button.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+// +--------------------------------------------------------------------+
+
+CmpSceneDlg::CmpSceneDlg(Screen* s, FormDef& def, CmpnScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ mov_scene(0), subtitles_box(0), cam_view(0), disp_view(0), old_disp_win(0),
+ flare1(0), flare2(0), flare3(0), flare4(0), subtitles_delay(0), subtitles_time(0)
+{
+ Init(def);
+
+ DataLoader* loader = DataLoader::GetLoader();
+ const char* oldpath = loader->GetDataPath();
+
+ loader->SetDataPath(0);
+ loader->LoadTexture("flare0+.pcx", flare1, Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("flare2.pcx", flare2, Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("flare3.pcx", flare3, Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("flare4.pcx", flare4, Bitmap::BMP_TRANSLUCENT);
+ loader->SetDataPath(oldpath);
+}
+
+CmpSceneDlg::~CmpSceneDlg()
+{
+}
+
+void
+CmpSceneDlg::RegisterControls()
+{
+ mov_scene = FindControl(101);
+ subtitles_box = (RichTextBox*) FindControl(102);
+
+ if (mov_scene) {
+ CameraDirector* cam_dir = CameraDirector::GetInstance();
+ cam_view = new(__FILE__,__LINE__) CameraView(mov_scene, cam_dir->GetCamera(), 0);
+ if (cam_view)
+ mov_scene->AddView(cam_view);
+
+ disp_view = DisplayView::GetInstance();
+ }
+}
+
+void
+CmpSceneDlg::Show()
+{
+ FormWindow::Show();
+
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars->InCutscene()) {
+ Sim* sim = Sim::GetSim();
+
+ if (sim) {
+ cam_view->UseCamera(CameraDirector::GetInstance()->GetCamera());
+ cam_view->UseScene(sim->GetScene());
+ }
+
+ // initialize lens flare bitmaps:
+ if (stars->LensFlare()) {
+ cam_view->LensFlareElements(flare1, flare4, flare2, flare3);
+ cam_view->LensFlare(true);
+ }
+
+ // if lens flare disabled, just create the corona:
+ else if (stars->Corona()) {
+ cam_view->LensFlareElements(flare1, 0, 0, 0);
+ cam_view->LensFlare(true);
+ }
+
+ if (disp_view) {
+ old_disp_win = disp_view->GetWindow();
+
+ disp_view->SetWindow(mov_scene);
+ mov_scene->AddView(disp_view);
+ }
+
+ if (subtitles_box) {
+ subtitles_box->SetText(stars->GetSubtitles());
+ subtitles_delay = 0;
+ subtitles_time = 0;
+ }
+ }
+}
+
+void
+CmpSceneDlg::Hide()
+{
+ FormWindow::Hide();
+
+ if (disp_view && mov_scene && old_disp_win) {
+ mov_scene->DelView(disp_view);
+ disp_view->SetWindow(old_disp_win);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+CameraView*
+CmpSceneDlg::GetCameraView()
+{
+ return cam_view;
+}
+
+DisplayView*
+CmpSceneDlg::GetDisplayView()
+{
+ return disp_view;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpSceneDlg::ExecFrame()
+{
+ Starshatter* stars = Starshatter::GetInstance();
+ Mission* cutscene_mission = stars->GetCutsceneMission();
+
+ if (cutscene_mission && disp_view) {
+ disp_view->ExecFrame();
+
+ if (subtitles_box && subtitles_box->GetLineCount() > 0) {
+ if (subtitles_delay == 0) {
+ int nlines = subtitles_box->GetLineCount();
+
+ MissionEvent* begin_scene = cutscene_mission->FindEvent(MissionEvent::BEGIN_SCENE);
+ MissionEvent* end_scene = cutscene_mission->FindEvent(MissionEvent::END_SCENE);
+
+ if (begin_scene && end_scene) {
+ double total_time = end_scene->Time() - begin_scene->Time();
+ subtitles_delay = total_time / nlines;
+ subtitles_time = Game::RealTime() / 1000.0 + subtitles_delay;
+ }
+ else {
+ subtitles_delay = -1;
+ }
+ }
+
+ if (subtitles_delay > 0) {
+ double seconds = Game::RealTime() / 1000.0;
+
+ if (subtitles_time <= seconds) {
+ subtitles_time = seconds + subtitles_delay;
+ subtitles_box->Scroll(ScrollWindow::SCROLL_DOWN);
+ }
+ }
+ }
+ }
+ else {
+ manager->ShowCmdDlg();
+ }
+}
+
diff --git a/Stars45/CmpSceneDlg.h b/Stars45/CmpSceneDlg.h
new file mode 100644
index 0000000..86ff8d6
--- /dev/null
+++ b/Stars45/CmpSceneDlg.h
@@ -0,0 +1,68 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmpSceneDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Campaign title card and load progress dialog
+*/
+
+#ifndef CmpSceneDlg_h
+#define CmpSceneDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "CameraView.h"
+#include "DisplayView.h"
+
+#include "ImageBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class CmpnScreen;
+
+// +--------------------------------------------------------------------+
+
+class CmpSceneDlg : public FormWindow
+{
+public:
+ CmpSceneDlg(Screen* s, FormDef& def, CmpnScreen* mgr);
+ virtual ~CmpSceneDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void Hide();
+
+ // Operations:
+ virtual void ExecFrame();
+
+ CameraView* GetCameraView();
+ DisplayView* GetDisplayView();
+
+protected:
+ ActiveWindow* mov_scene;
+ RichTextBox* subtitles_box;
+ CameraView* cam_view;
+ DisplayView* disp_view;
+ Window* old_disp_win;
+
+ Bitmap* flare1;
+ Bitmap* flare2;
+ Bitmap* flare3;
+ Bitmap* flare4;
+
+ CmpnScreen* manager;
+
+ double subtitles_delay;
+ double subtitles_time;
+};
+
+#endif CmpSceneDlg_h
+
diff --git a/Stars45/CmpSelectDlg.cpp b/Stars45/CmpSelectDlg.cpp
new file mode 100644
index 0000000..39b5923
--- /dev/null
+++ b/Stars45/CmpSelectDlg.cpp
@@ -0,0 +1,623 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmpSelectDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Select Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "CmpSelectDlg.h"
+#include "ConfirmDlg.h"
+#include "MenuScreen.h"
+#include "Starshatter.h"
+#include "Campaign.h"
+#include "CampaignSaveGame.h"
+#include "CombatGroup.h"
+#include "ShipDesign.h"
+#include "Player.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(CmpSelectDlg, OnNew);
+DEF_MAP_CLIENT(CmpSelectDlg, OnSaved);
+DEF_MAP_CLIENT(CmpSelectDlg, OnDelete);
+DEF_MAP_CLIENT(CmpSelectDlg, OnConfirmDelete);
+DEF_MAP_CLIENT(CmpSelectDlg, OnAccept);
+DEF_MAP_CLIENT(CmpSelectDlg, OnCancel);
+DEF_MAP_CLIENT(CmpSelectDlg, OnCampaignSelect);
+
+// +--------------------------------------------------------------------+
+
+CmpSelectDlg::CmpSelectDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ lst_campaigns(0), btn_new(0), btn_saved(0), btn_delete(0),
+ btn_accept(0), btn_cancel(0), description(0), stars(0), campaign(0),
+ selected_mission(-1), show_saved(false), loading(false),
+ loaded(false), hproc(0)
+{
+ stars = Starshatter::GetInstance();
+ select_msg = Game::GetText("CmpSelectDlg.select_msg");
+ Init(def);
+}
+
+CmpSelectDlg::~CmpSelectDlg()
+{
+ StopLoadProc();
+ images.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpSelectDlg::RegisterControls()
+{
+ btn_new = (Button*) FindControl(100);
+ btn_saved = (Button*) FindControl(101);
+ btn_delete = (Button*) FindControl(102);
+ btn_accept = (Button*) FindControl( 1);
+ btn_cancel = (Button*) FindControl( 2);
+
+ if (btn_new)
+ REGISTER_CLIENT(EID_CLICK, btn_new, CmpSelectDlg, OnNew);
+
+ if (btn_saved)
+ REGISTER_CLIENT(EID_CLICK, btn_saved, CmpSelectDlg, OnSaved);
+
+ if (btn_delete) {
+ btn_delete->SetEnabled(false);
+ REGISTER_CLIENT(EID_CLICK, btn_delete, CmpSelectDlg, OnDelete);
+ REGISTER_CLIENT(EID_USER_1, btn_delete, CmpSelectDlg, OnConfirmDelete);
+ }
+
+ if (btn_accept) {
+ btn_accept->SetEnabled(false);
+ REGISTER_CLIENT(EID_CLICK, btn_accept, CmpSelectDlg, OnAccept);
+ }
+
+ if (btn_cancel) {
+ btn_cancel->SetEnabled(true);
+ REGISTER_CLIENT(EID_CLICK, btn_cancel, CmpSelectDlg, OnCancel);
+ }
+
+ description = FindControl(200);
+
+ lst_campaigns = (ListBox*) FindControl(201);
+
+ if (lst_campaigns)
+ REGISTER_CLIENT(EID_SELECT, lst_campaigns, CmpSelectDlg, OnCampaignSelect);
+
+ ShowNewCampaigns();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpSelectDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ if (btn_accept && btn_accept->IsEnabled())
+ OnAccept(0);
+ }
+
+ AutoThreadSync a(sync);
+
+ if (loaded) {
+ loaded = false;
+
+ if (btn_cancel)
+ btn_cancel->SetEnabled(true);
+
+ if (description && btn_accept) {
+ if (campaign) {
+ Campaign::SelectCampaign(campaign->Name());
+
+ if (load_index >= 0) {
+ if (lst_campaigns) {
+ images[load_index]->CopyBitmap(*campaign->GetImage(1));
+ lst_campaigns->SetItemImage(load_index, images[load_index]);
+ }
+
+ description->SetText(Text("<font Limerick12><color ffffff>") +
+ campaign->Name() +
+ Text("<font Verdana>\n\n") +
+ Text("<color ffff80>") +
+ Game::GetText("CmpSelectDlg.scenario") +
+ Text("<color ffffff>\n\t") +
+ campaign->Description());
+ }
+ else {
+ char time_buf[32];
+ char score_buf[32];
+
+ double t = campaign->GetLoadTime() - campaign->GetStartTime();
+ FormatDayTime(time_buf, t);
+
+ sprintf(score_buf, "%d", campaign->GetPlayerTeamScore());
+
+ Text desc = Text("<font Limerick12><color ffffff>") +
+ campaign->Name() +
+ Text("<font Verdana>\n\n") +
+ Text("<color ffff80>") +
+ Game::GetText("CmpSelectDlg.scenario") +
+ Text("<color ffffff>\n\t") +
+ campaign->Description() +
+ Text("\n\n<color ffff80>") +
+ Game::GetText("CmpSelectDlg.campaign-time") +
+ Text("<color ffffff>\n\t") +
+ time_buf +
+ Text("\n\n<color ffff80>") +
+ Game::GetText("CmpSelectDlg.assignment") +
+ Text("<color ffffff>\n\t");
+
+ if (campaign->GetPlayerGroup())
+ desc += campaign->GetPlayerGroup()->GetDescription();
+ else
+ desc += "n/a";
+
+ desc += Text("\n\n<color ffff80>") +
+ Game::GetText("CmpSelectDlg.team-score") +
+ Text("<color ffffff>\n\t") +
+ score_buf;
+
+ description->SetText(desc);
+ }
+
+ btn_accept->SetEnabled(true);
+
+ if (btn_delete)
+ btn_delete->SetEnabled(show_saved);
+ }
+ else {
+ description->SetText(select_msg);
+ btn_accept->SetEnabled(true);
+ }
+ }
+ }
+}
+
+bool
+CmpSelectDlg::CanClose()
+{
+ AutoThreadSync a(sync);
+ return !loading;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpSelectDlg::ShowNewCampaigns()
+{
+ AutoThreadSync a(sync);
+
+ if (loading && description) {
+ description->SetText(Game::GetText("CmpSelectDlg.already-loading"));
+ Button::PlaySound(Button::SND_REJECT);
+ return;
+ }
+
+ if (btn_new)
+ btn_new->SetButtonState(1);
+
+ if (btn_saved)
+ btn_saved->SetButtonState(0);
+
+ if (btn_delete)
+ btn_delete->SetEnabled(false);
+
+ if (lst_campaigns) {
+ images.destroy();
+
+ lst_campaigns->SetSelectedStyle(ListBox::LIST_ITEM_STYLE_PLAIN);
+ lst_campaigns->SetLeading(0);
+ lst_campaigns->ClearItems();
+ lst_campaigns->SetLineHeight(100);
+
+ Player* player = Player::GetCurrentPlayer();
+ if (!player) return;
+
+ ListIter<Campaign> iter = Campaign::GetAllCampaigns();
+ while (++iter) {
+ Campaign* c = iter.value();
+
+ if (c->GetCampaignId() < Campaign::SINGLE_MISSIONS) {
+ Bitmap* bmp = new(__FILE__,__LINE__) Bitmap;
+ bmp->CopyBitmap(*c->GetImage(0));
+ images.append(bmp);
+
+ int n = lst_campaigns->AddImage(bmp) - 1;
+ lst_campaigns->SetItemText(n, c->Name());
+
+ // if campaign is not available, show the grayed-out image
+
+#ifdef STARSHATTER_DEMO_RELEASE
+ // DEMO VERSION (only one campaign):
+ if (c->GetCampaignId() > 2) {
+ images[n]->CopyBitmap(*c->GetImage(2));
+ lst_campaigns->SetItemImage(n, images[n]);
+ }
+
+#else
+ // FULL GAME CRITERIA (based on player record):
+ if (c->GetCampaignId() > 2 && c->GetCampaignId() < 10 &&
+ !player->HasCompletedCampaign(c->GetCampaignId()-1)) {
+ images[n]->CopyBitmap(*c->GetImage(2));
+ lst_campaigns->SetItemImage(n, images[n]);
+ }
+
+ // Two additional sequences of ten campaigns (10-19 and 20-29)
+ // for mod authors to use:
+ else if (c->GetCampaignId() >= 10 && c->GetCampaignId() < 30 &&
+ (c->GetCampaignId() % 10) != 0 &&
+ !player->HasCompletedCampaign(c->GetCampaignId()-1)) {
+ images[n]->CopyBitmap(*c->GetImage(2));
+ lst_campaigns->SetItemImage(n, images[n]);
+ }
+
+ // NOTE: Campaigns 10, 20, and 30-99 are always enabled if they exist!
+#endif
+ }
+ }
+ }
+
+ if (description)
+ description->SetText(select_msg);
+
+ if (btn_accept)
+ btn_accept->SetEnabled(false);
+
+ show_saved = false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpSelectDlg::ShowSavedCampaigns()
+{
+ AutoThreadSync a(sync);
+
+ if (loading && description) {
+ description->SetText(Game::GetText("CmpSelectDlg.already-loading"));
+ Button::PlaySound(Button::SND_REJECT);
+ return;
+ }
+
+ if (btn_new)
+ btn_new->SetButtonState(0);
+
+ if (btn_saved)
+ btn_saved->SetButtonState(1);
+
+ if (btn_delete)
+ btn_delete->SetEnabled(false);
+
+ if (lst_campaigns) {
+ lst_campaigns->SetSelectedStyle(ListBox::LIST_ITEM_STYLE_FILLED_BOX);
+ lst_campaigns->SetLeading(4);
+ lst_campaigns->ClearItems();
+ lst_campaigns->SetLineHeight(12);
+
+ List<Text> save_list;
+
+ CampaignSaveGame::GetSaveGameList(save_list);
+ save_list.sort();
+
+ for (int i = 0; i < save_list.size(); i++)
+ lst_campaigns->AddItem(*save_list[i]);
+
+ save_list.destroy();
+ }
+
+ if (description)
+ description->SetText(select_msg);
+
+ if (btn_accept)
+ btn_accept->SetEnabled(false);
+
+ show_saved = true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpSelectDlg::OnCampaignSelect(AWEvent* event)
+{
+ if (description && lst_campaigns) {
+ AutoThreadSync a(sync);
+
+ if (loading) {
+ description->SetText(Game::GetText("CmpSelectDlg.already-loading"));
+ Button::PlaySound(Button::SND_REJECT);
+ return;
+ }
+
+ load_index = -1;
+ load_file = "";
+
+ Player* player = Player::GetCurrentPlayer();
+ if (!player) return;
+
+ // NEW CAMPAIGN:
+ if (btn_new && btn_new->GetButtonState()) {
+ List<Campaign>& list = Campaign::GetAllCampaigns();
+
+ for (int i = 0; i < lst_campaigns->NumItems(); i++) {
+ Campaign* c = list[i];
+
+ // is campaign available?
+
+#ifdef STARSHATTER_DEMO_RELEASE
+ // DEMO VERSION (only one campaign):
+ if (c->GetCampaignId() <= 2)
+#else
+ // FULL GAME CRITERIA (based on player record):
+ if (c->GetCampaignId() <= 2 ||
+ player->HasCompletedCampaign(c->GetCampaignId()-1))
+#endif
+ {
+
+ if (lst_campaigns->IsSelected(i)) {
+ images[i]->CopyBitmap(*c->GetImage(1));
+ lst_campaigns->SetItemImage(i, images[i]);
+
+ AutoThreadSync a(sync);
+ load_index = i;
+ }
+ else {
+ images[i]->CopyBitmap(*c->GetImage(0));
+ lst_campaigns->SetItemImage(i, images[i]);
+ }
+ }
+
+ // if not, then don't select
+ else {
+ images[i]->CopyBitmap(*c->GetImage(2));
+ lst_campaigns->SetItemImage(i, images[i]);
+
+ if (lst_campaigns->IsSelected(i)) {
+ description->SetText(select_msg);
+ }
+ }
+ }
+ }
+
+ // SAVED CAMPAIGN:
+ else {
+ int seln = lst_campaigns->GetSelection();
+
+ if (seln < 0) {
+ description->SetText(select_msg);
+ }
+
+ else {
+ load_index = -1;
+ load_file = lst_campaigns->GetItemText(seln);
+ }
+ }
+
+ if (btn_accept)
+ btn_accept->SetEnabled(false);
+ }
+
+ if (!loading && (load_index >= 0 || load_file.length() > 0)) {
+ if (btn_cancel)
+ btn_cancel->SetEnabled(false);
+
+ StartLoadProc();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpSelectDlg::Show()
+{
+ FormWindow::Show();
+ ShowNewCampaigns();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpSelectDlg::OnNew(AWEvent* event)
+{
+ ShowNewCampaigns();
+}
+
+void
+CmpSelectDlg::OnSaved(AWEvent* event)
+{
+ ShowSavedCampaigns();
+}
+
+void
+CmpSelectDlg::OnDelete(AWEvent* event)
+{
+ load_file = "";
+
+ if (lst_campaigns) {
+ int seln = lst_campaigns->GetSelection();
+
+ if (seln < 0) {
+ description->SetText(select_msg);
+ btn_accept->SetEnabled(false);
+ }
+
+ else {
+ load_index = -1;
+ load_file = lst_campaigns->GetItemText(seln);
+ }
+ }
+
+ if (load_file.length()) {
+ ConfirmDlg* confirm = manager->GetConfirmDlg();
+ if (confirm) {
+ char msg[256];
+ sprintf(msg, Game::GetText("CmpSelectDlg.are-you-sure"), load_file.data());
+ confirm->SetMessage(msg);
+ confirm->SetTitle(Game::GetText("CmpSelectDlg.confirm"));
+
+ manager->ShowConfirmDlg();
+ }
+
+ else {
+ OnConfirmDelete(event);
+ }
+ }
+
+ ShowSavedCampaigns();
+}
+
+void
+CmpSelectDlg::OnConfirmDelete(AWEvent* event)
+{
+ if (load_file.length()) {
+ CampaignSaveGame::Delete(load_file);
+ }
+
+ ShowSavedCampaigns();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpSelectDlg::OnAccept(AWEvent* event)
+{
+ AutoThreadSync a(sync);
+
+ if (loading)
+ return;
+
+ // if this is to be a new campaign,
+ // re-instaniate the campaign object
+ if (btn_new->GetButtonState())
+ Campaign::GetCampaign()->Load();
+ else
+ Game::ResetGameTime();
+
+ Mouse::Show(false);
+ stars->SetGameMode(Starshatter::CLOD_MODE);
+}
+
+void
+CmpSelectDlg::OnCancel(AWEvent* event)
+{
+ manager->ShowMenuDlg();
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD WINAPI CmpSelectDlgLoadProc(LPVOID link);
+
+void
+CmpSelectDlg::StartLoadProc()
+{
+ if (hproc != 0) {
+ DWORD result = 0;
+ GetExitCodeThread(hproc, &result);
+
+ if (result != STILL_ACTIVE) {
+ CloseHandle(hproc);
+ hproc = 0;
+ }
+ else {
+ return;
+ }
+ }
+
+ if (hproc == 0) {
+ campaign = 0;
+ loading = true;
+ loaded = false;
+
+ if (description)
+ description->SetText(Game::GetText("CmpSelectDlg.loading"));
+
+ DWORD thread_id = 0;
+ hproc = CreateThread(0, 4096, CmpSelectDlgLoadProc, (LPVOID) this, 0, &thread_id);
+
+ if (hproc == 0) {
+ static int report = 10;
+ if (report > 0) {
+ ::Print("WARNING: CmpSelectDlg() failed to create thread (err=%08x)\n", GetLastError());
+ report--;
+
+ if (report == 0)
+ ::Print(" Further warnings of this type will be supressed.\n");
+ }
+ }
+ }
+}
+
+void
+CmpSelectDlg::StopLoadProc()
+{
+ if (hproc != 0) {
+ WaitForSingleObject(hproc, 2500);
+ CloseHandle(hproc);
+ hproc = 0;
+ }
+}
+
+DWORD WINAPI CmpSelectDlgLoadProc(LPVOID link)
+{
+ CmpSelectDlg* dlg = (CmpSelectDlg*) link;
+
+ if (dlg)
+ return dlg->LoadProc();
+
+ return (DWORD) E_POINTER;
+}
+
+DWORD
+CmpSelectDlg::LoadProc()
+{
+ Campaign* c = 0;
+
+ // NEW CAMPAIGN:
+ if (load_index >= 0) {
+ List<Campaign>& list = Campaign::GetAllCampaigns();
+
+ if (load_index < list.size()) {
+ c = list[load_index];
+ c->Load();
+ }
+ }
+
+ // SAVED CAMPAIGN:
+ else {
+ CampaignSaveGame savegame;
+ savegame.Load(load_file);
+ c = savegame.GetCampaign();
+ }
+
+ sync.acquire();
+
+ loading = false;
+ loaded = true;
+ campaign = c;
+
+ sync.release();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
diff --git a/Stars45/CmpSelectDlg.h b/Stars45/CmpSelectDlg.h
new file mode 100644
index 0000000..db4ba37
--- /dev/null
+++ b/Stars45/CmpSelectDlg.h
@@ -0,0 +1,91 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmpSelectDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Select Dialog Active Window class
+*/
+
+#ifndef CmpSelectDlg_h
+#define CmpSelectDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+class Campaign;
+class Starshatter;
+
+// +--------------------------------------------------------------------+
+
+class CmpSelectDlg : public FormWindow
+{
+public:
+ CmpSelectDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~CmpSelectDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+ virtual bool CanClose();
+
+ // Operations:
+ virtual void OnCampaignSelect(AWEvent* event);
+ virtual void OnNew(AWEvent* event);
+ virtual void OnSaved(AWEvent* event);
+ virtual void OnDelete(AWEvent* event);
+ virtual void OnConfirmDelete(AWEvent* event);
+ virtual void OnAccept(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+
+ virtual DWORD LoadProc();
+
+protected:
+ virtual void StartLoadProc();
+ virtual void StopLoadProc();
+ virtual void ShowNewCampaigns();
+ virtual void ShowSavedCampaigns();
+
+ MenuScreen* manager;
+
+ Button* btn_new;
+ Button* btn_saved;
+ Button* btn_delete;
+ Button* btn_accept;
+ Button* btn_cancel;
+
+ ListBox* lst_campaigns;
+
+ ActiveWindow* description;
+
+ Starshatter* stars;
+ Campaign* campaign;
+ int selected_mission;
+ HANDLE hproc;
+ ThreadSync sync;
+ bool loading;
+ bool loaded;
+ Text load_file;
+ int load_index;
+ bool show_saved;
+ List<Bitmap> images;
+
+ Text select_msg;
+};
+
+#endif CmpSelectDlg_h
+
diff --git a/Stars45/CmpnScreen.cpp b/Stars45/CmpnScreen.cpp
new file mode 100644
index 0000000..0a08a88
--- /dev/null
+++ b/Stars45/CmpnScreen.cpp
@@ -0,0 +1,647 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: Stars
+ FILE: CmpnScreen.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include "MemDebug.h"
+#include "CmpnScreen.h"
+
+#include "CmdForceDlg.h"
+#include "CmdMissionsDlg.h"
+#include "CmdOrdersDlg.h"
+#include "CmdTheaterDlg.h"
+#include "CmdIntelDlg.h"
+#include "CmpCompleteDlg.h"
+#include "CmdMsgDlg.h"
+#include "CmpFileDlg.h"
+#include "CmpSceneDlg.h"
+#include "Campaign.h"
+#include "CombatEvent.h"
+#include "Mission.h"
+#include "Sim.h"
+#include "Starshatter.h"
+#include "StarSystem.h"
+#include "Player.h"
+#include "MusicDirector.h"
+
+#include "Game.h"
+#include "Video.h"
+#include "Screen.h"
+#include "Window.h"
+#include "ActiveWindow.h"
+#include "FormDef.h"
+#include "Mouse.h"
+#include "Color.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "FontMgr.h"
+#include "EventDispatch.h"
+#include "DataLoader.h"
+#include "Resource.h"
+
+// +--------------------------------------------------------------------+
+
+CmpnScreen::CmpnScreen()
+ : screen(0),
+ cmd_force_dlg(0), cmd_missions_dlg(0), cmd_orders_dlg(0),
+ cmd_intel_dlg(0), cmd_theater_dlg(0), cmd_msg_dlg(0), cmp_file_dlg(0),
+ cmp_end_dlg(0), cmp_scene_dlg(0),
+ isShown(false), campaign(0), stars(0), completion_stage(0)
+{
+ loader = DataLoader::GetLoader();
+ stars = Starshatter::GetInstance();
+}
+
+CmpnScreen::~CmpnScreen()
+{
+ TearDown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::Setup(Screen* s)
+{
+ if (!s)
+ return;
+
+ screen = s;
+
+ loader->UseFileSystem(true);
+
+ FormDef cmd_orders_def("CmdOrdersDlg", 0);
+ cmd_orders_def.Load("CmdOrdersDlg");
+ cmd_orders_dlg = new(__FILE__,__LINE__) CmdOrdersDlg(screen, cmd_orders_def, this);
+
+ FormDef cmd_force_def("CmdForceDlg", 0);
+ cmd_force_def.Load("CmdForceDlg");
+ cmd_force_dlg = new(__FILE__,__LINE__) CmdForceDlg(screen, cmd_force_def, this);
+
+ FormDef cmd_theater_def("CmdTheaterDlg", 0);
+ cmd_theater_def.Load("CmdTheaterDlg");
+ cmd_theater_dlg = new(__FILE__,__LINE__) CmdTheaterDlg(screen, cmd_theater_def, this);
+
+ FormDef cmd_intel_def("CmdIntelDlg", 0);
+ cmd_intel_def.Load("CmdIntelDlg");
+ cmd_intel_dlg = new(__FILE__,__LINE__) CmdIntelDlg(screen, cmd_intel_def, this);
+
+ FormDef cmd_missions_def("CmdMissionsDlg", 0);
+ cmd_missions_def.Load("CmdMissionsDlg");
+ cmd_missions_dlg = new(__FILE__,__LINE__) CmdMissionsDlg(screen, cmd_missions_def, this);
+
+ FormDef file_def("FileDlg", 0);
+ file_def.Load("FileDlg");
+ cmp_file_dlg = new(__FILE__,__LINE__) CmpFileDlg(screen, file_def, this);
+
+ FormDef msg_def("CmdMsgDlg", 0);
+ msg_def.Load("CmdMsgDlg");
+ cmd_msg_dlg = new(__FILE__,__LINE__) CmdMsgDlg(screen, msg_def, this);
+
+ FormDef end_def("CmpCompleteDlg", 0);
+ end_def.Load("CmpCompleteDlg");
+ cmp_end_dlg = new(__FILE__,__LINE__) CmpCompleteDlg(screen, end_def, this);
+
+ FormDef scene_def("CmpSceneDlg", 0);
+ scene_def.Load("CmpSceneDlg");
+ cmp_scene_dlg = new(__FILE__,__LINE__) CmpSceneDlg(screen, scene_def, this);
+
+ loader->UseFileSystem(Starshatter::UseFileSystem());
+
+ HideAll();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::TearDown()
+{
+ if (screen) {
+ screen->DelWindow(cmd_force_dlg);
+ screen->DelWindow(cmd_missions_dlg);
+ screen->DelWindow(cmd_orders_dlg);
+ screen->DelWindow(cmd_intel_dlg);
+ screen->DelWindow(cmd_theater_dlg);
+ screen->DelWindow(cmd_msg_dlg);
+ screen->DelWindow(cmp_file_dlg);
+ screen->DelWindow(cmp_end_dlg);
+ screen->DelWindow(cmp_scene_dlg);
+ }
+
+ delete cmd_force_dlg;
+ delete cmd_missions_dlg;
+ delete cmd_orders_dlg;
+ delete cmd_intel_dlg;
+ delete cmd_theater_dlg;
+ delete cmd_msg_dlg;
+ delete cmp_file_dlg;
+ delete cmp_end_dlg;
+ delete cmp_scene_dlg;
+
+ cmd_force_dlg = 0;
+ cmd_missions_dlg = 0;
+ cmd_orders_dlg = 0;
+ cmd_intel_dlg = 0;
+ cmd_theater_dlg = 0;
+ cmd_msg_dlg = 0;
+ cmp_file_dlg = 0;
+ cmp_end_dlg = 0;
+ cmp_scene_dlg = 0;
+ screen = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::SetFieldOfView(double fov)
+{
+ if (cmp_scene_dlg)
+ cmp_scene_dlg->GetCameraView()->SetFieldOfView(fov);
+}
+
+// +--------------------------------------------------------------------+
+
+double
+CmpnScreen::GetFieldOfView() const
+{
+ if (cmp_scene_dlg)
+ return cmp_scene_dlg->GetCameraView()->GetFieldOfView();
+
+ return 2;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::ExecFrame()
+{
+ Game::SetScreenColor(Color::Black);
+
+ if (cmd_orders_dlg && cmd_orders_dlg->IsShown()) {
+ cmd_orders_dlg->ExecFrame();
+ }
+
+ else if (cmd_force_dlg && cmd_force_dlg->IsShown()) {
+ cmd_force_dlg->ExecFrame();
+ }
+
+ else if (cmd_theater_dlg && cmd_theater_dlg->IsShown()) {
+ cmd_theater_dlg->ExecFrame();
+ }
+
+ else if (cmd_missions_dlg && cmd_missions_dlg->IsShown()) {
+ cmd_missions_dlg->ExecFrame();
+ }
+
+ else if (cmd_intel_dlg && cmd_intel_dlg->IsShown()) {
+ cmd_intel_dlg->ExecFrame();
+ }
+
+ if (cmp_file_dlg && cmp_file_dlg->IsShown()) {
+ cmp_file_dlg->ExecFrame();
+ }
+
+ if (cmd_msg_dlg && cmd_msg_dlg->IsShown()) {
+ cmd_msg_dlg->ExecFrame();
+ }
+
+ else if (cmp_end_dlg && cmp_end_dlg->IsShown()) {
+ cmp_end_dlg->ExecFrame();
+ completion_stage = 2;
+ }
+
+ else if (cmp_scene_dlg && cmp_scene_dlg->IsShown()) {
+ cmp_scene_dlg->ExecFrame();
+
+ if (completion_stage > 0)
+ completion_stage = 2;
+ }
+
+ else if (campaign) {
+ // if campaign is complete
+ if (completion_stage == 0) {
+ Player* player = Player::GetCurrentPlayer();
+ char msg_info[1024];
+
+ if (!player)
+ return;
+
+ if (campaign->IsTraining()) {
+ int all_missions = (1<<campaign->GetMissionList().size())-1;
+
+ if (player->Trained() >= all_missions && player->Trained() < 255) {
+ player->SetTrained(255);
+ cmd_msg_dlg->Title()->SetText(Game::GetText("CmpnScreen.training"));
+ sprintf(msg_info, Game::GetText("CmpnScreen.congrats"),
+ Player::RankName(player->Rank()),
+ player->Name().data());
+
+ cmd_msg_dlg->Message()->SetText(msg_info);
+ cmd_msg_dlg->Message()->SetTextAlign(DT_LEFT);
+
+ ShowCmdMsgDlg();
+ completion_stage = 1;
+ }
+ }
+
+ else if (campaign->IsComplete() || campaign->IsFailed()) {
+ bool cutscene = false;
+ CombatEvent* event = campaign->GetLastEvent();
+
+ if (event && !event->Visited() && event->SceneFile() && *event->SceneFile()) {
+ stars->ExecCutscene(event->SceneFile(), campaign->Path());
+
+ if (stars->InCutscene()) {
+ cutscene = true;
+ ShowCmpSceneDlg();
+ }
+ }
+
+ if (!cutscene) {
+ ShowCmpCompleteDlg();
+ }
+
+ if (campaign->IsComplete())
+ MusicDirector::SetMode(MusicDirector::VICTORY);
+ else
+ MusicDirector::SetMode(MusicDirector::DEFEAT);
+
+ completion_stage = 1;
+ }
+ }
+
+ // if message has been shown, restart
+ else if (completion_stage > 1) {
+ completion_stage = 0;
+
+ if (campaign->IsTraining()) {
+ List<Campaign>& list = Campaign::GetAllCampaigns();
+ Campaign* c = list[1];
+
+ if (c) {
+ c->Load();
+ Campaign::SelectCampaign(c->Name());
+ stars->SetGameMode(Starshatter::CLOD_MODE);
+ return;
+ }
+ }
+
+#ifdef STARSHATTER_DEMO_RELEASE
+ if (!campaign->IsTraining()) {
+ Mouse::Show(false);
+ MusicDirector::SetMode(MusicDirector::MENU);
+ stars->SetGameMode(Starshatter::MENU_MODE);
+ return;
+ }
+#endif
+
+ // continue on to the next available campaign:
+ if (campaign->GetCampaignId() < Campaign::GetLastCampaignId()) {
+ stars->StartOrResumeGame();
+ }
+
+ // if this was the last campaign, just go back to the menu:
+ else {
+ Mouse::Show(false);
+ MusicDirector::SetMode(MusicDirector::MENU);
+ stars->SetGameMode(Starshatter::MENU_MODE);
+ return;
+ }
+ }
+ }
+
+ if (completion_stage < 1) {
+ MusicDirector::SetMode(MusicDirector::MENU);
+ Mouse::Show(!IsCmpSceneShown());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CmpnScreen::CloseTopmost()
+{
+ bool processed = false;
+
+ if (IsCmdMsgShown()) {
+ HideCmdMsgDlg();
+ processed = true;
+ }
+
+ else if (IsCmpFileShown()) {
+ HideCmpFileDlg();
+ processed = true;
+ }
+
+ return processed;
+}
+
+void
+CmpnScreen::Show()
+{
+ if (!isShown) {
+ isShown = true;
+
+ campaign = Campaign::GetCampaign();
+ completion_stage = 0;
+
+ bool cutscene = false;
+ CombatEvent* event = 0;
+
+ if (campaign->IsActive() && !campaign->GetEvents().isEmpty()) {
+ ListIter<CombatEvent> iter = campaign->GetEvents();
+ while (++iter) {
+ event = iter.value();
+
+ if (event && !event->Visited() && event->SceneFile() && *event->SceneFile()) {
+ stars->ExecCutscene(event->SceneFile(), campaign->Path());
+
+ if (stars->InCutscene()) {
+ cutscene = true;
+ ShowCmpSceneDlg();
+ }
+
+ event->SetVisited(true);
+ break;
+ }
+ }
+ }
+
+ if (!cutscene)
+ ShowCmdDlg();
+ }
+}
+
+void
+CmpnScreen::Hide()
+{
+ if (isShown) {
+ HideAll();
+ isShown = false;
+ }
+}
+
+void
+CmpnScreen::HideAll()
+{
+ if (cmd_force_dlg) cmd_force_dlg->Hide();
+ if (cmd_missions_dlg) cmd_missions_dlg->Hide();
+ if (cmd_orders_dlg) cmd_orders_dlg->Hide();
+ if (cmd_intel_dlg) cmd_intel_dlg->Hide();
+ if (cmd_theater_dlg) cmd_theater_dlg->Hide();
+ if (cmd_msg_dlg) cmd_msg_dlg->Hide();
+ if (cmp_file_dlg) cmp_file_dlg->Hide();
+ if (cmp_end_dlg) cmp_end_dlg->Hide();
+ if (cmp_scene_dlg) cmp_scene_dlg->Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::ShowCmdDlg()
+{
+ ShowCmdOrdersDlg();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::ShowCmdForceDlg()
+{
+ HideAll();
+ cmd_force_dlg->Show();
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::HideCmdForceDlg()
+{
+ if (IsCmdForceShown())
+ cmd_force_dlg->Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CmpnScreen::IsCmdForceShown()
+{
+ return cmd_force_dlg && cmd_force_dlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::ShowCmdOrdersDlg()
+{
+ HideAll();
+ cmd_orders_dlg->Show();
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::HideCmdOrdersDlg()
+{
+ if (IsCmdOrdersShown())
+ cmd_orders_dlg->Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CmpnScreen::IsCmdOrdersShown()
+{
+ return cmd_orders_dlg && cmd_orders_dlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::ShowCmdMissionsDlg()
+{
+ HideAll();
+ cmd_missions_dlg->Show();
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::HideCmdMissionsDlg()
+{
+ if (IsCmdMissionsShown())
+ cmd_missions_dlg->Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CmpnScreen::IsCmdMissionsShown()
+{
+ return cmd_missions_dlg && cmd_missions_dlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::ShowCmdIntelDlg()
+{
+ HideAll();
+ cmd_intel_dlg->Show();
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::HideCmdIntelDlg()
+{
+ if (IsCmdIntelShown())
+ cmd_intel_dlg->Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CmpnScreen::IsCmdIntelShown()
+{
+ return cmd_intel_dlg && cmd_intel_dlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::ShowCmdTheaterDlg()
+{
+ HideAll();
+ cmd_theater_dlg->Show();
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::HideCmdTheaterDlg()
+{
+ if (IsCmdTheaterShown())
+ cmd_theater_dlg->Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CmpnScreen::IsCmdTheaterShown()
+{
+ return cmd_theater_dlg && cmd_theater_dlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::ShowCmpFileDlg()
+{
+ cmp_file_dlg->Show();
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::HideCmpFileDlg()
+{
+ if (IsCmpFileShown())
+ cmp_file_dlg->Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CmpnScreen::IsCmpFileShown()
+{
+ return cmp_file_dlg && cmp_file_dlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::ShowCmdMsgDlg()
+{
+ cmd_msg_dlg->Show();
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::HideCmdMsgDlg()
+{
+ if (IsCmdMsgShown())
+ cmd_msg_dlg->Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CmpnScreen::IsCmdMsgShown()
+{
+ return cmd_msg_dlg && cmd_msg_dlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::ShowCmpCompleteDlg()
+{
+ cmp_end_dlg->Show();
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::HideCmpCompleteDlg()
+{
+ if (IsCmpCompleteShown())
+ cmp_end_dlg->Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CmpnScreen::IsCmpCompleteShown()
+{
+ return cmp_end_dlg && cmp_end_dlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::ShowCmpSceneDlg()
+{
+ cmp_scene_dlg->Show();
+ Mouse::Show(false);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CmpnScreen::HideCmpSceneDlg()
+{
+ if (IsCmpSceneShown())
+ cmp_scene_dlg->Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CmpnScreen::IsCmpSceneShown()
+{
+ return cmp_scene_dlg && cmp_scene_dlg->IsShown();
+}
diff --git a/Stars45/CmpnScreen.h b/Stars45/CmpnScreen.h
new file mode 100644
index 0000000..67ebbac
--- /dev/null
+++ b/Stars45/CmpnScreen.h
@@ -0,0 +1,137 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CmpnScreen.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef CmpnScreen_h
+#define CmpnScreen_h
+
+#include "Types.h"
+#include "Bitmap.h"
+#include "Screen.h"
+#include "BaseScreen.h"
+
+// +--------------------------------------------------------------------+
+
+class CmdForceDlg;
+class CmdMissionsDlg;
+class CmdOrdersDlg;
+class CmdIntelDlg;
+class CmdTheaterDlg;
+
+class CmdMsgDlg;
+class CmpFileDlg;
+class CmpCompleteDlg;
+class CmpSceneDlg;
+
+class Campaign;
+class Starshatter;
+
+class Bitmap;
+class DataLoader;
+class Font;
+class FormEx;
+class Screen;
+class Video;
+
+// +--------------------------------------------------------------------+
+
+class CmpnScreen
+{
+public:
+ CmpnScreen();
+ virtual ~CmpnScreen();
+
+ virtual void Setup(Screen* screen);
+ virtual void TearDown();
+ virtual bool CloseTopmost();
+
+ virtual bool IsShown() const { return isShown; }
+ virtual void Show();
+ virtual void Hide();
+
+ virtual void ShowCmdDlg();
+
+ virtual void ShowCmdForceDlg();
+ virtual void HideCmdForceDlg();
+ virtual bool IsCmdForceShown();
+ virtual CmdForceDlg* GetCmdForceDlg() { return cmd_force_dlg; }
+
+ virtual void ShowCmdMissionsDlg();
+ virtual void HideCmdMissionsDlg();
+ virtual bool IsCmdMissionsShown();
+ virtual CmdMissionsDlg* GetCmdMissionsDlg() { return cmd_missions_dlg; }
+
+ virtual void ShowCmdOrdersDlg();
+ virtual void HideCmdOrdersDlg();
+ virtual bool IsCmdOrdersShown();
+ virtual CmdOrdersDlg* GetCmdOrdersDlg() { return cmd_orders_dlg; }
+
+ virtual void ShowCmdIntelDlg();
+ virtual void HideCmdIntelDlg();
+ virtual bool IsCmdIntelShown();
+ virtual CmdIntelDlg* GetCmdIntelDlg() { return cmd_intel_dlg; }
+
+ virtual void ShowCmdTheaterDlg();
+ virtual void HideCmdTheaterDlg();
+ virtual bool IsCmdTheaterShown();
+ virtual CmdTheaterDlg* GetCmdTheaterDlg() { return cmd_theater_dlg; }
+
+ virtual void ShowCmpFileDlg();
+ virtual void HideCmpFileDlg();
+ virtual bool IsCmpFileShown();
+ virtual CmpFileDlg* GetCmpFileDlg() { return cmp_file_dlg; }
+
+ virtual void ShowCmdMsgDlg();
+ virtual void HideCmdMsgDlg();
+ virtual bool IsCmdMsgShown();
+ virtual CmdMsgDlg* GetCmdMsgDlg() { return cmd_msg_dlg; }
+
+ virtual void ShowCmpCompleteDlg();
+ virtual void HideCmpCompleteDlg();
+ virtual bool IsCmpCompleteShown();
+ virtual CmpCompleteDlg* GetCmpCompleteDlg() { return cmp_end_dlg; }
+
+ virtual void ShowCmpSceneDlg();
+ virtual void HideCmpSceneDlg();
+ virtual bool IsCmpSceneShown();
+ virtual CmpSceneDlg* GetCmpSceneDlg() { return cmp_scene_dlg; }
+
+ virtual void HideAll();
+ virtual void ExecFrame();
+
+ void SetFieldOfView(double fov);
+ double GetFieldOfView() const;
+
+private:
+ Screen* screen;
+
+ CmdForceDlg* cmd_force_dlg;
+ CmdOrdersDlg* cmd_orders_dlg;
+ CmdMissionsDlg* cmd_missions_dlg;
+ CmdIntelDlg* cmd_intel_dlg;
+ CmdTheaterDlg* cmd_theater_dlg;
+
+ CmdMsgDlg* cmd_msg_dlg;
+ CmpFileDlg* cmp_file_dlg;
+ CmpCompleteDlg* cmp_end_dlg;
+ CmpSceneDlg* cmp_scene_dlg;
+
+ DataLoader* loader;
+
+ bool isShown;
+
+ Campaign* campaign;
+ Starshatter* stars;
+ int completion_stage;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif CmpnScreen_h
+
diff --git a/Stars45/CombatAction.cpp b/Stars45/CombatAction.cpp
new file mode 100644
index 0000000..7725ec4
--- /dev/null
+++ b/Stars45/CombatAction.cpp
@@ -0,0 +1,352 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CombatAction.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ A significant (newsworthy) event in the dynamic campaign.
+*/
+
+#include "MemDebug.h"
+#include "CombatAction.h"
+#include "CombatGroup.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "Player.h"
+#include "Random.h"
+
+// +----------------------------------------------------------------------+
+
+CombatAction::CombatAction(int n, int typ, int sub, int iff)
+ : id(n), type(typ), subtype(sub), opp_type(-1), team(iff),
+ status(PENDING), count(0), rval(-1), source(0), time(0),
+ start_before((int) 1e9), start_after(0),
+ min_rank(0), max_rank(100),
+ delay(0), probability(100), asset_type(0), target_type(0)
+{ }
+
+CombatAction::~CombatAction()
+{
+ requirements.destroy();
+ asset_kills.destroy();
+ target_kills.destroy();
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+CombatAction::IsAvailable() const
+{
+ CombatAction* pThis = (CombatAction*) this;
+
+ if (rval < 0) {
+ pThis->rval = (int) Random(0, 100);
+
+ if (rval > probability)
+ pThis->status = SKIPPED;
+ }
+
+ if (status != PENDING)
+ return false;
+
+ if (min_rank > 0 || max_rank < 100) {
+ Player* player = Player::GetCurrentPlayer();
+
+ if (player->Rank() < min_rank || player->Rank() > max_rank)
+ return false;
+ }
+
+ Campaign* campaign = Campaign::GetCampaign();
+ if (campaign) {
+ if (campaign->GetTime() < start_after) {
+ return false;
+ }
+
+ if (campaign->GetTime() > start_before) {
+ pThis->status = FAILED; // too late!
+ return false;
+ }
+
+ // check requirements against actions in current campaign:
+ ListIter<CombatActionReq> iter = pThis->requirements;
+ while (++iter) {
+ CombatActionReq* r = iter.value();
+ bool ok = false;
+
+ if (r->action > 0) {
+ ListIter<CombatAction> action = campaign->GetActions();
+ while (++action) {
+ CombatAction* a = action.value();
+
+ if (a->Identity() == r->action) {
+ if (r->not) {
+ if (a->Status() == r->stat)
+ return false;
+ }
+ else {
+ if (a->Status() != r->stat)
+ return false;
+ }
+ }
+ }
+ }
+
+ // group-based requirement
+ else if (r->group_type > 0) {
+ if (r->c1) {
+ CombatGroup* group = r->c1->FindGroup(r->group_type, r->group_id);
+
+ if (group) {
+ int test = 0;
+ int comp = 0;
+
+ if (r->intel) {
+ test = group->IntelLevel();
+ comp = r->intel;
+ }
+
+ else {
+ test = group->CalcValue();
+ comp = r->score;
+ }
+
+ switch (r->comp) {
+ case CombatActionReq::LT: ok = (test < comp); break;
+ case CombatActionReq::LE: ok = (test <= comp); break;
+ case CombatActionReq::GT: ok = (test > comp); break;
+ case CombatActionReq::GE: ok = (test >= comp); break;
+ case CombatActionReq::EQ: ok = (test == comp); break;
+ }
+ }
+
+ if (!ok)
+ return false;
+ }
+ }
+
+ // score-based requirement
+ else {
+ int test = 0;
+
+ if (r->comp <= CombatActionReq::EQ) { // absolute
+ if (r->c1) {
+ int test = r->c1->Score();
+
+ switch (r->comp) {
+ case CombatActionReq::LT: ok = (test < r->score); break;
+ case CombatActionReq::LE: ok = (test <= r->score); break;
+ case CombatActionReq::GT: ok = (test > r->score); break;
+ case CombatActionReq::GE: ok = (test >= r->score); break;
+ case CombatActionReq::EQ: ok = (test == r->score); break;
+ }
+ }
+ }
+
+ else { // relative
+ if (r->c1 && r->c2) {
+ int test = r->c1->Score() - r->c2->Score();
+
+ switch (r->comp) {
+ case CombatActionReq::RLT: ok = (test < r->score); break;
+ case CombatActionReq::RLE: ok = (test <= r->score); break;
+ case CombatActionReq::RGT: ok = (test > r->score); break;
+ case CombatActionReq::RGE: ok = (test >= r->score); break;
+ case CombatActionReq::REQ: ok = (test == r->score); break;
+ }
+ }
+ }
+
+ if (!ok)
+ return false;
+ }
+
+ if (delay > 0) {
+ pThis->start_after = (int) campaign->GetTime() + delay;
+ pThis->delay = 0;
+ return IsAvailable();
+ }
+ }
+ }
+
+ return true;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+CombatAction::FireAction()
+{
+ Campaign* campaign = Campaign::GetCampaign();
+ if (campaign)
+ time = (int) campaign->GetTime();
+
+ if (count >= 1)
+ count--;
+
+ if (count < 1)
+ status = COMPLETE;
+}
+
+void
+CombatAction::FailAction()
+{
+ Campaign* campaign = Campaign::GetCampaign();
+ if (campaign)
+ time = (int) campaign->GetTime();
+
+ count = 0;
+ status = FAILED;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+CombatAction::AddRequirement(int action, int stat, bool not)
+{
+ requirements.append(new(__FILE__,__LINE__) CombatActionReq(action, stat, not));
+}
+
+void
+CombatAction::AddRequirement(Combatant* c1, Combatant* c2, int comp, int score)
+{
+ requirements.append(new(__FILE__,__LINE__) CombatActionReq(c1, c2, comp, score));
+}
+
+void
+CombatAction::AddRequirement(Combatant* c1, int group_type, int group_id, int comp, int score, int intel)
+{
+ requirements.append(new(__FILE__,__LINE__) CombatActionReq(c1, group_type, group_id, comp, score, intel));
+}
+
+// +----------------------------------------------------------------------+
+
+int
+CombatAction::TypeFromName(const char* n)
+{
+ int type = 0;
+
+ if (!stricmp(n, "NO_ACTION"))
+ type = NO_ACTION;
+
+ else if (!stricmp(n, "MARKER"))
+ type = NO_ACTION;
+
+ else if (!stricmp(n, "STRATEGIC_DIRECTIVE"))
+ type = STRATEGIC_DIRECTIVE;
+
+ else if (!stricmp(n, "STRATEGIC"))
+ type = STRATEGIC_DIRECTIVE;
+
+ else if (!stricmp(n, "ZONE_ASSIGNMENT"))
+ type = ZONE_ASSIGNMENT;
+
+ else if (!stricmp(n, "ZONE"))
+ type = ZONE_ASSIGNMENT;
+
+ else if (!stricmp(n, "SYSTEM_ASSIGNMENT"))
+ type = SYSTEM_ASSIGNMENT;
+
+ else if (!stricmp(n, "SYSTEM"))
+ type = SYSTEM_ASSIGNMENT;
+
+ else if (!stricmp(n, "MISSION_TEMPLATE"))
+ type = MISSION_TEMPLATE;
+
+ else if (!stricmp(n, "MISSION"))
+ type = MISSION_TEMPLATE;
+
+ else if (!stricmp(n, "COMBAT_EVENT"))
+ type = COMBAT_EVENT;
+
+ else if (!stricmp(n, "EVENT"))
+ type = COMBAT_EVENT;
+
+ else if (!stricmp(n, "INTEL_EVENT"))
+ type = INTEL_EVENT;
+
+ else if (!stricmp(n, "INTEL"))
+ type = INTEL_EVENT;
+
+ else if (!stricmp(n, "CAMPAIGN_SITUATION"))
+ type = CAMPAIGN_SITUATION;
+
+ else if (!stricmp(n, "SITREP"))
+ type = CAMPAIGN_SITUATION;
+
+ else if (!stricmp(n, "CAMPAIGN_ORDERS"))
+ type = CAMPAIGN_ORDERS;
+
+ else if (!stricmp(n, "ORDERS"))
+ type = CAMPAIGN_ORDERS;
+
+ return type;
+}
+
+int
+CombatAction::StatusFromName(const char* n)
+{
+ int stat = 0;
+
+ if (!stricmp(n, "PENDING"))
+ stat = PENDING;
+
+ else if (!stricmp(n, "ACTIVE"))
+ stat = ACTIVE;
+
+ else if (!stricmp(n, "SKIPPED"))
+ stat = SKIPPED;
+
+ else if (!stricmp(n, "FAILED"))
+ stat = FAILED;
+
+ else if (!stricmp(n, "COMPLETE"))
+ stat = COMPLETE;
+
+ return stat;
+}
+
+
+// +----------------------------------------------------------------------+
+
+int
+CombatActionReq::CompFromName(const char* n)
+{
+ int comp = 0;
+
+ if (!stricmp(n, "LT"))
+ comp = LT;
+
+ else if (!stricmp(n, "LE"))
+ comp = LE;
+
+ else if (!stricmp(n, "GT"))
+ comp = GT;
+
+ else if (!stricmp(n, "GE"))
+ comp = GE;
+
+ else if (!stricmp(n, "EQ"))
+ comp = EQ;
+
+ else if (!stricmp(n, "RLT"))
+ comp = RLT;
+
+ else if (!stricmp(n, "RLE"))
+ comp = RLE;
+
+ else if (!stricmp(n, "RGT"))
+ comp = RGT;
+
+ else if (!stricmp(n, "RGE"))
+ comp = RGE;
+
+ else if (!stricmp(n, "REQ"))
+ comp = REQ;
+
+ return comp;
+}
diff --git a/Stars45/CombatAction.h b/Stars45/CombatAction.h
new file mode 100644
index 0000000..0de85f2
--- /dev/null
+++ b/Stars45/CombatAction.h
@@ -0,0 +1,205 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CombatAction.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ A planned action (mission/story/strategy) in a dynamic campaign.
+*/
+
+#ifndef CombatAction_h
+#define CombatAction_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Text.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Combatant;
+class CombatAction;
+class CombatActionReq;
+
+// +--------------------------------------------------------------------+
+
+class CombatAction
+{
+public:
+ static const char* TYPENAME() { return "CombatAction"; }
+
+ enum TYPE
+ {
+ NO_ACTION,
+ STRATEGIC_DIRECTIVE,
+ ZONE_ASSIGNMENT,
+ SYSTEM_ASSIGNMENT,
+ MISSION_TEMPLATE,
+ COMBAT_EVENT,
+ INTEL_EVENT,
+ CAMPAIGN_SITUATION,
+ CAMPAIGN_ORDERS
+ };
+
+ enum STATUS
+ {
+ PENDING,
+ ACTIVE,
+ SKIPPED,
+ FAILED,
+ COMPLETE
+ };
+
+ CombatAction(int id, int type, int subtype, int team);
+ ~CombatAction();
+
+ int operator == (const CombatAction& a) const { return id == a.id; }
+
+ bool IsAvailable() const;
+ void FireAction();
+ void FailAction();
+ void AddRequirement(int action, int stat, bool not = false);
+ void AddRequirement(Combatant* c1, Combatant* c2, int comp, int score);
+ void AddRequirement(Combatant* c1, int group_type, int group_id, int comp, int score, int intel=0);
+ static int TypeFromName(const char* n);
+ static int StatusFromName(const char* n);
+
+ // accessors/mutators:
+ int Identity() const { return id; }
+ int Type() const { return type; }
+ int Subtype() const { return subtype; }
+ int OpposingType() const { return opp_type; }
+ int GetIFF() const { return team; }
+ int Status() const { return status; }
+ int Source() const { return source; }
+ Point Location() const { return loc; }
+ const char* System() const { return system; }
+ const char* Region() const { return region; }
+ const char* Filename() const { return text_file; }
+ const char* ImageFile() const { return image_file; }
+ const char* SceneFile() const { return scene_file; }
+ int Count() const { return count; }
+ int ExecTime() const { return time; }
+ int StartBefore() const { return start_before; }
+ int StartAfter() const { return start_after; }
+ int MinRank() const { return min_rank; }
+ int MaxRank() const { return max_rank; }
+ int Delay() const { return delay; }
+ int Probability() const { return probability; }
+ int AssetType() const { return asset_type; }
+ int AssetId() const { return asset_id; }
+ List<Text>& AssetKills() { return asset_kills; }
+ int TargetType() const { return target_type; }
+ int TargetId() const { return target_id; }
+ int TargetIFF() const { return target_iff; }
+ List<Text>& TargetKills() { return target_kills; }
+ const char* GetText() const { return text; }
+
+ void SetType(int t) { type = (char) t; }
+ void SetSubtype(int s) { subtype = (char) s; }
+ void SetOpposingType(int t){ opp_type = (char) t; }
+ void SetIFF(int t) { team = (char) t; }
+ void SetStatus(int s) { status = (char) s; }
+ void SetSource(int s) { source = s; }
+ void SetLocation(const Point& p) { loc = p; }
+ void SetSystem(Text sys) { system = sys; }
+ void SetRegion(Text rgn) { region = rgn; }
+ void SetFilename(Text f) { text_file = f; }
+ void SetImageFile(Text f) { image_file = f; }
+ void SetSceneFile(Text f) { scene_file = f; }
+ void SetCount(int n) { count = (char) n; }
+ void SetExecTime(int t) { time = t; }
+ void SetStartBefore(int s) { start_before = s; }
+ void SetStartAfter(int s) { start_after = s; }
+ void SetMinRank(int n) { min_rank = (char) n; }
+ void SetMaxRank(int n) { max_rank = (char) n; }
+ void SetDelay(int d) { delay = d; }
+ void SetProbability(int n) { probability = n; }
+ void SetAssetType(int t) { asset_type = t; }
+ void SetAssetId(int n) { asset_id = n; }
+ void SetTargetType(int t) { target_type = t; }
+ void SetTargetId(int n) { target_id = n; }
+ void SetTargetIFF(int n) { target_iff = n; }
+ void SetText(Text t) { text = t; }
+
+
+private:
+ int id;
+ char type;
+ char subtype;
+ char opp_type;
+ char team;
+ char status;
+ char min_rank;
+ char max_rank;
+ int source;
+ Point loc;
+ Text system;
+ Text region;
+ Text text_file;
+ Text image_file;
+ Text scene_file;
+ char count;
+ int start_before;
+ int start_after;
+ int delay;
+ int probability;
+ int rval;
+ int time;
+
+ Text text;
+ int asset_type;
+ int asset_id;
+ List<Text> asset_kills;
+ int target_type;
+ int target_id;
+ int target_iff;
+ List<Text> target_kills;
+
+ List<CombatActionReq> requirements;
+};
+
+// +--------------------------------------------------------------------+
+
+class CombatActionReq {
+public:
+ static const char* TYPENAME() { return "CombatActionReq"; }
+
+ enum COMPARISON_OPERATOR {
+ LT, LE, GT, GE, EQ, // absolute score comparison
+ RLT, RLE, RGT, RGE, REQ // delta score comparison
+ };
+
+ CombatActionReq(int a, int s, bool n = false)
+ : action(a), stat(s), not(n), c1(0), c2(0), comp(0), score(0), intel(0) { }
+
+ CombatActionReq(Combatant* a1, Combatant* a2, int comparison, int value)
+ : action(0), stat(0), not(0), c1(a1), c2(a2), group_type(0), group_id(0),
+ comp(comparison), score(value), intel(0) { }
+
+ CombatActionReq(Combatant* a1, int gtype, int gid, int comparison, int value, int intel_level=0)
+ : action(0), stat(0), not(0), c1(a1), c2(0), group_type(gtype), group_id(gid),
+ comp(comparison), score(value), intel(intel_level) { }
+
+ static int CompFromName(const char* sym);
+
+ int action;
+ int stat;
+ bool not;
+
+ Combatant* c1;
+ Combatant* c2;
+ int comp;
+ int score;
+ int intel;
+ int group_type;
+ int group_id;
+};
+
+#endif CombatAction_h
+
diff --git a/Stars45/CombatAssignment.cpp b/Stars45/CombatAssignment.cpp
new file mode 100644
index 0000000..b4687cd
--- /dev/null
+++ b/Stars45/CombatAssignment.cpp
@@ -0,0 +1,68 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CombatAssignment.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ High level assignment of one group to damage another
+*/
+
+#include "MemDebug.h"
+#include "CombatAssignment.h"
+#include "CombatGroup.h"
+#include "Mission.h"
+
+// +--------------------------------------------------------------------+
+
+CombatAssignment::CombatAssignment(int t, CombatGroup* obj, CombatGroup* rsc)
+ : type(t), objective(obj), resource(rsc)
+{
+}
+
+// +--------------------------------------------------------------------+
+
+CombatAssignment::~CombatAssignment()
+{
+}
+
+// +--------------------------------------------------------------------+
+// This is used to sort assignments into a priority list.
+// Higher priorities should come first in the list, so the
+// sense of the operator is "backwards" from the usual.
+
+int
+CombatAssignment::operator < (const CombatAssignment& a) const
+{
+ if (!objective)
+ return 0;
+
+ if (!a.objective)
+ return 1;
+
+ return objective->GetPlanValue() > a.objective->GetPlanValue();
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+CombatAssignment::GetDescription() const
+{
+ static char desc[256];
+
+ if (!resource)
+ sprintf(desc, "%s %s",
+ (const char*) Mission::RoleName(type),
+ (const char*) objective->Name());
+ else
+ sprintf(desc, "%s %s %s",
+ (const char*) resource->Name(),
+ (const char*) Mission::RoleName(type),
+ (const char*) objective->Name());
+
+ return desc;
+}
diff --git a/Stars45/CombatAssignment.h b/Stars45/CombatAssignment.h
new file mode 100644
index 0000000..cd4cebb
--- /dev/null
+++ b/Stars45/CombatAssignment.h
@@ -0,0 +1,57 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CombatAssignment.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ High level assignment of one group to damage another
+*/
+
+#ifndef CombatAssignment_h
+#define CombatAssignment_h
+
+#include "Types.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class CombatGroup;
+class SimRegion;
+
+// +--------------------------------------------------------------------+
+
+class CombatAssignment
+{
+public:
+ static const char* TYPENAME() { return "CombatAssignment"; }
+
+ CombatAssignment(int t, CombatGroup* obj, CombatGroup* rsc=0);
+ ~CombatAssignment();
+
+ int operator < (const CombatAssignment& a) const;
+
+ // operations:
+ void SetObjective(CombatGroup* o) { objective = o; }
+ void SetResource(CombatGroup* r) { resource = r; }
+
+ // accessors:
+ int Type() { return type; }
+ CombatGroup* GetObjective() { return objective; }
+ CombatGroup* GetResource() { return resource; }
+
+ const char* GetDescription() const;
+ bool IsActive() const { return resource != 0; }
+
+private:
+ int type;
+ CombatGroup* objective;
+ CombatGroup* resource;
+};
+
+
+#endif CombatAssignment_h
diff --git a/Stars45/CombatEvent.cpp b/Stars45/CombatEvent.cpp
new file mode 100644
index 0000000..eea73a9
--- /dev/null
+++ b/Stars45/CombatEvent.cpp
@@ -0,0 +1,153 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CombatEvent.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ A significant (newsworthy) event in the dynamic campaign.
+*/
+
+#include "MemDebug.h"
+#include "CombatEvent.h"
+#include "CombatGroup.h"
+#include "Campaign.h"
+#include "Player.h"
+#include "ShipDesign.h"
+#include "Ship.h"
+
+#include "Term.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+#include "DataLoader.h"
+
+// +----------------------------------------------------------------------+
+
+CombatEvent::CombatEvent(Campaign* c, int typ, int tim, int tem,
+ int src, const char* rgn)
+ : campaign(c), type(typ), time(tim), team(tem), source(src),
+ region(rgn), points(0), visited(false)
+{ }
+
+// +----------------------------------------------------------------------+
+
+const char*
+CombatEvent::SourceName() const
+{
+ return SourceName(source);
+}
+
+// +----------------------------------------------------------------------+
+
+const char*
+CombatEvent::TypeName() const
+{
+ return TypeName(type);
+}
+
+// +----------------------------------------------------------------------+
+
+const char*
+CombatEvent::SourceName(int n)
+{
+ switch (n) {
+ case FORCOM: return "FORCOM";
+ case TACNET: return "TACNET";
+ case INTEL: return "SECURE";
+ case MAIL: return "Mail";
+ case NEWS: return "News";
+ }
+
+ return "Unknown";
+}
+
+int
+CombatEvent::SourceFromName(const char* n)
+{
+ for (int i = FORCOM; i <= NEWS; i++)
+ if (!stricmp(n, SourceName(i)))
+ return i;
+
+ return -1;
+}
+
+// +----------------------------------------------------------------------+
+
+const char*
+CombatEvent::TypeName(int n)
+{
+ switch (n) {
+ case ATTACK: return "ATTACK";
+ case DEFEND: return "DEFEND";
+ case MOVE_TO: return "MOVE_TO";
+ case CAPTURE: return "CAPTURE";
+ case STRATEGY: return "STRATEGY";
+ case STORY: return "STORY";
+ case CAMPAIGN_START: return "CAMPAIGN_START";
+ case CAMPAIGN_END: return "CAMPAIGN_END";
+ case CAMPAIGN_FAIL: return "CAMPAIGN_FAIL";
+ }
+
+ return "Unknown";
+}
+
+int
+CombatEvent::TypeFromName(const char* n)
+{
+ for (int i = ATTACK; i <= CAMPAIGN_FAIL; i++)
+ if (!stricmp(n, TypeName(i)))
+ return i;
+
+ return -1;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+CombatEvent::Load()
+{
+ DataLoader* loader = DataLoader::GetLoader();
+
+ if (!campaign || !loader)
+ return;
+
+ loader->SetDataPath(campaign->Path());
+
+ if (file.length() > 0) {
+ const char* filename = file.data();
+ BYTE* block = 0;
+
+ loader->LoadBuffer(filename, block, true);
+ info = (const char*) block;
+ loader->ReleaseBuffer(block);
+
+ if (info.contains('$')) {
+ Player* player = Player::GetCurrentPlayer();
+ CombatGroup* group = campaign->GetPlayerGroup();
+
+ if (player) {
+ info = FormatTextReplace(info, "$NAME", player->Name().data());
+ info = FormatTextReplace(info, "$RANK", Player::RankName(player->Rank()));
+ }
+
+ if (group) {
+ info = FormatTextReplace(info, "$GROUP", group->GetDescription());
+ }
+
+ char timestr[32];
+ FormatDayTime(timestr, campaign->GetTime(), true);
+ info = FormatTextReplace(info, "$TIME", timestr);
+ }
+ }
+
+ if (type < CAMPAIGN_END && image_file.length() > 0) {
+ loader->LoadBitmap(image_file, image);
+ }
+
+ loader->SetDataPath(0);
+}
+
diff --git a/Stars45/CombatEvent.h b/Stars45/CombatEvent.h
new file mode 100644
index 0000000..35dd7c0
--- /dev/null
+++ b/Stars45/CombatEvent.h
@@ -0,0 +1,122 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CombatEvent.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ A significant (newsworthy) event in the dynamic campaign.
+*/
+
+#ifndef CombatEvent_h
+#define CombatEvent_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Bitmap.h"
+#include "Text.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Campaign;
+class CombatGroup;
+class CombatUnit;
+
+// +--------------------------------------------------------------------+
+
+class CombatEvent
+{
+public:
+ static const char* TYPENAME() { return "CombatEvent"; }
+
+ enum EVENT_TYPE {
+ ATTACK,
+ DEFEND,
+ MOVE_TO,
+ CAPTURE,
+ STRATEGY,
+
+ CAMPAIGN_START,
+ STORY,
+ CAMPAIGN_END,
+ CAMPAIGN_FAIL
+ };
+
+ enum EVENT_SOURCE {
+ FORCOM,
+ TACNET,
+ INTEL,
+ MAIL,
+ NEWS
+ };
+
+ CombatEvent(Campaign* c, int type, int time, int team, int source, const char* rgn);
+
+ int operator == (const CombatEvent& u) const { return this == &u; }
+
+ // accessors/mutators:
+ int Type() const { return type; }
+ int Time() const { return time; }
+ int GetIFF() const { return team; }
+ int Points() const { return points; }
+ int Source() const { return source; }
+ Point Location() const { return loc; }
+ const char* Region() const { return region; }
+ const char* Title() const { return title; }
+ const char* Information() const { return info; }
+ const char* Filename() const { return file; }
+ const char* ImageFile() const { return image_file; }
+ const char* SceneFile() const { return scene_file; }
+ Bitmap& Image() { return image; }
+ const char* SourceName() const;
+ const char* TypeName() const;
+ bool Visited() const { return visited; }
+
+ void SetType(int t) { type = t; }
+ void SetTime(int t) { time = t; }
+ void SetIFF(int t) { team = t; }
+ void SetPoints(int p) { points = p; }
+ void SetSource(int s) { source = s; }
+ void SetLocation(const Point& p) { loc = p; }
+ void SetRegion(Text rgn) { region = rgn; }
+ void SetTitle(Text t) { title = t; }
+ void SetInformation(Text t) { info = t; }
+ void SetFilename(Text f) { file = f; }
+ void SetImageFile(Text f) { image_file = f; }
+ void SetSceneFile(Text f) { scene_file = f; }
+ void SetVisited(bool v) { visited = v; }
+
+ // operations:
+ void Load();
+
+ // utilities:
+ static int TypeFromName(const char* n);
+ static int SourceFromName(const char* n);
+ static const char* TypeName(int n);
+ static const char* SourceName(int n);
+
+private:
+ Campaign* campaign;
+ int type;
+ int time;
+ int team;
+ int points;
+ int source;
+ bool visited;
+ Point loc;
+ Text region;
+ Text title;
+ Text info;
+ Text file;
+ Text image_file;
+ Text scene_file;
+ Bitmap image;
+};
+
+#endif CombatEvent_h
+
diff --git a/Stars45/CombatGroup.cpp b/Stars45/CombatGroup.cpp
new file mode 100644
index 0000000..2e8e270
--- /dev/null
+++ b/Stars45/CombatGroup.cpp
@@ -0,0 +1,1589 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CombatGroup.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ An element in the dynamic campaign
+*/
+
+#include "MemDebug.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "CombatZone.h"
+#include "Combatant.h"
+#include "CombatAssignment.h"
+#include "Campaign.h"
+#include "ShipDesign.h"
+#include "Ship.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "ParseUtil.h"
+
+// +----------------------------------------------------------------------+
+
+CombatGroup::CombatGroup(int t, int n, const char* s, int iff_code, int e, CombatGroup* p)
+ : type(t), id(n), name(s), iff(iff_code), enemy_intel(e),
+ parent(p), value(0), plan_value(0), unit_index(0), combatant(0),
+ expanded(false), sorties(0), kills(0), points(0),
+ current_zone(0), assigned_zone(0), zone_lock(false)
+{
+ if (parent)
+ parent->AddComponent(this);
+}
+
+CombatGroup::~CombatGroup()
+{
+ assignments.destroy();
+ components.destroy();
+ units.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CombatGroup::AddComponent(CombatGroup* g)
+{
+ if (g) {
+ g->parent = this;
+ components.append(g);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CombatGroup::IsAssignable() const
+{
+ switch (type) {
+ case CARRIER_GROUP:
+ case BATTLE_GROUP:
+ case DESTROYER_SQUADRON:
+ case ATTACK_SQUADRON:
+ case FIGHTER_SQUADRON:
+ case INTERCEPT_SQUADRON:
+ case LCA_SQUADRON:
+ return ((CombatGroup*) this)->CalcValue() > 0;
+ }
+
+ return false;
+}
+
+bool
+CombatGroup::IsTargetable() const
+{
+ // neutral / non-combatants are not *strategic* targets
+ // for any combatant:
+ if (iff < 1 || iff >= 100)
+ return false;
+
+ // civilian / non-combatant are not strategic targets:
+ if (type == PASSENGER ||
+ type == PRIVATE ||
+ type == MEDICAL ||
+ type == HABITAT)
+ return false;
+
+ // must have units of our own to be targetable:
+ if (units.size() < 1)
+ return false;
+
+ return ((CombatGroup*) this)->CalcValue() > 0;
+}
+
+bool
+CombatGroup::IsDefensible() const
+{
+ if (type >= SUPPORT)
+ return ((CombatGroup*) this)->CalcValue() > 0;
+
+ return false;
+}
+
+bool
+CombatGroup::IsStrikeTarget() const
+{
+ if (type < BATTALION ||
+ type == MINEFIELD || // assault, not strike
+ type == PASSENGER ||
+ type == PRIVATE ||
+ type == MEDICAL ||
+ type == HABITAT)
+ return false;
+
+ return ((CombatGroup*) this)->CalcValue() > 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CombatGroup::IsMovable() const
+{
+ switch (type) {
+ case CARRIER_GROUP:
+ case BATTLE_GROUP:
+ case DESTROYER_SQUADRON:
+ case ATTACK_SQUADRON:
+ case FIGHTER_SQUADRON:
+ case INTERCEPT_SQUADRON:
+ case LCA_SQUADRON:
+ case COURIER:
+ case MEDICAL:
+ case SUPPLY:
+ case REPAIR:
+ case FREIGHT:
+ case PASSENGER:
+ case PRIVATE:
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CombatGroup::IsFighterGroup() const
+{
+ switch (type) {
+ case WING:
+ case INTERCEPT_SQUADRON:
+ case FIGHTER_SQUADRON:
+ case ATTACK_SQUADRON:
+ return true;
+ }
+
+ return false;
+}
+
+bool
+CombatGroup::IsStarshipGroup() const
+{
+ switch (type) {
+ case DESTROYER_SQUADRON:
+ case BATTLE_GROUP:
+ case CARRIER_GROUP:
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CombatGroup::IsReserve() const
+{
+ if (enemy_intel <= Intel::RESERVE)
+ return true;
+
+ if (parent)
+ return parent->IsReserve();
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+const int*
+CombatGroup::PreferredAttacker(int type)
+{
+ static int p[8];
+
+ ZeroMemory(p, sizeof(p));
+
+ switch (type) {
+ //case FLEET:
+ case DESTROYER_SQUADRON: p[0] = DESTROYER_SQUADRON;
+ p[1] = BATTLE_GROUP;
+ p[2] = CARRIER_GROUP;
+ p[3] = ATTACK_SQUADRON;
+ break;
+
+ case BATTLE_GROUP: p[0] = BATTLE_GROUP;
+ p[1] = DESTROYER_SQUADRON;
+ p[2] = CARRIER_GROUP;
+ p[3] = ATTACK_SQUADRON;
+ break;
+
+ case CARRIER_GROUP: p[0] = ATTACK_SQUADRON;
+ p[1] = BATTLE_GROUP;
+ p[2] = DESTROYER_SQUADRON;
+ p[3] = CARRIER_GROUP;
+ break;
+
+ //case WING:
+ case LCA_SQUADRON:
+ case ATTACK_SQUADRON:
+ case INTERCEPT_SQUADRON:
+ case FIGHTER_SQUADRON: p[0] = INTERCEPT_SQUADRON;
+ p[1] = FIGHTER_SQUADRON;
+ break;
+
+ //case BATTALION:
+ case STATION: p[0] = BATTLE_GROUP;
+ p[1] = CARRIER_GROUP;
+ break;
+
+ case STARBASE:
+ case BATTERY:
+ case MISSILE: p[0] = ATTACK_SQUADRON;
+ p[1] = FIGHTER_SQUADRON;
+ break;
+
+ //case C3I:
+ case MINEFIELD:
+ case COMM_RELAY:
+ case EARLY_WARNING:
+ case FWD_CONTROL_CTR:
+ case ECM: p[0] = ATTACK_SQUADRON;
+ p[1] = FIGHTER_SQUADRON;
+ p[2] = DESTROYER_SQUADRON;
+ break;
+
+ //case SUPPORT:
+ case COURIER:
+ case MEDICAL:
+ case SUPPLY:
+ case REPAIR: p[0] = DESTROYER_SQUADRON;
+ p[1] = BATTLE_GROUP;
+ p[2] = ATTACK_SQUADRON;
+ break;
+
+ //case CIVILIAN:
+
+ //case WAR_PRODuCTION:
+ case FACTORY:
+ case REFINERY:
+ case RESOURCE: p[0] = ATTACK_SQUADRON;
+ p[1] = FIGHTER_SQUADRON;
+ break;
+
+ //case INFRASTRUCTURE:
+ case TRANSPORT:
+ case NETWORK:
+ case HABITAT:
+ case STORAGE: p[0] = ATTACK_SQUADRON;
+ p[1] = FIGHTER_SQUADRON;
+ break;
+
+ //case NON_COM:
+ case FREIGHT:
+ case PASSENGER:
+ case PRIVATE: p[0] = DESTROYER_SQUADRON;
+ p[1] = ATTACK_SQUADRON;
+ break;
+ }
+
+ return p;
+}
+
+// +--------------------------------------------------------------------+
+
+const int*
+CombatGroup::PreferredDefender(int type)
+{
+ static int p[8];
+
+ ZeroMemory(p, sizeof(p));
+
+ switch (type) {
+ //case FLEET:
+ case CARRIER_GROUP:
+ case BATTLE_GROUP:
+ case DESTROYER_SQUADRON:
+
+ //case WING:
+ case LCA_SQUADRON:
+ case ATTACK_SQUADRON:
+ case INTERCEPT_SQUADRON:
+ case FIGHTER_SQUADRON: break;
+
+ //case BATTALION:
+ case STATION: p[0] = BATTLE_GROUP;
+ p[1] = CARRIER_GROUP;
+ p[2] = DESTROYER_SQUADRON;
+ break;
+ case STARBASE:
+ case MINEFIELD:
+ case BATTERY:
+ case MISSILE: p[0] = FIGHTER_SQUADRON;
+ p[1] = INTERCEPT_SQUADRON;
+ break;
+
+ //case C3I:
+ case COMM_RELAY:
+ case EARLY_WARNING:
+ case FWD_CONTROL_CTR:
+ case ECM: p[0] = FIGHTER_SQUADRON;
+ p[1] = INTERCEPT_SQUADRON;
+ break;
+
+ //case SUPPORT:
+ case COURIER:
+ case MEDICAL:
+ case SUPPLY:
+ case REPAIR: p[0] = DESTROYER_SQUADRON;
+ p[1] = BATTLE_GROUP;
+ p[2] = ATTACK_SQUADRON;
+ break;
+
+ //case CIVILIAN:
+
+ //case WAR_PRODuCTION:
+ case FACTORY:
+ case REFINERY:
+ case RESOURCE: p[0] = FIGHTER_SQUADRON;
+ p[1] = INTERCEPT_SQUADRON;
+ break;
+
+ //case INFRASTRUCTURE:
+ case TRANSPORT:
+ case NETWORK:
+ case HABITAT:
+ case STORAGE: p[0] = FIGHTER_SQUADRON;
+ p[1] = INTERCEPT_SQUADRON;
+ break;
+
+ //case NON_COM:
+ case FREIGHT:
+ case PASSENGER:
+ case PRIVATE: p[0] = DESTROYER_SQUADRON;
+ p[1] = BATTLE_GROUP;
+ break;
+ }
+
+ return p;
+}
+
+// +--------------------------------------------------------------------+
+
+CombatGroup*
+CombatGroup::FindGroup(int t, int n)
+{
+ CombatGroup* result = 0;
+
+ if (type == t && (n < 0 || id == n))
+ result = this;
+
+ ListIter<CombatGroup> group = components;
+ while (!result && ++group) {
+ result = group->FindGroup(t, n);
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+CombatGroup*
+CombatGroup::Clone(bool deep)
+{
+ CombatGroup* clone = new(__FILE__,__LINE__)
+ CombatGroup(type, id, name, iff, enemy_intel);
+
+ clone->combatant = combatant;
+ clone->region = region;
+ clone->location = location;
+ clone->value = value;
+ clone->expanded = expanded;
+
+ for (int i = 0; i < units.size(); i++) {
+ CombatUnit* u = new(__FILE__,__LINE__) CombatUnit(*units[i]);
+ u->SetCombatGroup(clone);
+ clone->units.append(u);
+ }
+
+ if (deep) {
+ for (i = 0; i < components.size(); i++) {
+ CombatGroup* g = components[i]->Clone(deep);
+ clone->AddComponent(g);
+
+ if (g->Type() == FIGHTER_SQUADRON ||
+ g->Type() == INTERCEPT_SQUADRON ||
+ g->Type() == ATTACK_SQUADRON ||
+ g->Type() == LCA_SQUADRON) {
+
+ if (units.size() > 0) {
+ CombatUnit* carrier = units[0];
+
+ for (int u = 0; u < g->GetUnits().size(); u++) {
+ CombatUnit* unit = g->GetUnits()[u];
+
+ if (unit->Type() >= Ship::FIGHTER ||
+ unit->Type() <= Ship::LCA) {
+ unit->SetCarrier(carrier);
+ unit->SetRegion(carrier->GetRegion());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return clone;
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+CombatGroup::GetOrdinal() const
+{
+ static char ordinal[16];
+
+ int last_two_digits = id % 100;
+
+ if (last_two_digits > 10 && last_two_digits < 20) {
+ sprintf(ordinal, "ordinal.%d", last_two_digits);
+ Text suffix = Game::GetText(ordinal);
+ if (suffix != ordinal)
+ sprintf(ordinal, "%d%s", id, suffix.data());
+ else
+ sprintf(ordinal, "%dth", id);
+ }
+ else {
+ int last_digit = last_two_digits % 10;
+ sprintf(ordinal, "ordinal.%d", last_digit);
+ Text suffix = Game::GetText(ordinal);
+ if (suffix != ordinal)
+ sprintf(ordinal, "%d%s", id, suffix.data());
+ else if (last_digit == 1)
+ sprintf(ordinal, "%dst", id);
+ else if (last_digit == 2)
+ sprintf(ordinal, "%dnd", id);
+ else if (last_digit == 3)
+ sprintf(ordinal, "%drd", id);
+ else
+ sprintf(ordinal, "%dth", id);
+ }
+
+ return ordinal;
+}
+
+const char*
+CombatGroup::GetDescription() const
+{
+ static char desc[256];
+ static char name_desc[256];
+
+ if (name.length())
+ sprintf(name_desc, " \"%s\"", (const char*) name);
+ else
+ name_desc[0] = 0;
+
+ switch (type) {
+ case FORCE: strcpy(desc, (const char*) name); break;
+
+ case FLEET: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.FLEET").data(), name_desc); break;
+ case CARRIER_GROUP: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.CARRIER_GROUP").data(), name_desc); break;
+ case BATTLE_GROUP: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.BATTLE_GROUP").data(), name_desc); break;
+ case DESTROYER_SQUADRON: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.DESTROYER_SQUADRON").data(), name_desc); break;
+
+ case WING: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.WING").data(), name_desc); break;
+ case ATTACK_SQUADRON: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.ATTACK_SQUADRON").data(), name_desc); break;
+ case FIGHTER_SQUADRON: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.FIGHTER_SQUADRON").data(), name_desc); break;
+ case INTERCEPT_SQUADRON: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.INTERCEPT_SQUADRON").data(), name_desc); break;
+ case LCA_SQUADRON: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.LCA_SQUADRON").data(), name_desc); break;
+
+ case BATTALION: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.BATTALION").data(), name_desc); break;
+ case STATION: sprintf(desc, "%s %s", Game::GetText("CombatGroup.STATION").data(), name); break;
+ case STARBASE: sprintf(desc, "%s %d%s", Game::GetText("CombatGroup.STARBASE").data(), id, name_desc); break;
+ case MINEFIELD: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.MINEFIELD").data(), name_desc); break;
+ case BATTERY: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.BATTERY").data(), name_desc); break;
+ case MISSILE: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.MISSILE").data(), name_desc); break;
+
+ case C3I: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.C3I").data(), name_desc); break;
+ case COMM_RELAY: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.COMM_RELAY").data(), name_desc); break;
+ case EARLY_WARNING: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.EARLY_WARNING").data(), name_desc); break;
+ case FWD_CONTROL_CTR: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.FWD_CONTROL_CTR").data(), name_desc); break;
+ case ECM: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.ECM").data(), name_desc); break;
+
+ case SUPPORT: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.SUPPORT").data(), name_desc); break;
+ case COURIER: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.COURIER").data(), name_desc); break;
+ case SUPPLY: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.SUPPLY").data(), name_desc); break;
+ case REPAIR: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.REPAIR").data(), name_desc); break;
+ case MEDICAL: sprintf(desc, "%s %s%s", GetOrdinal(), Game::GetText("CombatGroup.MEDICAL").data(), name_desc); break;
+
+ case CIVILIAN:
+ case WAR_PRODUCTION:
+ case FACTORY:
+ case REFINERY:
+ case RESOURCE: strcpy(desc, (const char*) name); break;
+
+ case INFRASTRUCTURE:
+ case TRANSPORT:
+ case NETWORK:
+ case HABITAT:
+ case STORAGE:
+ case FREIGHT:
+ case PASSENGER:
+ case PRIVATE: strcpy(desc, (const char*) name); break;
+
+ default: sprintf(desc, "%s%s", Game::GetText("CombatGroup.default").data(), name_desc); break;
+ }
+
+ return desc;
+}
+
+const char*
+CombatGroup::GetShortDescription() const
+{
+ static char desc[256];
+
+ switch (type) {
+ case FORCE: strcpy(desc, (const char*) name); break;
+
+ case FLEET: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.FLEET").data()); break;
+ case CARRIER_GROUP: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.CARRIER_GROUP").data()); break;
+ case BATTLE_GROUP: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.BATTLE_GROUP").data()); break;
+ case DESTROYER_SQUADRON: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.DESTROYER_SQUADRON").data()); break;
+
+ case WING: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.WING").data()); break;
+ case ATTACK_SQUADRON: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.ATTACK_SQUADRON").data()); break;
+ case FIGHTER_SQUADRON: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.FIGHTER_SQUADRON").data()); break;
+ case INTERCEPT_SQUADRON: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.INTERCEPT_SQUADRON").data()); break;
+ case LCA_SQUADRON: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.LCA_SQUADRON").data()); break;
+
+ case BATTALION: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.BATTALION").data()); break;
+ case STATION: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.STATION").data()); break;
+ case STARBASE: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.STARBASE").data()); break;
+ case MINEFIELD: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.MINEFIELD").data()); break;
+ case BATTERY: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.BATTERY").data()); break;
+
+ case C3I: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.C3I").data()); break;
+ case COMM_RELAY: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.COMM_RELAY").data()); break;
+ case EARLY_WARNING: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.EARLY_WARNING").data()); break;
+ case FWD_CONTROL_CTR: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.FWD_CONTROL_CTR").data()); break;
+ case ECM: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.ECM").data()); break;
+
+ case SUPPORT: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.SUPPORT").data()); break;
+ case COURIER: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.COURIER").data()); break;
+ case MEDICAL: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.MEDICAL").data()); break;
+ case SUPPLY: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.SUPPLY").data()); break;
+ case REPAIR: sprintf(desc, "%s %s", GetOrdinal(), Game::GetText("CombatGroup.abrv.REPAIR").data()); break;
+
+ case CIVILIAN:
+ case WAR_PRODUCTION:
+ case FACTORY:
+ case REFINERY:
+ case RESOURCE: strcpy(desc, (const char*) name); break;
+
+ case INFRASTRUCTURE:
+ case TRANSPORT:
+ case NETWORK:
+ case HABITAT:
+ case STORAGE:
+ case FREIGHT:
+ case PASSENGER:
+ case PRIVATE: strcpy(desc, (const char*) name); break;
+
+ default: sprintf(desc, "%s", Game::GetText("CombatGroup.abrv.default").data()); break;
+ }
+
+ return desc;
+}
+
+// +--------------------------------------------------------------------+
+
+double
+CombatGroup::GetNextJumpTime() const
+{
+ double t = 0;
+
+ ListIter<CombatUnit> unit = ((CombatGroup*) this)->units;
+ while (++unit)
+ if (unit->GetNextJumpTime() > t)
+ t = unit->GetNextJumpTime();
+
+ return t;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CombatGroup::MoveTo(const Point& loc)
+{
+ location = loc;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CombatGroup::SetAssignedSystem(const char* s)
+{
+ assigned_system = s;
+ assigned_zone = 0;
+ zone_lock = false;
+
+ ListIter<CombatGroup> iter = components;
+ while (++iter) {
+ CombatGroup* g = iter.value();
+ g->SetAssignedSystem(s);
+ }
+}
+
+void
+CombatGroup::SetAssignedZone(CombatZone* z)
+{
+ assigned_zone = z;
+
+ if (!assigned_zone)
+ zone_lock = false;
+
+ ListIter<CombatGroup> iter = components;
+ while (++iter) {
+ CombatGroup* g = iter.value();
+ g->SetAssignedZone(z);
+ }
+}
+
+void
+CombatGroup::ClearUnlockedZones()
+{
+ if (!zone_lock)
+ assigned_zone = 0;
+
+ ListIter<CombatGroup> iter = components;
+ while (++iter) {
+ CombatGroup* g = iter.value();
+ g->ClearUnlockedZones();
+ }
+}
+
+void
+CombatGroup::SetZoneLock(bool lock)
+{
+ if (!assigned_zone)
+ zone_lock = false;
+ else
+ zone_lock = lock;
+
+ if (zone_lock)
+ assigned_system = Text();
+
+ ListIter<CombatGroup> iter = components;
+ while (++iter) {
+ CombatGroup* g = iter.value();
+ g->SetZoneLock(lock);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CombatGroup::SetIntelLevel(int n)
+{
+ if (n < Intel::RESERVE || n > Intel::TRACKED) return;
+
+ enemy_intel = n;
+
+ // if this group has been discovered, the entire
+ // branch of the OOB tree must be exposed. Otherwise,
+ // no missions would ever be planned against this
+ // combat group.
+
+ if (n > Intel::SECRET) {
+ CombatGroup* p = parent;
+ while (p) {
+ if (p->enemy_intel < Intel::KNOWN)
+ p->enemy_intel = Intel::KNOWN;
+
+ p = p->parent;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+CombatGroup::CalcValue()
+{
+ int val = 0;
+
+ ListIter<CombatUnit> unit = units;
+ while (++unit)
+ val += unit->GetValue();
+
+ ListIter<CombatGroup> comp = components;
+ while (++comp)
+ val += comp->CalcValue();
+
+ value = val;
+ return value;
+}
+
+int
+CombatGroup::CountUnits() const
+{
+ int n = 0;
+
+ CombatGroup* g = (CombatGroup*) this;
+
+ ListIter<CombatUnit> unit = g->units;
+ while (++unit)
+ n += unit->Count() - unit->DeadCount();
+
+ CombatGroup* pThis = ((CombatGroup*) this);
+ pThis->live_comp.clear();
+ ListIter<CombatGroup> iter = g->components;
+ while (++iter) {
+ CombatGroup* comp = iter.value();
+
+ if (!comp->IsReserve()) {
+ int unit_count = comp->CountUnits();
+ if (unit_count > 0)
+ pThis->live_comp.append(comp);
+
+ n += unit_count;
+ }
+ }
+
+ return n;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CombatGroup::ClearAssignments()
+{
+ assignments.destroy();
+
+ ListIter<CombatGroup> comp = components;
+ while (++comp)
+ comp->ClearAssignments();
+}
+
+// +--------------------------------------------------------------------+
+
+CombatGroup*
+CombatGroup::FindCarrier()
+{
+ CombatGroup* p = GetParent();
+
+ while (p != 0 &&
+ p->Type() != CombatGroup::CARRIER_GROUP &&
+ p->Type() != CombatGroup::STATION &&
+ p->Type() != CombatGroup::STARBASE)
+ p = p->GetParent();
+
+ if (p && p->GetUnits().size())
+ return p;
+
+ return 0;
+}
+
+CombatUnit*
+CombatGroup::GetRandomUnit()
+{
+ CombatUnit* result = 0;
+ List<CombatUnit> live;
+
+ ListIter<CombatUnit> unit = units;
+ while (++unit) {
+ if (unit->Count() - unit->DeadCount() > 0)
+ live.append(unit.value());
+ }
+
+ if (live.size() > 0) {
+ int ntries = 5;
+ while (!result && ntries-- > 0) {
+ int index = rand() % live.size();
+ result = live[index];
+
+ int ship_class = result->GetShipClass();
+ if (ship_class >= Ship::CRUISER &&
+ ship_class <= Ship::FARCASTER)
+ result = 0;
+ }
+ }
+
+ if (!result) {
+ ListIter<CombatGroup> comp = components;
+ while (++comp && !result) {
+ CombatUnit* u = comp->GetRandomUnit();
+ if (u)
+ result = u;
+ }
+ }
+
+ return result;
+}
+
+CombatUnit*
+CombatGroup::GetFirstUnit()
+{
+ int tmp_index = unit_index;
+ unit_index = 0;
+ CombatUnit* result = GetNextUnit();
+ unit_index = tmp_index;
+
+ return result;
+}
+
+CombatUnit*
+CombatGroup::GetNextUnit()
+{
+ if (units.size() > 0) {
+ List<CombatUnit> live;
+
+ ListIter<CombatUnit> unit = units;
+ while (++unit) {
+ if (unit->Count() - unit->DeadCount() > 0)
+ live.append(unit.value());
+ }
+
+ if (live.size() > 0) {
+ return live[unit_index++ % live.size()];
+ }
+ }
+
+ if (components.size() > 0) {
+ return components[unit_index % components.size()]->GetNextUnit();
+ }
+
+ return 0;
+}
+
+CombatUnit*
+CombatGroup::FindUnit(const char* name)
+{
+ if (units.size() > 0) {
+ ListIter<CombatUnit> iter = units;
+ while (++iter) {
+ CombatUnit* unit = iter.value();
+ if (unit->Name() == name) {
+ if (unit->Count() - unit->DeadCount() > 0)
+ return unit;
+ else
+ return 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void
+CombatGroup::AssignRegion(Text rgn)
+{
+ region = rgn;
+
+ ListIter<CombatGroup> comp = components;
+ while (++comp)
+ comp->AssignRegion(rgn);
+
+ ListIter<CombatUnit> unit = units;
+ while (++unit)
+ unit->SetRegion(rgn);
+}
+
+// +--------------------------------------------------------------------+
+
+static const char* group_name[] = {
+ "",
+ "force",
+ "wing",
+ "intercept_squadron",
+ "fighter_squadron",
+ "attack_squadron",
+ "lca_squadron",
+ "fleet",
+ "destroyer_squadron",
+ "battle_group",
+ "carrier_group",
+ "battalion",
+ "minefield",
+ "battery",
+ "missile",
+ "station",
+ "starbase",
+ "c3i",
+ "comm_relay",
+ "early_warning",
+ "fwd_control_ctr",
+ "ecm",
+ "support",
+ "courier",
+ "medical",
+ "supply",
+ "repair",
+ "civilian",
+ "war_production",
+ "factory",
+ "refinery",
+ "resource",
+ "infrastructure",
+ "transport",
+ "network",
+ "habitat",
+ "storage",
+ "non_com",
+ "freight",
+ "passenger",
+ "private"
+};
+
+// +--------------------------------------------------------------------+
+
+int
+CombatGroup::TypeFromName(const char* type_name)
+{
+ for (int i = FORCE; i < PRIVATE; i++)
+ if (!stricmp(type_name, group_name[i]))
+ return i;
+
+ return 0;
+}
+
+const char*
+CombatGroup::NameFromType(int type)
+{
+ return group_name[type];
+}
+
+// +--------------------------------------------------------------------+
+
+int ShipClassFromName(const char* type_name)
+{
+ return Ship::ClassForName(type_name);
+}
+
+// +--------------------------------------------------------------------+
+
+#define GET_DEF_BOOL(n) if (pdef->name()->value()==(#n)) GetDefBool((n), pdef, filename)
+#define GET_DEF_TEXT(n) if (pdef->name()->value()==(#n)) GetDefText((n), pdef, filename)
+#define GET_DEF_NUM(n) if (pdef->name()->value()==(#n)) GetDefNumber((n), pdef, filename)
+#define GET_DEF_VEC(n) if (pdef->name()->value()==(#n)) GetDefVec((n), pdef, filename)
+
+CombatGroup*
+CombatGroup::LoadOrderOfBattle(const char* filename, int team, Combatant* combatant)
+{
+ CombatGroup* force = 0;
+ DataLoader* loader = DataLoader::GetLoader();
+ BYTE* block;
+ loader->LoadBuffer(filename, block, true);
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse order of battle '%s'\n", filename);
+ return 0;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "ORDER_OF_BATTLE") {
+ Print("ERROR: invalid Order of Battle file '%s'\n", filename);
+ term->print(10);
+ return 0;
+ }
+ }
+
+
+ do {
+ delete term; term = 0;
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "group") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: group struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+
+ char name[256];
+ char type[64];
+ char intel[64];
+ char region[64];
+ char system[64];
+ char parent_type[64];
+ int parent_id = 0;
+ int id = 0;
+ int iff = -1;
+ Vec3 loc = Vec3(1.0e9f,0.0f,0.0f);
+
+ List<CombatUnit> unit_list;
+ char unit_name[64];
+ char unit_regnum[16];
+ char unit_design[64];
+ char unit_skin[64];
+ int unit_class = 0;
+ int unit_count = 1;
+ int unit_dead = 0;
+ int unit_damage = 0;
+ int unit_heading= 0;
+ int unit_index = 0;
+
+ *name = 0;
+ *type = 0;
+ *intel = 0;
+ *region = 0;
+ *system = 0;
+ *parent_type = 0;
+ *unit_name = 0;
+ *unit_regnum = 0;
+ *unit_design = 0;
+ *unit_skin = 0;
+
+ strcpy(intel, "KNOWN");
+
+ // all groups in this OOB default to the IFF of the main force
+ if (force)
+ iff = force->GetIFF();
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef && (iff < 0 || team < 0 || iff == team)) {
+ GET_DEF_TEXT(name);
+ else GET_DEF_TEXT(type);
+ else GET_DEF_TEXT(intel);
+ else GET_DEF_TEXT(region);
+ else GET_DEF_TEXT(system);
+ else GET_DEF_VEC(loc);
+ else GET_DEF_TEXT(parent_type);
+ else GET_DEF_NUM(parent_id);
+ else GET_DEF_NUM(iff);
+ else GET_DEF_NUM(id);
+ else GET_DEF_NUM(unit_index);
+
+ else if ((iff == team || team < 0) && pdef->name()->value() == "unit") {
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: unit struct missing for group '%s' in '%s'\n", name, filename);
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+
+ char unit_region[64];
+ char design[256];
+ Vec3 unit_loc = Vec3(1.0e9f,0.0f,0.0f);
+ unit_count = 1;
+
+ ZeroMemory(unit_region, sizeof(unit_region));
+ ZeroMemory(design, sizeof(design));
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "name") {
+ GetDefText(unit_name, pdef, filename);
+ }
+ else if (pdef->name()->value() == "regnum") {
+ GetDefText(unit_regnum, pdef, filename);
+ }
+ else if (pdef->name()->value() == "region") {
+ GetDefText(unit_region, pdef, filename);
+ }
+ else if (pdef->name()->value() == "loc") {
+ GetDefVec(unit_loc, pdef, filename);
+ }
+ else if (pdef->name()->value() == "type") {
+ char typestr[32];
+ GetDefText(typestr, pdef, filename);
+ unit_class = ShipDesign::ClassForName(typestr);
+ }
+ else if (pdef->name()->value() == "design") {
+ GetDefText(unit_design, pdef, filename);
+ }
+ else if (pdef->name()->value() == "skin") {
+ GetDefText(unit_skin, pdef, filename);
+ }
+ else if (pdef->name()->value() == "count") {
+ GetDefNumber(unit_count, pdef, filename);
+ }
+ else if (pdef->name()->value() == "dead_count") {
+ GetDefNumber(unit_dead, pdef, filename);
+ }
+ else if (pdef->name()->value() == "damage") {
+ GetDefNumber(unit_damage, pdef, filename);
+ }
+ else if (pdef->name()->value() == "heading") {
+ GetDefNumber(unit_heading, pdef, filename);
+ }
+ }
+ }
+
+ if (!ShipDesign::CheckName(unit_design)) {
+ Print("ERROR: invalid design '%s' for unit '%s' in '%s'\n", unit_design, unit_name, filename);
+ return 0;
+ }
+
+ CombatUnit* cu = new(__FILE__,__LINE__) CombatUnit(unit_name, unit_regnum, unit_class, unit_design, unit_count, iff);
+ cu->SetRegion(unit_region);
+ cu->SetSkin(unit_skin);
+ cu->MoveTo(unit_loc);
+ cu->Kill(unit_dead);
+ cu->SetSustainedDamage(unit_damage);
+ cu->SetHeading(unit_heading * DEGREES);
+ unit_list.append(cu);
+ }
+ }
+ }
+ } // elements
+
+ if (iff >= 0 && (iff == team || team < 0)) {
+ CombatGroup* parent_group = 0;
+
+ if (force) {
+ parent_group = force->FindGroup(TypeFromName(parent_type), parent_id);
+ }
+
+ CombatGroup* g = new(__FILE__,__LINE__)
+ CombatGroup(TypeFromName(type), id, name, iff, Intel::IntelFromName(intel), parent_group);
+
+ g->region = region;
+ g->combatant = combatant;
+ g->unit_index = unit_index;
+
+ if (loc.x >= 1e9) {
+ if (parent_group)
+ g->location = parent_group->location;
+ else
+ g->location = Vec3(0,0,0);
+ }
+ else {
+ g->location = loc;
+ }
+
+ if (unit_list.size()) {
+ unit_list[0]->SetLeader(true);
+
+ ListIter<CombatUnit> u = unit_list;
+ while (++u) {
+ u->SetCombatGroup(g);
+
+ if (u->GetRegion().length() < 1) {
+ u->SetRegion(g->GetRegion());
+ u->MoveTo(g->Location());
+ }
+
+ if (parent_group &&
+ (u->Type() == Ship::FIGHTER ||
+ u->Type() == Ship::ATTACK)) {
+
+ CombatUnit* carrier = 0;
+ CombatGroup* p = parent_group;
+
+ while (p && !carrier) {
+ if (p->units.size() && p->units[0]->Type() == Ship::CARRIER) {
+ carrier = p->units[0];
+ u->SetCarrier(carrier);
+ u->SetRegion(carrier->GetRegion());
+ }
+
+ p = p->parent;
+ }
+ }
+ }
+
+ g->units.append(unit_list);
+ }
+
+ if (!force)
+ force = g;
+ } // iff == team?
+ } // group-struct
+ } // group
+ } // def
+ } // term
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+ Print("Order of Battle Loaded (%s).\n", force ? force->Name() : "unknown force");
+
+ if (force)
+ force->CalcValue();
+
+ return force;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CombatGroup::MergeOrderOfBattle(BYTE* block, const char* filename, int team, Combatant* combatant, Campaign* campaign)
+{
+ CombatGroup* force = 0;
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse order of battle '%s'\n", filename);
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "SAVEGAME") {
+ Print("ERROR: invalid Save Game file '%s'\n", filename);
+ term->print(10);
+ return;
+ }
+ }
+
+
+ do {
+ delete term; term = 0;
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "group") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: group struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+
+ char name[256];
+ char type[64];
+ char intel[64];
+ char region[64];
+ char system[64];
+ char zone[64];
+ bool zone_locked = false;
+ int id = 0;
+ int iff = -1;
+ int sorties = -1;
+ int kills = -1;
+ int points = -1;
+ Vec3 loc = Vec3(1.0e9f,0.0f,0.0f);
+
+ List<CombatUnit> unit_list;
+ char unit_name[64];
+ char unit_regnum[16];
+ char unit_design[64];
+ int unit_class = 0;
+ int unit_count = 1;
+ int unit_dead = 0;
+ int unit_damage = 0;
+ int unit_heading= 0;
+ int unit_index = 0;
+
+ *name = 0;
+ *type = 0;
+ *intel = 0;
+ *region = 0;
+ *system = 0;
+ *zone = 0;
+ *unit_name = 0;
+ *unit_regnum = 0;
+ *unit_design = 0;
+
+ strcpy(intel, "KNOWN");
+
+ // all groups in this OOB default to the IFF of the main force
+ if (force)
+ iff = force->GetIFF();
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef && (iff < 0 || team < 0 || iff == team)) {
+ GET_DEF_TEXT(name);
+ else GET_DEF_TEXT(type);
+ else GET_DEF_TEXT(intel);
+ else GET_DEF_TEXT(region);
+ else GET_DEF_TEXT(system);
+ else GET_DEF_TEXT(zone);
+ else GET_DEF_BOOL(zone_locked);
+ else GET_DEF_VEC(loc);
+ else GET_DEF_NUM(iff);
+ else GET_DEF_NUM(id);
+ else GET_DEF_NUM(sorties);
+ else GET_DEF_NUM(kills);
+ else GET_DEF_NUM(points);
+ else GET_DEF_NUM(unit_index);
+
+ else if ((iff == team || team < 0) && pdef->name()->value() == "unit") {
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: unit struct missing for group '%s' in '%s'\n", name, filename);
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+
+ char unit_region[64];
+ char design[256];
+ Vec3 unit_loc=Vec3(0.0f,0.0f,0.0f);
+ unit_count = 1;
+
+ ZeroMemory(unit_region, sizeof(unit_region));
+ ZeroMemory(design, sizeof(design));
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "name") {
+ GetDefText(unit_name, pdef, filename);
+ }
+ else if (pdef->name()->value() == "regnum") {
+ GetDefText(unit_regnum, pdef, filename);
+ }
+ else if (pdef->name()->value() == "region") {
+ GetDefText(unit_region, pdef, filename);
+ }
+ else if (pdef->name()->value() == "loc") {
+ GetDefVec(unit_loc, pdef, filename);
+ }
+ else if (pdef->name()->value() == "type") {
+ char typestr[32];
+ GetDefText(typestr, pdef, filename);
+ unit_class = ShipDesign::ClassForName(typestr);
+ }
+ else if (pdef->name()->value() == "design") {
+ GetDefText(unit_design, pdef, filename);
+ }
+ else if (pdef->name()->value() == "count") {
+ GetDefNumber(unit_count, pdef, filename);
+ }
+ else if (pdef->name()->value() == "dead_count") {
+ GetDefNumber(unit_dead, pdef, filename);
+ }
+ else if (pdef->name()->value() == "damage") {
+ GetDefNumber(unit_damage, pdef, filename);
+ }
+ else if (pdef->name()->value() == "heading") {
+ GetDefNumber(unit_heading, pdef, filename);
+ }
+ }
+ }
+
+ if (!ShipDesign::CheckName(unit_design)) {
+ Print("ERROR: invalid design '%s' for unit '%s' in '%s'\n", unit_design, unit_name, filename);
+ return;
+ }
+
+ if (force) {
+ CombatUnit* cu = new(__FILE__,__LINE__) CombatUnit(unit_name, unit_regnum, unit_class, unit_design, unit_count, iff);
+ cu->SetRegion(unit_region);
+ cu->MoveTo(unit_loc);
+ cu->Kill(unit_dead);
+ cu->SetSustainedDamage(unit_damage);
+ cu->SetHeading(unit_heading * DEGREES);
+ unit_list.append(cu);
+ }
+ }
+ }
+ }
+ } // elements
+
+ if (iff >= 0 && (iff == team || team < 0)) {
+ // have we found the force group we are looking for yet?
+ if (!force && !stricmp(name, combatant->Name())) {
+ force = combatant->GetForce();
+ }
+
+ else {
+ if (!force)
+ continue;
+
+ // if we already have a force, and we find a second one,
+ // it must be the start of a different combatant.
+ // So don't process any further:
+ if (TypeFromName(type) == CombatGroup::FORCE) {
+ break;
+ }
+ }
+
+ CombatGroup* g = force->FindGroup(TypeFromName(type), id);
+
+ if (!g) {
+ ::Print("WARNING: unexpected combat group %s %d '%s' in '%s'\n", type, id, name, filename);
+ continue;
+ }
+
+ g->region = region;
+ g->combatant = combatant;
+ g->location = loc;
+ g->enemy_intel = Intel::IntelFromName(intel);
+ g->unit_index = unit_index;
+
+ if (*zone) {
+ CombatZone* combat_zone = campaign->GetZone(zone);
+
+ if (combat_zone) {
+ g->SetAssignedZone(combat_zone);
+ g->SetZoneLock(zone_locked);
+ }
+ else {
+ ::Print("WARNING: could not find combat zone '%s' for group %s %d '%s' in '%s'\n", zone, type, id, name, filename);
+ }
+ }
+ else if (*system) {
+ g->SetAssignedSystem(system);
+ }
+
+ if (sorties >= 0) g->SetSorties(sorties);
+ if (kills >= 0) g->SetKills(kills);
+ if (points >= 0) g->SetPoints(points);
+
+ if (unit_list.size()) {
+ ListIter<CombatUnit> u_iter = unit_list;
+ while (++u_iter) {
+ CombatUnit* load_unit = u_iter.value();
+ CombatUnit* u = g->FindUnit(load_unit->Name());
+
+ if (u) {
+ if (load_unit->GetRegion().length() > 0) {
+ u->SetRegion(load_unit->GetRegion());
+ u->MoveTo(load_unit->Location());
+ }
+ else {
+ u->SetRegion(g->GetRegion());
+ u->MoveTo(g->Location());
+ }
+ u->SetDeadCount(load_unit->DeadCount());
+ u->SetSustainedDamage(load_unit->GetSustainedDamage());
+ u->SetHeading(load_unit->GetHeading());
+ }
+ }
+
+ unit_list.destroy();
+ }
+
+ if (!force)
+ force = g;
+ } // iff == team?
+ } // group-struct
+ } // group
+ } // def
+ } // term
+ }
+ while (term);
+
+ Print("Order of Battle Loaded (%s).\n", force ? force->Name() : "unknown force");
+
+ if (force)
+ force->CalcValue();
+}
+
+// +--------------------------------------------------------------------+
+
+Text FormatNumber(double n)
+{
+ char buffer[64];
+
+ if (fabs(n) < 1000)
+ sprintf(buffer, "%d", (int) n);
+
+ else if (fabs(n) < 1e6) {
+ int nn = (int) n / 1000;
+ sprintf(buffer, "%de3", nn);
+ }
+
+ else
+ sprintf(buffer, "%g", n);
+
+ return buffer;
+}
+
+void
+SaveCombatUnit(FILE* f, CombatUnit* u)
+{
+ int type = u->Type();
+
+ if (type == 0 && u->GetDesign())
+ type = u->GetDesign()->type;
+
+ fprintf(f, "\n unit: {");
+ fprintf(f, " name: \"%s\",", u->Name().data());
+ fprintf(f, " type: \"%s\",", Ship::ClassName(type));
+ fprintf(f, " design: \"%s\",", u->DesignName().data());
+
+ if (u->Count() > 1) {
+ fprintf(f, " count: %d,", u->Count());
+ }
+ else {
+ fprintf(f, " regnum:\"%s\",", u->Registry().data());
+ }
+
+ if (u->GetRegion().length() > 0) {
+ fprintf(f, " region:\"%s\",", u->GetRegion().data());
+
+ Text x = FormatNumber(u->Location().x);
+ Text y = FormatNumber(u->Location().y);
+ Text z = FormatNumber(u->Location().z);
+
+ fprintf(f, " loc:(%s, %s, %s),", x.data(), y.data(), z.data());
+ }
+
+ fprintf(f, " dead_count: %d, damage: %d, heading: %d },",
+ (int) u->DeadCount(),
+ (int) u->GetSustainedDamage(),
+ (int) (u->GetHeading() / DEGREES));
+}
+
+void
+SaveCombatGroup(FILE* f, CombatGroup* g)
+{
+ fprintf(f, "group: {");
+ fprintf(f, " type: %s,", CombatGroup::NameFromType(g->Type()));
+ fprintf(f, " id: %d,", g->GetID());
+ fprintf(f, " name: \"%s\",", g->Name());
+ fprintf(f, " intel: %s,", Intel::NameFromIntel(g->IntelLevel()));
+ fprintf(f, " iff: %d,", g->GetIFF());
+ fprintf(f, " unit_index: %d,", g->UnitIndex());
+
+ if (g->GetRegion().length()) {
+ fprintf(f, " region:\"%s\",", g->GetRegion());
+ }
+
+ if (g->GetAssignedSystem().length()) {
+ fprintf(f, " system: \"%s\",", g->GetAssignedSystem().data());
+ }
+
+ if (g->GetAssignedZone()) {
+ fprintf(f, " zone: \"%s\",", g->GetAssignedZone()->Name().data());
+ if (g->IsZoneLocked()) {
+ fprintf(f, " zone_locked: true,");
+ }
+ }
+
+ Text x = FormatNumber(g->Location().x);
+ Text y = FormatNumber(g->Location().y);
+ Text z = FormatNumber(g->Location().z);
+
+ fprintf(f, " loc: (%s, %s, %s),", x.data(), y.data(), z.data());
+
+ CombatGroup* parent = g->GetParent();
+ if (parent) {
+ fprintf(f, " parent_type:%s,", CombatGroup::NameFromType(parent->Type()));
+ fprintf(f, " parent_id:%d,", parent->GetID());
+ }
+
+ fprintf(f, " sorties: %d,", g->Sorties());
+ fprintf(f, " kills: %d,", g->Kills());
+ fprintf(f, " points: %d,", g->Points());
+
+ ListIter<CombatUnit> u = g->GetUnits();
+ while (++u) {
+ SaveCombatUnit(f, u.value());
+ }
+
+ fprintf(f, " }\n");
+
+ ListIter<CombatGroup> c = g->GetComponents();
+ while (++c) {
+ SaveCombatGroup(f, c.value());
+ }
+}
+
+void
+CombatGroup::SaveOrderOfBattle(const char* filename, CombatGroup* force)
+{
+ FILE* f = ::fopen(filename, "a+");
+
+ if (f) {
+ SaveCombatGroup(f, force);
+ fprintf(f, "\n");
+ fclose(f);
+ }
+}
diff --git a/Stars45/CombatGroup.h b/Stars45/CombatGroup.h
new file mode 100644
index 0000000..54c4119
--- /dev/null
+++ b/Stars45/CombatGroup.h
@@ -0,0 +1,231 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CombatGroup.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#ifndef CombatGroup_h
+#define CombatGroup_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Text.h"
+#include "List.h"
+#include "Intel.h"
+
+// +--------------------------------------------------------------------+
+
+class Campaign;
+class Combatant;
+class CombatGroup;
+class CombatUnit;
+class CombatZone;
+class CombatAssignment;
+
+// +--------------------------------------------------------------------+
+
+class CombatGroup
+{
+public:
+ static const char* TYPENAME() { return "CombatGroup"; }
+
+ enum GROUP_TYPE {
+ FORCE = 1, // Commander In Chief
+
+ WING, // Air Force
+ INTERCEPT_SQUADRON, // a2a fighter
+ FIGHTER_SQUADRON, // multi-role fighter
+ ATTACK_SQUADRON, // strike / attack
+ LCA_SQUADRON, // landing craft
+
+ FLEET, // Navy
+ DESTROYER_SQUADRON, // destroyer
+ BATTLE_GROUP, // heavy cruiser(s)
+ CARRIER_GROUP, // fleet carrier
+
+ BATTALION, // Army
+ MINEFIELD,
+ BATTERY,
+ MISSILE,
+ STATION, // orbital station
+ STARBASE, // planet-side base
+
+ C3I, // Command, Control, Communications, Intelligence
+ COMM_RELAY,
+ EARLY_WARNING,
+ FWD_CONTROL_CTR,
+ ECM,
+
+ SUPPORT,
+ COURIER,
+ MEDICAL,
+ SUPPLY,
+ REPAIR,
+
+ CIVILIAN, // root for civilian groups
+
+ WAR_PRODUCTION,
+ FACTORY,
+ REFINERY,
+ RESOURCE,
+
+ INFRASTRUCTURE,
+ TRANSPORT,
+ NETWORK,
+ HABITAT,
+ STORAGE,
+
+ NON_COM, // other civilian traffic
+ FREIGHT,
+ PASSENGER,
+ PRIVATE
+ };
+
+ CombatGroup(int t, int n, const char* s, int i, int e, CombatGroup* p=0);
+ ~CombatGroup();
+
+ // comparison operators are used to sort combat groups into a priority list
+ // in DESCENDING order, so the sense of the comparison is backwards from
+ // usual...
+ int operator < (const CombatGroup& g) const { return value > g.value; }
+ int operator <= (const CombatGroup& g) const { return value >= g.value; }
+ int operator == (const CombatGroup& g) const { return this == &g; }
+
+ // operations:
+ static CombatGroup* LoadOrderOfBattle(const char* fname, int iff, Combatant* combatant);
+ static void SaveOrderOfBattle(const char* fname, CombatGroup* force);
+ static void MergeOrderOfBattle(BYTE* block, const char* fname, int iff, Combatant* combatant, Campaign* campaign);
+
+ void AddComponent(CombatGroup* g);
+ CombatGroup* FindGroup(int t, int n=-1);
+ CombatGroup* Clone(bool deep=true);
+
+ // accessors and mutators:
+ const char* GetDescription() const;
+ const char* GetShortDescription() const;
+
+ void SetCombatant(Combatant* c) { combatant = c; }
+
+ Combatant* GetCombatant() { return combatant; }
+ CombatGroup* GetParent() { return parent; }
+ List<CombatGroup>& GetComponents() { return components; }
+ List<CombatGroup>& GetLiveComponents() { return live_comp; }
+ List<CombatUnit>& GetUnits() { return units; }
+ CombatUnit* GetRandomUnit();
+ CombatUnit* GetFirstUnit();
+ CombatUnit* GetNextUnit();
+ CombatUnit* FindUnit(const char* name);
+ CombatGroup* FindCarrier();
+
+ const Text& Name() const { return name; }
+ int Type() const { return type; }
+ int CountUnits() const;
+ int IntelLevel() const { return enemy_intel;}
+ int GetID() const { return id; }
+ int GetIFF() const { return iff; }
+ Point Location() const { return location; }
+ void MoveTo(const Point& loc);
+ const Text& GetRegion() const { return region; }
+ void SetRegion(Text rgn) { region = rgn; }
+ void AssignRegion(Text rgn);
+ int Value() const { return value; }
+ int Sorties() const { return sorties; }
+ void SetSorties(int n) { sorties = n; }
+ int Kills() const { return kills; }
+ void SetKills(int n) { kills = n; }
+ int Points() const { return points; }
+ void SetPoints(int n) { points = n; }
+ int UnitIndex() const { return unit_index; }
+
+ double GetNextJumpTime() const;
+
+ double GetPlanValue() const { return plan_value; }
+ void SetPlanValue(double v) { plan_value = v; }
+
+ bool IsAssignable() const;
+ bool IsTargetable() const;
+ bool IsDefensible() const;
+ bool IsStrikeTarget() const;
+ bool IsMovable() const;
+ bool IsFighterGroup() const;
+ bool IsStarshipGroup() const;
+ bool IsReserve() const;
+
+ // these two methods return zero terminated arrays of
+ // integers identifying the preferred assets for attack
+ // or defense in priority order:
+ static const int* PreferredAttacker(int type);
+ static const int* PreferredDefender(int type);
+
+ bool IsExpanded() const { return expanded; }
+ void SetExpanded(bool e) { expanded = e; }
+
+ const Text& GetAssignedSystem() const { return assigned_system; }
+ void SetAssignedSystem(const char* s);
+ CombatZone* GetCurrentZone() const { return current_zone; }
+ void SetCurrentZone(CombatZone* z) { current_zone = z; }
+ CombatZone* GetAssignedZone() const { return assigned_zone; }
+ void SetAssignedZone(CombatZone* z);
+ void ClearUnlockedZones();
+ bool IsZoneLocked() const { return assigned_zone && zone_lock; }
+ void SetZoneLock(bool lock=true);
+ bool IsSystemLocked() const { return assigned_system.length() > 0; }
+
+ const Text& GetStrategicDirection() const { return strategic_direction; }
+ void SetStrategicDirection(Text dir) { strategic_direction = dir; }
+
+ void SetIntelLevel(int n);
+ int CalcValue();
+
+ List<CombatAssignment>& GetAssignments() { return assignments; }
+ void ClearAssignments();
+
+ static int TypeFromName(const char* name);
+ static const char* NameFromType(int type);
+
+private:
+ const char* GetOrdinal() const;
+
+ // attributes:
+ int type;
+ int id;
+ Text name;
+ int iff;
+ int enemy_intel;
+
+ double plan_value; // scratch pad for plan modules
+
+ List<CombatUnit> units;
+ List<CombatGroup> components;
+ List<CombatGroup> live_comp;
+ Combatant* combatant;
+ CombatGroup* parent;
+ Text region;
+ Point location;
+ int value;
+ int unit_index;
+
+ int sorties;
+ int kills;
+ int points;
+
+ bool expanded; // for tree control
+
+ Text assigned_system;
+ CombatZone* current_zone;
+ CombatZone* assigned_zone;
+ bool zone_lock;
+ List<CombatAssignment> assignments;
+
+ Text strategic_direction;
+};
+
+#endif CombatGroup_h
+
diff --git a/Stars45/CombatRoster.cpp b/Stars45/CombatRoster.cpp
new file mode 100644
index 0000000..66f7455
--- /dev/null
+++ b/Stars45/CombatRoster.cpp
@@ -0,0 +1,93 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CombatRoster.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ The complete roster of all known persistent entities
+ for all combatants in the game.
+*/
+
+#include "MemDebug.h"
+#include "CombatRoster.h"
+#include "CombatGroup.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+static CombatRoster* roster = 0;
+
+// +--------------------------------------------------------------------+
+
+CombatRoster::CombatRoster()
+{
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Campaigns/");
+
+ List<Text> files;
+ loader->ListFiles("*.def", files);
+
+ for (int i = 0; i < files.size(); i++) {
+ Text filename = *files[i];
+
+ if (!filename.contains("/") && !filename.contains("\\")) {
+ loader->SetDataPath("Campaigns/");
+ CombatGroup* g = CombatGroup::LoadOrderOfBattle(filename, -1, 0);
+ forces.append(g);
+ }
+ }
+
+ files.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+CombatRoster::~CombatRoster()
+{
+ forces.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+CombatGroup*
+CombatRoster::GetForce(const char* name)
+{
+ ListIter<CombatGroup> iter = forces;
+ while (++iter) {
+ CombatGroup* f = iter.value();
+
+ if (f->Name() == name)
+ return f;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CombatRoster::Initialize()
+{
+ roster = new(__FILE__,__LINE__) CombatRoster();
+}
+
+void
+CombatRoster::Close()
+{
+ delete roster;
+ roster = 0;
+}
+
+CombatRoster*
+CombatRoster::GetInstance()
+{
+ return roster;
+} \ No newline at end of file
diff --git a/Stars45/CombatRoster.h b/Stars45/CombatRoster.h
new file mode 100644
index 0000000..2c1f4c8
--- /dev/null
+++ b/Stars45/CombatRoster.h
@@ -0,0 +1,48 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CombatRoster.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ The complete roster of all known persistent entities
+ for all combatants in the game.
+*/
+
+#ifndef CombatRoster_h
+#define CombatRoster_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class CombatGroup;
+
+// +--------------------------------------------------------------------+
+
+class CombatRoster
+{
+ CombatRoster();
+ ~CombatRoster();
+
+public:
+ static const char* TYPENAME() { return "CombatRoster"; }
+
+ static void Initialize();
+ static void Close();
+ static CombatRoster* GetInstance();
+
+ CombatGroup* GetForce(const char* name);
+
+private:
+ List<CombatGroup> forces;
+};
+
+
+#endif CombatRoster_h
diff --git a/Stars45/CombatUnit.cpp b/Stars45/CombatUnit.cpp
new file mode 100644
index 0000000..cdbf993
--- /dev/null
+++ b/Stars45/CombatUnit.cpp
@@ -0,0 +1,402 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CombatUnit.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ A ship, station, or ground unit in the dynamic campaign.
+*/
+
+#include "MemDebug.h"
+#include "CombatUnit.h"
+#include "CombatGroup.h"
+#include "Campaign.h"
+#include "ShipDesign.h"
+#include "Ship.h"
+
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+inline double random() { return (double) rand() / (double) RAND_MAX; }
+
+// +----------------------------------------------------------------------+
+
+CombatUnit::CombatUnit(const char* n, const char* reg, int t, const char* d, int c, int i)
+ : name(n), regnum(reg), type(t), design_name(d), design(0),
+ count(c), iff(i), leader(false), dead_count(0), available(c),
+ carrier(0), plan_value(0), launch_time(-1e6), jump_time(0),
+ sustained_damage(0), target(0), group(0), heading(0)
+{ }
+
+CombatUnit::CombatUnit(const CombatUnit& u)
+ : name(u.name), regnum(u.regnum), type(u.type), design_name(u.design_name),
+ design(u.design), count(u.count), iff(u.iff),
+ dead_count(u.dead_count), available(u.available),
+ leader(u.leader), region(u.region), location(u.location),
+ carrier(u.carrier), plan_value(0), launch_time(u.launch_time),
+ jump_time(u.jump_time), sustained_damage(u.sustained_damage),
+ target(0), group(0), heading(u.heading)
+{ }
+
+// +----------------------------------------------------------------------+
+
+const ShipDesign*
+CombatUnit::GetDesign()
+{
+ if (!design)
+ design = ShipDesign::Get(design_name);
+
+ return design;
+}
+
+int
+CombatUnit::GetShipClass() const
+{
+ if (design)
+ return design->type;
+
+ return type;
+}
+
+int
+CombatUnit::GetValue() const
+{
+ return GetSingleValue() * LiveCount();
+}
+
+int
+CombatUnit::GetSingleValue() const
+{
+ return Ship::Value(GetShipClass());
+}
+
+// +----------------------------------------------------------------------+
+
+const char*
+CombatUnit::GetDescription() const
+{
+ if (!design) {
+ CombatUnit* pThis = (CombatUnit*) this; // cast-away const
+ pThis->GetDesign();
+ }
+
+ static char desc[256];
+
+ if (!design) {
+ strcpy(desc, Game::GetText("[unknown]").data());
+ }
+
+ else if (count > 1) {
+ sprintf(desc, "%dx %s %s", LiveCount(), design->abrv, design->DisplayName());
+ }
+
+ else {
+ if (regnum.length() > 0)
+ sprintf(desc, "%s-%s %s", design->abrv, (const char*) regnum, (const char*) name);
+ else
+ sprintf(desc, "%s %s", design->abrv, (const char*) name);
+
+ if (dead_count > 0) {
+ strcat(desc, " ");
+ strcat(desc, Game::GetText("killed.in.action"));
+ }
+ }
+
+ return desc;
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+CombatUnit::CanAssign() const
+{
+ bool result = false;
+
+ switch (type) {
+ case Ship::FIGHTER:
+ case Ship::ATTACK:
+ case Ship::CORVETTE:
+ case Ship::FRIGATE:
+ case Ship::DESTROYER:
+ case Ship::CRUISER:
+ case Ship::CARRIER: result = true; break;
+ }
+
+ return result;
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+CombatUnit::CanLaunch() const
+{
+ bool result = false;
+
+ switch (type) {
+ case Ship::FIGHTER:
+ case Ship::ATTACK: result = (Campaign::Stardate() - launch_time) >= 300;
+ break;
+
+ case Ship::CORVETTE:
+ case Ship::FRIGATE:
+ case Ship::DESTROYER:
+ case Ship::CRUISER:
+ case Ship::CARRIER: result = true;
+ break;
+ }
+
+ return result;
+}
+
+// +----------------------------------------------------------------------+
+
+Color
+CombatUnit::MarkerColor() const
+{
+ return Ship::IFFColor(iff);
+}
+
+bool
+CombatUnit::IsGroundUnit() const
+{
+ return (design && (design->type & Ship::GROUND_UNITS)) ? true : false;
+}
+
+bool
+CombatUnit::IsStarship() const
+{
+ return (design && (design->type & Ship::STARSHIPS)) ? true : false;
+}
+
+bool
+CombatUnit::IsDropship() const
+{
+ return (design && (design->type & Ship::DROPSHIPS)) ? true : false;
+}
+
+bool
+CombatUnit::IsStatic() const
+{
+ return design && (design->type >= Ship::STATION);
+}
+
+// +----------------------------------------------------------------------+
+
+double
+CombatUnit::MaxRange() const
+{
+ return 100e3;
+}
+
+double CombatUnit::MaxEffectiveRange() const
+{
+ return 50e3;
+}
+
+double CombatUnit::OptimumRange() const
+{
+ if (type == Ship::FIGHTER || type == Ship::ATTACK)
+ return 15e3;
+
+ return 30e3;
+}
+
+// +----------------------------------------------------------------------+
+
+bool CombatUnit::CanDefend(CombatUnit* unit) const
+{
+ if (unit == 0 || unit == this)
+ return false;
+
+ if (type > Ship::STATION)
+ return false;
+
+ double distance = (location - unit->location).length();
+
+ if (type > unit->type)
+ return false;
+
+ if (distance > MaxRange())
+ return false;
+
+ return true;
+}
+
+// +----------------------------------------------------------------------+
+
+double CombatUnit::PowerVersus(CombatUnit* tgt) const
+{
+ if (tgt == 0 || tgt == this || available < 1)
+ return 0;
+
+ if (type > Ship::STATION)
+ return 0;
+
+ double effectiveness = 1;
+ double distance = (location - tgt->location).length();
+
+ if (distance > MaxRange())
+ return 0;
+
+ if (distance > MaxEffectiveRange())
+ effectiveness = 0.5;
+
+ if (type == Ship::FIGHTER) {
+ if (tgt->type == Ship::FIGHTER || tgt->type == Ship::ATTACK)
+ return Ship::FIGHTER * 2 * available * effectiveness;
+ else
+ return 0;
+ }
+ else if (type == Ship::ATTACK) {
+ if (tgt->type > Ship::ATTACK)
+ return Ship::ATTACK * 3 * available * effectiveness;
+ else
+ return 0;
+ }
+ else if (type == Ship::CARRIER) {
+ return 0;
+ }
+ else if (type == Ship::CRUISER) {
+ if (tgt->type <= Ship::ATTACK)
+ return type * effectiveness;
+
+ else
+ return 0;
+ }
+ else {
+ if (tgt->type > Ship::ATTACK)
+ return type * effectiveness;
+ else
+ return type * 0.1 * effectiveness;
+ }
+
+ return 0;
+}
+
+// +----------------------------------------------------------------------+
+
+int
+CombatUnit::AssignMission()
+{
+ int assign = count;
+
+ if (count > 4)
+ assign = 4;
+
+ if (assign > 0) {
+ available -= assign;
+ launch_time = Campaign::Stardate();
+ return assign;
+ }
+
+ return 0;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+CombatUnit::CompleteMission()
+{
+ Disengage();
+
+ if (count > 4)
+ available += 4;
+
+ else
+ available += count;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+CombatUnit::MoveTo(const Point& loc)
+{
+ if (!carrier)
+ location = loc;
+ else
+ location = carrier->location;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+CombatUnit::Engage(CombatUnit* tgt)
+{
+ if (!tgt)
+ Disengage();
+
+ else if (!tgt->attackers.contains(this))
+ tgt->attackers.append(this);
+
+ target = tgt;
+}
+
+void
+CombatUnit::Disengage()
+{
+ if (target)
+ target->attackers.remove(this);
+
+ target = 0;
+}
+
+// +----------------------------------------------------------------------+
+
+static int KillGroup(CombatGroup* group)
+{
+ int value_killed = 0;
+
+ if (group) {
+ ListIter<CombatUnit> u_iter = group->GetUnits();
+ while (++u_iter) {
+ CombatUnit* u = u_iter.value();
+ value_killed += u->Kill(u->LiveCount());
+ }
+
+ ListIter<CombatGroup> g_iter = group->GetComponents();
+ while (++g_iter) {
+ CombatGroup* g = g_iter.value();
+ value_killed += KillGroup(g);
+ }
+ }
+
+ return value_killed;
+}
+
+int
+CombatUnit::Kill(int n)
+{
+ int killed = n;
+
+ if (killed > LiveCount())
+ killed = LiveCount();
+
+ dead_count += killed;
+
+ int value_killed = killed * GetSingleValue();
+
+ if (killed) {
+ // if unit could support children, kill them too:
+ if (type == Ship::CARRIER ||
+ type == Ship::STATION ||
+ type == Ship::STARBASE) {
+
+ if (group) {
+ ListIter<CombatGroup> iter = group->GetComponents();
+ while (++iter) {
+ CombatGroup* g = iter.value();
+ value_killed += KillGroup(g);
+ }
+ }
+ }
+ }
+
+ return value_killed;
+}
+
diff --git a/Stars45/CombatUnit.h b/Stars45/CombatUnit.h
new file mode 100644
index 0000000..13af80a
--- /dev/null
+++ b/Stars45/CombatUnit.h
@@ -0,0 +1,134 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CombatUnit.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ A ship, station, or ground unit in the dynamic campaign.
+*/
+
+#ifndef CombatUnit_h
+#define CombatUnit_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Color.h"
+#include "Text.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class CombatGroup;
+class ShipDesign;
+
+// +--------------------------------------------------------------------+
+
+class CombatUnit
+{
+public:
+ static const char* TYPENAME() { return "CombatUnit"; }
+
+ CombatUnit(const char* n, const char* reg, int t, const char* dname, int number, int i);
+ CombatUnit(const CombatUnit& unit);
+
+ int operator == (const CombatUnit& u) const { return this == &u; }
+
+ const char* GetDescription() const;
+
+ int GetValue() const;
+ int GetSingleValue() const;
+ bool CanDefend(CombatUnit* unit) const;
+ bool CanAssign() const;
+ bool CanLaunch() const;
+ double PowerVersus(CombatUnit* tgt) const;
+ int AssignMission();
+ void CompleteMission();
+
+ double MaxRange() const;
+ double MaxEffectiveRange() const;
+ double OptimumRange() const;
+
+ void Engage(CombatUnit* tgt);
+ void Disengage();
+
+ // accessors and mutators:
+ const Text& Name() const { return name; }
+ const Text& Registry() const { return regnum; }
+ const Text& DesignName() const { return design_name; }
+ const Text& Skin() const { return skin; }
+ void SetSkin(const char* s) { skin = s; }
+ int Type() const { return type; }
+ int Count() const { return count; }
+ int LiveCount() const { return count - dead_count; }
+ int DeadCount() const { return dead_count; }
+ void SetDeadCount(int n) { dead_count = n; }
+ int Kill(int n);
+ int Available() const { return available; }
+ int GetIFF() const { return iff; }
+ bool IsLeader() const { return leader; }
+ void SetLeader(bool l) { leader = l; }
+ Point Location() const { return location; }
+ void MoveTo(const Point& loc);
+ Text GetRegion() const { return region; }
+ void SetRegion(Text rgn) { region = rgn; }
+ CombatGroup* GetCombatGroup() const { return group; }
+ void SetCombatGroup(CombatGroup* g){ group = g; }
+
+ Color MarkerColor() const;
+ bool IsGroundUnit() const;
+ bool IsStarship() const;
+ bool IsDropship() const;
+ bool IsStatic() const;
+
+ CombatUnit* GetCarrier() const { return carrier; }
+ void SetCarrier(CombatUnit* c) { carrier = c; }
+
+ const ShipDesign* GetDesign();
+ int GetShipClass() const;
+
+ List<CombatUnit>& GetAttackers() { return attackers; }
+
+ double GetPlanValue() const { return plan_value; }
+ void SetPlanValue(int v) { plan_value = v; }
+
+ double GetSustainedDamage() const { return sustained_damage; }
+ void SetSustainedDamage(double d) { sustained_damage = d; }
+
+ double GetHeading() const { return heading; }
+ void SetHeading(double d) { heading = d; }
+
+ double GetNextJumpTime() const { return jump_time; }
+
+private:
+ Text name;
+ Text regnum;
+ Text design_name;
+ Text skin;
+ int type;
+ const ShipDesign* design;
+ int count;
+ int dead_count;
+ int available;
+ int iff;
+ bool leader;
+ Text region;
+ Point location;
+ double plan_value; // scratch pad for plan modules
+ double launch_time;
+ double jump_time;
+ double sustained_damage;
+ double heading;
+
+ CombatUnit* carrier;
+ List<CombatUnit> attackers;
+ CombatUnit* target;
+ CombatGroup* group;
+};
+
+#endif CombatUnit_h
+
diff --git a/Stars45/CombatZone.cpp b/Stars45/CombatZone.cpp
new file mode 100644
index 0000000..d829074
--- /dev/null
+++ b/Stars45/CombatZone.cpp
@@ -0,0 +1,282 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CombatZone.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CombatZone is used by the dynamic campaign strategy
+ and logistics algorithms to assign forces to locations
+ within the campaign. A CombatZone is a collection of
+ closely related sectors, and the assets contained
+ within them.
+*/
+
+#include "MemDebug.h"
+#include "CombatZone.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "Campaign.h"
+#include "ShipDesign.h"
+#include "Ship.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "ParseUtil.h"
+
+// +----------------------------------------------------------------------+
+
+CombatZone::CombatZone()
+{
+}
+
+CombatZone::~CombatZone()
+{
+ regions.destroy();
+ forces.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CombatZone::Clear()
+{
+ forces.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CombatZone::AddGroup(CombatGroup* group)
+{
+ if (group) {
+ int iff = group->GetIFF();
+ ZoneForce* f = FindForce(iff);
+ f->AddGroup(group);
+ group->SetCurrentZone(this);
+ }
+}
+
+void
+CombatZone::RemoveGroup(CombatGroup* group)
+{
+ if (group) {
+ int iff = group->GetIFF();
+ ZoneForce* f = FindForce(iff);
+ f->RemoveGroup(group);
+ group->SetCurrentZone(0);
+ }
+}
+
+bool
+CombatZone::HasGroup(CombatGroup* group)
+{
+ if (group) {
+ int iff = group->GetIFF();
+ ZoneForce* f = FindForce(iff);
+ return f->HasGroup(group);
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CombatZone::AddRegion(const char* rgn)
+{
+ if (rgn && *rgn) {
+ regions.append(new (__FILE__,__LINE__) Text(rgn));
+
+ if (name.length() < 1)
+ name = rgn;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+CombatZone::HasRegion(const char* rgn)
+{
+ if (rgn && *rgn && regions.size()) {
+ Text test(rgn);
+ return regions.contains(&test);
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+ZoneForce*
+CombatZone::FindForce(int iff)
+{
+ ListIter<ZoneForce> f = forces;
+ while (++f) {
+ if (f->GetIFF() == iff)
+ return f.value();
+ }
+
+ return MakeForce(iff);
+}
+
+// +--------------------------------------------------------------------+
+
+ZoneForce*
+CombatZone::MakeForce(int iff)
+{
+ ZoneForce* f = new(__FILE__,__LINE__) ZoneForce(iff);
+ forces.append(f);
+ return f;
+}
+
+// +--------------------------------------------------------------------+
+
+static List<CombatZone> zonelist;
+
+List<CombatZone>&
+CombatZone::Load(const char* filename)
+{
+ zonelist.clear();
+
+ DataLoader* loader = DataLoader::GetLoader();
+ BYTE* block = 0;
+
+ loader->LoadBuffer(filename, block, true);
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
+
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ return zonelist;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "ZONES") {
+ return zonelist;
+ }
+ }
+
+ do {
+ delete term; term = 0;
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "zone") {
+ if (!def->term() || !def->term()->isStruct()) {
+ ::Print("WARNING: zone struct missing in '%s%s'\n", loader->GetDataPath(), filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+
+ CombatZone* zone = new(__FILE__,__LINE__) CombatZone();
+ char rgn[64];
+ ZeroMemory(rgn, sizeof(rgn));
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "region") {
+ GetDefText(rgn, pdef, filename);
+ zone->AddRegion(rgn);
+ }
+ else if (pdef->name()->value() == "system") {
+ GetDefText(rgn, pdef, filename);
+ zone->system = rgn;
+ }
+ }
+ }
+
+ zonelist.append(zone);
+ }
+ }
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+
+ return zonelist;
+}
+
+// +--------------------------------------------------------------------+
+
+ZoneForce::ZoneForce(int i)
+{
+ iff = i;
+
+ for (int n = 0; n < 8; n++)
+ need[n] = 0;
+}
+
+void
+ZoneForce::AddGroup(CombatGroup* group)
+{
+ if (group)
+ groups.append(group);
+}
+
+void
+ZoneForce::RemoveGroup(CombatGroup* group)
+{
+ if (group)
+ groups.remove(group);
+}
+
+bool
+ZoneForce::HasGroup(CombatGroup* group)
+{
+ if (group)
+ return groups.contains(group);
+
+ return false;
+}
+
+int
+ZoneForce::GetNeed(int group_type) const
+{
+ switch (group_type) {
+ case CombatGroup::CARRIER_GROUP: return need[0];
+ case CombatGroup::BATTLE_GROUP: return need[1];
+ case CombatGroup::DESTROYER_SQUADRON: return need[2];
+ case CombatGroup::ATTACK_SQUADRON: return need[3];
+ case CombatGroup::FIGHTER_SQUADRON: return need[4];
+ case CombatGroup::INTERCEPT_SQUADRON: return need[5];
+ }
+
+ return 0;
+}
+
+void
+ZoneForce::SetNeed(int group_type, int needed)
+{
+ switch (group_type) {
+ case CombatGroup::CARRIER_GROUP: need[0] = needed; break;
+ case CombatGroup::BATTLE_GROUP: need[1] = needed; break;
+ case CombatGroup::DESTROYER_SQUADRON: need[2] = needed; break;
+ case CombatGroup::ATTACK_SQUADRON: need[3] = needed; break;
+ case CombatGroup::FIGHTER_SQUADRON: need[4] = needed; break;
+ case CombatGroup::INTERCEPT_SQUADRON: need[5] = needed; break;
+ }
+}
+
+void
+ZoneForce::AddNeed(int group_type, int needed)
+{
+ switch (group_type) {
+ case CombatGroup::CARRIER_GROUP: need[0] += needed; break;
+ case CombatGroup::BATTLE_GROUP: need[1] += needed; break;
+ case CombatGroup::DESTROYER_SQUADRON: need[2] += needed; break;
+ case CombatGroup::ATTACK_SQUADRON: need[3] += needed; break;
+ case CombatGroup::FIGHTER_SQUADRON: need[4] += needed; break;
+ case CombatGroup::INTERCEPT_SQUADRON: need[5] += needed; break;
+ }
+}
+
diff --git a/Stars45/CombatZone.h b/Stars45/CombatZone.h
new file mode 100644
index 0000000..3c1058d
--- /dev/null
+++ b/Stars45/CombatZone.h
@@ -0,0 +1,103 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CombatZone.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ CombatZone is used by the dynamic campaign strategy
+ and logistics algorithms to assign forces to locations
+ within the campaign. A CombatZone is a collection of
+ closely related sectors, and the assets contained
+ within them.
+*/
+
+#ifndef CombatZone_h
+#define CombatZone_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Text.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class CombatGroup;
+class CombatUnit;
+class ZoneForce;
+
+// +--------------------------------------------------------------------+
+
+class CombatZone
+{
+public:
+ static const char* TYPENAME() { return "CombatZone"; }
+
+ CombatZone();
+ ~CombatZone();
+
+ int operator == (const CombatZone& g) const { return this == &g; }
+
+ const Text& Name() const { return name; }
+ const Text& System() const { return system; }
+ void AddGroup(CombatGroup* g);
+ void RemoveGroup(CombatGroup* g);
+ bool HasGroup(CombatGroup* g);
+ void AddRegion(const char* rgn);
+ bool HasRegion(const char* rgn);
+ List<Text>& GetRegions() { return regions; }
+ List<ZoneForce>& GetForces() { return forces; }
+
+ ZoneForce* FindForce(int iff);
+ ZoneForce* MakeForce(int iff);
+
+ void Clear();
+
+ static List<CombatZone>&
+ Load(const char* filename);
+
+private:
+ // attributes:
+ Text name;
+ Text system;
+ List<Text> regions;
+ List<ZoneForce> forces;
+};
+
+// +--------------------------------------------------------------------+
+
+class ZoneForce
+{
+public:
+ ZoneForce(int i);
+
+ int GetIFF() { return iff; }
+ List<CombatGroup>& GetGroups() { return groups; }
+ List<CombatGroup>& GetTargetList() { return target_list; }
+ List<CombatGroup>& GetDefendList() { return defend_list; }
+
+ void AddGroup(CombatGroup* g);
+ void RemoveGroup(CombatGroup* g);
+ bool HasGroup(CombatGroup* g);
+
+ int GetNeed(int group_type) const;
+ void SetNeed(int group_type, int needed);
+ void AddNeed(int group_type, int needed);
+
+private:
+ // attributes:
+ int iff;
+ List<CombatGroup> groups;
+ List<CombatGroup> defend_list;
+ List<CombatGroup> target_list;
+ int need[8];
+};
+
+// +--------------------------------------------------------------------+
+
+#endif CombatZone_h
+
diff --git a/Stars45/Combatant.cpp b/Stars45/Combatant.cpp
new file mode 100644
index 0000000..21fcc78
--- /dev/null
+++ b/Stars45/Combatant.cpp
@@ -0,0 +1,172 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Combatant.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ One side in a military conflict
+*/
+
+#include "MemDebug.h"
+#include "Combatant.h"
+#include "CombatGroup.h"
+#include "Mission.h"
+
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+
+static void SetCombatant(CombatGroup* g, Combatant* c)
+{
+ if (!g) return;
+
+ g->SetCombatant(c);
+
+ ListIter<CombatGroup> iter = g->GetComponents();
+ while (++iter)
+ SetCombatant(iter.value(), c);
+}
+
+// +--------------------------------------------------------------------+
+
+Combatant::Combatant(const char* com_name, const char* fname, int team)
+ : name(com_name), iff(team), score(0), force(0)
+{
+ for (int i = 0; i < 6; i++)
+ target_factor[i] = 1;
+
+ target_factor[2] = 1000;
+
+ if (fname)
+ force = CombatGroup::LoadOrderOfBattle(fname, iff, this);
+}
+
+Combatant::Combatant(const char* com_name, CombatGroup* f)
+ : name(com_name), iff(0), score(0), force(f)
+{
+ for (int i = 0; i < 6; i++)
+ target_factor[i] = 1;
+
+ target_factor[2] = 1000;
+
+ if (force) {
+ SetCombatant(force, this);
+ iff = force->GetIFF();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Combatant::~Combatant()
+{
+ mission_list.clear();
+ target_list.clear();
+ defend_list.clear();
+ delete force;
+}
+
+// +--------------------------------------------------------------------+
+
+CombatGroup*
+Combatant::FindGroup(int type, int id)
+{
+ if (force)
+ return force->FindGroup(type, id);
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Combatant::AddMission(Mission* mission)
+{
+ mission_list.append(mission);
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Combatant::GetTargetStratFactor(int type)
+{
+ switch (type) {
+ case CombatGroup::FLEET:
+ case CombatGroup::CARRIER_GROUP:
+ case CombatGroup::BATTLE_GROUP:
+ case CombatGroup::DESTROYER_SQUADRON: return target_factor[0];
+
+ case CombatGroup::WING:
+ case CombatGroup::ATTACK_SQUADRON:
+ case CombatGroup::INTERCEPT_SQUADRON:
+ case CombatGroup::FIGHTER_SQUADRON: return target_factor[1];
+
+ case CombatGroup::BATTERY:
+ case CombatGroup::MISSILE: return target_factor[2];
+
+ case CombatGroup::BATTALION:
+ case CombatGroup::STARBASE:
+ case CombatGroup::C3I:
+ case CombatGroup::COMM_RELAY:
+ case CombatGroup::EARLY_WARNING:
+ case CombatGroup::FWD_CONTROL_CTR:
+ case CombatGroup::ECM: return target_factor[3];
+
+ case CombatGroup::SUPPORT:
+ case CombatGroup::COURIER:
+ case CombatGroup::MEDICAL:
+ case CombatGroup::SUPPLY:
+ case CombatGroup::REPAIR: return target_factor[4];
+ }
+
+ return target_factor[5];
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Combatant::SetTargetStratFactor(int type, double factor)
+{
+ switch (type) {
+ case CombatGroup::FLEET:
+ case CombatGroup::CARRIER_GROUP:
+ case CombatGroup::BATTLE_GROUP:
+ case CombatGroup::DESTROYER_SQUADRON: target_factor[0] = factor;
+ break;
+
+ case CombatGroup::WING:
+ case CombatGroup::ATTACK_SQUADRON:
+ case CombatGroup::INTERCEPT_SQUADRON:
+ case CombatGroup::FIGHTER_SQUADRON: target_factor[1] = factor;
+ break;
+
+ case CombatGroup::BATTALION:
+ case CombatGroup::STARBASE:
+ case CombatGroup::BATTERY:
+ case CombatGroup::MISSILE: target_factor[2] = factor;
+ break;
+
+ case CombatGroup::C3I:
+ case CombatGroup::COMM_RELAY:
+ case CombatGroup::EARLY_WARNING:
+ case CombatGroup::FWD_CONTROL_CTR:
+ case CombatGroup::ECM: target_factor[3] = factor;
+ break;
+
+ case CombatGroup::SUPPORT:
+ case CombatGroup::COURIER:
+ case CombatGroup::MEDICAL:
+ case CombatGroup::SUPPLY:
+ case CombatGroup::REPAIR: target_factor[4] = factor;
+ break;
+
+ default: target_factor[5] = factor;
+ break;
+ }
+}
+
+
diff --git a/Stars45/Combatant.h b/Stars45/Combatant.h
new file mode 100644
index 0000000..c449bea
--- /dev/null
+++ b/Stars45/Combatant.h
@@ -0,0 +1,73 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Combatant.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ One side in a military conflict
+*/
+
+#ifndef Combatant_h
+#define Combatant_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Text.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class CombatGroup;
+class Mission;
+
+// +--------------------------------------------------------------------+
+
+class Combatant
+{
+public:
+ static const char* TYPENAME() { return "Combatant"; }
+
+ Combatant(const char* com_name, const char* file_name, int iff);
+ Combatant(const char* com_name, CombatGroup* force);
+ ~Combatant();
+
+ // operations:
+
+ // accessors:
+ const char* Name() const { return name; }
+ int GetIFF() const { return iff; }
+ int Score() const { return score; }
+ const char* GetDescription() const { return name; }
+ CombatGroup* GetForce() { return force; }
+ CombatGroup* FindGroup(int type, int id=-1);
+ List<CombatGroup>& GetTargetList() { return target_list; }
+ List<CombatGroup>& GetDefendList() { return defend_list; }
+ List<Mission>& GetMissionList() { return mission_list; }
+
+ void AddMission(Mission* m);
+ void SetScore(int points) { score = points; }
+ void AddScore(int points) { score += points; }
+
+ double GetTargetStratFactor(int type);
+ void SetTargetStratFactor(int type, double f);
+
+private:
+ Text name;
+ int iff;
+ int score;
+
+ CombatGroup* force;
+ List<CombatGroup> target_list;
+ List<CombatGroup> defend_list;
+ List<Mission> mission_list;
+
+ double target_factor[8];
+};
+
+
+#endif Combatant_h
diff --git a/Stars45/Component.cpp b/Stars45/Component.cpp
new file mode 100644
index 0000000..c9ad2cb
--- /dev/null
+++ b/Stars45/Component.cpp
@@ -0,0 +1,177 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Component.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Generic ship system sub-component class
+*/
+
+#include "MemDebug.h"
+#include "Component.h"
+#include "System.h"
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+ComponentDesign::ComponentDesign()
+ : repair_time(0.0f), replace_time(0.0f), spares(0), affects(0)
+{ }
+
+// +----------------------------------------------------------------------+
+
+ComponentDesign::~ComponentDesign()
+{ }
+
+// +----------------------------------------------------------------------+
+
+Component::Component(ComponentDesign* d, System* s)
+ : design(d), system(s),
+ status(NOMINAL), availability(100.0f), time_remaining(0.0f),
+ spares(0), jerried(0)
+{
+ if (design)
+ spares = design->spares;
+}
+
+// +----------------------------------------------------------------------+
+
+Component::Component(const Component& c)
+ : design(c.design), system(c.system),
+ status(c.status), availability(c.availability), time_remaining(c.time_remaining),
+ spares(c.spares), jerried(c.jerried)
+{
+}
+
+// +--------------------------------------------------------------------+
+
+Component::~Component()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+Component::ExecMaintFrame(double seconds)
+{
+ if (status > NOMINAL) {
+ time_remaining -= (float) seconds;
+
+ // when repairs are complete:
+ if (time_remaining <= 0) {
+ if (status == REPAIR) {
+ // did we just jerry-rig a failed component?
+ if (availability < 50)
+ jerried++;
+
+ if (jerried < 5)
+ availability += 50.0f - 10 * jerried;
+ if (availability > 100) availability = 100.0f;
+ }
+ else {
+ availability = 100.0f;
+ }
+
+ if (availability > 99)
+ status = NOMINAL;
+ else if (availability > 49)
+ status = DEGRADED;
+ else
+ status = CRITICAL;
+
+ time_remaining = 0.0f;
+
+ if (system)
+ system->CalcStatus();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Component::ApplyDamage(double damage)
+{
+ availability -= (float) damage;
+ if (availability < 1) availability = 0.0f;
+
+ if (status < REPLACE) {
+ if (availability > 99)
+ status = NOMINAL;
+ else if (availability > 49)
+ status = DEGRADED;
+ else
+ status = CRITICAL;
+ }
+
+ if (system)
+ system->CalcStatus();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Component::Repair()
+{
+ if (status < NOMINAL) {
+ status = REPAIR;
+ time_remaining = design->repair_time;
+
+ if (system)
+ system->CalcStatus();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Component::Replace()
+{
+ if (status <= NOMINAL) {
+ status = REPLACE;
+ spares--;
+ time_remaining = design->replace_time;
+
+ if (system)
+ system->CalcStatus();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+float
+Component::Availability() const
+{
+ if (status > NOMINAL && availability > 50)
+ return 50.0f;
+
+ return availability;
+}
+
+float
+Component::TimeRemaining() const
+{
+ return (float) time_remaining;
+}
+
+int
+Component::SpareCount() const
+{
+ return spares;
+}
+
+bool
+Component::IsJerried() const
+{
+ return jerried?true:false;
+}
+
+int
+Component::NumJerried() const
+{
+ return jerried;
+} \ No newline at end of file
diff --git a/Stars45/Component.h b/Stars45/Component.h
new file mode 100644
index 0000000..c5bbdc2
--- /dev/null
+++ b/Stars45/Component.h
@@ -0,0 +1,100 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Component.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Generic ship system sub-component class
+*/
+
+#ifndef Component_h
+#define Component_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class ComponentDesign
+{
+public:
+ static const char* TYPENAME() { return "ComponentDesign"; }
+
+ ComponentDesign();
+ ~ComponentDesign();
+ int operator == (const ComponentDesign& rhs) const { return (name == rhs.name); }
+
+ // identification:
+ Text name;
+ Text abrv;
+
+ float repair_time;
+ float replace_time;
+ int spares;
+ DWORD affects;
+};
+
+// +--------------------------------------------------------------------+
+
+class System;
+
+// +--------------------------------------------------------------------+
+
+class Component
+{
+public:
+ static const char* TYPENAME() { return "Component"; }
+
+ enum STATUS { DESTROYED, CRITICAL, DEGRADED, NOMINAL, REPLACE, REPAIR };
+ enum DAMAGE { DAMAGE_EFFICIENCY = 0x01,
+ DAMAGE_SAFETY = 0x02,
+ DAMAGE_STABILITY = 0x04 };
+
+ Component(ComponentDesign* d, System* s);
+ Component(const Component& c);
+ virtual ~Component();
+
+ const char* Name() const { return design->name; }
+ const char* Abbreviation() const { return design->abrv; }
+ float RepairTime() const { return design->repair_time; }
+ float ReplaceTime() const { return design->replace_time; }
+
+ bool DamageEfficiency() const { return (design->affects & DAMAGE_EFFICIENCY)?true:false; }
+ bool DamageSafety() const { return (design->affects & DAMAGE_SAFETY)?true:false; }
+ bool DamageStability() const { return (design->affects & DAMAGE_STABILITY)?true:false; }
+
+ STATUS Status() const { return status; }
+ float Availability() const;
+ float TimeRemaining() const;
+ int SpareCount() const;
+ bool IsJerried() const;
+ int NumJerried() const;
+
+ void SetSystem(System* s) { system = s; }
+ System* GetSystem() const { return system; }
+
+ virtual void ApplyDamage(double damage);
+ virtual void ExecMaintFrame(double seconds);
+ virtual void Repair();
+ virtual void Replace();
+
+protected:
+ ComponentDesign* design;
+
+ // Component health status:
+ STATUS status;
+ float availability;
+ float time_remaining;
+ int spares;
+ int jerried;
+ System* system;
+};
+
+#endif Component_h
+
diff --git a/Stars45/Computer.cpp b/Stars45/Computer.cpp
new file mode 100644
index 0000000..9661ee3
--- /dev/null
+++ b/Stars45/Computer.cpp
@@ -0,0 +1,93 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Computer.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Computer System class
+*/
+
+#include "MemDebug.h"
+#include "Computer.h"
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+static int computer_value[] = {
+ 0, 1, 1, 1, 1
+};
+
+// +----------------------------------------------------------------------+
+
+Computer::Computer(int comp_type, const char* comp_name)
+ : System(COMPUTER, comp_type, comp_name, 1, 1, 1, 1)
+{
+ SetAbbreviation(Game::GetText("sys.computer.abrv"));
+ power_flags = POWER_WATTS | POWER_CRITICAL;
+
+ if (subtype == FLIGHT) {
+ crit_level = -1.0f;
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+Computer::Computer(const Computer& c)
+ : System(c)
+{
+ Mount(c);
+ SetAbbreviation(c.Abbreviation());
+ power_flags = POWER_WATTS | POWER_CRITICAL;
+
+ if (subtype == FLIGHT) {
+ crit_level = -1.0f;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Computer::~Computer()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+Computer::ApplyDamage(double damage)
+{
+ System::ApplyDamage(damage);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Computer::ExecFrame(double seconds)
+{
+ energy = 0.0f;
+ System::ExecFrame(seconds);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Computer::Distribute(double delivered_energy, double seconds)
+{
+ if (IsPowerOn()) {
+ // convert Joules to Watts:
+ energy = (float) (delivered_energy/seconds);
+
+ // brown out:
+ if (energy < capacity*0.75f)
+ power_on = false;
+
+ // spike:
+ else if (energy > capacity*1.5f) {
+ power_on = false;
+ ApplyDamage(50);
+ }
+ }
+}
diff --git a/Stars45/Computer.h b/Stars45/Computer.h
new file mode 100644
index 0000000..18452ec
--- /dev/null
+++ b/Stars45/Computer.h
@@ -0,0 +1,41 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Computer.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Various computer systems class
+*/
+
+#ifndef Computer_h
+#define Computer_h
+
+#include "Types.h"
+#include "System.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class Computer : public System
+{
+public:
+ enum CompType { AVIONICS=1, FLIGHT, TACTICAL };
+
+ Computer(int comp_type, const char* comp_name);
+ Computer(const Computer& rhs);
+ virtual ~Computer();
+
+ virtual void ApplyDamage(double damage);
+ virtual void ExecFrame(double seconds);
+ virtual void Distribute(double delivered_energy, double seconds);
+
+protected:
+};
+
+#endif Computer_h
+
diff --git a/Stars45/ConfirmDlg.cpp b/Stars45/ConfirmDlg.cpp
new file mode 100644
index 0000000..858faee
--- /dev/null
+++ b/Stars45/ConfirmDlg.cpp
@@ -0,0 +1,150 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ConfirmDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ General-purpose confirmation dialog class
+*/
+
+#include "MemDebug.h"
+#include "ConfirmDlg.h"
+#include "MenuScreen.h"
+#include "Starshatter.h"
+#include "FormatUtil.h"
+
+#include "Game.h"
+#include "Keyboard.h"
+#include "Button.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(ConfirmDlg, OnApply);
+DEF_MAP_CLIENT(ConfirmDlg, OnCancel);
+
+// +--------------------------------------------------------------------+
+
+ConfirmDlg::ConfirmDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ parent_control(0), btn_apply(0), btn_cancel(0)
+{
+ Init(def);
+}
+
+ConfirmDlg::~ConfirmDlg()
+{
+}
+
+void
+ConfirmDlg::RegisterControls()
+{
+ if (btn_apply)
+ return;
+
+ btn_apply = (Button*) FindControl(1);
+ REGISTER_CLIENT(EID_CLICK, btn_apply, ConfirmDlg, OnApply);
+
+ btn_cancel = (Button*) FindControl(2);
+ REGISTER_CLIENT(EID_CLICK, btn_cancel, ConfirmDlg, OnCancel);
+
+ lbl_title = FindControl(100);
+ lbl_message = FindControl(101);
+}
+
+// +--------------------------------------------------------------------+
+
+ActiveWindow*
+ConfirmDlg::GetParentControl()
+{
+ return parent_control;
+}
+
+void
+ConfirmDlg::SetParentControl(ActiveWindow* p)
+{
+ parent_control = p;
+}
+
+Text
+ConfirmDlg::GetTitle()
+{
+ if (lbl_title)
+ return lbl_title->GetText();
+
+ return "";
+}
+
+void
+ConfirmDlg::SetTitle(const char* t)
+{
+ if (lbl_title)
+ lbl_title->SetText(t);
+}
+
+Text
+ConfirmDlg::GetMessage()
+{
+ if (lbl_message)
+ return lbl_message->GetText();
+
+ return "";
+}
+
+void
+ConfirmDlg::SetMessage(const char* m)
+{
+ if (lbl_message)
+ lbl_message->SetText(m);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ConfirmDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ OnApply(0);
+ }
+
+ if (Keyboard::KeyDown(VK_ESCAPE)) {
+ OnCancel(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ConfirmDlg::Show()
+{
+ if (!IsShown()) {
+ Button::PlaySound(Button::SND_CONFIRM);
+ }
+
+ FormWindow::Show();
+ SetFocus();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ConfirmDlg::OnApply(AWEvent* event)
+{
+ manager->HideConfirmDlg();
+
+ if (parent_control)
+ parent_control->ClientEvent(EID_USER_1);
+}
+
+void
+ConfirmDlg::OnCancel(AWEvent* event)
+{
+ manager->HideConfirmDlg();
+}
+
+// +--------------------------------------------------------------------+
diff --git a/Stars45/ConfirmDlg.h b/Stars45/ConfirmDlg.h
new file mode 100644
index 0000000..0991518
--- /dev/null
+++ b/Stars45/ConfirmDlg.h
@@ -0,0 +1,64 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ConfirmDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ General-purpose confirmation dialog class
+*/
+
+#ifndef ConfirmDlg_h
+#define ConfirmDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+
+// +--------------------------------------------------------------------+
+
+class ConfirmDlg : public FormWindow
+{
+public:
+ ConfirmDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~ConfirmDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+
+ ActiveWindow* GetParentControl();
+ void SetParentControl(ActiveWindow* p);
+
+ Text GetTitle();
+ void SetTitle(const char* t);
+
+ Text GetMessage();
+ void SetMessage(const char* m);
+
+ // Operations:
+ virtual void ExecFrame();
+
+ virtual void OnApply(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+
+protected:
+ MenuScreen* manager;
+
+ ActiveWindow* lbl_title;
+ ActiveWindow* lbl_message;
+
+ Button* btn_apply;
+ Button* btn_cancel;
+
+ ActiveWindow* parent_control;
+};
+
+#endif ConfirmDlg_h
+
diff --git a/Stars45/Contact.cpp b/Stars45/Contact.cpp
new file mode 100644
index 0000000..f77c1e3
--- /dev/null
+++ b/Stars45/Contact.cpp
@@ -0,0 +1,365 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Contact.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Integrated (Passive and Active) Sensor Package class implementation
+*/
+
+#include "MemDebug.h"
+#include "Contact.h"
+#include "Drone.h"
+#include "Sensor.h"
+#include "Ship.h"
+#include "Sim.h"
+#include "WeaponDesign.h"
+
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+const int DEFAULT_TRACK_UPDATE = 500; // milliseconds
+const int DEFAULT_TRACK_LENGTH = 20; // 10 seconds
+const double DEFAULT_TRACK_AGE = 10; // 10 seconds
+const double SENSOR_THRESHOLD = 0.25;
+
+// +----------------------------------------------------------------------+
+
+Contact::Contact()
+ : ship(0), shot(0), d_pas(0.0f), d_act(0.0f),
+ track(0), ntrack(0), time(0), track_time(0), probe(false)
+{
+ acquire_time = Game::GameTime();
+}
+
+Contact::Contact(Ship* s, float p, float a)
+ : ship(s), shot(0), d_pas(p), d_act(a),
+ track(0), ntrack(0), time(0), track_time(0), probe(false)
+{
+ acquire_time = Game::GameTime();
+ Observe(ship);
+}
+
+Contact::Contact(Shot* s, float p, float a)
+ : ship(0), shot(s), d_pas(p), d_act(a),
+ track(0), ntrack(0), time(0), track_time(0), probe(false)
+{
+ acquire_time = Game::GameTime();
+ Observe(shot);
+}
+
+// +----------------------------------------------------------------------+
+
+Contact::~Contact()
+{
+ delete [] track;
+}
+
+// +----------------------------------------------------------------------+
+
+int
+Contact::operator == (const Contact& c) const
+{
+ if (ship)
+ return ship == c.ship;
+
+ else if (shot)
+ return shot == c.shot;
+
+ return 0;
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+Contact::Update(SimObject* obj)
+{
+ if (obj == ship || obj == shot) {
+ ship = 0;
+ shot = 0;
+ d_act = 0;
+ d_pas = 0;
+
+ ClearTrack();
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+Contact::GetObserverName() const
+{
+ static char name[128];
+
+ if (ship)
+ sprintf(name, "Contact Ship='%s'", ship->Name());
+ else if (shot)
+ sprintf(name, "Contact Shot='%s' %s", shot->Name(), shot->DesignName());
+ else
+ sprintf(name, "Contact (unknown)");
+
+ return name;
+}
+
+// +----------------------------------------------------------------------+
+
+double
+Contact::Age() const
+{
+ double age = 0;
+
+ if (!ship && !shot)
+ return age;
+
+ double seconds = (Game::GameTime() - time) / 1000.0;
+
+ age = 1.0 - seconds/DEFAULT_TRACK_AGE;
+
+ if (age < 0)
+ age = 0;
+
+ return age;
+}
+
+int
+Contact::GetIFF(const Ship* observer) const
+{
+ int i = 0;
+
+ if (ship) {
+ i = ship->GetIFF();
+
+ // if the contact is on our side or has locked us up,
+ // we know whose side he's on.
+
+ // Otherwise:
+ if (i != observer->GetIFF() && !Threat(observer)) {
+ if (d_pas < 2*SENSOR_THRESHOLD && d_act < SENSOR_THRESHOLD && !Visible(observer))
+ i = -1000; // indeterminate iff reading
+ }
+ }
+
+ else if (shot && shot->Owner()) {
+ i = shot->Owner()->GetIFF();
+ }
+
+ return i;
+}
+
+bool
+Contact::ActLock() const
+{
+ return d_act >= SENSOR_THRESHOLD;
+}
+
+bool
+Contact::PasLock() const
+{
+ return d_pas >= SENSOR_THRESHOLD;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Contact::GetBearing(const Ship* observer, double& az, double& el, double& rng) const
+{
+ // translate:
+ Point targ_pt = loc - observer->Location();
+
+ // rotate:
+ const Camera* cam = &observer->Cam();
+ double tx = targ_pt * cam->vrt();
+ double ty = targ_pt * cam->vup();
+ double tz = targ_pt * cam->vpn();
+
+ // convert to spherical coords:
+ rng = targ_pt.length();
+ az = asin(fabs(tx) / rng);
+ el = asin(fabs(ty) / rng);
+
+ if (tx < 0) az = -az;
+ if (ty < 0) el = -el;
+
+ // correct az/el for back hemisphere:
+ if (tz < 0) {
+ if (az < 0) az = -PI - az;
+ else az = PI - az;
+ }
+}
+
+double
+Contact::Range(const Ship* observer, double limit) const
+{
+ double r = Point(loc - observer->Location()).length();
+
+ // if passive only, return approximate range:
+ if (!ActLock()) {
+ const int chunk = 25000;
+
+ if (!PasLock()) {
+ r = (int) limit;
+ }
+
+ else if (r <= chunk) {
+ r = chunk;
+ }
+
+ else {
+ int r1 = (int) (r + chunk/2) / chunk;
+ r = r1 * chunk;
+ }
+ }
+
+ return r;
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+Contact::InFront(const Ship* observer) const
+{
+ // translate:
+ Point targ_pt = loc - observer->Location();
+
+ // rotate:
+ const Camera* cam = &observer->Cam();
+ double tz = targ_pt * cam->vpn();
+
+ if (tz > 1.0)
+ return true;
+
+ return false;
+}
+
+bool
+Contact::Threat(const Ship* observer) const
+{
+ bool threat = false;
+
+ if (observer && observer->Life() != 0) {
+ if (ship && ship->Life() != 0) {
+ threat = (ship->GetIFF() &&
+ ship->GetIFF() != observer->GetIFF() &&
+ ship->GetEMCON() > 2 &&
+ ship->IsTracking((Ship*) observer) &&
+ ship->Weapons().size() > 0);
+
+ if (threat && observer->GetIFF() == 0)
+ threat = ship->GetIFF() > 1;
+ }
+
+ else if (shot) {
+ threat = shot->IsTracking((Ship*) observer);
+
+ if (!threat && shot->Design()->probe && shot->GetIFF() != observer->GetIFF()) {
+ Point probe_pt = shot->Location() - observer->Location();
+ double prng = probe_pt.length();
+
+ threat = (prng < shot->Design()->lethal_radius);
+ }
+ }
+ }
+
+ return threat;
+}
+
+bool
+Contact::Visible(const Ship* observer) const
+{
+ // translate:
+ Point targ_pt = loc - observer->Location();
+ double radius = 0;
+
+ if (ship)
+ radius = ship->Radius();
+
+ else if (shot)
+ radius = shot->Radius();
+
+ // rotate:
+ const Camera* cam = &observer->Cam();
+ double rng = targ_pt.length();
+
+ return radius/rng > 0.002;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Contact::Reset()
+{
+ if (Game::Paused()) return;
+
+ float step_down = (float) (Game::FrameTime() / 10);
+
+ if (d_pas > 0)
+ d_pas -= step_down;
+
+ if (d_act > 0)
+ d_act -= step_down;
+}
+
+void
+Contact::Merge(Contact* c)
+{
+ if (c->GetShip() == ship && c->GetShot() == shot) {
+ if (c->d_pas > d_pas)
+ d_pas = c->d_pas;
+
+ if (c->d_act > d_act)
+ d_act = c->d_act;
+ }
+}
+
+void
+Contact::ClearTrack()
+{
+ delete [] track;
+ track = 0;
+ ntrack = 0;
+}
+
+void
+Contact::UpdateTrack()
+{
+ time = Game::GameTime();
+
+ if (shot || (ship && ship->IsGroundUnit()))
+ return;
+
+ if (!track) {
+ track = new(__FILE__,__LINE__) Point[DEFAULT_TRACK_LENGTH];
+ track[0] = loc;
+ ntrack = 1;
+ track_time = time;
+ }
+
+ else if (time - track_time > DEFAULT_TRACK_UPDATE) {
+ if (loc != track[0]) {
+ for (int i = DEFAULT_TRACK_LENGTH-2; i >= 0; i--)
+ track[i+1] = track[i];
+
+ track[0] = loc;
+ if (ntrack < DEFAULT_TRACK_LENGTH) ntrack++;
+ }
+
+ track_time = time;
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+Point
+Contact::TrackPoint(int i) const
+{
+ if (track && i < ntrack)
+ return track[i];
+
+ return Point();
+}
diff --git a/Stars45/Contact.h b/Stars45/Contact.h
new file mode 100644
index 0000000..04e7d0b
--- /dev/null
+++ b/Stars45/Contact.h
@@ -0,0 +1,91 @@
+/* Project STARSHATTER
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Contact.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Sensor Contact class
+*/
+
+#ifndef Contact_h
+#define Contact_h
+
+#include "Types.h"
+#include "SimObject.h"
+#include "System.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+class Shot;
+
+class Contact : public SimObserver
+{
+ friend class Sensor;
+
+public:
+ static const char* TYPENAME() { return "Contact"; }
+
+ Contact();
+ Contact(Ship* s, float p, float a);
+ Contact(Shot* s, float p, float a);
+ virtual ~Contact();
+
+ int operator == (const Contact& c) const;
+
+ Ship* GetShip() const { return ship; }
+ Shot* GetShot() const { return shot; }
+ Point Location() const { return loc; }
+
+ double PasReturn() const { return d_pas; }
+ double ActReturn() const { return d_act; }
+ bool PasLock() const;
+ bool ActLock() const;
+ double Age() const;
+ bool IsProbed() const { return probe; }
+
+ DWORD AcquisitionTime() const { return acquire_time; }
+
+ int GetIFF(const Ship* observer) const;
+ void GetBearing(const Ship* observer, double& az, double& el, double& r) const;
+ double Range(const Ship* observer,
+ double limit=75e3) const;
+
+ bool InFront(const Ship* observer) const;
+ bool Threat(const Ship* observer) const;
+ bool Visible(const Ship* observer) const;
+
+ void Reset();
+ void Merge(Contact* c);
+ void ClearTrack();
+ void UpdateTrack();
+ int TrackLength() const { return ntrack; }
+ Point TrackPoint(int i) const;
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+private:
+ Ship* ship;
+ Shot* shot;
+ Point loc;
+ DWORD acquire_time;
+ DWORD time;
+
+ Point* track;
+ int ntrack;
+ DWORD track_time;
+
+ float d_pas; // power output
+ float d_act; // mass, size
+ bool probe; // scanned by probe
+};
+
+#endif Contact_h
+
diff --git a/Stars45/CtlDlg.cpp b/Stars45/CtlDlg.cpp
new file mode 100644
index 0000000..230cc29
--- /dev/null
+++ b/Stars45/CtlDlg.cpp
@@ -0,0 +1,461 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CtlDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Control Options (keyboard/joystick) Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "CtlDlg.h"
+#include "KeyDlg.h"
+#include "MenuScreen.h"
+#include "Starshatter.h"
+#include "Ship.h"
+
+#include "DataLoader.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Joystick.h"
+#include "MachineInfo.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(CtlDlg, OnCommand);
+DEF_MAP_CLIENT(CtlDlg, OnCategory);
+DEF_MAP_CLIENT(CtlDlg, OnControlModel);
+DEF_MAP_CLIENT(CtlDlg, OnJoySelect);
+DEF_MAP_CLIENT(CtlDlg, OnJoyThrottle);
+DEF_MAP_CLIENT(CtlDlg, OnJoyRudder);
+DEF_MAP_CLIENT(CtlDlg, OnJoySensitivity);
+DEF_MAP_CLIENT(CtlDlg, OnJoyAxis);
+DEF_MAP_CLIENT(CtlDlg, OnMouseSelect);
+DEF_MAP_CLIENT(CtlDlg, OnMouseSensitivity);
+DEF_MAP_CLIENT(CtlDlg, OnMouseInvert);
+DEF_MAP_CLIENT(CtlDlg, OnApply);
+DEF_MAP_CLIENT(CtlDlg, OnCancel);
+DEF_MAP_CLIENT(CtlDlg, OnAudio);
+DEF_MAP_CLIENT(CtlDlg, OnVideo);
+DEF_MAP_CLIENT(CtlDlg, OnOptions);
+DEF_MAP_CLIENT(CtlDlg, OnControls);
+DEF_MAP_CLIENT(CtlDlg, OnMod);
+
+// +--------------------------------------------------------------------+
+
+CtlDlg::CtlDlg(Screen* s, FormDef& def, BaseScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ selected_category(0), command_index(0), control_model(0),
+ joy_select(0), joy_throttle(0), joy_rudder(0), joy_sensitivity(0),
+ mouse_select(0), mouse_sensitivity(0), mouse_invert(0),
+ apply(0), cancel(0), vid_btn(0), aud_btn(0), ctl_btn(0), opt_btn(0), mod_btn(0),
+ closed(true)
+{
+ Init(def);
+}
+
+CtlDlg::~CtlDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CtlDlg::RegisterControls()
+{
+ if (apply)
+ return;
+
+ for (int i = 0; i < 4; i++) {
+ category[i] = (Button*) FindControl(101 + i);
+ REGISTER_CLIENT(EID_CLICK, category[i], CtlDlg, OnCategory);
+ }
+
+ commands = (ListBox*) FindControl(200);
+ REGISTER_CLIENT(EID_SELECT, commands, CtlDlg, OnCommand);
+
+ control_model_combo = (ComboBox*) FindControl(210);
+ REGISTER_CLIENT(EID_SELECT, control_model_combo, CtlDlg, OnControlModel);
+
+ joy_select_combo = (ComboBox*) FindControl(211);
+ REGISTER_CLIENT(EID_SELECT, joy_select_combo, CtlDlg, OnJoySelect);
+
+ joy_throttle_combo = (ComboBox*) FindControl(212);
+ REGISTER_CLIENT(EID_SELECT, joy_throttle_combo, CtlDlg, OnJoyThrottle);
+
+ joy_rudder_combo = (ComboBox*) FindControl(213);
+ REGISTER_CLIENT(EID_SELECT, joy_rudder_combo, CtlDlg, OnJoyRudder);
+
+ joy_sensitivity_slider = (Slider*) FindControl(214);
+
+ if (joy_sensitivity_slider) {
+ joy_sensitivity_slider->SetRangeMin(0);
+ joy_sensitivity_slider->SetRangeMax(10);
+ joy_sensitivity_slider->SetStepSize(1);
+ REGISTER_CLIENT(EID_CLICK, joy_sensitivity_slider, CtlDlg, OnJoySensitivity);
+ }
+
+ joy_axis_button = (Button*) FindControl(215);
+ REGISTER_CLIENT(EID_CLICK, joy_axis_button, CtlDlg, OnJoyAxis);
+
+ mouse_select_combo = (ComboBox*) FindControl(511);
+ REGISTER_CLIENT(EID_SELECT, mouse_select_combo, CtlDlg, OnMouseSelect);
+
+ mouse_sensitivity_slider = (Slider*) FindControl(514);
+ if (mouse_sensitivity_slider) {
+ mouse_sensitivity_slider->SetRangeMin(0);
+ mouse_sensitivity_slider->SetRangeMax(50);
+ mouse_sensitivity_slider->SetStepSize(1);
+ REGISTER_CLIENT(EID_CLICK, mouse_sensitivity_slider, CtlDlg, OnMouseSensitivity);
+ }
+
+ mouse_invert_checkbox = (Button*) FindControl(515);
+ REGISTER_CLIENT(EID_CLICK, mouse_invert_checkbox, CtlDlg, OnMouseInvert);
+
+ apply = (Button*) FindControl(1);
+ REGISTER_CLIENT(EID_CLICK, apply, CtlDlg, OnApply);
+
+ cancel = (Button*) FindControl(2);
+ REGISTER_CLIENT(EID_CLICK, cancel, CtlDlg, OnCancel);
+
+ vid_btn = (Button*) FindControl(901);
+ REGISTER_CLIENT(EID_CLICK, vid_btn, CtlDlg, OnVideo);
+
+ aud_btn = (Button*) FindControl(902);
+ REGISTER_CLIENT(EID_CLICK, aud_btn, CtlDlg, OnAudio);
+
+ ctl_btn = (Button*) FindControl(903);
+ REGISTER_CLIENT(EID_CLICK, ctl_btn, CtlDlg, OnControls);
+
+ opt_btn = (Button*) FindControl(904);
+ REGISTER_CLIENT(EID_CLICK, opt_btn, CtlDlg, OnOptions);
+
+ mod_btn = (Button*) FindControl(905);
+ if (mod_btn) {
+ REGISTER_CLIENT(EID_CLICK, mod_btn, CtlDlg, OnMod);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CtlDlg::Show()
+{
+ if (!IsShown())
+ FormWindow::Show();
+
+ if (closed)
+ ShowCategory();
+ else
+ UpdateCategory();
+
+ if (vid_btn) vid_btn->SetButtonState(0);
+ if (aud_btn) aud_btn->SetButtonState(0);
+ if (ctl_btn) ctl_btn->SetButtonState(1);
+ if (opt_btn) opt_btn->SetButtonState(0);
+ if (mod_btn) mod_btn->SetButtonState(0);
+
+ closed = false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CtlDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ OnApply(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CtlDlg::ShowCategory()
+{
+ if (!commands || !category[0])
+ return;
+
+ for (int i = 0; i < 4; i++) {
+ if (i == selected_category)
+ category[i]->SetButtonState(1);
+ else
+ category[i]->SetButtonState(0);
+ }
+
+ commands->ClearItems();
+ commands->SetSelectedStyle(ListBox::LIST_ITEM_STYLE_FILLED_BOX);
+ commands->SetLeading(2);
+
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars) {
+ KeyMap& keymap = stars->GetKeyMap();
+ int n = 0;
+
+ for (int i = 0; i < 256; i++) {
+ KeyMapEntry* k = keymap.GetKeyMap(i);
+
+ switch (k->act) {
+ case 0:
+ break;
+
+ case KEY_CONTROL_MODEL:
+ control_model = k->key;
+ control_model_combo->SetSelection(control_model);
+ break;
+
+ case KEY_JOY_SELECT:
+ joy_select = k->key;
+ joy_select_combo->SetSelection(joy_select);
+ break;
+
+ case KEY_JOY_RUDDER:
+ joy_rudder = k->key;
+ joy_rudder_combo->SetSelection(joy_rudder);
+ break;
+
+ case KEY_JOY_THROTTLE:
+ joy_throttle = k->key;
+ joy_throttle_combo->SetSelection(joy_throttle);
+ break;
+
+ case KEY_JOY_SENSE:
+ joy_sensitivity = k->key;
+ joy_sensitivity_slider->SetValue(joy_sensitivity);
+ break;
+
+ case KEY_JOY_SWAP:
+ break;
+
+ case KEY_JOY_DEAD_ZONE:
+ break;
+
+ case KEY_MOUSE_SELECT:
+ mouse_select = k->key;
+ mouse_select_combo->SetSelection(mouse_select);
+ break;
+
+ case KEY_MOUSE_SENSE:
+ mouse_sensitivity = k->key;
+ mouse_sensitivity_slider->SetValue(mouse_sensitivity);
+ break;
+
+ case KEY_MOUSE_INVERT:
+ mouse_invert = k->key;
+ mouse_invert_checkbox->SetButtonState(mouse_invert > 0);
+ break;
+
+ case KEY_AXIS_YAW:
+ case KEY_AXIS_PITCH:
+ case KEY_AXIS_ROLL:
+ case KEY_AXIS_THROTTLE:
+
+ case KEY_AXIS_YAW_INVERT:
+ case KEY_AXIS_PITCH_INVERT:
+ case KEY_AXIS_ROLL_INVERT:
+ case KEY_AXIS_THROTTLE_INVERT:
+ break;
+
+ default:
+ if (keymap.GetCategory(i) == selected_category) {
+ commands->AddItemWithData(keymap.DescribeAction(i), (DWORD) i);
+ commands->SetItemText(n++, 1, keymap.DescribeKey(i));
+ }
+ break;
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CtlDlg::UpdateCategory()
+{
+ if (!commands)
+ return;
+
+ Starshatter* stars = Starshatter::GetInstance();
+ KeyMap& keymap = stars->GetKeyMap();
+
+ for (int i = 0; i < commands->NumItems(); i++) {
+ int key = commands->GetItemData(i);
+ commands->SetItemText(i, 1, keymap.DescribeKey(key));
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CtlDlg::OnCategory(AWEvent* event)
+{
+ selected_category = 0;
+
+ for (int i = 0; i < 4; i++)
+ if (event->window == category[i])
+ selected_category = i;
+
+ ShowCategory();
+}
+
+// +--------------------------------------------------------------------+
+
+static DWORD command_click_time = 0;
+
+void
+CtlDlg::OnCommand(AWEvent* event)
+{
+ int list_index = commands->GetListIndex();
+
+ // double-click:
+ if (list_index == command_index && Game::RealTime() - command_click_time < 350) {
+ KeyDlg* key_dlg = 0;
+
+ if (manager)
+ key_dlg = manager->GetKeyDlg();
+
+ if (key_dlg) {
+ key_dlg->SetKeyMapIndex(commands->GetItemData(list_index));
+ }
+
+ if (manager)
+ manager->ShowKeyDlg();
+ }
+
+ command_click_time = Game::RealTime();
+ command_index = list_index;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CtlDlg::OnControlModel(AWEvent* event)
+{
+ control_model = control_model_combo->GetSelectedIndex();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CtlDlg::OnJoySelect(AWEvent* event)
+{
+ joy_select = joy_select_combo->GetSelectedIndex();
+}
+
+void
+CtlDlg::OnJoyThrottle(AWEvent* event)
+{
+ joy_throttle = joy_throttle_combo->GetSelectedIndex();
+}
+
+void
+CtlDlg::OnJoyRudder(AWEvent* event)
+{
+ joy_rudder = joy_rudder_combo->GetSelectedIndex();
+}
+
+void
+CtlDlg::OnJoySensitivity(AWEvent* event)
+{
+ joy_sensitivity = joy_sensitivity_slider->GetValue();
+}
+
+void
+CtlDlg::OnJoyAxis(AWEvent* event)
+{
+ if (manager)
+ manager->ShowJoyDlg();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CtlDlg::OnMouseSelect(AWEvent* event)
+{
+ mouse_select = mouse_select_combo->GetSelectedIndex();
+}
+
+void
+CtlDlg::OnMouseSensitivity(AWEvent* event)
+{
+ mouse_sensitivity = mouse_sensitivity_slider->GetValue();
+}
+
+void
+CtlDlg::OnMouseInvert(AWEvent* event)
+{
+ mouse_invert = mouse_invert_checkbox->GetButtonState() > 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void CtlDlg::OnAudio(AWEvent* event) { manager->ShowAudDlg(); }
+void CtlDlg::OnVideo(AWEvent* event) { manager->ShowVidDlg(); }
+void CtlDlg::OnOptions(AWEvent* event) { manager->ShowOptDlg(); }
+void CtlDlg::OnControls(AWEvent* event) { manager->ShowCtlDlg(); }
+void CtlDlg::OnMod(AWEvent* event) { manager->ShowModDlg(); }
+
+// +--------------------------------------------------------------------+
+
+void
+CtlDlg::OnApply(AWEvent* event)
+{
+ if (manager)
+ manager->ApplyOptions();
+}
+
+void
+CtlDlg::OnCancel(AWEvent* event)
+{
+ if (manager)
+ manager->CancelOptions();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CtlDlg::Apply()
+{
+ if (closed) return;
+
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars) {
+ KeyMap& keymap = stars->GetKeyMap();
+
+ keymap.Bind(KEY_CONTROL_MODEL, control_model, 0);
+
+ keymap.Bind(KEY_JOY_SELECT, joy_select, 0);
+ keymap.Bind(KEY_JOY_RUDDER, joy_rudder, 0);
+ keymap.Bind(KEY_JOY_THROTTLE, joy_throttle, 0);
+ keymap.Bind(KEY_JOY_SENSE, joy_sensitivity, 0);
+
+ keymap.Bind(KEY_MOUSE_SELECT, mouse_select, 0);
+ keymap.Bind(KEY_MOUSE_SENSE, mouse_sensitivity, 0);
+ keymap.Bind(KEY_MOUSE_INVERT, mouse_invert, 0);
+
+ keymap.SaveKeyMap("key.cfg", 256);
+
+ stars->MapKeys();
+ Ship::SetControlModel(control_model);
+ }
+
+ closed = true;
+}
+
+void
+CtlDlg::Cancel()
+{
+ closed = true;
+}
diff --git a/Stars45/CtlDlg.h b/Stars45/CtlDlg.h
new file mode 100644
index 0000000..4110c96
--- /dev/null
+++ b/Stars45/CtlDlg.h
@@ -0,0 +1,119 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: CtlDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Control Options (keyboard/joystick) Dialog Active Window class
+*/
+
+#ifndef CtlDlg_h
+#define CtlDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+
+// +--------------------------------------------------------------------+
+
+class BaseScreen;
+class MenuScreen;
+class GameScreen;
+
+// +--------------------------------------------------------------------+
+
+class CtlDlg : public FormWindow
+{
+public:
+ CtlDlg(Screen* s, FormDef& def, BaseScreen* mgr);
+ virtual ~CtlDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnCommand(AWEvent* event);
+ virtual void OnCategory(AWEvent* event);
+
+ virtual void OnControlModel(AWEvent* event);
+
+ virtual void OnJoySelect(AWEvent* event);
+ virtual void OnJoyThrottle(AWEvent* event);
+ virtual void OnJoyRudder(AWEvent* event);
+ virtual void OnJoySensitivity(AWEvent* event);
+ virtual void OnJoyAxis(AWEvent* event);
+
+ virtual void OnMouseSelect(AWEvent* event);
+ virtual void OnMouseSensitivity(AWEvent* event);
+ virtual void OnMouseInvert(AWEvent* event);
+
+ virtual void Apply();
+ virtual void Cancel();
+
+ virtual void OnApply(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+
+ virtual void OnAudio(AWEvent* event);
+ virtual void OnVideo(AWEvent* event);
+ virtual void OnOptions(AWEvent* event);
+ virtual void OnControls(AWEvent* event);
+ virtual void OnMod(AWEvent* event);
+
+protected:
+ void ShowCategory();
+ void UpdateCategory();
+
+ BaseScreen* manager;
+
+ Button* category[4];
+ ListBox* commands;
+ int command_index;
+
+ ComboBox* control_model_combo;
+
+ ComboBox* joy_select_combo;
+ ComboBox* joy_throttle_combo;
+ ComboBox* joy_rudder_combo;
+ Slider* joy_sensitivity_slider;
+ Button* joy_axis_button;
+
+ ComboBox* mouse_select_combo;
+ Slider* mouse_sensitivity_slider;
+ Button* mouse_invert_checkbox;
+
+ Button* aud_btn;
+ Button* vid_btn;
+ Button* opt_btn;
+ Button* ctl_btn;
+ Button* mod_btn;
+
+ Button* apply;
+ Button* cancel;
+
+ int selected_category;
+ int control_model;
+
+ int joy_select;
+ int joy_throttle;
+ int joy_rudder;
+ int joy_sensitivity;
+
+ int mouse_select;
+ int mouse_sensitivity;
+ int mouse_invert;
+
+ bool closed;
+};
+
+#endif CtlDlg_h
+
diff --git a/Stars45/DebriefDlg.cpp b/Stars45/DebriefDlg.cpp
new file mode 100644
index 0000000..ea2fab2
--- /dev/null
+++ b/Stars45/DebriefDlg.cpp
@@ -0,0 +1,375 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: DebriefDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Debriefing Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "DebriefDlg.h"
+#include "PlanScreen.h"
+#include "Starshatter.h"
+#include "Campaign.h"
+#include "Element.h"
+#include "Instruction.h"
+#include "Mission.h"
+#include "Sim.h"
+#include "SimEvent.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "StarSystem.h"
+#include "FormatUtil.h"
+#include "Player.h"
+#include "Campaign.h"
+
+#include "NetLobby.h"
+#include "HttpServer.h"
+
+#include "Game.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "ParseUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(DebriefDlg, OnClose);
+DEF_MAP_CLIENT(DebriefDlg, OnUnit);
+
+// +--------------------------------------------------------------------+
+
+DebriefDlg::DebriefDlg(Screen* s, FormDef& def, PlanScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ close_btn(0), campaign(0), mission(0),
+ unit_index(0), info(0), sim(0), ship(0)
+{
+ campaign = Campaign::GetCampaign();
+
+ if (campaign)
+ mission = campaign->GetMission();
+
+ Init(def);
+}
+
+DebriefDlg::~DebriefDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DebriefDlg::RegisterControls()
+{
+ mission_name = FindControl(200);
+ mission_system = FindControl(202);
+ mission_sector = FindControl(204);
+ mission_time_start = FindControl(206);
+
+ objectives = FindControl(210);
+ situation = FindControl(240);
+ mission_score = FindControl(211);
+ unit_list = (ListBox*) FindControl(320);
+ summary_list = (ListBox*) FindControl(330);
+ event_list = (ListBox*) FindControl(340);
+
+ if (unit_list)
+ REGISTER_CLIENT(EID_SELECT, unit_list, DebriefDlg, OnUnit);
+
+ close_btn = (Button*) FindControl(1);
+
+ if (close_btn)
+ REGISTER_CLIENT(EID_CLICK, close_btn, DebriefDlg, OnClose);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DebriefDlg::Show()
+{
+ FormWindow::Show();
+ Game::SetTimeCompression(1);
+
+ mission = 0;
+ campaign = Campaign::GetCampaign();
+ sim = Sim::GetSim();
+
+ if (sim)
+ ship = sim->GetPlayerShip();
+
+ if (campaign)
+ mission = campaign->GetMission();
+
+ if (mission_name) {
+ if (mission)
+ mission_name->SetText(mission->Name());
+ else
+ mission_name->SetText(Game::GetText("DebriefDlg.mission-name"));
+ }
+
+ if (mission_system) {
+ mission_system->SetText("");
+
+ if (mission) {
+ StarSystem* sys = mission->GetStarSystem();
+
+ if (sys)
+ mission_system->SetText(sys->Name());
+ }
+ }
+
+ if (mission_sector) {
+ mission_sector->SetText("");
+
+ if (mission) {
+ MissionElement* elem = mission->GetElements()[0];
+
+ if (elem)
+ mission_sector->SetText(elem->Region());
+ }
+ }
+
+ if (mission_time_start) {
+ if (mission) {
+ char txt[32];
+ FormatDayTime(txt, mission->Start());
+ mission_time_start->SetText(txt);
+ }
+ }
+
+ if (objectives) {
+ bool found_objectives = false;
+
+ if (sim && sim->GetPlayerElement()) {
+ Text text;
+ Element* elem = sim->GetPlayerElement();
+
+ for (int i = 0; i < elem->NumObjectives(); i++) {
+ Instruction* obj = elem->GetObjective(i);
+ text += Text("* ") + obj->GetDescription() + Text("\n");
+
+ found_objectives = true;
+ }
+
+ objectives->SetText(text);
+ }
+
+ if (!found_objectives) {
+ if (mission)
+ objectives->SetText(mission->Objective());
+ else
+ objectives->SetText(Game::GetText("DebriefDlg.unspecified"));
+ }
+ }
+
+ if (situation) {
+ if (mission)
+ situation->SetText(mission->Situation());
+ else
+ situation->SetText(Game::GetText("DebriefDlg.unknown"));
+ }
+
+ if (mission_score) {
+ mission_score->SetText(Game::GetText("DebriefDlg.no-stats"));
+
+ if (ship) {
+ for (int i = 0; i < ShipStats::NumStats(); i++) {
+ ShipStats* stats = ShipStats::GetStats(i);
+ if (stats && !strcmp(ship->Name(), stats->GetName())) {
+ stats->Summarize();
+
+ Player* player = Player::GetCurrentPlayer();
+ int points = stats->GetPoints() +
+ stats->GetCommandPoints();
+
+ if (player && sim)
+ points = player->GetMissionPoints(stats, sim->StartTime()) +
+ stats->GetCommandPoints();
+
+ char score[32];
+ sprintf(score, "%d %s", points, Game::GetText("DebriefDlg.points").data());
+ mission_score->SetText(score);
+ break;
+ }
+ }
+ }
+ }
+
+ DrawUnits();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DebriefDlg::DrawUnits()
+{
+ if (mission) {
+ if (unit_list) {
+ unit_list->ClearItems();
+
+ int seln = -1;
+ bool netgame = false;
+
+ if (sim && sim->IsNetGame())
+ netgame = true;
+
+ for (int i = 0; i < ShipStats::NumStats(); i++) {
+ ShipStats* stats = ShipStats::GetStats(i);
+ stats->Summarize();
+
+ if (netgame || (stats->GetIFF() == mission->Team() &&
+ !strcmp(stats->GetRegion(), mission->GetRegion()))) {
+ int n = unit_list->AddItemWithData(" ", i) - 1;
+ unit_list->SetItemText(n, 1, stats->GetName());
+ unit_list->SetItemText(n, 2, stats->GetRole());
+ unit_list->SetItemText(n, 3, stats->GetType());
+
+ if (ship && !strcmp(ship->Name(), stats->GetName()))
+ seln = n;
+ }
+ }
+
+ if (seln >= 0) {
+ unit_list->SetSelected(seln);
+ OnUnit(0);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DebriefDlg::ExecFrame()
+{
+ if (unit_list && unit_list->NumItems() && unit_list->GetSelCount() < 1) {
+ unit_list->SetSelected(0);
+ OnUnit(0);
+ }
+
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ OnClose(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DebriefDlg::OnUnit(AWEvent* event)
+{
+ if (!unit_list || !event_list || !summary_list)
+ return;
+
+ summary_list->ClearItems();
+ event_list->ClearItems();
+
+ int seln = unit_list->GetSelection();
+ int unit = unit_list->GetItemData(seln);
+
+ ShipStats* stats = ShipStats::GetStats(unit);
+ if (stats) {
+ stats->Summarize();
+
+ char txt[64];
+ int i = 0;
+
+ sprintf(txt, "%d", stats->GetGunShots());
+ summary_list->AddItem("Guns Fired: ");
+ summary_list->SetItemText(i++, 1, txt);
+
+ sprintf(txt, "%d", stats->GetGunHits());
+ summary_list->AddItem("Gun Hits: ");
+ summary_list->SetItemText(i++, 1, txt);
+
+ sprintf(txt, "%d", stats->GetGunKills());
+ summary_list->AddItem("Gun Kills: ");
+ summary_list->SetItemText(i++, 1, txt);
+
+ // one line spacer:
+ summary_list->AddItem(" ");
+ i++;
+
+ sprintf(txt, "%d", stats->GetMissileShots());
+ summary_list->AddItem("Missiles Fired: ");
+ summary_list->SetItemText(i++, 1, txt);
+
+ sprintf(txt, "%d", stats->GetMissileHits());
+ summary_list->AddItem("Missile Hits: ");
+ summary_list->SetItemText(i++, 1, txt);
+
+ sprintf(txt, "%d", stats->GetMissileKills());
+ summary_list->AddItem("Missile Kills: ");
+ summary_list->SetItemText(i++, 1, txt);
+
+ i = 0;
+ ListIter<SimEvent> iter = stats->GetEvents();
+ while (++iter) {
+ SimEvent* event = iter.value();
+
+ char txt[64];
+ int time = event->GetTime();
+
+ if (time > 24 * 60 * 60)
+ FormatDayTime(txt, time);
+ else
+ FormatTime(txt, time);
+
+ event_list->AddItem(txt);
+ event_list->SetItemText(i, 1, event->GetEventDesc());
+
+ if (event->GetTarget())
+ event_list->SetItemText(i, 2, event->GetTarget());
+
+ i++;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DebriefDlg::OnClose(AWEvent* event)
+{
+ Sim* sim = Sim::GetSim();
+
+ sim->CommitMission();
+ sim->UnloadMission();
+
+ NetLobby* lobby = NetLobby::GetInstance();
+ if (lobby && lobby->IsHost()) {
+ lobby->SelectMission(0);
+ lobby->ExecFrame();
+ }
+
+ Player* player = Player::GetCurrentPlayer();
+ if (player && player->ShowAward()) {
+ manager->ShowAwardDlg();
+ }
+
+ else {
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars) {
+ Mouse::Show(false);
+
+ Campaign* campaign = Campaign::GetCampaign();
+ if (campaign && campaign->GetCampaignId() < Campaign::SINGLE_MISSIONS)
+ stars->SetGameMode(Starshatter::CMPN_MODE);
+ else
+ stars->SetGameMode(Starshatter::MENU_MODE);
+ }
+
+ else {
+ Game::Panic("DebriefDlg::OnClose() - Game instance not found");
+ }
+ }
+}
diff --git a/Stars45/DebriefDlg.h b/Stars45/DebriefDlg.h
new file mode 100644
index 0000000..8c0fce4
--- /dev/null
+++ b/Stars45/DebriefDlg.h
@@ -0,0 +1,81 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: DebriefDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Debriefing Dialog Active Window class
+*/
+
+#ifndef DebriefDlg_h
+#define DebriefDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class PlanScreen;
+class Campaign;
+class Mission;
+class MissionInfo;
+class Sim;
+class Ship;
+
+// +--------------------------------------------------------------------+
+
+class DebriefDlg : public FormWindow
+{
+public:
+ DebriefDlg(Screen* s, FormDef& def, PlanScreen* mgr);
+ virtual ~DebriefDlg();
+
+ virtual void RegisterControls();
+ virtual void ExecFrame();
+ virtual void Show();
+
+ // Operations:
+ virtual void OnClose(AWEvent* event);
+ virtual void OnUnit(AWEvent* event);
+
+protected:
+ virtual void DrawUnits();
+
+ PlanScreen* manager;
+ Button* close_btn;
+
+ ActiveWindow* mission_name;
+ ActiveWindow* mission_system;
+ ActiveWindow* mission_sector;
+ ActiveWindow* mission_time_start;
+
+ ActiveWindow* objectives;
+ ActiveWindow* situation;
+ ActiveWindow* mission_score;
+
+ ListBox* unit_list;
+ ListBox* summary_list;
+ ListBox* event_list;
+
+ Campaign* campaign;
+ Mission* mission;
+ MissionInfo* info;
+ int unit_index;
+
+ Sim* sim;
+ Ship* ship;
+};
+
+#endif DebriefDlg_h
+
diff --git a/Stars45/Debris.cpp b/Stars45/Debris.cpp
new file mode 100644
index 0000000..18624b7
--- /dev/null
+++ b/Stars45/Debris.cpp
@@ -0,0 +1,220 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Debris.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Debris Sprite animation class
+*/
+
+#include "MemDebug.h"
+#include "Debris.h"
+#include "Shot.h"
+#include "Explosion.h"
+#include "Sim.h"
+#include "StarSystem.h"
+#include "Terrain.h"
+
+#include "Solid.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Game.h"
+#include "Random.h"
+
+// +--------------------------------------------------------------------+
+
+Debris::Debris(Model* model, const Vec3& pos, const Vec3& vel, double m)
+ : SimObject("Debris", SimObject::SIM_DEBRIS)
+{
+ MoveTo(pos);
+
+ velocity = vel;
+ mass = (float) m;
+ integrity = mass * 10.0f;
+ life = 300;
+
+ Solid* solid = new(__FILE__,__LINE__) Solid;
+
+ if (solid) {
+ solid->UseModel(model);
+ solid->MoveTo(pos);
+
+ rep = solid;
+
+ radius = solid->Radius();
+ }
+
+ Point torque = RandomVector(Mass()/2);
+
+ if (Mass() < 10)
+ torque *= (rand() / 3200);
+ else if (Mass() > 10e3)
+ torque *= 0.25;
+ else if (Mass() > 10e6)
+ torque *= 0.005;
+
+ ApplyTorque(torque);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Debris::HitBy(Shot* shot, Point& impact)
+{
+ if (!shot->IsArmed()) return 0;
+
+ const int HIT_NOTHING = 0;
+ const int HIT_HULL = 1;
+
+ Point hull_impact;
+ int hit_type = HIT_NOTHING;
+ bool hit_hull = true;
+ Point shot_loc = shot->Location();
+ Point delta = shot_loc - Location();
+ double dlen = delta.length();
+ double dscale = 1;
+ float scale = 1.0f;
+ Sim* sim = Sim::GetSim();
+
+ // MISSILE PROCESSING ------------------------------------------------
+
+ if (shot->IsMissile()) {
+ if (dlen < Radius()) {
+ hull_impact = impact = shot_loc;
+ sim->CreateExplosion(impact, Velocity(), Explosion::HULL_FLASH, 0.3f * scale, scale, region, this);
+ sim->CreateExplosion(impact, Point(), Explosion::SHOT_BLAST, 2.0f, scale, region);
+ hit_type = HIT_HULL;
+ }
+ }
+
+ // ENERGY WEP PROCESSING ---------------------------------------------
+
+ else {
+
+ Solid* solid = (Solid*) rep;
+
+ Point shot_loc = shot->Location();
+ Point shot_vpn = shot_loc - shot->Origin();
+ double shot_len = shot_vpn.Normalize();
+ if (shot_len == 0) shot_len = 1000;
+
+ // impact:
+ if (solid) {
+ if (solid->CheckRayIntersection(shot->Origin(), shot_vpn, shot_len, impact)) {
+ // trim beam shots to impact point:
+ if (shot->IsBeam())
+ shot->SetBeamPoints(shot->Origin(), impact);
+
+ hull_impact = impact;
+
+ if (shot->IsBeam())
+ sim->CreateExplosion(impact, Velocity(), Explosion::BEAM_FLASH, 0.30f * scale, scale, region, this);
+ else
+ sim->CreateExplosion(impact, Velocity(), Explosion::HULL_FLASH, 0.30f * scale, scale, region, this);
+
+ Point burst_vel = hull_impact - Location();
+ burst_vel.Normalize();
+ burst_vel *= Radius() * 0.5;
+ burst_vel += Velocity();
+
+ sim->CreateExplosion(hull_impact, burst_vel, Explosion::HULL_BURST, 0.50f * scale, scale, region, this);
+
+ hit_type = HIT_HULL;
+ hit_hull = true;
+ }
+ }
+
+ else {
+ if (dlen < Radius()) {
+ hull_impact = impact = shot_loc;
+
+ if (shot->IsBeam())
+ sim->CreateExplosion(impact, Velocity(), Explosion::BEAM_FLASH, 0.30f * scale, scale, region, this);
+ else
+ sim->CreateExplosion(impact, Velocity(), Explosion::HULL_FLASH, 0.30f * scale, scale, region, this);
+
+ hit_type = HIT_HULL;
+ }
+ }
+ }
+
+ // DAMAGE RESOLUTION -------------------------------------------------
+
+ if (hit_type != HIT_NOTHING) {
+ double effective_damage = shot->Damage() * dscale;
+
+ if (shot->IsBeam()) {
+ effective_damage *= Game::FrameTime();
+ }
+ else {
+ ApplyTorque(shot->Velocity() * (float) effective_damage * 1e-6f);
+ }
+
+ if (effective_damage > 0)
+ Physical::InflictDamage(effective_damage);
+ }
+
+ return hit_type;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Debris::ExecFrame(double seconds)
+{
+ if (GetRegion()->Type() == SimRegion::AIR_SPACE) {
+ if (AltitudeAGL() < Radius()) {
+ velocity = Point();
+ arcade_velocity = Point();
+
+ Terrain* terrain = region->GetTerrain();
+
+ if (terrain) {
+ Point loc = Location();
+ MoveTo(Point(loc.x, terrain->Height(loc.x, loc.z), loc.z));
+ }
+ }
+ else {
+ if (mass > 100) {
+ Orbital* primary = GetRegion()->GetOrbitalRegion()->Primary();
+
+ const double GRAV = 6.673e-11;
+ double m0 = primary->Mass();
+ double r = primary->Radius();
+
+ SetDrag(0.001);
+ SetGravity(6 * GRAV * m0 / (r*r)); // accentuate gravity
+ SetBaseDensity(1.0f);
+ }
+
+ AeroFrame(seconds);
+ }
+ }
+ else {
+ Physical::ExecFrame(seconds);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Debris::AltitudeAGL() const
+{
+ Point loc = Location();
+ double altitude_agl = loc.y;
+
+ Terrain* terrain = region->GetTerrain();
+
+ if (terrain)
+ altitude_agl -= terrain->Height(loc.x, loc.z);
+
+ if (!_finite(altitude_agl))
+ altitude_agl = 0;
+
+ return altitude_agl;
+} \ No newline at end of file
diff --git a/Stars45/Debris.h b/Stars45/Debris.h
new file mode 100644
index 0000000..03802b0
--- /dev/null
+++ b/Stars45/Debris.h
@@ -0,0 +1,43 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Debris.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Debris Sprite class
+*/
+
+#ifndef Debris_h
+#define Debris_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "SimObject.h"
+
+// +--------------------------------------------------------------------+
+
+class Solid;
+class Model;
+class Shot;
+
+// +--------------------------------------------------------------------+
+
+class Debris : public SimObject
+{
+public:
+ Debris(Model* model, const Vec3& pos, const Vec3& vel, double mass);
+
+ void SetLife(int seconds) { life = seconds; }
+ virtual int HitBy(Shot* shot, Point& impact);
+
+ virtual void ExecFrame(double seconds);
+ virtual double AltitudeAGL() const;
+};
+
+#endif Debris_h
+
diff --git a/Stars45/DetailSet.cpp b/Stars45/DetailSet.cpp
new file mode 100644
index 0000000..39f49d0
--- /dev/null
+++ b/Stars45/DetailSet.cpp
@@ -0,0 +1,228 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: DetailSet.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Level of Detail manager class
+*/
+
+#include "MemDebug.h"
+#include "DetailSet.h"
+#include "Random.h"
+
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+
+SimRegion* DetailSet::ref_rgn = 0;
+Point DetailSet::ref_loc;
+
+// +--------------------------------------------------------------------+
+
+DetailSet::DetailSet()
+{
+ for (int i = 0; i < MAX_DETAIL; i++)
+ rad[i] = 0;
+
+ index = -1;
+ levels = 0;
+ rgn = 0;
+}
+
+DetailSet::~DetailSet()
+{
+ Destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DetailSet::DefineLevel(double r, Graphic* g, Point* o, Point* spin_rate)
+{
+ if (levels < MAX_DETAIL && rep[levels].size() == 0) {
+ rad[levels] = r;
+
+ if (g)
+ rep[levels].append(g);
+
+ if (o)
+ off[levels].append(o);
+
+ else if (g)
+ off[levels].append(new(__FILE__,__LINE__) Point(0,0,0));
+
+ if (rate.size() == 0) {
+ if (spin_rate) {
+ rate.append(spin_rate);
+
+ // randomize the initial orientation:
+ Point* initial_spin = new(__FILE__,__LINE__) Point();
+ if (spin_rate->x != 0) initial_spin->x = Random(-PI, PI);
+ if (spin_rate->y != 0) initial_spin->y = Random(-PI, PI);
+ if (spin_rate->z != 0) initial_spin->z = Random(-PI, PI);
+
+ spin.append(initial_spin);
+ }
+ else {
+ rate.append(new(__FILE__,__LINE__) Point());
+ spin.append(new(__FILE__,__LINE__) Point());
+ }
+ }
+ else {
+ if (spin_rate)
+ rate[ rep[levels].size() - 1 ] = spin_rate;
+ }
+
+ levels++;
+ }
+
+ return levels-1;
+}
+
+void
+DetailSet::AddToLevel(int level, Graphic* g, Point* offset, Point* spin_rate)
+{
+ if (g && level >= 0 && level < levels) {
+ rep[level].append(g);
+
+ if (!offset)
+ offset = new(__FILE__,__LINE__) Point(0,0,0);
+
+ off[level].append(offset);
+
+ if (spin_rate) {
+ int nrep = rep[level].size();
+ if (nrep > rate.size())
+ rate.append(spin_rate);
+ else
+ rate[ nrep-1 ] = spin_rate;
+ }
+
+ if (spin.size() < rep[level].size()) {
+ Point* initial_spin = new(__FILE__,__LINE__) Point();
+
+ if (spin_rate) {
+ // randomize the initial orientation:
+ if (spin_rate->x != 0) initial_spin->x = Random(-PI, PI);
+ if (spin_rate->y != 0) initial_spin->y = Random(-PI, PI);
+ if (spin_rate->z != 0) initial_spin->z = Random(-PI, PI);
+ }
+
+ spin.append(initial_spin);
+ }
+ }
+}
+
+int
+DetailSet::NumModels(int level) const
+{
+ if (level >= 0 && level < levels)
+ return rep[level].size();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DetailSet::ExecFrame(double seconds)
+{
+ for (int i = 0; i < spin.size() && i < rate.size(); i++)
+ (*spin[i]) += (*rate[i]) * seconds;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DetailSet::SetLocation(SimRegion* r, const Point& p)
+{
+ rgn = r;
+ loc = p;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DetailSet::SetReference(SimRegion* r, const Point& p)
+{
+ ref_rgn = r;
+ ref_loc = p;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DetailSet::GetDetailLevel()
+{
+ index = 0;
+
+ if (rgn == ref_rgn) {
+ double screen_width = Game::GetScreenWidth();
+ Point delta = loc - ref_loc;
+ double distance = delta.length();
+
+ for (int i = 1; i < levels && rad[i] > 0; i++) {
+ double apparent_feature_size = (rad[i] * screen_width) / distance;
+
+ if (apparent_feature_size > 0.4)
+ index = i;
+ }
+ }
+
+ return index;
+}
+
+// +--------------------------------------------------------------------+
+
+Graphic*
+DetailSet::GetRep(int level, int n)
+{
+ if (level >= 0 && level < levels && n >= 0 && n < rep[level].size())
+ return rep[level].at(n);
+
+ return 0;
+}
+
+Point
+DetailSet::GetOffset(int level, int n)
+{
+ if (level >= 0 && level < levels && n >= 0 && n < off[level].size())
+ return *(off[level].at(n));
+
+ return Point();
+}
+
+Point
+DetailSet::GetSpin(int level, int n)
+{
+ if (n >= 0 && n < spin.size())
+ return *(spin.at(n));
+
+ return Point();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DetailSet::Destroy()
+{
+ for (int i = 0; i < levels; i++) {
+ ListIter<Graphic> iter = rep[i];
+
+ while (++iter) {
+ Graphic* g = iter.removeItem();
+ g->Destroy(); // this will delete the object (g)
+ }
+
+ off[i].destroy();
+ }
+
+ rate.destroy();
+ spin.destroy();
+}
diff --git a/Stars45/DetailSet.h b/Stars45/DetailSet.h
new file mode 100644
index 0000000..8a847ed
--- /dev/null
+++ b/Stars45/DetailSet.h
@@ -0,0 +1,73 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: DetailSet.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Level of Detail Manger class
+*/
+
+#ifndef DetailSet_h
+#define DetailSet_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Graphic.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Sim;
+class SimRegion;
+
+// +--------------------------------------------------------------------+
+
+class DetailSet
+{
+public:
+ enum { MAX_DETAIL = 4 };
+
+ DetailSet();
+ virtual ~DetailSet();
+
+ int DefineLevel(double r, Graphic* g=0, Point* offset=0, Point* spin=0);
+ void AddToLevel(int level, Graphic* g, Point* offset=0, Point* spin=0);
+ int NumLevels() const { return levels; }
+ int NumModels(int level) const;
+
+ void ExecFrame(double seconds);
+ void SetLocation(SimRegion* rgn, const Point& loc);
+ static void SetReference(SimRegion* rgn, const Point& loc);
+
+ int GetDetailLevel();
+ Graphic* GetRep(int level, int n=0);
+ Point GetOffset(int level, int n=0);
+ Point GetSpin(int level, int n=0);
+ void Destroy();
+
+protected:
+ List<Graphic> rep[MAX_DETAIL];
+ List<Point> off[MAX_DETAIL];
+ double rad[MAX_DETAIL];
+
+ List<Point> spin;
+ List<Point> rate;
+
+ int index;
+ int levels;
+ SimRegion* rgn;
+ Point loc;
+
+ static SimRegion* ref_rgn;
+ static Point ref_loc;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif DetailSet_h
+
diff --git a/Stars45/DisplayView.cpp b/Stars45/DisplayView.cpp
new file mode 100644
index 0000000..64088df
--- /dev/null
+++ b/Stars45/DisplayView.cpp
@@ -0,0 +1,239 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: DisplayView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ View class for Quantum Destination HUD Overlay
+*/
+
+#include "MemDebug.h"
+#include "DisplayView.h"
+#include "QuantumDrive.h"
+#include "HUDView.h"
+#include "Ship.h"
+#include "Element.h"
+#include "Sim.h"
+#include "StarSystem.h"
+#include "FormatUtil.h"
+
+#include "Color.h"
+#include "Window.h"
+#include "Video.h"
+#include "Screen.h"
+#include "DataLoader.h"
+#include "Scene.h"
+#include "Font.h"
+#include "FontMgr.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "Game.h"
+#include "Menu.h"
+
+// +====================================================================+
+
+class DisplayElement
+{
+public:
+ static const char* TYPENAME() { return "DisplayElement"; }
+
+ DisplayElement() : image(0), font(0), blend(0), hold(0), fade_in(0), fade_out(0) { }
+
+ Text text;
+ Bitmap* image;
+ Font* font;
+ Color color;
+ Rect rect;
+ int blend;
+ double hold;
+ double fade_in;
+ double fade_out;
+};
+
+// +====================================================================+
+
+DisplayView* display_view = 0;
+
+DisplayView::DisplayView(Window* c)
+ : View(c), width(0), height(0), xcenter(0), ycenter(0)
+{
+ display_view = this;
+ DisplayView::OnWindowMove();
+}
+
+DisplayView::~DisplayView()
+{
+ if (display_view == this)
+ display_view = 0;
+
+ elements.destroy();
+}
+
+DisplayView*
+DisplayView::GetInstance()
+{
+ if (display_view == 0)
+ display_view = new DisplayView(0);
+
+ return display_view;
+}
+
+void
+DisplayView::OnWindowMove()
+{
+ if (window) {
+ width = window->Width();
+ height = window->Height();
+ xcenter = (width / 2.0) - 0.5;
+ ycenter = (height / 2.0) + 0.5;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DisplayView::Refresh()
+{
+ ListIter<DisplayElement> iter = elements;
+ while (++iter) {
+ DisplayElement* elem = iter.value();
+
+ // convert relative rect to window rect:
+ Rect elem_rect = elem->rect;
+ if (elem_rect.x == 0 && elem_rect.y == 0 && elem_rect.w == 0 && elem_rect.h == 0) {
+ // stretch to fit
+ elem_rect.w = width;
+ elem_rect.h = height;
+ }
+ else if (elem_rect.w < 0 && elem_rect.h < 0) {
+ // center image in window
+ elem_rect.w *= -1;
+ elem_rect.h *= -1;
+
+ elem_rect.x = (width - elem_rect.w)/2;
+ elem_rect.y = (height - elem_rect.h)/2;
+ }
+ else {
+ // offset from right or bottom
+ if (elem_rect.x < 0) elem_rect.x += width;
+ if (elem_rect.y < 0) elem_rect.y += height;
+ }
+
+ // compute current fade,
+ // assumes fades are 1 second or less:
+ double fade = 0;
+ if (elem->fade_in > 0) fade = 1 - elem->fade_in;
+ else if (elem->hold > 0) fade = 1;
+ else if (elem->fade_out > 0) fade = elem->fade_out;
+
+ // draw text:
+ if (elem->text.length() && elem->font) {
+ elem->font->SetColor(elem->color);
+ elem->font->SetAlpha(fade);
+ window->SetFont(elem->font);
+ window->DrawText(elem->text, elem->text.length(), elem_rect, DT_WORDBREAK);
+ }
+
+ // draw image:
+ else if (elem->image) {
+ window->FadeBitmap( elem_rect.x,
+ elem_rect.y,
+ elem_rect.x + elem_rect.w,
+ elem_rect.y + elem_rect.h,
+ elem->image,
+ elem->color * fade,
+ elem->blend );
+
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DisplayView::ExecFrame()
+{
+ double seconds = Game::GUITime();
+
+ ListIter<DisplayElement> iter = elements;
+ while (++iter) {
+ DisplayElement* elem = iter.value();
+
+ if (elem->fade_in > 0)
+ elem->fade_in -= seconds;
+
+ else if (elem->hold > 0)
+ elem->hold -= seconds;
+
+ else if (elem->fade_out > 0)
+ elem->fade_out -= seconds;
+
+ else
+ delete iter.removeItem();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DisplayView::ClearDisplay()
+{
+ elements.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DisplayView::AddText( const char* text,
+ Font* font,
+ Color color,
+ const Rect& rect,
+ double hold,
+ double fade_in,
+ double fade_out)
+{
+ DisplayElement* elem = new(__FILE__,__LINE__) DisplayElement;
+
+ if (fade_in == 0 && fade_out == 0 && hold == 0)
+ hold = 300;
+
+ elem->text = text;
+ elem->font = font;
+ elem->color = color;
+ elem->rect = rect;
+ elem->hold = hold;
+ elem->fade_in = fade_in;
+ elem->fade_out = fade_out;
+
+ elements.append(elem);
+}
+
+void
+DisplayView::AddImage(Bitmap* bmp,
+ Color color,
+ int blend,
+ const Rect& rect,
+ double hold,
+ double fade_in,
+ double fade_out)
+{
+ DisplayElement* elem = new(__FILE__,__LINE__) DisplayElement;
+
+ if (fade_in == 0 && fade_out == 0 && hold == 0)
+ hold = 300;
+
+ elem->image = bmp;
+ elem->rect = rect;
+ elem->color = color;
+ elem->blend = blend;
+ elem->hold = hold;
+ elem->fade_in = fade_in;
+ elem->fade_out = fade_out;
+
+ elements.append(elem);
+}
diff --git a/Stars45/DisplayView.h b/Stars45/DisplayView.h
new file mode 100644
index 0000000..616e5f7
--- /dev/null
+++ b/Stars45/DisplayView.h
@@ -0,0 +1,71 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: DisplayView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ View class for Radio Communications HUD Overlay
+*/
+
+#ifndef DisplayView_h
+#define DisplayView_h
+
+#include "Types.h"
+#include "View.h"
+#include "SimObject.h"
+#include "Color.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+class DisplayElement;
+class Font;
+
+// +--------------------------------------------------------------------+
+
+class DisplayView : public View
+{
+public:
+ DisplayView(Window* c);
+ virtual ~DisplayView();
+
+ // Operations:
+ virtual void Refresh();
+ virtual void OnWindowMove();
+ virtual void ExecFrame();
+ virtual void ClearDisplay();
+
+ virtual void AddText(const char* txt,
+ Font* font,
+ Color color,
+ const Rect& rect,
+ double hold = 1e9,
+ double fade_in = 0,
+ double fade_out = 0);
+
+ virtual void AddImage(Bitmap* bmp,
+ Color color,
+ int blend,
+ const Rect& rect,
+ double hold = 1e9,
+ double fade_in = 0,
+ double fade_out = 0);
+
+ static DisplayView* GetInstance();
+
+protected:
+ int width, height;
+ double xcenter, ycenter;
+
+ List<DisplayElement>
+ elements;
+};
+
+#endif DisplayView_h
+
diff --git a/Stars45/Drive.cpp b/Stars45/Drive.cpp
new file mode 100644
index 0000000..21748c1
--- /dev/null
+++ b/Stars45/Drive.cpp
@@ -0,0 +1,497 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Drive.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Weapon class
+*/
+
+#include "MemDebug.h"
+#include "Drive.h"
+#include "Power.h"
+#include "Ship.h"
+#include "Sim.h"
+#include "DriveSprite.h"
+#include "CameraDirector.h"
+#include "AudioConfig.h"
+
+#include "Light.h"
+#include "Bitmap.h"
+#include "Sound.h"
+#include "DataLoader.h"
+#include "Bolt.h"
+#include "Solid.h"
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+
+static int drive_value[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1
+};
+
+static float drive_light[] = {
+ 10.0f, 100.0f, 5.0f, 1.0e3f, 100.0f, 10.0f, 0.0f, 0.0f
+};
+
+Bitmap* drive_flare_bitmap[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+Bitmap* drive_trail_bitmap[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+Bitmap* drive_glow_bitmap[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+static Sound* sound_resource[3] = { 0, 0, 0 };
+
+#define CLAMP(x, a, b) if (x < (a)) x = (a); else if (x > (b)) x = (b);
+
+// +----------------------------------------------------------------------+
+
+DrivePort::DrivePort(const Point& l, float s)
+ : loc(l), flare(0), trail(0), scale(s)
+{ }
+
+DrivePort::~DrivePort()
+{
+ GRAPHIC_DESTROY(flare);
+ GRAPHIC_DESTROY(trail);
+}
+
+// +----------------------------------------------------------------------+
+
+Drive::Drive(SUBTYPE drive_type, float max_thrust, float max_aug, bool show)
+ : System(DRIVE, drive_type, "Drive", drive_value[drive_type],
+ max_thrust*2, max_thrust*2, max_thrust*2),
+ thrust(max_thrust), augmenter(max_aug), scale(0.0f),
+ throttle(0.0f), augmenter_throttle(0.0f), intensity(0.0f),
+ sound(0), burner_sound(0), show_trail(show)
+{
+ power_flags = POWER_WATTS;
+
+ switch (drive_type) {
+ default:
+ case PLASMA: name = Game::GetText("sys.drive.plasma"); break;
+ case FUSION: name = Game::GetText("sys.drive.fusion"); break;
+ case GREEN: name = Game::GetText("sys.drive.green"); break;
+ case RED: name = Game::GetText("sys.drive.red"); break;
+ case BLUE: name = Game::GetText("sys.drive.blue"); break;
+ case YELLOW: name = Game::GetText("sys.drive.yellow"); break;
+ case STEALTH: name = Game::GetText("sys.drive.stealth"); break;
+ }
+
+ abrv = Game::GetText("sys.drive.abrv");
+
+ emcon_power[0] = 0;
+ emcon_power[1] = 50;
+ emcon_power[2] = 100;
+}
+
+// +----------------------------------------------------------------------+
+
+Drive::Drive(const Drive& d)
+ : System(d), thrust(d.thrust), augmenter(d.augmenter), scale(d.scale),
+ throttle(0.0f), augmenter_throttle(0.0f), intensity(0.0f),
+ sound(0), burner_sound(0), show_trail(d.show_trail)
+{
+ power_flags = POWER_WATTS;
+
+ Mount(d);
+
+ if (subtype != Drive::STEALTH) {
+ for (int i = 0; i < d.ports.size(); i++) {
+ DrivePort* p = d.ports[i];
+ CreatePort(p->loc, p->scale);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Drive::~Drive()
+{
+ if (sound) {
+ sound->Stop();
+ sound->Release();
+ sound = 0;
+ }
+
+ if (burner_sound) {
+ burner_sound->Stop();
+ burner_sound->Release();
+ burner_sound = 0;
+ }
+
+ ports.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Drive::Initialize()
+{
+ static int initialized = 0;
+ if (initialized) return;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Drive/");
+ loader->LoadTexture("Drive0.pcx", drive_flare_bitmap[0], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("Drive1.pcx", drive_flare_bitmap[1], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("Drive2.pcx", drive_flare_bitmap[2], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("Drive3.pcx", drive_flare_bitmap[3], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("Drive4.pcx", drive_flare_bitmap[4], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("Drive5.pcx", drive_flare_bitmap[5], Bitmap::BMP_TRANSLUCENT);
+
+ loader->LoadTexture("Trail0.pcx", drive_trail_bitmap[0], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("Trail1.pcx", drive_trail_bitmap[1], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("Trail2.pcx", drive_trail_bitmap[2], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("Trail3.pcx", drive_trail_bitmap[3], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("Trail4.pcx", drive_trail_bitmap[4], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("Trail5.pcx", drive_trail_bitmap[5], Bitmap::BMP_TRANSLUCENT);
+
+ loader->LoadTexture("Glow0.pcx", drive_glow_bitmap[0], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("Glow1.pcx", drive_glow_bitmap[1], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("Glow2.pcx", drive_glow_bitmap[2], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("Glow3.pcx", drive_glow_bitmap[3], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("Glow4.pcx", drive_glow_bitmap[4], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("Glow5.pcx", drive_glow_bitmap[5], Bitmap::BMP_TRANSLUCENT);
+
+ const int SOUND_FLAGS = Sound::LOCALIZED |
+ Sound::LOC_3D |
+ Sound::LOOP |
+ Sound::LOCKED;
+
+ loader->SetDataPath("Sounds/");
+ loader->LoadSound("engine.wav", sound_resource[0], SOUND_FLAGS);
+ loader->LoadSound("burner2.wav", sound_resource[1], SOUND_FLAGS);
+ loader->LoadSound("rumble.wav", sound_resource[2], SOUND_FLAGS);
+ loader->SetDataPath(0);
+
+ if (sound_resource[0])
+ sound_resource[0]->SetMaxDistance(30.0e3f);
+
+ if (sound_resource[1])
+ sound_resource[1]->SetMaxDistance(30.0e3f);
+
+ if (sound_resource[2])
+ sound_resource[2]->SetMaxDistance(50.0e3f);
+
+ initialized = 1;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Drive::Close()
+{
+ for (int i = 0; i < 3; i++) {
+ delete sound_resource[i];
+ sound_resource[i] = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Drive::StartFrame()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Drive::AddPort(const Point& loc, float flare_scale)
+{
+ if (flare_scale == 0) flare_scale = scale;
+ DrivePort* port = new(__FILE__,__LINE__) DrivePort(loc, flare_scale);
+ ports.append(port);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Drive::CreatePort(const Point& loc, float flare_scale)
+{
+ Bitmap* flare_bmp = drive_flare_bitmap[subtype];
+ Bitmap* trail_bmp = drive_trail_bitmap[subtype];
+ Bitmap* glow_bmp = 0;
+
+ if (flare_scale <= 0)
+ flare_scale = scale;
+
+ if (augmenter <= 0)
+ glow_bmp = drive_glow_bitmap[subtype];
+
+ if (subtype != Drive::STEALTH && flare_scale > 0) {
+ DrivePort* port = new(__FILE__,__LINE__) DrivePort(loc, flare_scale);
+
+ if (flare_bmp) {
+ DriveSprite* flare_rep = new(__FILE__,__LINE__) DriveSprite(flare_bmp, glow_bmp);
+ flare_rep->Scale(flare_scale * 1.5);
+ flare_rep->SetShade(0);
+ port->flare = flare_rep;
+ }
+
+ if (trail_bmp && show_trail) {
+ Bolt* trail_rep = new(__FILE__,__LINE__) Bolt(flare_scale * 30, flare_scale * 8, trail_bmp, true);
+ port->trail = trail_rep;
+ }
+
+ ports.append(port);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Drive::Orient(const Physical* rep)
+{
+ System::Orient(rep);
+
+ const Matrix& orientation = rep->Cam().Orientation();
+ Point ship_loc = rep->Location();
+
+ for (int i = 0; i < ports.size(); i++) {
+ DrivePort* p = ports[i];
+
+ Point projector = (p->loc * orientation) + ship_loc;
+
+ if (p->flare) {
+ p->flare->MoveTo(projector);
+ p->flare->SetFront(rep->Cam().vpn() * -10 * p->scale);
+ }
+
+ if (p->trail) {
+ if (intensity > 0.5) {
+ double len = -60 * p->scale * intensity;
+
+ if (augmenter > 0 && augmenter_throttle > 0)
+ len += len * augmenter_throttle;
+
+ p->trail->Show();
+ p->trail->SetEndPoints(projector, projector + rep->Cam().vpn() * len);
+ }
+ else {
+ p->trail->Hide();
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static double drive_seconds=0;
+
+void
+Drive::SetThrottle(double t, bool aug)
+{
+ double spool = 1.2 * drive_seconds;
+ double throttle_request = t / 100;
+
+ if (throttle < throttle_request) {
+ if (throttle_request-throttle < spool) {
+ throttle = (float) throttle_request;
+ }
+ else {
+ throttle += (float) spool;
+ }
+ }
+
+ else if (throttle > throttle_request) {
+ if (throttle - throttle_request < spool) {
+ throttle = (float) throttle_request;
+ }
+ else {
+ throttle -= (float) spool;
+ }
+ }
+
+ if (throttle < 0.5)
+ aug = false;
+
+ if (aug && augmenter_throttle < 1) {
+ augmenter_throttle += (float) spool;
+
+ if (augmenter_throttle > 1)
+ augmenter_throttle = 1.0f;
+ }
+ else if (!aug && augmenter_throttle > 0) {
+ augmenter_throttle -= (float) spool;
+
+ if (augmenter_throttle < 0)
+ augmenter_throttle = 0.0f;
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+double
+Drive::GetRequest(double seconds) const
+{
+ if (!power_on) return 0;
+
+ double t_factor = max(throttle + 0.5 * augmenter_throttle, 0.3);
+
+ return t_factor * power_level * sink_rate * seconds;
+}
+
+bool
+Drive::IsAugmenterOn() const
+{
+ return augmenter > 0 &&
+ augmenter_throttle > 0.05 &&
+ IsPowerOn() &&
+ status > CRITICAL;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Drive::NumEngines() const
+{
+ return ports.size();
+}
+
+DriveSprite*
+Drive::GetFlare(int port) const
+{
+ if (port >= 0 && port < ports.size()) {
+ return ports[port]->flare;
+ }
+
+ return 0;
+}
+
+Bolt*
+Drive::GetTrail(int port) const
+{
+ if (port >= 0 && port < ports.size()) {
+ return ports[port]->trail;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+float
+Drive::Thrust(double seconds)
+{
+ drive_seconds = seconds;
+
+ float eff = (energy/capacity) * availability * 100.0f;
+ float output = throttle * thrust * eff;
+ bool aug_on = IsAugmenterOn();
+
+ if (aug_on) {
+ output += augmenter * augmenter_throttle * eff;
+
+ // augmenter burns extra fuel:
+ PowerSource* reac = ship->Reactors()[source_index];
+ reac->SetCapacity(reac->GetCapacity() - (0.1 * drive_seconds));
+ }
+
+ energy = 0.0f;
+
+ if (output < 0 || GetPowerLevel() < 0.01)
+ output = 0.0f;
+
+ int vol = -10000;
+ int vol_aug = -10000;
+ double fraction = output / thrust;
+
+ for (int i = 0; i < ports.size(); i++) {
+ DrivePort* p = ports[i];
+
+ if (p->flare) {
+ if (i == 0) {
+ if (fraction > 0)
+ intensity += (float) seconds;
+ else
+ intensity -= (float) seconds;
+
+ // capture volume based on actual output:
+ CLAMP(intensity, 0.0f, 1.0f);
+
+ if (intensity > 0.25) {
+ vol = (int) ((intensity - 1.0) * 10000.0);
+ CLAMP(vol, -10000, -1500);
+
+ if (aug_on && intensity > 0.5) {
+ vol_aug = (int) ((5 * augmenter_throttle - 1.0) * 10000.0);
+ CLAMP(vol_aug, -10000, -1000);
+ }
+ }
+ }
+
+ p->flare->SetShade(intensity);
+ }
+
+ if (p->trail) {
+ p->trail->SetShade(intensity);
+ }
+ }
+
+ CameraDirector* cam_dir = CameraDirector::GetInstance();
+
+ // no sound when paused!
+ if (!Game::Paused() && subtype != STEALTH && cam_dir && cam_dir->GetCamera()) {
+ if (ship && ship->GetRegion() == Sim::GetSim()->GetActiveRegion()) {
+ if (!sound) {
+ int sound_index = 0;
+ if (thrust > 100)
+ sound_index = 2;
+
+ if (sound_resource[sound_index])
+ sound = sound_resource[sound_index]->Duplicate();
+ }
+
+ if (aug_on && !burner_sound) {
+ if (sound_resource[1])
+ burner_sound = sound_resource[1]->Duplicate();
+ }
+
+ Point cam_loc = cam_dir->GetCamera()->Pos();
+ double dist = (ship->Location() - cam_loc).length();
+
+ if (sound && dist < sound->GetMaxDistance()) {
+ long max_vol = AudioConfig::EfxVolume();
+
+ if (vol > max_vol)
+ vol = max_vol;
+
+ if (sound) {
+ sound->SetLocation(ship->Location());
+ sound->SetVolume(vol);
+ sound->Play();
+ }
+
+ if (burner_sound) {
+ if (vol_aug > max_vol)
+ vol_aug = max_vol;
+
+ burner_sound->SetLocation(ship->Location());
+ burner_sound->SetVolume(vol_aug);
+ burner_sound->Play();
+ }
+ }
+ else {
+ if (sound && sound->IsPlaying())
+ sound->Stop();
+
+ if (burner_sound && burner_sound->IsPlaying())
+ burner_sound->Stop();
+ }
+ }
+ else {
+ if (sound && sound->IsPlaying())
+ sound->Stop();
+
+ if (burner_sound && burner_sound->IsPlaying())
+ burner_sound->Stop();
+ }
+ }
+
+ return output;
+}
diff --git a/Stars45/Drive.h b/Stars45/Drive.h
new file mode 100644
index 0000000..1bf3873
--- /dev/null
+++ b/Stars45/Drive.h
@@ -0,0 +1,93 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Drive.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Conventional Drive (system) class
+*/
+
+#ifndef Drive_h
+#define Drive_h
+
+#include "Types.h"
+#include "System.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class Bolt;
+class DriveSprite;
+class Light;
+class Sound;
+class Ship;
+
+// +--------------------------------------------------------------------+
+
+struct DrivePort {
+ static const char* TYPENAME() { return "DrivePort"; }
+
+ DrivePort(const Point& l, float s);
+ ~DrivePort();
+
+ Point loc;
+ float scale;
+
+ DriveSprite* flare;
+ Bolt* trail;
+};
+
+// +--------------------------------------------------------------------+
+
+class Drive : public System
+{
+public:
+ enum SUBTYPE { PLASMA, FUSION, GREEN, RED, BLUE, YELLOW, STEALTH };
+ enum Constants { MAX_ENGINES=16 };
+
+ Drive(SUBTYPE s, float max_thrust, float max_aug, bool show_trail=true);
+ Drive(const Drive& rhs);
+ virtual ~Drive();
+
+ static void Initialize();
+ static void Close();
+ static void StartFrame();
+
+ float Thrust(double seconds);
+ float MaxThrust() const { return thrust; }
+ float MaxAugmenter() const { return augmenter; }
+ int NumEngines() const;
+ DriveSprite* GetFlare(int port) const;
+ Bolt* GetTrail(int port) const;
+ bool IsAugmenterOn() const;
+
+ virtual void AddPort(const Point& loc, float flare_scale=0);
+ virtual void CreatePort(const Point& loc, float flare_scale);
+
+ virtual void Orient(const Physical* rep);
+
+ void SetThrottle(double t, bool aug=false);
+ virtual double GetRequest(double seconds) const;
+
+protected:
+ float thrust;
+ float augmenter;
+ float scale;
+ float throttle;
+ float augmenter_throttle;
+ float intensity;
+
+ List<DrivePort> ports;
+
+ Sound* sound;
+ Sound* burner_sound;
+ bool show_trail;
+};
+
+#endif Drive_h
+
diff --git a/Stars45/DriveSprite.cpp b/Stars45/DriveSprite.cpp
new file mode 100644
index 0000000..c38adc7
--- /dev/null
+++ b/Stars45/DriveSprite.cpp
@@ -0,0 +1,143 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: DriveSprite.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Sprite for rendering drive flares. Remains visible at extreme ranges.
+*/
+
+#include "MemDebug.h"
+#include "DriveSprite.h"
+
+#include "Bitmap.h"
+#include "Camera.h"
+#include "Scene.h"
+#include "Video.h"
+
+// +--------------------------------------------------------------------+
+
+DriveSprite::DriveSprite()
+ : Sprite(), glow(0), effective_radius(0), front(0,0,0), bias(0)
+{ luminous = true; }
+
+DriveSprite::DriveSprite(Bitmap* animation, Bitmap* g)
+ : Sprite(animation), glow(g), effective_radius(0), front(0,0,0), bias(0)
+{ luminous = true; }
+
+DriveSprite::DriveSprite(Bitmap* animation, int length, int repeat, int share)
+ : Sprite(animation, length, repeat, share), glow(0), effective_radius(0),
+ front(0,0,0), bias(0)
+{ luminous = true; }
+
+DriveSprite::~DriveSprite()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+DriveSprite::SetFront(const Vec3& f)
+{
+ front = f;
+ front.Normalize();
+}
+
+void
+DriveSprite::SetBias(DWORD b)
+{
+ bias = b;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DriveSprite::Render(Video* video, DWORD flags)
+{
+ if (!video || ((flags & RENDER_ADDITIVE) == 0))
+ return;
+
+ if (shade > 0 && !hidden && (life > 0 || loop)) {
+ const Camera* cam = video->GetCamera();
+ bool z_disable = false;
+
+ if (bias)
+ video->SetRenderState(Video::Z_BIAS, bias);
+
+ if (front.length()) {
+ Point test = loc;
+
+ if (scene && cam) {
+ Vec3 dir = front;
+
+ double intensity = cam->vpn() * dir * -1;
+ double distance = Point(cam->Pos() - test).length();
+
+ if (intensity > 0.05) {
+ if (!scene->IsLightObscured(cam->Pos(), test, 8)) {
+ video->SetRenderState(Video::Z_ENABLE, false);
+ z_disable = true;
+
+ if (glow) {
+ intensity = pow(intensity, 3);
+
+ if (distance > 5e3)
+ intensity *= (1 - (distance-5e3)/45e3);
+
+ if (intensity > 0) {
+ Bitmap* tmp_frame = frames;
+ double tmp_shade = shade;
+ int tmp_w = w;
+ int tmp_h = h;
+
+ if (glow->Width() != frames->Width()) {
+ double wscale = glow->Width() / frames->Width();
+ double hscale = glow->Height() / frames->Height();
+
+ w = (int) (w * wscale);
+ h = (int) (h * hscale);
+ }
+
+ shade = intensity;
+ frames = glow;
+
+ Sprite::Render(video, flags);
+
+ frames = tmp_frame;
+ shade = tmp_shade;
+ w = tmp_w;
+ h = tmp_h;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (effective_radius-radius > 0.1) {
+ double scale_up = effective_radius / radius;
+ int tmp_w = w;
+ int tmp_h = h;
+
+ w = (int) (w * scale_up);
+ h = (int) (h * scale_up);
+
+ Sprite::Render(video, flags);
+
+ w = tmp_w;
+ h = tmp_h;
+ }
+
+ else {
+ Sprite::Render(video, flags);
+ }
+
+ if (bias) video->SetRenderState(Video::Z_BIAS, 0);
+ if (z_disable) video->SetRenderState(Video::Z_ENABLE, true);
+ }
+}
+
diff --git a/Stars45/DriveSprite.h b/Stars45/DriveSprite.h
new file mode 100644
index 0000000..0556c59
--- /dev/null
+++ b/Stars45/DriveSprite.h
@@ -0,0 +1,47 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: DriveSprite.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Specialized Drive Sprite Object
+*/
+
+#ifndef DriveSprite_h
+#define DriveSprite_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Sprite.h"
+
+// +--------------------------------------------------------------------+
+
+class DriveSprite : public Sprite
+{
+public:
+ DriveSprite();
+ DriveSprite(Bitmap* animation, Bitmap* glow);
+ DriveSprite(Bitmap* animation, int length=1, int repeat=1, int share=1);
+ virtual ~DriveSprite();
+
+ // operations
+ virtual void Render(Video* video, DWORD flags);
+ virtual void SetFront(const Vec3& f);
+ virtual void SetBias(DWORD b);
+
+protected:
+ double effective_radius;
+ Vec3 front;
+ Bitmap* glow;
+ DWORD bias;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif DriveSprite_h
+
diff --git a/Stars45/Drone.cpp b/Stars45/Drone.cpp
new file mode 100644
index 0000000..49b06fc
--- /dev/null
+++ b/Stars45/Drone.cpp
@@ -0,0 +1,196 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Drone.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Laser and Missile class
+*/
+
+#include "MemDebug.h"
+#include "Drone.h"
+#include "Weapon.h"
+#include "Ship.h"
+#include "Sim.h"
+#include "Explosion.h"
+
+#include "Game.h"
+#include "Bolt.h"
+#include "Sprite.h"
+#include "Solid.h"
+#include "Light.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Sound.h"
+
+// +--------------------------------------------------------------------+
+
+Drone::Drone(const Point& pos, const Camera& shot_cam, WeaponDesign* dsn, const Ship* ship)
+ : Shot(pos, shot_cam, dsn, ship),
+ decoy_type(0), iff_code(0)
+{
+ obj_type = SimObject::SIM_DRONE;
+
+ if (dsn) {
+ decoy_type = dsn->decoy_type;
+ probe = dsn->probe;
+ integrity = dsn->integrity;
+ sprintf(name, "Drone %04d", Identity());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Drone::~Drone()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Drone::SeekTarget(SimObject* target, System* sub)
+{
+ if (!probe)
+ Shot::SeekTarget(target, sub);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Drone::ExecFrame(double seconds)
+{
+ Shot::ExecFrame(seconds);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Drone::Disarm()
+{
+ Shot::Disarm();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Drone::Destroy()
+{
+ Shot::Destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Drone::PCS() const
+{
+ if (decoy_type == 0 && !probe)
+ return 10e3;
+
+ return 0;
+}
+
+double
+Drone::ACS() const
+{
+ if (decoy_type == 0 && !probe)
+ return 1e3;
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+Drone::ClassName() const
+{
+ return Ship::ClassName(decoy_type);
+}
+
+int
+Drone::Class() const
+{
+ return decoy_type;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Drone::HitBy(Shot* shot, Point& impact)
+{
+ if (life == 0 || !shot->IsArmed()) return 0;
+
+ const int HIT_NOTHING = 0;
+ const int HIT_HULL = 1;
+
+ Point hull_impact;
+ int hit_type = HIT_NOTHING;
+ Point shot_loc = shot->Location();
+ Point shot_org = shot->Origin();
+ Point delta = shot_loc - Location();
+ double dlen = delta.length();
+ double dscale = 1;
+ float scale = design->explosion_scale;
+ Sim* sim = Sim::GetSim();
+
+ if (scale <= 0)
+ scale = design->scale;
+
+ // MISSILE PROCESSING ------------------------------------------------
+
+ if (shot->IsMissile()) {
+ if (dlen < 10 * Radius()) {
+ hull_impact = impact = shot_loc;
+ sim->CreateExplosion(impact, Velocity(), Explosion::HULL_FLASH, 0.3f * scale, scale, region);
+ sim->CreateExplosion(impact, Point(), Explosion::SHOT_BLAST, 2.0f, scale, region);
+ hit_type = HIT_HULL;
+ }
+ }
+
+ // ENERGY WEP PROCESSING ---------------------------------------------
+
+ else {
+ if (shot->IsBeam()) {
+ // check right angle spherical distance:
+ Point d0 = Location() - shot_org;
+ Point w = shot_loc - shot_org; w.Normalize();
+ Point test = shot_org + w * (d0 * w);
+ Point d1 = test - Location();
+ double dlen = d1.length(); // distance of point from line
+
+ if (dlen < 2*Radius()) {
+ hull_impact = impact = test;
+ shot->SetBeamPoints(shot_org, impact);
+ sim->CreateExplosion(impact, Velocity(), Explosion::BEAM_FLASH, 0.30f * scale, scale, region);
+ hit_type = HIT_HULL;
+ }
+ }
+ else if (dlen < 2*Radius()) {
+ hull_impact = impact = shot_loc;
+ sim->CreateExplosion(impact, Velocity(), Explosion::HULL_FLASH, 0.30f * scale, scale, region);
+ hit_type = HIT_HULL;
+ }
+ }
+
+ // DAMAGE RESOLUTION -------------------------------------------------
+
+ if (hit_type != HIT_NOTHING) {
+ double effective_damage = shot->Damage() * dscale;
+
+ if (shot->IsBeam()) {
+ effective_damage *= Game::FrameTime();
+ }
+ else {
+ ApplyTorque(shot->Velocity() * (float) effective_damage * 1e-6f);
+ }
+
+ if (effective_damage > 0)
+ Physical::InflictDamage(effective_damage);
+ }
+
+ return hit_type;
+}
diff --git a/Stars45/Drone.h b/Stars45/Drone.h
new file mode 100644
index 0000000..cac3a09
--- /dev/null
+++ b/Stars45/Drone.h
@@ -0,0 +1,69 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Drone.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Decoy / Weapons Drone class
+*/
+
+#ifndef Drone_h
+#define Drone_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Shot.h"
+
+// +--------------------------------------------------------------------+
+
+class Camera;
+class Ship;
+class Trail;
+class System;
+class WeaponDesign;
+class Sprite3D;
+class Sound;
+
+// +--------------------------------------------------------------------+
+
+class Drone : public Shot
+{
+public:
+ static const char* TYPENAME() { return "Drone"; }
+
+ Drone(const Point& pos, const Camera& cam, WeaponDesign* design, const Ship* ship=0);
+ virtual ~Drone();
+
+ virtual void SeekTarget(SimObject* target, System* sub=0);
+ virtual void ExecFrame(double factor);
+
+ virtual bool IsDrone() const { return true; }
+ virtual bool IsDecoy() const { return decoy_type != 0; }
+ virtual bool IsProbe() const { return probe?true:false; }
+
+ virtual void Disarm();
+ virtual void Destroy();
+
+ // SENSORS AND VISIBILITY:
+ virtual double PCS() const;
+ virtual double ACS() const;
+ virtual const char* ClassName() const;
+ virtual int Class() const;
+
+ // DAMAGE RESOLUTION:
+ void SetLife(int seconds) { life = seconds; }
+ virtual int HitBy(Shot* shot, Point& impact);
+
+protected:
+ int iff_code;
+ int decoy_type;
+ int probe;
+};
+
+#endif Drone_h
+
diff --git a/Stars45/DropShipAI.cpp b/Stars45/DropShipAI.cpp
new file mode 100644
index 0000000..41672cc
--- /dev/null
+++ b/Stars45/DropShipAI.cpp
@@ -0,0 +1,122 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: DropShipAI.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Drop Ship (orbit/surface and surface/orbit) AI class
+*/
+
+#include "MemDebug.h"
+#include "DropShipAI.h"
+#include "TacticalAI.h"
+#include "Ship.h"
+#include "ShipCtrl.h"
+#include "Drive.h"
+#include "Sim.h"
+#include "StarSystem.h"
+#include "KeyMap.h"
+
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+DropShipAI::DropShipAI(Ship* s)
+ : ShipAI(s)
+{
+ seek_gain = 20;
+ seek_damp = 0.5;
+
+ delete tactical;
+ tactical = 0;
+}
+
+DropShipAI::~DropShipAI()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DropShipAI::FindObjective()
+{
+ distance = 0;
+
+ if (!ship) return;
+
+ Sim* sim = Sim::GetSim();
+ SimRegion* self_rgn = ship->GetRegion();
+
+ // if making orbit, go up:
+ if (self_rgn->Type() == Sim::AIR_SPACE) {
+ obj_w = self->Location() + Point(0, 1e3, 0);
+ }
+
+ // if breaking orbit, head for terrain region:
+ else {
+ SimRegion* dst_rgn = sim->FindNearestTerrainRegion(ship);
+ Point dst = dst_rgn->GetOrbitalRegion()->Location() -
+ self_rgn->GetOrbitalRegion()->Location() +
+ Point(0, 0, -1e6);
+
+ obj_w = dst.OtherHand();
+ }
+
+ // distance from self to navpt:
+ distance = Point(obj_w - self->Location()).length();
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+ objective.Normalize();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DropShipAI::Navigator()
+{
+ accumulator.Clear();
+ magnitude = 0;
+
+ if (other)
+ ship->SetFLCSMode(Ship::FLCS_AUTO);
+ else
+ ship->SetFLCSMode(Ship::FLCS_MANUAL);
+
+ Accumulate(AvoidCollision());
+ Accumulate(Seek(objective));
+
+ // are we being asked to flee?
+ if (fabs(accumulator.yaw) == 1.0 && accumulator.pitch == 0.0) {
+ accumulator.pitch = -0.7f;
+ accumulator.yaw *= 0.25f;
+ }
+
+ self->ApplyRoll((float) (accumulator.yaw * -0.4));
+ self->ApplyYaw((float) (accumulator.yaw * 0.2));
+
+ if (fabs(accumulator.yaw) > 0.5 && fabs(accumulator.pitch) < 0.1)
+ accumulator.pitch -= 0.1f;
+
+ if (accumulator.pitch != 0)
+ self->ApplyPitch((float) accumulator.pitch);
+
+ // if not turning, roll to orient with world coords:
+ if (fabs(accumulator.yaw) < 0.1) {
+ Point vrt = ((Camera*) &(self->Cam()))->vrt();
+ double deflection = vrt.y;
+ if (deflection != 0) {
+ double theta = asin(deflection/vrt.length());
+ self->ApplyRoll(-theta);
+ }
+ }
+
+ ship->SetThrottle(100);
+ ship->ExecFLCSFrame();
+}
+
diff --git a/Stars45/DropShipAI.h b/Stars45/DropShipAI.h
new file mode 100644
index 0000000..fe34571
--- /dev/null
+++ b/Stars45/DropShipAI.h
@@ -0,0 +1,47 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: DropShipAI.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Drop Ship (orbit/surface and surface/orbit) AI class
+*/
+
+#ifndef DropShipAI_h
+#define DropShipAI_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "System.h"
+#include "ShipAI.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+
+// +--------------------------------------------------------------------+
+
+class DropShipAI : public ShipAI
+{
+public:
+ DropShipAI(Ship* s);
+ virtual ~DropShipAI();
+
+ enum { DIR_TYPE = 2001 };
+ virtual int Type() const { return DIR_TYPE; }
+
+protected:
+ // accumulate behaviors:
+ virtual void Navigator();
+ virtual void FindObjective();
+};
+
+// +--------------------------------------------------------------------+
+
+#endif DropShipAI_h
+
diff --git a/Stars45/Element.cpp b/Stars45/Element.cpp
new file mode 100644
index 0000000..a375cc2
--- /dev/null
+++ b/Stars45/Element.cpp
@@ -0,0 +1,667 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars
+ FILE: Element.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Package Element (e.g. Flight) class implementation
+*/
+
+#include "MemDebug.h"
+#include "Element.h"
+#include "Instruction.h"
+#include "RadioMessage.h"
+#include "RadioHandler.h"
+#include "Sim.h"
+#include "Ship.h"
+#include "NetUtil.h"
+
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+static int id_key = 1000;
+
+Element::Element(const char* call_sign, int a_iff, int a_type)
+ : id(id_key++), name(call_sign), type(a_type), iff(a_iff),
+ player(0), command_ai(1), commander(0), assignment(0), carrier(0),
+ combat_group(0), combat_unit(0), launch_time(0), hold_time(0),
+ zone_lock(0), respawns(0), count(0), rogue(false), playable(true), intel(0)
+{
+ if (!call_sign) {
+ char buf[32];
+ sprintf(buf, "Pkg %d", id);
+ name = buf;
+ }
+
+ SetLoadout(0);
+}
+
+Element::~Element()
+{
+ flight_plan.destroy();
+ objectives.destroy();
+ instructions.destroy();
+
+ for (int i = 0; i < ships.size(); i++)
+ ships[i]->SetElement(0);
+
+ respawns = 0;
+}
+
+// +----------------------------------------------------------------------+
+
+int
+Element::AddShip(Ship* ship, int index)
+{
+ if (ship && !ships.contains(ship)) {
+ Observe(ship);
+
+ if (index < 0) {
+ ships.append(ship);
+ index = ships.size();
+ }
+ else {
+ ships.insert(ship, index-1);
+ }
+
+ ship->SetElement(this);
+
+ if (respawns < ship->RespawnCount())
+ respawns = ship->RespawnCount();
+ }
+
+ return index;
+}
+
+void
+Element::DelShip(Ship* ship)
+{
+ if (ship && ships.contains(ship)) {
+ ships.remove(ship);
+ ship->SetElement(0);
+
+ if (ships.isEmpty())
+ respawns = ship->RespawnCount();
+ }
+}
+
+Ship*
+Element::GetShip(int index)
+{
+ if (index >= 1 && index <= ships.size())
+ return ships[index-1];
+
+ return 0;
+}
+
+int
+Element::GetShipClass()
+{
+ if (ships.size())
+ return ships[0]->Class();
+
+ return 0;
+}
+
+int
+Element::FindIndex(const Ship* s)
+{
+ return ships.index(s) + 1;
+}
+
+bool
+Element::Contains(const Ship* s)
+{
+ return ships.contains(s);
+}
+
+bool
+Element::IsActive() const
+{
+ bool active = false;
+
+ for (int i = 0; i < ships.size() && !active; i++) {
+ Ship* s = ships[i];
+ if (s->Life() && s->MissionClock())
+ active = true;
+ }
+
+ return active;
+}
+
+bool
+Element::IsFinished() const
+{
+ bool finished = false;
+
+ if (launch_time > 0 && respawns < 1) {
+ finished = true;
+
+ if (ships.size() > 0) {
+ for (int i = 0; i < ships.size() && finished; i++) {
+ Ship* s = ships[i];
+ if (s->RespawnCount() > 0 ||
+ s->MissionClock() == 0 ||
+ s->Life() && !s->GetInbound())
+ finished = false;
+ }
+ }
+ }
+
+ return finished;
+}
+
+bool
+Element::IsNetObserver() const
+{
+ bool observer = !IsSquadron();
+
+ for (int i = 0; i < ships.size() && observer; i++) {
+ Ship* s = ships[i];
+
+ if (!s->IsNetObserver())
+ observer = false;
+ }
+
+ return observer;
+}
+
+bool
+Element::IsSquadron() const
+{
+ return count > 0;
+}
+
+bool
+Element::IsStatic() const
+{
+ if (IsSquadron() || IsFinished())
+ return false;
+
+ const Ship* s = ships.at(0);
+ if (s && s->IsStatic())
+ return true;
+
+ return false;
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+Element::IsHostileTo(const Ship* s) const
+{
+ if (iff <= 0 || iff >= 100 || !s || launch_time == 0 || IsFinished())
+ return false;
+
+ if (IsSquadron())
+ return false;
+
+ if (s->IsRogue())
+ return true;
+
+ int s_iff = s->GetIFF();
+
+ if (s_iff <= 0 || s_iff >= 100 || s_iff == iff)
+ return false;
+
+ if (ships.size() > 0 && ships[0]->GetRegion() != s->GetRegion())
+ return false;
+
+ return true;
+}
+
+bool
+Element::IsHostileTo(int iff_code) const
+{
+ if (iff <= 0 || iff >= 100 || launch_time == 0 || IsFinished())
+ return false;
+
+ if (IsSquadron())
+ return false;
+
+ if (iff_code <= 0 || iff_code >= 100 || iff_code == iff)
+ return false;
+
+ return true;
+}
+
+bool
+Element::IsObjectiveTargetOf(const Ship* s) const
+{
+ if (!s || launch_time == 0 || IsFinished())
+ return false;
+
+ const char* e_name = Name().data();
+ int e_len = Name().length();
+
+ Instruction* orders = s->GetRadioOrders();
+ if (orders && orders->Action() > Instruction::SWEEP) {
+ const char* o_name = orders->TargetName();
+ int o_len = 0;
+
+ if (o_name && *o_name)
+ o_len = strlen(o_name);
+
+ if (e_len < o_len)
+ o_len = e_len;
+
+ if (!strncmp(e_name, o_name, o_len))
+ return true;
+ }
+
+ Element* elem = s->GetElement();
+ if (elem) {
+ for (int i = 0; i < elem->NumObjectives(); i++) {
+ Instruction* obj = elem->GetObjective(i);
+
+ if (obj) {
+ const char* o_name = obj->TargetName();
+ int o_len = 0;
+
+ if (o_name && *o_name)
+ o_len = strlen(o_name);
+
+ if (e_len < o_len)
+ o_len = e_len;
+
+ if (!strncmp(e_name, o_name, o_len))
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Element::SetLaunchTime(DWORD t)
+{
+ if (launch_time == 0 || t == 0)
+ launch_time = t;
+}
+
+double
+Element::GetHoldTime()
+{
+ return hold_time;
+}
+
+void
+Element::SetHoldTime(double t)
+{
+ if (t >= 0)
+ hold_time = t;
+}
+
+bool
+Element::GetZoneLock()
+{
+ return zone_lock;
+}
+
+void
+Element::SetZoneLock(bool z)
+{
+ zone_lock = z;
+}
+
+void
+Element::SetLoadout(int* l)
+{
+ if (l) {
+ CopyMemory(load, l, sizeof(load));
+ }
+ else {
+ for (int i = 0; i < 16; i++)
+ load[i] = -1;
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+Element::Update(SimObject* obj)
+{
+ // false alarm, keep watching:
+ if (obj->Life() != 0) {
+ ::Print("Element (%s) false update on (%s) life = %f\n", Name().data(), obj->Name(), obj->Life());
+ return false;
+ }
+
+ Ship* s = (Ship*) obj;
+ ships.remove(s);
+
+ if (ships.isEmpty())
+ respawns = s->RespawnCount();
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+Element::GetObserverName() const
+{
+ return (const char*) (Text("Element ") + Name());
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Element::AddNavPoint(Instruction* pt, Instruction* afterPoint, bool send)
+{
+ if (pt && !flight_plan.contains(pt)) {
+ int index = -1;
+
+ if (afterPoint) {
+ index = flight_plan.index(afterPoint);
+
+ if (index > -1)
+ flight_plan.insert(pt, index+1);
+ else
+ flight_plan.append(pt);
+ }
+
+ else {
+ flight_plan.append(pt);
+ }
+
+ if (send) {
+ NetUtil::SendNavData(true, this, index, pt);
+ }
+ }
+}
+
+void
+Element::DelNavPoint(Instruction* pt, bool send)
+{
+ // XXX MEMORY LEAK
+ // This is a small memory leak, but I'm not sure if it is
+ // safe to delete the navpoint when removing it from the
+ // flight plan. Other ships in the element might have
+ // pointers to the object...?
+
+ if (pt) {
+ int index = flight_plan.index(pt);
+ flight_plan.remove(pt);
+
+ if (send) {
+ NetUtil::SendNavDelete(this, index);
+ }
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Element::ClearFlightPlan(bool send)
+{
+ hold_time = 0;
+ flight_plan.destroy();
+ objectives.destroy();
+ instructions.destroy();
+
+ if (send) {
+ NetUtil::SendNavDelete(this, -1);
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+Instruction*
+Element::GetNextNavPoint()
+{
+ if (hold_time <= 0 && flight_plan.size() > 0) {
+ ListIter<Instruction> iter = flight_plan;
+ while (++iter) {
+ Instruction* navpt = iter.value();
+
+ if (navpt->Status() == Instruction::COMPLETE && navpt->HoldTime() > 0)
+ return navpt;
+
+ if (navpt->Status() <= Instruction::ACTIVE)
+ return navpt;
+ }
+ }
+
+ return 0;
+}
+
+// +----------------------------------------------------------------------+
+
+int
+Element::GetNavIndex(const Instruction* n)
+{
+ int index = 0;
+
+ if (flight_plan.size() > 0) {
+ ListIter<Instruction> navpt = flight_plan;
+ while (++navpt) {
+ index++;
+ if (navpt.value() == n)
+ return index;
+ }
+ }
+
+ return 0;
+}
+
+// +----------------------------------------------------------------------+
+
+List<Instruction>&
+Element::GetFlightPlan()
+{
+ return flight_plan;
+}
+
+int
+Element::FlightPlanLength()
+{
+ return flight_plan.size();
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Element::ClearObjectives()
+{
+ objectives.destroy();
+}
+
+void
+Element::AddObjective(Instruction* obj)
+{
+ objectives.append(obj);
+}
+
+Instruction*
+Element::GetObjective(int index)
+{
+ if (objectives.isEmpty())
+ return 0;
+
+ if (index < 0)
+ index = 0;
+
+ else if (index >= objectives.size())
+ index = index % objectives.size();
+
+ return objectives.at(index);
+}
+
+Instruction*
+Element::GetTargetObjective()
+{
+ for (int i = 0; i < objectives.size(); i++) {
+ Instruction* obj = objectives[i];
+
+ if (obj->Status() <= Instruction::ACTIVE) {
+ switch (obj->Action()) {
+ case Instruction::INTERCEPT:
+ case Instruction::STRIKE:
+ case Instruction::ASSAULT:
+ case Instruction::SWEEP:
+ case Instruction::PATROL:
+ case Instruction::RECON:
+ case Instruction::ESCORT:
+ case Instruction::DEFEND:
+ return obj;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Element::ClearInstructions()
+{
+ instructions.clear();
+}
+
+void
+Element::AddInstruction(const char* instr)
+{
+ instructions.append(new(__FILE__,__LINE__) Text(instr));
+}
+
+Text
+Element::GetInstruction(int index)
+{
+ if (instructions.isEmpty())
+ return Text();
+
+ if (index < 0)
+ index = 0;
+
+ if (index >= instructions.size())
+ index = index % instructions.size();
+
+ return *instructions.at(index);
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Element::ResumeAssignment()
+{
+ SetAssignment(0);
+
+ if (objectives.isEmpty())
+ return;
+
+ Instruction* objective = 0;
+
+ for (int i = 0; i < objectives.size() && !objective; i++) {
+ Instruction* instr = objectives[i];
+
+ if (instr->Status() <= Instruction::ACTIVE) {
+ switch (instr->Action()) {
+ case Instruction::INTERCEPT:
+ case Instruction::STRIKE:
+ case Instruction::ASSAULT:
+ objective = instr;
+ break;
+ }
+ }
+ }
+
+ if (objective) {
+ Sim* sim = Sim::GetSim();
+
+ ListIter<Element> iter = sim->GetElements();
+ while (++iter) {
+ Element* elem = iter.value();
+ SimObject* tgt = objective->GetTarget();
+
+ if (tgt && tgt->Type() == SimObject::SIM_SHIP && elem->Contains((const Ship*) tgt)) {
+ SetAssignment(elem);
+ return;
+ }
+ }
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Element::HandleRadioMessage(RadioMessage* msg)
+{
+ if (!msg) return;
+
+ static RadioHandler rh;
+
+ // if this is a message from within the element,
+ // then all ships should report in. Otherwise,
+ // just the leader will acknowledge the message.
+ int full_report = ships.contains(msg->Sender());
+ int reported = false;
+
+ ListIter<Ship> s = ships;
+ while (++s) {
+ if (rh.ProcessMessage(msg, s.value())) {
+ if (full_report) {
+ if (s.value() != msg->Sender())
+ rh.AcknowledgeMessage(msg, s.value());
+ }
+
+ else if (!reported) {
+ rh.AcknowledgeMessage(msg, s.value());
+ reported = true;
+ }
+ }
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+Element::CanCommand(Element* e)
+{
+ while (e) {
+ if (e->commander == this)
+ return true;
+ e = e->commander;
+ }
+
+ return false;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Element::ExecFrame(double seconds)
+{
+ if (hold_time > 0) {
+ hold_time -= seconds;
+ return;
+ }
+
+ ListIter<Instruction> iter = flight_plan;
+ while (++iter) {
+ Instruction* instr = iter.value();
+
+ if (instr->Status() == Instruction::COMPLETE && instr->HoldTime() > 0)
+ instr->SetHoldTime(instr->HoldTime() - seconds);
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Element::SetIFF(int iff)
+{
+ for (int i = 0; i < ships.size(); i++)
+ ships[i]->SetIFF(iff);
+}
diff --git a/Stars45/Element.h b/Stars45/Element.h
new file mode 100644
index 0000000..967d712
--- /dev/null
+++ b/Stars45/Element.h
@@ -0,0 +1,172 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Element.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Package Element (e.g. Flight) class
+*/
+
+#ifndef Element_h
+#define Element_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "SimObject.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+class Instruction;
+class RadioMessage;
+
+class CombatGroup;
+class CombatUnit;
+
+// +--------------------------------------------------------------------+
+
+class Element : public SimObserver
+{
+public:
+ // CONSTRUCTORS:
+ Element(const char* call_sign, int iff, int type=0 /*PATROL*/);
+ virtual ~Element();
+
+ int operator == (const Element& e) const { return id == e.id; }
+
+ // GENERAL ACCESSORS:
+ int Identity() const { return id; }
+ int Type() const { return type; }
+ const Text& Name() const { return name; }
+ void SetName(const char* s) { name = s; }
+ virtual int GetIFF() const { return iff; }
+ int Player() const { return player; }
+ void SetPlayer(int p) { player = p; }
+ DWORD GetLaunchTime() const { return launch_time; }
+ void SetLaunchTime(DWORD t);
+ int IntelLevel() const { return intel; }
+ void SetIntelLevel(int i) { intel = i; }
+
+ // ELEMENT COMPONENTS:
+ int NumShips() const { return ships.size(); }
+ int AddShip(Ship*, int index=-1);
+ void DelShip(Ship*);
+ Ship* GetShip(int index);
+ int GetShipClass();
+ int FindIndex(const Ship* s);
+ bool Contains(const Ship* s);
+ bool IsActive() const;
+ bool IsFinished() const;
+ bool IsNetObserver() const;
+ bool IsSquadron() const;
+ bool IsStatic() const;
+ bool IsHostileTo(const Ship* s) const;
+ bool IsHostileTo(int iff_code) const;
+ bool IsObjectiveTargetOf(const Ship* s) const;
+ bool IsRogue() const { return rogue; }
+ bool IsPlayable() const { return playable; }
+ int* Loadout() { return load; }
+
+ void SetRogue(bool r) { rogue = r; }
+ void SetPlayable(bool p) { playable = p; }
+ void SetLoadout(int* l);
+ virtual void SetIFF(int iff);
+
+ virtual void ExecFrame(double seconds);
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+ // OBJECTIVES:
+ void ClearObjectives();
+ void AddObjective(Instruction* obj);
+ Instruction* GetObjective(int index);
+ Instruction* GetTargetObjective();
+ int NumObjectives() const { return objectives.size(); }
+
+ void ClearInstructions();
+ void AddInstruction(const char* instr);
+ Text GetInstruction(int index);
+ int NumInstructions() const { return instructions.size(); }
+
+ // ORDERS AND NAVIGATION:
+ double GetHoldTime();
+ void SetHoldTime(double t);
+ bool GetZoneLock();
+ void SetZoneLock(bool z);
+ void AddNavPoint(Instruction* pt, Instruction* afterPoint=0, bool send=true);
+ void DelNavPoint(Instruction* pt, bool send=true);
+ void ClearFlightPlan(bool send=true);
+ Instruction* GetNextNavPoint();
+ int GetNavIndex(const Instruction* n);
+ List<Instruction>& GetFlightPlan();
+ int FlightPlanLength();
+ virtual void HandleRadioMessage(RadioMessage* msg);
+
+ // CHAIN OF COMMAND:
+ Element* GetCommander() const { return commander; }
+ void SetCommander(Element* e) { commander = e; }
+ Element* GetAssignment() const { return assignment; }
+ void SetAssignment(Element* e) { assignment = e; }
+ void ResumeAssignment();
+ bool CanCommand(Element* e);
+ Ship* GetCarrier() const { return carrier; }
+ void SetCarrier(Ship* c) { carrier = c; }
+ int GetCommandAILevel() const { return command_ai; }
+ void SetCommandAILevel(int n) { command_ai = n; }
+ const Text& GetSquadron() const { return squadron; }
+ void SetSquadron(const char* s) { squadron = s; }
+
+ // DYNAMIC CAMPAIGN:
+ CombatGroup* GetCombatGroup() { return combat_group; }
+ void SetCombatGroup(CombatGroup* g) { combat_group = g; }
+ CombatUnit* GetCombatUnit() { return combat_unit; }
+ void SetCombatUnit(CombatUnit* u) { combat_unit = u; }
+
+ // SQUADRON STUFF:
+ int GetCount() const { return count; }
+ void SetCount(int n) { count = n; }
+
+protected:
+ int id;
+ int iff;
+ int type;
+ int player;
+ int command_ai;
+ int respawns;
+ int intel;
+ Text name;
+
+ // squadron elements only:
+ int count;
+
+ List<Ship> ships;
+ List<Text> ship_names;
+ List<Text> instructions;
+ List<Instruction> objectives;
+ List<Instruction> flight_plan;
+
+ Element* commander;
+ Element* assignment;
+ Ship* carrier;
+ Text squadron;
+
+ CombatGroup* combat_group;
+ CombatUnit* combat_unit;
+ DWORD launch_time;
+ double hold_time;
+
+ bool rogue;
+ bool playable;
+ bool zone_lock;
+ int load[16];
+};
+
+#endif Element_h
+
diff --git a/Stars45/EngDlg.cpp b/Stars45/EngDlg.cpp
new file mode 100644
index 0000000..9af1fce
--- /dev/null
+++ b/Stars45/EngDlg.cpp
@@ -0,0 +1,1043 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: EngDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Engineering (Power/Maint) Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "EngDlg.h"
+#include "GameScreen.h"
+#include "Ship.h"
+#include "Power.h"
+#include "Component.h"
+#include "Sim.h"
+
+#include "DataLoader.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Mouse.h"
+#include "Keyboard.h"
+#include "Game.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(EngDlg, OnSource);
+DEF_MAP_CLIENT(EngDlg, OnClient);
+DEF_MAP_CLIENT(EngDlg, OnRouteStart);
+DEF_MAP_CLIENT(EngDlg, OnRouteComplete);
+DEF_MAP_CLIENT(EngDlg, OnPowerOff);
+DEF_MAP_CLIENT(EngDlg, OnPowerOn);
+DEF_MAP_CLIENT(EngDlg, OnOverride);
+DEF_MAP_CLIENT(EngDlg, OnPowerLevel);
+DEF_MAP_CLIENT(EngDlg, OnComponent);
+DEF_MAP_CLIENT(EngDlg, OnAutoRepair);
+DEF_MAP_CLIENT(EngDlg, OnRepair);
+DEF_MAP_CLIENT(EngDlg, OnReplace);
+DEF_MAP_CLIENT(EngDlg, OnQueue);
+DEF_MAP_CLIENT(EngDlg, OnPriorityIncrease);
+DEF_MAP_CLIENT(EngDlg, OnPriorityDecrease);
+DEF_MAP_CLIENT(EngDlg, OnClose);
+
+// +--------------------------------------------------------------------+
+
+EngDlg::EngDlg(Screen* s, FormDef& def, GameScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ ship(0), route_source(0), selected_source(0),
+ selected_repair(0), selected_component(0)
+{
+ Init(def);
+}
+
+EngDlg::~EngDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EngDlg::RegisterControls()
+{
+ for (int i = 0; i < 4; i++) {
+ sources[i] = (Button*) FindControl(201 + i);
+ REGISTER_CLIENT(EID_CLICK, sources[i], EngDlg, OnSource);
+
+ source_levels[i] = (Slider*) FindControl(211 + i);
+
+ clients[i] = (ListBox*) FindControl(301 + i);
+
+ if (clients[i]) {
+ clients[i]->SetSelectedStyle(ListBox::LIST_ITEM_STYLE_FILLED_BOX);
+ clients[i]->SetSortColumn(0);
+
+ REGISTER_CLIENT(EID_SELECT, clients[i], EngDlg, OnClient);
+ REGISTER_CLIENT(EID_DRAG_START, clients[i], EngDlg, OnRouteStart);
+ REGISTER_CLIENT(EID_DRAG_DROP, clients[i], EngDlg, OnRouteComplete);
+ }
+ }
+
+ close_btn = (Button*) FindControl(1);
+ selected_name = (ActiveWindow*) FindControl(401);
+ power_off = (Button*) FindControl(402);
+ power_on = (Button*) FindControl(403);
+ override = (Button*) FindControl(410);
+ power_level = (Slider*) FindControl(404);
+ capacity = (Slider*) FindControl(405);
+
+ if (close_btn)
+ REGISTER_CLIENT(EID_CLICK, close_btn, EngDlg, OnClose);
+
+ if (power_off)
+ REGISTER_CLIENT(EID_CLICK, power_off, EngDlg, OnPowerOff);
+
+ if (power_on)
+ REGISTER_CLIENT(EID_CLICK, power_on, EngDlg, OnPowerOn);
+
+ if (override)
+ REGISTER_CLIENT(EID_CLICK, override, EngDlg, OnOverride);
+
+ if (power_level)
+ REGISTER_CLIENT(EID_CLICK, power_level, EngDlg, OnPowerLevel);
+
+ components = (ListBox*) FindControl(501);
+
+ if (components) {
+ components->SetSelectedStyle(ListBox::LIST_ITEM_STYLE_FILLED_BOX);
+ components->SetSortColumn(0);
+ REGISTER_CLIENT(EID_SELECT, components, EngDlg, OnComponent);
+ }
+
+ auto_repair = (Button*) FindControl(700);
+ repair = (Button*) FindControl(502);
+ replace = (Button*) FindControl(503);
+ repair_time = FindControl(512);
+ replace_time = FindControl(513);
+ priority_increase = (Button*) FindControl(602);
+ priority_decrease = (Button*) FindControl(603);
+
+ if (auto_repair)
+ REGISTER_CLIENT(EID_CLICK, auto_repair, EngDlg, OnAutoRepair);
+
+ if (repair)
+ REGISTER_CLIENT(EID_CLICK, repair, EngDlg, OnRepair);
+
+ if (replace)
+ REGISTER_CLIENT(EID_CLICK, replace, EngDlg, OnReplace);
+
+ if (repair_time)
+ repair_time->Hide();
+
+ if (replace_time)
+ replace_time->Hide();
+
+ if (priority_increase) {
+ char up_arrow[2];
+ up_arrow[0] = Font::ARROW_UP;
+ up_arrow[1] = 0;
+ priority_increase->SetText(up_arrow);
+
+ REGISTER_CLIENT(EID_CLICK, priority_increase, EngDlg, OnPriorityIncrease);
+ }
+
+ if (priority_decrease) {
+ char dn_arrow[2];
+ dn_arrow[0] = Font::ARROW_DOWN;
+ dn_arrow[1] = 0;
+ priority_decrease->SetText(dn_arrow);
+
+ REGISTER_CLIENT(EID_CLICK, priority_decrease, EngDlg, OnPriorityDecrease);
+ }
+
+ repair_queue = (ListBox*) FindControl(601);
+
+ if (repair_queue) {
+ repair_queue->SetSelectedStyle(ListBox::LIST_ITEM_STYLE_FILLED_BOX);
+ REGISTER_CLIENT(EID_SELECT, repair_queue, EngDlg, OnQueue);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EngDlg::Show()
+{
+ FormWindow::Show();
+
+ if (ship) {
+ int nsources = ship->Reactors().size();
+
+ for (int i = 0; i < 4; i++) {
+ if (i >= nsources) {
+ sources[i]->Hide();
+ source_levels[i]->Hide();
+ clients[i]->Hide();
+ }
+ }
+ }
+}
+
+void
+EngDlg::Hide()
+{
+ FormWindow::Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EngDlg::SetShip(Ship* s)
+{
+ if (IsShown() && ship != s) {
+ selected_source = 0;
+ selected_repair = 0;
+ selected_component = 0;
+ selected_clients.clear();
+
+ ship = s;
+
+ UpdateRouteTables();
+ ExecFrame();
+ }
+
+ // make sure we clear the ship, even if not shown:
+ else if (!s) {
+ ship = 0;
+ }
+}
+
+void
+EngDlg::UpdateRouteTables()
+{
+ for (int i = 0; i < 4; i++)
+ clients[i]->ClearSelection();
+
+ if (components)
+ components->ClearItems();
+
+ if (priority_increase)
+ priority_increase->SetEnabled(false);
+
+ if (priority_decrease)
+ priority_decrease->SetEnabled(false);
+
+ if (ship) {
+ for (int i = 0; i < 4; i++) {
+ if (sources[i])
+ sources[i]->Hide();
+
+ if (source_levels[i])
+ source_levels[i]->Hide();
+
+ if (clients[i]) {
+ clients[i]->ClearItems();
+ clients[i]->Hide();
+ }
+ }
+
+ int reactor_index = 0;
+ ListIter<PowerSource> reactor_iter = ship->Reactors();
+
+ while (++reactor_iter) {
+ PowerSource* reactor = reactor_iter.value();
+
+ if (sources[reactor_index] && clients[reactor_index]) {
+ sources[reactor_index]->SetText(Game::GetText(reactor->Abbreviation()));
+ sources[reactor_index]->Show();
+
+ source_levels[reactor_index]->Show();
+ source_levels[reactor_index]->SetValue((int) reactor->GetPowerLevel());
+
+ clients[reactor_index]->Show();
+
+ int index = 0;
+ ListIter<System> client = reactor->Clients();
+ while (++client) {
+ char abrv[64], num[20];
+ FormatNumber(num, client->GetPowerLevel());
+ strcpy(abrv, Game::GetText(client->Name()));
+
+ clients[reactor_index]->AddItemWithData(Game::GetText(abrv), index);
+ clients[reactor_index]->SetItemText(index, 1, num);
+ clients[reactor_index]->SetItemData(index, 1, (DWORD) client->GetPowerLevel());
+
+ index++;
+ }
+
+ clients[reactor_index]->SortItems();
+ }
+
+ reactor->RouteScanned();
+ reactor_index++;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EngDlg::ExecFrame()
+{
+ if (IsShown())
+ UpdateSelection();
+}
+
+void
+EngDlg::UpdateSelection()
+{
+ if (!ship) return;
+
+ char num[20];
+ int nsources = ship->Reactors().size();
+
+ // update the route tables:
+ for (int source_index = 0; source_index < nsources; source_index++) {
+ PowerSource* reac = ship->Reactors()[source_index];
+
+ if (reac->RouteChanged())
+ UpdateRouteTables();
+
+ Color c(62,106,151);
+
+ if (reac->IsPowerOn()) {
+ switch (reac->Status()) {
+ default:
+ case System::NOMINAL: break;
+ case System::DEGRADED: c = Color::Yellow; break;
+ case System::CRITICAL: c = Color::BrightRed; break;
+ case System::DESTROYED: c = Color::DarkRed; break;
+ }
+ }
+ else {
+ c = Color::Gray;
+ }
+
+ sources[source_index]->SetBackColor(c);
+ source_levels[source_index]->SetEnabled(reac->IsPowerOn());
+ source_levels[source_index]->SetValue((int) reac->GetPowerLevel());
+
+ ListBox* client_list = clients[source_index];
+
+ for (int i = 0; i < client_list->NumItems(); i++) {
+ int index = client_list->GetItemData(i);
+
+ System* client = reac->Clients()[index];
+ FormatNumber(num, client->GetPowerLevel());
+ client_list->SetItemText(i, 1, num);
+ client_list->SetItemData(i, 1, (DWORD) client->GetPowerLevel());
+
+ if (client->IsPowerOn()) {
+ Color c;
+
+ switch (client->Status()) {
+ default:
+ case System::NOMINAL: c = Color::White; break;
+ case System::DEGRADED: c = Color::Yellow; break;
+ case System::CRITICAL: c = Color::BrightRed; break;
+ case System::DESTROYED: c = Color::DarkRed; break;
+ }
+
+ client_list->SetItemColor(i, c);
+ }
+ else {
+ client_list->SetItemColor(i, Color::Gray);
+ }
+
+ }
+ }
+
+ // update the detail info:
+ if (selected_source) {
+ selected_name->SetText(Game::GetText(selected_source->Name()));
+ power_off->SetEnabled(true);
+ power_on->SetEnabled(true);
+ override->SetEnabled(true);
+ if (override->GetButtonState() != 2)
+ override->SetButtonState(selected_source->GetPowerLevel() > 100 ? 1 : 0);
+ power_level->SetEnabled(selected_source->IsPowerOn());
+ power_level->SetValue((int) selected_source->GetPowerLevel());
+
+ if (selected_source->Safety() < 100) {
+ power_level->SetMarker((int) selected_source->Safety(), 0);
+ power_level->SetMarker((int) selected_source->Safety(), 1);
+ }
+ else {
+ power_level->SetMarker(-1, 0);
+ power_level->SetMarker(-1, 1);
+ }
+
+ capacity->SetEnabled(true);
+ capacity->SetValue((int) selected_source->Charge());
+
+ if (selected_source->IsPowerOn()) {
+ if (power_on->GetButtonState() != 2)
+ power_on->SetButtonState(1);
+ if (power_off->GetButtonState() != 2)
+ power_off->SetButtonState(0);
+ }
+ else {
+ if (power_on->GetButtonState() != 2)
+ power_on->SetButtonState(0);
+ if (power_off->GetButtonState() != 2)
+ power_off->SetButtonState(1);
+ }
+
+ if (components) {
+ for (int i = 0; i < components->NumItems(); i++) {
+ int index = components->GetItemData(i);
+
+ Component* comp = selected_source->GetComponents()[index];
+
+ Text stat = "OK";
+ Color c;
+
+ switch (comp->Status()) {
+ case Component::DESTROYED:
+ case Component::CRITICAL: stat = "FAIL"; c = Color::BrightRed; break;
+ case Component::DEGRADED: stat = "WARN"; c = Color::Yellow; break;
+ case Component::NOMINAL: stat = "OK"; c = Color::White; break;
+ case Component::REPLACE:
+ case Component::REPAIR: stat = "MAINT"; c = Color::Cyan; break;
+ }
+
+ stat = Game::GetText(Text("EngDlg.") + stat);
+ components->SetItemText(i, 1, stat);
+ components->SetItemData(i, 1, (int) comp->Status());
+
+ FormatNumber(num, comp->SpareCount());
+ components->SetItemText(i, 2, num);
+ components->SetItemData(i, 2, comp->SpareCount());
+ components->SetItemColor(i, c);
+ }
+ }
+ }
+
+ else if (selected_clients.size() == 1) {
+ System* sink = selected_clients[0];
+
+ selected_name->SetText(Game::GetText(sink->Name()));
+ power_off->SetEnabled(true);
+ power_on->SetEnabled(true);
+ override->SetEnabled(true);
+ if (override->GetButtonState() != 2)
+ override->SetButtonState(sink->GetPowerLevel() > 100 ? 1 : 0);
+ power_level->SetEnabled(sink->IsPowerOn());
+ power_level->SetValue((int) sink->GetPowerLevel());
+
+ if (sink->Safety() < 100) {
+ power_level->SetMarker((int) sink->Safety(), 0);
+ power_level->SetMarker((int) sink->Safety(), 1);
+ }
+ else {
+ power_level->SetMarker(-1, 0);
+ power_level->SetMarker(-1, 1);
+ }
+
+ capacity->SetEnabled(true);
+ capacity->SetValue((int) sink->Charge());
+
+ if (sink->IsPowerOn()) {
+ if (power_on->GetButtonState() != 2)
+ power_on->SetButtonState(1);
+ if (power_off->GetButtonState() != 2)
+ power_off->SetButtonState(0);
+ }
+ else {
+ if (power_on->GetButtonState() != 2)
+ power_on->SetButtonState(0);
+ if (power_off->GetButtonState() != 2)
+ power_off->SetButtonState(1);
+ }
+
+ if (components) {
+ for (int i = 0; i < components->NumItems(); i++) {
+ int index = components->GetItemData(i);
+
+ Component* comp = sink->GetComponents()[index];
+
+ Text stat = "OK";
+ Color c;
+
+ switch (comp->Status()) {
+ case Component::DESTROYED:
+ case Component::CRITICAL: stat = "FAIL"; c = Color::BrightRed; break;
+ case Component::DEGRADED: stat = "WARN"; c = Color::Yellow; break;
+ case Component::NOMINAL: stat = "OK"; c = Color::White; break;
+ case Component::REPLACE:
+ case Component::REPAIR: stat = "MAINT"; c = Color::Cyan; break;
+ }
+
+ stat = Game::GetText(Text("EngDlg.") + stat);
+ components->SetItemText(i, 1, stat);
+ components->SetItemData(i, 1, (int) comp->Status());
+
+ FormatNumber(num, comp->SpareCount());
+ components->SetItemText(i, 2, num);
+ components->SetItemData(i, 2, comp->SpareCount());
+ components->SetItemColor(i, c);
+ }
+ }
+ }
+
+ else if (selected_clients.size() > 1) {
+ System* sink = selected_clients[0];
+
+ selected_name->SetText(Game::GetText("[Multiple]"));
+ power_off->SetEnabled(true);
+ power_on->SetEnabled(true);
+ override->SetEnabled(true);
+ if (override->GetButtonState() != 2)
+ override->SetButtonState(sink->GetPowerLevel() > 100 ? 1 : 0);
+ power_level->SetEnabled(true);
+ power_level->SetValue((int) sink->GetPowerLevel());
+
+ if (sink->Safety() < 100) {
+ power_level->SetMarker((int) sink->Safety(), 0);
+ power_level->SetMarker((int) sink->Safety(), 1);
+ }
+ else {
+ power_level->SetMarker(-1, 0);
+ power_level->SetMarker(-1, 1);
+ }
+
+ capacity->SetEnabled(true);
+ capacity->SetValue((int) sink->Charge());
+
+ if (sink->IsPowerOn()) {
+ if (power_on->GetButtonState() != 2)
+ power_on->SetButtonState(1);
+ if (power_off->GetButtonState() != 2)
+ power_off->SetButtonState(0);
+ }
+ else {
+ if (power_on->GetButtonState() != 2)
+ power_on->SetButtonState(0);
+ if (power_off->GetButtonState() != 2)
+ power_off->SetButtonState(1);
+ }
+ }
+
+ else {
+ selected_name->SetText(Game::GetText("No Selection"));
+ power_off->SetEnabled(false);
+ power_off->SetButtonState(0);
+ power_on->SetEnabled(false);
+ power_on->SetButtonState(0);
+ override->SetEnabled(false);
+ override->SetButtonState(0);
+ power_level->SetEnabled(false);
+ power_level->SetValue(0);
+ power_level->SetMarker(-1, 0);
+ power_level->SetMarker(-1, 1);
+ capacity->SetEnabled(false);
+ capacity->SetValue(0);
+ }
+
+ // display the repair queue:
+ if (repair_queue) {
+ // if adding to the queue, dump and reload:
+ if (repair_queue->NumItems() < ship->RepairQueue().size()) {
+ repair_queue->ClearItems();
+
+ int i = 0;
+ ListIter<System> iter = ship->RepairQueue();
+ while (++iter) {
+ double time_remaining = 0;
+ char etr[20];
+
+ System* sys = iter.value();
+
+ ListIter<Component> comp = sys->GetComponents();
+ while (++comp) {
+ Component* c = comp.value();
+ if (c->TimeRemaining() > time_remaining)
+ time_remaining = c->TimeRemaining();
+ }
+
+ FormatTime(etr, (int) (time_remaining / ship->RepairSpeed()));
+ repair_queue->AddItem(Game::GetText(sys->Name()));
+ repair_queue->SetItemText(i, 1, etr);
+ i++;
+ }
+ }
+
+ // otherwise, update in place:
+ else {
+ while (repair_queue->NumItems() > ship->RepairQueue().size())
+ repair_queue->RemoveItem(0);
+
+ bool found = false;
+
+ for (int i = 0; i < repair_queue->NumItems(); i++) {
+ double time_remaining = 0;
+ char etr[20];
+
+ System* sys = ship->RepairQueue().at(i);
+
+ ListIter<Component> comp = sys->GetComponents();
+ while (++comp) {
+ Component* c = comp.value();
+ if (c->TimeRemaining() > time_remaining)
+ time_remaining = c->TimeRemaining();
+ }
+
+ FormatTime(etr, (int) time_remaining);
+ repair_queue->SetItemText(i, sys->Name());
+ repair_queue->SetItemText(i, 1, etr);
+
+ if (sys == selected_repair) {
+ found = true;
+
+ if (!Mouse::LButton())
+ repair_queue->SetSelected(i);
+ }
+ }
+
+ if (!found)
+ selected_repair = 0;
+ }
+ }
+
+ if (auto_repair && auto_repair->GetButtonState() != 2)
+ auto_repair->SetButtonState(ship->AutoRepair() ? 1 : 0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EngDlg::OnSource(AWEvent* event)
+{
+ selected_source = 0;
+ selected_clients.clear();
+ selected_component = 0;
+
+ if (!ship) return;
+
+ int source_index = -1;
+
+ for (int i = 0; i < 4; i++)
+ if (event->window == sources[i])
+ source_index = i;
+
+ // found the source list:
+ if (source_index >= 0) {
+ selected_source = ship->Reactors()[source_index];
+ }
+
+ for (i = 0; i < 4; i++) {
+ clients[i]->ClearSelection();
+ }
+
+ if (components) {
+ components->ClearItems();
+
+ if (repair) repair->SetEnabled(false);
+ if (replace) replace->SetEnabled(false);
+ if (repair_time) repair_time->Hide();
+ if (replace_time) replace_time->Hide();
+
+ if (selected_source && selected_source->GetComponents().size()) {
+ int index = 0;
+ ListIter<Component> comp = selected_source->GetComponents();
+ while (++comp)
+ components->AddItemWithData(Game::GetText(comp->Abbreviation()), index++);
+ }
+ }
+}
+
+void
+EngDlg::OnClient(AWEvent* event)
+{
+ selected_source = 0;
+ selected_clients.clear();
+ selected_component = 0;
+
+ if (!ship) return;
+
+ int source_index = -1;
+
+ for (int i = 0; i < 4; i++) {
+ if (event->window == clients[i])
+ source_index = i;
+ else
+ clients[i]->ClearSelection();
+ }
+
+ // found the source list:
+ if (source_index >= 0) {
+ // find the power source:
+ PowerSource* src = ship->Reactors()[source_index];
+
+ // build a list of the clients to be manipulated:
+ List<System>& client_list = src->Clients();
+ for (int i = 0; i < clients[source_index]->NumItems(); i++) {
+ if (clients[source_index]->IsSelected(i)) {
+ int index = clients[source_index]->GetItemData(i);
+ selected_clients.append(client_list[index]);
+ }
+ }
+ }
+
+ if (components) {
+ components->ClearItems();
+
+ if (repair) repair->SetEnabled(false);
+ if (replace) replace->SetEnabled(false);
+ if (repair_time) repair_time->Hide();
+ if (replace_time) replace_time->Hide();
+
+ if (selected_clients.size() == 1) {
+ System* sink = selected_clients[0];
+
+ if (sink->GetComponents().size()) {
+ int index = 0;
+ ListIter<Component> comp = sink->GetComponents();
+ while (++comp)
+ components->AddItemWithData(Game::GetText(comp->Abbreviation()), index++);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EngDlg::OnRouteStart(AWEvent* event)
+{
+ if (!ship) return;
+
+ int source_index = -1;
+
+ for (int i = 0; i < 4; i++)
+ if (event->window == clients[i])
+ source_index = i;
+
+ // found the source list:
+ if (source_index >= 0) {
+ // remember the power source:
+ route_source = ship->Reactors()[source_index];
+ route_list.clear();
+
+ // build a list of the clients to be moved:
+ List<System>& rsc = route_source->Clients();
+ for (int i = 0; i < clients[source_index]->NumItems(); i++) {
+ if (clients[source_index]->IsSelected(i)) {
+ int index = clients[source_index]->GetItemData(i);
+ route_list.append(rsc[index]);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EngDlg::OnRouteComplete(AWEvent* event)
+{
+ if (!ship || !route_source) return;
+
+ int dest_index = -1;
+
+ for (int i = 0; i < 4; i++)
+ if (event->window == clients[i])
+ dest_index = i;
+
+ // found the destination list, copy the clients over:
+ if (dest_index >= 0) {
+ PowerSource* route_dest = ship->Reactors()[dest_index];
+
+ if (!route_dest)
+ return;
+
+ ListIter<System> iter = route_list;
+ while (++iter) {
+ System* client = iter.value();
+
+ route_source->RemoveClient(client);
+ route_dest->AddClient(client);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EngDlg::OnPowerOff(AWEvent* event)
+{
+ if (selected_source) {
+ selected_source->PowerOff();
+
+ power_off->SetButtonState(1);
+ power_on->SetButtonState(0);
+ override->SetButtonState(0);
+ }
+
+ else if (selected_clients.size() > 0) {
+ ListIter<System> iter = selected_clients;
+ while (++iter)
+ iter->PowerOff();
+
+ power_off->SetButtonState(1);
+ power_on->SetButtonState(0);
+ override->SetButtonState(0);
+ }
+}
+
+void
+EngDlg::OnPowerOn(AWEvent* event)
+{
+ if (selected_source) {
+ selected_source->PowerOn();
+
+ power_off->SetButtonState(0);
+ power_on->SetButtonState(1);
+ override->SetButtonState(0);
+ }
+
+ else if (selected_clients.size() > 0) {
+ ListIter<System> iter = selected_clients;
+ while (++iter)
+ iter->PowerOn();
+
+ power_off->SetButtonState(0);
+ power_on->SetButtonState(1);
+ override->SetButtonState(0);
+ }
+}
+
+void
+EngDlg::OnOverride(AWEvent* event)
+{
+ bool over = false;
+
+ if (override->GetButtonState() > 0)
+ over = true;
+
+ if (selected_source) {
+ selected_source->SetOverride(over);
+ }
+
+ else if (selected_clients.size() > 0) {
+ ListIter<System> iter = selected_clients;
+ while (++iter)
+ iter->SetOverride(over);
+ }
+
+ if (over) {
+ power_off->SetButtonState(0);
+ power_on->SetButtonState(1);
+ }
+}
+
+void
+EngDlg::OnPowerLevel(AWEvent* event)
+{
+ int level = power_level->GetValue();
+
+ if (level < 0)
+ level = 0;
+ else if (level > 100)
+ level = 100;
+
+ if (selected_source)
+ selected_source->SetPowerLevel(level);
+
+ else if (selected_clients.size() > 0) {
+ ListIter<System> iter = selected_clients;
+ while (++iter)
+ iter->SetPowerLevel(level);
+ }
+
+ if (override)
+ override->SetButtonState(0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EngDlg::OnComponent(AWEvent* event)
+{
+ selected_component = 0;
+
+ if (repair) repair->SetEnabled(false);
+ if (replace) replace->SetEnabled(false);
+ if (repair_time) repair_time->Hide();
+ if (replace_time) replace_time->Hide();
+
+ // find index of selected component:
+ int component_index = -1;
+
+ if (components) {
+ int n = components->GetSelection();
+ if (n >= 0)
+ component_index = components->GetItemData(n);
+ }
+
+ // now examine the component info:
+ if (component_index >= 0) {
+ repair->SetEnabled(true);
+
+ if (selected_source) {
+ List<Component>& comps = selected_source->GetComponents();
+ selected_component = comps[component_index];
+
+ if (repair_time) {
+ char t[32];
+ int s = (int) (selected_component->RepairTime() /
+ ship->RepairSpeed());
+ FormatTime(t, s);
+ repair_time->SetText(t);
+ repair_time->Show();
+ }
+
+ if (selected_component->SpareCount() > 0) {
+ if (replace) replace->SetEnabled(true);
+
+ if (replace_time) {
+ char t[32];
+ int s = (int) (selected_component->ReplaceTime() /
+ ship->RepairSpeed());
+ FormatTime(t, s);
+ replace_time->SetText(t);
+ replace_time->Show();
+ }
+ }
+ }
+ else if (selected_clients.size() > 0) {
+ List<Component>& comps = selected_clients[0]->GetComponents();
+ selected_component = comps[component_index];
+
+ if (repair_time) {
+ char t[32];
+ int s = (int) (selected_component->RepairTime() /
+ ship->RepairSpeed());
+ FormatTime(t, s);
+ repair_time->SetText(t);
+ repair_time->Show();
+ }
+
+ if (selected_component->SpareCount() > 0) {
+ if (replace) replace->SetEnabled(true);
+
+ if (replace_time) {
+ char t[32];
+ int s = (int) (selected_component->ReplaceTime() /
+ ship->RepairSpeed());
+ FormatTime(t, s);
+ replace_time->SetText(t);
+ replace_time->Show();
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EngDlg::OnAutoRepair(AWEvent* event)
+{
+ if (ship)
+ ship->EnableRepair(auto_repair->GetButtonState() > 0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EngDlg::OnRepair(AWEvent* event)
+{
+ if (selected_component) {
+ selected_component->Repair();
+
+ if (ship)
+ ship->RepairSystem(selected_component->GetSystem());
+ }
+}
+
+void
+EngDlg::OnReplace(AWEvent* event)
+{
+ if (selected_component) {
+ selected_component->Replace();
+
+ if (ship)
+ ship->RepairSystem(selected_component->GetSystem());
+ }
+}
+
+void
+EngDlg::OnQueue(AWEvent* event)
+{
+ selected_repair = 0;
+
+ if (priority_increase) priority_increase->SetEnabled(false);
+ if (priority_decrease) priority_decrease->SetEnabled(false);
+
+ if (repair_queue) {
+ int n = repair_queue->GetSelection();
+
+ if (n >= 0)
+ selected_repair = ship->RepairQueue().at(n);
+ }
+
+ if (!selected_repair)
+ return;
+
+ selected_clients.clear();
+ selected_clients.append(selected_repair);
+
+ if (components) {
+ components->ClearItems();
+
+ if (repair) repair->SetEnabled(false);
+ if (replace) replace->SetEnabled(false);
+ if (repair_time) repair_time->Hide();
+ if (replace_time) replace_time->Hide();
+
+ if (selected_repair->GetComponents().size()) {
+ int index = 0;
+ ListIter<Component> comp = selected_repair->GetComponents();
+ while (++comp)
+ components->AddItemWithData(Game::GetText(comp->Abbreviation()), index++);
+ }
+ }
+
+ if (priority_increase) priority_increase->SetEnabled(true);
+ if (priority_decrease) priority_decrease->SetEnabled(true);
+}
+
+void
+EngDlg::OnPriorityIncrease(AWEvent* event)
+{
+ if (ship && repair_queue)
+ ship->IncreaseRepairPriority(repair_queue->GetSelection());
+}
+
+void
+EngDlg::OnPriorityDecrease(AWEvent* event)
+{
+ if (ship && repair_queue)
+ ship->DecreaseRepairPriority(repair_queue->GetSelection());
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EngDlg::OnClose(AWEvent* event)
+{
+ if (manager)
+ manager->CloseTopmost();
+}
+
+
+
diff --git a/Stars45/EngDlg.h b/Stars45/EngDlg.h
new file mode 100644
index 0000000..0ca2737
--- /dev/null
+++ b/Stars45/EngDlg.h
@@ -0,0 +1,106 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: EngDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Engineering (Power/Maint) Dialog Active Window class
+*/
+
+#ifndef EngDlg_h
+#define EngDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+
+// +--------------------------------------------------------------------+
+
+class GameScreen;
+class Ship;
+class PowerSource;
+class System;
+class Component;
+
+class PowerClient;
+
+// +--------------------------------------------------------------------+
+
+class EngDlg : public FormWindow
+{
+public:
+ EngDlg(Screen* s, FormDef& def, GameScreen* mgr);
+ virtual ~EngDlg();
+
+ virtual void Show();
+ virtual void Hide();
+ virtual void RegisterControls();
+
+ // Operations:
+ virtual void OnSource(AWEvent* event);
+ virtual void OnClient(AWEvent* event);
+ virtual void OnRouteStart(AWEvent* event);
+ virtual void OnRouteComplete(AWEvent* event);
+ virtual void OnPowerOff(AWEvent* event);
+ virtual void OnPowerOn(AWEvent* event);
+ virtual void OnOverride(AWEvent* event);
+ virtual void OnPowerLevel(AWEvent* event);
+ virtual void OnComponent(AWEvent* event);
+ virtual void OnAutoRepair(AWEvent* event);
+ virtual void OnRepair(AWEvent* event);
+ virtual void OnReplace(AWEvent* event);
+ virtual void OnQueue(AWEvent* event);
+ virtual void OnPriorityIncrease(AWEvent* event);
+ virtual void OnPriorityDecrease(AWEvent* event);
+ virtual void OnClose(AWEvent* event);
+
+ virtual void ExecFrame();
+ void UpdateRouteTables();
+ void UpdateSelection();
+ void SetShip(Ship* s);
+
+protected:
+ Ship* ship;
+ GameScreen* manager;
+
+ Button* close_btn;
+ Button* sources[4];
+ Slider* source_levels[4];
+ ListBox* clients[4];
+ ListBox* components;
+ ListBox* repair_queue;
+ ActiveWindow* selected_name;
+ Button* power_off;
+ Button* power_on;
+ Button* override;
+ Slider* power_level;
+ Slider* capacity;
+ Button* auto_repair;
+ Button* repair;
+ Button* replace;
+ ActiveWindow* repair_time;
+ ActiveWindow* replace_time;
+ Button* priority_increase;
+ Button* priority_decrease;
+
+ PowerSource* route_source;
+ List<System> route_list;
+
+ PowerSource* selected_source;
+ List<System> selected_clients;
+
+ System* selected_repair;
+ Component* selected_component;
+};
+
+#endif EngDlg_h
+
diff --git a/Stars45/ExceptionHandler.cpp b/Stars45/ExceptionHandler.cpp
new file mode 100644
index 0000000..8946c26
--- /dev/null
+++ b/Stars45/ExceptionHandler.cpp
@@ -0,0 +1,429 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ExceptionHandler.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+
+#include <windows.h>
+#include <imagehlp.h>
+
+extern void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+class ExceptionHandler
+{
+public:
+ ExceptionHandler();
+ ~ExceptionHandler();
+
+private:
+ static LPTOP_LEVEL_EXCEPTION_FILTER old_filter;
+
+ static LONG WINAPI ExceptionFilter(EXCEPTION_POINTERS* info);
+
+ static void PrintReport(EXCEPTION_POINTERS* info);
+
+ // Helper functions
+ static const char* GetExceptionString(DWORD dwCode);
+ static BOOL GetLogicalAddress(VOID* addr, char* module, int len,
+ DWORD& section, DWORD& offset);
+
+ static BOOL InitImageHelp();
+ static void ImageStackTrace(CONTEXT* context);
+ static void IntelStackTrace(CONTEXT* context);
+
+
+ // Make typedefs for some IMAGEHLP.DLL functions so that we can use them
+ // with GetProcAddress
+ typedef BOOL (__stdcall * SYMINITIALIZEPROC)(HANDLE, LPSTR, BOOL);
+ typedef BOOL (__stdcall * SYMCLEANUPPROC)(HANDLE);
+
+ typedef LPVOID (__stdcall *SYMFUNCTIONTABLEACCESSPROC)(HANDLE, DWORD);
+ typedef DWORD (__stdcall *SYMGETMODULEBASEPROC)(HANDLE, DWORD);
+ typedef BOOL (__stdcall *SYMGETSYMFROMADDRPROC)(HANDLE, DWORD, PDWORD, PIMAGEHLP_SYMBOL);
+
+ typedef BOOL (__stdcall * STACKWALKPROC)(DWORD,
+ HANDLE,
+ HANDLE,
+ LPSTACKFRAME,
+ LPVOID,
+ PREAD_PROCESS_MEMORY_ROUTINE,
+ PFUNCTION_TABLE_ACCESS_ROUTINE,
+ PGET_MODULE_BASE_ROUTINE,
+ PTRANSLATE_ADDRESS_ROUTINE);
+
+ static SYMINITIALIZEPROC SymInitialize;
+ static SYMCLEANUPPROC SymCleanup;
+ static STACKWALKPROC StackTrace;
+ static SYMFUNCTIONTABLEACCESSPROC SymFunctionTableAccess;
+ static SYMGETMODULEBASEPROC SymGetModuleBase;
+ static SYMGETSYMFROMADDRPROC SymGetSymFromAddr;
+};
+
+// +--------------------------------------------------------------------+
+
+LPTOP_LEVEL_EXCEPTION_FILTER ExceptionHandler::old_filter = 0;
+
+ExceptionHandler::SYMINITIALIZEPROC ExceptionHandler::SymInitialize = 0;
+ExceptionHandler::SYMCLEANUPPROC ExceptionHandler::SymCleanup = 0;
+ExceptionHandler::STACKWALKPROC ExceptionHandler::StackTrace = 0;
+
+ExceptionHandler::SYMFUNCTIONTABLEACCESSPROC
+ ExceptionHandler::SymFunctionTableAccess = 0;
+
+ExceptionHandler::SYMGETMODULEBASEPROC
+ ExceptionHandler::SymGetModuleBase = 0;
+
+ExceptionHandler::SYMGETSYMFROMADDRPROC
+ ExceptionHandler::SymGetSymFromAddr = 0;
+
+ExceptionHandler global_exception_handler;
+
+
+// +--------------------------------------------------------------------+
+
+ExceptionHandler::ExceptionHandler()
+{
+ old_filter = SetUnhandledExceptionFilter(ExceptionFilter);
+}
+
+ExceptionHandler::~ExceptionHandler()
+{
+ SetUnhandledExceptionFilter(old_filter);
+}
+
+// +--------------------------------------------------------------------+
+
+static bool in_filter = false;
+
+LONG WINAPI
+ExceptionHandler::ExceptionFilter(EXCEPTION_POINTERS* info)
+{
+ if (in_filter) {
+ Print("\n\n*********************************************\n");
+ Print("SECOND EXCEPTION CAUGHT: TERMINATING.\n");
+ Print("*********************************************\n");
+ }
+
+ else {
+ in_filter = true;
+ PrintReport(info);
+ in_filter = false;
+ }
+
+ if (old_filter)
+ return old_filter(info);
+ else
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ExceptionHandler::PrintReport(EXCEPTION_POINTERS* info)
+{
+ EXCEPTION_RECORD* record = info->ExceptionRecord;
+ CONTEXT* context = info->ContextRecord;
+ DWORD code = record->ExceptionCode;
+
+ Print("\n*********************************************\n");
+ Print("FATAL EXCEPTION:\n");
+
+ Print("\nRegisters:\n");
+ Print("EAX: %08x\n", context->Eax);
+ Print("EBX: %08x\n", context->Ebx);
+ Print("ECX: %08x\n", context->Ecx);
+ Print("EDX: %08x\n", context->Edx);
+ Print("EDI: %08x\n", context->Edi);
+ Print("ESI: %08x\n", context->Esi);
+ Print("EBP: %08x\n", context->Ebp);
+ Print("\n");
+ Print("CS:EIP: %04x:%08x\n", context->SegCs, context->Eip);
+ Print("SS:ESP: %04x:%08x\n", context->SegSs, context->Esp);
+ Print("DS: %04x\n", context->SegDs);
+ Print("ES: %04x\n", context->SegEs);
+ Print("FS: %04x\n", context->SegFs);
+ Print("GS: %04x\n", context->SegGs);
+ Print("Flags: %08x\n", context->EFlags );
+ Print("\n");
+
+ Print("Exception Code: %08x %s\n",code, GetExceptionString(code));
+ Print("Exception Addr: %08x \n", record->ExceptionAddress);
+
+ if (code == EXCEPTION_ACCESS_VIOLATION && record->NumberParameters >= 2) {
+ if (record->ExceptionInformation[0])
+ Print(" Program attempted to WRITE to address 0x%08x\n", record->ExceptionInformation[1]);
+ else
+ Print(" Program attempted to READ from address 0x%08x\n", record->ExceptionInformation[1]);
+ }
+
+ if (InitImageHelp()) {
+ ImageStackTrace(context);
+ SymCleanup(GetCurrentProcess());
+ }
+ else {
+ IntelStackTrace(context);
+ }
+
+ Print("\n*********************************************\nPROGRAM TERMINATED.\n");
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+ExceptionHandler::GetExceptionString(DWORD code)
+{
+ #define EXCEPTION( x ) case EXCEPTION_##x: return #x;
+
+ switch (code) {
+ EXCEPTION( ACCESS_VIOLATION )
+ EXCEPTION( DATATYPE_MISALIGNMENT )
+ EXCEPTION( BREAKPOINT )
+ EXCEPTION( SINGLE_STEP )
+ EXCEPTION( ARRAY_BOUNDS_EXCEEDED )
+ EXCEPTION( FLT_DENORMAL_OPERAND )
+ EXCEPTION( FLT_DIVIDE_BY_ZERO )
+ EXCEPTION( FLT_INEXACT_RESULT )
+ EXCEPTION( FLT_INVALID_OPERATION )
+ EXCEPTION( FLT_OVERFLOW )
+ EXCEPTION( FLT_STACK_CHECK )
+ EXCEPTION( FLT_UNDERFLOW )
+ EXCEPTION( INT_DIVIDE_BY_ZERO )
+ EXCEPTION( INT_OVERFLOW )
+ EXCEPTION( PRIV_INSTRUCTION )
+ EXCEPTION( IN_PAGE_ERROR )
+ EXCEPTION( ILLEGAL_INSTRUCTION )
+ EXCEPTION( NONCONTINUABLE_EXCEPTION )
+ EXCEPTION( STACK_OVERFLOW )
+ EXCEPTION( INVALID_DISPOSITION )
+ EXCEPTION( GUARD_PAGE )
+ EXCEPTION( INVALID_HANDLE )
+ }
+
+ static char buffer[512] = { 0 };
+
+ FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
+ GetModuleHandle("NTDLL.DLL"),
+ code, 0, buffer, sizeof(buffer), 0 );
+
+ return buffer;
+}
+
+// +--------------------------------------------------------------------+
+
+BOOL
+ExceptionHandler::GetLogicalAddress(void* addr, char* mod_name, int len, DWORD& section, DWORD& offset)
+{
+ MEMORY_BASIC_INFORMATION mbi;
+
+ if (!VirtualQuery(addr, &mbi, sizeof(mbi)))
+ return FALSE;
+
+ DWORD hMod = (DWORD)mbi.AllocationBase;
+
+ if (!GetModuleFileName((HMODULE)hMod, mod_name, len))
+ return FALSE;
+
+ PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER) hMod;
+ PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + pDosHdr->e_lfanew);
+ PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr );
+
+ DWORD rva = (DWORD)addr - hMod; // RVA is offset from module load address
+
+ // Iterate through the section table, looking for the one that encompasses
+ // the linear address.
+ for (unsigned i = 0; i < pNtHdr->FileHeader.NumberOfSections; i++, pSection++ ) {
+ DWORD sectionStart = pSection->VirtualAddress;
+ DWORD sectionEnd = sectionStart
+ + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize);
+
+ // Is the address in this section???
+ if ((rva >= sectionStart) && (rva <= sectionEnd)) {
+ // Yes, address is in the section. Calculate section and offset,
+ // and store in the "section" & "offset" params, which were
+ // passed by reference.
+ section = i+1;
+ offset = rva - sectionStart;
+ return TRUE;
+ }
+ }
+
+ return FALSE; // Should never get here!
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ExceptionHandler::IntelStackTrace(CONTEXT* context)
+{
+ Print("\nStack Trace (Intel):\n");
+ Print("Address Frame Logical addr Module\n");
+
+ DWORD pc = context->Eip;
+ DWORD* pFrame;
+ DWORD* pPrevFrame;
+
+ pFrame = (DWORD*)context->Ebp;
+
+ do {
+ char mod_name[256] = { 0 };
+ DWORD section = 0, offset = 0;
+
+ GetLogicalAddress((void*)pc, mod_name, 256, section, offset);
+
+ Print("%08X %08X %04X:%08X %s\n",
+ pc, pFrame, section, offset, mod_name);
+
+ pc = pFrame[1];
+ pPrevFrame = pFrame;
+ pFrame = (PDWORD)pFrame[0]; // proceed to next higher frame on stack
+
+ if ((DWORD)pFrame & 3) // Frame pointer must be aligned on a
+ break; // DWORD boundary. Bail if not so.
+
+ if (pFrame <= pPrevFrame)
+ break;
+
+ // Can two DWORDs be read from the supposed frame address?
+ if (IsBadWritePtr(pFrame, sizeof(PVOID)*2))
+ break;
+
+ }
+ while ( 1 );
+}
+
+// +--------------------------------------------------------------------+
+
+void ExceptionHandler::ImageStackTrace(CONTEXT* context)
+{
+ Print("\nStack Trace (Symbolic):\n");
+ Print("Address Frame\n");
+
+ // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
+ STACKFRAME sf;
+ memset(&sf, 0, sizeof(sf));
+
+ // Initialize the STACKFRAME structure for the first call. This is only
+ // necessary for Intel CPUs, and isn't mentioned in the documentation.
+ sf.AddrPC.Offset = context->Eip;
+ sf.AddrPC.Mode = AddrModeFlat;
+ sf.AddrStack.Offset = context->Esp;
+ sf.AddrStack.Mode = AddrModeFlat;
+ sf.AddrFrame.Offset = context->Ebp;
+ sf.AddrFrame.Mode = AddrModeFlat;
+
+ while ( 1 ) {
+ if (!StackTrace( IMAGE_FILE_MACHINE_I386,
+ GetCurrentProcess(),
+ GetCurrentThread(),
+ &sf,
+ context,
+ 0,
+ SymFunctionTableAccess,
+ SymGetModuleBase,
+ 0))
+ break;
+
+ if (sf.AddrFrame.Offset == 0) // Basic sanity check to make sure
+ break; // the frame is OK. Bail if not.
+
+ Print("%08x %08x ", sf.AddrPC.Offset, sf.AddrFrame.Offset);
+
+ // IMAGEHLP is wacky, and requires you to pass in a pointer to an
+ // IMAGEHLP_SYMBOL structure. The problem is that this structure is
+ // variable length. That is, you determine how big the structure is
+ // at runtime. This means that you can't use sizeof(struct).
+ // So...make a buffer that's big enough, and make a pointer
+ // to the buffer. We also need to initialize not one, but TWO
+ // members of the structure before it can be used.
+
+ BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 512];
+ PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)symbolBuffer;
+ pSymbol->SizeOfStruct = sizeof(symbolBuffer);
+ pSymbol->MaxNameLength = 512;
+
+ DWORD symDisplacement = 0; // Displacement of the input address,
+ // relative to the start of the symbol
+
+ if (SymGetSymFromAddr(GetCurrentProcess(), sf.AddrPC.Offset,
+ &symDisplacement, pSymbol)) {
+ Print("%-40s [%04X]\n", pSymbol->Name, symDisplacement);
+ }
+ else {
+ char mod_name[256] = { 0 };
+ DWORD section = 0, offset = 0;
+
+ GetLogicalAddress((PVOID)sf.AddrPC.Offset,
+ mod_name, 256, section, offset );
+
+ Print("%04X:%08X %s\n", section, offset, mod_name);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+BOOL
+ExceptionHandler::InitImageHelp()
+{
+ Print("\n");
+
+ HMODULE h = LoadLibrary("IMAGEHLP.DLL");
+ if (!h) {
+ Print("--- could not load IMAGEHLP.DLL (%08x) ---\n", GetLastError());
+ return FALSE;
+ }
+
+ SymInitialize = (SYMINITIALIZEPROC) GetProcAddress(h, "SymInitialize");
+ if (!SymInitialize) {
+ Print("--- could not find SymInitialize ---\n");
+ return FALSE;
+ }
+
+ SymCleanup = (SYMCLEANUPPROC) GetProcAddress(h, "SymCleanup");
+ if (!SymCleanup) {
+ Print("--- could not find SymCleanup ---\n");
+ return FALSE;
+ }
+
+ StackTrace = (STACKWALKPROC) GetProcAddress(h, "StackWalk");
+ if (!StackTrace) {
+ Print("--- could not find StackWalk ---\n");
+ return FALSE;
+ }
+
+ SymFunctionTableAccess = (SYMFUNCTIONTABLEACCESSPROC)
+ GetProcAddress(h, "SymFunctionTableAccess");
+
+ if (!SymFunctionTableAccess) {
+ Print("--- could not find SymFunctionTableAccess ---\n");
+ return FALSE;
+ }
+
+ SymGetModuleBase = (SYMGETMODULEBASEPROC) GetProcAddress(h, "SymGetModuleBase");
+ if (!SymGetModuleBase) {
+ Print("--- could not find SymGetModuleBase ---\n");
+ return FALSE;
+ }
+
+ SymGetSymFromAddr = (SYMGETSYMFROMADDRPROC) GetProcAddress(h, "SymGetSymFromAddr");
+ if (!SymGetSymFromAddr) {
+ Print("--- could not find SymGetSymFromAddr ---\n");
+ return FALSE;
+ }
+
+ if (!SymInitialize(GetCurrentProcess(), 0, TRUE)) {
+ Print("--- could not Initialize IMAGEHLP.DLL (%08x) ---\n", GetLastError());
+ return FALSE;
+ }
+
+ Print("Loaded IMAGEHLP.DLL\n");
+ return TRUE;
+}
+
diff --git a/Stars45/ExitDlg.cpp b/Stars45/ExitDlg.cpp
new file mode 100644
index 0000000..189928a
--- /dev/null
+++ b/Stars45/ExitDlg.cpp
@@ -0,0 +1,152 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ExitDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "ExitDlg.h"
+#include "MenuScreen.h"
+#include "MusicDirector.h"
+#include "Starshatter.h"
+#include "FormatUtil.h"
+
+#include "Game.h"
+#include "Keyboard.h"
+#include "Button.h"
+#include "RichTextBox.h"
+#include "DataLoader.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(ExitDlg, OnApply);
+DEF_MAP_CLIENT(ExitDlg, OnCancel);
+
+// +--------------------------------------------------------------------+
+
+ExitDlg::ExitDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()),
+ manager(mgr), exit_latch(false),
+ credits(0), apply(0), cancel(0),
+ def_rect(def.GetRect())
+{
+ Init(def);
+}
+
+ExitDlg::~ExitDlg()
+{
+}
+
+void
+ExitDlg::RegisterControls()
+{
+ if (apply)
+ return;
+
+ credits = (RichTextBox*) FindControl(201);
+
+ apply = (Button*) FindControl(1);
+ REGISTER_CLIENT(EID_CLICK, apply, ExitDlg, OnApply);
+
+ cancel = (Button*) FindControl(2);
+ REGISTER_CLIENT(EID_CLICK, cancel, ExitDlg, OnCancel);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ExitDlg::ExecFrame()
+{
+ if (credits && credits->GetLineCount() > 0) {
+ credits->SmoothScroll(ScrollWindow::SCROLL_DOWN, Game::GUITime());
+
+ if (credits->GetTopIndex() >= credits->GetLineCount()-1) {
+ credits->ScrollTo(0);
+ }
+ }
+
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ OnApply(0);
+ }
+
+ if (Keyboard::KeyDown(VK_ESCAPE)) {
+ if (!exit_latch)
+ OnCancel(0);
+ }
+ else {
+ exit_latch = false;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ExitDlg::Show()
+{
+ if (!IsShown()) {
+ Rect r = def_rect;
+
+ if (r.w > screen->Width()) {
+ int extra = r.w - screen->Width();
+ r.w -= extra;
+ }
+
+ if (r.h > screen->Height()) {
+ int extra = r.h - screen->Height();
+ r.h -= extra;
+ }
+
+ r.x = (screen->Width() - r.w) / 2;
+ r.y = (screen->Height() - r.h) / 2;
+
+ MoveTo(r);
+
+ exit_latch = true;
+ Button::PlaySound(Button::SND_CONFIRM);
+ MusicDirector::SetMode(MusicDirector::CREDITS);
+
+ DataLoader* loader = DataLoader::GetLoader();
+ BYTE* block = 0;
+
+ loader->SetDataPath(0);
+ loader->LoadBuffer("credits.txt", block, true);
+
+ if (block && credits) {
+ credits->SetText((const char*) block);
+ }
+
+ loader->ReleaseBuffer(block);
+ }
+
+ FormWindow::Show();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ExitDlg::OnApply(AWEvent* event)
+{
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars) {
+ ::Print("Exit Confirmed.\n");
+ stars->Exit();
+ }
+}
+
+void
+ExitDlg::OnCancel(AWEvent* event)
+{
+ manager->ShowMenuDlg();
+ MusicDirector::SetMode(MusicDirector::MENU);
+}
+
+// +--------------------------------------------------------------------+
diff --git a/Stars45/ExitDlg.h b/Stars45/ExitDlg.h
new file mode 100644
index 0000000..63227a0
--- /dev/null
+++ b/Stars45/ExitDlg.h
@@ -0,0 +1,60 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ExitDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Navigation Active Window class
+*/
+
+#ifndef ExitDlg_h
+#define ExitDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+
+// +--------------------------------------------------------------------+
+
+class ExitDlg : public FormWindow
+{
+public:
+ ExitDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~ExitDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+
+ // Operations:
+ virtual void ExecFrame();
+
+ virtual void OnApply(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+
+protected:
+ MenuScreen* manager;
+
+ RichTextBox* credits;
+ Button* apply;
+ Button* cancel;
+ Rect def_rect;
+
+ bool exit_latch;
+};
+
+#endif ExitDlg_h
+
diff --git a/Stars45/Explosion.cpp b/Stars45/Explosion.cpp
new file mode 100644
index 0000000..ec21c36
--- /dev/null
+++ b/Stars45/Explosion.cpp
@@ -0,0 +1,611 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Explos.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Explosion Sprite animation class
+*/
+
+#include "MemDebug.h"
+#include "Explosion.h"
+#include "QuantumFlash.h"
+#include "Particles.h"
+#include "Ship.h"
+#include "Sim.h"
+#include "CameraDirector.h"
+#include "AudioConfig.h"
+
+#include "Light.h"
+#include "Sprite.h"
+#include "Solid.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Game.h"
+#include "Scene.h"
+#include "ParseUtil.h"
+
+// +--------------------------------------------------------------------+
+
+const int MAX_EXPLOSION_TYPES = 32;
+
+static float lifetimes[MAX_EXPLOSION_TYPES];
+static float scales[MAX_EXPLOSION_TYPES];
+static Bitmap* bitmaps[MAX_EXPLOSION_TYPES];
+static Bitmap* particle_bitmaps[MAX_EXPLOSION_TYPES];
+static int part_frames[MAX_EXPLOSION_TYPES];
+static int lengths[MAX_EXPLOSION_TYPES];
+static float light_levels[MAX_EXPLOSION_TYPES];
+static float light_decays[MAX_EXPLOSION_TYPES];
+static Color light_colors[MAX_EXPLOSION_TYPES];
+static int num_parts[MAX_EXPLOSION_TYPES];
+static int part_types[MAX_EXPLOSION_TYPES];
+static float part_speeds[MAX_EXPLOSION_TYPES];
+static float part_drags[MAX_EXPLOSION_TYPES];
+static float part_scales[MAX_EXPLOSION_TYPES];
+static float part_blooms[MAX_EXPLOSION_TYPES];
+static float part_rates[MAX_EXPLOSION_TYPES];
+static float part_decays[MAX_EXPLOSION_TYPES];
+static bool part_trails[MAX_EXPLOSION_TYPES];
+static int part_alphas[MAX_EXPLOSION_TYPES];
+static Sound* sounds[MAX_EXPLOSION_TYPES];
+static bool recycles[MAX_EXPLOSION_TYPES];
+
+// +--------------------------------------------------------------------+
+
+Explosion::Explosion(int t, const Vec3& pos, const Vec3& vel,
+ float exp_scale, float part_scale,
+ SimRegion* rgn, SimObject* src)
+ : SimObject("Explosion", t), type(t), particles(0), source(src)
+{
+ Observe(source);
+
+ MoveTo(pos);
+ velocity = vel;
+ drag = 0.3f;
+ rep = 0;
+ light = 0;
+ life = 0;
+
+ if (type == QUANTUM_FLASH) {
+ life = 1.1;
+
+ QuantumFlash* q = new(__FILE__,__LINE__) QuantumFlash();
+ rep = q;
+
+ light = new(__FILE__,__LINE__) Light(1e9, 0.66f);
+ light->SetColor(Color(180, 200, 255));
+ }
+
+ else if (type >= 0 && type < MAX_EXPLOSION_TYPES) {
+ life = lifetimes[type];
+
+ if (source) {
+ Matrix src_orient = source->Cam().Orientation();
+ src_orient.Transpose();
+ mount_rel = (pos - source->Location()) * src_orient;
+ }
+
+ if (lengths[type] > 0) {
+ int repeat = (lengths[type] == 1);
+ Sprite* s = new(__FILE__,__LINE__) Sprite(bitmaps[type], lengths[type], repeat);
+ s->Scale(exp_scale * scales[type]);
+ s->SetAngle(PI * rand()/16384.0);
+ s->SetLuminous(true);
+ rep = s;
+ }
+
+ if (light_levels[type] > 0) {
+ light = new(__FILE__,__LINE__) Light(light_levels[type], light_decays[type]);
+ light->SetColor(light_colors[type]);
+ }
+
+ if (num_parts[type] > 0) {
+ particles = new(__FILE__,__LINE__) Particles(particle_bitmaps[type],
+ num_parts[type],
+ pos,
+ Vec3(0.0f, 0.0f, 0.0f),
+ part_speeds[type] * part_scale,
+ part_drags[type],
+ part_scales[type] * part_scale,
+ part_blooms[type] * part_scale,
+ part_decays[type],
+ part_rates[type],
+ recycles[type],
+ part_trails[type],
+ (rgn && rgn->IsAirSpace()),
+ part_alphas[type],
+ part_frames[type]);
+ }
+ }
+
+ if (rep)
+ rep->MoveTo(pos);
+
+ if (light)
+ light->MoveTo(pos);
+}
+
+// +--------------------------------------------------------------------+
+
+Explosion::~Explosion()
+{
+ GRAPHIC_DESTROY(particles);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Explosion::Update(SimObject* obj)
+{
+ if (obj == source) {
+ source = 0;
+
+ if (life < 0 || life > 4)
+ life = 4;
+
+ if (particles)
+ particles->StopEmitting();
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+Explosion::GetObserverName() const
+{
+ static char name[128];
+ if (source)
+ sprintf(name, "Explosion(%s)", source->Name());
+ else
+ sprintf(name, "Explosion");
+ return name;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Explosion::Initialize()
+{
+ static int initialized = 0;
+ if (initialized) return;
+
+ ZeroMemory(lifetimes, sizeof(lifetimes));
+ ZeroMemory(scales, sizeof(scales));
+ ZeroMemory(bitmaps, sizeof(bitmaps));
+ ZeroMemory(particle_bitmaps, sizeof(particle_bitmaps));
+ ZeroMemory(lengths, sizeof(lengths));
+ ZeroMemory(light_levels, sizeof(light_levels));
+ ZeroMemory(light_decays, sizeof(light_decays));
+ ZeroMemory(light_colors, sizeof(light_colors));
+ ZeroMemory(num_parts, sizeof(num_parts));
+ ZeroMemory(part_types, sizeof(part_types));
+ ZeroMemory(part_speeds, sizeof(part_speeds));
+ ZeroMemory(part_drags, sizeof(part_drags));
+ ZeroMemory(part_scales, sizeof(part_scales));
+ ZeroMemory(part_blooms, sizeof(part_blooms));
+ ZeroMemory(part_decays, sizeof(part_decays));
+ ZeroMemory(part_rates, sizeof(part_rates));
+ ZeroMemory(part_trails, sizeof(part_trails));
+ ZeroMemory(part_alphas, sizeof(part_alphas));
+ ZeroMemory(sounds, sizeof(sounds));
+ ZeroMemory(recycles, sizeof(recycles));
+
+ const char* filename = "Explosions.def";
+ Print("Loading Explosion Defs '%s'\n", filename);
+
+ // Load Design File:
+ DataLoader* loader = DataLoader::GetLoader();
+ BYTE* block;
+
+ loader->SetDataPath("Explosions/");
+ int blocklen = loader->LoadBuffer(filename, block, true);
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'\n", filename);
+ exit(-3);
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "EXPLOSION") {
+ Print("ERROR: invalid explosion def file '%s'\n", filename);
+ exit(-4);
+ }
+ }
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "explosion") {
+
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: explosion structure missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ int type = -1;
+ char type_name[32];
+ char bitmap[32];
+ char particle_bitmap[32];
+ char sound_file[32];
+ float lifetime = 0.0f;
+ float scale = 1.0f;
+ int length = 0;
+ float light_level = 0.0f;
+ float light_decay = 1.0f;
+ Color light_color;
+ int num_part = 0;
+ int part_type = 0;
+ float part_speed = 0.0f;
+ float part_drag = 1.0f;
+ float part_scale = 1.0f;
+ float part_bloom = 0.0f;
+ float part_decay = 100.0f;
+ float part_rate = 1.0f;
+ bool part_trail = true;
+ bool continuous = false;
+ int part_alpha = 4;
+ int part_nframes = 1;
+ float sound_min_dist = 1.0f;
+ float sound_max_dist = 1.0e5f;
+
+ type_name[0] = 0;
+ bitmap[0] = 0;
+ particle_bitmap[0] = 0;
+ sound_file[0] = 0;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+
+ if (pdef->name()->value() == "type") {
+ if (pdef->term()->isText()) {
+ GetDefText(type_name, pdef, filename);
+
+ const char* names[15] = {
+ "SHIELD_FLASH",
+ "HULL_FLASH",
+ "BEAM_FLASH",
+ "SHOT_BLAST",
+ "HULL_BURST",
+ "HULL_FIRE",
+ "PLASMA_LEAK",
+ "SMOKE_TRAIL",
+ "SMALL_FIRE",
+ "SMALL_EXPLOSION",
+ "LARGE_EXPLOSION",
+ "LARGE_BURST",
+ "NUKE_EXPLOSION",
+ "QUANTUM_FLASH",
+ "HYPER_FLASH"
+ };
+
+ for (int n = 0; n < 15; n++)
+ if (!stricmp(type_name, names[n]))
+ type = n + 1;
+ }
+
+ else if (pdef->term()->isNumber()) {
+ GetDefNumber(type, pdef, filename);
+
+ if (type < 0 || type >= MAX_EXPLOSION_TYPES) {
+ ::Print("Warning - invalid explosion type %d ignored\n", type);
+ }
+ }
+
+ else {
+ ::Print("Warning - weird explosion type:\n");
+ pdef->print();
+ }
+ }
+
+ else if (pdef->name()->value() == "image" ||
+ pdef->name()->value() == "bitmap")
+ GetDefText(bitmap, pdef, filename);
+
+ else if (pdef->name()->value() == "particles" ||
+ pdef->name()->value() == "particle_bitmap")
+ GetDefText(particle_bitmap, pdef, filename);
+
+ else if (pdef->name()->value() == "sound")
+ GetDefText(sound_file, pdef, filename);
+
+ else if (pdef->name()->value() == "lifetime")
+ GetDefNumber(lifetime, pdef, filename);
+
+ else if (pdef->name()->value() == "scale")
+ GetDefNumber(scale, pdef, filename);
+
+ else if (pdef->name()->value() == "length")
+ GetDefNumber(length, pdef, filename);
+
+ else if (pdef->name()->value() == "light_level")
+ GetDefNumber(light_level, pdef, filename);
+
+ else if (pdef->name()->value() == "light_decay")
+ GetDefNumber(light_decay, pdef, filename);
+
+ else if (pdef->name()->value() == "light_color")
+ GetDefColor(light_color, pdef, filename);
+
+ else if (pdef->name()->value() == "num_parts")
+ GetDefNumber(num_part, pdef, filename);
+
+ else if (pdef->name()->value() == "part_frames")
+ GetDefNumber(part_nframes, pdef, filename);
+
+ else if (pdef->name()->value() == "part_type")
+ GetDefNumber(part_type, pdef, filename);
+
+ else if (pdef->name()->value() == "part_speed")
+ GetDefNumber(part_speed, pdef, filename);
+
+ else if (pdef->name()->value() == "part_drag")
+ GetDefNumber(part_drag, pdef, filename);
+
+ else if (pdef->name()->value() == "part_scale")
+ GetDefNumber(part_scale, pdef, filename);
+
+ else if (pdef->name()->value() == "part_bloom")
+ GetDefNumber(part_bloom, pdef, filename);
+
+ else if (pdef->name()->value() == "part_decay")
+ GetDefNumber(part_decay, pdef, filename);
+
+ else if (pdef->name()->value() == "part_rate")
+ GetDefNumber(part_rate, pdef, filename);
+
+ else if (pdef->name()->value() == "part_trail")
+ GetDefBool(part_trail, pdef, filename);
+
+ else if (pdef->name()->value() == "part_alpha")
+ GetDefNumber(part_alpha, pdef, filename);
+
+ else if (pdef->name()->value() == "continuous")
+ GetDefBool(continuous, pdef, filename);
+
+ else if (pdef->name()->value() == "sound_min_dist")
+ GetDefNumber(sound_min_dist, pdef, filename);
+
+ else if (pdef->name()->value() == "sound_max_dist")
+ GetDefNumber(sound_max_dist, pdef, filename);
+
+ else {
+ Print("WARNING: parameter '%s' ignored in '%s'\n",
+ pdef->name()->value().data(), filename);
+ }
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ val->elements()->at(i)->print();
+ }
+ }
+
+ if (type >= 0 && type < MAX_EXPLOSION_TYPES) {
+ if (part_alpha > 2)
+ part_alpha = 4;
+
+ lengths[type] = length;
+ lifetimes[type] = lifetime;
+ scales[type] = scale;
+ light_levels[type] = light_level;
+ light_decays[type] = light_decay;
+ light_colors[type] = light_color;
+ num_parts[type] = num_part;
+ part_types[type] = part_type;
+ part_speeds[type] = part_speed;
+ part_drags[type] = part_drag;
+ part_scales[type] = part_scale;
+ part_blooms[type] = part_bloom;
+ part_frames[type] = part_nframes;
+ part_decays[type] = part_decay;
+ part_rates[type] = part_rate;
+ part_trails[type] = part_trail;
+ part_alphas[type] = part_alpha;
+ recycles[type] = continuous;
+
+ if (length > 0) {
+ bitmaps[type] = new(__FILE__,__LINE__) Bitmap[length];
+
+ if (length > 1) {
+ for (int n = 0; n < length; n++) {
+ char img_name[64];
+ sprintf(img_name, "%s%02d.pcx", bitmap, n);
+ loader->LoadBitmap(img_name, bitmaps[type][n], Bitmap::BMP_TRANSLUCENT);
+ }
+ }
+
+ else {
+ loader->LoadBitmap(bitmap, bitmaps[type][0], Bitmap::BMP_TRANSLUCENT);
+ }
+ }
+
+ if (particle_bitmap[0]) {
+ particle_bitmaps[type] = new(__FILE__,__LINE__) Bitmap[part_nframes];
+
+ if (part_nframes > 1) {
+ for (int i = 0; i < part_nframes; i++) {
+ char fname[64];
+ sprintf(fname, "%s%02d.pcx", particle_bitmap, i);
+ loader->LoadBitmap(fname, particle_bitmaps[type][i], Bitmap::BMP_TRANSLUCENT);
+ particle_bitmaps[type][i].MakeTexture();
+ }
+ }
+
+ else {
+ loader->LoadBitmap(particle_bitmap, particle_bitmaps[type][0], Bitmap::BMP_TRANSLUCENT);
+ particle_bitmaps[type][0].MakeTexture();
+ }
+ }
+
+ if (sound_file[0]) {
+ loader->SetDataPath("Sounds/");
+ loader->LoadSound(sound_file, sounds[type], Sound::LOCALIZED | Sound::LOC_3D);
+ loader->SetDataPath("Explosions/");
+
+ if (sounds[type]) {
+ sounds[type]->SetMinDistance(sound_min_dist);
+ sounds[type]->SetMaxDistance(sound_max_dist);
+ }
+ }
+ }
+ }
+ }
+
+ else
+ Print("WARNING: unknown definition '%s' in '%s'\n",
+ def->name()->value().data(), filename);
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ term->print();
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+ loader->SetDataPath(0);
+ initialized = 1;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Explosion::Close()
+{
+ for (int t = 0; t < MAX_EXPLOSION_TYPES; t++) {
+ if (lengths[t] && bitmaps[t]) {
+ for (int i = 0; i < lengths[t]; i++)
+ bitmaps[t][i].ClearImage();
+ delete [] bitmaps[t];
+ }
+
+ if (particle_bitmaps[t]) {
+ for (int i = 0; i < part_frames[t]; i++)
+ particle_bitmaps[t][i].ClearImage();
+ delete [] particle_bitmaps[t];
+ }
+
+ delete sounds[t];
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Explosion::ExecFrame(double seconds)
+{
+ if (source) {
+ const Matrix& orientation = source->Cam().Orientation();
+ MoveTo((mount_rel * orientation) + source->Location());
+
+ if (rep) rep->Show();
+
+ if (particles) particles->Show();
+
+ if (source == Sim::GetSim()->GetPlayerShip()) {
+ Ship* ship = (Ship*) source;
+ if (CameraDirector::GetCameraMode() == CameraDirector::MODE_COCKPIT &&
+ !ship->IsDying()) {
+ if (rep) rep->Hide();
+ if (particles) particles->Hide();
+ }
+ }
+ }
+
+ life -= seconds;
+
+ if (rep) {
+ rep->MoveTo(Location());
+
+ if (rep->Life() == 0) {
+ rep = 0; // about to be GC'd
+ }
+
+ else if (rep->IsSprite()) {
+ Sprite* s = (Sprite*) rep;
+ s->SetAngle(s->Angle() + seconds * 0.5);
+ }
+
+ else if (type == QUANTUM_FLASH) {
+ QuantumFlash* q = (QuantumFlash*) rep;
+ q->SetShade(q->Shade() - seconds);
+ }
+ }
+
+ if (light) {
+ light->Update();
+
+ if (light->Life() == 0)
+ light = 0; // about to be GC'd
+ }
+
+ if (particles) {
+ particles->MoveTo(Location());
+ particles->ExecFrame(seconds);
+ }
+
+ if (source && source->Life() == 0)
+ life = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Explosion::Activate(Scene& scene)
+{
+ bool filter = false;
+
+ CameraDirector* cam_dir = CameraDirector::GetInstance();
+ if (cam_dir && cam_dir->GetCamera()) {
+ if (Point(cam_dir->GetCamera()->Pos() - Location()).length() < 100)
+ filter = true;
+ }
+
+ if (rep && !filter)
+ scene.AddGraphic(rep);
+
+ if (light)
+ scene.AddLight(light);
+
+ if (particles && !filter)
+ scene.AddGraphic(particles);
+
+ if (sounds[obj_type]) {
+ Sound* sound = sounds[obj_type]->Duplicate();
+
+ // fire and forget:
+ if (sound) {
+ sound->SetLocation(Location());
+ sound->SetVolume(AudioConfig::EfxVolume());
+ sound->Play();
+ }
+ }
+
+ active = true;
+}
+
+void
+Explosion::Deactivate(Scene& scene)
+{
+ SimObject::Deactivate(scene);
+
+ if (particles)
+ scene.DelGraphic(particles);
+}
+
+// +--------------------------------------------------------------------+
+
diff --git a/Stars45/Explosion.h b/Stars45/Explosion.h
new file mode 100644
index 0000000..baab278
--- /dev/null
+++ b/Stars45/Explosion.h
@@ -0,0 +1,84 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Explosion.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Explosion Sprite class
+*/
+
+#ifndef Explosion_h
+#define Explosion_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "SimObject.h"
+#include "Sound.h"
+
+// +--------------------------------------------------------------------+
+
+class Solid;
+class Particles;
+class System;
+
+// +--------------------------------------------------------------------+
+
+class Explosion : public SimObject,
+ public SimObserver
+{
+public:
+ static const char* TYPENAME() { return "Explosion"; }
+
+ enum Type { SHIELD_FLASH = 1,
+ HULL_FLASH = 2,
+ BEAM_FLASH = 3,
+ SHOT_BLAST = 4,
+ HULL_BURST = 5,
+ HULL_FIRE = 6,
+ PLASMA_LEAK = 7,
+ SMOKE_TRAIL = 8,
+ SMALL_FIRE = 9,
+ SMALL_EXPLOSION = 10,
+ LARGE_EXPLOSION = 11,
+ LARGE_BURST = 12,
+ NUKE_EXPLOSION = 13,
+ QUANTUM_FLASH = 14,
+ HYPER_FLASH = 15
+ };
+
+ Explosion(int type, const Vec3& pos, const Vec3& vel,
+ float exp_scale, float part_scale,
+ SimRegion* rgn=0, SimObject* source=0);
+ virtual ~Explosion();
+
+ static void Initialize();
+ static void Close();
+
+ virtual void ExecFrame(double seconds);
+ Particles* GetParticles() { return particles; }
+
+ virtual void Activate(Scene& scene);
+ virtual void Deactivate(Scene& scene);
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+protected:
+ int type;
+ Particles* particles;
+
+ float scale;
+ float scale1;
+ float scale2;
+
+ SimObject* source;
+ Point mount_rel;
+};
+
+#endif Explosion_h
+
diff --git a/Stars45/Farcaster.cpp b/Stars45/Farcaster.cpp
new file mode 100644
index 0000000..b0777ff
--- /dev/null
+++ b/Stars45/Farcaster.cpp
@@ -0,0 +1,288 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Farcaster.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "Farcaster.h"
+#include "QuantumDrive.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Explosion.h"
+#include "Sim.h"
+#include "Element.h"
+#include "Instruction.h"
+
+#include "Game.h"
+#include "Solid.h"
+#include "Light.h"
+#include "Sound.h"
+#include "DataLoader.h"
+
+// +======================================================================+
+
+Farcaster::Farcaster(double cap, double rate)
+ : System(FARCASTER, 0, "Farcaster", 1, (float) cap, (float) cap, (float) rate),
+ ship(0), dest(0), jumpship(0), cycle_time(10),
+ active_state(QuantumDrive::ACTIVE_READY), warp_fov(1), no_dest(false)
+{
+ name = Game::GetText("sys.farcaster");
+ abrv = Game::GetText("sys.farcaster.abrv");
+}
+
+// +----------------------------------------------------------------------+
+
+Farcaster::Farcaster(const Farcaster& s)
+ : System(s),
+ ship(0), dest(0), start_rel(s.start_rel),
+ end_rel(s.end_rel), jumpship(0), cycle_time(s.cycle_time),
+ active_state(QuantumDrive::ACTIVE_READY), warp_fov(1), no_dest(false)
+{
+ Mount(s);
+ SetAbbreviation(s.Abbreviation());
+
+ for (int i = 0; i < NUM_APPROACH_PTS; i++)
+ approach_rel[i] = s.approach_rel[i];
+}
+
+// +--------------------------------------------------------------------+
+
+Farcaster::~Farcaster()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Farcaster::ExecFrame(double seconds)
+{
+ System::ExecFrame(seconds);
+
+ if (ship && !no_dest) {
+ if (!dest) {
+ Element* elem = ship->GetElement();
+
+ if (elem->NumObjectives()) {
+ Sim* sim = Sim::GetSim();
+ Instruction* obj = elem->GetObjective(0);
+
+ if (obj)
+ dest = sim->FindShip(obj->TargetName());
+ }
+
+ if (!dest)
+ no_dest = true;
+ }
+ else {
+ if (dest->IsDying() || dest->IsDead()) {
+ dest = 0;
+ no_dest = true;
+ }
+ }
+ }
+
+ // if no destination, show red nav lights:
+ if (no_dest)
+ energy = 0.0f;
+
+ if (active_state == QuantumDrive::ACTIVE_READY && energy >= capacity &&
+ ship && ship->GetRegion() && dest && dest->GetRegion()) {
+ SimRegion* rgn = ship->GetRegion();
+ SimRegion* dst = dest->GetRegion();
+ ListIter<Ship> s_iter = rgn->Ships();
+
+ jumpship = 0;
+
+ while (++s_iter) {
+ Ship* s = s_iter.value();
+
+ if (s == ship || s->IsStatic() || s->WarpFactor() > 1)
+ continue;
+
+ Point delta = s->Location() - ship->Location();
+
+ // activate:
+ if (delta.length() < 1000) {
+ active_state = QuantumDrive::ACTIVE_PREWARP;
+ jumpship = s;
+ Observe(jumpship);
+ break;
+ }
+ }
+ }
+
+ if (active_state == QuantumDrive::ACTIVE_READY)
+ return;
+
+ if (ship) {
+ bool warping = false;
+
+ if (active_state == QuantumDrive::ACTIVE_PREWARP) {
+ if (warp_fov < 5000) {
+ warp_fov *= 1.5;
+ }
+ else {
+ Jump();
+ }
+
+ warping = true;
+ }
+
+ else if (active_state == QuantumDrive::ACTIVE_POSTWARP) {
+ if (warp_fov > 1) {
+ warp_fov *= 0.75;
+ }
+ else {
+ warp_fov = 1;
+ active_state = QuantumDrive::ACTIVE_READY;
+ }
+
+ warping = true;
+ }
+
+ if (jumpship) {
+ if (warping) {
+ jumpship->SetWarp(warp_fov);
+
+ SimRegion* r = ship->GetRegion();
+ ListIter<Ship> neighbor = r->Ships();
+
+ while (++neighbor) {
+ if (neighbor->IsDropship()) {
+ Ship* s = neighbor.value();
+ Point delta = s->Location() - ship->Location();
+
+ if (delta.length() < 5e3)
+ s->SetWarp(warp_fov);
+ }
+ }
+ }
+ else {
+ warp_fov = 1;
+ jumpship->SetWarp(warp_fov);
+ }
+ }
+ }
+}
+
+void
+Farcaster::Jump()
+{
+ Sim* sim = Sim::GetSim();
+ SimRegion* rgn = ship->GetRegion();
+ SimRegion* dst = dest->GetRegion();
+
+ sim->CreateExplosion(jumpship->Location(), Point(0,0,0),
+ Explosion::QUANTUM_FLASH, 1.0f, 0, rgn);
+ sim->RequestHyperJump(jumpship, dst, dest->Location().OtherHand(), 0, ship, dest);
+
+ energy = 0.0f;
+
+ Farcaster* f = dest->GetFarcaster();
+ if (f) f->Arrive(jumpship);
+
+ active_state = QuantumDrive::ACTIVE_READY;
+ warp_fov = 1;
+ jumpship = 0;
+}
+
+void
+Farcaster::Arrive(Ship* s)
+{
+ energy = 0.0f;
+
+ active_state = QuantumDrive::ACTIVE_POSTWARP;
+ warp_fov = 5000;
+ jumpship = s;
+
+ if (jumpship && jumpship->Velocity().length() < 500) {
+ jumpship->SetVelocity(jumpship->Heading() * 500);
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Farcaster::SetApproachPoint(int i, Point loc)
+{
+ if (i >= 0 && i < NUM_APPROACH_PTS)
+ approach_rel[i] = loc;
+}
+
+void
+Farcaster::SetStartPoint(Point loc)
+{
+ start_rel = loc;
+}
+
+void
+Farcaster::SetEndPoint(Point loc)
+{
+ end_rel = loc;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Farcaster::SetCycleTime(double t)
+{
+ cycle_time = t;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Farcaster::Orient(const Physical* rep)
+{
+ System::Orient(rep);
+
+ Matrix orientation = rep->Cam().Orientation();
+ Point loc = rep->Location();
+
+ start_point = (start_rel * orientation) + loc;
+ end_point = (end_rel * orientation) + loc;
+
+ for (int i = 0; i < NUM_APPROACH_PTS; i++)
+ approach_point[i] = (approach_rel[i] * orientation) + loc;
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+Farcaster::Update(SimObject* obj)
+{
+ if (obj == jumpship) {
+ jumpship->SetWarp(1);
+
+ SimRegion* r = ship->GetRegion();
+ ListIter<Ship> neighbor = r->Ships();
+
+ while (++neighbor) {
+ if (neighbor->IsDropship()) {
+ Ship* s = neighbor.value();
+ Point delta = s->Location() - ship->Location();
+
+ if (delta.length() < 5e3)
+ s->SetWarp(1);
+ }
+ }
+
+ jumpship = 0;
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+Farcaster::GetObserverName() const
+{
+ return Name();
+}
diff --git a/Stars45/Farcaster.h b/Stars45/Farcaster.h
new file mode 100644
index 0000000..d1b922a
--- /dev/null
+++ b/Stars45/Farcaster.h
@@ -0,0 +1,92 @@
+/* Project STARSHATTER
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Farcaster.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#ifndef Farcaster_h
+#define Farcaster_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "System.h"
+#include "SimObject.h"
+#include "text.h"
+
+// +----------------------------------------------------------------------+
+
+class Ship;
+class ShipDesign;
+class Farcaster;
+
+// +----------------------------------------------------------------------+
+
+class Farcaster : public System, public SimObserver
+{
+public:
+ Farcaster(double capacity, double sink_rate);
+ Farcaster(const Farcaster& rhs);
+ virtual ~Farcaster();
+
+ enum CONSTANTS { NUM_APPROACH_PTS = 4 };
+
+ virtual void ExecFrame(double seconds);
+ void SetShip(Ship* s) { ship = s; }
+ void SetDest(Ship* d) { dest = d; }
+
+ Point ApproachPoint(int i) const { return approach_point[i]; }
+ Point StartPoint() const { return start_point; }
+ Point EndPoint() const { return end_point; }
+
+ virtual void SetApproachPoint(int i, Point loc);
+ virtual void SetStartPoint(Point loc);
+ virtual void SetEndPoint(Point loc);
+ virtual void SetCycleTime(double time);
+
+ virtual void Orient(const Physical* rep);
+
+ // SimObserver:
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+ // accessors:
+ const Ship* GetShip() const { return ship; }
+ const Ship* GetDest() const { return dest; }
+
+ int ActiveState() const { return active_state; }
+ double WarpFactor() const { return warp_fov; }
+
+protected:
+ virtual void Jump();
+ virtual void Arrive(Ship* s);
+
+ Ship* ship;
+ Ship* dest;
+ Ship* jumpship;
+
+ Point start_rel;
+ Point end_rel;
+ Point approach_rel[NUM_APPROACH_PTS];
+
+ Point start_point;
+ Point end_point;
+ Point approach_point[NUM_APPROACH_PTS];
+
+ double cycle_time;
+ int active_state;
+ double warp_fov;
+
+ bool no_dest;
+};
+
+// +----------------------------------------------------------------------+
+
+#endif Farcaster_h
+
diff --git a/Stars45/FighterAI.cpp b/Stars45/FighterAI.cpp
new file mode 100644
index 0000000..c38679e
--- /dev/null
+++ b/Stars45/FighterAI.cpp
@@ -0,0 +1,1831 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: FighterAI.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Fighter (low-level) Artificial Intelligence class
+*/
+
+#include "MemDebug.h"
+#include "FighterAI.h"
+#include "FighterTacticalAI.h"
+#include "Ship.h"
+#include "Shot.h"
+#include "Sensor.h"
+#include "Element.h"
+#include "ShipDesign.h"
+#include "Instruction.h"
+#include "Weapon.h"
+#include "WeaponGroup.h"
+#include "Drive.h"
+#include "QuantumDrive.h"
+#include "Farcaster.h"
+#include "FlightComp.h"
+#include "FlightDeck.h"
+#include "Hangar.h"
+#include "Sim.h"
+#include "StarSystem.h"
+#include "RadioMessage.h"
+#include "RadioTraffic.h"
+
+#include "Game.h"
+
+static const double TIME_TO_DOCK = 30;
+
+// +----------------------------------------------------------------------+
+
+FighterAI::FighterAI(SimObject* s)
+ : ShipAI(s), brakes(0), drop_state(0), jink_time(0), evading(false),
+ decoy_missile(0), missile_time(0), terrain_warning(false), inbound(0),
+ rtb_code(0), form_up(false), over_threshold(false), time_to_dock(0),
+ go_manual(false)
+{
+ ai_type = FIGHTER;
+ seek_gain = 22;
+ seek_damp = 0.55;
+ brakes = 0;
+ z_shift = 0;
+
+ tactical = new(__FILE__,__LINE__) FighterTacticalAI(this);
+}
+
+
+// +--------------------------------------------------------------------+
+
+FighterAI::~FighterAI()
+{ }
+
+// +--------------------------------------------------------------------+
+
+static double frame_time = 0;
+
+void
+FighterAI::ExecFrame(double s)
+{
+ if (!ship) return;
+
+ evading = false;
+ inbound = ship->GetInbound();
+ missile_time -= s;
+
+ int order = 0;
+
+ if (navpt)
+ order = navpt->Action();
+
+ if (inbound) {
+ form_up = false;
+ rtb_code = 1;
+
+ // CHEAT LANDING:
+ if (inbound->Final() && time_to_dock > 0) {
+ FlightDeck* deck = inbound->GetDeck();
+ if (deck) {
+ Point dst = deck->EndPoint();
+ Point approach = deck->StartPoint() - dst;
+
+ const Ship* carrier = deck->GetCarrier();
+
+ Camera landing_cam;
+ landing_cam.Clone(carrier->Cam());
+ landing_cam.Yaw(deck->Azimuth());
+
+ if (time_to_dock > TIME_TO_DOCK/2) {
+ double lr, lp, lw;
+ double sr, sp, sw;
+
+ landing_cam.Orientation().ComputeEulerAngles(lr, lp, lw);
+ ship->Cam().Orientation().ComputeEulerAngles(sr, sp, sw);
+
+ double nr = sr + s*(lr-sr);
+ double np = sp + s*(lp-sp);
+ double nw = sw + s*(lw-sw)*0.5;
+
+ Camera work;
+ work.Aim(nr,np,nw);
+ landing_cam.Clone(work);
+ }
+
+ ship->CloneCam(landing_cam);
+ ship->MoveTo(dst + approach * (time_to_dock / TIME_TO_DOCK));
+ ship->SetVelocity(carrier->Velocity() + ship->Heading() * 50);
+ ship->SetThrottle(50);
+ ship->ExecFLCSFrame();
+
+ time_to_dock -= s;
+
+ if (time_to_dock <= 0) {
+ deck->Dock(ship);
+ time_to_dock = 0;
+ }
+
+ return;
+ }
+ }
+
+ else if (ship->GetFlightPhase() == Ship::DOCKING) {
+ // deal with (pathological) moving carrier deck:
+
+ FlightDeck* deck = inbound->GetDeck();
+ if (deck) {
+ Point dst = deck->EndPoint();
+
+ if (ship->IsAirborne()) {
+ double alt = dst.y;
+ dst = ship->Location();
+ dst.y = alt;
+ }
+
+ const Ship* carrier = deck->GetCarrier();
+
+ Camera landing_cam;
+ landing_cam.Clone(carrier->Cam());
+ landing_cam.Yaw(deck->Azimuth());
+
+ ship->CloneCam(landing_cam);
+ ship->MoveTo(dst);
+
+ if (!ship->IsAirborne()) {
+ ship->SetVelocity(carrier->Velocity());
+ }
+ else {
+ Point taxi(landing_cam.vpn());
+ ship->SetVelocity(taxi * 95);
+ }
+
+ ship->SetThrottle(0);
+ ship->ExecFLCSFrame();
+ }
+
+ return;
+ }
+ }
+ else {
+ Instruction* orders = ship->GetRadioOrders();
+
+ if (orders &&
+ (orders->Action() == RadioMessage::WEP_HOLD ||
+ orders->Action() == RadioMessage::FORM_UP)) {
+ form_up = true;
+ rtb_code = 0;
+ }
+ else {
+ form_up = false;
+ }
+ }
+
+ if (!target && order != Instruction::STRIKE)
+ ship->SetSensorMode(Sensor::STD);
+
+ ShipAI::ExecFrame(s); // this must be the last line of this method
+
+ // IT IS NOT SAFE TO PLACE CODE HERE
+ // if this class decides to break orbit,
+ // this object will be deleted during
+ // ShipAI::ExecFrame() (which calls
+ // FighterAI::Navigator() - see below)
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FighterAI::FindObjective()
+{
+ distance = 0;
+
+ // ALWAYS complete initial launch navpt:
+ if (!navpt) {
+ navpt = ship->GetNextNavPoint();
+ if (navpt && (navpt->Action() != Instruction::LAUNCH || navpt->Status() == Instruction::COMPLETE))
+ navpt = 0;
+ }
+
+ if (navpt && navpt->Action() == Instruction::LAUNCH) {
+ if (navpt->Status() != Instruction::COMPLETE) {
+ FindObjectiveNavPoint();
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+ ship->SetDirectorInfo(Game::GetText("ai.launch"));
+ return;
+ }
+ else {
+ navpt = 0;
+ }
+ }
+
+ // runway takeoff:
+ else if (takeoff) {
+ obj_w = ship->Location() + ship->Heading() * 10e3;
+ obj_w.y = ship->Location().y + 2e3;
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+ ship->SetDirectorInfo(Game::GetText("ai.takeoff"));
+ return;
+ }
+
+ // approaching a carrier or runway:
+ else if (inbound) {
+ FlightDeck* deck = inbound->GetDeck();
+
+ if (!deck) {
+ objective = Point();
+ return;
+ }
+
+ // initial approach
+ if (inbound->Approach() > 0 || !inbound->Cleared()) {
+ obj_w = deck->ApproachPoint(inbound->Approach()) + inbound->Offset();
+
+ distance = (obj_w - ship->Location()).length();
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+ ship->SetDirectorInfo(Game::GetText("ai.inbound"));
+
+ return;
+ }
+
+ // final approach
+ else {
+ ship->SetDirectorInfo(Game::GetText("ai.finals"));
+
+ obj_w = deck->StartPoint();
+ if (inbound->Final()) {
+ obj_w = deck->EndPoint();
+
+ if (deck->OverThreshold(ship)) {
+ obj_w = deck->MountLocation();
+ over_threshold = true;
+ }
+ }
+
+ distance = (obj_w - ship->Location()).length();
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+
+ return;
+ }
+ }
+
+ // not inbound yet, check for RTB order:
+ else {
+ Instruction* orders = (Instruction*) ship->GetRadioOrders();
+ int action = 0;
+
+ if (orders)
+ action = orders->Action();
+
+ if (navpt && !action) {
+ FindObjectiveNavPoint();
+ if (distance < 5e3) {
+ action = navpt->Action();
+ }
+ }
+
+ if (action == RadioMessage::RTB ||
+ action == RadioMessage::DOCK_WITH) {
+
+ Ship* controller = ship->GetController();
+
+ if (orders && orders->Action() == RadioMessage::DOCK_WITH && orders->GetTarget()) {
+ controller = (Ship*) orders->GetTarget();
+ }
+
+ else if (navpt && navpt->Action() == RadioMessage::DOCK_WITH && navpt->GetTarget()) {
+ controller = (Ship*) navpt->GetTarget();
+ }
+
+ ReturnToBase(controller);
+
+ if (rtb_code)
+ return;
+ }
+ }
+
+ ShipAI::FindObjective();
+}
+
+void
+FighterAI::ReturnToBase(Ship* controller)
+{
+ rtb_code = 0;
+
+ if (controller) {
+ SimRegion* self_rgn = ship->GetRegion();
+ SimRegion* rtb_rgn = controller->GetRegion();
+
+ if (self_rgn && !rtb_rgn) {
+ rtb_rgn = self_rgn;
+ }
+
+ if (self_rgn && rtb_rgn && self_rgn != rtb_rgn) {
+ // is the carrier in orbit above us
+ // (or on the ground below us)?
+
+ if (rtb_rgn->GetOrbitalRegion()->Primary() ==
+ self_rgn->GetOrbitalRegion()->Primary()) {
+
+ Point npt = rtb_rgn->Location() - self_rgn->Location();
+ obj_w = npt.OtherHand();
+
+ // distance from self to navpt:
+ distance = Point(obj_w - ship->Location()).length();
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+
+ if (rtb_rgn->IsAirSpace()) {
+ drop_state = -1;
+ }
+ else if (rtb_rgn->IsOrbital()) {
+ drop_state = 1;
+ }
+
+ rtb_code = 2;
+ }
+
+ // try to find a jumpgate that will take us home:
+ else {
+ QuantumDrive* qdrive = ship->GetQuantumDrive();
+ bool use_farcaster = !qdrive ||
+ !qdrive->IsPowerOn() ||
+ qdrive->Status() < System::DEGRADED;
+
+ if (use_farcaster) {
+ if (!farcaster) {
+ ListIter<Ship> s = self_rgn->Ships();
+ while (++s && !farcaster) {
+ if (s->GetFarcaster()) {
+ const Ship* dest = s->GetFarcaster()->GetDest();
+ if (dest && dest->GetRegion() == rtb_rgn) {
+ farcaster = s->GetFarcaster();
+ }
+ }
+ }
+ }
+
+ if (farcaster) {
+ Point apt = farcaster->ApproachPoint(0);
+ Point npt = farcaster->StartPoint();
+ double r1 = (ship->Location() - npt).length();
+
+ if (r1 > 50e3) {
+ obj_w = apt;
+ distance = r1;
+ objective = Transform(obj_w);
+ }
+
+ else {
+ double r2 = (ship->Location() - apt).length();
+ double r3 = (npt - apt).length();
+
+ if (r1+r2 < 1.2*r3) {
+ obj_w = npt;
+ distance = r1;
+ objective = Transform(obj_w);
+ }
+ else {
+ obj_w = apt;
+ distance = r2;
+ objective = Transform(obj_w);
+ }
+ }
+
+ rtb_code = 3;
+ }
+
+ // can't find a way back home, ignore the RTB order:
+ else {
+ ship->ClearRadioOrders();
+ rtb_code = 0;
+ return;
+ }
+ }
+ else if (qdrive) {
+ if (qdrive->ActiveState() == QuantumDrive::ACTIVE_READY) {
+ qdrive->SetDestination(rtb_rgn, controller->Location());
+ qdrive->Engage();
+ }
+
+ rtb_code = 3;
+ }
+ }
+ }
+
+ else {
+ obj_w = controller->Location();
+
+ distance = (obj_w - ship->Location()).length();
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+ ship->SetDirectorInfo(Game::GetText("ai.return-to-base"));
+
+ rtb_code = 1;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FighterAI::FindObjectiveNavPoint()
+{
+ SimRegion* self_rgn = ship->GetRegion();
+ SimRegion* nav_rgn = navpt->Region();
+
+ if (self_rgn && !nav_rgn) {
+ nav_rgn = self_rgn;
+ navpt->SetRegion(nav_rgn);
+ }
+
+ if (self_rgn && nav_rgn && self_rgn != nav_rgn) {
+ if (nav_rgn->GetOrbitalRegion()->Primary() ==
+ self_rgn->GetOrbitalRegion()->Primary()) {
+
+ Point npt = nav_rgn->Location() - self_rgn->Location();
+ obj_w = npt.OtherHand();
+
+ // distance from self to navpt:
+ distance = Point(obj_w - ship->Location()).length();
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+
+ if (nav_rgn->IsAirSpace()) {
+ drop_state = -1;
+ }
+ else if (nav_rgn->IsOrbital()) {
+ drop_state = 1;
+ }
+
+ return;
+ }
+
+ else {
+ QuantumDrive* q = ship->GetQuantumDrive();
+
+ if (q) {
+ if (q->ActiveState() == QuantumDrive::ACTIVE_READY) {
+ q->SetDestination(navpt->Region(), navpt->Location());
+ q->Engage();
+ return;
+ }
+ }
+ }
+ }
+
+ ShipAI::FindObjectiveNavPoint();
+}
+
+// +--------------------------------------------------------------------+
+
+Point
+FighterAI::ClosingVelocity()
+{
+ if (ship) {
+ WeaponDesign* wep_design = ship->GetPrimaryDesign();
+
+ if (target && wep_design) {
+ Point aim_vec = ship->Heading();
+ aim_vec.Normalize();
+
+ Point shot_vel = ship->Velocity() + aim_vec * wep_design->speed;
+ return shot_vel - target->Velocity();
+ }
+
+ else if (target) {
+ return ship->Velocity() - target->Velocity();
+ }
+
+ else {
+ return ship->Velocity();
+ }
+ }
+
+ return Point(1,0,0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FighterAI::Navigator()
+{
+ go_manual = false;
+
+ if (takeoff) {
+ accumulator.Clear();
+ magnitude = 0;
+ brakes = 0;
+ z_shift = 0;
+
+ Accumulate(SeekTarget());
+ HelmControl();
+ ThrottleControl();
+ ship->ExecFLCSFrame();
+ return;
+ }
+
+ Element* elem = ship->GetElement();
+
+ if (elem) {
+ Ship* lead = elem->GetShip(1);
+
+ if (lead && lead != ship) {
+ if (lead->IsDropping() && !ship->IsDropping()) {
+ ship->DropOrbit();
+ // careful: this object has just been deleted!
+ return;
+ }
+
+ if (lead->IsAttaining() && !ship->IsAttaining()) {
+ ship->MakeOrbit();
+ // careful: this object has just been deleted!
+ return;
+ }
+ }
+
+ else {
+ if (drop_state < 0) {
+ ship->DropOrbit();
+ // careful: this object has just been deleted!
+ return;
+ }
+
+ if (drop_state > 0) {
+ ship->MakeOrbit();
+ // careful: this object has just been deleted!
+ return;
+ }
+ }
+ }
+
+ int order = 0;
+
+ if (navpt)
+ order = navpt->Action();
+
+ if (rtb_code == 1 && navpt && navpt->Status() < Instruction::SKIPPED &&
+ !inbound && distance < 35e3) { // (this should be distance to the ship)
+
+ if (order == Instruction::RTB) {
+ Ship* controller = ship->GetController();
+ Hangar* hangar = controller ? controller->GetHangar() : 0;
+
+ if (hangar && hangar->CanStow(ship)) {
+ for (int i = 0; i < elem->NumShips(); i++) {
+ Ship* s = elem->GetShip(i+1);
+
+ if (s && s->GetDirector() && s->GetDirector()->Type() >= ShipAI::FIGHTER)
+ RadioTraffic::SendQuickMessage(s, RadioMessage::CALL_INBOUND);
+ }
+
+ if (element_index == 1)
+ ship->SetNavptStatus(navpt, Instruction::COMPLETE);
+ }
+
+ else {
+ if (element_index == 1) {
+ ::Print("WARNING: FighterAI NAVPT RTB, but no controller or hangar found for ship '%s'\n", ship->Name());
+ ship->SetNavptStatus(navpt, Instruction::SKIPPED);
+ }
+ }
+ }
+
+ else {
+ Ship* dock_target = (Ship*) navpt->GetTarget();
+ if (dock_target) {
+ for (int i = 0; i < elem->NumShips(); i++) {
+ Ship* s = elem->GetShip(i+1);
+
+ if (s) {
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(dock_target, s, RadioMessage::CALL_INBOUND);
+ RadioTraffic::Transmit(msg);
+ }
+ }
+
+ if (element_index == 1)
+ ship->SetNavptStatus(navpt, Instruction::COMPLETE);
+ }
+
+ else {
+ if (element_index == 1) {
+ ::Print("WARNING: FighterAI NAVPT DOCK, but no dock target found for ship '%s'\n", ship->Name());
+ ship->SetNavptStatus(navpt, Instruction::SKIPPED);
+ }
+ }
+ }
+ }
+
+ if (target)
+ ship->SetDirectorInfo(Game::GetText("ai.seek-target"));
+
+ accumulator.Clear();
+ magnitude = 0;
+ brakes = 0;
+ z_shift = 0;
+
+ hold = false;
+ if ((ship->GetElement() && ship->GetElement()->GetHoldTime() > 0) ||
+ (navpt && navpt->Status() == Instruction::COMPLETE && navpt->HoldTime() > 0))
+ hold = true;
+
+ if (ship->MissionClock() < 10000) {
+ if (ship->IsAirborne())
+ Accumulate(SeekTarget());
+ }
+
+ else if ((farcaster && distance < 20e3) || (inbound && inbound->Final())) {
+ Accumulate(SeekTarget());
+ }
+
+ else {
+ if (!ship->IsAirborne() || ship->AltitudeAGL() > 100)
+ ship->RaiseGear();
+
+ Accumulate(AvoidTerrain());
+ Steer avoid = AvoidCollision();
+
+ if (other && inbound && inbound->GetDeck() && inbound->Cleared()) {
+ if (other != (SimObject*) inbound->GetDeck()->GetCarrier())
+ Accumulate(avoid);
+ }
+ else {
+ Accumulate(avoid);
+ }
+
+ if (!too_close && !hold && !terrain_warning) {
+ Accumulate(SeekTarget());
+ Accumulate(EvadeThreat());
+ }
+ }
+
+ HelmControl();
+ ThrottleControl();
+ FireControl();
+ AdjustDefenses();
+
+ ship->ExecFLCSFrame();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FighterAI::HelmControl()
+{
+ Camera* cam = ((Camera*) &(ship->Cam()));
+ Point vrt = cam->vrt();
+ double deflection = vrt.y;
+ double theta = 0;
+ bool formation = element_index > 1;
+ bool station_keeping = distance < 0;
+ bool inverted = cam->vup().y < -0.5;
+ Ship* ward = ship->GetWard();
+
+ if (takeoff || inbound || station_keeping)
+ formation = false;
+
+ if (takeoff || navpt || farcaster || patrol || inbound || rtb_code || target || ward || threat || formation) {
+ // are we being asked to flee?
+ if (fabs(accumulator.yaw) == 1.0 && accumulator.pitch == 0.0) {
+ accumulator.pitch = -0.7f;
+ accumulator.yaw *= 0.25f;
+
+ if (ship->IsAirborne() && ship->GetFlightModel() == 0)
+ accumulator.pitch = -0.45f;
+
+ // low ai -> lower turning rate
+ accumulator.pitch += 0.1f * (2-ai_level);
+ }
+
+ ship->ApplyRoll((float) (accumulator.yaw * -0.7));
+ ship->ApplyYaw((float) (accumulator.yaw * 0.2));
+
+ if (fabs(accumulator.yaw) > 0.5 && fabs(accumulator.pitch) < 0.1)
+ accumulator.pitch -= 0.1f;
+
+ ship->ApplyPitch((float) accumulator.pitch);
+ }
+
+ else {
+ ship->SetDirectorInfo(Game::GetText("ai.station-keeping"));
+ station_keeping = true;
+
+ // go into a slow orbit if airborne:
+ if (ship->IsAirborne() && ship->Class() < Ship::LCA) {
+ accumulator.brake = 0.2;
+ accumulator.stop = 0;
+
+ double compass_pitch = ship->CompassPitch();
+ double desired_bank = -PI/4;
+ double current_bank = asin(deflection);
+ double theta = desired_bank - current_bank;
+ ship->ApplyRoll(theta);
+
+ double coord_pitch = compass_pitch - 0.2 * fabs(current_bank);
+ ship->ApplyPitch(coord_pitch);
+ }
+ else {
+ accumulator.brake = 1;
+ accumulator.stop = 1;
+ }
+ }
+
+ // if not turning, roll to orient with world coords:
+ if (ship->Design()->auto_roll > 0) {
+ if (fabs(accumulator.pitch) < 0.1 && fabs(accumulator.yaw) < 0.25) {
+ // zolon spiral behavior:
+ if (ship->Design()->auto_roll > 1) {
+ if ((element_index + (ship->MissionClock()>>10)) & 0x4)
+ ship->ApplyRoll( 0.60);
+ else
+ ship->ApplyRoll(-0.35);
+ }
+
+ // normal behavior - roll to upright:
+ else if (fabs(deflection) > 0.1 || inverted) {
+ double theta = asin(deflection/vrt.length()) * 0.5;
+ ship->ApplyRoll(-theta);
+ }
+ }
+ }
+
+ // if not otherwise occupied, pitch to orient with world coords:
+ if (station_keeping && (!ship->IsAirborne() || ship->Class() < Ship::LCA)) {
+ Point heading = ship->Heading();
+ double pitch_deflection = heading.y;
+
+ if (fabs(pitch_deflection) > 0.05) {
+ double rho = asin(pitch_deflection) * 3;
+ ship->ApplyPitch(rho);
+ }
+ }
+
+ ship->SetTransX(0);
+ ship->SetTransY(0);
+ ship->SetTransZ(z_shift * ship->Design()->trans_z);
+ ship->SetFLCSMode(go_manual ? Ship::FLCS_MANUAL : Ship::FLCS_AUTO);
+}
+
+void
+FighterAI::ThrottleControl()
+{
+ Element* elem = ship->GetElement();
+ double ship_speed = ship->Velocity() * ship->Heading();
+ double desired = 1000;
+ bool formation = element_index > 1;
+ bool station_keeping = distance < 0;
+ bool augmenter = false;
+ Ship* ward = ship->GetWard();
+
+ if (inbound || station_keeping)
+ formation = false;
+
+ // LAUNCH / TAKEOFF
+ if (ship->MissionClock() < 10000) {
+ formation = false;
+ throttle = 100;
+ brakes = 0;
+ }
+
+ // STATION KEEPING
+ else if (station_keeping) {
+ // go into a slow orbit if airborne:
+ if (ship->IsAirborne() && ship->Class() < Ship::LCA) {
+ throttle = 30;
+ brakes = 0;
+ }
+ else {
+ throttle = 0;
+ brakes = 1;
+ }
+ }
+
+ // TRY TO STAY AIRBORNE, YES?
+ else if (ship->IsAirborne() && ship_speed < 250 && ship->Class() < Ship::LCA) {
+ throttle = 100;
+ brakes = 0;
+
+ if (ship_speed < 200)
+ augmenter = true;
+ }
+
+ // INBOUND
+ else if (inbound) {
+ double carrier_speed = inbound->GetDeck()->GetCarrier()->Velocity().length();
+ desired = 250 + carrier_speed;
+
+ if (distance > 25.0e3)
+ desired = 750 + carrier_speed;
+
+ else if (ship->IsAirborne())
+ desired = 300;
+
+ else if (inbound->Final())
+ desired = 75 + carrier_speed;
+
+ throttle = 0;
+
+ // holding short?
+ if (inbound->Approach() == 0 && !inbound->Cleared() &&
+ distance < 2000 && !ship->IsAirborne())
+ desired = 0;
+
+ if (ship_speed > desired+5)
+ brakes = 0.25;
+
+ else if (ship->IsAirborne() || Ship::GetFlightModel() > 0) {
+ throttle = old_throttle + 1;
+ }
+
+ else if (ship_speed < 0.85 * desired) {
+ throttle = 100;
+
+ if (ship_speed < 0 && ship->GetFuelLevel() > 10)
+ augmenter = true;
+ }
+
+ else if (ship_speed < desired-5) {
+ throttle = 30;
+ }
+ }
+
+ else if (rtb_code || farcaster) {
+ desired = 750;
+
+ if (threat || threat_missile) {
+ throttle = 100;
+
+ if (!threat_missile && ship->GetFuelLevel() > 15)
+ augmenter = true;
+ }
+
+ else {
+ throttle = 0;
+
+ if (ship_speed > desired+5)
+ brakes = 0.25;
+
+ else if (Ship::GetFlightModel() > 0) {
+ throttle = old_throttle + 1;
+ }
+
+ else if (ship_speed < 0.85 * desired) {
+ throttle = 100;
+
+ if (ship_speed < 0 && ship->GetFuelLevel() > 10)
+ augmenter = true;
+ }
+
+ else if (ship_speed < desired-5) {
+ throttle = 30;
+ }
+ }
+ }
+
+ // RUN AWAY!!!
+ else if (evading) {
+ throttle = 100;
+
+ if (!threat_missile && ship->GetFuelLevel() > 15)
+ augmenter = true;
+ }
+
+ // PATROL AND FORMATION
+ else if (!navpt && !target && !ward) {
+ if (!elem || !formation) { // element lead
+ if (patrol) {
+ desired = 250;
+
+ if (distance > 10e3)
+ desired = 750;
+
+ if (ship_speed > desired+5) {
+ brakes = 0.25;
+ throttle = old_throttle - 5;
+ }
+
+ else if (ship_speed < 0.85 * desired) {
+ throttle = 100;
+
+ if (ship_speed < 0 && ship->GetFuelLevel() > 10)
+ augmenter = true;
+ }
+
+ else if (ship_speed < desired-5)
+ throttle = old_throttle + 5;
+ }
+
+ else {
+ throttle = 35;
+
+ if (threat)
+ throttle = 100;
+
+ brakes = accumulator.brake;
+
+ if (brakes > 0.1)
+ throttle = 0;
+ }
+ }
+
+ else { // wingman
+ Ship* lead = elem->GetShip(1);
+ double zone = ship->Radius() * 3;
+
+ if (lead)
+ desired = lead->Velocity() * lead->Heading();
+
+ if (fabs(slot_dist) < distance/4) // try to prevent porpoising
+ throttle = old_throttle;
+
+ else if (slot_dist > zone*2) {
+ throttle = 100;
+
+ if (objective.z > 10e3 && ship_speed < desired && ship->GetFuelLevel() > 25)
+ augmenter = true;
+ }
+
+ else if (slot_dist > zone)
+ throttle = lead->Throttle() + 10;
+
+ else if (slot_dist < -zone*2) {
+ throttle = old_throttle - 10;
+ brakes = 1;
+ }
+
+ else if (slot_dist < -zone) {
+ throttle = old_throttle;
+ brakes = 0.5;
+ }
+
+ else if (lead) {
+ double lv = lead->Velocity().length();
+ double sv = ship_speed;
+ double dv = lv-sv;
+ double dt = 0;
+
+ if (dv > 0) dt = dv * 1e-5 * frame_time;
+ else if (dv < 0) dt = dv * 1e-2 * frame_time;
+
+ throttle = old_throttle + dt;
+ }
+
+ else {
+ throttle = old_throttle;
+ }
+ }
+ }
+
+ // TARGET/WARD/NAVPOINT SEEKING
+ else {
+ throttle = old_throttle;
+
+ if (target) {
+ desired = 1250;
+
+ if (ai_level < 1) {
+ throttle = 70;
+ }
+
+ else if (ship->IsAirborne()) {
+ throttle = 100;
+
+ if (!threat_missile && fabs(objective.z) > 6e3 && ship->GetFuelLevel() > 25)
+ augmenter = true;
+ }
+
+ else {
+ throttle = 100;
+
+ if (objective.z > 20e3 && ship_speed < desired && ship->GetFuelLevel() > 35)
+ augmenter = true;
+
+ else if (objective.z > 0 && objective.z < 10e3)
+ throttle = 50;
+ }
+ }
+
+ else if (ward) {
+ double d = (ship->Location() - ward->Location()).length();
+
+ if (d > 5000) {
+ if (ai_level < 1)
+ throttle = 50;
+ else
+ throttle = 80;
+ }
+ else {
+ double speed = ward->Velocity().length();
+
+ if (speed > 0) {
+ if (ship_speed > speed) {
+ throttle = old_throttle - 5;
+ brakes = 0.25;
+ }
+ else if (ship_speed < speed - 10) {
+ throttle = old_throttle + 1;
+ }
+ }
+ }
+ }
+
+ else if (navpt) {
+ desired = navpt->Speed();
+
+ if (hold) {
+ // go into a slow orbit if airborne:
+ if (ship->IsAirborne() && ship->Class() < Ship::LCA) {
+ throttle = 25;
+ brakes = 0;
+ }
+ else {
+ throttle = 0;
+ brakes = 1;
+ }
+ }
+
+ else if (desired > 0) {
+ if (ship_speed > desired) {
+ throttle = old_throttle - 5;
+ brakes = 0.25;
+ }
+
+ else if (ship_speed < 0.85 * desired) {
+ throttle = 100;
+
+ if ((ship->IsAirborne() || ship_speed < 0.35 * desired) && ship->GetFuelLevel() > 30)
+ augmenter = true;
+ }
+
+ else if (ship_speed < desired - 10) {
+ throttle = old_throttle + 1;
+ }
+
+ else if (Ship::GetFlightModel() > 0) {
+ throttle = old_throttle;
+ }
+ }
+ }
+
+ else {
+ throttle = 0;
+ brakes = 1;
+ }
+ }
+
+ if (ship->IsAirborne() && throttle < 20 && ship->Class() < Ship::LCA)
+ throttle = 20;
+ else if (ship->Design()->auto_roll > 1 && throttle < 5)
+ throttle = 5;
+ else if (throttle < 0)
+ throttle = 0;
+
+ old_throttle = throttle;
+ ship->SetThrottle((int) throttle);
+ ship->SetAugmenter(augmenter);
+
+ if (accumulator.stop && ship->GetFLCS() != 0)
+ ship->GetFLCS()->FullStop();
+
+ else if (ship_speed > 1 && brakes > 0)
+ ship->SetTransY(-brakes * ship->Design()->trans_y);
+
+ else if (throttle > 10 && (ship->GetEMCON() < 2 || ship->GetFuelLevel() < 10))
+ ship->SetTransY(ship->Design()->trans_y);
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+FighterAI::AvoidTerrain()
+{
+ Steer avoid;
+
+ terrain_warning = false;
+
+ if (!ship || !ship->GetRegion() || !ship->GetRegion()->IsActive() ||
+ (navpt && navpt->Action() == Instruction::LAUNCH))
+ return avoid;
+
+ if (ship->IsAirborne() && ship->GetFlightPhase() == Ship::ACTIVE) {
+ // too high?
+ if (ship->AltitudeMSL() > 25e3) {
+ if (!navpt || (navpt->Region() == ship->GetRegion() && navpt->Location().z < 27e3)) {
+ terrain_warning = true;
+ ship->SetDirectorInfo(Game::GetText("ai.too-high"));
+
+ // where will we be?
+ Point selfpt = ship->Location() + ship->Velocity() + Point(0, -15e3, 0);
+
+ // transform into camera coords:
+ Point obj = Transform(selfpt);
+
+ // head down!
+ avoid = Seek(obj);
+ }
+ }
+
+ // too low?
+ else if (ship->AltitudeAGL() < 2500) {
+ terrain_warning = true;
+ ship->SetDirectorInfo(Game::GetText("ai.too-low"));
+
+ // way too low?
+ if (ship->AltitudeAGL() < 1500) {
+ ship->SetDirectorInfo(Game::GetText("ai.way-too-low"));
+ target = 0;
+ drop_time = 5;
+ }
+
+ // where will we be?
+ Point selfpt = ship->Location() + ship->Velocity() + Point(0, 10e3, 0);
+
+ // transform into camera coords:
+ Point obj = Transform(selfpt);
+
+ // pull up!
+ avoid = Seek(obj);
+ }
+ }
+
+ return avoid;
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+FighterAI::SeekTarget()
+{
+ if (ship->GetFlightPhase() < Ship::ACTIVE)
+ return Seek(objective);
+
+ Ship* ward = ship->GetWard();
+
+ if ((!target && !ward && !navpt && !farcaster && !patrol && !inbound && !rtb_code) || ship->MissionClock() < 10000) {
+ if (element_index > 1) {
+ // break formation if threatened:
+ if (threat_missile)
+ return Steer();
+
+ else if (threat && !form_up)
+ return Steer();
+
+ // otherwise, keep in formation:
+ return SeekFormationSlot();
+ }
+ else {
+ return Steer();
+ }
+ }
+
+ if (patrol) {
+ Steer result = Seek(objective);
+ ship->SetDirectorInfo(Game::GetText("ai.seek-patrol-point"));
+
+ if (distance < 10 * self->Radius()) {
+ patrol = 0;
+ result.brake = 1;
+ result.stop = 1;
+ }
+
+ return result;
+ }
+
+ if (inbound) {
+ Steer result = Seek(objective);
+
+ if (over_threshold && objective.z < 0) {
+ result = Steer();
+ result.brake = 1;
+ result.stop = 1;
+ }
+ else {
+ ship->SetDirectorInfo(Game::GetText("ai.seek-inbound"));
+
+ // approach legs:
+ if (inbound->Approach() > 0) {
+ if (distance < 20 * self->Radius())
+ inbound->SetApproach(inbound->Approach() - 1);
+ }
+
+ // marshall point and finals:
+ else {
+ if (inbound->Cleared() && distance < 10 * self->Radius()) {
+ if (!inbound->Final()) {
+ time_to_dock = TIME_TO_DOCK;
+
+ FlightDeck* deck = inbound->GetDeck();
+ if (deck) {
+ double total_dist = Point(deck->EndPoint() - deck->StartPoint()).length();
+ double current_dist = Point(deck->EndPoint() - ship->Location()).length();
+
+ time_to_dock *= (current_dist / total_dist);
+ }
+
+ RadioTraffic::SendQuickMessage(ship, RadioMessage::CALL_FINALS);
+ }
+
+ inbound->SetFinal(true);
+ ship->LowerGear();
+ result.brake = 1;
+ result.stop = 1;
+ }
+
+ else if (!inbound->Cleared() && distance < 2000) {
+ ship->SetDirectorInfo(Game::GetText("ai.hold-final"));
+ result = Steer();
+ result.brake = 1;
+ result.stop = 1;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ else if (rtb_code) {
+ return Seek(objective);
+ }
+
+ SimObject* tgt = target;
+
+ if (ward && !tgt)
+ tgt = ward;
+
+ if (tgt && too_close == tgt->Identity()) {
+ drop_time = 4;
+ return Steer();
+ }
+
+ else if (navpt && navpt->Action() == Instruction::LAUNCH) {
+ ship->SetDirectorInfo(Game::GetText("ai.launch"));
+ return Seek(objective);
+ }
+
+ else if (farcaster) {
+ // wingmen should
+ if (element_index > 1)
+ return SeekFormationSlot();
+
+ ship->SetDirectorInfo(Game::GetText("ai.seek-farcaster"));
+ return Seek(objective);
+ }
+
+ else if (drop_time > 0) {
+ return Steer();
+ }
+
+ if (tgt) {
+ double basis = self->Radius() + tgt->Radius();
+ double gap = distance - basis;
+
+ // target behind:
+ if (objective.z < 0) {
+ // leave some room for an attack run:
+ if (gap < 8000) {
+ Steer s;
+
+ s.pitch = -0.1;
+ if (objective.x > 0) s.yaw = 0.1;
+ else s.yaw = -0.1;
+
+ return s;
+ }
+
+ // start the attack run:
+ else {
+ return Seek(objective);
+ }
+ }
+
+ // target in front:
+ else {
+ if (tgt->Type() == SimObject::SIM_SHIP) {
+ Ship* tgt_ship = (Ship*) tgt;
+
+ // capital target strike:
+ if (tgt_ship->IsStatic()) {
+ if (gap < 2500)
+ return Flee(objective);
+ }
+
+ else if (tgt_ship->IsStarship()) {
+ if (gap < 1000)
+ return Flee(objective);
+
+ else if (ship->GetFlightModel() == Ship::FM_STANDARD && gap < 20e3)
+ go_manual = true;
+ }
+ }
+
+ // fighter melee:
+ if (tgt->Velocity() * ship->Velocity() < 0) {
+ // head-to-head pass:
+ if (gap < 1250)
+ return Flee(objective);
+ }
+
+ else if (gap < 250) {
+ return Steer();
+ }
+
+ ship->SetDirectorInfo(Game::GetText("ai.seek-target"));
+ return Seek(objective);
+ }
+ }
+
+ if (navpt) {
+ ship->SetDirectorInfo(Game::GetText("ai.seek-navpt"));
+ }
+
+ return Seek(objective);
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+FighterAI::SeekFormationSlot()
+{
+ Steer s;
+
+ // advance memory pipeline:
+ az[2] = az[1]; az[1] = az[0];
+ el[2] = el[1]; el[1] = el[0];
+
+ Element* elem = ship->GetElement();
+ Ship* lead = elem->GetShip(1);
+
+ if (lead) {
+ SimRegion* self_rgn = ship->GetRegion();
+ SimRegion* lead_rgn = lead->GetRegion();
+
+ if (self_rgn != lead_rgn) {
+ QuantumDrive* qdrive = ship->GetQuantumDrive();
+ bool use_farcaster = !qdrive ||
+ !qdrive->IsPowerOn() ||
+ qdrive->Status() < System::DEGRADED;
+
+ if (use_farcaster) {
+ FindObjectiveFarcaster(self_rgn, lead_rgn);
+ }
+
+ else if (qdrive) {
+ if (qdrive->ActiveState() == QuantumDrive::ACTIVE_READY) {
+ qdrive->SetDestination(lead_rgn, lead->Location());
+ qdrive->Engage();
+ }
+ }
+ }
+ }
+
+ // do station keeping?
+ if (distance < ship->Radius() * 10 && lead->Velocity().length() < 50) {
+ distance = -1;
+ return s;
+ }
+
+ // approach
+ if (objective.z > ship->Radius() * -4) {
+ az[0] = atan2(fabs(objective.x), objective.z) * 50;
+ el[0] = atan2(fabs(objective.y), objective.z) * 50;
+
+ if (objective.x < 0) az[0] = -az[0];
+ if (objective.y > 0) el[0] = -el[0];
+
+ s.yaw = az[0] - seek_damp * (az[1] + az[2] * 0.5);
+ s.pitch = el[0] - seek_damp * (el[1] + el[2] * 0.5);
+ }
+
+ // reverse
+ else {
+ if (objective.x > 0) s.yaw = 1.0f;
+ else s.yaw = -1.0f;
+
+ s.pitch = -objective.y * 0.5f;
+ }
+
+ seeking = 1;
+ ship->SetDirectorInfo(Game::GetText("ai.seek-formation"));
+
+ return s;
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+FighterAI::Seek(const Point& point)
+{
+ Steer s;
+
+ // advance memory pipeline:
+ az[2] = az[1]; az[1] = az[0];
+ el[2] = el[1]; el[1] = el[0];
+
+ // approach
+ if (point.z > 0.0f) {
+ az[0] = atan2(fabs(point.x), point.z) * seek_gain;
+ el[0] = atan2(fabs(point.y), point.z) * seek_gain;
+
+ if (point.x < 0) az[0] = -az[0];
+ if (point.y > 0) el[0] = -el[0];
+
+ s.yaw = az[0] - seek_damp * (az[1] + az[2] * 0.5);
+ s.pitch = el[0] - seek_damp * (el[1] + el[2] * 0.5);
+
+ // pull up:
+ if (ship->IsAirborne() && point.y > 5e3)
+ s.pitch = -1.0f;
+ }
+
+ // reverse
+ else {
+ if (ship->IsAirborne()) {
+ // pull up:
+ if (point.y > 5e3) {
+ s.pitch = -1.0f;
+ }
+
+ // head down:
+ else if (point.y < -5e3) {
+ s.pitch = 1.0f;
+ }
+
+ // level turn:
+ else {
+ if (point.x > 0) s.yaw = 1.0f;
+ else s.yaw = -1.0f;
+
+ s.brake = 0.5f;
+ }
+ }
+
+ else {
+ if (point.x > 0) s.yaw = 1.0f;
+ else s.yaw = -1.0f;
+ }
+ }
+
+ seeking = 1;
+
+ return s;
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+FighterAI::EvadeThreat()
+{
+ // MISSILE THREAT REACTION:
+ if (threat_missile) {
+ evading = true;
+ SetTarget(0);
+ drop_time = 3 * (3-ai_level);
+
+ // dropped a decoy for this missile yet?
+ if (decoy_missile != threat_missile) {
+ ship->FireDecoy();
+ decoy_missile = threat_missile;
+ }
+
+ // beam the missile
+ ship->SetDirectorInfo(Game::GetText("ai.evade-missile"));
+
+ Point beam_line = threat_missile->Velocity().cross(Point(0,1,0));
+ beam_line.Normalize();
+ beam_line *= 1e6;
+
+ Point evade_p;
+ Point evade_w1 = threat_missile->Location() + beam_line;
+ Point evade_w2 = threat_missile->Location() - beam_line;
+
+ double d1 = Point(evade_w1 - ship->Location()).length();
+ double d2 = Point(evade_w2 - ship->Location()).length();
+
+ if (d1 > d2)
+ evade_p = Transform(evade_w1);
+ else
+ evade_p = Transform(evade_w2);
+
+ return Seek(evade_p);
+ }
+
+ // GENERAL THREAT EVASION:
+ if (threat && !form_up) {
+ double threat_range = 20e3;
+
+ Ship* threat_ship = (Ship*) threat;
+ double threat_dist = Point(threat->Location() - ship->Location()).length();
+
+ if (threat_ship->IsStarship()) {
+ threat_range = CalcDefensePerimeter(threat_ship);
+ }
+
+ if (threat_dist <= threat_range) {
+ ship->SetDirectorInfo(Game::GetText("ai.evade-threat"));
+
+ if (ship->IsAirborne()) {
+ evading = true;
+ Point beam_line = threat->Velocity().cross(Point(0,1,0));
+ beam_line.Normalize();
+ beam_line *= threat_range;
+
+ Point evade_w = threat->Location() + beam_line;
+ Point evade_p = Transform(evade_w);
+
+ return Seek(evade_p);
+ }
+
+ else if (threat_ship->IsStarship()) {
+ evading = true;
+
+ if (target == threat_ship && threat_dist < threat_range / 4) {
+ SetTarget(0);
+ drop_time = 5;
+ }
+
+ if (!target) {
+ ship->SetDirectorInfo(Game::GetText("ai.evade-starship"));
+
+ // flee for three seconds:
+ if ((ship->MissionClock() & 3) != 3) {
+ return Flee(Transform(threat->Location()));
+ }
+
+ // jink for one second:
+ else {
+ if (Game::GameTime() - jink_time > 1500) {
+ jink_time = Game::GameTime();
+ jink = Point(rand() - 16384,
+ rand() - 16384,
+ rand() - 16384) * 15e3;
+ }
+
+ Point evade_w = ship->Location() + jink;
+ Point evade_p = Transform(evade_w);
+
+ return Seek(evade_p);
+ }
+ }
+
+ else {
+ ship->SetDirectorInfo(Game::GetText("ai.evade-and-seek"));
+
+ // seek for three seconds:
+ if ((ship->MissionClock() & 3) < 3) {
+ return Steer(); // no evasion
+ }
+
+ // jink for one second:
+ else {
+ if (Game::GameTime() - jink_time > 1000) {
+ jink_time = Game::GameTime();
+ jink = Point(rand() - 16384,
+ rand() - 16384,
+ rand() - 16384);
+ }
+
+ Point evade_w = target->Location() + jink;
+ Point evade_p = Transform(evade_w);
+
+ return Seek(evade_p);
+ }
+ }
+ }
+
+ else {
+ evading = true;
+
+ if (target == threat) {
+ if (target->Type() == SimObject::SIM_SHIP) {
+ Ship* tgt_ship = (Ship*) target;
+ if (tgt_ship->GetTrigger(0)) {
+ SetTarget(0);
+ drop_time = 3;
+ }
+ }
+ }
+
+ else if (target && threat_dist < threat_range / 2) {
+ SetTarget(0);
+ drop_time = 3;
+ }
+
+ if (target)
+ ship->SetDirectorInfo(Game::GetText("ai.evade-and-seek"));
+ else
+ ship->SetDirectorInfo(Game::GetText("ai.random-evade"));
+
+ // beam the threat
+ Point beam_line = threat->Velocity().cross(Point(0,1,0));
+ beam_line.Normalize();
+ beam_line *= 1e6;
+
+ Point evade_p;
+ Point evade_w1 = threat->Location() + beam_line;
+ Point evade_w2 = threat->Location() - beam_line;
+
+ double d1 = Point(evade_w1 - ship->Location()).length();
+ double d2 = Point(evade_w2 - ship->Location()).length();
+
+ if (d1 > d2)
+ evade_p = Transform(evade_w1);
+ else
+ evade_p = Transform(evade_w2);
+
+ if (!target) {
+ DWORD jink_rate = 400 + 200 * (3-ai_level);
+
+ if (Game::GameTime() - jink_time > jink_rate) {
+ jink_time = Game::GameTime();
+ jink = Point(rand() - 16384,
+ rand() - 16384,
+ rand() - 16384) * 2000;
+ }
+
+ evade_p += jink;
+ }
+
+ Steer steer = Seek(evade_p);
+
+ if (target)
+ return steer / 4;
+
+ return steer;
+ }
+ }
+ }
+
+ return Steer();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FighterAI::FireControl()
+{
+ // if nothing to shoot at, forget it:
+ if (!target || target->Integrity() < 1)
+ return;
+
+ // if the objective is a navpt or landing bay (not a target), then don't shoot!
+ if (inbound || farcaster || navpt && navpt->Action() < Instruction::DEFEND)
+ return;
+
+ // object behind us, or too close:
+ if (objective.z < 0 || distance < 4 * self->Radius())
+ return;
+
+ // compute the firing cone:
+ double cross_section = 2 * target->Radius() / distance;
+ double gun_basket = cross_section * 2;
+
+ Weapon* primary = ship->GetPrimary();
+ Weapon* secondary = ship->GetSecondary();
+ const WeaponDesign* dsgn_primary = 0;
+ const WeaponDesign* dsgn_secondary = 0;
+ bool use_primary = true;
+ Ship* tgt_ship = 0;
+
+ if (target->Type() == SimObject::SIM_SHIP) {
+ tgt_ship = (Ship*) target;
+
+ if (tgt_ship->InTransition())
+ return;
+ }
+
+ if (primary) {
+ dsgn_primary = primary->Design();
+
+ if (dsgn_primary->aim_az_max > 5*DEGREES && distance > dsgn_primary->max_range/2)
+ gun_basket = cross_section * 4;
+
+ gun_basket *= (3-ai_level);
+
+ if (tgt_ship) {
+ if (!primary->CanTarget(tgt_ship->Class()))
+ use_primary = false;
+
+ /*** XXX NEED TO SUBTARGET SYSTEMS IF TARGET IS STARSHIP...
+ else if (tgt_ship->ShieldStrength() > 10)
+ use_primary = false;
+ ***/
+ }
+
+ if (use_primary) {
+ // is target in the basket?
+ double dx = fabs(objective.x / distance);
+ double dy = fabs(objective.y / distance);
+
+ if (primary->GetFiringOrders() == Weapon::MANUAL &&
+ dx < gun_basket && dy < gun_basket &&
+ distance > dsgn_primary->min_range &&
+ distance < dsgn_primary->max_range &&
+ !primary->IsBlockedFriendly())
+ {
+ ship->FirePrimary();
+ }
+ }
+ }
+
+ if (secondary && secondary->GetFiringOrders() == Weapon::MANUAL) {
+ dsgn_secondary = secondary->Design();
+
+ if (missile_time <= 0 && secondary->Ammo() && !secondary->IsBlockedFriendly()) {
+ if (secondary->Locked() || !dsgn_secondary->self_aiming) {
+ // is target in basket?
+ Point tgt = AimTransform(target->Location());
+ double tgt_range = tgt.Normalize();
+
+ int factor = 2-ai_level;
+ double s_range = 0.5 + 0.2 * factor;
+ double s_basket = 0.3 + 0.2 * factor;
+ double extra_time = 10 * factor * factor + 5;
+
+ if (!dsgn_secondary->self_aiming)
+ s_basket *= 0.33;
+
+ if (tgt_ship) {
+ if (tgt_ship->Class() == Ship::MINE) {
+ extra_time = 10;
+ s_range = 0.75;
+ }
+
+ else if (!tgt_ship->IsDropship()) {
+ extra_time = 0.5 * factor + 0.5;
+ s_range = 0.9;
+ }
+ }
+
+ // is target in decent range?
+ if (tgt_range < secondary->Design()->max_range * s_range) {
+ double dx = fabs(tgt.x);
+ double dy = fabs(tgt.y);
+
+ if (dx < s_basket && dy < s_basket && tgt.z > 0) {
+ if (ship->FireSecondary()) {
+ missile_time = secondary->Design()->salvo_delay + extra_time;
+
+ if (Game::GameTime() - last_call_time > 6000) {
+ // call fox:
+ int call = RadioMessage::FOX_3; // A2A
+
+ if (secondary->CanTarget(Ship::GROUND_UNITS)) // AGM
+ call = RadioMessage::FOX_1;
+
+ else if (secondary->CanTarget(Ship::DESTROYER)) // ASM
+ call = RadioMessage::FOX_2;
+
+ RadioTraffic::SendQuickMessage(ship, call);
+ last_call_time = Game::GameTime();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+double
+FighterAI::CalcDefensePerimeter(Ship* starship)
+{
+ double perimeter = 15e3;
+
+ if (starship) {
+ ListIter<WeaponGroup> g_iter = starship->Weapons();
+ while (++g_iter) {
+ WeaponGroup* group = g_iter.value();
+
+ ListIter<Weapon> w_iter = group->GetWeapons();
+ while (++w_iter) {
+ Weapon* weapon = w_iter.value();
+
+ if (weapon->Ammo() &&
+ weapon->GetTarget() == ship &&
+ !weapon->IsBlockedFriendly()) {
+
+ double range = weapon->Design()->max_range * 1.2;
+ if (range > perimeter)
+ perimeter = range;
+ }
+ }
+ }
+ }
+
+ return perimeter;
+}
+
+
+
diff --git a/Stars45/FighterAI.h b/Stars45/FighterAI.h
new file mode 100644
index 0000000..99a3121
--- /dev/null
+++ b/Stars45/FighterAI.h
@@ -0,0 +1,85 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: FighterAI.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Fighter (low-level) Artifical Intelligence class
+*/
+
+#ifndef FighterAI_h
+#define FighterAI_h
+
+#include "Types.h"
+#include "ShipAI.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+class Shot;
+class InboundSlot;
+
+// +--------------------------------------------------------------------+
+
+class FighterAI : public ShipAI
+{
+public:
+ FighterAI(SimObject* s);
+ virtual ~FighterAI();
+
+ virtual void ExecFrame(double seconds);
+ virtual int Subframe() const { return true; }
+
+ // convert the goal point from world to local coords:
+ virtual void FindObjective();
+ virtual void FindObjectiveNavPoint();
+
+protected:
+ // behaviors:
+ virtual Steer AvoidTerrain();
+ virtual Steer SeekTarget();
+ virtual Steer EvadeThreat();
+ virtual Point ClosingVelocity();
+
+ // accumulate behaviors:
+ virtual void Navigator();
+
+ // steering functions:
+ virtual Steer Seek(const Point& point);
+ virtual Steer SeekFormationSlot();
+
+ // fire on target if appropriate:
+ virtual void FireControl();
+ virtual void HelmControl();
+ virtual void ThrottleControl();
+
+ virtual double CalcDefensePerimeter(Ship* starship);
+ virtual void ReturnToBase(Ship* controller);
+
+ Shot* decoy_missile;
+ double missile_time;
+ int terrain_warning;
+ int drop_state;
+ char dir_info[32];
+ double brakes;
+ double z_shift;
+ double time_to_dock;
+ InboundSlot* inbound;
+ int rtb_code;
+ bool evading;
+ DWORD jink_time;
+ Point jink;
+ bool over_threshold;
+ bool form_up;
+ bool go_manual;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif FighterAI_h
+
diff --git a/Stars45/FighterTacticalAI.cpp b/Stars45/FighterTacticalAI.cpp
new file mode 100644
index 0000000..dc0c417
--- /dev/null
+++ b/Stars45/FighterTacticalAI.cpp
@@ -0,0 +1,562 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: FighterTacticalAI.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Fighter-specific mid-level (tactical) AI class
+*/
+
+#include "MemDebug.h"
+#include "FighterTacticalAI.h"
+#include "ShipAI.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Shot.h"
+#include "Element.h"
+#include "Instruction.h"
+#include "RadioMessage.h"
+#include "Sensor.h"
+#include "Contact.h"
+#include "WeaponGroup.h"
+#include "Drive.h"
+#include "Sim.h"
+#include "StarSystem.h"
+
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+const int WINCHESTER_FIGHTER = 0;
+const int WINCHESTER_ASSAULT = 1;
+const int WINCHESTER_STRIKE = 2;
+const int WINCHESTER_STATIC = 3;
+
+// +----------------------------------------------------------------------+
+
+FighterTacticalAI::FighterTacticalAI(ShipAI* ai)
+ : TacticalAI(ai), secondary_selection_time(0)
+{
+ for (int i = 0; i < 4; i++)
+ winchester[i] = false;
+
+ ai_level = ai->GetAILevel();
+
+ switch (ai_level) {
+ default:
+ case 2: THREAT_REACTION_TIME = 1000; break;
+ case 1: THREAT_REACTION_TIME = 3000; break;
+ case 0: THREAT_REACTION_TIME = 6000; break;
+ }
+}
+
+
+// +--------------------------------------------------------------------+
+
+FighterTacticalAI::~FighterTacticalAI()
+{ }
+
+// +--------------------------------------------------------------------+
+
+bool
+FighterTacticalAI::CheckFlightPlan()
+{
+ navpt = ship->GetNextNavPoint();
+
+ int order = Instruction::PATROL;
+ roe = FLEXIBLE;
+
+ if (navpt) {
+ order = navpt->Action();
+
+ switch (order) {
+ case Instruction::LAUNCH:
+ case Instruction::DOCK:
+ case Instruction::RTB: roe = NONE;
+ break;
+
+ case Instruction::VECTOR:
+ roe = SELF_DEFENSIVE;
+ if (element_index > 1)
+ roe = DEFENSIVE;
+ break;
+
+ case Instruction::DEFEND:
+ case Instruction::ESCORT: roe = DEFENSIVE;
+ break;
+
+ case Instruction::INTERCEPT:
+ if (element_index > 1)
+ roe = DEFENSIVE;
+ else
+ roe = DIRECTED;
+ break;
+
+ case Instruction::RECON:
+ case Instruction::STRIKE:
+ case Instruction::ASSAULT: roe = DIRECTED;
+ break;
+
+ case Instruction::PATROL:
+ case Instruction::SWEEP: roe = FLEXIBLE;
+ break;
+
+ default: break;
+ }
+
+ if (order == Instruction::STRIKE) {
+ ship->SetSensorMode(Sensor::GM);
+
+ if (IsStrikeComplete(navpt)) {
+ ship->SetNavptStatus(navpt, Instruction::COMPLETE);
+ }
+ }
+
+ else if (order == Instruction::ASSAULT) {
+ if (ship->GetSensorMode() == Sensor::GM)
+ ship->SetSensorMode(Sensor::STD);
+
+ if (IsStrikeComplete(navpt)) {
+ ship->SetNavptStatus(navpt, Instruction::COMPLETE);
+ }
+ }
+
+ else {
+ if (ship->GetSensorMode() == Sensor::GM)
+ ship->SetSensorMode(Sensor::STD);
+ }
+ }
+
+ switch (roe) {
+ case NONE: ship->SetDirectorInfo(Game::GetText("ai.none")); break;
+ case SELF_DEFENSIVE: ship->SetDirectorInfo(Game::GetText("ai.self-defensive")); break;
+ case DEFENSIVE: ship->SetDirectorInfo(Game::GetText("ai.defensive")); break;
+ case DIRECTED: ship->SetDirectorInfo(Game::GetText("ai.directed")); break;
+ case FLEXIBLE: ship->SetDirectorInfo(Game::GetText("ai.flexible")); break;
+ default: ship->SetDirectorInfo(Game::GetText("ai.default")); break;
+ }
+
+ return (navpt != 0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FighterTacticalAI::SelectTarget()
+{
+ TacticalAI::SelectTarget();
+
+ SimObject* target = ship_ai->GetTarget();
+
+ if (target && (target->Type() == SimObject::SIM_SHIP) &&
+ (Game::GameTime() - secondary_selection_time) > THREAT_REACTION_TIME) {
+ SelectSecondaryForTarget((Ship*) target);
+ secondary_selection_time = Game::GameTime();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FighterTacticalAI::SelectTargetDirected(Ship* tgt)
+{
+ Ship* potential_target = tgt;
+
+ if (!tgt) {
+ // try to target one of the element's objectives
+ // (if it shows up in the contact list)
+
+ Element* elem = ship->GetElement();
+
+ if (elem) {
+ Instruction* objective = elem->GetTargetObjective();
+
+ if (objective) {
+ SimObject* obj_sim_obj = objective->GetTarget();
+ Ship* obj_tgt = 0;
+
+ if (obj_sim_obj && obj_sim_obj->Type() == SimObject::SIM_SHIP)
+ obj_tgt = (Ship*) obj_sim_obj;
+
+ if (obj_tgt && ship->FindContact(obj_tgt))
+ potential_target = obj_tgt;
+ }
+ }
+ }
+
+ if (!CanTarget(potential_target))
+ potential_target = 0;
+
+ ship_ai->SetTarget(potential_target);
+ SelectSecondaryForTarget(potential_target);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FighterTacticalAI::SelectTargetOpportunity()
+{
+ // NON-COMBATANTS do not pick targets of opportunity:
+ if (ship->GetIFF() == 0)
+ return;
+
+ Ship* potential_target = 0;
+ Shot* current_shot_target = 0;
+
+ // pick the closest combatant ship with a different IFF code:
+ double target_dist = 1.0e15;
+ double min_dist = 5.0e3;
+
+ // FIGHTERS are primarily anti-air platforms, but may
+ // also attack smaller starships:
+
+ Ship* ward = 0;
+ if (element_index > 1)
+ ward = ship->GetLeader();
+
+ // commit range for patrol/sweep is 80 Km
+ // (about 2 minutes for a fighter at max speed)
+ if (roe == FLEXIBLE || roe == AGRESSIVE)
+ target_dist = ship->Design()->commit_range;
+
+ if (roe < FLEXIBLE)
+ target_dist = 0.5 * ship->Design()->commit_range;
+
+ int class_limit = Ship::LCA;
+
+ if (ship->Class() == Ship::ATTACK)
+ class_limit = Ship::DESTROYER;
+
+ ListIter<Contact> c_iter = ship->ContactList();
+ while (++c_iter) {
+ Contact* contact = c_iter.value();
+ Ship* c_ship = contact->GetShip();
+ Shot* c_shot = contact->GetShot();
+ int c_iff = contact->GetIFF(ship);
+ bool rogue = false;
+
+ if (c_ship)
+ rogue = c_ship->IsRogue();
+
+ if (!rogue && (c_iff <= 0 || c_iff == ship->GetIFF() || c_iff == 1000))
+ continue;
+
+ // reasonable target?
+ if (c_ship && c_ship->Class() <= class_limit && !c_ship->InTransition()) {
+ if (!rogue) {
+ SimObject* ttgt = c_ship->GetTarget();
+
+ // if we are self-defensive, is this contact engaging us?
+ if (roe == SELF_DEFENSIVE && ttgt != ship)
+ continue;
+
+ // if we are defending, is this contact engaging us or our ward?
+ if (roe == DEFENSIVE && ttgt != ship && ttgt != ward)
+ continue;
+ }
+
+ // found an enemy, check distance:
+ double dist = (ship->Location() - c_ship->Location()).length();
+
+ if (dist < 0.75 * target_dist) {
+
+ // if on patrol, check target distance from navpoint:
+ if (roe == FLEXIBLE && navpt) {
+ double ndist = (navpt->Location().OtherHand() - c_ship->Location()).length();
+ if (ndist > 80e3)
+ continue;
+ }
+
+ potential_target = c_ship;
+ target_dist = dist;
+ }
+ }
+
+ else if (c_shot && c_shot->IsDrone()) {
+ // found an enemy shot, do we have enough time to engage?
+ if (c_shot->GetEta() < 10)
+ continue;
+
+ // found an enemy shot, check distance:
+ double dist = (ship->Location() - c_shot->Location()).length();
+
+ if (!current_shot_target) {
+ current_shot_target = c_shot;
+ target_dist = dist;
+ }
+
+ // is this shot a better target than the one we've found?
+ else {
+ Ship* ward = ship_ai->GetWard();
+
+ if ((c_shot->IsTracking(ward) && !current_shot_target->IsTracking(ward)) ||
+ (dist < target_dist)) {
+ current_shot_target = c_shot;
+ target_dist = dist;
+ }
+ }
+ }
+ }
+
+ if (current_shot_target) {
+ ship_ai->SetTarget(current_shot_target);
+ }
+ else {
+ ship_ai->SetTarget(potential_target);
+ SelectSecondaryForTarget(potential_target);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+FighterTacticalAI::ListSecondariesForTarget(Ship* tgt, List<WeaponGroup>& weps)
+{
+ weps.clear();
+
+ if (tgt) {
+ ListIter<WeaponGroup> iter = ship->Weapons();
+ while (++iter) {
+ WeaponGroup* w = iter.value();
+
+ if (w->Ammo() && w->CanTarget(tgt->Class()))
+ weps.append(w);
+ }
+ }
+
+ return weps.size();
+}
+
+void
+FighterTacticalAI::SelectSecondaryForTarget(Ship* tgt)
+{
+ if (tgt) {
+ int wix = WINCHESTER_FIGHTER;
+
+ if (tgt->IsGroundUnit()) wix = WINCHESTER_STRIKE;
+ else if (tgt->IsStatic()) wix = WINCHESTER_STATIC;
+ else if (tgt->IsStarship()) wix = WINCHESTER_ASSAULT;
+
+ WeaponGroup* best = 0;
+ List<WeaponGroup> weps;
+
+ if (ListSecondariesForTarget(tgt, weps)) {
+ winchester[wix] = false;
+
+ // select best weapon for the job:
+ double range = (ship->Location() - tgt->Location()).length();
+ double best_range = 0;
+ double best_damage = 0;
+
+ ListIter<WeaponGroup> iter = weps;
+ while (++iter) {
+ WeaponGroup* w = iter.value();
+
+ if (!best) {
+ best = w;
+
+ WeaponDesign* d = best->GetDesign();
+ best_range = d->max_range;
+ best_damage = d->damage * d->ripple_count;
+
+ if (best_range < range)
+ best = 0;
+ }
+
+ else {
+ WeaponDesign* d = w->GetDesign();
+ double w_range = d->max_range;
+ double w_damage = d->damage * d->ripple_count;
+
+ if (w_range > range) {
+ if (w_range < best_range || w_damage > best_damage)
+ best = w;
+ }
+ }
+ }
+
+ // now cycle weapons until you pick the best one:
+ WeaponGroup* current_missile = ship->GetSecondaryGroup();
+
+ if (current_missile && best && current_missile != best) {
+ ship->CycleSecondary();
+ WeaponGroup* m = ship->GetSecondaryGroup();
+
+ while (m != current_missile && m != best) {
+ ship->CycleSecondary();
+ m = ship->GetSecondaryGroup();
+ }
+ }
+ }
+
+ else {
+ winchester[wix] = true;
+
+ // if we have NO weapons that can hit this target,
+ // just drop it:
+
+ Weapon* primary = ship->GetPrimary();
+ if (!primary || !primary->CanTarget(tgt->Class())) {
+ ship_ai->DropTarget(3);
+ ship->DropTarget();
+ }
+ }
+
+ if (tgt->IsGroundUnit())
+ ship->SetSensorMode(Sensor::GM);
+
+ else if (ship->GetSensorMode() == Sensor::GM)
+ ship->SetSensorMode(Sensor::STD);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FighterTacticalAI::FindFormationSlot(int formation)
+{
+ // find the formation delta:
+ int s = element_index - 1;
+ Point delta(5*s, 0, -5*s);
+
+ // diamond:
+ if (formation == Instruction::DIAMOND) {
+ switch (element_index) {
+ case 2: delta = Point( 12, -1, -10); break;
+ case 3: delta = Point(-12, -1, -10); break;
+ case 4: delta = Point( 0, -2, -20); break;
+ }
+ }
+
+ // spread:
+ if (formation == Instruction::SPREAD) {
+ switch (element_index) {
+ case 2: delta = Point( 15, 0, 0); break;
+ case 3: delta = Point(-15, 0, 0); break;
+ case 4: delta = Point(-30, 0, 0); break;
+ }
+ }
+
+ // box:
+ if (formation == Instruction::BOX) {
+ switch (element_index) {
+ case 2: delta = Point(15, 0, 0); break;
+ case 3: delta = Point( 0, -2, -20); break;
+ case 4: delta = Point(15, -2, -20); break;
+ }
+ }
+
+ // trail:
+ if (formation == Instruction::TRAIL) {
+ delta = Point(0, s, -20*s);
+ }
+
+ ship_ai->SetFormationDelta(delta * ship->Radius() * 2);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FighterTacticalAI::FindThreat()
+{
+ // pick the closest contact on Threat Warning System:
+ Ship* threat_ship = 0;
+ Shot* threat_missile = 0;
+ double threat_dist = 1e9;
+
+ ListIter<Contact> c_iter = ship->ContactList();
+
+ while (++c_iter) {
+ Contact* contact = c_iter.value();
+
+ if (contact->Threat(ship) &&
+ (Game::GameTime() - contact->AcquisitionTime()) > THREAT_REACTION_TIME) {
+
+ double rng = contact->Range(ship);
+
+ if (contact->GetShot()) {
+ threat_missile = contact->GetShot();
+ }
+
+ else if (rng < threat_dist && contact->GetShip()) {
+ Ship* candidate = contact->GetShip();
+
+ if (candidate->InTransition())
+ continue;
+
+ if (candidate->IsStarship() && rng < 50e3) {
+ threat_ship = candidate;
+ threat_dist = rng;
+ }
+
+ else if (candidate->IsDropship() && rng < 25e3) {
+ threat_ship = candidate;
+ threat_dist = rng;
+ }
+
+ // static and ground units:
+ else if (rng < 30e3) {
+ threat_ship = candidate;
+ threat_dist = rng;
+ }
+ }
+ }
+ }
+
+ ship_ai->SetThreat(threat_ship);
+ ship_ai->SetThreatMissile(threat_missile);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+FighterTacticalAI::IsStrikeComplete(Instruction* instr)
+{
+ // wingmen can not call a halt to a strike:
+ if (!ship || element_index > 1)
+ return false;
+
+ // if there's nothing to shoot at, we must be done:
+ if (!instr || !instr->GetTarget() || instr->GetTarget()->Life() == 0 ||
+ instr->GetTarget()->Type() != SimObject::SIM_SHIP)
+ return true;
+
+ // break off strike only when ALL weapons are expended:
+ // (remember to check all relevant wingmen)
+ Element* element = ship->GetElement();
+ Ship* target = (Ship*) instr->GetTarget();
+
+ if (!element)
+ return true;
+
+ for (int i = 0; i < element->NumShips(); i++) {
+ Ship* s = element->GetShip(i+1);
+
+ if (!s || s->Integrity() < 25) // || (s->Location() - target->Location()).length() > 250e3)
+ continue;
+
+ ListIter<WeaponGroup> g_iter = s->Weapons();
+ while (++g_iter) {
+ WeaponGroup* w = g_iter.value();
+
+ if (w->Ammo() && w->CanTarget(target->Class())) {
+ ListIter<Weapon> w_iter = w->GetWeapons();
+
+ while (++w_iter) {
+ Weapon* weapon = w_iter.value();
+
+ if (weapon->Status() > System::CRITICAL)
+ return false;
+ }
+ }
+ }
+ }
+
+ // still here? we must be done!
+ return true;
+} \ No newline at end of file
diff --git a/Stars45/FighterTacticalAI.h b/Stars45/FighterTacticalAI.h
new file mode 100644
index 0000000..d1f434f
--- /dev/null
+++ b/Stars45/FighterTacticalAI.h
@@ -0,0 +1,56 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: FighterTacticalAI.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Fighter-specific mid-level (tactical) AI
+*/
+
+#ifndef FighterTacticalAI_h
+#define FighterTacticalAI_h
+
+#include "Types.h"
+#include "TacticalAI.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class WeaponGroup;
+
+// +--------------------------------------------------------------------+
+
+class FighterTacticalAI : public TacticalAI
+{
+public:
+ FighterTacticalAI(ShipAI* ai);
+ virtual ~FighterTacticalAI();
+
+protected:
+ virtual bool CheckFlightPlan();
+ virtual bool IsStrikeComplete(Instruction* instr=0);
+
+ virtual void SelectTarget();
+ virtual void SelectTargetDirected(Ship* tgt=0);
+ virtual void SelectTargetOpportunity();
+ virtual void FindFormationSlot(int formation);
+ virtual void FindThreat();
+
+ virtual void SelectSecondaryForTarget(Ship* tgt);
+ virtual int ListSecondariesForTarget(Ship* tgt, List<WeaponGroup>& weps);
+
+ bool winchester[4];
+ DWORD THREAT_REACTION_TIME;
+ DWORD secondary_selection_time;
+ int ai_level;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif FighterTacticalAI_h
+
diff --git a/Stars45/FirstTimeDlg.cpp b/Stars45/FirstTimeDlg.cpp
new file mode 100644
index 0000000..7fb5d10
--- /dev/null
+++ b/Stars45/FirstTimeDlg.cpp
@@ -0,0 +1,147 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: FirstTimeDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "FirstTimeDlg.h"
+#include "Player.h"
+#include "MenuScreen.h"
+#include "Ship.h"
+#include "Starshatter.h"
+#include "KeyMap.h"
+#include "Random.h"
+
+#include "DataLoader.h"
+#include "Button.h"
+#include "EditBox.h"
+#include "ComboBox.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "MachineInfo.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(FirstTimeDlg, OnApply);
+
+// +--------------------------------------------------------------------+
+
+FirstTimeDlg::FirstTimeDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr)
+{
+ Init(def);
+}
+
+FirstTimeDlg::~FirstTimeDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FirstTimeDlg::RegisterControls()
+{
+ edt_name = (EditBox*) FindControl(200);
+ cmb_playstyle = (ComboBox*) FindControl(201);
+ cmb_experience = (ComboBox*) FindControl(202);
+
+ btn_apply = (Button*) FindControl(1);
+ REGISTER_CLIENT(EID_CLICK, btn_apply, FirstTimeDlg, OnApply);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FirstTimeDlg::Show()
+{
+ if (!IsShown())
+ FormWindow::Show();
+
+ if (edt_name)
+ edt_name->SetText("Noobie");
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FirstTimeDlg::ExecFrame()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FirstTimeDlg::OnApply(AWEvent* event)
+{
+ Starshatter* stars = Starshatter::GetInstance();
+ Player* player = Player::GetCurrentPlayer();
+
+ if (player) {
+ if (edt_name) {
+ char password[16];
+ sprintf(password, "%08x", (DWORD) Random(0, 2e9));
+
+ player->SetName(edt_name->GetText());
+ player->SetPassword(password);
+ }
+
+ if (cmb_playstyle) {
+ // ARCADE:
+ if (cmb_playstyle->GetSelectedIndex() == 0) {
+ player->SetFlightModel(2);
+ player->SetLandingModel(1);
+ player->SetHUDMode(0);
+ player->SetGunsight(1);
+
+ if (stars) {
+ KeyMap& keymap = stars->GetKeyMap();
+
+ keymap.Bind(KEY_CONTROL_MODEL, 1, 0);
+ keymap.SaveKeyMap("key.cfg", 256);
+
+ stars->MapKeys();
+ }
+
+ Ship::SetControlModel(1);
+ }
+
+ // HARDCORE:
+ else {
+ player->SetFlightModel(0);
+ player->SetLandingModel(0);
+ player->SetHUDMode(0);
+ player->SetGunsight(0);
+
+ if (stars) {
+ KeyMap& keymap = stars->GetKeyMap();
+
+ keymap.Bind(KEY_CONTROL_MODEL, 0, 0);
+ keymap.SaveKeyMap("key.cfg", 256);
+
+ stars->MapKeys();
+ }
+
+ Ship::SetControlModel(0);
+ }
+ }
+
+ if (cmb_experience && cmb_experience->GetSelectedIndex() > 0) {
+ player->SetRank(2); // Lieutenant
+ player->SetTrained(255); // Fully Trained
+ }
+
+ Player::Save();
+ }
+
+ manager->ShowMenuDlg();
+}
diff --git a/Stars45/FirstTimeDlg.h b/Stars45/FirstTimeDlg.h
new file mode 100644
index 0000000..6ec95f6
--- /dev/null
+++ b/Stars45/FirstTimeDlg.h
@@ -0,0 +1,56 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: FirstTimeDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#ifndef FirstTimeDlg_h
+#define FirstTimeDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+
+// +--------------------------------------------------------------------+
+
+class FirstTimeDlg : public FormWindow
+{
+public:
+ FirstTimeDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~FirstTimeDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnApply(AWEvent* event);
+
+protected:
+ MenuScreen* manager;
+
+ EditBox* edt_name;
+ ComboBox* cmb_playstyle;
+ ComboBox* cmb_experience;
+
+ Button* btn_apply;
+};
+
+#endif FirstTimeDlg_h
+
diff --git a/Stars45/FlightComp.cpp b/Stars45/FlightComp.cpp
new file mode 100644
index 0000000..02b7b39
--- /dev/null
+++ b/Stars45/FlightComp.cpp
@@ -0,0 +1,305 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: FlightComp.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Flight Computer System class
+*/
+
+#include "MemDebug.h"
+#include "FlightComp.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Thruster.h"
+
+// +----------------------------------------------------------------------+
+
+FlightComp::FlightComp(int comp_type, const char* comp_name)
+ : Computer(comp_type, comp_name), mode(0), vlimit(0.0f),
+ trans_x_limit(0.0f), trans_y_limit(0.0f), trans_z_limit(0.0f),
+ throttle(0.0f), halt(0)
+{ }
+
+// +----------------------------------------------------------------------+
+
+FlightComp::FlightComp(const Computer& c)
+ : Computer(c), mode(0), vlimit(0.0f),
+ trans_x_limit(0.0f), trans_y_limit(0.0f), trans_z_limit(0.0f),
+ throttle(0.0f), halt(0)
+{ }
+
+// +--------------------------------------------------------------------+
+
+FlightComp::~FlightComp()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+FlightComp::SetTransLimit(double x, double y, double z)
+{
+ trans_x_limit = 0.0f;
+ trans_y_limit = 0.0f;
+ trans_z_limit = 0.0f;
+
+ if (x >= 0) trans_x_limit = (float) x;
+ if (y >= 0) trans_y_limit = (float) y;
+ if (z >= 0) trans_z_limit = (float) z;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FlightComp::ExecSubFrame()
+{
+ if (ship) {
+ ExecThrottle();
+ ExecTrans();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FlightComp::ExecThrottle()
+{
+ throttle = (float) ship->Throttle();
+
+ if (throttle > 5)
+ halt = false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FlightComp::ExecTrans()
+{
+ double tx = ship->TransX();
+ double ty = ship->TransY();
+ double tz = ship->TransZ();
+
+ double trans_x = tx;
+ double trans_y = ty;
+ double trans_z = tz;
+
+ bool flcs_operative = false;
+
+ if (IsPowerOn())
+ flcs_operative = Status() == System::NOMINAL ||
+ Status() == System::DEGRADED;
+
+ // ----------------------------------------------------------
+ // FIGHTER FLCS AUTO MODE
+ // ----------------------------------------------------------
+
+ if (mode == Ship::FLCS_AUTO) {
+ // auto thrust to align flight path with orientation:
+ if (tx == 0) {
+ if (flcs_operative)
+ trans_x = (ship->Velocity() * ship->BeamLine() * -200);
+
+ else
+ trans_x = 0;
+ }
+
+ // manual thrust up to vlimit/2:
+ else {
+ double vfwd = ship->BeamLine() * ship->Velocity();
+
+ if (fabs(vfwd)>= vlimit) {
+ if (trans_x > 0 && vfwd > 0)
+ trans_x = 0;
+
+ else if (trans_x < 0 && vfwd < 0)
+ trans_x = 0;
+ }
+ }
+
+ if (halt && flcs_operative) {
+ if (ty == 0) {
+ double vfwd = ship->Heading() * ship->Velocity();
+ double vmag = fabs(vfwd);
+
+ if (vmag > 0) {
+ if (vfwd > 0)
+ trans_y = -trans_y_limit;
+ else
+ trans_y = trans_y_limit;
+
+ if (vfwd < vlimit/2)
+ trans_y *= (vmag/(vlimit/2));
+ }
+ }
+ }
+
+
+ // auto thrust to align flight path with orientation:
+ if (tz == 0) {
+ if (flcs_operative)
+ trans_z = (ship->Velocity() * ship->LiftLine() * -200);
+
+ else
+ trans_z = 0;
+ }
+
+ // manual thrust up to vlimit/2:
+ else {
+ double vfwd = ship->LiftLine() * ship->Velocity();
+
+ if (fabs(vfwd) >= vlimit) {
+ if (trans_z > 0 && vfwd > 0)
+ trans_z = 0;
+
+ else if (trans_z < 0 && vfwd < 0)
+ trans_z = 0;
+ }
+ }
+ }
+
+ // ----------------------------------------------------------
+ // STARSHIP HELM MODE
+ // ----------------------------------------------------------
+
+ else if (mode == Ship::FLCS_HELM) {
+ if (flcs_operative) {
+ double compass_heading = ship->CompassHeading();
+ double compass_pitch = ship->CompassPitch();
+ // rotate helm into compass orientation:
+ double helm = ship->GetHelmHeading() - compass_heading;
+
+ if (helm > PI)
+ helm -= 2*PI;
+ else if (helm < -PI)
+ helm += 2*PI;
+
+ // turn to align with helm heading:
+ if (helm != 0)
+ ship->ApplyYaw(helm);
+
+ // pitch to align with helm pitch:
+ if (compass_pitch != ship->GetHelmPitch())
+ ship->ApplyPitch(compass_pitch - ship->GetHelmPitch());
+
+ // roll to align with world coordinates:
+ if (ship->Design()->auto_roll > 0) {
+ Point vrt = ship->Cam().vrt();
+ double deflection = vrt.y;
+
+ if (fabs(helm) < PI/16 || ship->Design()->turn_bank < 0.01) {
+ if (ship->Design()->auto_roll > 1) {
+ ship->ApplyRoll(0.5);
+ }
+ else if (deflection != 0) {
+ double theta = asin(deflection);
+ ship->ApplyRoll(-theta);
+ }
+ }
+
+ // else roll through turn maneuvers:
+ else {
+ double desired_bank = ship->Design()->turn_bank;
+
+ if (helm >= 0)
+ desired_bank = -desired_bank;
+
+ double current_bank = asin(deflection);
+ double theta = desired_bank - current_bank;
+ ship->ApplyRoll(theta);
+
+ // coordinate the turn:
+ if (current_bank < 0 && desired_bank < 0 ||
+ current_bank > 0 && desired_bank > 0) {
+
+ double coord_pitch = compass_pitch
+ - ship->GetHelmPitch()
+ - fabs(helm) * fabs(current_bank);
+ ship->ApplyPitch(coord_pitch);
+ }
+ }
+ }
+ }
+
+ // flcs inoperative, set helm heading based on actual compass heading:
+ else {
+ ship->SetHelmHeading(ship->CompassHeading());
+ ship->SetHelmPitch(ship->CompassPitch());
+ }
+
+ // auto thrust to align flight path with helm order:
+ if (tx == 0) {
+ if (flcs_operative)
+ trans_x = (ship->Velocity() * ship->BeamLine() * ship->Mass() * -1);
+
+ else
+ trans_x = 0;
+ }
+
+ // manual thrust up to vlimit/2:
+ else {
+ double vfwd = ship->BeamLine() * ship->Velocity();
+
+ if (fabs(vfwd) >= vlimit/2) {
+ if (trans_x > 0 && vfwd > 0)
+ trans_x = 0;
+
+ else if (trans_x < 0 && vfwd < 0)
+ trans_x = 0;
+ }
+ }
+
+ if (trans_y == 0 && halt) {
+ double vfwd = ship->Heading() * ship->Velocity();
+ double vdesired = 0;
+
+ if (vfwd > vdesired) {
+ trans_y = -trans_y_limit;
+
+ if (!flcs_operative)
+ trans_y = 0;
+
+ double vdelta = vfwd-vdesired;
+ if (vdelta < vlimit/2)
+ trans_y *= (vdelta/(vlimit/2));
+ }
+ }
+
+
+
+ // auto thrust to align flight path with helm order:
+ if (tz == 0) {
+ if (flcs_operative)
+ trans_z = (ship->Velocity() * ship->LiftLine() * ship->Mass() * -1);
+
+ else
+ trans_z = 0;
+ }
+
+ // manual thrust up to vlimit/2:
+ else {
+ double vfwd = ship->LiftLine() * ship->Velocity();
+
+ if (fabs(vfwd) > vlimit/2) {
+ if (trans_z > 0 && vfwd > 0)
+ trans_z = 0;
+
+ else if (trans_z < 0 && vfwd < 0)
+ trans_z = 0;
+ }
+ }
+ }
+
+ if (ship->GetThruster()) {
+ ship->GetThruster()->ExecTrans(trans_x, trans_y, trans_z);
+ }
+ else {
+ ship->SetTransX(trans_x);
+ ship->SetTransY(trans_y);
+ ship->SetTransZ(trans_z);
+ }
+}
diff --git a/Stars45/FlightComp.h b/Stars45/FlightComp.h
new file mode 100644
index 0000000..8696cd0
--- /dev/null
+++ b/Stars45/FlightComp.h
@@ -0,0 +1,63 @@
+/* Project STARSHATTER
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: FlightComp.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Flight Computer systems class
+*/
+
+#ifndef FLIGHT_COMP_H
+#define FLIGHT_COMP_H
+
+#include "Types.h"
+#include "Computer.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+
+// +--------------------------------------------------------------------+
+
+class FlightComp : public Computer
+{
+public:
+ enum CompType { AVIONICS=1, FLIGHT, TACTICAL };
+
+ FlightComp(int comp_type, const char* comp_name);
+ FlightComp(const Computer& rhs);
+ virtual ~FlightComp();
+
+ virtual void ExecSubFrame();
+
+ int Mode() const { return mode; }
+ double Throttle() const { return throttle; }
+
+ void SetMode(int m) { mode = m; }
+ void SetVelocityLimit(double v) { vlimit = (float) v; }
+ void SetTransLimit(double x, double y, double z);
+
+ void FullStop() { halt = true; }
+
+protected:
+ virtual void ExecTrans();
+ virtual void ExecThrottle();
+
+ int mode;
+ int halt;
+ float throttle;
+
+ float vlimit;
+ float trans_x_limit;
+ float trans_y_limit;
+ float trans_z_limit;
+};
+
+#endif FLIGHT_COMP_H
+
diff --git a/Stars45/FlightDeck.cpp b/Stars45/FlightDeck.cpp
new file mode 100644
index 0000000..fe1f5db
--- /dev/null
+++ b/Stars45/FlightDeck.cpp
@@ -0,0 +1,1186 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: FlightDeck.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Everything needed to launch and recover space craft...
+*/
+
+#include "MemDebug.h"
+#include "FlightDeck.h"
+#include "Ship.h"
+#include "ShipCtrl.h"
+#include "ShipDesign.h"
+#include "Element.h"
+#include "Mission.h"
+#include "MissionEvent.h"
+#include "Drive.h"
+#include "Sim.h"
+#include "Instruction.h"
+#include "Hoop.h"
+#include "LandingGear.h"
+#include "RadioMessage.h"
+#include "RadioTraffic.h"
+#include "SimEvent.h"
+#include "AudioConfig.h"
+#include "CameraDirector.h"
+#include "Combatant.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+
+#include "NetData.h"
+#include "NetUtil.h"
+
+#include "Game.h"
+#include "Solid.h"
+#include "Light.h"
+#include "Sound.h"
+#include "DataLoader.h"
+
+static Sound* tire_sound = 0;
+static Sound* catapult_sound = 0;
+
+// +======================================================================+
+
+class FlightDeckSlot
+{
+public:
+ FlightDeckSlot() : ship(0), state(0), sequence(0), time(0), filter(0xf) { }
+ int operator == (const FlightDeckSlot& that) const { return this == &that; }
+
+ Ship* ship;
+ Point spot_rel;
+ Point spot_loc;
+
+ int state;
+ int sequence;
+ double time;
+ double clearance;
+ DWORD filter;
+};
+
+// +======================================================================+
+
+InboundSlot::InboundSlot(Ship* s, FlightDeck* d, int squad, int index)
+ : ship(s), deck(d), squadron(squad), slot(index), cleared(0), final(0), approach(0)
+{
+ if (ship)
+ Observe(ship);
+
+ if (deck && deck->GetCarrier())
+ Observe((SimObject*) deck->GetCarrier());
+}
+
+int InboundSlot::operator < (const InboundSlot& that) const
+{
+ double dthis = 0;
+ double dthat = 0;
+
+ if (ship == that.ship) {
+ return false;
+ }
+
+ if (cleared && !that.cleared) {
+ return true;
+ }
+
+ if (!cleared && that.cleared) {
+ return false;
+ }
+
+ if (ship && deck && that.ship) {
+ dthis = Point(ship->Location() - deck->MountLocation()).length();
+ dthat = Point(that.ship->Location() - deck->MountLocation()).length();
+ }
+
+ if (dthis == dthat) {
+ return (DWORD) this < (DWORD) &that;
+ }
+
+ return dthis < dthat;
+}
+
+int InboundSlot::operator <= (const InboundSlot& that) const
+{
+ double dthis = 0;
+ double dthat = 0;
+
+ if (ship == that.ship)
+ return true;
+
+ if (cleared && !that.cleared)
+ return true;
+
+ if (!cleared && that.cleared)
+ return false;
+
+ if (ship && deck && that.ship) {
+ dthis = Point(ship->Location() - deck->MountLocation()).length();
+ dthat = Point(that.ship->Location() - deck->MountLocation()).length();
+ }
+
+ return dthis <= dthat;
+}
+
+int InboundSlot::operator == (const InboundSlot& that) const
+{
+ return this == &that;
+}
+
+void InboundSlot::Clear(bool c)
+{
+ if (ship)
+ cleared = c;
+}
+
+double InboundSlot::Distance()
+{
+ if (ship && deck) {
+ return Point(ship->Location() - deck->MountLocation()).length();
+ }
+
+ return 1e9;
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+InboundSlot::Update(SimObject* obj)
+{
+ if (obj->Type() == SimObject::SIM_SHIP) {
+ Ship* s = (Ship*) obj;
+
+ if (s == ship) {
+ ship = 0;
+ }
+
+ // Actually, this can't happen. The flight deck
+ // owns the slot. When the carrier is destroyed,
+ // the flight decks and slots are destroyed before
+ // the carrier updates the observers.
+
+ // I'm leaving this block in, just in case.
+
+ else if (deck && s == deck->GetCarrier()) {
+ ship = 0;
+ deck = 0;
+ }
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+InboundSlot::GetObserverName() const
+{
+ return "InboundSlot";
+}
+
+// +======================================================================+
+
+FlightDeck::FlightDeck()
+ : System(FLIGHT_DECK, FLIGHT_DECK_LAUNCH, "Flight Deck", 1, 1),
+ carrier(0), index(0), num_slots(0), slots(0), cycle_time(5), num_hoops(0), hoops(0),
+ azimuth(0), light(0), num_catsounds(0), num_approach_pts(0)
+{
+ name = Game::GetText("sys.flight-deck");
+ abrv = Game::GetText("sys.flight-deck.abrv");
+}
+
+// +----------------------------------------------------------------------+
+
+FlightDeck::FlightDeck(const FlightDeck& s)
+ : System(s),
+ carrier(0), index(0), start_rel(s.start_rel),
+ end_rel(s.end_rel), cam_rel(s.cam_rel),
+ cycle_time(s.cycle_time),
+ num_slots(s.num_slots), slots(0),
+ num_hoops(0), hoops(0), azimuth(s.azimuth), light(0),
+ num_catsounds(0), num_approach_pts(s.num_approach_pts)
+{
+ Mount(s);
+
+ slots = new(__FILE__,__LINE__) FlightDeckSlot[num_slots];
+ for (int i = 0; i < num_slots; i++) {
+ slots[i].spot_rel = s.slots[i].spot_rel;
+ slots[i].filter = s.slots[i].filter;
+ }
+
+ for (i = 0; i < NUM_APPROACH_PTS; i++)
+ approach_rel[i] = s.approach_rel[i];
+
+ for (i = 0; i < 2; i++)
+ runway_rel[i] = s.runway_rel[i];
+
+ if (s.light) {
+ light = new(__FILE__,__LINE__) Light(*s.light);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+FlightDeck::~FlightDeck()
+{
+ if (hoops && num_hoops) {
+ for (int i = 0; i < num_hoops; i++) {
+ Hoop* h = &hoops[i];
+ Scene* scene = h->GetScene();
+ if (scene)
+ scene->DelGraphic(h);
+ }
+ }
+
+ delete [] slots;
+ delete [] hoops;
+
+ LIGHT_DESTROY(light);
+
+ recovery_queue.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FlightDeck::Initialize()
+{
+ static int initialized = 0;
+ if (initialized) return;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Sounds/");
+
+ const int SOUND_FLAGS = Sound::LOCALIZED |
+ Sound::LOC_3D;
+
+ loader->LoadSound("Tires.wav", tire_sound, SOUND_FLAGS);
+ loader->LoadSound("Catapult.wav", catapult_sound, SOUND_FLAGS);
+ loader->SetDataPath(0);
+
+ if (tire_sound)
+ tire_sound->SetMaxDistance(2.5e3f);
+
+ if (catapult_sound)
+ catapult_sound->SetMaxDistance(0.5e3f);
+
+ initialized = 1;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FlightDeck::Close()
+{
+ delete tire_sound;
+ delete catapult_sound;
+
+ tire_sound = 0;
+ catapult_sound = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FlightDeck::ExecFrame(double seconds)
+{
+ System::ExecFrame(seconds);
+
+ bool advance_queue = false;
+ long max_vol = AudioConfig::EfxVolume();
+ long volume = -1000;
+ Sim* sim = Sim::GetSim();
+
+ if (volume > max_vol)
+ volume = max_vol;
+
+ // update ship status:
+ for (int i = 0; i < num_slots; i++) {
+ FlightDeckSlot* slot = &slots[i];
+ Ship* slot_ship = 0;
+
+ if (slot->ship == 0) {
+ slot->state = CLEAR;
+ }
+ else {
+ slot_ship = slot->ship;
+ slot_ship->SetThrottle(0);
+ }
+
+ switch (slot->state) {
+ case CLEAR:
+ if (slot->time > 0) {
+ slot->time -= seconds;
+ }
+ else if (IsRecoveryDeck()) {
+ GrantClearance();
+ }
+ break;
+
+ case READY: {
+ Camera c;
+ c.Clone(carrier->Cam());
+ c.Yaw(azimuth);
+
+ if (slot_ship) {
+ slot_ship->CloneCam(c);
+ slot_ship->MoveTo(slot->spot_loc);
+ slot_ship->TranslateBy(carrier->Cam().vup() * slot->clearance);
+ slot_ship->SetVelocity(carrier->Velocity());
+ }
+ slot->time = 0;
+ }
+ break;
+
+ case QUEUED:
+ if (slot->time > 0) {
+ Camera c;
+ c.Clone(carrier->Cam());
+ c.Yaw(azimuth);
+
+ slot->time -= seconds;
+ if (slot_ship) {
+ slot_ship->CloneCam(c);
+ slot_ship->MoveTo(slot->spot_loc);
+ slot_ship->TranslateBy(carrier->Cam().vup() * slot->clearance);
+ slot_ship->SetFlightPhase(Ship::ALERT);
+ }
+ }
+
+ if (slot->sequence == 1 && slot->time <= 0) {
+ bool clear_for_launch = true;
+ for (int i = 0; i < num_slots; i++)
+ if (slots[i].state == LOCKED)
+ clear_for_launch = false;
+
+ if (clear_for_launch) {
+ slot->sequence = 0;
+ slot->state = LOCKED;
+ slot->time = cycle_time;
+ if (slot_ship)
+ slot_ship->SetFlightPhase(Ship::LOCKED);
+ num_catsounds = 0;
+
+ advance_queue = true;
+ }
+ }
+ break;
+
+ case LOCKED:
+ if (slot->time > 0) {
+ slot->time -= seconds;
+
+ if (slot_ship) {
+ double ct4 = cycle_time/4;
+
+ double dx = start_rel.x - slot->spot_rel.x;
+ double dy = start_rel.z - slot->spot_rel.z;
+ double dyaw = atan2(dx,dy) - azimuth;
+
+ Camera c;
+ c.Clone(carrier->Cam());
+ c.Yaw(azimuth);
+
+ // rotate:
+ if (slot->time > 3*ct4) {
+ double step = 1 - (slot->time - 3*ct4) / ct4;
+ c.Yaw(dyaw * step);
+ slot_ship->CloneCam(c);
+ slot_ship->MoveTo(slot->spot_loc);
+ slot_ship->TranslateBy(carrier->Cam().vup() * slot->clearance);
+
+ if (carrier->IsGroundUnit()) {
+ slot_ship->SetThrottle(25);
+ }
+
+ else if (num_catsounds < 1) {
+ if (catapult_sound) {
+ Sound* sound = catapult_sound->Duplicate();
+ sound->SetLocation(slot_ship->Location());
+ sound->SetVolume(volume);
+ sound->Play();
+ }
+ num_catsounds = 1;
+ }
+ }
+
+ // translate:
+ else if (slot->time > 2*ct4) {
+ double step = (slot->time - 2*ct4) / ct4;
+
+ Point loc = start_point +
+ (slot->spot_loc - start_point) * step;
+
+ c.Yaw(dyaw);
+ slot_ship->CloneCam(c);
+ slot_ship->MoveTo(loc);
+ slot_ship->TranslateBy(carrier->Cam().vup() * slot->clearance);
+
+ if (carrier->IsGroundUnit()) {
+ slot_ship->SetThrottle(25);
+ }
+
+ else if (num_catsounds < 2) {
+ if (catapult_sound) {
+ Sound* sound = catapult_sound->Duplicate();
+ sound->SetLocation(slot_ship->Location());
+ sound->SetVolume(volume);
+ sound->Play();
+ }
+ num_catsounds = 2;
+ }
+ }
+
+ // rotate:
+ else if (slot->time > ct4) {
+ double step = (slot->time - ct4) / ct4;
+ c.Yaw(dyaw*step);
+ slot_ship->CloneCam(c);
+ slot_ship->MoveTo(start_point);
+ slot_ship->TranslateBy(carrier->Cam().vup() * slot->clearance);
+
+ if (carrier->IsGroundUnit()) {
+ slot_ship->SetThrottle(25);
+ }
+
+ else if (num_catsounds < 3) {
+ if (catapult_sound) {
+ Sound* sound = catapult_sound->Duplicate();
+ sound->SetLocation(slot_ship->Location());
+ sound->SetVolume(volume);
+ sound->Play();
+ }
+ num_catsounds = 3;
+ }
+ }
+
+ // wait:
+ else {
+ slot_ship->SetThrottle(100);
+ slot_ship->CloneCam(c);
+ slot_ship->MoveTo(start_point);
+ slot_ship->TranslateBy(carrier->Cam().vup() * slot->clearance);
+ }
+
+ slot_ship->SetFlightPhase(Ship::LOCKED);
+ }
+ }
+ else {
+ slot->state = LAUNCH;
+ slot->time = 0;
+
+ }
+ break;
+
+ case LAUNCH:
+ LaunchShip(slot_ship);
+ break;
+
+ case DOCKING:
+ if (slot_ship && !slot_ship->IsAirborne()) {
+ // do arresting gear stuff:
+ if (slot_ship->GetFlightModel() == Ship::FM_ARCADE)
+ slot_ship->ArcadeStop();
+
+ slot_ship->SetVelocity(carrier->Velocity());
+ }
+
+ if (slot->time > 0) {
+ slot->time -= seconds;
+ }
+ else {
+ if (slot_ship) {
+ slot_ship->SetFlightPhase(Ship::DOCKED);
+ slot_ship->Stow();
+
+ NetUtil::SendObjKill(slot_ship, carrier, NetObjKill::KILL_DOCK, index);
+ }
+
+ Clear(i);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (advance_queue) {
+ for (i = 0; i < num_slots; i++) {
+ FlightDeckSlot* slot = &slots[i];
+ if (slot->state == QUEUED && slot->sequence > 1)
+ slot->sequence--;
+ }
+ }
+}
+
+bool
+FlightDeck::LaunchShip(Ship* slot_ship)
+{
+ FlightDeckSlot* slot = 0;
+ Sim* sim = Sim::GetSim();
+
+ if (slot_ship) {
+ for (int i = 0; i < num_slots; i++) {
+ if (slots[i].ship == slot_ship)
+ slot = &(slots[i]);
+ }
+
+ // only suggest a launch point if no flight plan has been filed...
+ if (slot_ship->GetElement()->FlightPlanLength() == 0) {
+ Point departure = end_point;
+ departure = departure.OtherHand();
+
+ Instruction* launch_point = new(__FILE__,__LINE__)
+ Instruction(carrier->GetRegion(), departure, Instruction::LAUNCH);
+ launch_point->SetSpeed(350);
+
+ slot_ship->SetLaunchPoint(launch_point);
+ }
+
+ if (!slot_ship->IsAirborne()) {
+ Point cat;
+ if (fabs(azimuth) < 5*DEGREES) {
+ cat = carrier->Heading() * 300;
+ }
+ else {
+ Camera c;
+ c.Clone(carrier->Cam());
+ c.Yaw(azimuth);
+ cat = c.vpn() * 300;
+ }
+
+ slot_ship->SetVelocity(carrier->Velocity() + cat);
+ slot_ship->SetFlightPhase(Ship::LAUNCH);
+ }
+ else {
+ slot_ship->SetFlightPhase(Ship::TAKEOFF);
+ }
+
+ Director* dir = slot_ship->GetDirector();
+ if (dir && dir->Type() == ShipCtrl::DIR_TYPE) {
+ ShipCtrl* ctrl = (ShipCtrl*) dir;
+ ctrl->Launch();
+ }
+
+ ShipStats* c = ShipStats::Find(carrier->Name());
+ if (c) c->AddEvent(SimEvent::LAUNCH_SHIP, slot_ship->Name());
+
+ ShipStats* stats = ShipStats::Find(slot_ship->Name());
+ if (stats) {
+ stats->SetRegion(carrier->GetRegion()->Name());
+ stats->SetType(slot_ship->Design()->name);
+
+ if (slot_ship->GetElement()) {
+ Element* elem = slot_ship->GetElement();
+ stats->SetRole(Mission::RoleName(elem->Type()));
+ stats->SetCombatGroup(elem->GetCombatGroup());
+ stats->SetCombatUnit(elem->GetCombatUnit());
+ stats->SetElementIndex(slot_ship->GetElementIndex());
+ }
+
+ stats->SetIFF(slot_ship->GetIFF());
+ stats->AddEvent(SimEvent::LAUNCH, carrier->Name());
+
+ if (slot_ship == sim->GetPlayerShip())
+ stats->SetPlayer(true);
+ }
+
+ sim->ProcessEventTrigger(MissionEvent::TRIGGER_LAUNCH, 0, slot_ship->Name());
+
+ if (slot) {
+ slot->ship = 0;
+ slot->state = CLEAR;
+ slot->sequence = 0;
+ slot->time = 0;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+FlightDeck::SetLight(double l)
+{
+ LIGHT_DESTROY(light);
+ light = new(__FILE__,__LINE__) Light((float) l);
+}
+
+void
+FlightDeck::SetApproachPoint(int i, Point loc)
+{
+ if (i >= 0 && i < NUM_APPROACH_PTS) {
+ approach_rel[i] = loc;
+
+ if (i >= num_approach_pts)
+ num_approach_pts = i+1;
+ }
+}
+
+void
+FlightDeck::SetRunwayPoint(int i, Point loc)
+{
+ if (i >= 0 && i < 2)
+ runway_rel[i] = loc;
+}
+
+void
+FlightDeck::SetStartPoint(Point loc)
+{
+ start_rel = loc;
+}
+
+void
+FlightDeck::SetEndPoint(Point loc)
+{
+ end_rel = loc;
+}
+
+void
+FlightDeck::SetCamLoc(Point loc)
+{
+ cam_rel = loc;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+FlightDeck::AddSlot(const Point& spot, DWORD filter)
+{
+ if (!slots)
+ slots = new(__FILE__,__LINE__) FlightDeckSlot[10];
+
+ slots[num_slots].spot_rel = spot;
+ slots[num_slots].filter = filter;
+ num_slots++;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+FlightDeck::SetCycleTime(double t)
+{
+ cycle_time = t;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+FlightDeck::Orient(const Physical* rep)
+{
+ System::Orient(rep);
+
+ Matrix orientation = rep->Cam().Orientation();
+ Point loc = rep->Location();
+
+ start_point = (start_rel * orientation) + loc;
+ end_point = (end_rel * orientation) + loc;
+ cam_loc = (cam_rel * orientation) + loc;
+
+ for (int i = 0; i < num_approach_pts; i++)
+ approach_point[i] = (approach_rel[i] * orientation) + loc;
+
+ for (i = 0; i < num_slots; i++)
+ slots[i].spot_loc = (slots[i].spot_rel * orientation) + loc;
+
+ if (IsRecoveryDeck()) {
+ if (carrier->IsAirborne()) {
+ runway_point[0] = (runway_rel[0] * orientation) + loc;
+ runway_point[1] = (runway_rel[1] * orientation) + loc;
+ }
+
+ if (num_hoops < 1) {
+ num_hoops = 4;
+ hoops = new(__FILE__,__LINE__) Hoop[num_hoops];
+ }
+
+ Point hoop_vec = start_point - end_point;
+ double hoop_d = hoop_vec.Normalize() / num_hoops;
+
+ orientation.Yaw(azimuth);
+
+ for (int i = 0; i < num_hoops; i++) {
+ hoops[i].MoveTo(end_point + hoop_vec * (i+1) * hoop_d);
+ hoops[i].SetOrientation(orientation);
+ }
+ }
+
+ if (light)
+ light->MoveTo(mount_loc);
+}
+
+// +----------------------------------------------------------------------+
+
+int
+FlightDeck::SpaceLeft(int type) const
+{
+ int space_left = 0;
+
+ for (int i = 0; i < num_slots; i++)
+ if (slots[i].ship == 0 && (slots[i].filter & type))
+ space_left++;
+
+ return space_left;
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+FlightDeck::Spot(Ship* s, int& index)
+{
+ if (!s)
+ return false;
+
+ if (index < 0) {
+ for (int i = 0; i < num_slots; i++) {
+ if (slots[i].ship == 0 && (slots[i].filter & s->Class())) {
+ index = i;
+ break;
+ }
+ }
+ }
+
+ if (index >= 0 && index < num_slots && slots[index].ship == 0) {
+ slots[index].state = READY;
+ slots[index].ship = s;
+ slots[index].clearance = 0;
+
+ if (s->GetGear())
+ slots[index].clearance = s->GetGear()->GetClearance();
+
+ if (IsRecoveryDeck() && !s->IsAirborne()) {
+ s->SetVelocity(s->Velocity() * 0.01); // hit the brakes!
+ }
+
+ if (!IsRecoveryDeck()) {
+ Camera work;
+ work.Clone(carrier->Cam());
+ work.Yaw(azimuth);
+
+ s->CloneCam(work);
+ s->MoveTo(slots[index].spot_loc);
+
+ LandingGear* g = s->GetGear();
+ if (g) {
+ g->SetState(LandingGear::GEAR_DOWN);
+ g->ExecFrame(0);
+ s->TranslateBy(carrier->Cam().vup() * slots[index].clearance);
+ }
+
+ s->SetFlightPhase(Ship::ALERT);
+ }
+
+ s->SetCarrier(carrier, this);
+ Observe(s);
+
+ return true;
+ }
+
+ return false;
+}
+
+bool
+FlightDeck::Clear(int index)
+{
+ if (index >= 0 && index < num_slots && slots[index].ship != 0) {
+ Ship* s = slots[index].ship;
+
+ slots[index].ship = 0;
+ slots[index].time = cycle_time;
+ slots[index].state = CLEAR;
+
+ ListIter<InboundSlot> iter = recovery_queue;
+ while (++iter) {
+ if (iter->GetShip() == s) {
+ delete iter.removeItem(); // ??? SHOULD DELETE HERE ???
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+FlightDeck::Launch(int index)
+{
+ if (subtype == FLIGHT_DECK_LAUNCH && index >= 0 && index < num_slots) {
+ FlightDeckSlot* slot = &slots[index];
+
+ if (slot->ship && slot->state == READY) {
+ int last = 0;
+ FlightDeckSlot* last_slot = 0;
+ FlightDeckSlot* lock_slot = 0;
+
+ for (int i = 0; i < num_slots; i++) {
+ FlightDeckSlot* s = &slots[i];
+
+ if (s->state == QUEUED && s->sequence > last) {
+ last = s->sequence;
+ last_slot = s;
+ }
+
+ else if (s->state == LOCKED) {
+ lock_slot = s;
+ }
+ }
+
+ // queue the slot for launch:
+ slot->state = QUEUED;
+ slot->sequence = last + 1;
+ slot->time = 0;
+
+ if (last_slot)
+ slot->time = last_slot->time + cycle_time;
+
+ else if (lock_slot)
+ slot->time = lock_slot->time;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+FlightDeck::Recover(Ship* s)
+{
+ if (s && subtype == FLIGHT_DECK_RECOVERY) {
+ if (OverThreshold(s)) {
+ if (s->GetFlightPhase() < Ship::RECOVERY) {
+ s->SetFlightPhase(Ship::RECOVERY);
+ s->SetCarrier(carrier, this); // let ship know which flight deck it's in (needed for dock cam)
+ }
+
+ // are we there yet?
+ if (s->GetFlightPhase() >= Ship::ACTIVE && s->GetFlightPhase() < Ship::DOCKING) {
+ if (slots[0].ship == 0) { // only need to ask this on approach
+ double dock_distance = (s->Location() - MountLocation()).length();
+
+ if (s->IsAirborne()) {
+ double altitude = s->Location().y - MountLocation().y;
+ if (dock_distance < Radius()*3 && altitude < s->Radius())
+ Dock(s);
+ }
+ else {
+ if (dock_distance < s->Radius())
+ Dock(s);
+ }
+ }
+ }
+
+ return true;
+ }
+ else {
+ if (s->GetFlightPhase() == Ship::RECOVERY)
+ s->SetFlightPhase(Ship::ACTIVE);
+ }
+ }
+
+ return false;
+}
+
+bool
+FlightDeck::Dock(Ship* s)
+{
+ if (s && subtype == FLIGHT_DECK_RECOVERY && slots[0].time <= 0) {
+ int index = 0;
+ if (Spot(s, index)) {
+ s->SetFlightPhase(Ship::DOCKING);
+ s->SetCarrier(carrier, this);
+
+ // hard landings?
+ if (Ship::GetLandingModel() == 0) {
+ double base_damage = s->Design()->integrity;
+
+ // did player do something stupid?
+ if (s->GetGear() && !s->IsGearDown()) {
+ Print("FlightDeck::Dock(%s) Belly landing!\n", s->Name());
+ s->InflictDamage(0.5 * base_damage);
+ }
+
+ double docking_deflection = fabs(carrier->Cam().vup().y - s->Cam().vup().y);
+
+ if (docking_deflection > 0.35) {
+ Print("Landing upside down? y = %.3f\n", docking_deflection);
+ s->InflictDamage(0.8 * base_damage);
+ }
+
+ // did incoming ship exceed safe landing parameters?
+ if (s->IsAirborne()) {
+ if (s->Velocity().y < -20) {
+ Print("FlightDeck::Dock(%s) Slammed it!\n", s->Name());
+ s->InflictDamage(0.1 * base_damage);
+ }
+ }
+
+ // did incoming ship exceed safe docking speed?
+ else {
+ Point delta_v = s->Velocity() - carrier->Velocity();
+ double excess = (delta_v.length() - 100);
+
+ if (excess > 0)
+ s->InflictDamage(excess);
+ }
+ }
+
+ if (s->IsAirborne()) {
+ if (s == Sim::GetSim()->GetPlayerShip() && tire_sound) {
+ Sound* sound = tire_sound->Duplicate();
+ sound->Play();
+ }
+ }
+
+ if (s->GetDrive())
+ s->GetDrive()->PowerOff();
+
+ slots[index].state = DOCKING;
+ slots[index].time = 5;
+
+ if (s->IsAirborne())
+ slots[index].time = 7.5;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int
+FlightDeck::Inbound(InboundSlot*& s)
+{
+ if (s && s->GetShip()) {
+ Ship* inbound = s->GetShip();
+
+ if (recovery_queue.contains(s)) {
+ InboundSlot* orig = s;
+ s = recovery_queue.find(s);
+ delete orig;
+ }
+ else {
+ recovery_queue.append(s);
+ Observe(s->GetShip());
+ }
+
+ inbound->SetInbound(s);
+
+ // find the best initial approach point for this ship:
+ double current_distance = 1e9;
+ for (int i = 0; i < num_approach_pts; i++) {
+ double distance = Point(inbound->Location() - approach_point[i]).length();
+ if (distance < current_distance) {
+ current_distance = distance;
+ s->SetApproach(i);
+ }
+ }
+
+ Point offset(rand()-16000, rand()-16000, rand()-16000);
+ offset.Normalize();
+ offset *= 200;
+
+ s->SetOffset(offset);
+
+ // *** DEBUG ***
+ // PrintQueue();
+
+ // if the new guy is first in line, and the deck is almost ready,
+ // go ahead and clear him for approach now
+ recovery_queue.sort();
+ if (recovery_queue[0] == s && !s->Cleared() && slots[0].time <= 3)
+ s->Clear();
+
+ return recovery_queue.index(s) + 1;
+ }
+
+ return 0;
+}
+
+void
+FlightDeck::GrantClearance()
+{
+ if (recovery_queue.size() > 0) {
+ if (recovery_queue[0]->Cleared() && recovery_queue[0]->Distance() > 45e3) {
+ recovery_queue[0]->Clear(false);
+ }
+
+ if (!recovery_queue[0]->Cleared()) {
+ recovery_queue.sort();
+
+ if (recovery_queue[0]->Distance() < 35e3) {
+ recovery_queue[0]->Clear();
+
+ Ship* dst = recovery_queue[0]->GetShip();
+
+ RadioMessage* clearance = new(__FILE__,__LINE__) RadioMessage(dst, carrier, RadioMessage::CALL_CLEARANCE);
+ clearance->SetInfo(Text("for final approach to ") + Name());
+ RadioTraffic::Transmit(clearance);
+ }
+ }
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+FlightDeck::PrintQueue()
+{
+ Print("\nRecovery Queue for %s\n", Name());
+ if (recovery_queue.size() < 1) {
+ Print(" (empty)\n\n");
+ return;
+ }
+
+ for (int i = 0; i < recovery_queue.size(); i++) {
+ InboundSlot* s = recovery_queue.at(i);
+
+ if (!s) {
+ Print(" %2d. null\n", i);
+ }
+ else if (!s->GetShip()) {
+ Print(" %2d. ship is null\n", i);
+ }
+ else {
+ double d = Point(s->GetShip()->Location() - MountLocation()).length();
+ Print(" %2d. %c %-20s %8d km\n", i, s->Cleared()?'*':' ', s->GetShip()->Name(), (int) (d/1000));
+ }
+ }
+
+ Print("\n");
+}
+
+// +----------------------------------------------------------------------+
+
+Ship*
+FlightDeck::GetShip(int index) const
+{
+ if (index >= 0 && index < num_slots)
+ return slots[index].ship;
+
+ return 0;
+}
+
+double
+FlightDeck::TimeRemaining(int index) const
+{
+ if (index >= 0 && index < num_slots)
+ return slots[index].time;
+
+ return 0;
+}
+
+
+int
+FlightDeck::State(int index) const
+{
+ if (index >= 0 && index < num_slots)
+ return slots[index].state;
+
+ return 0;
+}
+
+int
+FlightDeck::Sequence(int index) const
+{
+ if (index >= 0 && index < num_slots)
+ return slots[index].sequence;
+
+ return 0;
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+FlightDeck::Update(SimObject* obj)
+{
+ if (obj->Type() == SimObject::SIM_SHIP) {
+ Ship* s = (Ship*) obj;
+
+ ListIter<InboundSlot> iter = recovery_queue;
+ while (++iter) {
+ InboundSlot* recovery_slot = iter.value();
+
+ if (recovery_slot->GetShip() == s || recovery_slot->GetShip() == 0) {
+ delete iter.removeItem();
+ }
+ }
+
+ for (int i = 0; i < num_slots; i++) {
+ FlightDeckSlot* slot = &slots[i];
+
+ if (slot->ship == s) {
+ slot->ship = 0;
+ slot->state = 0;
+ slot->sequence = 0;
+ slot->time = 0;
+ break;
+ }
+ }
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+FlightDeck::GetObserverName() const
+{
+ return Name();
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+FlightDeck::OverThreshold(Ship* s) const
+{
+ if (carrier->IsAirborne()) {
+ if (s->AltitudeAGL() > s->Radius() * 4)
+ return false;
+
+ const Point& sloc = s->Location();
+
+ // is ship between the markers?
+ double distance = 1e9;
+
+ Point d0 = runway_point[0] - sloc;
+ Point d1 = runway_point[1] - sloc;
+ double d = d0 * d1;
+ bool between = (d0 * d1) < 0;
+
+ if (between) {
+ // distance from point to line:
+ Point src = runway_point[0];
+ Point dir = runway_point[1] - src;
+ Point w = (sloc - src).cross(dir);
+
+ distance = w.length() / dir.length();
+ }
+
+ return distance < Radius();
+ }
+
+ else {
+ return (s->Location() - MountLocation()).length() < (s->Radius() + Radius());
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+FlightDeck::ContainsPoint(const Point& p) const
+{
+ return (p - MountLocation()).length() < Radius();
+}
diff --git a/Stars45/FlightDeck.h b/Stars45/FlightDeck.h
new file mode 100644
index 0000000..cc8eefc
--- /dev/null
+++ b/Stars45/FlightDeck.h
@@ -0,0 +1,195 @@
+/* Project STARSHATTER
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: FlightDeck.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Everything needed to launch and recover space craft
+
+ See Also: Hangar
+*/
+
+#ifndef FlightDeck_h
+#define FlightDeck_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "System.h"
+#include "SimObject.h"
+#include "Text.h"
+
+// +----------------------------------------------------------------------+
+
+class Hoop;
+class Light;
+class Ship;
+class ShipDesign;
+class FlightDeck;
+class FlightDeckSlot;
+class InboundSlot;
+
+// +======================================================================+
+
+class InboundSlot : public SimObserver
+{
+public:
+ static const char* TYPENAME() { return "InboundSlot"; }
+
+ InboundSlot() : ship(0), deck(0), squadron(0), slot(0), cleared(0), final(0), approach(0) { }
+ InboundSlot(Ship* s, FlightDeck* d, int squad, int index);
+
+ int operator < (const InboundSlot& that) const;
+ int operator <= (const InboundSlot& that) const;
+ int operator == (const InboundSlot& that) const;
+
+ // SimObserver:
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+ Ship* GetShip() { return ship; }
+ FlightDeck* GetDeck() { return deck; }
+ int Squadron() { return squadron; }
+ int Index() { return slot; }
+ int Cleared() { return cleared; }
+ int Final() { return final; }
+ int Approach() { return approach; }
+ Point Offset() { return offset; }
+ double Distance();
+
+ void SetApproach(int a) { approach = a; }
+ void SetOffset(const Point& p) { offset = p; }
+ void SetFinal(int f) { final = f; }
+ void Clear(bool clear=true);
+
+private:
+ Ship* ship;
+ FlightDeck* deck;
+ int squadron;
+ int slot;
+ int cleared;
+ int final;
+ int approach;
+ Point offset;
+};
+
+// +----------------------------------------------------------------------+
+
+class FlightDeck : public System, public SimObserver
+{
+public:
+ static const char* TYPENAME() { return "FlightDeck"; }
+
+ FlightDeck();
+ FlightDeck(const FlightDeck& rhs);
+ virtual ~FlightDeck();
+
+ enum FLIGHT_DECK_MODE { FLIGHT_DECK_LAUNCH, FLIGHT_DECK_RECOVERY };
+ enum FLIGHT_SLOT_STATE { CLEAR, READY, QUEUED, LOCKED, LAUNCH, DOCKING };
+ enum CONSTANTS { NUM_APPROACH_PTS = 8 };
+
+ static void Initialize();
+ static void Close();
+
+ virtual void ExecFrame(double seconds);
+ void SetCarrier(Ship* s) { ship = carrier = s; }
+ void SetIndex(int n) { index = n; }
+
+ virtual int SpaceLeft(int type) const;
+
+ virtual bool Spot(Ship* s, int& index);
+ virtual bool Clear(int index);
+ virtual bool Launch(int index);
+ virtual bool LaunchShip(Ship* s);
+ virtual bool Recover(Ship* s);
+ virtual bool Dock(Ship* s);
+ virtual int Inbound(InboundSlot*& s);
+ virtual void GrantClearance();
+
+ virtual void AddSlot(const Point& loc, DWORD filter=0xf);
+
+ virtual bool IsLaunchDeck() const { return subtype == FLIGHT_DECK_LAUNCH; }
+ virtual void SetLaunchDeck() { subtype = FLIGHT_DECK_LAUNCH; }
+ virtual bool IsRecoveryDeck() const { return subtype == FLIGHT_DECK_RECOVERY; }
+ virtual void SetRecoveryDeck() { subtype = FLIGHT_DECK_RECOVERY; }
+
+ Point BoundingBox() const { return box; }
+ Point ApproachPoint(int i) const { return approach_point[i]; }
+ Point RunwayPoint(int i) const { return runway_point[i]; }
+ Point StartPoint() const { return start_point; }
+ Point EndPoint() const { return end_point; }
+ Point CamLoc() const { return cam_loc; }
+ double Azimuth() const { return azimuth; }
+
+ virtual void SetBoundingBox(Point dimensions) { box = dimensions; }
+ virtual void SetApproachPoint(int i, Point loc);
+ virtual void SetRunwayPoint(int i, Point loc);
+ virtual void SetStartPoint(Point loc);
+ virtual void SetEndPoint(Point loc);
+ virtual void SetCamLoc(Point loc);
+ virtual void SetCycleTime(double time);
+ virtual void SetAzimuth(double az) { azimuth = az; }
+ virtual void SetLight(double l);
+
+ virtual void Orient(const Physical* rep);
+
+ // SimObserver:
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+ // accessors:
+ int NumSlots() const { return num_slots; }
+ double TimeRemaining(int index) const;
+ int State(int index) const;
+ int Sequence(int index) const;
+ const Ship* GetCarrier() const { return carrier; }
+ int GetIndex() const { return index; }
+ Ship* GetShip(int index) const;
+ int NumHoops() const { return num_hoops; }
+ Hoop* GetHoops() const { return hoops; }
+ Light* GetLight() { return light; }
+
+ List<InboundSlot>& GetRecoveryQueue() { return recovery_queue; }
+ void PrintQueue();
+
+ bool OverThreshold(Ship* s) const;
+ bool ContainsPoint(const Point& p) const;
+
+protected:
+ Ship* carrier;
+ int index;
+ int num_slots;
+ FlightDeckSlot* slots;
+
+ Point box;
+ Point start_rel;
+ Point end_rel;
+ Point cam_rel;
+ Point approach_rel[NUM_APPROACH_PTS];
+ Point runway_rel[2];
+
+ Point start_point;
+ Point end_point;
+ Point cam_loc;
+ Point approach_point[NUM_APPROACH_PTS];
+ Point runway_point[2];
+
+ double azimuth;
+ double cycle_time;
+
+ int num_approach_pts;
+ int num_catsounds;
+ int num_hoops;
+ Hoop* hoops;
+ Light* light;
+ List<InboundSlot> recovery_queue;
+};
+
+// +----------------------------------------------------------------------+
+
+#endif FlightDeck_h
+
diff --git a/Stars45/FlightPlanner.cpp b/Stars45/FlightPlanner.cpp
new file mode 100644
index 0000000..7d2cf7c
--- /dev/null
+++ b/Stars45/FlightPlanner.cpp
@@ -0,0 +1,372 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: FlightPlanner.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Flight Planning class for creating navpoint routes for fighter elements.
+ Used both by the CarrierAI class and the Flight Dialog.
+*/
+
+#include "MemDebug.h"
+#include "FlightPlanner.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Element.h"
+#include "Instruction.h"
+#include "RadioMessage.h"
+#include "RadioTraffic.h"
+#include "Hangar.h"
+#include "FlightDeck.h"
+#include "Mission.h"
+#include "Contact.h"
+#include "Sim.h"
+#include "StarSystem.h"
+#include "Callsign.h"
+
+#include "Game.h"
+#include "Random.h"
+
+// +----------------------------------------------------------------------+
+
+FlightPlanner::FlightPlanner(Ship* s)
+ : sim(0), ship(s)
+{
+ sim = Sim::GetSim();
+
+ patrol_range = (float) (250e3 + 10e3 * (int) Random(0, 8.9));
+}
+
+FlightPlanner::~FlightPlanner()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+FlightPlanner::CreatePatrolRoute(Element* elem, int index)
+{
+ RLoc rloc;
+ Vec3 dummy(0,0,0);
+ Point loc = ship->Location();
+ double zone = ship->CompassHeading();
+ Instruction* instr = 0;
+
+ if (ship->IsAirborne())
+ loc.y += 8e3;
+ else
+ loc.y += 1e3;
+
+ loc = loc.OtherHand();
+
+ if (index > 2)
+ zone += 170*DEGREES;
+ else if (index > 1)
+ zone += -90*DEGREES;
+ else if (index > 0)
+ zone += 90*DEGREES;
+
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(loc);
+ rloc.SetDistance(30e3);
+ rloc.SetDistanceVar(0);
+ rloc.SetAzimuth(-10*DEGREES + zone);
+ rloc.SetAzimuthVar(0);
+
+ instr = new(__FILE__,__LINE__) Instruction(ship->GetRegion(), dummy, Instruction::VECTOR);
+ instr->SetSpeed(750);
+ instr->GetRLoc() = rloc;
+
+ elem->AddNavPoint(instr);
+
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(loc);
+ if (ship->IsAirborne())
+ rloc.SetDistance(140e3);
+ else
+ rloc.SetDistance(220e3);
+ rloc.SetDistanceVar(50e3);
+ rloc.SetAzimuth(-20*DEGREES + zone);
+ rloc.SetAzimuthVar(15*DEGREES);
+
+ instr = new(__FILE__,__LINE__) Instruction(ship->GetRegion(), dummy, Instruction::PATROL);
+ instr->SetSpeed(500);
+ instr->GetRLoc() = rloc;
+
+ elem->AddNavPoint(instr);
+
+ rloc.SetReferenceLoc(&instr->GetRLoc());
+ rloc.SetDistance(120e3);
+ rloc.SetDistanceVar(30e3);
+ rloc.SetAzimuth(60*DEGREES + zone);
+ rloc.SetAzimuthVar(20*DEGREES);
+
+ instr = new(__FILE__,__LINE__) Instruction(ship->GetRegion(), dummy, Instruction::PATROL);
+ instr->SetSpeed(350);
+ instr->GetRLoc() = rloc;
+
+ elem->AddNavPoint(instr);
+
+ rloc.SetReferenceLoc(&instr->GetRLoc());
+ rloc.SetDistance(120e3);
+ rloc.SetDistanceVar(30e3);
+ rloc.SetAzimuth(120*DEGREES + zone);
+ rloc.SetAzimuthVar(20*DEGREES);
+
+ instr = new(__FILE__,__LINE__) Instruction(ship->GetRegion(), dummy, Instruction::PATROL);
+ instr->SetSpeed(350);
+ instr->GetRLoc() = rloc;
+
+ elem->AddNavPoint(instr);
+
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(loc);
+ rloc.SetDistance(40e3);
+ rloc.SetDistanceVar(0);
+ rloc.SetAzimuth(180*DEGREES + ship->CompassHeading());
+ rloc.SetAzimuthVar(0*DEGREES);
+
+ instr = new(__FILE__,__LINE__) Instruction(ship->GetRegion(), dummy, Instruction::RTB);
+ instr->SetSpeed(500);
+ instr->GetRLoc() = rloc;
+
+ elem->AddNavPoint(instr);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FlightPlanner::CreateStrikeRoute(Element* elem, Element* target)
+{
+ if (!elem) return;
+
+ RLoc rloc;
+ Vec3 dummy(0,0,0);
+ Point loc = ship->Location();
+ double head = ship->CompassHeading() + 15*DEGREES;
+ double dist = 30e3;
+ Instruction* instr = 0;
+ Ship* tgt_ship = 0;
+
+ if (ship->IsAirborne())
+ loc += ship->Cam().vup() * 8e3;
+ else
+ loc += ship->Cam().vup() * 1e3;
+
+ loc = loc.OtherHand();
+
+ if (target)
+ tgt_ship = target->GetShip(1);
+
+ if (tgt_ship) {
+ double range = Point(tgt_ship->Location() - ship->Location()).length();
+
+ if (range < 100e3)
+ dist = 20e3;
+ }
+
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(loc);
+ rloc.SetDistance(dist);
+ rloc.SetDistanceVar(0);
+ rloc.SetAzimuth(head);
+ rloc.SetAzimuthVar(2*DEGREES);
+
+ instr = new(__FILE__,__LINE__) Instruction(ship->GetRegion(), dummy, Instruction::VECTOR);
+ instr->SetSpeed(750);
+ instr->GetRLoc() = rloc;
+
+ elem->AddNavPoint(instr);
+
+ if (tgt_ship) {
+ Ship* tgt_ship = target->GetShip(1);
+ Point tgt = tgt_ship->Location() + tgt_ship->Velocity() * 10;
+ Point mid = ship->Location() + (tgt - ship->Location()) * 0.5;
+ double beam = tgt_ship->CompassHeading() + 90*DEGREES;
+
+ if (tgt_ship->IsAirborne())
+ tgt += tgt_ship->Cam().vup() * 8e3;
+ else
+ tgt += tgt_ship->Cam().vup() * 1e3;
+
+ tgt = tgt.OtherHand();
+ mid = mid.OtherHand();
+
+ if (tgt_ship && tgt_ship->IsStarship()) {
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(tgt);
+ rloc.SetDistance(60e3);
+ rloc.SetDistanceVar(5e3);
+ rloc.SetAzimuth(beam);
+ rloc.SetAzimuthVar(5*DEGREES);
+
+ instr = new(__FILE__,__LINE__) Instruction(tgt_ship->GetRegion(), dummy, Instruction::ASSAULT);
+ instr->SetSpeed(750);
+ instr->GetRLoc() = rloc;
+ instr->SetTarget(target->Name());
+ instr->SetFormation(Instruction::TRAIL);
+
+ elem->AddNavPoint(instr);
+ }
+
+ if (tgt_ship && tgt_ship->IsStatic()) {
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(mid);
+ rloc.SetDistance(60e3);
+ rloc.SetDistanceVar(5e3);
+ rloc.SetAzimuth(beam);
+ rloc.SetAzimuthVar(15*DEGREES);
+
+ instr = new(__FILE__,__LINE__) Instruction(tgt_ship->GetRegion(), dummy, Instruction::VECTOR);
+ instr->SetSpeed(750);
+ instr->GetRLoc() = rloc;
+
+ elem->AddNavPoint(instr);
+
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(tgt);
+ rloc.SetDistance(40e3);
+ rloc.SetDistanceVar(5e3);
+ rloc.SetAzimuth(beam);
+ rloc.SetAzimuthVar(5*DEGREES);
+
+ int action = Instruction::ASSAULT;
+
+ if (tgt_ship->IsGroundUnit())
+ action = Instruction::STRIKE;
+
+ instr = new(__FILE__,__LINE__) Instruction(tgt_ship->GetRegion(), dummy, action);
+ instr->SetSpeed(750);
+ instr->GetRLoc() = rloc;
+ instr->SetTarget(target->Name());
+ instr->SetFormation(Instruction::TRAIL);
+
+ elem->AddNavPoint(instr);
+ }
+
+ else if (tgt_ship && tgt_ship->IsDropship()) {
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(tgt);
+ rloc.SetDistance(60e3);
+ rloc.SetDistanceVar(5e3);
+ rloc.SetAzimuth(tgt_ship->CompassHeading());
+ rloc.SetAzimuthVar(20*DEGREES);
+
+ instr = new(__FILE__,__LINE__) Instruction(tgt_ship->GetRegion(), dummy, Instruction::INTERCEPT);
+ instr->SetSpeed(750);
+ instr->GetRLoc() = rloc;
+ instr->SetTarget(target->Name());
+ instr->SetFormation(Instruction::SPREAD);
+
+ elem->AddNavPoint(instr);
+ }
+ }
+
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(loc);
+ rloc.SetDistance(40e3);
+ rloc.SetDistanceVar(0);
+ rloc.SetAzimuth(180*DEGREES + ship->CompassHeading());
+ rloc.SetAzimuthVar(0*DEGREES);
+
+ instr = new(__FILE__,__LINE__) Instruction(ship->GetRegion(), dummy, Instruction::RTB);
+ instr->SetSpeed(500);
+ instr->GetRLoc() = rloc;
+
+ elem->AddNavPoint(instr);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FlightPlanner::CreateEscortRoute(Element* elem, Element* ward)
+{
+ if (!elem) return;
+
+ RLoc rloc;
+ Vec3 dummy(0,0,0);
+ Point loc = ship->Location();
+ double head = ship->CompassHeading();
+ Instruction* instr = 0;
+
+ if (ship->IsAirborne())
+ loc += ship->Cam().vup() * 8e3;
+ else
+ loc += ship->Cam().vup() * 1e3;
+
+ loc = loc.OtherHand();
+
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(loc);
+ rloc.SetDistance(30e3);
+ rloc.SetDistanceVar(0);
+ rloc.SetAzimuth(head);
+ rloc.SetAzimuthVar(0);
+
+ instr = new(__FILE__,__LINE__) Instruction(ship->GetRegion(), dummy, Instruction::VECTOR);
+ instr->SetSpeed(750);
+ instr->GetRLoc() = rloc;
+
+ elem->AddNavPoint(instr);
+
+ if (ward && ward->GetShip(1)) {
+ // follow ward's flight plan:
+ if (ward->GetFlightPlan().size()) {
+ ListIter<Instruction> iter = ward->GetFlightPlan();
+
+ while (++iter) {
+ Instruction* ward_instr = iter.value();
+
+ if (ward_instr->Action() != Instruction::RTB) {
+ rloc.SetReferenceLoc(&ward_instr->GetRLoc());
+ rloc.SetDistance(25e3);
+ rloc.SetDistanceVar(5e3);
+ rloc.SetAzimuth(0);
+ rloc.SetAzimuthVar(90*DEGREES);
+
+ instr = new(__FILE__,__LINE__) Instruction(ship->GetRegion(), dummy, Instruction::ESCORT);
+ instr->SetSpeed(350);
+ instr->GetRLoc() = rloc;
+ instr->SetTarget(ward->Name());
+
+ elem->AddNavPoint(instr);
+ }
+ }
+ }
+
+ // if ward has no flight plan, just go to a point nearby:
+ else {
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(ward->GetShip(1)->Location());
+ rloc.SetDistance(25e3);
+ rloc.SetDistanceVar(5e3);
+ rloc.SetAzimuth(0);
+ rloc.SetAzimuthVar(90*DEGREES);
+
+ instr = new(__FILE__,__LINE__) Instruction(ship->GetRegion(), dummy, Instruction::DEFEND);
+ instr->SetSpeed(500);
+ instr->GetRLoc() = rloc;
+ instr->SetTarget(ward->Name());
+ instr->SetHoldTime(15 * 60); // fifteen minutes
+
+ elem->AddNavPoint(instr);
+ }
+ }
+
+ rloc.SetReferenceLoc(0);
+ rloc.SetBaseLocation(loc);
+ rloc.SetDistance(40e3);
+ rloc.SetDistanceVar(0);
+ rloc.SetAzimuth(180*DEGREES + ship->CompassHeading());
+ rloc.SetAzimuthVar(0*DEGREES);
+
+ instr = new(__FILE__,__LINE__) Instruction(ship->GetRegion(), dummy, Instruction::RTB);
+ instr->SetSpeed(500);
+ instr->GetRLoc() = rloc;
+
+ elem->AddNavPoint(instr);
+}
diff --git a/Stars45/FlightPlanner.h b/Stars45/FlightPlanner.h
new file mode 100644
index 0000000..1e691cd
--- /dev/null
+++ b/Stars45/FlightPlanner.h
@@ -0,0 +1,51 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: FlightPlanner.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Flight Planning class for creating navpoint routes for fighter elements.
+ Used both by the CarrierAI class and the Flight Dialog.
+*/
+
+#ifndef FlightPlanner_h
+#define FlightPlanner_h
+
+#include "Types.h"
+#include "Director.h"
+
+// +--------------------------------------------------------------------+
+
+class Sim;
+class Ship;
+class ShipAI;
+class Instruction;
+class Hangar;
+class Element;
+
+// +--------------------------------------------------------------------+
+
+class FlightPlanner
+{
+public:
+ FlightPlanner(Ship* s);
+ virtual ~FlightPlanner();
+
+ virtual void CreatePatrolRoute(Element* elem, int index);
+ virtual void CreateStrikeRoute(Element* strike, Element* target);
+ virtual void CreateEscortRoute(Element* escort, Element* ward);
+
+ Sim* sim;
+ Ship* ship;
+ float patrol_range;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif FlightPlanner_h
+
diff --git a/Stars45/FltDlg.cpp b/Stars45/FltDlg.cpp
new file mode 100644
index 0000000..6a89b86
--- /dev/null
+++ b/Stars45/FltDlg.cpp
@@ -0,0 +1,1084 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: FltDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "FltDlg.h"
+#include "GameScreen.h"
+#include "Sim.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Hangar.h"
+#include "FlightDeck.h"
+#include "Element.h"
+#include "CombatGroup.h"
+#include "Mission.h"
+#include "RadioMessage.h"
+#include "RadioTraffic.h"
+#include "Instruction.h"
+#include "FlightPlanner.h"
+#include "NetUtil.h"
+
+#include "Game.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "ComboBox.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+DEF_MAP_CLIENT(FltDlg, OnFilter);
+DEF_MAP_CLIENT(FltDlg, OnPackage);
+DEF_MAP_CLIENT(FltDlg, OnAlert);
+DEF_MAP_CLIENT(FltDlg, OnLaunch);
+DEF_MAP_CLIENT(FltDlg, OnStandDown);
+DEF_MAP_CLIENT(FltDlg, OnRecall);
+DEF_MAP_CLIENT(FltDlg, OnClose);
+DEF_MAP_CLIENT(FltDlg, OnMissionType);
+
+// +--------------------------------------------------------------------+
+
+static const ShipDesign* design = 0;
+
+// +--------------------------------------------------------------------+
+
+FltDlg::FltDlg(Screen* s, FormDef& def, GameScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ ship(0), filter_list(0), hangar_list(0),
+ package_btn(0), alert_btn(0), launch_btn(0), stand_btn(0), recall_btn(0),
+ mission_type(-1), flight_planner(0), patrol_pattern(0)
+{
+ Init(def);
+}
+
+FltDlg::~FltDlg()
+{
+ delete flight_planner;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FltDlg::RegisterControls()
+{
+ filter_list = (ComboBox*) FindControl(101);
+ hangar_list = (ListBox*) FindControl(102);
+ package_btn = (Button*) FindControl(110);
+ alert_btn = (Button*) FindControl(111);
+ launch_btn = (Button*) FindControl(112);
+ stand_btn = (Button*) FindControl(113);
+ recall_btn = (Button*) FindControl(114);
+ close_btn = (Button*) FindControl(1);
+
+ if (filter_list)
+ REGISTER_CLIENT(EID_SELECT, filter_list, FltDlg, OnFilter);
+
+ if (package_btn)
+ REGISTER_CLIENT(EID_CLICK, package_btn, FltDlg, OnPackage);
+
+ if (alert_btn)
+ REGISTER_CLIENT(EID_CLICK, alert_btn, FltDlg, OnAlert);
+
+ if (launch_btn)
+ REGISTER_CLIENT(EID_CLICK, launch_btn, FltDlg, OnLaunch);
+
+ if (stand_btn)
+ REGISTER_CLIENT(EID_CLICK, stand_btn, FltDlg, OnStandDown);
+
+ if (recall_btn)
+ REGISTER_CLIENT(EID_CLICK, recall_btn, FltDlg, OnRecall);
+
+ if (close_btn)
+ REGISTER_CLIENT(EID_CLICK, close_btn, FltDlg, OnClose);
+
+ for (int i = 0; i < 6; i++) {
+ mission_btn[i] = (Button*) FindControl(210 + i);
+ if (mission_btn[i])
+ REGISTER_CLIENT(EID_CLICK, mission_btn[i], FltDlg, OnMissionType);
+ }
+
+ objective_list = (ListBox*) FindControl(221);
+ loadout_list = (ListBox*) FindControl(222);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FltDlg::SetShip(Ship* s)
+{
+ if (ship != s) {
+ ship = s;
+
+ delete flight_planner;
+ flight_planner = 0;
+
+ if (filter_list) {
+ filter_list->ClearItems();
+
+ if (ship) {
+ int nsquadrons = 0;
+ int nslots = 0;
+ Hangar* hangar = ship->GetHangar();
+
+ if (hangar) {
+ nsquadrons = hangar->NumSquadrons();
+
+ for (int i = 0; i < nsquadrons; i++) {
+ char filter[64];
+ sprintf(filter, "%s %s",
+ hangar->SquadronDesign(i)->abrv,
+ hangar->SquadronName(i));
+
+ filter_list->AddItem(filter);
+ }
+
+ filter_list->AddItem(Game::GetText("FltDlg.PENDING"));
+ filter_list->AddItem(Game::GetText("FltDlg.ACTIVE"));
+ }
+
+ flight_planner = new(__FILE__,__LINE__) FlightPlanner(ship);
+ }
+
+ OnFilter(0);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FltDlg::ExecFrame()
+{
+ if (!ship || !ship->GetHangar()) {
+ manager->HideFltDlg();
+ }
+ else {
+ UpdateSelection();
+ UpdateObjective();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FltDlg::UpdateSelection()
+{
+ if (!filter_list || !hangar_list || !ship) return;
+
+ design = 0;
+
+ bool package = false;
+ bool alert = false;
+ bool launch = false;
+ bool stand = false;
+ bool recall = false;
+
+ Hangar* hangar = ship->GetHangar();
+ int seln = filter_list->GetSelectedIndex();
+ char txt[32];
+
+ // selected squadron:
+ if (seln < hangar->NumSquadrons()) {
+ int nslots = hangar->SquadronSize(seln);
+
+ for (int item = 0; item < hangar_list->NumItems(); item++) {
+ int i = hangar_list->GetItemData(item);
+ const HangarSlot* s = hangar->GetSlot(seln, i);
+
+ if (hangar->GetState(s) == Hangar::UNAVAIL)
+ hangar_list->SetItemColor(item, Color::DarkGray);
+ else if (hangar->GetState(s) == Hangar::MAINT)
+ hangar_list->SetItemColor(item, Color::Gray);
+ else
+ hangar_list->SetItemColor(item, Color::White);
+
+ if (hangar->GetState(s) > Hangar::STORAGE) {
+ if (hangar->GetShip(s))
+ hangar_list->SetItemText(item, 1, hangar->GetShip(s)->Name());
+ else if (hangar->GetPackageElement(s))
+ hangar_list->SetItemText(item, 1, hangar->GetPackageElement(s)->Name());
+ else
+ hangar_list->SetItemText(item, 1, hangar->SquadronName(seln));
+
+ if (hangar->GetPackageElement(s))
+ hangar_list->SetItemText(item, 3, Mission::RoleName(hangar->GetPackageElement(s)->Type()));
+ else
+ hangar_list->SetItemText(item, 3, "--");
+
+ }
+ else {
+ hangar_list->SetItemText(item, 1, "--");
+ hangar_list->SetItemText(item, 3, "--");
+ }
+
+ hangar_list->SetItemText(item, 2, hangar->StatusName(s));
+
+ if (hangar->GetState(s) >= Hangar::ACTIVE) {
+ FormatTime(txt, hangar->GetShip(s)->MissionClock() / 1000);
+ hangar_list->SetItemText(item, 4, txt);
+ }
+
+ else if (hangar->GetState(s) || Hangar::MAINT ||
+ hangar->GetState(s) > Hangar::STORAGE) {
+ FormatTime(txt, hangar->TimeRemaining(s));
+ hangar_list->SetItemText(item, 4, txt);
+ }
+
+ else {
+ hangar_list->SetItemText(item, 4, "");
+ }
+
+ if (hangar_list->IsSelected(item)) {
+ if (!design) design = hangar->GetDesign(s);
+
+ switch (hangar->GetState(s)) {
+ case Hangar::STORAGE: alert = true; break;
+ case Hangar::ALERT: launch = true;
+ stand = true; break;
+ case Hangar::QUEUED: stand = true; break;
+ case Hangar::ACTIVE: recall = true; break;
+ }
+ }
+ }
+ }
+
+ // selected pending filter:
+ else if (seln == hangar->NumSquadrons()) {
+ for (int item = 0; item < hangar_list->NumItems(); item++) {
+ int f = hangar_list->GetItemData(item, 1);
+ int i = hangar_list->GetItemData(item, 2);
+
+ int squadron = -1;
+ int slot = -1;
+ const HangarSlot* s = 0;
+
+ FlightDeck* deck = ship->GetFlightDeck(f);
+
+ if (deck->IsLaunchDeck()) {
+ int state = deck->State(i);
+ int seq = deck->Sequence(i);
+ double time = deck->TimeRemaining(i);
+ Ship* deckship = deck->GetShip(i);
+
+ if (deckship) {
+ hangar_list->SetItemText(item, 1, deckship->Name());
+
+ for (int a = 0; !s && a < hangar->NumSquadrons(); a++) {
+ for (int b = 0; !s && b < hangar->SquadronSize(a); b++) {
+ const HangarSlot* test = hangar->GetSlot(a,b);
+ if (hangar->GetShip(test) == deckship) {
+ s = test;
+ squadron = a;
+ slot = b;
+ }
+ }
+ }
+
+ if (s) {
+ hangar_list->SetItemText(item, 2, hangar->StatusName(s));
+ if (hangar->GetPackageElement(s))
+ hangar_list->SetItemText(item, 3, Mission::RoleName(hangar->GetPackageElement(s)->Type()));
+ }
+ }
+ else {
+ hangar_list->SetItemText(item, 1, "--");
+ hangar_list->SetItemText(item, 2, Game::GetText("FltDlg.Open"));
+ }
+
+ FormatTime(txt, time);
+ hangar_list->SetItemText(item, 4, txt);
+ hangar_list->SetItemData(item, 3, squadron);
+ hangar_list->SetItemData(item, 4, slot);
+
+ if (hangar_list->IsSelected(item) && s) {
+ if (!design) design = hangar->GetDesign(s);
+
+ switch (hangar->GetState(s)) {
+ case Hangar::ALERT: launch = true;
+ stand = true; break;
+ case Hangar::QUEUED: stand = true; break;
+ }
+ }
+ }
+ }
+ }
+
+ // selected active filter:
+ else if (seln == hangar->NumSquadrons()+1) {
+ int last_index = -1;
+
+ for (int item = 0; item < hangar_list->NumItems(); item++) {
+ int squadron = hangar_list->GetItemData(item, 1);
+ int slot = hangar_list->GetItemData(item, 2);
+
+ int nslots = hangar->SquadronSize(squadron);
+
+ if (slot >= 0 && slot < nslots) {
+ const HangarSlot* s = hangar->GetSlot(squadron, slot);
+
+ if (hangar->GetState(s) > Hangar::STORAGE) {
+ if (hangar->GetShip(s))
+ hangar_list->SetItemText(item, 1, hangar->GetShip(s)->Name());
+ else if (hangar->GetPackageElement(s))
+ hangar_list->SetItemText(item, 1, hangar->GetPackageElement(s)->Name());
+ else
+ hangar_list->SetItemText(item, 1, hangar->SquadronName(squadron));
+
+ if (hangar->GetPackageElement(s))
+ hangar_list->SetItemText(item, 3, Mission::RoleName(hangar->GetPackageElement(s)->Type()));
+ else
+ hangar_list->SetItemText(item, 3, "--");
+
+ hangar_list->SetItemText(item, 2, hangar->StatusName(s));
+
+ FormatTime(txt, hangar->GetShip(s)->MissionClock() / 1000);
+ hangar_list->SetItemText(item, 4, txt);
+
+ if (last_index < (int) hangar_list->GetItemData(item))
+ last_index = (int) hangar_list->GetItemData(item);
+ }
+ else {
+ hangar_list->RemoveItem(item);
+ item--;
+ }
+ }
+ else {
+ hangar_list->RemoveItem(item);
+ item--;
+ }
+ }
+
+ for (int i = 0; i < hangar->NumSquadrons(); i++) {
+ int nslots = hangar->SquadronSize(i);
+
+ for (int j = 0; j < nslots; j++) {
+ const HangarSlot* s = hangar->GetSlot(i, j);
+
+ if (hangar->GetState(s) >= Hangar::ACTIVE) {
+ bool found = false;
+
+ for (int n = 0; !found && n < hangar_list->NumItems(); n++) {
+ if (hangar_list->GetItemData(n, 1) == (DWORD) i &&
+ hangar_list->GetItemData(n, 2) == (DWORD) j)
+ found = true;
+ }
+
+ if (!found) {
+ last_index++;
+
+ char txt[32];
+ sprintf(txt, "%2d ", last_index+1);
+ hangar_list->AddItemWithData(txt, last_index); // use data for sort
+
+ if (hangar->GetShip(s))
+ hangar_list->SetItemText(item, 1, hangar->GetShip(s)->Name());
+
+ hangar_list->SetItemText(item, 2, hangar->StatusName(s));
+
+ if (hangar->GetPackageElement(s))
+ hangar_list->SetItemText(item, 3, Mission::RoleName(hangar->GetPackageElement(s)->Type()));
+
+ FormatTime(txt, hangar->GetShip(s)->MissionClock() / 1000);
+ hangar_list->SetItemText(item, 4, txt);
+
+ hangar_list->SetItemData(item, 1, i);
+ hangar_list->SetItemData(item, 2, j);
+
+ item++;
+ }
+ }
+ }
+ }
+
+ if (hangar_list->GetSelCount() > 0)
+ recall = true;
+ }
+
+ if (package_btn) {
+ bool pkg_ok = alert && mission_type > -1;
+
+ if (pkg_ok && mission_type > 0) {
+ pkg_ok = objective_list &&
+ objective_list->GetSelCount() > 0;
+
+ if (pkg_ok) {
+ int obj_index = objective_list->GetSelection();
+ DWORD obj_data = objective_list->GetItemData(obj_index, 2);
+
+ if (obj_data > 1e9)
+ pkg_ok = false;
+ }
+ }
+
+ package_btn->SetEnabled(pkg_ok);
+ }
+
+ if (alert_btn) {
+ alert_btn->SetEnabled(alert);
+
+ for (int i = 0; i < 6; i++)
+ if (mission_btn[i])
+ mission_btn[i]->SetEnabled(alert);
+ }
+
+ if (launch_btn)
+ launch_btn->SetEnabled(launch);
+
+ if (stand_btn)
+ stand_btn->SetEnabled(stand);
+
+ if (recall_btn)
+ recall_btn->SetEnabled(recall);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FltDlg::UpdateObjective()
+{
+ if (!objective_list || mission_type < 0 || !ship) return;
+
+ Sim* sim = Sim::GetSim();
+ char txt[32];
+
+ for (int item = 0; item < objective_list->NumItems(); item++) {
+ const char* obj_name = objective_list->GetItemText(item);
+
+ Element* elem = sim->FindElement(obj_name);
+
+ // if element has expired, remove it from the objective list
+ if (!elem || !elem->IsActive() || elem->IsFinished()) {
+ objective_list->RemoveItem(item);
+ item--;
+ }
+
+ // otherwise, the element is still active, so update range/region
+ else {
+ Ship* s = elem->GetShip(1);
+ double r = 0;
+ bool con = false;
+
+ if (s) {
+ Point s_loc = s->Location() + s->GetRegion()->Location();
+ Point h_loc = ship->Location() + ship->GetRegion()->Location();
+
+ r = (s_loc - h_loc).length();
+
+ con = ship->FindContact(s) != 0;
+
+ if (con) {
+ FormatNumber(txt, r);
+ }
+ else {
+ strcpy(txt, Game::GetText("FltDlg.Unknown").data());
+ r = 2e9;
+ }
+ }
+
+ objective_list->SetItemText(item, 1, s->GetRegion()->Name());
+
+ objective_list->SetItemText(item, 2, txt);
+ objective_list->SetItemData(item, 2, (DWORD) r);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FltDlg::Show()
+{
+ if (shown) return;
+
+ FormWindow::Show();
+ UpdateSelection();
+ UpdateObjective();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FltDlg::Hide()
+{
+ FormWindow::Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FltDlg::OnFilter(AWEvent* event)
+{
+ if (!filter_list || !hangar_list) return;
+
+ int seln = filter_list->GetSelectedIndex();
+
+ hangar_list->ClearItems();
+
+ if (!ship) return;
+
+ Hangar* hangar = ship->GetHangar();
+
+ // selected squadron:
+ if (seln < hangar->NumSquadrons()) {
+ int nslots = hangar->SquadronSize(seln);
+
+ for (int i = 0; i < nslots; i++) {
+ char txt[32];
+ sprintf(txt, " %2d ", i+1);
+
+ const HangarSlot* s = hangar->GetSlot(seln, i);
+ hangar_list->AddItemWithData(txt, i);
+
+ hangar_list->SetItemText(i, 1, "--");
+ hangar_list->SetItemText(i, 2, hangar->StatusName(s));
+
+ FormatTime(txt, hangar->TimeRemaining(s));
+ hangar_list->SetItemText(i, 4, txt);
+ }
+ }
+
+ // selected pending filter:
+ else if (seln == hangar->NumSquadrons()) {
+ int item = 0;
+
+ for (int f = 0; f < ship->NumFlightDecks(); f++) {
+ FlightDeck* deck = ship->GetFlightDeck(f);
+
+ if (deck->IsLaunchDeck()) {
+ for (int i = 0; i < deck->NumSlots(); i++) {
+ int state = deck->State(i);
+ int seq = deck->Sequence(i);
+ double time = deck->TimeRemaining(i);
+ Ship* deckship = deck->GetShip(i);
+
+ int squadron = -1;
+ int slot = -1;
+
+ char txt[32];
+ sprintf(txt, "%d-%d ", f+1, i+1);
+
+ hangar_list->AddItemWithData(txt, item); // use data for sort
+
+ if (deckship) {
+ hangar_list->SetItemText(item, 1, deckship->Name());
+
+ const HangarSlot* s = 0;
+
+ for (int a = 0; !s && a < hangar->NumSquadrons(); a++) {
+ for (int b = 0; !s && b < hangar->SquadronSize(a); b++) {
+ const HangarSlot* test = hangar->GetSlot(a, b);
+ if (hangar->GetShip(test) == deckship) {
+ s = test;
+ squadron = a;
+ slot = b;
+ }
+ }
+ }
+
+ if (s) {
+ hangar_list->SetItemText(item, 2, hangar->StatusName(s));
+ if (hangar->GetPackageElement(s))
+ hangar_list->SetItemText(item, 3, Mission::RoleName(hangar->GetPackageElement(s)->Type()));
+ }
+ }
+ else {
+ hangar_list->SetItemText(item, 1, "--");
+ hangar_list->SetItemText(item, 2, Game::GetText("FltDlg.Open"));
+ }
+
+
+ FormatTime(txt, time);
+ hangar_list->SetItemText(item, 4, txt);
+
+ hangar_list->SetItemData(item, 1, f);
+ hangar_list->SetItemData(item, 2, i);
+ hangar_list->SetItemData(item, 3, squadron);
+ hangar_list->SetItemData(item, 4, slot);
+
+ item++;
+ }
+ }
+ }
+ }
+
+ // selected active filter:
+ else if (seln == hangar->NumSquadrons()+1) {
+ int item = 0;
+
+ for (int i = 0; i < hangar->NumSquadrons(); i++) {
+ int nslots = hangar->SquadronSize(i);
+
+ for (int j = 0; j < nslots; j++) {
+ const HangarSlot* s = hangar->GetSlot(i,j);
+
+ if (hangar->GetState(s) >= Hangar::ACTIVE) {
+ char txt[32];
+ sprintf(txt, " %2d ", item+1);
+
+ hangar_list->AddItemWithData(txt, item); // use data for sort
+
+ if (hangar->GetShip(s))
+ hangar_list->SetItemText(item, 1, hangar->GetShip(s)->Name());
+
+ hangar_list->SetItemText(item, 2, hangar->StatusName(s));
+
+ if (hangar->GetPackageElement(s))
+ hangar_list->SetItemText(item, 3, Mission::RoleName(hangar->GetPackageElement(s)->Type()));
+
+ FormatTime(txt, hangar->TimeRemaining(s));
+ hangar_list->SetItemText(item, 4, txt);
+
+ hangar_list->SetItemData(item, 1, i);
+ hangar_list->SetItemData(item, 2, j);
+
+ item++;
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FltDlg::OnPackage(AWEvent* event)
+{
+ if (!filter_list || !hangar_list || !ship) return;
+
+ int code = Mission::PATROL;
+
+ switch (mission_type) {
+ case 0: code = Mission::PATROL; break;
+ case 1: code = Mission::INTERCEPT; break;
+ case 2: code = Mission::ASSAULT; break;
+ case 3: code = Mission::STRIKE; break;
+ case 4: code = Mission::ESCORT; break;
+ case 5: code = Mission::INTEL; break;
+ }
+
+ int squad = filter_list->GetSelectedIndex();
+ Hangar* hangar = ship->GetHangar();
+ Sim* sim = Sim::GetSim();
+ const char* call = sim->FindAvailCallsign(ship->GetIFF());
+ Element* elem = sim->CreateElement(call, ship->GetIFF(), code);
+ Element* tgt = 0;
+ FlightDeck* deck = 0;
+ int queue = 1000;
+ int* load = 0;
+
+ elem->SetSquadron(hangar->SquadronName(squad));
+ elem->SetCarrier(ship);
+
+ if (objective_list) {
+ int index = objective_list->GetListIndex();
+ Text target = objective_list->GetItemText(index);
+
+ Instruction* objective = new(__FILE__,__LINE__) Instruction(code, target.data());
+ elem->AddObjective(objective);
+
+ tgt = sim->FindElement(target.data());
+ }
+
+ if (loadout_list && design) {
+ int index = loadout_list->GetListIndex();
+ Text loadname = loadout_list->GetItemText(index);
+
+ ListIter<ShipLoad> sl = (List<ShipLoad>&) design->loadouts;
+ while (++sl && !load) {
+ if (sl->name == loadname) {
+ load = sl->load;
+ elem->SetLoadout(load);
+ }
+ }
+ }
+
+ for (int i = 0; i < ship->NumFlightDecks(); i++) {
+ FlightDeck* d = ship->GetFlightDeck(i);
+
+ if (d && d->IsLaunchDeck()) {
+ int dq = hangar->PreflightQueue(d);
+
+ if (dq < queue) {
+ queue = dq;
+ deck = d;
+ }
+ }
+ }
+
+ int npackage = 0;
+ int slots[4];
+
+ for (i = 0; i < 4; i++)
+ slots[i] = -1;
+
+ for (i = 0; i < hangar_list->NumItems(); i++) {
+ if (hangar_list->IsSelected(i)) {
+ int nslot = hangar_list->GetItemData(i);
+ hangar->GotoAlert(squad, nslot, deck, elem, load, true);
+ slots[npackage] = nslot;
+ hangar_list->SetSelected(i, false);
+ npackage++;
+
+ if (npackage >= 4)
+ break;
+ }
+ }
+
+ NetUtil::SendElemCreate(elem, squad, slots, false);
+
+ if (flight_planner) {
+ switch (mission_type) {
+ case 0:
+ default:
+ flight_planner->CreatePatrolRoute(elem, patrol_pattern++);
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ if (tgt)
+ flight_planner->CreateStrikeRoute(elem, tgt);
+ else
+ flight_planner->CreatePatrolRoute(elem, patrol_pattern++);
+ break;
+
+ case 4:
+ if (tgt)
+ flight_planner->CreateEscortRoute(elem, tgt);
+ else
+ flight_planner->CreatePatrolRoute(elem, patrol_pattern++);
+ break;
+ }
+
+ if (patrol_pattern < 0 || patrol_pattern > 3)
+ patrol_pattern = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FltDlg::OnAlert(AWEvent* event)
+{
+ if (!filter_list || !hangar_list || !ship) return;
+
+ int squad = filter_list->GetSelectedIndex();
+ Hangar* hangar = ship->GetHangar();
+ Sim* sim = Sim::GetSim();
+ const char* call = sim->FindAvailCallsign(ship->GetIFF());
+ Element* elem = sim->CreateElement(call, ship->GetIFF());
+ FlightDeck* deck = 0;
+ int queue = 1000;
+ const int* load = 0;
+
+ elem->SetSquadron(hangar->SquadronName(squad));
+ elem->SetCarrier(ship);
+
+ for (int i = 0; i < ship->NumFlightDecks(); i++) {
+ FlightDeck* d = ship->GetFlightDeck(i);
+
+ if (d && d->IsLaunchDeck()) {
+ int dq = hangar->PreflightQueue(d);
+
+ if (dq < queue) {
+ queue = dq;
+ deck = d;
+ }
+ }
+ }
+
+ int nalert = 0;
+ int slots[4];
+
+ for (i = 0; i < 4; i++)
+ slots[i] = -1;
+
+ for (i = 0; i < hangar_list->NumItems(); i++) {
+ if (hangar_list->IsSelected(i)) {
+ int nslot = hangar_list->GetItemData(i);
+ slots[nalert] = nslot;
+
+ if (!load) {
+ const HangarSlot* hangar_slot = hangar->GetSlot(squad, nslot);
+ if (hangar_slot) {
+ load = hangar->GetLoadout(hangar_slot);
+ elem->SetLoadout((int*) load);
+ }
+ }
+
+ hangar->GotoAlert(squad, nslot, deck, elem);
+ hangar_list->SetSelected(i, false);
+ nalert++;
+
+ if (nalert >= 4)
+ break;
+ }
+ }
+
+ NetUtil::SendElemCreate(elem, squad, slots, true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FltDlg::OnLaunch(AWEvent* event)
+{
+ if (!filter_list || !hangar_list || !ship) return;
+
+ int squad = filter_list->GetSelectedIndex();
+
+ Hangar* hangar = ship->GetHangar();
+
+ // selected squadron:
+ if (squad < hangar->NumSquadrons()) {
+ for (int i = 0; i < hangar_list->NumItems(); i++) {
+ if (hangar_list->IsSelected(i)) {
+ int nslot = hangar_list->GetItemData(i);
+ hangar->Launch(squad, nslot);
+ NetUtil::SendShipLaunch(ship, squad, nslot);
+ }
+ }
+ }
+
+ // selected pending filter:
+ else if (squad == hangar->NumSquadrons()) {
+ for (int item = 0; item < hangar_list->NumItems(); item++) {
+ if (hangar_list->IsSelected(item)) {
+ int squadron = hangar_list->GetItemData(item, 3);
+ int slot = hangar_list->GetItemData(item, 4);
+
+ if (squadron >= 0 && slot >= 0) {
+ hangar->Launch(squadron, slot);
+ NetUtil::SendShipLaunch(ship, squadron, slot);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FltDlg::OnStandDown(AWEvent* event)
+{
+ if (!filter_list || !hangar_list || !ship) return;
+
+ int seln = filter_list->GetSelectedIndex();
+
+ Hangar* hangar = ship->GetHangar();
+
+ // selected squadron:
+ if (seln < hangar->NumSquadrons()) {
+ for (int i = 0; i < hangar_list->NumItems(); i++) {
+ if (hangar_list->IsSelected(i)) {
+ int nslot = hangar_list->GetItemData(i);
+ hangar->StandDown(seln, nslot);
+ }
+ }
+ }
+
+ // selected pending filter:
+ else if (seln == hangar->NumSquadrons()) {
+ for (int item = 0; item < hangar_list->NumItems(); item++) {
+ if (hangar_list->IsSelected(item)) {
+ int squadron = hangar_list->GetItemData(item, 3);
+ int slot = hangar_list->GetItemData(item, 4);
+
+ if (squadron >= 0 && slot >= 0)
+ hangar->StandDown(squadron, slot);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FltDlg::OnRecall(AWEvent* event)
+{
+ if (!filter_list || !hangar_list || !ship) return;
+
+ int seln = filter_list->GetSelectedIndex();
+
+ Hangar* hangar = ship->GetHangar();
+
+ // selected squadron: or selected active filter:
+ if (seln < hangar->NumSquadrons() || seln == hangar->NumSquadrons()+1) {
+ for (int i = 0; i < hangar_list->NumItems(); i++) {
+ if (hangar_list->IsSelected(i)) {
+ int nsquad = seln;
+ int nslot = hangar_list->GetItemData(i);
+
+ if (seln > hangar->NumSquadrons()) {
+ nsquad = hangar_list->GetItemData(i, 1);
+ nslot = hangar_list->GetItemData(i, 2);
+ }
+
+ const HangarSlot* slot = hangar->GetSlot(nsquad, nslot);
+ Ship* recall = hangar->GetShip(slot);
+
+ if (recall) {
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(recall, ship, RadioMessage::RTB);
+ RadioTraffic::Transmit(msg);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FltDlg::OnMissionType(AWEvent* event)
+{
+ mission_type = -1;
+
+ for (int i = 0; i < 6; i++) {
+ if (mission_btn[i]) {
+ if (mission_btn[i] == event->window) {
+ mission_btn[i]->SetButtonState(1);
+ mission_type = i;
+ }
+ else {
+ mission_btn[i]->SetButtonState(0);
+ }
+ }
+ }
+
+ if (objective_list && mission_type > -1) {
+ objective_list->ClearItems();
+
+ char txt[32];
+ Sim* sim = Sim::GetSim();
+ ListIter<Element> iter = sim->GetElements();
+
+ while (++iter) {
+ Element* elem = iter.value();
+
+ if (!elem->IsActive() || elem->IsFinished() || elem->IsSquadron())
+ continue;
+
+ CombatGroup* group = elem->GetCombatGroup();
+ int iff = elem->GetIFF();
+ Ship* s = elem->GetShip(1);
+ double r = 0;
+ bool con = false;
+
+ if (iff != ship->GetIFF()) {
+ if (elem->IntelLevel() < Intel::LOCATED)
+ continue;
+
+ if (group && group->IntelLevel() < Intel::LOCATED)
+ continue;
+ }
+
+ if (s) {
+ Point s_loc = s->Location() + s->GetRegion()->Location();
+ Point h_loc = ship->Location() + ship->GetRegion()->Location();
+
+ r = (s_loc - h_loc).length();
+
+ con = ship->FindContact(s) != 0;
+
+ if (con) {
+ FormatNumber(txt, r);
+ }
+ else {
+ strcpy(txt, Game::GetText("FltDlg.Unknown").data());
+ r = 2e9;
+ }
+ }
+
+ switch (mission_type) {
+ case 1: // INTERCEPT
+ if (iff && iff != ship->GetIFF() && s && s->IsDropship()) {
+ int item = objective_list->AddItem(elem->Name()) - 1;
+ objective_list->SetItemText(item, 1, s->GetRegion()->Name());
+
+ objective_list->SetItemText(item, 2, txt);
+ objective_list->SetItemData(item, 2, (DWORD) r);
+ }
+ break;
+
+ case 2: // ASSAULT
+ if (iff && iff != ship->GetIFF() && s && (s->IsStarship() || s->IsStatic())) {
+ int item = objective_list->AddItem(elem->Name()) - 1;
+ objective_list->SetItemText(item, 1, s->GetRegion()->Name());
+
+ objective_list->SetItemText(item, 2, txt);
+ objective_list->SetItemData(item, 2, (DWORD) r);
+ }
+ break;
+
+ case 3: // STRIKE
+ if (iff && iff != ship->GetIFF() && s && s->IsGroundUnit()) {
+ int item = objective_list->AddItem(elem->Name()) - 1;
+ objective_list->SetItemText(item, 1, s->GetRegion()->Name());
+
+ objective_list->SetItemText(item, 2, txt);
+ objective_list->SetItemData(item, 2, (DWORD) r);
+ }
+ break;
+
+ case 4: // ESCORT
+ if ((iff == 0 || iff == ship->GetIFF()) && (!s || !s->IsStatic())) {
+ int item = objective_list->AddItem(elem->Name()) - 1;
+
+ if (s) {
+ objective_list->SetItemText(item, 1, s->GetRegion()->Name());
+ objective_list->SetItemText(item, 2, txt);
+ objective_list->SetItemData(item, 2, (DWORD) r);
+ }
+
+ else {
+ objective_list->SetItemText(item, 1, "0");
+ objective_list->SetItemData(item, 1, 0);
+ }
+ }
+ break;
+
+ case 5: // SCOUT?
+ break;
+
+ default: break;
+ }
+ }
+ }
+
+ if (loadout_list && mission_type > -1) {
+ loadout_list->ClearItems();
+
+ if (design) {
+ ListIter<ShipLoad> sl = (List<ShipLoad>&) design->loadouts;
+ while (++sl) {
+ int item = loadout_list->AddItem(sl->name) - 1;
+
+ char weight[32];
+ sprintf(weight, "%d kg", (int) ((design->mass + sl->mass) * 1000));
+ loadout_list->SetItemText(item, 1, weight);
+ loadout_list->SetItemData(item, 1, (DWORD) (sl->mass * 1000));
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FltDlg::OnClose(AWEvent* event)
+{
+ if (manager)
+ manager->CloseTopmost();
+}
+
+
+
diff --git a/Stars45/FltDlg.h b/Stars45/FltDlg.h
new file mode 100644
index 0000000..13deb42
--- /dev/null
+++ b/Stars45/FltDlg.h
@@ -0,0 +1,81 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: FltDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Flight Operations Active Window class
+*/
+
+#ifndef FltDlg_h
+#define FltDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+
+// +--------------------------------------------------------------------+
+
+class FlightPlanner;
+class GameScreen;
+class Ship;
+
+// +--------------------------------------------------------------------+
+
+class FltDlg : public FormWindow
+{
+public:
+ FltDlg(Screen* s, FormDef& def, GameScreen* mgr);
+ virtual ~FltDlg();
+
+ virtual void RegisterControls();
+
+ // Operations:
+ virtual void Show();
+ virtual void Hide();
+
+ virtual void OnFilter(AWEvent* event);
+ virtual void OnPackage(AWEvent* event);
+ virtual void OnAlert(AWEvent* event);
+ virtual void OnLaunch(AWEvent* event);
+ virtual void OnStandDown(AWEvent* event);
+ virtual void OnRecall(AWEvent* event);
+ virtual void OnClose(AWEvent* event);
+ virtual void OnMissionType(AWEvent* event);
+
+ virtual void ExecFrame();
+ void SetShip(Ship* s);
+ void UpdateSelection();
+ void UpdateObjective();
+
+protected:
+ GameScreen* manager;
+
+ ComboBox* filter_list;
+ ListBox* hangar_list;
+
+ Button* package_btn;
+ Button* alert_btn;
+ Button* launch_btn;
+ Button* stand_btn;
+ Button* recall_btn;
+ Button* close_btn;
+
+ int mission_type;
+ Button* mission_btn[6];
+
+ ListBox* objective_list;
+ ListBox* loadout_list;
+
+ Ship* ship;
+ FlightPlanner* flight_planner;
+
+ int patrol_pattern;
+};
+
+#endif FltDlg_h
+
diff --git a/Stars45/Galaxy.cpp b/Stars45/Galaxy.cpp
new file mode 100644
index 0000000..1862459
--- /dev/null
+++ b/Stars45/Galaxy.cpp
@@ -0,0 +1,283 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Galaxy.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Galaxy (list of star systems) for a single campaign.
+*/
+
+#include "MemDebug.h"
+#include "Galaxy.h"
+#include "StarSystem.h"
+#include "Starshatter.h"
+
+#include "Game.h"
+#include "Solid.h"
+#include "Sprite.h"
+#include "Light.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "ParseUtil.h"
+
+static Galaxy* galaxy = 0;
+
+// +--------------------------------------------------------------------+
+
+Galaxy::Galaxy(const char* n)
+ : name(n), radius(10)
+{ }
+
+// +--------------------------------------------------------------------+
+
+Galaxy::~Galaxy()
+{
+ Print(" Destroying Galaxy %s\n", (const char*) name);
+ systems.destroy();
+ stars.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Galaxy::Initialize()
+{
+ if (galaxy) delete galaxy;
+ galaxy = new(__FILE__,__LINE__) Galaxy("Galaxy");
+ galaxy->Load();
+}
+
+void
+Galaxy::Close()
+{
+ delete galaxy;
+ galaxy = 0;
+}
+
+Galaxy*
+Galaxy::GetInstance()
+{
+ return galaxy;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Galaxy::Load()
+{
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Galaxy/");
+ sprintf(filename, "%s.def", (const char*) name);
+ Load(filename);
+
+ // load mod galaxies:
+ List<Text> mod_galaxies;
+ loader->SetDataPath("Mods/Galaxy/");
+ loader->ListFiles("*.def", mod_galaxies);
+
+ ListIter<Text> iter = mod_galaxies;
+ while (++iter) {
+ Text* name = iter.value();
+
+ if (!name->contains("/")) {
+ loader->SetDataPath("Mods/Galaxy/");
+ Load(name->data());
+ }
+ }
+}
+
+void
+Galaxy::Load(const char* filename)
+{
+ Print("\nLoading Galaxy: %s\n", filename);
+
+ BYTE* block = 0;
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->LoadBuffer(filename, block, true);
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
+
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("WARNING: could not parse '%s'\n", filename);
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "GALAXY") {
+ Print("WARNING: invalid galaxy file '%s'\n", filename);
+ return;
+ }
+ }
+
+ // parse the galaxy:
+ do {
+ delete term;
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "radius") {
+ GetDefNumber(radius, def, filename);
+ }
+
+ else if (def->name()->value() == "system") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: system struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+
+ char sys_name[32];
+ char classname[32];
+ Vec3 sys_loc;
+ int sys_iff = 0;
+ int star_class = Star::G;
+
+ sys_name[0] = 0;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "name")
+ GetDefText(sys_name, pdef, filename);
+
+ else if (pdef->name()->value() == "loc")
+ GetDefVec(sys_loc, pdef, filename);
+
+ else if (pdef->name()->value() == "iff")
+ GetDefNumber(sys_iff, pdef, filename);
+
+ else if (pdef->name()->value() == "class") {
+ GetDefText(classname, pdef, filename);
+
+ switch (classname[0]) {
+ case 'O': star_class = Star::O; break;
+ case 'B': star_class = Star::B; break;
+ case 'A': star_class = Star::A; break;
+ case 'F': star_class = Star::F; break;
+ case 'G': star_class = Star::G; break;
+ case 'K': star_class = Star::K; break;
+ case 'M': star_class = Star::M; break;
+ case 'R': star_class = Star::RED_GIANT; break;
+ case 'W': star_class = Star::WHITE_DWARF; break;
+ case 'Z': star_class = Star::BLACK_HOLE; break;
+ }
+ }
+ }
+ }
+
+ if (sys_name[0]) {
+ StarSystem* star_system = new(__FILE__,__LINE__) StarSystem(sys_name, sys_loc, sys_iff, star_class);
+ star_system->Load();
+ systems.append(star_system);
+
+ Star* star = new(__FILE__,__LINE__) Star(sys_name, sys_loc, star_class);
+ stars.append(star);
+ }
+ }
+ }
+
+ else if (def->name()->value() == "star") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: star struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+
+ char star_name[32];
+ char classname[32];
+ Vec3 star_loc;
+ int star_class = Star::G;
+
+ star_name[0] = 0;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "name")
+ GetDefText(star_name, pdef, filename);
+
+ else if (pdef->name()->value() == "loc")
+ GetDefVec(star_loc, pdef, filename);
+
+ else if (pdef->name()->value() == "class") {
+ GetDefText(classname, pdef, filename);
+
+ switch (classname[0]) {
+ case 'O': star_class = Star::O; break;
+ case 'B': star_class = Star::B; break;
+ case 'A': star_class = Star::A; break;
+ case 'F': star_class = Star::F; break;
+ case 'G': star_class = Star::G; break;
+ case 'K': star_class = Star::K; break;
+ case 'M': star_class = Star::M; break;
+ case 'R': star_class = Star::RED_GIANT; break;
+ case 'W': star_class = Star::WHITE_DWARF; break;
+ case 'Z': star_class = Star::BLACK_HOLE; break;
+ }
+ }
+ }
+ }
+
+ if (star_name[0]) {
+ Star* star = new(__FILE__,__LINE__) Star(star_name, star_loc, star_class);
+ stars.append(star);
+ }
+ }
+ }
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+ loader->SetDataPath(0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Galaxy::ExecFrame()
+{
+ ListIter<StarSystem> sys = systems;
+ while (++sys) {
+ sys->ExecFrame();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+StarSystem*
+Galaxy::GetSystem(const char* name)
+{
+ ListIter<StarSystem> sys = systems;
+ while (++sys) {
+ if (!strcmp(sys->Name(), name))
+ return sys.value();
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+StarSystem*
+Galaxy::FindSystemByRegion(const char* rgn_name)
+{
+ ListIter<StarSystem> iter = systems;
+ while (++iter) {
+ StarSystem* sys = iter.value();
+ if (sys->FindRegion(rgn_name))
+ return sys;
+ }
+
+ return 0;
+}
diff --git a/Stars45/Galaxy.h b/Stars45/Galaxy.h
new file mode 100644
index 0000000..deacefb
--- /dev/null
+++ b/Stars45/Galaxy.h
@@ -0,0 +1,74 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Galaxy.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Galaxy (list of star systems) for a single campaign.
+*/
+
+#ifndef Galaxy_h
+#define Galaxy_h
+
+#include "Types.h"
+#include "Solid.h"
+#include "Bitmap.h"
+#include "Geometry.h"
+#include "Text.h"
+#include "Term.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Star;
+class StarSystem;
+class Graphic;
+class Light;
+class Scene;
+
+// +--------------------------------------------------------------------+
+
+class Galaxy
+{
+public:
+ Galaxy(const char* name);
+ virtual ~Galaxy();
+
+ int operator == (const Galaxy& s) const { return name == s.name; }
+
+ // operations:
+ virtual void Load();
+ virtual void Load(const char* filename);
+ virtual void ExecFrame();
+
+ // accessors:
+ const char* Name() const { return name; }
+ const char* Description() const { return description; }
+ List<StarSystem>& GetSystemList() { return systems; }
+ List<Star>& Stars() { return stars; }
+ double Radius() const { return radius; }
+
+ StarSystem* GetSystem(const char* name);
+ StarSystem* FindSystemByRegion(const char* rgn_name);
+
+ static void Initialize();
+ static void Close();
+ static Galaxy* GetInstance();
+
+protected:
+ char filename[64];
+ Text name;
+ Text description;
+ double radius; // radius in parsecs
+
+ List<StarSystem> systems;
+ List<Star> stars;
+};
+
+#endif Galaxy_h
+
diff --git a/Stars45/GameScreen.cpp b/Stars45/GameScreen.cpp
new file mode 100644
index 0000000..3d6471a
--- /dev/null
+++ b/Stars45/GameScreen.cpp
@@ -0,0 +1,1254 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars
+ FILE: GameScreen.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include "MemDebug.h"
+#include "GameScreen.h"
+
+#include "Sim.h"
+#include "Ship.h"
+#include "DisplayView.h"
+#include "HUDView.h"
+#include "HUDSounds.h"
+#include "WepView.h"
+#include "QuantumView.h"
+#include "QuitView.h"
+#include "RadioView.h"
+#include "TacticalView.h"
+#include "NavDlg.h"
+#include "EngDlg.h"
+#include "FltDlg.h"
+#include "AudDlg.h"
+#include "VidDlg.h"
+#include "OptDlg.h"
+#include "CtlDlg.h"
+#include "KeyDlg.h"
+#include "JoyDlg.h"
+#include "ModDlg.h"
+#include "ModInfoDlg.h"
+#include "FormDef.h"
+#include "Starshatter.h"
+#include "CameraDirector.h"
+
+#include "Game.h"
+#include "Video.h"
+#include "Screen.h"
+#include "Window.h"
+#include "ActiveWindow.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "MouseController.h"
+#include "CameraView.h"
+#include "FadeView.h"
+#include "Color.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "FontMgr.h"
+#include "EventDispatch.h"
+#include "DataLoader.h"
+#include "Resource.h"
+
+// +--------------------------------------------------------------------+
+
+GameScreen* GameScreen::game_screen = 0;
+
+static MouseController* mouse_con = 0;
+static bool mouse_active = false;
+
+// +--------------------------------------------------------------------+
+
+GameScreen::GameScreen()
+ : screen(0), gamewin(0),
+ navdlg(0), wep_view(0), engdlg(0), fltdlg(0),
+ HUDfont(0), GUIfont(0), GUI_small_font(0), title_font(0), cam_view(0),
+ isShown(false), disp_view(0), hud_view(0), tac_view(0), radio_view(0),
+ quantum_view(0), quit_view(0), ctldlg(0), joydlg(0), keydlg(0), auddlg(0),
+ viddlg(0), moddlg(0), modInfoDlg(0),
+ flare1(0), flare2(0), flare3(0), flare4(0),
+ optdlg(0), cam_dir(0), sim(0)
+{
+ cam_dir = new(__FILE__,__LINE__) CameraDirector;
+ sim = Sim::GetSim();
+ loader = DataLoader::GetLoader();
+
+ // find the fonts:
+ HUDfont = FontMgr::Find("HUD");
+ GUIfont = FontMgr::Find("GUI");
+ GUI_small_font = FontMgr::Find("GUIsmall");
+ title_font = FontMgr::Find("Title");
+
+ loader->LoadTexture("flare0+.pcx", flare1, Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("flare2.pcx", flare2, Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("flare3.pcx", flare3, Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("flare4.pcx", flare4, Bitmap::BMP_TRANSLUCENT);
+
+ mouse_con = MouseController::GetInstance();
+ game_screen = this;
+}
+
+GameScreen::~GameScreen()
+{
+ TearDown();
+ game_screen = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::Setup(Screen* s)
+{
+ if (!s)
+ return;
+
+ screen = s;
+
+ loader->UseFileSystem(true);
+
+ // create windows
+ gamewin = new(__FILE__,__LINE__) Window(screen, 0, 0, screen->Width(), screen->Height());
+
+ // attach views to windows (back to front)
+ // fade in:
+ gamewin->AddView(new(__FILE__,__LINE__) FadeView(gamewin, 1, 0, 0));
+
+ // camera:
+ cam_dir = CameraDirector::GetInstance();
+ cam_view = new(__FILE__,__LINE__) CameraView(gamewin, cam_dir->GetCamera(), 0);
+
+ if (cam_view)
+ gamewin->AddView(cam_view);
+
+ // HUD:
+ hud_view = new(__FILE__,__LINE__) HUDView(gamewin);
+ gamewin->AddView(hud_view);
+
+ wep_view = new(__FILE__,__LINE__) WepView(gamewin);
+ gamewin->AddView(wep_view);
+
+ quantum_view = new(__FILE__,__LINE__) QuantumView(gamewin);
+ gamewin->AddView(quantum_view);
+
+ radio_view = new(__FILE__,__LINE__) RadioView(gamewin);
+ gamewin->AddView(radio_view);
+
+ tac_view = new(__FILE__,__LINE__) TacticalView(gamewin, this);
+ gamewin->AddView(tac_view);
+
+ disp_view = DisplayView::GetInstance();
+
+ // note: quit view must be last in chain
+ // so it can release window surface
+ quit_view = new(__FILE__,__LINE__) QuitView(gamewin);
+ gamewin->AddView(quit_view);
+
+ Starshatter* stars = Starshatter::GetInstance();
+
+ // initialize lens flare bitmaps:
+ if (stars->LensFlare()) {
+ cam_view->LensFlareElements(flare1, flare4, flare2, flare3);
+ cam_view->LensFlare(true);
+ }
+
+ // if lens flare disabled, just create the corona:
+ else if (stars->Corona()) {
+ cam_view->LensFlareElements(flare1, 0, 0, 0);
+ cam_view->LensFlare(true);
+ }
+
+ screen->AddWindow(gamewin);
+
+ FormDef aud_def("AudDlg", 0);
+ aud_def.Load("AudDlg");
+ auddlg = new(__FILE__,__LINE__) AudDlg(screen, aud_def, this);
+
+ FormDef ctl_def("CtlDlg", 0);
+ ctl_def.Load("CtlDlg");
+ ctldlg = new(__FILE__,__LINE__) CtlDlg(screen, ctl_def, this);
+
+ FormDef opt_def("OptDlg", 0);
+ opt_def.Load("OptDlg");
+ optdlg = new(__FILE__,__LINE__) OptDlg(screen, opt_def, this);
+
+ FormDef vid_def("VidDlg", 0);
+ vid_def.Load("VidDlg");
+ viddlg = new(__FILE__,__LINE__) VidDlg(screen, vid_def, this);
+
+ FormDef mod_def("ModDlg", 0);
+ mod_def.Load("ModDlg");
+ moddlg = new(__FILE__,__LINE__) ModDlg(screen, mod_def, this);
+
+ FormDef joy_def("JoyDlg", 0);
+ joy_def.Load("JoyDlg");
+ joydlg = new(__FILE__,__LINE__) JoyDlg(screen, joy_def, this);
+
+ FormDef key_def("KeyDlg", 0);
+ key_def.Load("KeyDlg");
+ keydlg = new(__FILE__,__LINE__) KeyDlg(screen, key_def, this);
+
+ FormDef mod_info_def("ModInfoDlg", 0);
+ mod_info_def.Load("ModInfoDlg");
+ modInfoDlg = new(__FILE__,__LINE__) ModInfoDlg(screen, mod_info_def, this);
+
+ FormDef nav_def("NavDlg", 0);
+ nav_def.Load("NavDlg");
+ navdlg = new(__FILE__,__LINE__) NavDlg(screen, nav_def, this);
+
+ FormDef eng_def("EngDlg", 0);
+ eng_def.Load("EngDlg");
+ engdlg = new(__FILE__,__LINE__) EngDlg(screen, eng_def, this);
+
+ FormDef flt_def("FltDlg", 0);
+ flt_def.Load("FltDlg");
+ fltdlg = new(__FILE__,__LINE__) FltDlg(screen, flt_def, this);
+
+ if (engdlg) engdlg->Hide();
+ if (fltdlg) fltdlg->Hide();
+ if (navdlg) navdlg->Hide();
+ if (auddlg) auddlg->Hide();
+ if (viddlg) viddlg->Hide();
+ if (optdlg) optdlg->Hide();
+ if (ctldlg) ctldlg->Hide();
+ if (keydlg) keydlg->Hide();
+ if (joydlg) joydlg->Hide();
+ if (moddlg) moddlg->Hide();
+ if (modInfoDlg) modInfoDlg->Hide();
+
+ screen->SetBackgroundColor(Color::Black);
+
+ frame_rate = 0;
+
+ loader->UseFileSystem(Starshatter::UseFileSystem());
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::TearDown()
+{
+ if (gamewin && disp_view)
+ gamewin->DelView(disp_view);
+
+ if (screen) {
+ screen->DelWindow(engdlg);
+ screen->DelWindow(fltdlg);
+ screen->DelWindow(navdlg);
+ screen->DelWindow(modInfoDlg);
+ screen->DelWindow(auddlg);
+ screen->DelWindow(viddlg);
+ screen->DelWindow(optdlg);
+ screen->DelWindow(moddlg);
+ screen->DelWindow(ctldlg);
+ screen->DelWindow(keydlg);
+ screen->DelWindow(joydlg);
+ screen->DelWindow(gamewin);
+ }
+
+ delete engdlg;
+ delete fltdlg;
+ delete navdlg;
+ delete modInfoDlg;
+ delete auddlg;
+ delete viddlg;
+ delete optdlg;
+ delete moddlg;
+ delete ctldlg;
+ delete keydlg;
+ delete joydlg;
+ delete gamewin;
+ delete cam_dir;
+
+ engdlg = 0;
+ fltdlg = 0;
+ navdlg = 0;
+ modInfoDlg = 0;
+ auddlg = 0;
+ viddlg = 0;
+ optdlg = 0;
+ moddlg = 0;
+ ctldlg = 0;
+ keydlg = 0;
+ joydlg = 0;
+ gamewin = 0;
+ screen = 0;
+ cam_dir = 0;
+ cam_view = 0;
+ disp_view = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::FrameRate(double f)
+{
+ frame_rate = f;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::SetFieldOfView(double fov)
+{
+ cam_view->SetFieldOfView(fov);
+}
+
+// +--------------------------------------------------------------------+
+
+double
+GameScreen::GetFieldOfView() const
+{
+ return cam_view->GetFieldOfView();
+}
+
+// +--------------------------------------------------------------------+
+
+Bitmap*
+GameScreen::GetLensFlare(int index)
+{
+ switch (index) {
+ case 0: return flare1;
+ case 1: return flare2;
+ case 2: return flare3;
+ case 3: return flare4;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::ExecFrame()
+{
+ sim = Sim::GetSim();
+
+ if (sim) {
+ cam_view->UseCamera(CameraDirector::GetInstance()->GetCamera());
+ cam_view->UseScene(sim->GetScene());
+
+ Ship* player = sim->GetPlayerShip();
+
+ if (player) {
+ bool dialog_showing = false;
+
+ if (hud_view) {
+ hud_view->UseCameraView(cam_view);
+ hud_view->ExecFrame();
+ }
+
+ if (quit_view && quit_view->IsMenuShown()) {
+ quit_view->ExecFrame();
+ dialog_showing = true;
+ }
+
+ if (navdlg && navdlg->IsShown()) {
+ navdlg->SetShip(player);
+ navdlg->ExecFrame();
+ dialog_showing = true;
+ }
+
+ if (engdlg && engdlg->IsShown()) {
+ engdlg->SetShip(player);
+ engdlg->ExecFrame();
+ dialog_showing = true;
+ }
+
+ if (fltdlg && fltdlg->IsShown()) {
+ fltdlg->SetShip(player);
+ fltdlg->ExecFrame();
+ dialog_showing = true;
+ }
+
+ if (auddlg && auddlg->IsShown()) {
+ auddlg->ExecFrame();
+ dialog_showing = true;
+ }
+
+ if (viddlg && viddlg->IsShown()) {
+ viddlg->ExecFrame();
+ dialog_showing = true;
+ }
+
+ if (optdlg && optdlg->IsShown()) {
+ optdlg->ExecFrame();
+ dialog_showing = true;
+ }
+
+ if (ctldlg && ctldlg->IsShown()) {
+ ctldlg->ExecFrame();
+ dialog_showing = true;
+ }
+
+ if (keydlg && keydlg->IsShown()) {
+ keydlg->ExecFrame();
+ dialog_showing = true;
+ }
+
+ if (joydlg && joydlg->IsShown()) {
+ joydlg->ExecFrame();
+ dialog_showing = true;
+ }
+
+ if (moddlg && moddlg->IsShown()) {
+ moddlg->ExecFrame();
+ dialog_showing = true;
+ }
+
+ if (quantum_view && !dialog_showing) {
+ quantum_view->ExecFrame();
+ }
+
+ if (radio_view && !dialog_showing) {
+ radio_view->ExecFrame();
+ }
+
+ if (wep_view && !dialog_showing) {
+ wep_view->ExecFrame();
+ }
+
+ if (tac_view && !dialog_showing) {
+ if (cam_view)
+ tac_view->UseProjector(cam_view->GetProjector());
+ tac_view->ExecFrame();
+ }
+ }
+
+ if (disp_view) {
+ disp_view->ExecFrame();
+ }
+ }
+
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars) {
+ if (stars->LensFlare()) {
+ cam_view->LensFlareElements(flare1, flare4, flare2, flare3);
+ cam_view->LensFlare(true);
+ }
+
+ else if (stars->Corona()) {
+ cam_view->LensFlareElements(flare1, 0, 0, 0);
+ cam_view->LensFlare(true);
+ }
+
+ else {
+ cam_view->LensFlare(false);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::CycleMFDMode(int mfd)
+{
+ if (hud_view)
+ hud_view->CycleMFDMode(mfd);
+}
+
+void
+GameScreen::CycleHUDMode()
+{
+ if (hud_view)
+ hud_view->CycleHUDMode();
+}
+
+void
+GameScreen::CycleHUDColor()
+{
+ if (hud_view)
+ hud_view->CycleHUDColor();
+}
+
+void
+GameScreen::CycleHUDWarn()
+{
+ if (hud_view)
+ hud_view->CycleHUDWarn();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+GameScreen::CloseTopmost()
+{
+ bool processed = false;
+
+ if (!gamewin) return processed;
+
+ if (navdlg && navdlg->IsShown()) {
+ HideNavDlg();
+ processed = true;
+ }
+
+ else if (engdlg && engdlg->IsShown()) {
+ HideEngDlg();
+ processed = true;
+ }
+
+ else if (fltdlg && fltdlg->IsShown()) {
+ HideFltDlg();
+ processed = true;
+ }
+
+ else if (modInfoDlg && modInfoDlg->IsShown()) {
+ HideModInfoDlg();
+ processed = true;
+ }
+
+ else if (keydlg && keydlg->IsShown()) {
+ ShowCtlDlg();
+ processed = true;
+ }
+
+ else if (joydlg && joydlg->IsShown()) {
+ ShowCtlDlg();
+ processed = true;
+ }
+
+ else if (auddlg && auddlg->IsShown()) {
+ CancelOptions();
+ processed = true;
+ }
+
+ else if (viddlg && viddlg->IsShown()) {
+ CancelOptions();
+ processed = true;
+ }
+
+ else if (optdlg && optdlg->IsShown()) {
+ CancelOptions();
+ processed = true;
+ }
+
+ else if (moddlg && moddlg->IsShown()) {
+ CancelOptions();
+ processed = true;
+ }
+
+ else if (ctldlg && ctldlg->IsShown()) {
+ CancelOptions();
+ processed = true;
+ }
+
+ else if (quantum_view && quantum_view->IsMenuShown()) {
+ quantum_view->CloseMenu();
+ processed = true;
+ }
+
+ else if (quit_view && quit_view->IsMenuShown()) {
+ quit_view->CloseMenu();
+ processed = true;
+ }
+
+ else if (radio_view && radio_view->IsMenuShown()) {
+ radio_view->CloseMenu();
+ processed = true;
+ }
+
+ return processed;
+}
+
+static Window* old_disp_win = 0;
+
+void
+GameScreen::Show()
+{
+ if (!isShown) {
+ screen->AddWindow(gamewin);
+ isShown = true;
+
+ if (disp_view) {
+ old_disp_win = disp_view->GetWindow();
+
+ disp_view->SetWindow(gamewin);
+ gamewin->AddView(disp_view);
+ }
+ }
+}
+
+void
+GameScreen::Hide()
+{
+ if (isShown) {
+ HideAll();
+
+ if (disp_view && gamewin) {
+ gamewin->DelView(disp_view);
+ disp_view->SetWindow(old_disp_win);
+ }
+
+ if (engdlg) engdlg->SetShip(0);
+ if (fltdlg) fltdlg->SetShip(0);
+ if (navdlg) navdlg->SetShip(0);
+
+ HUDSounds::StopSound(HUDSounds::SND_RED_ALERT);
+
+ screen->DelWindow(gamewin);
+ isShown = false;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+GameScreen::IsFormShown() const
+{
+ bool form_shown = false;
+
+ if (navdlg && navdlg->IsShown())
+ form_shown = true;
+
+ else if (engdlg && engdlg->IsShown())
+ form_shown = true;
+
+ else if (fltdlg && fltdlg->IsShown())
+ form_shown = true;
+
+ else if (auddlg && auddlg->IsShown())
+ form_shown = true;
+
+ else if (viddlg && viddlg->IsShown())
+ form_shown = true;
+
+ else if (optdlg && optdlg->IsShown())
+ form_shown = true;
+
+ else if (moddlg && moddlg->IsShown())
+ form_shown = true;
+
+ else if (ctldlg && ctldlg->IsShown())
+ form_shown = true;
+
+ else if (keydlg && keydlg->IsShown())
+ form_shown = true;
+
+ else if (joydlg && joydlg->IsShown())
+ form_shown = true;
+
+ return form_shown;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::ShowExternal()
+{
+ if (!gamewin) return;
+
+ if ((navdlg && navdlg->IsShown()) ||
+ (engdlg && engdlg->IsShown()) ||
+ (fltdlg && fltdlg->IsShown()) ||
+ (auddlg && auddlg->IsShown()) ||
+ (viddlg && viddlg->IsShown()) ||
+ (optdlg && optdlg->IsShown()) ||
+ (moddlg && moddlg->IsShown()) ||
+ (ctldlg && ctldlg->IsShown()) ||
+ (keydlg && keydlg->IsShown()) ||
+ (joydlg && joydlg->IsShown()))
+ return;
+
+ gamewin->MoveTo(Rect(0, 0, screen->Width(), screen->Height()));
+ screen->AddWindow(gamewin);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::ShowNavDlg()
+{
+ if (!gamewin) return;
+
+ if (navdlg && !navdlg->IsShown()) {
+ HideAll();
+
+ navdlg->SetSystem(sim->GetStarSystem());
+ navdlg->SetShip(sim->GetPlayerShip());
+ navdlg->Show();
+
+ if (mouse_con) {
+ mouse_active = mouse_con->Active();
+ mouse_con->SetActive(false);
+ }
+
+ Mouse::Show(true);
+ }
+ else {
+ HideNavDlg();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::HideNavDlg()
+{
+ if (!gamewin) return;
+
+ if (navdlg && navdlg->IsShown()) {
+ navdlg->Hide();
+
+ if (mouse_con)
+ mouse_con->SetActive(mouse_active);
+
+ Mouse::Show(false);
+ screen->AddWindow(gamewin);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+GameScreen::IsNavShown()
+{
+ return gamewin && navdlg && navdlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::ShowEngDlg()
+{
+ if (!gamewin) return;
+
+ if (engdlg && !engdlg->IsShown()) {
+ HideAll();
+
+ engdlg->SetShip(sim->GetPlayerShip());
+ engdlg->Show();
+
+ if (mouse_con) {
+ mouse_active = mouse_con->Active();
+ mouse_con->SetActive(false);
+ }
+
+ Mouse::Show(true);
+ }
+ else {
+ HideEngDlg();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::HideEngDlg()
+{
+ if (!gamewin) return;
+
+ if (engdlg && engdlg->IsShown()) {
+ engdlg->Hide();
+
+ if (mouse_con)
+ mouse_con->SetActive(mouse_active);
+
+ Mouse::Show(false);
+ screen->AddWindow(gamewin);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+GameScreen::IsEngShown()
+{
+ return gamewin && engdlg && engdlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::ShowFltDlg()
+{
+ if (!gamewin) return;
+
+ if (fltdlg && !fltdlg->IsShown()) {
+ HideAll();
+
+ fltdlg->SetShip(sim->GetPlayerShip());
+ fltdlg->Show();
+
+ if (mouse_con) {
+ mouse_active = mouse_con->Active();
+ mouse_con->SetActive(false);
+ }
+
+ Mouse::Show(true);
+ }
+ else {
+ HideFltDlg();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::HideFltDlg()
+{
+ if (!gamewin) return;
+
+ if (fltdlg && fltdlg->IsShown()) {
+ fltdlg->Hide();
+
+ if (mouse_con)
+ mouse_con->SetActive(mouse_active);
+
+ Mouse::Show(false);
+ screen->AddWindow(gamewin);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+GameScreen::IsFltShown()
+{
+ return gamewin && fltdlg && fltdlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::ShowAudDlg()
+{
+ if (auddlg) {
+ if (quit_view) {
+ quit_view->CloseMenu();
+ Starshatter::GetInstance()->Pause(true);
+ }
+
+ HideAll();
+
+ auddlg->Show();
+ auddlg->SetTopMost(true);
+
+ if (mouse_con) {
+ mouse_active = mouse_con->Active();
+ mouse_con->SetActive(false);
+ }
+
+ Mouse::Show(true);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::HideAudDlg()
+{
+ if (auddlg && auddlg->IsShown()) {
+ auddlg->Hide();
+ Mouse::Show(false);
+ screen->AddWindow(gamewin);
+
+ if (quit_view)
+ quit_view->ShowMenu();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+GameScreen::IsAudShown()
+{
+ return auddlg && auddlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::ShowVidDlg()
+{
+ if (viddlg) {
+ if (quit_view) {
+ quit_view->CloseMenu();
+ Starshatter::GetInstance()->Pause(true);
+ }
+
+ HideAll();
+
+ viddlg->Show();
+ viddlg->SetTopMost(true);
+
+ if (mouse_con) {
+ mouse_active = mouse_con->Active();
+ mouse_con->SetActive(false);
+ }
+
+ Mouse::Show(true);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::HideVidDlg()
+{
+ if (viddlg && viddlg->IsShown()) {
+ viddlg->Hide();
+ Mouse::Show(false);
+ screen->AddWindow(gamewin);
+
+ if (quit_view)
+ quit_view->ShowMenu();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+GameScreen::IsVidShown()
+{
+ return viddlg && viddlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::ShowOptDlg()
+{
+ if (optdlg) {
+ if (quit_view) {
+ quit_view->CloseMenu();
+ Starshatter::GetInstance()->Pause(true);
+ }
+
+ HideAll();
+
+ optdlg->Show();
+ optdlg->SetTopMost(true);
+
+ if (mouse_con) {
+ mouse_active = mouse_con->Active();
+ mouse_con->SetActive(false);
+ }
+
+ Mouse::Show(true);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::HideOptDlg()
+{
+ if (optdlg && optdlg->IsShown()) {
+ optdlg->Hide();
+ Mouse::Show(false);
+ screen->AddWindow(gamewin);
+
+ if (quit_view)
+ quit_view->ShowMenu();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+GameScreen::IsOptShown()
+{
+ return optdlg && optdlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::ShowModDlg()
+{
+ if (moddlg) {
+ if (quit_view) {
+ quit_view->CloseMenu();
+ Starshatter::GetInstance()->Pause(true);
+ }
+
+ HideAll();
+
+ moddlg->Show();
+ moddlg->SetTopMost(true);
+
+ if (mouse_con) {
+ mouse_active = mouse_con->Active();
+ mouse_con->SetActive(false);
+ }
+
+ Mouse::Show(true);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::HideModDlg()
+{
+ if (moddlg && moddlg->IsShown()) {
+ moddlg->Hide();
+ Mouse::Show(false);
+ screen->AddWindow(gamewin);
+
+ if (quit_view)
+ quit_view->ShowMenu();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+GameScreen::IsModShown()
+{
+ return moddlg && moddlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::ShowModInfoDlg()
+{
+ if (moddlg && modInfoDlg) {
+ if (quit_view) {
+ quit_view->CloseMenu();
+ Starshatter::GetInstance()->Pause(true);
+ }
+
+ HideAll();
+
+ moddlg->Show();
+ moddlg->SetTopMost(false);
+
+ modInfoDlg->Show();
+ modInfoDlg->SetTopMost(true);
+
+ if (mouse_con) {
+ mouse_active = mouse_con->Active();
+ mouse_con->SetActive(false);
+ }
+
+ Mouse::Show(true);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::HideModInfoDlg()
+{
+ ShowModDlg();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+GameScreen::IsModInfoShown()
+{
+ return modInfoDlg && modInfoDlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::ShowCtlDlg()
+{
+ if (ctldlg) {
+ if (quit_view) {
+ quit_view->CloseMenu();
+ Starshatter::GetInstance()->Pause(true);
+ }
+
+ HideAll();
+
+ ctldlg->Show();
+ ctldlg->SetTopMost(true);
+
+ if (mouse_con) {
+ mouse_active = mouse_con->Active();
+ mouse_con->SetActive(false);
+ }
+
+ Mouse::Show(true);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::HideCtlDlg()
+{
+ if (ctldlg && ctldlg->IsShown()) {
+ ctldlg->Hide();
+ Mouse::Show(false);
+ screen->AddWindow(gamewin);
+
+ if (quit_view)
+ quit_view->ShowMenu();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+GameScreen::IsCtlShown()
+{
+ return ctldlg && ctldlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::ShowKeyDlg()
+{
+ if (keydlg) {
+ if (quit_view) {
+ quit_view->CloseMenu();
+ Starshatter::GetInstance()->Pause(true);
+ }
+
+ HideAll();
+
+ if (ctldlg) {
+ ctldlg->Show();
+ ctldlg->SetTopMost(false);
+ }
+
+ if (keydlg) keydlg->Show();
+
+ if (mouse_con) {
+ mouse_con->SetActive(false);
+ }
+
+ Mouse::Show(true);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+GameScreen::IsKeyShown()
+{
+ return keydlg && keydlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::ShowJoyDlg()
+{
+ if (joydlg) {
+ if (quit_view) {
+ quit_view->CloseMenu();
+ Starshatter::GetInstance()->Pause(true);
+ }
+
+ HideAll();
+
+ if (ctldlg) {
+ ctldlg->Show();
+ ctldlg->SetTopMost(false);
+ }
+
+ if (joydlg) joydlg->Show();
+
+ if (mouse_con) {
+ mouse_con->SetActive(false);
+ }
+
+ Mouse::Show(true);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+GameScreen::IsJoyShown()
+{
+ return joydlg && joydlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::ShowWeaponsOverlay()
+{
+ if (wep_view)
+ wep_view->CycleOverlayMode();
+}
+
+void
+GameScreen::HideWeaponsOverlay()
+{
+ if (wep_view)
+ wep_view->SetOverlayMode(0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::HideAll()
+{
+ screen->DelWindow(gamewin);
+
+ if (engdlg) engdlg->Hide();
+ if (fltdlg) fltdlg->Hide();
+ if (navdlg) navdlg->Hide();
+ if (auddlg) auddlg->Hide();
+ if (viddlg) viddlg->Hide();
+ if (optdlg) optdlg->Hide();
+ if (moddlg) moddlg->Hide();
+ if (modInfoDlg) modInfoDlg->Hide();
+ if (ctldlg) ctldlg->Hide();
+ if (keydlg) keydlg->Hide();
+ if (joydlg) joydlg->Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GameScreen::ApplyOptions()
+{
+ if (ctldlg) ctldlg->Apply();
+ if (optdlg) optdlg->Apply();
+ if (auddlg) auddlg->Apply();
+ if (viddlg) viddlg->Apply();
+
+ if (engdlg) engdlg->Hide();
+ if (fltdlg) fltdlg->Hide();
+ if (navdlg) navdlg->Hide();
+ if (ctldlg) ctldlg->Hide();
+ if (auddlg) auddlg->Hide();
+ if (viddlg) viddlg->Hide();
+ if (optdlg) optdlg->Hide();
+ if (moddlg) moddlg->Hide();
+ if (keydlg) keydlg->Hide();
+ if (joydlg) joydlg->Hide();
+
+ Mouse::Show(false);
+ screen->AddWindow(gamewin);
+ Starshatter::GetInstance()->Pause(false);
+}
+
+void
+GameScreen::CancelOptions()
+{
+ if (ctldlg) ctldlg->Cancel();
+ if (optdlg) optdlg->Cancel();
+ if (auddlg) auddlg->Cancel();
+ if (viddlg) viddlg->Cancel();
+
+ if (engdlg) engdlg->Hide();
+ if (fltdlg) fltdlg->Hide();
+ if (navdlg) navdlg->Hide();
+ if (ctldlg) ctldlg->Hide();
+ if (auddlg) auddlg->Hide();
+ if (viddlg) viddlg->Hide();
+ if (optdlg) optdlg->Hide();
+ if (moddlg) moddlg->Hide();
+ if (keydlg) keydlg->Hide();
+ if (joydlg) joydlg->Hide();
+
+ Mouse::Show(false);
+ screen->AddWindow(gamewin);
+ Starshatter::GetInstance()->Pause(false);
+}
diff --git a/Stars45/GameScreen.h b/Stars45/GameScreen.h
new file mode 100644
index 0000000..5f1c762
--- /dev/null
+++ b/Stars45/GameScreen.h
@@ -0,0 +1,192 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: GameScreen.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef GameScreen_h
+#define GameScreen_h
+
+#include "Types.h"
+#include "Bitmap.h"
+#include "Screen.h"
+#include "BaseScreen.h"
+
+// +--------------------------------------------------------------------+
+
+class Screen;
+class Sim;
+class Window;
+class Font;
+
+class NavDlg;
+class EngDlg;
+class FltDlg;
+class CtlDlg;
+class JoyDlg;
+class KeyDlg;
+class ModDlg;
+class ModInfoDlg;
+
+class CameraDirector;
+class DisplayView;
+class HUDView;
+class WepView;
+class QuantumView;
+class QuitView;
+class RadioView;
+class TacticalView;
+class CameraView;
+class PolyRender;
+class Bitmap;
+class DataLoader;
+class Video;
+class VideoFactory;
+
+// +--------------------------------------------------------------------+
+
+class GameScreen : public BaseScreen
+{
+public:
+ GameScreen();
+ virtual ~GameScreen();
+
+ virtual void Setup(Screen* screen);
+ virtual void TearDown();
+ virtual bool CloseTopmost();
+
+ virtual bool IsShown() const { return isShown; }
+ virtual void Show();
+ virtual void Hide();
+
+ virtual bool IsFormShown() const;
+ virtual void ShowExternal();
+
+ virtual void ShowNavDlg();
+ virtual void HideNavDlg();
+ virtual bool IsNavShown();
+ virtual NavDlg* GetNavDlg() { return navdlg; }
+
+ virtual void ShowEngDlg();
+ virtual void HideEngDlg();
+ virtual bool IsEngShown();
+ virtual EngDlg* GetEngDlg() { return engdlg; }
+
+ virtual void ShowFltDlg();
+ virtual void HideFltDlg();
+ virtual bool IsFltShown();
+ virtual FltDlg* GetFltDlg() { return fltdlg; }
+
+ virtual void ShowCtlDlg();
+ virtual void HideCtlDlg();
+ virtual bool IsCtlShown();
+
+ virtual void ShowJoyDlg();
+ virtual bool IsJoyShown();
+
+ virtual void ShowKeyDlg();
+ virtual bool IsKeyShown();
+
+
+ virtual AudDlg* GetAudDlg() const { return auddlg; }
+ virtual VidDlg* GetVidDlg() const { return viddlg; }
+ virtual OptDlg* GetOptDlg() const { return optdlg; }
+ virtual CtlDlg* GetCtlDlg() const { return ctldlg; }
+ virtual JoyDlg* GetJoyDlg() const { return joydlg; }
+ virtual KeyDlg* GetKeyDlg() const { return keydlg; }
+ virtual ModDlg* GetModDlg() const { return moddlg; }
+ virtual ModInfoDlg* GetModInfoDlg() const { return modInfoDlg; }
+
+ virtual void ShowAudDlg();
+ virtual void HideAudDlg();
+ virtual bool IsAudShown();
+
+ virtual void ShowVidDlg();
+ virtual void HideVidDlg();
+ virtual bool IsVidShown();
+
+ virtual void ShowOptDlg();
+ virtual void HideOptDlg();
+ virtual bool IsOptShown();
+
+ virtual void ShowModDlg();
+ virtual void HideModDlg();
+ virtual bool IsModShown();
+
+ virtual void ShowModInfoDlg();
+ virtual void HideModInfoDlg();
+ virtual bool IsModInfoShown();
+
+ virtual void ApplyOptions();
+ virtual void CancelOptions();
+
+ virtual void ShowWeaponsOverlay();
+ virtual void HideWeaponsOverlay();
+
+ void SetFieldOfView(double fov);
+ double GetFieldOfView() const;
+ void CycleMFDMode(int mfd);
+ void CycleHUDMode();
+ void CycleHUDColor();
+ void CycleHUDWarn();
+ void FrameRate(double f);
+ void ExecFrame();
+
+ static GameScreen* GetInstance() { return game_screen; }
+ CameraView* GetCameraView() const { return cam_view; }
+ Bitmap* GetLensFlare(int index);
+
+private:
+ void HideAll();
+
+ Sim* sim;
+ Screen* screen;
+
+ Window* gamewin;
+ NavDlg* navdlg;
+ EngDlg* engdlg;
+ FltDlg* fltdlg;
+ CtlDlg* ctldlg;
+ KeyDlg* keydlg;
+ JoyDlg* joydlg;
+ AudDlg* auddlg;
+ VidDlg* viddlg;
+ OptDlg* optdlg;
+ ModDlg* moddlg;
+ ModInfoDlg* modInfoDlg;
+
+ Font* HUDfont;
+ Font* GUIfont;
+ Font* GUI_small_font;
+ Font* title_font;
+
+ Bitmap* flare1;
+ Bitmap* flare2;
+ Bitmap* flare3;
+ Bitmap* flare4;
+
+ CameraDirector* cam_dir;
+ DisplayView* disp_view;
+ HUDView* hud_view;
+ WepView* wep_view;
+ QuantumView* quantum_view;
+ QuitView* quit_view;
+ TacticalView* tac_view;
+ RadioView* radio_view;
+ CameraView* cam_view;
+ DataLoader* loader;
+
+ double frame_rate;
+ bool isShown;
+
+ static GameScreen* game_screen;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif GameScreen_h
+
diff --git a/Stars45/Grid.cpp b/Stars45/Grid.cpp
new file mode 100644
index 0000000..b4b75c9
--- /dev/null
+++ b/Stars45/Grid.cpp
@@ -0,0 +1,92 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Grid.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Tactical Grid class
+*/
+
+#include "MemDebug.h"
+#include "Grid.h"
+
+#include "Game.h"
+#include "Video.h"
+#include "Window.h"
+
+static const Color DARK_LINE( 8, 8, 8);
+static const Color LITE_LINE(16, 16, 16);
+
+// +--------------------------------------------------------------------+
+
+Grid::Grid(int asize, int astep)
+ : size(asize), step(astep), drawn(0)
+{
+ radius = (float) (size * 1.414);
+}
+
+Grid::~Grid()
+{ }
+
+// +--------------------------------------------------------------------+
+
+int Grid::CollidesWith(Graphic& o) { return 0; }
+
+// +--------------------------------------------------------------------+
+
+void Grid::Render(Video* video, DWORD flags)
+{
+ if (!video || hidden) return;
+
+ int c = 0;
+ Color line;
+
+ for (int i = 0; i <= size; i += step) {
+ Point p1( i, 0, -size); p1 += Location();
+ Point p2( i, 0, size); p2 += Location();
+ Point p3(-i, 0, -size); p3 += Location();
+ Point p4(-i, 0, size); p4 += Location();
+
+ if (c) line = DARK_LINE;
+ else line = LITE_LINE;
+
+ DrawLine(video, p1,p2,line);
+ DrawLine(video, p3,p4,line);
+
+ c++;
+ if (c > 3) c = 0;
+ }
+
+ c = 0;
+
+ for (i = 0; i <= size; i += step) {
+ Point p1(-size, 0, i); p1 += Location();
+ Point p2( size, 0, i); p2 += Location();
+ Point p3(-size, 0, -i); p3 += Location();
+ Point p4( size, 0, -i); p4 += Location();
+
+ if (c) line = DARK_LINE;
+ else line = LITE_LINE;
+
+ DrawLine(video, p1,p2,line);
+ DrawLine(video, p3,p4,line);
+
+ c++;
+ if (c > 3) c = 0;
+ }
+}
+
+void Grid::DrawLine(Video* video, Point& p1, Point& p2, Color grid_color)
+{
+ Vec3 v[2];
+
+ v[0] = p1;
+ v[1] = p2;
+
+ video->DrawLines(1, v, grid_color, Video::BLEND_ADDITIVE);
+}
diff --git a/Stars45/Grid.h b/Stars45/Grid.h
new file mode 100644
index 0000000..6cbea7f
--- /dev/null
+++ b/Stars45/Grid.h
@@ -0,0 +1,50 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Grid.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Tactical Grid
+*/
+
+#ifndef Grid_h
+#define Grid_h
+
+#include "Types.h"
+#include "Graphic.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class Window;
+class Projector;
+class PolyRender;
+
+// +--------------------------------------------------------------------+
+
+class Grid : public Graphic
+{
+public:
+ Grid(int size, int step);
+ virtual ~Grid();
+
+ virtual void Render(Video* video, DWORD flags);
+ virtual int CollidesWith(Graphic& o);
+
+protected:
+ virtual void DrawLine(Video* video, Point& p1, Point& p2, Color c);
+
+ int size;
+ int step;
+ int drawn;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Grid_h
+
diff --git a/Stars45/GroundAI.cpp b/Stars45/GroundAI.cpp
new file mode 100644
index 0000000..c7b7c6b
--- /dev/null
+++ b/Stars45/GroundAI.cpp
@@ -0,0 +1,191 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: GroundAI.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Low-Level Artificial Intelligence class for Ground Units
+*/
+
+#include "MemDebug.h"
+#include "GroundAI.h"
+#include "SteerAI.h"
+#include "System.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Shield.h"
+#include "Sim.h"
+#include "Player.h"
+#include "CarrierAI.h"
+#include "Contact.h"
+#include "Weapon.h"
+#include "WeaponGroup.h"
+
+#include "Game.h"
+#include "Physical.h"
+
+// +----------------------------------------------------------------------+
+
+GroundAI::GroundAI(SimObject* s)
+ : ship((Ship*) s), target(0), subtarget(0), exec_time(0), carrier_ai(0)
+{
+ Sim* sim = Sim::GetSim();
+ Ship* pship = sim->GetPlayerShip();
+ int player_team = 1;
+ int ai_level = 1;
+
+ if (pship)
+ player_team = pship->GetIFF();
+
+ Player* player = Player::GetCurrentPlayer();
+ if (player) {
+ if (ship && ship->GetIFF() && ship->GetIFF() != player_team) {
+ ai_level = player->AILevel();
+ }
+ else if (player->AILevel() == 0) {
+ ai_level = 1;
+ }
+ }
+
+ // evil alien ships are *always* smart:
+ if (ship && ship->GetIFF() > 1 && ship->Design()->auto_roll > 1) {
+ ai_level = 2;
+ }
+
+ if (ship && ship->GetHangar() && ship->GetCommandAILevel() > 0)
+ carrier_ai = new(__FILE__,__LINE__) CarrierAI(ship, ai_level);
+}
+
+
+// +--------------------------------------------------------------------+
+
+GroundAI::~GroundAI()
+{
+ delete carrier_ai;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GroundAI::SetTarget(SimObject* targ, System* sub)
+{
+ if (target != targ) {
+ target = targ;
+
+ if (target)
+ Observe(target);
+ }
+
+ subtarget = sub;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+GroundAI::Update(SimObject* obj)
+{
+ if (obj == target) {
+ target = 0;
+ subtarget = 0;
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+GroundAI::GetObserverName() const
+{
+ static char name[64];
+ sprintf(name, "GroundAI(%s)", ship->Name());
+ return name;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GroundAI::SelectTarget()
+{
+ SimObject* potential_target = 0;
+
+ // pick the closest combatant ship with a different IFF code:
+ double target_dist = 1.0e15;
+
+ Ship* current_ship_target = 0;
+
+ ListIter<Contact> c_iter = ship->ContactList();
+ while (++c_iter) {
+ Contact* contact = c_iter.value();
+ int c_iff = contact->GetIFF(ship);
+ Ship* c_ship = contact->GetShip();
+ Shot* c_shot = contact->GetShot();
+ bool rogue = false;
+
+ if (c_ship)
+ rogue = c_ship->IsRogue();
+
+ if (rogue || c_iff > 0 && c_iff != ship->GetIFF() && c_iff < 1000) {
+ if (c_ship && !c_ship->InTransition()) {
+ // found an enemy, check distance:
+ double dist = (ship->Location() - c_ship->Location()).length();
+
+ if (!current_ship_target || (c_ship->Class() <= current_ship_target->Class() &&
+ dist < target_dist)) {
+ current_ship_target = c_ship;
+ target_dist = dist;
+ }
+ }
+ }
+
+ potential_target = current_ship_target;
+ }
+
+ SetTarget(potential_target);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+GroundAI::Type() const
+{
+ return SteerAI::GROUND;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+GroundAI::ExecFrame(double secs)
+{
+ const int exec_period = 1000;
+
+ if ((int) Game::GameTime() - exec_time > exec_period) {
+ exec_time = (int) Game::GameTime();
+ SelectTarget();
+ }
+
+ if (ship) {
+ Shield* shield = ship->GetShield();
+
+ if (shield)
+ shield->SetPowerLevel(100);
+
+ ListIter<WeaponGroup> iter = ship->Weapons();
+ while (++iter) {
+ WeaponGroup* group = (WeaponGroup*) iter.value();
+
+ if (group->NumWeapons() > 1 && group->CanTarget(Ship::DROPSHIPS))
+ group->SetFiringOrders(Weapon::POINT_DEFENSE);
+ else
+ group->SetFiringOrders(Weapon::AUTO);
+
+ group->SetTarget((Ship*) target, 0);
+ }
+
+ if (carrier_ai)
+ carrier_ai->ExecFrame(secs);
+ }
+}
diff --git a/Stars45/GroundAI.h b/Stars45/GroundAI.h
new file mode 100644
index 0000000..9b8f5a5
--- /dev/null
+++ b/Stars45/GroundAI.h
@@ -0,0 +1,61 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: GroundAI.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Ground Unit (low-level) Artifical Intelligence class
+*/
+
+#ifndef GroundAI_h
+#define GroundAI_h
+
+#include "Types.h"
+#include "SimObject.h"
+#include "Director.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+class System;
+class CarrierAI;
+
+// +--------------------------------------------------------------------+
+
+class GroundAI : public Director,
+ public SimObserver
+{
+public:
+ GroundAI(SimObject* self);
+ virtual ~GroundAI();
+
+ virtual void ExecFrame(double seconds);
+ virtual void SetTarget(SimObject* targ, System* sub=0);
+ virtual SimObject* GetTarget() const { return target; }
+ virtual System* GetSubTarget() const { return subtarget; }
+ virtual int Type() const;
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+protected:
+ virtual void SelectTarget();
+
+ Ship* ship;
+ SimObject* target;
+ System* subtarget;
+ double exec_time;
+ CarrierAI* carrier_ai;
+};
+
+
+// +--------------------------------------------------------------------+
+
+#endif GroundAI_h
+
diff --git a/Stars45/HUDSounds.cpp b/Stars45/HUDSounds.cpp
new file mode 100644
index 0000000..86d1ec8
--- /dev/null
+++ b/Stars45/HUDSounds.cpp
@@ -0,0 +1,118 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: HUDSounds.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ HUDSounds singleton class utility implementation
+*/
+
+#include "MemDebug.h"
+#include "HUDSounds.h"
+#include "AudioConfig.h"
+
+#include "Sound.h"
+#include "DataLoader.h"
+
+// +--------------------------------------------------------------------+
+
+static Sound* mfd_mode = 0;
+static Sound* nav_mode = 0;
+static Sound* wep_mode = 0;
+static Sound* wep_disp = 0;
+static Sound* hud_mode = 0;
+static Sound* hud_widget = 0;
+static Sound* shield_level = 0;
+static Sound* red_alert = 0;
+static Sound* tac_accept = 0;
+static Sound* tac_reject = 0;
+
+// +--------------------------------------------------------------------+
+
+static void LoadInterfaceSound(DataLoader* loader, const char* wave, Sound*& s)
+{
+ loader->LoadSound(wave, s, 0, true); // optional sound effect
+}
+
+void
+HUDSounds::Initialize()
+{
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Sounds/");
+
+ LoadInterfaceSound(loader, "mfd_mode.wav", mfd_mode);
+ LoadInterfaceSound(loader, "nav_mode.wav", nav_mode);
+ LoadInterfaceSound(loader, "wep_mode.wav", wep_mode);
+ LoadInterfaceSound(loader, "wep_disp.wav", wep_disp);
+ LoadInterfaceSound(loader, "hud_mode.wav", hud_mode);
+ LoadInterfaceSound(loader, "hud_widget.wav", hud_widget);
+ LoadInterfaceSound(loader, "shield_level.wav", shield_level);
+ LoadInterfaceSound(loader, "alarm.wav", red_alert);
+ LoadInterfaceSound(loader, "tac_accept.wav", tac_accept);
+ LoadInterfaceSound(loader, "tac_reject.wav", tac_reject);
+
+ if (red_alert)
+ red_alert->SetFlags(Sound::AMBIENT | Sound::LOOP | Sound::LOCKED);
+
+ loader->SetDataPath(0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDSounds::Close()
+{
+ delete mfd_mode;
+ delete nav_mode;
+ delete wep_mode;
+ delete wep_disp;
+ delete hud_mode;
+ delete hud_widget;
+ delete shield_level;
+ delete red_alert;
+ delete tac_accept;
+ delete tac_reject;
+}
+
+void HUDSounds::PlaySound(int n)
+{
+ Sound* sound = 0;
+
+ switch (n) {
+ default:
+ case SND_MFD_MODE: if (mfd_mode) sound = mfd_mode->Duplicate(); break;
+ case SND_NAV_MODE: if (nav_mode) sound = nav_mode->Duplicate(); break;
+ case SND_WEP_MODE: if (wep_mode) sound = wep_mode->Duplicate(); break;
+ case SND_WEP_DISP: if (wep_disp) sound = wep_disp->Duplicate(); break;
+ case SND_HUD_MODE: if (hud_mode) sound = hud_mode->Duplicate(); break;
+ case SND_HUD_WIDGET: if (hud_widget) sound = hud_widget->Duplicate(); break;
+ case SND_SHIELD_LEVEL: if (shield_level) sound = shield_level->Duplicate(); break;
+ case SND_TAC_ACCEPT: if (tac_accept) sound = tac_accept->Duplicate(); break;
+ case SND_TAC_REJECT: if (tac_reject) sound = tac_reject->Duplicate(); break;
+
+ // RED ALERT IS A SPECIAL CASE!
+ case SND_RED_ALERT:
+ if (red_alert) {
+ sound = red_alert;
+ }
+ break;
+ }
+
+ if (sound && !sound->IsPlaying()) {
+ int gui_volume = AudioConfig::GuiVolume();
+ sound->SetVolume(gui_volume);
+ sound->Play();
+ }
+}
+
+void HUDSounds::StopSound(int n)
+{
+ if (n == SND_RED_ALERT && red_alert) {
+ red_alert->Stop();
+ }
+}
diff --git a/Stars45/HUDSounds.h b/Stars45/HUDSounds.h
new file mode 100644
index 0000000..99a40b9
--- /dev/null
+++ b/Stars45/HUDSounds.h
@@ -0,0 +1,46 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: HUDSounds.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ HUDSounds singleton class utility
+*/
+
+#ifndef HUDSounds_h
+#define HUDSounds_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class HUDSounds
+{
+public:
+ enum SOUNDS {
+ SND_MFD_MODE,
+ SND_NAV_MODE,
+ SND_WEP_MODE,
+ SND_WEP_DISP,
+ SND_HUD_MODE,
+ SND_HUD_WIDGET,
+ SND_SHIELD_LEVEL,
+ SND_RED_ALERT,
+ SND_TAC_ACCEPT,
+ SND_TAC_REJECT
+ };
+
+ static void Initialize();
+ static void Close();
+
+ static void PlaySound(int n);
+ static void StopSound(int n);
+};
+
+#endif HUDSounds_h
+
diff --git a/Stars45/HUDView.cpp b/Stars45/HUDView.cpp
new file mode 100644
index 0000000..486ec19
--- /dev/null
+++ b/Stars45/HUDView.cpp
@@ -0,0 +1,4364 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: HUDView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ View class for Heads Up Display
+*/
+
+#include "MemDebug.h"
+#include "HUDView.h"
+#include "HUDSounds.h"
+#include "Ship.h"
+#include "Element.h"
+#include "Computer.h"
+#include "Drive.h"
+#include "Instruction.h"
+#include "NavSystem.h"
+#include "Power.h"
+#include "Shield.h"
+#include "Sensor.h"
+#include "Contact.h"
+#include "ShipDesign.h"
+#include "Shot.h"
+#include "Drone.h"
+#include "Thruster.h"
+#include "Weapon.h"
+#include "WeaponGroup.h"
+#include "FlightDeck.h"
+#include "SteerAI.h"
+#include "Sim.h"
+#include "StarSystem.h"
+#include "Starshatter.h"
+#include "CameraDirector.h"
+#include "MFD.h"
+#include "RadioView.h"
+#include "FormatUtil.h"
+#include "Hoop.h"
+#include "QuantumDrive.h"
+#include "KeyMap.h"
+#include "AudioConfig.h"
+#include "Player.h"
+
+#include "NetGame.h"
+#include "NetPlayer.h"
+
+#include "Color.h"
+#include "CameraView.h"
+#include "Screen.h"
+#include "DataLoader.h"
+#include "Scene.h"
+#include "FontMgr.h"
+#include "Graphic.h"
+#include "Sprite.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "MouseController.h"
+#include "Polygon.h"
+#include "Sound.h"
+#include "Game.h"
+#include "Window.h"
+
+static Bitmap hud_left_air;
+static Bitmap hud_right_air;
+static Bitmap hud_left_fighter;
+static Bitmap hud_right_fighter;
+static Bitmap hud_left_starship;
+static Bitmap hud_right_starship;
+static Bitmap instr_left;
+static Bitmap instr_right;
+static Bitmap warn_left;
+static Bitmap warn_right;
+static Bitmap lead;
+static Bitmap cross;
+static Bitmap cross1;
+static Bitmap cross2;
+static Bitmap cross3;
+static Bitmap cross4;
+static Bitmap fpm;
+static Bitmap hpm;
+static Bitmap pitch_ladder_pos;
+static Bitmap pitch_ladder_neg;
+static Bitmap chase_left;
+static Bitmap chase_right;
+static Bitmap chase_top;
+static Bitmap chase_bottom;
+static Bitmap icon_ship;
+static Bitmap icon_target;
+
+static BYTE* hud_left_shade_air = 0;
+static BYTE* hud_right_shade_air = 0;
+static BYTE* hud_left_shade_fighter = 0;
+static BYTE* hud_right_shade_fighter = 0;
+static BYTE* hud_left_shade_starship = 0;
+static BYTE* hud_right_shade_starship = 0;
+static BYTE* instr_left_shade = 0;
+static BYTE* instr_right_shade = 0;
+static BYTE* warn_left_shade = 0;
+static BYTE* warn_right_shade = 0;
+static BYTE* lead_shade = 0;
+static BYTE* cross_shade = 0;
+static BYTE* cross1_shade = 0;
+static BYTE* cross2_shade = 0;
+static BYTE* cross3_shade = 0;
+static BYTE* cross4_shade = 0;
+static BYTE* fpm_shade = 0;
+static BYTE* hpm_shade = 0;
+static BYTE* pitch_ladder_pos_shade = 0;
+static BYTE* pitch_ladder_neg_shade = 0;
+static BYTE* chase_left_shade = 0;
+static BYTE* chase_right_shade = 0;
+static BYTE* chase_top_shade = 0;
+static BYTE* chase_bottom_shade = 0;
+static BYTE* icon_ship_shade = 0;
+static BYTE* icon_target_shade = 0;
+
+static Sprite* hud_left_sprite = 0;
+static Sprite* hud_right_sprite = 0;
+static Sprite* fpm_sprite = 0;
+static Sprite* hpm_sprite = 0;
+static Sprite* lead_sprite = 0;
+static Sprite* aim_sprite = 0;
+static Sprite* tgt1_sprite = 0;
+static Sprite* tgt2_sprite = 0;
+static Sprite* tgt3_sprite = 0;
+static Sprite* tgt4_sprite = 0;
+static Sprite* chase_sprite = 0;
+static Sprite* instr_left_sprite = 0;
+static Sprite* instr_right_sprite = 0;
+static Sprite* warn_left_sprite = 0;
+static Sprite* warn_right_sprite = 0;
+static Sprite* icon_ship_sprite = 0;
+static Sprite* icon_target_sprite = 0;
+
+static Sound* missile_lock_sound;
+
+const int NUM_HUD_COLORS = 4;
+
+Color standard_hud_colors[NUM_HUD_COLORS] = {
+ Color(130,190,140), // green
+ Color(130,200,220), // cyan
+ Color(250,170, 80), // orange
+// Color(220,220,100), // yellow
+ Color( 16, 16, 16) // dark gray
+ };
+
+Color standard_txt_colors[NUM_HUD_COLORS] = {
+ Color(150,200,170), // green w/ green gray
+ Color(220,220,180), // cyan w/ light yellow
+ Color(220,220, 80), // orange w/ yellow
+// Color(180,200,220), // yellow w/ white
+ Color( 32, 32, 32) // dark gray
+ };
+
+Color night_vision_colors[NUM_HUD_COLORS] = {
+ Color( 20, 80, 20), // green
+ Color( 30, 80, 80), // cyan
+ Color( 80, 80, 20), // yellow
+// Color(180,200,220), // not used
+ Color( 0, 0, 0) // no night vision
+ };
+
+static Font* hud_font = 0;
+static Font* big_font = 0;
+
+static bool mouse_in = false;
+static int mouse_latch = 0;
+static int mouse_index = -1;
+
+static int ship_status = System::NOMINAL;
+static int tgt_status = System::NOMINAL;
+
+// +--------------------------------------------------------------------+
+
+static enum TXT {
+ MAX_CONTACT = 50,
+
+ TXT_CAUTION_TXT = 0,
+ TXT_LAST_CAUTION = 23,
+ TXT_CAM_ANGLE,
+ TXT_CAM_MODE,
+ TXT_PAUSED,
+ TXT_GEAR_DOWN,
+
+ TXT_HUD_MODE,
+ TXT_PRIMARY_WEP,
+ TXT_SECONDARY_WEP,
+ TXT_DECOY,
+ TXT_SHIELD,
+ TXT_AUTO,
+ TXT_SHOOT,
+ TXT_NAV_INDEX,
+ TXT_NAV_ACTION,
+ TXT_NAV_FORMATION,
+ TXT_NAV_SPEED,
+ TXT_NAV_ETR,
+ TXT_NAV_HOLD,
+
+ TXT_SPEED,
+ TXT_RANGE,
+ TXT_CLOSING_SPEED,
+ TXT_THREAT_WARN,
+ TXT_COMPASS,
+ TXT_HEADING,
+ TXT_PITCH,
+ TXT_ALTITUDE,
+ TXT_GFORCE,
+ TXT_MISSILE_T1,
+ TXT_MISSILE_T2,
+ TXT_ICON_SHIP_TYPE,
+ TXT_ICON_TARGET_TYPE,
+ TXT_TARGET_NAME,
+ TXT_TARGET_DESIGN,
+ TXT_TARGET_SHIELD,
+ TXT_TARGET_HULL,
+ TXT_TARGET_SUB,
+ TXT_TARGET_ETA,
+
+ TXT_MSG_1,
+ TXT_MSG_2,
+ TXT_MSG_3,
+ TXT_MSG_4,
+ TXT_MSG_5,
+ TXT_MSG_6,
+
+ TXT_NAV_PT,
+ TXT_SELF,
+ TXT_SELF_NAME,
+ TXT_CONTACT_NAME,
+ TXT_CONTACT_INFO = TXT_CONTACT_NAME + MAX_CONTACT,
+ TXT_LAST = TXT_CONTACT_INFO + MAX_CONTACT,
+
+ TXT_LAST_ACTIVE = TXT_NAV_HOLD,
+ TXT_INSTR_PAGE = TXT_CAUTION_TXT + 6,
+};
+
+static HUDText hud_text[TXT_LAST];
+
+void
+HUDView::DrawHUDText(int index, const char* txt, Rect& rect, int align, int upcase, bool box)
+{
+ if (index < 0 || index >= TXT_LAST)
+ return;
+
+ HUDText& ht = hud_text[index];
+ Color hc = ht.color;
+
+ char txt_buf[256];
+ int n = strlen(txt);
+
+ if (n > 250) n = 250;
+
+ for (int i = 0; i < n; i++) {
+ if (upcase && islower(txt[i]))
+ txt_buf[i] = toupper(txt[i]);
+ else
+ txt_buf[i] = txt[i];
+ }
+
+ txt_buf[i] = 0;
+
+ if (box) {
+ ht.font->DrawText(txt_buf, n, rect, DT_LEFT | DT_SINGLELINE | DT_CALCRECT);
+
+ if ((align & DT_CENTER) != 0) {
+ int cx = width/2;
+ rect.x = cx - rect.w/2;
+ }
+ }
+
+ if (!cockpit_hud_texture && rect.Contains(Mouse::X(), Mouse::Y())) {
+ mouse_in = true;
+
+ if (index <= TXT_LAST_ACTIVE)
+ hc = Color::White;
+
+ if (Mouse::LButton() && !mouse_latch) {
+ mouse_latch = 2;
+ mouse_index = index;
+ }
+ }
+
+ if (cockpit_hud_texture &&
+ index >= TXT_HUD_MODE &&
+ index <= TXT_TARGET_ETA &&
+ ht.font != big_font) {
+
+ Sprite* s = hud_sprite[0];
+
+ int cx = (int) s->Location().x;
+ int cy = (int) s->Location().y;
+ int w2 = s->Width() / 2;
+ int h2 = s->Height() / 2;
+
+ Rect txt_rect(rect);
+ txt_rect.x -= (cx-w2);
+ txt_rect.y -= (cy-h2);
+
+ if (index == TXT_ICON_SHIP_TYPE)
+ txt_rect = Rect(0, 500, 128, 12);
+
+ else if (index == TXT_ICON_TARGET_TYPE)
+ txt_rect = Rect(128, 500, 128, 12);
+
+ ht.font->SetColor(hc);
+ ht.font->DrawText(txt_buf, n, txt_rect, align | DT_SINGLELINE, cockpit_hud_texture);
+ ht.hidden = false;
+ }
+ else {
+ ht.font->SetColor(hc);
+ ht.font->DrawText(txt_buf, n, rect, align | DT_SINGLELINE);
+ ht.rect = rect;
+ ht.hidden = false;
+
+ if (box) {
+ rect.Inflate(3,2);
+ rect.h--;
+ window->DrawRect(rect, hud_color);
+ }
+ }
+}
+
+void
+HUDView::HideHUDText(int index)
+{
+ if (index >= TXT_LAST)
+ return;
+
+ hud_text[index].hidden = true;
+}
+
+// +--------------------------------------------------------------------+
+
+HUDView* HUDView::hud_view = 0;
+bool HUDView::arcade = false;
+bool HUDView::show_fps = false;
+int HUDView::def_color_set = 1;
+int HUDView::gunsight = 1;
+
+// +--------------------------------------------------------------------+
+
+HUDView::HUDView(Window* c)
+ : View(c), projector(0), camview(0),
+ sim(0), ship(0), target(0), mode(HUD_MODE_TAC),
+ tactical(0), overlay(0), cockpit_hud_texture(0),
+ threat(0), active_region(0), transition(false), docking(false),
+ az_ring(0), az_pointer(0), el_ring(0), el_pointer(0), compass_scale(1),
+ show_warn(false), show_inst(false), inst_page(0)
+{
+ hud_view = this;
+
+ sim = Sim::GetSim();
+
+ if (sim)
+ sim->ShowGrid(false);
+
+ int i;
+
+ width = window->Width();
+ height = window->Height();
+ xcenter = (width / 2.0) - 0.5;
+ ycenter = (height / 2.0) + 0.5;
+
+ PrepareBitmap("HUDleftA.pcx", hud_left_air, hud_left_shade_air);
+ PrepareBitmap("HUDrightA.pcx", hud_right_air, hud_right_shade_air);
+ PrepareBitmap("HUDleft.pcx", hud_left_fighter, hud_left_shade_fighter);
+ PrepareBitmap("HUDright.pcx", hud_right_fighter, hud_right_shade_fighter);
+ PrepareBitmap("HUDleft1.pcx", hud_left_starship, hud_left_shade_starship);
+ PrepareBitmap("HUDright1.pcx", hud_right_starship, hud_right_shade_starship);
+ PrepareBitmap("INSTR_left.pcx", instr_left, instr_left_shade);
+ PrepareBitmap("INSTR_right.pcx", instr_right, instr_right_shade);
+ PrepareBitmap("CAUTION_left.pcx", warn_left, warn_left_shade);
+ PrepareBitmap("CAUTION_right.pcx", warn_right, warn_right_shade);
+ PrepareBitmap("hud_icon.pcx", icon_ship, icon_ship_shade);
+ PrepareBitmap("hud_icon.pcx", icon_target, icon_target_shade);
+
+ PrepareBitmap("lead.pcx", lead, lead_shade);
+ PrepareBitmap("cross.pcx", cross, cross_shade);
+ PrepareBitmap("cross1.pcx", cross1, cross1_shade);
+ PrepareBitmap("cross2.pcx", cross2, cross2_shade);
+ PrepareBitmap("cross3.pcx", cross3, cross3_shade);
+ PrepareBitmap("cross4.pcx", cross4, cross4_shade);
+ PrepareBitmap("fpm.pcx", fpm, fpm_shade);
+ PrepareBitmap("hpm.pcx", hpm, hpm_shade);
+ PrepareBitmap("chase_l.pcx", chase_left, chase_left_shade);
+ PrepareBitmap("chase_r.pcx", chase_right, chase_right_shade);
+ PrepareBitmap("chase_t.pcx", chase_top, chase_top_shade);
+ PrepareBitmap("chase_b.pcx", chase_bottom, chase_bottom_shade);
+ PrepareBitmap("ladder1.pcx", pitch_ladder_pos,
+ pitch_ladder_pos_shade);
+ PrepareBitmap("ladder2.pcx", pitch_ladder_neg,
+ pitch_ladder_neg_shade);
+
+ hud_left_air.SetType(Bitmap::BMP_TRANSLUCENT);
+ hud_right_air.SetType(Bitmap::BMP_TRANSLUCENT);
+ hud_left_fighter.SetType(Bitmap::BMP_TRANSLUCENT);
+ hud_right_fighter.SetType(Bitmap::BMP_TRANSLUCENT);
+ hud_left_starship.SetType(Bitmap::BMP_TRANSLUCENT);
+ hud_right_starship.SetType(Bitmap::BMP_TRANSLUCENT);
+ instr_left.SetType(Bitmap::BMP_TRANSLUCENT);
+ instr_right.SetType(Bitmap::BMP_TRANSLUCENT);
+ warn_left.SetType(Bitmap::BMP_TRANSLUCENT);
+ warn_right.SetType(Bitmap::BMP_TRANSLUCENT);
+ icon_ship.SetType(Bitmap::BMP_TRANSLUCENT);
+ icon_target.SetType(Bitmap::BMP_TRANSLUCENT);
+ fpm.SetType(Bitmap::BMP_TRANSLUCENT);
+ hpm.SetType(Bitmap::BMP_TRANSLUCENT);
+ lead.SetType(Bitmap::BMP_TRANSLUCENT);
+ cross.SetType(Bitmap::BMP_TRANSLUCENT);
+ cross1.SetType(Bitmap::BMP_TRANSLUCENT);
+ cross2.SetType(Bitmap::BMP_TRANSLUCENT);
+ cross3.SetType(Bitmap::BMP_TRANSLUCENT);
+ cross4.SetType(Bitmap::BMP_TRANSLUCENT);
+ chase_left.SetType(Bitmap::BMP_TRANSLUCENT);
+ chase_right.SetType(Bitmap::BMP_TRANSLUCENT);
+ chase_top.SetType(Bitmap::BMP_TRANSLUCENT);
+ chase_bottom.SetType(Bitmap::BMP_TRANSLUCENT);
+ pitch_ladder_pos.SetType(Bitmap::BMP_TRANSLUCENT);
+ pitch_ladder_neg.SetType(Bitmap::BMP_TRANSLUCENT);
+
+ hud_left_sprite = new(__FILE__,__LINE__) Sprite(&hud_left_fighter);
+ hud_right_sprite = new(__FILE__,__LINE__) Sprite(&hud_right_fighter);
+ instr_left_sprite = new(__FILE__,__LINE__) Sprite(&instr_left);
+ instr_right_sprite = new(__FILE__,__LINE__) Sprite(&instr_right);
+ warn_left_sprite = new(__FILE__,__LINE__) Sprite(&warn_left);
+ warn_right_sprite = new(__FILE__,__LINE__) Sprite(&warn_right);
+ icon_ship_sprite = new(__FILE__,__LINE__) Sprite(&icon_ship);
+ icon_target_sprite = new(__FILE__,__LINE__) Sprite(&icon_target);
+ fpm_sprite = new(__FILE__,__LINE__) Sprite(&fpm);
+ hpm_sprite = new(__FILE__,__LINE__) Sprite(&hpm);
+ lead_sprite = new(__FILE__,__LINE__) Sprite(&lead);
+ aim_sprite = new(__FILE__,__LINE__) Sprite(&cross);
+ tgt1_sprite = new(__FILE__,__LINE__) Sprite(&cross1);
+ tgt2_sprite = new(__FILE__,__LINE__) Sprite(&cross2);
+ tgt3_sprite = new(__FILE__,__LINE__) Sprite(&cross3);
+ tgt4_sprite = new(__FILE__,__LINE__) Sprite(&cross4);
+ chase_sprite = new(__FILE__,__LINE__) Sprite(&chase_left);
+
+ ZeroMemory(hud_sprite, sizeof(hud_sprite));
+
+ hud_sprite[ 0] = hud_left_sprite;
+ hud_sprite[ 1] = hud_right_sprite;
+ hud_sprite[ 2] = instr_left_sprite;
+ hud_sprite[ 3] = instr_right_sprite;
+ hud_sprite[ 4] = warn_left_sprite;
+ hud_sprite[ 5] = warn_right_sprite;
+ hud_sprite[ 6] = icon_ship_sprite;
+ hud_sprite[ 7] = icon_target_sprite;
+ hud_sprite[ 8] = fpm_sprite;
+ hud_sprite[ 9] = hpm_sprite;
+ hud_sprite[10] = lead_sprite;
+ hud_sprite[11] = aim_sprite;
+ hud_sprite[12] = tgt1_sprite;
+ hud_sprite[13] = tgt2_sprite;
+ hud_sprite[14] = tgt3_sprite;
+ hud_sprite[15] = tgt4_sprite;
+ hud_sprite[16] = chase_sprite;
+
+ double pitch_ladder_UV[8] = { 0.125,0.0625, 0.875,0.0625, 0.875,0, 0.125,0 };
+ double UV[8];
+
+ for (i = 0; i < 15; i++) {
+ pitch_ladder[i] = new(__FILE__,__LINE__) Sprite(&pitch_ladder_pos);
+
+ CopyMemory(UV, pitch_ladder_UV, sizeof(UV));
+ UV[1] = UV[3] = (pitch_ladder_UV[1] * (i ));
+ UV[5] = UV[7] = (pitch_ladder_UV[1] * (i+1));
+
+ pitch_ladder[i]->Reshape(192, 16);
+ pitch_ladder[i]->SetTexCoords(UV);
+ pitch_ladder[i]->SetBlendMode(2);
+ pitch_ladder[i]->Hide();
+ }
+
+ // zero mark at i=15
+ {
+ pitch_ladder[i] = new(__FILE__,__LINE__) Sprite(&pitch_ladder_pos);
+
+ UV[0] = UV[6] = 0;
+ UV[2] = UV[4] = 1;
+ UV[1] = UV[3] = (pitch_ladder_UV[1] * (i+1));
+ UV[5] = UV[7] = (pitch_ladder_UV[1] * (i ));
+
+ pitch_ladder[i]->Reshape(256, 16);
+ pitch_ladder[i]->SetTexCoords(UV);
+ pitch_ladder[i]->SetBlendMode(2);
+ pitch_ladder[i]->Hide();
+ }
+
+ for (i = 16; i < 31; i++) {
+ pitch_ladder[i] = new(__FILE__,__LINE__) Sprite(&pitch_ladder_neg);
+
+ CopyMemory(UV, pitch_ladder_UV, sizeof(UV));
+ UV[1] = UV[3] = (pitch_ladder_UV[1] * (30 - i ));
+ UV[5] = UV[7] = (pitch_ladder_UV[1] * (30 - i+1));
+
+ pitch_ladder[i]->Reshape(192, 16);
+ pitch_ladder[i]->SetTexCoords(UV);
+ pitch_ladder[i]->SetBlendMode(2);
+ pitch_ladder[i]->Hide();
+ }
+
+ for (i = 0; i < 3; i++)
+ mfd[i] = new(__FILE__,__LINE__) MFD(window, i);
+
+ mfd[0]->SetRect(Rect( 8, height - 136, 128, 128));
+ mfd[1]->SetRect(Rect(width - 136, height - 136, 128, 128));
+ mfd[2]->SetRect(Rect( 8, 8, 128, 128));
+
+ hud_left_sprite->MoveTo( Point(width/2-128, height/2, 1));
+ hud_right_sprite->MoveTo(Point(width/2+128, height/2, 1));
+ hud_left_sprite->SetBlendMode(2);
+ hud_left_sprite->SetFilter(0);
+ hud_right_sprite->SetBlendMode(2);
+ hud_right_sprite->SetFilter(0);
+
+ instr_left_sprite->MoveTo( Point(width/2-128, height-128, 1));
+ instr_right_sprite->MoveTo(Point(width/2+128, height-128, 1));
+ instr_left_sprite->SetBlendMode(2);
+ instr_left_sprite->SetFilter(0);
+ instr_right_sprite->SetBlendMode(2);
+ instr_right_sprite->SetFilter(0);
+
+ warn_left_sprite->MoveTo( Point(width/2-128, height-128, 1));
+ warn_right_sprite->MoveTo(Point(width/2+128, height-128, 1));
+ warn_left_sprite->SetBlendMode(2);
+ warn_left_sprite->SetFilter(0);
+ warn_right_sprite->SetBlendMode(2);
+ warn_right_sprite->SetFilter(0);
+
+ icon_ship_sprite->MoveTo( Point( 184, height-72, 1));
+ icon_target_sprite->MoveTo(Point(width - 184, height-72, 1));
+ icon_ship_sprite->SetBlendMode(2);
+ icon_ship_sprite->SetFilter(0);
+ icon_target_sprite->SetBlendMode(2);
+ icon_target_sprite->SetFilter(0);
+
+ fpm_sprite->MoveTo(Point(width/2, height/2, 1));
+ hpm_sprite->MoveTo(Point(width/2, height/2, 1));
+ lead_sprite->MoveTo(Point(width/2, height/2, 1));
+ aim_sprite->MoveTo(Point(width/2, height/2, 1));
+ tgt1_sprite->MoveTo(Point(width/2, height/2, 1));
+ tgt2_sprite->MoveTo(Point(width/2, height/2, 1));
+ tgt3_sprite->MoveTo(Point(width/2, height/2, 1));
+ tgt4_sprite->MoveTo(Point(width/2, height/2, 1));
+
+ fpm_sprite->SetBlendMode(2);
+ hpm_sprite->SetBlendMode(2);
+ lead_sprite->SetBlendMode(2);
+ aim_sprite->SetBlendMode(2);
+ tgt1_sprite->SetBlendMode(2);
+ tgt2_sprite->SetBlendMode(2);
+ tgt3_sprite->SetBlendMode(2);
+ tgt4_sprite->SetBlendMode(2);
+ chase_sprite->SetBlendMode(2);
+
+ fpm_sprite->SetFilter(0);
+ hpm_sprite->SetFilter(0);
+ lead_sprite->SetFilter(0);
+ aim_sprite->SetFilter(0);
+ tgt1_sprite->SetFilter(0);
+ tgt2_sprite->SetFilter(0);
+ tgt3_sprite->SetFilter(0);
+ tgt4_sprite->SetFilter(0);
+ chase_sprite->SetFilter(0);
+
+ lead_sprite->Hide();
+ aim_sprite->Hide();
+ tgt1_sprite->Hide();
+ tgt2_sprite->Hide();
+ tgt3_sprite->Hide();
+ tgt4_sprite->Hide();
+ chase_sprite->Hide();
+
+ aw = chase_left.Width() / 2;
+ ah = chase_left.Height() / 2;
+
+ mfd[0]->SetMode(MFD::MFD_MODE_SHIP);
+ mfd[1]->SetMode(MFD::MFD_MODE_FOV);
+ mfd[2]->SetMode(MFD::MFD_MODE_GAME);
+
+ hud_font = FontMgr::Find("HUD");
+ big_font = FontMgr::Find("GUI");
+
+ for (i = 0; i < TXT_LAST; i++) {
+ hud_text[i].font = hud_font;
+ }
+
+ hud_text[TXT_THREAT_WARN].font = big_font;
+ hud_text[TXT_SHOOT].font = big_font;
+ hud_text[TXT_AUTO].font = big_font;
+
+ SetHUDColorSet(def_color_set);
+ MFD::SetColor(standard_hud_colors[color]);
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("HUD/");
+
+ az_ring = new(__FILE__,__LINE__) Solid;
+ az_pointer = new(__FILE__,__LINE__) Solid;
+ el_ring = new(__FILE__,__LINE__) Solid;
+ el_pointer = new(__FILE__,__LINE__) Solid;
+
+ az_ring->Load("CompassRing.mag", compass_scale);
+ az_pointer->Load("CompassPointer.mag", compass_scale);
+ el_ring->Load("PitchRing.mag", compass_scale);
+ el_pointer->Load("CompassPointer.mag", compass_scale);
+
+ loader->SetDataPath("Sounds/");
+ loader->LoadSound("MissileLock.wav", missile_lock_sound, Sound::LOOP | Sound::LOCKED);
+
+ loader->SetDataPath(0);
+
+ for (i = 0; i < MAX_MSG; i++)
+ msg_time[i] = 0;
+}
+
+HUDView::~HUDView()
+{
+ HideCompass();
+
+ if (missile_lock_sound) {
+ missile_lock_sound->Stop();
+ missile_lock_sound->Release();
+ missile_lock_sound = 0;
+ }
+
+ for (int i = 0; i < 3; i++) {
+ delete mfd[i];
+ mfd[i] = 0;
+ }
+
+ for (i = 0; i < 32; i++) {
+ GRAPHIC_DESTROY(hud_sprite[i]);
+ }
+
+ fpm.ClearImage();
+ hpm.ClearImage();
+ lead.ClearImage();
+ cross.ClearImage();
+ cross1.ClearImage();
+ cross2.ClearImage();
+ cross3.ClearImage();
+ cross4.ClearImage();
+ hud_left_air.ClearImage();
+ hud_right_air.ClearImage();
+ hud_left_fighter.ClearImage();
+ hud_right_fighter.ClearImage();
+ hud_left_starship.ClearImage();
+ hud_right_starship.ClearImage();
+ instr_left.ClearImage();
+ instr_right.ClearImage();
+ warn_left.ClearImage();
+ warn_right.ClearImage();
+ icon_ship.ClearImage();
+ icon_target.ClearImage();
+ chase_left.ClearImage();
+ chase_right.ClearImage();
+ chase_top.ClearImage();
+ chase_bottom.ClearImage();
+ pitch_ladder_pos.ClearImage();
+ pitch_ladder_neg.ClearImage();
+
+ delete [] fpm_shade;
+ delete [] hpm_shade;
+ delete [] lead_shade;
+ delete [] cross_shade;
+ delete [] cross1_shade;
+ delete [] cross2_shade;
+ delete [] cross3_shade;
+ delete [] cross4_shade;
+ delete [] hud_left_shade_air;
+ delete [] hud_right_shade_air;
+ delete [] hud_left_shade_fighter;
+ delete [] hud_right_shade_fighter;
+ delete [] hud_left_shade_starship;
+ delete [] hud_right_shade_starship;
+ delete [] instr_left_shade;
+ delete [] instr_right_shade;
+ delete [] warn_left_shade;
+ delete [] warn_right_shade;
+ delete [] icon_ship_shade;
+ delete [] icon_target_shade;
+ delete [] chase_left_shade;
+ delete [] chase_right_shade;
+ delete [] chase_top_shade;
+ delete [] chase_bottom_shade;
+ delete [] pitch_ladder_pos_shade;
+ delete [] pitch_ladder_neg_shade;
+
+ delete az_ring;
+ delete az_pointer;
+ delete el_ring;
+ delete el_pointer;
+
+ fpm_shade = 0;
+ hpm_shade = 0;
+ cross_shade = 0;
+ cross1_shade = 0;
+ cross2_shade = 0;
+ cross3_shade = 0;
+ cross4_shade = 0;
+ hud_left_shade_air = 0;
+ hud_right_shade_air = 0;
+ hud_left_shade_fighter = 0;
+ hud_right_shade_fighter = 0;
+ hud_left_shade_starship = 0;
+ hud_right_shade_starship = 0;
+ instr_left_shade = 0;
+ instr_right_shade = 0;
+ warn_left_shade = 0;
+ warn_right_shade = 0;
+ icon_ship_shade = 0;
+ icon_target_shade = 0;
+ chase_left_shade = 0;
+ chase_right_shade = 0;
+ chase_top_shade = 0;
+ chase_bottom_shade = 0;
+ pitch_ladder_pos_shade = 0;
+ pitch_ladder_neg_shade = 0;
+
+ az_ring = 0;
+ az_pointer = 0;
+ el_ring = 0;
+ el_pointer = 0;
+
+ hud_view = 0;
+}
+
+void
+HUDView::OnWindowMove()
+{
+ width = window->Width();
+ height = window->Height();
+ xcenter = (width / 2.0) - 0.5;
+ ycenter = (height / 2.0) + 0.5;
+
+ mfd[0]->SetRect(Rect( 8, height - 136, 128, 128));
+ mfd[1]->SetRect(Rect(width - 136, height - 136, 128, 128));
+ mfd[2]->SetRect(Rect( 8, 8, 128, 128));
+
+ hud_left_sprite->MoveTo( Point(width/2-128, height/2, 1));
+ hud_right_sprite->MoveTo(Point(width/2+128, height/2, 1));
+
+ instr_left_sprite->MoveTo( Point(width/2-128, height-128, 1));
+ instr_right_sprite->MoveTo(Point(width/2+128, height-128, 1));
+ warn_left_sprite->MoveTo( Point(width/2-128, height-128, 1));
+ warn_right_sprite->MoveTo(Point(width/2+128, height-128, 1));
+ icon_ship_sprite->MoveTo( Point( 184, height-72, 1));
+ icon_target_sprite->MoveTo(Point(width - 184, height-72, 1));
+
+ for (int i = 0; i < TXT_LAST; i++) {
+ hud_text[i].font = hud_font;
+ hud_text[i].color = standard_txt_colors[color];
+ }
+
+ if (big_font) {
+ hud_text[TXT_THREAT_WARN].font = big_font;
+ hud_text[TXT_SHOOT].font = big_font;
+ hud_text[TXT_AUTO].font = big_font;
+ }
+
+ MFD::SetColor(standard_hud_colors[color]);
+
+ int cx = width/2;
+ int cy = height/2;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::SetTacticalMode(int mode)
+{
+ if (tactical != mode) {
+ tactical = mode;
+
+ if (tactical) {
+ hud_left_sprite->Hide();
+ hud_right_sprite->Hide();
+
+ for (int i = 0; i < 31; i++)
+ pitch_ladder[i]->Hide();
+ }
+ else if (Game::MaxTexSize() > 128) {
+ hud_left_sprite->Show();
+ hud_right_sprite->Show();
+ }
+ }
+}
+
+void
+HUDView::SetOverlayMode(int mode)
+{
+ if (overlay != mode) {
+ overlay = mode;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+HUDView::Update(SimObject* obj)
+{
+ if (obj == ship) {
+ if (target)
+ SetTarget(0);
+
+ ship = 0;
+
+ for (int i = 0; i < 3; i++)
+ mfd[i]->SetShip(ship);
+ }
+
+ if (obj == target) {
+ target = 0;
+ PrepareBitmap("hud_icon.pcx", icon_target, icon_target_shade);
+ ColorizeBitmap(icon_target, icon_target_shade, txt_color);
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+HUDView::GetObserverName() const
+{
+ return "HUDView";
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::UseCameraView(CameraView* v)
+{
+ if (v && camview != v) {
+ camview = v;
+
+ for (int i = 0; i < 3; i++)
+ mfd[i]->UseCameraView(camview);
+
+ projector = camview->GetProjector();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+HUDView::MarkerColor(Contact* contact)
+{
+ Color c(80,80,80);
+
+ if (contact) {
+ Sim* sim = Sim::GetSim();
+ Ship* ship = sim->GetPlayerShip();
+
+ int c_iff = contact->GetIFF(ship);
+
+ c = Ship::IFFColor(c_iff) * contact->Age();
+
+ if (contact->GetShot() && contact->Threat(ship)) {
+ if ((Game::RealTime()/500) & 1)
+ c = c * 2;
+ else
+ c = c * 0.5;
+ }
+ }
+
+ return c;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawContactMarkers()
+{
+ threat = 0;
+
+ for (int i = 0; i < MAX_CONTACT; i++) {
+ HideHUDText(TXT_CONTACT_NAME+i);
+ HideHUDText(TXT_CONTACT_INFO+i);
+ }
+
+
+ if (!ship)
+ return;
+
+ int index = 0;
+ ListIter<Contact> contact = ship->ContactList();
+
+ // draw own sensor contacts:
+ while (++contact) {
+ Contact* c = contact.value();
+
+ // draw track ladder:
+ if (c->TrackLength() > 0 && c->GetShip() != ship) {
+ DrawTrack(c);
+ }
+
+ DrawContact(c, index++);
+ }
+
+ Color c = ship->MarkerColor();
+
+ // draw own ship track ladder:
+ if (CameraDirector::GetCameraMode() == CameraDirector::MODE_ORBIT && ship->TrackLength() > 0) {
+ int ctl = ship->TrackLength();
+
+ Point t1 = ship->Location();
+ Point t2 = ship->TrackPoint(0);
+
+ if (t1 != t2)
+ DrawTrackSegment(t1, t2, c);
+
+ for (int i = 0; i < ctl-1; i++) {
+ t1 = ship->TrackPoint(i);
+ t2 = ship->TrackPoint(i+1);
+
+ if (t1 != t2)
+ DrawTrackSegment(t1, t2, c * ((double) (ctl-i)/ (double) ctl));
+ }
+ }
+
+ // draw own ship marker:
+ Point mark_pt = ship->Location();
+ projector->Transform(mark_pt);
+
+ // clip:
+ if (CameraDirector::GetCameraMode() == CameraDirector::MODE_ORBIT && mark_pt.z > 1.0) {
+ projector->Project(mark_pt);
+
+ int x = (int) mark_pt.x;
+ int y = (int) mark_pt.y;
+
+ if (x > 4 && x < width-4 &&
+ y > 4 && y < height-4) {
+
+ DrawDiamond(x,y,5,c);
+
+ if (tactical) {
+ Rect self_rect(x+8, y-4, 200, 12);
+ DrawHUDText(TXT_SELF, ship->Name(), self_rect, DT_LEFT, HUD_MIXED_CASE);
+
+ if (NetGame::GetInstance()) {
+ Player* p = Player::GetCurrentPlayer();
+ if (p) {
+ Rect net_name_rect(x+8, y+6, 120, 12);
+ DrawHUDText(TXT_SELF_NAME, p->Name(), net_name_rect, DT_LEFT, HUD_MIXED_CASE);
+ }
+ }
+ }
+ }
+ }
+
+ // draw life bars on targeted ship:
+ if (target && target->Type() == SimObject::SIM_SHIP && target->Rep()) {
+ Ship* tgt_ship = (Ship*) target;
+ Graphic* g = tgt_ship->Rep();
+ Rect r = g->ScreenRect();
+
+ Point mark_pt;
+
+ if (tgt_ship)
+ mark_pt = tgt_ship->Location();
+
+ projector->Transform(mark_pt);
+
+ // clip:
+ if (mark_pt.z > 1.0) {
+ projector->Project(mark_pt);
+
+ int x = (int) mark_pt.x;
+ int y = r.y;
+
+ if (y >= 2000)
+ y = (int) mark_pt.y;
+
+ if (x > 4 && x < width-4 &&
+ y > 4 && y < height-4) {
+
+ const int BAR_LENGTH = 40;
+
+ // life bars:
+ int sx = x - BAR_LENGTH/2;
+ int sy = y - 8;
+
+ double hull_strength = tgt_ship->Integrity() / tgt_ship->Design()->integrity;
+
+ int hw = (int) (BAR_LENGTH * hull_strength);
+ int sw = (int) (BAR_LENGTH * (tgt_ship->ShieldStrength() / 100.0));
+
+ System::STATUS s = System::NOMINAL;
+
+ if (hull_strength < 0.30) s = System::CRITICAL;
+ else if (hull_strength < 0.60) s = System::DEGRADED;
+
+ Color hc = GetStatusColor(s);
+ Color sc = hud_color;
+
+ window->FillRect(sx, sy, sx+hw, sy+1, hc);
+ window->FillRect(sx, sy+3, sx+sw, sy+4, sc);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawContact(Contact* contact, int index)
+{
+ if (index >= MAX_CONTACT) return;
+
+ Color c = MarkerColor(contact);
+ int c_iff = contact->GetIFF(ship);
+ Ship* c_ship = contact->GetShip();
+ Shot* c_shot = contact->GetShot();
+ Point mark_pt = contact->Location();
+ double distance = 0;
+
+ if (!c_ship && !c_shot || c_ship == ship)
+ return;
+
+ if (c_ship && c_ship->GetFlightPhase() < Ship::ACTIVE)
+ return;
+
+ if (c_ship) {
+ mark_pt = c_ship->Location();
+
+ if (c_ship->IsGroundUnit())
+ mark_pt += Point(0,150,0);
+ }
+ else {
+ mark_pt = c_shot->Location();
+ }
+
+ projector->Transform(mark_pt);
+
+ // clip:
+ if (mark_pt.z > 1.0) {
+ distance = mark_pt.length();
+
+ projector->Project(mark_pt);
+
+ int x = (int) mark_pt.x;
+ int y = (int) mark_pt.y;
+
+ if (x > 4 && x < width-4 &&
+ y > 4 && y < height-4) {
+
+ DrawDiamond(x,y,3,c);
+
+ if (contact->Threat(ship)) {
+ if (c_ship) {
+ window->DrawEllipse(x-6, y-6, x+6, y+6, c);
+ }
+ else {
+ DrawDiamond(x,y,7,c);
+ }
+ }
+
+ bool name_crowded = false;
+
+ if (x < width-8) {
+ char code = *(Game::GetText("HUDView.symbol.fighter").data());
+
+ if (c_ship) {
+ if (c_ship->Class() > Ship::LCA)
+ code = *(Game::GetText("HUDView.symbol.starship").data());
+ }
+
+ else if (c_shot) {
+ code = *(Game::GetText("HUDView.symbol.torpedo").data());
+ }
+
+ Sensor* sensor = ship->GetSensor();
+ double limit = 75e3;
+
+ if (sensor)
+ limit = sensor->GetBeamRange();
+
+ double range = contact->Range(ship, limit);
+
+ char contact_buf[256];
+ Rect contact_rect(x+8, y-4, 120, 12);
+
+ if (range == 0) {
+ sprintf(contact_buf, "%c *", code);
+ }
+ else {
+ bool mega = false;
+
+ if (range > 999e3) {
+ range /= 1e6;
+ mega = true;
+ }
+ else if (range < 1e3)
+ range = 1;
+ else
+ range /= 1000;
+
+ if (arcade) {
+ if (c_ship)
+ strcpy(contact_buf, c_ship->Name());
+ else if (!mega)
+ sprintf(contact_buf, "%c %d", code, (int) range);
+ else
+ sprintf(contact_buf, "%c %.1f M", code, range);
+ }
+ else {
+ char closing = '+';
+ Point delta_v;
+
+ if (c_ship)
+ delta_v = ship->Velocity() - c_ship->Velocity();
+ else
+ delta_v = ship->Velocity() - c_shot->Velocity();
+
+ if (delta_v * ship->Velocity() < 0) // losing ground
+ closing = '-';
+
+ if (!mega)
+ sprintf(contact_buf, "%c %d%c", code, (int) range, closing);
+ else
+ sprintf(contact_buf, "%c %.1f M", code, range);
+ }
+ }
+
+ if (!IsNameCrowded(x, y)) {
+ DrawHUDText(TXT_CONTACT_INFO+index, contact_buf, contact_rect, DT_LEFT, HUD_MIXED_CASE);
+
+ if (c_shot || (c_ship && (c_ship->IsDropship() || c_ship->IsStatic())))
+ name_crowded = distance > 50e3;
+ }
+ else {
+ name_crowded = true;
+ }
+ }
+
+ bool name_drawn = false;
+ if (NetGame::GetInstance() && c_ship) {
+ NetPlayer* netp = NetGame::GetInstance()->FindPlayerByObjID(c_ship->GetObjID());
+ if (netp && strcmp(netp->Name(), "Server A.I. Ship")) {
+ Rect contact_rect(x+8, y+6, 120, 12);
+ DrawHUDText(TXT_CONTACT_NAME+index, netp->Name(), contact_rect, DT_LEFT, HUD_MIXED_CASE);
+ name_drawn = true;
+ }
+ }
+
+ if (!name_drawn && !name_crowded && c_ship && c_iff < 10 && !arcade) {
+ Rect contact_rect(x+8, y+6, 120, 12);
+ DrawHUDText(TXT_CONTACT_NAME+index, c_ship->Name(), contact_rect, DT_LEFT, HUD_MIXED_CASE);
+ }
+ }
+ }
+
+ if (contact->Threat(ship) && !ship->IsStarship()) {
+ if (threat < 1 && c_ship && !c_ship->IsStarship())
+ threat = 1;
+
+ if (c_shot)
+ threat = 2;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawTrackSegment(Point& t1, Point& t2, Color c)
+{
+ int x1, y1, x2, y2;
+
+ projector->Transform(t1);
+ projector->Transform(t2);
+
+ const double CLIP_Z = 0.1;
+
+ if (t1.z < CLIP_Z && t2.z < CLIP_Z)
+ return;
+
+ if (t1.z < CLIP_Z && t2.z >= CLIP_Z) {
+ double dx = t2.x - t1.x;
+ double dy = t2.y - t1.y;
+ double s = (CLIP_Z - t1.z) / (t2.z - t1.z);
+
+ t1.x += dx * s;
+ t1.y += dy * s;
+ t1.z = CLIP_Z;
+ }
+
+ else if (t2.z < CLIP_Z && t1.z >= CLIP_Z) {
+ double dx = t1.x - t2.x;
+ double dy = t1.y - t2.y;
+ double s = (CLIP_Z - t2.z) / (t1.z - t2.z);
+
+ t2.x += dx * s;
+ t2.y += dy * s;
+ t2.z = CLIP_Z;
+ }
+
+ if (t1.z >= CLIP_Z && t2.z >= CLIP_Z) {
+ projector->Project(t1, false);
+ projector->Project(t2, false);
+
+ x1 = (int) t1.x;
+ y1 = (int) t1.y;
+ x2 = (int) t2.x;
+ y2 = (int) t2.y;
+
+ if (window->ClipLine(x1,y1,x2,y2))
+ window->DrawLine(x1,y1,x2,y2,c);
+ }
+}
+
+void
+HUDView::DrawTrack(Contact* contact)
+{
+ Ship* c_ship = contact->GetShip();
+
+ if (c_ship && c_ship->GetFlightPhase() < Ship::ACTIVE)
+ return;
+
+ int ctl = contact->TrackLength();
+ Color c = MarkerColor(contact);
+
+ Point t1 = contact->Location();
+ Point t2 = contact->TrackPoint(0);
+
+ if (t1 != t2)
+ DrawTrackSegment(t1, t2, c);
+
+ for (int i = 0; i < ctl-1; i++) {
+ t1 = contact->TrackPoint(i);
+ t2 = contact->TrackPoint(i+1);
+
+ if (t1 != t2)
+ DrawTrackSegment(t1, t2, c * ((double) (ctl-i)/ (double) ctl));
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawRect(SimObject* targ)
+{
+ Graphic* g = targ->Rep();
+ Rect r = g->ScreenRect();
+ Color c;
+
+ if (targ->Type() == SimObject::SIM_SHIP)
+ c = ((Ship*) targ)->MarkerColor();
+ else
+ c = ((Shot*) targ)->MarkerColor();
+
+ if (r.w > 0 && r.h > 0) {
+ if (r.w < 8) {
+ r.x -= (8-r.w)/2;
+ r.w = 8;
+ }
+
+ if (r.h < 8) {
+ r.y -= (8-r.h)/2;
+ r.h = 8;
+ }
+ }
+
+ else {
+ Point mark_pt = targ->Location();
+ projector->Transform(mark_pt);
+
+ // clip:
+ if (mark_pt.z < 1.0)
+ return;
+
+ projector->Project(mark_pt);
+
+ int x = (int) mark_pt.x;
+ int y = (int) mark_pt.y;
+
+ if (x < 4 || x > width-4 || y < 4 || y > height-4)
+ return;
+
+ r.x = x-4;
+ r.y = y-4;
+ r.w = 8;
+ r.h = 8;
+ }
+
+ // horizontal
+ window->DrawLine(r.x, r.y, r.x+8, r.y, c);
+ window->DrawLine(r.x+r.w-8, r.y, r.x+r.w, r.y, c);
+ window->DrawLine(r.x, r.y+r.h, r.x+8, r.y+r.h, c);
+ window->DrawLine(r.x+r.w-8, r.y+r.h, r.x+r.w, r.y+r.h, c);
+ // vertical
+ window->DrawLine(r.x, r.y, r.x, r.y+8, c);
+ window->DrawLine(r.x, r.y+r.h-8, r.x, r.y+r.h, c);
+ window->DrawLine(r.x+r.w, r.y, r.x+r.w, r.y+8, c);
+ window->DrawLine(r.x+r.w, r.y+r.h-8, r.x+r.w, r.y+r.h, c);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawBars()
+{
+ fpm_sprite->Hide();
+ hpm_sprite->Hide();
+ lead_sprite->Hide();
+ aim_sprite->Hide();
+ tgt1_sprite->Hide();
+ tgt2_sprite->Hide();
+ tgt3_sprite->Hide();
+ tgt4_sprite->Hide();
+ chase_sprite->Hide();
+
+ for (int i = 0; i < 31; i++)
+ pitch_ladder[i]->Hide();
+
+ const int bar_width = 256;
+ const int bar_height = 192;
+ const int box_width = 120;
+
+ int cx = width/2;
+ int cy = height/2;
+ int l = cx - bar_width/2;
+ int r = cx + bar_width/2;
+ int t = cy - bar_height/2;
+ int b = cy + bar_height/2;
+ int align = DT_LEFT;
+
+ if (Game::Paused())
+ DrawHUDText(TXT_PAUSED, Game::GetText("HUDView.PAUSED"), Rect(cx-128, cy-60, 256, 12), DT_CENTER);
+
+ if (ship) {
+ DrawContactMarkers();
+
+ char txt[256];
+ double speed = ship->Velocity().length();
+
+ if (ship->Velocity() * ship->Heading() < 0)
+ speed = -speed;
+
+ FormatNumber(txt, speed);
+
+ if (tactical) {
+ l = box_width + 16;
+ r = width - box_width - 16;
+ }
+
+ Rect speed_rect(l-box_width-8, cy-5, box_width, 12);
+
+ align = (tactical) ? DT_LEFT : DT_RIGHT;
+ DrawHUDText(TXT_SPEED, txt, speed_rect, align);
+
+ // upper left hud quadrant (airborne fighters)
+ if (ship->IsAirborne()) {
+ double alt_msl = ship->AltitudeMSL();
+ double alt_agl = ship->AltitudeAGL();
+
+ if (alt_agl <= 1000)
+ sprintf(txt, "R %4d", (int) alt_agl);
+ else
+ FormatNumber(txt, alt_msl);
+
+ speed_rect.y -= 20;
+
+ if (arcade) {
+ char arcade_txt[32];
+ sprintf(arcade_txt, "%s %s", Game::GetText("HUDView.altitude").data(), txt);
+ align = (tactical) ? DT_LEFT : DT_RIGHT;
+ DrawHUDText(TXT_ALTITUDE, arcade_txt, speed_rect, align);
+ }
+ else {
+ align = (tactical) ? DT_LEFT : DT_RIGHT;
+ DrawHUDText(TXT_ALTITUDE, txt, speed_rect, align);
+ }
+
+ if (!arcade) {
+ sprintf(txt, "%.1f G", ship->GForce());
+ speed_rect.y -= 20;
+
+ align = (tactical) ? DT_LEFT : DT_RIGHT;
+ DrawHUDText(TXT_GFORCE, txt, speed_rect, align);
+
+ speed_rect.y += 40;
+ }
+ }
+
+ // upper left hud quadrant (starships)
+ else if (ship->IsStarship() && ship->GetFLCSMode() == Ship::FLCS_HELM && !arcade) {
+ sprintf(txt, "%s: %.1f", Game::GetText("HUDView.Pitch").data(), ship->GetHelmPitch()/DEGREES);
+ speed_rect.y -= 50;
+
+ align = (tactical) ? DT_LEFT : DT_RIGHT;
+ DrawHUDText(TXT_PITCH, txt, speed_rect, align);
+
+ speed_rect.y -= 10;
+ int heading_degrees = (int) (ship->GetHelmHeading()/DEGREES);
+ if (heading_degrees < 0) heading_degrees += 360;
+ sprintf(txt, "%s: %03d", Game::GetText("HUDView.Heading").data(), heading_degrees);
+ DrawHUDText(TXT_HEADING, txt, speed_rect, align);
+
+ speed_rect.y += 60;
+ }
+
+ // per user request, all ships should show compass heading
+ if (!tactical && !arcade) {
+ Rect heading_rect(l, t+5, bar_width, 12);
+ int heading_degrees = (int) (ship->CompassHeading()/DEGREES);
+ if (heading_degrees < 0) heading_degrees += 360;
+ sprintf(txt, "%d", heading_degrees);
+ DrawHUDText(TXT_COMPASS, txt, heading_rect, DT_CENTER);
+ }
+
+ switch (mode) {
+ case HUD_MODE_TAC: strcpy(txt, Game::GetText("HUDView.mode.tactical").data()); break;
+ case HUD_MODE_NAV: strcpy(txt, Game::GetText("HUDView.mode.navigation").data()); break;
+ case HUD_MODE_ILS: strcpy(txt, Game::GetText("HUDView.mode.landing").data()); break;
+ }
+
+ if (tactical) {
+ speed_rect.y += 76;
+ align = DT_LEFT;
+ }
+ else {
+ speed_rect.y = cy+76;
+ align = DT_RIGHT;
+ }
+
+ DrawHUDText(TXT_HUD_MODE, txt, speed_rect, align);
+
+ // landing gear:
+ if (ship->IsGearDown()) {
+ const char* gear_down = Game::GetText("HUDView.gear-down");
+
+ Rect gear_rect(l, b+20, box_width, 12);
+ DrawHUDText(TXT_GEAR_DOWN, gear_down, gear_rect, DT_CENTER, HUD_UPPER_CASE, true);
+ }
+
+ // sensor/missile lock warnings and quantum drive countdown:
+ QuantumDrive* quantum = ship->GetQuantumDrive();
+
+ if (threat || (quantum && quantum->JumpTime() > 0)) {
+ const char* threat_warn = Game::GetText("HUDView.threat-warn");
+ bool show_msg = true;
+
+ if (quantum && quantum->JumpTime() > 0) {
+ static char buf[64];
+ sprintf(buf, "%s: %d", Game::GetText("HUDView.quantum-jump").data(), (int) quantum->JumpTime());
+ threat_warn = buf;
+ }
+
+ else if (threat > 1) {
+ threat_warn = Game::GetText("HUDView.missile-warn");
+ show_msg = ((Game::RealTime()/500) & 1) != 0;
+ }
+
+ if (show_msg) {
+ Rect lock_rect(l, t-25, box_width, 12);
+ DrawHUDText(TXT_THREAT_WARN, threat_warn, lock_rect, DT_CENTER, HUD_MIXED_CASE, true);
+ }
+ }
+
+ if (ship->CanTimeSkip()) {
+ Rect auto_rect(l, t-40, box_width, 12);
+ DrawHUDText(TXT_AUTO, Game::GetText("HUDView.AUTO"), auto_rect, DT_CENTER, HUD_MIXED_CASE, true);
+ }
+
+ if (mode == HUD_MODE_NAV) {
+ Instruction* next = ship->GetNextNavPoint();
+
+ if (next) {
+ double distance = ship->RangeToNavPoint(next);
+ FormatNumber(txt, distance);
+
+ Rect range_rect(r-20, cy-5, box_width, 12);
+ DrawHUDText(TXT_RANGE, txt, range_rect, DT_RIGHT);
+ range_rect.Inflate(2,2);
+ }
+ }
+
+ // lower left hud quadrant
+ else if (mode == HUD_MODE_TAC) {
+ speed_rect.x = l-box_width-8;
+ speed_rect.y = cy-5 +20;
+ speed_rect.w = box_width;
+ align = (tactical) ? DT_LEFT : DT_RIGHT;
+
+ if (!arcade && ship->GetPrimary() && !ship->IsNetObserver())
+ DrawHUDText(TXT_PRIMARY_WEP, ship->GetPrimary()->Abbreviation(), speed_rect, align);
+
+ WeaponGroup* missile = ship->GetSecondaryGroup();
+
+ if (missile && missile->Ammo() > 0 && !ship->IsNetObserver()) {
+ if (!arcade) {
+ speed_rect.y = cy-5 +30;
+ sprintf(txt, "%s %d", missile->Name(), missile->Ammo());
+ DrawHUDText(TXT_SECONDARY_WEP, txt, speed_rect, align);
+ }
+
+ // missile range indicator
+ if (missile->GetSelected()->Locked()) {
+ Rect shoot_rect(l, b+5, box_width, 12);
+ DrawHUDText(TXT_SHOOT, Game::GetText("HUDView.SHOOT"), shoot_rect, DT_CENTER, HUD_MIXED_CASE, true);
+ }
+ }
+
+ if (!arcade && !ship->IsNetObserver()) {
+ if (ship->GetShield()) {
+ speed_rect.y = cy-5+40;
+ sprintf(txt, "%s - %03d +", Game::GetText("HUDView.SHIELD").data(), ship->ShieldStrength());
+ DrawHUDText(TXT_SHIELD, txt, speed_rect, align);
+ }
+ else if (ship->GetDecoy()) {
+ speed_rect.y = cy-5+40;
+ sprintf(txt, "%s %d", Game::GetText("HUDView.DECOY").data(), ship->GetDecoy()->Ammo());
+ DrawHUDText(TXT_DECOY, txt, speed_rect, align);
+ }
+
+
+ Rect eta_rect = speed_rect;
+ eta_rect.y += 10;
+
+ align = DT_RIGHT;
+
+ if (tactical) {
+ eta_rect.x = 8;
+ align = DT_LEFT;
+ }
+
+ for (int i = 0; i < 2; i++) {
+ int eta = ship->GetMissileEta(i);
+ if (eta > 0) {
+ int minutes = (eta/60) % 60;
+ int seconds = (eta ) % 60;
+
+ char eta_buf[16];
+ sprintf(eta_buf, "T %d:%02d", minutes, seconds);
+ DrawHUDText(TXT_MISSILE_T1+i, eta_buf, eta_rect, align);
+ eta_rect.y += 10;
+ }
+ }
+ }
+
+ NetGame* netgame = NetGame::GetInstance();
+ if (netgame && !netgame->IsActive()) {
+ Rect shoot_rect(l, b+5, box_width, 12);
+ DrawHUDText(TXT_SHOOT, Game::GetText("HUDView.NET-GAME-OVER"), shoot_rect, DT_CENTER, HUD_MIXED_CASE, true);
+ }
+
+ else if (ship->IsNetObserver()) {
+ Rect shoot_rect(l, b+5, box_width, 12);
+ DrawHUDText(TXT_SHOOT, Game::GetText("HUDView.OBSERVER"), shoot_rect, DT_CENTER, HUD_MIXED_CASE, true);
+ }
+
+ DrawTarget();
+ }
+
+ else if (mode == HUD_MODE_ILS) {
+ DrawTarget();
+ }
+
+ DrawNavInfo();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawFPM()
+{
+ fpm_sprite->Hide();
+
+ if (ship->Velocity().length() > 50) {
+ double xtarg = xcenter;
+ double ytarg = ycenter;
+
+ Point svel = ship->Velocity();
+ svel.Normalize();
+
+ Point tloc = ship->Location() + svel * 1e8;
+ // Translate into camera relative:
+ projector->Transform(tloc);
+
+ int behind = tloc.z < 0;
+
+ if (behind)
+ return;
+
+ // Project into screen coordinates:
+ projector->Project(tloc);
+
+ xtarg = tloc.x;
+ ytarg = tloc.y;
+
+ fpm_sprite->Show();
+ fpm_sprite->MoveTo(Point(xtarg, ytarg, 1));
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawPitchLadder()
+{
+ for (int i = 0; i < 31; i++)
+ pitch_ladder[i]->Hide();
+
+ if (ship->IsAirborne() && Game::MaxTexSize() > 128) {
+ double xtarg = xcenter;
+ double ytarg = ycenter;
+
+ Point uvec = Point(0,1,0);
+ Point svel = ship->Velocity();
+
+ if (svel.length() == 0)
+ svel = ship->Heading();
+
+ if (svel.x == 0 && svel.z == 0)
+ return;
+
+ svel.y = 0;
+ svel.Normalize();
+
+ Point gloc = ship->Location();
+ gloc.y = 0;
+
+ const double baseline = 1e9;
+ const double clip_angle = 20*DEGREES;
+
+ Point tloc = gloc + svel * baseline;
+
+ // Translate into camera relative:
+ projector->Transform(tloc);
+
+ // Project into screen coordinates:
+ projector->Project(tloc);
+
+ xtarg = tloc.x;
+ ytarg = tloc.y;
+
+ // compute roll angle:
+ double roll_angle = 0;
+ double pitch_angle = 0;
+
+ Point heading = ship->Heading();
+ heading.Normalize();
+
+ if (heading.x != 0 || heading.z != 0) {
+ Point gheading = heading;
+ gheading.y = 0;
+ gheading.Normalize();
+
+ double dot = gheading * heading;
+
+ if (heading.y < 0) dot = -dot;
+
+ pitch_angle = acos(dot);
+
+ if (pitch_angle > PI/2)
+ pitch_angle -= PI;
+
+ double s0 = sin(pitch_angle);
+ double c0 = cos(pitch_angle);
+ double s1 = sin(pitch_angle + 10*DEGREES);
+ double c1 = cos(pitch_angle + 10*DEGREES);
+
+ tloc = gloc + (svel * baseline * c0) + (uvec * baseline * s0);
+ projector->Transform(tloc);
+
+ double x0 = tloc.x;
+ double y0 = tloc.y;
+
+ tloc = gloc + (svel * baseline * c1) + (uvec * baseline * s1);
+ projector->Transform(tloc);
+
+ double x1 = tloc.x;
+ double y1 = tloc.y;
+
+ double dx = x1-x0;
+ double dy = y1-y0;
+
+ roll_angle = atan2(-dy,dx) + PI/2;
+ }
+
+ const double alias_limit = 0.1*DEGREES;
+
+ if (fabs(roll_angle) <= alias_limit) {
+ if (roll_angle > 0)
+ roll_angle = alias_limit;
+ else
+ roll_angle = -alias_limit;
+ }
+
+ else if (fabs(roll_angle-PI) <= alias_limit) {
+ roll_angle = PI - alias_limit;
+ }
+
+ if (fabs(pitch_angle) <= clip_angle) {
+ pitch_ladder[15]->Show();
+ pitch_ladder[15]->MoveTo(Point(xtarg, ytarg, 1));
+ pitch_ladder[15]->SetAngle(roll_angle);
+ }
+
+ for (i = 1; i <= 15; i++) {
+ double angle = i * 5 * DEGREES;
+
+ if (i > 12)
+ angle = (60 + (i-12)*10) * DEGREES;
+
+ double s = sin(angle);
+ double c = cos(angle);
+
+ if (fabs(pitch_angle - angle) <= clip_angle) {
+ // positive angle:
+ tloc = gloc + (svel * baseline * c) + (uvec * baseline * s);
+ projector->Transform(tloc);
+
+ if (tloc.z > 0) {
+ projector->Project(tloc);
+ pitch_ladder[15-i]->Show();
+ pitch_ladder[15-i]->MoveTo(Point(tloc.x, tloc.y, 1));
+ pitch_ladder[15-i]->SetAngle(roll_angle);
+ }
+ }
+
+ if (fabs(pitch_angle + angle) <= clip_angle) {
+ // negative angle:
+ tloc = gloc + (svel * baseline * c) + (uvec * -baseline * s);
+ projector->Transform(tloc);
+
+ if (tloc.z > 0) {
+ projector->Project(tloc);
+ pitch_ladder[15+i]->Show();
+ pitch_ladder[15+i]->MoveTo(Point(tloc.x, tloc.y, 1));
+ pitch_ladder[15+i]->SetAngle(roll_angle);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawHPM()
+{
+ hpm_sprite->Hide();
+
+ if (!ship)
+ return;
+
+ double xtarg = xcenter;
+ double ytarg = ycenter;
+
+ double az = ship->GetHelmHeading() - PI;
+ double el = ship->GetHelmPitch();
+
+ Point hvec = Point(sin(az), sin(el), cos(az));
+ hvec.Normalize();
+
+ Point tloc = ship->Location() + hvec * 1e8;
+ // Translate into camera relative:
+ projector->Transform(tloc);
+
+ int behind = tloc.z < 0;
+
+ if (behind)
+ return;
+
+ // Project into screen coordinates:
+ projector->Project(tloc);
+
+ xtarg = tloc.x;
+ ytarg = tloc.y;
+
+ hpm_sprite->Show();
+ hpm_sprite->MoveTo(Point(xtarg, ytarg, 1));
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::HideCompass()
+{
+ az_ring->Hide();
+ az_pointer->Hide();
+ el_ring->Hide();
+ el_pointer->Hide();
+
+ Scene* scene = az_ring->GetScene();
+ if (scene) {
+ scene->DelGraphic(az_ring);
+ scene->DelGraphic(az_pointer);
+ scene->DelGraphic(el_ring);
+ scene->DelGraphic(el_pointer);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawCompass()
+{
+ if (!ship || !ship->Rep())
+ return;
+
+ Solid* solid = (Solid*) ship->Rep();
+ Point loc = solid->Location();
+
+ az_ring->MoveTo(loc);
+ az_pointer->MoveTo(loc);
+ el_ring->MoveTo(loc);
+ el_pointer->MoveTo(loc);
+
+ double helm_heading = ship->GetHelmHeading();
+ double helm_pitch = ship->GetHelmPitch();
+ double curr_heading = ship->CompassHeading();
+ double curr_pitch = ship->CompassPitch();
+
+ bool show_az = fabs(helm_heading - curr_heading) > 5*DEGREES;
+ bool show_el = fabs(helm_pitch - curr_pitch) > 5*DEGREES;
+
+ Scene* scene = camview->GetScene();
+
+ if (show_az || show_el) {
+ scene->AddGraphic(az_ring);
+ az_ring->Show();
+
+ if (show_el || fabs(helm_pitch) > 5 * DEGREES) {
+ scene->AddGraphic(el_ring);
+ Matrix ring_orient;
+ ring_orient.Yaw(helm_heading + PI);
+ el_ring->SetOrientation(ring_orient);
+ el_ring->Show();
+
+ scene->AddGraphic(el_pointer);
+ Matrix pointer_orient;
+ pointer_orient.Yaw(helm_heading + PI);
+ pointer_orient.Pitch(-helm_pitch);
+ pointer_orient.Roll(PI/2);
+ el_pointer->SetOrientation(pointer_orient);
+ el_pointer->Show();
+ }
+ else {
+ scene->AddGraphic(az_pointer);
+ Matrix pointer_orient;
+ pointer_orient.Yaw(helm_heading + PI);
+
+ az_pointer->SetOrientation(pointer_orient);
+ az_pointer->Show();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawLCOS(SimObject* targ, double dist)
+{
+ lead_sprite->Hide();
+ aim_sprite->Hide();
+ chase_sprite->Hide();
+
+ double xtarg = xcenter;
+ double ytarg = ycenter;
+
+ Weapon* prim = ship->GetPrimary();
+ if (!prim) return;
+
+ Point tloc = targ->Location();
+ // Translate into camera relative:
+ projector->Transform(tloc);
+
+ int behind = tloc.z < 0;
+
+ if (behind)
+ tloc.z = -tloc.z;
+
+ // Project into screen coordinates:
+ projector->Project(tloc);
+
+ // DRAW THE OFFSCREEN CHASE INDICATOR:
+ if (behind ||
+ tloc.x <= 0 || tloc.x >= width-1 ||
+ tloc.y <= 0 || tloc.y >= height-1) {
+
+ // Left side:
+ if (tloc.x <= 0 || (behind && tloc.x < width/2)) {
+ if (tloc.y < ah) tloc.y = ah;
+ else if (tloc.y >= height-ah) tloc.y = height-1-ah;
+
+ chase_sprite->Show();
+ chase_sprite->SetAnimation(&chase_left);
+ chase_sprite->MoveTo(Point(aw, tloc.y, 1));
+ }
+
+ // Right side:
+ else if (tloc.x >= width-1 || behind) {
+ if (tloc.y < ah) tloc.y = ah;
+ else if (tloc.y >= height-ah) tloc.y = height-1-ah;
+
+ chase_sprite->Show();
+ chase_sprite->SetAnimation(&chase_right);
+ chase_sprite->MoveTo(Point(width-1-aw, tloc.y, 1));
+ }
+ else {
+ if (tloc.x < aw) tloc.x = aw;
+ else if (tloc.x >= width-aw) tloc.x = width-1-aw;
+
+ // Top edge:
+ if (tloc.y <= 0) {
+ chase_sprite->Show();
+ chase_sprite->SetAnimation(&chase_top);
+ chase_sprite->MoveTo(Point(tloc.x, ah, 1));
+ }
+
+ // Bottom edge:
+ else if (tloc.y >= height-1) {
+ chase_sprite->Show();
+ chase_sprite->SetAnimation(&chase_bottom);
+ chase_sprite->MoveTo(Point(tloc.x, height-1-ah, 1));
+ }
+ }
+ }
+
+ // DRAW THE LCOS:
+ else {
+ if (!ship->IsStarship()) {
+ Point aim_vec = ship->Heading();
+ aim_vec.Normalize();
+
+ // shot speed is relative to ship speed:
+ Point shot_vel = ship->Velocity() + aim_vec * prim->Design()->speed;
+ double shot_speed = shot_vel.length();
+
+ // time for shot to reach target
+ double time = dist / shot_speed;
+
+ // LCOS (Lead Computing Optical Sight)
+ if (gunsight == 0) {
+ // where the shot will be when it is the same distance
+ // away from the ship as the target:
+ Point impact = ship->Location() + (shot_vel * time);
+
+ // where the target will be when the shot reaches it:
+ Point targ_vel = targ->Velocity();
+ Point dest = targ->Location() + (targ_vel * time);
+ Point delta = impact - dest;
+
+ // draw the gun sight here in 3d world coordinates:
+ Point sight = targ->Location() + delta;
+
+ // Project into screen coordinates:
+ projector->Transform(sight);
+ projector->Project(sight);
+
+ xtarg = sight.x;
+ ytarg = sight.y;
+
+ aim_sprite->Show();
+ aim_sprite->MoveTo(Point(xtarg, ytarg, 1));
+ }
+
+ // Wing Commander style lead indicator
+ else {
+ // where the target will be when the shot reaches it:
+ Point targ_vel = targ->Velocity() - ship->Velocity();
+ Point dest = targ->Location() + (targ_vel * time);
+
+ // Translate into camera relative:
+ projector->Transform(dest);
+ projector->Project(dest);
+
+ xtarg = dest.x;
+ ytarg = dest.y;
+
+ lead_sprite->Show();
+ lead_sprite->MoveTo(Point(xtarg, ytarg, 1));
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawTarget()
+{
+ const int bar_width = 256;
+ const int bar_height = 192;
+ const int box_width = 120;
+
+ SimObject* old_target = target;
+
+ if (mode == HUD_MODE_ILS) {
+ Ship* controller = ship->GetController();
+ if (controller && !target)
+ target = controller;
+ }
+
+ if (target && target->Rep()) {
+ Sensor* sensor = ship->GetSensor();
+ Contact* contact = 0;
+
+ if (sensor && target->Type() == SimObject::SIM_SHIP) {
+ contact = sensor->FindContact((Ship*) target);
+ }
+
+ int cx = width/2;
+ int cy = height/2;
+ int l = cx - bar_width/2;
+ int r = cx + bar_width/2;
+ int t = cy - bar_height/2;
+ int b = cy + bar_height/2;
+ Point delta = target->Location() - ship->Location();
+ double distance = delta.length();
+ Point delta_v = ship->Velocity() - target->Velocity();
+ double speed = delta_v.length();
+ char txt[256];
+
+ if (mode == HUD_MODE_ILS && ship->GetInbound() && ship->GetInbound()->GetDeck()) {
+ delta = ship->GetInbound()->GetDeck()->EndPoint() - ship->Location();
+ distance = delta.length();
+ }
+
+ if (delta * ship->Velocity() > 0) { // in front
+ if (delta_v * ship->Velocity() < 0) // losing ground
+ speed = -speed;
+ }
+ else { // behind
+ if (delta_v * ship->Velocity() > 0) // passing
+ speed = -speed;
+ }
+
+ Rect range_rect(r-20, cy-5, box_width, 12);
+
+ if (tactical)
+ range_rect.x = width - range_rect.w - 8;
+
+ if (contact) {
+ Sensor* sensor = ship->GetSensor();
+ double limit = 75e3;
+
+ if (sensor)
+ limit = sensor->GetBeamRange();
+
+ distance = contact->Range(ship, limit);
+
+ if (!contact->ActLock() && !contact->PasLock()) {
+ strcpy(txt, Game::GetText("HUDView.No-Range").data());
+ speed = 0;
+ }
+ else {
+ FormatNumber(txt, distance);
+ }
+ }
+
+ else {
+ FormatNumber(txt, distance);
+ }
+
+ DrawHUDText(TXT_RANGE, txt, range_rect, DT_RIGHT);
+
+ if (arcade) {
+ target = old_target;
+ return;
+ }
+
+ range_rect.y += 18;
+ FormatNumber(txt, speed);
+ DrawHUDText(TXT_CLOSING_SPEED, txt, range_rect, DT_RIGHT);
+
+ // target info:
+ if (!tactical) {
+ range_rect.y = cy-76;
+ }
+
+ else {
+ range_rect.x = width - 2*box_width - 8;
+ range_rect.y = cy-76;
+ range_rect.w = 2*box_width;
+ }
+
+ DrawHUDText(TXT_TARGET_NAME, target->Name(), range_rect, DT_RIGHT);
+
+ if (target->Type() == SimObject::SIM_SHIP) {
+ Ship* tgt_ship = (Ship*) target;
+
+ range_rect.y += 10;
+ DrawHUDText(TXT_TARGET_DESIGN, tgt_ship->Design()->display_name, range_rect, DT_RIGHT);
+
+ if (mode != HUD_MODE_ILS) {
+ if (tgt_ship->IsStarship()) {
+ range_rect.y += 10;
+ sprintf(txt, "%s %03d", Game::GetText("HUDView.symbol.shield").data(), (int) tgt_ship->ShieldStrength());
+ DrawHUDText(TXT_TARGET_SHIELD, txt, range_rect, DT_RIGHT);
+ }
+
+ range_rect.y += 10;
+ sprintf(txt, "%s %03d", Game::GetText("HUDView.symbol.hull").data(), (int) (tgt_ship->Integrity() / tgt_ship->Design()->integrity * 100));
+ DrawHUDText(TXT_TARGET_HULL, txt, range_rect, DT_RIGHT);
+
+ System* sys = ship->GetSubTarget();
+ if (sys) {
+ Color stat = hud_color;
+ static DWORD blink = Game::RealTime();
+
+ int blink_delta = Game::RealTime() - blink;
+ sprintf(txt, "%s %03d", sys->Abbreviation(), (int) sys->Availability());
+
+ switch (sys->Status()) {
+ case System::DEGRADED: stat = Color(255,255, 0); break;
+ case System::CRITICAL:
+ case System::DESTROYED: stat = Color(255, 0, 0); break;
+ case System::MAINT:
+ if (blink_delta < 250)
+ stat = Color(8,8,8);
+ break;
+ }
+
+ if (blink_delta > 500)
+ blink = Game::RealTime();
+
+ range_rect.y += 10;
+ DrawHUDText(TXT_TARGET_SUB, txt, range_rect, DT_RIGHT);
+ }
+ }
+ }
+
+ else if (target->Type() == SimObject::SIM_DRONE) {
+ Drone* tgt_drone = (Drone*) target;
+
+ range_rect.y += 10;
+ DrawHUDText(TXT_TARGET_DESIGN, tgt_drone->DesignName(), range_rect, DT_RIGHT);
+
+ range_rect.y += 10;
+ int eta = tgt_drone->GetEta();
+
+ if (eta > 0) {
+ int minutes = (eta/60) % 60;
+ int seconds = (eta ) % 60;
+
+ char eta_buf[16];
+ sprintf(eta_buf, "T %d:%02d", minutes, seconds);
+ DrawHUDText(TXT_TARGET_ETA, eta_buf, range_rect, DT_RIGHT);
+ }
+ }
+ }
+
+ target = old_target;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawNavInfo()
+{
+ const int bar_width = 256;
+ const int bar_height = 192;
+ const int box_width = 120;
+
+ if (arcade) {
+ if (ship->IsAutoNavEngaged()) {
+ Rect info_rect(width/2-box_width, height/2+bar_height, box_width*2, 12);
+
+ if (big_font)
+ hud_text[TXT_NAV_INDEX].font = big_font;
+
+ DrawHUDText(TXT_NAV_INDEX, Game::GetText("HUDView.Auto-Nav"), info_rect, DT_CENTER);
+ }
+
+ return;
+ }
+
+ hud_text[TXT_NAV_INDEX].font = hud_font;
+
+ Instruction* navpt = ship->GetNextNavPoint();
+
+ if (navpt) {
+ int cx = width/2;
+ int cy = height/2;
+ int l = cx - bar_width/2;
+ int r = cx + bar_width/2;
+ int t = cy - bar_height/2;
+ int b = cy + bar_height/2;
+
+ int index = ship->GetNavIndex(navpt);
+ double distance = ship->RangeToNavPoint(navpt);
+ double speed = ship->Velocity().length();
+ int etr = 0;
+ char txt[256];
+
+ if (speed > 10)
+ etr = (int) (distance/speed);
+
+ Rect info_rect(r-20, cy+32, box_width, 12);
+
+ if (tactical)
+ info_rect.x = width - info_rect.w - 8;
+
+ if (ship->IsAutoNavEngaged())
+ sprintf(txt, "%s %d", Game::GetText("HUDView.Auto-Nav").data(), index);
+ else
+ sprintf(txt, "%s %d", Game::GetText("HUDView.Nav").data(), index);
+ DrawHUDText(TXT_NAV_INDEX, txt, info_rect, DT_RIGHT);
+
+ info_rect.y += 10;
+ if (navpt->Action())
+ DrawHUDText(TXT_NAV_ACTION, Instruction::ActionName(navpt->Action()), info_rect, DT_RIGHT);
+
+ info_rect.y += 10;
+ FormatNumber(txt, navpt->Speed());
+ DrawHUDText(TXT_NAV_SPEED, txt, info_rect, DT_RIGHT);
+
+ if (etr > 3600) {
+ info_rect.y += 10;
+ sprintf(txt, "%s XX:XX", Game::GetText("HUDView.time-enroute").data());
+ DrawHUDText(TXT_NAV_ETR, txt, info_rect, DT_RIGHT);
+ }
+ else if (etr > 0) {
+ info_rect.y += 10;
+
+ int minutes = (etr/60) % 60;
+ int seconds = (etr ) % 60;
+ sprintf(txt, "%s %2d:%02d", Game::GetText("HUDView.time-enroute").data(), minutes, seconds);
+ DrawHUDText(TXT_NAV_ETR, txt, info_rect, DT_RIGHT);
+ }
+
+ if (navpt->HoldTime() > 0) {
+ info_rect.y += 10;
+
+ int hold = (int) navpt->HoldTime();
+ int minutes = (hold/60) % 60;
+ int seconds = (hold ) % 60;
+ sprintf(txt, "%s %2d:%02d", Game::GetText("HUDView.HOLD").data(), minutes, seconds);
+ DrawHUDText(TXT_NAV_HOLD, txt, info_rect, DT_RIGHT);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawSight()
+{
+ if (target && target->Rep()) {
+ Point delta = target->Location() - ship->Location();
+ double distance = delta.length();
+
+ // draw LCOS on target:
+ if (!tactical)
+ DrawLCOS(target, distance);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawDesignators()
+{
+ double xtarg = xcenter;
+ double ytarg = ycenter;
+ SimObject* t1 = 0;
+ SimObject* t2 = 0;
+ SimObject* t3 = 0;
+ Sprite* sprite = 0;
+
+ tgt1_sprite->Hide();
+ tgt2_sprite->Hide();
+ tgt3_sprite->Hide();
+ tgt4_sprite->Hide();
+
+ // fighters just show primary target:
+ if (ship->IsDropship()) {
+ SimObject* t = ship->GetTarget();
+ System* s = ship->GetSubTarget();
+
+ if (t) {
+ Point tloc = t->Location();
+ if (s) {
+ tloc = s->MountLocation();
+ }
+ else if (t->Type() == SimObject::SIM_SHIP) {
+ Ship* tgt_ship = (Ship*) t;
+
+ if (tgt_ship->IsGroundUnit())
+ tloc += Point(0,150,0);
+ }
+
+ projector->Transform(tloc);
+
+ if (tloc.z > 0) {
+ projector->Project(tloc);
+
+ xtarg = tloc.x;
+ ytarg = tloc.y;
+
+ if (xtarg>0 && xtarg<width-1 && ytarg>0 && ytarg<height-1) {
+ double range = Point(t->Location() - ship->Location()).length();
+
+ // use out-of-range crosshair if out of range:
+ if (!ship->GetPrimaryDesign() || ship->GetPrimaryDesign()->max_range < range) {
+ tgt4_sprite->Show();
+ tgt4_sprite->MoveTo(Point(xtarg, ytarg, 1));
+ }
+
+ // else, use in-range primary crosshair:
+ else {
+ tgt1_sprite->Show();
+ tgt1_sprite->MoveTo(Point(xtarg, ytarg, 1));
+ }
+ }
+ }
+ }
+ }
+
+ // starships show up to three targets:
+ else {
+ ListIter<WeaponGroup> w = ship->Weapons();
+ while (!t3 && ++w) {
+ SimObject* t = w->GetTarget();
+ System* s = w->GetSubTarget();
+
+ if (w->Contains(ship->GetPrimary())) {
+ if (t == 0) t = ship->GetTarget();
+ t1 = t;
+ sprite = tgt1_sprite;
+ }
+
+ else if (t && w->Contains(ship->GetSecondary())) {
+ t2 = t;
+ sprite = tgt2_sprite;
+
+ if (t2 == t1)
+ continue; // don't overlap target designators
+ }
+
+ else if (t) {
+ t3 = t;
+ sprite = tgt3_sprite;
+
+ if (t3 == t1 || t3 == t2)
+ continue; // don't overlap target designators
+ }
+
+ if (t) {
+ Point tloc = t->Location();
+
+ if (s)
+ tloc = s->MountLocation();
+
+ projector->Transform(tloc);
+
+ if (tloc.z > 0) {
+ projector->Project(tloc);
+
+ xtarg = tloc.x;
+ ytarg = tloc.y;
+
+ if (xtarg>0 && xtarg<width-1 && ytarg>0 && ytarg<height-1) {
+ double range = Point(t->Location() - ship->Location()).length();
+
+ // flip to out-of-range crosshair
+ if (sprite == tgt1_sprite) {
+ if (!ship->GetPrimaryDesign() || ship->GetPrimaryDesign()->max_range < range) {
+ sprite = tgt4_sprite;
+ }
+ }
+
+ sprite->Show();
+ sprite->MoveTo(Point(xtarg, ytarg, 1));
+ }
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+HUDView::GetStatusColor(System::STATUS status)
+{
+ Color sc;
+
+ switch (status) {
+ default:
+ case System::NOMINAL: sc = Color( 32,192, 32); break;
+ case System::DEGRADED: sc = Color(255,255, 0); break;
+ case System::CRITICAL: sc = Color(255, 0, 0); break;
+ case System::DESTROYED: sc = Color( 0, 0, 0); break;
+ }
+
+ return sc;
+}
+
+void
+HUDView::SetStatusColor(System::STATUS status)
+{
+ switch (status) {
+ default:
+ case System::NOMINAL: status_color = txt_color; break;
+ case System::DEGRADED: status_color = Color(255,255, 0); break;
+ case System::CRITICAL: status_color = Color(255, 0, 0); break;
+ case System::DESTROYED: status_color = Color( 0, 0, 0); break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static int GetReactorStatus(Ship* ship)
+{
+ if (!ship || ship->Reactors().size() < 1)
+ return -1;
+
+ int status = System::NOMINAL;
+ bool maint = false;
+
+ ListIter<PowerSource> iter = ship->Reactors();
+ while (++iter) {
+ PowerSource* s = iter.value();
+
+ if (s->Status() < status)
+ status = s->Status();
+ }
+
+ if (maint && status == System::NOMINAL)
+ status = System::MAINT;
+
+ return status;
+}
+
+static int GetDriveStatus(Ship* ship)
+{
+ if (!ship || ship->Drives().size() < 1)
+ return -1;
+
+ int status = System::NOMINAL;
+ bool maint = false;
+
+ ListIter<Drive> iter = ship->Drives();
+ while (++iter) {
+ Drive* s = iter.value();
+
+ if (s->Status() < status)
+ status = s->Status();
+
+ else if (s->Status() == System::MAINT)
+ maint = true;
+ }
+
+ if (maint && status == System::NOMINAL)
+ status = System::MAINT;
+
+ return status;
+}
+
+static int GetQuantumStatus(Ship* ship)
+{
+ if (!ship || ship->GetQuantumDrive() == 0)
+ return -1;
+
+ QuantumDrive* s = ship->GetQuantumDrive();
+ return s->Status();
+}
+
+static int GetThrusterStatus(Ship* ship)
+{
+ if (!ship || ship->GetThruster() == 0)
+ return -1;
+
+ Thruster* s = ship->GetThruster();
+ return s->Status();
+}
+
+static int GetShieldStatus(Ship* ship)
+{
+ if (!ship)
+ return -1;
+
+ Shield* s = ship->GetShield();
+ Weapon* d = ship->GetDecoy();
+
+ if (!s && !d)
+ return -1;
+
+ int status = System::NOMINAL;
+ bool maint = false;
+
+ if (s) {
+ if (s->Status() < status)
+ status = s->Status();
+
+ else if (s->Status() == System::MAINT)
+ maint = true;
+ }
+
+ if (d) {
+ if (d->Status() < status)
+ status = d->Status();
+
+ else if (d->Status() == System::MAINT)
+ maint = true;
+ }
+
+ if (maint && status == System::NOMINAL)
+ status = System::MAINT;
+
+ return status;
+}
+
+static int GetWeaponStatus(Ship* ship, int index)
+{
+ if (!ship || ship->Weapons().size() <= index)
+ return -1;
+
+ WeaponGroup* group = ship->Weapons().at(index);
+
+ int status = System::NOMINAL;
+ bool maint = false;
+
+ ListIter<Weapon> iter = group->GetWeapons();
+ while (++iter) {
+ Weapon* s = iter.value();
+
+ if (s->Status() < status)
+ status = s->Status();
+
+ else if (s->Status() == System::MAINT)
+ maint = true;
+ }
+
+ if (maint && status == System::NOMINAL)
+ status = System::MAINT;
+
+ return status;
+}
+
+static int GetSensorStatus(Ship* ship)
+{
+ if (!ship || ship->GetSensor() == 0)
+ return -1;
+
+ Sensor* s = ship->GetSensor();
+ Weapon* p = ship->GetProbeLauncher();
+
+ int status = s->Status();
+ bool maint = s->Status() == System::MAINT;
+
+ if (p) {
+ if (p->Status() < status)
+ status = p->Status();
+
+ else if (p->Status() == System::MAINT)
+ maint = true;
+ }
+
+ if (maint && status == System::NOMINAL)
+ status = System::MAINT;
+
+ return status;
+}
+
+static int GetComputerStatus(Ship* ship)
+{
+ if (!ship || ship->Computers().size() < 1)
+ return -1;
+
+ int status = System::NOMINAL;
+ bool maint = false;
+
+ ListIter<Computer> iter = ship->Computers();
+ while (++iter) {
+ Computer* s = iter.value();
+
+ if (s->Status() < status)
+ status = s->Status();
+
+ else if (s->Status() == System::MAINT)
+ maint = true;
+ }
+
+ if (ship->GetNavSystem()) {
+ NavSystem* s = ship->GetNavSystem();
+
+ if (s->Status() < status)
+ status = s->Status();
+
+ else if (s->Status() == System::MAINT)
+ maint = true;
+ }
+
+ if (maint && status == System::NOMINAL)
+ status = System::MAINT;
+
+ return status;
+}
+
+static int GetFlightDeckStatus(Ship* ship)
+{
+ if (!ship || ship->FlightDecks().size() < 1)
+ return -1;
+
+ int status = System::NOMINAL;
+ bool maint = false;
+
+ ListIter<FlightDeck> iter = ship->FlightDecks();
+ while (++iter) {
+ FlightDeck* s = iter.value();
+
+ if (s->Status() < status)
+ status = s->Status();
+
+ else if (s->Status() == System::MAINT)
+ maint = true;
+ }
+
+ if (maint && status == System::NOMINAL)
+ status = System::MAINT;
+
+ return status;
+}
+
+void
+HUDView::DrawWarningPanel()
+{
+ int box_width = 75;
+ int box_height = 17;
+ int row_height = 28;
+ int box_left = width/2 - box_width*2;
+
+ if (cockpit_hud_texture) {
+ box_left = 275;
+ box_height = 18;
+ row_height = 18;
+ }
+
+ if (ship) {
+ if (Game::MaxTexSize() > 128) {
+ warn_left_sprite->Show();
+ warn_right_sprite->Show();
+ }
+
+ int x = box_left;
+ int y = cockpit_hud_texture ? 410 : height-97;
+ int c = cockpit_hud_texture ? 3 : 4;
+
+ static DWORD blink = Game::RealTime();
+
+ for (int index = 0; index < 12; index++) {
+ int stat = -1;
+ Text abrv = Game::GetText("HUDView.UNKNOWN");
+
+ switch (index) {
+ case 0: stat = GetReactorStatus(ship); abrv = Game::GetText("HUDView.REACTOR"); break;
+ case 1: stat = GetDriveStatus(ship); abrv = Game::GetText("HUDView.DRIVE"); break;
+ case 2: stat = GetQuantumStatus(ship); abrv = Game::GetText("HUDView.QUANTUM"); break;
+ case 3: stat = GetShieldStatus(ship); abrv = Game::GetText("HUDView.SHIELD");
+ if (ship->GetShield() == 0 && ship->GetDecoy())
+ abrv = Game::GetText("HUDView.DECOY");
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ case 7: stat = GetWeaponStatus(ship, index-4);
+ if (stat >= 0) {
+ WeaponGroup* g = ship->Weapons().at(index-4);
+ abrv = g->Name();
+ }
+ break;
+
+ case 8: stat = GetSensorStatus(ship); abrv = Game::GetText("HUDView.SENSOR"); break;
+ case 9: stat = GetComputerStatus(ship); abrv = Game::GetText("HUDView.COMPUTER"); break;
+ case 10: stat = GetThrusterStatus(ship); abrv = Game::GetText("HUDView.THRUSTER"); break;
+ case 11: stat = GetFlightDeckStatus(ship);abrv = Game::GetText("HUDView.FLTDECK"); break;
+ }
+
+ Rect warn_rect(x, y, box_width, box_height);
+
+ if (cockpit_hud_texture)
+ cockpit_hud_texture->DrawRect(warn_rect, Color::DarkGray);
+
+ if (stat >= 0) {
+ SetStatusColor((System::STATUS) stat);
+ Color tc = status_color;
+
+ if (stat != System::NOMINAL) {
+ if (Game::RealTime() - blink < 250) {
+ tc = cockpit_hud_texture ? txt_color : Color(8,8,8);
+ }
+ }
+
+ if (cockpit_hud_texture) {
+ if (tc != txt_color) {
+ Rect r2 = warn_rect;
+ r2.Inset(1,1,1,1);
+ cockpit_hud_texture->FillRect(r2, tc);
+ tc = Color::Black;
+ }
+
+ warn_rect.y += 4;
+
+ hud_font->SetColor(tc);
+ hud_font->DrawText(abrv, -1,
+ warn_rect,
+ DT_CENTER | DT_SINGLELINE,
+ cockpit_hud_texture);
+
+ warn_rect.y -= 4;
+ }
+ else {
+ DrawHUDText(TXT_CAUTION_TXT + index,
+ abrv,
+ warn_rect,
+ DT_CENTER);
+
+ hud_text[TXT_CAUTION_TXT + index].color = tc;
+ }
+ }
+
+ x += box_width;
+
+ if (--c <= 0) {
+ c = cockpit_hud_texture ? 3 : 4;
+ x = box_left;
+ y += row_height;
+ }
+ }
+
+ if (Game::RealTime() - blink > 500)
+ blink = Game::RealTime();
+
+ // reset for next time
+ SetStatusColor(System::NOMINAL);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawInstructions()
+{
+ if (!ship) return;
+
+ if (Game::MaxTexSize() > 128) {
+ instr_left_sprite->Show();
+ instr_right_sprite->Show();
+ }
+
+ int ninst = 0;
+ int nobj = 0;
+ Element* elem = ship->GetElement();
+
+ if (elem) {
+ ninst = elem->NumInstructions();
+ nobj = elem->NumObjectives();
+ }
+
+ Rect r = Rect(width/2 - 143, height - 105, 290, 17);
+
+ if (ninst) {
+ int npages = ninst/6 + (ninst%6 ? 1 : 0);
+
+ if (inst_page >= npages)
+ inst_page = npages-1;
+ else if (inst_page < 0)
+ inst_page = 0;
+
+ int first = inst_page * 6;
+ int last = first + 6;
+ if (last > ninst) last = ninst;
+
+ int n = TXT_CAUTION_TXT;
+
+ for (int i = first; i < last; i++) {
+ hud_text[n].color = standard_txt_colors[color];
+ DrawHUDText(n++, FormatInstruction(elem->GetInstruction(i)), r, DT_LEFT, HUD_MIXED_CASE);
+ r.y += 14;
+ }
+
+ char page[32];
+ sprintf(page, "%d / %d", inst_page+1, npages);
+ r = Rect(width/2 + 40, height-16, 110, 16);
+ DrawHUDText(TXT_INSTR_PAGE, page, r, DT_CENTER, HUD_MIXED_CASE);
+ }
+
+ else if (nobj) {
+ int n = TXT_CAUTION_TXT;
+
+ for (int i = 0; i < nobj; i++) {
+ char desc[256];
+ sprintf(desc, "* %s", elem->GetObjective(i)->GetShortDescription());
+ hud_text[n].color = standard_txt_colors[color];
+ DrawHUDText(n++, desc, r, DT_LEFT, HUD_MIXED_CASE);
+ r.y += 14;
+ }
+ }
+
+ else {
+ hud_text[TXT_CAUTION_TXT].color = standard_txt_colors[color];
+ DrawHUDText(TXT_CAUTION_TXT, Game::GetText("HUDView.No-Instructions"), r, DT_LEFT, HUD_MIXED_CASE);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+HUDView::FormatInstruction(Text instr)
+{
+ if (!instr.contains('$'))
+ return (const char*) instr;
+
+ static char result[256];
+
+ const char* s = (const char*) instr;
+ char* d = result;
+
+ KeyMap& keymap = Starshatter::GetInstance()->GetKeyMap();
+
+ while (*s) {
+ if (*s == '$') {
+ s++;
+ char action[32];
+ char* a = action;
+ while (*s && (isupper(*s) || isdigit(*s) || *s == '_')) *a++ = *s++;
+ *a = 0;
+
+ int act = KeyMap::GetKeyAction(action);
+ int key = keymap.FindMapIndex(act);
+ const char* s2 = keymap.DescribeKey(key);
+
+ if (!s2) s2 = action;
+ while (*s2) *d++ = *s2++;
+ }
+ else {
+ *d++ = *s++;
+ }
+ }
+
+ *d = 0;
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::CycleInstructions(int direction)
+{
+ if (direction > 0)
+ inst_page++;
+ else
+ inst_page--;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawMessages()
+{
+ int message_queue_empty = true;
+
+ // age messages:
+ for (int i = 0; i < MAX_MSG; i++) {
+ if (msg_time[i] > 0) {
+ msg_time[i] -= Game::GUITime();
+
+ if (msg_time[i] <= 0) {
+ msg_time[i] = 0;
+ msg_text[i] = "";
+ }
+
+ message_queue_empty = false;
+ }
+ }
+
+ if (!message_queue_empty) {
+ // advance message pipeline:
+ for (i = 0; i < MAX_MSG; i++) {
+ if (msg_time[0] == 0) {
+ for (int j = 0; j < MAX_MSG-1; j++) {
+ msg_time[j] = msg_time[j+1];
+ msg_text[j] = msg_text[j+1];
+ }
+
+ msg_time[MAX_MSG-1] = 0;
+ msg_text[MAX_MSG-1] = "";
+ }
+ }
+
+ // draw messages:
+ for (i = 0; i < MAX_MSG; i++) {
+ int index = TXT_MSG_1 + i;
+
+ if (msg_time[i] > 0) {
+ Rect msg_rect(10, 95 + i*10, width-20, 12);
+ DrawHUDText(index, msg_text[i], msg_rect, DT_LEFT, HUD_MIXED_CASE);
+ if (msg_time[i] > 1)
+ hud_text[index].color = txt_color;
+ else
+ hud_text[index].color = txt_color.dim(0.5 + 0.5*msg_time[i]);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawNav()
+{
+ if (!sim)
+ return;
+
+ active_region = sim->GetActiveRegion();
+
+ if (ship) {
+ int nav_index = 1;
+ Instruction* next = ship->GetNextNavPoint();
+
+ if (mode == HUD_MODE_NAV) {
+ if (next && next->Action() == Instruction::LAUNCH)
+ DrawNavPoint(*next, 0, true);
+
+ ListIter<Instruction> navpt = ship->GetFlightPlan();
+ while (++navpt) {
+ DrawNavPoint(*navpt.value(), nav_index++, (navpt.value() == next));
+ }
+ }
+ else if (next) {
+ DrawNavPoint(*next, 0, true);
+ }
+ }
+}
+
+void
+HUDView::DrawILS()
+{
+ if (ship) {
+ bool hoops_drawn = false;
+ bool same_sector = false;
+
+ InboundSlot* inbound = ship->GetInbound();
+ if (inbound) {
+ FlightDeck* fd = inbound->GetDeck();
+
+ if (fd && fd->IsRecoveryDeck() && fd->GetCarrier()) {
+ if (fd->GetCarrier()->GetRegion() == ship->GetRegion())
+ same_sector = true;
+
+ if (same_sector && mode == HUD_MODE_ILS && !transition && !docking) {
+ Point dst = fd->MountLocation();
+ projector->Transform(dst);
+
+ if (dst.z > 1.0) {
+ projector->Project(dst);
+
+ int x = (int) dst.x;
+ int y = (int) dst.y;
+
+ if (x > 4 && x < width-4 &&
+ y > 4 && y < height-4) {
+
+ window->DrawLine(x-6, y-6, x+6, y+6, hud_color);
+ window->DrawLine(x+6, y-6, x-6, y+6, hud_color);
+ }
+ }
+ }
+
+ // draw the hoops for this flight deck:
+ Scene* scene = camview->GetScene();
+ for (int h = 0; h < fd->NumHoops(); h++) {
+ Hoop* hoop = fd->GetHoops() + h;
+ if (hoop && scene) {
+ if (same_sector && mode == HUD_MODE_ILS && !transition && !docking) {
+ scene->AddGraphic(hoop);
+ hoop->Show();
+
+ hoops_drawn = true;
+ }
+ else {
+ hoop->Hide();
+ scene->DelGraphic(hoop);
+ }
+ }
+ }
+ }
+ }
+
+ if (!hoops_drawn) {
+ ListIter<Ship> iter = ship->GetRegion()->Carriers();
+ while (++iter) {
+ Ship* carrier = iter.value();
+
+ bool ours = (carrier->GetIFF() == ship->GetIFF()) ||
+ (carrier->GetIFF() == 0);
+
+ for (int i = 0; i < carrier->NumFlightDecks(); i++) {
+ FlightDeck* fd = carrier->GetFlightDeck(i);
+
+ if (fd && fd->IsRecoveryDeck()) {
+ if (mode == HUD_MODE_ILS && ours && !transition && !docking) {
+ Point dst = fd->MountLocation();
+ projector->Transform(dst);
+
+ if (dst.z > 1.0) {
+ projector->Project(dst);
+
+ int x = (int) dst.x;
+ int y = (int) dst.y;
+
+ if (x > 4 && x < width-4 &&
+ y > 4 && y < height-4) {
+
+ window->DrawLine(x-6, y-6, x+6, y+6, hud_color);
+ window->DrawLine(x+6, y-6, x-6, y+6, hud_color);
+ }
+ }
+ }
+
+ // draw the hoops for this flight deck:
+ Scene* scene = camview->GetScene();
+ for (int h = 0; h < fd->NumHoops(); h++) {
+ Hoop* hoop = fd->GetHoops() + h;
+ if (hoop && scene) {
+ if (mode == HUD_MODE_ILS && ours && !transition && !docking) {
+ hoop->Show();
+ if (!hoop->GetScene())
+ scene->AddGraphic(hoop);
+ }
+ else {
+ hoop->Hide();
+ if (hoop->GetScene())
+ scene->DelGraphic(hoop);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+HUDView::DrawObjective()
+{
+ if (ship && ship->GetDirector() && ship->GetDirector()->Type() >= SteerAI::SEEKER) {
+ SteerAI* steer = (SteerAI*) ship->GetDirector();
+ Point obj = steer->GetObjective();
+ projector->Transform(obj);
+
+ if (obj.z > 1.0) {
+ projector->Project(obj);
+
+ int x = (int) obj.x;
+ int y = (int) obj.y;
+
+ if (x > 4 && x < width-4 &&
+ y > 4 && y < height-4) {
+
+ Color c = Color::Cyan;
+ window->DrawRect(x-6, y-6, x+6, y+6, c);
+ window->DrawLine(x-6, y-6, x+6, y+6, c);
+ window->DrawLine(x+6, y-6, x-6, y+6, c);
+ }
+ }
+
+ if (steer->GetOther()) {
+ obj = steer->GetOther()->Location();
+ projector->Transform(obj);
+
+ if (obj.z > 1.0) {
+ projector->Project(obj);
+
+ int x = (int) obj.x;
+ int y = (int) obj.y;
+
+ if (x > 4 && x < width-4 &&
+ y > 4 && y < height-4) {
+
+ Color c = Color::Orange;
+ window->DrawRect(x-6, y-6, x+6, y+6, c);
+ window->DrawLine(x-6, y-6, x+6, y+6, c);
+ window->DrawLine(x+6, y-6, x-6, y+6, c);
+ }
+ }
+ }
+ }
+ /***/
+}
+
+void
+HUDView::DrawNavPoint(Instruction& navpt, int index, int next)
+{
+ if (index >= 15 || !navpt.Region()) return;
+
+ // transform from starsystem to world coordinates:
+ Point npt = navpt.Region()->Location() + navpt.Location();
+
+ if (active_region)
+ npt -= active_region->Location();
+
+ npt = npt.OtherHand();
+
+ // transform from world to camera:
+ projector->Transform(npt);
+
+ // clip:
+ if (npt.z > 1.0) {
+
+ // project:
+ projector->Project(npt);
+
+ int x = (int) npt.x;
+ int y = (int) npt.y;
+
+ // clip:
+ if (x > 4 && x < width-4 &&
+ y > 4 && y < height-4) {
+
+ Color c = Color::White;
+ if (navpt.Status() > Instruction::ACTIVE && navpt.HoldTime() <= 0)
+ c = Color::DarkGray;
+
+ // draw:
+ if (next)
+ window->DrawEllipse(x-6, y-6, x+5, y+5, c);
+
+ window->DrawLine(x-6, y-6, x+6, y+6, c);
+ window->DrawLine(x+6, y-6, x-6, y+6, c);
+
+ if (index > 0) {
+ char npt_buf[32];
+ Rect npt_rect(x+10, y-4, 200, 12);
+
+ if (navpt.Status() == Instruction::COMPLETE && navpt.HoldTime() > 0) {
+ char hold_time[32];
+ FormatTime(hold_time, navpt.HoldTime());
+ sprintf(npt_buf, "%d %s", index, hold_time);
+ }
+ else {
+ sprintf(npt_buf, "%d", index);
+ }
+
+ DrawHUDText(TXT_NAV_PT + index, npt_buf, npt_rect, DT_LEFT);
+ }
+ }
+ }
+
+ if (next && mode == HUD_MODE_NAV && navpt.Region() == ship->GetRegion()) {
+
+ // Translate into camera relative:
+ Point tloc = navpt.Location().OtherHand();
+ projector->Transform(tloc);
+
+ int behind = tloc.z < 0;
+
+ if (behind)
+ tloc.z = -tloc.z;
+
+ // Project into screen coordinates:
+ projector->Project(tloc);
+
+ // DRAW THE OFFSCREEN CHASE INDICATOR:
+ if (behind ||
+ tloc.x <= 0 || tloc.x >= width-1 ||
+ tloc.y <= 0 || tloc.y >= height-1) {
+
+ // Left side:
+ if (tloc.x <= 0 || (behind && tloc.x < width/2)) {
+ if (tloc.y < ah) tloc.y = ah;
+ else if (tloc.y >= height-ah) tloc.y = height-1-ah;
+
+ chase_sprite->Show();
+ chase_sprite->SetAnimation(&chase_left);
+ chase_sprite->MoveTo(Point(aw, tloc.y, 1));
+ }
+
+ // Right side:
+ else if (tloc.x >= width-1 || behind) {
+ if (tloc.y < ah) tloc.y = ah;
+ else if (tloc.y >= height-ah) tloc.y = height-1-ah;
+
+ chase_sprite->Show();
+ chase_sprite->SetAnimation(&chase_right);
+ chase_sprite->MoveTo(Point(width-1-aw, tloc.y, 1));
+ }
+ else {
+ if (tloc.x < aw) tloc.x = aw;
+ else if (tloc.x >= width-aw) tloc.x = width-1-aw;
+
+ // Top edge:
+ if (tloc.y <= 0) {
+ chase_sprite->Show();
+ chase_sprite->SetAnimation(&chase_top);
+ chase_sprite->MoveTo(Point(tloc.x, ah, 1));
+ }
+
+ // Bottom edge:
+ else if (tloc.y >= height-1) {
+ chase_sprite->Show();
+ chase_sprite->SetAnimation(&chase_bottom);
+ chase_sprite->MoveTo(Point(tloc.x, height-1-ah, 1));
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::SetShip(Ship* s)
+{
+ if (ship != s) {
+ double new_scale = 1;
+
+ ship_status = -1;
+ ship = s;
+
+ if (ship) {
+ if (ship->Life() == 0 || ship->IsDying() || ship->IsDead()) {
+ ship = 0;
+ }
+ else {
+ Observe(ship);
+ new_scale = 1.1 * ship->Radius() / 64;
+
+ if (ship->Design()->hud_icon.Width()) {
+ TransferBitmap(ship->Design()->hud_icon, icon_ship, icon_ship_shade);
+ ColorizeBitmap(icon_ship, icon_ship_shade, txt_color);
+ }
+ }
+ }
+
+ if (az_ring) {
+ az_ring->Rescale(1/compass_scale);
+ az_ring->Rescale(new_scale);
+ }
+
+ if (az_pointer) {
+ az_pointer->Rescale(1/compass_scale);
+ az_pointer->Rescale(new_scale);
+ }
+
+ if (el_ring) {
+ el_ring->Rescale(1/compass_scale);
+ el_ring->Rescale(new_scale);
+ }
+
+ if (el_pointer) {
+ el_pointer->Rescale(1/compass_scale);
+ el_pointer->Rescale(new_scale);
+ }
+
+ compass_scale = new_scale;
+ inst_page = 0;
+
+ if (ship && ship->GetElement() && ship->GetElement()->NumInstructions() > 0)
+ if (!show_inst)
+ CycleHUDInst();
+ }
+
+ else if (ship && ship->Design()->hud_icon.Width()) {
+ bool update = false;
+ System::STATUS s = System::NOMINAL;
+ int integrity = (int) (ship->Integrity() / ship->Design()->integrity * 100);
+
+ if (integrity < 30) s = System::CRITICAL;
+ else if (integrity < 60) s = System::DEGRADED;
+
+ if (s != ship_status) {
+ ship_status = s;
+ update = true;
+ }
+
+ if (update) {
+ SetStatusColor((System::STATUS) ship_status);
+ ColorizeBitmap(icon_ship, icon_ship_shade, status_color);
+ }
+ }
+
+ if (ship && ship->Cockpit()) {
+ Solid* cockpit = (Solid*) ship->Cockpit();
+
+ bool change = false;
+
+ if (cockpit->Hidden()) {
+ if (cockpit_hud_texture)
+ change = true;
+
+ cockpit_hud_texture = 0;
+ }
+ else {
+ if (!cockpit_hud_texture)
+ change = true;
+
+ Model* cockpit_model = cockpit->GetModel();
+ Material* hud_material = 0;
+
+ if (cockpit_model) {
+ hud_material = (Material*) cockpit_model->FindMaterial("HUD");
+ if (hud_material) {
+ cockpit_hud_texture = hud_material->tex_emissive;
+ }
+ }
+ }
+
+ if (change) {
+ SetHUDColorSet(color);
+ }
+ }
+}
+
+void
+HUDView::SetTarget(SimObject* t)
+{
+ bool update = false;
+
+ if (target != t) {
+ tgt_status = -1;
+ target = t;
+ if (target) Observe(target);
+ update = true;
+ }
+
+ if (target && target->Type() == SimObject::SIM_SHIP) {
+ System::STATUS s = System::NOMINAL;
+ Ship* tship = (Ship*) target;
+ int integrity = (int) (tship->Integrity() / tship->Design()->integrity * 100);
+
+ if (integrity < 30) s = System::CRITICAL;
+ else if (integrity < 60) s = System::DEGRADED;
+
+ if (s != tgt_status) {
+ tgt_status = s;
+ update = true;
+ }
+ }
+
+ if (update) {
+ if (target && target->Type() == SimObject::SIM_SHIP) {
+ Ship* tship = (Ship*) target;
+ TransferBitmap(tship->Design()->hud_icon, icon_target, icon_target_shade);
+ }
+ else {
+ PrepareBitmap("hud_icon.pcx", icon_target, icon_target_shade);
+ }
+
+ SetStatusColor((System::STATUS) tgt_status);
+ ColorizeBitmap(icon_target, icon_target_shade, status_color);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+MFD*
+HUDView::GetMFD(int n) const
+{
+ if (n >= 0 && n < 3)
+ return mfd[n];
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::Refresh()
+{
+ sim = Sim::GetSim();
+ mouse_in = false;
+
+ if (!sim || !camview || !projector) {
+ return;
+ }
+
+ if (Mouse::LButton() == 0) {
+ mouse_latch = 0;
+ mouse_index = -1;
+ }
+
+ int mouse_index_old = mouse_index;
+
+ SetShip(sim->GetPlayerShip());
+
+ if (mode == HUD_MODE_OFF) {
+ if (cockpit_hud_texture) {
+ cockpit_hud_texture->FillRect(0,0,512,256,Color::Black);
+ }
+
+ sim->ShowGrid(false);
+ return;
+ }
+
+ if (cockpit_hud_texture && cockpit_hud_texture->Width() == 512) {
+ Bitmap* hud_bmp = 0;
+
+ if (hud_sprite[0]) {
+ hud_bmp = hud_sprite[0]->Frame();
+ int bmp_w = hud_bmp->Width();
+ int bmp_h = hud_bmp->Height();
+
+ cockpit_hud_texture->BitBlt( 0, 0, *hud_bmp, 0, 0, bmp_w, bmp_h);
+ }
+
+ if (hud_sprite[1]) {
+ hud_bmp = hud_sprite[1]->Frame();
+ int bmp_w = hud_bmp->Width();
+ int bmp_h = hud_bmp->Height();
+
+ cockpit_hud_texture->BitBlt(256, 0, *hud_bmp, 0, 0, bmp_w, bmp_h);
+ }
+
+ if (hud_sprite[6]) {
+ if (hud_sprite[6]->Hidden()) {
+ cockpit_hud_texture->FillRect(0,384,128,512,Color::Black);
+ }
+ else {
+ hud_bmp = hud_sprite[6]->Frame();
+ int bmp_w = hud_bmp->Width();
+ int bmp_h = hud_bmp->Height();
+
+ cockpit_hud_texture->BitBlt(0,384, *hud_bmp, 0, 0, bmp_w, bmp_h);
+ }
+ }
+
+ if (hud_sprite[7]) {
+ if (hud_sprite[7]->Hidden()) {
+ cockpit_hud_texture->FillRect(128,384,256,512,Color::Black);
+ }
+ else {
+ hud_bmp = hud_sprite[7]->Frame();
+ int bmp_w = hud_bmp->Width();
+ int bmp_h = hud_bmp->Height();
+
+ cockpit_hud_texture->BitBlt(128,384, *hud_bmp, 0, 0, bmp_w, bmp_h);
+ }
+ }
+
+ for (int i = 8; i < 32; i++) {
+ if (hud_sprite[i] && !hud_sprite[i]->Hidden()) {
+ Sprite* s = hud_sprite[i];
+
+ int cx = (int) s->Location().x;
+ int cy = (int) s->Location().y;
+ int w2 = s->Width() / 2;
+ int h2 = s->Height() / 2;
+
+ window->DrawBitmap(cx-w2, cy-h2, cx+w2, cy+h2, s->Frame(), Video::BLEND_ALPHA);
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < 32; i++) {
+ if (hud_sprite[i] && !hud_sprite[i]->Hidden()) {
+ Sprite* s = hud_sprite[i];
+
+ int cx = (int) s->Location().x;
+ int cy = (int) s->Location().y;
+ int w2 = s->Width() / 2;
+ int h2 = s->Height() / 2;
+
+ window->DrawBitmap(cx-w2, cy-h2, cx+w2, cy+h2, s->Frame(), Video::BLEND_ALPHA);
+ }
+ }
+
+ Video* video = Video::GetInstance();
+
+ for (i = 0; i < 31; i++) {
+ Sprite* s = pitch_ladder[i];
+
+ if (s && !s->Hidden()) {
+ s->Render2D(video);
+ }
+ }
+ }
+
+ //DrawStarSystem();
+ DrawMessages();
+
+ if (ship) {
+ // no hud in transition:
+ if (ship->InTransition()) {
+ transition = true;
+ HideAll();
+ return;
+ }
+
+ else if (transition) {
+ transition = false;
+ RestoreHUD();
+ }
+
+ CameraDirector* cam_dir = CameraDirector::GetInstance();
+
+ // everything is off during docking, except the final message:
+ if (cam_dir && cam_dir->GetMode() == CameraDirector::MODE_DOCKING) {
+ docking = true;
+ HideAll();
+
+ if (ship->GetFlightPhase() == Ship::DOCKING) {
+ Rect dock_rect(width/2-100, height/6, 200, 20);
+
+ if (ship->IsAirborne())
+ DrawHUDText(TXT_AUTO, Game::GetText("HUDView.SUCCESSFUL-LANDING"), dock_rect, DT_CENTER);
+ else
+ DrawHUDText(TXT_AUTO, Game::GetText("HUDView.DOCKING-COMPLETE"), dock_rect, DT_CENTER);
+ }
+ return;
+ }
+ else if (docking) {
+ docking = false;
+ RestoreHUD();
+ }
+
+ // go to NAV mode during autopilot:
+ if (ship->GetNavSystem() && ship->GetNavSystem()->AutoNavEngaged() && !arcade)
+ mode = HUD_MODE_NAV;
+
+ SetTarget(ship->GetTarget());
+
+ // internal view of HUD reticule
+ if (CameraDirector::GetCameraMode() <= CameraDirector::MODE_CHASE)
+ SetTacticalMode(0);
+
+ // external view
+ else
+ SetTacticalMode(!cockpit_hud_texture);
+
+ sim->ShowGrid(tactical &&
+ !ship->IsAirborne() &&
+ CameraDirector::GetCameraMode() != CameraDirector::MODE_VIRTUAL);
+
+ // draw HUD bars:
+ DrawBars();
+
+ if (missile_lock_sound) {
+ if (threat > 1) {
+ long max_vol = AudioConfig::WrnVolume();
+ long volume = -1500;
+
+ if (volume > max_vol)
+ volume = max_vol;
+
+ missile_lock_sound->SetVolume(volume);
+ missile_lock_sound->Play();
+ }
+ else {
+ missile_lock_sound->Stop();
+ }
+ }
+
+ DrawNav();
+ DrawILS();
+
+ // FOR DEBUG PURPOSES ONLY:
+ // DrawObjective();
+
+ if (!overlay) {
+ Rect fov_rect(0, 10, width, 10);
+ int fov_degrees = 180 - 2 * (int)(projector->XAngle()*180/PI);
+
+ if (fov_degrees > 90)
+ DrawHUDText(TXT_CAM_ANGLE, Game::GetText("HUDView.Wide-Angle"), fov_rect, DT_CENTER);
+
+ fov_rect.y = 20;
+ DrawHUDText(TXT_CAM_MODE, CameraDirector::GetModeName(), fov_rect, DT_CENTER);
+ }
+
+ DrawMFDs();
+
+ instr_left_sprite->Hide();
+ instr_right_sprite->Hide();
+ warn_left_sprite->Hide();
+ warn_right_sprite->Hide();
+
+ if (cockpit_hud_texture)
+ cockpit_hud_texture->FillRect(256,384,512,512,Color::Black);
+
+ if (show_inst) {
+ DrawInstructions();
+ }
+
+ else if (!arcade) {
+ if (ship->MasterCaution() && !show_warn)
+ ShowHUDWarn();
+
+ if (show_warn)
+ DrawWarningPanel();
+ }
+
+ if (width > 640 || (!show_inst && !show_warn)) {
+ Rect icon_rect(120, height-24, 128, 16);
+
+ if (ship)
+ DrawHUDText(TXT_ICON_SHIP_TYPE, ship->DesignName(), icon_rect, DT_CENTER);
+
+ icon_rect.x = width - 248;
+
+ if (target && target->Type() == SimObject::SIM_SHIP) {
+ Ship* tship = (Ship*) target;
+ DrawHUDText(TXT_ICON_TARGET_TYPE, tship->DesignName(), icon_rect, DT_CENTER);
+ }
+ }
+ }
+ else {
+ if (target) {
+ SetTarget(0);
+ }
+ }
+
+ // latch mouse down to prevent dragging into a control:
+ if (Mouse::LButton() == 1)
+ mouse_latch = 1;
+
+ if (mouse_index > -1 && mouse_index_old != mouse_index)
+ MouseFrame();
+}
+
+void
+HUDView::DrawMFDs()
+{
+ for (int i = 0; i < 3; i++) {
+ mfd[i]->Show();
+ mfd[i]->SetShip(ship);
+ mfd[i]->SetCockpitHUDTexture(cockpit_hud_texture);
+ mfd[i]->Draw();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::DrawStarSystem()
+{
+ if (sim && sim->GetStarSystem()) {
+ StarSystem* sys = sim->GetStarSystem();
+
+ ListIter<OrbitalBody> iter = sys->Bodies();
+ while (++iter) {
+ OrbitalBody* body = iter.value();
+ DrawOrbitalBody(body);
+ }
+ }
+}
+
+void
+HUDView::DrawOrbitalBody(OrbitalBody* body)
+{
+ if (body) {
+ Point p = body->Rep()->Location();
+
+ projector->Transform(p);
+
+ if (p.z > 100) {
+ float r = (float) body->Radius();
+ r = projector->ProjectRadius(p, r);
+ projector->Project(p, false);
+
+ window->DrawEllipse((int) (p.x-r),
+ (int) (p.y-r),
+ (int) (p.x+r),
+ (int) (p.y+r),
+ Color::Cyan);
+ }
+
+ ListIter<OrbitalBody> iter = body->Satellites();
+ while (++iter) {
+ OrbitalBody* body = iter.value();
+ DrawOrbitalBody(body);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::ExecFrame()
+{
+ // update the position of HUD elements that are
+ // part of the 3D scene (like fpm and lcos sprites)
+ HideCompass();
+
+ if (ship && !transition && !docking && mode != HUD_MODE_OFF) {
+ Player* p = Player::GetCurrentPlayer();
+ gunsight = p->Gunsight();
+
+ if (ship->IsStarship()) {
+ if (tactical) {
+ hud_left_sprite->Hide();
+ hud_right_sprite->Hide();
+ }
+
+ else if (hud_left_sprite->Frame() != &hud_left_starship) {
+ hud_left_sprite->SetAnimation(&hud_left_starship);
+ hud_right_sprite->SetAnimation(&hud_right_starship);
+
+ hud_left_sprite->MoveTo( Point(width/2-128, height/2, 1));
+ hud_right_sprite->MoveTo(Point(width/2+128, height/2, 1));
+ }
+ }
+
+ else if (!ship->IsStarship()) {
+ if (ship->IsAirborne() && hud_left_sprite->Frame() != &hud_left_air) {
+ hud_left_sprite->SetAnimation(&hud_left_air);
+ hud_right_sprite->SetAnimation(&hud_right_air);
+ }
+
+ else if (!ship->IsAirborne() && hud_left_sprite->Frame() != &hud_left_fighter) {
+ hud_left_sprite->SetAnimation(&hud_left_fighter);
+ hud_right_sprite->SetAnimation(&hud_right_fighter);
+ }
+ }
+
+ if (!tactical) {
+ if (Game::MaxTexSize() > 128) {
+ hud_left_sprite->Show();
+ hud_right_sprite->Show();
+ }
+
+ if (!arcade)
+ DrawFPM();
+
+ if (ship->IsStarship() && ship->GetFLCSMode() == Ship::FLCS_HELM)
+ DrawHPM();
+ else if (!arcade)
+ DrawPitchLadder();
+ }
+
+ else {
+ if (ship->IsStarship() && ship->GetFLCSMode() == Ship::FLCS_HELM)
+ DrawCompass();
+ }
+
+ if (mode == HUD_MODE_TAC) {
+ DrawSight();
+ DrawDesignators();
+ }
+
+ if (width > 640 || (!show_inst && !show_warn)) {
+ icon_ship_sprite->Show();
+ icon_target_sprite->Show();
+ }
+ else {
+ icon_ship_sprite->Hide();
+ icon_target_sprite->Hide();
+ }
+ }
+
+ // if the hud is off or prohibited,
+ // hide all of the sprites:
+
+ else {
+ hud_left_sprite->Hide();
+ hud_right_sprite->Hide();
+ instr_left_sprite->Hide();
+ instr_right_sprite->Hide();
+ warn_left_sprite->Hide();
+ warn_right_sprite->Hide();
+ icon_ship_sprite->Hide();
+ icon_target_sprite->Hide();
+ fpm_sprite->Hide();
+ hpm_sprite->Hide();
+ lead_sprite->Hide();
+ aim_sprite->Hide();
+ tgt1_sprite->Hide();
+ tgt2_sprite->Hide();
+ tgt3_sprite->Hide();
+ tgt4_sprite->Hide();
+ chase_sprite->Hide();
+
+ for (int i = 0; i < 3; i++)
+ mfd[i]->Hide();
+
+ for (i = 0; i < 31; i++)
+ pitch_ladder[i]->Hide();
+
+ DrawILS();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::CycleMFDMode(int mfd_index)
+{
+ if (mfd_index < 0 || mfd_index > 2) return;
+
+ int m = mfd[mfd_index]->GetMode();
+ m++;
+
+ if (mfd_index == 2) {
+ if (m > MFD::MFD_MODE_SHIP)
+ m = MFD::MFD_MODE_OFF;
+ }
+ else {
+ if (m > MFD::MFD_MODE_3D)
+ m = MFD::MFD_MODE_OFF;
+
+ if (m == MFD::MFD_MODE_GAME)
+ m++;
+
+ if (mfd_index != 0 && m == MFD::MFD_MODE_SHIP)
+ m++;
+ }
+
+ mfd[mfd_index]->SetMode(m);
+ HUDSounds::PlaySound(HUDSounds::SND_MFD_MODE);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::ShowHUDWarn()
+{
+ if (!show_warn) {
+ show_warn = true;
+
+ if (ship && ship->HullStrength() <= 40) {
+ // TOO OBNOXIOUS!!
+ HUDSounds::PlaySound(HUDSounds::SND_RED_ALERT);
+ }
+ }
+}
+
+void
+HUDView::ShowHUDInst()
+{
+ show_inst = true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::HideHUDWarn()
+{
+ show_warn = false;
+
+ if (ship) {
+ ship->ClearCaution();
+ HUDSounds::StopSound(HUDSounds::SND_RED_ALERT);
+ }
+}
+
+void
+HUDView::HideHUDInst()
+{
+ show_inst = false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::CycleHUDWarn()
+{
+ HUDSounds::PlaySound(HUDSounds::SND_HUD_WIDGET);
+ show_warn = !show_warn;
+
+ if (ship && !show_warn) {
+ ship->ClearCaution();
+ HUDSounds::StopSound(HUDSounds::SND_RED_ALERT);
+ }
+}
+
+void
+HUDView::CycleHUDInst()
+{
+ show_inst = !show_inst;
+ HUDSounds::PlaySound(HUDSounds::SND_HUD_WIDGET);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::SetHUDMode(int m)
+{
+ if (mode != m) {
+ mode = m;
+
+ if (mode > HUD_MODE_ILS || mode < HUD_MODE_OFF)
+ mode = HUD_MODE_OFF;
+
+ if (ship && !ship->IsDropship() && mode == HUD_MODE_ILS)
+ mode = HUD_MODE_OFF;
+
+ RestoreHUD();
+ }
+}
+
+void
+HUDView::CycleHUDMode()
+{
+ mode++;
+
+ if (arcade && mode != HUD_MODE_TAC)
+ mode = HUD_MODE_OFF;
+
+ else if (mode > HUD_MODE_ILS || mode < HUD_MODE_OFF)
+ mode = HUD_MODE_OFF;
+
+ else if (!ship->IsDropship() && mode == HUD_MODE_ILS)
+ mode = HUD_MODE_OFF;
+
+ RestoreHUD();
+ HUDSounds::PlaySound(HUDSounds::SND_HUD_MODE);
+}
+
+void
+HUDView::RestoreHUD()
+{
+ if (mode == HUD_MODE_OFF) {
+ HideAll();
+ }
+ else {
+ for (int i = 0; i < 3; i++)
+ mfd[i]->Show();
+
+ if (width > 640 || (!show_inst && !show_warn)) {
+ icon_ship_sprite->Show();
+ icon_target_sprite->Show();
+ }
+ else {
+ icon_ship_sprite->Hide();
+ icon_target_sprite->Hide();
+ }
+
+ if (!tactical && Game::MaxTexSize() > 128) {
+ hud_left_sprite->Show();
+ hud_right_sprite->Show();
+ }
+
+ fpm_sprite->Show();
+
+ if (ship && ship->IsStarship())
+ hpm_sprite->Show();
+
+ if (gunsight == 0)
+ aim_sprite->Show();
+ else
+ lead_sprite->Show();
+ }
+}
+
+void
+HUDView::HideAll()
+{
+ for (int i = 0; i < 3; i++)
+ mfd[i]->Hide();
+
+ hud_left_sprite->Hide();
+ hud_right_sprite->Hide();
+ instr_left_sprite->Hide();
+ instr_right_sprite->Hide();
+ warn_left_sprite->Hide();
+ warn_right_sprite->Hide();
+ icon_ship_sprite->Hide();
+ icon_target_sprite->Hide();
+ fpm_sprite->Hide();
+ hpm_sprite->Hide();
+ lead_sprite->Hide();
+ aim_sprite->Hide();
+ tgt1_sprite->Hide();
+ tgt2_sprite->Hide();
+ tgt3_sprite->Hide();
+ tgt4_sprite->Hide();
+ chase_sprite->Hide();
+
+ sim->ShowGrid(false);
+
+ for (i = 0; i < 31; i++)
+ pitch_ladder[i]->Hide();
+
+ if (missile_lock_sound)
+ missile_lock_sound->Stop();
+
+ HideCompass();
+ DrawILS();
+ Mouse::Show(false);
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+HUDView::Ambient() const
+{
+ if (!sim || !ship || mode == HUD_MODE_OFF)
+ return Color::Black;
+
+ SimRegion* rgn = sim->GetActiveRegion();
+
+ if (!rgn || !rgn->IsAirSpace())
+ return Color::Black;
+
+ Color c = sim->GetStarSystem()->Ambient();
+
+ if (c.Red() > 32 || c.Green() > 32 || c.Blue() > 32)
+ return Color::Black;
+
+ // if we get this far, the night-vision aid is on
+ return night_vision_colors[color];
+}
+
+Color
+HUDView::CycleHUDColor()
+{
+ HUDSounds::PlaySound(HUDSounds::SND_HUD_MODE);
+ SetHUDColorSet(color+1);
+ return hud_color;
+}
+
+void
+HUDView::SetHUDColorSet(int c)
+{
+ color = c;
+ if (color > NUM_HUD_COLORS-1) color = 0;
+ hud_color = standard_hud_colors[color];
+ txt_color = standard_txt_colors[color];
+
+ ColorizeBitmap(fpm, fpm_shade, hud_color, true);
+ ColorizeBitmap(hpm, hpm_shade, hud_color, true);
+ ColorizeBitmap(lead, lead_shade, txt_color * 1.25, true);
+ ColorizeBitmap(cross, cross_shade, hud_color, true);
+ ColorizeBitmap(cross1, cross1_shade, hud_color, true);
+ ColorizeBitmap(cross2, cross2_shade, hud_color, true);
+ ColorizeBitmap(cross3, cross3_shade, hud_color, true);
+ ColorizeBitmap(cross4, cross4_shade, hud_color, true);
+
+ if (Game::MaxTexSize() > 128) {
+ ColorizeBitmap(hud_left_air, hud_left_shade_air, hud_color);
+ ColorizeBitmap(hud_right_air, hud_right_shade_air, hud_color);
+ ColorizeBitmap(hud_left_fighter, hud_left_shade_fighter, hud_color);
+ ColorizeBitmap(hud_right_fighter, hud_right_shade_fighter, hud_color);
+ ColorizeBitmap(hud_left_starship, hud_left_shade_starship, hud_color);
+ ColorizeBitmap(hud_right_starship, hud_right_shade_starship, hud_color);
+
+ ColorizeBitmap(instr_left, instr_left_shade, hud_color);
+ ColorizeBitmap(instr_right, instr_right_shade, hud_color);
+ ColorizeBitmap(warn_left, warn_left_shade, hud_color);
+ ColorizeBitmap(warn_right, warn_right_shade, hud_color);
+
+ ColorizeBitmap(pitch_ladder_pos, pitch_ladder_pos_shade, hud_color);
+ ColorizeBitmap(pitch_ladder_neg, pitch_ladder_neg_shade, hud_color);
+ }
+
+ ColorizeBitmap(icon_ship, icon_ship_shade, txt_color);
+ ColorizeBitmap(icon_target, icon_target_shade, txt_color);
+
+ ColorizeBitmap(chase_left, chase_left_shade, hud_color, true);
+ ColorizeBitmap(chase_right, chase_right_shade, hud_color, true);
+ ColorizeBitmap(chase_top, chase_top_shade, hud_color, true);
+ ColorizeBitmap(chase_bottom, chase_bottom_shade, hud_color, true);
+
+ MFD::SetColor(hud_color);
+ Hoop::SetColor(hud_color);
+
+ for (int i = 0; i < 3; i++)
+ mfd[i]->SetText3DColor(txt_color);
+
+ Font* font = FontMgr::Find("HUD");
+ if (font)
+ font->SetColor(txt_color);
+
+ for (i = 0; i < TXT_LAST; i++)
+ hud_text[i].color = txt_color;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::Message(const char* fmt, ...)
+{
+ if (fmt) {
+ char msg[512];
+ vsprintf(msg, fmt, (char *)(&fmt+1));
+
+ char* newline = strchr(msg, '\n');
+ if (newline)
+ *newline = 0;
+
+ Print("%s\n", msg);
+
+ if (hud_view) {
+ int index = -1;
+
+ for (int i = 0; i < MAX_MSG; i++) {
+ if (hud_view->msg_time[i] <= 0) {
+ index = i;
+ break;
+ }
+ }
+
+ // no space; advance pipeline:
+ if (index < 0) {
+ for (i = 0; i < MAX_MSG-1; i++) {
+ hud_view->msg_text[i] = hud_view->msg_text[i+1];
+ hud_view->msg_time[i] = hud_view->msg_time[i+1];
+ }
+
+ index = MAX_MSG-1;
+ }
+
+ hud_view->msg_text[index] = msg;
+ hud_view->msg_time[index] = 10;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::ClearMessages()
+{
+ if (hud_view) {
+ for (int i = 0; i < MAX_MSG-1; i++) {
+ hud_view->msg_text[i] = Text();
+ hud_view->msg_time[i] = 0;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::PrepareBitmap(const char* name, Bitmap& img, BYTE*& shades)
+{
+ delete [] shades;
+ shades = 0;
+
+ DataLoader* loader = DataLoader::GetLoader();
+
+ loader->SetDataPath("HUD/");
+ int loaded = loader->LoadBitmap(name, img, Bitmap::BMP_TRANSPARENT);
+ loader->SetDataPath(0);
+
+ if (!loaded)
+ return;
+
+ int w = img.Width();
+ int h = img.Height();
+
+ shades = new(__FILE__,__LINE__) BYTE[w*h];
+
+ for (int y = 0; y < h; y++)
+ for (int x = 0; x < w; x++)
+ shades[y*w+x] = (BYTE) (img.GetColor(x,y).Red() * 0.66);
+}
+
+void
+HUDView::TransferBitmap(const Bitmap& src, Bitmap& img, BYTE*& shades)
+{
+ delete [] shades;
+ shades = 0;
+
+ if (src.Width() != img.Width() || src.Height() != img.Height())
+ return;
+
+ img.CopyBitmap(src);
+ img.SetType(Bitmap::BMP_TRANSLUCENT);
+
+ int w = img.Width();
+ int h = img.Height();
+
+ shades = new(__FILE__,__LINE__) BYTE[w*h];
+
+ for (int y = 0; y < h; y++)
+ for (int x = 0; x < w; x++)
+ shades[y*w+x] = (BYTE) (img.GetColor(x,y).Red() * 0.5);
+}
+
+void
+HUDView::ColorizeBitmap(Bitmap& img, BYTE* shades, Color color, bool force_alpha)
+{
+ if (!shades) return;
+
+ int max_tex_size = Game::MaxTexSize();
+
+ if (max_tex_size < 128)
+ Game::SetMaxTexSize(128);
+
+ if (hud_view && hud_view->cockpit_hud_texture && !force_alpha) {
+ img.FillColor(Color::Black);
+ Color* dst = img.HiPixels();
+ BYTE* src = shades;
+
+ for (int y = 0; y < img.Height(); y++) {
+ for (int x = 0; x < img.Width(); x++) {
+ if (*src)
+ *dst = color.dim(*src/200.0);
+ else
+ *dst = Color::Black;
+
+ dst++;
+ src++;
+ }
+ }
+ img.MakeTexture();
+ }
+ else {
+ img.FillColor(color);
+ img.CopyAlphaImage(img.Width(), img.Height(), shades);
+ img.MakeTexture();
+ }
+
+ if (max_tex_size < 128)
+ Game::SetMaxTexSize(max_tex_size);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HUDView::MouseFrame()
+{
+ MouseController* ctrl = MouseController::GetInstance();
+ if (ctrl && ctrl->Active())
+ return;
+
+ if (mouse_index >= TXT_CAUTION_TXT && mouse_index <= TXT_LAST_CAUTION) {
+ if (show_inst) {
+ if (mouse_index == TXT_INSTR_PAGE) {
+ if (Mouse::X() > width/2 + 125)
+ CycleInstructions(1);
+ else if (Mouse::X() < width/2 + 65)
+ CycleInstructions(-1);
+ }
+ else
+ show_inst = false;
+ }
+ else {
+ CycleHUDWarn();
+ }
+ return;
+ }
+
+ Starshatter* stars = Starshatter::GetInstance();
+ if (mouse_index == TXT_PAUSED)
+ stars->Pause(!Game::Paused());
+
+ if (mouse_index == TXT_GEAR_DOWN)
+ ship->ToggleGear();
+
+ if (mouse_index == TXT_HUD_MODE) {
+ CycleHUDMode();
+
+ if (mode == HUD_MODE_OFF)
+ CycleHUDMode();
+ }
+
+ if (mouse_index == TXT_PRIMARY_WEP) {
+ HUDSounds::PlaySound(HUDSounds::SND_WEP_MODE);
+ ship->CyclePrimary();
+ }
+
+ if (mouse_index == TXT_SECONDARY_WEP) {
+ HUDSounds::PlaySound(HUDSounds::SND_WEP_MODE);
+ ship->CycleSecondary();
+ }
+
+ if (mouse_index == TXT_DECOY)
+ ship->FireDecoy();
+
+ if (mouse_index == TXT_SHIELD) {
+ Shield* shield = ship->GetShield();
+
+ if (shield) {
+ double level = shield->GetPowerLevel();
+
+ const Rect& r = hud_text[TXT_SHIELD].rect;
+ if (Mouse::X() < r.x + r.w * 0.75)
+ shield->SetPowerLevel(level - 10);
+ else
+ shield->SetPowerLevel(level + 10);
+
+ HUDSounds::PlaySound(HUDSounds::SND_SHIELD_LEVEL);
+ }
+ }
+
+ if (mouse_index == TXT_AUTO)
+ ship->TimeSkip();
+
+ if (mouse_index >= TXT_NAV_INDEX && mouse_index <= TXT_NAV_ETR) {
+ ship->SetAutoNav(!ship->IsAutoNavEngaged());
+ SetHUDMode(HUD_MODE_TAC);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+HUDView::IsMouseLatched()
+{
+ bool result = mouse_in;
+
+ if (!result) {
+ HUDView* hud = HUDView::GetInstance();
+
+ for (int i = 0; i < 3; i++)
+ result = result || hud->mfd[i]->IsMouseLatched();
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+HUDView::IsNameCrowded(int x, int y)
+{
+ for (int i = 0; i < MAX_CONTACT; i++) {
+ HUDText& test = hud_text[TXT_CONTACT_NAME + i];
+
+ if (!test.hidden) {
+ Rect r = test.rect;
+
+ int dx = r.x - x;
+ int dy = r.y - y;
+ int d = dx*dx + dy*dy;
+
+ if (d <= 400)
+ return true;
+ }
+
+ test = hud_text[TXT_CONTACT_INFO + i];
+
+ if (!test.hidden) {
+ Rect r = test.rect;
+
+ int dx = r.x - x;
+ int dy = r.y - y;
+ int d = dx*dx + dy*dy;
+
+ if (d <= 400)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+HUDView::DrawDiamond(int x, int y, int r, Color c)
+{
+ POINT diamond[4];
+
+ diamond[0].x = x;
+ diamond[0].y = y-r;
+
+ diamond[1].x = x+r;
+ diamond[1].y = y;
+
+ diamond[2].x = x;
+ diamond[2].y = y+r;
+
+ diamond[3].x = x-r;
+ diamond[3].y = y;
+
+ window->DrawPoly(4, diamond, c);
+} \ No newline at end of file
diff --git a/Stars45/HUDView.h b/Stars45/HUDView.h
new file mode 100644
index 0000000..ef973bf
--- /dev/null
+++ b/Stars45/HUDView.h
@@ -0,0 +1,218 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: HUDView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ View class for Heads Up Display
+*/
+
+#ifndef HUDView_h
+#define HUDView_h
+
+#include "Types.h"
+#include "View.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "System.h"
+#include "SimObject.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Graphic;
+class Sprite;
+class Solid;
+class Ship;
+class Contact;
+class Physical;
+class OrbitalBody;
+class OrbitalRegion;
+class Instruction;
+class CameraView;
+class Projector;
+class MFD;
+
+// +--------------------------------------------------------------------+
+
+class HUDView : public View,
+ public SimObserver
+{
+public:
+ HUDView(Window* c);
+ virtual ~HUDView();
+
+ enum HUDModes { HUD_MODE_OFF, HUD_MODE_TAC, HUD_MODE_NAV, HUD_MODE_ILS };
+
+ // Operations:
+ virtual void Refresh();
+ virtual void OnWindowMove();
+ virtual void ExecFrame();
+ virtual void UseCameraView(CameraView* v);
+
+ virtual Ship* GetShip() const { return ship; }
+ virtual SimObject* GetTarget() const { return target; }
+ virtual void SetShip(Ship* s);
+ virtual void SetTarget(SimObject* t);
+ virtual MFD* GetMFD(int n) const;
+
+ virtual void HideAll();
+ virtual void DrawBars();
+ virtual void DrawNav();
+ virtual void DrawILS();
+ virtual void DrawObjective();
+ virtual void DrawNavInfo();
+ virtual void DrawNavPoint(Instruction& navpt, int index, int next);
+ virtual void DrawContactMarkers();
+ virtual void DrawContact(Contact* c, int index);
+ virtual void DrawTrack(Contact* c);
+ virtual void DrawTrackSegment(Point& t1, Point& t2, Color c);
+ virtual void DrawRect(SimObject* targ);
+ virtual void DrawTarget();
+ virtual void DrawSight();
+ virtual void DrawLCOS(SimObject* targ, double dist);
+ virtual void DrawDesignators();
+ virtual void DrawFPM();
+ virtual void DrawHPM();
+ virtual void DrawCompass();
+ virtual void HideCompass();
+ virtual void DrawPitchLadder();
+ virtual void DrawStarSystem();
+
+ virtual void DrawMFDs();
+ virtual void DrawWarningPanel();
+ virtual void DrawInstructions();
+ virtual void DrawMessages();
+
+ virtual void MouseFrame();
+
+ virtual int GetHUDMode() const { return mode; }
+ virtual int GetTacticalMode() const { return tactical; }
+ virtual void SetTacticalMode(int mode=1);
+ virtual int GetOverlayMode() const { return overlay; }
+ virtual void SetOverlayMode(int mode=1);
+
+ virtual void SetHUDMode(int mode);
+ virtual void CycleHUDMode();
+ virtual Color CycleHUDColor();
+ virtual void SetHUDColorSet(int c);
+ virtual int GetHUDColorSet() const { return color; }
+ virtual Color GetHUDColor() const { return hud_color; }
+ virtual Color GetTextColor() const { return txt_color; }
+ virtual Color Ambient() const;
+ virtual void ShowHUDWarn();
+ virtual void ShowHUDInst();
+ virtual void HideHUDWarn();
+ virtual void HideHUDInst();
+ virtual void CycleHUDWarn();
+ virtual void CycleHUDInst();
+ virtual void CycleMFDMode(int mfd);
+ virtual void CycleInstructions(int direction);
+ virtual void RestoreHUD();
+
+ virtual void TargetOff() { target = 0; }
+ static Color MarkerColor(Contact* targ);
+
+ static bool IsNameCrowded(int x, int y);
+ static bool IsMouseLatched();
+ static HUDView* GetInstance() { return hud_view; }
+ static void Message(const char* fmt, ...);
+ static void ClearMessages();
+ static void PrepareBitmap(const char* name, Bitmap& img, BYTE*& shades);
+ static void TransferBitmap(const Bitmap& src, Bitmap& img, BYTE*& shades);
+ static void ColorizeBitmap(Bitmap& img, BYTE* shades, Color color, bool force_alpha=false);
+
+ static int GetGunsight() { return gunsight; }
+ static void SetGunsight(int s) { gunsight = s; }
+ static bool IsArcade() { return arcade; }
+ static void SetArcade(bool a) { arcade = a; }
+ static int DefaultColorSet() { return def_color_set; }
+ static void SetDefaultColorSet(int c) { def_color_set = c; }
+ static Color GetStatusColor(System::STATUS status);
+ static bool ShowFPS() { return show_fps; }
+ static void ShowFPS(bool f) { show_fps = f; }
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+protected:
+ const char* FormatInstruction(Text instr);
+ void SetStatusColor(System::STATUS status);
+
+ enum HUD_CASE { HUD_MIXED_CASE, HUD_UPPER_CASE };
+
+ void DrawDiamond(int x, int y, int r, Color c);
+ void DrawHUDText(int index, const char* txt, Rect& rect, int align, int upcase=HUD_UPPER_CASE, bool box=false);
+ void HideHUDText(int index);
+
+ void DrawOrbitalBody(OrbitalBody* body);
+
+ Projector* projector;
+ CameraView* camview;
+
+ int width, height, aw, ah;
+ double xcenter, ycenter;
+
+ Sim* sim;
+ Ship* ship;
+ SimObject* target;
+
+ SimRegion* active_region;
+
+ Bitmap* cockpit_hud_texture;
+
+ Color hud_color;
+ Color txt_color;
+ Color status_color;
+
+ bool show_warn;
+ bool show_inst;
+ int inst_page;
+ int threat;
+
+ int mode;
+ int color;
+ int tactical;
+ int overlay;
+ int transition;
+ int docking;
+
+ MFD* mfd[3];
+
+ Sprite* pitch_ladder[31];
+ Sprite* hud_sprite[32];
+
+ Solid* az_ring;
+ Solid* az_pointer;
+ Solid* el_ring;
+ Solid* el_pointer;
+ double compass_scale;
+
+ enum { MAX_MSG = 6 };
+ Text msg_text[MAX_MSG];
+ double msg_time[MAX_MSG];
+
+ static HUDView* hud_view;
+ static bool arcade;
+ static bool show_fps;
+ static int gunsight;
+ static int def_color_set;
+};
+
+
+// +--------------------------------------------------------------------+
+
+struct HUDText {
+ Font* font;
+ Color color;
+ Rect rect;
+ bool hidden;
+};
+
+#endif HUDView_h
+
diff --git a/Stars45/Hangar.cpp b/Stars45/Hangar.cpp
new file mode 100644
index 0000000..f1e8019
--- /dev/null
+++ b/Stars45/Hangar.cpp
@@ -0,0 +1,933 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Hangar.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Everything needed to store and maintain space craft
+
+ See Also: FlightDeck
+*/
+
+#include "MemDebug.h"
+#include "Hangar.h"
+#include "FlightDeck.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Sim.h"
+#include "Instruction.h"
+#include "Element.h"
+#include "Mission.h"
+#include "RadioMessage.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatGroup.h"
+
+#include "Game.h"
+#include "Random.h"
+
+// +======================================================================+
+
+class HangarSlot
+{
+ friend class Hangar;
+
+public:
+ static const char* TYPENAME() { return "HangarSlot"; }
+
+ HangarSlot();
+ ~HangarSlot();
+ int operator == (const HangarSlot& that) const { return this == &that; }
+
+private:
+ Text squadron;
+ CombatGroup* group;
+ Ship* ship;
+ int iff;
+ const ShipDesign* design;
+ FlightDeck* deck;
+ int slot;
+ int state;
+ double time;
+ Element* package;
+ bool alert_hold;
+ int loadout[16];
+};
+
+// +--------------------------------------------------------------------+
+
+HangarSlot::HangarSlot()
+ : ship(0), group(0), design(0), deck(0), slot(0),
+ state(0), time(0), package(0), alert_hold(false)
+{
+ for (int i = 0; i < 16; i++)
+ loadout[i] = -1;
+}
+
+HangarSlot::~HangarSlot()
+{
+}
+
+// +======================================================================+
+
+Hangar::Hangar()
+ : ship(0), nsquadrons(0), last_patrol_launch(0)
+{
+ ZeroMemory(nslots, sizeof(nslots));
+ ZeroMemory(squadrons, sizeof(squadrons));
+}
+
+// +----------------------------------------------------------------------+
+
+Hangar::Hangar(const Hangar& s)
+ : ship(0), nsquadrons(s.nsquadrons), last_patrol_launch(s.last_patrol_launch)
+{
+ ZeroMemory(nslots, sizeof(nslots));
+ ZeroMemory(squadrons, sizeof(squadrons));
+}
+
+// +--------------------------------------------------------------------+
+
+Hangar::~Hangar()
+{
+ for (int i = 0; i < MAX_SQUADRONS; i++)
+ delete [] squadrons[i];
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Hangar::ExecFrame(double seconds)
+{
+ for (int n = 0; n < nsquadrons; n++) {
+ if (squadrons[n] && nslots[n] > 0) {
+ for (int i = 0; i < nslots[n]; i++) {
+ HangarSlot* slot = &squadrons[n][i];
+
+ switch (slot->state) {
+ case UNAVAIL:
+ case STORAGE:
+ case APPROACH:
+ break;
+
+ case ACTIVE:
+ if (slot->ship && slot->ship->GetFlightPhase() == Ship::APPROACH)
+ slot->state = APPROACH;
+ break;
+
+ case MAINT:
+ if (slot->time > 0) {
+ slot->time -= seconds;
+ }
+ else {
+ slot->time = 0;
+ slot->state = STORAGE;
+ }
+ break;
+
+
+ case PREP:
+ if (slot->time > 0) {
+ slot->time -= seconds;
+ }
+ else {
+ slot->time = 0;
+ FinishPrep(slot);
+ }
+ break;
+
+ case ALERT:
+ if (slot->time > 0) {
+ slot->time -= seconds;
+ }
+ else if (slot->deck) {
+ slot->time = 0;
+
+ // if package has specific objective, launch as soon as possible:
+ if (!slot->alert_hold)
+ slot->deck->Launch(slot->slot);
+
+ switch (slot->deck->State(slot->slot)) {
+ case FlightDeck::READY: slot->state = ALERT; break;
+ case FlightDeck::QUEUED: slot->state = QUEUED; break;
+ case FlightDeck::LOCKED: slot->state = LOCKED; break;
+ case FlightDeck::LAUNCH: slot->state = LAUNCH; break;
+ default: slot->state = STORAGE; break;
+ }
+ }
+ break;
+
+ case QUEUED:
+ case LOCKED:
+ if (slot->deck) {
+ switch (slot->deck->State(slot->slot)) {
+ case FlightDeck::READY: slot->state = ALERT; break;
+ case FlightDeck::QUEUED: slot->state = QUEUED; break;
+ case FlightDeck::LOCKED: slot->state = LOCKED; break;
+ case FlightDeck::LAUNCH: slot->state = LAUNCH; break;
+ default: slot->state = STORAGE; break;
+ }
+
+ slot->time = slot->deck->TimeRemaining(slot->slot);
+ }
+ break;
+
+ case LAUNCH:
+ if (slot->deck) {
+ slot->time = slot->deck->TimeRemaining(slot->slot);
+
+ if (slot->ship && slot->ship->GetFlightPhase() > Ship::LAUNCH) {
+ slot->state = ACTIVE;
+ slot->time = 0;
+ }
+ }
+ break;
+
+ case RECOVERY:
+ break;
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Hangar::FinishPrep(HangarSlot* slot)
+{
+ if (slot->deck->SpaceLeft(slot->design->type)) {
+ Sim* sim = Sim::GetSim();
+
+ Text ship_name = slot->squadron;
+
+ slot->ship = sim->CreateShip(ship_name, "",
+ (ShipDesign*) slot->design,
+ ship->GetRegion()->Name(),
+ Point(0, 0, 0),
+ slot->iff,
+ ship->GetCommandAILevel(),
+ slot->loadout);
+
+ Observe(slot->ship);
+
+ if (slot->package) {
+ slot->package->SetCommander(ship->GetElement());
+ slot->package->AddShip(slot->ship);
+
+ if (slot->group) {
+ slot->package->SetCombatGroup(slot->group);
+ slot->package->SetCombatUnit(slot->group->GetNextUnit());
+ }
+
+ char name[64];
+ sprintf(name, "%s %d",
+ (const char*) slot->package->Name(),
+ slot->ship->GetElementIndex());
+ slot->ship->SetName(name);
+ }
+
+ slot->slot = -1; // take first available slot
+ if (slot->deck->Spot(slot->ship, slot->slot)) {
+ slot->state = ALERT;
+ return true;
+ }
+
+ Print("WARNING: Could not spot alert ship - carrier: '%s' ship '%s'\n",
+ ship->Name(), slot->ship->Name());
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Hangar::Update(SimObject* obj)
+{
+ bool found = false;
+
+ for (int n = 0; !found && n < nsquadrons; n++) {
+ if (squadrons[n] && nslots[n] > 0) {
+ for (int i = 0; !found && i < nslots[n]; i++) {
+ HangarSlot* slot = &squadrons[n][i];
+
+ if (slot->ship == obj) {
+ // was ship destroyed in combat,
+ // or did it just dock here?
+ if (slot->state != MAINT) {
+ slot->state = UNAVAIL;
+ slot->ship = 0;
+ slot->deck = 0;
+ slot->time = 0;
+ slot->package = 0;
+ }
+
+ found = true;
+ }
+ }
+ }
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+Hangar::GetObserverName() const
+{
+ static char name[64];
+ if (ship)
+ sprintf(name, "Hangar(%s)", ship->Name());
+ else
+ sprintf(name, "Hangar");
+ return name;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Hangar::CreateSquadron(Text squadron, CombatGroup* group,
+ const ShipDesign* design, int count, int iff,
+ int* def_load, int maint_count, int dead_count)
+{
+ if (nsquadrons < MAX_SQUADRONS && count > 0) {
+ HangarSlot* s = new(__FILE__,__LINE__) HangarSlot[count];
+
+ for (int i = 0; i < count; i++) {
+ s[i].squadron = squadron;
+ s[i].group = group;
+ s[i].design = design;
+ s[i].iff = iff;
+
+ if (def_load)
+ CopyMemory(s[i].loadout, def_load, sizeof(s[i].loadout));
+ }
+
+ squadrons[nsquadrons] = s;
+ nslots[nsquadrons] = count;
+ names[nsquadrons] = squadron;
+
+ i = count-1;
+ while (dead_count-- > 0)
+ s[i--].state = UNAVAIL;
+
+ while (maint_count-- > 0) {
+ s[i].state = MAINT;
+ s[i--].time = 600 + rand() / 15;
+ }
+
+ nsquadrons++;
+ return true;
+ }
+
+ return false;
+}
+
+bool
+Hangar::GotoActiveFlight(int squadron, int slot_index, Element* elem, int* loadout)
+{
+ if (elem && squadron < nsquadrons && slot_index < nslots[squadron]) {
+ HangarSlot* slot = &(squadrons[squadron][slot_index]);
+
+ if (slot->state == STORAGE) {
+ slot->deck = 0;
+ slot->state = ACTIVE;
+ slot->time = 0;
+ slot->package = elem;
+ slot->alert_hold = false;
+
+ if (loadout)
+ CopyMemory(slot->loadout, loadout, sizeof(slot->loadout));
+
+ Sim* sim = Sim::GetSim();
+
+ Text ship_name = slot->squadron;
+
+ slot->ship = sim->CreateShip(ship_name, "",
+ (ShipDesign*) slot->design,
+ ship->GetRegion()->Name(),
+ ship->Location() + RandomPoint(),
+ slot->iff,
+ ship->GetCommandAILevel(),
+ slot->loadout);
+
+ if (slot->ship) {
+ Observe(slot->ship);
+
+ elem->SetCommander(ship->GetElement());
+ elem->AddShip(slot->ship);
+
+ if (slot->group) {
+ elem->SetCombatGroup(slot->group);
+ elem->SetCombatUnit(slot->group->GetNextUnit());
+ }
+
+ char name[64];
+ sprintf(name, "%s %d",
+ (const char*) elem->Name(),
+ slot->ship->GetElementIndex());
+
+ slot->ship->SetName(name);
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+Hangar::GotoAlert(int squadron, int slot, FlightDeck* d, Element* elem, int* loadout, bool pkg, bool expedite)
+{
+ if (squadron < nsquadrons && slot < nslots[squadron]) {
+ HangarSlot* s = &(squadrons[squadron][slot]);
+
+ if (s->state == STORAGE) {
+ s->deck = d;
+ s->state = PREP;
+ s->time = expedite ? 3 : s->design->prep_time;
+ s->package = elem;
+ s->alert_hold = !pkg;
+
+ if (loadout)
+ CopyMemory(s->loadout, loadout, sizeof(s->loadout));
+
+ if (expedite)
+ FinishPrep(s);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+Hangar::Launch(int squadron, int slot)
+{
+ if (squadron < nsquadrons && slot < nslots[squadron]) {
+ HangarSlot* s = &(squadrons[squadron][slot]);
+
+ if (s->state == ALERT && s->deck)
+ return s->deck->Launch(s->slot);
+ }
+
+ return false;
+}
+
+bool
+Hangar::StandDown(int squadron, int slot)
+{
+ if (squadron < nsquadrons && slot < nslots[squadron]) {
+ HangarSlot* s = &(squadrons[squadron][slot]);
+
+ Element* package = 0;
+ bool clear_slot = false;
+
+ if (s->state == ALERT && s->deck) {
+ if (s->deck->Clear(s->slot)) {
+ if (s->ship) {
+ Sim* sim = Sim::GetSim();
+
+ if (s->package) {
+ package = s->package;
+ package->DelShip(s->ship);
+ }
+
+ sim->DestroyShip(s->ship);
+ }
+
+ clear_slot = true;
+ }
+ }
+
+ else if (s->state == PREP) {
+ clear_slot = true;
+ package = s->package;
+ }
+
+ if (clear_slot) {
+ s->state = STORAGE;
+ s->deck = 0;
+ s->slot = 0;
+ s->ship = 0;
+ s->package = 0;
+
+ if (package) {
+ int npkg = 0;
+ for (int i = 0; i < nslots[squadron]; i++) {
+ if (squadrons[squadron][i].package == package)
+ npkg++;
+ }
+
+ if (npkg == 0) {
+ Sim::GetSim()->DestroyElement(package);
+ }
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Hangar::CanStow(Ship* incoming)
+{
+ int squadron = -1;
+ int slot = -1;
+
+ if (FindSlot(incoming, squadron, slot))
+ return true;
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Hangar::Stow(Ship* incoming)
+{
+ int squadron = -1;
+ int slot = -1;
+
+ if (FindSlot(incoming, squadron, slot)) {
+ HangarSlot* s = &(squadrons[squadron][slot]);
+ s->state = MAINT;
+ s->design = incoming->Design();
+ s->time = 2400;
+ s->package = 0; // XXX MEMORY LEAK?
+
+ // extra maintenance time?
+ if (incoming->Integrity() < incoming->Design()->integrity) {
+ double damage = 100 * ((double) incoming->Design()->integrity - (double) incoming->Integrity()) / (double) incoming->Design()->integrity;
+
+ if (damage < 10) s->time *= 1.2;
+ else if (damage < 25) s->time *= 2;
+ else if (damage < 50) s->time *= 4;
+ else s->time *= 10;
+ }
+
+ // quicker turnaround during network play:
+ Sim* sim = Sim::GetSim();
+ if (sim && sim->IsNetGame())
+ s->time /= 40;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool
+Hangar::FindSlot(Ship* test, int& squadron, int& slot, int desired_state)
+{
+ if (test) {
+ // if test is already inbound to this carrier,
+ // keep the inbound squadron and slot selections:
+ if (desired_state == UNAVAIL && test->GetInbound()) {
+ InboundSlot* inbound = test->GetInbound();
+ FlightDeck* deck = inbound->GetDeck();
+
+ if (deck && deck->GetCarrier() == ship && deck->IsPowerOn()) {
+ squadron = inbound->Squadron();
+ slot = inbound->Index();
+ return true;
+ }
+ }
+
+ int avail_squadron = -1;
+ int avail_slot = -1;
+
+ for (int i = 0; i < nsquadrons; i++) {
+ if (squadron < 0 || squadron == i) {
+ for (int j = 0; j < nslots[i]; j++) {
+ HangarSlot* s = &(squadrons[i][j]);
+ if (s->ship == test) {
+ squadron = i;
+ slot = j;
+ return true;
+ }
+
+ else if (avail_slot < 0 && s->ship == 0) {
+ if ((desired_state > STORAGE && s->state == STORAGE) ||
+ (desired_state < STORAGE && s->state == UNAVAIL)) {
+ avail_squadron = i;
+ avail_slot = j;
+ }
+ }
+ }
+ }
+ }
+
+ if (avail_squadron >= 0 && avail_slot >= 0) {
+ squadron = avail_squadron;
+ slot = avail_slot;
+
+ if (desired_state > STORAGE) {
+ HangarSlot* s = &(squadrons[squadron][slot]);
+
+ s->ship = test;
+ s->design = test->Design();
+ s->state = desired_state;
+ s->deck = 0;
+ s->slot = 0;
+ s->package = test->GetElement();
+ s->time = 0;
+
+ Observe(s->ship);
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+Hangar::FindSquadronAndSlot(Ship* test, int& squadron, int& slot)
+{
+ if (test) {
+ for (int i = 0; i < nsquadrons; i++) {
+ if (squadron < 0 || squadron == i) {
+ for (int j = 0; j < nslots[i]; j++) {
+ HangarSlot* s = &(squadrons[i][j]);
+ if (s->ship == test) {
+ squadron = i;
+ slot = j;
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+
+bool
+Hangar::FindAvailSlot(const ShipDesign* design, int& squadron, int& slot)
+{
+ if (design) {
+ for (int i = 0; i < nsquadrons; i++) {
+ if (nslots[i] > 0 && squadrons[i]->design == design) {
+ for (int j = 0; j < nslots[i]; j++) {
+ HangarSlot* s = &(squadrons[i][j]);
+
+ if (s->state == STORAGE) {
+ squadron = i;
+ slot = j;
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool
+Hangar::Ready(int squadron, int slot, FlightDeck* d)
+{
+ if (squadron < 0 || squadron >= nsquadrons || slot < 0 || slot >= nslots[squadron] || !d)
+ return false;
+
+ HangarSlot* s = &(squadrons[squadron][slot]);
+
+ s->time = 3; // 5;
+ s->deck = d;
+ s->slot = -1; // take first available slot
+
+ if (d->Spot(s->ship, s->slot)) {
+ s->state = ALERT;
+ s->alert_hold = false;
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+Hangar::SquadronName(int n) const
+{
+ if (n >= 0 && n < nsquadrons)
+ return names[n];
+
+ return Game::GetText("Unknown");
+}
+
+int
+Hangar::SquadronSize(int n) const
+{
+ if (n >= 0 && n < nsquadrons)
+ return nslots[n];
+
+ return 0;
+}
+
+int
+Hangar::SquadronIFF(int n) const
+{
+ if (n >= 0 && n < nsquadrons)
+ return squadrons[n]->iff;
+
+ return 0;
+}
+
+const ShipDesign*
+Hangar::SquadronDesign(int n) const
+{
+ if (n >= 0 && n < nsquadrons && nslots[n])
+ return squadrons[n]->design;
+
+ return 0;
+}
+
+const HangarSlot*
+Hangar::GetSlot(int i, int j) const
+{
+ if (i >= 0 && i < nsquadrons)
+ if (j >= 0 && j < nslots[i])
+ return squadrons[i] + j;
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+Ship*
+Hangar::GetShip(const HangarSlot* s) const
+{
+ if (s) return s->ship;
+ return 0;
+}
+
+const ShipDesign*
+Hangar::GetDesign(const HangarSlot* s) const
+{
+ if (s) return s->design;
+ return 0;
+}
+
+FlightDeck*
+Hangar::GetFlightDeck(const HangarSlot* s) const
+{
+ if (s) return s->deck;
+ return 0;
+}
+
+int
+Hangar::GetFlightDeckSlot(const HangarSlot* s) const
+{
+ if (s) return s->slot;
+ return 0;
+}
+
+int
+Hangar::GetState(const HangarSlot* s) const
+{
+ if (s) return s->state;
+ return 0;
+}
+
+double
+Hangar::TimeRemaining(const HangarSlot* s) const
+{
+ if (s) return s->time;
+ return 0;
+}
+
+Element*
+Hangar::GetPackageElement(const HangarSlot* s) const
+{
+ if (s) return s->package;
+ return 0;
+}
+
+const int*
+Hangar::GetLoadout(const HangarSlot* s) const
+{
+ if (s) return s->loadout;
+ return 0;
+}
+
+Text
+Hangar::StatusName(const HangarSlot* s) const
+{
+ switch (s->state) {
+ default:
+ case UNAVAIL: return Game::GetText("hangar.UNAVAIL");
+ case MAINT: return Game::GetText("hangar.MAINT");
+ case STORAGE: return Game::GetText("hangar.STORAGE");
+ case PREP: return Game::GetText("hangar.PREP");
+ case ALERT: return Game::GetText("hangar.ALERT");
+ case QUEUED: {
+ Text state = Game::GetText("hangar.QUEUED");
+ char seq[8];
+ sprintf(seq, " %d", s->deck->Sequence(s->slot));
+ return state + seq;
+ }
+ case LOCKED: return Game::GetText("hangar.LOCKED");
+ case LAUNCH: return Game::GetText("hangar.LAUNCH");
+ case ACTIVE: return Game::GetText("hangar.ACTIVE");
+ case APPROACH: return Game::GetText("hangar.APPROACH");
+ case RECOVERY: return Game::GetText("hangar.RECOVERY");
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Hangar::PreflightQueue(FlightDeck* d) const
+{
+ int result = 0;
+
+ for (int n = 0; n < nsquadrons; n++) {
+ if (squadrons[n] && nslots[n] > 0) {
+ for (int i = 0; i < nslots[n]; i++) {
+ HangarSlot* slot = &squadrons[n][i];
+
+ if (slot->deck == d)
+ result++;
+ }
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Hangar::NumShipsReady(int n) const
+{
+ int result = 0;
+
+ if (n >= 0 && n < nsquadrons && squadrons[n] && nslots[n] > 0) {
+ for (int i = 0; i < nslots[n]; i++) {
+ HangarSlot* slot = &squadrons[n][i];
+
+ if (slot->state == STORAGE)
+ result++;
+ }
+ }
+
+ return result;
+}
+
+int
+Hangar::NumShipsMaint(int n) const
+{
+ int result = 0;
+
+ if (n >= 0 && n < nsquadrons && squadrons[n] && nslots[n] > 0) {
+ for (int i = 0; i < nslots[n]; i++) {
+ HangarSlot* slot = &squadrons[n][i];
+
+ if (slot->state == MAINT)
+ result++;
+ }
+ }
+
+ return result;
+}
+
+int
+Hangar::NumShipsDead(int n) const
+{
+ int result = 0;
+
+ if (n >= 0 && n < nsquadrons && squadrons[n] && nslots[n] > 0) {
+ for (int i = 0; i < nslots[n]; i++) {
+ HangarSlot* slot = &squadrons[n][i];
+
+ if (slot->state == UNAVAIL)
+ result++;
+ }
+ }
+
+ return result;
+}
+
+int
+Hangar::NumSlotsEmpty() const
+{
+ int result = 0;
+
+ for (int n = 0; n < nsquadrons; n++) {
+ if (squadrons[n] && nslots[n] > 0) {
+ for (int i = 0; i < nslots[n]; i++) {
+ HangarSlot* slot = &squadrons[n][i];
+
+ if (slot->state == UNAVAIL)
+ result++;
+ }
+ }
+ }
+
+ return result;
+}
+
+int
+Hangar::GetActiveElements(List<Element>& active_list)
+{
+ active_list.clear();
+
+ for (int n = 0; n < nsquadrons; n++) {
+ if (squadrons[n] && nslots[n] > 0) {
+ for (int i = 0; i < nslots[n]; i++) {
+ HangarSlot* slot = &squadrons[n][i];
+
+ if (slot->package != 0 && !active_list.contains(slot->package))
+ active_list.append(slot->package);
+ }
+ }
+ }
+
+ return active_list.size();
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+Hangar::GetLastPatrolLaunch() const
+{
+ return last_patrol_launch;
+}
+
+void
+Hangar::SetLastPatrolLaunch(DWORD t)
+{
+ last_patrol_launch = t;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Hangar::SetAllIFF(int iff)
+{
+ for (int n = 0; n < nsquadrons; n++) {
+ if (squadrons[n] && nslots[n] > 0) {
+ for (int i = 0; i < nslots[n]; i++) {
+ HangarSlot* slot = &squadrons[n][i];
+
+ if (slot->ship)
+ slot->ship->SetIFF(iff);
+ }
+ }
+ }
+}
diff --git a/Stars45/Hangar.h b/Stars45/Hangar.h
new file mode 100644
index 0000000..aa52829
--- /dev/null
+++ b/Stars45/Hangar.h
@@ -0,0 +1,127 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Hangar.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Everything needed to store and maintain space craft
+
+ See Also: FlightDeck
+*/
+
+#ifndef Hangar_h
+#define Hangar_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "SimObject.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+class ShipDesign;
+class FlightDeck;
+class FlightDeckSlot;
+class Element;
+class HangarSlot;
+class InboundSlot;
+class CombatGroup;
+
+// +--------------------------------------------------------------------+
+
+class Hangar : public SimObserver
+{
+public:
+ Hangar();
+ Hangar(const Hangar& rhs);
+ virtual ~Hangar();
+
+ enum HANGAR_STATE { UNAVAIL = -2,
+ MAINT = -1,
+ STORAGE = 0,
+ PREP,
+ ALERT,
+ QUEUED,
+ LOCKED,
+ LAUNCH,
+ ACTIVE,
+ APPROACH,
+ RECOVERY
+ };
+
+ enum CONSTANTS { MAX_SQUADRONS=10 };
+
+ virtual void ExecFrame(double seconds);
+ void SetShip(Ship* s) { ship = s; }
+
+ virtual bool CreateSquadron(Text squadron, CombatGroup* g,
+ const ShipDesign* design, int count, int iff=-1,
+ int* def_load=0, int maint_count=0, int dead_count=0);
+
+ // used only by net client to expedite creation of already active ships:
+ virtual bool GotoActiveFlight(int squadron, int slot, Element* elem, int* loadout);
+
+ virtual bool GotoAlert(int squadron, int slot, FlightDeck* d, Element* elem=0, int* loadout=0, bool pkg=false, bool expedite=false);
+ virtual bool Ready(int squadron, int slot, FlightDeck* d);
+ virtual bool Launch(int squadron, int slot);
+ virtual bool StandDown(int squadron, int slot);
+ virtual bool CanStow(Ship* s);
+ virtual bool Stow(Ship* s);
+ virtual bool FindSlot(Ship* s, int& squadron, int& slot, int state=UNAVAIL);
+ virtual bool FindSquadronAndSlot(Ship* s, int& squadron, int& slot);
+ virtual bool FindAvailSlot(const ShipDesign* s, int& squadron, int& slot);
+ virtual bool FinishPrep(HangarSlot* slot);
+ virtual void SetAllIFF(int iff);
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+ // accessors:
+ int NumSquadrons() const { return nsquadrons; }
+ Text SquadronName(int n) const;
+ int SquadronSize(int n) const;
+ int SquadronIFF(int n) const;
+ const ShipDesign* SquadronDesign(int n) const;
+
+ int NumShipsReady(int squadron) const;
+ int NumShipsMaint(int squadron) const;
+ int NumShipsDead(int squadron) const;
+ int NumSlotsEmpty() const;
+
+ int GetActiveElements(List<Element>& active_list);
+
+ const HangarSlot* GetSlot(int squadron, int index) const;
+ Ship* GetShip(const HangarSlot* s) const;
+ const ShipDesign* GetDesign(const HangarSlot* s) const;
+ FlightDeck* GetFlightDeck(const HangarSlot* s) const;
+ int GetFlightDeckSlot(const HangarSlot* s) const;
+ int GetState(const HangarSlot* s) const;
+ double TimeRemaining(const HangarSlot* s) const;
+ Element* GetPackageElement(const HangarSlot* s) const;
+ const int* GetLoadout(const HangarSlot* s) const;
+ Text StatusName(const HangarSlot* s) const;
+
+ int PreflightQueue(FlightDeck* d) const;
+ DWORD GetLastPatrolLaunch() const;
+ void SetLastPatrolLaunch(DWORD t);
+
+protected:
+ Ship* ship;
+ int nsquadrons;
+ int nslots[MAX_SQUADRONS];
+ Text names[MAX_SQUADRONS];
+ HangarSlot* squadrons[MAX_SQUADRONS];
+ DWORD last_patrol_launch;
+};
+
+// +--------------------------------------------------------------------+
+
+
+#endif Hangar_h
+
diff --git a/Stars45/HardPoint.cpp b/Stars45/HardPoint.cpp
new file mode 100644
index 0000000..f9b83a3
--- /dev/null
+++ b/Stars45/HardPoint.cpp
@@ -0,0 +1,104 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: HardPoint.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ HardPoint class
+*/
+
+#include "MemDebug.h"
+#include "HardPoint.h"
+#include "Weapon.h"
+#include "WeaponDesign.h"
+#include "Shot.h"
+#include "Ship.h"
+#include "Sim.h"
+
+// +----------------------------------------------------------------------+
+
+HardPoint::HardPoint(Vec3 muzzle_loc, double az, double el)
+ : aim_azimuth((float) az), aim_elevation((float) el), muzzle(muzzle_loc)
+{
+ ZeroMemory(designs, sizeof(designs));
+}
+
+// +----------------------------------------------------------------------+
+
+HardPoint::HardPoint(const HardPoint& h)
+ : aim_azimuth(h.aim_azimuth), aim_elevation(h.aim_elevation), muzzle(h.muzzle),
+ mount_rel(h.mount_rel), radius(h.radius), hull_factor(h.hull_factor)
+{
+ CopyMemory(designs, h.designs, sizeof(designs));
+}
+
+// +--------------------------------------------------------------------+
+
+HardPoint::~HardPoint()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HardPoint::Mount(Point loc, float rad, float hull)
+{
+ mount_rel = loc;
+ radius = rad;
+ hull_factor = hull;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+HardPoint::AddDesign(WeaponDesign* d)
+{
+ for (int i = 0; i < MAX_DESIGNS; i++) {
+ if (!designs[i]) {
+ designs[i] = d;
+ return;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Weapon*
+HardPoint::CreateWeapon(int type_index)
+{
+ if (type_index >= 0 && type_index < MAX_DESIGNS && designs[type_index]) {
+ Vec3 zero_pt = Vec3(0.0f, 0.0f, 0.0f);
+ Vec3* muzzle_pt = &zero_pt;
+
+ if (designs[type_index]->turret.length() == 0)
+ muzzle_pt = &muzzle;
+
+ Weapon* missile = new(__FILE__,__LINE__) Weapon(designs[type_index],
+ 1,
+ muzzle_pt,
+ aim_azimuth,
+ aim_elevation);
+ missile->SetAbbreviation(GetAbbreviation());
+ missile->Mount(mount_rel, radius, hull_factor);
+ return missile;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+double
+HardPoint::GetCarryMass(int type_index)
+{
+ if (type_index >= 0 && type_index < MAX_DESIGNS && designs[type_index])
+ return designs[type_index]->carry_mass;
+
+ return 0;
+}
+
diff --git a/Stars45/HardPoint.h b/Stars45/HardPoint.h
new file mode 100644
index 0000000..ae6d74d
--- /dev/null
+++ b/Stars45/HardPoint.h
@@ -0,0 +1,82 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: HardPoint.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Hard Point (gun or missile launcher) class
+*/
+
+#ifndef HardPoint_h
+#define HardPoint_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Weapon;
+class WeaponDesign;
+
+// +--------------------------------------------------------------------+
+
+class HardPoint
+{
+public:
+ static const char* TYPENAME() { return "HardPoint"; }
+
+ enum CONSTANTS { MAX_DESIGNS=8 };
+
+ HardPoint(Vec3 muzzle, double az=0, double el=0);
+ HardPoint(const HardPoint& rhs);
+ virtual ~HardPoint();
+
+ int operator==(const HardPoint& w) const { return this == &w; }
+
+ virtual void AddDesign(WeaponDesign* dsn);
+ virtual Weapon* CreateWeapon(int type_index=0);
+ virtual double GetCarryMass(int type_index=0);
+ WeaponDesign* GetWeaponDesign(int n) { return designs[n]; }
+
+ virtual void Mount(Point loc, float rad, float hull=0.5f);
+ Point MountLocation() const { return mount_rel; }
+ double Radius() const { return radius; }
+ double HullProtection() const { return hull_factor; }
+
+ virtual const char* GetName() const { return name; }
+ virtual void SetName(const char* s) { name = s; }
+ virtual const char* GetAbbreviation() const { return abrv; }
+ virtual void SetAbbreviation(const char* s) { abrv = s; }
+ virtual const char* GetDesign() const { return sys_dsn; }
+ virtual void SetDesign(const char* s) { sys_dsn = s; }
+
+ virtual double GetAzimuth() const { return aim_azimuth; }
+ virtual void SetAzimuth(double a) { aim_azimuth = (float) a; }
+ virtual double GetElevation() const { return aim_elevation; }
+ virtual void SetElevation(double e) { aim_elevation = (float) e; }
+
+protected:
+ // Displayable name:
+ Text name;
+ Text abrv;
+ Text sys_dsn;
+
+ WeaponDesign* designs[MAX_DESIGNS];
+ Vec3 muzzle;
+ float aim_azimuth;
+ float aim_elevation;
+
+ // Mounting:
+ Point mount_rel; // object space
+ float radius;
+ float hull_factor;
+};
+
+#endif HardPoint_h
+
diff --git a/Stars45/Hoop.cpp b/Stars45/Hoop.cpp
new file mode 100644
index 0000000..176ba46
--- /dev/null
+++ b/Stars45/Hoop.cpp
@@ -0,0 +1,156 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Hoop.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ILS Hoop (HUD display) class
+*/
+
+#include "MemDebug.h"
+#include "Hoop.h"
+
+#include "Game.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Window.h"
+
+static Color ils_color;
+
+// +--------------------------------------------------------------------+
+
+Hoop::Hoop()
+ : width(360), height(180), mtl(0)
+{
+ foreground = 1;
+ radius = (float) width;
+
+ DataLoader* loader = DataLoader::GetLoader();
+
+ loader->SetDataPath("HUD/");
+ loader->LoadTexture("ILS.pcx", hoop_texture, Bitmap::BMP_TRANSLUCENT);
+ loader->SetDataPath(0);
+
+ CreatePolys();
+}
+
+Hoop::~Hoop()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void Hoop::SetColor(Color c)
+{
+ ils_color = c;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Hoop::CreatePolys()
+{
+ Material* mtl = new(__FILE__,__LINE__) Material;
+
+ mtl->tex_diffuse = hoop_texture;
+ mtl->blend = Material::MTL_ADDITIVE;
+
+ int w = width /2;
+ int h = height/2;
+
+ model = new(__FILE__,__LINE__) Model;
+ own_model = 1;
+
+ Surface* surface = new(__FILE__,__LINE__) Surface;
+
+ surface->SetName("hoop");
+ surface->CreateVerts(4);
+ surface->CreatePolys(2);
+
+ VertexSet* vset = surface->GetVertexSet();
+ Poly* polys = surface->GetPolys();
+
+ ZeroMemory(polys, sizeof(Poly) * 2);
+
+ for (int i = 0; i < 4; i++) {
+ int x = w;
+ int y = h;
+ float u = 0;
+ float v = 0;
+
+ if (i == 0 || i == 3)
+ x = -x;
+ else
+ u = 1;
+
+ if (i < 2)
+ y = -y;
+ else
+ v = 1;
+
+ vset->loc[i] = Vec3(x, y, 0);
+ vset->nrm[i] = Vec3(0, 0, 0);
+
+ vset->tu[i] = u;
+ vset->tv[i] = v;
+ }
+
+ for (i = 0; i < 2; i++) {
+ Poly& poly = polys[i];
+
+ poly.nverts = 4;
+ poly.vertex_set = vset;
+ poly.material = mtl;
+
+ poly.verts[0] = i ? 3 : 0;
+ poly.verts[1] = i ? 2 : 1;
+ poly.verts[2] = i ? 1 : 2;
+ poly.verts[3] = i ? 0 : 3;
+
+ poly.plane = Plane(vset->loc[poly.verts[0]],
+ vset->loc[poly.verts[2]],
+ vset->loc[poly.verts[1]]);
+
+ surface->AddIndices(6);
+ }
+
+ // then assign them to cohesive segments:
+ Segment* segment = new(__FILE__,__LINE__) Segment;
+ segment->npolys = 2;
+ segment->polys = &polys[0];
+ segment->material = segment->polys->material;
+
+ surface->GetSegments().append(segment);
+
+ model->AddSurface(surface);
+
+
+ SetLuminous(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Hoop::Update()
+{
+ if (mtl)
+ mtl->Ke = ils_color;
+
+ if (model && luminous) {
+ ListIter<Surface> s_iter = model->GetSurfaces();
+ while (++s_iter) {
+ Surface* surface = s_iter.value();
+ VertexSet* vset = surface->GetVertexSet();
+
+ for (int i = 0; i < vset->nverts; i++) {
+ vset->diffuse[i] = ils_color.Value();
+ }
+ }
+
+ InvalidateSurfaceData();
+ }
+}
diff --git a/Stars45/Hoop.h b/Stars45/Hoop.h
new file mode 100644
index 0000000..f71dec4
--- /dev/null
+++ b/Stars45/Hoop.h
@@ -0,0 +1,45 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Hoop.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ILS Hoop (HUD display) class
+*/
+
+#ifndef Hoop_h
+#define Hoop_h
+
+#include "Types.h"
+#include "Solid.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class Hoop : public Solid
+{
+public:
+ Hoop();
+ virtual ~Hoop();
+
+ virtual void Update();
+ static void SetColor(Color c);
+
+protected:
+ virtual void CreatePolys();
+
+ Bitmap* hoop_texture;
+ Material* mtl;
+ int width;
+ int height;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Hoop_h
+
diff --git a/Stars45/Instruction.cpp b/Stars45/Instruction.cpp
new file mode 100644
index 0000000..2cad9b3
--- /dev/null
+++ b/Stars45/Instruction.cpp
@@ -0,0 +1,569 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Instruction.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Navigation Point class implementation
+*/
+
+#include "MemDebug.h"
+#include "Instruction.h"
+#include "Element.h"
+#include "RadioMessage.h"
+#include "Ship.h"
+#include "Sim.h"
+
+#include "Game.h"
+#include "Text.h"
+
+// +----------------------------------------------------------------------+
+
+Instruction::Instruction(int act, const char* tgt)
+ : region(0), action(act), formation(0), tgt_name(tgt),
+ status(PENDING), speed(0), target(0), emcon(0), wep_free(0),
+ priority(PRIMARY), farcast(0), hold_time(0)
+{ }
+
+Instruction::Instruction(const char* rgn, Point loc, int act)
+ : region(0), action(act), formation(0),
+ status(PENDING), speed(0), target(0), emcon(0), wep_free(0),
+ priority(PRIMARY), farcast(0), hold_time(0)
+{
+ rgn_name = rgn;
+ rloc.SetBaseLocation(loc);
+ rloc.SetDistance(0);
+}
+
+Instruction::Instruction(SimRegion* rgn, Point loc, int act)
+ : region(rgn), action(act), formation(0),
+ status(PENDING), speed(0), target(0), emcon(0), wep_free(0),
+ priority(PRIMARY), farcast(0), hold_time(0)
+{
+ rgn_name = region->Name();
+ rloc.SetBaseLocation(loc);
+ rloc.SetDistance(0);
+}
+
+Instruction::Instruction(const Instruction& instr)
+ : region(instr.region), rgn_name(instr.rgn_name),
+ rloc(instr.rloc), action(instr.action),
+ formation(instr.formation), status(instr.status), speed(instr.speed),
+ target(0), tgt_name(instr.tgt_name), tgt_desc(instr.tgt_desc),
+ emcon(instr.emcon), wep_free(instr.wep_free),
+ priority(instr.priority), farcast(instr.farcast),
+ hold_time(instr.hold_time)
+{
+ SetTarget(instr.target);
+}
+
+Instruction::~Instruction()
+{ }
+
+// +--------------------------------------------------------------------+
+
+Instruction&
+Instruction::operator = (const Instruction& n)
+{
+ rgn_name = n.rgn_name;
+ region = n.region;
+ rloc = n.rloc;
+ action = n.action;
+ formation = n.formation;
+ status = n.status;
+ speed = n.speed;
+
+ tgt_name = n.tgt_name;
+ tgt_desc = n.tgt_desc;
+ target = 0;
+ emcon = n.emcon;
+ wep_free = n.wep_free;
+ priority = n.priority;
+ farcast = n.farcast;
+ hold_time = n.hold_time;
+
+ SetTarget(n.target);
+ return *this;
+}
+
+// +--------------------------------------------------------------------+
+
+Point
+Instruction::Location() const
+{
+ Instruction* pThis = (Instruction*) this;
+ return pThis->rloc.Location();
+}
+
+void
+Instruction::SetLocation(const Point& l)
+{
+ rloc.SetBaseLocation(l);
+ rloc.SetReferenceLoc(0);
+ rloc.SetDistance(0);
+}
+
+// +----------------------------------------------------------------------+
+
+SimObject*
+Instruction::GetTarget()
+{
+ if (!target && tgt_name.length() > 0) {
+ Sim* sim = Sim::GetSim();
+
+ if (sim) {
+ Ship* s = sim->FindShip(tgt_name, rgn_name);
+
+ if (s) {
+ target = s;
+ Observe(target);
+ }
+ }
+ }
+
+ return target;
+}
+
+void
+Instruction::SetTarget(const char* n)
+{
+ if (n && *n && tgt_name != n) {
+ tgt_name = n;
+ tgt_desc = n;
+
+ if (target)
+ target = 0;
+ }
+}
+
+void
+Instruction::SetTarget(SimObject* s)
+{
+ if (s && target != s) {
+ tgt_name = s->Name();
+ target = s;
+ Observe(target);
+ }
+}
+
+void
+Instruction::SetTargetDesc(const char* d)
+{
+ if (d && *d)
+ tgt_desc = d;
+}
+
+void
+Instruction::ClearTarget()
+{
+ if (target) {
+ target = 0;
+ tgt_name = "";
+ tgt_desc = "";
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+Instruction::Update(SimObject* obj)
+{
+ if (target == obj)
+ target = 0;
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+Instruction::GetObserverName() const
+{
+ return "Instruction";
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Instruction::Evaluate(Ship* ship)
+{
+ Sim* sim = Sim::GetSim();
+
+ switch (action) {
+ case VECTOR:
+ break;
+
+ case LAUNCH:
+ if (ship->GetFlightPhase() == Ship::ACTIVE)
+ SetStatus(COMPLETE);
+ break;
+
+ case DOCK:
+ case RTB:
+ if (sim->GetPlayerShip() == ship &&
+ (ship->GetFlightPhase() == Ship::DOCKING ||
+ ship->GetFlightPhase() == Ship::DOCKED))
+ SetStatus(COMPLETE);
+ else if (ship->Integrity() < 1)
+ SetStatus(FAILED);
+ break;
+
+ case DEFEND:
+ case ESCORT:
+ {
+ bool found = false;
+ bool safe = true;
+
+ ListIter<Element> iter = sim->GetElements();
+ while (++iter && !found) {
+ Element* e = iter.value();
+
+ if (e->IsFinished() || e->IsSquadron())
+ continue;
+
+ if (e->Name() == tgt_name ||
+ (e->GetCommander() && e->GetCommander()->Name() == tgt_name)) {
+
+ found = true;
+
+ for (int i = 0; i < e->NumShips(); i++) {
+ Ship* s = e->GetShip(i+1);
+
+ if (s && s->Integrity() < 1)
+ SetStatus(FAILED);
+ }
+
+ if (status == PENDING) {
+ // if the element had a flight plan, and all nav points
+ // have been addressed, then the element is safe
+ if (e->FlightPlanLength() > 0) {
+ if (e->GetNextNavPoint() == 0)
+ SetStatus(COMPLETE);
+ else
+ safe = false;
+ }
+ }
+ }
+ }
+
+ if (status == PENDING && safe &&
+ sim->GetPlayerShip() == ship &&
+ (ship->GetFlightPhase() == Ship::DOCKING ||
+ ship->GetFlightPhase() == Ship::DOCKED)) {
+ SetStatus(COMPLETE);
+ }
+ }
+ break;
+
+ case PATROL:
+ case SWEEP:
+ {
+ Sim* sim = Sim::GetSim();
+ bool alive = false;
+
+ ListIter<Element> iter = sim->GetElements();
+ while (++iter) {
+ Element* e = iter.value();
+
+ if (e->IsFinished() || e->IsSquadron())
+ continue;
+
+ if (e->GetIFF() && e->GetIFF() != ship->GetIFF()) {
+ for (int i = 0; i < e->NumShips(); i++) {
+ Ship* s = e->GetShip(i+1);
+
+ if (s && s->Integrity() >= 1)
+ alive = true;
+ }
+ }
+ }
+
+ if (status == PENDING && !alive) {
+ SetStatus(COMPLETE);
+ }
+ }
+ break;
+
+ case INTERCEPT:
+ case STRIKE:
+ case ASSAULT:
+ {
+ Sim* sim = Sim::GetSim();
+ bool alive = false;
+
+ ListIter<Element> iter = sim->GetElements();
+ while (++iter) {
+ Element* e = iter.value();
+
+ if (e->IsFinished() || e->IsSquadron())
+ continue;
+
+ if (e->Name() == tgt_name) {
+ for (int i = 0; i < e->NumShips(); i++) {
+ Ship* s = e->GetShip(i+1);
+
+ if (s && s->Integrity() >= 1)
+ alive = true;
+ }
+ }
+ }
+
+ if (status == PENDING && !alive) {
+ SetStatus(COMPLETE);
+ }
+ }
+ break;
+
+ case RECON:
+ break;
+
+ default:
+ break;
+ }
+}
+
+void
+Instruction::SetStatus(int s)
+{
+ status = s;
+}
+
+// +----------------------------------------------------------------------+
+
+const char*
+Instruction::GetShortDescription() const
+{
+ static char desc[256];
+
+ switch (action) {
+ case VECTOR:
+ if (farcast)
+ sprintf(desc, Game::GetText("instr.short.farcast").data(), rgn_name.data());
+ else
+ sprintf(desc, Game::GetText("instr.short.vector").data(), rgn_name.data());
+ break;
+
+ case LAUNCH:
+ sprintf(desc, Game::GetText("instr.short.launch").data(), tgt_name.data());
+ break;
+
+ case DOCK:
+ sprintf(desc, Game::GetText("instr.short.dock").data(), tgt_name.data());
+ break;
+
+ case RTB:
+ sprintf(desc, Game::GetText("instr.short.return-to-base").data());
+ break;
+
+ case DEFEND:
+ if (priority == PRIMARY) {
+ sprintf(desc, Game::GetText("instr.short.defend").data(), ActionName(action), tgt_desc.data());
+ }
+ else {
+ sprintf(desc, Game::GetText("instr.short.protect").data(), tgt_desc.data());
+ }
+ break;
+
+ case ESCORT:
+ if (priority == PRIMARY) {
+ sprintf(desc, Game::GetText("instr.short.escort").data(), ActionName(action), tgt_desc.data());
+ }
+ else {
+ sprintf(desc, Game::GetText("instr.short.protect").data(), tgt_desc.data());
+ }
+ break;
+
+ case PATROL:
+ sprintf(desc, Game::GetText("instr.short.patrol").data(),
+ tgt_desc.data(),
+ rgn_name.data());
+ break;
+
+ case SWEEP:
+ sprintf(desc, Game::GetText("instr.short.sweep").data(),
+ tgt_desc.data(),
+ rgn_name.data());
+ break;
+
+ case INTERCEPT:
+ sprintf(desc, Game::GetText("instr.short.intercept").data(), tgt_desc.data());
+ break;
+
+ case STRIKE:
+ sprintf(desc, Game::GetText("instr.short.strike").data(), tgt_desc.data());
+ break;
+
+ case ASSAULT:
+ sprintf(desc, Game::GetText("instr.short.assault").data(), tgt_desc.data());
+ break;
+
+ case RECON:
+ sprintf(desc, Game::GetText("instr.short.recon").data(), tgt_desc.data());
+ break;
+
+ default:
+ sprintf(desc, "%s", ActionName(action));
+ break;
+ }
+
+ if (status != PENDING) {
+ strcat(desc, " - ");
+ strcat(desc, Game::GetText(StatusName(status)));
+ }
+
+ return desc;
+}
+
+// +----------------------------------------------------------------------+
+
+const char*
+Instruction::GetDescription() const
+{
+ static char desc[1024];
+
+ switch (action) {
+ case VECTOR:
+ if (farcast)
+ sprintf(desc, Game::GetText("instr.long.farcast").data(), rgn_name.data());
+ else
+ sprintf(desc, Game::GetText("instr.long.vector").data(), rgn_name.data());
+ break;
+
+ case LAUNCH:
+ sprintf(desc, Game::GetText("instr.long.launch").data(), tgt_name.data());
+ break;
+
+ case DOCK:
+ sprintf(desc, Game::GetText("instr.long.dock").data(), tgt_name.data());
+ break;
+
+ case RTB:
+ sprintf(desc, Game::GetText("instr.long.return-to-base").data());
+ break;
+
+ case DEFEND:
+ if (priority == PRIMARY) {
+ sprintf(desc, Game::GetText("instr.long.defend").data(), ActionName(action), tgt_desc.data());
+ }
+ else {
+ sprintf(desc, Game::GetText("instr.long.protect").data(), tgt_desc.data());
+ }
+ break;
+
+ case ESCORT:
+ if (priority == PRIMARY) {
+ sprintf(desc, Game::GetText("instr.long.escort").data(), ActionName(action), tgt_desc.data());
+ }
+ else {
+ sprintf(desc, Game::GetText("instr.long.protect").data(), tgt_desc.data());
+ }
+ break;
+
+ case PATROL:
+ sprintf(desc, Game::GetText("instr.long.patrol").data(),
+ tgt_desc.data(),
+ rgn_name.data());
+ break;
+
+ case SWEEP:
+ sprintf(desc, Game::GetText("instr.long.sweep").data(),
+ tgt_desc.data(),
+ rgn_name.data());
+ break;
+
+ case INTERCEPT:
+ sprintf(desc, Game::GetText("instr.long.intercept").data(), tgt_desc.data());
+ break;
+
+ case STRIKE:
+ sprintf(desc, Game::GetText("instr.long.strike").data(), tgt_desc.data());
+ break;
+
+ case ASSAULT:
+ sprintf(desc, Game::GetText("instr.long.assault").data(), tgt_desc.data());
+ break;
+
+ case RECON:
+ sprintf(desc, Game::GetText("instr.long.recon").data(), tgt_desc.data());
+ break;
+
+ default:
+ sprintf(desc, "%s", ActionName(action));
+ break;
+ }
+
+ if (status != PENDING) {
+ strcat(desc, " - ");
+ strcat(desc, Game::GetText(StatusName(status)));
+ }
+
+ return desc;
+}
+
+// +----------------------------------------------------------------------+
+
+const char*
+Instruction::ActionName(int a)
+{
+ switch (a) {
+ case VECTOR: return "Vector";
+ case LAUNCH: return "Launch";
+ case DOCK: return "Dock";
+ case RTB: return "RTB";
+
+ case DEFEND: return "Defend";
+ case ESCORT: return "Escort";
+ case PATROL: return "Patrol";
+ case SWEEP: return "Sweep";
+ case INTERCEPT: return "Intercept";
+ case STRIKE: return "Strike";
+ case ASSAULT: return "Assault";
+ case RECON: return "Recon";
+
+ default: return "Unknown";
+ }
+}
+
+const char*
+Instruction::StatusName(int s)
+{
+ switch (s) {
+ case PENDING: return "Pending";
+ case ACTIVE: return "Active";
+ case SKIPPED: return "Skipped";
+ case ABORTED: return "Aborted";
+ case FAILED: return "Failed";
+ case COMPLETE: return "Complete";
+
+ default: return "Unknown";
+ }
+}
+
+const char*
+Instruction::FormationName(int f)
+{
+ switch (f) {
+ case DIAMOND: return "Diamond";
+ case SPREAD: return "Spread";
+ case BOX: return "Box";
+ case TRAIL: return "Trail";
+
+ default: return "Unknown";
+ }
+}
+
+const char*
+Instruction::PriorityName(int p)
+{
+ switch (p) {
+ case PRIMARY: return "Primary";
+ case SECONDARY: return "Secondary";
+ case BONUS: return "Bonus";
+
+ default: return "Unknown";
+ }
+}
+
diff --git a/Stars45/Instruction.h b/Stars45/Instruction.h
new file mode 100644
index 0000000..57f83a9
--- /dev/null
+++ b/Stars45/Instruction.h
@@ -0,0 +1,166 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Instruction.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Instruction (NavPoint / Order / Objective) class declaration
+*/
+
+#ifndef Instruction_h
+#define Instruction_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "SimObject.h"
+#include "Text.h"
+#include "RLoc.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+
+// +--------------------------------------------------------------------+
+
+class Instruction : public SimObserver
+{
+public:
+ static const char* TYPENAME() { return "Instruction"; }
+
+ enum ACTION
+ {
+ VECTOR,
+ LAUNCH,
+ DOCK,
+ RTB,
+
+ DEFEND,
+ ESCORT,
+ PATROL,
+ SWEEP,
+ INTERCEPT,
+ STRIKE, // ground attack
+ ASSAULT, // starship attack
+ RECON,
+
+ //RECALL,
+ //DEPLOY,
+
+ NUM_ACTIONS
+ };
+
+ enum STATUS
+ {
+ PENDING,
+ ACTIVE,
+ SKIPPED,
+ ABORTED,
+ FAILED,
+ COMPLETE,
+
+ NUM_STATUS
+ };
+
+ enum FORMATION
+ {
+ DIAMOND,
+ SPREAD,
+ BOX,
+ TRAIL,
+
+ NUM_FORMATIONS
+ };
+
+ enum PRIORITY
+ {
+ PRIMARY = 1,
+ SECONDARY,
+ BONUS
+ };
+
+ Instruction(int action, const char* tgt);
+ Instruction(const char* rgn, Point loc, int act=VECTOR);
+ Instruction(SimRegion* rgn, Point loc, int act=VECTOR);
+ Instruction(const Instruction& instr);
+ virtual ~Instruction();
+
+ Instruction& operator = (const Instruction& n);
+
+ // accessors:
+ static const char* ActionName(int a);
+ static const char* StatusName(int s);
+ static const char* FormationName(int f);
+ static const char* PriorityName(int p);
+
+ const char* RegionName() const { return rgn_name; }
+ SimRegion* Region() const { return region; }
+ Point Location() const;
+ RLoc& GetRLoc() { return rloc; }
+ int Action() const { return action; }
+ int Status() const { return status; }
+ int Formation() const { return formation; }
+ int Speed() const { return speed; }
+ int EMCON() const { return emcon; }
+ int WeaponsFree() const { return wep_free; }
+ int Priority() const { return priority; }
+ int Farcast() const { return farcast; }
+ double HoldTime() const { return hold_time; }
+
+ const char* TargetName() const { return tgt_name; }
+ const char* TargetDesc() const { return tgt_desc; }
+ SimObject* GetTarget();
+
+ void Evaluate(Ship* s);
+ const char* GetShortDescription() const;
+ const char* GetDescription() const;
+
+ // mutators:
+ void SetRegion(SimRegion* r) { region = r; }
+ void SetLocation(const Point& l);
+ void SetAction(int s) { action = s; }
+ void SetStatus(int s);
+ void SetFormation(int s) { formation = s; }
+ void SetSpeed(int s) { speed = s; }
+ void SetEMCON(int e) { emcon = e; }
+ void SetWeaponsFree(int f) { wep_free = f; }
+ void SetPriority(int p) { priority = p; }
+ void SetFarcast(int f) { farcast = f; }
+ void SetHoldTime(double t) { hold_time = t; }
+
+ void SetTarget(const char* n);
+ void SetTarget(SimObject* s);
+ void SetTargetDesc(const char* d);
+ void ClearTarget();
+
+ virtual bool Update(SimObject* s);
+ virtual const char* GetObserverName() const;
+
+protected:
+ Text rgn_name;
+ SimRegion* region;
+ RLoc rloc;
+ int action;
+ int formation;
+ int status;
+ int speed;
+
+ Text tgt_name;
+ Text tgt_desc;
+ SimObject* target;
+ int emcon;
+ int wep_free;
+ int priority;
+ int farcast;
+
+ double hold_time;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Instruction_h
+
diff --git a/Stars45/Intel.cpp b/Stars45/Intel.cpp
new file mode 100644
index 0000000..2ce0e63
--- /dev/null
+++ b/Stars45/Intel.cpp
@@ -0,0 +1,46 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Intel.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ An element in the dynamic campaign
+*/
+
+#include "MemDebug.h"
+#include "Intel.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+
+// +--------------------------------------------------------------------+
+
+static const char* intel_name[] = {
+ "",
+ "Reserve",
+ "Secret",
+ "Known",
+ "Located",
+ "Tracked",
+};
+
+const char*
+Intel::NameFromIntel(int intel)
+{
+ return intel_name[intel];
+}
+
+int
+Intel::IntelFromName(const char* type_name)
+{
+ for (int i = RESERVE; i <= TRACKED; i++)
+ if (!stricmp(type_name, intel_name[i]))
+ return i;
+
+ return 0;
+}
diff --git a/Stars45/Intel.h b/Stars45/Intel.h
new file mode 100644
index 0000000..9c1e0fb
--- /dev/null
+++ b/Stars45/Intel.h
@@ -0,0 +1,37 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Intel.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#ifndef Intel_h
+#define Intel_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class Intel
+{
+public:
+ enum INTEL_TYPE {
+ RESERVE = 1, // out-system reserve: this group is not even here
+ SECRET, // enemy is completely unaware of this group
+ KNOWN, // enemy knows this group is in the system
+ LOCATED, // enemy has located at least the lead ship
+ TRACKED // enemy is tracking all elements
+ };
+
+ static int IntelFromName(const char* name);
+ static const char* NameFromIntel(int intel);
+};
+
+#endif Intel_h
+
diff --git a/Stars45/JoyDlg.cpp b/Stars45/JoyDlg.cpp
new file mode 100644
index 0000000..6581c94
--- /dev/null
+++ b/Stars45/JoyDlg.cpp
@@ -0,0 +1,230 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: JoyDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "JoyDlg.h"
+#include "KeyMap.h"
+#include "MenuScreen.h"
+#include "Starshatter.h"
+#include "FormatUtil.h"
+
+#include "Game.h"
+#include "ListBox.h"
+#include "ComboBox.h"
+#include "Button.h"
+#include "Joystick.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(JoyDlg, OnApply);
+DEF_MAP_CLIENT(JoyDlg, OnCancel);
+DEF_MAP_CLIENT(JoyDlg, OnAxis);
+
+static const char* joy_axis_names[] = {
+ "JoyDlg.axis.0",
+ "JoyDlg.axis.1",
+ "JoyDlg.axis.2",
+ "JoyDlg.axis.3",
+ "JoyDlg.axis.4",
+ "JoyDlg.axis.5",
+ "JoyDlg.axis.6",
+ "JoyDlg.axis.7"
+};
+
+static int selected_axis = -1;
+static int sample_axis = -1;
+static int samples[8];
+
+static int map_axis[4];
+
+// +--------------------------------------------------------------------+
+
+JoyDlg::JoyDlg(Screen* s, FormDef& def, BaseScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ apply(0), cancel(0), message(0)
+{
+ Init(def);
+}
+
+JoyDlg::~JoyDlg()
+{
+}
+
+void
+JoyDlg::RegisterControls()
+{
+ if (apply)
+ return;
+
+ for (int i = 0; i < 4; i++) {
+ axis_button[i] = (Button*) FindControl(201 + i);
+ invert_checkbox[i] = (Button*) FindControl(301 + i);
+
+ if (axis_button[i])
+ REGISTER_CLIENT(EID_CLICK, axis_button[i], JoyDlg, OnAxis);
+ }
+
+ message = FindControl(11);
+
+ apply = (Button*) FindControl(1);
+ REGISTER_CLIENT(EID_CLICK, apply, JoyDlg, OnApply);
+
+ cancel = (Button*) FindControl(2);
+ REGISTER_CLIENT(EID_CLICK, cancel, JoyDlg, OnCancel);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+JoyDlg::ExecFrame()
+{
+ if (selected_axis >= 0 && selected_axis < 4) {
+ Joystick* joystick = Joystick::GetInstance();
+ if (joystick) {
+ joystick->Acquire();
+
+ int delta = 1000;
+
+ for (int i = 0; i < 8; i++) {
+ int a = Joystick::ReadRawAxis(i + KEY_JOY_AXIS_X);
+
+ int d = a - samples[i];
+ if (d < 0) d = -d;
+
+ if (d > delta && samples[i] < 1e6) {
+ delta = d;
+ sample_axis = i;
+ }
+
+ samples[i] = a;
+ }
+
+ Button* b = axis_button[selected_axis];
+
+ if (sample_axis >= 0) {
+ b->SetText(Game::GetText(joy_axis_names[sample_axis]));
+ map_axis[selected_axis] = sample_axis;
+ }
+
+ else
+ b->SetText(Game::GetText("JoyDlg.select"));
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+JoyDlg::Show()
+{
+ FormWindow::Show();
+
+ for (int i = 0; i < 4; i++) {
+ Button* b = axis_button[i];
+ if (b) {
+ int map = Joystick::GetAxisMap(i) - KEY_JOY_AXIS_X;
+ int inv = Joystick::GetAxisInv(i);
+
+ if (map >= 0 && map < 8) {
+ b->SetText(Game::GetText(joy_axis_names[map]));
+ map_axis[i] = map;
+ }
+ else {
+ b->SetText(Game::GetText("JoyDlg.unmapped"));
+ }
+
+ b->SetButtonState(0);
+
+ invert_checkbox[i]->SetButtonState(inv ? 1 : 0);
+ }
+ }
+
+ SetFocus();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+JoyDlg::OnAxis(AWEvent* event)
+{
+ for (int i = 0; i < 4; i++) {
+ int map = map_axis[i];
+ Text name = Game::GetText("JoyDlg.unmapped");
+ Button* b = axis_button[i];
+
+ if (map >= 0 && map < 8)
+ name = Game::GetText(joy_axis_names[map]);
+
+ if (b) {
+ if (b == event->window) {
+ if (selected_axis == i) {
+ b->SetText(name);
+ b->SetButtonState(0);
+ selected_axis = -1;
+ }
+ else {
+ b->SetText(Game::GetText("JoyDlg.select"));
+ b->SetButtonState(1);
+ selected_axis = i;
+ }
+ }
+ else {
+ b->SetText(name);
+ b->SetButtonState(0);
+ }
+ }
+ }
+
+ for (i = 0; i < 8; i++) {
+ samples[i] = 10000000;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+JoyDlg::OnApply(AWEvent* event)
+{
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars) {
+ KeyMap& keymap = stars->GetKeyMap();
+
+ keymap.Bind(KEY_AXIS_YAW, map_axis[0]+KEY_JOY_AXIS_X, 0);
+ keymap.Bind(KEY_AXIS_PITCH, map_axis[1]+KEY_JOY_AXIS_X, 0);
+ keymap.Bind(KEY_AXIS_ROLL, map_axis[2]+KEY_JOY_AXIS_X, 0);
+ keymap.Bind(KEY_AXIS_THROTTLE, map_axis[3]+KEY_JOY_AXIS_X, 0);
+
+ keymap.Bind(KEY_AXIS_YAW_INVERT, invert_checkbox[0]->GetButtonState(), 0);
+ keymap.Bind(KEY_AXIS_PITCH_INVERT, invert_checkbox[1]->GetButtonState(), 0);
+ keymap.Bind(KEY_AXIS_ROLL_INVERT, invert_checkbox[2]->GetButtonState(), 0);
+ keymap.Bind(KEY_AXIS_THROTTLE_INVERT, invert_checkbox[3]->GetButtonState(), 0);
+
+ keymap.SaveKeyMap("key.cfg", 256);
+
+ stars->MapKeys();
+ }
+
+ if (manager)
+ manager->ShowCtlDlg();
+}
+
+void
+JoyDlg::OnCancel(AWEvent* event)
+{
+ if (manager)
+ manager->ShowCtlDlg();
+}
+
+// +--------------------------------------------------------------------+
diff --git a/Stars45/JoyDlg.h b/Stars45/JoyDlg.h
new file mode 100644
index 0000000..658e87a
--- /dev/null
+++ b/Stars45/JoyDlg.h
@@ -0,0 +1,56 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: JoyDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Navigation Active Window class
+*/
+
+#ifndef JoyDlg_h
+#define JoyDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+
+// +--------------------------------------------------------------------+
+
+class BaseScreen;
+
+// +--------------------------------------------------------------------+
+
+class JoyDlg : public FormWindow
+{
+public:
+ JoyDlg(Screen* s, FormDef& def, BaseScreen* mgr);
+ virtual ~JoyDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+
+ // Operations:
+ virtual void ExecFrame();
+
+ virtual void OnApply(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+
+ virtual void OnAxis(AWEvent* event);
+
+protected:
+ BaseScreen* manager;
+
+ ActiveWindow* message;
+ Button* axis_button[4];
+ Button* invert_checkbox[4];
+
+ Button* apply;
+ Button* cancel;
+};
+
+#endif JoyDlg_h
+
diff --git a/Stars45/KeyDlg.cpp b/Stars45/KeyDlg.cpp
new file mode 100644
index 0000000..ce2c12c
--- /dev/null
+++ b/Stars45/KeyDlg.cpp
@@ -0,0 +1,199 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: KeyDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "KeyDlg.h"
+#include "KeyMap.h"
+#include "MenuScreen.h"
+#include "Starshatter.h"
+#include "FormatUtil.h"
+
+#include "Game.h"
+#include "ListBox.h"
+#include "ComboBox.h"
+#include "Button.h"
+#include "Joystick.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(KeyDlg, OnApply);
+DEF_MAP_CLIENT(KeyDlg, OnCancel);
+DEF_MAP_CLIENT(KeyDlg, OnClear);
+
+// +--------------------------------------------------------------------+
+
+KeyDlg::KeyDlg(Screen* s, FormDef& def, BaseScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ key_key(0), key_shift(0), key_joy(0), key_clear(0),
+ command(0), current_key(0), new_key(0), clear(0),
+ apply(0), cancel(0)
+{
+ Init(def);
+}
+
+KeyDlg::~KeyDlg()
+{
+}
+
+void
+KeyDlg::RegisterControls()
+{
+ if (apply)
+ return;
+
+ command = FindControl(201);
+ current_key = FindControl(202);
+ new_key = FindControl(203);
+
+ clear = (Button*) FindControl(300);
+ REGISTER_CLIENT(EID_CLICK, clear, KeyDlg, OnClear);
+
+ apply = (Button*) FindControl(1);
+ REGISTER_CLIENT(EID_CLICK, apply, KeyDlg, OnApply);
+
+ cancel = (Button*) FindControl(2);
+ REGISTER_CLIENT(EID_CLICK, cancel, KeyDlg, OnCancel);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+KeyDlg::ExecFrame()
+{
+ int key = 0;
+ int shift = 0;
+ int joy = 0;
+ Joystick* joystick = Joystick::GetInstance();
+
+ if (joystick) joystick->Acquire();
+
+ for (int i = 0; i < 256; i++) {
+ int vk = KeyMap::GetMappableVKey(i);
+
+ if (vk >= KEY_JOY_1 && vk <= KEY_JOY_16) {
+ if (joystick && joystick->KeyDown(vk))
+ joy = vk;
+ }
+
+ else if (vk >= KEY_POV_0_UP && vk <= KEY_POV_3_RIGHT) {
+ if (joystick && joystick->KeyDown(vk))
+ joy = vk;
+ }
+
+ else if (GetAsyncKeyState(vk)) {
+ if (vk == VK_SHIFT || vk == VK_MENU)
+ shift = vk;
+ else
+ key = vk;
+ }
+ }
+
+ if (key) {
+ key_key = key;
+ key_shift = shift;
+
+ new_key->SetText(KeyMap::DescribeKey(key, shift, joy));
+ }
+
+ else if (joy) {
+ key_joy = joy;
+ new_key->SetText(KeyMap::DescribeKey(key, shift, joy));
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+KeyDlg::Show()
+{
+ FormWindow::Show();
+
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars) {
+ KeyMap& keymap = stars->GetKeyMap();
+
+ if (command)
+ command->SetText(keymap.DescribeAction(key_index));
+
+ if (current_key)
+ current_key->SetText(keymap.DescribeKey(key_index));
+ }
+
+ key_clear = false;
+ new_key->SetText("");
+ SetFocus();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+KeyDlg::SetKeyMapIndex(int i)
+{
+ key_index = i;
+ key_key = 0;
+ key_shift = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+KeyDlg::OnClear(AWEvent* event)
+{
+ key_clear = true;
+
+ key_key = 0;
+ key_shift = 0;
+ key_joy = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+KeyDlg::OnApply(AWEvent* event)
+{
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars) {
+ KeyMap& keymap = stars->GetKeyMap();
+ KeyMapEntry* map = keymap.GetKeyMap(key_index);
+
+ if (key_clear) {
+ map->key = 0;
+ map->alt = 0;
+ map->joy = 0;
+ }
+
+ if (key_key) {
+ map->key = key_key;
+ map->alt = key_shift;
+ }
+
+ if (key_joy) {
+ map->joy = key_joy;
+ }
+ }
+
+ if (manager)
+ manager->ShowCtlDlg();
+}
+
+void
+KeyDlg::OnCancel(AWEvent* event)
+{
+ if (manager)
+ manager->ShowCtlDlg();
+}
+
+// +--------------------------------------------------------------------+
diff --git a/Stars45/KeyDlg.h b/Stars45/KeyDlg.h
new file mode 100644
index 0000000..79fb16b
--- /dev/null
+++ b/Stars45/KeyDlg.h
@@ -0,0 +1,65 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: KeyDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Navigation Active Window class
+*/
+
+#ifndef KeyDlg_h
+#define KeyDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+
+// +--------------------------------------------------------------------+
+
+class BaseScreen;
+
+// +--------------------------------------------------------------------+
+
+class KeyDlg : public FormWindow
+{
+public:
+ KeyDlg(Screen* s, FormDef& def, BaseScreen* mgr);
+ virtual ~KeyDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+
+ // Operations:
+ virtual void ExecFrame();
+
+ virtual void OnApply(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+ virtual void OnClear(AWEvent* event);
+
+ int GetKeyMapIndex() const { return key_index; }
+ void SetKeyMapIndex(int i);
+
+protected:
+ BaseScreen* manager;
+
+ int key_index;
+ int key_key;
+ int key_shift;
+ int key_joy;
+ int key_clear;
+
+ Button* clear;
+ Button* apply;
+ Button* cancel;
+
+ ActiveWindow* command;
+ ActiveWindow* current_key;
+ ActiveWindow* new_key;
+};
+
+#endif KeyDlg_h
+
diff --git a/Stars45/KeyMap.cpp b/Stars45/KeyMap.cpp
new file mode 100644
index 0000000..a70ad4a
--- /dev/null
+++ b/Stars45/KeyMap.cpp
@@ -0,0 +1,824 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: KeyMap.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Weapon class
+*/
+
+#include "MemDebug.h"
+#include "Keyboard.h"
+#include "KeyMap.h"
+#include "Game.h"
+#include "Ship.h"
+
+// +----------------------------------------------------------------------+
+
+struct KeyName
+{
+ int category;
+ int key;
+ const char* name;
+ const char* desc;
+};
+
+static KeyName key_action_table[] = {
+ { KeyMap::KEY_FLIGHT, KEY_PLUS_X, "KEY_PLUS_X", "Translate Right" },
+ { KeyMap::KEY_FLIGHT, KEY_MINUS_X, "KEY_MINUS_X", "Translate Left" },
+ { KeyMap::KEY_FLIGHT, KEY_PLUS_Y, "KEY_PLUS_Y", "Translate Forward" },
+ { KeyMap::KEY_FLIGHT, KEY_MINUS_Y, "KEY_MINUS_Y", "Translate Aft" },
+ { KeyMap::KEY_FLIGHT, KEY_PLUS_Z, "KEY_PLUS_Z", "Translate Up" },
+ { KeyMap::KEY_FLIGHT, KEY_MINUS_Z, "KEY_MINUS_Z", "Translate Down" },
+
+ { KeyMap::KEY_FLIGHT, KEY_PITCH_UP, "KEY_PITCH_UP", "Pitch Up" },
+ { KeyMap::KEY_FLIGHT, KEY_PITCH_DOWN, "KEY_PITCH_DOWN", "Pitch Down" },
+ { KeyMap::KEY_FLIGHT, KEY_YAW_LEFT, "KEY_YAW_LEFT", "Yaw Left" },
+ { KeyMap::KEY_FLIGHT, KEY_YAW_RIGHT, "KEY_YAW_RIGHT", "Yaw Right" },
+ { KeyMap::KEY_FLIGHT, KEY_ROLL_LEFT, "KEY_ROLL_LEFT", "Roll Left" },
+ { KeyMap::KEY_FLIGHT, KEY_ROLL_RIGHT, "KEY_ROLL_RIGHT", "Roll Right" },
+ { KeyMap::KEY_FLIGHT, KEY_CENTER, "KEY_CENTER", "Center" },
+ { KeyMap::KEY_FLIGHT, KEY_ROLL_ENABLE, "KEY_ROLL_ENABLE", "Roll Enable" },
+ { KeyMap::KEY_FLIGHT, KEY_SWAP_ROLL_YAW, "KEY_SWAP_ROLL_YAW", "Swap Ctrl Axes" },
+
+ { KeyMap::KEY_WEAPONS, KEY_ACTION_0, "KEY_ACTION_0", "Fire Primary" },
+ { KeyMap::KEY_WEAPONS, KEY_ACTION_1, "KEY_ACTION_1", "Fire Secondary" },
+ { KeyMap::KEY_WEAPONS, KEY_ACTION_2, "KEY_ACTION_2", "Action 2" },
+ { KeyMap::KEY_WEAPONS, KEY_ACTION_3, "KEY_ACTION_3", "Action 3" },
+
+ { KeyMap::KEY_FLIGHT, KEY_CONTROL_MODEL, "KEY_CONTROL_MODEL" },
+
+ { KeyMap::KEY_FLIGHT, KEY_MOUSE_SELECT, "KEY_MOUSE_SELECT" },
+ { KeyMap::KEY_FLIGHT, KEY_MOUSE_SENSE, "KEY_MOUSE_SENSE" },
+ { KeyMap::KEY_FLIGHT, KEY_MOUSE_SWAP, "KEY_MOUSE_SWAP" },
+ { KeyMap::KEY_FLIGHT, KEY_MOUSE_INVERT, "KEY_MOUSE_INVERT" },
+ { KeyMap::KEY_FLIGHT, KEY_MOUSE_ACTIVE, "KEY_MOUSE_ACTIVE", "Toggle Mouse Ctrl" },
+
+ { KeyMap::KEY_FLIGHT, KEY_JOY_SELECT, "KEY_JOY_SELECT" },
+ { KeyMap::KEY_FLIGHT, KEY_JOY_RUDDER, "KEY_JOY_RUDDER" },
+ { KeyMap::KEY_FLIGHT, KEY_JOY_THROTTLE, "KEY_JOY_THROTTLE" },
+ { KeyMap::KEY_FLIGHT, KEY_JOY_SENSE, "KEY_JOY_SENSE" },
+ { KeyMap::KEY_FLIGHT, KEY_JOY_DEAD_ZONE, "KEY_JOY_DEAD_ZONE" },
+ { KeyMap::KEY_FLIGHT, KEY_JOY_SWAP, "KEY_JOY_SWAP" },
+
+ { KeyMap::KEY_FLIGHT, KEY_AXIS_YAW, "KEY_AXIS_YAW" },
+ { KeyMap::KEY_FLIGHT, KEY_AXIS_PITCH, "KEY_AXIS_PITCH" },
+ { KeyMap::KEY_FLIGHT, KEY_AXIS_ROLL, "KEY_AXIS_ROLL" },
+ { KeyMap::KEY_FLIGHT, KEY_AXIS_THROTTLE, "KEY_AXIS_THROTTLE" },
+ { KeyMap::KEY_FLIGHT, KEY_AXIS_YAW_INVERT, "KEY_AXIS_YAW_INVERT" },
+ { KeyMap::KEY_FLIGHT, KEY_AXIS_PITCH_INVERT, "KEY_AXIS_PITCH_INVERT" },
+ { KeyMap::KEY_FLIGHT, KEY_AXIS_ROLL_INVERT, "KEY_AXIS_ROLL_INVERT" },
+ { KeyMap::KEY_FLIGHT, KEY_AXIS_THROTTLE_INVERT, "KEY_AXIS_THROTTLE_INVERT" },
+
+ { KeyMap::KEY_MISC, KEY_EXIT, "KEY_EXIT", "Exit" },
+ { KeyMap::KEY_MISC, KEY_PAUSE, "KEY_PAUSE", "Pause" },
+// { KeyMap::KEY_VIEW, KEY_NEXT_VIEW, "KEY_NEXT_VIEW", "Next View" },
+ { KeyMap::KEY_VIEW, KEY_TARGET_PADLOCK, "KEY_TARGET_PADLOCK","Padlock Target" },
+ { KeyMap::KEY_VIEW, KEY_THREAT_PADLOCK, "KEY_THREAT_PADLOCK","Padlock Threat" },
+ { KeyMap::KEY_WEAPONS, KEY_LOCK_TARGET, "KEY_LOCK_TARGET", "Lock Target" },
+ { KeyMap::KEY_WEAPONS, KEY_LOCK_THREAT, "KEY_LOCK_THREAT", "Lock Threat" },
+ { KeyMap::KEY_WEAPONS, KEY_LOCK_CLOSEST_SHIP, "KEY_LOCK_CLOSEST_SHIP", "Lock Closest Ship" },
+ { KeyMap::KEY_WEAPONS, KEY_LOCK_CLOSEST_THREAT, "KEY_LOCK_CLOSEST_THREAT", "Lock Closest Threat" },
+ { KeyMap::KEY_WEAPONS, KEY_LOCK_HOSTILE_SHIP, "KEY_LOCK_HOSTILE_SHIP", "Lock Hostile Ship" },
+ { KeyMap::KEY_WEAPONS, KEY_LOCK_HOSTILE_THREAT, "KEY_LOCK_HOSTILE_THREAT", "Lock Hostile Threat" },
+ { KeyMap::KEY_WEAPONS, KEY_CYCLE_SUBTARGET, "KEY_CYCLE_SUBTARGET", "Target Subsystem" },
+ { KeyMap::KEY_WEAPONS, KEY_PREV_SUBTARGET, "KEY_PREV_SUBTARGET", "Previous Subsystem" },
+ { KeyMap::KEY_FLIGHT, KEY_AUTO_NAV, "KEY_AUTO_NAV", "Autonav" },
+ { KeyMap::KEY_MISC, KEY_TIME_COMPRESS, "KEY_TIME_COMPRESS", "Compress Time" },
+ { KeyMap::KEY_MISC, KEY_TIME_EXPAND, "KEY_TIME_EXPAND", "Expand Time" },
+ { KeyMap::KEY_MISC, KEY_TIME_SKIP, "KEY_TIME_SKIP", "Skip to Next Event" },
+
+ { KeyMap::KEY_FLIGHT, KEY_THROTTLE_UP, "KEY_THROTTLE_UP", "Throttle Up" },
+ { KeyMap::KEY_FLIGHT, KEY_THROTTLE_DOWN, "KEY_THROTTLE_DOWN", "Throttle Down" },
+ { KeyMap::KEY_FLIGHT, KEY_THROTTLE_ZERO, "KEY_THROTTLE_ZERO", "All Stop" },
+ { KeyMap::KEY_FLIGHT, KEY_THROTTLE_FULL, "KEY_THROTTLE_FULL", "Full Throttle" },
+ { KeyMap::KEY_FLIGHT, KEY_AUGMENTER, "KEY_AUGMENTER", "Augmenter" },
+ { KeyMap::KEY_FLIGHT, KEY_FLCS_MODE_AUTO, "KEY_FLCS_MODE_AUTO","FLCS Mode Toggle" },
+ { KeyMap::KEY_FLIGHT, KEY_COMMAND_MODE, "KEY_COMMAND_MODE", "CMD Mode Toggle" },
+ { KeyMap::KEY_FLIGHT, KEY_DROP_ORBIT, "KEY_DROP_ORBIT", "Break Orbit" },
+ { KeyMap::KEY_FLIGHT, KEY_GEAR_TOGGLE, "KEY_GEAR_TOGGLE", "Landing Gear" },
+ { KeyMap::KEY_FLIGHT, KEY_NAVLIGHT_TOGGLE, "KEY_NAVLIGHT_TOGGLE","Nav Lights" },
+
+ { KeyMap::KEY_WEAPONS, KEY_CYCLE_PRIMARY, "KEY_CYCLE_PRIMARY", "Cycle Primary" },
+ { KeyMap::KEY_WEAPONS, KEY_CYCLE_SECONDARY, "KEY_CYCLE_SECONDARY", "Cycle Secondary" },
+
+ { KeyMap::KEY_VIEW, KEY_HUD_INST, "KEY_HUD_INST", "Instr. Display" },
+ { KeyMap::KEY_VIEW, KEY_CAM_BRIDGE, "KEY_CAM_BRIDGE", "Bridge Cam" },
+ { KeyMap::KEY_VIEW, KEY_CAM_VIRT, "KEY_CAM_VIRT", "Virtual Cockpit" },
+ { KeyMap::KEY_VIEW, KEY_CAM_CHASE, "KEY_CAM_CHASE", "Chase Cam" },
+ { KeyMap::KEY_VIEW, KEY_CAM_DROP, "KEY_CAM_DROP", "Drop Cam" },
+ { KeyMap::KEY_VIEW, KEY_CAM_EXTERN, "KEY_CAM_EXTERN", "Orbit Cam" },
+ { KeyMap::KEY_MISC, KEY_HUD_MODE, "KEY_HUD_MODE", "HUD Mode" },
+ { KeyMap::KEY_MISC, KEY_HUD_COLOR, "KEY_HUD_COLOR", "HUD Color" },
+ { KeyMap::KEY_MISC, KEY_HUD_WARN, "KEY_HUD_WARN", "Master Caution" },
+ { KeyMap::KEY_MISC, KEY_NAV_DLG, "KEY_NAV_DLG", "NAV Window" },
+ { KeyMap::KEY_MISC, KEY_WEP_DLG, "KEY_WEP_DLG", "TAC Overlay" },
+ { KeyMap::KEY_MISC, KEY_FLT_DLG, "KEY_FLT_DLG", "FLT Window" },
+ { KeyMap::KEY_MISC, KEY_ENG_DLG, "KEY_ENG_DLG", "ENG Window" },
+
+ { KeyMap::KEY_VIEW, KEY_ZOOM_WIDE, "KEY_ZOOM_WIDE", "Toggle Wide Angle" },
+ { KeyMap::KEY_VIEW, KEY_ZOOM_IN, "KEY_ZOOM_IN", "Zoom In" },
+ { KeyMap::KEY_VIEW, KEY_ZOOM_OUT, "KEY_ZOOM_OUT", "Zoom Out" },
+
+ { KeyMap::KEY_VIEW, KEY_CAM_VIRT_PLUS_AZ, "KEY_CAM_VIRT_PLUS_AZ", "Look Left" },
+ { KeyMap::KEY_VIEW, KEY_CAM_VIRT_MINUS_AZ, "KEY_CAM_VIRT_MINUS_AZ", "Look Right" },
+ { KeyMap::KEY_VIEW, KEY_CAM_VIRT_PLUS_EL, "KEY_CAM_VIRT_PLUS_EL", "Look Up" },
+ { KeyMap::KEY_VIEW, KEY_CAM_VIRT_MINUS_EL, "KEY_CAM_VIRT_MINUS_EL", "Look Down" },
+ { KeyMap::KEY_VIEW, KEY_CAM_CYCLE_OBJECT, "KEY_CAM_CYCLE_OBJECT", "View Next Object" },
+ { KeyMap::KEY_VIEW, KEY_CAM_EXT_PLUS_AZ, "KEY_CAM_EXT_PLUS_AZ", "View Spin Left" },
+ { KeyMap::KEY_VIEW, KEY_CAM_EXT_MINUS_AZ, "KEY_CAM_EXT_MINUS_AZ", "View Spin Right" },
+ { KeyMap::KEY_VIEW, KEY_CAM_EXT_PLUS_EL, "KEY_CAM_EXT_PLUS_EL", "View Raise" },
+ { KeyMap::KEY_VIEW, KEY_CAM_EXT_MINUS_EL, "KEY_CAM_EXT_MINUS_EL", "View Lower" },
+ { KeyMap::KEY_VIEW, KEY_CAM_EXT_PLUS_RANGE, "KEY_CAM_EXT_PLUS_RANGE", "View Farther" },
+ { KeyMap::KEY_VIEW, KEY_CAM_EXT_MINUS_RANGE,"KEY_CAM_EXT_MINUS_RANGE", "View Closer" },
+ { KeyMap::KEY_VIEW, KEY_CAM_VIEW_SELECTION, "KEY_CAM_VIEW_SELECTION", "View Selection" },
+
+ { KeyMap::KEY_WEAPONS, KEY_TARGET_SELECTION, "KEY_TARGET_SELECTION", "Target Selection" },
+ { KeyMap::KEY_MISC, KEY_RADIO_MENU, "KEY_RADIO_MENU", "Radio Call" },
+ { KeyMap::KEY_MISC, KEY_QUANTUM_MENU, "KEY_QUANTUM_MENU", "Quantum Drive" },
+
+ { KeyMap::KEY_MISC, KEY_MFD1, "KEY_MFD1", "MFD 1" },
+ { KeyMap::KEY_MISC, KEY_MFD2, "KEY_MFD2", "MFD 2" },
+ { KeyMap::KEY_MISC, KEY_MFD3, "KEY_MFD3", "MFD 3" },
+ { KeyMap::KEY_MISC, KEY_MFD4, "KEY_MFD4", "MFD 4" },
+ { KeyMap::KEY_MISC, KEY_SELF_DESTRUCT, "KEY_SELF_DESTRUCT", "Self Destruct" },
+
+ { KeyMap::KEY_MISC, KEY_COMM_ATTACK_TGT, "KEY_COMM_ATTACK_TGT", "'Attack Tgt'" },
+ { KeyMap::KEY_MISC, KEY_COMM_ESCORT_TGT, "KEY_COMM_ESCORT_TGT", "'Escort Tgt'" },
+ { KeyMap::KEY_MISC, KEY_COMM_WEP_FREE, "KEY_COMM_WEP_FREE", "'Break & Attack'" },
+ { KeyMap::KEY_MISC, KEY_COMM_WEP_HOLD, "KEY_COMM_WEP_HOLD", "'Form Up'" },
+ { KeyMap::KEY_MISC, KEY_COMM_COVER_ME, "KEY_COMM_COVER_ME", "'Help Me Out!'" },
+ { KeyMap::KEY_MISC, KEY_COMM_SKIP_NAV, "KEY_COMM_SKIP_NAV", "'Skip Navpoint'" },
+ { KeyMap::KEY_MISC, KEY_COMM_RETURN_TO_BASE,"KEY_COMM_RETURN_TO_BASE", "'Return to Base'" },
+ { KeyMap::KEY_MISC, KEY_COMM_CALL_INBOUND, "KEY_COMM_CALL_INBOUND", "'Call Inbound'" },
+ { KeyMap::KEY_MISC, KEY_COMM_REQUEST_PICTURE, "KEY_COMM_REQUEST_PICTURE", "'Request Picture'" },
+ { KeyMap::KEY_MISC, KEY_COMM_REQUEST_SUPPORT, "KEY_COMM_REQUEST_SUPPORT", "'Request Support'" },
+ { KeyMap::KEY_MISC, KEY_CHAT_BROADCAST, "KEY_CHAT_BROADCAST", "Chat Broadcast" },
+ { KeyMap::KEY_MISC, KEY_CHAT_TEAM, "KEY_CHAT_TEAM", "Chat Team" },
+ { KeyMap::KEY_MISC, KEY_CHAT_WING, "KEY_CHAT_WING", "Chat Wingman" },
+ { KeyMap::KEY_MISC, KEY_CHAT_UNIT, "KEY_CHAT_UNIT", "Chat Unit" },
+
+ { KeyMap::KEY_WEAPONS, KEY_SHIELDS_UP, "KEY_SHIELDS_UP", "Raise Shields" },
+ { KeyMap::KEY_WEAPONS, KEY_SHIELDS_DOWN, "KEY_SHIELDS_DOWN", "Lower Shields" },
+ { KeyMap::KEY_WEAPONS, KEY_SHIELDS_FULL, "KEY_SHIELDS_FULL", "Full Shields" },
+ { KeyMap::KEY_WEAPONS, KEY_SHIELDS_ZERO, "KEY_SHIELDS_ZERO", "Zero Shields" },
+
+ { KeyMap::KEY_WEAPONS, KEY_SENSOR_MODE, "KEY_SENSOR_MODE", "Sensor Mode" },
+ { KeyMap::KEY_WEAPONS, KEY_SENSOR_GROUND_MODE, "KEY_SENSOR_GROUND_MODE", "Sensor AGM" },
+ { KeyMap::KEY_WEAPONS, KEY_SENSOR_BEAM, "KEY_SENSOR_BEAM", "Sensor Sweep Angle" },
+ { KeyMap::KEY_WEAPONS, KEY_SENSOR_RANGE_PLUS, "KEY_SENSOR_RANGE_PLUS", "Inc Sensor Range" },
+ { KeyMap::KEY_WEAPONS, KEY_SENSOR_RANGE_MINUS, "KEY_SENSOR_RANGE_MINUS", "Dec Sensor Range" },
+ { KeyMap::KEY_WEAPONS, KEY_EMCON_PLUS, "KEY_EMCON_PLUS", "Inc EMCON Level" },
+ { KeyMap::KEY_WEAPONS, KEY_EMCON_MINUS, "KEY_EMCON_MINUS", "Dec EMCON Level" },
+
+ { KeyMap::KEY_WEAPONS, KEY_DECOY, "KEY_DECOY", "Launch Decoy" },
+ { KeyMap::KEY_WEAPONS, KEY_LAUNCH_PROBE, "KEY_LAUNCH_PROBE", "Launch Probe" },
+
+};
+
+static KeyName key_key_table[] = {
+ { 0, VK_ESCAPE, "VK_ESCAPE", "Escape" },
+ { 0, VK_UP, "VK_UP", "Up" },
+ { 0, VK_DOWN, "VK_DOWN", "Down" },
+ { 0, VK_LEFT, "VK_LEFT", "Left" },
+ { 0, VK_RIGHT, "VK_RIGHT", "Right" },
+ { 0, VK_NEXT, "VK_NEXT", "Pg Down" },
+ { 0, VK_PRIOR, "VK_PRIOR", "Pg Up" },
+ { 0, VK_ADD, "VK_ADD", "+" },
+ { 0, VK_SUBTRACT, "VK_SUBTRACT", "-" },
+ { 0, VK_MULTIPLY, "VK_MULTIPLY", "*" },
+ { 0, VK_DIVIDE, "VK_DIVIDE", "/" },
+ { 0, VK_SPACE, "VK_SPACE", "Space" },
+ { 0, VK_TAB, "VK_TAB", "Tab" },
+ { 0, VK_RETURN, "VK_RETURN", "Enter" },
+ { 0, VK_HOME, "VK_HOME", "Home" },
+ { 0, VK_END, "VK_END", "End" },
+ { 0, VK_SHIFT, "VK_SHIFT", "Shift" },
+ { 0, VK_CONTROL, "VK_CONTROL", "Ctrl" },
+ { 0, VK_MENU, "VK_MENU", "Alt" },
+
+ { 0, VK_DECIMAL, "VK_DECIMAL", "." },
+ { 0, VK_SEPARATOR, "VK_SEPARATOR", "Separator" },
+ { 0, VK_PAUSE, "VK_PAUSE", "Pause" },
+ { 0, VK_BACK, "VK_BACK", "Backspace" },
+ { 0, VK_INSERT, "VK_INSERT", "Insert" },
+ { 0, VK_DELETE, "VK_DELETE", "Delete" },
+ { 0, 20, "CAP", "CapsLock" },
+ { 0, 144, "NUM", "NumLock" },
+ { 0, 145, "SCROLL", "Scroll" },
+ { 0, 44, "PRINT", "PrintScr" },
+
+ { 0, VK_NUMPAD0, "VK_NUMPAD0", "Num 0" },
+ { 0, VK_NUMPAD1, "VK_NUMPAD1", "Num 1" },
+ { 0, VK_NUMPAD2, "VK_NUMPAD2", "Num 2" },
+ { 0, VK_NUMPAD3, "VK_NUMPAD3", "Num 3" },
+ { 0, VK_NUMPAD4, "VK_NUMPAD4", "Num 4" },
+ { 0, VK_NUMPAD5, "VK_NUMPAD5", "Num 5" },
+ { 0, VK_NUMPAD6, "VK_NUMPAD6", "Num 6" },
+ { 0, VK_NUMPAD7, "VK_NUMPAD7", "Num 7" },
+ { 0, VK_NUMPAD8, "VK_NUMPAD8", "Num 8" },
+ { 0, VK_NUMPAD9, "VK_NUMPAD9", "Num 9" },
+
+ { 0, VK_F1, "VK_F1", "F1" },
+ { 0, VK_F2, "VK_F2", "F2" },
+ { 0, VK_F3, "VK_F3", "F3" },
+ { 0, VK_F4, "VK_F4", "F4" },
+ { 0, VK_F5, "VK_F5", "F5" },
+ { 0, VK_F6, "VK_F6", "F6" },
+ { 0, VK_F7, "VK_F7", "F7" },
+ { 0, VK_F8, "VK_F8", "F8" },
+ { 0, VK_F9, "VK_F9", "F9" },
+ { 0, VK_F10, "VK_F10", "F10" },
+ { 0, VK_F11, "VK_F11", "F11" },
+ { 0, VK_F12, "VK_F12", "F12" },
+
+ { 0, KEY_JOY_1, "KEY_JOY_1", "Joy 1" },
+ { 0, KEY_JOY_2, "KEY_JOY_2", "Joy 2" },
+ { 0, KEY_JOY_3, "KEY_JOY_3", "Joy 3" },
+ { 0, KEY_JOY_4, "KEY_JOY_4", "Joy 4" },
+ { 0, KEY_JOY_5, "KEY_JOY_5", "Joy 5" },
+ { 0, KEY_JOY_6, "KEY_JOY_6", "Joy 6" },
+ { 0, KEY_JOY_7, "KEY_JOY_7", "Joy 7" },
+ { 0, KEY_JOY_8, "KEY_JOY_8", "Joy 8" },
+ { 0, KEY_JOY_9, "KEY_JOY_9", "Joy 9" },
+ { 0, KEY_JOY_10, "KEY_JOY_10", "Joy 10" },
+ { 0, KEY_JOY_11, "KEY_JOY_11", "Joy 11" },
+ { 0, KEY_JOY_12, "KEY_JOY_12", "Joy 12" },
+ { 0, KEY_JOY_13, "KEY_JOY_13", "Joy 13" },
+ { 0, KEY_JOY_14, "KEY_JOY_14", "Joy 14" },
+ { 0, KEY_JOY_15, "KEY_JOY_15", "Joy 15" },
+ { 0, KEY_JOY_16, "KEY_JOY_16", "Joy 16" },
+
+ { 0, KEY_POV_0_UP, "KEY_POV_0_UP", "Hat 1 Up" },
+ { 0, KEY_POV_0_DOWN, "KEY_POV_0_DOWN", "Hat 1 Down" },
+ { 0, KEY_POV_0_LEFT, "KEY_POV_0_LEFT", "Hat 1 Left" },
+ { 0, KEY_POV_0_RIGHT, "KEY_POV_0_RIGHT", "Hat 1 Right" },
+ { 0, KEY_POV_1_UP, "KEY_POV_1_UP", "Hat 2 Up" },
+ { 0, KEY_POV_1_DOWN, "KEY_POV_1_DOWN", "Hat 2 Down" },
+ { 0, KEY_POV_1_LEFT, "KEY_POV_1_LEFT", "Hat 2 Left" },
+ { 0, KEY_POV_1_RIGHT, "KEY_POV_1_RIGHT", "Hat 2 Right" },
+ { 0, KEY_POV_2_UP, "KEY_POV_2_UP", "Hat 3 Up" },
+ { 0, KEY_POV_2_DOWN, "KEY_POV_2_DOWN", "Hat 3 Down" },
+ { 0, KEY_POV_2_LEFT, "KEY_POV_2_LEFT", "Hat 3 Left" },
+ { 0, KEY_POV_2_RIGHT, "KEY_POV_2_RIGHT", "Hat 3 Right" },
+ { 0, KEY_POV_3_UP, "KEY_POV_3_UP", "Hat 4 Up" },
+ { 0, KEY_POV_3_DOWN, "KEY_POV_3_DOWN", "Hat 4 Down" },
+ { 0, KEY_POV_3_LEFT, "KEY_POV_3_LEFT", "Hat 4 Left" },
+ { 0, KEY_POV_3_RIGHT, "KEY_POV_3_RIGHT", "Hat 4 Right" },
+
+ { 0, 186, ";", ";" },
+ { 0, 188, ",", "<" },
+ { 0, 190, ".", ">" },
+ { 0, 191, "/", "/" },
+ { 0, 192, "~", "~" },
+ { 0, 219, "[", "[" },
+ { 0, 220, "\\", "\\" },
+ { 0, 221, "]", "]" },
+ { 0, 222, "'", "'" },
+
+ { 0, '0', "0", "0" },
+ { 0, '1', "1", "1" },
+ { 0, '2', "2", "2" },
+ { 0, '3', "3", "3" },
+ { 0, '4', "4", "4" },
+ { 0, '5', "5", "5" },
+ { 0, '6', "6", "6" },
+ { 0, '7', "7", "7" },
+ { 0, '8', "8", "8" },
+ { 0, '9', "9", "9" },
+
+ { 0, 'A', "A", "A" },
+ { 0, 'B', "B", "B" },
+ { 0, 'C', "C", "C" },
+ { 0, 'D', "D", "D" },
+ { 0, 'E', "E", "E" },
+ { 0, 'F', "F", "F" },
+ { 0, 'G', "G", "G" },
+ { 0, 'H', "H", "H" },
+ { 0, 'I', "I", "I" },
+ { 0, 'J', "J", "J" },
+ { 0, 'K', "K", "K" },
+ { 0, 'L', "L", "L" },
+ { 0, 'M', "M", "M" },
+ { 0, 'N', "N", "N" },
+ { 0, 'O', "O", "O" },
+ { 0, 'P', "P", "P" },
+ { 0, 'Q', "Q", "Q" },
+ { 0, 'R', "R", "R" },
+ { 0, 'S', "S", "S" },
+ { 0, 'T', "T", "T" },
+ { 0, 'U', "U", "U" },
+ { 0, 'V', "V", "V" },
+ { 0, 'W', "W", "W" },
+ { 0, 'X', "X", "X" },
+ { 0, 'Y', "Y", "Y" },
+ { 0, 'Z', "Z", "Z" },
+
+};
+
+// +----------------------------------------------------------------------+
+
+int KeyMap::GetKeyAction(const char* act_str)
+{
+ if (!act_str) return -1;
+
+ int nactions = sizeof(key_action_table) / sizeof(KeyName);
+
+ for (int i = 0; i < nactions; i++)
+ if (!stricmp(act_str, key_action_table[i].name))
+ return key_action_table[i].key;
+
+ return -1;
+}
+
+// +----------------------------------------------------------------------+
+
+int KeyMap::GetKeyActionIndex(int act)
+{
+ int nactions = sizeof(key_action_table) / sizeof(KeyName);
+
+ for (int i = 0; i < nactions; i++)
+ if (key_action_table[i].key == act)
+ return i;
+
+ return -1;
+}
+
+// +----------------------------------------------------------------------+
+
+int KeyMap::GetKeyKey(const char* key_str)
+{
+ if (!key_str) return 0;
+
+ if (*key_str == '=') {
+ int value = 0;
+ sscanf(key_str, "=%d", &value);
+ return value;
+ }
+
+ int nkeys = sizeof(key_key_table) / sizeof(KeyName);
+
+ for (int i = 0; i < nkeys; i++)
+ if (!stricmp(key_str, key_key_table[i].name))
+ return key_key_table[i].key;
+
+ return 0;
+}
+
+// +----------------------------------------------------------------------+
+
+int KeyMap::GetKeyKeyIndex(int key)
+{
+ int nkeys = sizeof(key_key_table) / sizeof(KeyName);
+
+ for (int i = 0; i < nkeys; i++)
+ if (key_key_table[i].key == key)
+ return i;
+
+ return -1;
+}
+
+// +----------------------------------------------------------------------+
+
+
+KeyMap::KeyMap()
+ : nkeys(0)
+{
+ int n = BuildDefaultKeyMap();
+ DefaultKeyMap(n);
+}
+
+KeyMap::~KeyMap()
+{ }
+
+// +----------------------------------------------------------------------+
+
+int
+KeyMap::BuildDefaultKeyMap()
+{
+ int i = 0;
+
+ defmap[i++] = KeyMapEntry(KEY_PITCH_UP, VK_DOWN);
+ defmap[i++] = KeyMapEntry(KEY_PITCH_DOWN, VK_UP);
+ defmap[i++] = KeyMapEntry(KEY_YAW_LEFT, VK_LEFT);
+ defmap[i++] = KeyMapEntry(KEY_YAW_RIGHT, VK_RIGHT);
+ defmap[i++] = KeyMapEntry(KEY_ROLL_LEFT, VK_NUMPAD7);
+ defmap[i++] = KeyMapEntry(KEY_ROLL_RIGHT, VK_NUMPAD9);
+
+ defmap[i++] = KeyMapEntry(KEY_PLUS_X, 190, 0, KEY_POV_0_RIGHT); // .
+ defmap[i++] = KeyMapEntry(KEY_MINUS_X, 188, 0, KEY_POV_0_LEFT); // ,
+ defmap[i++] = KeyMapEntry(KEY_PLUS_Y, VK_HOME);
+ defmap[i++] = KeyMapEntry(KEY_MINUS_Y, VK_END);
+ defmap[i++] = KeyMapEntry(KEY_PLUS_Z, VK_PRIOR, 0, KEY_POV_0_UP);
+ defmap[i++] = KeyMapEntry(KEY_MINUS_Z, VK_NEXT, 0, KEY_POV_0_DOWN);
+
+ defmap[i++] = KeyMapEntry(KEY_ACTION_0, VK_CONTROL, 0, KEY_JOY_1);
+ defmap[i++] = KeyMapEntry(KEY_ACTION_1, VK_SPACE, 0, KEY_JOY_2);
+
+
+ defmap[i++] = KeyMapEntry(KEY_THROTTLE_UP, 'A');
+ defmap[i++] = KeyMapEntry(KEY_THROTTLE_DOWN, 'Z');
+ defmap[i++] = KeyMapEntry(KEY_THROTTLE_FULL, 'A', VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_THROTTLE_ZERO, 'Z', VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_AUGMENTER, VK_TAB);
+ defmap[i++] = KeyMapEntry(KEY_FLCS_MODE_AUTO, 'M');
+ defmap[i++] = KeyMapEntry(KEY_COMMAND_MODE, 'M', VK_SHIFT);
+
+ defmap[i++] = KeyMapEntry(KEY_CYCLE_PRIMARY, VK_BACK, VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_CYCLE_SECONDARY, VK_BACK);
+ defmap[i++] = KeyMapEntry(KEY_LOCK_TARGET, 'T', 0, KEY_JOY_3);
+ defmap[i++] = KeyMapEntry(KEY_LOCK_THREAT, 'T', VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_LOCK_CLOSEST_SHIP, 'U');
+ defmap[i++] = KeyMapEntry(KEY_LOCK_CLOSEST_THREAT, 'U', VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_LOCK_HOSTILE_SHIP, 'Y');
+ defmap[i++] = KeyMapEntry(KEY_LOCK_HOSTILE_THREAT, 'Y', VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_CYCLE_SUBTARGET, 186);
+ defmap[i++] = KeyMapEntry(KEY_PREV_SUBTARGET, 186, VK_SHIFT);
+
+ defmap[i++] = KeyMapEntry(KEY_DECOY, 'D', 0, KEY_JOY_4);
+ defmap[i++] = KeyMapEntry(KEY_GEAR_TOGGLE, 'G');
+ defmap[i++] = KeyMapEntry(KEY_NAVLIGHT_TOGGLE, 'L');
+
+ defmap[i++] = KeyMapEntry(KEY_AUTO_NAV, 'N', VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_DROP_ORBIT, 'O');
+
+ defmap[i++] = KeyMapEntry(KEY_SHIELDS_UP, 'S');
+ defmap[i++] = KeyMapEntry(KEY_SHIELDS_DOWN, 'X');
+ defmap[i++] = KeyMapEntry(KEY_SHIELDS_FULL, 'S', VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_SHIELDS_ZERO, 'X', VK_SHIFT);
+
+ defmap[i++] = KeyMapEntry(KEY_SENSOR_MODE, VK_F5);
+ defmap[i++] = KeyMapEntry(KEY_SENSOR_GROUND_MODE, VK_F5, VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_LAUNCH_PROBE, VK_F6);
+ defmap[i++] = KeyMapEntry(KEY_SENSOR_RANGE_MINUS, VK_F7);
+ defmap[i++] = KeyMapEntry(KEY_SENSOR_RANGE_PLUS, VK_F8);
+ defmap[i++] = KeyMapEntry(KEY_EMCON_MINUS, VK_F9);
+ defmap[i++] = KeyMapEntry(KEY_EMCON_PLUS, VK_F10);
+
+ defmap[i++] = KeyMapEntry(KEY_EXIT, VK_ESCAPE);
+ defmap[i++] = KeyMapEntry(KEY_PAUSE, VK_PAUSE);
+// defmap[i++] = KeyMapEntry(KEY_NEXT_VIEW, VK_TAB);
+ defmap[i++] = KeyMapEntry(KEY_TIME_EXPAND, VK_DELETE);
+ defmap[i++] = KeyMapEntry(KEY_TIME_COMPRESS, VK_INSERT);
+ defmap[i++] = KeyMapEntry(KEY_TIME_SKIP, VK_INSERT, VK_SHIFT);
+
+ defmap[i++] = KeyMapEntry(KEY_CAM_BRIDGE, VK_F1);
+ defmap[i++] = KeyMapEntry(KEY_CAM_VIRT, VK_F1, VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_CAM_CHASE, VK_F2);
+ defmap[i++] = KeyMapEntry(KEY_CAM_DROP, VK_F2, VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_CAM_EXTERN, VK_F3);
+ defmap[i++] = KeyMapEntry(KEY_TARGET_PADLOCK, VK_F4);
+
+ defmap[i++] = KeyMapEntry(KEY_ZOOM_WIDE, 'K');
+ defmap[i++] = KeyMapEntry(KEY_HUD_MODE, 'H');
+ defmap[i++] = KeyMapEntry(KEY_HUD_COLOR, 'H', VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_HUD_WARN, 'C');
+ defmap[i++] = KeyMapEntry(KEY_HUD_INST, 'I');
+ defmap[i++] = KeyMapEntry(KEY_NAV_DLG, 'N');
+ defmap[i++] = KeyMapEntry(KEY_WEP_DLG, 'W');
+ defmap[i++] = KeyMapEntry(KEY_ENG_DLG, 'E');
+ defmap[i++] = KeyMapEntry(KEY_FLT_DLG, 'F');
+ defmap[i++] = KeyMapEntry(KEY_RADIO_MENU, 'R');
+ defmap[i++] = KeyMapEntry(KEY_QUANTUM_MENU, 'Q');
+
+ defmap[i++] = KeyMapEntry(KEY_MFD1, 219); // [
+ defmap[i++] = KeyMapEntry(KEY_MFD2, 221); // ]
+ defmap[i++] = KeyMapEntry(KEY_SELF_DESTRUCT, VK_ESCAPE, VK_SHIFT);
+
+ defmap[i++] = KeyMapEntry(KEY_CAM_CYCLE_OBJECT, VK_TAB, VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_CAM_EXT_PLUS_AZ, VK_LEFT, VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_CAM_EXT_MINUS_AZ, VK_RIGHT, VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_CAM_EXT_PLUS_EL, VK_UP, VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_CAM_EXT_MINUS_EL, VK_DOWN, VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_CAM_EXT_PLUS_RANGE, VK_SUBTRACT);
+ defmap[i++] = KeyMapEntry(KEY_CAM_EXT_MINUS_RANGE, VK_ADD);
+ defmap[i++] = KeyMapEntry(KEY_CAM_VIEW_SELECTION, 'V');
+ defmap[i++] = KeyMapEntry(KEY_CAM_VIRT_PLUS_AZ, VK_LEFT, VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_CAM_VIRT_MINUS_AZ, VK_RIGHT, VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_CAM_VIRT_PLUS_EL, VK_DOWN, VK_SHIFT);
+ defmap[i++] = KeyMapEntry(KEY_CAM_VIRT_MINUS_EL, VK_UP, VK_SHIFT);
+
+ defmap[i++] = KeyMapEntry(KEY_SWAP_ROLL_YAW, 'J');
+
+ defmap[i++] = KeyMapEntry(KEY_COMM_ATTACK_TGT, 'A', VK_MENU);
+ defmap[i++] = KeyMapEntry(KEY_COMM_ESCORT_TGT, 'E', VK_MENU);
+ defmap[i++] = KeyMapEntry(KEY_COMM_WEP_FREE, 'B', VK_MENU);
+ defmap[i++] = KeyMapEntry(KEY_COMM_WEP_HOLD, 'F', VK_MENU);
+ defmap[i++] = KeyMapEntry(KEY_COMM_COVER_ME, 'H', VK_MENU);
+ defmap[i++] = KeyMapEntry(KEY_COMM_SKIP_NAV, 'N', VK_MENU);
+ defmap[i++] = KeyMapEntry(KEY_COMM_RETURN_TO_BASE, 'R', VK_MENU);
+ defmap[i++] = KeyMapEntry(KEY_COMM_CALL_INBOUND, 'I', VK_MENU);
+ defmap[i++] = KeyMapEntry(KEY_COMM_REQUEST_PICTURE,'P', VK_MENU);
+ defmap[i++] = KeyMapEntry(KEY_COMM_REQUEST_SUPPORT,'S', VK_MENU);
+
+ defmap[i++] = KeyMapEntry(KEY_CHAT_BROADCAST, '1', VK_MENU);
+ defmap[i++] = KeyMapEntry(KEY_CHAT_TEAM, '2', VK_MENU);
+ defmap[i++] = KeyMapEntry(KEY_CHAT_WING, '3', VK_MENU);
+ defmap[i++] = KeyMapEntry(KEY_CHAT_UNIT, '4', VK_MENU);
+
+ defmap[i++] = KeyMapEntry(KEY_CONTROL_MODEL, 0);
+
+ defmap[i++] = KeyMapEntry(KEY_JOY_SELECT, 1);
+ defmap[i++] = KeyMapEntry(KEY_JOY_RUDDER, 0);
+ defmap[i++] = KeyMapEntry(KEY_JOY_THROTTLE, 1);
+ defmap[i++] = KeyMapEntry(KEY_JOY_SENSE, 1);
+ defmap[i++] = KeyMapEntry(KEY_JOY_DEAD_ZONE, 500);
+
+ defmap[i++] = KeyMapEntry(KEY_MOUSE_SELECT, 1);
+ defmap[i++] = KeyMapEntry(KEY_MOUSE_SENSE, 10);
+ defmap[i++] = KeyMapEntry(KEY_MOUSE_ACTIVE, 192); // ~ key
+
+/*** For Debug Convenience Only: ***/
+ defmap[i++] = KeyMapEntry(KEY_INC_STARDATE, VK_F11);
+ defmap[i++] = KeyMapEntry(KEY_DEC_STARDATE, VK_F11, VK_SHIFT);
+/***/
+
+ return i;
+}
+
+// +----------------------------------------------------------------------+
+
+int
+KeyMap::DefaultKeyMap(int max_keys)
+{
+ for (int i = 0; i < max_keys; i++)
+ map[i] = defmap[i];
+
+ nkeys = max_keys;
+ return nkeys;
+}
+
+void
+KeyMap::Bind(int a, int k, int s)
+{
+ if (!a) return;
+
+ for (int i = 0; i < nkeys; i++) {
+ if (map[i].act == a) {
+ map[i].key = k;
+ map[i].alt = s;
+
+ return;
+ }
+ }
+
+ map[nkeys++] = KeyMapEntry(a, k, s);
+}
+
+// +----------------------------------------------------------------------+
+
+int
+KeyMap::LoadKeyMap(const char* filename, int max_keys)
+{
+ FILE* f = fopen(filename, "r");
+ if (!f) return nkeys;
+
+ char line[256];
+
+ while (fgets(line, sizeof(line), f)) {
+ int act = -1, key = -1, alt = 0, joy = -1;
+ char act_str[128], key_str[128], alt_str[128], joy_str[128];
+
+ ZeroMemory(act_str, sizeof(act_str));
+ ZeroMemory(key_str, sizeof(key_str));
+ ZeroMemory(alt_str, sizeof(alt_str));
+ ZeroMemory(joy_str, sizeof(joy_str));
+
+ sscanf(line, "%s %s %s %s", act_str, key_str, alt_str, joy_str);
+
+ act = GetKeyAction(act_str);
+ key = GetKeyKey(key_str);
+ alt = GetKeyKey(alt_str);
+ joy = GetKeyKey(joy_str);
+
+ if (act != -1 && key != -1) {
+ if (act == KEY_CONTROL_MODEL)
+ Ship::SetControlModel(key);
+
+ int mapped = false;
+
+ for (int i = 0; i < max_keys && !mapped; i++) {
+ if (map[i].act == act) {
+ Print(" Remapping: '%s' => %s(%d) %s(%d) %s(%d)\n",
+ act_str, key_str, key, alt_str, alt, joy_str, joy);
+
+ map[i] = KeyMapEntry(act, key, alt, joy);
+ mapped = true;
+ }
+ }
+
+ if (!mapped) {
+ Print(" Mapping: '%s' => %s(%d) %s(%d) %s(%d)\n",
+ act_str, key_str, key, alt_str, alt, joy_str, joy);
+ map[nkeys++] = KeyMapEntry(act, key, alt, joy);
+ }
+ }
+
+ if (nkeys >= max_keys-1) {
+ Print(" Too many keys in configuration...\n");
+ break;
+ }
+ }
+
+ fclose(f);
+ return nkeys;
+}
+
+// +----------------------------------------------------------------------+
+
+int
+KeyMap::SaveKeyMap(const char* filename, int max_keys)
+{
+ FILE* f = fopen(filename, "w");
+ if (!f) return 0;
+
+ for (int i = 0; i < nkeys; i++) {
+ if (map[i].act >= KEY_CONTROL_MODEL &&
+ map[i].act <= KEY_AXIS_THROTTLE_INVERT) {
+
+ int a = GetKeyActionIndex(map[i].act);
+
+ fprintf(f, "%-24s =%d\n", key_action_table[a].name, map[i].key);
+ }
+
+ else if (map[i] != defmap[i]) {
+ int a = GetKeyActionIndex(map[i].act);
+ int k = GetKeyKeyIndex(map[i].key);
+ int s = GetKeyKeyIndex(map[i].alt);
+ int j = GetKeyKeyIndex(map[i].joy);
+
+ if (a > -1) {
+ if (j > -1) {
+ if (s > -1) {
+ fprintf(f, "%-24s %-16s %-16s %-16s\n",
+ key_action_table[a].name,
+ key_key_table[ k].name,
+ key_key_table[ s].name,
+ key_key_table[ j].name);
+ }
+ else if (k > -1) {
+ fprintf(f, "%-24s %-16s %-16s %-16s\n",
+ key_action_table[a].name,
+ key_key_table[ k].name,
+ "null",
+ key_key_table[ j].name);
+ }
+ else {
+ fprintf(f, "%-24s %-16s %-16s %-16s\n",
+ key_action_table[a].name,
+ "null",
+ "null",
+ key_key_table[ j].name);
+ }
+ }
+ else if (s > -1) {
+ fprintf(f, "%-24s %-16s %-16s\n",
+ key_action_table[a].name,
+ key_key_table[ k].name,
+ key_key_table[ s].name);
+ }
+ else if (k > -1) {
+ fprintf(f, "%-24s %-16s\n",
+ key_action_table[a].name,
+ key_key_table[ k].name);
+ }
+ }
+ }
+ }
+
+ fclose(f);
+ return nkeys;
+}
+
+// +----------------------------------------------------------------------+
+
+int
+KeyMap::GetCategory(int n)
+{
+ int nactions = sizeof(key_action_table) / sizeof(KeyName);
+
+ for (int i = 0; i < nactions; i++) {
+ if (map[n].act == key_action_table[i].key) {
+ return key_action_table[i].category;
+ }
+ }
+
+ return KEY_MISC;
+}
+
+// +----------------------------------------------------------------------+
+
+int
+KeyMap::FindMapIndex(int act)
+{
+ for (int n = 0; n < nkeys; n++)
+ if (map[n].act == act)
+ return n;
+ return -1;
+}
+
+// +----------------------------------------------------------------------+
+
+const char*
+KeyMap::DescribeAction(int n)
+{
+ int nactions = sizeof(key_action_table) / sizeof(KeyName);
+
+ for (int i = 0; i < nactions; i++) {
+ if (map[n].act == key_action_table[i].key) {
+ if (key_action_table[i].desc)
+ return key_action_table[i].desc;
+
+ return key_action_table[i].name;
+ }
+ }
+
+ return 0;
+}
+
+// +----------------------------------------------------------------------+
+
+static char key_desc[32];
+
+const char*
+KeyMap::DescribeKey(int n)
+{
+ if (n >= 0 && n < 256)
+ return DescribeKey(map[n].key, map[n].alt, map[n].joy);
+
+ return 0;
+}
+
+const char*
+KeyMap::DescribeKey(int vk, int shift, int j)
+{
+ const char* key = 0;
+ const char* alt = 0;
+ const char* joy = 0;
+
+ int nkeys = sizeof(key_key_table) / sizeof(KeyName);
+
+ for (int i = 0; i < nkeys; i++) {
+ if (vk > 0 && vk == key_key_table[i].key) {
+ if (key_key_table[i].desc)
+ key = key_key_table[i].desc;
+ else
+ key = key_key_table[i].name;
+ }
+
+ if (shift > 0 && shift == key_key_table[i].key) {
+ if (key_key_table[i].desc)
+ alt = key_key_table[i].desc;
+ else
+ alt = key_key_table[i].name;
+ }
+
+ if (j > 0 && j == key_key_table[i].key) {
+ if (key_key_table[i].desc)
+ joy = key_key_table[i].desc;
+ else
+ joy = key_key_table[i].name;
+ }
+ }
+
+ if (key) {
+ if (alt) {
+ sprintf(key_desc, "%s+%s", alt, key);
+ }
+ else {
+ strcpy(key_desc, key);
+ }
+
+ if (joy) {
+ strcat(key_desc, ", ");
+ strcat(key_desc, joy);
+ }
+ }
+
+ else if (joy) {
+ strcpy(key_desc, joy);
+ }
+
+ else {
+ sprintf(key_desc, "%d", vk);
+ }
+
+ return key_desc;
+}
+
+int
+KeyMap::GetMappableVKey(int n)
+{
+ int nkeys = sizeof(key_key_table) / sizeof(KeyName);
+
+ if (n >= 0 && n < nkeys) {
+ return key_key_table[n].key;
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/Stars45/KeyMap.h b/Stars45/KeyMap.h
new file mode 100644
index 0000000..46821d6
--- /dev/null
+++ b/Stars45/KeyMap.h
@@ -0,0 +1,181 @@
+/* Project Starshatter 4.6
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: KeyMap.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Keyboard Mapping
+*/
+
+#ifndef KeyMap_h
+#define KeyMap_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "MotionController.h"
+
+// +--------------------------------------------------------------------+
+
+class KeyMap
+{
+public:
+ KeyMap();
+ virtual ~KeyMap();
+
+ int DefaultKeyMap(int max_keys = 256);
+ int LoadKeyMap(const char* filename, int max_keys = 256);
+ int SaveKeyMap(const char* filename, int max_keys = 256);
+
+ enum KEY_CATEGORY { KEY_FLIGHT, KEY_WEAPONS, KEY_VIEW, KEY_MISC };
+
+ int GetCategory(int i);
+ const char* DescribeAction(int i);
+ const char* DescribeKey(int i);
+ int FindMapIndex(int act);
+
+ static const char* DescribeKey(int vk, int shift, int joy);
+ static int GetMappableVKey(int n);
+
+ int GetNumKeys() { return nkeys; }
+ KeyMapEntry* GetMapping() { return map; }
+ KeyMapEntry* GetKeyMap(int i) { return &map[i]; }
+ KeyMapEntry* GetDefault(int i) { return &defmap[i]; }
+
+ void Bind(int a, int k, int s);
+
+ static int GetKeyAction(const char* act_str);
+ static int GetKeyActionIndex(int act);
+ static int GetKeyKey(const char* key_str);
+ static int GetKeyKeyIndex(int key);
+
+protected:
+ int BuildDefaultKeyMap();
+
+ KeyMapEntry map[256];
+ KeyMapEntry defmap[256];
+ int nkeys;
+};
+
+// +--------------------------------------------------------------------+
+
+
+const int KEY_EXIT = 0 + KEY_USER_BASE;
+const int KEY_PAUSE = 1 + KEY_USER_BASE;
+const int KEY_NEXT_VIEW = 2 + KEY_USER_BASE;
+const int KEY_TARGET_PADLOCK = 3 + KEY_USER_BASE;
+const int KEY_THREAT_PADLOCK = 4 + KEY_USER_BASE;
+const int KEY_LOCK_TARGET = 5 + KEY_USER_BASE;
+const int KEY_LOCK_THREAT = 6 + KEY_USER_BASE;
+const int KEY_AUTO_NAV = 7 + KEY_USER_BASE;
+const int KEY_TIME_COMPRESS = 8 + KEY_USER_BASE;
+const int KEY_TIME_EXPAND = 9 + KEY_USER_BASE;
+const int KEY_TIME_SKIP = 10 + KEY_USER_BASE;
+
+const int KEY_SWAP_ROLL_YAW = 11 + KEY_USER_BASE;
+
+const int KEY_THROTTLE_UP = 15 + KEY_USER_BASE;
+const int KEY_THROTTLE_DOWN = 16 + KEY_USER_BASE;
+const int KEY_THROTTLE_ZERO = 17 + KEY_USER_BASE;
+const int KEY_THROTTLE_FULL = 18 + KEY_USER_BASE;
+const int KEY_CYCLE_PRIMARY = 19 + KEY_USER_BASE;
+const int KEY_CYCLE_SECONDARY = 20 + KEY_USER_BASE;
+const int KEY_FLCS_MODE_AUTO = 21 + KEY_USER_BASE;
+const int KEY_DROP_ORBIT = 22 + KEY_USER_BASE;
+
+const int KEY_HUD_INST = 23 + KEY_USER_BASE;
+const int KEY_CAM_BRIDGE = 24 + KEY_USER_BASE;
+const int KEY_CAM_CHASE = 25 + KEY_USER_BASE;
+const int KEY_CAM_EXTERN = 26 + KEY_USER_BASE;
+const int KEY_HUD_MODE = 27 + KEY_USER_BASE;
+const int KEY_HUD_COLOR = 28 + KEY_USER_BASE;
+const int KEY_HUD_WARN = 29 + KEY_USER_BASE;
+const int KEY_NAV_DLG = 30 + KEY_USER_BASE;
+const int KEY_WEP_DLG = 31 + KEY_USER_BASE;
+const int KEY_FLT_DLG = 32 + KEY_USER_BASE;
+const int KEY_ENG_DLG = 33 + KEY_USER_BASE;
+
+const int KEY_ZOOM_WIDE = 34 + KEY_USER_BASE;
+const int KEY_ZOOM_IN = 35 + KEY_USER_BASE;
+const int KEY_ZOOM_OUT = 36 + KEY_USER_BASE;
+const int KEY_CAM_CYCLE_OBJECT = 37 + KEY_USER_BASE;
+const int KEY_CAM_EXT_PLUS_AZ = 38 + KEY_USER_BASE;
+const int KEY_CAM_EXT_MINUS_AZ = 39 + KEY_USER_BASE;
+const int KEY_CAM_EXT_PLUS_EL = 40 + KEY_USER_BASE;
+const int KEY_CAM_EXT_MINUS_EL = 41 + KEY_USER_BASE;
+const int KEY_CAM_EXT_PLUS_RANGE = 42 + KEY_USER_BASE;
+const int KEY_CAM_EXT_MINUS_RANGE = 43 + KEY_USER_BASE;
+const int KEY_CAM_VIEW_SELECTION = 44 + KEY_USER_BASE;
+const int KEY_CAM_DROP = 45 + KEY_USER_BASE;
+
+const int KEY_TARGET_SELECTION = 50 + KEY_USER_BASE;
+const int KEY_RADIO_MENU = 51 + KEY_USER_BASE;
+const int KEY_QUANTUM_MENU = 52 + KEY_USER_BASE;
+const int KEY_MFD1 = 53 + KEY_USER_BASE;
+const int KEY_MFD2 = 54 + KEY_USER_BASE;
+const int KEY_MFD3 = 55 + KEY_USER_BASE;
+const int KEY_MFD4 = 56 + KEY_USER_BASE;
+
+const int KEY_SENSOR_MODE = 60 + KEY_USER_BASE;
+const int KEY_SENSOR_GROUND_MODE = 61 + KEY_USER_BASE;
+const int KEY_SENSOR_BEAM = 62 + KEY_USER_BASE;
+const int KEY_SENSOR_RANGE_PLUS = 63 + KEY_USER_BASE;
+const int KEY_SENSOR_RANGE_MINUS = 64 + KEY_USER_BASE;
+const int KEY_EMCON_PLUS = 65 + KEY_USER_BASE;
+const int KEY_EMCON_MINUS = 66 + KEY_USER_BASE;
+
+const int KEY_SHIELDS_UP = 67 + KEY_USER_BASE;
+const int KEY_SHIELDS_DOWN = 68 + KEY_USER_BASE;
+const int KEY_SHIELDS_FULL = 69 + KEY_USER_BASE;
+const int KEY_SHIELDS_ZERO = 70 + KEY_USER_BASE;
+const int KEY_DECOY = 71 + KEY_USER_BASE;
+const int KEY_ECM_TOGGLE = 72 + KEY_USER_BASE;
+const int KEY_LAUNCH_PROBE = 73 + KEY_USER_BASE;
+const int KEY_GEAR_TOGGLE = 74 + KEY_USER_BASE;
+
+const int KEY_LOCK_CLOSEST_SHIP = 75 + KEY_USER_BASE;
+const int KEY_LOCK_CLOSEST_THREAT = 76 + KEY_USER_BASE;
+const int KEY_LOCK_HOSTILE_SHIP = 77 + KEY_USER_BASE;
+const int KEY_LOCK_HOSTILE_THREAT = 78 + KEY_USER_BASE;
+const int KEY_CYCLE_SUBTARGET = 79 + KEY_USER_BASE;
+const int KEY_PREV_SUBTARGET = 80 + KEY_USER_BASE;
+
+const int KEY_AUGMENTER = 81 + KEY_USER_BASE;
+const int KEY_NAVLIGHT_TOGGLE = 82 + KEY_USER_BASE;
+
+const int KEY_CAM_VIRT = 85 + KEY_USER_BASE;
+const int KEY_CAM_VIRT_PLUS_AZ = 86 + KEY_USER_BASE;
+const int KEY_CAM_VIRT_MINUS_AZ = 87 + KEY_USER_BASE;
+const int KEY_CAM_VIRT_PLUS_EL = 88 + KEY_USER_BASE;
+const int KEY_CAM_VIRT_MINUS_EL = 89 + KEY_USER_BASE;
+
+const int KEY_COMM_ATTACK_TGT = 90 + KEY_USER_BASE;
+const int KEY_COMM_ESCORT_TGT = 91 + KEY_USER_BASE;
+const int KEY_COMM_WEP_FREE = 92 + KEY_USER_BASE;
+const int KEY_COMM_WEP_HOLD = 93 + KEY_USER_BASE;
+const int KEY_COMM_COVER_ME = 94 + KEY_USER_BASE;
+const int KEY_COMM_SKIP_NAV = 95 + KEY_USER_BASE;
+const int KEY_COMM_RETURN_TO_BASE = 96 + KEY_USER_BASE;
+const int KEY_COMM_CALL_INBOUND = 97 + KEY_USER_BASE;
+const int KEY_COMM_REQUEST_PICTURE = 98 + KEY_USER_BASE;
+const int KEY_COMM_REQUEST_SUPPORT = 99 + KEY_USER_BASE;
+
+const int KEY_CHAT_BROADCAST = 100 + KEY_USER_BASE;
+const int KEY_CHAT_TEAM = 101 + KEY_USER_BASE;
+const int KEY_CHAT_WING = 102 + KEY_USER_BASE;
+const int KEY_CHAT_UNIT = 103 + KEY_USER_BASE;
+
+const int KEY_COMMAND_MODE = 104 + KEY_USER_BASE;
+const int KEY_SELF_DESTRUCT = 105 + KEY_USER_BASE;
+
+/*** For Debug Convenience Only: ***/
+const int KEY_INC_STARDATE = 120 + KEY_USER_BASE;
+const int KEY_DEC_STARDATE = 121 + KEY_USER_BASE;
+/***/
+
+#endif KeyMap_h
+
diff --git a/Stars45/LandingGear.cpp b/Stars45/LandingGear.cpp
new file mode 100644
index 0000000..61135ed
--- /dev/null
+++ b/Stars45/LandingGear.cpp
@@ -0,0 +1,278 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: LandingGear.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ LandingGear System class
+*/
+
+#include "MemDebug.h"
+#include "LandingGear.h"
+#include "Ship.h"
+#include "Sim.h"
+#include "AudioConfig.h"
+
+#include "DataLoader.h"
+#include "Physical.h"
+#include "Scene.h"
+#include "Sound.h"
+#include "Game.h"
+
+static Sound* gear_transit_sound = 0;
+
+// +----------------------------------------------------------------------+
+
+LandingGear::LandingGear()
+ : System(MISC_SYSTEM, 0, "Landing Gear", 1, 1, 1, 1),
+ state(GEAR_UP), transit(0), ngear(0), clearance(0)
+{
+ name = Game::GetText("sys.landing-gear");
+ abrv = Game::GetText("sys.landing-gear.abrv");
+
+ for (int i = 0; i < MAX_GEAR; i++) {
+ models[i] = 0;
+ gear[i] = 0;
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+LandingGear::LandingGear(const LandingGear& g)
+ : System(g), state(GEAR_UP), transit(0), ngear(g.ngear), clearance(0)
+{
+ Mount(g);
+ SetAbbreviation(g.Abbreviation());
+
+ for (int i = 0; i < ngear; i++) {
+ models[i] = 0;
+ gear[i] = new(__FILE__,__LINE__) Solid;
+ start[i] = g.start[i];
+ end[i] = g.end[i];
+
+ gear[i]->UseModel(g.models[i]);
+
+ if (clearance > end[i].y)
+ clearance = end[i].y;
+ }
+
+ while (i < MAX_GEAR) {
+ models[i] = 0;
+ gear[i] = 0;
+ i++;
+ }
+
+ clearance += mount_rel.y;
+}
+
+// +--------------------------------------------------------------------+
+
+LandingGear::~LandingGear()
+{
+ for (int i = 0; i < MAX_GEAR; i++) {
+ delete models[i];
+
+ if (gear[i]) {
+ Solid* g = gear[i];
+
+ if (g->GetScene())
+ g->GetScene()->DelGraphic(g);
+ delete g;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+LandingGear::Initialize()
+{
+ if (!gear_transit_sound) {
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Sounds/");
+ loader->LoadSound("GearTransit.wav", gear_transit_sound);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+LandingGear::Close()
+{
+ delete gear_transit_sound;
+ gear_transit_sound = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+LandingGear::AddGear(Model* m, const Point& s, const Point& e)
+{
+ if (ngear < MAX_GEAR) {
+ models[ngear] = m;
+ start[ngear] = s;
+ end[ngear] = e;
+
+ ngear++;
+ }
+
+ return ngear;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+LandingGear::SetState(GEAR_STATE s)
+{
+ if (state != s) {
+ state = s;
+
+ if (ship && ship == Sim::GetSim()->GetPlayerShip()) {
+ if (state == GEAR_LOWER || state == GEAR_RAISE) {
+ if (gear_transit_sound) {
+ Sound* sound = gear_transit_sound->Duplicate();
+ sound->SetVolume(AudioConfig::EfxVolume());
+ sound->Play();
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+LandingGear::ExecFrame(double seconds)
+{
+ System::ExecFrame(seconds);
+
+ switch (state) {
+ case GEAR_UP:
+ transit = 0;
+ break;
+
+ case GEAR_DOWN:
+ transit = 1;
+ break;
+
+ case GEAR_LOWER:
+ if (transit < 1) {
+ transit += seconds;
+
+ Scene* s = 0;
+ if (ship && ship->Rep())
+ s = ship->Rep()->GetScene();
+
+ if (s) {
+ for (int i = 0; i < ngear; i++) {
+ if (gear[i] && !gear[i]->GetScene()) {
+ s->AddGraphic(gear[i]);
+ }
+ }
+ }
+ }
+ else {
+ transit = 1;
+ state = GEAR_DOWN;
+ }
+ break;
+
+ case GEAR_RAISE:
+ if (transit > 0) {
+ transit -= seconds;
+ }
+ else {
+ transit = 0;
+ state = GEAR_UP;
+
+ for (int i = 0; i < ngear; i++) {
+ if (gear[i]) {
+ Scene* s = gear[i]->GetScene();
+ if (s) s->DelGraphic(gear[i]);
+ }
+ }
+ }
+ break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+LandingGear::Orient(const Physical* rep)
+{
+ System::Orient(rep);
+
+ const Matrix& orientation = rep->Cam().Orientation();
+ Point ship_loc = rep->Location();
+
+ for (int i = 0; i < ngear; i++) {
+ Point gloc;
+ if (transit < 1) gloc = start[i] + (end[i]-start[i])*transit;
+ else gloc = end[i];
+
+ Point projector = (gloc * orientation) + ship_loc;
+ if (gear[i]) {
+ gear[i]->MoveTo(projector);
+ gear[i]->SetOrientation(orientation);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Solid*
+LandingGear::GetGear(int index)
+{
+ if (index >= 0 && index < ngear) {
+ Solid* g = gear[index];
+ return g;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+Point
+LandingGear::GetGearStop(int index)
+{
+ if (index >= 0 && index < ngear) {
+ Solid* g = gear[index];
+
+ if (g)
+ return g->Location();
+ }
+
+ return Point();
+}
+
+// +--------------------------------------------------------------------+
+
+double
+LandingGear::GetTouchDown()
+{
+ double down = 0;
+
+ if (ship) {
+ down = ship->Location().y;
+
+ if (state != GEAR_UP) {
+ for (int i = 0; i < ngear; i++) {
+ if (gear[i]) {
+ Point stop = gear[i]->Location();
+
+ if (stop.y < down)
+ down = stop.y;
+ }
+ }
+ }
+ }
+
+ return down;
+} \ No newline at end of file
diff --git a/Stars45/LandingGear.h b/Stars45/LandingGear.h
new file mode 100644
index 0000000..30b18ac
--- /dev/null
+++ b/Stars45/LandingGear.h
@@ -0,0 +1,66 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: LandingGear.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Fighter undercarriage (landing gear) system class
+*/
+
+#ifndef LandingGear_h
+#define LandingGear_h
+
+#include "Types.h"
+#include "System.h"
+#include "Solid.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+
+// +--------------------------------------------------------------------+
+
+class LandingGear : public System
+{
+public:
+ enum CONSTANTS { MAX_GEAR = 4 };
+ enum GEAR_STATE { GEAR_UP, GEAR_LOWER, GEAR_DOWN, GEAR_RAISE };
+
+ LandingGear();
+ LandingGear(const LandingGear& rhs);
+ virtual ~LandingGear();
+
+ virtual int AddGear(Model* m, const Point& s, const Point& e);
+ virtual void ExecFrame(double seconds);
+ virtual void Orient(const Physical* rep);
+
+ GEAR_STATE GetState() const { return state; }
+ void SetState(GEAR_STATE s);
+ int NumGear() const { return ngear; }
+ Solid* GetGear(int i);
+ Point GetGearStop(int i);
+ double GetTouchDown();
+ double GetClearance() const { return clearance; }
+
+ static void Initialize();
+ static void Close();
+
+protected:
+ GEAR_STATE state;
+ double transit;
+ double clearance;
+
+ int ngear;
+ Model* models[MAX_GEAR];
+ Solid* gear[MAX_GEAR];
+ Point start[MAX_GEAR];
+ Point end[MAX_GEAR];
+};
+
+#endif LandingGear_h
+
diff --git a/Stars45/LoadDlg.cpp b/Stars45/LoadDlg.cpp
new file mode 100644
index 0000000..313aa22
--- /dev/null
+++ b/Stars45/LoadDlg.cpp
@@ -0,0 +1,77 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: LoadDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Loading progress dialog box
+*/
+
+
+#include "MemDebug.h"
+#include "LoadDlg.h"
+#include "Starshatter.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ListBox.h"
+#include "ComboBox.h"
+#include "Slider.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+
+LoadDlg::LoadDlg(Screen* s, FormDef& def)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()),
+ progress(0), activity(0)
+{
+ Init(def);
+}
+
+LoadDlg::~LoadDlg()
+{
+}
+
+void
+LoadDlg::RegisterControls()
+{
+ title = FindControl(100);
+ activity = FindControl(101);
+ progress = (Slider*) FindControl(102);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+LoadDlg::ExecFrame()
+{
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars) {
+ if (title) {
+ if (stars->GetGameMode() == Starshatter::CLOD_MODE ||
+ stars->GetGameMode() == Starshatter::CMPN_MODE)
+ title->SetText(Game::GetText("LoadDlg.campaign"));
+
+ else if (stars->GetGameMode() == Starshatter::MENU_MODE)
+ title->SetText(Game::GetText("LoadDlg.tac-ref"));
+
+ else
+ title->SetText(Game::GetText("LoadDlg.mission"));
+ }
+
+ activity->SetText(stars->GetLoadActivity());
+ progress->SetValue(stars->GetLoadProgress());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
diff --git a/Stars45/LoadDlg.h b/Stars45/LoadDlg.h
new file mode 100644
index 0000000..c33bb8d
--- /dev/null
+++ b/Stars45/LoadDlg.h
@@ -0,0 +1,41 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: LoadDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Loading progress dialog box
+*/
+
+#ifndef LoadDlg_h
+#define LoadDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+
+// +--------------------------------------------------------------------+
+
+class LoadDlg : public FormWindow
+{
+public:
+ LoadDlg(Screen* s, FormDef& def);
+ virtual ~LoadDlg();
+
+ virtual void RegisterControls();
+
+ // Operations:
+ virtual void ExecFrame();
+
+protected:
+ ActiveWindow* title;
+ ActiveWindow* activity;
+ Slider* progress;
+};
+
+#endif LoadDlg_h
+
diff --git a/Stars45/LoadScreen.cpp b/Stars45/LoadScreen.cpp
new file mode 100644
index 0000000..337a19d
--- /dev/null
+++ b/Stars45/LoadScreen.cpp
@@ -0,0 +1,163 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars
+ FILE: LoadScreen.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include "MemDebug.h"
+#include "LoadScreen.h"
+#include "LoadDlg.h"
+#include "CmpLoadDlg.h"
+#include "Starshatter.h"
+
+#include "Game.h"
+#include "Video.h"
+#include "Screen.h"
+#include "FormDef.h"
+#include "Window.h"
+#include "ActiveWindow.h"
+#include "Mouse.h"
+#include "Color.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "FontMgr.h"
+#include "DataLoader.h"
+#include "Resource.h"
+
+// +--------------------------------------------------------------------+
+
+LoadScreen::LoadScreen()
+ : screen(0), load_dlg(0), cmp_load_dlg(0), isShown(false)
+{ }
+
+LoadScreen::~LoadScreen()
+{
+ TearDown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+LoadScreen::Setup(Screen* s)
+{
+ if (!s)
+ return;
+
+ screen = s;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->UseFileSystem(true);
+
+ // create windows
+ FormDef load_def("LoadDlg", 0);
+ load_def.Load("LoadDlg");
+ load_dlg = new(__FILE__,__LINE__) LoadDlg(screen, load_def);
+
+ FormDef cmp_load_def("CmpLoadDlg", 0);
+ cmp_load_def.Load("CmpLoadDlg");
+ cmp_load_dlg = new(__FILE__,__LINE__) CmpLoadDlg(screen, cmp_load_def);
+
+ loader->UseFileSystem(Starshatter::UseFileSystem());
+ ShowLoadDlg();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+LoadScreen::TearDown()
+{
+ if (screen) {
+ if (load_dlg) screen->DelWindow(load_dlg);
+ if (cmp_load_dlg) screen->DelWindow(cmp_load_dlg);
+ }
+
+ delete load_dlg;
+ delete cmp_load_dlg;
+
+ load_dlg = 0;
+ cmp_load_dlg = 0;
+ screen = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+LoadScreen::ExecFrame()
+{
+ Game::SetScreenColor(Color::Black);
+
+ if (load_dlg && load_dlg->IsShown())
+ load_dlg->ExecFrame();
+
+ if (cmp_load_dlg && cmp_load_dlg->IsShown())
+ cmp_load_dlg->ExecFrame();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+LoadScreen::CloseTopmost()
+{
+ return false;
+}
+
+void
+LoadScreen::Show()
+{
+ if (!isShown) {
+ ShowLoadDlg();
+ isShown = true;
+ }
+}
+
+void
+LoadScreen::Hide()
+{
+ if (isShown) {
+ HideLoadDlg();
+ isShown = false;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+LoadScreen::ShowLoadDlg()
+{
+ if (load_dlg) load_dlg->Hide();
+ if (cmp_load_dlg) cmp_load_dlg->Hide();
+
+ Starshatter* stars = Starshatter::GetInstance();
+
+ // show campaign load dialog if available and loading campaign
+ if (stars && cmp_load_dlg) {
+ if (stars->GetGameMode() == Starshatter::CLOD_MODE ||
+ stars->GetGameMode() == Starshatter::CMPN_MODE) {
+ cmp_load_dlg->Show();
+ Mouse::Show(false);
+ return;
+ }
+ }
+
+ // otherwise, show regular load dialog
+ if (load_dlg) {
+ load_dlg->Show();
+ Mouse::Show(false);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+LoadScreen::HideLoadDlg()
+{
+ if (load_dlg && load_dlg->IsShown())
+ load_dlg->Hide();
+
+ if (cmp_load_dlg && cmp_load_dlg->IsShown())
+ cmp_load_dlg->Hide();
+}
diff --git a/Stars45/LoadScreen.h b/Stars45/LoadScreen.h
new file mode 100644
index 0000000..39756e5
--- /dev/null
+++ b/Stars45/LoadScreen.h
@@ -0,0 +1,64 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: LoadScreen.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef LoadScreen_h
+#define LoadScreen_h
+
+#include "Types.h"
+#include "Bitmap.h"
+#include "Screen.h"
+
+// +--------------------------------------------------------------------+
+
+class LoadDlg;
+class CmpLoadDlg;
+
+class Bitmap;
+class DataLoader;
+class Font;
+class Screen;
+class Video;
+class VideoFactory;
+
+// +--------------------------------------------------------------------+
+
+class LoadScreen
+{
+public:
+ LoadScreen();
+ virtual ~LoadScreen();
+
+ virtual void Setup(Screen* screen);
+ virtual void TearDown();
+ virtual bool CloseTopmost();
+
+ virtual bool IsShown() const { return isShown; }
+ virtual void Show();
+ virtual void Hide();
+
+ virtual void ShowLoadDlg();
+ virtual void HideLoadDlg();
+ virtual LoadDlg* GetLoadDlg() { return load_dlg; }
+ virtual CmpLoadDlg* GetCmpLoadDlg() { return cmp_load_dlg; }
+
+ virtual void ExecFrame();
+
+private:
+ Screen* screen;
+ LoadDlg* load_dlg;
+ CmpLoadDlg* cmp_load_dlg;
+
+ bool isShown;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif LoadScreen_h
+
diff --git a/Stars45/Main.cpp b/Stars45/Main.cpp
new file mode 100644
index 0000000..c71bd4d
--- /dev/null
+++ b/Stars45/Main.cpp
@@ -0,0 +1,186 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: main.cpp
+ AUTHOR: John DiCamillo
+*/
+
+
+#include "MemDebug.h"
+#include "Starshatter.h"
+#include "StarServer.h"
+#include "Authorization.h"
+#include "HUDView.h"
+
+#include "NetHost.h"
+#include "NetAddr.h"
+#include "NetLayer.h"
+#include "NetBrokerClient.h"
+#include "NetClient.h"
+#include "HttpClient.h"
+
+#include "Color.h"
+#include "DataLoader.h"
+#include "Pcx.h"
+#include "MachineInfo.h"
+#include "Encrypt.h"
+#include "FormatUtil.h"
+#include "ParseUtil.h"
+#include "Random.h"
+
+// +--------------------------------------------------------------------+
+// WinMain
+// +--------------------------------------------------------------------+
+
+extern FILE* ErrLog;
+extern int VD3D_describe_things;
+int dump_missions = 0;
+
+#ifdef STARSHATTER_DEMO_RELEASE
+const char* versionInfo = "5.0 DEMO";
+#else
+const char* versionInfo = "5.0.4";
+#endif
+
+static void PrintLogHeader()
+{
+ Text sTime = FormatTimeString();
+
+ Print("+====================================================================+\n");
+ Print("| STARSHATTER %-25s%29s |\n", versionInfo, sTime.data());
+
+ Memory::SetLevel(Memory::MAXIMAL);
+ Memory::OpenLog();
+ MachineInfo::DescribeMachine();
+}
+
+int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine, int nCmdShow)
+{
+ int result = 0;
+ int test_mode = 0;
+ int do_server = 0;
+
+ if (strstr(lpCmdLine, "-server"))
+ ErrLog = fopen("serverlog.txt", "w");
+ else
+ ErrLog = fopen("errlog.txt", "w");
+
+ PrintLogHeader();
+
+ if (strstr(lpCmdLine, "-test")) {
+ Print(" Request TEST mode\n");
+ test_mode = 1;
+ }
+
+ if (strstr(lpCmdLine, "-fps")) {
+ HUDView::ShowFPS(true);
+ }
+
+ if (strstr(lpCmdLine, "-dump")) {
+ Print(" Request dump dynamic missions\n");
+ dump_missions = 1;
+ }
+
+ if (strstr(lpCmdLine, "-lan")) {
+ Print(" Request LAN ONLY mode\n");
+ NetBrokerClient::Disable();
+ }
+
+ if (strstr(lpCmdLine, "-server")) {
+ do_server = 1;
+ Print(" Request Standalone Server Mode\n");
+ }
+
+ char* d3dinfo = strstr(lpCmdLine, "-d3d");
+ if (d3dinfo) {
+ int n = d3dinfo[4] - '0';
+
+ if (n >= 0 && n <= 5)
+ VD3D_describe_things = n;
+
+ Print(" D3D Info Level: %d\n", VD3D_describe_things);
+ }
+ else {
+ VD3D_describe_things = 0;
+ }
+
+
+ // FREE VERSION - AUTHORIZATION DISABLED
+ /*
+ ::Print(" Checking authorization codes...\n");
+ if (!Authorization::IsUserAuthorized()) {
+ if (!DataLoader::GetLoader()) {
+ DataLoader::Initialize();
+ DataLoader::GetLoader()->EnableDatafile("content.dat");
+ }
+
+ Game* game = new Game();
+ game->InitContent();
+
+ MessageBox(0, FormatTextEscape(Game::GetText("main.auth-invalid")).data(),
+ Game::GetText("main.title.error").data(), MB_OK);
+ ::Print(" Not authorized.\n");
+
+ delete game;
+ DataLoader::Close();
+ }
+ else {
+ ::Print(" Authorized\n");
+ */
+ try {
+ NetLayer net;
+
+ if (do_server) {
+ StarServer* server = new(__FILE__,__LINE__) StarServer();
+
+ if (server->Init(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
+ result = server->Run();
+
+ Print("\n+====================================================================+\n");
+ Print(" Begin Shutdown...\n");
+
+ delete server;
+ }
+
+ else {
+ Starshatter* stars = 0;
+
+ stars = new(__FILE__,__LINE__) Starshatter;
+ stars->SetTestMode(test_mode);
+
+ if (stars->Init(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
+ result = stars->Run();
+
+ Print("\n+====================================================================+\n");
+ Print(" Begin Shutdown...\n");
+
+ delete stars;
+ }
+
+ Token::close();
+
+ if (*Game::GetPanicMessage())
+ MessageBox(0, Game::GetPanicMessage(), "Starshatter - Error", MB_OK);
+ }
+
+ catch (const char* msg) {
+ Print(" FATAL EXCEPTION: '%s'\n", msg);
+ }
+ /* } */
+
+ Memory::Stats();
+ Memory::DumpLeaks();
+ Memory::CloseLog();
+
+ Print("+====================================================================+\n");
+ Print(" END OF LINE.\n");
+
+ fclose(ErrLog);
+
+ return result;
+}
+
+
diff --git a/Stars45/MapView.cpp b/Stars45/MapView.cpp
new file mode 100644
index 0000000..1c65d6e
--- /dev/null
+++ b/Stars45/MapView.cpp
@@ -0,0 +1,3478 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MapView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Star Map class
+*/
+
+#include "MemDebug.h"
+#include "MapView.h"
+
+#include "Galaxy.h"
+#include "StarSystem.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Instruction.h"
+#include "Element.h"
+#include "NavAI.h"
+#include "Weapon.h"
+#include "Sim.h"
+#include "Mission.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "Contact.h"
+#include "MenuView.h"
+
+#include "NetLobby.h"
+#include "NetUtil.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "EventDispatch.h"
+#include "Video.h"
+#include "Button.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "FontMgr.h"
+#include "Mouse.h"
+#include "FormatUtil.h"
+#include "Menu.h"
+
+// +--------------------------------------------------------------------+
+
+// Supported Selection Modes:
+
+const int SELECT_NONE = -1;
+const int SELECT_SYSTEM = 0;
+const int SELECT_PLANET = 1;
+const int SELECT_REGION = 2;
+const int SELECT_STATION = 3;
+const int SELECT_STARSHIP = 4;
+const int SELECT_FIGHTER = 5;
+const int SELECT_NAVPT = 6;
+
+const int VIEW_GALAXY = 0;
+const int VIEW_SYSTEM = 1;
+const int VIEW_REGION = 2;
+
+// +--------------------------------------------------------------------+
+
+MapView::MapView(Window* win)
+ : View(win)
+ , system(0), zoom(1.1), offset_x(0), offset_y(0), ship(0), campaign(0)
+ , captured(false), dragging(false), adding_navpt(false)
+ , moving_navpt(false), moving_elem(false)
+ , view_mode(VIEW_SYSTEM), seln_mode(SELECT_REGION), ship_filter(0xffffffff)
+ , current_star(0), current_planet(0), current_region(0)
+ , current_ship(0), current_elem(0), current_navpt(0), mission(0)
+ , scrolling(0), scroll_x(0), scroll_y(0), click_x(0), click_y(0)
+ , active_menu(0), map_menu(0), map_system_menu(0), map_sector_menu(0)
+ , ship_menu(0), editor(false)
+ , nav_menu(0), action_menu(0), objective_menu(0), formation_menu(0), speed_menu(0)
+ , hold_menu(0), farcast_menu(0), menu_view(0)
+{
+ for (int i = 0; i < 3; i++) {
+ view_zoom[i] = zoom;
+ view_offset_x[i] = offset_x;
+ view_offset_y[i] = offset_y;
+ }
+
+ menu_view = new(__FILE__,__LINE__) MenuView(window);
+
+ title_font = FontMgr::Find("Limerick12");
+ font = FontMgr::Find("Verdana");
+
+ active_window = (ActiveWindow*) window;
+
+ if (active_window)
+ active_window->AddView(this);
+}
+
+// +--------------------------------------------------------------------+
+
+MapView::~MapView()
+{
+ ClearMenu();
+ galaxy_image.ClearImage();
+
+ delete menu_view;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::OnWindowMove()
+{
+ if (menu_view)
+ menu_view->OnWindowMove();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::SetGalaxy(List<StarSystem>& g)
+{
+ system_list.clear();
+ system_list.append(g);
+
+ if (system_list.size() > 0) {
+ SetSystem(system_list[0]);
+ }
+}
+
+void
+MapView::SetSystem(StarSystem* s)
+{
+ if (system != s) {
+ system = s;
+
+ // forget invalid selection:
+ current_star = 0;
+ current_planet = 0;
+ current_region = 0;
+ current_ship = 0;
+ current_elem = 0;
+ current_navpt = 0;
+
+ // flush old object pointers:
+ stars.clear();
+ planets.clear();
+ regions.clear();
+
+ // insert objects from star system:
+ if (system) {
+ ListIter<OrbitalBody> star = system->Bodies();
+ while (++star) {
+ switch (star->Type()) {
+ case Orbital::STAR: stars.append(star.value());
+ break;
+ case Orbital::PLANET:
+ case Orbital::MOON: planets.append(star.value());
+ break;
+ }
+ }
+
+ ListIter<OrbitalBody> planet = star->Satellites();
+ while (++planet) {
+ planets.append(planet.value());
+
+ ListIter<OrbitalBody> moon = planet->Satellites();
+ while (++moon) {
+ planets.append(moon.value());
+ }
+ }
+
+ ListIter<OrbitalRegion> rgn = system->AllRegions();
+ while (++rgn)
+ regions.append(rgn.value());
+
+ // sort region list by distance from the star:
+ regions.sort();
+ }
+
+ BuildMenu();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::SetShip(Ship* s)
+{
+ if (ship != s) {
+ ship = s;
+
+ // forget invalid selection:
+ current_star = 0;
+ current_planet = 0;
+ current_region = 0;
+ current_ship = 0;
+ current_elem = 0;
+ current_navpt = 0;
+
+ if (ship && system_list.size() > 0) {
+ SimRegion* rgn = ship->GetRegion();
+
+ if (rgn && rgn->System()) {
+ system = 0;
+ SetSystem(rgn->System());
+ }
+ }
+
+ BuildMenu();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::SetMission(Mission* m)
+{
+ if (mission != m) {
+ mission = m;
+
+ // forget invalid selection:
+ current_star = 0;
+ current_planet = 0;
+ current_region = 0;
+ current_ship = 0;
+ current_elem = 0;
+ current_navpt = 0;
+
+ if (mission && system_list.size() > 0) {
+ system = 0;
+ SetSystem(mission->GetStarSystem());
+ }
+
+ BuildMenu();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::SetCampaign(Campaign* c)
+{
+ if (campaign != c) {
+ campaign = c;
+
+ // forget invalid selection:
+ current_star = 0;
+ current_planet = 0;
+ current_region = 0;
+ current_ship = 0;
+ current_elem = 0;
+ current_navpt = 0;
+
+ if (campaign)
+ SetGalaxy(campaign->GetSystemList());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+enum MapView_MENU {
+ MAP_SYSTEM = 1000,
+ MAP_SECTOR = 2000,
+ MAP_SHIP = 3000,
+ MAP_NAV = 4000,
+ MAP_ADDNAV = 4001,
+ MAP_DELETE = 4002,
+ MAP_CLEAR = 4003,
+ MAP_ACTION = 5000,
+ MAP_FORMATION = 6000,
+ MAP_SPEED = 7000,
+ MAP_HOLD = 8000,
+ MAP_FARCAST = 8500,
+ MAP_OBJECTIVE = 9000
+};
+
+void
+MapView::BuildMenu()
+{
+ ClearMenu();
+
+ map_system_menu = new(__FILE__,__LINE__) Menu(Game::GetText("MapView.menu.STARSYSTEM"));
+
+ if (system_list.size() > 0) {
+ int i = 0;
+ ListIter<StarSystem> iter = system_list;
+ while (++iter) {
+ StarSystem* s = iter.value();
+ map_system_menu->AddItem(s->Name(), MAP_SYSTEM + i);
+ i++;
+ }
+ }
+
+ else if (system) {
+ map_system_menu->AddItem(system->Name(), MAP_SYSTEM);
+ }
+
+ map_sector_menu = new(__FILE__,__LINE__) Menu(Game::GetText("MapView.menu.SECTOR"));
+ for (int i = 0; i < regions.size(); i++) {
+ Orbital* rgn = regions[i];
+ map_sector_menu->AddItem(rgn->Name(), MAP_SECTOR + i);
+ }
+
+ map_menu = new(__FILE__,__LINE__) Menu(Game::GetText("MapView.menu.MAP"));
+ map_menu->AddMenu("System", map_system_menu);
+ map_menu->AddMenu("Sector", map_sector_menu);
+
+ if (ship || mission) {
+ ship_menu = new(__FILE__,__LINE__) Menu(Game::GetText("MapView.menu.SHIP"));
+ ship_menu->AddMenu(Game::GetText("MapView.item.Starsystem"), map_system_menu);
+ ship_menu->AddMenu(Game::GetText("MapView.item.Sector"), map_sector_menu);
+
+ ship_menu->AddItem("", 0);
+ ship_menu->AddItem(Game::GetText("MapView.item.Add-Nav"), MAP_ADDNAV);
+ ship_menu->AddItem(Game::GetText("MapView.item.Clear-All"), MAP_CLEAR);
+
+ action_menu = new(__FILE__,__LINE__) Menu(Game::GetText("MapView.menu.ACTION"));
+ for (i = 0; i < Instruction::NUM_ACTIONS; i++) {
+ action_menu->AddItem(Game::GetText(Text("MapView.item.") + Instruction::ActionName(i)), MAP_ACTION + i);
+ }
+
+ formation_menu = new(__FILE__,__LINE__) Menu(Game::GetText("MapView.menu.FORMATION"));
+ for (i = 0; i < Instruction::NUM_FORMATIONS; i++) {
+ formation_menu->AddItem(Game::GetText(Text("MapView.item.") + Instruction::FormationName(i)), MAP_FORMATION + i);
+ }
+
+ speed_menu = new(__FILE__,__LINE__) Menu(Game::GetText("MapView.menu.SPEED"));
+ speed_menu->AddItem("250", MAP_SPEED + 0);
+ speed_menu->AddItem("500", MAP_SPEED + 1);
+ speed_menu->AddItem("750", MAP_SPEED + 2);
+ speed_menu->AddItem("1000", MAP_SPEED + 3);
+
+ hold_menu = new(__FILE__,__LINE__) Menu(Game::GetText("MapView.menu.HOLD"));
+ hold_menu->AddItem(Game::GetText("MapView.item.None"), MAP_HOLD + 0);
+ hold_menu->AddItem(Game::GetText("MapView.item.1-Minute"), MAP_HOLD + 1);
+ hold_menu->AddItem(Game::GetText("MapView.item.5-Minutes"), MAP_HOLD + 2);
+ hold_menu->AddItem(Game::GetText("MapView.item.10-Minutes"), MAP_HOLD + 3);
+ hold_menu->AddItem(Game::GetText("MapView.item.15-Minutes"), MAP_HOLD + 4);
+
+ farcast_menu = new(__FILE__,__LINE__) Menu(Game::GetText("MapView.menu.FARCAST"));
+ farcast_menu->AddItem(Game::GetText("MapView.item.Use-Quantum"), MAP_FARCAST + 0);
+ farcast_menu->AddItem(Game::GetText("MapView.item.Use-Farcast"), MAP_FARCAST + 1);
+
+ objective_menu = new(__FILE__,__LINE__) Menu(Game::GetText("MapView.menu.OBJECTIVE"));
+
+ nav_menu = new(__FILE__,__LINE__) Menu(Game::GetText("MapView.menu.NAVPT"));
+ nav_menu->AddMenu(Game::GetText("MapView.item.Action"), action_menu);
+ nav_menu->AddMenu(Game::GetText("MapView.item.Objective"), objective_menu);
+ nav_menu->AddMenu(Game::GetText("MapView.item.Formation"), formation_menu);
+ nav_menu->AddMenu(Game::GetText("MapView.item.Speed"), speed_menu);
+ nav_menu->AddMenu(Game::GetText("MapView.item.Hold"), hold_menu);
+ nav_menu->AddMenu(Game::GetText("MapView.item.Farcast"), farcast_menu);
+ nav_menu->AddItem("", 0);
+ nav_menu->AddItem(Game::GetText("MapView.item.Add-Nav"), MAP_ADDNAV);
+ nav_menu->AddItem(Game::GetText("MapView.item.Del-Nav"), MAP_DELETE);
+ }
+
+ else if (campaign) {
+ ship_menu = 0;
+ speed_menu = 0;
+ hold_menu = 0;
+ farcast_menu = 0;
+ objective_menu = 0;
+ formation_menu = 0;
+ nav_menu = 0;
+ }
+
+ active_menu = map_menu;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::ClearMenu()
+{
+ delete map_menu;
+ delete map_system_menu;
+ delete map_sector_menu;
+ delete ship_menu;
+ delete nav_menu;
+ delete action_menu;
+ delete objective_menu;
+ delete formation_menu;
+ delete speed_menu;
+ delete hold_menu;
+ delete farcast_menu;
+
+ map_menu = 0;
+ map_system_menu = 0;
+ map_sector_menu = 0;
+ ship_menu = 0;
+ nav_menu = 0;
+ action_menu = 0;
+ objective_menu = 0;
+ formation_menu = 0;
+ speed_menu = 0;
+ hold_menu = 0;
+ farcast_menu = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::ProcessMenuItem(int action)
+{
+ bool send_nav_data = false;
+ bool can_command = true;
+
+ if (ship && current_ship && ship != current_ship) {
+ if (ship->GetElement() && current_ship->GetElement()) {
+ if (!ship->GetElement()->CanCommand(current_ship->GetElement())) {
+ can_command = false;
+ }
+ }
+ }
+
+ else if (current_elem && NetLobby::GetInstance()) {
+ can_command = false;
+ }
+
+ if (action >= MAP_OBJECTIVE) {
+ int index = action - MAP_OBJECTIVE;
+
+ if (current_navpt && can_command) {
+ current_navpt->SetTarget(objective_menu->GetItem(index)->GetText());
+ send_nav_data = true;
+ }
+ }
+
+ else if (action >= MAP_FARCAST) {
+ if (current_navpt && can_command) {
+ current_navpt->SetFarcast(action - MAP_FARCAST);
+ send_nav_data = true;
+ }
+ }
+
+ else if (action >= MAP_HOLD) {
+ int hold_time = 0;
+ switch (action) {
+ default:
+ case MAP_HOLD + 0: hold_time = 0; break;
+ case MAP_HOLD + 1: hold_time = 60; break;
+ case MAP_HOLD + 2: hold_time = 300; break;
+ case MAP_HOLD + 3: hold_time = 600; break;
+ case MAP_HOLD + 4: hold_time = 900; break;
+ }
+
+ if (current_navpt && can_command) {
+ current_navpt->SetHoldTime(hold_time);
+ send_nav_data = true;
+ }
+ }
+
+ else if (action >= MAP_SPEED) {
+ if (current_navpt && can_command) {
+ current_navpt->SetSpeed((action - MAP_SPEED + 1) * 250);
+ send_nav_data = true;
+ }
+ }
+
+ else if (action >= MAP_FORMATION) {
+ if (current_navpt && can_command) {
+ current_navpt->SetFormation(action - MAP_FORMATION);
+ send_nav_data = true;
+ }
+ }
+
+ else if (action >= MAP_ACTION) {
+ if (current_navpt && can_command) {
+ current_navpt->SetAction(action - MAP_ACTION);
+ SelectNavpt(current_navpt);
+ send_nav_data = true;
+ }
+ }
+
+ else if (action == MAP_ADDNAV) {
+ Text rgn_name = regions[current_region]->Name();
+ Instruction* prior = current_navpt;
+ Instruction* n = 0;
+
+ if (current_ship && can_command) {
+ Sim* sim = Sim::GetSim();
+ SimRegion* rgn = sim->FindRegion(rgn_name);
+ Point init_pt;
+
+ if (rgn) {
+ if (rgn->IsAirSpace())
+ init_pt.z = 10e3;
+
+ n = new(__FILE__,__LINE__) Instruction(rgn, init_pt);
+ }
+ else {
+ n = new(__FILE__,__LINE__) Instruction(rgn_name, init_pt);
+ }
+
+ n->SetSpeed(500);
+
+ if (prior) {
+ n->SetAction(prior->Action());
+ n->SetFormation(prior->Formation());
+ n->SetSpeed(prior->Speed());
+ n->SetTarget(prior->GetTarget());
+ }
+
+ current_ship->AddNavPoint(n, prior);
+ }
+
+ else if (current_elem && can_command) {
+ Point init_pt;
+
+ if (regions[current_region]->Type() == Orbital::TERRAIN)
+ init_pt.z = 10e3;
+
+ n = new(__FILE__,__LINE__) Instruction(rgn_name, init_pt);
+ n->SetSpeed(500);
+
+ if (prior) {
+ n->SetAction(prior->Action());
+ n->SetFormation(prior->Formation());
+ n->SetSpeed(prior->Speed());
+ n->SetTarget(prior->GetTarget());
+ }
+
+ current_elem->AddNavPoint(n, prior);
+ }
+
+ if (can_command) {
+ current_navpt = n;
+ current_status = Instruction::PENDING;
+ adding_navpt = true;
+ captured = SetCapture();
+ }
+ }
+
+ else if (action == MAP_DELETE) {
+ if (current_navpt && can_command) {
+ if (current_ship)
+ current_ship->DelNavPoint(current_navpt);
+ else if (current_elem && can_command)
+ current_elem->DelNavPoint(current_navpt);
+
+ SelectNavpt(0);
+ }
+ }
+
+ else if (action == MAP_CLEAR) {
+ if (current_ship && can_command)
+ current_ship->ClearFlightPlan();
+ else if (current_elem && can_command)
+ current_elem->ClearFlightPlan();
+
+ SelectNavpt(0);
+ }
+
+ else if (action >= MAP_NAV) {
+ }
+
+ else if (action >= MAP_SHIP) {
+ }
+
+ else if (action >= MAP_SECTOR) {
+ int index = action - MAP_SECTOR;
+
+ if (index < regions.size())
+ current_region = index;
+
+ if (view_mode == VIEW_SYSTEM) {
+ Orbital* s = regions[current_region];
+ SetupScroll(s);
+ }
+ }
+
+ else if (system_list.size() > 0 && action >= MAP_SYSTEM) {
+ int index = action - MAP_SYSTEM;
+
+ if (index < system_list.size())
+ SetSystem(system_list[index]);
+ }
+
+ else {
+ }
+
+ Sim* sim = Sim::GetSim();
+ if (send_nav_data && sim) {
+ Ship* s = current_ship;
+
+ if (s && s->GetElement()) {
+ Element* elem = s->GetElement();
+ int index = elem->GetNavIndex(current_navpt);
+
+ if (index >= 0)
+ NetUtil::SendNavData(false, elem, index-1, current_navpt);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+MapView::SetCapture()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch)
+ return dispatch->CaptureMouse(this) ? true : false;
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+MapView::ReleaseCapture()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch)
+ return dispatch->ReleaseMouse(this) ? true : false;
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::SetViewMode(int mode)
+{
+ if (mode >= 0 && mode < 3) {
+ // save state:
+ view_zoom[view_mode] = zoom;
+ view_offset_x[view_mode] = offset_x;
+ view_offset_y[view_mode] = offset_y;
+
+ // switch mode:
+ view_mode = mode;
+
+ // restore state:
+
+ if (view_mode == VIEW_GALAXY) {
+ zoom = 1;
+ offset_x = 0;
+ offset_y = 0;
+ }
+ else {
+ zoom = view_zoom[view_mode];
+ offset_x = view_offset_x[view_mode];
+ offset_y = view_offset_y[view_mode];
+ }
+
+ scrolling = 0;
+ scroll_x = 0;
+ scroll_y = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+MapView::Update(SimObject* obj)
+{
+ if (obj == current_ship) {
+ current_ship = 0;
+ active_menu = map_menu;
+ }
+
+ return SimObserver::Update(obj);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::SelectShip(Ship* selship)
+{
+ if (selship != current_ship) {
+ current_ship = selship;
+
+ if (current_ship) {
+ if (current_ship->Life() == 0 || current_ship->IsDying() || current_ship->IsDead()) {
+ current_ship = 0;
+ }
+ else {
+ Observe(current_ship);
+ }
+ }
+ }
+
+ SelectNavpt(0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::SelectElem(MissionElement* elem)
+{
+ if (elem != current_elem) {
+ current_elem = elem;
+
+ if (current_elem) {
+ if (current_elem->IsStarship()) {
+ ship_menu->GetItem(3)->SetEnabled(true);
+ }
+
+ else if (current_elem->IsDropship()) {
+ ship_menu->GetItem(3)->SetEnabled(true);
+ }
+
+ else {
+ ship_menu->GetItem(3)->SetEnabled(false);
+ }
+ }
+ }
+
+ SelectNavpt(0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::SelectNavpt(Instruction* navpt)
+{
+ current_navpt = navpt;
+
+ if (current_navpt) {
+ current_status = current_navpt->Status();
+
+ List<Text> ships;
+ objective_menu->ClearItems();
+
+ switch (current_navpt->Action()) {
+ case Instruction::VECTOR:
+ case Instruction::LAUNCH:
+ case Instruction::PATROL:
+ case Instruction::SWEEP:
+ case Instruction::RECON:
+ objective_menu->AddItem(Game::GetText("MapView.item.not-available"), 0);
+ objective_menu->GetItem(0)->SetEnabled(false);
+ break;
+
+ case Instruction::DOCK:
+ FindShips(true, true, true, false, ships);
+ break;
+
+ case Instruction::DEFEND:
+ FindShips(true, true, true, false, ships);
+ break;
+
+ case Instruction::ESCORT:
+ FindShips(true, false, true, true, ships);
+ break;
+
+ case Instruction::INTERCEPT:
+ FindShips(false, false, false, true, ships);
+ break;
+
+ case Instruction::ASSAULT:
+ FindShips(false, false, true, false, ships);
+ break;
+
+ case Instruction::STRIKE:
+ FindShips(false, true, false, false, ships);
+ break;
+ }
+
+ for (int i = 0; i < ships.size(); i++)
+ objective_menu->AddItem(ships[i]->data(), MAP_OBJECTIVE + i);
+
+ ships.destroy();
+ }
+ else {
+ objective_menu->ClearItems();
+ objective_menu->AddItem(Game::GetText("MapView.item.not-available"), 0);
+ objective_menu->GetItem(0)->SetEnabled(false);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::FindShips(bool friendly, bool station, bool starship, bool dropship,
+ List<Text>& result)
+{
+ if (mission) {
+ for (int i = 0; i < mission->GetElements().size(); i++) {
+ MissionElement* elem = mission->GetElements().at(i);
+
+ if (elem->IsSquadron()) continue;
+ if (!station && elem->IsStatic()) continue;
+ if (!starship && elem->IsStarship()) continue;
+ if (!dropship && elem->IsDropship()) continue;
+
+ if (!editor && friendly && elem->GetIFF() > 0 && elem->GetIFF() != mission->Team())
+ continue;
+
+ if (!editor && !friendly && (elem->GetIFF() == 0 || elem->GetIFF() == mission->Team()))
+ continue;
+
+ result.append(new(__FILE__,__LINE__) Text(elem->Name()));
+ }
+ }
+
+ else if (ship) {
+ Sim* sim = Sim::GetSim();
+
+ if (sim) {
+ for (int r = 0; r < sim->GetRegions().size(); r++) {
+ SimRegion* rgn = sim->GetRegions().at(r);
+
+ for (int i = 0; i < rgn->Ships().size(); i++) {
+ Ship* s = rgn->Ships().at(i);
+
+ if (!station && s->IsStatic()) continue;
+ if (!starship && s->IsStarship()) continue;
+ if (!dropship && s->IsDropship()) continue;
+
+ if (friendly && s->GetIFF() > 0 && s->GetIFF() != ship->GetIFF())
+ continue;
+
+ if (!friendly && (s->GetIFF() == 0 || s->GetIFF() == ship->GetIFF()))
+ continue;
+
+ result.append(new(__FILE__,__LINE__) Text(s->Name()));
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::SetSelectionMode(int mode)
+{
+ if (mode <= SELECT_NONE) {
+ seln_mode = SELECT_NONE;
+ return;
+ }
+
+ if (mode != seln_mode && mode <= SELECT_FIGHTER) {
+ seln_mode = mode;
+
+ // when changing mode,
+ // select the item closest to the current center:
+ if (system && view_mode == VIEW_SYSTEM)
+ SelectAt(rect.x + rect.w/2,
+ rect.y + rect.h/2);
+ }
+}
+
+void
+MapView::SetSelection(int index)
+{
+ if (scrolling) return;
+ Orbital* s = 0;
+
+ switch (seln_mode) {
+ case SELECT_SYSTEM:
+ if (index < system_list.size())
+ SetSystem(system_list[index]);
+ s = stars[current_star];
+ break;
+
+ default:
+ case SELECT_PLANET:
+ if (index < planets.size())
+ current_planet = index;
+ s = planets[current_planet];
+ break;
+
+ case SELECT_REGION:
+ if (index < regions.size())
+ current_region = index;
+ s = regions[current_region];
+ break;
+
+ case SELECT_STATION:
+ {
+ if (mission) {
+ MissionElement* selected_elem = 0;
+
+ ListIter<MissionElement> elem = mission->GetElements();
+ while (++elem) {
+ if (elem->IsStatic()) {
+ if (elem->Identity() == index) {
+ selected_elem = elem.value();
+ break;
+ }
+ }
+ }
+
+ SelectElem(selected_elem);
+
+ if (selected_elem && regions.size()) {
+ ListIter<Orbital> rgn = regions;
+ while (++rgn) {
+ if (!stricmp(selected_elem->Region(), rgn->Name())) {
+ Orbital* elem_region = rgn.value();
+ current_region = regions.index(elem_region);
+ }
+ }
+ }
+ }
+
+ else {
+ Ship* selship = 0;
+
+ if (ship) {
+ SimRegion* simrgn = ship->GetRegion();
+ if (simrgn) {
+ ListIter<Ship> s = simrgn->Ships();
+ while (++s) {
+ if (s->IsStatic()) {
+ if (s->Identity() == index) {
+ selship = s.value();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ SelectShip(selship);
+
+ if (selship) {
+ s = selship->GetRegion()->GetOrbitalRegion();
+ current_region = regions.index(s);
+ }
+ }
+ }
+ break;
+
+ case SELECT_STARSHIP:
+ {
+ if (mission) {
+ MissionElement* selected_elem = 0;
+
+ ListIter<MissionElement> elem = mission->GetElements();
+ while (++elem) {
+ if (elem->IsStarship()) {
+ if (elem->Identity() == index) {
+ selected_elem = elem.value();
+ break;
+ }
+ }
+ }
+
+ SelectElem(selected_elem);
+
+ if (selected_elem && regions.size()) {
+ ListIter<Orbital> rgn = regions;
+ while (++rgn) {
+ if (!stricmp(selected_elem->Region(), rgn->Name())) {
+ Orbital* elem_region = rgn.value();
+ current_region = regions.index(elem_region);
+ }
+ }
+ }
+ }
+
+ else {
+ Ship* selship = 0;
+
+ if (ship) {
+ SimRegion* simrgn = ship->GetRegion();
+ if (simrgn) {
+ ListIter<Ship> s = simrgn->Ships();
+ while (++s) {
+ if (s->IsStarship()) {
+ if (s->Identity() == index) {
+ selship = s.value();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ SelectShip(selship);
+
+ if (selship) {
+ s = selship->GetRegion()->GetOrbitalRegion();
+ current_region = regions.index(s);
+ }
+ }
+ }
+ break;
+
+ case SELECT_FIGHTER:
+ {
+ if (mission) {
+ MissionElement* selected_elem = 0;
+
+ ListIter<MissionElement> elem = mission->GetElements();
+ while (++elem) {
+ if (elem->IsDropship() && !elem->IsSquadron()) {
+ if (elem->Identity() == index) {
+ selected_elem = elem.value();
+ break;
+ }
+ }
+ }
+
+ SelectElem(selected_elem);
+
+ if (selected_elem && regions.size()) {
+ ListIter<Orbital> rgn = regions;
+ while (++rgn) {
+ if (!stricmp(selected_elem->Region(), rgn->Name())) {
+ Orbital* elem_region = rgn.value();
+ current_region = regions.index(elem_region);
+ }
+ }
+ }
+ }
+
+ else {
+ Ship* selship = 0;
+
+ if (ship) {
+ SimRegion* simrgn = ship->GetRegion();
+ if (simrgn) {
+ ListIter<Ship> s = simrgn->Ships();
+ while (++s) {
+ if (s->IsDropship()) {
+ if (s->Identity() == index) {
+ selship = s.value();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ SelectShip(selship);
+
+ if (selship) {
+ s = selship->GetRegion()->GetOrbitalRegion();
+ current_region = regions.index(s);
+ }
+ }
+ }
+ break;
+ }
+
+ SetupScroll(s);
+}
+
+void
+MapView::SetSelectedShip(Ship* ship)
+{
+ if (scrolling) return;
+ Orbital* s = 0;
+ Ship* selship = 0;
+
+
+ switch (seln_mode) {
+ case SELECT_SYSTEM:
+ case SELECT_PLANET:
+ case SELECT_REGION:
+ default:
+ break;
+
+ case SELECT_STATION:
+ case SELECT_STARSHIP:
+ case SELECT_FIGHTER:
+ {
+ if (ship) {
+ SimRegion* simrgn = ship->GetRegion();
+
+ if (simrgn && simrgn->NumShips()) {
+ selship = simrgn->Ships().find(ship);
+ }
+ }
+
+ SelectShip(selship);
+ }
+ break;
+ }
+
+ if (selship)
+ SetupScroll(s);
+}
+
+void
+MapView::SetSelectedElem(MissionElement* elem)
+{
+ if (scrolling) return;
+ Orbital* s = 0;
+
+ switch (seln_mode) {
+ case SELECT_SYSTEM:
+ case SELECT_PLANET:
+ case SELECT_REGION:
+ default:
+ break;
+
+ case SELECT_STATION:
+ case SELECT_STARSHIP:
+ case SELECT_FIGHTER:
+ {
+ SelectElem(elem);
+ }
+ break;
+ }
+
+ if (current_elem)
+ SetupScroll(s);
+}
+
+void
+MapView::SetupScroll(Orbital* s)
+{
+ switch (view_mode) {
+ case VIEW_GALAXY:
+ zoom = 1;
+ offset_x = 0;
+ offset_y = 0;
+ scrolling = 0;
+ break;
+
+ case VIEW_SYSTEM:
+ if (s == 0) {
+ offset_x = 0;
+ offset_y = 0;
+ scrolling = 0;
+ }
+ else {
+ scroll_x = (offset_x + s->Location().x) / 5.0;
+ scroll_y = (offset_y + s->Location().y) / 5.0;
+ scrolling = 5;
+ }
+ break;
+
+ case VIEW_REGION:
+ if (current_navpt) {
+ // don't move the map
+ scrolling = 0;
+ }
+ else if (current_ship) {
+ Point sloc = current_ship->Location().OtherHand();
+
+ if (!IsVisible(sloc)) {
+ scroll_x = (offset_x + sloc.x) / 5.0;
+ scroll_y = (offset_y + sloc.y) / 5.0;
+ scrolling = 5;
+ }
+ else {
+ scroll_x = 0;
+ scroll_y = 0;
+ scrolling = 0;
+ }
+ }
+ else if (current_elem) {
+ Point sloc = current_elem->Location();
+
+ if (!IsVisible(sloc)) {
+ scroll_x = (offset_x + sloc.x) / 5.0;
+ scroll_y = (offset_y + sloc.y) / 5.0;
+ scrolling = 5;
+ }
+ else {
+ scroll_x = 0;
+ scroll_y = 0;
+ scrolling = 0;
+ }
+ }
+ else {
+ offset_x = 0;
+ offset_y = 0;
+ scrolling = 0;
+ }
+ break;
+ }
+}
+
+bool
+MapView::IsVisible(const Point& loc)
+{
+ if (view_mode == VIEW_REGION) {
+ double scale = c/r;
+ double ox = offset_x * scale;
+ double oy = offset_y * scale;
+ double sx = loc.x * scale;
+ double sy = loc.y * scale;
+ double cx = rect.w/2;
+ double cy = rect.h/2;
+
+ int test_x = (int) (cx + sx + ox);
+ int test_y = (int) (cy + sy + oy);
+
+ bool visible = test_x >= 0 && test_x < rect.w &&
+ test_y >= 0 && test_y < rect.h;
+
+ return visible;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::SetRegion(OrbitalRegion* rgn)
+{
+ if (scrolling || rgn == 0) return;
+
+ int index = regions.index(rgn);
+
+ if (index < 0 || index == current_region || index >= regions.size())
+ return;
+
+ current_region = index;
+ Orbital* s = regions[current_region];
+
+ if (!s)
+ return;
+
+ switch (view_mode) {
+ case VIEW_GALAXY:
+ case VIEW_SYSTEM:
+ scroll_x = (offset_x + s->Location().x) / 5.0;
+ scroll_y = (offset_y + s->Location().y) / 5.0;
+ scrolling = 5;
+ break;
+
+ case VIEW_REGION:
+ offset_x = 0;
+ offset_y = 0;
+ scrolling = 0;
+ break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::SetRegionByName(const char* rgn_name)
+{
+ OrbitalRegion* rgn = 0;
+
+ for (int i = 0; i < regions.size(); i++) {
+ Orbital* r = regions[i];
+ if (!strcmp(rgn_name, r->Name())) {
+ rgn = (OrbitalRegion*) r;
+ break;
+ }
+ }
+
+ SetRegion(rgn);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::SelectAt(int x, int y)
+{
+ if (scrolling) return;
+ if (c == 0) return;
+ if (seln_mode < 0) return;
+
+ Orbital* s = 0;
+
+ double scale = r/c;
+ double cx = rect.w/2;
+ double cy = rect.h/2;
+ double test_x = (x - rect.x - cx) * scale - offset_x;
+ double test_y = (y - rect.y - cy) * scale - offset_y;
+ double dist = 1.0e20;
+ int closest = 0;
+
+ if (view_mode == VIEW_GALAXY) {
+ c = (cx>cy) ? cx : cy;
+ r = 10;
+
+ Galaxy* g = Galaxy::GetInstance();
+ if (g)
+ r = g->Radius();
+
+ StarSystem* closest_system = 0;
+
+ // draw the list of systems, and their connections:
+ ListIter<StarSystem> iter = system_list;
+ while (++iter) {
+ StarSystem* s = iter.value();
+
+ double dx = (s->Location().x - test_x);
+ double dy = (s->Location().y - test_y);
+ double d = sqrt(dx*dx + dy*dy);
+
+ if (d < dist) {
+ dist = d;
+ closest_system = s;
+ }
+ }
+
+ if (closest_system)
+ SetSystem(closest_system);
+ }
+
+ else if (view_mode == VIEW_SYSTEM) {
+ switch (seln_mode) {
+ case SELECT_SYSTEM: {
+ if (stars.isEmpty()) return;
+ int index = 0;
+ ListIter<Orbital> star = stars;
+ while (++star) {
+ double dx = (star->Location().x - test_x);
+ double dy = (star->Location().y - test_y);
+ double d = sqrt(dx*dx + dy*dy);
+
+ if (d < dist) {
+ dist = d;
+ closest = index;
+ }
+
+ index++;
+ }
+
+ current_star = closest;
+ }
+ s = stars[current_star];
+ break;
+
+ case SELECT_PLANET: {
+ if (planets.isEmpty()) return;
+ int index = 0;
+ ListIter<Orbital> planet = planets;
+ while (++planet) {
+ double dx = (planet->Location().x - test_x);
+ double dy = (planet->Location().y - test_y);
+ double d = sqrt(dx*dx + dy*dy);
+
+ if (d < dist) {
+ dist = d;
+ closest = index;
+ }
+
+ index++;
+ }
+
+ current_planet = closest;
+ }
+ s = planets[current_planet];
+ break;
+
+ default:
+ case SELECT_REGION: {
+ if (regions.isEmpty()) return;
+ int index = 0;
+ ListIter<Orbital> region = regions;
+ while (++region) {
+ double dx = (region->Location().x - test_x);
+ double dy = (region->Location().y - test_y);
+ double d = sqrt(dx*dx + dy*dy);
+
+ if (d < dist) {
+ dist = d;
+ closest = index;
+ }
+
+ index++;
+ }
+
+ current_region = closest;
+ }
+ s = regions[current_region];
+ break;
+ }
+ }
+
+ else if (view_mode == VIEW_REGION) {
+ dist = 5.0e3;
+
+ if (mission) {
+ Orbital* rgn = regions[current_region];
+ MissionElement* sel_elem = 0;
+ Instruction* sel_nav = 0;
+
+ if (!rgn) return;
+
+ // check nav points:
+ ListIter<MissionElement> elem = mission->GetElements();
+ while (++elem) {
+ MissionElement* e = elem.value();
+
+ if (!e->IsSquadron() && (editor || e->GetIFF() == mission->Team())) {
+ ListIter<Instruction> navpt = e->NavList();
+ while (++navpt) {
+ Instruction* n = navpt.value();
+
+ if (!stricmp(n->RegionName(), rgn->Name())) {
+ Point nloc = n->Location();
+ double dx = nloc.x - test_x;
+ double dy = nloc.y - test_y;
+ double d = sqrt(dx*dx + dy*dy);
+
+ if (d < dist) {
+ dist = d;
+ sel_nav = n;
+ sel_elem = e;
+ }
+ }
+ }
+ }
+ }
+
+ if (sel_nav) {
+ SelectElem(sel_elem);
+ SelectNavpt(sel_nav);
+ }
+
+ // check elements:
+ else {
+ elem.reset();
+ while (++elem) {
+ MissionElement* e = elem.value();
+
+ if (e->Region() == rgn->Name() && !e->IsSquadron()) {
+ Point sloc = e->Location();
+ double dx = sloc.x - test_x;
+ double dy = sloc.y - test_y;
+ double d = sqrt(dx*dx + dy*dy);
+
+ if (d < dist) {
+ dist = d;
+ sel_elem = e;
+ }
+ }
+ }
+
+ SelectElem(sel_elem);
+
+ if (sel_elem)
+ s = rgn;
+ }
+ }
+ else if (ship) {
+ Sim* sim = Sim::GetSim();
+ Orbital* rgn = regions[current_region];
+ SimRegion* simrgn = 0;
+ Ship* sel_ship = 0;
+ Instruction* sel_nav = 0;
+
+ if (sim && rgn)
+ simrgn = sim->FindRegion(rgn->Name());
+
+ // check nav points:
+ if (simrgn) {
+ for (int r = 0; r < sim->GetRegions().size(); r++) {
+ SimRegion* simrgn = sim->GetRegions().at(r);
+
+ for (int i = 0; i < simrgn->Ships().size(); i++) {
+ Ship* s = simrgn->Ships().at(i);
+
+ if (s->GetIFF() == ship->GetIFF() && s->GetElementIndex() == 1) {
+ ListIter<Instruction> navpt = s->GetFlightPlan();
+ while (++navpt) {
+ Instruction* n = navpt.value();
+
+ if (!stricmp(n->RegionName(), rgn->Name())) {
+ Point nloc = n->Location();
+ double dx = nloc.x - test_x;
+ double dy = nloc.y - test_y;
+ double d = sqrt(dx*dx + dy*dy);
+
+ if (d < dist) {
+ dist = d;
+ sel_nav = n;
+ sel_ship = s;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (sel_nav) {
+ SelectShip(sel_ship);
+ SelectNavpt(sel_nav);
+ }
+
+ // check ships:
+ else if (simrgn->NumShips()) {
+ ListIter<Ship> ship = simrgn->Ships();
+ while (++ship) {
+ Ship* s = ship.value();
+
+ if (!IsClutter(*s)) {
+ Point sloc = s->Location().OtherHand();
+ double dx = sloc.x - test_x;
+ double dy = sloc.y - test_y;
+ double d = sqrt(dx*dx + dy*dy);
+
+ if (d < dist) {
+ dist = d;
+ sel_ship = s;
+ }
+ }
+ }
+
+ SelectShip(sel_ship);
+ }
+ else {
+ SelectShip(0);
+ SelectNavpt(0);
+ }
+ }
+ }
+
+ if (s)
+ SetupScroll(s);
+}
+
+// +--------------------------------------------------------------------+
+
+Orbital*
+MapView::GetSelection()
+{
+ Orbital* s = 0;
+
+ switch (seln_mode) {
+ case SELECT_SYSTEM:
+ if (current_star < stars.size())
+ s = stars[current_star];
+ break;
+
+ default:
+ case SELECT_PLANET:
+ if (current_planet < planets.size())
+ s = planets[current_planet];
+ break;
+
+ case SELECT_REGION:
+ if (current_region < regions.size())
+ s = regions[current_region];
+ break;
+
+ case SELECT_STATION:
+ case SELECT_STARSHIP:
+ case SELECT_FIGHTER:
+ break;
+ }
+
+ return s;
+}
+
+Ship*
+MapView::GetSelectedShip()
+{
+ return current_ship;
+}
+
+MissionElement*
+MapView::GetSelectedElem()
+{
+ return current_elem;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MapView::GetSelectionIndex()
+{
+ int s = 0;
+
+ switch (seln_mode) {
+ case SELECT_SYSTEM: s = current_star; break;
+ default:
+ case SELECT_PLANET: s = current_planet; break;
+ case SELECT_REGION: s = current_region; break;
+
+ case SELECT_STATION:
+ case SELECT_STARSHIP:
+ case SELECT_FIGHTER:
+ {
+ s = -1;
+ Sim* sim = Sim::GetSim();
+ Orbital* rgn = regions[current_region];
+ SimRegion* simrgn = 0;
+
+ if (sim && rgn)
+ simrgn = sim->FindRegion(rgn->Name());
+
+ if (simrgn) {
+ if (current_ship && simrgn->NumShips()) {
+ s = simrgn->Ships().index(current_ship);
+ }
+ }
+ }
+ break;
+ }
+
+ return s;
+}
+
+void
+MapView::DrawTabbedText(Font* font, const char* text)
+{
+ if (font && text && *text) {
+ Rect label_rect;
+
+ label_rect.w = rect.w;
+ label_rect.h = rect.h;
+
+ label_rect.Inset(8,8,8,8);
+
+ DWORD text_flags = DT_WORDBREAK | DT_LEFT;
+
+ active_window->SetFont(font);
+ active_window->DrawText(text, 0, label_rect, text_flags);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::Refresh()
+{
+ rect = window->GetRect();
+
+ if (!system) {
+ DrawGrid();
+ DrawTabbedText(title_font, Game::GetText("MapView.item.no-system"));
+ return;
+ }
+
+ if (font)
+ font->SetColor(Color::White);
+
+ if (scrolling) {
+ offset_x -= scroll_x;
+ offset_y -= scroll_y;
+ scrolling--;
+ }
+
+ rect.w -= 2;
+ rect.h -= 2;
+
+ switch (view_mode) {
+ case VIEW_GALAXY: DrawGalaxy(); break;
+ case VIEW_SYSTEM: DrawSystem(); break;
+ case VIEW_REGION: DrawRegion(); break;
+ default: DrawGrid(); break;
+ }
+
+ rect.w += 2;
+ rect.h += 2;
+
+ if (menu_view) {
+ if (current_navpt) {
+ active_menu = nav_menu;
+ }
+ else if (current_ship) {
+ if (current_ship->GetIFF() == ship->GetIFF())
+ active_menu = ship_menu;
+ else
+ active_menu = map_menu;
+ }
+ else if (current_elem) {
+ if (editor || current_elem->GetIFF() == mission->Team())
+ active_menu = ship_menu;
+ else
+ active_menu = map_menu;
+ }
+ else {
+ active_menu = map_menu;
+ }
+
+ menu_view->SetBackColor(Color::Gray);
+ menu_view->SetTextColor(Color::White);
+ menu_view->SetMenu(active_menu);
+ menu_view->DoMouseFrame();
+
+ if (menu_view->GetAction()) {
+ ProcessMenuItem(menu_view->GetAction());
+ }
+
+ menu_view->Refresh();
+ }
+
+ DrawTitle();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::DrawTitle()
+{
+ title_font->SetColor(active_window->GetForeColor());
+ DrawTabbedText(title_font, title);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::DrawGalaxy()
+{
+ title = Game::GetText("MapView.title.Galaxy");
+ DrawGrid();
+
+ double cx = rect.w/2;
+ double cy = rect.h/2;
+
+ c = (cx>cy) ? cx : cy;
+ r = 10; // * zoom;
+
+ Galaxy* g = Galaxy::GetInstance();
+ if (g)
+ r = g->Radius(); // * zoom;
+
+ double scale = c/r;
+ double ox = 0;
+ double oy = 0;
+
+ // compute offset:
+ ListIter<StarSystem> iter = system_list;
+ while (++iter) {
+ StarSystem* s = iter.value();
+
+ if (system == s) {
+ if (fabs(s->Location().x) > 10 || fabs(s->Location().y) > 10) {
+ int sx = (int) s->Location().x;
+ int sy = (int) s->Location().y;
+
+ sx -= sx % 10;
+ sy -= sy % 10;
+
+ ox = sx * -scale;
+ oy = sy * -scale;
+ }
+ }
+ }
+
+ // draw the list of systems, and their connections:
+ iter.reset();
+ while (++iter) {
+ StarSystem* s = iter.value();
+
+ int sx = (int) (cx + ox + s->Location().x * scale);
+ int sy = (int) (cy + oy + s->Location().y * scale);
+
+ if (sx < 4 || sx > rect.w-4 || sy < 4 || sy > rect.h-4)
+ continue;
+
+ window->DrawEllipse(sx-7, sy-7, sx+7, sy+7, Ship::IFFColor(s->Affiliation()));
+
+ if (s == system) {
+ window->DrawLine(0, sy, rect.w, sy, Color::Gray, Video::BLEND_ADDITIVE);
+ window->DrawLine(sx, 0, sx, rect.h, Color::Gray, Video::BLEND_ADDITIVE);
+ }
+
+ ListIter<StarSystem> iter2 = system_list;
+ while (++iter2) {
+ StarSystem* s2 = iter2.value();
+
+ if (s != s2 && s->HasLinkTo(s2)) {
+ int ax = sx;
+ int ay = sy;
+
+ int bx = (int) (cx + ox + s2->Location().x * scale);
+ int by = (int) (cy + oy + s2->Location().y * scale);
+
+ if (ax == bx) {
+ if (ay < by) {
+ ay += 8; by -= 8;
+ }
+ else {
+ ay -= 8; by += 8;
+ }
+ }
+
+ else if (ay == by) {
+ if (ax < bx) {
+ ax += 8; bx -= 8;
+ }
+ else {
+ ax -= 8; bx += 8;
+ }
+ }
+
+ else {
+ Point d = Point(bx, by, 0) - Point(ax, ay, 0);
+ d.Normalize();
+
+ ax += (int) (8 * d.x);
+ ay += (int) (8 * d.y);
+
+ bx -= (int) (8 * d.x);
+ by -= (int) (8 * d.y);
+ }
+
+ window->DrawLine(ax, ay, bx, by, Color(120,120,120), Video::BLEND_ADDITIVE);
+ }
+ }
+ }
+
+ // finally draw all the stars in the galaxy:
+ if (g) {
+ ListIter<Star> iter = g->Stars();
+ while (++iter) {
+ Star* s = iter.value();
+
+ int sx = (int) (cx + ox + s->Location().x * scale);
+ int sy = (int) (cy + oy + s->Location().y * scale);
+ int sr = s->GetSize();
+
+ if (sx < 4 || sx > rect.w-4 || sy < 4 || sy > rect.h-4)
+ continue;
+
+ window->FillEllipse(sx-sr, sy-sr, sx+sr, sy+sr, s->GetColor());
+
+ if (!strncmp(s->Name(), "GSC", 3))
+ font->SetColor(Color(100,100,100));
+ else
+ font->SetColor(Color::White);
+
+ Rect name_rect(sx-60, sy+8, 120, 20);
+ active_window->SetFont(font);
+ active_window->DrawText(s->Name(), 0, name_rect, DT_SINGLELINE | DT_CENTER);
+ }
+ }
+
+ font->SetColor(Color::White);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::DrawSystem()
+{
+ Text caption = Game::GetText("MapView.title.Starsystem");
+ caption += " ";
+ caption += system->Name();
+
+ if (current_ship) {
+ caption += "\n";
+ caption += Game::GetText("MapView.title.Ship");
+ caption += " ";
+ caption += current_ship->Name();
+ }
+ else if (current_elem) {
+ caption += "\n";
+ caption += Game::GetText("MapView.title.Ship");
+ caption += " ";
+ caption += current_elem->Name();
+ }
+
+ title = caption;
+
+ ListIter<OrbitalBody> star = system->Bodies();
+ while (++star) {
+ int p_orb = 1;
+
+ ListIter<OrbitalBody> planet = star->Satellites();
+ while (++planet) {
+ DrawOrbital(*planet, p_orb++);
+
+ int m_orb = 1;
+
+ ListIter<OrbitalBody> moon = planet->Satellites();
+ while (++moon) {
+ DrawOrbital(*moon, m_orb++);
+
+ ListIter<OrbitalRegion> region = moon->Regions();
+ while (++region) {
+ DrawOrbital(*region, 1);
+ }
+ }
+
+ ListIter<OrbitalRegion> region = planet->Regions();
+ while (++region) {
+ DrawOrbital(*region, 1);
+ }
+ }
+
+ ListIter<OrbitalRegion> region = star->Regions();
+ while (++region) {
+ DrawOrbital(*region, 1);
+ }
+
+ DrawOrbital(*star, 0);
+ }
+
+ char r_txt[32];
+ FormatNumber(r_txt, system->Radius() * zoom);
+ char resolution[64];
+ sprintf(resolution, "%s: %s", Game::GetText("MapView.info.Resolution").data(), r_txt);
+
+ active_window->SetFont(font);
+ active_window->DrawText(resolution, -1, Rect(4, 4, rect.w-8, 24), DT_SINGLELINE|DT_RIGHT);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::DrawRegion()
+{
+ OrbitalRegion* rgn = (OrbitalRegion*) regions[current_region];
+
+ Text caption = Game::GetText("MapView.title.Sector");
+ caption += " ";
+ caption += rgn->Name();
+
+ if (current_ship) {
+ caption += "\n";
+ caption += Game::GetText("MapView.title.Ship");
+ caption += " ";
+ caption += current_ship->Name();
+ }
+ else if (current_elem) {
+ caption += "\n";
+ caption += Game::GetText("MapView.title.Ship");
+ caption += " ";
+ caption += current_elem->Name();
+ }
+
+ title = caption;
+
+ double cx = rect.w/2;
+ double cy = rect.h/2;
+
+ int size = (int) rgn->Radius();
+ int step = (int) rgn->GridSpace();
+
+ c = (cx<cy) ? cx : cy;
+ r = rgn->Radius() * zoom;
+ double scale = c/r;
+
+ double ox = offset_x * scale;
+ double oy = offset_y * scale;
+
+ int left = (int) (-size * scale + ox + cx);
+ int right = (int) ( size * scale + ox + cx);
+ int top = (int) (-size * scale + oy + cy);
+ int bottom = (int) ( size * scale + oy + cy);
+
+ Color major(48,48,48);
+ Color minor(24,24,24);
+
+ int x,y;
+ int tick = 0;
+
+ for (x = 0; x <= size; x += step) {
+ int lx = (int) (x * scale + ox + cx);
+ if (!tick)
+ window->DrawLine(lx, top, lx, bottom, major, Video::BLEND_ADDITIVE);
+ else
+ window->DrawLine(lx, top, lx, bottom, minor, Video::BLEND_ADDITIVE);
+
+ lx = (int) (-x * scale + ox + cx);
+ if (!tick)
+ window->DrawLine(lx, top, lx, bottom, major, Video::BLEND_ADDITIVE);
+ else
+ window->DrawLine(lx, top, lx, bottom, minor, Video::BLEND_ADDITIVE);
+
+ if (++tick > 3) tick = 0;
+ }
+
+ tick = 0;
+
+ for (y = 0; y <= size; y += step) {
+ int ly = (int) (y * scale + oy + cy);
+ if (!tick)
+ window->DrawLine(left, ly, right, ly, major, Video::BLEND_ADDITIVE);
+ else
+ window->DrawLine(left, ly, right, ly, minor, Video::BLEND_ADDITIVE);
+
+ ly = (int) (-y * scale + oy + cy);
+ if (!tick)
+ window->DrawLine(left, ly, right, ly, major, Video::BLEND_ADDITIVE);
+ else
+ window->DrawLine(left, ly, right, ly, minor, Video::BLEND_ADDITIVE);
+
+ if (++tick > 3) tick = 0;
+ }
+
+ int rep = 3;
+ if (r > 70e3) rep = 2;
+ if (r > 250e3) rep = 1;
+
+ if (campaign && rgn) {
+ // draw the combatants in this region:
+ ListIter<Combatant> iter = campaign->GetCombatants();
+ while (++iter) {
+ Combatant* combatant = iter.value();
+ DrawCombatGroup(combatant->GetForce(), rep);
+ }
+ }
+
+ else if (mission && rgn) {
+ // draw the elements in this region:
+ ListIter<MissionElement> elem = mission->GetElements();
+ while (++elem)
+ if (!elem->IsSquadron())
+ DrawElem(*elem, (elem.value() == current_elem), rep);
+ }
+
+ else if (ship) {
+ // draw the ships in this region:
+ Sim* sim = Sim::GetSim();
+ SimRegion* simrgn = 0;
+
+ if (sim && rgn)
+ simrgn = sim->FindRegion(rgn->Name());
+
+ // this algorithm uses the allied track list for the region,
+ // even if this ship is in a different region. a previous
+ // version used the ship's own contact list, which only shows
+ // ships in the player's region. this way is less "realistic"
+ // but more fun for managing large battles.
+
+ if (simrgn) {
+ ListIter<Contact> c = simrgn->TrackList(ship->GetIFF());
+ while (++c) {
+ Contact* contact = c.value();
+ Ship* s = contact->GetShip();
+
+ if (s && (s->Class() & ship_filter) && !IsClutter(*s) && s != ship)
+ DrawShip(*s, (s == current_ship), rep);
+ }
+
+ ListIter<Ship> s_iter = simrgn->Ships();
+ while (++s_iter) {
+ Ship* s = s_iter.value();
+
+ if (s && (s->IsStatic()) && !IsClutter(*s) &&
+ (s->GetIFF() == ship->GetIFF() || s->GetIFF() == 0))
+ DrawShip(*s, (s == current_ship), rep);
+ }
+
+ // draw nav routes for allied ships not in the region:
+ ListIter<SimRegion> r_iter = sim->GetRegions();
+ while (++r_iter) {
+ SimRegion* r = r_iter.value();
+
+ if (r != simrgn) {
+ ListIter<Ship> s_iter = r->Ships();
+ while (++s_iter) {
+ Ship* s = s_iter.value();
+
+ if (s && !s->IsStatic() && !IsClutter(*s) &&
+ (s->GetIFF() == ship->GetIFF() || s->GetIFF() == 0)) {
+ DrawNavRoute(simrgn->GetOrbitalRegion(),
+ s->GetFlightPlan(),
+ s->MarkerColor(),
+ s, 0);
+ }
+ }
+ }
+ }
+ }
+
+ // draw our own ship:
+ DrawShip(*ship, (ship == current_ship), rep);
+ }
+
+ char r_txt[32];
+ FormatNumber(r_txt, r*2);
+ char resolution[64];
+ sprintf(resolution, "%s: %s", Game::GetText("MapView.info.Resolution").data(), r_txt);
+
+ active_window->SetFont(font);
+ active_window->DrawText(resolution, -1, Rect(4, 4, rect.w-8, 24), DT_SINGLELINE|DT_RIGHT);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::DrawGrid()
+{
+ int grid_step = rect.w/8;
+ int cx = rect.w/2;
+ int cy = rect.h/2;
+
+ Color c(32,32,32);
+
+ window->DrawLine(0, cy, rect.w, cy, c, Video::BLEND_ADDITIVE);
+ window->DrawLine(cx, 0, cx, rect.h, c, Video::BLEND_ADDITIVE);
+
+ for (int i = 1; i < 4; i++) {
+ window->DrawLine(0, cy + (i*grid_step), rect.w, cy + (i*grid_step), c, Video::BLEND_ADDITIVE);
+ window->DrawLine(0, cy - (i*grid_step), rect.w, cy - (i*grid_step), c, Video::BLEND_ADDITIVE);
+ window->DrawLine(cx + (i*grid_step), 0, cx + (i*grid_step), rect.h, c, Video::BLEND_ADDITIVE);
+ window->DrawLine(cx - (i*grid_step), 0, cx - (i*grid_step), rect.h, c, Video::BLEND_ADDITIVE);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::DrawOrbital(Orbital& body, int index)
+{
+ int type = body.Type();
+
+ if (type == Orbital::NOTHING)
+ return;
+
+ int x1, y1, x2, y2;
+ Rect label_rect;
+ int label_w = 64;
+ int label_h = 18;
+
+ double cx = rect.w/2;
+ double cy = rect.h/2;
+
+ c = (cx<cy) ? cx : cy;
+ r = system->Radius() * zoom;
+
+ if ((r > 300e9) && (type > Orbital::PLANET))
+ return;
+
+ double xscale = cx / r;
+ double yscale = cy / r * 0.75;
+
+ double ox = offset_x * xscale;
+ double oy = offset_y * yscale;
+
+ double bo_x = body.Orbit() * xscale;
+ double bo_y = body.Orbit() * yscale;
+ double br = body.Radius() * yscale;
+ double bx = body.Location().x * xscale;
+ double by = body.Location().y * yscale;
+
+ double px = 0;
+ double py = 0;
+
+ if (body.Primary()) {
+ double min_pr = GetMinRadius(body.Primary()->Type());
+
+ if (index) {
+ if (min_pr < 4)
+ min_pr = 4;
+
+ min_pr *= (index+1);
+ }
+
+ double min_x = min_pr * xscale / yscale;
+ double min_y = min_pr;
+
+ if (bo_x < min_x)
+ bo_x = min_x;
+
+ if (bo_y < min_y)
+ bo_y = min_y;
+
+ px = body.Primary()->Location().x * xscale;
+ py = body.Primary()->Location().y * yscale;
+ }
+
+ if (type == Orbital::TERRAIN)
+ bo_x = bo_y;
+
+ int ipx = (int) (cx + px + ox);
+ int ipy = (int) (cy + py + oy);
+ int ibo_x = (int) bo_x;
+ int ibo_y = (int) bo_y;
+
+ x1 = ipx - ibo_x;
+ y1 = ipy - ibo_y;
+ x2 = ipx + ibo_x;
+ y2 = ipy + ibo_y;
+
+ if (type != Orbital::TERRAIN) {
+ double a = x2-x1;
+ double b = rect.w*32;
+
+ if (a < b)
+ window->DrawEllipse(x1, y1, x2, y2, Color(64,64,64), Video::BLEND_ADDITIVE);
+ }
+
+ // show body's location on possibly magnified orbit
+ bx = px + bo_x * cos(body.Phase());
+ by = py + bo_y * sin(body.Phase());
+
+ double min_br = GetMinRadius(type);
+ if (br < min_br) br = min_br;
+
+ Color color;
+
+ switch (type) {
+ case Orbital::STAR: color = Color(248, 248, 128); break;
+ case Orbital::PLANET: color = Color( 64, 64, 192); break;
+ case Orbital::MOON: color = Color( 32, 192, 96); break;
+ case Orbital::REGION: color = Color(255, 255, 255); break;
+ case Orbital::TERRAIN: color = Color( 16, 128, 48); break;
+ }
+
+ int icx = (int) (cx + bx + ox);
+ int icy = (int) (cy + by + oy);
+ int ibr = (int) br;
+
+ x1 = icx - ibr;
+ y1 = icy - ibr;
+ x2 = icx + ibr;
+ y2 = icy + ibr;
+
+ if (type < Orbital::REGION) {
+ if (body.GetMapIcon().Width() > 64) {
+ Bitmap* map_icon = (Bitmap*) &body.GetMapIcon();
+
+ if (type == Orbital::STAR)
+ window->DrawBitmap(x1,y1,x2,y2, map_icon, Video::BLEND_ADDITIVE);
+ else
+ window->DrawBitmap(x1,y1,x2,y2, map_icon, Video::BLEND_ALPHA);
+ }
+ else {
+ window->FillEllipse(x1, y1, x2, y2, color);
+ }
+ }
+ else {
+ window->DrawRect(x1, y1, x2, y2, color);
+
+ if (campaign) {
+ ListIter<Combatant> iter = campaign->GetCombatants();
+ while (++iter) {
+ Combatant* combatant = iter.value();
+
+ if (ibr >= 4 || combatant->GetIFF() == 1)
+ DrawCombatantSystem(combatant, &body, icx, icy, ibr);
+ }
+ }
+ }
+
+ if (type == Orbital::STAR || bo_y > label_h) {
+ label_rect.x = x1 - label_w + (int) br;
+ label_rect.y = y1 - label_h;
+ label_rect.w = label_w * 2;
+ label_rect.h = label_h;
+
+ active_window->SetFont(font);
+ active_window->DrawText(body.Name(), -1, label_rect, DT_SINGLELINE|DT_CENTER);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+double
+MapView::GetMinRadius(int type)
+{
+ switch (type) {
+ case Orbital::STAR: return 8;
+ case Orbital::PLANET: return 4;
+ case Orbital::MOON: return 2;
+ case Orbital::REGION: return 2;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+static void
+ColorizeBitmap(Bitmap& img, Color color)
+{
+ int w = img.Width();
+ int h = img.Height();
+
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ Color c = img.GetColor(x, y);
+
+ if (c != Color::Black && c != Color::White) {
+ img.SetColor(x,y,color.ShadeColor(c.Blue()/2));
+ }
+ }
+ }
+
+ img.AutoMask();
+}
+
+static POINT shipshape1[] = { {6,0}, {-6,4}, {-6,-4} };
+static POINT shipshape2[] = { {8,0}, { 4,4}, {-8,4}, {-8,-4}, {4,-4} };
+static POINT shipshape3[] = { {8,8}, {-8,8}, {-8,-8}, {8,-8} };
+
+void
+MapView::DrawShip(Ship& s, bool current, int rep)
+{
+ OrbitalRegion* rgn = (OrbitalRegion*) regions[current_region];
+ if (!rgn) return;
+
+ int x1, y1, x2, y2;
+ POINT shiploc;
+ Point sloc = s.Location().OtherHand();
+
+ double cx = rect.w/2;
+ double cy = rect.h/2;
+
+ c = (cx<cy) ? cx : cy;
+ r = rgn->Radius() * zoom;
+
+ double scale = c/r;
+
+ double ox = offset_x * scale;
+ double oy = offset_y * scale;
+
+ double rlx = 0;
+ double rly = 0;
+
+ int sprite_width = 10;
+
+ window->SetFont(font);
+
+ // draw ship icon:
+ if (rep && view_mode == VIEW_REGION && rgn == s.GetRegion()->GetOrbitalRegion()) {
+ double sx = (sloc.x + rlx) * scale;
+ double sy = (sloc.y + rly) * scale;
+
+ shiploc.x = (int) (cx + sx + ox);
+ shiploc.y = (int) (cy + sy + oy);
+
+ bool ship_visible = shiploc.x >= 0 && shiploc.x < rect.w &&
+ shiploc.y >= 0 && shiploc.y < rect.h;
+
+ if (ship_visible) {
+ if (rep < 3) {
+ window->FillRect(shiploc.x-2, shiploc.y-2, shiploc.x+2, shiploc.y+2, s.MarkerColor());
+ sprite_width = 2;
+
+ if (&s == ship || !IsCrowded(s))
+ window->Print(shiploc.x-sprite_width, shiploc.y+sprite_width+2, s.Name());
+ }
+ else {
+ Point heading = s.Heading().OtherHand();
+ heading.z = 0;
+ heading.Normalize();
+
+ double theta = 0;
+
+ if (heading.y > 0)
+ theta = acos(heading.x);
+ else
+ theta = -acos(heading.x);
+
+ const double THETA_SLICE = 4 / PI;
+ const double THETA_OFFSET = PI + THETA_SLICE/2;
+
+ int sprite_index = (int) ((theta + THETA_OFFSET) * THETA_SLICE);
+ int nsprites = s.Design()->map_sprites.size();
+
+ if (nsprites) {
+ if (sprite_index < 0 || sprite_index >= nsprites)
+ sprite_index = sprite_index % nsprites;
+
+ Bitmap* map_sprite = s.Design()->map_sprites[sprite_index];
+
+ Bitmap bmp;
+ bmp.CopyBitmap(*map_sprite);
+ ColorizeBitmap(bmp, s.MarkerColor());
+ sprite_width = bmp.Width()/2;
+ int h = bmp.Height()/2;
+
+ window->DrawBitmap(shiploc.x-sprite_width,
+ shiploc.y-h,
+ shiploc.x+sprite_width,
+ shiploc.y+h,
+ &bmp,
+ Video::BLEND_ALPHA);
+ }
+
+ else {
+ theta -= PI/2;
+
+ if (s.IsStatic()) {
+ window->FillRect(shiploc.x-6, shiploc.y-6, shiploc.x+6, shiploc.y+6, s.MarkerColor());
+ window->DrawRect(shiploc.x-6, shiploc.y-6, shiploc.x+6, shiploc.y+6, Color::White);
+ }
+ else if (s.IsStarship()) {
+ window->FillRect(shiploc.x-4, shiploc.y-4, shiploc.x+4, shiploc.y+4, s.MarkerColor());
+ window->DrawRect(shiploc.x-4, shiploc.y-4, shiploc.x+4, shiploc.y+4, Color::White);
+ }
+ else {
+ window->FillRect(shiploc.x-3, shiploc.y-3, shiploc.x+3, shiploc.y+3, s.MarkerColor());
+ window->DrawRect(shiploc.x-3, shiploc.y-3, shiploc.x+3, shiploc.y+3, Color::White);
+ }
+ }
+
+ window->Print(shiploc.x-sprite_width, shiploc.y+sprite_width+2, s.Name());
+ }
+ }
+ }
+
+ // draw nav route:
+ // draw current ship marker:
+ if (current && Text(s.GetRegion()->Name()) == regions[current_region]->Name()) {
+ x1 = (int) (shiploc.x - sprite_width - 1);
+ x2 = (int) (shiploc.x + sprite_width + 1);
+ y1 = (int) (shiploc.y - sprite_width - 1);
+ y2 = (int) (shiploc.y + sprite_width + 1);
+
+ window->DrawRect(x1, y1, x2, y2, Color::White);
+ }
+
+ // only see routes for your own team:
+ if (s.GetIFF() == 0 || ship && s.GetIFF() == ship->GetIFF()) {
+ DrawNavRoute(rgn, s.GetFlightPlan(), s.MarkerColor(), &s, 0);
+ }
+}
+
+void
+MapView::DrawElem(MissionElement& s, bool current, int rep)
+{
+ if (!mission) return;
+
+ bool visible = editor ||
+ s.GetIFF() == 0 ||
+ s.GetIFF() == mission->Team() ||
+ s.IntelLevel() > Intel::KNOWN;
+
+ if (!visible) return;
+
+ OrbitalRegion* rgn = (OrbitalRegion*) regions[current_region];
+
+ if (!rgn) return;
+
+ int x1, y1, x2, y2;
+ POINT shiploc;
+
+ double cx = rect.w/2;
+ double cy = rect.h/2;
+
+ c = (cx<cy) ? cx : cy;
+ r = rgn->Radius() * zoom;
+
+ double scale = c/r;
+
+ double ox = offset_x * scale;
+ double oy = offset_y * scale;
+
+ double rlx = 0;
+ double rly = 0;
+
+ int sprite_width = 10;
+
+ window->SetFont(font);
+
+ // draw ship icon:
+ if (!stricmp(s.Region(), rgn->Name())) {
+ double sx = (s.Location().x + rlx) * scale;
+ double sy = (s.Location().y + rly) * scale;
+
+ shiploc.x = (int) (cx + sx + ox);
+ shiploc.y = (int) (cy + sy + oy);
+
+ bool ship_visible = shiploc.x >= 0 && shiploc.x < rect.w &&
+ shiploc.y >= 0 && shiploc.y < rect.h;
+
+ if (ship_visible) {
+ if (rep < 3) {
+ window->FillRect(shiploc.x-2, shiploc.y-2, shiploc.x+2, shiploc.y+2, s.MarkerColor());
+ sprite_width = 2;
+
+ if (!IsCrowded(s))
+ window->Print(shiploc.x-sprite_width, shiploc.y+sprite_width+2, s.Name());
+ }
+ else {
+ double theta = s.Heading();
+
+ const double THETA_SLICE = 4 / PI;
+ const double THETA_OFFSET = PI / 2;
+
+ int sprite_index = (int) ((theta + THETA_OFFSET) * THETA_SLICE);
+ int nsprites = 0;
+
+ if (s.GetDesign())
+ nsprites = s.GetDesign()->map_sprites.size();
+
+ if (nsprites > 0) {
+ if (sprite_index < 0 || sprite_index >= nsprites)
+ sprite_index = sprite_index % nsprites;
+
+ Bitmap* map_sprite = s.GetDesign()->map_sprites[sprite_index];
+
+ Bitmap bmp;
+ bmp.CopyBitmap(*map_sprite);
+ ColorizeBitmap(bmp, s.MarkerColor());
+ sprite_width = bmp.Width()/2;
+ int h = bmp.Height()/2;
+
+ window->DrawBitmap(shiploc.x-sprite_width,
+ shiploc.y-h,
+ shiploc.x+sprite_width,
+ shiploc.y+h,
+ &bmp,
+ Video::BLEND_ALPHA);
+ }
+
+ else {
+ theta -= PI/2;
+
+ if (s.IsStatic()) {
+ window->FillRect(shiploc.x-6, shiploc.y-6, shiploc.x+6, shiploc.y+6, s.MarkerColor());
+ window->DrawRect(shiploc.x-6, shiploc.y-6, shiploc.x+6, shiploc.y+6, Color::White);
+ }
+ else if (s.IsStarship()) {
+ window->FillRect(shiploc.x-4, shiploc.y-4, shiploc.x+4, shiploc.y+4, s.MarkerColor());
+ window->DrawRect(shiploc.x-4, shiploc.y-4, shiploc.x+4, shiploc.y+4, Color::White);
+ }
+ else {
+ window->FillRect(shiploc.x-3, shiploc.y-3, shiploc.x+3, shiploc.y+3, s.MarkerColor());
+ window->DrawRect(shiploc.x-3, shiploc.y-3, shiploc.x+3, shiploc.y+3, Color::White);
+ }
+ }
+
+ char label[64];
+
+ if (s.Count() > 1)
+ sprintf(label, "%s x %d", (const char*) s.Name(), s.Count());
+ else
+ strcpy(label, (const char*) s.Name());
+
+ window->Print(shiploc.x-sprite_width, shiploc.y+sprite_width+2, label);
+ }
+ }
+ }
+
+ // draw nav route:
+ // draw current ship marker:
+ if (current && s.Region() == regions[current_region]->Name()) {
+ x1 = (int) (shiploc.x - sprite_width - 1);
+ x2 = (int) (shiploc.x + sprite_width + 1);
+ y1 = (int) (shiploc.y - sprite_width - 1);
+ y2 = (int) (shiploc.y + sprite_width + 1);
+
+ window->DrawRect(x1, y1, x2, y2, Color::White);
+ }
+
+ // only see routes for your own team:
+ if (editor || s.GetIFF() == 0 || mission && s.GetIFF() == mission->Team()) {
+ DrawNavRoute(rgn, s.NavList(), s.MarkerColor(), 0, &s);
+ }
+}
+
+void
+MapView::DrawNavRoute(OrbitalRegion* rgn,
+ List<Instruction>& s_route,
+ Color s_marker,
+ Ship* ship,
+ MissionElement* elem)
+{
+ int x1, y1, x2, y2;
+ double cx = rect.w/2;
+ double cy = rect.h/2;
+
+ c = (cx<cy) ? cx : cy;
+ r = system->Radius() * zoom;
+
+ if (view_mode == VIEW_REGION) {
+ if (!rgn) return;
+ r = rgn->Radius() * zoom;
+ }
+
+ double scale = c/r;
+ double ox = offset_x * scale;
+ double oy = offset_y * scale;
+
+ Point old_loc;
+ double old_x = 0;
+ double old_y = 0;
+ bool old_in = false;
+
+ Point first_loc;
+ int first_x = 0;
+ int first_y = 0;
+ bool first_in = false;
+
+ bool draw_route = true;
+ bool draw_bold = false;
+
+ if (ship && ship->GetElementIndex() > 1)
+ draw_route = false;
+
+ if (ship && ship == current_ship) {
+ s_marker = s_marker * 1.5;
+ draw_bold = true;
+ }
+
+ else if (elem && elem == current_elem) {
+ s_marker = s_marker * 1.5;
+ draw_bold = true;
+ }
+
+ for (int i = 0; i < s_route.size(); i++) {
+ Instruction* navpt = s_route[i];
+
+ if (!stricmp(navpt->RegionName(), rgn->Name())) {
+ double nav_x = navpt->Location().x * scale;
+ double nav_y = navpt->Location().y * scale;
+
+ int isx = (int) (cx + nav_x + ox);
+ int isy = (int) (cy + nav_y + oy);
+
+ if (old_in && draw_route) {
+ int iox = (int) (cx + old_x + ox);
+ int ioy = (int) (cy + old_y + oy);
+ window->DrawLine(iox, ioy, isx, isy, s_marker);
+
+ int x1 = (iox-isx);
+ int y1 = (ioy-isy);
+
+ if (draw_bold) {
+ if (fabs(x1) > fabs(y1)) {
+ window->DrawLine(iox, ioy+1, isx, isy+1, s_marker);
+ }
+ else {
+ window->DrawLine(iox+1, ioy, isx+1, isy, s_marker);
+ }
+ }
+
+ if ((x1*x1 + y1*y1) > 2000) {
+ double dist = Point(navpt->Location() - old_loc).length();
+
+ int imx = (int) (cx + (old_x+nav_x)/2 + ox);
+ int imy = (int) (cy + (old_y+nav_y)/2 + oy);
+
+ char dist_txt[32];
+ FormatNumber(dist_txt, dist);
+ font->SetColor(Color::Gray);
+ window->SetFont(font);
+ window->Print(imx-20, imy-6, dist_txt);
+ font->SetColor(Color::White);
+ }
+ }
+
+ x1 = isx - 3;
+ y1 = isy - 3;
+ x2 = isx + 3;
+ y2 = isy + 3;
+
+ Color c = Color::White;
+ if (navpt->Status() > Instruction::ACTIVE) {
+ c = Color::Gray;
+ }
+ else if (!first_in) {
+ first_in = true;
+ first_loc = navpt->Location();
+ first_x = isx;
+ first_y = isy;
+ }
+
+ if (draw_route) {
+ window->DrawLine(x1, y1, x2, y2, c);
+ window->DrawLine(x1, y2, x2, y1, c);
+
+ if (navpt == current_navpt)
+ window->DrawRect(x1-2, y1-2, x2+2, y2+2, c);
+
+ char buf[256];
+ sprintf(buf, "%d", i+1);
+ window->SetFont(font);
+ window->Print(x2+3, y1, buf);
+
+ if (navpt == current_navpt) {
+ if (navpt->TargetName() && strlen(navpt->TargetName())) {
+ sprintf(buf, "%s %s", Game::GetText(Text("MapView.item.") + Instruction::ActionName(navpt->Action())).data(), navpt->TargetName());
+ window->Print(x2+3, y1+10, buf);
+ }
+ else {
+ sprintf(buf, "%s", Game::GetText(Text("MapView.item.") + Instruction::ActionName(navpt->Action())).data());
+ window->Print(x2+3, y1+10, buf);
+ }
+
+ sprintf(buf, "%s", Game::GetText(Text("MapView.item.") + Instruction::FormationName(navpt->Formation())).data());
+ window->Print(x2+3, y1+20, buf);
+
+ sprintf(buf, "%d", navpt->Speed());
+ window->Print(x2+3, y1+30, buf);
+
+ if (navpt->HoldTime()) {
+ char hold_time[32];
+ FormatTime(hold_time, navpt->HoldTime());
+
+ sprintf(buf, "%s %s", Game::GetText("MapView.item.Hold").data(), hold_time);
+ window->Print(x2+3, y1+40, buf);
+ }
+ }
+ }
+
+ old_loc = navpt->Location();
+ old_x = nav_x;
+ old_y = nav_y;
+ old_in = true;
+ }
+
+ else {
+ old_loc = navpt->Location();
+ old_x = 0;
+ old_y = 0;
+ old_in = false;
+ }
+ }
+
+ // if the ship and the first active navpoint are both in the region,
+ // draw a line from the ship to the first active navpoint:
+
+ if (first_in) {
+ old_in = false;
+
+ if (ship && ship->GetRegion()) {
+ old_in = (ship->GetRegion()->GetOrbitalRegion() == rgn);
+
+ if (old_in) {
+ old_loc = ship->Location().OtherHand();
+ old_x = old_loc.x * scale;
+ old_y = old_loc.y * scale;
+ }
+ }
+
+ else if (elem) {
+ old_in = (elem->Region() == rgn->Name()) ? true : false;
+
+ if (old_in) {
+ old_loc = elem->Location();
+ old_x = old_loc.x * scale;
+ old_y = old_loc.y * scale;
+ }
+ }
+
+ if (old_in) {
+ int iox = (int) (cx + old_x + ox);
+ int ioy = (int) (cy + old_y + oy);
+ window->DrawLine(iox, ioy, first_x, first_y, s_marker);
+
+ int x1 = (iox-first_x);
+ int y1 = (ioy-first_y);
+
+ if (draw_bold) {
+ if (fabs(x1) > fabs(y1)) {
+ window->DrawLine(iox, ioy+1, first_x, first_y+1, s_marker);
+ }
+ else {
+ window->DrawLine(iox+1, ioy, first_x+1, first_y, s_marker);
+ }
+ }
+
+ if ((x1*x1 + y1*y1) > 2000) {
+ double dist = Point(first_loc - old_loc).length();
+ double nav_x = first_loc.x * scale;
+ double nav_y = first_loc.y * scale;
+
+ int imx = (int) (cx + (old_x+nav_x)/2 + ox);
+ int imy = (int) (cy + (old_y+nav_y)/2 + oy);
+
+ char dist_txt[32];
+ FormatNumber(dist_txt, dist);
+ font->SetColor(Color::Gray);
+ window->SetFont(font);
+ window->Print(imx-20, imy-6, dist_txt);
+ font->SetColor(Color::White);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::DrawCombatantSystem(Combatant* c, Orbital* rgn, int x, int y, int r)
+{
+ int team = c->GetIFF();
+ int x1 = 0;
+ int x2 = 0;
+ int y1 = y - r;
+ int a = 0;
+
+ switch (team) {
+ case 0: x1 = x - 64;
+ x2 = x + 64;
+ y1 = y + r + 4;
+ a = DT_CENTER;
+ break;
+
+ case 1: x1 = x - 200;
+ x2 = x - r - 4;
+ a = DT_RIGHT;
+ break;
+
+ default: x1 = x + r + 4;
+ x2 = x + 200;
+ a = DT_LEFT;
+ break;
+ }
+
+ DrawCombatGroupSystem(c->GetForce(), rgn, x1, x2, y1, a);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::DrawCombatGroupSystem(CombatGroup* group, Orbital* rgn, int x1, int x2, int& y, int a)
+{
+ if (!group || group->IsReserve() || group->CalcValue() < 1)
+ return;
+
+ char txt[80];
+
+ if (group->GetRegion() == rgn->Name()) {
+ switch (group->Type()) {
+ case CombatGroup::CARRIER_GROUP:
+ case CombatGroup::BATTLE_GROUP:
+ case CombatGroup::DESTROYER_SQUADRON:
+ sprintf(txt, "%s '%s'", group->GetShortDescription(), group->Name().data());
+ active_window->SetFont(font);
+ active_window->DrawText(txt, 0, Rect(x1, y, x2-x1, 12), a);
+ y += 10;
+ break;
+
+ case CombatGroup::BATTALION:
+ case CombatGroup::STATION:
+ case CombatGroup::STARBASE:
+
+ case CombatGroup::MINEFIELD:
+ case CombatGroup::BATTERY:
+ case CombatGroup::MISSILE:
+
+ active_window->SetFont(font);
+ active_window->DrawText(group->GetShortDescription(), 0, Rect(x1, y, x2-x1, 12), a);
+ y += 10;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ ListIter<CombatGroup> iter = group->GetComponents();
+ while (++iter) {
+ CombatGroup* g = iter.value();
+ DrawCombatGroupSystem(g, rgn, x1, x2, y, a);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::DrawCombatGroup(CombatGroup* group, int rep)
+{
+ // does group even exist yet?
+ if (!group || group->IsReserve() || group->CalcValue() < 1)
+ return;
+
+ // is group a squadron? don't draw squadrons on map:
+ if (group->Type() >= CombatGroup::WING && group->Type() < CombatGroup::FLEET)
+ return;
+
+ // has group been discovered yet?
+ CombatGroup* player_group = campaign->GetPlayerGroup();
+ if (group->GetIFF() && player_group && player_group->GetIFF() != group->GetIFF())
+ if (group->IntelLevel() <= Intel::KNOWN)
+ return;
+
+ // has group been destroyed already?
+ if (group->CalcValue() < 1)
+ return;
+
+ OrbitalRegion* rgn = (OrbitalRegion*) regions[current_region];
+ if (!rgn) return;
+
+ POINT shiploc;
+
+ double cx = rect.w/2;
+ double cy = rect.h/2;
+
+ c = (cx<cy) ? cx : cy;
+ r = rgn->Radius() * zoom;
+
+ double scale = c/r;
+
+ double ox = offset_x * scale;
+ double oy = offset_y * scale;
+
+ double rlx = 0;
+ double rly = 0;
+
+ int sprite_width = 10;
+
+ if (group->GetUnits().size() > 0) {
+ CombatUnit* unit = 0;
+
+ for (int i = 0; i < group->GetUnits().size(); i++) {
+ unit = group->GetUnits().at(i);
+
+ if (unit->Count() - unit->DeadCount() > 0)
+ break;
+ }
+
+ // draw unit icon:
+ if (unit->GetRegion() == rgn->Name() && unit->Type() > Ship::LCA && unit->Count() > 0) {
+ double sx = (unit->Location().x + rlx) * scale;
+ double sy = (unit->Location().y + rly) * scale;
+
+ shiploc.x = (int) (cx + sx + ox);
+ shiploc.y = (int) (cy + sy + oy);
+
+ bool ship_visible = shiploc.x >= 0 && shiploc.x < rect.w &&
+ shiploc.y >= 0 && shiploc.y < rect.h;
+
+ if (ship_visible) {
+ if (rep < 3) {
+ window->FillRect(shiploc.x-2, shiploc.y-2, shiploc.x+2, shiploc.y+2, unit->MarkerColor());
+ sprite_width = 2;
+
+ char buf[256];
+ sprintf(buf, "%s", unit->Name().data());
+ window->SetFont(font);
+ window->Print(shiploc.x-sprite_width, shiploc.y+sprite_width+2, buf);
+ }
+ else {
+ int sprite_index = 2;
+ int nsprites = 0;
+
+ if (unit->GetDesign())
+ nsprites = unit->GetDesign()->map_sprites.size();
+
+ if (nsprites) {
+ if (sprite_index < 0 || sprite_index >= nsprites)
+ sprite_index = sprite_index % nsprites;
+
+ Bitmap* map_sprite = unit->GetDesign()->map_sprites[sprite_index];
+
+ Bitmap bmp;
+ bmp.CopyBitmap(*map_sprite);
+ ColorizeBitmap(bmp, unit->MarkerColor());
+ sprite_width = bmp.Width()/2;
+ int h = bmp.Height()/2;
+
+ window->DrawBitmap(shiploc.x-sprite_width,
+ shiploc.y-h,
+ shiploc.x+sprite_width,
+ shiploc.y+h,
+ &bmp,
+ Video::BLEND_ALPHA);
+ }
+
+ else {
+ if (unit->IsStatic()) {
+ window->FillRect(shiploc.x-6, shiploc.y-6, shiploc.x+6, shiploc.y+6, unit->MarkerColor());
+ window->DrawRect(shiploc.x-6, shiploc.y-6, shiploc.x+6, shiploc.y+6, Color::White);
+ }
+ else if (unit->IsStarship()) {
+ window->FillRect(shiploc.x-4, shiploc.y-4, shiploc.x+4, shiploc.y+4, unit->MarkerColor());
+ window->DrawRect(shiploc.x-4, shiploc.y-4, shiploc.x+4, shiploc.y+4, Color::White);
+ }
+ else {
+ window->FillRect(shiploc.x-3, shiploc.y-3, shiploc.x+3, shiploc.y+3, unit->MarkerColor());
+ window->DrawRect(shiploc.x-3, shiploc.y-3, shiploc.x+3, shiploc.y+3, Color::White);
+ }
+ }
+
+ char label[128];
+ strcpy(label, unit->GetDescription());
+ window->SetFont(font);
+ window->Print(shiploc.x-sprite_width, shiploc.y+sprite_width+2, label);
+ }
+ }
+ }
+ }
+
+ // recurse
+ ListIter<CombatGroup> iter = group->GetComponents();
+ while (++iter) {
+ CombatGroup* g = iter.value();
+ DrawCombatGroup(g, rep);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+MapView::IsClutter(Ship& test)
+{
+ // get leader:
+ Ship* lead = test.GetLeader();
+
+ if (lead == &test) // this is the leader:
+ return false;
+
+ // too close?
+ if (lead) {
+ POINT testloc, leadloc;
+
+ GetShipLoc(test, testloc);
+ GetShipLoc(*lead, leadloc);
+
+ double dx = testloc.x - leadloc.x;
+ double dy = testloc.y - leadloc.y;
+ double d = dx*dx + dy*dy;
+
+ if (d <= 64)
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+MapView::IsCrowded(Ship& test)
+{
+ POINT testloc, refloc;
+ Sim* sim = Sim::GetSim();
+ Orbital* rgn = regions[current_region];
+ SimRegion* simrgn = sim->FindRegion(rgn->Name());
+
+ if (simrgn) {
+ GetShipLoc(test, testloc);
+
+ ListIter<Ship> s = simrgn->Ships();
+ while (++s) {
+ Ship* ref = s.value();
+
+ // too close?
+ if (ref && ref != &test) {
+ GetShipLoc(*ref, refloc);
+
+ double dx = testloc.x - refloc.x;
+ double dy = testloc.y - refloc.y;
+ double d = dx*dx + dy*dy;
+
+ if (d <= 64)
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void
+MapView::GetShipLoc(Ship& s, POINT& shiploc)
+{
+ double cx = rect.w/2;
+ double cy = rect.h/2;
+
+ c = (cx<cy) ? cx : cy;
+ r = system->Radius() * zoom;
+
+ OrbitalRegion* rgn = (OrbitalRegion*) regions[current_region];
+
+ if (view_mode == VIEW_REGION) {
+ if (!rgn) return;
+ r = rgn->Radius() * zoom;
+ }
+
+ double scale = c/r;
+
+ double ox = offset_x * scale;
+ double oy = offset_y * scale;
+
+ double rlx = 0;
+ double rly = 0;
+
+ if (view_mode == VIEW_SYSTEM) {
+ rgn = system->ActiveRegion();
+
+ if (rgn) {
+ rlx = rgn->Location().x;
+ rly = rgn->Location().y;
+ }
+ }
+
+ if (view_mode == VIEW_SYSTEM ||
+ (view_mode == VIEW_REGION && rgn == s.GetRegion()->GetOrbitalRegion())) {
+ double sx = (s.Location().x + rlx) * scale;
+ double sy = (s.Location().y + rly) * scale;
+
+ shiploc.x = (int) (cx + sx + ox);
+ shiploc.y = (int) (cy + sy + oy);
+ }
+ else {
+ shiploc.x = -1;
+ shiploc.y = -1;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+MapView::IsCrowded(MissionElement& test)
+{
+ POINT testloc, refloc;
+ Sim* sim = Sim::GetSim();
+ Orbital* rgn = regions[current_region];
+
+ if (mission) {
+ GetElemLoc(test, testloc);
+
+ ListIter<MissionElement> s = mission->GetElements();
+ while (++s) {
+ MissionElement* ref = s.value();
+
+ if (ref && ref != &test && !stricmp(ref->Region(), rgn->Name())) {
+ GetElemLoc(*ref, refloc);
+
+ double dx = testloc.x - refloc.x;
+ double dy = testloc.y - refloc.y;
+ double d = dx*dx + dy*dy;
+
+ if (d <= 64)
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void
+MapView::GetElemLoc(MissionElement& s, POINT& shiploc)
+{
+ double cx = rect.w/2;
+ double cy = rect.h/2;
+
+ c = (cx<cy) ? cx : cy;
+ r = system->Radius() * zoom;
+
+ OrbitalRegion* rgn = (OrbitalRegion*) regions[current_region];
+
+ if (view_mode == VIEW_REGION) {
+ if (!rgn) return;
+ r = rgn->Radius() * zoom;
+ }
+
+ double scale = c/r;
+
+ double ox = offset_x * scale;
+ double oy = offset_y * scale;
+
+ double rlx = 0;
+ double rly = 0;
+
+ if (view_mode == VIEW_SYSTEM) {
+ rgn = system->ActiveRegion();
+
+ if (rgn) {
+ rlx = rgn->Location().x;
+ rly = rgn->Location().y;
+ }
+ }
+
+ if (view_mode == VIEW_SYSTEM ||
+ (view_mode == VIEW_REGION && !stricmp(s.Region(), rgn->Name()))) {
+ double sx = (s.Location().x + rlx) * scale;
+ double sy = (s.Location().y + rly) * scale;
+
+ shiploc.x = (int) (cx + sx + ox);
+ shiploc.y = (int) (cy + sy + oy);
+ }
+ else {
+ shiploc.x = -1;
+ shiploc.y = -1;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+OrbitalRegion*
+MapView::GetRegion() const
+{
+ OrbitalRegion* result = 0;
+
+ if (current_region < regions.size())
+ result = (OrbitalRegion*) regions[current_region];
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::ZoomIn()
+{
+ zoom *= 0.9;
+
+ if (view_mode == VIEW_SYSTEM) {
+ if (system && zoom * system->Radius() < 2e6) {
+ zoom = 2e6 / system->Radius();
+ }
+ }
+ else if (view_mode == VIEW_REGION) {
+ OrbitalRegion* rgn = GetRegion();
+ if (rgn && zoom * rgn->Radius() < 1e3) {
+ zoom = 1e3 / rgn->Radius();
+ }
+ }
+}
+
+void
+MapView::ZoomOut()
+{
+ zoom *= 1.1;
+
+ if (view_mode == VIEW_SYSTEM) {
+ if (system && zoom * system->Radius() > 500e9) {
+ zoom = 500e9 / system->Radius();
+ }
+ }
+ else if (view_mode == VIEW_REGION) {
+ OrbitalRegion* rgn = GetRegion();
+ if (rgn && zoom * rgn->Radius() > 1e6) {
+ zoom = 1e6 / rgn->Radius();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MapView::OnShow()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch)
+ dispatch->Register(this);
+}
+
+void
+MapView::OnHide()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch)
+ dispatch->Unregister(this);
+
+ if (captured) {
+ ReleaseCapture();
+ captured = false;
+ Mouse::Show(true);
+ }
+
+ dragging = false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+MapView::IsEnabled() const
+{
+ if (active_window)
+ return active_window->IsEnabled();
+
+ return false;
+}
+
+bool
+MapView::IsVisible() const
+{
+ if (active_window)
+ return active_window->IsVisible();
+
+ return false;
+}
+
+bool
+MapView::IsFormActive() const
+{
+ if (active_window)
+ return active_window->IsFormActive();
+
+ return false;
+}
+
+Rect
+MapView::TargetRect() const
+{
+ if (active_window)
+ return active_window->TargetRect();
+
+ return Rect();
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MapView::OnMouseMove(int x, int y)
+{
+ if (captured) {
+ EventTarget* test = 0;
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch)
+ test = dispatch->GetCapture();
+
+ if (test != this) {
+ captured = false;
+ Mouse::Show(true);
+ }
+
+ else {
+ if (dragging) {
+ int delta_x = x - mouse_x;
+ int delta_y = y - mouse_y;
+
+ offset_x += delta_x * r / c;
+ offset_y += delta_y * r / c;
+
+ Mouse::SetCursorPos(mouse_x, mouse_y);
+ }
+
+ else if (view_mode == VIEW_REGION) {
+ double scale = r/c;
+ click_x = (x - rect.x - rect.w/2) * scale - offset_x;
+ click_y = (y - rect.y - rect.h/2) * scale - offset_y;
+
+ if ((adding_navpt || moving_navpt) && current_navpt) {
+ Point loc = current_navpt->Location();
+ loc.x = click_x;
+ loc.y = click_y;
+ current_navpt->SetLocation(loc);
+ current_navpt->SetStatus(current_status);
+ }
+
+ else if (editor && moving_elem && current_elem) {
+ Point loc = current_elem->Location();
+ loc.x = click_x;
+ loc.y = click_y;
+ current_elem->SetLocation(loc);
+ }
+ }
+ }
+ }
+
+ return active_window->OnMouseMove(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MapView::OnRButtonDown(int x, int y)
+{
+ if (!captured)
+ captured = SetCapture();
+
+ if (captured) {
+ dragging = true;
+ mouse_x = x;
+ mouse_y = y;
+ Mouse::Show(false);
+ }
+
+ return active_window->OnRButtonDown(x, y);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MapView::OnRButtonUp(int x, int y)
+{
+ if (captured) {
+ ReleaseCapture();
+ captured = false;
+ Mouse::Show(true);
+ }
+
+ dragging = false;
+
+ return active_window->OnRButtonUp(x, y);
+}
+
+// +--------------------------------------------------------------------+
+
+int MapView::OnClick()
+{
+ return active_window->OnClick();
+}
+
+int MapView::OnLButtonDown(int x, int y)
+{
+ if (menu_view && menu_view->IsShown()) {
+ // ignore this event...
+ }
+ else {
+ if (!captured)
+ captured = SetCapture();
+
+ if (view_mode == VIEW_REGION) {
+ double scale = r/c;
+ click_x = (x - rect.x - rect.w/2) * scale - offset_x;
+ click_y = (y - rect.y - rect.h/2) * scale - offset_y;
+
+ if (current_navpt) {
+ Point nloc = current_navpt->Location();
+ double dx = nloc.x - click_x;
+ double dy = nloc.y - click_y;
+ double d = sqrt(dx*dx + dy*dy);
+
+ if (d < 5e3) {
+ moving_navpt = true;
+
+ if (ship && current_ship && ship != current_ship) {
+ if (ship->GetElement() && current_ship->GetElement()) {
+ if (!ship->GetElement()->CanCommand(current_ship->GetElement())) {
+ moving_navpt = false;
+ }
+ }
+ }
+ else if (current_elem && NetLobby::GetInstance()) {
+ moving_navpt = false;
+ }
+ }
+ }
+
+ else if (editor && current_elem) {
+ Point nloc = current_elem->Location();
+ double dx = nloc.x - click_x;
+ double dy = nloc.y - click_y;
+ double d = sqrt(dx*dx + dy*dy);
+
+ if (d < 5e3) {
+ moving_elem = true;
+
+ if (current_elem && NetLobby::GetInstance()) {
+ moving_elem = false;
+ }
+ }
+ }
+ }
+ }
+
+ return active_window->OnLButtonDown(x,y);
+}
+
+int MapView::OnLButtonUp(int x, int y)
+{
+ bool process_event = false;
+
+ if (captured) {
+ process_event = true;
+ ReleaseCapture();
+ captured = false;
+ Mouse::Show(true);
+ }
+
+ if (process_event && !adding_navpt) {
+ if (!moving_navpt && !moving_elem) {
+ Button::PlaySound();
+ }
+
+ if (view_mode == VIEW_REGION) {
+ double scale = r/c;
+ click_x = (x - rect.x - rect.w/2) * scale - offset_x;
+ click_y = (y - rect.y - rect.h/2) * scale - offset_y;
+
+ if (!scrolling)
+ SelectAt(x,y);
+
+ active_window->ClientEvent(EID_MAP_CLICK, x, y);
+ }
+
+ else if (!scrolling) {
+ SelectAt(x,y);
+
+ active_window->ClientEvent(EID_MAP_CLICK, x, y);
+ }
+ }
+
+ if ((adding_navpt || moving_navpt) && current_navpt) {
+ current_navpt->SetStatus(current_status);
+
+ Sim* sim = Sim::GetSim();
+ if (sim) {
+ Ship* s = current_ship;
+
+ if (s && s->GetElement()) {
+ Element* elem = s->GetElement();
+ int index = elem->GetNavIndex(current_navpt);
+
+ if (index >= 0)
+ NetUtil::SendNavData(false, elem, index-1, current_navpt);
+ }
+ }
+ }
+
+ adding_navpt = false;
+ moving_navpt = false;
+ moving_elem = false;
+
+ return active_window->OnLButtonUp(x,y);
+}
+
+
diff --git a/Stars45/MapView.h b/Stars45/MapView.h
new file mode 100644
index 0000000..d48fd89
--- /dev/null
+++ b/Stars45/MapView.h
@@ -0,0 +1,223 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MapView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Star Map class
+*/
+
+#ifndef MapView_h
+#define MapView_h
+
+#include "Types.h"
+#include "SimObject.h"
+#include "View.h"
+#include "EventTarget.h"
+#include "Bitmap.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class ActiveWindow;
+class StarSystem;
+class Orbital;
+class OrbitalRegion;
+class Ship;
+class Instruction;
+class Mission;
+class MissionElement;
+class Campaign;
+class Combatant;
+class CombatGroup;
+class Menu;
+class MenuItem;
+class MenuView;
+class Font;
+
+const int EID_MAP_CLICK = 1000;
+
+// +--------------------------------------------------------------------+
+
+class MapView : public View,
+ public EventTarget,
+ public SimObserver
+{
+public:
+ MapView(Window* win);
+ virtual ~MapView();
+
+ // Operations:
+ virtual void Refresh();
+ virtual void OnWindowMove();
+ virtual void OnShow();
+ virtual void OnHide();
+
+ virtual void DrawTitle();
+ virtual void DrawGalaxy();
+ virtual void DrawSystem();
+ virtual void DrawRegion();
+
+ virtual void DrawGrid();
+ virtual void DrawOrbital(Orbital& orbital, int index);
+ virtual void DrawShip(Ship& ship, bool current=false, int rep=3);
+ virtual void DrawElem(MissionElement& elem, bool current=false, int rep=3);
+ virtual void DrawNavRoute(OrbitalRegion* rgn,
+ List<Instruction>& route,
+ Color smarker,
+ Ship* ship=0,
+ MissionElement* elem=0);
+
+ virtual void DrawCombatantSystem(Combatant* c, Orbital* rgn, int x, int y, int r);
+ virtual void DrawCombatGroupSystem(CombatGroup* g, Orbital* rgn, int x1, int x2, int& y, int a);
+ virtual void DrawCombatGroup(CombatGroup* g, int rep=3);
+
+ virtual int GetViewMode() const { return view_mode; }
+ virtual void SetViewMode(int mode);
+ virtual void SetSelectionMode(int mode);
+ virtual int GetSelectionMode() const { return seln_mode; }
+ virtual void SetSelection(int index);
+ virtual void SetSelectedShip(Ship* ship);
+ virtual void SetSelectedElem(MissionElement* elem);
+ virtual void SetRegion(OrbitalRegion* rgn);
+ virtual void SetRegionByName(const char* rgn_name);
+ virtual void SelectAt(int x, int y);
+ virtual Orbital* GetSelection();
+ virtual Ship* GetSelectedShip();
+ virtual MissionElement* GetSelectedElem();
+ virtual int GetSelectionIndex();
+ virtual void SetShipFilter(DWORD f) { ship_filter = f; }
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnClick();
+ virtual int OnRButtonDown(int x, int y);
+ virtual int OnRButtonUp(int x, int y);
+
+ virtual bool IsEnabled() const;
+ virtual bool IsVisible() const;
+ virtual bool IsFormActive() const;
+ virtual Rect TargetRect() const;
+
+ void ZoomIn();
+ void ZoomOut();
+
+ void SetGalaxy(List<StarSystem>& systems);
+ void SetSystem(StarSystem* s);
+ void SetMission(Mission* m);
+ void SetShip(Ship* s);
+ void SetCampaign(Campaign* c);
+
+ bool IsVisible(const Point& loc);
+
+ // accessors:
+ virtual void GetClickLoc(double& x, double& y) { x = click_x; y = click_y; }
+ List<StarSystem>& GetGalaxy() { return system_list; }
+ StarSystem* GetSystem() const { return system; }
+ OrbitalRegion* GetRegion() const;
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const { return "MapWin"; }
+
+ bool GetEditorMode() const { return editor; }
+ void SetEditorMode(bool b) { editor = b; }
+
+protected:
+ virtual void BuildMenu();
+ virtual void ClearMenu();
+ virtual void ProcessMenuItem(int action);
+ virtual bool SetCapture();
+ virtual bool ReleaseCapture();
+
+ virtual void DrawTabbedText(Font* font, const char* text);
+
+ bool IsClutter(Ship& s);
+ bool IsCrowded(Ship& s);
+ bool IsCrowded(MissionElement& elem);
+ void GetShipLoc(Ship& s, POINT& loc);
+ void GetElemLoc(MissionElement& s, POINT& loc);
+ void SelectShip(Ship* selship);
+ void SelectElem(MissionElement* selelem);
+ void SelectNavpt(Instruction* navpt);
+ void FindShips(bool friendly, bool station, bool starship, bool dropship,
+ List<Text>& result);
+ void SetupScroll(Orbital* s);
+
+ double GetMinRadius(int type);
+
+ Text title;
+ Rect rect;
+ Campaign* campaign;
+ Mission* mission;
+ List<StarSystem> system_list;
+ StarSystem* system;
+ List<Orbital> stars;
+ List<Orbital> planets;
+ List<Orbital> regions;
+ Ship* ship;
+ Bitmap galaxy_image;
+ bool editor;
+
+ int current_star;
+ int current_planet;
+ int current_region;
+ Ship* current_ship;
+ MissionElement* current_elem;
+ Instruction* current_navpt;
+ int current_status;
+
+ int view_mode;
+ int seln_mode;
+ bool captured;
+ bool dragging;
+ bool adding_navpt;
+ bool moving_navpt;
+ bool moving_elem;
+ int scrolling;
+ int mouse_x;
+ int mouse_y;
+ DWORD ship_filter;
+
+ double zoom;
+ double view_zoom[3];
+ double offset_x;
+ double offset_y;
+ double view_offset_x[3];
+ double view_offset_y[3];
+ double c, r;
+ double scroll_x;
+ double scroll_y;
+ double click_x;
+ double click_y;
+
+ Font* font;
+ Font* title_font;
+
+ ActiveWindow* active_window;
+ Menu* active_menu;
+
+ Menu* map_menu;
+ Menu* map_system_menu;
+ Menu* map_sector_menu;
+ Menu* ship_menu;
+ Menu* nav_menu;
+ Menu* action_menu;
+ Menu* objective_menu;
+ Menu* formation_menu;
+ Menu* speed_menu;
+ Menu* hold_menu;
+ Menu* farcast_menu;
+
+ MenuView* menu_view;
+};
+
+#endif MapView_h
+
diff --git a/Stars45/MenuDlg.cpp b/Stars45/MenuDlg.cpp
new file mode 100644
index 0000000..436526d
--- /dev/null
+++ b/Stars45/MenuDlg.cpp
@@ -0,0 +1,276 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MenuDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "MenuDlg.h"
+#include "MenuScreen.h"
+#include "Starshatter.h"
+#include "Campaign.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ParseUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(MenuDlg, OnStart);
+DEF_MAP_CLIENT(MenuDlg, OnCampaign);
+DEF_MAP_CLIENT(MenuDlg, OnMission);
+DEF_MAP_CLIENT(MenuDlg, OnPlayer);
+DEF_MAP_CLIENT(MenuDlg, OnMultiplayer);
+DEF_MAP_CLIENT(MenuDlg, OnMod);
+DEF_MAP_CLIENT(MenuDlg, OnTacReference);
+DEF_MAP_CLIENT(MenuDlg, OnVideo);
+DEF_MAP_CLIENT(MenuDlg, OnOptions);
+DEF_MAP_CLIENT(MenuDlg, OnControls);
+DEF_MAP_CLIENT(MenuDlg, OnQuit);
+DEF_MAP_CLIENT(MenuDlg, OnButtonEnter);
+DEF_MAP_CLIENT(MenuDlg, OnButtonExit);
+
+extern const char* versionInfo;
+
+// +--------------------------------------------------------------------+
+
+MenuDlg::MenuDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()),
+ manager(mgr), btn_start(0),
+ btn_campaign(0), btn_mission(0), btn_player(0), btn_multi(0),
+ btn_mod(0), btn_tac(0),
+ btn_options(0), btn_controls(0), btn_quit(0),
+ version(0), description(0), stars(0), campaign(0)
+{
+ stars = Starshatter::GetInstance();
+ campaign = Campaign::GetCampaign();
+
+ Init(def);
+}
+
+MenuDlg::~MenuDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuDlg::RegisterControls()
+{
+ if (btn_start)
+ return;
+
+ btn_start = (Button*) FindControl(120);
+ btn_campaign = (Button*) FindControl(101);
+ btn_mission = (Button*) FindControl(102);
+ btn_player = (Button*) FindControl(103);
+ btn_multi = (Button*) FindControl(104);
+ btn_video = (Button*) FindControl(111);
+ btn_options = (Button*) FindControl(112);
+ btn_controls = (Button*) FindControl(113);
+ btn_quit = (Button*) FindControl(114);
+ btn_mod = (Button*) FindControl(115);
+ btn_tac = (Button*) FindControl(116);
+
+ if (btn_start) {
+ REGISTER_CLIENT(EID_CLICK, btn_start, MenuDlg, OnStart);
+ REGISTER_CLIENT(EID_MOUSE_ENTER, btn_start, MenuDlg, OnButtonEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, btn_start, MenuDlg, OnButtonExit);
+ }
+
+ if (btn_campaign) {
+ REGISTER_CLIENT(EID_CLICK, btn_campaign, MenuDlg, OnCampaign);
+ REGISTER_CLIENT(EID_MOUSE_ENTER, btn_campaign, MenuDlg, OnButtonEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, btn_campaign, MenuDlg, OnButtonExit);
+ }
+
+ if (btn_mission) {
+ REGISTER_CLIENT(EID_CLICK, btn_mission, MenuDlg, OnMission);
+ REGISTER_CLIENT(EID_MOUSE_ENTER, btn_mission, MenuDlg, OnButtonEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, btn_mission, MenuDlg, OnButtonExit);
+ }
+
+ if (btn_player) {
+ REGISTER_CLIENT(EID_CLICK, btn_player, MenuDlg, OnPlayer);
+ REGISTER_CLIENT(EID_MOUSE_ENTER, btn_player, MenuDlg, OnButtonEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, btn_player, MenuDlg, OnButtonExit);
+ }
+
+ if (btn_multi) {
+ REGISTER_CLIENT(EID_CLICK, btn_multi, MenuDlg, OnMultiplayer);
+ REGISTER_CLIENT(EID_MOUSE_ENTER, btn_multi, MenuDlg, OnButtonEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, btn_multi, MenuDlg, OnButtonExit);
+ }
+
+ if (btn_video) {
+ REGISTER_CLIENT(EID_CLICK, btn_video, MenuDlg, OnVideo);
+ REGISTER_CLIENT(EID_MOUSE_ENTER, btn_video, MenuDlg, OnButtonEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, btn_video, MenuDlg, OnButtonExit);
+ }
+
+ if (btn_options) {
+ REGISTER_CLIENT(EID_CLICK, btn_options, MenuDlg, OnOptions);
+ REGISTER_CLIENT(EID_MOUSE_ENTER, btn_options, MenuDlg, OnButtonEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, btn_options, MenuDlg, OnButtonExit);
+ }
+
+ if (btn_controls) {
+ REGISTER_CLIENT(EID_CLICK, btn_controls, MenuDlg, OnControls);
+ REGISTER_CLIENT(EID_MOUSE_ENTER, btn_controls, MenuDlg, OnButtonEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, btn_controls, MenuDlg, OnButtonExit);
+ }
+
+ if (btn_mod) {
+ REGISTER_CLIENT(EID_CLICK, btn_mod, MenuDlg, OnMod);
+ REGISTER_CLIENT(EID_MOUSE_ENTER, btn_mod, MenuDlg, OnButtonEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, btn_mod, MenuDlg, OnButtonExit);
+ }
+
+ if (btn_tac) {
+ REGISTER_CLIENT(EID_CLICK, btn_tac, MenuDlg, OnTacReference);
+ REGISTER_CLIENT(EID_MOUSE_ENTER, btn_tac, MenuDlg, OnButtonEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, btn_tac, MenuDlg, OnButtonExit);
+ }
+
+ if (btn_quit) {
+ REGISTER_CLIENT(EID_CLICK, btn_quit, MenuDlg, OnQuit);
+ REGISTER_CLIENT(EID_MOUSE_ENTER, btn_quit, MenuDlg, OnButtonEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, btn_quit, MenuDlg, OnButtonExit);
+ }
+
+ version = FindControl(100);
+
+ if (version) {
+ version->SetText(versionInfo);
+ }
+
+ description = FindControl(202);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuDlg::Show()
+{
+ FormWindow::Show();
+
+ if (btn_multi && Starshatter::UseFileSystem())
+ btn_multi->SetEnabled(false);
+}
+
+void
+MenuDlg::ExecFrame()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuDlg::OnStart(AWEvent* event)
+{
+ if (description) description->SetText("");
+ stars->StartOrResumeGame();
+}
+
+void
+MenuDlg::OnCampaign(AWEvent* event)
+{
+ if (description) description->SetText("");
+ manager->ShowCmpSelectDlg();
+}
+
+void
+MenuDlg::OnMission(AWEvent* event)
+{
+ if (description) description->SetText("");
+ manager->ShowMsnSelectDlg();
+}
+
+void
+MenuDlg::OnPlayer(AWEvent* event)
+{
+ if (description) description->SetText("");
+ manager->ShowPlayerDlg();
+}
+
+void
+MenuDlg::OnMultiplayer(AWEvent* event)
+{
+ if (description) description->SetText("");
+ manager->ShowNetClientDlg();
+}
+
+void
+MenuDlg::OnVideo(AWEvent* event)
+{
+ if (description) description->SetText("");
+ manager->ShowVidDlg();
+}
+
+void
+MenuDlg::OnOptions(AWEvent* event)
+{
+ if (description) description->SetText("");
+ manager->ShowOptDlg();
+}
+
+void
+MenuDlg::OnControls(AWEvent* event)
+{
+ if (description) description->SetText("");
+ manager->ShowCtlDlg();
+}
+
+void
+MenuDlg::OnMod(AWEvent* event)
+{
+ if (description) description->SetText("");
+ manager->ShowModDlg();
+}
+
+void
+MenuDlg::OnTacReference(AWEvent* event)
+{
+ if (description) description->SetText("");
+ stars->OpenTacticalReference();
+}
+
+void
+MenuDlg::OnQuit(AWEvent* event)
+{
+ if (description) description->SetText("");
+ manager->ShowExitDlg();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuDlg::OnButtonEnter(AWEvent* event)
+{
+ ActiveWindow* src = event->window;
+
+ if (src && description)
+ description->SetText(src->GetAltText());
+}
+
+void
+MenuDlg::OnButtonExit(AWEvent* event)
+{
+ if (description)
+ description->SetText("");
+}
diff --git a/Stars45/MenuDlg.h b/Stars45/MenuDlg.h
new file mode 100644
index 0000000..be21feb
--- /dev/null
+++ b/Stars45/MenuDlg.h
@@ -0,0 +1,86 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MenuDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#ifndef MenuDlg_h
+#define MenuDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+class Campaign;
+class Starshatter;
+
+// +--------------------------------------------------------------------+
+
+class MenuDlg : public FormWindow
+{
+public:
+ MenuDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~MenuDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnStart(AWEvent* event);
+ virtual void OnCampaign(AWEvent* event);
+ virtual void OnMission(AWEvent* event);
+ virtual void OnPlayer(AWEvent* event);
+ virtual void OnMultiplayer(AWEvent* event);
+ virtual void OnMod(AWEvent* event);
+ virtual void OnTacReference(AWEvent* event);
+
+ virtual void OnVideo(AWEvent* event);
+ virtual void OnOptions(AWEvent* event);
+ virtual void OnControls(AWEvent* event);
+ virtual void OnQuit(AWEvent* event);
+
+ virtual void OnButtonEnter(AWEvent* event);
+ virtual void OnButtonExit(AWEvent* event);
+
+protected:
+ MenuScreen* manager;
+
+ Button* btn_start;
+ Button* btn_campaign;
+ Button* btn_mission;
+ Button* btn_player;
+ Button* btn_multi;
+ Button* btn_mod;
+ Button* btn_tac;
+
+ Button* btn_video;
+ Button* btn_options;
+ Button* btn_controls;
+ Button* btn_quit;
+
+ ActiveWindow* version;
+ ActiveWindow* description;
+
+ Starshatter* stars;
+ Campaign* campaign;
+};
+
+#endif MenuDlg_h
+
diff --git a/Stars45/MenuScreen.cpp b/Stars45/MenuScreen.cpp
new file mode 100644
index 0000000..b84ac6a
--- /dev/null
+++ b/Stars45/MenuScreen.cpp
@@ -0,0 +1,1076 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: main.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include "MemDebug.h"
+#include "MenuScreen.h"
+
+#include "FormDef.h"
+#include "MenuDlg.h"
+
+#include "ExitDlg.h"
+#include "AudDlg.h"
+#include "VidDlg.h"
+#include "OptDlg.h"
+#include "CtlDlg.h"
+#include "JoyDlg.h"
+#include "KeyDlg.h"
+#include "ConfirmDlg.h"
+#include "PlayerDlg.h"
+#include "ModDlg.h"
+#include "ModInfoDlg.h"
+#include "MsnSelectDlg.h"
+#include "MsnEditDlg.h"
+#include "MsnElemDlg.h"
+#include "MsnEventDlg.h"
+#include "MsnEditNavDlg.h"
+#include "FirstTimeDlg.h"
+#include "AwardShowDlg.h"
+#include "LoadDlg.h"
+#include "TacRefDlg.h"
+
+#include "CmpSelectDlg.h"
+#include "NetClientDlg.h"
+#include "NetLobbyDlg.h"
+#include "NetServerDlg.h"
+#include "NetUnitDlg.h"
+#include "NetAddrDlg.h"
+#include "NetPassDlg.h"
+
+#include "Starshatter.h"
+#include "Player.h"
+#include "NetLobby.h"
+#include "NetClientConfig.h"
+#include "NetServerConfig.h"
+
+#include "Game.h"
+#include "Video.h"
+#include "Screen.h"
+#include "ActiveWindow.h"
+#include "Mouse.h"
+#include "Keyboard.h"
+#include "FadeView.h"
+#include "Color.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "FontMgr.h"
+#include "EventDispatch.h"
+#include "DataLoader.h"
+#include "Resource.h"
+
+// +--------------------------------------------------------------------+
+
+MenuScreen::MenuScreen()
+ : screen(0), menudlg(0),
+ fadewin(0), fadeview(0), exitdlg(0),
+ auddlg(0), viddlg(0), optdlg(0), ctldlg(0), joydlg(0), keydlg(0),
+ playdlg(0), confirmdlg(0), firstdlg(0), awarddlg(0), cmpSelectDlg(0),
+ msnSelectDlg(0), modDlg(0), modInfoDlg(0),
+ msnEditDlg(0), msnElemDlg(0), msnEventDlg(0), msnEditNavDlg(0),
+ netClientDlg(0), netAddrDlg(0), netPassDlg(0), netLobbyDlg(0), netServerDlg(0),
+ netUnitDlg(0), loadDlg(0), tacRefDlg(0), current_dlg(0), isShown(false)
+{
+ loader = DataLoader::GetLoader();
+}
+
+MenuScreen::~MenuScreen()
+{
+ TearDown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::Setup(Screen* s)
+{
+ if (!s)
+ return;
+
+ screen = s;
+
+ Color::SetFade(0);
+
+ // create windows
+
+ loader->UseFileSystem(true);
+
+ FormDef menu_def("MenuDlg", 0);
+ menu_def.Load("MenuDlg");
+ menudlg = new(__FILE__,__LINE__) MenuDlg(screen, menu_def, this);
+
+ FormDef exit_def("ExitDlg", 0);
+ exit_def.Load("ExitDlg");
+ exitdlg = new(__FILE__,__LINE__) ExitDlg(screen, exit_def, this);
+
+ FormDef aud_def("AudDlg", 0);
+ aud_def.Load("AudDlg");
+ auddlg = new(__FILE__,__LINE__) AudDlg(screen, aud_def, this);
+
+ FormDef ctl_def("CtlDlg", 0);
+ ctl_def.Load("CtlDlg");
+ ctldlg = new(__FILE__,__LINE__) CtlDlg(screen, ctl_def, this);
+
+ FormDef opt_def("OptDlg", 0);
+ opt_def.Load("OptDlg");
+ optdlg = new(__FILE__,__LINE__) OptDlg(screen, opt_def, this);
+
+ FormDef vid_def("VidDlg", 0);
+ vid_def.Load("VidDlg");
+ viddlg = new(__FILE__,__LINE__) VidDlg(screen, vid_def, this);
+
+ FormDef mod_def("ModDlg", 0);
+ mod_def.Load("ModDlg");
+ modDlg = new(__FILE__,__LINE__) ModDlg(screen, mod_def, this);
+
+ FormDef tac_ref_def("TacRefDlg", 0);
+ tac_ref_def.Load("TacRefDlg");
+ tacRefDlg = new(__FILE__,__LINE__) TacRefDlg(screen, tac_ref_def, this);
+
+ FormDef cmp_sel_def("CmpSelectDlg", 0);
+ cmp_sel_def.Load("CmpSelectDlg");
+ cmpSelectDlg = new(__FILE__,__LINE__) CmpSelectDlg(screen, cmp_sel_def, this);
+
+ FormDef net_lobby_def("NetLobbyDlg", 0);
+ net_lobby_def.Load("NetLobbyDlg");
+ netLobbyDlg = new(__FILE__,__LINE__) NetLobbyDlg(screen, net_lobby_def, this);
+
+ FormDef net_client_def("NetClientDlg", 0);
+ net_client_def.Load("NetClientDlg");
+ netClientDlg = new(__FILE__,__LINE__) NetClientDlg(screen, net_client_def, this);
+
+ FormDef net_server_def("NetServerDlg", 0);
+ net_server_def.Load("NetServerDlg");
+ netServerDlg = new(__FILE__,__LINE__) NetServerDlg(screen, net_server_def, this);
+
+ FormDef net_unit_def("NetUnitDlg", 0);
+ net_unit_def.Load("NetUnitDlg");
+ netUnitDlg = new(__FILE__,__LINE__) NetUnitDlg(screen, net_unit_def, this);
+
+ FormDef net_addr_def("NetAddrDlg", 0);
+ net_addr_def.Load("NetAddrDlg");
+ netAddrDlg = new(__FILE__,__LINE__) NetAddrDlg(screen, net_addr_def, this);
+
+ FormDef net_pass_def("NetPassDlg", 0);
+ net_pass_def.Load("NetPassDlg");
+ netPassDlg = new(__FILE__,__LINE__) NetPassDlg(screen, net_pass_def, this);
+
+ FormDef msn_edit_def("MsnEditDlg", 0);
+ msn_edit_def.Load("MsnEditDlg");
+ msnEditDlg = new(__FILE__,__LINE__) MsnEditDlg(screen, msn_edit_def, this);
+
+ FormDef msn_nav_def("MsnEditNavDlg", 0);
+ msn_nav_def.Load("MsnEditNavDlg");
+ msnEditNavDlg = new(__FILE__,__LINE__) MsnEditNavDlg(screen, msn_nav_def, this);
+
+ FormDef msn_elem_def("MsnElemDlg", 0);
+ msn_elem_def.Load("MsnElemDlg");
+ msnElemDlg = new(__FILE__,__LINE__) MsnElemDlg(screen, msn_elem_def, this);
+
+ FormDef msn_event_def("MsnEventDlg", 0);
+ msn_event_def.Load("MsnEventDlg");
+ msnEventDlg = new(__FILE__,__LINE__) MsnEventDlg(screen, msn_event_def, this);
+
+ FormDef msn_def("MsnSelectDlg", 0);
+ msn_def.Load("MsnSelectDlg");
+ msnSelectDlg = new(__FILE__,__LINE__) MsnSelectDlg(screen, msn_def, this);
+
+ FormDef player_def("PlayerDlg", 0);
+ player_def.Load("PlayerDlg");
+ playdlg = new(__FILE__,__LINE__) PlayerDlg(screen, player_def, this);
+
+ FormDef award_def("AwardDlg", 0);
+ award_def.Load("AwardDlg");
+ awarddlg = new(__FILE__,__LINE__) AwardShowDlg(screen, award_def, this);
+
+ FormDef joy_def("JoyDlg", 0);
+ joy_def.Load("JoyDlg");
+ joydlg = new(__FILE__,__LINE__) JoyDlg(screen, joy_def, this);
+
+ FormDef key_def("KeyDlg", 0);
+ key_def.Load("KeyDlg");
+ keydlg = new(__FILE__,__LINE__) KeyDlg(screen, key_def, this);
+
+ FormDef first_def("FirstTimeDlg", 0);
+ first_def.Load("FirstTimeDlg");
+ firstdlg = new(__FILE__,__LINE__) FirstTimeDlg(screen, first_def, this);
+
+ FormDef mod_info_def("ModInfoDlg", 0);
+ mod_info_def.Load("ModInfoDlg");
+ modInfoDlg = new(__FILE__,__LINE__) ModInfoDlg(screen, mod_info_def, this);
+
+ FormDef confirm_def("ConfirmDlg", 0);
+ confirm_def.Load("ConfirmDlg");
+ confirmdlg = new(__FILE__,__LINE__) ConfirmDlg(screen, confirm_def, this);
+
+ FormDef load_def("LoadDlg", 0);
+ load_def.Load("LoadDlg");
+ loadDlg = new(__FILE__,__LINE__) LoadDlg(screen, load_def);
+
+ fadewin = new(__FILE__,__LINE__) Window(screen, 0, 0, 1, 1);
+ fadeview = new(__FILE__,__LINE__) FadeView(fadewin, 2, 0, 0);
+ fadewin->AddView(fadeview);
+ screen->AddWindow(fadewin);
+
+ loader->UseFileSystem(Starshatter::UseFileSystem());
+
+ ShowMenuDlg();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::TearDown()
+{
+ if (screen) {
+ if (menudlg) screen->DelWindow(menudlg);
+ if (netClientDlg) screen->DelWindow(netClientDlg);
+ if (netAddrDlg) screen->DelWindow(netAddrDlg);
+ if (netPassDlg) screen->DelWindow(netPassDlg);
+ if (netLobbyDlg) screen->DelWindow(netLobbyDlg);
+ if (netServerDlg) screen->DelWindow(netServerDlg);
+ if (netUnitDlg) screen->DelWindow(netUnitDlg);
+
+ if (cmpSelectDlg) screen->DelWindow(cmpSelectDlg);
+ if (awarddlg) screen->DelWindow(awarddlg);
+ if (firstdlg) screen->DelWindow(firstdlg);
+ if (msnSelectDlg) screen->DelWindow(msnSelectDlg);
+ if (msnEditDlg) screen->DelWindow(msnEditDlg);
+ if (msnElemDlg) screen->DelWindow(msnElemDlg);
+ if (msnEventDlg) screen->DelWindow(msnEventDlg);
+ if (msnEditNavDlg) screen->DelWindow(msnEditNavDlg);
+ if (tacRefDlg) screen->DelWindow(tacRefDlg);
+ if (loadDlg) screen->DelWindow(loadDlg);
+
+ if (auddlg) screen->DelWindow(auddlg);
+ if (viddlg) screen->DelWindow(viddlg);
+ if (optdlg) screen->DelWindow(optdlg);
+ if (ctldlg) screen->DelWindow(ctldlg);
+ if (modDlg) screen->DelWindow(modDlg);
+ if (modInfoDlg) screen->DelWindow(modInfoDlg);
+ if (joydlg) screen->DelWindow(joydlg);
+ if (keydlg) screen->DelWindow(keydlg);
+ if (exitdlg) screen->DelWindow(exitdlg);
+ if (playdlg) screen->DelWindow(playdlg);
+ if (confirmdlg) screen->DelWindow(confirmdlg);
+ if (fadewin) screen->DelWindow(fadewin);
+ }
+
+ delete menudlg;
+ delete fadewin;
+ delete exitdlg;
+ delete auddlg;
+ delete viddlg;
+ delete optdlg;
+ delete ctldlg;
+ delete modDlg;
+ delete modInfoDlg;
+ delete joydlg;
+ delete keydlg;
+ delete playdlg;
+ delete confirmdlg;
+
+ delete netClientDlg;
+ delete netAddrDlg;
+ delete netPassDlg;
+ delete netLobbyDlg;
+ delete netServerDlg;
+ delete netUnitDlg;
+ delete msnSelectDlg;
+ delete msnEditDlg;
+ delete msnElemDlg;
+ delete msnEventDlg;
+ delete msnEditNavDlg;
+ delete tacRefDlg;
+ delete loadDlg;
+ delete firstdlg;
+ delete awarddlg;
+ delete cmpSelectDlg;
+
+ screen = 0;
+ fadewin = 0;
+ fadeview = 0;
+ menudlg = 0;
+ exitdlg = 0;
+ playdlg = 0;
+ confirmdlg = 0;
+ msnSelectDlg = 0;
+ msnEditDlg = 0;
+ msnElemDlg = 0;
+ msnEventDlg = 0;
+ msnEditNavDlg = 0;
+ cmpSelectDlg = 0;
+ awarddlg = 0;
+ firstdlg = 0;
+ netClientDlg = 0;
+ netAddrDlg = 0;
+ netPassDlg = 0;
+ netLobbyDlg = 0;
+ netServerDlg = 0;
+ netUnitDlg = 0;
+ loadDlg = 0;
+ tacRefDlg = 0;
+
+ auddlg = 0;
+ viddlg = 0;
+ optdlg = 0;
+ ctldlg = 0;
+ modDlg = 0;
+ modInfoDlg = 0;
+ joydlg = 0;
+ keydlg = 0;
+
+ screen = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ExecFrame()
+{
+ Game::SetScreenColor(Color::Black);
+
+ if (menudlg && menudlg->IsShown())
+ menudlg->ExecFrame();
+
+ if (exitdlg && exitdlg->IsShown())
+ exitdlg->ExecFrame();
+
+ if (joydlg && joydlg->IsShown())
+ joydlg->ExecFrame();
+
+ if (keydlg && keydlg->IsShown())
+ keydlg->ExecFrame();
+
+ if (ctldlg && ctldlg->IsShown())
+ ctldlg->ExecFrame();
+
+ if (optdlg && optdlg->IsShown())
+ optdlg->ExecFrame();
+
+ if (auddlg && auddlg->IsShown())
+ auddlg->ExecFrame();
+
+ if (viddlg && viddlg->IsShown())
+ viddlg->ExecFrame();
+
+ if (confirmdlg && confirmdlg->IsShown())
+ confirmdlg->ExecFrame();
+
+ if (playdlg && playdlg->IsShown())
+ playdlg->ExecFrame();
+
+ if (msnSelectDlg && msnSelectDlg->IsShown())
+ msnSelectDlg->ExecFrame();
+
+ if (msnEditNavDlg && msnEditNavDlg->IsShown())
+ msnEditNavDlg->ExecFrame();
+
+ if (firstdlg && firstdlg->IsShown())
+ firstdlg->ExecFrame();
+
+ if (awarddlg && awarddlg->IsShown())
+ awarddlg->ExecFrame();
+
+ if (cmpSelectDlg && cmpSelectDlg->IsShown())
+ cmpSelectDlg->ExecFrame();
+
+ if (netClientDlg && netClientDlg->IsShown())
+ netClientDlg->ExecFrame();
+
+ if (netAddrDlg && netAddrDlg->IsShown())
+ netAddrDlg->ExecFrame();
+
+ if (netPassDlg && netPassDlg->IsShown())
+ netPassDlg->ExecFrame();
+
+ if (netLobbyDlg && netLobbyDlg->IsShown())
+ netLobbyDlg->ExecFrame();
+
+ if (netServerDlg && netServerDlg->IsShown())
+ netServerDlg->ExecFrame();
+
+ if (netUnitDlg && netUnitDlg->IsShown())
+ netUnitDlg->ExecFrame();
+
+ if (loadDlg && loadDlg->IsShown())
+ loadDlg->ExecFrame();
+
+ if (tacRefDlg && tacRefDlg->IsShown())
+ tacRefDlg->ExecFrame();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+MenuScreen::CloseTopmost()
+{
+ bool processed = false;
+ if (joydlg && joydlg->IsShown()) {
+ ShowCtlDlg();
+ processed = true;
+ }
+
+ else if (keydlg && keydlg->IsShown()) {
+ ShowCtlDlg();
+ processed = true;
+ }
+
+ else if (msnElemDlg && msnElemDlg->IsShown()) {
+ HideMsnElemDlg();
+ processed = true;
+ }
+
+ else if (msnEventDlg && msnEventDlg->IsShown()) {
+ HideMsnEventDlg();
+ processed = true;
+ }
+
+ else if (netAddrDlg && netAddrDlg->IsShown()) {
+ ShowNetClientDlg();
+ processed = true;
+ }
+
+ else if (netPassDlg && netPassDlg->IsShown()) {
+ ShowNetClientDlg();
+ processed = true;
+ }
+
+ else if (netServerDlg && netServerDlg->IsShown()) {
+ ShowNetClientDlg();
+ processed = true;
+ }
+
+ else if (netUnitDlg && netUnitDlg->IsShown()) {
+ netUnitDlg->OnCancel(0);
+ processed = true;
+ }
+
+ else if (netLobbyDlg && netLobbyDlg->IsShown()) {
+ netLobbyDlg->OnCancel(0);
+ processed = true;
+ }
+
+ else if (netClientDlg && netClientDlg->IsShown()) {
+ netClientDlg->OnCancel(0);
+ processed = true;
+ }
+
+ else if (exitdlg && exitdlg->IsShown()) {
+ // key_exit is handled in the exit dlg...
+ }
+
+ else if (cmpSelectDlg && cmpSelectDlg->IsShown()) {
+ if (cmpSelectDlg->CanClose())
+ ShowMenuDlg();
+
+ processed = true;
+ }
+
+ else if (menudlg && !menudlg->IsShown()) {
+ ShowMenuDlg();
+ processed = true;
+ }
+
+ return processed;
+}
+
+void
+MenuScreen::Show()
+{
+ if (!isShown) {
+ Starshatter* stars = Starshatter::GetInstance();
+ NetLobby* lobby = NetLobby::GetInstance();
+
+ if (lobby) {
+ ShowNetLobbyDlg();
+ }
+ else if (current_dlg == msnSelectDlg) {
+ ShowMsnSelectDlg();
+ }
+ else {
+ if (Player::ConfigExists()) {
+ ShowMenuDlg();
+ }
+
+ else {
+ ShowFirstTimeDlg();
+ }
+ }
+
+ isShown = true;
+ }
+}
+
+void
+MenuScreen::Hide()
+{
+ if (isShown) {
+ HideAll();
+ isShown = false;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowMenuDlg()
+{
+ HideAll();
+ if (menudlg) {
+ menudlg->Show();
+ menudlg->SetTopMost(true);
+ }
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowCmpSelectDlg()
+{
+ HideAll();
+ current_dlg = cmpSelectDlg;
+ if (cmpSelectDlg)
+ cmpSelectDlg->Show();
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowMsnSelectDlg()
+{
+ HideAll();
+ current_dlg = msnSelectDlg;
+
+ if (msnSelectDlg)
+ msnSelectDlg->Show();
+
+ if (msnEditNavDlg)
+ msnEditNavDlg->SetMission(0);
+
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowModDlg()
+{
+ if (modDlg) {
+ HideAll();
+ modDlg->Show();
+ modDlg->SetTopMost(true);
+ Mouse::Show(true);
+ }
+ else {
+ ShowMsnSelectDlg();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowModInfoDlg()
+{
+ if (modDlg && modInfoDlg) {
+ HideAll();
+ modDlg->Show();
+ modDlg->SetTopMost(false);
+ modInfoDlg->Show();
+ Mouse::Show(true);
+ }
+ else {
+ ShowMsnSelectDlg();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowMsnEditDlg()
+{
+ if (msnEditDlg) {
+ bool nav_shown = false;
+ if (msnEditNavDlg && msnEditNavDlg->IsShown())
+ nav_shown = true;
+
+ HideAll();
+
+ if (nav_shown) {
+ msnEditNavDlg->Show();
+ msnEditNavDlg->SetTopMost(true);
+ }
+ else {
+ msnEditDlg->Show();
+ msnEditDlg->SetTopMost(true);
+ }
+
+ Mouse::Show(true);
+ }
+ else {
+ ShowMsnSelectDlg();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowMsnElemDlg()
+{
+ if (msnElemDlg) {
+ if (msnEditDlg && msnEditDlg->IsShown())
+ msnEditDlg->SetTopMost(false);
+
+ if (msnEditNavDlg && msnEditNavDlg->IsShown())
+ msnEditNavDlg->SetTopMost(false);
+
+ msnElemDlg->Show();
+ msnElemDlg->SetTopMost(true);
+ Mouse::Show(true);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowMsnEventDlg()
+{
+ if (msnEventDlg) {
+ if (msnEditDlg && msnEditDlg->IsShown())
+ msnEditDlg->SetTopMost(false);
+
+ if (msnEditNavDlg && msnEditNavDlg->IsShown())
+ msnEditNavDlg->SetTopMost(false);
+
+ msnEventDlg->Show();
+ msnEventDlg->SetTopMost(true);
+ Mouse::Show(true);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowNavDlg()
+{
+ if (msnEditNavDlg && !msnEditNavDlg->IsShown()) {
+ HideAll();
+ msnEditNavDlg->Show();
+ Mouse::Show(true);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowFirstTimeDlg()
+{
+ HideAll();
+ if (menudlg && firstdlg) {
+ menudlg->Show();
+ menudlg->SetTopMost(false);
+
+ firstdlg->Show();
+ firstdlg->SetTopMost(true);
+ }
+
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowPlayerDlg()
+{
+ if (playdlg) {
+ HideAll();
+ playdlg->Show();
+ }
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowAwardDlg()
+{
+ if (awarddlg) {
+ HideAll();
+ awarddlg->Show();
+ }
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowTacRefDlg()
+{
+ if (tacRefDlg) {
+ HideAll();
+ tacRefDlg->Show();
+ }
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowNetClientDlg()
+{
+ if (netClientDlg) {
+ HideAll();
+ netClientDlg->Show();
+ netClientDlg->SetTopMost(true);
+ }
+
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowNetAddrDlg()
+{
+ if (netAddrDlg) {
+ if (netClientDlg) {
+ netClientDlg->Show();
+ netClientDlg->SetTopMost(false);
+ }
+
+ netAddrDlg->Show();
+ }
+
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowNetPassDlg()
+{
+ if (netPassDlg) {
+ ShowNetClientDlg();
+ if (netClientDlg)
+ netClientDlg->SetTopMost(false);
+
+ netPassDlg->Show();
+ }
+
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowNetLobbyDlg()
+{
+ if (netLobbyDlg) {
+ HideAll();
+ netLobbyDlg->Show();
+ netLobbyDlg->SetTopMost(true);
+ }
+
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowNetServerDlg()
+{
+ if (netServerDlg) {
+ netServerDlg->Show();
+ netServerDlg->SetTopMost(true);
+ }
+
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowNetUnitDlg()
+{
+ if (netUnitDlg) {
+ HideAll();
+ netUnitDlg->Show();
+ netUnitDlg->SetTopMost(true);
+ }
+
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowAudDlg()
+{
+ HideAll();
+ if (auddlg)
+ auddlg->Show();
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowVidDlg()
+{
+ HideAll();
+ if (viddlg)
+ viddlg->Show();
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowOptDlg()
+{
+ HideAll();
+ if (optdlg)
+ optdlg->Show();
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowCtlDlg()
+{
+ HideAll();
+ if (ctldlg) {
+ ctldlg->Show();
+ ctldlg->SetTopMost(true);
+ }
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowJoyDlg()
+{
+ HideAll();
+ if (ctldlg) {
+ ctldlg->Show();
+ ctldlg->SetTopMost(false);
+ }
+
+ if (joydlg)
+ joydlg->Show();
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowKeyDlg()
+{
+ HideAll();
+
+ if (ctldlg) {
+ ctldlg->Show();
+ ctldlg->SetTopMost(false);
+ }
+
+ if (keydlg)
+ keydlg->Show();
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowConfirmDlg()
+{
+ if (confirmdlg) {
+ if (msnSelectDlg && msnSelectDlg->IsShown())
+ msnSelectDlg->SetTopMost(false);
+
+ if (playdlg && playdlg->IsShown())
+ playdlg->SetTopMost(false);
+
+ confirmdlg->Show();
+ confirmdlg->SetTopMost(true);
+ Mouse::Show(true);
+ }
+}
+
+void
+MenuScreen::HideConfirmDlg()
+{
+ if (confirmdlg)
+ confirmdlg->Hide();
+
+ if (msnSelectDlg && msnSelectDlg->IsShown())
+ msnSelectDlg->SetTopMost(true);
+
+ if (playdlg && playdlg->IsShown())
+ playdlg->SetTopMost(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowLoadDlg()
+{
+ if (loadDlg) {
+ if (menudlg && menudlg->IsShown())
+ menudlg->SetTopMost(false);
+
+ loadDlg->Show();
+ loadDlg->SetTopMost(true);
+ Mouse::Show(true);
+ }
+}
+
+void
+MenuScreen::HideLoadDlg()
+{
+ if (loadDlg)
+ loadDlg->Hide();
+
+ if (menudlg && menudlg->IsShown())
+ menudlg->SetTopMost(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ShowExitDlg()
+{
+ HideAll();
+
+ if (menudlg) {
+ menudlg->Show();
+ menudlg->SetTopMost(false);
+ }
+
+ if (exitdlg)
+ exitdlg->Show();
+ else
+ Starshatter::GetInstance()->Exit();
+
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::HideNavDlg()
+{
+ if (msnEditNavDlg)
+ msnEditNavDlg->Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::HideMsnElemDlg()
+{
+ if (msnElemDlg)
+ msnElemDlg->Hide();
+
+ if (msnEditDlg && msnEditDlg->IsShown())
+ msnEditDlg->SetTopMost(true);
+
+ if (msnEditNavDlg && msnEditNavDlg->IsShown())
+ msnEditNavDlg->SetTopMost(true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::HideMsnEventDlg()
+{
+ if (msnEventDlg)
+ msnEventDlg->Hide();
+
+ if (msnEditDlg && msnEditDlg->IsShown())
+ msnEditDlg->SetTopMost(true);
+
+ if (msnEditNavDlg && msnEditNavDlg->IsShown())
+ msnEditNavDlg->SetTopMost(true);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+MenuScreen::IsNavShown()
+{
+ return msnEditNavDlg && msnEditNavDlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::HideAll()
+{
+ Keyboard::FlushKeys();
+
+ current_dlg = 0;
+
+ if (menudlg) menudlg->Hide();
+ if (exitdlg) exitdlg->Hide();
+ if (auddlg) auddlg->Hide();
+ if (viddlg) viddlg->Hide();
+ if (ctldlg) ctldlg->Hide();
+ if (optdlg) optdlg->Hide();
+ if (joydlg) joydlg->Hide();
+ if (keydlg) keydlg->Hide();
+ if (playdlg) playdlg->Hide();
+ if (confirmdlg) confirmdlg->Hide();
+ if (modDlg) modDlg->Hide();
+ if (modInfoDlg) modInfoDlg->Hide();
+ if (msnSelectDlg) msnSelectDlg->Hide();
+ if (msnEditDlg) msnEditDlg->Hide();
+ if (msnElemDlg) msnElemDlg->Hide();
+ if (msnEventDlg) msnEventDlg->Hide();
+ if (msnEditNavDlg) msnEditNavDlg->Hide();
+ if (netClientDlg) netClientDlg->Hide();
+ if (netAddrDlg) netAddrDlg->Hide();
+ if (netPassDlg) netPassDlg->Hide();
+ if (netLobbyDlg) netLobbyDlg->Hide();
+ if (netServerDlg) netServerDlg->Hide();
+ if (netUnitDlg) netUnitDlg->Hide();
+ if (firstdlg) firstdlg->Hide();
+ if (awarddlg) awarddlg->Hide();
+ if (cmpSelectDlg) cmpSelectDlg->Hide();
+ if (tacRefDlg) tacRefDlg->Hide();
+ if (loadDlg) loadDlg->Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuScreen::ApplyOptions()
+{
+ if (ctldlg) ctldlg->Apply();
+ if (optdlg) optdlg->Apply();
+ if (auddlg) auddlg->Apply();
+ if (viddlg) viddlg->Apply();
+ if (modDlg) modDlg->Apply();
+
+ ShowMenuDlg();
+}
+
+void
+MenuScreen::CancelOptions()
+{
+ if (ctldlg) ctldlg->Cancel();
+ if (optdlg) optdlg->Cancel();
+ if (auddlg) auddlg->Cancel();
+ if (viddlg) viddlg->Cancel();
+ if (modDlg) modDlg->Cancel();
+
+ ShowMenuDlg();
+}
diff --git a/Stars45/MenuScreen.h b/Stars45/MenuScreen.h
new file mode 100644
index 0000000..fea3e1c
--- /dev/null
+++ b/Stars45/MenuScreen.h
@@ -0,0 +1,199 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MenuScreen.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef MenuScreen_h
+#define MenuScreen_h
+
+#include "Types.h"
+#include "Bitmap.h"
+#include "Screen.h"
+#include "BaseScreen.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuDlg;
+class AudDlg;
+class VidDlg;
+class OptDlg;
+class CtlDlg;
+class JoyDlg;
+class KeyDlg;
+class ExitDlg;
+class ConfirmDlg;
+
+class FirstTimeDlg;
+class PlayerDlg;
+class AwardShowDlg;
+
+class MsnSelectDlg;
+class CmpSelectDlg;
+class ModDlg;
+class ModInfoDlg;
+class MsnEditDlg;
+class MsnElemDlg;
+class MsnEventDlg;
+
+class NetClientDlg;
+class NetAddrDlg;
+class NetPassDlg;
+class NetLobbyDlg;
+class NetServerDlg;
+class NetUnitDlg;
+
+class LoadDlg;
+class TacRefDlg;
+
+class ActiveWindow;
+class Bitmap;
+class DataLoader;
+class FadeView;
+class Font;
+class FormWindow;
+class Screen;
+class Video;
+class VideoFactory;
+class Window;
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen : public BaseScreen
+{
+public:
+ MenuScreen();
+ virtual ~MenuScreen();
+
+ virtual void Setup(Screen* screen);
+ virtual void TearDown();
+ virtual bool CloseTopmost();
+
+ virtual bool IsShown() const { return isShown; }
+ virtual void Show();
+ virtual void Hide();
+
+ virtual void ExecFrame();
+
+ virtual void ShowMenuDlg();
+ virtual void ShowCmpSelectDlg();
+ virtual void ShowMsnSelectDlg();
+ virtual void ShowModDlg();
+ virtual void ShowModInfoDlg();
+ virtual void ShowMsnEditDlg();
+ virtual void ShowNetClientDlg();
+ virtual void ShowNetAddrDlg();
+ virtual void ShowNetPassDlg();
+ virtual void ShowNetLobbyDlg();
+ virtual void ShowNetServerDlg();
+ virtual void ShowNetUnitDlg();
+ virtual void ShowFirstTimeDlg();
+ virtual void ShowPlayerDlg();
+ virtual void ShowTacRefDlg();
+ virtual void ShowAwardDlg();
+ virtual void ShowAudDlg();
+ virtual void ShowVidDlg();
+ virtual void ShowOptDlg();
+ virtual void ShowCtlDlg();
+ virtual void ShowJoyDlg();
+ virtual void ShowKeyDlg();
+ virtual void ShowExitDlg();
+ virtual void ShowConfirmDlg();
+ virtual void HideConfirmDlg();
+ virtual void ShowLoadDlg();
+ virtual void HideLoadDlg();
+
+ // base screen interface:
+ virtual void ShowMsnElemDlg();
+ virtual void HideMsnElemDlg();
+ virtual MsnElemDlg* GetMsnElemDlg() { return msnElemDlg; }
+
+ virtual void ShowMsnEventDlg();
+ virtual void HideMsnEventDlg();
+ virtual MsnEventDlg* GetMsnEventDlg() { return msnEventDlg; }
+
+ virtual void ShowNavDlg();
+ virtual void HideNavDlg();
+ virtual bool IsNavShown();
+ virtual NavDlg* GetNavDlg() { return msnEditNavDlg; }
+
+ virtual MsnSelectDlg* GetMsnSelectDlg() const { return msnSelectDlg; }
+ virtual ModDlg* GetModDlg() const { return modDlg; }
+ virtual ModInfoDlg* GetModInfoDlg() const { return modInfoDlg; }
+ virtual MsnEditDlg* GetMsnEditDlg() const { return msnEditDlg; }
+ virtual NetClientDlg* GetNetClientDlg() const { return netClientDlg; }
+ virtual NetAddrDlg* GetNetAddrDlg() const { return netAddrDlg; }
+ virtual NetPassDlg* GetNetPassDlg() const { return netPassDlg; }
+ virtual NetLobbyDlg* GetNetLobbyDlg() const { return netLobbyDlg; }
+ virtual NetServerDlg* GetNetServerDlg() const { return netServerDlg; }
+ virtual NetUnitDlg* GetNetUnitDlg() const { return netUnitDlg; }
+ virtual LoadDlg* GetLoadDlg() const { return loadDlg; }
+ virtual TacRefDlg* GetTacRefDlg() const { return tacRefDlg; }
+
+ virtual AudDlg* GetAudDlg() const { return auddlg; }
+ virtual VidDlg* GetVidDlg() const { return viddlg; }
+ virtual OptDlg* GetOptDlg() const { return optdlg; }
+ virtual CtlDlg* GetCtlDlg() const { return ctldlg; }
+ virtual JoyDlg* GetJoyDlg() const { return joydlg; }
+ virtual KeyDlg* GetKeyDlg() const { return keydlg; }
+ virtual ExitDlg* GetExitDlg() const { return exitdlg; }
+ virtual FirstTimeDlg* GetFirstTimeDlg() const { return firstdlg; }
+ virtual PlayerDlg* GetPlayerDlg() const { return playdlg; }
+ virtual AwardShowDlg* GetAwardDlg() const { return awarddlg; }
+ virtual ConfirmDlg* GetConfirmDlg() const { return confirmdlg; }
+
+ virtual void ApplyOptions();
+ virtual void CancelOptions();
+
+private:
+ void HideAll();
+
+ Screen* screen;
+ MenuDlg* menudlg;
+
+ Window* fadewin;
+ FadeView* fadeview;
+ ExitDlg* exitdlg;
+ AudDlg* auddlg;
+ VidDlg* viddlg;
+ OptDlg* optdlg;
+ CtlDlg* ctldlg;
+ JoyDlg* joydlg;
+ KeyDlg* keydlg;
+ ConfirmDlg* confirmdlg;
+ PlayerDlg* playdlg;
+ AwardShowDlg* awarddlg;
+ ModDlg* modDlg;
+ ModInfoDlg* modInfoDlg;
+ MsnSelectDlg* msnSelectDlg;
+ MsnEditDlg* msnEditDlg;
+ MsnElemDlg* msnElemDlg;
+ MsnEventDlg* msnEventDlg;
+ NavDlg* msnEditNavDlg;
+ LoadDlg* loadDlg;
+ TacRefDlg* tacRefDlg;
+
+ CmpSelectDlg* cmpSelectDlg;
+ FirstTimeDlg* firstdlg;
+ NetClientDlg* netClientDlg;
+ NetAddrDlg* netAddrDlg;
+ NetPassDlg* netPassDlg;
+ NetLobbyDlg* netLobbyDlg;
+ NetServerDlg* netServerDlg;
+ NetUnitDlg* netUnitDlg;
+
+ FormWindow* current_dlg;
+ DataLoader* loader;
+
+ int wc, hc;
+ bool isShown;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif MenuScreen_h
+
diff --git a/Stars45/MenuView.cpp b/Stars45/MenuView.cpp
new file mode 100644
index 0000000..fec343b
--- /dev/null
+++ b/Stars45/MenuView.cpp
@@ -0,0 +1,333 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MenuView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+
+*/
+
+#include "MemDebug.h"
+#include "MenuView.h"
+
+#include "Color.h"
+#include "Window.h"
+#include "Video.h"
+#include "DataLoader.h"
+#include "Scene.h"
+#include "FontMgr.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "MouseController.h"
+#include "Menu.h"
+#include "Button.h"
+#include "Game.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+
+MenuView::MenuView(Window* c)
+ : View(c),
+ mouse_down(0), right_down(0), shift_down(0), show_menu(false),
+ action(0), menu(0), menu_item(0), selected(0),
+ text_color(Color::White), back_color(Color::Black)
+{
+ right_start.x = 0;
+ right_start.y = 0;
+
+ OnWindowMove();
+}
+
+MenuView::~MenuView()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuView::OnWindowMove()
+{
+ offset.x = window->X();
+ offset.y = window->Y();
+ width = window->Width();
+ height = window->Height();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuView::Refresh()
+{
+ if (show_menu)
+ DrawMenu();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuView::DoMouseFrame()
+{
+ static DWORD rbutton_latch = 0;
+
+ action = 0;
+
+ if (Mouse::RButton()) {
+ MouseController* mouse_con = MouseController::GetInstance();
+ if (!right_down && (!mouse_con || !mouse_con->Active())) {
+ rbutton_latch = Game::RealTime();
+ right_down = true;
+ show_menu = false;
+ }
+ }
+ else {
+ if (right_down && (Game::RealTime() - rbutton_latch < 250)) {
+ right_start.x = Mouse::X() - offset.x;
+ right_start.y = Mouse::Y() - offset.y;
+ show_menu = true;
+ Button::PlaySound(Button::SND_MENU_OPEN);
+ }
+
+ right_down = false;
+ }
+
+ MouseController* mouse_con = MouseController::GetInstance();
+
+ if (!mouse_con || !mouse_con->Active()) {
+ if (Mouse::LButton()) {
+ if (!mouse_down)
+ shift_down = Keyboard::KeyDown(VK_SHIFT);
+
+ mouse_down = true;
+ }
+
+ else if (mouse_down) {
+ int mouse_x = Mouse::X() - offset.x;
+ int mouse_y = Mouse::Y() - offset.y;
+ int keep_menu = false;
+
+ if (show_menu) {
+ keep_menu = ProcessMenuItem();
+ Mouse::Show(true);
+ }
+
+ mouse_down = false;
+
+ if (!keep_menu) {
+ ClearMenuSelection(menu);
+ show_menu = false;
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MenuView::ProcessMenuItem()
+{
+ if (!menu_item || !menu_item->GetEnabled())
+ return false;
+
+ if (menu_item->GetSubmenu()) {
+ ListIter<MenuItem> item = menu_item->GetMenu()->GetItems();
+ while (++item)
+ if (item.value() == menu_item)
+ item->SetSelected(2);
+ else
+ item->SetSelected(0);
+
+ Button::PlaySound(Button::SND_MENU_OPEN);
+ return true; // keep menu showing
+ }
+
+ action = menu_item->GetData();
+ Button::PlaySound(Button::SND_MENU_SELECT);
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuView::DrawMenu()
+{
+ menu_item = 0;
+
+ if (menu) {
+ int mx = right_start.x - 2;
+ int my = right_start.y - 2;
+
+ DrawMenu(mx, my, menu);
+ }
+}
+
+void
+MenuView::DrawMenu(int mx, int my, Menu* m)
+{
+ if (m) {
+ MenuItem* locked_item = 0;
+ Menu* submenu = 0;
+ int subx = 0;
+ int suby = 0;
+ Font* font = FontMgr::Find("HUD");
+
+ Rect menu_rect(mx, my, 100, m->NumItems() * 10 + 6);
+
+ int max_width = 0;
+ int max_height = 0;
+ int extra_width = 16;
+
+ ListIter<MenuItem> item = m->GetItems();
+ while (++item) {
+ menu_rect.w = width/2;
+
+ if (item->GetText().length()) {
+ window->SetFont(font);
+ window->DrawText(item->GetText(), 0, menu_rect, DT_LEFT|DT_SINGLELINE|DT_CALCRECT);
+ if (menu_rect.w > max_width)
+ max_width = menu_rect.w;
+ max_height += 11;
+
+ if (item->GetSubmenu())
+ extra_width = 28;
+
+ if (item->GetSelected() > 1)
+ locked_item = item.value();
+ }
+ else {
+ max_height += 4;
+ }
+ }
+
+ menu_rect.h = max_height + 6;
+ menu_rect.w = max_width + extra_width;
+
+ if (menu_rect.x + menu_rect.w >= width)
+ menu_rect.x = width - menu_rect.w - 2;
+
+ if (menu_rect.y + menu_rect.h >= height)
+ menu_rect.y = height - menu_rect.h - 2;
+
+ window->FillRect(menu_rect, back_color * 0.2);
+ window->DrawRect(menu_rect, back_color);
+
+ Rect item_rect = menu_rect;
+
+ item_rect.x += 4;
+ item_rect.y += 3;
+ item_rect.w -= 8;
+ item_rect.h = 12;
+
+ item.reset();
+ while (++item) {
+ int line_height = 0;
+
+ if (item->GetText().length()) {
+ Rect fill_rect = item_rect;
+ fill_rect.Inflate(2,-1);
+ fill_rect.y -= 1;
+
+ int mx = Mouse::X() - offset.x;
+ int my = Mouse::Y() - offset.y;
+
+ // is this item picked?
+ if (menu_rect.Contains(mx, my)) {
+ if (my >= fill_rect.y && my <= fill_rect.y+fill_rect.h) {
+ if (Mouse::LButton()) {
+ menu_item = item.value();
+ item->SetSelected(2);
+ if (locked_item && locked_item->GetMenu() == m)
+ locked_item->SetSelected(0);
+ locked_item = menu_item;
+ }
+ else if (!locked_item || locked_item->GetMenu() != m) {
+ item->SetSelected(true);
+ menu_item = item.value();
+ }
+
+ if (menu_item && menu_item != selected) {
+ selected = menu_item;
+ Button::PlaySound(Button::SND_MENU_HILITE);
+ }
+ }
+ else if (item.value() != locked_item) {
+ item->SetSelected(false);
+ }
+ }
+
+ if (item->GetSelected()) {
+ window->FillRect(fill_rect, back_color * 0.35);
+ window->DrawRect(fill_rect, back_color * 0.75);
+
+ if (item->GetSubmenu()) {
+ submenu = item->GetSubmenu();
+ subx = menu_rect.x + max_width + extra_width;
+ suby = fill_rect.y - 3;
+ }
+ }
+
+ if (item->GetEnabled())
+ font->SetColor(text_color);
+ else
+ font->SetColor(text_color * 0.33);
+
+ window->SetFont(font);
+ window->DrawText(item->GetText(), 0, item_rect, DT_LEFT|DT_SINGLELINE);
+ line_height = 11;
+ }
+ else {
+ window->DrawLine(item_rect.x,
+ item_rect.y + 2,
+ item_rect.x + max_width + extra_width - 8,
+ item_rect.y + 2,
+ back_color);
+ line_height = 4;
+ }
+
+ if (item->GetSubmenu()) {
+ int left = item_rect.x + max_width + 10;
+ int top = item_rect.y + 1;
+
+ // draw the arrow:
+ POINT arrow[3];
+
+ arrow[0].x = left;
+ arrow[0].y = top;
+ arrow[1].x = left + 8;
+ arrow[1].y = top + 4;
+ arrow[2].x = left;
+ arrow[2].y = top + 8;
+
+ window->FillPoly(3, arrow, back_color);
+ }
+
+ item_rect.y += line_height;
+ }
+
+ if (submenu) {
+ if (subx + 60 > width)
+ subx = menu_rect.x - 60;
+
+ DrawMenu(subx, suby, submenu);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MenuView::ClearMenuSelection(Menu* menu)
+{
+ if (menu) {
+ ListIter<MenuItem> item = menu->GetItems();
+ while (++item) {
+ item->SetSelected(0);
+ if (item->GetSubmenu())
+ ClearMenuSelection(item->GetSubmenu());
+ }
+ }
+}
diff --git a/Stars45/MenuView.h b/Stars45/MenuView.h
new file mode 100644
index 0000000..7303afb
--- /dev/null
+++ b/Stars45/MenuView.h
@@ -0,0 +1,77 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MenuView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ View class for displaying right-click context menus
+*/
+
+#ifndef MenuView_h
+#define MenuView_h
+
+#include "Types.h"
+#include "View.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Menu;
+class MenuItem;
+
+// +--------------------------------------------------------------------+
+
+class MenuView : public View
+{
+public:
+ MenuView(Window* c);
+ virtual ~MenuView();
+
+ // Operations:
+ virtual void Refresh();
+ virtual void OnWindowMove();
+ virtual void DoMouseFrame();
+ virtual void DrawMenu();
+ virtual void DrawMenu(int x, int y, Menu* menu);
+ virtual int ProcessMenuItem();
+ virtual void ClearMenuSelection(Menu* menu);
+
+ virtual bool IsShown() { return show_menu != 0; }
+ virtual int GetAction() { return action; }
+ virtual Menu* GetMenu() { return menu; }
+ virtual void SetMenu(Menu* m) { menu = m; }
+ virtual MenuItem* GetMenuItem() { return menu_item; }
+
+ virtual Color GetBackColor() { return back_color; }
+ virtual void SetBackColor(Color c) { back_color = c; }
+ virtual Color GetTextColor() { return text_color; }
+ virtual void SetTextColor(Color c) { text_color = c; }
+
+protected:
+ int width, height;
+
+ int shift_down;
+ int mouse_down;
+ int right_down;
+ int show_menu;
+ POINT right_start;
+ POINT offset;
+
+ int action;
+ Menu* menu;
+ MenuItem* menu_item;
+ MenuItem* selected;
+
+ Color back_color;
+ Color text_color;
+};
+
+#endif MenuView_h
+
diff --git a/Stars45/Mfd.cpp b/Stars45/Mfd.cpp
new file mode 100644
index 0000000..caf8bee
--- /dev/null
+++ b/Stars45/Mfd.cpp
@@ -0,0 +1,1403 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MFD.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Class for all Multi Function Displays
+*/
+
+#include "MemDebug.h"
+#include "MFD.h"
+#include "HUDView.h"
+#include "Ship.h"
+#include "NavSystem.h"
+#include "Power.h"
+#include "Shield.h"
+#include "Sensor.h"
+#include "Contact.h"
+#include "ShipDesign.h"
+#include "Shot.h"
+#include "Weapon.h"
+#include "WeaponGroup.h"
+#include "Sim.h"
+#include "StarSystem.h"
+#include "Starshatter.h"
+#include "Drive.h"
+#include "QuantumDrive.h"
+#include "Power.h"
+#include "Instruction.h"
+
+#include "NetGame.h"
+
+#include "CameraView.h"
+#include "Color.h"
+#include "Font.h"
+#include "FontMgr.h"
+#include "Window.h"
+#include "Video.h"
+#include "Screen.h"
+#include "DataLoader.h"
+#include "Scene.h"
+#include "Graphic.h"
+#include "Sprite.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "Game.h"
+
+static Bitmap sensor_fov;
+static Bitmap sensor_fwd;
+static Bitmap sensor_hsd;
+static Bitmap sensor_3d;
+
+static BYTE* sensor_fov_shade;
+static BYTE* sensor_fwd_shade;
+static BYTE* sensor_hsd_shade;
+static BYTE* sensor_3d_shade;
+
+static Color hud_color = Color::Black;
+static Color txt_color = Color::Black;
+
+// +--------------------------------------------------------------------+
+
+MFD::MFD(Window* c, int n)
+ : window(c), rect(0,0,0,0), index(n), mode(MFD_MODE_OFF), sprite(0),
+ ship(0), hidden(true), camview(0), lines(0), mouse_latch(0), mouse_in(false),
+ cockpit_hud_texture(0)
+{
+ sprite = new(__FILE__,__LINE__) Sprite(&sensor_fov);
+
+ sprite->SetBlendMode(2);
+ sprite->SetFilter(0);
+ sprite->Hide();
+
+ Font* font = FontMgr::Find("HUD");
+
+ for (int i = 0; i < TXT_LAST; i++) {
+ mfd_text[i].font = font;
+ mfd_text[i].color = Color::White;
+ mfd_text[i].hidden = true;
+ }
+}
+
+MFD::~MFD()
+{
+ GRAPHIC_DESTROY(sprite);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MFD::Initialize()
+{
+ static int initialized = 0;
+ if (initialized) return;
+
+ HUDView::PrepareBitmap("sensor_fov.pcx", sensor_fov, sensor_fov_shade);
+ HUDView::PrepareBitmap("sensor_fwd.pcx", sensor_fwd, sensor_fwd_shade);
+ HUDView::PrepareBitmap("sensor_hsd.pcx", sensor_hsd, sensor_hsd_shade);
+ HUDView::PrepareBitmap("sensor_3d.pcx", sensor_3d, sensor_3d_shade);
+
+ sensor_fov.SetType(Bitmap::BMP_TRANSLUCENT);
+ sensor_fwd.SetType(Bitmap::BMP_TRANSLUCENT);
+ sensor_hsd.SetType(Bitmap::BMP_TRANSLUCENT);
+ sensor_3d.SetType(Bitmap::BMP_TRANSLUCENT);
+
+ initialized = 1;
+}
+
+void
+MFD::Close()
+{
+ sensor_fov.ClearImage();
+ sensor_fwd.ClearImage();
+ sensor_hsd.ClearImage();
+ sensor_3d.ClearImage();
+
+ delete [] sensor_fov_shade;
+ delete [] sensor_fwd_shade;
+ delete [] sensor_hsd_shade;
+ delete [] sensor_3d_shade;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MFD::UseCameraView(CameraView* v)
+{
+ if (v && !camview) {
+ camview = v;
+ }
+}
+
+void
+MFD::SetColor(Color c)
+{
+ HUDView* hud = HUDView::GetInstance();
+
+ if (hud) {
+ hud_color = hud->GetHUDColor();
+ txt_color = hud->GetTextColor();
+ }
+ else {
+ hud_color = c;
+ txt_color = c;
+ }
+
+ HUDView::ColorizeBitmap(sensor_fov, sensor_fov_shade, c);
+ HUDView::ColorizeBitmap(sensor_fwd, sensor_fwd_shade, c);
+ HUDView::ColorizeBitmap(sensor_hsd, sensor_hsd_shade, c);
+ HUDView::ColorizeBitmap(sensor_3d, sensor_3d_shade, c);
+}
+
+void
+MFD::SetText3DColor(Color c)
+{
+ for (int i = 0; i < TXT_LAST; i++)
+ mfd_text[i].color = c;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MFD::Show()
+{
+ switch (mode) {
+ case MFD_MODE_FOV:
+ case MFD_MODE_HSD:
+ case MFD_MODE_3D:
+ if (sprite)
+ sprite->Show();
+ break;
+ }
+
+ hidden = false;
+}
+
+void
+MFD::Hide()
+{
+ if (sprite)
+ sprite->Hide();
+
+ for (int i = 0; i < TXT_LAST; i++)
+ HideMFDText(i);
+
+ hidden = true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MFD::SetRect(const Rect& r)
+{
+ rect = r;
+
+ if (sprite)
+ sprite->MoveTo(Point(rect.x + sprite->Width()/2,
+ rect.y + sprite->Height()/2,
+ 1));
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MFD::SetMode(int m)
+{
+ if (m < MFD_MODE_OFF || m > MFD_MODE_3D)
+ mode = MFD_MODE_OFF;
+ else
+ mode = m;
+
+ sprite->Hide();
+
+ for (int i = 0; i < TXT_LAST; i++)
+ HideMFDText(i);
+
+ switch (mode) {
+ case MFD_MODE_GAME:
+ case MFD_MODE_SHIP:
+ lines = 0;
+ break;
+
+ case MFD_MODE_FOV:
+ sprite->SetAnimation(&sensor_fov);
+ sprite->Show();
+ sprite->Reshape(sensor_fov.Width()-8, 16);
+ break;
+ case MFD_MODE_HSD:
+ sprite->SetAnimation(&sensor_hsd);
+ sprite->Show();
+ sprite->Reshape(sensor_hsd.Width()-8, 16);
+ break;
+ case MFD_MODE_3D:
+ sprite->SetAnimation(&sensor_3d);
+ sprite->Show();
+ sprite->Reshape(sensor_3d.Width()-8, 16);
+ break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MFD::Draw()
+{
+ mouse_in = false;
+
+ if (Mouse::LButton() == 0)
+ mouse_latch = 0;
+
+ if (rect.Contains(Mouse::X(), Mouse::Y()))
+ mouse_in = true;
+
+ // click to turn on MFD when off:
+ if (mode < MFD_MODE_FOV && Mouse::LButton() && !mouse_latch) {
+ mouse_latch = 1;
+ if (mouse_in) {
+ HUDView* hud = HUDView::GetInstance();
+ if (hud)
+ hud->CycleMFDMode(index);
+ }
+ }
+
+ for (int i = 0; i < TXT_LAST; i++)
+ HideMFDText(i);
+
+ if (hidden || mode < MFD_MODE_FOV) {
+ if (cockpit_hud_texture) {
+ int x1 = index*128;
+ int y1 = 256;
+ int x2 = x1 + 128;
+ int y2 = y1 + 128;
+
+ cockpit_hud_texture->FillRect(x1, y1, x2, y2, Color::Black);
+ }
+
+ if (hidden)
+ return;
+ }
+
+ if (sprite && !sprite->Hidden()) {
+ if (cockpit_hud_texture) {
+ int x1 = index*128;
+ int y1 = 256;
+ int w = sprite->Width();
+ int h = sprite->Height();
+
+ cockpit_hud_texture->BitBlt(x1, y1, *sprite->Frame(), 0,0,w,h);
+ }
+ else {
+ int cx = rect.x + rect.w/2;
+ int cy = rect.y + rect.h/2;
+ int w2 = sprite->Width()/2;
+ int h2 = sprite->Height()/2;
+
+ window->DrawBitmap(cx-w2, cy-h2, cx+w2, cy+h2, sprite->Frame(), Video::BLEND_ALPHA);
+ }
+ }
+
+ switch (mode) {
+ default:
+ case MFD_MODE_OFF: break;
+ case MFD_MODE_GAME: DrawGameMFD(); break;
+ case MFD_MODE_SHIP: DrawStatusMFD(); break;
+
+ // sensor sub-modes:
+ case MFD_MODE_FOV: DrawSensorMFD(); break;
+ case MFD_MODE_HSD: DrawHSD(); break;
+ case MFD_MODE_3D: Draw3D(); break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MFD::DrawSensorLabels(const char* mfd_mode)
+{
+ Sensor* sensor = ship->GetSensor();
+ char mode_buf[8] = " ";
+ int scan_r = rect.w;
+ int scan_x = rect.x;
+ int scan_y = rect.y;
+
+ switch (sensor->GetMode()) {
+ case Sensor::PAS: strcpy(mode_buf, Game::GetText("MFD.mode.passive").data()); break;
+ case Sensor::STD: strcpy(mode_buf, Game::GetText("MFD.mode.standard").data()); break;
+ case Sensor::ACM: strcpy(mode_buf, Game::GetText("MFD.mode.auto-combat").data()); break;
+ case Sensor::GM: strcpy(mode_buf, Game::GetText("MFD.mode.ground").data()); break;
+ case Sensor::PST: strcpy(mode_buf, Game::GetText("MFD.mode.passive").data()); break;
+ case Sensor::CST: strcpy(mode_buf, Game::GetText("MFD.mode.combined").data()); break;
+ default: break;
+ }
+
+ Rect mode_rect(scan_x+2, scan_y+2, 40, 12);
+ DrawMFDText(0, mode_buf, mode_rect, DT_LEFT);
+
+ char range_txt[12];
+ double beam_range = sensor->GetBeamRange() + 1;
+ if (beam_range >= 1e6)
+ sprintf(range_txt, "-%dM+", (int) (beam_range / 1e6));
+ else
+ sprintf(range_txt, "-%3d+", (int) (beam_range / 1e3));
+
+ Rect range_rect(scan_x+2, scan_y+scan_r-12, 40, 12);
+ DrawMFDText(1, range_txt, range_rect, DT_LEFT);
+
+ Rect disp_rect(scan_x+scan_r-41, scan_y+2, 40, 12);
+ DrawMFDText(2, mfd_mode, disp_rect, DT_RIGHT);
+
+ Rect probe_rect(scan_x+scan_r-41, scan_y+scan_r-12, 40, 12);
+
+ if (ship->GetProbeLauncher()) {
+ char probes[32];
+ sprintf(probes, "%s %02d", Game::GetText("MFD.probe").data(), ship->GetProbeLauncher()->Ammo());
+ DrawMFDText(3, probes, probe_rect, DT_RIGHT);
+ }
+ else {
+ HideMFDText(3);
+ }
+
+ if (Mouse::LButton() && !mouse_latch) {
+ mouse_latch = 1;
+
+ if (mode_rect.Contains(Mouse::X(), Mouse::Y())) {
+ if (sensor->GetMode() < Sensor::PST) {
+ int sensor_mode = sensor->GetMode() + 1;
+ if (sensor_mode > Sensor::GM)
+ sensor_mode = Sensor::PAS;
+
+ sensor->SetMode((Sensor::Mode) sensor_mode);
+ }
+ }
+
+ else if (range_rect.Contains(Mouse::X(), Mouse::Y())) {
+ if (Mouse::X() > range_rect.x+range_rect.w/2)
+ sensor->IncreaseRange();
+ else
+ sensor->DecreaseRange();
+ }
+
+ else if (disp_rect.Contains(Mouse::X(), Mouse::Y())) {
+ HUDView* hud = HUDView::GetInstance();
+ if (hud)
+ hud->CycleMFDMode(index);
+ }
+
+ else if (probe_rect.Contains(Mouse::X(), Mouse::Y())) {
+ ship->LaunchProbe();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+// AZIMUTH-ELEVATION ANGULAR SCANNER
+
+void
+MFD::DrawSensorMFD()
+{
+ int scan_r = rect.w;
+ int scan_x = cockpit_hud_texture ? (index*128) : rect.x;
+ int scan_y = cockpit_hud_texture ? 256 : rect.y;
+ int r = scan_r / 2;
+
+ double xctr = (scan_r / 2.0) - 0.5;
+ double yctr = (scan_r / 2.0) + 0.5;
+
+ Sensor* sensor = ship->GetSensor();
+ if (!sensor) {
+ DrawMFDText(0, Game::GetText("MFD.inactive").data(), rect, DT_CENTER);
+ return;
+ }
+
+ int w = sprite->Width();
+ int h = sprite->Height();
+
+ if (w < sprite->Frame()->Width())
+ w += 2;
+
+ if (h < sprite->Frame()->Height())
+ h += 16;
+
+ sprite->Reshape(w, h);
+ sprite->Show();
+
+ if (h < sprite->Frame()->Height())
+ return;
+
+ double sweep_scale = r / (PI/2);
+
+ if (sensor->GetBeamLimit() > 90*DEGREES)
+ sweep_scale = (double) r / (90*DEGREES);
+
+ int az = (int) (sensor->GetBeamLimit() * sweep_scale);
+ int el = az;
+ int xc = (int) (scan_x + xctr);
+ int yc = (int) (scan_y + yctr);
+
+ if (mode == MFD_MODE_FOV) {
+ if (sensor->GetMode() < Sensor::GM) {
+ if (cockpit_hud_texture)
+ cockpit_hud_texture->DrawEllipse(xc-az, yc-el, xc+az, yc+el, hud_color);
+ else
+ window->DrawEllipse(xc-az, yc-el, xc+az, yc+el, hud_color);
+ }
+ }
+ else {
+ char az_txt[8];
+ sprintf(az_txt, "%d", (int) (sensor->GetBeamLimit() / DEGREES));
+
+ Rect az_rect(scan_x+2, scan_y+scan_r-12, 32, 12);
+ DrawMFDText(1, az_txt, az_rect, DT_LEFT);
+
+ az_rect.x = scan_x + (scan_r/2) - (az_rect.w/2);
+ DrawMFDText(2, "0", az_rect, DT_CENTER);
+
+ az_rect.x = scan_x + scan_r - az_rect.w - 2;
+ DrawMFDText(3, az_txt, az_rect, DT_RIGHT);
+ }
+
+ // draw next nav point:
+ Instruction* navpt = ship->GetNextNavPoint();
+ if (navpt && navpt->Region() == ship->GetRegion()) {
+ const Camera* cam = &ship->Cam();
+
+ // translate:
+ Point pt = navpt->Location().OtherHand() - ship->Location();
+
+ // rotate:
+ double tx = pt * cam->vrt();
+ double ty = pt * cam->vup();
+ double tz = pt * cam->vpn();
+
+ if (tz > 1.0) {
+ // convert to spherical coords:
+ double rng = pt.length();
+ double az = asin(fabs(tx) / rng);
+ double el = asin(fabs(ty) / rng);
+
+ if (tx < 0) az = -az;
+ if (ty < 0) el = -el;
+
+ if (fabs(az) < 90*DEGREES) {
+ az *= sweep_scale;
+ el *= sweep_scale;
+
+ int x = (int) (r + az);
+ int y = (int) (r - el);
+
+ // clip again:
+ if (x > 0 && x < scan_r &&
+ y > 0 && y < scan_r) {
+
+ // draw:
+ int xc = scan_x + x;
+ int yc = scan_y + y;
+
+ if (cockpit_hud_texture) {
+ cockpit_hud_texture->DrawLine(xc-2, yc-2, xc+2, yc+2, Color::White);
+ cockpit_hud_texture->DrawLine(xc-2, yc+2, xc+2, yc-2, Color::White);
+ }
+ else {
+ window->DrawLine(xc-2, yc-2, xc+2, yc+2, Color::White);
+ window->DrawLine(xc-2, yc+2, xc+2, yc-2, Color::White);
+ }
+ }
+ }
+ }
+ }
+
+ int num_contacts = ship->NumContacts();
+ ListIter<Contact> iter = ship->ContactList();
+
+ while (++iter) {
+ Contact* contact = iter.value();
+ Ship* c_ship = contact->GetShip();
+ double az, el, rng;
+ bool aft = false;
+
+ if (c_ship == ship) continue;
+
+ contact->GetBearing(ship, az, el, rng);
+
+ // clip (is in-front):
+ if (fabs(az) < 90*DEGREES) {
+ az *= sweep_scale;
+ el *= sweep_scale;
+ }
+
+ // rear anulus:
+ else {
+ double len = sqrt(az*az + el*el);
+
+ if (len > 1e-6) {
+ az = r * az/len;
+ el = r * el/len;
+ }
+ else {
+ az = -r;
+ el = 0;
+ }
+
+ aft = true;
+ }
+
+ int x = (int) (r + az);
+ int y = (int) (r - el);
+
+ // clip again:
+ if (x < 0 || x > scan_r) continue;
+ if (y < 0 || y > scan_r) continue;
+
+ // draw:
+ Color mark = HUDView::MarkerColor(contact);
+
+ if (aft)
+ mark = mark * 0.75;
+
+ int xc = scan_x + x;
+ int yc = scan_y + y;
+ int size = 1;
+
+ if (c_ship && c_ship == ship->GetTarget())
+ size = 2;
+
+ if (cockpit_hud_texture)
+ cockpit_hud_texture->FillRect(xc-size, yc-size, xc+size, yc+size, mark);
+ else
+ window->FillRect(xc-size, yc-size, xc+size, yc+size, mark);
+
+ if (contact->Threat(ship)) {
+ if (c_ship) {
+ if (cockpit_hud_texture)
+ cockpit_hud_texture->DrawEllipse(xc-4, yc-4, xc+3, yc+3, mark);
+ else
+ window->DrawEllipse(xc-4, yc-4, xc+3, yc+3, mark);
+ }
+ else {
+ if (cockpit_hud_texture) {
+ cockpit_hud_texture->DrawLine(xc, yc-5, xc+5, yc, mark);
+ cockpit_hud_texture->DrawLine(xc+5, yc, xc, yc+5, mark);
+ cockpit_hud_texture->DrawLine(xc, yc+5, xc-5, yc, mark);
+ cockpit_hud_texture->DrawLine(xc-5, yc, xc, yc-5, mark);
+ }
+ else {
+ window->DrawLine(xc, yc-5, xc+5, yc, mark);
+ window->DrawLine(xc+5, yc, xc, yc+5, mark);
+ window->DrawLine(xc, yc+5, xc-5, yc, mark);
+ window->DrawLine(xc-5, yc, xc, yc-5, mark);
+ }
+ }
+ }
+ }
+
+ DrawSensorLabels(Game::GetText("MFD.mode.field-of-view").data());
+}
+
+// +--------------------------------------------------------------------+
+
+// HORIZONTAL SITUATION DISPLAY
+
+void
+MFD::DrawHSD()
+{
+ int scan_r = rect.w;
+ int scan_x = cockpit_hud_texture ? (index*128) : rect.x;
+ int scan_y = cockpit_hud_texture ? 256 : rect.y;
+ int r = scan_r / 2 - 4;
+
+ double xctr = (scan_r / 2.0) - 0.5;
+ double yctr = (scan_r / 2.0) + 0.5;
+
+ int xc = (int) xctr + scan_x;
+ int yc = (int) yctr + scan_y;
+
+ Sensor* sensor = ship->GetSensor();
+ if (!sensor) {
+ DrawMFDText(0, Game::GetText("MFD.inactive").data(), rect, DT_CENTER);
+ return;
+ }
+
+ int w = sprite->Width();
+ int h = sprite->Height();
+
+ if (w < sprite->Frame()->Width())
+ w += 2;
+
+ if (h < sprite->Frame()->Height())
+ h += 16;
+
+ sprite->Reshape(w, h);
+ sprite->Show();
+
+ if (h < sprite->Frame()->Height())
+ return;
+
+ if (sensor->GetMode() < Sensor::PST) {
+ double s = sin(sensor->GetBeamLimit());
+ double c = cos(sensor->GetBeamLimit());
+
+ int x0 = (int) (0.1*r*s);
+ int y0 = (int) (0.1*r*c);
+ int x1 = (int) (1.0*r*s);
+ int y1 = (int) (1.0*r*c);
+
+ if (cockpit_hud_texture) {
+ cockpit_hud_texture->DrawLine(xc-x0, yc-y0, xc-x1, yc-y1, hud_color);
+ cockpit_hud_texture->DrawLine(xc+x0, yc-y0, xc+x1, yc-y1, hud_color);
+ }
+ else {
+ window->DrawLine(xc-x0, yc-y0, xc-x1, yc-y1, hud_color);
+ window->DrawLine(xc+x0, yc-y0, xc+x1, yc-y1, hud_color);
+ }
+ }
+
+ double rscale = (double) r/(sensor->GetBeamRange());
+
+ Camera hsd_cam = ship->Cam();
+ Point look = ship->Location() + ship->Heading() * 1000;
+ look.y = ship->Location().y;
+
+ hsd_cam.LookAt(look);
+
+ // draw tick marks on range rings:
+ for (int dir = 0; dir < 4; dir++) {
+ Point tick;
+
+ switch (dir) {
+ case 0: tick = Point( 0, 0, 1000); break;
+ case 1: tick = Point( 1000, 0, 0); break;
+ case 2: tick = Point( 0, 0, -1000); break;
+ case 3: tick = Point(-1000, 0, 0); break;
+ }
+
+ double tx = tick * hsd_cam.vrt();
+ double tz = tick * hsd_cam.vpn();
+ double az = asin(fabs(tx) / 1000);
+
+ if (tx < 0) az = -az;
+
+ if (tz < 0)
+ if (az < 0) az = -PI - az;
+ else az = PI - az;
+
+ for (double range = 0.3; range < 1; range += 0.3) {
+ int x0 = (int) (sin(az) * r * range);
+ int y0 = (int) (cos(az) * r * range);
+ int x1 = (int) (sin(az) * r * (range + 0.1));
+ int y1 = (int) (cos(az) * r * (range + 0.1));
+
+ if (cockpit_hud_texture) {
+ cockpit_hud_texture->DrawLine(xc+x0, yc-y0, xc+x1, yc-y1, hud_color);
+ }
+ else {
+ window->DrawLine(xc+x0, yc-y0, xc+x1, yc-y1, hud_color);
+ }
+ }
+ }
+
+ // draw next nav point:
+ Instruction* navpt = ship->GetNextNavPoint();
+ if (navpt && navpt->Region() == ship->GetRegion()) {
+ const Camera* cam = &hsd_cam;
+
+ // translate:
+ Point pt = navpt->Location().OtherHand() - ship->Location();
+
+ // rotate:
+ double tx = pt * cam->vrt();
+ double ty = pt * cam->vup();
+ double tz = pt * cam->vpn();
+
+ // convert to spherical coords:
+ double rng = pt.length();
+ double az = asin(fabs(tx) / rng);
+
+ if (rng > sensor->GetBeamRange())
+ rng = sensor->GetBeamRange();
+
+ if (tx < 0)
+ az = -az;
+
+ if (tz < 0)
+ if (az < 0)
+ az = -PI - az;
+ else
+ az = PI - az;
+
+ // draw:
+ int x = (int) (xc + sin(az) * rng * rscale);
+ int y = (int) (yc - cos(az) * rng * rscale);
+
+ if (cockpit_hud_texture) {
+ cockpit_hud_texture->DrawLine(x-2, y-2, x+2, y+2, Color::White);
+ cockpit_hud_texture->DrawLine(x-2, y+2, x+2, y-2, Color::White);
+ }
+ else {
+ window->DrawLine(x-2, y-2, x+2, y+2, Color::White);
+ window->DrawLine(x-2, y+2, x+2, y-2, Color::White);
+ }
+ }
+
+ // draw contact markers:
+ double limit = sensor->GetBeamRange();
+ ListIter<Contact> contact = ship->ContactList();
+
+ while (++contact) {
+ Ship* c_ship = contact->GetShip();
+ if (c_ship == ship) continue;
+
+ // translate:
+ Point targ_pt = contact->Location() - hsd_cam.Pos();
+
+ // rotate:
+ double tx = targ_pt * hsd_cam.vrt();
+ double rg = contact->Range(ship, limit);
+ double true_range = targ_pt.length();
+ double az = asin(fabs(tx) / true_range);
+
+ // clip:
+ if (rg > limit || rg <= 0)
+ continue;
+
+ if (tx < 0)
+ az = -az;
+
+ if (!contact->InFront(ship))
+ if (az < 0)
+ az = -PI - az;
+ else
+ az = PI - az;
+
+ // draw:
+ int x = (int) (xc + sin(az) * rg * rscale);
+ int y = (int) (yc - cos(az) * rg * rscale);
+ int size = 2;
+
+ // clip again:
+ if (x < scan_x || y < scan_y)
+ continue;
+
+ if (c_ship && c_ship == ship->GetTarget())
+ size = 3;
+
+ Color mark = HUDView::MarkerColor(contact.value());
+ if (cockpit_hud_texture) {
+ cockpit_hud_texture->FillRect(x-size, y-size, x+size, y+size, mark);
+ }
+ else {
+ window->FillRect(x-size, y-size, x+size, y+size, mark);
+ }
+
+ if (contact->Threat(ship)) {
+ if (c_ship) {
+ if (cockpit_hud_texture) {
+ cockpit_hud_texture->DrawEllipse(x-4, y-4, x+3, y+3, mark);
+ }
+ else {
+ window->DrawEllipse(x-4, y-4, x+3, y+3, mark);
+ }
+ }
+ else {
+ if (cockpit_hud_texture) {
+ cockpit_hud_texture->DrawLine(x, y-5, x+5, y, mark);
+ cockpit_hud_texture->DrawLine(x+5, y, x, y+5, mark);
+ cockpit_hud_texture->DrawLine(x, y+5, x-5, y, mark);
+ cockpit_hud_texture->DrawLine(x-5, y, x, y-5, mark);
+ }
+ else {
+ window->DrawLine(x, y-5, x+5, y, mark);
+ window->DrawLine(x+5, y, x, y+5, mark);
+ window->DrawLine(x, y+5, x-5, y, mark);
+ window->DrawLine(x-5, y, x, y-5, mark);
+ }
+ }
+ }
+ }
+
+ DrawSensorLabels(Game::GetText("MFD.mode.horizontal").data());
+}
+
+// +--------------------------------------------------------------------+
+
+// ELITE-STYLE 3D RADAR
+
+void
+MFD::Draw3D()
+{
+ int scan_r = rect.w;
+ int scan_x = cockpit_hud_texture ? (index*128) : rect.x;
+ int scan_y = cockpit_hud_texture ? 256 : rect.y;
+ int r = scan_r / 2 - 4;
+
+ double xctr = (scan_r / 2.0) - 0.5;
+ double yctr = (scan_r / 2.0) + 0.5;
+
+ int xc = (int) xctr + scan_x;
+ int yc = (int) yctr + scan_y;
+
+ Sensor* sensor = ship->GetSensor();
+ if (!sensor) {
+ DrawMFDText(0, Game::GetText("MFD.inactive").data(), rect, DT_CENTER);
+ return;
+ }
+
+ int w = sprite->Width();
+ int h = sprite->Height();
+
+ if (w < sprite->Frame()->Width())
+ w += 2;
+
+ if (h < sprite->Frame()->Height())
+ h += 16;
+
+ sprite->Reshape(w, h);
+ sprite->Show();
+
+ if (h < sprite->Frame()->Height())
+ return;
+
+ double rscale = (double) r/(sensor->GetBeamRange());
+
+ Camera hsd_cam = ship->Cam();
+
+ if (ship->IsStarship()) {
+ Point look = ship->Location() + ship->Heading() * 1000;
+ look.y = ship->Location().y;
+
+ hsd_cam.LookAt(look);
+ }
+
+
+ // draw next nav point:
+ Instruction* navpt = ship->GetNextNavPoint();
+ if (navpt && navpt->Region() == ship->GetRegion()) {
+ const Camera* cam = &hsd_cam;
+
+ // translate:
+ Point pt = navpt->Location().OtherHand() - ship->Location();
+
+ // rotate:
+ double tx = pt * cam->vrt();
+ double ty = pt * cam->vup();
+ double tz = pt * cam->vpn();
+
+ // convert to cylindrical coords:
+ double rng = pt.length();
+ double az = asin(fabs(tx) / rng);
+
+ if (rng > sensor->GetBeamRange())
+ rng = sensor->GetBeamRange();
+
+ if (tx < 0)
+ az = -az;
+
+ if (tz < 0) {
+ if (az < 0)
+ az = -PI - az;
+ else
+ az = PI - az;
+ }
+
+ // accentuate vertical:
+ if (ty > 10)
+ ty = log10(ty-9) * r/8;
+
+ else if (ty < -10)
+ ty = -log10(9-ty) * r/8;
+
+ else
+ ty = 0;
+
+ // draw:
+ int x = (int) (sin(az) * rng * rscale);
+ int y = (int) (cos(az) * rng * rscale/2);
+ int z = (int) (ty);
+
+ int x0 = xc+x;
+ int y0 = yc-y-z;
+
+ if (cockpit_hud_texture) {
+ cockpit_hud_texture->DrawLine(x0-2, y0-2, x0+2, y0+2, Color::White);
+ cockpit_hud_texture->DrawLine(x0-2, y0+2, x0+2, y0-2, Color::White);
+ }
+ else {
+ window->DrawLine(x0-2, y0-2, x0+2, y0+2, Color::White);
+ window->DrawLine(x0-2, y0+2, x0+2, y0-2, Color::White);
+ }
+
+ if (cockpit_hud_texture) {
+ if (z > 0)
+ cockpit_hud_texture->DrawLine(x0, y0+1, x0, y0+z, Color::White);
+ else if (z < 0)
+ cockpit_hud_texture->DrawLine(x0, y0+z, x0, y0-1, Color::White);
+ }
+ else {
+ if (z > 0)
+ window->DrawLine(x0, y0+1, x0, y0+z, Color::White);
+ else if (z < 0)
+ window->DrawLine(x0, y0+z, x0, y0-1, Color::White);
+ }
+ }
+
+
+ // draw contact markers:
+ double limit = sensor->GetBeamRange();
+ ListIter<Contact> contact = ship->ContactList();
+
+ while (++contact) {
+ Ship* c_ship = contact->GetShip();
+ if (c_ship == ship) continue;
+
+ // translate:
+ Point targ_pt = contact->Location() - hsd_cam.Pos();
+
+ // rotate:
+ double tx = targ_pt * hsd_cam.vrt();
+ double ty = targ_pt * hsd_cam.vup();
+ double rg = contact->Range(ship, limit);
+ double true_range = targ_pt.length();
+ double az = asin(fabs(tx) / true_range);
+
+ // clip:
+ if (rg > limit || rg <= 0)
+ continue;
+
+ if (tx < 0)
+ az = -az;
+
+ if (!contact->InFront(ship))
+ if (az < 0)
+ az = -PI - az;
+ else
+ az = PI - az;
+
+ // accentuate vertical:
+ ty *= 4;
+
+ // draw:
+ int x = (int) (sin(az) * rg * rscale);
+ int y = (int) (cos(az) * rg * rscale/2);
+ int z = (int) (ty * rscale/2);
+ int size = 1;
+
+ int x0 = xc+x;
+ int y0 = yc-y-z;
+
+ if (c_ship && c_ship == ship->GetTarget())
+ size = 2;
+
+ Color mark = HUDView::MarkerColor(contact.value());
+
+ if (cockpit_hud_texture) {
+ cockpit_hud_texture->FillRect(x0-size, y0-size, x0+size, y0+size, mark);
+
+ if (contact->Threat(ship)) {
+ if (c_ship) {
+ cockpit_hud_texture->DrawEllipse(x0-4, y0-4, x0+3, y0+3, mark);
+ }
+ else {
+ cockpit_hud_texture->DrawLine(x0, y0-5, x0+5, y0, mark);
+ cockpit_hud_texture->DrawLine(x0+5, y0, x0, y0+5, mark);
+ cockpit_hud_texture->DrawLine(x0, y0+5, x0-5, y0, mark);
+ cockpit_hud_texture->DrawLine(x0-5, y0, x0, y0-5, mark);
+ }
+ }
+
+ if (z > 0)
+ cockpit_hud_texture->FillRect(x0-1, y0+size, x0, y0+z, mark);
+ else if (z < 0)
+ cockpit_hud_texture->FillRect(x0-1, y0+z, x0, y0-size, mark);
+ }
+ else {
+ window->FillRect(x0-size, y0-size, x0+size, y0+size, mark);
+
+ if (contact->Threat(ship)) {
+ if (c_ship) {
+ window->DrawEllipse(x0-4, y0-4, x0+3, y0+3, mark);
+ }
+ else {
+ window->DrawLine(x0, y0-5, x0+5, y0, mark);
+ window->DrawLine(x0+5, y0, x0, y0+5, mark);
+ window->DrawLine(x0, y0+5, x0-5, y0, mark);
+ window->DrawLine(x0-5, y0, x0, y0-5, mark);
+ }
+ }
+
+ if (z > 0)
+ window->FillRect(x0-1, y0+size, x0, y0+z, mark);
+ else if (z < 0)
+ window->FillRect(x0-1, y0+z, x0, y0-size, mark);
+ }
+ }
+
+ DrawSensorLabels(Game::GetText("MFD.mode.3D").data());
+}
+
+// +--------------------------------------------------------------------+
+
+// GROUND MAP
+
+void
+MFD::DrawMap()
+{
+ DrawMFDText(0, Game::GetText("MFD.mode.ground").data(), Rect(rect.x, rect.y, rect.w, 12), DT_CENTER);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MFD::DrawGauge(int x, int y, int percent)
+{
+ if (cockpit_hud_texture) {
+ x += this->index * 128 - this->rect.x;
+ y += 256 - this->rect.y;
+ cockpit_hud_texture->DrawRect(x, y, x+53, y+8, Color::DarkGray);
+ }
+ else {
+ window->DrawRect(x, y, x+53, y+8, Color::DarkGray);
+ }
+
+ if (percent < 3) return;
+ if (percent > 100) percent = 100;
+
+ percent /= 2;
+
+ if (cockpit_hud_texture)
+ cockpit_hud_texture->FillRect(x+2, y+2, x+2+percent, y+7, Color::Gray);
+ else
+ window->FillRect(x+2, y+2, x+2+percent, y+7, Color::Gray);
+}
+
+void
+MFD::DrawGameMFD()
+{
+ if (lines < 10) lines++;
+
+ char txt[64];
+ Rect txt_rect(rect.x, rect.y, rect.w, 12);
+
+ int t = 0;
+
+ if (!HUDView::IsArcade() && HUDView::ShowFPS()) {
+ sprintf(txt, "FPS: %6.2f", Game::FrameRate());
+ DrawMFDText(t++, txt, txt_rect, DT_LEFT);
+ txt_rect.y += 10;
+
+ if (lines <= 1) return;
+
+ Starshatter* game = Starshatter::GetInstance();
+ sprintf(txt, "Polys: %d", game->GetPolyStats().npolys);
+ DrawMFDText(t++, txt, txt_rect, DT_LEFT);
+ txt_rect.y += 10;
+ }
+
+ if (ship) {
+ DrawMFDText(t++, ship->Name(), txt_rect, DT_LEFT);
+ txt_rect.y += 10;
+ }
+
+ if (lines <= 2) return;
+
+ int hours = (Game::GameTime() / 3600000) ;
+ int minutes = (Game::GameTime() / 60000) % 60;
+ int seconds = (Game::GameTime() / 1000) % 60;
+
+ if (ship) {
+ DWORD clock = ship->MissionClock();
+
+ hours = (clock / 3600000) ;
+ minutes = (clock / 60000) % 60;
+ seconds = (clock / 1000) % 60;
+ }
+
+ if (Game::TimeCompression() > 1)
+ sprintf(txt, "%02d:%02d:%02d x%d", hours, minutes, seconds, Game::TimeCompression());
+ else
+ sprintf(txt, "%02d:%02d:%02d", hours, minutes, seconds);
+
+ DrawMFDText(t++, txt, txt_rect, DT_LEFT);
+ txt_rect.y += 10;
+
+ if (HUDView::IsArcade() || lines <= 3) return;
+
+ DrawMFDText(t++, ship->GetRegion()->Name(), txt_rect, DT_LEFT);
+ txt_rect.y += 10;
+
+ if (lines <= 4) return;
+
+ if (ship) {
+ switch (ship->GetFlightPhase()) {
+ case Ship::DOCKED: DrawMFDText(t++, Game::GetText("MFD.phase.DOCKED").data(), txt_rect, DT_LEFT); break;
+ case Ship::ALERT: DrawMFDText(t++, Game::GetText("MFD.phase.ALERT").data(), txt_rect, DT_LEFT); break;
+ case Ship::LOCKED: DrawMFDText(t++, Game::GetText("MFD.phase.LOCKED").data(), txt_rect, DT_LEFT); break;
+ case Ship::LAUNCH: DrawMFDText(t++, Game::GetText("MFD.phase.LAUNCH").data(), txt_rect, DT_LEFT); break;
+ case Ship::TAKEOFF: DrawMFDText(t++, Game::GetText("MFD.phase.TAKEOFF").data(), txt_rect, DT_LEFT); break;
+ case Ship::ACTIVE: DrawMFDText(t++, Game::GetText("MFD.phase.ACTIVE").data(), txt_rect, DT_LEFT); break;
+ case Ship::APPROACH: DrawMFDText(t++, Game::GetText("MFD.phase.APPROACH").data(), txt_rect, DT_LEFT); break;
+ case Ship::RECOVERY: DrawMFDText(t++, Game::GetText("MFD.phase.RECOVERY").data(), txt_rect, DT_LEFT); break;
+ case Ship::DOCKING: DrawMFDText(t++, Game::GetText("MFD.phase.DOCKING").data(), txt_rect, DT_LEFT); break;
+ }
+ }
+}
+
+void
+MFD::DrawStatusMFD()
+{
+ if (lines < 10) lines++;
+
+ Rect status_rect(rect.x, rect.y, rect.w, 12);
+ int row = 0;
+ char txt[32];
+
+ if (status_rect.y > 320 && !ship->IsStarship())
+ status_rect.y += 32;
+
+ if (ship) {
+ Drive* drive = ship->GetDrive();
+ if (drive) {
+ DrawMFDText(row++, Game::GetText("MFD.status.THRUST").data(), status_rect, DT_LEFT);
+ DrawGauge(status_rect.x+70, status_rect.y, (int) ship->Throttle());
+ status_rect.y += 10;
+ }
+
+ if (lines <= 1) return;
+
+ if (ship->Reactors().size() > 0) {
+ PowerSource* reactor = ship->Reactors()[0];
+ if (reactor) {
+ DrawMFDText(row++, Game::GetText("MFD.status.FUEL").data(), status_rect, DT_LEFT);
+ DrawGauge(status_rect.x+70, status_rect.y, reactor->Charge());
+ status_rect.y += 10;
+ }
+ }
+
+ if (lines <= 2) return;
+
+ QuantumDrive* quantum_drive = ship->GetQuantumDrive();
+ if (quantum_drive) {
+ DrawMFDText(row++, Game::GetText("MFD.status.QUANTUM").data(), status_rect, DT_LEFT);
+ DrawGauge(status_rect.x+70, status_rect.y, (int) quantum_drive->Charge());
+ status_rect.y += 10;
+ }
+
+ if (lines <= 3) return;
+
+ double hull = ship->Integrity() / ship->Design()->integrity * 100;
+ int hull_status = System::CRITICAL;
+
+ if (hull > 66)
+ hull_status = System::NOMINAL;
+ else if (hull > 33)
+ hull_status = System::DEGRADED;
+
+ DrawMFDText(row++, Game::GetText("MFD.status.HULL").data(), status_rect, DT_LEFT);
+ DrawGauge(status_rect.x+70, status_rect.y, (int) hull);
+ status_rect.y += 10;
+
+ if (lines <= 4) return;
+
+ Shield* shield = ship->GetShield();
+ if (shield) {
+ DrawMFDText(row++, Game::GetText("MFD.status.SHIELD").data(), status_rect, DT_LEFT);
+ DrawGauge(status_rect.x+70, status_rect.y, ship->ShieldStrength());
+ status_rect.y += 10;
+ }
+
+ if (lines <= 5) return;
+
+ Weapon* primary = ship->GetPrimary();
+ if (primary) {
+ DrawMFDText(row++, Game::GetText("MFD.status.GUNS").data(), status_rect, DT_LEFT);
+ DrawGauge(status_rect.x+70, status_rect.y, primary->Charge());
+ status_rect.y += 10;
+ }
+
+ if (lines <= 6) return;
+
+ if (HUDView::IsArcade()) {
+ for (int i = 0; i < ship->Weapons().size() && i < 4; i++) {
+ WeaponGroup* w = ship->Weapons().at(i);
+
+ if (w->IsMissile()) {
+ char ammo[8];
+
+ if (ship->GetSecondaryGroup() == w)
+ sprintf(ammo, "%d *", w->Ammo());
+ else
+ sprintf(ammo, "%d", w->Ammo());
+
+ DrawMFDText(row++, (const char*) w->GetDesign()->name, status_rect, DT_LEFT);
+ status_rect.x += 70;
+ DrawMFDText(row++, ammo, status_rect, DT_LEFT);
+ status_rect.x -= 70;
+ status_rect.y += 10;
+ }
+ }
+
+ if (ship->GetDecoy()) {
+ char ammo[8];
+ sprintf(ammo, "%d", ship->GetDecoy()->Ammo());
+ DrawMFDText(row++, Game::GetText("MFD.status.DECOY").data(), status_rect, DT_LEFT);
+ status_rect.x += 70;
+ DrawMFDText(row++, ammo, status_rect, DT_LEFT);
+ status_rect.x -= 70;
+ status_rect.y += 10;
+ }
+
+ if (NetGame::GetInstance()) {
+ char lives[8];
+ sprintf(lives, "%d", ship->RespawnCount() + 1);
+ DrawMFDText(row++, Game::GetText("MFD.status.LIVES").data(), status_rect, DT_LEFT);
+ status_rect.x += 70;
+ DrawMFDText(row++, lives, status_rect, DT_LEFT);
+ status_rect.x -= 70;
+ status_rect.y += 10;
+ }
+
+ return;
+ }
+
+ Sensor* sensor = ship->GetSensor();
+ if (sensor) {
+ if (ship->GetFlightPhase() != Ship::ACTIVE) {
+ DrawMFDText(row++, Game::GetText("MFD.status.SENSOR").data(), status_rect, DT_LEFT);
+ status_rect.x += 70;
+ DrawMFDText(row++, Game::GetText("MFD.status.OFFLINE").data(), status_rect, DT_LEFT);
+ status_rect.x -= 70;
+ status_rect.y += 10;
+ }
+
+ else {
+ DrawMFDText(row++, Game::GetText("MFD.status.EMCON").data(), status_rect, DT_LEFT);
+ status_rect.x += 70;
+
+ sprintf(txt, "%s %d", Game::GetText("MFD.status.MODE").data(), ship->GetEMCON());
+
+ if (!sensor->IsPowerOn() || sensor->GetEnergy() == 0) {
+ if (!Game::Paused() && (Game::RealTime()/1000) & 2)
+ strcpy(txt, Game::GetText("MFD.status.SENSOR-OFF").data());
+ }
+
+ DrawMFDText(row++, txt, status_rect, DT_LEFT);
+ status_rect.x -= 70;
+ status_rect.y += 10;
+ }
+ }
+
+ if (lines <= 7) return;
+
+ DrawMFDText(row++, Game::GetText("MFD.status.SYSTEMS").data(), status_rect, DT_LEFT);
+ status_rect.x += 70;
+ DrawMFDText(row++, ship->GetDirectorInfo(), status_rect, DT_LEFT);
+
+ if (NetGame::GetInstance()) {
+ char lives[8];
+ sprintf(lives, "%d", ship->RespawnCount() + 1);
+ status_rect.x -= 70;
+ status_rect.y += 10;
+ DrawMFDText(row++, Game::GetText("MFD.status.LIVES").data(), status_rect, DT_LEFT);
+ status_rect.x += 70;
+ DrawMFDText(row++, lives, status_rect, DT_LEFT);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MFD::SetStatusColor(int status)
+{
+ Color status_color;
+
+ switch (status) {
+ default:
+ case System::NOMINAL: status_color = txt_color; break;
+ case System::DEGRADED: status_color = Color(255,255, 0); break;
+ case System::CRITICAL: status_color = Color(255, 0, 0); break;
+ case System::DESTROYED: status_color = Color( 0, 0, 0); break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool MFD::IsMouseLatched() const
+{
+ return mouse_in;
+}
+
+// +--------------------------------------------------------------------+
+
+void MFD::DrawMFDText(int index, const char* txt, Rect& txt_rect, int align, int status)
+{
+ if (index >= MFD::TXT_LAST) {
+ Print("MFD DrawMFDText() invalid mfd_text index %d '%s'\n", index, txt);
+ }
+ else {
+ HUDText& mt = mfd_text[index];
+ Color mc = mt.color;
+
+ switch (status) {
+ default:
+ case System::NOMINAL: mc = txt_color; break;
+ case System::DEGRADED: mc = Color(255,255, 0); break;
+ case System::CRITICAL: mc = Color(255, 0, 0); break;
+ case System::DESTROYED: mc = Color( 0, 0, 0); break;
+ }
+
+ char txt_buf[256];
+ int n = strlen(txt);
+
+ if (n > 250) n = 250;
+
+ for (int i = 0; i < n; i++) {
+ if (islower(txt[i]))
+ txt_buf[i] = toupper(txt[i]);
+ else
+ txt_buf[i] = txt[i];
+ }
+
+ txt_buf[i] = 0;
+
+
+ if (cockpit_hud_texture) {
+ Rect hud_rect(txt_rect);
+
+ hud_rect.x = txt_rect.x + this->index * 128 - this->rect.x;
+ hud_rect.y = txt_rect.y + 256 - this->rect.y;
+
+ mt.font->SetColor(mc);
+ mt.font->DrawText(txt_buf, 0, hud_rect, align | DT_SINGLELINE, cockpit_hud_texture);
+ mt.rect = rect;
+ mt.hidden = false;
+ }
+ else {
+ if (txt_rect.Contains(Mouse::X(), Mouse::Y()))
+ mc = Color::White;
+
+ mt.font->SetColor(mc);
+ mt.font->DrawText(txt_buf, 0, txt_rect, align | DT_SINGLELINE);
+ mt.rect = rect;
+ mt.hidden = false;
+ }
+
+ }
+}
+
+void MFD::HideMFDText(int index)
+{
+ if (index >= MFD::TXT_LAST)
+ Print("MFD HideMFDText() invalid mfd_text index %d\n", index);
+ else
+ mfd_text[index].hidden = true;
+}
+
+
+
+
diff --git a/Stars45/Mfd.h b/Stars45/Mfd.h
new file mode 100644
index 0000000..d1985ea
--- /dev/null
+++ b/Stars45/Mfd.h
@@ -0,0 +1,104 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MFD.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Class for all Multi Function Displays
+*/
+
+#ifndef MFD_h
+#define MFD_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Color.h"
+#include "HUDView.h"
+
+// +--------------------------------------------------------------------+
+
+class Contact;
+class Ship;
+class Sprite;
+class Window;
+
+// +--------------------------------------------------------------------+
+
+class MFD
+{
+public:
+ enum Modes { MFD_MODE_OFF, MFD_MODE_GAME, MFD_MODE_SHIP,
+ MFD_MODE_FOV, /*MFD_MODE_FWD, MFD_MODE_MAP,*/
+ MFD_MODE_HSD, MFD_MODE_3D
+ };
+
+ MFD(Window* c, int index);
+ virtual ~MFD();
+
+ int operator == (const MFD& that) const { return this == &that; }
+
+ static void Initialize();
+ static void Close();
+ static void SetColor(Color c);
+
+ // Operations:
+ virtual void Draw();
+ virtual void DrawGameMFD();
+ virtual void DrawStatusMFD();
+ virtual void DrawSensorMFD();
+ virtual void DrawHSD();
+ virtual void Draw3D();
+ virtual void DrawSensorLabels(const char* mfd_mode);
+ virtual void DrawMap();
+ virtual void DrawGauge(int x, int y, int percent);
+ virtual void SetStatusColor(int status);
+
+ virtual void SetWindow(Window* w) { window = w; }
+ virtual Window* GetWindow() { return window; }
+ virtual void SetRect(const Rect& r);
+ virtual const Rect& GetRect() const { return rect; }
+ virtual void SetMode(int m);
+ virtual int GetMode() const { return mode; }
+ virtual Sprite* GetSprite() { return sprite; }
+
+ virtual void SetShip(Ship* s) { ship = s; }
+ virtual Ship* GetShip() { return ship; }
+
+ virtual void Show();
+ virtual void Hide();
+
+ virtual void UseCameraView(CameraView* v);
+ void DrawMFDText(int index, const char* txt, Rect& r, int align, int status=-1);
+ void HideMFDText(int index);
+ void SetText3DColor(Color c);
+ void SetCockpitHUDTexture(Bitmap* bmp) { cockpit_hud_texture = bmp; }
+
+ bool IsMouseLatched() const;
+
+protected:
+
+ enum { TXT_LAST=20 };
+
+ Window* window;
+ Rect rect;
+ int index;
+ int mode;
+ int lines;
+ Sprite* sprite;
+ bool hidden;
+ Ship* ship;
+ HUDText mfd_text[TXT_LAST];
+ CameraView* camview;
+ Bitmap* cockpit_hud_texture;
+
+ int mouse_latch;
+ bool mouse_in;
+};
+
+#endif MFD_h
+
diff --git a/Stars45/Mission.cpp b/Stars45/Mission.cpp
new file mode 100644
index 0000000..be8a711
--- /dev/null
+++ b/Stars45/Mission.cpp
@@ -0,0 +1,2160 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Mission.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission classes
+*/
+
+#include "MemDebug.h"
+#include "Mission.h"
+#include "MissionEvent.h"
+#include "StarSystem.h"
+#include "Galaxy.h"
+#include "Starshatter.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Element.h"
+#include "Instruction.h"
+#include "WeaponDesign.h"
+#include "Sim.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+#include "Random.h"
+#include "Skin.h"
+
+// +--------------------------------------------------------------------+
+
+Mission::Mission(int identity, const char* fname, const char* pname)
+ : id(identity), type(0), team(1), ok(false), active(false), complete(false),
+ star_system(0), start(33 * 3600), stardate(0), target(0), ward(0),
+ current(0), degrees(false)
+{
+ objective = Game::GetText("Mission.unspecified");
+ sitrep = Game::GetText("Mission.unknown");
+
+ if (fname)
+ strcpy(filename, fname);
+ else
+ ZeroMemory(filename, sizeof(filename));
+
+ if (pname)
+ strcpy(path, pname);
+ else
+ strcpy(path, "Missions/");
+}
+
+Mission::~Mission()
+{
+ ::Print("Mission::~Mission() id = %d name = '%s'\n", id, name.data());
+ elements.destroy();
+ events.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+Mission::Subtitles() const
+{
+ return subtitles;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Mission::AddElement(MissionElement* elem)
+{
+ if (elem)
+ elements.append(elem);
+}
+
+// +--------------------------------------------------------------------+
+
+MissionElement*
+Mission::FindElement(const char* name)
+{
+ ListIter<MissionElement> iter = elements;
+ while (++iter) {
+ MissionElement* elem = iter.value();
+
+ if (elem->Name() == name)
+ return elem;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Mission::IncreaseElemPriority(int elem_index)
+{
+ if (elem_index > 0 && elem_index < elements.size()) {
+ MissionElement* elem1 = elements.at(elem_index-1);
+ MissionElement* elem2 = elements.at(elem_index);
+
+ elements.at(elem_index-1) = elem2;
+ elements.at(elem_index) = elem1;
+ }
+}
+
+void
+Mission::DecreaseElemPriority(int elem_index)
+{
+ if (elem_index >= 0 && elem_index < elements.size()-1) {
+ MissionElement* elem1 = elements.at(elem_index);
+ MissionElement* elem2 = elements.at(elem_index+1);
+
+ elements.at(elem_index) = elem2;
+ elements.at(elem_index+1) = elem1;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Mission::IncreaseEventPriority(int event_index)
+{
+ if (event_index > 0 && event_index < events.size()) {
+ MissionEvent* event1 = events.at(event_index-1);
+ MissionEvent* event2 = events.at(event_index);
+
+ events.at(event_index-1) = event2;
+ events.at(event_index) = event1;
+ }
+}
+
+void
+Mission::DecreaseEventPriority(int event_index)
+{
+ if (event_index >= 0 && event_index < events.size()-1) {
+ MissionEvent* event1 = events.at(event_index);
+ MissionEvent* event2 = events.at(event_index+1);
+
+ events.at(event_index) = event2;
+ events.at(event_index+1) = event1;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Mission::SetStarSystem(StarSystem* s)
+{
+ if (star_system != s) {
+ star_system = s;
+
+ if (!system_list.contains(s))
+ system_list.append(s);
+ }
+}
+
+void
+Mission::ClearSystemList()
+{
+ star_system = 0;
+ system_list.clear();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Mission::SetPlayer(MissionElement* player_element)
+{
+ ListIter<MissionElement> elem = elements;
+ while (++elem) {
+ MissionElement* element = elem.value();
+ if (element == player_element)
+ element->player = 1;
+ else
+ element->player = 0;
+ }
+}
+
+MissionElement*
+Mission::GetPlayer()
+{
+ MissionElement* p = 0;
+
+ ListIter<MissionElement> elem = elements;
+ while (++elem) {
+ if (elem->player > 0)
+ p = elem.value();
+ }
+
+ return p;
+}
+
+// +--------------------------------------------------------------------+
+
+MissionEvent*
+Mission::FindEvent(int event_type) const
+{
+ Mission* pThis = (Mission*) this;
+ ListIter<MissionEvent> iter = pThis->events;
+ while (++iter) {
+ MissionEvent* event = iter.value();
+
+ if (event->Event() == event_type)
+ return event;
+ }
+
+ return 0;
+}
+
+void
+Mission::AddEvent(MissionEvent* event)
+{
+ if (event)
+ events.append(event);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Mission::Load(const char* fname, const char* pname)
+{
+ ok = false;
+
+ if (fname)
+ strcpy(filename, fname);
+
+ if (pname)
+ strcpy(path, pname);
+
+ if (!filename[0]) {
+ Print("\nCan't Load Mission, script unspecified.\n");
+ return ok;
+ }
+
+ // wipe existing mission before attempting to load...
+ elements.destroy();
+ events.destroy();
+
+ Print("\nLoad Mission: '%s'\n", filename);
+
+ DataLoader* loader = DataLoader::GetLoader();
+ bool old_fs = loader->IsFileSystemEnabled();
+ BYTE* block = 0;
+
+ loader->UseFileSystem(true);
+ loader->SetDataPath(path);
+ loader->LoadBuffer(filename, block, true);
+ loader->SetDataPath(0);
+ loader->UseFileSystem(old_fs);
+
+ ok = ParseMission((const char*) block);
+
+ loader->ReleaseBuffer(block);
+ Print("Mission Loaded.\n\n");
+
+ if (ok)
+ Validate();
+
+ return ok;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Mission::ParseMission(const char* block)
+{
+ Parser parser(new(__FILE__,__LINE__) BlockReader(block));
+ Term* term = parser.ParseTerm();
+ char err[256];
+
+ if (!term) {
+ sprintf(err, "ERROR: could not parse '%s'\n", filename);
+ AddError(err);
+ return ok;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "MISSION") {
+ sprintf(err, "ERROR: invalid mission file '%s'\n", filename);
+ AddError(err);
+ term->print(10);
+ return ok;
+ }
+ }
+
+ ok = true;
+
+ char target_name[256];
+ char ward_name[256];
+
+ target_name[0] = 0;
+ ward_name[0] = 0;
+
+ do {
+ delete term; term = 0;
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ Text defname = def->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "name") {
+ GetDefText(name, def, filename);
+ name = Game::GetText(name);
+ }
+
+ else if (defname == "desc") {
+ GetDefText(desc, def, filename);
+ if (desc.length() > 0 && desc.length() < 32)
+ desc = Game::GetText(desc);
+ }
+
+ else if (defname == "type") {
+ char typestr[64];
+ GetDefText(typestr, def, filename);
+ type = TypeFromName(typestr);
+ }
+
+ else if (defname == "system") {
+ char sysname[64];
+ GetDefText(sysname, def, filename);
+
+ Galaxy* galaxy = Galaxy::GetInstance();
+
+ if (galaxy) {
+ SetStarSystem(galaxy->GetSystem(sysname));
+ }
+ }
+
+ else if (defname == "degrees")
+ GetDefBool(degrees, def, filename);
+
+ else if (defname == "region")
+ GetDefText(region, def, filename);
+
+ else if (defname == "objective") {
+ GetDefText(objective, def, filename);
+ if (objective.length() > 0 && objective.length() < 32)
+ objective = Game::GetText(objective);
+ }
+
+ else if (defname == "sitrep") {
+ GetDefText(sitrep, def, filename);
+ if (sitrep.length() > 0 && sitrep.length() < 32)
+ sitrep = Game::GetText(sitrep);
+ }
+
+ else if (defname == "subtitles") {
+ Text subtitles_path;
+ DataLoader* loader = DataLoader::GetLoader();
+ BYTE* block = 0;
+
+ GetDefText(subtitles_path, def, filename);
+ loader->SetDataPath(0);
+ loader->LoadBuffer(subtitles_path, block, true);
+
+ subtitles = Text("\n") + (const char*) block;
+
+ loader->ReleaseBuffer(block);
+ }
+
+ else if (defname == "start")
+ GetDefTime(start, def, filename);
+
+ else if (defname == "stardate")
+ GetDefNumber(stardate, def, filename);
+
+ else if (defname == "team")
+ GetDefNumber(team, def, filename);
+
+ else if (defname == "target")
+ GetDefText(target_name, def, filename);
+
+ else if (defname == "ward")
+ GetDefText(ward_name, def, filename);
+
+ else if ((defname == "element") ||
+ (defname == "ship") ||
+ (defname == "station")) {
+
+ if (!def->term() || !def->term()->isStruct()) {
+ sprintf(err, "ERROR: element struct missing in '%s'\n", filename);
+ AddError(err);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ MissionElement* elem = ParseElement(val);
+ AddElement(elem);
+ }
+ }
+
+ else if (defname == "event") {
+ if (!def->term() || !def->term()->isStruct()) {
+ sprintf(err, "ERROR: event struct missing in '%s'\n", filename);
+ AddError(err);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ MissionEvent* event = ParseEvent(val);
+ AddEvent(event);
+ }
+ }
+ } // def
+ } // term
+ }
+ while (term);
+
+ if (ok) {
+ if (target_name[0])
+ target = FindElement(target_name);
+
+ if (ward_name[0])
+ ward = FindElement(ward_name);
+ }
+
+ return ok;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Mission::Save()
+{
+ Validate();
+
+ if (!filename[0] || !path[0]) {
+ AddError(Game::GetText("Mission.error.no-file"));
+ return ok;
+ }
+
+ Text content = Serialize();
+
+ if (content.length() < 8) {
+ AddError(Game::GetText("Mission.error.no-serial"));
+ return ok;
+ }
+
+ if (!stricmp(path, "mods/missions/")) {
+ CreateDirectory("Mods", 0);
+ CreateDirectory("Mods/Missions", 0);
+ }
+
+ else if (!stricmp(path, "multiplayer/")) {
+ CreateDirectory("Multiplayer", 0);
+ }
+
+ char fname[256];
+ sprintf(fname, "%s%s", path, filename);
+ FILE* f = fopen(fname, "w");
+ if (f) {
+ fwrite(content.data(), content.length(), 1, f);
+ fclose(f);
+ }
+
+ return ok;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Mission::Validate()
+{
+ char err[256];
+
+ ok = true;
+
+ if (elements.isEmpty()) {
+ sprintf(err, Game::GetText("Mission.error.no-elem").data(), filename);
+ AddError(err);
+ }
+ else {
+ bool found_player = false;
+
+ for (int i = 0; i < elements.size(); i++) {
+ MissionElement* elem = elements.at(i);
+
+ if (elem->Name().length() < 1) {
+ sprintf(err, Game::GetText("Mission.error.unnamed-elem").data(), filename);
+ AddError(err);
+ }
+
+ if (elem->Player() > 0) {
+ if (!found_player) {
+ found_player = true;
+
+ if (elem->Region() != GetRegion()) {
+ sprintf(err, Game::GetText("Mission.error.wrong-sector").data(),
+ elem->Name().data(),
+ GetRegion());
+ AddError(err);
+ }
+ }
+ else {
+ sprintf(err, Game::GetText("Mission.error.extra-player").data(),
+ elem->Name().data(),
+ filename);
+ AddError(err);
+ }
+ }
+ }
+
+ if (!found_player) {
+ sprintf(err, Game::GetText("Mission.error.no-player").data(), filename);
+ AddError(err);
+ }
+ }
+}
+
+void
+Mission::AddError(Text err)
+{
+ ::Print(err);
+ errmsg += err;
+
+ ok = false;
+}
+
+// +--------------------------------------------------------------------+
+
+#define MSN_CHECK(x) if (!stricmp(n, #x)) result = Mission::x;
+
+int
+Mission::TypeFromName(const char* n)
+{
+ int result = -1;
+
+ MSN_CHECK(PATROL)
+ else MSN_CHECK(SWEEP)
+ else MSN_CHECK(INTERCEPT)
+ else MSN_CHECK(AIR_PATROL)
+ else MSN_CHECK(AIR_SWEEP)
+ else MSN_CHECK(AIR_INTERCEPT)
+ else MSN_CHECK(STRIKE)
+ else MSN_CHECK(DEFEND)
+ else MSN_CHECK(ESCORT)
+ else MSN_CHECK(ESCORT_FREIGHT)
+ else MSN_CHECK(ESCORT_SHUTTLE)
+ else MSN_CHECK(ESCORT_STRIKE)
+ else MSN_CHECK(INTEL)
+ else MSN_CHECK(SCOUT)
+ else MSN_CHECK(RECON)
+ else MSN_CHECK(TRANSPORT)
+ else MSN_CHECK(BLOCKADE)
+ else MSN_CHECK(FLEET)
+ else MSN_CHECK(BOMBARDMENT)
+ else MSN_CHECK(FLIGHT_OPS)
+ else MSN_CHECK(TRANSPORT)
+ else MSN_CHECK(CARGO)
+ else MSN_CHECK(TRAINING)
+ else MSN_CHECK(OTHER)
+
+ if (result < PATROL) {
+ for (int i = PATROL; i <= OTHER && result < PATROL; i++) {
+ if (!stricmp(n, RoleName(i))) {
+ result = i;
+ }
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+static int elem_id = 351;
+
+MissionElement*
+Mission::ParseElement(TermStruct* val)
+{
+ Text design;
+ Text skin_name;
+ Text role_name;
+ int deck = 1;
+ char err[256];
+
+ MissionElement* element = new(__FILE__,__LINE__) MissionElement();
+ element->rgn_name = region;
+ element->elem_id = elem_id++;
+
+ current = element;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "name")
+ GetDefText(element->name, pdef, filename);
+
+ else if (defname == "carrier")
+ GetDefText(element->carrier, pdef, filename);
+
+ else if (defname == "commander")
+ GetDefText(element->commander, pdef, filename);
+
+ else if (defname == "squadron")
+ GetDefText(element->squadron, pdef, filename);
+
+ else if (defname == "path")
+ GetDefText(element->path, pdef, filename);
+
+ else if (defname == "design") {
+ GetDefText(design, pdef, filename);
+ element->design = ShipDesign::Get(design, element->path);
+
+ if (!element->design) {
+ sprintf(err, Game::GetText("Mission.error.unknown-ship").data(), design.data(), filename);
+ AddError(err);
+ }
+ }
+
+ else if (defname == "skin") {
+ if (!element->design) {
+ sprintf(err, Game::GetText("Mission.error.out-of-order").data(), filename);
+ AddError(err);
+ }
+
+ else if (pdef->term()->isText()) {
+ GetDefText(skin_name, pdef, filename);
+ element->skin = element->design->FindSkin(skin_name);
+ }
+
+ else if (pdef->term()->isStruct()) {
+ sprintf(err, Game::GetText("Mission.error.bad-skin").data(), filename);
+ AddError(err);
+ }
+ }
+
+ else if (defname == "mission") {
+ GetDefText(role_name, pdef, filename);
+ element->mission_role = TypeFromName(role_name);
+ }
+
+ else if (defname == "intel") {
+ GetDefText(role_name, pdef, filename);
+ element->intel = Intel::IntelFromName(role_name);
+ }
+
+ else if (defname == "loc") {
+ Vec3 loc;
+ GetDefVec(loc, pdef, filename);
+ element->SetLocation(loc);
+ }
+
+ else if (defname == "rloc") {
+ if (pdef->term()->isStruct()) {
+ RLoc* rloc = ParseRLoc(pdef->term()->isStruct());
+ element->SetRLoc(*rloc);
+ delete rloc;
+ }
+ }
+
+ else if (defname.indexOf("head") == 0) {
+ if (pdef->term()->isArray()) {
+ Vec3 head;
+ GetDefVec(head, pdef, filename);
+ if (degrees) head.z *= (float) DEGREES;
+ element->heading = head.z;
+ }
+ else if (pdef->term()->isNumber()) {
+ double heading = 0;
+ GetDefNumber(heading, pdef, filename);
+ if (degrees) heading *= DEGREES;
+ element->heading = heading;
+ }
+ }
+
+ else if (defname == "region" || defname == "rgn")
+ GetDefText(element->rgn_name, pdef, filename);
+
+ else if (defname == "iff")
+ GetDefNumber(element->IFF_code, pdef, filename);
+
+ else if (defname == "count")
+ GetDefNumber(element->count, pdef, filename);
+
+ else if (defname == "maint_count")
+ GetDefNumber(element->maint_count, pdef, filename);
+
+ else if (defname == "dead_count")
+ GetDefNumber(element->dead_count, pdef, filename);
+
+ else if (defname == "player")
+ GetDefNumber(element->player, pdef, filename);
+
+ else if (defname == "alert")
+ GetDefBool(element->alert, pdef, filename);
+
+ else if (defname == "playable")
+ GetDefBool(element->playable, pdef, filename);
+
+ else if (defname == "rogue")
+ GetDefBool(element->rogue, pdef, filename);
+
+ else if (defname == "invulnerable")
+ GetDefBool(element->invulnerable, pdef, filename);
+
+ else if (defname == "command_ai")
+ GetDefNumber(element->command_ai, pdef, filename);
+
+ else if (defname.indexOf("respawn") == 0)
+ GetDefNumber(element->respawns, pdef, filename);
+
+ else if (defname.indexOf("hold") == 0)
+ GetDefNumber(element->hold_time, pdef, filename);
+
+ else if (defname.indexOf("zone") == 0) {
+ if (pdef->term() && pdef->term()->isBool()) {
+ bool locked = false;
+ GetDefBool(locked, pdef, filename);
+ element->zone_lock = locked;
+ }
+ else {
+ GetDefNumber(element->zone_lock, pdef, filename);
+ }
+ }
+
+ else if (defname == "objective") {
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ sprintf(err, Game::GetText("Mission.error.no-objective").data(), element->name.data(), filename);
+ AddError(err);
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+ Instruction* obj = ParseInstruction(val, element);
+ element->objectives.append(obj);
+ }
+ }
+
+ else if (defname == "instr") {
+ Text* obj = new(__FILE__,__LINE__) Text;
+ if (GetDefText(*obj, pdef, filename))
+ element->instructions.append(obj);
+ else
+ delete obj;
+ }
+
+ else if (defname == "ship") {
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ sprintf(err, Game::GetText("Mission.error.no-ship").data(), element->name.data(), filename);
+ AddError(err);
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+ MissionShip* s = ParseShip(val, element);
+ element->ships.append(s);
+
+ if (s->Integrity() < 0 && element->design)
+ s->SetIntegrity(element->design->integrity);
+ }
+ }
+
+ else if (defname == "order" || defname == "navpt") {
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ sprintf(err, Game::GetText("Mission.error.no-navpt").data(), element->name.data(), filename);
+ AddError(err);
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+ Instruction* npt = ParseInstruction(val, element);
+ element->navlist.append(npt);
+ }
+ }
+
+ else if (defname == "loadout") {
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ sprintf(err, Game::GetText("Mission.error.no-loadout").data(), element->name.data(), filename);
+ AddError(err);
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+ ParseLoadout(val, element);
+ }
+ }
+ }
+ }
+
+ if (element->name.length() < 1) {
+ sprintf(err, Game::GetText("Mission.error.unnamed-elem").data(), filename);
+ AddError(err);
+ }
+
+ else if (element->design == 0) {
+ sprintf(err, Game::GetText("Mission.error.unknown-ship").data(), element->name.data(), filename);
+ AddError(err);
+ }
+
+ current = 0;
+
+ return element;
+}
+
+MissionEvent*
+Mission::ParseEvent(TermStruct* val)
+{
+ MissionEvent* event = new(__FILE__,__LINE__) MissionEvent;
+ Text event_name;
+ Text trigger_name;
+ static int event_id = 1;
+ static double event_time = 0;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "event") {
+ GetDefText(event_name, pdef, filename);
+ event->event = MissionEvent::EventForName(event_name);
+ }
+
+ else if (defname == "trigger") {
+ GetDefText(trigger_name, pdef, filename);
+ event->trigger = MissionEvent::TriggerForName(trigger_name);
+ }
+
+ else if (defname == "id")
+ GetDefNumber(event_id, pdef, filename);
+
+ else if (defname == "time")
+ GetDefNumber(event_time, pdef, filename);
+
+ else if (defname == "delay")
+ GetDefNumber(event->delay, pdef, filename);
+
+ else if (defname == "event_param" || defname == "param" || defname == "color") {
+ ZeroMemory(event->event_param, sizeof(event->event_param));
+
+ if (pdef->term()->isNumber()) {
+ GetDefNumber(event->event_param[0], pdef, filename);
+ event->event_nparams = 1;
+ }
+
+ else if (pdef->term()->isArray()) {
+ FloatList plist;
+ GetDefArray(plist, pdef, filename);
+
+ for (int i = 0; i < 10 && i < plist.size(); i++) {
+ float f = plist[i];
+ event->event_param[i] = (int) f;
+ event->event_nparams = i + 1;
+ }
+ }
+ }
+
+ else if (defname == "trigger_param") {
+ ZeroMemory(event->trigger_param, sizeof(event->trigger_param));
+
+ if (pdef->term()->isNumber()) {
+ GetDefNumber(event->trigger_param[0], pdef, filename);
+ event->trigger_nparams = 1;
+ }
+
+ else if (pdef->term()->isArray()) {
+ FloatList plist;
+ GetDefArray(plist, pdef, filename);
+
+ for (int i = 0; i < 10 && i < plist.size(); i++) {
+ float f = plist[i];
+ event->trigger_param[i] = (int) f;
+ event->trigger_nparams = i + 1;
+ }
+ }
+ }
+
+ else if (defname == "event_ship" || defname == "ship")
+ GetDefText(event->event_ship, pdef, filename);
+
+ else if (defname == "event_source" || defname == "source" || defname == "font")
+ GetDefText(event->event_source, pdef, filename);
+
+ else if (defname == "event_target" || defname == "target" || defname == "image")
+ GetDefText(event->event_target, pdef, filename);
+
+ else if (defname == "event_message" || defname == "message") {
+ Text raw_msg;
+ GetDefText(raw_msg, pdef, filename);
+ raw_msg = Game::GetText(raw_msg);
+ event->event_message = FormatTextEscape(raw_msg);
+ }
+
+ else if (defname == "event_chance" || defname == "chance")
+ GetDefNumber(event->event_chance, pdef, filename);
+
+ else if (defname == "event_sound" || defname == "sound")
+ GetDefText(event->event_sound, pdef, filename);
+
+ else if (defname == "loc" || defname == "vec" || defname == "fade")
+ GetDefVec(event->event_point, pdef, filename);
+
+ else if (defname == "rect")
+ GetDefRect(event->event_rect, pdef, filename);
+
+ else if (defname == "trigger_ship")
+ GetDefText(event->trigger_ship, pdef, filename);
+
+ else if (defname == "trigger_target")
+ GetDefText(event->trigger_target, pdef, filename);
+ }
+ }
+
+ event->id = event_id++;
+ event->time = event_time;
+ return event;
+}
+
+MissionShip*
+Mission::ParseShip(TermStruct* val, MissionElement* element)
+{
+ MissionShip* msn_ship = new(__FILE__,__LINE__) MissionShip;
+
+ Text name;
+ Text skin_name;
+ Text regnum;
+ Text region;
+ char err[256];
+ Vec3 loc(-1.0e9f, -1.0e9f, -1.0e9f);
+ Vec3 vel(-1.0e9f, -1.0e9f, -1.0e9f);
+ int respawns = -1;
+ double heading = -1e9;
+ double integrity = -1;
+ int ammo[16];
+ int fuel[4];
+ int i;
+
+ for (i = 0; i < 16; i++)
+ ammo[i] = -10;
+
+ for (i = 0; i < 4; i++)
+ fuel[i] = -10;
+
+ for (i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "name")
+ GetDefText(name, pdef, filename);
+
+ else if (defname == "skin") {
+ if (!element || !element->design) {
+ sprintf(err, Game::GetText("Mission.error.out-of-order").data(), filename);
+ AddError(err);
+ }
+
+ else if (pdef->term()->isText()) {
+ GetDefText(skin_name, pdef, filename);
+ msn_ship->skin = element->design->FindSkin(skin_name);
+ }
+
+ else if (pdef->term()->isStruct()) {
+ sprintf(err, Game::GetText("Mission.error.bad-skin").data(), filename);
+ AddError(err);
+ }
+ }
+
+ else if (defname == "regnum")
+ GetDefText(regnum, pdef, filename);
+
+ else if (defname == "region")
+ GetDefText(region, pdef, filename);
+
+ else if (defname == "loc")
+ GetDefVec(loc, pdef, filename);
+
+ else if (defname == "velocity")
+ GetDefVec(vel, pdef, filename);
+
+ else if (defname == "respawns")
+ GetDefNumber(respawns, pdef, filename);
+
+ else if (defname == "heading") {
+ if (pdef->term()->isArray()) {
+ Vec3 h;
+ GetDefVec(h, pdef, filename);
+ if (degrees) h.z *= (float) DEGREES;
+ heading = h.z;
+ }
+ else if (pdef->term()->isNumber()) {
+ double h = 0;
+ GetDefNumber(h, pdef, filename);
+ if (degrees) h *= DEGREES;
+ heading = h;
+ }
+ }
+
+ else if (defname == "integrity")
+ GetDefNumber(integrity, pdef, filename);
+
+ else if (defname == "ammo")
+ GetDefArray(ammo, 16, pdef, filename);
+
+ else if (defname == "fuel")
+ GetDefArray(fuel, 4, pdef, filename);
+ }
+ }
+
+ msn_ship->SetName(name);
+ msn_ship->SetRegNum(regnum);
+ msn_ship->SetRegion(region);
+ msn_ship->SetIntegrity(integrity);
+
+ if (loc.x > -1e9)
+ msn_ship->SetLocation(loc);
+
+ if (vel.x > -1e9)
+ msn_ship->SetVelocity(vel);
+
+ if (respawns > -1)
+ msn_ship->SetRespawns(respawns);
+
+ if (heading > -1e9)
+ msn_ship->SetHeading(heading);
+
+ if (ammo[0] > -10)
+ msn_ship->SetAmmo(ammo);
+
+ if (fuel[0] > -10)
+ msn_ship->SetFuel(fuel);
+
+ return msn_ship;
+}
+
+Instruction*
+Mission::ParseInstruction(TermStruct* val, MissionElement* element)
+{
+ int order = Instruction::VECTOR;
+ int status = Instruction::PENDING;
+ int formation = 0;
+ int speed = 0;
+ int priority = 1;
+ int farcast = 0;
+ int hold = 0;
+ int emcon = 0;
+ Vec3 loc(0,0,0);
+ RLoc* rloc = 0;
+ Text order_name;
+ Text status_name;
+ Text order_rgn_name;
+ Text tgt_name;
+ Text tgt_desc;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "cmd") {
+ GetDefText(order_name, pdef, filename);
+
+ for (int cmd = 0; cmd < Instruction::NUM_ACTIONS; cmd++)
+ if (!stricmp(order_name, Instruction::ActionName(cmd)))
+ order = cmd;
+ }
+
+ else if (defname == "status") {
+ GetDefText(status_name, pdef, filename);
+
+ for (int n = 0; n < Instruction::NUM_STATUS; n++)
+ if (!stricmp(status_name, Instruction::StatusName(n)))
+ status = n;
+ }
+
+ else if (defname == "loc") {
+ GetDefVec(loc, pdef, filename);
+ }
+
+ else if (defname == "rloc") {
+ if (pdef->term()->isStruct())
+ rloc = ParseRLoc(pdef->term()->isStruct());
+ }
+
+ else if (defname == "rgn") {
+ GetDefText(order_rgn_name, pdef, filename);
+ }
+ else if (defname == "speed") {
+ GetDefNumber(speed, pdef, filename);
+ }
+ else if (defname == "formation") {
+ GetDefNumber(formation, pdef, filename);
+ }
+ else if (defname == "emcon") {
+ GetDefNumber(emcon, pdef, filename);
+ }
+ else if (defname == "priority") {
+ GetDefNumber(priority, pdef, filename);
+ }
+ else if (defname == "farcast") {
+ if (pdef->term()->isBool()) {
+ bool f = false;
+ GetDefBool(f, pdef, filename);
+ farcast = f;
+ }
+ else {
+ GetDefNumber(farcast, pdef, filename);
+ }
+ }
+ else if (defname == "tgt") {
+ GetDefText(tgt_name, pdef, filename);
+ }
+ else if (defname == "tgt_desc") {
+ GetDefText(tgt_desc, pdef, filename);
+ }
+ else if (defname.indexOf("hold") == 0) {
+ GetDefNumber(hold, pdef, filename);
+ }
+ }
+ }
+
+ Text rgn;
+
+ if (order_rgn_name.length() > 0)
+ rgn = order_rgn_name;
+
+ else if (element->navlist.size() > 0)
+ rgn = element->navlist[element->navlist.size()-1]->RegionName();
+
+ else
+ rgn = region;
+
+ if (tgt_desc.length() && tgt_name.length())
+ tgt_desc = tgt_desc + " " + tgt_name;
+
+ Instruction* instr = new(__FILE__,__LINE__) Instruction(rgn, loc, order);
+
+ instr->SetStatus(status);
+ instr->SetEMCON(emcon);
+ instr->SetFormation(formation);
+ instr->SetSpeed(speed);
+ instr->SetTarget(tgt_name);
+ instr->SetTargetDesc(tgt_desc);
+ instr->SetPriority(priority-1);
+ instr->SetFarcast(farcast);
+ instr->SetHoldTime(hold);
+
+ if (rloc) {
+ instr->GetRLoc() = *rloc;
+ delete rloc;
+ }
+
+ return instr;
+}
+
+void
+Mission::ParseLoadout(TermStruct* val, MissionElement* element)
+{
+ int ship = -1;
+ int stations[16];
+ Text name;
+
+ ZeroMemory(stations, sizeof(stations));
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "ship") {
+ GetDefNumber(ship, pdef, filename);
+ }
+ else if (defname == "name") {
+ GetDefText(name, pdef, filename);
+ }
+ else if (defname == "stations") {
+ GetDefArray(stations, 16, pdef, filename);
+ }
+ }
+ }
+
+ MissionLoad* load = new(__FILE__,__LINE__) MissionLoad(ship);
+
+ if (name.length())
+ load->SetName(name);
+
+ for (i = 0; i < 16; i++)
+ load->SetStation(i, stations[i]);
+
+ element->loadouts.append(load);
+}
+
+RLoc*
+Mission::ParseRLoc(TermStruct* val)
+{
+ Vec3 base_loc;
+ RLoc* rloc = new(__FILE__,__LINE__) RLoc;
+ RLoc* ref = 0;
+
+ double dex = 0;
+ double dex_var = 5e3;
+ double az = 0;
+ double az_var = 3.1415;
+ double el = 0;
+ double el_var = 0.1;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "dex") {
+ GetDefNumber(dex, pdef, filename);
+ rloc->SetDistance(dex);
+ }
+ else if (defname == "dex_var") {
+ GetDefNumber(dex_var, pdef, filename);
+ rloc->SetDistanceVar(dex_var);
+ }
+ else if (defname == "az") {
+ GetDefNumber(az, pdef, filename);
+ if (degrees) az *= DEGREES;
+ rloc->SetAzimuth(az);
+ }
+ else if (defname == "az_var") {
+ GetDefNumber(az_var, pdef, filename);
+ if (degrees) az_var *= DEGREES;
+ rloc->SetAzimuthVar(az_var);
+ }
+ else if (defname == "el") {
+ GetDefNumber(el, pdef, filename);
+ if (degrees) el *= DEGREES;
+ rloc->SetElevation(el);
+ }
+ else if (defname == "el_var") {
+ GetDefNumber(el_var, pdef, filename);
+ if (degrees) el_var *= DEGREES;
+ rloc->SetElevationVar(el_var);
+ }
+ else if (defname == "loc") {
+ GetDefVec(base_loc, pdef, filename);
+ rloc->SetBaseLocation(base_loc);
+ }
+
+ else if (defname == "ref") {
+ Text refstr;
+ GetDefText(refstr, pdef, filename);
+
+ int sep = refstr.indexOf(':');
+
+ if (sep >= 0) {
+ Text elem_name = refstr.substring(0, sep);
+ Text nav_name = refstr.substring(sep+1, refstr.length());
+ MissionElement* elem = 0;
+
+ if (elem_name == "this")
+ elem = current;
+ else
+ elem = FindElement(elem_name);
+
+ if (elem && elem->NavList().size() > 0) {
+ int index = atoi(nav_name)-1;
+ if (index < 0)
+ index = 0;
+ else if (index >= elem->NavList().size())
+ index = elem->NavList().size()-1;
+
+ ref = &elem->NavList()[index]->GetRLoc();
+ rloc->SetReferenceLoc(ref);
+ }
+ else {
+ ::Print("Warning: no ref found for rloc '%s' in elem '%s'\n", refstr.data(), current->Name().data());
+ rloc->SetBaseLocation(RandomPoint());
+ }
+ }
+ else {
+ MissionElement* elem = 0;
+
+ if (refstr == "this")
+ elem = current;
+ else
+ elem = FindElement(refstr);
+
+ if (elem) {
+ ref = &elem->GetRLoc();
+ rloc->SetReferenceLoc(ref);
+ }
+ else {
+ ::Print("Warning: no ref found for rloc '%s' in elem '%s'\n", refstr.data(), current->Name().data());
+ rloc->SetBaseLocation(RandomPoint());
+ }
+ }
+ }
+ }
+ }
+
+ return rloc;
+}
+
+const char*
+Mission::RoleName(int role)
+{
+ switch (role) {
+ case PATROL: return "Patrol";
+ case SWEEP: return "Sweep";
+ case INTERCEPT: return "Intercept";
+ case AIR_PATROL: return "Airborne Patrol";
+ case AIR_SWEEP: return "Airborne Sweep";
+ case AIR_INTERCEPT: return "Airborne Intercept";
+ case STRIKE: return "Strike";
+ case ASSAULT: return "Assault";
+ case DEFEND: return "Defend";
+ case ESCORT: return "Escort";
+ case ESCORT_FREIGHT: return "Freight Escort";
+ case ESCORT_SHUTTLE: return "Shuttle Escort";
+ case ESCORT_STRIKE: return "Strike Escort";
+ case INTEL: return "Intel";
+ case SCOUT: return "Scout";
+ case RECON: return "Recon";
+ case BLOCKADE: return "Blockade";
+ case FLEET: return "Fleet";
+ case BOMBARDMENT: return "Attack";
+ case FLIGHT_OPS: return "Flight Ops";
+ case TRANSPORT: return "Transport";
+ case CARGO: return "Cargo";
+ case TRAINING: return "Training";
+ default:
+ case OTHER: return "Misc";
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+Mission::Serialize(const char* player_elem, int player_index)
+{
+ Text s = "MISSION\n\nname: \"";
+ s += SafeString(Name());
+
+ if (desc.length()) {
+ s += "\"\ndesc: \"";
+ s += SafeString(desc);
+ }
+
+ s += "\"\ntype: \"";
+ s += SafeString(TypeName());
+
+ ListIter<StarSystem> sys_iter = system_list;
+ while (++sys_iter) {
+ StarSystem* sys = sys_iter.value();
+
+ if (sys != star_system) {
+ s += "\"\nsystem: \"";
+ s += sys->Name();
+ }
+ }
+
+ s += "\"\nsystem: \"";
+ if (GetStarSystem())
+ s += SafeString(GetStarSystem()->Name());
+ else
+ s += "null";
+
+ ListIter<MissionElement> iter = GetElements();
+
+ Sim* sim = Sim::GetSim();
+ if (sim && sim->GetElements().size() > 0)
+ iter = sim->GetMissionElements();
+
+ s += "\"\n";
+
+ bool region_set = false;
+ if (player_elem && *player_elem) {
+ // set the mission region to that of the active player
+ while (++iter) {
+ MissionElement* e = iter.value();
+ if (e->Name() == player_elem) {
+ char buf[32];
+ sprintf(buf, "team: %d\n", e->GetIFF());
+ s += buf;
+
+ s += "region: \"";
+ s += SafeString(e->Region());
+ s += "\"\n\n";
+
+ region_set = true;
+ break;
+ }
+ }
+
+ iter.reset();
+ }
+
+ if (!region_set) {
+ s += "region: \"";
+ s += SafeString(GetRegion());
+ s += "\"\n\n";
+ }
+
+ if (Objective() && *Objective()) {
+ s += "objective: \"";
+ s += SafeString(Objective());
+ s += "\"\n\n";
+ }
+
+ if (Situation() && *Situation()) {
+ s += "sitrep: \"";
+ s += SafeString(Situation());
+ s += "\"\n\n";
+ }
+
+ char buffer[256];
+ FormatTime(buffer, Start());
+
+ s += "start: \"";
+ s += buffer;
+ s += "\"\n\n";
+ s += "degrees: true\n\n";
+
+ while (++iter) {
+ MissionElement* elem = iter.value();
+
+ s += "element: {\n";
+ s += " name: \"";
+ s += SafeString(elem->Name());
+ s += "\"\n";
+
+ if (elem->Path().length()) {
+ s += " path: \"";
+ s += SafeString(elem->Path());
+ s += "\"\n";
+ }
+
+ if (elem->GetDesign()) {
+ s += " design: \"";
+ s += SafeString(elem->GetDesign()->name);
+ s += "\"\n";
+ }
+
+ if (elem->GetSkin()) {
+ s += " skin: \"";
+ s += SafeString(elem->GetSkin()->Name());
+ s += "\"\n";
+ }
+
+ if (elem->Squadron().length()) {
+ s += " squadron: \"";
+ s += SafeString(elem->Squadron());
+ s += "\"\n";
+ }
+
+ if (elem->Carrier().length()) {
+ s += " carrier: \"";
+ s += SafeString(elem->Carrier());
+ s += "\"\n";
+ }
+
+ if (elem->Commander().length()) {
+ s += " commander: \"";
+ s += SafeString(elem->Commander());
+ s += "\"\n";
+ }
+
+ s += " mission: \"";
+ s += elem->RoleName();
+ s += "\"\n\n";
+
+ if (elem->IntelLevel()) {
+ s += " intel: \"";
+ s += Intel::NameFromIntel(elem->IntelLevel());
+ s += "\"\n";
+ }
+
+ sprintf(buffer, " count: %d\n", elem->Count());
+ s += buffer;
+
+ if (elem->MaintCount()) {
+ sprintf(buffer, " maint_count: %d\n", elem->MaintCount());
+ s += buffer;
+ }
+
+ if (elem->DeadCount()) {
+ sprintf(buffer, " dead_count: %d\n", elem->DeadCount());
+ s += buffer;
+ }
+
+ if (elem->RespawnCount()) {
+ sprintf(buffer, " respawn_count: %d\n", elem->RespawnCount());
+ s += buffer;
+ }
+
+ if (elem->HoldTime()) {
+ sprintf(buffer, " hold_time: %d\n", elem->HoldTime());
+ s += buffer;
+ }
+
+ if (elem->ZoneLock()) {
+ sprintf(buffer, " zone_lock: %d\n", elem->ZoneLock());
+ s += buffer;
+ }
+
+ if (elem->IsAlert()) {
+ s += " alert: true\n";
+ }
+
+ if (!elem->IsSquadron()) {
+ sprintf(buffer, " command_ai:%d\n", elem->CommandAI());
+ s += buffer;
+ }
+
+ sprintf(buffer, " iff: %d\n", elem->GetIFF());
+ s += buffer;
+
+ if (player_elem) {
+ if (elem->Name() == player_elem) {
+ if (player_index < 1)
+ player_index = 1;
+
+ sprintf(buffer, " player: %d\n", player_index);
+ s += buffer;
+ }
+ }
+
+ else {
+ if (elem->Player()) {
+ sprintf(buffer, " player: %d\n", elem->Player());
+ s += buffer;
+ }
+ }
+
+ if (!elem->IsSquadron()) {
+ if (elem->IsPlayable())
+ s += " playable: true\n";
+ else
+ s += " playable: false\n";
+ }
+
+
+ s += " region: \"";
+ s += elem->Region();
+ s += "\"\n";
+
+ sprintf(buffer, " loc: (%.0f, %.0f, %.0f)\n",
+ elem->Location().x,
+ elem->Location().y,
+ elem->Location().z);
+ s += buffer;
+
+ if (elem->Heading() != 0) {
+ sprintf(buffer, " head: %d\n", (int) (elem->Heading()/DEGREES));
+ s += buffer;
+ }
+
+ if (elem->Loadouts().size()) {
+ s += "\n";
+
+ ListIter<MissionLoad> load_iter = elem->Loadouts();
+ while (++load_iter) {
+ MissionLoad* load = load_iter.value();
+
+ sprintf(buffer, " loadout: { ship: %d, ", load->GetShip());
+ s += buffer;
+
+ if (load->GetName().length()) {
+ s += "name: \"";
+ s += SafeString(load->GetName());
+ s += "\" }\n";
+ }
+ else {
+ s += "stations: (";
+
+ for (int i = 0; i < 16; i++) {
+ sprintf(buffer, "%d", load->GetStation(i));
+ s += buffer;
+
+ if (i < 15)
+ s += ", ";
+ }
+
+ s += ") }\n";
+ }
+ }
+ }
+
+ if (elem->Objectives().size()) {
+ s += "\n";
+
+ ListIter<Instruction> obj_iter = elem->Objectives();
+ while (++obj_iter) {
+ Instruction* inst = obj_iter.value();
+
+ s += " objective: { cmd: ";
+ s += Instruction::ActionName(inst->Action());
+ s += ", tgt: \"";
+ s += SafeString(inst->TargetName());
+ s += "\" }\n";
+ }
+ }
+
+ if (elem->NavList().size()) {
+ s += "\n";
+
+ ListIter<Instruction> nav_iter = elem->NavList();
+ while (++nav_iter) {
+ Instruction* inst = nav_iter.value();
+
+ s += " navpt: { cmd: ";
+ s += Instruction::ActionName(inst->Action());
+ s += ", status: ";
+ s += Instruction::StatusName(inst->Status());
+
+ if (inst->TargetName() && *inst->TargetName()) {
+ s += ", tgt: \"";
+ s += SafeString(inst->TargetName());
+ s += "\"";
+ }
+
+ sprintf(buffer, ", loc: (%.0f, %.0f, %.0f), speed: %d",
+ inst->Location().x,
+ inst->Location().y,
+ inst->Location().z,
+ inst->Speed());
+ s += buffer;
+
+ if (inst->RegionName() && *inst->RegionName()) {
+ s += ", rgn: \"";
+ s += inst->RegionName();
+ s += "\"";
+ }
+
+ if (inst->HoldTime()) {
+ sprintf(buffer, ", hold: %d", (int) inst->HoldTime());
+ s += buffer;
+ }
+
+ if (inst->Farcast()) {
+ s += ", farcast: true";
+ }
+
+ if (inst->Formation() > Instruction::DIAMOND) {
+ sprintf(buffer, ", formation: %d", (int) inst->Formation());
+ s += buffer;
+ }
+
+ if (inst->Priority() > Instruction::PRIMARY) {
+ sprintf(buffer, ", priority: %d", (int) inst->Priority());
+ s += buffer;
+ }
+
+ s += " }\n";
+ }
+ }
+
+ if (elem->Instructions().size()) {
+ s += "\n";
+
+ ListIter<Text> i_iter = elem->Instructions();
+ while (++i_iter) {
+ s += " instr: \"";
+ s += SafeString(*i_iter.value());
+ s += "\"\n";
+ }
+ }
+
+ if (elem->Ships().size()) {
+ ListIter<MissionShip> s_iter = elem->Ships();
+ while (++s_iter) {
+ MissionShip* ship = s_iter.value();
+
+ s += "\n ship: {\n";
+
+ if (ship->Name().length()) {
+ s += " name: \"";
+ s += SafeString(ship->Name());
+ s += "\"\n";
+ }
+
+ if (ship->RegNum().length()) {
+ s += " regnum: \"";
+ s += SafeString(ship->RegNum());
+ s += "\"\n";
+ }
+
+ if (ship->Region().length()) {
+ s += " region: \"";
+ s += SafeString(ship->Region());
+ s += "\"\n";
+ }
+
+ if (fabs(ship->Location().x) < 1e9) {
+ sprintf(buffer, " loc: (%.0f, %.0f, %.0f),\n",
+ ship->Location().x,
+ ship->Location().y,
+ ship->Location().z);
+ s += buffer;
+ }
+
+ if (fabs(ship->Velocity().x) < 1e9) {
+ sprintf(buffer, " velocity: (%.1f, %.1f, %.1f),\n",
+ ship->Velocity().x,
+ ship->Velocity().y,
+ ship->Velocity().z);
+ s += buffer;
+ }
+
+ if (ship->Respawns() > -1) {
+ sprintf(buffer, " respawns: %d,\n", ship->Respawns());
+ s += buffer;
+ }
+
+ if (ship->Heading() > -1e9) {
+ sprintf(buffer, " heading: %d,\n", (int) (ship->Heading()/DEGREES));
+ s += buffer;
+ }
+
+ if (ship->Integrity() > -1) {
+ sprintf(buffer, " integrity: %d,\n", (int) ship->Integrity());
+ s += buffer;
+ }
+
+ if (ship->Decoys() > -1) {
+ sprintf(buffer, " decoys: %d,\n", ship->Decoys());
+ s += buffer;
+ }
+
+ if (ship->Probes() > -1) {
+ sprintf(buffer, " probes: %d,\n", ship->Probes());
+ s += buffer;
+ }
+
+ if (ship->Ammo()[0] > -10) {
+ s += "\n ammo: (";
+
+ for (int i = 0; i < 16; i++) {
+ sprintf(buffer, "%d", ship->Ammo()[i]);
+ s += buffer;
+
+ if (i < 15)
+ s += ", ";
+ }
+
+ s += ")\n";
+ }
+
+ if (ship->Fuel()[0] > -10) {
+ s += "\n fuel: (";
+
+ for (int i = 0; i < 4; i++) {
+ sprintf(buffer, "%d", ship->Fuel()[i]);
+ s += buffer;
+
+ if (i < 3)
+ s += ", ";
+ }
+
+ s += ")\n";
+ }
+
+ s += " }\n";
+ }
+ }
+
+ s += "}\n\n";
+ }
+
+ ListIter<MissionEvent> iter2 = GetEvents();
+ while (++iter2) {
+ MissionEvent* event = iter2.value();
+
+ s += "event: {\n";
+
+ s += " id: ";
+ sprintf(buffer, "%d", event->EventID());
+ s += buffer;
+ s += ",\n time: ";
+ sprintf(buffer, "%.1f", event->Time());
+ s += buffer;
+ s += ",\n delay: ";
+ sprintf(buffer, "%.1f", event->Delay());
+ s += buffer;
+ s += ",\n event: ";
+ s += event->EventName();
+ s += "\n";
+
+ if (event->EventShip().length()) {
+ s += " event_ship: \"";
+ s += SafeString(event->EventShip());
+ s += "\"\n";
+ }
+
+ if (event->EventSource().length()) {
+ s += " event_source: \"";
+ s += SafeString(event->EventSource());
+ s += "\"\n";
+ }
+
+ if (event->EventTarget().length()) {
+ s += " event_target: \"";
+ s += SafeString(event->EventTarget());
+ s += "\"\n";
+ }
+
+ if (event->EventSound().length()) {
+ s += " event_sound: \"";
+ s += SafeString(event->EventSound());
+ s += "\"\n";
+ }
+
+ if (event->EventMessage().length()) {
+ s += " event_message: \"";
+ s += SafeString(event->EventMessage());
+ s += "\"\n";
+ }
+
+ if (event->EventParam()) {
+ sprintf(buffer, "%d", event->EventParam());
+ s += " event_param: ";
+ s += buffer;
+ s += "\n";
+ }
+
+ if (event->EventChance()) {
+ sprintf(buffer, "%d", event->EventChance());
+ s += " event_chance: ";
+ s += buffer;
+ s += "\n";
+ }
+
+ s += " trigger: \"";
+ s += event->TriggerName();
+ s += "\"\n";
+
+ if (event->TriggerShip().length()) {
+ s += " trigger_ship: \"";
+ s += SafeString(event->TriggerShip());
+ s += "\"\n";
+ }
+
+ if (event->TriggerTarget().length()) {
+ s += " trigger_target: \"";
+ s += SafeString(event->TriggerTarget());
+ s += "\"\n";
+ }
+
+ Text param_str = event->TriggerParamStr();
+
+ if (param_str.length()) {
+ s += " trigger_param: ";
+ s += param_str;
+ s += "\n";
+ }
+
+ s += "}\n\n";
+ }
+
+ s += "// EOF\n";
+
+ return s;
+}
+
+// +====================================================================+
+
+static int elem_idkey = 1;
+
+MissionElement::MissionElement()
+ : id (elem_idkey++),
+ elem_id(0), design(0), skin(0), count(1), maint_count(0), dead_count(0),
+ IFF_code(0), player(0), alert(false), playable(false), rogue(false), invulnerable(false),
+ respawns(0), hold_time(0), zone_lock(0), heading(0), mission_role(Mission::OTHER),
+ intel(Intel::SECRET), command_ai(1), combat_group(0), combat_unit(0)
+{
+}
+
+MissionElement::~MissionElement()
+{
+ ships.destroy();
+ objectives.destroy();
+ instructions.destroy();
+ navlist.destroy();
+ loadouts.destroy();
+}
+
+Text
+MissionElement::Abbreviation() const
+{
+ if (design)
+ return design->abrv;
+
+ return "UNK";
+}
+
+Text
+MissionElement::GetShipName(int index) const
+{
+ if (index < 0 || index >= ships.size()) {
+ if (count > 1) {
+ char sname[256];
+ sprintf(sname, "%s %d", (const char*) name, index+1);
+ return sname;
+ }
+ else {
+ return name;
+ }
+ }
+
+ return ships.at(index)->Name();
+}
+
+Text
+MissionElement::GetRegistry(int index) const
+{
+ if (index < 0 || index >= ships.size()) {
+ return Text();
+ }
+
+ return ships.at(index)->RegNum();
+}
+
+Text
+MissionElement::RoleName() const
+{
+ return Mission::RoleName(mission_role);
+}
+
+Color
+MissionElement::MarkerColor() const
+{
+ return Ship::IFFColor(IFF_code);
+}
+
+bool
+MissionElement::IsStatic() const
+{
+ int design_type = 0;
+ if (GetDesign())
+ design_type = GetDesign()->type;
+
+ return design_type >= Ship::STATION;
+}
+
+bool
+MissionElement::IsGroundUnit() const
+{
+ int design_type = 0;
+ if (GetDesign())
+ design_type = GetDesign()->type;
+
+ return (design_type & Ship::GROUND_UNITS) ? true : false;
+}
+
+bool
+MissionElement::IsStarship() const
+{
+ int design_type = 0;
+ if (GetDesign())
+ design_type = GetDesign()->type;
+
+ return (design_type & Ship::STARSHIPS) ? true : false;
+}
+
+bool
+MissionElement::IsDropship() const
+{
+ int design_type = 0;
+ if (GetDesign())
+ design_type = GetDesign()->type;
+
+ return (design_type & Ship::DROPSHIPS) ? true : false;
+}
+
+bool
+MissionElement::IsCarrier() const
+{
+ const ShipDesign* design = GetDesign();
+ if (design && design->flight_decks.size() > 0)
+ return true;
+
+ return false;
+}
+
+bool
+MissionElement::IsSquadron() const
+{
+ if (carrier.length() > 0)
+ return true;
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+Point
+MissionElement::Location() const
+{
+ MissionElement* pThis = (MissionElement*) this;
+ return pThis->rloc.Location();
+}
+
+void
+MissionElement::SetLocation(const Point& l)
+{
+ rloc.SetBaseLocation(l);
+ rloc.SetReferenceLoc(0);
+ rloc.SetDistance(0);
+}
+
+void
+MissionElement::SetRLoc(const RLoc& r)
+{
+ rloc = r;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+MissionElement::AddNavPoint(Instruction* pt, Instruction* afterPoint)
+{
+ if (pt && !navlist.contains(pt)) {
+ if (afterPoint) {
+ int index = navlist.index(afterPoint);
+
+ if (index > -1)
+ navlist.insert(pt, index+1);
+ else
+ navlist.append(pt);
+ }
+
+ else {
+ navlist.append(pt);
+ }
+ }
+}
+
+void
+MissionElement::DelNavPoint(Instruction* pt)
+{
+ if (pt)
+ delete navlist.remove(pt);
+}
+
+void
+MissionElement::ClearFlightPlan()
+{
+ navlist.destroy();
+}
+
+// +----------------------------------------------------------------------+
+
+int
+MissionElement::GetNavIndex(const Instruction* n)
+{
+ int index = 0;
+
+ if (navlist.size() > 0) {
+ ListIter<Instruction> navpt = navlist;
+ while (++navpt) {
+ index++;
+ if (navpt.value() == n)
+ return index;
+ }
+ }
+
+ return 0;
+}
+
+// +====================================================================+
+
+MissionLoad::MissionLoad(int s, const char* n)
+ : ship(s)
+{
+ for (int i = 0; i < 16; i++)
+ load[i] = -1; // default: no weapon mounted
+
+ if (n)
+ name = n;
+}
+
+MissionLoad::~MissionLoad()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MissionLoad::GetShip() const
+{
+ return ship;
+}
+
+void
+MissionLoad::SetShip(int s)
+{
+ ship = s;
+}
+
+Text
+MissionLoad::GetName() const
+{
+ return name;
+}
+
+void
+MissionLoad::SetName(Text n)
+{
+ name = n;
+}
+
+int*
+MissionLoad::GetStations()
+{
+ return load;
+}
+
+int
+MissionLoad::GetStation(int index)
+{
+ if (index >= 0 && index < 16)
+ return load[index];
+
+ return 0;
+}
+
+void
+MissionLoad::SetStation(int index, int selection)
+{
+ if (index >= 0 && index < 16)
+ load[index] = selection;
+}
+
+
+// +====================================================================+
+
+MissionShip::MissionShip()
+ : loc(-1e9, -1e9, -1e9), respawns(0), heading(0), integrity(100),
+ decoys(-10), probes(-10), skin(0)
+{
+ for (int i = 0; i < 16; i++)
+ ammo[i] = -10;
+
+ for (i = 0; i < 4; i++)
+ fuel[i] = -10;
+}
+
+void
+MissionShip::SetAmmo(const int* a)
+{
+ if (a) {
+ for (int i = 0; i < 16; i++)
+ ammo[i] = a[i];
+ }
+}
+
+void
+MissionShip::SetFuel(const int* f)
+{
+ if (f) {
+ for (int i = 0; i < 4; i++)
+ fuel[i] = f[i];
+ }
+}
diff --git a/Stars45/Mission.h b/Stars45/Mission.h
new file mode 100644
index 0000000..f792951
--- /dev/null
+++ b/Stars45/Mission.h
@@ -0,0 +1,426 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Mission.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Simulation Universe and Region classes
+*/
+
+#ifndef Mission_h
+#define Mission_h
+
+#include "Types.h"
+#include "Intel.h"
+#include "RLoc.h"
+#include "Universe.h"
+#include "Scene.h"
+#include "Skin.h"
+#include "Physical.h"
+#include "Geometry.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Mission;
+class MissionElement;
+class MissionLoad;
+class MissionEvent;
+class MissionShip;
+
+class CombatGroup;
+class CombatUnit;
+
+class Ship;
+class System;
+class Element;
+class ShipDesign;
+class WeaponDesign;
+class StarSystem;
+class Instruction;
+
+class Term;
+class TermArray;
+class TermStruct;
+
+// +--------------------------------------------------------------------+
+
+class Mission
+{
+public:
+ static const char* TYPENAME() { return "Mission"; }
+
+ enum TYPE
+ {
+ PATROL,
+ SWEEP,
+ INTERCEPT,
+ AIR_PATROL,
+ AIR_SWEEP,
+ AIR_INTERCEPT,
+ STRIKE, // ground attack
+ ASSAULT, // starship attack
+ DEFEND,
+ ESCORT,
+ ESCORT_FREIGHT,
+ ESCORT_SHUTTLE,
+ ESCORT_STRIKE,
+ INTEL,
+ SCOUT,
+ RECON,
+ BLOCKADE,
+ FLEET,
+ BOMBARDMENT,
+ FLIGHT_OPS,
+ TRANSPORT,
+ CARGO,
+ TRAINING,
+ OTHER
+ };
+
+ Mission(int id, const char* filename=0, const char* path=0);
+ virtual ~Mission();
+
+ int operator == (const Mission& m) const { return id == m.id; }
+
+ virtual void Validate();
+ virtual bool Load(const char* filename=0, const char* path=0);
+ virtual bool Save();
+ virtual bool ParseMission(const char* buffer);
+ virtual void SetPlayer(MissionElement* player_element);
+ virtual MissionElement* GetPlayer();
+
+ // accessors/mutators:
+ int Identity() const { return id; }
+ const char* FileName() const { return filename; }
+ const char* Name() const { return name; }
+ const char* Description() const { return desc; }
+ const char* Situation() const { return sitrep; }
+ const char* Objective() const { return objective; }
+ const char* Subtitles() const;
+ int Start() const { return start; }
+ double Stardate() const { return stardate; }
+ int Type() const { return type; }
+ const char* TypeName() const { return RoleName(type); }
+ int Team() const { return team; }
+ bool IsOK() const { return ok; }
+ bool IsActive() const { return active; }
+ bool IsComplete() const { return complete; }
+
+ StarSystem* GetStarSystem() const { return star_system; }
+ List<StarSystem>& GetSystemList() { return system_list; }
+ const char* GetRegion() const { return region; }
+
+ List<MissionElement>& GetElements() { return elements; }
+ virtual MissionElement* FindElement(const char* name);
+ virtual void AddElement(MissionElement* elem);
+
+ List<MissionEvent>& GetEvents() { return events; }
+ MissionEvent* FindEvent(int event_type) const;
+ virtual void AddEvent(MissionEvent* event);
+
+ MissionElement* GetTarget() const { return target; }
+ MissionElement* GetWard() const { return ward; }
+
+ void SetName(const char* n) { name = n; }
+ void SetDescription(const char* d) { desc = d; }
+ void SetSituation(const char* sit) { sitrep = sit; }
+ void SetObjective(const char* obj) { objective = obj; }
+ void SetStart(int s) { start = s; }
+ void SetType(int t) { type = t; }
+ void SetTeam(int iff) { team = iff; }
+ void SetStarSystem(StarSystem* s);
+ void SetRegion(const char* rgn) { region = rgn; }
+ void SetOK(bool a) { ok = a; }
+ void SetActive(bool a) { active = a; }
+ void SetComplete(bool c) { complete = c; }
+ void SetTarget(MissionElement* t) { target = t; }
+ void SetWard(MissionElement* w) { ward = w; }
+
+ void ClearSystemList();
+
+ void IncreaseElemPriority(int index);
+ void DecreaseElemPriority(int index);
+ void IncreaseEventPriority(int index);
+ void DecreaseEventPriority(int index);
+
+ static const char* RoleName(int role);
+ static int TypeFromName(const char* n);
+
+ Text ErrorMessage() const { return errmsg; }
+ void AddError(Text err);
+
+ Text Serialize(const char* player_elem=0, int player_index=0);
+
+protected:
+ MissionElement* ParseElement(TermStruct* val);
+ MissionEvent* ParseEvent(TermStruct* val);
+ MissionShip* ParseShip(TermStruct* val, MissionElement* element);
+ Instruction* ParseInstruction(TermStruct* val, MissionElement* element);
+ void ParseLoadout(TermStruct* val, MissionElement* element);
+ RLoc* ParseRLoc(TermStruct* val);
+
+ int id;
+ char filename[64];
+ char path[64];
+ Text region;
+ Text name;
+ Text desc;
+ int type;
+ int team;
+ int start;
+ double stardate;
+ bool ok;
+ bool active;
+ bool complete;
+ bool degrees;
+ Text objective;
+ Text sitrep;
+ Text errmsg;
+ Text subtitles;
+ StarSystem* star_system;
+ List<StarSystem> system_list;
+
+ List<MissionElement> elements;
+ List<MissionEvent> events;
+
+ MissionElement* target;
+ MissionElement* ward;
+ MissionElement* current;
+};
+
+// +--------------------------------------------------------------------+
+
+class MissionElement
+{
+ friend class Mission;
+
+public:
+ static const char* TYPENAME() { return "MissionElement"; }
+
+ MissionElement();
+ ~MissionElement();
+
+ int operator == (const MissionElement& r) const { return id == r.id; }
+
+ int Identity() const { return id; }
+ const Text& Name() const { return name; }
+ Text Abbreviation() const;
+ const Text& Carrier() const { return carrier; }
+ const Text& Commander() const { return commander; }
+ const Text& Squadron() const { return squadron; }
+ const Text& Path() const { return path; }
+ int ElementID() const { return elem_id; }
+ const ShipDesign* GetDesign() const { return design; }
+ const Skin* GetSkin() const { return skin; }
+ int Count() const { return count; }
+ int MaintCount() const { return maint_count; }
+ int DeadCount() const { return dead_count; }
+ int GetIFF() const { return IFF_code; }
+ int IntelLevel() const { return intel; }
+ int MissionRole() const { return mission_role; }
+ int Player() const { return player; }
+ Text RoleName() const;
+ Color MarkerColor() const;
+ bool IsStarship() const;
+ bool IsDropship() const;
+ bool IsStatic() const;
+ bool IsGroundUnit() const;
+ bool IsSquadron() const;
+ bool IsCarrier() const;
+ bool IsAlert() const { return alert; }
+ bool IsPlayable() const { return playable; }
+ bool IsRogue() const { return rogue; }
+ bool IsInvulnerable() const { return invulnerable; }
+ int RespawnCount() const { return respawns; }
+ int HoldTime() const { return hold_time; }
+ int CommandAI() const { return command_ai; }
+ int ZoneLock() const { return zone_lock; }
+
+ const Text& Region() const { return rgn_name; }
+ Point Location() const;
+ RLoc& GetRLoc() { return rloc; }
+ double Heading() const { return heading; }
+
+ Text GetShipName(int n) const;
+ Text GetRegistry(int n) const;
+
+ List<Instruction>& Objectives() { return objectives; }
+ List<Text>& Instructions() { return instructions; }
+ List<Instruction>& NavList() { return navlist; }
+ List<MissionLoad>& Loadouts() { return loadouts; }
+ List<MissionShip>& Ships() { return ships; }
+
+ void SetName(const char* n) { name = n; }
+ void SetCarrier(const char* c) { carrier = c; }
+ void SetCommander(const char* c) { commander = c; }
+ void SetSquadron(const char* s) { squadron = s; }
+ void SetPath(const char* p) { path = p; }
+ void SetElementID(int id) { elem_id = id; }
+ void SetDesign(const ShipDesign* d){ design = d; }
+ void SetSkin(const Skin* s) { skin = s; }
+ void SetCount(int n) { count = n; }
+ void SetMaintCount(int n) { maint_count = n; }
+ void SetDeadCount(int n) { dead_count = n; }
+ void SetIFF(int iff) { IFF_code = iff; }
+ void SetIntelLevel(int i) { intel = i; }
+ void SetMissionRole(int r) { mission_role = r; }
+ void SetPlayer(int p) { player = p; }
+ void SetPlayable(bool p) { playable = p; }
+ void SetRogue(bool r) { rogue = r; }
+ void SetInvulnerable(bool n) { invulnerable = n; }
+ void SetAlert(bool a) { alert = a; }
+ void SetCommandAI(int a) { command_ai = a; }
+ void SetRegion(const char* rgn) { rgn_name = rgn; }
+ void SetLocation(const Point& p);
+ void SetRLoc(const RLoc& r);
+ void SetHeading(double h) { heading = h; }
+ void SetRespawnCount(int r) { respawns = r; }
+ void SetHoldTime(int t) { hold_time = t; }
+ void SetZoneLock(int z) { zone_lock = z; }
+
+ void AddNavPoint(Instruction* pt, Instruction* afterPoint=0);
+ void DelNavPoint(Instruction* pt);
+ void ClearFlightPlan();
+ int GetNavIndex(const Instruction* n);
+
+ void AddObjective(Instruction* obj) { objectives.append(obj); }
+ void AddInstruction(const char* i) { instructions.append(new(__FILE__,__LINE__) Text(i)); }
+
+ CombatGroup* GetCombatGroup() { return combat_group; }
+ void SetCombatGroup(CombatGroup* g) { combat_group = g; }
+ CombatUnit* GetCombatUnit() { return combat_unit; }
+ void SetCombatUnit(CombatUnit* u) { combat_unit = u; }
+
+protected:
+ int id;
+ Text name;
+ Text carrier;
+ Text commander;
+ Text squadron;
+ Text path;
+ int elem_id;
+ const ShipDesign* design;
+ const Skin* skin;
+ int count;
+ int maint_count;
+ int dead_count;
+ int IFF_code;
+ int mission_role;
+ int intel;
+ int respawns;
+ int hold_time;
+ int zone_lock;
+ int player;
+ int command_ai;
+ bool alert;
+ bool playable;
+ bool rogue;
+ bool invulnerable;
+
+ Text rgn_name;
+ RLoc rloc;
+ double heading;
+
+ CombatGroup* combat_group;
+ CombatUnit* combat_unit;
+
+ List<Instruction> objectives;
+ List<Text> instructions;
+ List<Instruction> navlist;
+ List<MissionLoad> loadouts;
+ List<MissionShip> ships;
+};
+
+// +--------------------------------------------------------------------+
+
+class MissionLoad
+{
+ friend class Mission;
+
+public:
+ static const char* TYPENAME() { return "MissionLoad"; }
+
+ MissionLoad(int ship=-1, const char* name=0);
+ ~MissionLoad();
+
+ int GetShip() const;
+ void SetShip(int ship);
+
+ Text GetName() const;
+ void SetName(Text name);
+
+ int* GetStations();
+ int GetStation(int index);
+ void SetStation(int index, int selection);
+
+protected:
+ int ship;
+ Text name;
+ int load[16];
+};
+
+// +--------------------------------------------------------------------+
+
+class MissionShip
+{
+ friend class Mission;
+
+public:
+ static const char* TYPENAME() { return "MissionShip"; }
+
+ MissionShip();
+ ~MissionShip() { }
+
+ const Text& Name() const { return name; }
+ const Text& RegNum() const { return regnum; }
+ const Text& Region() const { return region; }
+ const Skin* GetSkin() const { return skin; }
+ const Point& Location() const { return loc; }
+ const Point& Velocity() const { return velocity; }
+ int Respawns() const { return respawns; }
+ double Heading() const { return heading; }
+ double Integrity() const { return integrity; }
+ int Decoys() const { return decoys; }
+ int Probes() const { return probes; }
+ const int* Ammo() const { return ammo; }
+ const int* Fuel() const { return fuel; }
+
+ void SetName(const char* n) { name = n; }
+ void SetRegNum(const char* n) { regnum = n; }
+ void SetRegion(const char* n) { region = n; }
+ void SetSkin(const Skin* s) { skin = s; }
+ void SetLocation(const Point& p) { loc = p; }
+ void SetVelocity(const Point& p) { velocity = p; }
+ void SetRespawns(int r) { respawns = r; }
+ void SetHeading(double h) { heading = h; }
+ void SetIntegrity(double n) { integrity = n; }
+ void SetDecoys(int d) { decoys = d; }
+ void SetProbes(int p) { probes = p; }
+ void SetAmmo(const int* a);
+ void SetFuel(const int* f);
+
+protected:
+ Text name;
+ Text regnum;
+ Text region;
+ const Skin* skin;
+ Point loc;
+ Point velocity;
+ int respawns;
+ double heading;
+ double integrity;
+ int decoys;
+ int probes;
+ int ammo[16];
+ int fuel[4];
+};
+
+#endif Mission_h
+
diff --git a/Stars45/MissionEvent.cpp b/Stars45/MissionEvent.cpp
new file mode 100644
index 0000000..3eebf30
--- /dev/null
+++ b/Stars45/MissionEvent.cpp
@@ -0,0 +1,874 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MissionEvent.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Events for mission scripting
+*/
+
+#include "MemDebug.h"
+#include "MissionEvent.h"
+#include "Mission.h"
+#include "StarSystem.h"
+#include "Galaxy.h"
+#include "Starshatter.h"
+#include "StarServer.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Element.h"
+#include "DisplayView.h"
+#include "HUDView.h"
+#include "Instruction.h"
+#include "QuantumDrive.h"
+#include "Sim.h"
+#include "AudioConfig.h"
+#include "CameraDirector.h"
+#include "RadioMessage.h"
+#include "RadioTraffic.h"
+#include "Weapon.h"
+#include "WeaponGroup.h"
+#include "Player.h"
+#include "Campaign.h"
+#include "CombatGroup.h"
+
+#include "NetData.h"
+#include "NetUtil.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Font.h"
+#include "FontMgr.h"
+#include "Sound.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+#include "Random.h"
+
+const char* FormatGameTime();
+
+// +--------------------------------------------------------------------+
+
+MissionEvent::MissionEvent()
+ : id(0), status(PENDING), time(0), delay(0), event(0), event_nparams(0),
+ event_chance(100), trigger(0), trigger_nparams(0), sound(0)
+{
+ ZeroMemory(event_param, sizeof(event_param));
+ ZeroMemory(trigger_param, sizeof(trigger_param));
+}
+
+MissionEvent::~MissionEvent()
+{
+ if (sound) {
+ sound->Stop();
+ sound->Release();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MissionEvent::ExecFrame(double seconds)
+{
+ Sim* sim = Sim::GetSim();
+
+ if (!sim) {
+ status = PENDING;
+ return;
+ }
+
+ if (status == PENDING)
+ CheckTrigger();
+
+ if (status == ACTIVE) {
+ if (delay > 0)
+ delay -= seconds;
+
+ else
+ Execute();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MissionEvent::Activate()
+{
+ if (status == PENDING) {
+ if (event_chance > 0 && event_chance < 100) {
+ if (Random(0, 100) < event_chance)
+ status = ACTIVE;
+ else
+ status = SKIPPED;
+ }
+
+ else {
+ status = ACTIVE;
+ }
+
+ if (status == SKIPPED) {
+ Sim::GetSim()->ProcessEventTrigger(TRIGGER_SKIPPED, id);
+ }
+ }
+}
+
+void
+MissionEvent::Skip()
+{
+ if (status == PENDING) {
+ status = SKIPPED;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+MissionEvent::CheckTrigger()
+{
+ Sim* sim = Sim::GetSim();
+
+ if (time > 0 && time > sim->MissionClock())
+ return false;
+
+ switch (trigger) {
+ case TRIGGER_TIME: {
+ if (time <= sim->MissionClock())
+ Activate();
+ }
+ break;
+
+ case TRIGGER_DAMAGE: {
+ Ship* ship = sim->FindShip(trigger_ship);
+ if (ship) {
+ double damage = 100.0 * (ship->Design()->integrity - ship->Integrity()) /
+ (ship->Design()->integrity);
+
+ if (damage >= trigger_param[0])
+ Activate();
+ }
+ }
+ break;
+
+ case TRIGGER_DETECT: {
+ Ship* ship = sim->FindShip(trigger_ship);
+ Ship* tgt = sim->FindShip(trigger_target);
+
+ if (ship && tgt) {
+ if (ship->FindContact(tgt))
+ Activate();
+ }
+ else {
+ Skip();
+ }
+ }
+ break;
+
+ case TRIGGER_RANGE: {
+ Ship* ship = sim->FindShip(trigger_ship);
+ Ship* tgt = sim->FindShip(trigger_target);
+
+ if (ship && tgt) {
+ double range = (ship->Location() - tgt->Location()).length();
+ double min_range = 0;
+ double max_range = 1e12;
+
+ if (trigger_param[0] > 0)
+ min_range = trigger_param[0];
+ else
+ max_range = -trigger_param[0];
+
+ if (range < min_range || range > max_range)
+ Activate();
+ }
+ else {
+ Skip();
+ }
+ }
+ break;
+
+ case TRIGGER_SHIPS_LEFT: {
+ int alive = 0;
+ int count = 0;
+ int iff = -1;
+ int nparams = NumTriggerParams();
+
+ if (nparams > 0) count = TriggerParam(0);
+ if (nparams > 1) iff = TriggerParam(1);
+
+ ListIter<SimRegion> iter = sim->GetRegions();
+ while (++iter) {
+ SimRegion* rgn = iter.value();
+
+ ListIter<Ship> s_iter = rgn->Ships();
+ while (++s_iter) {
+ Ship* ship = s_iter.value();
+
+ if (ship->Type() >= Ship::STATION)
+ continue;
+
+ if (ship->Life() == 0 && ship->RespawnCount() < 1)
+ continue;
+
+ if (iff < 0 || ship->GetIFF() == iff)
+ alive++;
+ }
+ }
+
+ if (alive <= count)
+ Activate();
+ }
+ break;
+
+ case TRIGGER_EVENT_ALL: {
+ bool all = true;
+ int nparams = NumTriggerParams();
+ for (int i = 0; all && i < nparams; i++) {
+ int trigger_id = TriggerParam(i);
+
+ ListIter<MissionEvent> iter = sim->GetEvents();
+ while (++iter) {
+ MissionEvent* e = iter.value();
+ if (e->EventID() == trigger_id) {
+ if (e->Status() != COMPLETE)
+ all = false;
+ break;
+ }
+
+ else if (e->EventID() == -trigger_id) {
+ if (e->Status() == COMPLETE)
+ all = false;
+ break;
+ }
+ }
+ }
+
+ if (all)
+ Activate();
+ }
+ break;
+
+ case TRIGGER_EVENT_ANY: {
+ bool any = false;
+ int nparams = NumTriggerParams();
+ for (int i = 0; !any && i < nparams; i++) {
+ int trigger_id = TriggerParam(i);
+
+ ListIter<MissionEvent> iter = sim->GetEvents();
+ while (++iter) {
+ MissionEvent* e = iter.value();
+ if (e->EventID() == trigger_id) {
+ if (e->Status() == COMPLETE)
+ any = true;
+ break;
+ }
+ }
+ }
+
+ if (any)
+ Activate();
+ }
+ break;
+ }
+
+ return status == ACTIVE;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MissionEvent::Execute(bool silent)
+{
+ Starshatter* stars = Starshatter::GetInstance();
+ HUDView* hud = HUDView::GetInstance();
+ Sim* sim = Sim::GetSim();
+ Ship* player = sim->GetPlayerShip();
+ Ship* ship = 0;
+ Ship* src = 0;
+ Ship* tgt = 0;
+ Element* elem = 0;
+ int pan = 0;
+ bool end_mission = false;
+
+ if (event_ship.length())
+ ship = sim->FindShip(event_ship);
+ else
+ ship = player;
+
+ if (event_source.length())
+ src = sim->FindShip(event_source);
+
+ if (event_target.length())
+ tgt = sim->FindShip(event_target);
+
+ if (ship)
+ elem = ship->GetElement();
+
+ else if (event_ship.length()) {
+ elem = sim->FindElement(event_ship);
+
+ if (elem)
+ ship = elem->GetShip(1);
+ }
+
+ // expire the delay, if any remains
+ delay = 0;
+
+ // fire the event action
+ switch (event) {
+ case MESSAGE:
+ if (event_message.length() > 0) {
+ if (ship) {
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(ship, src, event_param[0]);
+ msg->SetInfo(event_message);
+ msg->SetChannel(ship->GetIFF());
+ if (tgt)
+ msg->AddTarget(tgt);
+ RadioTraffic::Transmit(msg);
+ }
+
+ else if (elem) {
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(elem, src, event_param[0]);
+ msg->SetInfo(event_message);
+ msg->SetChannel(elem->GetIFF());
+ if (tgt)
+ msg->AddTarget(tgt);
+ RadioTraffic::Transmit(msg);
+ }
+ }
+
+ if (event_sound.length() > 0) {
+ pan = event_param[0];
+ }
+ break;
+
+ case OBJECTIVE:
+ if (elem) {
+ if (event_param[0]) {
+ elem->ClearInstructions();
+ elem->ClearObjectives();
+ }
+
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(event_param[0], 0);
+ obj->SetTarget(event_target);
+ elem->AddObjective(obj);
+
+ if (elem->Contains(player)) {
+ HUDView* hud = HUDView::GetInstance();
+
+ if (hud)
+ hud->ShowHUDInst();
+ }
+ }
+ break;
+
+ case INSTRUCTION:
+ if (elem) {
+ if (event_param[0])
+ elem->ClearInstructions();
+
+ elem->AddInstruction(event_message);
+
+ if (elem->Contains(player) && event_message.length() > 0) {
+ HUDView* hud = HUDView::GetInstance();
+
+ if (hud)
+ hud->ShowHUDInst();
+ }
+ }
+ break;
+
+ case IFF:
+ if (elem) {
+ elem->SetIFF(event_param[0]);
+ }
+
+ else if (ship) {
+ ship->SetIFF(event_param[0]);
+ }
+ break;
+
+ case DAMAGE:
+ if (ship) {
+ ship->InflictDamage(event_param[0]);
+
+ if (ship->Integrity() < 1) {
+ NetUtil::SendObjKill(ship, 0, NetObjKill::KILL_MISC);
+ ship->DeathSpiral();
+ Print(" %s Killed By Scripted Event %d (%s)\n", (const char*) ship->Name(), id, FormatGameTime());
+ }
+ }
+ else {
+ Print(" EVENT %d: Could not apply damage to ship '%s' (not found).\n", id, (const char*) event_ship);
+ }
+ break;
+
+ case JUMP:
+ if (ship) {
+ SimRegion* rgn = sim->FindRegion(event_target);
+
+ if (rgn && ship->GetRegion() != rgn) {
+ if (rgn->IsOrbital()) {
+ QuantumDrive* quantum_drive = ship->GetQuantumDrive();
+ if (quantum_drive) {
+ quantum_drive->SetDestination(rgn, Point(0,0,0));
+ quantum_drive->Engage(true); // request immediate jump
+ }
+
+ else if (ship->IsAirborne()) {
+ ship->MakeOrbit();
+ }
+ }
+
+ else {
+ ship->DropOrbit();
+ }
+ }
+
+ }
+ break;
+
+ case HOLD:
+ if (elem)
+ elem->SetHoldTime(event_param[0]);
+ break;
+
+ case SKIP: {
+ for (int i = 0; i < event_nparams; i++) {
+ int skip_id = event_param[i];
+
+ ListIter<MissionEvent> iter = sim->GetEvents();
+ while (++iter) {
+ MissionEvent* e = iter.value();
+ if (e->EventID() == skip_id) {
+ if (e->status != COMPLETE)
+ e->status = SKIPPED;
+ }
+ }
+ }
+ }
+ break;
+
+ case END_MISSION:
+ Print(" END MISSION By Scripted Event %d (%s)\n", id, FormatGameTime());
+ end_mission = true;
+ break;
+
+ //
+ // NOTE: CUTSCENE EVENTS DO NOT APPLY IN MULTIPLAYER
+ //
+ case BEGIN_SCENE:
+ Print(" ------------------------------------\n");
+ Print(" Begin Cutscene '%s'\n", event_message.data());
+ stars->BeginCutscene();
+ break;
+
+ case END_SCENE:
+ Print(" End Cutscene '%s'\n", event_message.data());
+ Print(" ------------------------------------\n");
+ stars->EndCutscene();
+ break;
+
+ case CAMERA:
+ if (stars->InCutscene()) {
+ CameraDirector* cam_dir = CameraDirector::GetInstance();
+
+ if (!cam_dir->GetShip())
+ cam_dir->SetShip(player);
+
+ switch (event_param[0]) {
+ case 1:
+ if (cam_dir->GetMode() != CameraDirector::MODE_COCKPIT)
+ cam_dir->SetMode(CameraDirector::MODE_COCKPIT, event_rect.x);
+ break;
+
+ case 2:
+ if (cam_dir->GetMode() != CameraDirector::MODE_CHASE)
+ cam_dir->SetMode(CameraDirector::MODE_CHASE, event_rect.x);
+ break;
+
+ case 3:
+ if (cam_dir->GetMode() != CameraDirector::MODE_ORBIT)
+ cam_dir->SetMode(CameraDirector::MODE_ORBIT, event_rect.x);
+ break;
+
+ case 4:
+ if (cam_dir->GetMode() != CameraDirector::MODE_TARGET)
+ cam_dir->SetMode(CameraDirector::MODE_TARGET, event_rect.x);
+ break;
+ }
+
+ if (event_target.length()) {
+ ::Print("Mission Event %d: setting camera target to %s\n", id, (const char*) event_target);
+ Ship* s_tgt = 0;
+
+ if (event_target.indexOf("body:") < 0)
+ s_tgt = sim->FindShip(event_target);
+
+ if (s_tgt) {
+ ::Print(" found ship %s\n", s_tgt->Name());
+ cam_dir->SetViewOrbital(0);
+
+ if (cam_dir->GetViewObject() != s_tgt) {
+
+ if (event_param[0] == 6) {
+ s_tgt->DropCam(event_param[1], event_param[2]);
+ cam_dir->SetShip(s_tgt);
+ cam_dir->SetMode(CameraDirector::MODE_DROP, 0);
+ }
+ else {
+ Ship* cam_ship = cam_dir->GetShip();
+
+ if (cam_ship && cam_ship->IsDropCam()) {
+ cam_ship->CompleteTransition();
+ }
+
+ if (cam_dir->GetShip() != sim->GetPlayerShip())
+ cam_dir->SetShip(sim->GetPlayerShip());
+ cam_dir->SetViewObject(s_tgt, true); // immediate, no transition
+ }
+ }
+ }
+
+ else {
+ const char* body_name = event_target.data();
+
+ if (!strncmp(body_name, "body:", 5))
+ body_name += 5;
+
+ Orbital* orb = sim->FindOrbitalBody(body_name);
+
+ if (orb) {
+ ::Print(" found body %s\n", orb->Name());
+ cam_dir->SetViewOrbital(orb);
+ }
+ }
+ }
+
+ if (event_param[0] == 3) {
+ cam_dir->SetOrbitPoint(event_point.x, event_point.y, event_point.z);
+ }
+
+ else if (event_param[0] == 5) {
+ cam_dir->SetOrbitRates(event_point.x, event_point.y, event_point.z);
+ }
+ }
+ break;
+
+ case VOLUME:
+ if (stars->InCutscene()) {
+ AudioConfig* audio_cfg = AudioConfig::GetInstance();
+
+ audio_cfg->SetEfxVolume(event_param[0]);
+ audio_cfg->SetWrnVolume(event_param[0]);
+ }
+ break;
+
+ case DISPLAY:
+ if (stars->InCutscene()) {
+ DisplayView* disp_view = DisplayView::GetInstance();
+
+ if (disp_view) {
+ Color color;
+ color.Set(event_param[0]);
+
+ if (event_message.length() && event_source.length()) {
+
+ if (event_message.contains('$')) {
+ Campaign* campaign = Campaign::GetCampaign();
+ Player* user = Player::GetCurrentPlayer();
+ CombatGroup* group = campaign->GetPlayerGroup();
+
+ if (user) {
+ event_message = FormatTextReplace(event_message, "$NAME", user->Name().data());
+ event_message = FormatTextReplace(event_message, "$RANK", Player::RankName(user->Rank()));
+ }
+
+ if (group) {
+ event_message = FormatTextReplace(event_message, "$GROUP", group->GetDescription());
+ }
+
+ if (event_message.contains("$TIME")) {
+ char timestr[32];
+ FormatDayTime(timestr, campaign->GetTime(), true);
+ event_message = FormatTextReplace(event_message, "$TIME", timestr);
+ }
+ }
+
+ disp_view->AddText( event_message,
+ FontMgr::Find(event_source),
+ color,
+ event_rect,
+ event_point.y,
+ event_point.x,
+ event_point.z);
+
+ }
+
+ else if (event_target.length()) {
+ DataLoader* loader = DataLoader::GetLoader();
+
+ if (loader) {
+ loader->SetDataPath(0);
+ loader->LoadBitmap(event_target, image, 0, true);
+ }
+
+ if (image.Width() && image.Height())
+ disp_view->AddImage( &image,
+ color,
+ Video::BLEND_ALPHA,
+ event_rect,
+ event_point.y,
+ event_point.x,
+ event_point.z);
+ }
+ }
+ }
+ break;
+
+ case FIRE_WEAPON:
+ if (ship) {
+ // fire single weapon:
+ if (event_param[0] >= 0) {
+ ship->FireWeapon(event_param[0]);
+ }
+
+ // fire all weapons:
+ else {
+ ListIter<WeaponGroup> g_iter = ship->Weapons();
+ while (++g_iter) {
+ ListIter<Weapon> w_iter = g_iter->GetWeapons();
+ while (++w_iter) {
+ Weapon* w = w_iter.value();
+ w->Fire();
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ sim->ProcessEventTrigger(TRIGGER_EVENT, id);
+
+ if (!silent && !sound && event_sound.length()) {
+ DataLoader* loader = DataLoader::GetLoader();
+ bool use_fs = loader->IsFileSystemEnabled();
+ DWORD flags = pan ? Sound::LOCKED|Sound::LOCALIZED :
+ Sound::LOCKED|Sound::AMBIENT;
+
+ loader->UseFileSystem(true);
+ loader->SetDataPath("Sounds/");
+ loader->LoadSound(event_sound, sound, flags);
+ loader->SetDataPath(0);
+
+ if (!sound) {
+ loader->SetDataPath("Mods/Sounds/");
+ loader->LoadSound(event_sound, sound, flags);
+ loader->SetDataPath(0);
+ }
+
+ if (!sound) {
+ loader->LoadSound(event_sound, sound, flags);
+ }
+
+ loader->UseFileSystem(use_fs);
+
+ // fire and forget:
+ if (sound) {
+ if (sound->GetFlags() & Sound::STREAMED) {
+ sound->SetFlags(flags | sound->GetFlags());
+ sound->SetVolume(AudioConfig::VoxVolume());
+ sound->Play();
+ }
+ else {
+ sound->SetFlags(flags);
+ sound->SetVolume(AudioConfig::VoxVolume());
+ sound->SetPan(pan);
+ sound->SetFilename(event_sound);
+ sound->AddToSoundCard();
+ sound->Play();
+ }
+ }
+ }
+
+ status = COMPLETE;
+
+ if (end_mission) {
+ StarServer* server = StarServer::GetInstance();
+
+ if (stars) {
+ stars->EndMission();
+ }
+
+ else if (server) {
+ // end mission event uses event_target member
+ // to forward server to next mission in the chain:
+ if (event_target.length())
+ server->SetNextMission(event_target);
+
+ server->SetGameMode(StarServer::MENU_MODE);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+MissionEvent::TriggerParamStr() const
+{
+
+ Text result;
+ char buffer[8];
+
+ if (trigger_param[0] == 0) {
+ // nothing
+ }
+
+ else if (trigger_param[1] == 0) {
+ sprintf(buffer, "%d", trigger_param[0]);
+ result = buffer;
+ }
+
+ else {
+ result = "(";
+
+ for (int i = 0; i < 8; i++) {
+ if (trigger_param[i] == 0)
+ break;
+
+ if (i < 7 && trigger_param[i+1] != 0)
+ sprintf(buffer, "%d, ", trigger_param[i]);
+ else
+ sprintf(buffer, "%d", trigger_param[i]);
+
+ result += buffer;
+ }
+
+ result += ")";
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MissionEvent::EventParam(int index) const
+{
+ if (index >= 0 && index < NumEventParams())
+ return event_param[index];
+
+ return 0;
+}
+
+int
+MissionEvent::NumEventParams() const
+{
+ return event_nparams;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MissionEvent::TriggerParam(int index) const
+{
+ if (index >= 0 && index < NumTriggerParams())
+ return trigger_param[index];
+
+ return 0;
+}
+
+int
+MissionEvent::NumTriggerParams() const
+{
+ return trigger_nparams;
+}
+
+// +--------------------------------------------------------------------+
+
+static const char* event_names[] = {
+ "Message",
+ "Objective",
+ "Instruction",
+ "IFF",
+ "Damage",
+ "Jump",
+ "Hold",
+ "Skip",
+ "Exit",
+
+ "BeginScene",
+ "Camera",
+ "Volume",
+ "Display",
+ "Fire",
+ "EndScene"
+};
+
+static const char* trigger_names[] = {
+ "Time",
+ "Damage",
+ "Destroyed",
+ "Jump",
+ "Launch",
+ "Dock",
+ "Navpoint",
+ "Event",
+ "Skipped",
+ "Target",
+ "Ships Left",
+ "Detect",
+ "Range",
+ "Event (ALL)",
+ "Event (ANY)"
+};
+
+const char*
+MissionEvent::EventName() const
+{
+ return event_names[event];
+}
+
+const char*
+MissionEvent::EventName(int n)
+{
+ return event_names[n];
+}
+
+int
+MissionEvent::EventForName(const char* n)
+{
+ for (int i = 0; i < NUM_EVENTS; i++)
+ if (!stricmp(n, event_names[i]))
+ return i;
+
+ return 0;
+}
+
+const char*
+MissionEvent::TriggerName() const
+{
+ return trigger_names[trigger];
+}
+
+const char*
+MissionEvent::TriggerName(int n)
+{
+ return trigger_names[n];
+}
+
+int
+MissionEvent::TriggerForName(const char* n)
+{
+ for (int i = 0; i < NUM_TRIGGERS; i++)
+ if (!stricmp(n, trigger_names[i]))
+ return i;
+
+ return 0;
+} \ No newline at end of file
diff --git a/Stars45/MissionEvent.h b/Stars45/MissionEvent.h
new file mode 100644
index 0000000..c4025d1
--- /dev/null
+++ b/Stars45/MissionEvent.h
@@ -0,0 +1,167 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MissionEvent.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Events for mission scripting
+*/
+
+#ifndef MissionEvent_h
+#define MissionEvent_h
+
+#include "Types.h"
+#include "List.h"
+#include "Text.h"
+#include "Geometry.h"
+#include "Bitmap.h"
+
+// +--------------------------------------------------------------------+
+
+class Mission;
+class MissionElement;
+class MissionLoad;
+class MissionEvent;
+
+class Ship;
+class System;
+class Element;
+class ShipDesign;
+class WeaponDesign;
+class StarSystem;
+class Instruction;
+class Sound;
+
+// +--------------------------------------------------------------------+
+
+class MissionEvent
+{
+ friend class Mission;
+ friend class MissionTemplate;
+ friend class MsnEditDlg;
+ friend class MsnEventDlg;
+
+public:
+ static const char* TYPENAME() { return "MissionEvent"; }
+
+ enum EVENT_TYPE {
+ MESSAGE,
+ OBJECTIVE,
+ INSTRUCTION,
+ IFF,
+ DAMAGE,
+ JUMP,
+ HOLD,
+ SKIP,
+ END_MISSION,
+
+ BEGIN_SCENE,
+ CAMERA,
+ VOLUME,
+ DISPLAY,
+ FIRE_WEAPON,
+ END_SCENE,
+
+ NUM_EVENTS
+ };
+
+ enum EVENT_STATUS {
+ PENDING, ACTIVE, COMPLETE, SKIPPED
+ };
+
+ enum EVENT_TRIGGER {
+ TRIGGER_TIME, TRIGGER_DAMAGE, TRIGGER_DESTROYED,
+ TRIGGER_JUMP, TRIGGER_LAUNCH, TRIGGER_DOCK,
+ TRIGGER_NAVPT, TRIGGER_EVENT, TRIGGER_SKIPPED,
+ TRIGGER_TARGET, TRIGGER_SHIPS_LEFT, TRIGGER_DETECT,
+ TRIGGER_RANGE, TRIGGER_EVENT_ALL, TRIGGER_EVENT_ANY,
+ NUM_TRIGGERS
+ };
+
+ MissionEvent();
+ ~MissionEvent();
+
+ // operations:
+ void ExecFrame(double seconds);
+ void Activate();
+
+ virtual bool CheckTrigger();
+ virtual void Execute(bool silent=false);
+ virtual void Skip();
+
+ // accessors:
+ int EventID() const { return id; }
+ int Status() const { return status; }
+ bool IsPending() const { return status == PENDING; }
+ bool IsActive() const { return status == ACTIVE; }
+ bool IsComplete() const { return status == COMPLETE; }
+ bool IsSkipped() const { return status == SKIPPED; }
+
+ double Time() const { return time; }
+ double Delay() const { return delay; }
+
+ int Event() const { return event; }
+ const char* EventName() const;
+ Text EventShip() const { return event_ship; }
+ Text EventSource() const { return event_source; }
+ Text EventTarget() const { return event_target; }
+ Text EventMessage() const { return event_message; }
+ Text EventSound() const { return event_sound; }
+
+ int EventParam(int index=0) const;
+ int NumEventParams() const;
+
+ int EventChance() const { return event_chance; }
+ Point EventPoint() const { return event_point; }
+ Rect EventRect() const { return event_rect; }
+
+ int Trigger() const { return trigger; }
+ const char* TriggerName() const;
+ Text TriggerShip() const { return trigger_ship; }
+ Text TriggerTarget() const { return trigger_target; }
+
+ Text TriggerParamStr() const;
+ int TriggerParam(int index=0) const;
+ int NumTriggerParams() const;
+
+ static const char* EventName(int n);
+ static int EventForName(const char* n);
+ static const char* TriggerName(int n);
+ static int TriggerForName(const char* n);
+
+protected:
+ int id;
+ int status;
+ double time;
+ double delay;
+
+ int event;
+ Text event_ship;
+ Text event_source;
+ Text event_target;
+ Text event_message;
+ Text event_sound;
+ int event_param[10];
+ int event_nparams;
+ int event_chance;
+ Vec3 event_point;
+ Rect event_rect;
+
+ int trigger;
+ Text trigger_ship;
+ Text trigger_target;
+ int trigger_param[10];
+ int trigger_nparams;
+
+ Bitmap image;
+ Sound* sound;
+};
+
+
+#endif MissionEvent_h
+
diff --git a/Stars45/MissionTemplate.cpp b/Stars45/MissionTemplate.cpp
new file mode 100644
index 0000000..dbb6ca9
--- /dev/null
+++ b/Stars45/MissionTemplate.cpp
@@ -0,0 +1,846 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Mission.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Template classes
+*/
+
+#include "MemDebug.h"
+#include "MissionTemplate.h"
+#include "MissionEvent.h"
+#include "StarSystem.h"
+#include "Galaxy.h"
+#include "Callsign.h"
+#include "Campaign.h"
+#include "Combatant.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "Starshatter.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Element.h"
+#include "Instruction.h"
+#include "Random.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "ParseUtil.h"
+
+// +--------------------------------------------------------------------+
+
+MissionTemplate::MissionTemplate(int identity, const char* fname, const char* pname)
+ : Mission(identity, fname, pname)
+{
+}
+
+MissionTemplate::~MissionTemplate()
+{
+ callsigns.destroy();
+ aliases.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MissionTemplate::AddElement(MissionElement* elem)
+{
+ if (elem) {
+ elements.append(elem);
+ aliases.append(new(__FILE__,__LINE__) MissionAlias(elem->Name(), elem));
+ }
+}
+
+bool
+MissionTemplate::MapElement(MissionElement* elem)
+{
+ bool result = false;
+
+ if (elem && !elem->GetCombatUnit()) {
+ if (elem->IsDropship()) {
+ Text callsign = MapCallsign(elem->Name(), elem->GetIFF());
+
+ if (callsign.length())
+ elem->SetName(callsign);
+ }
+
+ ListIter<Instruction> obj = elem->Objectives();
+ while (++obj) {
+ Instruction* i = obj.value();
+ if (strlen(i->TargetName())) {
+ // find a callsign, only if one already exists:
+ Text callsign = MapCallsign(i->TargetName(), -1);
+ if (callsign.length())
+ i->SetTarget(callsign.data());
+ }
+ }
+
+ ListIter<Instruction> nav = elem->NavList();
+ while (++nav) {
+ Instruction* i = nav.value();
+ if (strlen(i->TargetName())) {
+ // find a callsign, only if one already exists:
+ Text callsign = MapCallsign(i->TargetName(), -1);
+ if (callsign.length())
+ i->SetTarget(callsign.data());
+ }
+ }
+
+ CombatGroup* g = FindCombatGroup(elem->GetIFF(), elem->GetDesign());
+
+ if (g) {
+ CombatUnit* u = g->GetNextUnit();
+
+ if (u) {
+ elem->SetCombatGroup(g);
+ elem->SetCombatUnit(u);
+ }
+ }
+
+ if (elem->GetCombatUnit()) {
+ MissionElement* cmdr = FindElement(elem->Commander());
+ if (cmdr)
+ elem->SetCommander(cmdr->Name());
+
+ MissionElement* sqdr = FindElement(elem->Squadron());
+ if (sqdr)
+ elem->SetSquadron(sqdr->Name());
+
+ if (!elem->IsDropship()) {
+ aliases.append(new(__FILE__,__LINE__) MissionAlias(elem->Name(), elem));
+ elem->SetName(elem->GetCombatUnit()->Name());
+ }
+
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+Text
+MissionTemplate::MapShip(Text name)
+{
+ Text result = name;
+ int len = name.length();
+
+ if (len) {
+ MissionElement* elem = 0;
+
+ // single ship from an element (e.g. "Alpha 1")?
+ if (isdigit(name[len-1]) && isspace(name[len-2])) {
+ Text elem_name = name.substring(0, len-2);
+
+ elem = FindElement(elem_name);
+ if (elem)
+ result = elem->Name() + name.substring(len-2, 2);
+ }
+
+ // full element name
+ // (also used to try again if single-ship search fails)
+ if (!elem) {
+ elem = FindElement(name);
+
+ if (elem)
+ result = elem->Name();
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+MissionTemplate::MapEvent(MissionEvent* event)
+{
+ bool result = false;
+
+ if (event) {
+ event->event_ship = MapShip(event->event_ship);
+ event->event_source = MapShip(event->event_source);
+ event->event_target = MapShip(event->event_target);
+ event->trigger_ship = MapShip(event->trigger_ship);
+ event->trigger_target = MapShip(event->trigger_target);
+
+ result = true;
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+MissionTemplate::MapCallsign(const char* name, int iff)
+{
+ for (int i = 0; i < callsigns.size(); i++) {
+ if (callsigns[i]->Name() == name)
+ return callsigns[i]->Callsign();
+ }
+
+ if (iff >= 0) {
+ const char* callsign = Callsign::GetCallsign(iff);
+ MissionCallsign* mc = new(__FILE__,__LINE__) MissionCallsign(callsign, name);
+ callsigns.append(mc);
+
+ return mc->Callsign();
+ }
+
+ return name;
+}
+
+// +--------------------------------------------------------------------+
+
+static void SelectCombatGroups(CombatGroup* g, const ShipDesign* d, List<CombatGroup>& list)
+{
+ if (g->IntelLevel() <= Intel::RESERVE)
+ return;
+
+ if (g->GetUnits().size() > 0) {
+ for (int i = 0; i < g->GetUnits().size(); i++) {
+ CombatUnit* u = g->GetUnits().at(i);
+ if (u->GetDesign() == d && u->Count() - u->DeadCount() > 0) {
+ list.append(g);
+ }
+ }
+ }
+
+ ListIter<CombatGroup> subgroup = g->GetComponents();
+ while (++subgroup)
+ SelectCombatGroups(subgroup.value(), d, list);
+}
+
+CombatGroup*
+MissionTemplate::FindCombatGroup(int iff, const ShipDesign* d)
+{
+ CombatGroup* result = 0;
+ Campaign* campaign = Campaign::GetCampaign();
+ List<CombatGroup> group_list;
+ static int combat_group_index = 0;
+
+ if (campaign) {
+ ListIter<Combatant> combatant = campaign->GetCombatants();
+ while (++combatant && !result) {
+ if (combatant->GetIFF() == iff) {
+ ::SelectCombatGroups(combatant->GetForce(), d, group_list);
+ }
+ }
+
+ if (group_list.size() > 0)
+ result = group_list[combat_group_index++ % group_list.size()];
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+MissionElement*
+MissionTemplate::FindElement(const char* n)
+{
+ Text name = n;
+
+ ListIter<MissionCallsign> c_iter = callsigns;
+ while (++c_iter) {
+ MissionCallsign* c = c_iter.value();
+ if (c->Name() == name) {
+ name = c->Callsign();
+ break;
+ }
+ }
+
+ ListIter<MissionAlias> a_iter = aliases;
+ while (++a_iter) {
+ MissionAlias* a = a_iter.value();
+ if (a->Name() == name)
+ return a->Element();
+ }
+
+ ListIter<MissionElement> e_iter = elements;
+ while (++e_iter) {
+ MissionElement* elem = e_iter.value();
+ if (elem->Name() == name)
+ return elem;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+MissionTemplate::Load(const char* fname, const char* pname)
+{
+ ok = false;
+
+ if (fname)
+ strcpy(filename, fname);
+
+ if (pname)
+ strcpy(path, pname);
+
+ if (!filename[0]) {
+ Print("\nCan't Load Mission Template, script unspecified.\n");
+ return ok;
+ }
+
+ Print("\nLoad Mission Template: '%s'\n", filename);
+
+ int max_ships = (int) 1e6;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ bool old_fs = loader->IsFileSystemEnabled();
+ BYTE* block = 0;
+
+ loader->UseFileSystem(true);
+ loader->SetDataPath(path);
+ loader->LoadBuffer(filename, block, true);
+ loader->SetDataPath(0);
+ loader->UseFileSystem(old_fs);
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
+
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'\n", filename);
+ return ok;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "MISSION_TEMPLATE") {
+ Print("ERROR: invalid MISSION TEMPLATE file '%s'\n", filename);
+ term->print(10);
+ return ok;
+ }
+ }
+
+ ok = true;
+
+ char target_name[256];
+ char ward_name[256];
+
+ target_name[0] = 0;
+ ward_name[0] = 0;
+
+ do {
+ delete term; term = 0;
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ Text defname = def->name()->value();
+
+ if (defname == "name")
+ GetDefText(name, def, filename);
+
+ else if (defname == "type") {
+ char typestr[64];
+ GetDefText(typestr, def, filename);
+ type = TypeFromName(typestr);
+ }
+
+ else if (defname == "system") {
+ char sysname[64];
+ GetDefText(sysname, def, filename);
+
+ Campaign* campaign = Campaign::GetCampaign();
+
+ if (campaign) {
+ Galaxy* galaxy = Galaxy::GetInstance();
+
+ if (galaxy) {
+ star_system = galaxy->GetSystem(sysname);
+ }
+ }
+ }
+
+ else if (defname == "degrees")
+ GetDefBool(degrees, def, filename);
+
+ else if (defname == "region")
+ GetDefText(region, def, filename);
+
+ else if (defname == "objective")
+ GetDefText(objective, def, filename);
+
+ else if (defname == "sitrep")
+ GetDefText(sitrep, def, filename);
+
+ else if (defname == "start")
+ GetDefTime(start, def, filename);
+
+ else if (defname == "team")
+ GetDefNumber(team, def, filename);
+
+ else if (defname == "target")
+ GetDefText(target_name, def, filename);
+
+ else if (defname == "ward")
+ GetDefText(ward_name, def, filename);
+
+ else if ((defname == "alias")) {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: alias struct missing in '%s'\n", filename);
+ ok = false;
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseAlias(val);
+ }
+ }
+
+ else if ((defname == "callsign")) {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: callsign struct missing in '%s'\n", filename);
+ ok = false;
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseCallsign(val);
+ }
+ }
+
+ else if (defname == "optional") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: optional group struct missing in '%s'\n", filename);
+ ok = false;
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseOptional(val);
+ }
+ }
+
+ else if (defname == "element") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: element struct missing in '%s'\n", filename);
+ ok = false;
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ MissionElement* elem = ParseElement(val);
+ if (MapElement(elem)) {
+ AddElement(elem);
+ }
+ else {
+ Print("WARNING: failed to map element %s '%s' in '%s'\n",
+ elem->GetDesign() ? elem->GetDesign()->name : "NO DSN",
+ elem->Name().data(),
+ filename);
+ val->print();
+ Print("\n");
+ delete elem;
+ ok = false;
+ }
+ }
+ }
+
+ else if (defname == "event") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: event struct missing in '%s'\n", filename);
+ ok = false;
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ MissionEvent* event = ParseEvent(val);
+
+ if (MapEvent(event))
+ AddEvent(event);
+ }
+ }
+ } // def
+ } // term
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+
+ if (ok) {
+ CheckObjectives();
+
+ if (target_name[0])
+ target = FindElement(target_name);
+
+ if (ward_name[0])
+ ward = FindElement(ward_name);
+
+ Print("Mission Template Loaded.\n\n");
+ }
+
+ return ok;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MissionTemplate::ParseAlias(TermStruct* val)
+{
+ Text name;
+ Text design;
+ Text code;
+ Text elem_name;
+ int iff = -1;
+ int player = 0;
+ RLoc* rloc = 0;
+ bool use_loc = false;
+ Vec3 loc;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "name")
+ GetDefText(name, pdef, filename);
+
+ else if (defname == "elem")
+ GetDefText(elem_name, pdef, filename);
+
+ else if (defname == "code")
+ GetDefText(code, pdef, filename);
+
+ else if (defname == "design")
+ GetDefText(design, pdef, filename);
+
+ else if (defname == "iff")
+ GetDefNumber(iff, pdef, filename);
+
+ else if (defname == "loc") {
+ loc;
+ GetDefVec(loc, pdef, filename);
+ use_loc = true;
+ }
+
+ else if (defname == "rloc") {
+ if (pdef->term()->isStruct()) {
+ rloc = ParseRLoc(pdef->term()->isStruct());
+ }
+ }
+
+ else if (defname == "player") {
+ GetDefNumber(player, pdef, filename);
+
+ if (player && !code.length())
+ code = "player";
+ }
+ }
+ }
+
+ MissionElement* elem = 0;
+
+ // now find element and create alias:
+ if (name.length()) {
+ for (int i = 0; i < aliases.size(); i++)
+ if (aliases[i]->Name() == name)
+ return;
+
+ // by element name?
+ if (elem_name.length()) {
+ elem = FindElement(elem_name);
+ }
+
+ // by special code?
+ else if (code.length()) {
+ code.toLower();
+ Campaign* campaign = Campaign::GetCampaign();
+
+ if (code == "player") {
+ for (int i = 0; !elem && i < elements.size(); i++) {
+ MissionElement* e = elements[i];
+ if (e->Player() > 0) {
+ elem = e;
+ }
+ }
+ }
+
+ else if (campaign && code == "player_carrier") {
+ CombatGroup* player_group = campaign->GetPlayerGroup();
+
+ if (player_group) {
+ CombatGroup* carrier = player_group->FindCarrier();
+
+ if (carrier) {
+ elem = FindElement(carrier->Name());
+ }
+ }
+ }
+
+ else if (campaign && code == "player_squadron") {
+ CombatGroup* player_group = player_squadron;
+
+ if (!player_group)
+ player_group = campaign->GetPlayerGroup();
+
+ if (player_group &&
+ (player_group->Type() == CombatGroup::INTERCEPT_SQUADRON ||
+ player_group->Type() == CombatGroup::FIGHTER_SQUADRON ||
+ player_group->Type() == CombatGroup::ATTACK_SQUADRON)) {
+ elem = FindElement(player_group->Name());
+ }
+ }
+
+ else if (campaign && code == "strike_target") {
+ CombatGroup* player_group = campaign->GetPlayerGroup();
+
+ if (player_group) {
+ CombatGroup* strike_target = campaign->FindStrikeTarget(player_group->GetIFF(), player_group);
+
+ if (strike_target) {
+ elem = FindElement(strike_target->Name());
+ }
+ }
+ }
+ }
+
+ // by design and team?
+ else {
+ MissionElement* first_match = 0;
+
+ for (int i = 0; !elem && i < elements.size(); i++) {
+ MissionElement* e = elements[i];
+ if (e->GetIFF() == iff && design == e->GetDesign()->name) {
+ // do we already have an alias for this element?
+ bool found = false;
+ for (int a = 0; !found && a < aliases.size(); a++)
+ if (aliases[a]->Element() == e)
+ found = true;
+
+ if (!found)
+ elem = e;
+
+ else if (!first_match)
+ first_match = e;
+ }
+ }
+
+ if (first_match && !elem)
+ elem = first_match;
+ }
+
+ if (elem) {
+ if (rloc) elem->SetRLoc(*rloc);
+ else if (use_loc) elem->SetLocation(loc);
+
+ delete rloc;
+
+ aliases.append(new(__FILE__,__LINE__) MissionAlias(name, elem));
+ }
+ else {
+ ::Print("WARNING: Could not resolve mission alias '%s'\n", (const char*) name);
+ ok = false;
+ }
+ }
+
+ if (!elem || !ok) return;
+
+ // re-parse the struct, dealing with stuff
+ // that needs to be attached to the element:
+ for (i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "objective") {
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: order struct missing for element '%s' in '%s'\n", (const char*) elem->Name(), filename);
+ ok = false;
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+ Instruction* obj = ParseInstruction(val, elem);
+ elem->Objectives().append(obj);
+ }
+ }
+
+ else if (defname == "instr") {
+ Text* obj = new(__FILE__,__LINE__) Text;
+ if (GetDefText(*obj, pdef, filename))
+ elem->Instructions().append(obj);
+ }
+
+ else if (defname == "order" || defname == "navpt") {
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: order struct missing for element '%s' in '%s'\n", (const char*) elem->Name(), filename);
+ ok = false;
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+ Instruction* npt = ParseInstruction(val, elem);
+ elem->NavList().append(npt);
+ }
+ }
+
+ else if (defname == "loadout") {
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: loadout struct missing for element '%s' in '%s'\n", (const char*) elem->Name(), filename);
+ ok = false;
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+ ParseLoadout(val, elem);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MissionTemplate::ParseCallsign(TermStruct* val)
+{
+ Text name;
+ int iff = -1;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "name")
+ GetDefText(name, pdef, filename);
+
+ else if (defname == "iff")
+ GetDefNumber(iff, pdef, filename);
+ }
+ }
+
+ if (name.length() > 0 && iff >= 0)
+ MapCallsign(name, iff);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+MissionTemplate::ParseOptional(TermStruct* val)
+{
+ int n = 0;
+ int min = 0;
+ int max = 1000;
+ int skip = 0;
+ int total = val->elements()->size();
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "min") {
+ GetDefNumber(min, pdef, filename);
+ total--;
+ }
+
+ else if (defname == "max") {
+ GetDefNumber(max, pdef, filename);
+ total--;
+ }
+
+
+ else if ((defname == "optional")) {
+ bool select;
+
+ if (n >= max)
+ select = false;
+ else if (total - n - skip <= min)
+ select = true;
+ else
+ select = RandomChance();
+
+ if (select) {
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: optional group struct missing in '%s'\n", filename);
+ ok = false;
+ skip++;
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+ if (ParseOptional(val))
+ n++;
+ else
+ skip++;
+ }
+ }
+ else {
+ skip++;
+ }
+ }
+
+ else if (defname == "element") {
+ bool select;
+
+ if (n >= max)
+ select = false;
+ else if (total - n - skip <= min)
+ select = true;
+ else
+ select = RandomChance();
+
+ if (select) {
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: element struct missing in '%s'\n", filename);
+ ok = false;
+ skip++;
+ }
+ else {
+ TermStruct* es = pdef->term()->isStruct();
+ MissionElement* elem = ParseElement(es);
+ if (MapElement(elem)) {
+ AddElement(elem);
+ n++;
+ }
+ else {
+ delete elem;
+ skip++;
+ }
+ }
+ }
+ else {
+ skip++;
+ }
+ }
+ }
+ }
+
+ return n > 0 && n >= min;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MissionTemplate::CheckObjectives()
+{
+ ListIter<MissionElement> iter = elements;
+ while (++iter) {
+ MissionElement* elem = iter.value();
+
+ ListIter<Instruction> obj = elem->Objectives();
+ while (++obj) {
+ Instruction* o = obj.value();
+ Text tgt = o->TargetName();
+
+ MissionElement* tgt_elem = 0;
+
+ if (tgt.length()) {
+ tgt_elem = FindElement(tgt);
+
+ if (!tgt_elem)
+ obj.removeItem();
+ else
+ o->SetTarget(tgt_elem->Name());
+ }
+ }
+ }
+}
diff --git a/Stars45/MissionTemplate.h b/Stars45/MissionTemplate.h
new file mode 100644
index 0000000..f0772fa
--- /dev/null
+++ b/Stars45/MissionTemplate.h
@@ -0,0 +1,117 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MissionTemplate.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Simulation Universe and Region classes
+*/
+
+#ifndef MissionTemplate_h
+#define MissionTemplate_h
+
+#include "Types.h"
+#include "Mission.h"
+
+// +--------------------------------------------------------------------+
+
+class MissionTemplate;
+class MissionAlias;
+class MissionCallsign;
+class MissionEvent;
+
+// +--------------------------------------------------------------------+
+
+class MissionTemplate : public Mission
+{
+public:
+ static const char* TYPENAME() { return "MissionTemplate"; }
+
+ MissionTemplate(int id, const char* filename=0, const char* path=0);
+ virtual ~MissionTemplate();
+
+ virtual bool Load(const char* filename=0, const char* path=0);
+
+ // accessors/mutators:
+ virtual MissionElement* FindElement(const char* name);
+ virtual void AddElement(MissionElement* elem);
+ virtual bool MapElement(MissionElement* elem);
+ virtual Text MapShip(Text name);
+ virtual CombatGroup* GetPlayerSquadron() const { return player_squadron; }
+ virtual void SetPlayerSquadron(CombatGroup* ps) { player_squadron = ps; }
+ virtual Text MapCallsign(const char* name, int iff);
+ virtual bool MapEvent(MissionEvent* event);
+
+
+protected:
+ CombatGroup* FindCombatGroup(int iff, const ShipDesign* dsn);
+ void ParseAlias(TermStruct* val);
+ void ParseCallsign(TermStruct* val);
+ bool ParseOptional(TermStruct* val);
+ void CheckObjectives();
+
+ List<MissionAlias> aliases;
+ List<MissionCallsign> callsigns;
+ CombatGroup* player_squadron;
+};
+
+// +--------------------------------------------------------------------+
+
+class MissionAlias
+{
+ friend class MissionTemplate;
+
+public:
+ static const char* TYPENAME() { return "MissionAlias"; }
+
+ MissionAlias() : elem(0) { }
+ MissionAlias(const char* n, MissionElement* e) : name(n), elem(e) { }
+ virtual ~MissionAlias() { }
+
+ int operator == (const MissionAlias& a) const { return name == a.name; }
+
+ Text Name() const { return name; }
+ MissionElement* Element() const { return elem; }
+
+ void SetName(const char* n) { name = n; }
+ void SetElement(MissionElement* e) { elem = e; }
+
+protected:
+ Text name;
+ MissionElement* elem;
+};
+
+// +--------------------------------------------------------------------+
+
+class MissionCallsign
+{
+ friend class MissionTemplate;
+
+public:
+ static const char* TYPENAME() { return "MissionCallsign"; }
+
+ MissionCallsign() { }
+ MissionCallsign(const char* c, const char* n) : call(c), name(n) { }
+ virtual ~MissionCallsign() { }
+
+ int operator == (const MissionCallsign& a)const { return call == a.call; }
+
+ Text Callsign() const { return call; }
+ Text Name() const { return name; }
+
+ void SetCallsign(const char* c) { call = c; }
+ void SetName(const char* n) { name = n; }
+
+protected:
+ Text call;
+ Text name;
+};
+
+
+#endif MissionTemplate_h
+
diff --git a/Stars45/ModConfig.cpp b/Stars45/ModConfig.cpp
new file mode 100644
index 0000000..dd2f96e
--- /dev/null
+++ b/Stars45/ModConfig.cpp
@@ -0,0 +1,381 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ModConfig.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mod file deployment configuration and manager
+*/
+
+
+#include "MemDebug.h"
+#include "ModConfig.h"
+#include "ModInfo.h"
+#include "Campaign.h"
+#include "ShipDesign.h"
+#include "WeaponDesign.h"
+#include "Starshatter.h"
+#include "ParseUtil.h"
+#include "DataLoader.h"
+
+// +-------------------------------------------------------------------+
+
+static ModConfig* mod_config = 0;
+
+// +-------------------------------------------------------------------+
+
+ModConfig::ModConfig()
+{
+ mod_config = this;
+
+ Load();
+ FindMods();
+ Deploy();
+}
+
+ModConfig::~ModConfig()
+{
+ if (mod_config == this)
+ mod_config = 0;
+
+ Undeploy();
+
+ enabled.destroy();
+ disabled.destroy();
+ mods.destroy();
+}
+
+// +-------------------------------------------------------------------+
+
+void
+ModConfig::Initialize()
+{
+ mod_config = new(__FILE__,__LINE__) ModConfig;
+}
+
+void
+ModConfig::Close()
+{
+ delete mod_config;
+ mod_config = 0;
+}
+
+// +-------------------------------------------------------------------+
+
+ModConfig*
+ModConfig::GetInstance()
+{
+ return mod_config;
+}
+
+// +-------------------------------------------------------------------+
+
+void
+ModConfig::Load()
+{
+ // read the config file:
+ BYTE* block = 0;
+ int blocklen = 0;
+
+ char filename[64];
+ strcpy(filename, "mod.cfg");
+
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ ::fseek(f, 0, SEEK_END);
+ blocklen = ftell(f);
+ ::fseek(f, 0, SEEK_SET);
+
+ block = new(__FILE__,__LINE__) BYTE[blocklen+1];
+ block[blocklen] = 0;
+
+ ::fread(block, blocklen, 1, f);
+ ::fclose(f);
+ }
+
+ if (blocklen == 0)
+ return;
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'.\n", filename);
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "MOD_CONFIG") {
+ Print("WARNING: invalid '%s' file. No mods deployed\n", filename);
+ return;
+ }
+ }
+
+ enabled.destroy();
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ Text name;
+ GetDefText(name, def, filename);
+ enabled.append(new(__FILE__,__LINE__) Text(name));
+ }
+ }
+ }
+ while (term);
+
+ delete [] block;
+}
+
+void
+ModConfig::Save()
+{
+ FILE* f = fopen("mod.cfg", "w");
+ if (f) {
+ fprintf(f, "MOD_CONFIG\n\n");
+
+ ListIter<Text> iter = enabled;
+ while (++iter) {
+ Text* name = iter.value();
+ fprintf(f, "mod: \"%s\"\n", name->data());
+ }
+
+ fclose(f);
+ }
+}
+
+void
+ModConfig::FindMods()
+{
+ disabled.destroy();
+
+ DataLoader* loader = DataLoader::GetLoader();
+
+ if (loader) {
+ loader->UseFileSystem(true);
+ loader->ListFiles("*.dat", disabled, true);
+ loader->UseFileSystem(Starshatter::UseFileSystem());
+
+ ListIter<Text> iter = disabled;
+ while (++iter) {
+ Text* name = iter.value();
+ name->setSensitive(false);
+
+ if (*name == "shatter.dat" ||
+ *name == "beta.dat" ||
+ *name == "start.dat" ||
+ *name == "irunin.dat" ||
+ *name == "vox.dat" ||
+ name->contains("uninstall") ||
+ enabled.contains(name))
+ delete iter.removeItem();
+ }
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+ModInfo*
+ModConfig::GetModInfo(const char* filename)
+{
+ for (int i = 0; i < mods.size(); i++) {
+ ModInfo* mod_info = mods[i];
+
+ if (mod_info->Filename() == filename && mod_info->IsEnabled())
+ return mod_info;
+ }
+
+ return false;
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+ModConfig::IsDeployed(const char* name)
+{
+ for (int i = 0; i < mods.size(); i++) {
+ ModInfo* mod_info = mods[i];
+
+ if (mod_info->Name() == name && mod_info->IsEnabled())
+ return true;
+ }
+
+ return false;
+}
+
+// +-------------------------------------------------------------------+
+
+void
+ModConfig::Deploy()
+{
+ Save();
+
+ if (enabled.size() < 1)
+ return;
+
+#ifdef STARSHATTER_DEMO_RELEASE
+ Print("\nPACKAGED MODS ARE NOT SUPPORTED IN THIS DEMO\n");
+#else
+ Print("\nDEPLOYING MODS\n--------------\n");
+
+ int i = 1;
+ ListIter<Text> iter = enabled;
+ while (++iter) {
+ Text* name = iter.value();
+
+ if (IsDeployed(name->data())) {
+ Print(" %d. %s is already deployed (skipping)\n", i++, name->data());
+ continue;
+ }
+
+ Print(" %d. %s\n", i++, name->data());
+
+ ModInfo* mod_info = new(__FILE__,__LINE__) ModInfo;
+
+ if (mod_info->Load(name->data()) && mod_info->Enable()) {
+ mods.append(mod_info);
+ }
+ else {
+ Print(" Could not deploy '%s' - disabling\n", name->data());
+
+ delete mod_info;
+ iter.removeItem();
+ disabled.append(name);
+ }
+ }
+
+ Print("\n");
+ Game::UseLocale(0);
+#endif
+}
+
+void
+ModConfig::Undeploy()
+{
+ Print("UNDEPLOYING MODS\n");
+ mods.destroy();
+
+ ShipDesign::ClearModCatalog();
+ WeaponDesign::ClearModCatalog();
+}
+
+void
+ModConfig::Redeploy()
+{
+ Undeploy();
+ Deploy();
+
+ Campaign::Close();
+ Campaign::Initialize();
+ Campaign::SelectCampaign("Single Missions");
+}
+
+// +-------------------------------------------------------------------+
+
+void
+ModConfig::EnableMod(const char* name)
+{
+#ifdef STARSHATTER_DEMO_RELEASE
+ DisableMod(name);
+#else
+
+ if (!name || !*name)
+ return;
+
+ Text* mod_name;
+
+ ListIter<Text> iter = disabled;
+ while (++iter) {
+ Text* t = iter.value();
+
+ if (*t == name) {
+ mod_name = t;
+ iter.removeItem();
+ break;
+ }
+ }
+
+ if (mod_name) {
+ enabled.append(mod_name);
+
+ if (!IsDeployed(*mod_name)) {
+ ModInfo* mod_info = new(__FILE__,__LINE__) ModInfo;
+
+ if (mod_info->Load(*mod_name) && mod_info->Enable()) {
+ mods.append(mod_info);
+ }
+ }
+ }
+#endif
+}
+
+void
+ModConfig::DisableMod(const char* name)
+{
+ if (!name || !*name)
+ return;
+
+ Text* mod_name;
+
+ ListIter<Text> iter = enabled;
+ while (++iter) {
+ Text* t = iter.value();
+
+ if (*t == name) {
+ mod_name = t;
+ iter.removeItem();
+ break;
+ }
+ }
+
+ if (mod_name) {
+ disabled.append(mod_name);
+
+ ListIter<ModInfo> iter = mods;
+ while (++iter) {
+ ModInfo* mod_info = iter.value();
+ if (mod_info->Name() == *mod_name) {
+ delete iter.removeItem();
+ break;
+ }
+ }
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+ModConfig::IncreaseModPriority(int mod_index)
+{
+ if (mod_index > 0 && mod_index < enabled.size()) {
+ Text* mod1 = enabled.at(mod_index-1);
+ Text* mod2 = enabled.at(mod_index);
+
+ enabled.at(mod_index-1) = mod2;
+ enabled.at(mod_index) = mod1;
+ }
+}
+
+void
+ModConfig::DecreaseModPriority(int mod_index)
+{
+ if (mod_index >= 0 && mod_index < enabled.size()-1) {
+ Text* mod1 = enabled.at(mod_index);
+ Text* mod2 = enabled.at(mod_index+1);
+
+ enabled.at(mod_index) = mod2;
+ enabled.at(mod_index+1) = mod1;
+ }
+}
+
diff --git a/Stars45/ModConfig.h b/Stars45/ModConfig.h
new file mode 100644
index 0000000..9e1f703
--- /dev/null
+++ b/Stars45/ModConfig.h
@@ -0,0 +1,75 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ModConfig.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mod file deployment configuration and manager
+*/
+
+
+#ifndef ModConfig_h
+#define ModConfig_h
+
+#include "Types.h"
+#include "Bitmap.h"
+#include "Text.h"
+#include "List.h"
+
+// +-------------------------------------------------------------------+
+
+class ModConfig;
+class ModInfo;
+class ModCampaign;
+
+// +-------------------------------------------------------------------+
+
+class ModConfig
+{
+public:
+ static const char* TYPENAME() { return "ModConfig"; }
+
+ ModConfig();
+ ~ModConfig();
+
+ int operator == (const ModConfig& cfg) const { return this == &cfg; }
+
+ static void Initialize();
+ static void Close();
+ static ModConfig* GetInstance();
+
+ void Load();
+ void Save();
+ void FindMods();
+
+ bool IsDeployed(const char* name);
+ void Deploy();
+ void Undeploy();
+ void Redeploy();
+
+ // these methods change the configuration only
+ // you must Redeploy() to have them take effect:
+
+ void EnableMod(const char* name);
+ void DisableMod(const char* name);
+ void IncreaseModPriority(int mod_index);
+ void DecreaseModPriority(int mod_index);
+
+ List<Text>& EnabledMods() { return enabled; }
+ List<Text>& DisabledMods() { return disabled; }
+ List<ModInfo>& GetModInfoList() { return mods; }
+
+ ModInfo* GetModInfo(const char* filename);
+
+private:
+ List<Text> enabled;
+ List<Text> disabled;
+ List<ModInfo> mods;
+};
+
+#endif ModConfig_h \ No newline at end of file
diff --git a/Stars45/ModDlg.cpp b/Stars45/ModDlg.cpp
new file mode 100644
index 0000000..14096e3
--- /dev/null
+++ b/Stars45/ModDlg.cpp
@@ -0,0 +1,350 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ModDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mod Config Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "ModDlg.h"
+#include "ModInfoDlg.h"
+#include "BaseScreen.h"
+#include "ModConfig.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ParseUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(ModDlg, OnIncrease);
+DEF_MAP_CLIENT(ModDlg, OnDecrease);
+DEF_MAP_CLIENT(ModDlg, OnEnable);
+DEF_MAP_CLIENT(ModDlg, OnDisable);
+DEF_MAP_CLIENT(ModDlg, OnSelectEnabled);
+DEF_MAP_CLIENT(ModDlg, OnSelectDisabled);
+DEF_MAP_CLIENT(ModDlg, OnAccept);
+DEF_MAP_CLIENT(ModDlg, OnCancel);
+DEF_MAP_CLIENT(ModDlg, OnAudio);
+DEF_MAP_CLIENT(ModDlg, OnVideo);
+DEF_MAP_CLIENT(ModDlg, OnOptions);
+DEF_MAP_CLIENT(ModDlg, OnControls);
+DEF_MAP_CLIENT(ModDlg, OnMod);
+
+// +--------------------------------------------------------------------+
+
+ModDlg::ModDlg(Screen* s, FormDef& def, BaseScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ lst_disabled(0), lst_enabled(0), btn_enable(0), btn_disable(0),
+ btn_increase(0), btn_decrease(0), btn_accept(0), btn_cancel(0),
+ aud_btn(0), vid_btn(0), ctl_btn(0), opt_btn(0), mod_btn(0),
+ config(0), changed(false)
+{
+ config = ModConfig::GetInstance();
+ Init(def);
+}
+
+ModDlg::~ModDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ModDlg::RegisterControls()
+{
+ btn_accept = (Button*) FindControl( 1);
+ btn_cancel = (Button*) FindControl( 2);
+ btn_enable = (Button*) FindControl(301);
+ btn_disable = (Button*) FindControl(302);
+ btn_increase = (Button*) FindControl(303);
+ btn_decrease = (Button*) FindControl(304);
+
+ lst_disabled = (ListBox*) FindControl(201);
+ lst_enabled = (ListBox*) FindControl(202);
+
+ if (btn_accept)
+ REGISTER_CLIENT(EID_CLICK, btn_accept, ModDlg, OnAccept);
+
+ if (btn_cancel)
+ REGISTER_CLIENT(EID_CLICK, btn_cancel, ModDlg, OnCancel);
+
+ if (lst_enabled)
+ REGISTER_CLIENT(EID_SELECT, lst_enabled, ModDlg, OnSelectEnabled);
+
+ if (lst_disabled)
+ REGISTER_CLIENT(EID_SELECT, lst_disabled, ModDlg, OnSelectDisabled);
+
+ if (btn_enable) {
+ REGISTER_CLIENT(EID_CLICK, btn_enable, ModDlg, OnEnable);
+ btn_enable->SetEnabled(false);
+ }
+
+ if (btn_disable) {
+ REGISTER_CLIENT(EID_CLICK, btn_disable, ModDlg, OnDisable);
+ btn_disable->SetEnabled(false);
+ }
+
+ if (btn_increase) {
+ char up_arrow[2];
+ up_arrow[0] = Font::ARROW_UP;
+ up_arrow[1] = 0;
+ btn_increase->SetText(up_arrow);
+ btn_increase->SetEnabled(false);
+ REGISTER_CLIENT(EID_CLICK, btn_increase, ModDlg, OnIncrease);
+ }
+
+ if (btn_decrease) {
+ char dn_arrow[2];
+ dn_arrow[0] = Font::ARROW_DOWN;
+ dn_arrow[1] = 0;
+ btn_decrease->SetText(dn_arrow);
+ btn_decrease->SetEnabled(false);
+ REGISTER_CLIENT(EID_CLICK, btn_decrease, ModDlg, OnDecrease);
+ }
+
+ vid_btn = (Button*) FindControl(901);
+ REGISTER_CLIENT(EID_CLICK, vid_btn, ModDlg, OnVideo);
+
+ aud_btn = (Button*) FindControl(902);
+ REGISTER_CLIENT(EID_CLICK, aud_btn, ModDlg, OnAudio);
+
+ ctl_btn = (Button*) FindControl(903);
+ REGISTER_CLIENT(EID_CLICK, ctl_btn, ModDlg, OnControls);
+
+ opt_btn = (Button*) FindControl(904);
+ REGISTER_CLIENT(EID_CLICK, opt_btn, ModDlg, OnOptions);
+
+ mod_btn = (Button*) FindControl(905);
+ if (mod_btn)
+ REGISTER_CLIENT(EID_CLICK, mod_btn, ModDlg, OnMod);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ModDlg::Show()
+{
+ FormWindow::Show();
+ UpdateLists();
+
+ if (vid_btn) vid_btn->SetButtonState(0);
+ if (aud_btn) aud_btn->SetButtonState(0);
+ if (ctl_btn) ctl_btn->SetButtonState(0);
+ if (opt_btn) opt_btn->SetButtonState(0);
+ if (mod_btn) mod_btn->SetButtonState(1);
+}
+
+void
+ModDlg::UpdateLists()
+{
+ config = ModConfig::GetInstance();
+
+ if (config && lst_disabled && lst_enabled) {
+ lst_disabled->ClearItems();
+ lst_enabled->ClearItems();
+
+ ListIter<Text> iter_d = config->DisabledMods();
+ while (++iter_d) {
+ Text* t = iter_d.value();
+ lst_disabled->AddItem(*t);
+ }
+
+ ListIter<Text> iter_e = config->EnabledMods();
+ while (++iter_e) {
+ Text* t = iter_e.value();
+ lst_enabled->AddItem(*t);
+ }
+ }
+
+ if (btn_disable)
+ btn_disable->SetEnabled(false);
+
+ if (btn_enable)
+ btn_enable->SetEnabled(false);
+
+ if (btn_increase)
+ btn_increase->SetEnabled(false);
+
+ if (btn_decrease)
+ btn_decrease->SetEnabled(false);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ModDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ if (btn_accept && btn_accept->IsEnabled())
+ OnAccept(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ModDlg::OnSelectEnabled(AWEvent* event)
+{
+ static DWORD click_time = 0;
+
+ if (lst_enabled) {
+ if (btn_disable)
+ btn_disable->SetEnabled(lst_enabled->GetSelCount() == 1);
+
+ if (btn_increase)
+ btn_increase->SetEnabled(lst_enabled->GetSelection() > 0);
+
+ if (btn_decrease)
+ btn_decrease->SetEnabled(lst_enabled->GetSelection() < lst_enabled->NumItems() - 1);
+
+ // double-click:
+ if (Game::RealTime() - click_time < 350) {
+ if (lst_enabled->GetSelCount() == 1) {
+ int index = lst_enabled->GetSelection();
+ Text mod_name = lst_enabled->GetItemText(index);
+ ModInfo* mod_info = config->GetModInfo(mod_name);
+ ModInfoDlg* mod_info_dlg = manager->GetModInfoDlg();
+
+ if (mod_info && mod_info_dlg) {
+ mod_info_dlg->SetModInfo(mod_info);
+ manager->ShowModInfoDlg();
+ }
+ }
+ }
+ }
+
+ click_time = Game::RealTime();
+}
+
+void
+ModDlg::OnSelectDisabled(AWEvent* event)
+{
+#ifdef STARSHATTER_DEMO_RELEASE
+ if (btn_enable) {
+ btn_enable->SetEnabled(false);
+ }
+#else
+ if (btn_enable && lst_disabled) {
+ btn_enable->SetEnabled(lst_disabled->GetSelCount() == 1);
+ }
+#endif
+}
+
+void
+ModDlg::OnEnable(AWEvent* event)
+{
+ int index = lst_disabled->GetSelection();
+ Text mod_name = lst_disabled->GetItemText(index);
+
+ config->EnableMod(mod_name);
+ changed = true;
+
+ UpdateLists();
+
+ ModInfo* mod_info = config->GetModInfo(mod_name);
+ ModInfoDlg* mod_info_dlg = manager->GetModInfoDlg();
+
+ if (mod_info && mod_info_dlg) {
+ mod_info_dlg->SetModInfo(mod_info);
+ manager->ShowModInfoDlg();
+ }
+}
+
+void
+ModDlg::OnDisable(AWEvent* event)
+{
+ int index = lst_enabled->GetSelection();
+ Text mod_name = lst_enabled->GetItemText(index);
+
+ config->DisableMod(mod_name);
+ changed = true;
+
+ UpdateLists();
+}
+
+void
+ModDlg::OnIncrease(AWEvent* event)
+{
+ int index = lst_enabled->GetSelection();
+ config->IncreaseModPriority(index--);
+
+ UpdateLists();
+ lst_enabled->SetSelected(index);
+ btn_disable->SetEnabled(true);
+ btn_increase->SetEnabled(index > 0);
+ btn_decrease->SetEnabled(index < lst_enabled->NumItems()-1);
+}
+
+void
+ModDlg::OnDecrease(AWEvent* event)
+{
+ int index = lst_enabled->GetSelection();
+ config->DecreaseModPriority(index++);
+
+ UpdateLists();
+ lst_enabled->SetSelected(index);
+ btn_disable->SetEnabled(true);
+ btn_increase->SetEnabled(index > 0);
+ btn_decrease->SetEnabled(index < lst_enabled->NumItems()-1);
+}
+
+// +--------------------------------------------------------------------+
+
+void ModDlg::OnAudio(AWEvent* event) { manager->ShowAudDlg(); }
+void ModDlg::OnVideo(AWEvent* event) { manager->ShowVidDlg(); }
+void ModDlg::OnOptions(AWEvent* event) { manager->ShowOptDlg(); }
+void ModDlg::OnControls(AWEvent* event) { manager->ShowCtlDlg(); }
+void ModDlg::OnMod(AWEvent* event) { manager->ShowModDlg(); }
+
+// +--------------------------------------------------------------------+
+
+void
+ModDlg::Apply()
+{
+ if (changed) {
+ config->Save();
+ config->FindMods();
+ config->Redeploy();
+ changed = false;
+ }
+}
+
+void
+ModDlg::Cancel()
+{
+ if (changed) {
+ config->Load();
+ config->FindMods();
+ config->Redeploy();
+ changed = false;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ModDlg::OnAccept(AWEvent* event)
+{
+ manager->ApplyOptions();
+}
+
+void
+ModDlg::OnCancel(AWEvent* event)
+{
+ manager->CancelOptions();
+}
diff --git a/Stars45/ModDlg.h b/Stars45/ModDlg.h
new file mode 100644
index 0000000..b5b93b3
--- /dev/null
+++ b/Stars45/ModDlg.h
@@ -0,0 +1,94 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ModDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mod Config Dialog Active Window class
+*/
+
+#ifndef ModDlg_h
+#define ModDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class BaseScreen;
+class Campaign;
+class ModConfig;
+
+// +--------------------------------------------------------------------+
+
+class ModDlg : public FormWindow
+{
+public:
+ ModDlg(Screen* s, FormDef& def, BaseScreen* mgr);
+ virtual ~ModDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+
+ virtual void OnIncrease(AWEvent* event);
+ virtual void OnDecrease(AWEvent* event);
+
+ virtual void OnSelectEnabled(AWEvent* event);
+ virtual void OnSelectDisabled(AWEvent* event);
+
+ virtual void OnEnable(AWEvent* event);
+ virtual void OnDisable(AWEvent* event);
+
+ virtual void OnAccept(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+
+ virtual void Apply();
+ virtual void Cancel();
+
+ virtual void OnAudio(AWEvent* event);
+ virtual void OnVideo(AWEvent* event);
+ virtual void OnOptions(AWEvent* event);
+ virtual void OnControls(AWEvent* event);
+ virtual void OnMod(AWEvent* event);
+
+protected:
+ void UpdateLists();
+
+ BaseScreen* manager;
+
+ ListBox* lst_disabled;
+ ListBox* lst_enabled;
+
+ Button* btn_accept;
+ Button* btn_cancel;
+ Button* btn_enable;
+ Button* btn_disable;
+ Button* btn_increase;
+ Button* btn_decrease;
+
+ Button* aud_btn;
+ Button* vid_btn;
+ Button* opt_btn;
+ Button* ctl_btn;
+ Button* mod_btn;
+
+ ModConfig* config;
+ bool changed;
+};
+
+#endif ModDlg_h
+
diff --git a/Stars45/ModInfo.cpp b/Stars45/ModInfo.cpp
new file mode 100644
index 0000000..70ecb02
--- /dev/null
+++ b/Stars45/ModInfo.cpp
@@ -0,0 +1,292 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ModInfo.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Information block for describing and deploying third party mods
+*/
+
+
+#include "MemDebug.h"
+#include "ModInfo.h"
+#include "Campaign.h"
+#include "ShipDesign.h"
+#include "ParseUtil.h"
+
+#include "Archive.h"
+#include "DataLoader.h"
+#include "PCX.h"
+#include "Bitmap.h"
+
+// +-------------------------------------------------------------------+
+
+ModInfo::ModInfo()
+ : logo(0), distribute(false), enabled(false), catalog(0)
+{ }
+
+ModInfo::ModInfo(const char* fname)
+ : logo(0), distribute(false), enabled(false), catalog(0)
+{
+ if (fname && *fname) {
+ Load(fname);
+ }
+}
+
+ModInfo::ModInfo(const char* n, const char* v, const char* u)
+ : name(n), logo(0), version(v), url(u), distribute(false), enabled(false), catalog(0)
+{ }
+
+ModInfo::~ModInfo()
+{
+ if (enabled)
+ Disable();
+
+ delete logo;
+ delete catalog;
+ campaigns.destroy();
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+ModInfo::Load(const char* fname)
+{
+ bool ok = false;
+
+ filename = fname;
+ DataArchive a(filename);
+
+ int n = a.FindEntry("mod_info.def");
+ if (n > -1) {
+ BYTE* buf = 0;
+ int len = a.ExpandEntry(n, buf, true);
+
+ if (len > 0 && buf != 0) {
+ ok = ParseModInfo((const char*) buf);
+ delete [] buf;
+ }
+ }
+
+ if (logoname.length()) {
+ PcxImage pcx;
+
+ logo = new(__FILE__,__LINE__) Bitmap;
+
+ n = a.FindEntry(logoname);
+ if (n > -1) {
+ BYTE* buf = 0;
+ int len = a.ExpandEntry(n, buf, true);
+
+ pcx.LoadBuffer(buf, len);
+ delete [] buf;
+ }
+
+ // now copy the image into the bitmap:
+ if (pcx.bitmap) {
+ logo->CopyImage(pcx.width, pcx.height, pcx.bitmap);
+ }
+
+ else if (pcx.himap) {
+ logo->CopyHighColorImage(pcx.width, pcx.height, pcx.himap);
+ }
+
+ else {
+ logo->ClearImage();
+ }
+ }
+
+ return ok;
+}
+
+bool
+ModInfo::ParseModInfo(const char* block)
+{
+ bool ok = false;
+ Parser parser(new(__FILE__,__LINE__) BlockReader(block));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'\n", filename.data());
+ return ok;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "MOD_INFO") {
+ Print("ERROR: invalid mod_info file '%s'\n", filename.data());
+ term->print(10);
+ return ok;
+ }
+ }
+
+ ok = true;
+
+ do {
+ delete term; term = 0;
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ Text defname = def->name()->value();
+
+ if (defname == "name")
+ GetDefText(name, def, filename);
+
+ else if (defname == "desc" || defname == "description")
+ GetDefText(desc, def, filename);
+
+ else if (defname == "author")
+ GetDefText(author, def, filename);
+
+ else if (defname == "url")
+ GetDefText(url, def, filename);
+
+ else if (defname == "copyright")
+ GetDefText(copyright, def, filename);
+
+ else if (defname == "logo")
+ GetDefText(logoname, def, filename);
+
+ else if (defname == "version") {
+ if (def->term()) {
+ if (def->term()->isNumber()) {
+ int v = 0;
+ char buf[32];
+ GetDefNumber(v, def, filename);
+ sprintf(buf, "%d", v);
+
+ version = buf;
+ }
+
+ else if (def->term()->isText()) {
+ GetDefText(version, def, filename);
+ }
+ }
+ }
+
+ else if (defname == "distribute")
+ GetDefBool(distribute, def, filename);
+
+ else if (defname == "campaign") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: campaign structure missing in mod_info.def for '%s'\n", name.data());
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+
+ ModCampaign* c = new(__FILE__,__LINE__) ModCampaign;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ defname = pdef->name()->value();
+
+ if (defname == "name") {
+ GetDefText(c->name, pdef, filename);
+ }
+
+ else if (defname == "path") {
+ GetDefText(c->path, pdef, filename);
+ }
+
+ else if (defname == "dynamic") {
+ GetDefBool(c->dynamic, pdef, filename);
+ }
+ }
+ }
+
+ if (c->name.length() && c->path.length())
+ campaigns.append(c);
+ else
+ delete c;
+ }
+ }
+
+ else if (defname == "catalog") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: catalog structure missing in mod_info.def for '%s'\n", name.data());
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+
+ ModCatalog* c = new(__FILE__,__LINE__) ModCatalog;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ defname = pdef->name()->value();
+
+ if (defname == "file") {
+ GetDefText(c->file, pdef, filename);
+ }
+
+ else if (defname == "path") {
+ GetDefText(c->path, pdef, filename);
+
+ char last_char = c->path[c->path.length()-1];
+
+ if (last_char != '/' && last_char != '\\')
+ c->path += "/";
+ }
+ }
+ }
+
+ if (c->file.length() && c->path.length() && !catalog)
+ catalog = c;
+ else
+ delete c;
+ }
+ }
+ } // def
+ } // term
+ }
+ while (term);
+
+ return ok;
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+ModInfo::Enable()
+{
+ DataLoader* loader = DataLoader::GetLoader();
+
+ if (loader && !enabled) {
+ loader->EnableDatafile(filename);
+ enabled = true;
+
+ if (catalog)
+ ShipDesign::LoadCatalog(catalog->Path(), catalog->File(), true);
+
+ ShipDesign::LoadSkins("/Mods/Skins/", filename);
+
+ for (int i = 0; i < campaigns.size(); i++) {
+ ModCampaign* c = campaigns[i];
+ Campaign::CreateCustomCampaign(c->Name(), c->Path());
+ }
+ }
+
+ return enabled;
+}
+
+bool
+ModInfo::Disable()
+{
+ DataLoader* loader = DataLoader::GetLoader();
+
+ if (loader) {
+ loader->DisableDatafile(filename);
+ enabled = false;
+ }
+
+ return !enabled;
+}
+
+// +-------------------------------------------------------------------+
diff --git a/Stars45/ModInfo.h b/Stars45/ModInfo.h
new file mode 100644
index 0000000..997746c
--- /dev/null
+++ b/Stars45/ModInfo.h
@@ -0,0 +1,122 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ModInfo.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Information block for describing and deploying third party mods
+*/
+
+
+#ifndef ModInfo_h
+#define ModInfo_h
+
+#include "Types.h"
+#include "Bitmap.h"
+#include "Text.h"
+#include "List.h"
+
+// +-------------------------------------------------------------------+
+
+class ModInfo;
+class ModCampaign;
+class ModCatalog;
+
+// +-------------------------------------------------------------------+
+
+class ModInfo
+{
+public:
+ static const char* TYPENAME() { return "ModInfo"; }
+
+ ModInfo();
+ ModInfo(const char* filename);
+ ModInfo(const char* name, const char* version, const char* url);
+ ~ModInfo();
+
+ int operator == (const ModInfo& m) const { return name.length() && name == m.name; }
+
+ const Text& Name() const { return name; }
+ const Text& Description() const { return desc; }
+ const Text& Author() const { return author; }
+ const Text& URL() const { return url; }
+ const Text& Filename() const { return filename; }
+ const Text& Copyright() const { return copyright; }
+ Bitmap* LogoImage() const { return logo; }
+ const Text& Version() const { return version; }
+ bool Distribute() const { return distribute; }
+ bool IsEnabled() const { return enabled; }
+
+ List<ModCampaign>& GetCampaigns() { return campaigns; }
+
+ bool Load(const char* filename);
+ bool ParseModInfo(const char* buffer);
+
+ bool Enable();
+ bool Disable();
+
+private:
+ Text name;
+ Text desc;
+ Text author;
+ Text url;
+ Text filename;
+ Text copyright;
+ Bitmap* logo;
+ Text logoname;
+ Text version;
+ bool distribute;
+ bool enabled;
+
+ List<ModCampaign> campaigns;
+ ModCatalog* catalog;
+};
+
+// +-------------------------------------------------------------------+
+
+class ModCampaign
+{
+ friend class ModInfo;
+
+public:
+ static const char* TYPENAME() { return "ModCampaign"; }
+
+ ModCampaign() : dynamic(false) { }
+ ~ModCampaign() { }
+
+ const Text& Name() const { return name; }
+ const Text& Path() const { return path; }
+ bool IsDynamic() const { return dynamic; }
+
+private:
+ Text name;
+ Text path;
+ bool dynamic;
+};
+
+// +-------------------------------------------------------------------+
+
+class ModCatalog
+{
+ friend class ModInfo;
+
+public:
+ static const char* TYPENAME() { return "ModCatalog"; }
+
+ ModCatalog() { }
+ ~ModCatalog() { }
+
+ const Text& File() const { return file; }
+ const Text& Path() const { return path; }
+
+private:
+ Text file;
+ Text path;
+};
+
+#endif ModInfo_h \ No newline at end of file
diff --git a/Stars45/ModInfoDlg.cpp b/Stars45/ModInfoDlg.cpp
new file mode 100644
index 0000000..f7dfb34
--- /dev/null
+++ b/Stars45/ModInfoDlg.cpp
@@ -0,0 +1,115 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ModInfoDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mod Config Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "ModInfoDlg.h"
+#include "BaseScreen.h"
+#include "ModConfig.h"
+#include "ModInfo.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "ImageBox.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ParseUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(ModInfoDlg, OnAccept);
+
+// +--------------------------------------------------------------------+
+
+ModInfoDlg::ModInfoDlg(Screen* s, FormDef& def, BaseScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ btn_accept(0), mod_info(0)
+{
+ Init(def);
+}
+
+ModInfoDlg::~ModInfoDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ModInfoDlg::RegisterControls()
+{
+ btn_accept = (Button*) FindControl( 1);
+
+ if (btn_accept)
+ REGISTER_CLIENT(EID_CLICK, btn_accept, ModInfoDlg, OnAccept);
+
+ lbl_name = FindControl(101);
+ lbl_desc = FindControl(102);
+ lbl_copy = FindControl(103);
+
+ img_logo = (ImageBox*) FindControl(200);
+
+ if (img_logo) {
+ img_logo->GetPicture(bmp_default);
+ img_logo->SetBlendMode(Video::BLEND_SOLID);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ModInfoDlg::Show()
+{
+ FormWindow::Show();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ModInfoDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ if (btn_accept && btn_accept->IsEnabled())
+ OnAccept(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ModInfoDlg::SetModInfo(ModInfo* info)
+{
+ mod_info = info;
+
+ if (mod_info) {
+ if (lbl_name) lbl_name->SetText(mod_info->Name());
+ if (lbl_desc) lbl_desc->SetText(mod_info->Description());
+ if (lbl_copy) lbl_copy->SetText(mod_info->Copyright());
+
+ if (img_logo && mod_info->LogoImage() && mod_info->LogoImage()->Width() > 32)
+ img_logo->SetPicture(*mod_info->LogoImage());
+ else if (img_logo)
+ img_logo->SetPicture(bmp_default);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ModInfoDlg::OnAccept(AWEvent* event)
+{
+ manager->ShowModDlg();
+}
diff --git a/Stars45/ModInfoDlg.h b/Stars45/ModInfoDlg.h
new file mode 100644
index 0000000..c2dacde
--- /dev/null
+++ b/Stars45/ModInfoDlg.h
@@ -0,0 +1,65 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ModInfoDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mod Info Splash Dialog Active Window class
+*/
+
+#ifndef ModInfoDlg_h
+#define ModInfoDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class BaseScreen;
+class ModConfig;
+class ModInfo;
+
+// +--------------------------------------------------------------------+
+
+class ModInfoDlg : public FormWindow
+{
+public:
+ ModInfoDlg(Screen* s, FormDef& def, BaseScreen* mgr);
+ virtual ~ModInfoDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void SetModInfo(ModInfo* info);
+ virtual void OnAccept(AWEvent* event);
+
+protected:
+ BaseScreen* manager;
+
+ ActiveWindow* lbl_name;
+ ActiveWindow* lbl_desc;
+ ActiveWindow* lbl_copy;
+ ImageBox* img_logo;
+
+ Button* btn_accept;
+
+ ModInfo* mod_info;
+
+ Bitmap bmp_default;
+};
+
+#endif ModInfoDlg_h
+
diff --git a/Stars45/MsnDlg.cpp b/Stars45/MsnDlg.cpp
new file mode 100644
index 0000000..0884bbe
--- /dev/null
+++ b/Stars45/MsnDlg.cpp
@@ -0,0 +1,303 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Briefing Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "MsnDlg.h"
+#include "PlanScreen.h"
+#include "Starshatter.h"
+#include "Campaign.h"
+#include "Mission.h"
+#include "Instruction.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "StarSystem.h"
+
+#include "NetLobby.h"
+
+#include "Game.h"
+#include "FormWindow.h"
+#include "FormatUtil.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "ParseUtil.h"
+
+// +--------------------------------------------------------------------+
+
+MsnDlg::MsnDlg(PlanScreen* mgr)
+ : plan_screen(mgr),
+ commit(0), cancel(0), campaign(0), mission(0),
+ pkg_index(-1), info(0)
+{
+ campaign = Campaign::GetCampaign();
+
+ if (campaign)
+ mission = campaign->GetMission();
+
+ mission_name = 0;
+ mission_system = 0;
+ mission_sector = 0;
+ mission_time_start = 0;
+ mission_time_target = 0;
+ mission_time_target_label = 0;
+
+ sit_button = 0;
+ pkg_button = 0;
+ nav_button = 0;
+ wep_button = 0;
+ commit = 0;
+ cancel = 0;
+}
+
+MsnDlg::~MsnDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnDlg::RegisterMsnControls(FormWindow* win)
+{
+ mission_name = win->FindControl(200);
+ mission_system = win->FindControl(202);
+ mission_sector = win->FindControl(204);
+ mission_time_start = win->FindControl(206);
+ mission_time_target = win->FindControl(208);
+ mission_time_target_label = win->FindControl(207);
+
+ sit_button = (Button*) win->FindControl(900);
+ pkg_button = (Button*) win->FindControl(901);
+ nav_button = (Button*) win->FindControl(902);
+ wep_button = (Button*) win->FindControl(903);
+ commit = (Button*) win->FindControl(1);
+ cancel = (Button*) win->FindControl(2);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnDlg::ShowMsnDlg()
+{
+ campaign = Campaign::GetCampaign();
+
+ mission = 0;
+ pkg_index = -1;
+
+ if (campaign) {
+ mission = campaign->GetMission();
+
+ if (!mission)
+ ::Print("ERROR - MsnDlg::Show() no mission.\n");
+ else
+ ::Print("MsnDlg::Show() mission id = %d name = '%s'\n",
+ mission->Identity(),
+ mission->Name());
+ }
+ else {
+ ::Print("ERROR - MsnDlg::Show() no campaign.\n");
+ }
+
+ if (mission_name) {
+ if (mission)
+ mission_name->SetText(mission->Name());
+ else
+ mission_name->SetText(Game::GetText("MsnDlg.no-mission"));
+ }
+
+ if (mission_system) {
+ mission_system->SetText("");
+
+ if (mission) {
+ StarSystem* sys = mission->GetStarSystem();
+
+ if (sys)
+ mission_system->SetText(sys->Name());
+ }
+ }
+
+ if (mission_sector) {
+ mission_sector->SetText("");
+
+ if (mission) {
+ mission_sector->SetText(mission->GetRegion());
+ }
+ }
+
+ if (mission_time_start) {
+ if (mission) {
+ char txt[32];
+ FormatDayTime(txt, mission->Start());
+ mission_time_start->SetText(txt);
+ }
+ }
+
+ if (mission_time_target) {
+ int time_on_target = CalcTimeOnTarget();
+
+ if (time_on_target) {
+ char txt[32];
+ FormatDayTime(txt, time_on_target);
+ mission_time_target->SetText(txt);
+ mission_time_target_label->SetText(Game::GetText("MsnDlg.target"));
+ }
+ else {
+ mission_time_target->SetText("");
+ mission_time_target_label->SetText("");
+ }
+ }
+
+
+ if (sit_button) {
+ sit_button->SetButtonState(plan_screen->IsMsnObjShown());
+ sit_button->SetEnabled(true);
+ }
+
+ if (pkg_button) {
+ pkg_button->SetButtonState(plan_screen->IsMsnPkgShown());
+ pkg_button->SetEnabled(true);
+ }
+
+ if (nav_button) {
+ nav_button->SetButtonState(plan_screen->IsNavShown());
+ nav_button->SetEnabled(true);
+ }
+
+ if (wep_button) {
+ wep_button->SetButtonState(plan_screen->IsMsnWepShown());
+ wep_button->SetEnabled(true);
+ }
+
+ bool mission_ok = true;
+
+ if (!mission || !mission->IsOK()) {
+ mission_ok = false;
+
+ if (sit_button) sit_button->SetEnabled(false);
+ if (pkg_button) pkg_button->SetEnabled(false);
+ if (nav_button) nav_button->SetEnabled(false);
+ if (wep_button) wep_button->SetEnabled(false);
+ }
+ else {
+ MissionElement* player_elem = mission->GetPlayer();
+
+ if (wep_button && player_elem)
+ wep_button->SetEnabled(player_elem->Loadouts().size() > 0);
+ }
+
+ if (wep_button && NetLobby::GetInstance())
+ wep_button->SetEnabled(false);
+
+ commit->SetEnabled(mission_ok);
+ cancel->SetEnabled(true);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MsnDlg::CalcTimeOnTarget()
+{
+ if (mission) {
+ MissionElement* element = mission->GetElements()[0];
+ if (element) {
+ Point loc = element->Location();
+ loc.SwapYZ(); // navpts use Z for altitude, element loc uses Y for altitude.
+
+ int mission_time = mission->Start();
+
+ int i = 0;
+ ListIter<Instruction> navpt = element->NavList();
+ while (++navpt) {
+ int action = navpt->Action();
+
+ double dist = Point(loc - navpt->Location()).length();
+
+ int etr = 0;
+
+ if (navpt->Speed() > 0)
+ etr = (int) (dist / navpt->Speed());
+ else
+ etr = (int) (dist / 500);
+
+ mission_time += etr;
+
+ loc = navpt->Location();
+ i++;
+
+ if (action >= Instruction::ESCORT) { // this is the target!
+ return mission_time;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnDlg::OnTabButton(AWEvent* event)
+{
+ if (event->window == sit_button) {
+ plan_screen->ShowMsnObjDlg();
+ }
+
+ if (event->window == pkg_button) {
+ plan_screen->ShowMsnPkgDlg();
+ }
+
+ if (event->window == nav_button) {
+ plan_screen->ShowNavDlg();
+ }
+
+ if (event->window == wep_button) {
+ plan_screen->ShowMsnWepDlg();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnDlg::OnCommit(AWEvent* event)
+{
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars) {
+ Mouse::Show(false);
+ stars->SetGameMode(Starshatter::LOAD_MODE);
+ }
+
+ else
+ Game::Panic("MsnDlg::OnCommit() - Game instance not found");
+}
+
+void
+MsnDlg::OnCancel(AWEvent* event)
+{
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars) {
+ Mouse::Show(false);
+
+ if (campaign && (campaign->IsDynamic() || campaign->IsTraining()))
+ stars->SetGameMode(Starshatter::CMPN_MODE);
+ else
+ stars->SetGameMode(Starshatter::MENU_MODE);
+ }
+
+ else
+ Game::Panic("MsnDlg::OnCancel() - Game instance not found");
+}
diff --git a/Stars45/MsnDlg.h b/Stars45/MsnDlg.h
new file mode 100644
index 0000000..8af6544
--- /dev/null
+++ b/Stars45/MsnDlg.h
@@ -0,0 +1,73 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Briefing Dialog Active Window class
+*/
+
+#ifndef MsnDlg_h
+#define MsnDlg_h
+
+#include "Types.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class FormWindow;
+class PlanScreen;
+class Campaign;
+class Mission;
+class MissionInfo;
+
+// +--------------------------------------------------------------------+
+
+class MsnDlg
+{
+public:
+ MsnDlg(PlanScreen* mgr);
+ virtual ~MsnDlg();
+
+ void RegisterMsnControls(FormWindow* win);
+ void ShowMsnDlg();
+
+ // Operations:
+ virtual void OnCommit(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+ virtual void OnTabButton(AWEvent* event);
+
+protected:
+ virtual int CalcTimeOnTarget();
+
+ PlanScreen* plan_screen;
+ Button* commit;
+ Button* cancel;
+ Button* sit_button;
+ Button* pkg_button;
+ Button* nav_button;
+ Button* wep_button;
+
+ ActiveWindow* mission_name;
+ ActiveWindow* mission_system;
+ ActiveWindow* mission_sector;
+ ActiveWindow* mission_time_start;
+ ActiveWindow* mission_time_target;
+ ActiveWindow* mission_time_target_label;
+
+ Campaign* campaign;
+ Mission* mission;
+ MissionInfo* info;
+ int pkg_index;
+};
+
+#endif MsnDlg_h
+
diff --git a/Stars45/MsnEditDlg.cpp b/Stars45/MsnEditDlg.cpp
new file mode 100644
index 0000000..05f64c5
--- /dev/null
+++ b/Stars45/MsnEditDlg.cpp
@@ -0,0 +1,896 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnEditDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Editor Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "MsnEditDlg.h"
+#include "MsnElemDlg.h"
+#include "MsnEventDlg.h"
+#include "NavDlg.h"
+#include "MenuScreen.h"
+#include "Campaign.h"
+#include "Mission.h"
+#include "MissionEvent.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "StarSystem.h"
+#include "Galaxy.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "EditBox.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+#include "Random.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(MsnEditDlg, OnAccept);
+DEF_MAP_CLIENT(MsnEditDlg, OnCancel);
+DEF_MAP_CLIENT(MsnEditDlg, OnTabButton);
+DEF_MAP_CLIENT(MsnEditDlg, OnSystemSelect);
+DEF_MAP_CLIENT(MsnEditDlg, OnElemAdd);
+DEF_MAP_CLIENT(MsnEditDlg, OnElemEdit);
+DEF_MAP_CLIENT(MsnEditDlg, OnElemDel);
+DEF_MAP_CLIENT(MsnEditDlg, OnElemSelect);
+DEF_MAP_CLIENT(MsnEditDlg, OnElemInc);
+DEF_MAP_CLIENT(MsnEditDlg, OnElemDec);
+DEF_MAP_CLIENT(MsnEditDlg, OnEventAdd);
+DEF_MAP_CLIENT(MsnEditDlg, OnEventEdit);
+DEF_MAP_CLIENT(MsnEditDlg, OnEventDel);
+DEF_MAP_CLIENT(MsnEditDlg, OnEventSelect);
+DEF_MAP_CLIENT(MsnEditDlg, OnEventInc);
+DEF_MAP_CLIENT(MsnEditDlg, OnEventDec);
+
+// +--------------------------------------------------------------------+
+
+MsnEditDlg::MsnEditDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ btn_accept(0), btn_cancel(0),
+ btn_elem_add(0), btn_elem_edit(0), btn_elem_del(0), btn_elem_inc(0), btn_elem_dec(0),
+ btn_event_add(0), btn_event_edit(0), btn_event_del(0), btn_event_inc(0), btn_event_dec(0),
+ btn_sit(0), btn_pkg(0), btn_map(0),
+ txt_name(0), cmb_type(0), cmb_system(0), cmb_region(0),
+ txt_description(0), txt_situation(0), txt_objective(0),
+ lst_elem(0), lst_event(0), mission(0), mission_info(0), current_tab(0),
+ exit_latch(true)
+{
+ Init(def);
+}
+
+MsnEditDlg::~MsnEditDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditDlg::SetMission(Mission* m)
+{
+ mission = m;
+ current_tab = 0;
+}
+
+void
+MsnEditDlg::SetMissionInfo(MissionInfo* m)
+{
+ mission_info = m;
+ current_tab = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditDlg::RegisterControls()
+{
+ btn_accept = (Button*) FindControl( 1);
+ btn_cancel = (Button*) FindControl( 2);
+ btn_sit = (Button*) FindControl(301);
+ btn_pkg = (Button*) FindControl(302);
+ btn_map = (Button*) FindControl(303);
+
+ btn_elem_add = (Button*) FindControl(501);
+ btn_elem_edit = (Button*) FindControl(505);
+ btn_elem_del = (Button*) FindControl(502);
+ btn_elem_inc = (Button*) FindControl(503);
+ btn_elem_dec = (Button*) FindControl(504);
+
+ btn_event_add = (Button*) FindControl(511);
+ btn_event_edit = (Button*) FindControl(515);
+ btn_event_del = (Button*) FindControl(512);
+ btn_event_inc = (Button*) FindControl(513);
+ btn_event_dec = (Button*) FindControl(514);
+
+ txt_name = (EditBox*) FindControl(201);
+ cmb_type = (ComboBox*) FindControl(202);
+ cmb_system = (ComboBox*) FindControl(203);
+ cmb_region = (ComboBox*) FindControl(204);
+
+ txt_description= (EditBox*) FindControl(410);
+ txt_situation = (EditBox*) FindControl(411);
+ txt_objective = (EditBox*) FindControl(412);
+
+ lst_elem = (ListBox*) FindControl(510);
+ lst_event = (ListBox*) FindControl(520);
+
+ if (btn_accept)
+ REGISTER_CLIENT(EID_CLICK, btn_accept, MsnEditDlg, OnAccept);
+
+ if (btn_cancel)
+ REGISTER_CLIENT(EID_CLICK, btn_cancel, MsnEditDlg, OnCancel);
+
+ if (btn_elem_add)
+ REGISTER_CLIENT(EID_CLICK, btn_elem_add, MsnEditDlg, OnElemAdd);
+
+ if (btn_elem_edit)
+ REGISTER_CLIENT(EID_CLICK, btn_elem_edit, MsnEditDlg, OnElemEdit);
+
+ if (btn_elem_del)
+ REGISTER_CLIENT(EID_CLICK, btn_elem_del, MsnEditDlg, OnElemDel);
+
+ if (btn_elem_inc) {
+ char up_arrow[2];
+ up_arrow[0] = Font::ARROW_UP;
+ up_arrow[1] = 0;
+ btn_elem_inc->SetText(up_arrow);
+ btn_elem_inc->SetEnabled(false);
+ REGISTER_CLIENT(EID_CLICK, btn_elem_inc, MsnEditDlg, OnElemInc);
+ }
+
+ if (btn_elem_dec) {
+ char dn_arrow[2];
+ dn_arrow[0] = Font::ARROW_DOWN;
+ dn_arrow[1] = 0;
+ btn_elem_dec->SetText(dn_arrow);
+ btn_elem_dec->SetEnabled(false);
+ REGISTER_CLIENT(EID_CLICK, btn_elem_dec, MsnEditDlg, OnElemDec);
+ }
+
+ if (btn_event_add)
+ REGISTER_CLIENT(EID_CLICK, btn_event_add, MsnEditDlg, OnEventAdd);
+
+ if (btn_event_edit)
+ REGISTER_CLIENT(EID_CLICK, btn_event_edit, MsnEditDlg, OnEventEdit);
+
+ if (btn_event_del)
+ REGISTER_CLIENT(EID_CLICK, btn_event_del, MsnEditDlg, OnEventDel);
+
+ if (btn_event_inc) {
+ char up_arrow[2];
+ up_arrow[0] = Font::ARROW_UP;
+ up_arrow[1] = 0;
+ btn_event_inc->SetText(up_arrow);
+ btn_event_inc->SetEnabled(false);
+ REGISTER_CLIENT(EID_CLICK, btn_event_inc, MsnEditDlg, OnEventInc);
+ }
+
+ if (btn_event_dec) {
+ char dn_arrow[2];
+ dn_arrow[0] = Font::ARROW_DOWN;
+ dn_arrow[1] = 0;
+ btn_event_dec->SetText(dn_arrow);
+ btn_event_dec->SetEnabled(false);
+ REGISTER_CLIENT(EID_CLICK, btn_event_dec, MsnEditDlg, OnEventDec);
+ }
+
+ if (btn_sit)
+ REGISTER_CLIENT(EID_CLICK, btn_sit, MsnEditDlg, OnTabButton);
+
+ if (btn_pkg)
+ REGISTER_CLIENT(EID_CLICK, btn_pkg, MsnEditDlg, OnTabButton);
+
+ if (btn_map)
+ REGISTER_CLIENT(EID_CLICK, btn_map, MsnEditDlg, OnTabButton);
+
+ if (cmb_system)
+ REGISTER_CLIENT(EID_SELECT, cmb_system, MsnEditDlg, OnSystemSelect);
+
+ if (lst_elem)
+ REGISTER_CLIENT(EID_SELECT, lst_elem, MsnEditDlg, OnElemSelect);
+
+ if (lst_event)
+ REGISTER_CLIENT(EID_SELECT, lst_event, MsnEditDlg, OnEventSelect);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditDlg::Show()
+{
+ FormWindow::Show();
+
+ if (!txt_name || !cmb_type) return;
+
+ txt_name->SetText("");
+
+ if (cmb_system) {
+ cmb_system->ClearItems();
+
+ Galaxy* galaxy = Galaxy::GetInstance();
+ ListIter<StarSystem> iter = galaxy->GetSystemList();
+ while (++iter) {
+ cmb_system->AddItem(iter->Name());
+ }
+ }
+
+ if (mission) {
+ int i;
+
+ txt_name->SetText(mission->Name());
+ cmb_type->SetSelection(mission->Type());
+
+ StarSystem* sys = mission->GetStarSystem();
+ if (sys && cmb_system && cmb_region) {
+ for (i = 0; i < cmb_system->NumItems(); i++) {
+ if (!strcmp(cmb_system->GetItem(i), sys->Name())) {
+ cmb_system->SetSelection(i);
+ break;
+ }
+ }
+
+ cmb_region->ClearItems();
+ int sel_rgn = 0;
+
+ List<OrbitalRegion> regions;
+ regions.append(sys->AllRegions());
+ regions.sort();
+
+ i = 0;
+ ListIter<OrbitalRegion> iter = regions;
+ while (++iter) {
+ OrbitalRegion* region = iter.value();
+ cmb_region->AddItem(region->Name());
+
+ if (!strcmp(mission->GetRegion(), region->Name())) {
+ sel_rgn = i;
+ }
+
+ i++;
+ }
+
+ cmb_region->SetSelection(sel_rgn);
+ }
+
+ if (txt_description && mission_info)
+ txt_description->SetText(mission_info->description);
+
+ if (txt_situation)
+ txt_situation->SetText(mission->Situation());
+
+ if (txt_objective)
+ txt_objective->SetText(mission->Objective());
+
+ DrawPackages();
+ }
+
+ ShowTab(current_tab);
+
+ exit_latch = true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditDlg::DrawPackages()
+{
+ bool elem_del = false;
+ bool elem_edit = false;
+ bool elem_inc = false;
+ bool elem_dec = false;
+
+ bool event_del = false;
+ bool event_edit = false;
+ bool event_inc = false;
+ bool event_dec = false;
+
+ if (mission) {
+ if (lst_elem) {
+ bool cleared = false;
+ int index = lst_elem->GetSelection();
+
+ if (lst_elem->NumItems() != mission->GetElements().size()) {
+ if (lst_elem->NumItems() < mission->GetElements().size())
+ index = lst_elem->NumItems();
+
+ lst_elem->ClearItems();
+ cleared = true;
+ }
+
+ int i = 0;
+ ListIter<MissionElement> elem = mission->GetElements();
+ while (++elem) {
+ char txt[256];
+
+ sprintf(txt, "%d", elem->Identity());
+
+ if (cleared) {
+ lst_elem->AddItemWithData(txt, elem->ElementID());
+ }
+ else {
+ lst_elem->SetItemText(i, txt);
+ lst_elem->SetItemData(i, elem->ElementID());
+ }
+
+ sprintf(txt, "%d", elem->GetIFF());
+ lst_elem->SetItemText(i, 1, txt);
+ lst_elem->SetItemText(i, 2, elem->Name());
+ lst_elem->SetItemText(i, 4, elem->RoleName());
+ lst_elem->SetItemText(i, 5, elem->Region());
+
+ const ShipDesign* design = elem->GetDesign();
+
+ if (design) {
+ if (elem->Count() > 1)
+ sprintf(txt, "%d %s", elem->Count(), design->abrv);
+ else
+ sprintf(txt, "%s %s", design->abrv, design->name);
+ }
+ else {
+ sprintf(txt, Game::GetText("MsnDlg.undefined").data());
+ }
+
+ lst_elem->SetItemText(i, 3, txt);
+
+ i++;
+ }
+
+ int nitems = lst_elem->NumItems();
+ if (nitems) {
+ if (index >= nitems)
+ index = nitems - 1;
+
+ if (index >= 0) {
+ lst_elem->SetSelected(index);
+ elem_del = true;
+ elem_edit = true;
+ elem_inc = index > 0;
+ elem_dec = index < nitems-1;
+ }
+ }
+ }
+
+ if (lst_event) {
+ bool cleared = false;
+ int index = lst_event->GetSelection();
+
+ if (lst_event->NumItems() != mission->GetEvents().size()) {
+ if (lst_event->NumItems() < mission->GetEvents().size())
+ index = lst_event->NumItems();
+
+ lst_event->ClearItems();
+ cleared = true;
+ }
+
+ int i = 0;
+ ListIter<MissionEvent> event = mission->GetEvents();
+ while (++event) {
+ char txt[256];
+
+ sprintf(txt, "%d", event->EventID());
+ if (cleared) {
+ lst_event->AddItemWithData(txt, event->EventID());
+ }
+ else {
+ lst_event->SetItemText(i, txt);
+ lst_event->SetItemData(i, event->EventID());
+ }
+
+ if (event->Time()) {
+ FormatTime(txt, event->Time());
+ }
+ else if (event->Delay()) {
+ txt[0] = '+';
+ txt[1] = ' ';
+ FormatTime(txt+2, event->Delay());
+ }
+ else {
+ strcpy(txt, " ");
+ }
+
+ lst_event->SetItemText(i, 1, txt);
+ lst_event->SetItemText(i, 2, event->EventName());
+ lst_event->SetItemText(i, 3, event->EventShip());
+ lst_event->SetItemText(i, 4, event->EventMessage());
+
+ i++;
+ }
+
+ int nitems = lst_event->NumItems();
+ if (nitems) {
+ if (index >= nitems)
+ index = nitems - 1;
+
+ if (index >= 0) {
+ lst_event->SetSelected(index);
+ event_del = true;
+ event_edit = true;
+ event_inc = index > 0;
+ event_dec = index < nitems-1;
+ }
+ }
+ }
+ }
+
+ if (btn_elem_del) btn_elem_del->SetEnabled(elem_del);
+ if (btn_elem_edit) btn_elem_edit->SetEnabled(elem_edit);
+ if (btn_elem_inc) btn_elem_inc->SetEnabled(elem_inc);
+ if (btn_elem_dec) btn_elem_dec->SetEnabled(elem_del);
+
+ if (btn_event_del) btn_event_del->SetEnabled(event_del);
+ if (btn_event_edit) btn_event_edit->SetEnabled(event_edit);
+ if (btn_event_inc) btn_event_inc->SetEnabled(event_inc);
+ if (btn_event_dec) btn_event_dec->SetEnabled(event_dec);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ if (!exit_latch && btn_accept && btn_accept->IsEnabled())
+ OnAccept(0);
+ }
+
+ else if (Keyboard::KeyDown(VK_ESCAPE)) {
+ if (!exit_latch)
+ OnCancel(0);
+ }
+
+ else {
+ exit_latch = false;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditDlg::ScrapeForm()
+{
+ if (mission) {
+ if (txt_name) {
+ mission->SetName(txt_name->GetText());
+ }
+
+ if (cmb_type) {
+ mission->SetType(cmb_type->GetSelectedIndex());
+
+ if (mission_info)
+ mission_info->type = cmb_type->GetSelectedIndex();
+ }
+
+ Galaxy* galaxy = Galaxy::GetInstance();
+ StarSystem* system = 0;
+
+ if (galaxy)
+ system = galaxy->GetSystem(cmb_system->GetSelectedItem());
+
+ if (cmb_system && system) {
+ mission->ClearSystemList();
+ mission->SetStarSystem(system);
+
+ if (mission_info)
+ mission_info->system = system->Name();
+ }
+
+ if (cmb_region) {
+ mission->SetRegion(cmb_region->GetSelectedItem());
+
+ if (mission_info)
+ mission_info->region = cmb_region->GetSelectedItem();
+ }
+
+ if (txt_description && mission_info) {
+ mission_info->description = txt_description->GetText();
+ mission->SetDescription(txt_description->GetText());
+ }
+
+ if (txt_situation)
+ mission->SetSituation(txt_situation->GetText());
+
+ if (txt_objective)
+ mission->SetObjective(txt_objective->GetText());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditDlg::ShowTab(int tab)
+{
+ current_tab = tab;
+
+ if (current_tab < 0 || current_tab > 2)
+ current_tab = 0;
+
+ if (btn_sit) btn_sit->SetButtonState(tab == 0 ? 1 : 0);
+ if (btn_pkg) btn_pkg->SetButtonState(tab == 1 ? 1 : 0);
+ if (btn_map) btn_map->SetButtonState(tab == 2 ? 1 : 0);
+
+ DWORD low = 400 + tab*100;
+ DWORD high = low + 100;
+
+ ListIter<ActiveWindow> iter = Controls();
+ while (++iter) {
+ ActiveWindow* a = iter.value();
+
+ if (a->GetID() < 400)
+ a->Show();
+
+ else if (a->GetID() >= low && a->GetID() < high)
+ a->Show();
+
+ else
+ a->Hide();
+ }
+
+ if (tab == 2) {
+ NavDlg* navdlg = manager->GetNavDlg();
+
+ if (navdlg) {
+ navdlg->SetMission(mission);
+ navdlg->SetEditorMode(true);
+ }
+
+ manager->ShowNavDlg();
+ }
+ else {
+ manager->HideNavDlg();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditDlg::OnTabButton(AWEvent* event)
+{
+ if (!event) return;
+
+ if (event->window == btn_sit)
+ ShowTab(0);
+
+ else if (event->window == btn_pkg)
+ ShowTab(1);
+
+ else if (event->window == btn_map)
+ ShowTab(2);
+}
+
+void
+MsnEditDlg::OnSystemSelect(AWEvent* event)
+{
+ StarSystem* sys = 0;
+
+ if (cmb_system) {
+ const char* name = cmb_system->GetSelectedItem();
+
+ Galaxy* galaxy = Galaxy::GetInstance();
+ ListIter<StarSystem> iter = galaxy->GetSystemList();
+ while (++iter) {
+ StarSystem* s = iter.value();
+
+ if (!strcmp(s->Name(), name)) {
+ sys = s;
+ break;
+ }
+ }
+ }
+
+ if (sys && cmb_region) {
+ cmb_region->ClearItems();
+
+ List<OrbitalRegion> regions;
+ regions.append(sys->AllRegions());
+ regions.sort();
+
+ ListIter<OrbitalRegion> iter = regions;
+ while (++iter) {
+ OrbitalRegion* region = iter.value();
+ cmb_region->AddItem(region->Name());
+ }
+ }
+
+ ScrapeForm();
+
+ NavDlg* navdlg = manager->GetNavDlg();
+
+ if (navdlg)
+ navdlg->SetMission(mission);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditDlg::OnElemSelect(AWEvent* event)
+{
+ static DWORD click_time = 0;
+
+ if (lst_elem && mission) {
+ int selection = lst_elem->GetSelection();
+
+ if (btn_elem_edit)
+ btn_elem_edit->SetEnabled(selection >= 0);
+
+ if (btn_elem_del)
+ btn_elem_del->SetEnabled(selection >= 0);
+
+ if (btn_elem_inc)
+ btn_elem_inc->SetEnabled(selection > 0);
+
+ if (btn_elem_dec)
+ btn_elem_dec->SetEnabled(selection >= 0 && selection < lst_elem->NumItems() - 1);
+
+ // double-click:
+ if (Game::RealTime() - click_time < 350) {
+ if (lst_elem->GetSelCount() == 1) {
+ int index = lst_elem->GetSelection();
+ MissionElement* elem = mission->GetElements().at(index);
+ MsnElemDlg* msn_elem_dlg = manager->GetMsnElemDlg();
+
+ if (elem && msn_elem_dlg) {
+ ScrapeForm();
+ msn_elem_dlg->SetMission(mission);
+ msn_elem_dlg->SetMissionElement(elem);
+ manager->ShowMsnElemDlg();
+ }
+ }
+ }
+ }
+
+ click_time = Game::RealTime();
+}
+
+void
+MsnEditDlg::OnElemInc(AWEvent* event)
+{
+ int index = lst_elem->GetSelection();
+ mission->IncreaseElemPriority(index--);
+
+ DrawPackages();
+ lst_elem->SetSelected(index);
+ btn_elem_edit->SetEnabled(true);
+ btn_elem_del->SetEnabled(true);
+ btn_elem_inc->SetEnabled(index > 0);
+ btn_elem_dec->SetEnabled(index >= 0 && index < lst_elem->NumItems()-1);
+}
+
+void
+MsnEditDlg::OnElemDec(AWEvent* event)
+{
+ int index = lst_elem->GetSelection();
+ mission->DecreaseElemPriority(index++);
+
+ DrawPackages();
+ lst_elem->SetSelected(index);
+ btn_elem_edit->SetEnabled(true);
+ btn_elem_del->SetEnabled(true);
+ btn_elem_inc->SetEnabled(index > 0);
+ btn_elem_dec->SetEnabled(index >= 0 && index < lst_elem->NumItems()-1);
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditDlg::OnElemAdd(AWEvent* event)
+{
+ if (lst_elem && mission) {
+ List<MissionElement>& elements = mission->GetElements();
+ MissionElement* elem = new(__FILE__,__LINE__) MissionElement;
+
+ if (elements.size() > 0)
+ elem->SetLocation(RandomPoint());
+
+ if (cmb_region)
+ elem->SetRegion(cmb_region->GetSelectedItem());
+
+ elements.append(elem);
+ DrawPackages();
+
+ MsnElemDlg* msn_elem_dlg = manager->GetMsnElemDlg();
+
+ if (msn_elem_dlg) {
+ ScrapeForm();
+ msn_elem_dlg->SetMission(mission);
+ msn_elem_dlg->SetMissionElement(elem);
+ manager->ShowMsnElemDlg();
+ }
+ }
+}
+
+void
+MsnEditDlg::OnElemDel(AWEvent* event)
+{
+ if (lst_elem && mission) {
+ List<MissionElement>& elements = mission->GetElements();
+ delete elements.removeIndex(lst_elem->GetSelection());
+ DrawPackages();
+ }
+}
+
+void
+MsnEditDlg::OnElemEdit(AWEvent* event)
+{
+ if (lst_elem && mission && lst_elem->GetSelCount() == 1) {
+ int index = lst_elem->GetSelection();
+ MissionElement* elem = mission->GetElements().at(index);
+ MsnElemDlg* msn_elem_dlg = manager->GetMsnElemDlg();
+
+ if (elem && msn_elem_dlg) {
+ ScrapeForm();
+ msn_elem_dlg->SetMission(mission);
+ msn_elem_dlg->SetMissionElement(elem);
+ manager->ShowMsnElemDlg();
+ }
+ }
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditDlg::OnEventSelect(AWEvent* event)
+{
+ static DWORD click_time = 0;
+
+ if (lst_event && mission) {
+ int selection = lst_event->GetSelection();
+
+ if (btn_event_edit)
+ btn_event_edit->SetEnabled(selection >= 0);
+
+ if (btn_event_del)
+ btn_event_del->SetEnabled(selection >= 0);
+
+ if (btn_event_inc)
+ btn_event_inc->SetEnabled(selection > 0);
+
+ if (btn_event_dec)
+ btn_event_dec->SetEnabled(selection >= 0 && selection < lst_event->NumItems() - 1);
+
+ // double-click:
+ if (Game::RealTime() - click_time < 350) {
+ if (lst_event->GetSelCount() == 1) {
+ int index = lst_event->GetSelection();
+ MissionEvent* event = mission->GetEvents().at(index);
+ MsnEventDlg* msn_event_dlg = manager->GetMsnEventDlg();
+
+ if (event && msn_event_dlg) {
+ ScrapeForm();
+ msn_event_dlg->SetMission(mission);
+ msn_event_dlg->SetMissionEvent(event);
+ manager->ShowMsnEventDlg();
+ }
+ }
+ }
+ }
+
+ click_time = Game::RealTime();
+}
+
+void
+MsnEditDlg::OnEventInc(AWEvent* event)
+{
+ int index = lst_event->GetSelection();
+ mission->IncreaseEventPriority(index--);
+
+ DrawPackages();
+ lst_event->SetSelected(index);
+ btn_event_edit->SetEnabled(true);
+ btn_event_del->SetEnabled(true);
+ btn_event_inc->SetEnabled(index > 0);
+ btn_event_dec->SetEnabled(index >= 0 && index < lst_event->NumItems()-1);
+}
+
+void
+MsnEditDlg::OnEventDec(AWEvent* event)
+{
+ int index = lst_event->GetSelection();
+ mission->DecreaseEventPriority(index++);
+
+ DrawPackages();
+ lst_event->SetSelected(index);
+ btn_event_edit->SetEnabled(true);
+ btn_event_del->SetEnabled(true);
+ btn_event_inc->SetEnabled(index > 0);
+ btn_event_dec->SetEnabled(index >= 0 && index < lst_event->NumItems()-1);
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditDlg::OnEventAdd(AWEvent* event)
+{
+ if (lst_event && mission) {
+ List<MissionEvent>& events = mission->GetEvents();
+ MissionEvent* event = new(__FILE__,__LINE__) MissionEvent;
+
+ int id = 1;
+ for (int i = 0; i < events.size(); i++) {
+ MissionEvent* e = events[i];
+ if (e->EventID() >= id)
+ id = e->EventID() + 1;
+ }
+
+ event->id = id;
+
+ events.append(event);
+ DrawPackages();
+
+ MsnEventDlg* msn_event_dlg = manager->GetMsnEventDlg();
+
+ if (msn_event_dlg) {
+ ScrapeForm();
+ msn_event_dlg->SetMission(mission);
+ msn_event_dlg->SetMissionEvent(event);
+ manager->ShowMsnEventDlg();
+ }
+ }
+}
+
+void
+MsnEditDlg::OnEventDel(AWEvent* event)
+{
+ if (lst_event && mission) {
+ List<MissionEvent>& events = mission->GetEvents();
+ delete events.removeIndex(lst_event->GetSelection());
+ DrawPackages();
+ }
+}
+
+void
+MsnEditDlg::OnEventEdit(AWEvent* event)
+{
+ if (lst_event && mission && lst_event->GetSelCount() == 1) {
+ int index = lst_event->GetSelection();
+ MissionEvent* event = mission->GetEvents().at(index);
+ MsnEventDlg* msn_event_dlg = manager->GetMsnEventDlg();
+
+ if (event && msn_event_dlg) {
+ ScrapeForm();
+ msn_event_dlg->SetMission(mission);
+ msn_event_dlg->SetMissionEvent(event);
+ manager->ShowMsnEventDlg();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditDlg::OnAccept(AWEvent* event)
+{
+ if (mission) {
+ ScrapeForm();
+ mission_info->name = mission->Name();
+ mission->Save();
+ }
+
+ manager->ShowMsnSelectDlg();
+}
+
+void
+MsnEditDlg::OnCancel(AWEvent* event)
+{
+ if (mission)
+ mission->Load();
+
+ manager->ShowMsnSelectDlg();
+}
diff --git a/Stars45/MsnEditDlg.h b/Stars45/MsnEditDlg.h
new file mode 100644
index 0000000..03d1b33
--- /dev/null
+++ b/Stars45/MsnEditDlg.h
@@ -0,0 +1,116 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnEditDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Editor Dialog Active Window class
+*/
+
+#ifndef MsnEditDlg_h
+#define MsnEditDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+class Campaign;
+class Mission;
+class MissionInfo;
+
+// +--------------------------------------------------------------------+
+
+class MsnEditDlg : public FormWindow
+{
+public:
+ friend class MsnEditNavDlg;
+
+ MsnEditDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~MsnEditDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+ virtual void ScrapeForm();
+
+ // Operations:
+ virtual void OnAccept(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+ virtual void OnTabButton(AWEvent* event);
+ virtual void OnSystemSelect(AWEvent* event);
+ virtual void OnElemAdd(AWEvent* event);
+ virtual void OnElemEdit(AWEvent* event);
+ virtual void OnElemDel(AWEvent* event);
+ virtual void OnElemSelect(AWEvent* event);
+ virtual void OnElemInc(AWEvent* event);
+ virtual void OnElemDec(AWEvent* event);
+ virtual void OnEventAdd(AWEvent* event);
+ virtual void OnEventEdit(AWEvent* event);
+ virtual void OnEventDel(AWEvent* event);
+ virtual void OnEventSelect(AWEvent* event);
+ virtual void OnEventInc(AWEvent* event);
+ virtual void OnEventDec(AWEvent* event);
+
+ virtual Mission* GetMission() const { return mission; }
+ virtual void SetMission(Mission* m);
+ virtual void SetMissionInfo(MissionInfo* m);
+
+protected:
+ virtual void DrawPackages();
+ virtual void ShowTab(int tab);
+
+ MenuScreen* manager;
+
+ Button* btn_accept;
+ Button* btn_cancel;
+
+ Button* btn_elem_add;
+ Button* btn_elem_edit;
+ Button* btn_elem_del;
+ Button* btn_elem_inc;
+ Button* btn_elem_dec;
+
+ Button* btn_event_add;
+ Button* btn_event_edit;
+ Button* btn_event_del;
+ Button* btn_event_inc;
+ Button* btn_event_dec;
+
+ Button* btn_sit;
+ Button* btn_pkg;
+ Button* btn_map;
+
+ EditBox* txt_name;
+ ComboBox* cmb_type;
+ ComboBox* cmb_system;
+ ComboBox* cmb_region;
+
+ EditBox* txt_description;
+ EditBox* txt_situation;
+ EditBox* txt_objective;
+
+ ListBox* lst_elem;
+ ListBox* lst_event;
+
+ Mission* mission;
+ MissionInfo* mission_info;
+
+ int current_tab;
+ bool exit_latch;
+};
+
+#endif MsnEditDlg_h
+
diff --git a/Stars45/MsnEditNavDlg.cpp b/Stars45/MsnEditNavDlg.cpp
new file mode 100644
index 0000000..88cf103
--- /dev/null
+++ b/Stars45/MsnEditNavDlg.cpp
@@ -0,0 +1,300 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnEditNavDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Briefing Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "MsnEditNavDlg.h"
+#include "MsnEditDlg.h"
+#include "MenuScreen.h"
+#include "Campaign.h"
+#include "Galaxy.h"
+#include "Instruction.h"
+#include "Mission.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "StarSystem.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+DEF_MAP_CLIENT(MsnEditNavDlg, OnCommit);
+DEF_MAP_CLIENT(MsnEditNavDlg, OnCancel);
+DEF_MAP_CLIENT(MsnEditNavDlg, OnTabButton);
+DEF_MAP_CLIENT(MsnEditNavDlg, OnSystemSelect);
+
+// +--------------------------------------------------------------------+
+
+MsnEditNavDlg::MsnEditNavDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : NavDlg(s, def, mgr), menu_screen(mgr), mission_info(0),
+ btn_accept(0), btn_cancel(0), btn_sit(0), btn_pkg(0), btn_map(0)
+{
+ RegisterControls();
+}
+
+MsnEditNavDlg::~MsnEditNavDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditNavDlg::RegisterControls()
+{
+ btn_accept = (Button*) FindControl( 1);
+ btn_cancel = (Button*) FindControl( 2);
+ btn_sit = (Button*) FindControl(301);
+ btn_pkg = (Button*) FindControl(302);
+ btn_map = (Button*) FindControl(303);
+
+ txt_name = (EditBox*) FindControl(201);
+ cmb_type = (ComboBox*) FindControl(202);
+ cmb_system = (ComboBox*) FindControl(203);
+ cmb_region = (ComboBox*) FindControl(204);
+
+ if (btn_accept)
+ REGISTER_CLIENT(EID_CLICK, btn_accept, MsnEditNavDlg, OnCommit);
+
+ if (btn_cancel)
+ REGISTER_CLIENT(EID_CLICK, btn_cancel, MsnEditNavDlg, OnCancel);
+
+ if (btn_sit)
+ REGISTER_CLIENT(EID_CLICK, btn_sit, MsnEditNavDlg, OnTabButton);
+
+ if (btn_pkg)
+ REGISTER_CLIENT(EID_CLICK, btn_pkg, MsnEditNavDlg, OnTabButton);
+
+ if (btn_map)
+ REGISTER_CLIENT(EID_CLICK, btn_map, MsnEditNavDlg, OnTabButton);
+
+ if (cmb_system)
+ REGISTER_CLIENT(EID_SELECT, cmb_system, MsnEditNavDlg, OnSystemSelect);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditNavDlg::Show()
+{
+ bool need_tab_update = !shown;
+
+ NavDlg::Show();
+
+ if (txt_name && cmb_type) {
+ txt_name->SetText("");
+
+ if (cmb_system) {
+ cmb_system->ClearItems();
+
+ Galaxy* galaxy = Galaxy::GetInstance();
+ ListIter<StarSystem> iter = galaxy->GetSystemList();
+ while (++iter) {
+ cmb_system->AddItem(iter->Name());
+ }
+ }
+
+ if (mission) {
+ int i;
+
+ txt_name->SetText(mission->Name());
+ cmb_type->SetSelection(mission->Type());
+
+ StarSystem* sys = mission->GetStarSystem();
+ if (sys && cmb_system && cmb_region) {
+ for (i = 0; i < cmb_system->NumItems(); i++) {
+ if (!strcmp(cmb_system->GetItem(i), sys->Name())) {
+ cmb_system->SetSelection(i);
+ break;
+ }
+ }
+
+ cmb_region->ClearItems();
+ int sel_rgn = 0;
+
+ List<OrbitalRegion> regions;
+ regions.append(sys->AllRegions());
+ regions.sort();
+
+ i = 0;
+ ListIter<OrbitalRegion> iter = regions;
+ while (++iter) {
+ OrbitalRegion* region = iter.value();
+ cmb_region->AddItem(region->Name());
+
+ if (!strcmp(mission->GetRegion(), region->Name())) {
+ sel_rgn = i;
+ }
+
+ i++;
+ }
+
+ cmb_region->SetSelection(sel_rgn);
+ }
+ }
+ }
+
+ if (need_tab_update) {
+ ShowTab(2);
+ }
+
+ exit_latch = true;
+}
+
+void
+MsnEditNavDlg::SetMissionInfo(MissionInfo* m)
+{
+ mission_info = m;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditNavDlg::ScrapeForm()
+{
+ if (mission) {
+ if (txt_name) {
+ mission->SetName(txt_name->GetText());
+ }
+
+ if (cmb_type) {
+ mission->SetType(cmb_type->GetSelectedIndex());
+
+ if (mission_info)
+ mission_info->type = cmb_type->GetSelectedIndex();
+ }
+
+ Galaxy* galaxy = Galaxy::GetInstance();
+ StarSystem* system = 0;
+
+ if (galaxy)
+ system = galaxy->GetSystem(cmb_system->GetSelectedItem());
+
+ if (cmb_system && system) {
+ mission->ClearSystemList();
+ mission->SetStarSystem(system);
+
+ if (mission_info)
+ mission_info->system = system->Name();
+ }
+
+ if (cmb_region) {
+ mission->SetRegion(cmb_region->GetSelectedItem());
+
+ if (mission_info)
+ mission_info->region = cmb_region->GetSelectedItem();
+ }
+
+ SetSystem(system);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditNavDlg::ShowTab(int tab)
+{
+ if (tab < 0 || tab > 2)
+ tab = 0;
+
+ if (btn_sit) btn_sit->SetButtonState(tab == 0 ? 1 : 0);
+ if (btn_pkg) btn_pkg->SetButtonState(tab == 1 ? 1 : 0);
+ if (btn_map) btn_map->SetButtonState(tab == 2 ? 1 : 0);
+
+ if (tab != 2) {
+ MsnEditDlg* msnEditDlg = menu_screen->GetMsnEditDlg();
+
+ if (msnEditDlg)
+ msnEditDlg->ShowTab(tab);
+
+ menu_screen->ShowMsnEditDlg();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditNavDlg::OnTabButton(AWEvent* event)
+{
+ if (!event) return;
+
+ if (event->window == btn_sit)
+ ShowTab(0);
+
+ else if (event->window == btn_pkg)
+ ShowTab(1);
+
+ else if (event->window == btn_map)
+ ShowTab(2);
+}
+
+void
+MsnEditNavDlg::OnSystemSelect(AWEvent* event)
+{
+ StarSystem* sys = 0;
+
+ if (cmb_system) {
+ const char* name = cmb_system->GetSelectedItem();
+
+ Galaxy* galaxy = Galaxy::GetInstance();
+ ListIter<StarSystem> iter = galaxy->GetSystemList();
+ while (++iter) {
+ StarSystem* s = iter.value();
+
+ if (!strcmp(s->Name(), name)) {
+ sys = s;
+ break;
+ }
+ }
+ }
+
+ if (sys && cmb_region) {
+ cmb_region->ClearItems();
+
+ List<OrbitalRegion> regions;
+ regions.append(sys->AllRegions());
+ regions.sort();
+
+ ListIter<OrbitalRegion> iter = regions;
+ while (++iter) {
+ OrbitalRegion* region = iter.value();
+ cmb_region->AddItem(region->Name());
+ }
+ }
+
+ ScrapeForm();
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEditNavDlg::OnCommit(AWEvent* event)
+{
+ if (mission) {
+ ScrapeForm();
+
+ if (mission_info)
+ mission_info->name = mission->Name();
+
+ mission->Save();
+ }
+
+ menu_screen->ShowMsnSelectDlg();
+}
+
+void
+MsnEditNavDlg::OnCancel(AWEvent* event)
+{
+ if (mission)
+ mission->Load();
+
+ menu_screen->ShowMsnSelectDlg();
+}
diff --git a/Stars45/MsnEditNavDlg.h b/Stars45/MsnEditNavDlg.h
new file mode 100644
index 0000000..d00d3ce
--- /dev/null
+++ b/Stars45/MsnEditNavDlg.h
@@ -0,0 +1,78 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnEditNavDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Briefing Dialog Active Window class
+*/
+
+#ifndef MsnEditNavDlg_h
+#define MsnEditNavDlg_h
+
+#include "Types.h"
+#include "NavDlg.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "EditBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+class Campaign;
+class Mission;
+class MissionInfo;
+
+// +--------------------------------------------------------------------+
+
+class MsnEditNavDlg : public NavDlg
+{
+public:
+ friend class MsnEditDlg;
+
+ MsnEditNavDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~MsnEditNavDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void SetMissionInfo(MissionInfo* m);
+ virtual void ScrapeForm();
+
+ // Operations:
+ virtual void OnCommit(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+ virtual void OnTabButton(AWEvent* event);
+ virtual void OnSystemSelect(AWEvent* event);
+
+protected:
+ virtual void ShowTab(int tab);
+
+ MenuScreen* menu_screen;
+
+ Button* btn_accept;
+ Button* btn_cancel;
+
+ Button* btn_sit;
+ Button* btn_pkg;
+ Button* btn_map;
+
+ EditBox* txt_name;
+ ComboBox* cmb_type;
+ ComboBox* cmb_system;
+ ComboBox* cmb_region;
+ MissionInfo* mission_info;
+
+ bool exit_latch;
+};
+
+#endif MsnEditNavDlg_h
+
diff --git a/Stars45/MsnElemDlg.cpp b/Stars45/MsnElemDlg.cpp
new file mode 100644
index 0000000..5d44bbf
--- /dev/null
+++ b/Stars45/MsnElemDlg.cpp
@@ -0,0 +1,805 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnElemDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mod Config Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "MsnElemDlg.h"
+#include "MsnEditDlg.h"
+#include "MenuScreen.h"
+#include "Campaign.h"
+#include "Mission.h"
+#include "Instruction.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "StarSystem.h"
+#include "Galaxy.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "EditBox.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ParseUtil.h"
+#include "Skin.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(MsnElemDlg, OnAccept);
+DEF_MAP_CLIENT(MsnElemDlg, OnCancel);
+DEF_MAP_CLIENT(MsnElemDlg, OnClassSelect);
+DEF_MAP_CLIENT(MsnElemDlg, OnDesignSelect);
+DEF_MAP_CLIENT(MsnElemDlg, OnObjectiveSelect);
+DEF_MAP_CLIENT(MsnElemDlg, OnIFFChange);
+
+// +--------------------------------------------------------------------+
+
+MsnElemDlg::MsnElemDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ btn_accept(0), btn_cancel(0), edt_name(0), cmb_class(0), cmb_design(0),
+ edt_size(0), edt_iff(0), cmb_role(0), cmb_region(0), cmb_skin(0),
+ edt_loc_x(0), edt_loc_y(0), edt_loc_z(0), cmb_heading(0), edt_hold_time(0),
+ btn_player(0), btn_playable(0), btn_alert(0), btn_command_ai(0),
+ edt_respawns(0), cmb_commander(0), cmb_carrier(0), cmb_squadron(0),
+ cmb_intel(0), cmb_loadout(0), cmb_objective(0), cmb_target(0),
+ mission(0), elem(0), exit_latch(true)
+{
+ Init(def);
+}
+
+MsnElemDlg::~MsnElemDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnElemDlg::RegisterControls()
+{
+ btn_accept = (Button*) FindControl( 1);
+ if (btn_accept)
+ REGISTER_CLIENT(EID_CLICK, btn_accept, MsnElemDlg, OnAccept);
+
+ btn_cancel = (Button*) FindControl( 2);
+ if (btn_accept)
+ REGISTER_CLIENT(EID_CLICK, btn_cancel, MsnElemDlg, OnCancel);
+
+ edt_name = (EditBox*) FindControl(201);
+ cmb_class = (ComboBox*) FindControl(202);
+ cmb_design = (ComboBox*) FindControl(203);
+ cmb_skin = (ComboBox*) FindControl(213);
+ edt_size = (EditBox*) FindControl(204);
+ edt_iff = (EditBox*) FindControl(205);
+ cmb_role = (ComboBox*) FindControl(206);
+ cmb_region = (ComboBox*) FindControl(207);
+ edt_loc_x = (EditBox*) FindControl(208);
+ edt_loc_y = (EditBox*) FindControl(209);
+ edt_loc_z = (EditBox*) FindControl(210);
+ cmb_heading = (ComboBox*) FindControl(211);
+ edt_hold_time = (EditBox*) FindControl(212);
+
+ btn_player = (Button*) FindControl(221);
+ btn_alert = (Button*) FindControl(222);
+ btn_playable = (Button*) FindControl(223);
+ btn_command_ai = (Button*) FindControl(224);
+ edt_respawns = (EditBox*) FindControl(225);
+ cmb_commander = (ComboBox*) FindControl(226);
+ cmb_carrier = (ComboBox*) FindControl(227);
+ cmb_squadron = (ComboBox*) FindControl(228);
+ cmb_intel = (ComboBox*) FindControl(229);
+ cmb_loadout = (ComboBox*) FindControl(230);
+ cmb_objective = (ComboBox*) FindControl(231);
+ cmb_target = (ComboBox*) FindControl(232);
+
+ if (cmb_class)
+ REGISTER_CLIENT(EID_SELECT, cmb_class, MsnElemDlg, OnClassSelect);
+
+ if (cmb_design)
+ REGISTER_CLIENT(EID_SELECT, cmb_design, MsnElemDlg, OnDesignSelect);
+
+ if (cmb_objective)
+ REGISTER_CLIENT(EID_SELECT, cmb_objective, MsnElemDlg, OnObjectiveSelect);
+
+ if (edt_iff)
+ REGISTER_CLIENT(EID_KILL_FOCUS, edt_iff, MsnElemDlg, OnIFFChange);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnElemDlg::Show()
+{
+ FormWindow::Show();
+
+ if (!elem) return;
+
+ int current_class = 0;
+
+ if (cmb_class) {
+ cmb_class->ClearItems();
+
+ cmb_class->AddItem(Ship::ClassName(Ship::DRONE));
+ cmb_class->AddItem(Ship::ClassName(Ship::FIGHTER));
+ cmb_class->AddItem(Ship::ClassName(Ship::ATTACK));
+
+ cmb_class->AddItem(Ship::ClassName(Ship::LCA));
+ cmb_class->AddItem(Ship::ClassName(Ship::COURIER));
+ cmb_class->AddItem(Ship::ClassName(Ship::CARGO));
+ cmb_class->AddItem(Ship::ClassName(Ship::CORVETTE));
+ cmb_class->AddItem(Ship::ClassName(Ship::FREIGHTER));
+ cmb_class->AddItem(Ship::ClassName(Ship::FRIGATE));
+ cmb_class->AddItem(Ship::ClassName(Ship::DESTROYER));
+ cmb_class->AddItem(Ship::ClassName(Ship::CRUISER));
+ cmb_class->AddItem(Ship::ClassName(Ship::BATTLESHIP));
+ cmb_class->AddItem(Ship::ClassName(Ship::CARRIER));
+ cmb_class->AddItem(Ship::ClassName(Ship::DREADNAUGHT));
+ cmb_class->AddItem(Ship::ClassName(Ship::STATION));
+ cmb_class->AddItem(Ship::ClassName(Ship::FARCASTER));
+
+ cmb_class->AddItem(Ship::ClassName(Ship::MINE));
+ cmb_class->AddItem(Ship::ClassName(Ship::COMSAT));
+ cmb_class->AddItem(Ship::ClassName(Ship::DEFSAT));
+ cmb_class->AddItem(Ship::ClassName(Ship::SWACS));
+
+ cmb_class->AddItem(Ship::ClassName(Ship::BUILDING));
+ cmb_class->AddItem(Ship::ClassName(Ship::FACTORY));
+ cmb_class->AddItem(Ship::ClassName(Ship::SAM));
+ cmb_class->AddItem(Ship::ClassName(Ship::EWR));
+ cmb_class->AddItem(Ship::ClassName(Ship::C3I));
+ cmb_class->AddItem(Ship::ClassName(Ship::STARBASE));
+
+ const ShipDesign* design = elem->GetDesign();
+
+ for (int i = 0; i < cmb_class->NumItems(); i++) {
+ const char* cname = cmb_class->GetItem(i);
+ int classid = Ship::ClassForName(cname);
+
+ if (design && classid == design->type) {
+ cmb_class->SetSelection(i);
+ current_class = classid;
+ break;
+ }
+ }
+ }
+
+ if (cmb_design) {
+ OnClassSelect(0);
+ OnDesignSelect(0);
+ }
+
+ if (cmb_role) {
+ cmb_role->ClearItems();
+
+ for (int i = Mission::PATROL; i <= Mission::OTHER; i++) {
+ cmb_role->AddItem(Mission::RoleName(i));
+
+ if (i == 0)
+ cmb_role->SetSelection(0);
+
+ else if (elem->MissionRole() == i)
+ cmb_role->SetSelection(cmb_role->NumItems()-1);
+ }
+ }
+
+ if (cmb_region) {
+ cmb_region->ClearItems();
+
+ if (mission) {
+ StarSystem* sys = mission->GetStarSystem();
+ if (sys) {
+ List<OrbitalRegion> regions;
+ regions.append(sys->AllRegions());
+ regions.sort();
+
+ ListIter<OrbitalRegion> iter = regions;
+ while (++iter) {
+ OrbitalRegion* region = iter.value();
+ cmb_region->AddItem(region->Name());
+
+ if (!strcmp(elem->Region(), region->Name()))
+ cmb_region->SetSelection(cmb_region->NumItems()-1);
+ }
+ }
+ }
+ }
+
+ char buf[64];
+
+ if (edt_name) edt_name->SetText(elem->Name());
+
+ sprintf(buf, "%d", elem->Count());
+ if (edt_size) edt_size->SetText(buf);
+
+ sprintf(buf, "%d", elem->GetIFF());
+ if (edt_iff) edt_iff->SetText(buf);
+
+ sprintf(buf, "%d", (int) elem->Location().x / 1000);
+ if (edt_loc_x) edt_loc_x->SetText(buf);
+
+ sprintf(buf, "%d", (int) elem->Location().y / 1000);
+ if (edt_loc_y) edt_loc_y->SetText(buf);
+
+ sprintf(buf, "%d", (int) elem->Location().z / 1000);
+ if (edt_loc_z) edt_loc_z->SetText(buf);
+
+ sprintf(buf, "%d", elem->RespawnCount());
+ if (edt_respawns) edt_respawns->SetText(buf);
+
+ sprintf(buf, "%d", elem->HoldTime());
+ if (edt_hold_time) edt_hold_time->SetText(buf);
+
+ if (btn_player) btn_player->SetButtonState(elem->Player() > 0 ? 1 : 0);
+ if (btn_playable) btn_playable->SetButtonState(elem->IsPlayable() ? 1 : 0);
+ if (btn_alert) btn_alert->SetButtonState(elem->IsAlert() ? 1 : 0);
+ if (btn_command_ai) btn_command_ai->SetButtonState(elem->CommandAI());
+
+ UpdateTeamInfo();
+
+ if (cmb_intel) {
+ cmb_intel->ClearItems();
+
+ for (int i = Intel::RESERVE; i < Intel::TRACKED; i++) {
+ cmb_intel->AddItem(Intel::NameFromIntel(i));
+
+ if (i == 0)
+ cmb_intel->SetSelection(0);
+
+ else if (elem->IntelLevel() == i)
+ cmb_intel->SetSelection(cmb_intel->NumItems()-1);
+ }
+ }
+
+ Instruction* instr = 0;
+ if (elem->Objectives().size() > 0)
+ instr = elem->Objectives().at(0);
+
+ if (cmb_objective) {
+ cmb_objective->ClearItems();
+ cmb_objective->AddItem("");
+ cmb_objective->SetSelection(0);
+
+ for (int i = 0; i < Instruction::NUM_ACTIONS; i++) {
+ cmb_objective->AddItem(Instruction::ActionName(i));
+
+ if (instr && instr->Action() == i)
+ cmb_objective->SetSelection(i+1);
+ }
+ }
+
+ if (cmb_target) {
+ OnObjectiveSelect(0);
+ }
+
+ if (cmb_heading) {
+ double heading = elem->Heading();
+
+ while (heading > 2*PI)
+ heading -= 2*PI;
+
+ while (heading < 0)
+ heading += 2*PI;
+
+ if (heading >= PI/4 && heading < 3*PI/4)
+ cmb_heading->SetSelection(1);
+
+ else if (heading >= 3*PI/4 && heading < 5*PI/4)
+ cmb_heading->SetSelection(2);
+
+ else if (heading >= 5*PI/4 && heading < 7*PI/4)
+ cmb_heading->SetSelection(3);
+
+ else
+ cmb_heading->SetSelection(0);
+ }
+
+ exit_latch = true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnElemDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ if (!exit_latch && btn_accept && btn_accept->IsEnabled())
+ OnAccept(0);
+ }
+
+ else if (Keyboard::KeyDown(VK_ESCAPE)) {
+ if (!exit_latch)
+ OnCancel(0);
+ }
+
+ else {
+ exit_latch = false;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnElemDlg::SetMission(Mission* m)
+{
+ mission = m;
+}
+
+void
+MsnElemDlg::SetMissionElement(MissionElement* e)
+{
+ elem = e;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnElemDlg::UpdateTeamInfo()
+{
+ if (cmb_commander) {
+ cmb_commander->ClearItems();
+ cmb_commander->AddItem("");
+ cmb_commander->SetSelection(0);
+
+ if (mission && elem) {
+ ListIter<MissionElement> iter = mission->GetElements();
+ while (++iter) {
+ MissionElement* e = iter.value();
+
+ if (CanCommand(e, elem)) {
+ cmb_commander->AddItem(e->Name());
+
+ if (elem->Commander() == e->Name())
+ cmb_commander->SetSelection(cmb_commander->NumItems()-1);
+ }
+ }
+ }
+ }
+
+ if (cmb_squadron) {
+ cmb_squadron->ClearItems();
+ cmb_squadron->AddItem("");
+ cmb_squadron->SetSelection(0);
+
+ if (mission && elem) {
+ ListIter<MissionElement> iter = mission->GetElements();
+ while (++iter) {
+ MissionElement* e = iter.value();
+
+ if (e->GetIFF() == elem->GetIFF() && e != elem && e->IsSquadron()) {
+ cmb_squadron->AddItem(e->Name());
+
+ if (elem->Squadron() == e->Name())
+ cmb_squadron->SetSelection(cmb_squadron->NumItems()-1);
+ }
+ }
+ }
+ }
+
+ if (cmb_carrier) {
+ cmb_carrier->ClearItems();
+ cmb_carrier->AddItem("");
+ cmb_carrier->SetSelection(0);
+
+ if (mission && elem) {
+ ListIter<MissionElement> iter = mission->GetElements();
+ while (++iter) {
+ MissionElement* e = iter.value();
+
+ if (e->GetIFF() == elem->GetIFF() && e != elem && e->GetDesign() && e->GetDesign()->flight_decks.size()) {
+ cmb_carrier->AddItem(e->Name());
+
+ if (elem->Carrier() == e->Name())
+ cmb_carrier->SetSelection(cmb_carrier->NumItems()-1);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnElemDlg::OnClassSelect(AWEvent* event)
+{
+ if (!cmb_class || !cmb_design) return;
+
+ const char* cname = cmb_class->GetSelectedItem();
+ int classid = Ship::ClassForName(cname);
+
+ cmb_design->ClearItems();
+
+ List<Text> designs;
+ ShipDesign::GetDesignList(classid, designs);
+
+ if (designs.size() > 0) {
+ const ShipDesign* design = elem->GetDesign();
+ bool found = false;
+
+ for (int i = 0; i < designs.size(); i++) {
+ const char* dsn = designs[i]->data();
+ cmb_design->AddItem(dsn);
+
+ if (design && !stricmp(dsn, design->name)) {
+ cmb_design->SetSelection(i);
+ found = true;
+ }
+ }
+
+ if (!found)
+ cmb_design->SetSelection(0);
+ }
+ else {
+ cmb_design->AddItem("");
+ cmb_design->SetSelection(0);
+ }
+
+ OnDesignSelect(0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnElemDlg::OnDesignSelect(AWEvent* event)
+{
+ if (!cmb_design) return;
+
+ ShipDesign* design = 0;
+
+ const char* dname = cmb_design->GetSelectedItem();
+ int load_index = 0;
+
+ if (dname)
+ design = ShipDesign::Get(dname);
+
+ if (cmb_loadout) {
+ cmb_loadout->ClearItems();
+
+ if (design) {
+ MissionLoad* mload = 0;
+
+ if (elem && elem->Loadouts().size() > 0)
+ mload = elem->Loadouts().at(0);
+
+ const List<ShipLoad>& loadouts = design->loadouts;
+
+ if (loadouts.size() > 0) {
+ for (int i = 0; i < loadouts.size(); i++) {
+ const ShipLoad* load = loadouts[i];
+ if (load->name[0]) {
+ cmb_loadout->AddItem(load->name);
+
+ if (mload && mload->GetName() == load->name) {
+ load_index = cmb_loadout->NumItems()-1;
+ }
+ }
+ }
+ }
+ }
+
+ if (cmb_loadout->NumItems() < 1)
+ cmb_loadout->AddItem("");
+
+ cmb_loadout->SetSelection(load_index);
+ }
+
+ if (cmb_skin) {
+ cmb_skin->ClearItems();
+
+ if (design) {
+ cmb_skin->AddItem(Game::GetText("MsnDlg.default"));
+ cmb_skin->SetSelection(0);
+
+ ListIter<Skin> iter = design->skins;
+
+ while (++iter) {
+ Skin* s = iter.value();
+ cmb_skin->AddItem(s->Name());
+
+ if (elem && elem->GetSkin() && !strcmp(s->Name(), elem->GetSkin()->Name())) {
+ cmb_skin->SetSelection(cmb_skin->NumItems()-1);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnElemDlg::OnObjectiveSelect(AWEvent* event)
+{
+ if (!cmb_objective || !cmb_target) return;
+
+ Instruction* instr = 0;
+ if (elem->Objectives().size() > 0)
+ instr = elem->Objectives().at(0);
+
+ int objid = cmb_objective->GetSelectedIndex() - 1;
+
+ cmb_target->ClearItems();
+ cmb_target->AddItem("");
+
+ if (mission) {
+ ListIter<MissionElement> iter = mission->GetElements();
+ while (++iter) {
+ MissionElement* e = iter.value();
+
+ if (e != elem) {
+ bool add = false;
+
+ if (objid < Instruction::PATROL)
+ add = e->GetIFF() == 0 || e->GetIFF() == elem->GetIFF();
+ else
+ add = e->GetIFF() != elem->GetIFF();
+
+ if (add) {
+ cmb_target->AddItem(e->Name());
+
+ if (instr && !stricmp(instr->TargetName(), e->Name()))
+ cmb_target->SetSelection(cmb_target->NumItems()-1);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnElemDlg::OnIFFChange(AWEvent* event)
+{
+ if (edt_iff && elem) {
+ int elem_iff = 0;
+ sscanf(edt_iff->GetText().data(), "%d", &elem_iff);
+
+ if (elem->GetIFF() == elem_iff)
+ return;
+
+ elem->SetIFF(elem_iff);
+ }
+
+ UpdateTeamInfo();
+
+ if (cmb_target)
+ OnObjectiveSelect(0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnElemDlg::OnAccept(AWEvent* event)
+{
+ if (mission && elem) {
+ char buf[64];
+ int val;
+
+ if (edt_name) {
+ elem->SetName(edt_name->GetText());
+ }
+
+ if (edt_size) {
+ strcpy(buf, edt_size->GetText());
+
+ if (isdigit(*buf))
+ sscanf(buf, "%d", &val);
+ else
+ val = 1;
+
+ elem->SetCount(val);
+ }
+
+ if (edt_iff) {
+ strcpy(buf, edt_iff->GetText());
+
+ if (isdigit(*buf))
+ sscanf(buf, "%d", &val);
+ else
+ val = 1;
+
+ elem->SetIFF(val);
+ }
+
+ if (edt_loc_x && edt_loc_y && edt_loc_z) {
+ Point loc;
+
+ strcpy(buf, edt_loc_x->GetText());
+
+ if (isdigit(*buf) || *buf == '-')
+ sscanf(buf, "%d", &val);
+ else
+ val = 0;
+
+ loc.x = val * 1000;
+
+ strcpy(buf, edt_loc_y->GetText());
+
+ if (isdigit(*buf) || *buf == '-')
+ sscanf(buf, "%d", &val);
+ else
+ val = 0;
+
+ loc.y = val * 1000;
+
+ strcpy(buf, edt_loc_z->GetText());
+
+ if (isdigit(*buf) || *buf == '-')
+ sscanf(buf, "%d", &val);
+ else
+ val = 0;
+
+ loc.z = val * 1000;
+
+ elem->SetLocation(loc);
+ }
+
+ if (edt_respawns) {
+ strcpy(buf, edt_respawns->GetText());
+
+ if (isdigit(*buf))
+ sscanf(buf, "%d", &val);
+ else
+ val = 0;
+
+ elem->SetRespawnCount(val);
+ }
+
+ if (edt_hold_time) {
+ strcpy(buf, edt_hold_time->GetText());
+
+ if (isdigit(*buf))
+ sscanf(buf, "%d", &val);
+ else
+ val = 0;
+
+ elem->SetHoldTime(val);
+ }
+
+ if (btn_player) {
+ if (btn_player->GetButtonState() > 0) {
+ mission->SetPlayer(elem);
+ }
+ else {
+ elem->SetPlayer(0);
+ }
+ }
+
+ if (btn_playable)
+ elem->SetPlayable(btn_playable->GetButtonState() ? true : false);
+
+ if (btn_alert)
+ elem->SetAlert(btn_alert->GetButtonState() ? true : false);
+
+ if (btn_command_ai)
+ elem->SetCommandAI(btn_command_ai->GetButtonState() ? 1 : 0);
+
+ if (cmb_design) {
+ ShipDesign* d = ShipDesign::Get(cmb_design->GetSelectedItem());
+
+ if (d) {
+ elem->SetDesign(d);
+
+ if (cmb_skin) {
+ const char* skin_name = cmb_skin->GetSelectedItem();
+
+ if (strcmp(skin_name, Game::GetText("MsnDlg.default").data())) {
+ elem->SetSkin(d->FindSkin(skin_name));
+ }
+ else {
+ elem->SetSkin(0);
+ }
+ }
+ }
+ }
+
+ if (cmb_role) {
+ elem->SetMissionRole(cmb_role->GetSelectedIndex());
+ }
+
+ if (cmb_region) {
+ elem->SetRegion(cmb_region->GetSelectedItem());
+
+ if (elem->Player() > 0) {
+ mission->SetRegion(cmb_region->GetSelectedItem());
+ }
+ }
+
+ if (cmb_heading) {
+ switch (cmb_heading->GetSelectedIndex()) {
+ default:
+ case 0: elem->SetHeading(0); break;
+ case 1: elem->SetHeading(PI/2); break;
+ case 2: elem->SetHeading(PI); break;
+ case 3: elem->SetHeading(3*PI/2); break;
+ }
+ }
+
+ if (cmb_commander) {
+ elem->SetCommander(cmb_commander->GetSelectedItem());
+ }
+
+ if (cmb_squadron) {
+ elem->SetSquadron(cmb_squadron->GetSelectedItem());
+ }
+
+ if (cmb_carrier) {
+ elem->SetCarrier(cmb_carrier->GetSelectedItem());
+ }
+
+ if (cmb_intel) {
+ elem->SetIntelLevel(Intel::IntelFromName(cmb_intel->GetSelectedItem()));
+ }
+
+ if (cmb_loadout && cmb_loadout->NumItems()) {
+ elem->Loadouts().destroy();
+
+ const char* loadname = cmb_loadout->GetSelectedItem();
+ if (loadname && *loadname) {
+ MissionLoad* mload = new(__FILE__,__LINE__) MissionLoad(-1, cmb_loadout->GetSelectedItem());
+ elem->Loadouts().append(mload);
+ }
+ }
+
+ if (cmb_objective && cmb_target) {
+ List<Instruction>& objectives = elem->Objectives();
+ objectives.destroy();
+
+ int action = cmb_objective->GetSelectedIndex() - 1;
+ const char* target = cmb_target->GetSelectedItem();
+
+ if (action >= Instruction::VECTOR) {
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(action, target);
+ objectives.append(obj);
+ }
+ }
+
+ if (elem->Player()) {
+ mission->SetTeam(elem->GetIFF());
+ }
+ }
+
+ manager->ShowMsnEditDlg();
+}
+
+void
+MsnElemDlg::OnCancel(AWEvent* event)
+{
+ manager->ShowMsnEditDlg();
+}
+
+bool
+MsnElemDlg::CanCommand(const MissionElement* commander,
+ const MissionElement* subordinate) const
+{
+ if (mission && commander && subordinate && commander != subordinate) {
+ if (commander->GetIFF() != subordinate->GetIFF())
+ return false;
+
+ if (commander->IsSquadron())
+ return false;
+
+ if (commander->Commander().length() == 0)
+ return true;
+
+ if (subordinate->Name() == commander->Commander())
+ return false;
+
+ MissionElement* elem = mission->FindElement(commander->Commander());
+
+ if (elem)
+ return CanCommand(elem, subordinate);
+ }
+
+ return false;
+} \ No newline at end of file
diff --git a/Stars45/MsnElemDlg.h b/Stars45/MsnElemDlg.h
new file mode 100644
index 0000000..fb7be33
--- /dev/null
+++ b/Stars45/MsnElemDlg.h
@@ -0,0 +1,99 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnElemDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Editor Element Dialog Active Window class
+*/
+
+#ifndef MsnElemDlg_h
+#define MsnElemDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+class MsnEditDlg;
+class Mission;
+class MissionElement;
+
+// +--------------------------------------------------------------------+
+
+class MsnElemDlg : public FormWindow
+{
+public:
+ MsnElemDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~MsnElemDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void SetMission(Mission* elem);
+ virtual void SetMissionElement(MissionElement* elem);
+ virtual void OnAccept(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+ virtual void OnClassSelect(AWEvent* event);
+ virtual void OnDesignSelect(AWEvent* event);
+ virtual void OnObjectiveSelect(AWEvent* event);
+ virtual void OnIFFChange(AWEvent* event);
+
+ virtual void UpdateTeamInfo();
+ virtual bool CanCommand(const MissionElement* commander,
+ const MissionElement* subordinate) const;
+
+protected:
+ MenuScreen* manager;
+
+ EditBox* edt_name;
+ ComboBox* cmb_class;
+ ComboBox* cmb_design;
+ ComboBox* cmb_skin;
+ EditBox* edt_size;
+ EditBox* edt_iff;
+ ComboBox* cmb_role;
+ ComboBox* cmb_region;
+ EditBox* edt_loc_x;
+ EditBox* edt_loc_y;
+ EditBox* edt_loc_z;
+ ComboBox* cmb_heading;
+ EditBox* edt_hold_time;
+
+ Button* btn_player;
+ Button* btn_playable;
+ Button* btn_alert;
+ Button* btn_command_ai;
+ EditBox* edt_respawns;
+ ComboBox* cmb_carrier;
+ ComboBox* cmb_squadron;
+ ComboBox* cmb_commander;
+ ComboBox* cmb_intel;
+ ComboBox* cmb_loadout;
+ ComboBox* cmb_objective;
+ ComboBox* cmb_target;
+
+ Button* btn_accept;
+ Button* btn_cancel;
+
+ Mission* mission;
+ MissionElement* elem;
+ bool exit_latch;
+};
+
+#endif MsnElemDlg_h
+
diff --git a/Stars45/MsnEventDlg.cpp b/Stars45/MsnEventDlg.cpp
new file mode 100644
index 0000000..c1b779b
--- /dev/null
+++ b/Stars45/MsnEventDlg.cpp
@@ -0,0 +1,416 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnEventDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mod Config Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "MsnEventDlg.h"
+#include "MsnEditDlg.h"
+#include "MenuScreen.h"
+#include "Campaign.h"
+#include "Mission.h"
+#include "MissionEvent.h"
+#include "Instruction.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "StarSystem.h"
+#include "Galaxy.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "EditBox.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ParseUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(MsnEventDlg, OnEventSelect);
+DEF_MAP_CLIENT(MsnEventDlg, OnAccept);
+DEF_MAP_CLIENT(MsnEventDlg, OnCancel);
+
+// +--------------------------------------------------------------------+
+
+MsnEventDlg::MsnEventDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ btn_accept(0), btn_cancel(0), mission(0), event(0)
+{
+ Init(def);
+}
+
+MsnEventDlg::~MsnEventDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEventDlg::RegisterControls()
+{
+ btn_accept = (Button*) FindControl( 1);
+ if (btn_accept)
+ REGISTER_CLIENT(EID_CLICK, btn_accept, MsnEventDlg, OnAccept);
+
+ btn_cancel = (Button*) FindControl( 2);
+ if (btn_accept)
+ REGISTER_CLIENT(EID_CLICK, btn_cancel, MsnEventDlg, OnCancel);
+
+ lbl_id = FindControl(201);
+ edt_time = (EditBox*) FindControl(202);
+ edt_delay = (EditBox*) FindControl(203);
+ cmb_event = (ComboBox*) FindControl(204);
+ cmb_event_ship = (ComboBox*) FindControl(205);
+ cmb_event_source = (ComboBox*) FindControl(206);
+ cmb_event_target = (ComboBox*) FindControl(207);
+ edt_event_param = (EditBox*) FindControl(208);
+ edt_event_chance = (EditBox*) FindControl(220);
+ edt_event_sound = (EditBox*) FindControl(209);
+ edt_event_message = (EditBox*) FindControl(210);
+
+ cmb_trigger = (ComboBox*) FindControl(221);
+ cmb_trigger_ship = (ComboBox*) FindControl(222);
+ cmb_trigger_target = (ComboBox*) FindControl(223);
+ edt_trigger_param = (EditBox*) FindControl(224);
+
+ if (cmb_event)
+ REGISTER_CLIENT(EID_SELECT, cmb_event, MsnEventDlg, OnEventSelect);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEventDlg::Show()
+{
+ FormWindow::Show();
+
+ if (!event) return;
+
+ FillShipList(cmb_event_ship, event->EventShip());
+ FillShipList(cmb_event_source, event->EventSource());
+
+ if (event->Event() == MissionEvent::JUMP)
+ FillRgnList(cmb_event_target, event->EventTarget());
+ else
+ FillShipList(cmb_event_target,event->EventTarget());
+
+ FillShipList(cmb_trigger_ship, event->TriggerShip());
+ FillShipList(cmb_trigger_target, event->TriggerTarget());
+
+ char buf[64];
+
+ sprintf(buf, "%d", event->EventID());
+ if (lbl_id) lbl_id->SetText(buf);
+
+ if (edt_time) {
+ sprintf(buf, "%.1f", event->Time());
+ edt_time->SetText(buf);
+ }
+
+ if (edt_delay) {
+ sprintf(buf, "%.1f", event->Delay());
+ edt_delay->SetText(buf);
+ }
+
+ if (edt_event_chance) {
+ sprintf(buf, "%d", event->EventChance());
+ edt_event_chance->SetText(buf);
+ }
+
+ sprintf(buf, "%d", event->EventParam());
+ if (edt_event_param) edt_event_param->SetText(buf);
+
+ if (edt_trigger_param)
+ edt_trigger_param->SetText(event->TriggerParamStr());
+
+ if (edt_event_message)
+ edt_event_message->SetText(event->EventMessage());
+
+ if (edt_event_sound)
+ edt_event_sound->SetText(event->EventSound());
+
+ if (cmb_event) {
+ cmb_event->ClearItems();
+
+ for (int i = 0; i < MissionEvent::NUM_EVENTS; i++) {
+ cmb_event->AddItem(MissionEvent::EventName(i));
+ }
+
+ cmb_event->SetSelection(event->Event());
+ }
+
+ if (cmb_trigger) {
+ cmb_trigger->ClearItems();
+
+ for (int i = 0; i < MissionEvent::NUM_TRIGGERS; i++) {
+ cmb_trigger->AddItem(MissionEvent::TriggerName(i));
+ }
+
+ cmb_trigger->SetSelection(event->Trigger());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEventDlg::FillShipList(ComboBox* cmb, const char* seln)
+{
+ if (!cmb) return;
+ cmb->ClearItems();
+
+ if (!mission) return;
+
+ int index = 1;
+ int selected_index = 0;
+ cmb->AddItem("");
+
+ List<MissionElement>& list = mission->GetElements();
+ for (int i = 0; i < list.size(); i++) {
+ MissionElement* elem = list[i];
+
+ if (elem->IsSquadron())
+ continue;
+
+ if (elem->Count() == 1) {
+ cmb->AddItem(elem->Name());
+
+ if (elem->Name() == seln)
+ selected_index = index;
+
+ index++;
+ }
+ else {
+ char ship_name[256];
+
+ for (int n = 0; n < elem->Count(); n++) {
+ sprintf(ship_name, "%s %d", elem->Name().data(), n+1);
+ cmb->AddItem(ship_name);
+
+ if (!stricmp(ship_name, seln))
+ selected_index = index;
+
+ index++;
+ }
+ }
+ }
+
+ cmb->SetSelection(selected_index);
+}
+
+void
+MsnEventDlg::FillRgnList(ComboBox* cmb, const char* seln)
+{
+ if (!cmb) return;
+ cmb->ClearItems();
+
+ if (!mission) return;
+
+ int selected_index = 0;
+ int i = 0;
+
+ ListIter<StarSystem> iter = mission->GetSystemList();
+ while (++iter) {
+ StarSystem* sys = iter.value();
+
+ ListIter<OrbitalRegion> iter2 = sys->AllRegions();
+ while (++iter2) {
+ OrbitalRegion* region = iter2.value();
+
+ if (!strcmp(region->Name(), seln))
+ selected_index = i;
+
+ cmb->AddItem(region->Name());
+ i++;
+ }
+ }
+
+ cmb->SetSelection(selected_index);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEventDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ if (btn_accept && btn_accept->IsEnabled())
+ OnAccept(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEventDlg::SetMission(Mission* m)
+{
+ mission = m;
+}
+
+void
+MsnEventDlg::SetMissionEvent(MissionEvent* e)
+{
+ event = e;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEventDlg::OnEventSelect(AWEvent* e)
+{
+ if (cmb_event->GetSelectedIndex() == MissionEvent::JUMP)
+ FillRgnList(cmb_event_target, event->EventTarget());
+
+ else
+ FillShipList(cmb_event_target, event->EventTarget());
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnEventDlg::OnAccept(AWEvent* e)
+{
+ if (mission && event) {
+ char buf[64];
+ int val;
+
+ if (edt_time) {
+ strcpy(buf, edt_time->GetText());
+
+ float t = 0;
+ sscanf(buf, "%f", &t);
+
+ event->time = t;
+ }
+
+ if (edt_delay) {
+ strcpy(buf, edt_delay->GetText());
+
+ float t = 0;
+ sscanf(buf, "%f", &t);
+
+ event->delay = t;
+ }
+
+ if (edt_event_param) {
+ strcpy(buf, edt_event_param->GetText());
+
+ if (isdigit(*buf)) {
+ sscanf(buf, "%d", &val);
+ event->event_param[0] = val;
+ event->event_nparams = 1;
+ }
+ else if (*buf == '(') {
+ Parser parser(new(__FILE__,__LINE__) BlockReader(buf));
+ Term* term = parser.ParseTerm();
+
+ if (term && term->isArray()) {
+ TermArray* val = term->isArray();
+ if (val) {
+ int nelem = val->elements()->size();
+
+ if (nelem > 10)
+ nelem = 10;
+
+ for (int i = 0; i < nelem; i++)
+ event->event_param[i] = (int) (val->elements()->at(i)->isNumber()->value());
+
+ event->event_nparams = nelem;
+ }
+ }
+ }
+ }
+
+ if (edt_event_chance) {
+ strcpy(buf, edt_event_chance->GetText());
+
+ if (isdigit(*buf)) {
+ sscanf(buf, "%d", &val);
+ }
+ else {
+ val = 0;
+ }
+
+ event->event_chance = val;
+ }
+
+ if (edt_event_message)
+ event->event_message = edt_event_message->GetText();
+
+ if (edt_event_sound)
+ event->event_sound = edt_event_sound->GetText();
+
+ if (edt_trigger_param) {
+ strcpy(buf, edt_trigger_param->GetText());
+
+ ZeroMemory(event->trigger_param, sizeof(event->trigger_param));
+
+ if (isdigit(*buf)) {
+ sscanf(buf, "%d", &val);
+ event->trigger_param[0] = val;
+ event->trigger_nparams = 1;
+ }
+
+ else if (*buf == '(') {
+ Parser parser(new(__FILE__,__LINE__) BlockReader(buf));
+ Term* term = parser.ParseTerm();
+
+ if (term && term->isArray()) {
+ TermArray* val = term->isArray();
+ if (val) {
+ int nelem = val->elements()->size();
+
+ if (nelem > 10)
+ nelem = 10;
+
+ for (int i = 0; i < nelem; i++)
+ event->trigger_param[i] = (int) (val->elements()->at(i)->isNumber()->value());
+
+ event->trigger_nparams = nelem;
+ }
+ }
+ }
+
+ }
+
+ if (cmb_event)
+ event->event = cmb_event->GetSelectedIndex();
+
+ if (cmb_event_ship)
+ event->event_ship = cmb_event_ship->GetSelectedItem();
+
+ if (cmb_event_source)
+ event->event_source = cmb_event_source->GetSelectedItem();
+
+ if (cmb_event_target)
+ event->event_target = cmb_event_target->GetSelectedItem();
+
+ if (cmb_trigger)
+ event->trigger = cmb_trigger->GetSelectedIndex();
+
+ if (cmb_trigger_ship)
+ event->trigger_ship = cmb_trigger_ship->GetSelectedItem();
+
+ if (cmb_trigger_target)
+ event->trigger_target = cmb_trigger_target->GetSelectedItem();
+ }
+
+ manager->ShowMsnEditDlg();
+}
+
+void
+MsnEventDlg::OnCancel(AWEvent* event)
+{
+ manager->ShowMsnEditDlg();
+}
diff --git a/Stars45/MsnEventDlg.h b/Stars45/MsnEventDlg.h
new file mode 100644
index 0000000..1bf85f0
--- /dev/null
+++ b/Stars45/MsnEventDlg.h
@@ -0,0 +1,86 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnEventDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Editor Element Dialog Active Window class
+*/
+
+#ifndef MsnEventDlg_h
+#define MsnEventDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+class MsnEditDlg;
+class Mission;
+class MissionElement;
+class MissionEvent;
+
+// +--------------------------------------------------------------------+
+
+class MsnEventDlg : public FormWindow
+{
+public:
+ MsnEventDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~MsnEventDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void SetMission(Mission* msn);
+ virtual void SetMissionEvent(MissionEvent* event);
+ virtual void OnEventSelect(AWEvent* e);
+ virtual void OnAccept(AWEvent* e);
+ virtual void OnCancel(AWEvent* e);
+
+protected:
+ virtual void FillShipList(ComboBox* cmb, const char* seln);
+ virtual void FillRgnList(ComboBox* cmb, const char* seln);
+
+ MenuScreen* manager;
+
+ ActiveWindow* lbl_id;
+ EditBox* edt_time;
+ EditBox* edt_delay;
+
+ ComboBox* cmb_event;
+ ComboBox* cmb_event_ship;
+ ComboBox* cmb_event_source;
+ ComboBox* cmb_event_target;
+ EditBox* edt_event_param;
+ EditBox* edt_event_chance;
+ EditBox* edt_event_sound;
+ EditBox* edt_event_message;
+
+ ComboBox* cmb_trigger;
+ ComboBox* cmb_trigger_ship;
+ ComboBox* cmb_trigger_target;
+ EditBox* edt_trigger_param;
+
+ Button* btn_accept;
+ Button* btn_cancel;
+
+ Mission* mission;
+ MissionEvent* event;
+};
+
+#endif MsnEventDlg_h
+
diff --git a/Stars45/MsnNavDlg.cpp b/Stars45/MsnNavDlg.cpp
new file mode 100644
index 0000000..1f3dfb2
--- /dev/null
+++ b/Stars45/MsnNavDlg.cpp
@@ -0,0 +1,104 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnNavDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Briefing Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "MsnNavDlg.h"
+#include "PlanScreen.h"
+#include "Campaign.h"
+#include "Mission.h"
+#include "Instruction.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "StarSystem.h"
+
+#include "Keyboard.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+DEF_MAP_CLIENT(MsnNavDlg, OnCommit);
+DEF_MAP_CLIENT(MsnNavDlg, OnCancel);
+DEF_MAP_CLIENT(MsnNavDlg, OnTabButton);
+
+// +--------------------------------------------------------------------+
+
+MsnNavDlg::MsnNavDlg(Screen* s, FormDef& def, PlanScreen* mgr)
+ : NavDlg(s, def, mgr), MsnDlg(mgr)
+{
+ RegisterControls();
+}
+
+MsnNavDlg::~MsnNavDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnNavDlg::RegisterControls()
+{
+ RegisterMsnControls(this);
+
+ if (commit)
+ REGISTER_CLIENT(EID_CLICK, commit, MsnNavDlg, OnCommit);
+
+ if (cancel)
+ REGISTER_CLIENT(EID_CLICK, cancel, MsnNavDlg, OnCancel);
+
+ if (sit_button)
+ REGISTER_CLIENT(EID_CLICK, sit_button, MsnNavDlg, OnTabButton);
+
+ if (pkg_button)
+ REGISTER_CLIENT(EID_CLICK, pkg_button, MsnNavDlg, OnTabButton);
+
+ if (nav_button)
+ REGISTER_CLIENT(EID_CLICK, nav_button, MsnNavDlg, OnTabButton);
+
+ if (wep_button)
+ REGISTER_CLIENT(EID_CLICK, wep_button, MsnNavDlg, OnTabButton);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnNavDlg::Show()
+{
+ NavDlg::Show();
+ ShowMsnDlg();
+}
+
+void
+MsnNavDlg::ExecFrame()
+{
+ NavDlg::ExecFrame();
+
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ OnCommit(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnNavDlg::OnCommit(AWEvent* event)
+{
+ MsnDlg::OnCommit(event);
+ NavDlg::OnCommit(event);
+}
+
+void
+MsnNavDlg::OnCancel(AWEvent* event)
+{
+ MsnDlg::OnCancel(event);
+ NavDlg::OnCancel(event);
+}
diff --git a/Stars45/MsnNavDlg.h b/Stars45/MsnNavDlg.h
new file mode 100644
index 0000000..869e076
--- /dev/null
+++ b/Stars45/MsnNavDlg.h
@@ -0,0 +1,54 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnNavDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Briefing Dialog Active Window class
+*/
+
+#ifndef MsnNavDlg_h
+#define MsnNavDlg_h
+
+#include "Types.h"
+#include "MsnDlg.h"
+#include "NavDlg.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class PlanScreen;
+class Campaign;
+class Mission;
+class MissionInfo;
+
+// +--------------------------------------------------------------------+
+
+class MsnNavDlg : public NavDlg,
+ public MsnDlg
+{
+public:
+ MsnNavDlg(Screen* s, FormDef& def, PlanScreen* mgr);
+ virtual ~MsnNavDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnCommit(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+};
+
+#endif MsnNavDlg_h
+
diff --git a/Stars45/MsnObjDlg.cpp b/Stars45/MsnObjDlg.cpp
new file mode 100644
index 0000000..cee0b86
--- /dev/null
+++ b/Stars45/MsnObjDlg.cpp
@@ -0,0 +1,316 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnObjDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Briefing Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "MsnObjDlg.h"
+#include "PlanScreen.h"
+#include "Campaign.h"
+#include "Mission.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "ShipSolid.h"
+#include "StarSystem.h"
+
+#include "Game.h"
+#include "Mouse.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+#include "Light.h"
+#include "Solid.h"
+#include "Keyboard.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+DEF_MAP_CLIENT(MsnObjDlg, OnCommit);
+DEF_MAP_CLIENT(MsnObjDlg, OnCancel);
+DEF_MAP_CLIENT(MsnObjDlg, OnTabButton);
+DEF_MAP_CLIENT(MsnObjDlg, OnSkin);
+
+// +--------------------------------------------------------------------+
+
+MsnObjDlg::MsnObjDlg(Screen* s, FormDef& def, PlanScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), MsnDlg(mgr),
+ objectives(0), sitrep(0), beauty(0), camview(0), player_desc(0),
+ ship(0)
+{
+ campaign = Campaign::GetCampaign();
+
+ if (campaign)
+ mission = campaign->GetMission();
+
+ Init(def);
+}
+
+MsnObjDlg::~MsnObjDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnObjDlg::RegisterControls()
+{
+ objectives = FindControl(400);
+ sitrep = FindControl(401);
+ beauty = FindControl(300);
+ player_desc = FindControl(301);
+ cmb_skin = (ComboBox*) FindControl(302);
+
+ RegisterMsnControls(this);
+
+ if (commit)
+ REGISTER_CLIENT(EID_CLICK, commit, MsnObjDlg, OnCommit);
+
+ if (cancel)
+ REGISTER_CLIENT(EID_CLICK, cancel, MsnObjDlg, OnCancel);
+
+ if (sit_button)
+ REGISTER_CLIENT(EID_CLICK, sit_button, MsnObjDlg, OnTabButton);
+
+ if (pkg_button)
+ REGISTER_CLIENT(EID_CLICK, pkg_button, MsnObjDlg, OnTabButton);
+
+ if (nav_button)
+ REGISTER_CLIENT(EID_CLICK, nav_button, MsnObjDlg, OnTabButton);
+
+ if (wep_button)
+ REGISTER_CLIENT(EID_CLICK, wep_button, MsnObjDlg, OnTabButton);
+
+ if (cmb_skin) {
+ REGISTER_CLIENT(EID_SELECT, cmb_skin, MsnObjDlg, OnSkin);
+ }
+
+ if (beauty) {
+ scene.SetAmbient(Color(72,75,78));
+
+ Point light_pos(3e6, 5e6, 4e6);
+
+ Light* main_light = new Light(1.2f);
+ main_light->MoveTo(light_pos);
+ main_light->SetType(Light::LIGHT_DIRECTIONAL);
+ main_light->SetColor(Color::White);
+ main_light->SetShadow(true);
+
+ scene.AddLight(main_light);
+
+ Light* back_light = new Light(0.35f);
+ back_light->MoveTo(light_pos * -1);
+ back_light->SetType(Light::LIGHT_DIRECTIONAL);
+ back_light->SetColor(Color::White);
+ back_light->SetShadow(false);
+
+ scene.AddLight(back_light);
+
+ camview = new(__FILE__,__LINE__) CameraView(beauty, &cam, &scene);
+ camview->SetProjectionType(Video::PROJECTION_PERSPECTIVE);
+ camview->SetFieldOfView(2);
+
+ beauty->AddView(camview);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnObjDlg::Show()
+{
+ bool update_scene = !shown;
+
+ FormWindow::Show();
+ ShowMsnDlg();
+
+ if (objectives) {
+ if (mission) {
+ if (mission->IsOK())
+ objectives->SetText(mission->Objective());
+ else
+ objectives->SetText("");
+ }
+ else {
+ objectives->SetText(Game::GetText("MsnDlg.no-mission"));
+ }
+ }
+
+ if (sitrep) {
+ if (mission) {
+ if (mission->IsOK())
+ sitrep->SetText(mission->Situation());
+ else
+ sitrep->SetText(Game::GetText("MsnDlg.found-errors") +
+ mission->ErrorMessage());
+ }
+ else {
+ sitrep->SetText(Game::GetText("MsnDlg.no-mission"));
+ }
+ }
+
+ if (cmb_skin) {
+ cmb_skin->ClearItems();
+ cmb_skin->Hide();
+ }
+
+ if (beauty) {
+ if (mission && mission->IsOK()) {
+ MissionElement* elem = mission->GetPlayer();
+
+ if (elem) {
+ const ShipDesign* design = elem->GetDesign();
+
+ if (design && camview && update_scene) {
+ double az = -PI/6;
+ double el = PI/8;
+ double zoom = 1.8;
+
+ scene.Graphics().clear();
+
+ if (elem->IsStarship()) {
+ az = -PI/8;
+ el = PI/12;
+ zoom = 1.7;
+ }
+
+ if (design->beauty_cam.z > 0) {
+ az = design->beauty_cam.x;
+ el = design->beauty_cam.y;
+ zoom = design->beauty_cam.z;
+ }
+
+ double r = design->radius;
+ double x = zoom * r * sin(az) * cos(el);
+ double y = zoom * r * cos(az) * cos(el);
+ double z = zoom * r * sin(el);
+
+ cam.LookAt(Point(0,0,r/5), Point(x,z,y), Point(0,1,0));
+
+ int n = design->lod_levels;
+
+ if (n >= 1) {
+ Model* model = design->models[n-1].at(0);
+
+ if (model) {
+ ship = new(__FILE__,__LINE__) ShipSolid(0);
+ ship->UseModel(model);
+ ship->CreateShadows(1);
+ ship->SetSkin(elem->GetSkin());
+
+ Matrix o;
+ o.Pitch( 3 * DEGREES);
+ o.Roll( 13 * DEGREES);
+
+ ship->SetOrientation(o);
+
+ scene.Graphics().append(ship);
+ }
+ }
+ }
+
+ if (cmb_skin && design && design->skins.size()) {
+ cmb_skin->Show();
+ cmb_skin->AddItem(Game::GetText("MsnDlg.default"));
+ cmb_skin->SetSelection(0);
+ ListIter<Skin> iter = ((ShipDesign*) design)->skins;
+
+ while (++iter) {
+ Skin* s = iter.value();
+ cmb_skin->AddItem(s->Name());
+
+ if (elem && elem->GetSkin() && !strcmp(s->Name(), elem->GetSkin()->Name())) {
+ cmb_skin->SetSelection(cmb_skin->NumItems()-1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (player_desc) {
+ player_desc->SetText("");
+
+ if (mission && mission->IsOK()) {
+ MissionElement* elem = mission->GetPlayer();
+
+ if (elem) {
+ const ShipDesign* design = elem->GetDesign();
+
+ if (design) {
+ char txt[256];
+
+ if (design->type <= Ship::ATTACK)
+ sprintf(txt, "%s %s", design->abrv, design->display_name);
+ else
+ sprintf(txt, "%s %s", design->abrv, elem->Name().data());
+
+ player_desc->SetText(txt);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnObjDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ OnCommit(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnObjDlg::OnSkin(AWEvent* event)
+{
+ Text skin_name = cmb_skin->GetSelectedItem();
+
+ if (mission && mission->IsOK()) {
+ MissionElement* elem = mission->GetPlayer();
+
+ if (elem) {
+ const ShipDesign* design = elem->GetDesign();
+
+ if (design) {
+ const Skin* skin = design->FindSkin(skin_name);
+
+ elem->SetSkin(skin);
+
+ if (ship)
+ ship->SetSkin(skin);
+ }
+ }
+ }
+}
+
+void
+MsnObjDlg::OnCommit(AWEvent* event)
+{
+ MsnDlg::OnCommit(event);
+}
+
+void
+MsnObjDlg::OnCancel(AWEvent* event)
+{
+ MsnDlg::OnCancel(event);
+}
+
+void
+MsnObjDlg::OnTabButton(AWEvent* event)
+{
+ MsnDlg::OnTabButton(event);
+}
diff --git a/Stars45/MsnObjDlg.h b/Stars45/MsnObjDlg.h
new file mode 100644
index 0000000..890ec11
--- /dev/null
+++ b/Stars45/MsnObjDlg.h
@@ -0,0 +1,68 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnObjDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Briefing Dialog Active Window class
+*/
+
+#ifndef MsnObjDlg_h
+#define MsnObjDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "MsnDlg.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "CameraView.h"
+#include "Scene.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class PlanScreen;
+class Campaign;
+class Mission;
+class MissionInfo;
+class ShipSolid;
+
+// +--------------------------------------------------------------------+
+
+class MsnObjDlg : public FormWindow,
+ public MsnDlg
+{
+public:
+ MsnObjDlg(Screen* s, FormDef& def, PlanScreen* mgr);
+ virtual ~MsnObjDlg();
+
+ virtual void RegisterControls();
+ virtual void ExecFrame();
+ virtual void Show();
+
+ // Operations:
+ virtual void OnCommit(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+ virtual void OnTabButton(AWEvent* event);
+ virtual void OnSkin(AWEvent* event);
+
+protected:
+ ActiveWindow* objectives;
+ ActiveWindow* sitrep;
+ ActiveWindow* player_desc;
+ ActiveWindow* beauty;
+ ComboBox* cmb_skin;
+ CameraView* camview;
+ Scene scene;
+ Camera cam;
+ ShipSolid* ship;
+};
+
+#endif MsnObjDlg_h
+
diff --git a/Stars45/MsnPkgDlg.cpp b/Stars45/MsnPkgDlg.cpp
new file mode 100644
index 0000000..ce7493c
--- /dev/null
+++ b/Stars45/MsnPkgDlg.cpp
@@ -0,0 +1,336 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnPkgDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Briefing Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "MsnPkgDlg.h"
+#include "PlanScreen.h"
+#include "Campaign.h"
+#include "Mission.h"
+#include "Instruction.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "StarSystem.h"
+
+#include "Game.h"
+#include "Mouse.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+#include "Keyboard.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+DEF_MAP_CLIENT(MsnPkgDlg, OnPackage);
+DEF_MAP_CLIENT(MsnPkgDlg, OnCommit);
+DEF_MAP_CLIENT(MsnPkgDlg, OnCancel);
+DEF_MAP_CLIENT(MsnPkgDlg, OnTabButton);
+
+// +--------------------------------------------------------------------+
+
+MsnPkgDlg::MsnPkgDlg(Screen* s, FormDef& def, PlanScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), MsnDlg(mgr)
+{
+ campaign = Campaign::GetCampaign();
+
+ if (campaign)
+ mission = campaign->GetMission();
+
+ Init(def);
+}
+
+MsnPkgDlg::~MsnPkgDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnPkgDlg::RegisterControls()
+{
+ pkg_list = (ListBox*) FindControl(320);
+ nav_list = (ListBox*) FindControl(330);
+
+ for (int i = 0; i < 5; i++)
+ threat[i] = FindControl(251 + i);
+
+ RegisterMsnControls(this);
+
+ if (pkg_list)
+ REGISTER_CLIENT(EID_SELECT, pkg_list, MsnPkgDlg, OnPackage);
+
+ if (commit)
+ REGISTER_CLIENT(EID_CLICK, commit, MsnPkgDlg, OnCommit);
+
+ if (cancel)
+ REGISTER_CLIENT(EID_CLICK, cancel, MsnPkgDlg, OnCancel);
+
+ if (sit_button)
+ REGISTER_CLIENT(EID_CLICK, sit_button, MsnPkgDlg, OnTabButton);
+
+ if (pkg_button)
+ REGISTER_CLIENT(EID_CLICK, pkg_button, MsnPkgDlg, OnTabButton);
+
+ if (nav_button)
+ REGISTER_CLIENT(EID_CLICK, nav_button, MsnPkgDlg, OnTabButton);
+
+ if (wep_button)
+ REGISTER_CLIENT(EID_CLICK, wep_button, MsnPkgDlg, OnTabButton);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnPkgDlg::Show()
+{
+ FormWindow::Show();
+ ShowMsnDlg();
+
+ DrawPackages();
+ DrawNavPlan();
+ DrawThreats();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnPkgDlg::DrawPackages()
+{
+ if (mission) {
+ if (pkg_list) {
+ pkg_list->ClearItems();
+
+ int i = 0;
+ int elem_index = 0;
+ ListIter<MissionElement> elem = mission->GetElements();
+ while (++elem) {
+ // display this element?
+ if (elem->GetIFF() == mission->Team() &&
+ !elem->IsSquadron() &&
+ elem->Region() == mission->GetRegion() &&
+ elem->GetDesign()->type < Ship::STATION) {
+
+ char txt[256];
+
+ if (elem->Player() > 0) {
+ sprintf(txt, "==>");
+ if (pkg_index < 0)
+ pkg_index = elem_index;
+ }
+ else {
+ strcpy(txt, " ");
+ }
+
+ pkg_list->AddItemWithData(txt, elem->ElementID());
+ pkg_list->SetItemText(i, 1, elem->Name());
+ pkg_list->SetItemText(i, 2, elem->RoleName());
+
+ const ShipDesign* design = elem->GetDesign();
+
+ if (elem->Count() > 1)
+ sprintf(txt, "%d %s", elem->Count(), design->abrv);
+ else
+ sprintf(txt, "%s %s", design->abrv, design->name);
+ pkg_list->SetItemText(i, 3, txt);
+
+ i++;
+ }
+
+ elem_index++;
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnPkgDlg::DrawNavPlan()
+{
+ if (mission) {
+ if (pkg_index < 0 || pkg_index >= mission->GetElements().size())
+ pkg_index = 0;
+
+ MissionElement* element = mission->GetElements()[pkg_index];
+ if (nav_list && element) {
+ nav_list->ClearItems();
+
+ Point loc = element->Location();
+ int i = 0;
+
+ ListIter<Instruction> navpt = element->NavList();
+ while (++navpt) {
+ char txt[256];
+ sprintf(txt, "%d", i + 1);
+
+ nav_list->AddItem(txt);
+ nav_list->SetItemText(i, 1, Instruction::ActionName(navpt->Action()));
+ nav_list->SetItemText(i, 2, navpt->RegionName());
+
+ double dist = Point(loc - navpt->Location()).length();
+ FormatNumber(txt, dist);
+ nav_list->SetItemText(i, 3, txt);
+
+ sprintf(txt, "%d", navpt->Speed());
+ nav_list->SetItemText(i, 4, txt);
+
+ loc = navpt->Location();
+ i++;
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnPkgDlg::DrawThreats()
+{
+ for (int i = 0; i < 5; i++)
+ if (threat[i])
+ threat[i]->SetText("");
+
+ if (!mission) return;
+
+ MissionElement* player = mission->GetPlayer();
+ Text rgn0 = player->Region();
+ Text rgn1;
+ int iff = player->GetIFF();
+
+ if (!player)
+ return;
+
+ ListIter<Instruction> nav = player->NavList();
+ while (++nav) {
+ if (rgn0 != nav->RegionName())
+ rgn1 = nav->RegionName();
+ }
+
+ if (threat[0]) {
+ Point base_loc = mission->GetElements()[0]->Location();
+
+ int i = 0;
+ ListIter<MissionElement> iter = mission->GetElements();
+ while (++iter) {
+ MissionElement* elem = iter.value();
+
+ if (elem->GetIFF() == 0 || elem->GetIFF() == iff || elem->IntelLevel() <= Intel::SECRET)
+ continue;
+
+ if (elem->IsSquadron())
+ continue;
+
+ if (elem->IsGroundUnit()) {
+ if (!elem->GetDesign() ||
+ elem->GetDesign()->type != Ship::SAM)
+ continue;
+
+ if (elem->Region() != rgn0 &&
+ elem->Region() != rgn1)
+ continue;
+ }
+
+ int mission_role = elem->MissionRole();
+
+ if (mission_role == Mission::STRIKE ||
+ mission_role == Mission::INTEL ||
+ mission_role >= Mission::TRANSPORT)
+ continue;
+
+ char rng[32];
+ char role[32];
+ char txt[256];
+
+ if (mission_role == Mission::SWEEP ||
+ mission_role == Mission::INTERCEPT ||
+ mission_role == Mission::FLEET ||
+ mission_role == Mission::BOMBARDMENT)
+ strcpy(role, Game::GetText("MsnDlg.ATTACK").data());
+ else
+ strcpy(role, Game::GetText("MsnDlg.PATROL").data());
+
+ double dist = Point(base_loc - elem->Location()).length();
+ FormatNumber(rng, dist);
+
+ sprintf(txt, "%s - %d %s - %s", role,
+ elem->Count(),
+ elem->GetDesign()->abrv,
+ rng);
+ if (threat[i])
+ threat[i]->SetText(txt);
+
+ i++;
+
+ if (i >= 5)
+ break;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnPkgDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ OnCommit(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnPkgDlg::OnPackage(AWEvent* event)
+{
+ if (!pkg_list || !mission)
+ return;
+
+ int seln = pkg_list->GetListIndex();
+ int pkg = pkg_list->GetItemData(seln);
+
+ int i = 0;
+ ListIter<MissionElement> elem = mission->GetElements();
+ while (++elem) {
+ if (elem->ElementID() == pkg) {
+ pkg_index = i;
+ //mission->SetPlayer(elem.value());
+ }
+
+ i++;
+ }
+
+ //DrawPackages();
+ DrawNavPlan();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnPkgDlg::OnCommit(AWEvent* event)
+{
+ MsnDlg::OnCommit(event);
+}
+
+void
+MsnPkgDlg::OnCancel(AWEvent* event)
+{
+ MsnDlg::OnCancel(event);
+}
+
+void
+MsnPkgDlg::OnTabButton(AWEvent* event)
+{
+ MsnDlg::OnTabButton(event);
+}
diff --git a/Stars45/MsnPkgDlg.h b/Stars45/MsnPkgDlg.h
new file mode 100644
index 0000000..f64304f
--- /dev/null
+++ b/Stars45/MsnPkgDlg.h
@@ -0,0 +1,66 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnPkgDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Briefing Dialog Active Window class
+*/
+
+#ifndef MsnPkgDlg_h
+#define MsnPkgDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "MsnDlg.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class PlanScreen;
+class Campaign;
+class Mission;
+class MissionInfo;
+
+// +--------------------------------------------------------------------+
+
+class MsnPkgDlg : public FormWindow,
+ public MsnDlg
+{
+public:
+ MsnPkgDlg(Screen* s, FormDef& def, PlanScreen* mgr);
+ virtual ~MsnPkgDlg();
+
+ virtual void RegisterControls();
+ virtual void ExecFrame();
+ virtual void Show();
+
+ // Operations:
+ virtual void OnCommit(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+ virtual void OnTabButton(AWEvent* event);
+ virtual void OnPackage(AWEvent* event);
+
+protected:
+ virtual void DrawPackages();
+ virtual void DrawNavPlan();
+ virtual void DrawThreats();
+
+ ListBox* pkg_list;
+ ListBox* nav_list;
+
+ ActiveWindow* threat[5];
+};
+
+#endif MsnPkgDlg_h
+
diff --git a/Stars45/MsnSelectDlg.cpp b/Stars45/MsnSelectDlg.cpp
new file mode 100644
index 0000000..fa5386a
--- /dev/null
+++ b/Stars45/MsnSelectDlg.cpp
@@ -0,0 +1,499 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnSelectDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Select Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "MsnSelectDlg.h"
+#include "MsnEditDlg.h"
+#include "MsnEditNavDlg.h"
+#include "ConfirmDlg.h"
+#include "MenuScreen.h"
+#include "Starshatter.h"
+#include "Campaign.h"
+#include "Mission.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(MsnSelectDlg, OnAccept);
+DEF_MAP_CLIENT(MsnSelectDlg, OnCancel);
+DEF_MAP_CLIENT(MsnSelectDlg, OnMod);
+DEF_MAP_CLIENT(MsnSelectDlg, OnNew);
+DEF_MAP_CLIENT(MsnSelectDlg, OnEdit);
+DEF_MAP_CLIENT(MsnSelectDlg, OnDel);
+DEF_MAP_CLIENT(MsnSelectDlg, OnDelConfirm);
+DEF_MAP_CLIENT(MsnSelectDlg, OnCampaignSelect);
+DEF_MAP_CLIENT(MsnSelectDlg, OnMissionSelect);
+
+// +--------------------------------------------------------------------+
+
+static Mission* edit_mission;
+
+// +--------------------------------------------------------------------+
+
+MsnSelectDlg::MsnSelectDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ cmb_campaigns(0), lst_campaigns(0), lst_missions(0),
+ btn_accept(0), btn_cancel(0), btn_mod(0),
+ btn_new(0), btn_edit(0), btn_del(0), editable(false),
+ description(0), stars(0), campaign(0),
+ selected_mission(-1), mission_id(0)
+{
+ stars = Starshatter::GetInstance();
+ campaign = Campaign::GetCampaign();
+ edit_mission = 0;
+
+ Init(def);
+}
+
+MsnSelectDlg::~MsnSelectDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnSelectDlg::RegisterControls()
+{
+ btn_accept = (Button*) FindControl( 1);
+ btn_cancel = (Button*) FindControl( 2);
+
+ if (btn_accept) {
+ btn_accept->SetEnabled(false);
+ REGISTER_CLIENT(EID_CLICK, btn_accept, MsnSelectDlg, OnAccept);
+ }
+
+ if (btn_cancel) {
+ REGISTER_CLIENT(EID_CLICK, btn_cancel, MsnSelectDlg, OnCancel);
+ }
+
+ btn_mod = (Button*) FindControl(300);
+ btn_new = (Button*) FindControl(301);
+ btn_edit = (Button*) FindControl(302);
+ btn_del = (Button*) FindControl(303);
+
+ if (btn_mod)
+ REGISTER_CLIENT(EID_CLICK, btn_mod, MsnSelectDlg, OnMod);
+
+ if (btn_new)
+ REGISTER_CLIENT(EID_CLICK, btn_new, MsnSelectDlg, OnNew);
+
+ if (btn_edit)
+ REGISTER_CLIENT(EID_CLICK, btn_edit, MsnSelectDlg, OnEdit);
+
+ if (btn_del) {
+ REGISTER_CLIENT(EID_CLICK, btn_del, MsnSelectDlg, OnDel);
+ REGISTER_CLIENT(EID_USER_1, btn_del, MsnSelectDlg, OnDelConfirm);
+ }
+
+ description = FindControl(200);
+
+ cmb_campaigns = (ComboBox*) FindControl(201);
+ lst_campaigns = (ListBox*) FindControl(203);
+ lst_missions = (ListBox*) FindControl(202);
+
+ if (cmb_campaigns) {
+ REGISTER_CLIENT(EID_SELECT, cmb_campaigns, MsnSelectDlg, OnCampaignSelect);
+ }
+
+ if (lst_campaigns) {
+ REGISTER_CLIENT(EID_SELECT, lst_campaigns, MsnSelectDlg, OnCampaignSelect);
+
+ lst_campaigns->SetSelectedStyle(ListBox::LIST_ITEM_STYLE_FILLED_BOX);
+ lst_campaigns->SetLeading(4);
+ }
+
+ if (lst_missions) {
+ REGISTER_CLIENT(EID_SELECT, lst_missions, MsnSelectDlg, OnMissionSelect);
+
+ lst_missions->SetSelectedStyle(ListBox::LIST_ITEM_STYLE_FILLED_BOX);
+ lst_missions->SetLeading(4);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnSelectDlg::Show()
+{
+ FormWindow::Show();
+ campaign = Campaign::GetCampaign();
+
+ if (cmb_campaigns) {
+ int n = 0;
+ cmb_campaigns->ClearItems();
+ ListIter<Campaign> iter = Campaign::GetAllCampaigns();
+ while (++iter) {
+ Campaign* c = iter.value();
+
+ if (c->GetCampaignId() >= Campaign::SINGLE_MISSIONS) {
+ cmb_campaigns->AddItem(c->Name());
+
+ if (campaign->GetCampaignId() < Campaign::SINGLE_MISSIONS) {
+ campaign = Campaign::SelectCampaign(c->Name());
+ cmb_campaigns->SetSelection(n);
+ }
+
+ else if (campaign->GetCampaignId() == c->GetCampaignId()) {
+ cmb_campaigns->SetSelection(n);
+ }
+
+ n++;
+ }
+ }
+ }
+
+ else if (lst_campaigns) {
+ int n = 0;
+ lst_campaigns->ClearItems();
+ ListIter<Campaign> iter = Campaign::GetAllCampaigns();
+ while (++iter) {
+ Campaign* c = iter.value();
+
+ if (c->GetCampaignId() >= Campaign::SINGLE_MISSIONS) {
+ lst_campaigns->AddItem(c->Name());
+
+ if (campaign->GetCampaignId() < Campaign::SINGLE_MISSIONS) {
+ campaign = Campaign::SelectCampaign(c->Name());
+ lst_campaigns->SetSelected(n);
+ }
+
+ else if (campaign->GetCampaignId() == c->GetCampaignId()) {
+ lst_campaigns->SetSelected(n);
+ }
+
+ n++;
+ }
+ }
+ }
+
+ if (campaign) {
+ int id = campaign->GetCampaignId();
+ editable = (id >= Campaign::MULTIPLAYER_MISSIONS &&
+ id <= Campaign::CUSTOM_MISSIONS);
+
+ if (btn_new) btn_new->SetEnabled(editable);
+ if (btn_edit) btn_edit->SetEnabled(false);
+ if (btn_del) btn_del->SetEnabled(false);
+ }
+
+ if (description)
+ description->SetText(Game::GetText("MsnSelectDlg.choose"));
+
+ if (lst_missions) {
+ lst_missions->ClearItems();
+
+ if (campaign) {
+ ListIter<MissionInfo> iter = campaign->GetMissionList();
+ while (++iter) {
+ MissionInfo* info = iter.value();
+ Mission* m = info->mission;
+
+ lst_missions->AddItem(info->name);
+
+ if (m && m == edit_mission) {
+ lst_missions->SetSelected(lst_missions->NumItems()-1);
+ }
+ }
+
+ if (selected_mission >= 0 && lst_missions->GetSelCount() == 0) {
+ lst_missions->SetSelected(selected_mission);
+ }
+ }
+
+ OnMissionSelect(0);
+ edit_mission = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnSelectDlg::OnCampaignSelect(AWEvent* event)
+{
+ const char* selected_campaign = 0;
+
+ if (cmb_campaigns)
+ selected_campaign = cmb_campaigns->GetSelectedItem();
+ else if (lst_campaigns)
+ selected_campaign = lst_campaigns->GetSelectedItem();
+
+ Campaign* c = Campaign::SelectCampaign(selected_campaign);
+
+ if (c) {
+ campaign = c;
+
+ if (cmb_campaigns) {
+ cmb_campaigns->ClearItems();
+
+ ListIter<MissionInfo> iter = campaign->GetMissionList();
+ while (++iter) {
+ cmb_campaigns->AddItem(iter->name);
+ }
+ }
+
+ else if (lst_missions) {
+ lst_missions->ClearItems();
+
+ ListIter<MissionInfo> iter = campaign->GetMissionList();
+ while (++iter) {
+ lst_missions->AddItem(iter->name);
+ }
+
+ lst_missions->ScrollTo(0);
+ }
+
+ if (btn_accept)
+ btn_accept->SetEnabled(false);
+
+ if (description)
+ description->SetText(Game::GetText("MsnSelectDlg.choose"));
+
+ int id = c->GetCampaignId();
+ editable = (id >= Campaign::MULTIPLAYER_MISSIONS &&
+ id <= Campaign::CUSTOM_MISSIONS);
+
+ if (btn_new) btn_new->SetEnabled(editable);
+ if (btn_edit) btn_edit->SetEnabled(false);
+ if (btn_del) btn_del->SetEnabled(false);
+ }
+}
+
+void
+MsnSelectDlg::OnMissionSelect(AWEvent* event)
+{
+ selected_mission = -1;
+
+ for (int i = 0; i < lst_missions->NumItems(); i++)
+ if (lst_missions->IsSelected(i))
+ selected_mission = i;
+
+ if (btn_accept && description && campaign) {
+ List<MissionInfo>& mission_info_list = campaign->GetMissionList();
+
+ if (selected_mission >= 0 && selected_mission < mission_info_list.size()) {
+ MissionInfo* info = mission_info_list[selected_mission];
+ mission_id = info->id;
+
+ char time_buf[32];
+ FormatDayTime(time_buf, info->start);
+
+ Text d("<font Limerick12><color ffffff>");
+ d += info->name;
+ d += "<font Verdana>\n\n<color ffff80>";
+ d += Game::GetText("MsnSelectDlg.mission-type");
+ d += "<color ffffff>\n\t";
+ d += Mission::RoleName(info->type);
+ d += "\n\n<color ffff80>";
+ d += Game::GetText("MsnSelectDlg.scenario");
+ d += "<color ffffff>\n\t";
+ d += info->description;
+ d += "\n\n<color ffff80>";
+ d += Game::GetText("MsnSelectDlg.location");
+ d += "<color ffffff>\n\t";
+ d += info->region;
+ d += " ";
+ d += Game::GetText("MsnSelectDlg.sector");
+ d += " / ";
+ d += info->system;
+ d += " ";
+ d += Game::GetText("MsnSelectDlg.system");
+ d += "\n\n<color ffff80>";
+ d += Game::GetText("MsnSelectDlg.start-time");
+ d += "<color ffffff>\n\t";
+ d += time_buf;
+
+ description->SetText(d);
+ btn_accept->SetEnabled(true);
+
+ if (btn_edit) btn_edit->SetEnabled(editable);
+ if (btn_del) btn_del->SetEnabled(editable);
+ }
+
+ else {
+ description->SetText(Game::GetText("MsnSelectDlg.choose"));
+ btn_accept->SetEnabled(false);
+
+ if (btn_edit) btn_edit->SetEnabled(false);
+ if (btn_del) btn_del->SetEnabled(false);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnSelectDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ if (btn_accept && btn_accept->IsEnabled())
+ OnAccept(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnSelectDlg::OnMod(AWEvent* event)
+{
+}
+
+void
+MsnSelectDlg::OnNew(AWEvent* event)
+{
+ const char* cname = 0;
+
+ if (cmb_campaigns)
+ cname = cmb_campaigns->GetSelectedItem();
+ else if (lst_campaigns)
+ cname = lst_campaigns->GetSelectedItem();
+
+ Campaign* c = Campaign::SelectCampaign(cname);
+ if (!c) return;
+
+ MissionInfo* info = c->CreateNewMission();
+ if (!info || !info->mission)
+ return;
+
+ mission_id = info->id;
+
+ MsnEditDlg* editor = manager->GetMsnEditDlg();
+
+ if (editor) {
+ edit_mission = info->mission;
+
+ editor->SetMissionInfo(info);
+ editor->SetMission(info->mission);
+ manager->ShowMsnEditDlg();
+ }
+
+ MsnEditNavDlg* navdlg = (MsnEditNavDlg*) manager->GetNavDlg();
+
+ if (navdlg) {
+ navdlg->SetMission(info->mission);
+ navdlg->SetMissionInfo(info);
+ }
+}
+
+void
+MsnSelectDlg::OnEdit(AWEvent* event)
+{
+ const char* cname = 0;
+
+ if (cmb_campaigns)
+ cname = cmb_campaigns->GetSelectedItem();
+ else if (lst_campaigns)
+ cname = lst_campaigns->GetSelectedItem();
+
+ Campaign* c = Campaign::SelectCampaign(cname);
+ if (!c) return;
+
+ Mission* m = c->GetMission(mission_id);
+ if (!m) return;
+
+ MsnEditDlg* editor = manager->GetMsnEditDlg();
+
+ if (editor) {
+ edit_mission = m;
+
+ editor->SetMissionInfo(c->GetMissionInfo(mission_id));
+ editor->SetMission(m);
+ manager->ShowMsnEditDlg();
+ }
+}
+
+void
+MsnSelectDlg::OnDel(AWEvent* event)
+{
+ const char* cname = 0;
+
+ if (cmb_campaigns)
+ cname = cmb_campaigns->GetSelectedItem();
+ else if (lst_campaigns)
+ cname = lst_campaigns->GetSelectedItem();
+
+ Campaign* c = Campaign::SelectCampaign(cname);
+ if (!c) return;
+
+ Mission* m = c->GetMission(mission_id);
+ if (!m) return;
+
+ ConfirmDlg* confirm = manager->GetConfirmDlg();
+ if (confirm) {
+ char msg[256];
+ sprintf(msg, Game::GetText("MsnSelectDlg.are-you-sure").data(), m->Name());
+ confirm->SetMessage(msg);
+ confirm->SetTitle(Game::GetText("MsnSelectDlg.confirm-delete"));
+ confirm->SetParentControl(btn_del);
+
+ manager->ShowConfirmDlg();
+ }
+
+ else {
+ OnDelConfirm(event);
+ }
+}
+
+void
+MsnSelectDlg::OnDelConfirm(AWEvent* event)
+{
+ const char* cname = 0;
+
+ if (cmb_campaigns)
+ cname = cmb_campaigns->GetSelectedItem();
+ else if (lst_campaigns)
+ cname = lst_campaigns->GetSelectedItem();
+
+ Campaign* c = Campaign::SelectCampaign(cname);
+ if (!c) return;
+
+ edit_mission = 0;
+ c->DeleteMission(mission_id);
+ Show();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnSelectDlg::OnAccept(AWEvent* event)
+{
+ if (selected_mission >= 0) {
+ Mouse::Show(false);
+
+ int id = campaign->GetMissionList()[selected_mission]->id;
+ campaign->SetMissionId(id);
+ campaign->ReloadMission(id);
+
+ stars->SetGameMode(Starshatter::PREP_MODE);
+ }
+}
+
+void
+MsnSelectDlg::OnCancel(AWEvent* event)
+{
+ manager->ShowMenuDlg();
+}
+
+// +--------------------------------------------------------------------+
diff --git a/Stars45/MsnSelectDlg.h b/Stars45/MsnSelectDlg.h
new file mode 100644
index 0000000..a9cef55
--- /dev/null
+++ b/Stars45/MsnSelectDlg.h
@@ -0,0 +1,81 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnSelectDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Select Dialog Active Window class
+*/
+
+#ifndef MsnSelectDlg_h
+#define MsnSelectDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+class Campaign;
+class Starshatter;
+
+// +--------------------------------------------------------------------+
+
+class MsnSelectDlg : public FormWindow
+{
+public:
+ MsnSelectDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~MsnSelectDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnCampaignSelect(AWEvent* event);
+ virtual void OnMissionSelect(AWEvent* event);
+
+ virtual void OnMod(AWEvent* event);
+ virtual void OnNew(AWEvent* event);
+ virtual void OnEdit(AWEvent* event);
+ virtual void OnDel(AWEvent* event);
+ virtual void OnDelConfirm(AWEvent* event);
+ virtual void OnAccept(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+
+protected:
+ MenuScreen* manager;
+
+ Button* btn_mod;
+ Button* btn_new;
+ Button* btn_edit;
+ Button* btn_del;
+ Button* btn_accept;
+ Button* btn_cancel;
+
+ ComboBox* cmb_campaigns;
+ ListBox* lst_campaigns;
+ ListBox* lst_missions;
+
+ ActiveWindow* description;
+
+ Starshatter* stars;
+ Campaign* campaign;
+ int selected_mission;
+ int mission_id;
+ bool editable;
+};
+
+#endif MsnSelectDlg_h
+
diff --git a/Stars45/MsnWepDlg.cpp b/Stars45/MsnWepDlg.cpp
new file mode 100644
index 0000000..15cb125
--- /dev/null
+++ b/Stars45/MsnWepDlg.cpp
@@ -0,0 +1,517 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnWepDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Briefing Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "MsnWepDlg.h"
+#include "PlanScreen.h"
+#include "Starshatter.h"
+#include "Campaign.h"
+#include "Mission.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "WeaponDesign.h"
+#include "HardPoint.h"
+#include "StarSystem.h"
+#include "FormatUtil.h"
+
+#include "Game.h"
+#include "Mouse.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "ParseUtil.h"
+#include "Keyboard.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+DEF_MAP_CLIENT(MsnWepDlg, OnCommit);
+DEF_MAP_CLIENT(MsnWepDlg, OnCancel);
+DEF_MAP_CLIENT(MsnWepDlg, OnTabButton);
+DEF_MAP_CLIENT(MsnWepDlg, OnMount);
+DEF_MAP_CLIENT(MsnWepDlg, OnLoadout);
+
+// +--------------------------------------------------------------------+
+
+MsnWepDlg::MsnWepDlg(Screen* s, FormDef& def, PlanScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), MsnDlg(mgr),
+ elem(0), first_station(0), beauty(0), player_desc(0)
+{
+ campaign = Campaign::GetCampaign();
+
+ if (campaign)
+ mission = campaign->GetMission();
+
+ ZeroMemory(designs, sizeof(designs));
+ ZeroMemory(mounts, sizeof(mounts));
+ Init(def);
+}
+
+MsnWepDlg::~MsnWepDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnWepDlg::RegisterControls()
+{
+ lbl_element = FindControl(601);
+ lbl_type = FindControl(602);
+ lbl_weight = FindControl(603);
+ loadout_list = (ListBox*) FindControl(604);
+ beauty = (ImageBox*) FindControl(300);
+ player_desc = FindControl(301);
+
+ if (loadout_list)
+ REGISTER_CLIENT(EID_SELECT, loadout_list, MsnWepDlg, OnLoadout);
+
+ for (int i = 0; i < 8; i++) {
+ lbl_desc[i] = FindControl(500 + i*10);
+ lbl_station[i] = FindControl(401 + i);
+
+ for (int n = 0; n < 8; n++) {
+ btn_load[i][n] = (Button*) FindControl(500 + i*10 + n + 1);
+
+ if (btn_load[i][n]) {
+ if (i == 0) {
+ if (n == 0)
+ btn_load[i][n]->GetPicture(led_off);
+ else if (n == 1)
+ btn_load[i][n]->GetPicture(led_on);
+ }
+
+ btn_load[i][n]->SetPicture(led_off);
+ btn_load[i][n]->SetPictureLocation(4); // centered
+ REGISTER_CLIENT(EID_CLICK, btn_load[i][n], MsnWepDlg, OnMount);
+ }
+ }
+ }
+
+ RegisterMsnControls(this);
+
+ if (commit)
+ REGISTER_CLIENT(EID_CLICK, commit, MsnWepDlg, OnCommit);
+
+ if (cancel)
+ REGISTER_CLIENT(EID_CLICK, cancel, MsnWepDlg, OnCancel);
+
+ if (sit_button)
+ REGISTER_CLIENT(EID_CLICK, sit_button, MsnWepDlg, OnTabButton);
+
+ if (pkg_button)
+ REGISTER_CLIENT(EID_CLICK, pkg_button, MsnWepDlg, OnTabButton);
+
+ if (nav_button)
+ REGISTER_CLIENT(EID_CLICK, nav_button, MsnWepDlg, OnTabButton);
+
+ if (wep_button)
+ REGISTER_CLIENT(EID_CLICK, wep_button, MsnWepDlg, OnTabButton);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnWepDlg::Show()
+{
+ FormWindow::Show();
+ ShowMsnDlg();
+
+ if (mission) {
+ for (int i = 0; i < mission->GetElements().size(); i++) {
+ MissionElement* e = mission->GetElements().at(i);
+ if (e->Player()) {
+ elem = e;
+ break;
+ }
+ }
+ }
+
+ if (elem) {
+ SetupControls();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnWepDlg::SetupControls()
+{
+ ShipDesign* design = (ShipDesign*) elem->GetDesign();
+
+ if (lbl_element)
+ lbl_element->SetText(elem->Name());
+
+ if (lbl_type)
+ lbl_type->SetText(design->name);
+
+ BuildLists();
+
+ for (int i = 0; i < 8; i++) {
+ if (!lbl_desc[i]) continue;
+
+ if (designs[i]) {
+ lbl_desc[i]->Show();
+ lbl_desc[i]->SetText(designs[i]->group + " " + designs[i]->name);
+
+ for (int n = 0; n < 8; n++) {
+ if (mounts[i][n]) {
+ btn_load[i][n]->Show();
+ btn_load[i][n]->SetPicture((loads[n]==i) ? led_on : led_off);
+ }
+ else {
+ btn_load[i][n]->Hide();
+ }
+ }
+ }
+ else {
+ lbl_desc[i]->Hide();
+
+ for (int n = 0; n < 8; n++) {
+ btn_load[i][n]->Hide();
+ }
+ }
+ }
+
+ double loaded_mass = 0;
+ char weight[32];
+
+ if (loadout_list) {
+ loadout_list->ClearItems();
+
+ if (design) {
+ ListIter<ShipLoad> sl = (List<ShipLoad>&) design->loadouts;
+ while (++sl) {
+ ShipLoad* load = sl.value();
+ int item = loadout_list->AddItem(load->name) - 1;
+
+ sprintf(weight, "%d kg", (int) ((design->mass + load->mass) * 1000));
+ loadout_list->SetItemText(item, 1, weight);
+ loadout_list->SetItemData(item, 1, (DWORD) (load->mass * 1000));
+
+ if (elem->Loadouts().size() > 0 &&
+ elem->Loadouts().at(0)->GetName() == load->name) {
+ loadout_list->SetSelected(item, true);
+ loaded_mass = design->mass + load->mass;
+ }
+ }
+ }
+ }
+
+ if (lbl_weight) {
+ if (loaded_mass < 1)
+ loaded_mass = design->mass;
+
+ sprintf(weight, "%d kg", (int) (loaded_mass * 1000));
+ lbl_weight->SetText(weight);
+ }
+
+ if (beauty && design) {
+ beauty->SetPicture(design->beauty);
+ }
+
+ if (player_desc && design) {
+ char txt[256];
+
+ if (design->type <= Ship::ATTACK)
+ sprintf(txt, "%s %s", design->abrv, design->display_name);
+ else
+ sprintf(txt, "%s %s", design->abrv, elem->Name().data());
+
+ player_desc->SetText(txt);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnWepDlg::BuildLists()
+{
+ ZeroMemory(designs, sizeof(designs));
+ ZeroMemory(mounts, sizeof(mounts));
+
+ if (elem) {
+ ShipDesign* d = (ShipDesign*) elem->GetDesign();
+ int nstations = d->hard_points.size();
+
+ first_station = (8 - nstations) / 2;
+
+ int index = 0;
+ int station = first_station;
+
+ for (int s = 0; s < 8; s++)
+ if (lbl_station[s])
+ lbl_station[s]->SetText("");
+
+ ListIter<HardPoint> iter = d->hard_points;
+ while (++iter) {
+ HardPoint* hp = iter.value();
+
+ if (lbl_station[station])
+ lbl_station[station]->SetText(hp->GetAbbreviation());
+
+ for (int n = 0; n < HardPoint::MAX_DESIGNS; n++) {
+ WeaponDesign* wep_dsn = hp->GetWeaponDesign(n);
+
+ if (wep_dsn) {
+ bool found = false;
+
+ for (int i = 0; i < 8 && !found; i++) {
+ if (designs[i] == wep_dsn) {
+ found = true;
+ mounts[i][station] = true;
+ }
+ }
+
+ if (!found) {
+ mounts[index][station] = true;
+ designs[index++] = wep_dsn;
+ }
+ }
+ }
+
+ station++;
+ }
+
+ if (elem->Loadouts().size()) {
+ MissionLoad* msn_load = elem->Loadouts().at(0);
+
+ for (int i = 0; i < 8; i++)
+ loads[i] = -1;
+
+ // map loadout:
+ int* loadout = 0;
+ if (msn_load->GetName().length()) {
+ ListIter<ShipLoad> sl = ((ShipDesign*) elem->GetDesign())->loadouts;
+ while (++sl) {
+ if (!stricmp(sl->name, msn_load->GetName()))
+ loadout = sl->load;
+ }
+ }
+ else {
+ loadout = msn_load->GetStations();
+ }
+
+ for (i = 0; i < nstations; i++) {
+ loads[i + first_station] = loadout[i];
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnWepDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ OnCommit(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MsnWepDlg::LoadToPointIndex(int n)
+{
+ int nn = n + first_station;
+
+ if (!elem || nn < 0 || nn >= 8 || loads[nn] == -1)
+ return -1;
+
+ int index = -1;
+ WeaponDesign* wep_design = designs[ loads[nn] ];
+ ShipDesign* design = (ShipDesign*) elem->GetDesign();
+ HardPoint* hard_point = design->hard_points[n];
+
+ for (int i = 0; i < 8 && index < 0; i++) {
+ if (hard_point->GetWeaponDesign(i) == wep_design) {
+ index = i;
+ }
+ }
+
+ return index;
+}
+
+int
+MsnWepDlg::PointIndexToLoad(int n, int index)
+{
+ int nn = n + first_station;
+
+ if (!elem || nn < 0 || nn >= 8)
+ return -1;
+
+ int result = -1;
+ ShipDesign* design = (ShipDesign*) elem->GetDesign();
+ HardPoint* hard_point = design->hard_points[n];
+ WeaponDesign* wep_design = hard_point->GetWeaponDesign(index);
+
+ for (int i = 0; i < 8 && result < 0; i++) {
+ if (designs[i] == wep_design) {
+ result = i;
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnWepDlg::OnMount(AWEvent* event)
+{
+ int station = -1;
+ int item = -1;
+
+ for (int i = 0; i < 8 && item < 0; i++) {
+ for (int n = 0; n < 8 && station < 0; n++) {
+ if (btn_load[i][n] == event->window) {
+ station = n;
+ item = i;
+ }
+ }
+ }
+
+ if (item >= 0 && station >= 0) {
+ if (loads[station] == item)
+ item = -1;
+
+ loads[station] = item;
+
+ for (int n = 0; n < 8; n++) {
+ btn_load[n][station]->SetPicture(n == item ? led_on : led_off);
+ }
+
+ if (elem) {
+ int nstations = elem->GetDesign()->hard_points.size();
+
+ if (elem->Loadouts().size() < 1) {
+ MissionLoad* l = new(__FILE__,__LINE__) MissionLoad;
+ elem->Loadouts().append(l);
+
+ for (int n = 0; n < nstations; n++)
+ l->SetStation(n, LoadToPointIndex(n));
+ }
+ else {
+ ListIter<MissionLoad> l = elem->Loadouts();
+ while (++l) {
+ // if the player customizes the loadout,
+ // tell the sim loader not to use a named
+ // loadout from the ship design:
+ l->SetName("");
+
+ for (int n = 0; n < nstations; n++)
+ l->SetStation(n, LoadToPointIndex(n));
+ }
+ }
+ }
+ }
+
+ if (loadout_list)
+ loadout_list->ClearSelection();
+
+ if (lbl_weight && elem) {
+ ShipDesign* d = (ShipDesign*) elem->GetDesign();
+ int nstations = d->hard_points.size();
+ double mass = d->mass;
+
+ for (int n = 0; n < nstations; n++) {
+ int item = loads[n+first_station];
+ mass += d->hard_points[n]->GetCarryMass(item);
+ }
+
+ char weight[32];
+ sprintf(weight, "%d kg", (int) (mass * 1000));
+ lbl_weight->SetText(weight);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnWepDlg::OnLoadout(AWEvent* event)
+{
+ if (!elem) return;
+
+ ShipDesign* design = (ShipDesign*) elem->GetDesign();
+ ShipLoad* shipload = 0;
+
+ if (loadout_list && design) {
+ int index = loadout_list->GetListIndex();
+ Text loadname = loadout_list->GetItemText(index);
+
+ ListIter<ShipLoad> sl = (List<ShipLoad>&) design->loadouts;
+ while (++sl) {
+ if (sl->name == loadname) {
+ shipload = sl.value();
+ }
+ }
+
+ if (!shipload) return;
+
+ if (lbl_weight) {
+ char weight[32];
+ sprintf(weight, "%d kg", (int) ((design->mass + shipload->mass) * 1000));
+ lbl_weight->SetText(weight);
+ }
+
+ if (elem->Loadouts().size() < 1) {
+ MissionLoad* l = new(__FILE__,__LINE__) MissionLoad(-1, shipload->name);
+ elem->Loadouts().append(l);
+ }
+ else {
+ ListIter<MissionLoad> l = elem->Loadouts();
+ while (++l) {
+ // if the player chooses a std loadout,
+ // tell the sim loader to use a named
+ // loadout from the ship design:
+ l->SetName(shipload->name);
+ }
+ }
+
+ int nstations = design->hard_points.size();
+ int* loadout = shipload->load;
+
+ for (int i = 0; i < 8; i++)
+ loads[i] = -1;
+
+ for (i = 0; i < nstations; i++)
+ loads[i + first_station] = PointIndexToLoad(i, loadout[i]);
+
+ for (i = 0; i < 8; i++) {
+ for (int n = 0; n < 8; n++) {
+ btn_load[i][n]->SetPicture(i == loads[n] ? led_on: led_off);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MsnWepDlg::OnCommit(AWEvent* event)
+{
+ MsnDlg::OnCommit(event);
+}
+
+void
+MsnWepDlg::OnCancel(AWEvent* event)
+{
+ MsnDlg::OnCancel(event);
+}
+
+void
+MsnWepDlg::OnTabButton(AWEvent* event)
+{
+ MsnDlg::OnTabButton(event);
+}
diff --git a/Stars45/MsnWepDlg.h b/Stars45/MsnWepDlg.h
new file mode 100644
index 0000000..e9d82be
--- /dev/null
+++ b/Stars45/MsnWepDlg.h
@@ -0,0 +1,86 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MsnWepDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Briefing Dialog Active Window class
+*/
+
+#ifndef MsnWepDlg_h
+#define MsnWepDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "MsnDlg.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ImageBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class PlanScreen;
+class Campaign;
+class Mission;
+class MissionElement;
+class HardPoint;
+class WeaponDesign;
+
+// +--------------------------------------------------------------------+
+
+class MsnWepDlg : public FormWindow,
+ public MsnDlg
+{
+public:
+ MsnWepDlg(Screen* s, FormDef& def, PlanScreen* mgr);
+ virtual ~MsnWepDlg();
+
+ virtual void RegisterControls();
+ virtual void ExecFrame();
+ virtual void Show();
+
+ // Operations:
+ virtual void OnCommit(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+ virtual void OnTabButton(AWEvent* event);
+ virtual void OnMount(AWEvent* event);
+ virtual void OnLoadout(AWEvent* event);
+
+protected:
+ virtual void SetupControls();
+ virtual void BuildLists();
+ virtual int LoadToPointIndex(int n);
+ virtual int PointIndexToLoad(int n, int index);
+
+ ActiveWindow* lbl_element;
+ ActiveWindow* lbl_type;
+ ActiveWindow* lbl_weight;
+ ActiveWindow* player_desc;
+ ImageBox* beauty;
+
+ ActiveWindow* lbl_station[8];
+ ActiveWindow* lbl_desc[8];
+ Button* btn_load[8][8];
+
+ ListBox* loadout_list;
+
+ MissionElement* elem;
+ WeaponDesign* designs[8];
+ bool mounts[8][8];
+ int loads[8];
+ int first_station;
+
+ Bitmap led_off;
+ Bitmap led_on;
+};
+
+#endif MsnWepDlg_h
+
diff --git a/Stars45/MusicDirector.cpp b/Stars45/MusicDirector.cpp
new file mode 100644
index 0000000..76199af
--- /dev/null
+++ b/Stars45/MusicDirector.cpp
@@ -0,0 +1,520 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MusicDirector.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Music Director class to manage selection, setup, and playback
+ of background music tracks for both menu and game modes
+*/
+
+
+#include "MemDebug.h"
+#include "MusicDirector.h"
+#include "MusicTrack.h"
+
+#include "Random.h"
+#include "DataLoader.h"
+#include "FormatUtil.h"
+#include "Sound.h"
+
+static MusicDirector* music_director = 0;
+
+// +-------------------------------------------------------------------+
+
+MusicDirector::MusicDirector()
+ : mode(0), transition(0), track(0), next_track(0), no_music(true),
+ hproc(0)
+{
+ music_director = this;
+
+ ScanTracks();
+
+ if (!no_music)
+ StartThread();
+}
+
+MusicDirector::~MusicDirector()
+{
+ StopThread();
+
+ delete track;
+ delete next_track;
+
+ menu_tracks.destroy();
+ intro_tracks.destroy();
+ brief_tracks.destroy();
+ debrief_tracks.destroy();
+ promote_tracks.destroy();
+ flight_tracks.destroy();
+ combat_tracks.destroy();
+ launch_tracks.destroy();
+ recovery_tracks.destroy();
+ victory_tracks.destroy();
+ defeat_tracks.destroy();
+ credit_tracks.destroy();
+
+ if (this == music_director)
+ music_director = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MusicDirector::Initialize()
+{
+ if (music_director) delete music_director;
+ music_director = new(__FILE__,__LINE__) MusicDirector();
+}
+
+void
+MusicDirector::Close()
+{
+ delete music_director;
+ music_director = 0;
+}
+
+MusicDirector*
+MusicDirector::GetInstance()
+{
+ return music_director;
+}
+
+// +-------------------------------------------------------------------+
+
+void
+MusicDirector::ExecFrame()
+{
+ if (no_music) return;
+
+ AutoThreadSync a(sync);
+
+ if (next_track && !track) {
+ track = next_track;
+ next_track = 0;
+ }
+
+ if (track) {
+ if (track->IsDone()) {
+ if (mode != NONE && mode != SHUTDOWN && next_track == 0) {
+ GetNextTrack(track->GetIndex()+1);
+ }
+
+ delete track;
+ track = next_track;
+ next_track = 0;
+ }
+
+ else if (track->IsLooped()) {
+ if (mode != NONE && mode != SHUTDOWN && next_track == 0) {
+ GetNextTrack(track->GetIndex()+1);
+ }
+
+ track->FadeOut();
+ track->ExecFrame();
+ }
+
+ else {
+ track->ExecFrame();
+ }
+ }
+
+ if (next_track) {
+ if (next_track->IsDone()) {
+ delete next_track;
+ next_track = 0;
+ }
+
+ else if (next_track->IsLooped()) {
+ next_track->FadeOut();
+ next_track->ExecFrame();
+ }
+
+ else {
+ next_track->ExecFrame();
+ }
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+MusicDirector::ScanTracks()
+{
+ DataLoader* loader = DataLoader::GetLoader();
+
+ bool old_file_system = loader->IsFileSystemEnabled();
+ loader->UseFileSystem(true);
+ loader->SetDataPath("Music/");
+
+ List<Text> files;
+ loader->ListFiles("*.ogg", files, true);
+
+ if (files.size() == 0) {
+ loader->UseFileSystem(old_file_system);
+ no_music = true;
+ return;
+ }
+
+ no_music = false;
+
+ ListIter<Text> iter = files;
+ while (++iter) {
+ Text* name = iter.value();
+ Text* file = new(__FILE__,__LINE__) Text("Music/");
+
+ name->setSensitive(false);
+ file->append(*name);
+
+ if (name->indexOf("Menu") == 0) {
+ menu_tracks.append(file);
+ }
+
+ else if (name->indexOf("Intro") == 0) {
+ intro_tracks.append(file);
+ }
+
+ else if (name->indexOf("Brief") == 0) {
+ brief_tracks.append(file);
+ }
+
+ else if (name->indexOf("Debrief") == 0) {
+ debrief_tracks.append(file);
+ }
+
+ else if (name->indexOf("Promot") == 0) {
+ promote_tracks.append(file);
+ }
+
+ else if (name->indexOf("Flight") == 0) {
+ flight_tracks.append(file);
+ }
+
+ else if (name->indexOf("Combat") == 0) {
+ combat_tracks.append(file);
+ }
+
+ else if (name->indexOf("Launch") == 0) {
+ launch_tracks.append(file);
+ }
+
+ else if (name->indexOf("Recovery") == 0) {
+ recovery_tracks.append(file);
+ }
+
+ else if (name->indexOf("Victory") == 0) {
+ victory_tracks.append(file);
+ }
+
+ else if (name->indexOf("Defeat") == 0) {
+ defeat_tracks.append(file);
+ }
+
+ else if (name->indexOf("Credit") == 0) {
+ credit_tracks.append(file);
+ }
+
+ else {
+ menu_tracks.append(file);
+ }
+
+ delete iter.removeItem();
+ }
+
+ loader->UseFileSystem(old_file_system);
+
+ menu_tracks.sort();
+ intro_tracks.sort();
+ brief_tracks.sort();
+ debrief_tracks.sort();
+ promote_tracks.sort();
+ flight_tracks.sort();
+ combat_tracks.sort();
+ launch_tracks.sort();
+ recovery_tracks.sort();
+ victory_tracks.sort();
+ defeat_tracks.sort();
+ credit_tracks.sort();
+}
+
+// +-------------------------------------------------------------------+
+
+const char*
+MusicDirector::GetModeName(int mode)
+{
+ switch (mode) {
+ case NONE: return "NONE";
+ case MENU: return "MENU";
+ case INTRO: return "INTRO";
+ case BRIEFING: return "BRIEFING";
+ case DEBRIEFING: return "DEBRIEFING";
+ case PROMOTION: return "PROMOTION";
+ case FLIGHT: return "FLIGHT";
+ case COMBAT: return "COMBAT";
+ case LAUNCH: return "LAUNCH";
+ case RECOVERY: return "RECOVERY";
+ case VICTORY: return "VICTORY";
+ case DEFEAT: return "DEFEAT";
+ case CREDITS: return "CREDITS";
+ case SHUTDOWN: return "SHUTDOWN";
+ }
+
+ return "UNKNOWN?";
+}
+
+// +-------------------------------------------------------------------+
+
+void
+MusicDirector::SetMode(int mode)
+{
+ if (!music_director || music_director->no_music) return;
+
+ AutoThreadSync a(music_director->sync);
+
+ // stay in intro mode until it is complete:
+ if (mode == MENU && (music_director->GetMode() == NONE ||
+ music_director->GetMode() == INTRO))
+ mode = INTRO;
+
+ mode = music_director->CheckMode(mode);
+
+ if (mode != music_director->mode) {
+ ::Print("MusicDirector::SetMode() old: %s new: %s\n",
+ GetModeName(music_director->mode),
+ GetModeName(mode));
+
+ music_director->mode = mode;
+
+ MusicTrack* t = music_director->track;
+ if (t && t->GetState() && !t->IsDone()) {
+ if (mode == NONE || mode == SHUTDOWN)
+ t->SetFadeTime(0.5);
+
+ t->FadeOut();
+ }
+
+ t = music_director->next_track;
+ if (t && t->GetState() && !t->IsDone()) {
+ if (mode == NONE || mode == SHUTDOWN)
+ t->SetFadeTime(0.5);
+ t->FadeOut();
+
+ delete music_director->track;
+ music_director->track = t;
+ music_director->next_track = 0;
+ }
+
+ music_director->ShuffleTracks();
+ music_director->GetNextTrack(0);
+
+ if (music_director->next_track)
+ music_director->next_track->FadeIn();
+ }
+}
+
+int
+MusicDirector::CheckMode(int req_mode)
+{
+ if (req_mode == RECOVERY && recovery_tracks.size() == 0)
+ req_mode = LAUNCH;
+
+ if (req_mode == LAUNCH && launch_tracks.size() == 0)
+ req_mode = FLIGHT;
+
+ if (req_mode == COMBAT && combat_tracks.size() == 0)
+ req_mode = FLIGHT;
+
+ if (req_mode == FLIGHT && flight_tracks.size() == 0)
+ req_mode = NONE;
+
+ if (req_mode == PROMOTION && promote_tracks.size() == 0)
+ req_mode = VICTORY;
+
+ if (req_mode == DEBRIEFING && debrief_tracks.size() == 0)
+ req_mode = BRIEFING;
+
+ if (req_mode == BRIEFING && brief_tracks.size() == 0)
+ req_mode = MENU;
+
+ if (req_mode == INTRO && intro_tracks.size() == 0)
+ req_mode = MENU;
+
+ if (req_mode == VICTORY && victory_tracks.size() == 0)
+ req_mode = MENU;
+
+ if (req_mode == DEFEAT && defeat_tracks.size() == 0)
+ req_mode = MENU;
+
+ if (req_mode == CREDITS && credit_tracks.size() == 0)
+ req_mode = MENU;
+
+ if (req_mode == MENU && menu_tracks.size() == 0)
+ req_mode = NONE;
+
+ return req_mode;
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+MusicDirector::IsNoMusic()
+{
+ if (music_director)
+ return music_director->no_music;
+
+ return true;
+}
+
+// +-------------------------------------------------------------------+
+
+void
+MusicDirector::GetNextTrack(int index)
+{
+ List<Text>* tracks = 0;
+
+ switch (mode) {
+ case MENU: tracks = &menu_tracks; break;
+ case INTRO: tracks = &intro_tracks; break;
+ case BRIEFING: tracks = &brief_tracks; break;
+ case DEBRIEFING: tracks = &debrief_tracks; break;
+ case PROMOTION: tracks = &promote_tracks; break;
+ case FLIGHT: tracks = &flight_tracks; break;
+ case COMBAT: tracks = &combat_tracks; break;
+ case LAUNCH: tracks = &launch_tracks; break;
+ case RECOVERY: tracks = &recovery_tracks; break;
+ case VICTORY: tracks = &victory_tracks; break;
+ case DEFEAT: tracks = &defeat_tracks; break;
+ case CREDITS: tracks = &credit_tracks; break;
+ default: tracks = 0; break;
+ }
+
+ if (tracks && tracks->size()) {
+ if (next_track)
+ delete next_track;
+
+ if (index < 0 || index >= tracks->size()) {
+ index = 0;
+
+ if (mode == INTRO) {
+ mode = MENU;
+ ShuffleTracks();
+ tracks = &menu_tracks;
+
+ ::Print("MusicDirector: INTRO mode complete, switching to MENU\n");
+
+ if (!tracks || !tracks->size())
+ return;
+ }
+ }
+
+ next_track = new(__FILE__,__LINE__) MusicTrack(*tracks->at(index), mode, index);
+ next_track->FadeIn();
+ }
+
+ else if (next_track) {
+ next_track->FadeOut();
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+MusicDirector::ShuffleTracks()
+{
+ List<Text>* tracks = 0;
+
+ switch (mode) {
+ case MENU: tracks = &menu_tracks; break;
+ case INTRO: tracks = &intro_tracks; break;
+ case BRIEFING: tracks = &brief_tracks; break;
+ case DEBRIEFING: tracks = &debrief_tracks; break;
+ case PROMOTION: tracks = &promote_tracks; break;
+ case FLIGHT: tracks = &flight_tracks; break;
+ case COMBAT: tracks = &combat_tracks; break;
+ case LAUNCH: tracks = &launch_tracks; break;
+ case RECOVERY: tracks = &recovery_tracks; break;
+ case VICTORY: tracks = &victory_tracks; break;
+ case DEFEAT: tracks = &defeat_tracks; break;
+ case CREDITS: tracks = &credit_tracks; break;
+ default: tracks = 0; break;
+ }
+
+ if (tracks && tracks->size() > 1) {
+ tracks->sort();
+
+ Text* t = tracks->at(0);
+
+ if (!isdigit(*t[0]))
+ tracks->shuffle();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD WINAPI MusicDirectorThreadProc(LPVOID link);
+
+void
+MusicDirector::StartThread()
+{
+ if (hproc != 0) {
+ DWORD result = 0;
+ GetExitCodeThread(hproc, &result);
+
+ if (result != STILL_ACTIVE) {
+ CloseHandle(hproc);
+ hproc = 0;
+ }
+ else {
+ return;
+ }
+ }
+
+ if (hproc == 0) {
+ DWORD thread_id = 0;
+ hproc = CreateThread(0, 4096, MusicDirectorThreadProc, (LPVOID) this, 0, &thread_id);
+
+ if (hproc == 0) {
+ static int report = 10;
+ if (report > 0) {
+ ::Print("WARNING: MusicDirector failed to create thread (err=%08x)\n", GetLastError());
+ report--;
+
+ if (report == 0)
+ ::Print(" Further warnings of this type will be supressed.\n");
+ }
+ }
+ }
+}
+
+void
+MusicDirector::StopThread()
+{
+ if (hproc != 0) {
+ SetMode(SHUTDOWN);
+ WaitForSingleObject(hproc, 1500);
+ CloseHandle(hproc);
+ hproc = 0;
+ }
+}
+
+DWORD WINAPI MusicDirectorThreadProc(LPVOID link)
+{
+ MusicDirector* dir = (MusicDirector*) link;
+
+ if (dir) {
+ while (dir->GetMode() != MusicDirector::SHUTDOWN) {
+ dir->ExecFrame();
+ Sleep(100);
+ }
+
+ return 0;
+ }
+
+ return (DWORD) E_POINTER;
+}
+
diff --git a/Stars45/MusicDirector.h b/Stars45/MusicDirector.h
new file mode 100644
index 0000000..6299d5b
--- /dev/null
+++ b/Stars45/MusicDirector.h
@@ -0,0 +1,114 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MusicDirector.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Music Director class to manage selection, setup, and playback
+ of background music tracks for both menu and game modes
+*/
+
+
+#ifndef MusicDirector_h
+#define MusicDirector_h
+
+#include "Types.h"
+#include "List.h"
+#include "Text.h"
+
+// +-------------------------------------------------------------------+
+
+class MusicTrack;
+
+// +-------------------------------------------------------------------+
+
+class MusicDirector
+{
+public:
+ enum MODES {
+ NONE,
+
+ // menu modes:
+
+ MENU,
+ INTRO,
+ BRIEFING,
+ DEBRIEFING,
+ PROMOTION,
+ VICTORY,
+ DEFEAT,
+ CREDITS,
+
+ // in game modes:
+
+ FLIGHT,
+ COMBAT,
+ LAUNCH,
+ RECOVERY,
+
+ // special modes:
+ SHUTDOWN
+ };
+
+ enum TRANSITIONS {
+ CUT,
+ FADE_OUT,
+ FADE_IN,
+ FADE_BOTH,
+ CROSS_FADE
+ };
+
+ MusicDirector();
+ ~MusicDirector();
+
+ // Operations:
+ void ExecFrame();
+ void ScanTracks();
+
+ int CheckMode(int mode);
+ int GetMode() const { return mode; }
+
+ static void Initialize();
+ static void Close();
+ static MusicDirector* GetInstance();
+ static void SetMode(int mode);
+ static const char* GetModeName(int mode);
+ static bool IsNoMusic();
+
+protected:
+ void StartThread();
+ void StopThread();
+ void GetNextTrack(int index);
+ void ShuffleTracks();
+
+ int mode;
+ int transition;
+
+ MusicTrack* track;
+ MusicTrack* next_track;
+
+ List<Text> menu_tracks;
+ List<Text> intro_tracks;
+ List<Text> brief_tracks;
+ List<Text> debrief_tracks;
+ List<Text> promote_tracks;
+ List<Text> flight_tracks;
+ List<Text> combat_tracks;
+ List<Text> launch_tracks;
+ List<Text> recovery_tracks;
+ List<Text> victory_tracks;
+ List<Text> defeat_tracks;
+ List<Text> credit_tracks;
+
+ bool no_music;
+
+ HANDLE hproc;
+ ThreadSync sync;
+};
+
+#endif MusicDirector_h \ No newline at end of file
diff --git a/Stars45/MusicTrack.cpp b/Stars45/MusicTrack.cpp
new file mode 100644
index 0000000..8e69890
--- /dev/null
+++ b/Stars45/MusicTrack.cpp
@@ -0,0 +1,273 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MusicTrack.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Music Director class to manage selection, setup, and playback
+ of background music tracks for both menu and game modes
+*/
+
+
+#include "MemDebug.h"
+#include "MusicTrack.h"
+#include "MusicDirector.h"
+#include "Starshatter.h"
+#include "AudioConfig.h"
+
+#include "Game.h"
+#include "Sound.h"
+
+// +-------------------------------------------------------------------+
+
+const double FADE_TIME = 1.5;
+const double SILENCE = -5000;
+
+// +-------------------------------------------------------------------+
+
+MusicTrack::MusicTrack(const Text& txt, int m, int n)
+ : name(txt), sound(0), state(NONE), mode(m), index(n),
+ fade(0), fade_time(FADE_TIME)
+{
+ long max_vol = 0;
+
+ if (mode >= MusicDirector::FLIGHT)
+ max_vol = AudioConfig::GameMusic();
+ else
+ max_vol = AudioConfig::MenuMusic();
+
+ if (max_vol <= AudioConfig::Silence())
+ return;
+
+ name.setSensitive(false);
+
+ if (name.contains(".ogg")) {
+ sound = Sound::CreateOggStream(name);
+
+ if (name.contains("-loop")) {
+ sound->SetFlags(Sound::STREAMED |
+ Sound::OGGVORBIS |
+ Sound::LOOP |
+ Sound::LOCKED);
+ }
+
+ else {
+ sound->SetFlags(Sound::STREAMED |
+ Sound::OGGVORBIS |
+ Sound::LOCKED);
+ }
+
+ sound->SetVolume((long) SILENCE);
+ }
+}
+
+MusicTrack::~MusicTrack()
+{
+ if (sound) {
+ sound->Stop();
+ sound->Release();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MusicTrack::ExecFrame()
+{
+ bool music_pause = false;
+
+ Starshatter* stars = Starshatter::GetInstance();
+ if (stars) {
+ music_pause = (stars->GetGameMode() == Starshatter::PLAY_MODE) &&
+ Game::Paused();
+ }
+
+ if (sound && !music_pause) {
+ double fvol = 1;
+ long volume = 0;
+
+ switch (state) {
+ case PLAY:
+ if (sound->IsReady())
+ sound->Play();
+ SetVolume(volume);
+ break;
+
+ case FADE_IN:
+ if (sound->IsReady())
+ sound->Play();
+
+ if (fade > 0) {
+ fvol = fade/fade_time;
+ volume = (long) (fvol * SILENCE);
+ SetVolume(volume);
+ }
+
+ if (fade < 0.01)
+ state = PLAY;
+ break;
+
+ case FADE_OUT:
+ if (sound->IsReady())
+ sound->Play();
+
+ if (fade > 0) {
+ fvol = 1 - fade/fade_time;
+ volume = (long) (fvol * SILENCE);
+ SetVolume(volume);
+ }
+
+ if (fade < 0.01)
+ state = STOP;
+ break;
+
+ case STOP:
+ if (sound->IsPlaying()) {
+ sound->Stop();
+ sound->Release();
+ sound = 0;
+ }
+ break;
+ }
+
+ if (fade > 0)
+ fade -= Game::GUITime();
+
+ if (fade < 0)
+ fade = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MusicTrack::Play()
+{
+ state = PLAY;
+ fade = 0;
+}
+
+void
+MusicTrack::Stop()
+{
+ state = STOP;
+ fade = 0;
+}
+
+void
+MusicTrack::FadeIn()
+{
+ if (state != FADE_IN && state != PLAY) {
+ state = FADE_IN;
+ fade = fade_time;
+ }
+}
+
+void
+MusicTrack::FadeOut()
+{
+ if (state != FADE_OUT && state != STOP) {
+ state = FADE_OUT;
+ fade = fade_time;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MusicTrack::IsReady() const
+{
+ if (sound)
+ return sound->IsReady();
+
+ return false;
+}
+
+int
+MusicTrack::IsPlaying() const
+{
+ if (sound)
+ return sound->IsPlaying();
+
+ return false;
+}
+
+int
+MusicTrack::IsDone() const
+{
+ if (sound)
+ return sound->IsDone() || sound->LoopCount() >= 5;
+
+ return true;
+}
+
+int
+MusicTrack::IsLooped() const
+{
+ if (sound)
+ return sound->IsDone() || sound->LoopCount() >= 4;
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+long
+MusicTrack::GetVolume() const
+{
+ if (sound)
+ return sound->GetVolume();
+
+ return 0;
+}
+
+void
+MusicTrack::SetVolume(long v)
+{
+ if (sound) {
+ long max_vol = 0;
+
+ if (mode >= MusicDirector::FLIGHT)
+ max_vol = AudioConfig::GameMusic();
+ else
+ max_vol = AudioConfig::MenuMusic();
+
+ if (v > max_vol)
+ v = max_vol;
+
+ sound->SetVolume(v);
+ }
+}
+
+double
+MusicTrack::GetTotalTime() const
+{
+ if (sound)
+ return sound->GetTotalTime();
+
+ return 0;
+}
+
+double
+MusicTrack::GetTimeRemaining() const
+{
+ if (sound)
+ return sound->GetTimeRemaining();
+
+ return 0;
+}
+
+double
+MusicTrack::GetTimeElapsed() const
+{
+ if (sound)
+ return sound->GetTimeElapsed();
+
+ return 0;
+}
+
diff --git a/Stars45/MusicTrack.h b/Stars45/MusicTrack.h
new file mode 100644
index 0000000..366e810
--- /dev/null
+++ b/Stars45/MusicTrack.h
@@ -0,0 +1,77 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: MusicTrack.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ MusicTrack class
+*/
+
+
+#ifndef MusicTrack_h
+#define MusicTrack_h
+
+#include "Types.h"
+#include "List.h"
+#include "Text.h"
+
+// +-------------------------------------------------------------------+
+
+class Sound;
+
+// +-------------------------------------------------------------------+
+
+class MusicTrack
+{
+public:
+ enum STATE { NONE, FADE_IN, PLAY, FADE_OUT, STOP };
+
+ MusicTrack(const Text& name, int mode=0, int index=0);
+ virtual ~MusicTrack();
+
+ // Operations:
+ virtual void ExecFrame();
+
+ virtual void Play();
+ virtual void Stop();
+ virtual void FadeIn();
+ virtual void FadeOut();
+
+ // accessors / mutators
+ const Text& Name() const { return name; }
+ Sound* GetSound() const { return sound; }
+ int GetState() const { return state; }
+ int GetMode() const { return mode; }
+ int GetIndex() const { return index; }
+
+ int IsReady() const;
+ int IsPlaying() const;
+ int IsDone() const;
+ int IsLooped() const;
+
+ virtual long GetVolume() const;
+ virtual void SetVolume(long v);
+
+ virtual double GetTotalTime() const;
+ virtual double GetTimeRemaining() const;
+ virtual double GetTimeElapsed() const;
+
+ virtual double GetFadeTime() const { return fade_time; }
+ virtual void SetFadeTime(double t) { fade_time = t; }
+
+protected:
+ Text name;
+ Sound* sound;
+ int state;
+ int mode;
+ int index;
+ double fade;
+ double fade_time;
+};
+
+#endif MusicTrack_h \ No newline at end of file
diff --git a/Stars45/NPClient.h b/Stars45/NPClient.h
new file mode 100644
index 0000000..04a34b1
--- /dev/null
+++ b/Stars45/NPClient.h
@@ -0,0 +1,190 @@
+// *******************************************************************************
+// *
+// * Module Name:
+// * NPClient.h
+// *
+// * Doyle Nickless -- 13 Jan, 2003 -- for Eye Control Technology.
+// *
+// * Abstract:
+// * Header for NaturalPoint Game Client API.
+// *
+// * Environment:
+// * Microsoft Windows -- User mode
+// *
+// *******************************************************************************
+
+#ifndef _NPCLIENT_H_DEFINED_
+#define _NPCLIENT_H_DEFINED_
+
+#pragma pack( push, npclient_h ) // Save current pack value
+#pragma pack(1)
+
+//////////////////
+/// Defines //////////////////////////////////////////////////////////////////////
+/////////////////
+#define VERSION_MAJOR 1
+#define VERSION_MINOR 0
+#define VERSION_BUILD 1
+
+// magic to get the preprocessor to do what we want
+#define lita(arg) #arg
+#define xlita(arg) lita(arg)
+#define cat3(w,x,z) w##.##x##.##z##\000
+#define xcat3(w,x,z) cat3(w,x,z)
+#define VERSION_STRING xlita(xcat3(VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD))
+//
+// Versioning hasn't been worked out yet...
+//
+// The following is the previous spec definition of versioning info -- I can probably do
+// something very similar to this -- will keep you posted.
+//
+// request version information using 2 messages, they cannot be expected to arrive in a specific order - so always parse using the High byte
+// the messages have a NPCONTROL byte in the first parameter, and the second parameter has packed bytes.
+// Message 1) (first parameter)NPCONTROL : (second parameter) (High Byte)NPVERSIONMAJOR (Low Byte) major version number data
+// Message 2) (first parameter)NPCONTROL : (second parameter) (High Byte)NPVERSIONMINOR (Low Byte) minor version number data
+
+#define NPQUERYVERSION 1040
+
+#define NPSTATUS_REMOTEACTIVE 0
+#define NPSTATUS_REMOTEDISABLED 1
+
+// CONTROL DATA SUBFIELDS
+#define NPVERSIONMAJOR 1
+#define NPVERSIONMINOR 2
+
+// DATA FIELDS
+#define NPControl 8 // indicates a control data field
+ // the second parameter of a message bearing control data information contains a packed data format.
+ // The High byte indicates what the data is, and the Low byte contains the actual data
+// roll, pitch, yaw
+#define NPRoll 1 // +/- 16383 (representing +/- 180) [data = input - 16383]
+#define NPPitch 2 // +/- 16383 (representing +/- 180) [data = input - 16383]
+#define NPYaw 4 // +/- 16383 (representing +/- 180) [data = input - 16383]
+
+// x, y, z - remaining 6dof coordinates
+#define NPX 16 // +/- 16383 [data = input - 16383]
+#define NPY 32 // +/- 16383 [data = input - 16383]
+#define NPZ 64 // +/- 16383 [data = input - 16383]
+
+// raw object position from imager
+#define NPRawX 128 // 0..25600 (actual value is multiplied x 100 to pass two decimal places of precision) [data = input / 100]
+#define NPRawY 256 // 0..25600 (actual value is multiplied x 100 to pass two decimal places of precision) [data = input / 100]
+#define NPRawZ 512 // 0..25600 (actual value is multiplied x 100 to pass two decimal places of precision) [data = input / 100]
+
+// x, y, z deltas from raw imager position
+#define NPDeltaX 1024 // +/- 2560 (actual value is multiplied x 10 to pass two decimal places of precision) [data = (input / 10) - 256]
+#define NPDeltaY 2048 // +/- 2560 (actual value is multiplied x 10 to pass two decimal places of precision) [data = (input / 10) - 256]
+#define NPDeltaZ 4096 // +/- 2560 (actual value is multiplied x 10 to pass two decimal places of precision) [data = (input / 10) - 256]
+
+// raw object position from imager
+#define NPSmoothX 8192 // 0..32766 (actual value is multiplied x 10 to pass one decimal place of precision) [data = input / 10]
+#define NPSmoothY 16384 // 0..32766 (actual value is multiplied x 10 to pass one decimal place of precision) [data = input / 10]
+#define NPSmoothZ 32768 // 0..32766 (actual value is multiplied x 10 to pass one decimal place of precision) [data = input / 10]
+
+
+//////////////////
+/// Typedefs /////////////////////////////////////////////////////////////////////
+/////////////////
+
+// NPESULT values are returned from the Game Client API functions.
+//
+typedef enum tagNPResult
+{
+ NP_OK = 0,
+ NP_ERR_DEVICE_NOT_PRESENT,
+ NP_ERR_UNSUPPORTED_OS,
+ NP_ERR_INVALID_ARG,
+ NP_ERR_DLL_NOT_FOUND,
+ NP_ERR_NO_DATA,
+ NP_ERR_INTERNAL_DATA
+
+} NPRESULT;
+
+typedef struct tagTrackIRSignature
+{
+ char DllSignature[200];
+ char AppSignature[200];
+
+} SIGNATUREDATA, *LPTRACKIRSIGNATURE;
+
+typedef struct tagTrackIRData
+{
+ unsigned short wNPStatus;
+ unsigned short wPFrameSignature;
+ unsigned long dwNPIOData;
+
+ float fNPRoll;
+ float fNPPitch;
+ float fNPYaw;
+ float fNPX;
+ float fNPY;
+ float fNPZ;
+ float fNPRawX;
+ float fNPRawY;
+ float fNPRawZ;
+ float fNPDeltaX;
+ float fNPDeltaY;
+ float fNPDeltaZ;
+ float fNPSmoothX;
+ float fNPSmoothY;
+ float fNPSmoothZ;
+
+} TRACKIRDATA, *LPTRACKIRDATA;
+
+
+//
+// Typedef for pointer to the notify callback function that is implemented within
+// the client -- this function receives head tracker reports from the game client API
+//
+typedef NPRESULT (__stdcall *PF_NOTIFYCALLBACK)( unsigned short, unsigned short );
+
+// Typedefs for game client API functions (useful for declaring pointers to these
+// functions within the client for use during GetProcAddress() ops)
+//
+typedef NPRESULT (__stdcall *PF_NP_REGISTERWINDOWHANDLE)( HWND );
+typedef NPRESULT (__stdcall *PF_NP_UNREGISTERWINDOWHANDLE)( void );
+typedef NPRESULT (__stdcall *PF_NP_REGISTERPROGRAMPROFILEID)( unsigned short );
+typedef NPRESULT (__stdcall *PF_NP_QUERYVERSION)( unsigned short* );
+typedef NPRESULT (__stdcall *PF_NP_REQUESTDATA)( unsigned short );
+typedef NPRESULT (__stdcall *PF_NP_GETSIGNATURE)( LPTRACKIRSIGNATURE );
+typedef NPRESULT (__stdcall *PF_NP_GETDATA)( LPTRACKIRDATA );
+typedef NPRESULT (__stdcall *PF_NP_REGISTERNOTIFY)( PF_NOTIFYCALLBACK );
+typedef NPRESULT (__stdcall *PF_NP_UNREGISTERNOTIFY)( void );
+typedef NPRESULT (__stdcall *PF_NP_STARTCURSOR)( void );
+typedef NPRESULT (__stdcall *PF_NP_STOPCURSOR)( void );
+typedef NPRESULT (__stdcall *PF_NP_RECENTER)( void );
+typedef NPRESULT (__stdcall *PF_NP_STARTDATATRANSMISSION)( void );
+typedef NPRESULT (__stdcall *PF_NP_STOPDATATRANSMISSION)( void );
+
+//// Function Prototypes ///////////////////////////////////////////////
+//
+// Functions exported from game client API DLL ( note __stdcall calling convention
+// is used for ease of interface to clients of differing implementations including
+// C, C++, Pascal (Delphi) and VB. )
+//
+NPRESULT __stdcall NP_RegisterWindowHandle( HWND hWnd );
+NPRESULT __stdcall NP_UnregisterWindowHandle( void );
+NPRESULT __stdcall NP_RegisterProgramProfileID( unsigned short wPPID );
+NPRESULT __stdcall NP_QueryVersion( unsigned short* pwVersion );
+NPRESULT __stdcall NP_RequestData( unsigned short wDataReq );
+NPRESULT __stdcall NP_GetSignature( LPTRACKIRSIGNATURE pSignature );
+NPRESULT __stdcall NP_GetData( LPTRACKIRDATA pTID );
+NPRESULT __stdcall NP_RegisterNotify( PF_NOTIFYCALLBACK pfNotify );
+NPRESULT __stdcall NP_UnregisterNotify( void );
+NPRESULT __stdcall NP_StartCursor( void );
+NPRESULT __stdcall NP_StopCursor( void );
+NPRESULT __stdcall NP_ReCenter( void );
+NPRESULT __stdcall NP_StartDataTransmission( void );
+NPRESULT __stdcall NP_StopDataTransmission( void );
+
+/////////////////////////////////////////////////////////////////////////
+
+#pragma pack( pop, npclient_h ) // Ensure previous pack value is restored
+
+#endif // #ifdef NPCLIENT_H_DEFINED_
+
+//
+// *** End of file: NPClient.h ***
+//
+
+
diff --git a/Stars45/NPClientWraps.cpp b/Stars45/NPClientWraps.cpp
new file mode 100644
index 0000000..015526c
--- /dev/null
+++ b/Stars45/NPClientWraps.cpp
@@ -0,0 +1,257 @@
+// *******************************************************************************
+// *
+// * Module Name:
+// * NPClientWraps.cpp
+// *
+// * Software Engineer:
+// * Doyle Nickless - GoFlight Inc., for Eye Control Technology.
+// *
+// * Abstract:
+// * This module implements the wrapper code for interfacing to the NaturalPoint
+// * Game Client API. Developers of client apps can include this module into
+// * their projects to simplify communication with the NaturalPoint software.
+// *
+// * This is necessary since the NPClient DLL is run-time linked rather than
+// * load-time linked, avoiding the need to link a static library into the
+// * client program (only this module is needed, and can be supplied in source
+// * form.)
+// *
+// * Environment:
+// * User mode
+// *
+// *******************************************************************************
+//
+#include "MemDebug.h"
+#include "Game.h"
+#include "Text.h"
+
+#include "NPClient.h"
+#include "NPClientWraps.h"
+
+/////////////
+// Defines ///////////////////////////////////////////////////////////////////////
+/////////////
+//
+
+/////////////////
+// Global Data ///////////////////////////////////////////////////////////////////
+/////////////////
+//
+PF_NP_REGISTERWINDOWHANDLE gpfNP_RegisterWindowHandle = NULL;
+PF_NP_UNREGISTERWINDOWHANDLE gpfNP_UnregisterWindowHandle = NULL;
+PF_NP_REGISTERPROGRAMPROFILEID gpfNP_RegisterProgramProfileID = NULL;
+PF_NP_QUERYVERSION gpfNP_QueryVersion = NULL;
+PF_NP_REQUESTDATA gpfNP_RequestData = NULL;
+PF_NP_GETSIGNATURE gpfNP_GetSignature = NULL;
+PF_NP_GETDATA gpfNP_GetData = NULL;
+PF_NP_STARTCURSOR gpfNP_StartCursor = NULL;
+PF_NP_STOPCURSOR gpfNP_StopCursor = NULL;
+PF_NP_RECENTER gpfNP_ReCenter = NULL;
+PF_NP_STARTDATATRANSMISSION gpfNP_StartDataTransmission = NULL;
+PF_NP_STOPDATATRANSMISSION gpfNP_StopDataTransmission = NULL;
+
+HMODULE ghNPClientDLL = (HMODULE)NULL;
+
+////////////////////////////////////////////////////
+// NaturalPoint Game Client API function wrappers /////////////////////////////
+////////////////////////////////////////////////////
+//
+NPRESULT __stdcall NP_RegisterWindowHandle( HWND hWnd )
+{
+ NPRESULT result = NP_ERR_DLL_NOT_FOUND;
+
+ if( NULL != gpfNP_RegisterWindowHandle )
+ result = (*gpfNP_RegisterWindowHandle)( hWnd );
+
+ return result;
+} // NP_RegisterWindowHandle()
+
+
+NPRESULT __stdcall NP_UnregisterWindowHandle()
+{
+ NPRESULT result = NP_ERR_DLL_NOT_FOUND;
+
+ if( NULL != gpfNP_UnregisterWindowHandle )
+ result = (*gpfNP_UnregisterWindowHandle)();
+
+ return result;
+} // NP_UnregisterWindowHandle()
+
+
+NPRESULT __stdcall NP_RegisterProgramProfileID( unsigned short wPPID )
+{
+ NPRESULT result = NP_ERR_DLL_NOT_FOUND;
+
+ if( NULL != gpfNP_RegisterProgramProfileID )
+ result = (*gpfNP_RegisterProgramProfileID)( wPPID );
+
+ return result;
+} // NP_RegisterProgramProfileID()
+
+
+NPRESULT __stdcall NP_QueryVersion( unsigned short* pwVersion )
+{
+ NPRESULT result = NP_ERR_DLL_NOT_FOUND;
+
+ if( NULL != gpfNP_QueryVersion )
+ result = (*gpfNP_QueryVersion)( pwVersion );
+
+ return result;
+} // NP_QueryVersion()
+
+
+NPRESULT __stdcall NP_RequestData( unsigned short wDataReq )
+{
+ NPRESULT result = NP_ERR_DLL_NOT_FOUND;
+
+ if( NULL != gpfNP_RequestData )
+ result = (*gpfNP_RequestData)( wDataReq );
+
+ return result;
+} // NP_RequestData()
+
+NPRESULT __stdcall NP_GetSignature( LPTRACKIRSIGNATURE pSignature )
+{
+ NPRESULT result = NP_ERR_DLL_NOT_FOUND;
+
+ if( NULL != gpfNP_GetSignature )
+ result = (*gpfNP_GetSignature)( pSignature );
+
+ return result;
+} // NP_GetSignature()
+
+
+NPRESULT __stdcall NP_GetData( LPTRACKIRDATA pTID )
+{
+ NPRESULT result = NP_ERR_DLL_NOT_FOUND;
+
+ if( NULL != gpfNP_GetData )
+ result = (*gpfNP_GetData)( pTID );
+
+ return result;
+} // NP_GetData()
+
+
+NPRESULT __stdcall NP_StartCursor()
+{
+ NPRESULT result = NP_ERR_DLL_NOT_FOUND;
+
+ if( NULL != gpfNP_StartCursor )
+ result = (*gpfNP_StartCursor)();
+
+ return result;
+} // NP_StartCursor()
+
+
+NPRESULT __stdcall NP_StopCursor()
+{
+ NPRESULT result = NP_ERR_DLL_NOT_FOUND;
+
+ if( NULL != gpfNP_StopCursor )
+ result = (*gpfNP_StopCursor)();
+
+ return result;
+} // NP_StopCursor()
+
+
+NPRESULT __stdcall NP_ReCenter()
+{
+ NPRESULT result = NP_ERR_DLL_NOT_FOUND;
+
+ if( NULL != gpfNP_ReCenter )
+ result = (*gpfNP_ReCenter)();
+
+ return result;
+} // NP_ReCenter()
+
+
+NPRESULT __stdcall NP_StartDataTransmission()
+{
+ NPRESULT result = NP_ERR_DLL_NOT_FOUND;
+
+ if( NULL != gpfNP_StartDataTransmission )
+ result = (*gpfNP_StartDataTransmission)();
+
+ return result;
+} // NP_StartDataTransmission()
+
+
+NPRESULT __stdcall NP_StopDataTransmission()
+{
+ NPRESULT result = NP_ERR_DLL_NOT_FOUND;
+
+ if( NULL != gpfNP_StopDataTransmission )
+ result = (*gpfNP_StopDataTransmission)();
+
+ return result;
+} // NP_StopDataTransmission()
+
+
+//////////////////////////////////////////////////////////////////////////////
+// NPClientInit() -- Loads the DLL and retrieves pointers to all exports
+//
+NPRESULT NPClient_Init( const char* csDLLPath )
+{
+ NPRESULT result = NP_OK;
+
+ Text csNPClientDLLFullPath;
+
+ if (csDLLPath && *csDLLPath)
+ csNPClientDLLFullPath = Text(csDLLPath) + "\\";
+ csNPClientDLLFullPath += "NPClient.dll";
+
+ ghNPClientDLL = ::LoadLibrary( csNPClientDLLFullPath.data() );
+
+ if (NULL != ghNPClientDLL) {
+ // verify the dll signature
+ gpfNP_GetSignature = (PF_NP_GETSIGNATURE)::GetProcAddress( ghNPClientDLL, "NP_GetSignature" );
+
+ SIGNATUREDATA pSignature;
+ SIGNATUREDATA verifySignature;
+ // init the signatures
+ strcpy(verifySignature.DllSignature, "precise head tracking\n put your head into the game\n now go look around\n\n Copyright EyeControl Technologies");
+ strcpy(verifySignature.AppSignature, "hardware camera\n software processing data\n track user movement\n\n Copyright EyeControl Technologies");
+ // query the dll and compare the results
+ NPRESULT vresult = NP_GetSignature( &pSignature );
+ if( vresult == NP_OK )
+ {
+ if ((strcmp(verifySignature.DllSignature,pSignature.DllSignature)==0)
+ && (strcmp(verifySignature.AppSignature,pSignature.AppSignature)==0))
+ {
+ result = NP_OK;
+
+ // Get addresses of all exported functions
+ gpfNP_RegisterWindowHandle = (PF_NP_REGISTERWINDOWHANDLE)::GetProcAddress( ghNPClientDLL, "NP_RegisterWindowHandle" );
+ gpfNP_UnregisterWindowHandle = (PF_NP_UNREGISTERWINDOWHANDLE)::GetProcAddress( ghNPClientDLL, "NP_UnregisterWindowHandle" );
+ gpfNP_RegisterProgramProfileID = (PF_NP_REGISTERPROGRAMPROFILEID)::GetProcAddress( ghNPClientDLL, "NP_RegisterProgramProfileID" );
+ gpfNP_QueryVersion = (PF_NP_QUERYVERSION)::GetProcAddress( ghNPClientDLL, "NP_QueryVersion" );
+ gpfNP_RequestData = (PF_NP_REQUESTDATA)::GetProcAddress( ghNPClientDLL, "NP_RequestData" );
+ gpfNP_GetData = (PF_NP_GETDATA)::GetProcAddress( ghNPClientDLL, "NP_GetData" );
+ gpfNP_StartCursor = (PF_NP_STARTCURSOR)::GetProcAddress( ghNPClientDLL, "NP_StartCursor" );
+ gpfNP_StopCursor = (PF_NP_STOPCURSOR)::GetProcAddress( ghNPClientDLL, "NP_StopCursor" );
+ gpfNP_ReCenter = (PF_NP_RECENTER)::GetProcAddress( ghNPClientDLL, "NP_ReCenter" );
+ gpfNP_StartDataTransmission = (PF_NP_STARTDATATRANSMISSION)::GetProcAddress( ghNPClientDLL, "NP_StartDataTransmission" );
+ gpfNP_StopDataTransmission = (PF_NP_STOPDATATRANSMISSION)::GetProcAddress( ghNPClientDLL, "NP_StopDataTransmission" );
+ }
+ else
+ {
+ result = NP_ERR_DLL_NOT_FOUND;
+ }
+ }
+ else
+ {
+ result = NP_ERR_DLL_NOT_FOUND;
+ }
+ }
+ else
+ result = NP_ERR_DLL_NOT_FOUND;
+
+ return result;
+
+} // NPClient_Init()
+
+//////////////////////////////////////////////////////////////////////////////
+
+
+
+
diff --git a/Stars45/NPClientWraps.h b/Stars45/NPClientWraps.h
new file mode 100644
index 0000000..dbe579d
--- /dev/null
+++ b/Stars45/NPClientWraps.h
@@ -0,0 +1,33 @@
+// *******************************************************************************
+// *
+// * Module Name:
+// * NPClientWraps.h
+// *
+// * Software Engineer:
+// * Doyle Nickless - GoFlight Inc., for Eye Control Technology.
+// *
+// * Abstract:
+// * Header file for NPClientWraps.cpp module.
+// *
+// * Environment:
+// * User mode
+// *
+// *******************************************************************************
+//
+#ifndef _NPCLIENTWRAPS_H_DEFINED_
+#define _NPCLIENTWRAPS_H_DEFINED_
+
+#include "NPClient.h"
+
+/////////////
+// Defines ///////////////////////////////////////////////////////////////////////
+/////////////
+//
+
+/////////////////////////
+// Function Prototypes ///////////////////////////////////////////////////////////
+/////////////////////////
+//
+NPRESULT NPClient_Init( const char* csDLLPath );
+
+#endif // #ifdef _NPCLIENTWRAPS_H_DEFINED_
diff --git a/Stars45/NavAI.cpp b/Stars45/NavAI.cpp
new file mode 100644
index 0000000..c863064
--- /dev/null
+++ b/Stars45/NavAI.cpp
@@ -0,0 +1,622 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NavAI.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Automatic Navigator Artificial Intelligence class
+*/
+
+#include "MemDebug.h"
+#include "NavAI.h"
+#include "TacticalAI.h"
+#include "Instruction.h"
+#include "NavSystem.h"
+#include "QuantumDrive.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "ShipCtrl.h"
+#include "Drive.h"
+#include "Farcaster.h"
+#include "Shield.h"
+#include "Sim.h"
+#include "StarSystem.h"
+#include "KeyMap.h"
+#include "HUDView.h"
+#include "HUDSounds.h"
+
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+NavAI::NavAI(Ship* s)
+ : ShipAI(s), complete(false), brakes(0),
+ drop_state(0), quantum_state(0), farcaster(0), terrain_warning(false)
+{
+ seek_gain = 20;
+ seek_damp = 0.55;
+
+ delete tactical;
+ tactical = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+NavAI::~NavAI()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+NavAI::ExecFrame(double s)
+{
+ if (!ship) return;
+
+ seconds = s;
+
+ ship->SetDirectorInfo(" ");
+
+ if (ship->GetFlightPhase() == Ship::TAKEOFF)
+ takeoff = true;
+
+ else if (takeoff && ship->MissionClock() > 10000)
+ takeoff = false;
+
+ FindObjective();
+ Navigator();
+
+ // watch for disconnect:
+ if (ShipCtrl::Toggled(KEY_AUTO_NAV)) {
+ NavSystem* navsys = ship->GetNavSystem();
+ if (navsys) {
+ HUDView::GetInstance()->SetHUDMode(HUDView::HUD_MODE_TAC);
+ navsys->DisengageAutoNav();
+
+ Sim* sim = Sim::GetSim();
+ if (sim) {
+ ship->SetControls(sim->GetControls());
+ return;
+ }
+ }
+ }
+
+ static double time_til_change = 0.0;
+
+ if (time_til_change < 0.001) {
+ if (ship->GetShield()) {
+ Shield* shield = ship->GetShield();
+ double level = shield->GetPowerLevel();
+
+ if (ShipCtrl::KeyDown(KEY_SHIELDS_FULL)) {
+ HUDSounds::PlaySound(HUDSounds::SND_SHIELD_LEVEL);
+ shield->SetPowerLevel(100);
+ time_til_change = 0.5f;
+ }
+
+ else if (ShipCtrl::KeyDown(KEY_SHIELDS_ZERO)) {
+ HUDSounds::PlaySound(HUDSounds::SND_SHIELD_LEVEL);
+ shield->SetPowerLevel(0);
+ time_til_change = 0.5f;
+ }
+
+ else if (ShipCtrl::KeyDown(KEY_SHIELDS_UP)) {
+ if (level < 25) level = 25;
+ else if (level < 50) level = 50;
+ else if (level < 75) level = 75;
+ else level = 100;
+
+ HUDSounds::PlaySound(HUDSounds::SND_SHIELD_LEVEL);
+ shield->SetPowerLevel(level);
+ time_til_change = 0.5f;
+ }
+
+ else if (ShipCtrl::KeyDown(KEY_SHIELDS_DOWN)) {
+ if (level > 75) level = 75;
+ else if (level > 50) level = 50;
+ else if (level > 25) level = 25;
+ else level = 0;
+
+ HUDSounds::PlaySound(HUDSounds::SND_SHIELD_LEVEL);
+ shield->SetPowerLevel(level);
+ time_til_change = 0.5f;
+ }
+
+ }
+ }
+ else {
+ time_til_change -= seconds;
+ }
+
+ if (ShipCtrl::Toggled(KEY_DECOY))
+ ship->FireDecoy();
+
+ if (ShipCtrl::Toggled(KEY_LAUNCH_PROBE))
+ ship->LaunchProbe();
+
+ if (ShipCtrl::Toggled(KEY_GEAR_TOGGLE))
+ ship->ToggleGear();
+
+ if (ShipCtrl::Toggled(KEY_NAVLIGHT_TOGGLE))
+ ship->ToggleNavlights();
+
+ if (drop_state < 0) {
+ ship->DropOrbit();
+ return;
+ }
+
+ if (drop_state > 0) {
+ ship->MakeOrbit();
+ return;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavAI::FindObjective()
+{
+ navpt = 0;
+ distance = 0;
+
+ // runway takeoff:
+ if (takeoff) {
+ obj_w = ship->Location() + ship->Heading() * 10e3;
+ obj_w.y = ship->Location().y + 2e3;
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+ ship->SetDirectorInfo(Game::GetText("ai.takeoff"));
+ return;
+ }
+
+ // PART I: Find next NavPoint:
+ if (ship->GetNavSystem())
+ navpt = ship->GetNextNavPoint();
+
+ complete = !navpt;
+ if (complete) return;
+
+ // PART II: Compute Objective from NavPoint:
+ Point npt = navpt->Location();
+ Sim* sim = Sim::GetSim();
+ SimRegion* self_rgn = ship->GetRegion();
+ SimRegion* nav_rgn = navpt->Region();
+
+ if (self_rgn && !nav_rgn) {
+ nav_rgn = self_rgn;
+ navpt->SetRegion(nav_rgn);
+ }
+
+ if (self_rgn == nav_rgn) {
+ if (farcaster) {
+ if (farcaster->GetShip()->GetRegion() != self_rgn)
+ farcaster = farcaster->GetDest()->GetFarcaster();
+
+ obj_w = farcaster->EndPoint();
+ }
+
+ else {
+ obj_w = npt.OtherHand();
+ }
+
+ // distance from self to navpt:
+ distance = Point(obj_w - self->Location()).length();
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+
+ if (!ship->IsStarship())
+ objective.Normalize();
+
+ if (farcaster && distance < 1000)
+ farcaster = 0;
+ }
+
+ // PART III: Deal with orbital transitions:
+ else if (ship->IsDropship()) {
+ if (nav_rgn->GetOrbitalRegion()->Primary() ==
+ self_rgn->GetOrbitalRegion()->Primary()) {
+
+ Point npt = nav_rgn->Location() - self_rgn->Location();
+ obj_w = npt.OtherHand();
+
+ // distance from self to navpt:
+ distance = Point(obj_w - ship->Location()).length();
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+
+ if (nav_rgn->IsAirSpace()) {
+ drop_state = -1;
+ }
+ else if (nav_rgn->IsOrbital()) {
+ drop_state = 1;
+ }
+ }
+
+ // PART IIIa: Deal with farcaster jumps:
+ else if (nav_rgn->IsOrbital() && self_rgn->IsOrbital()) {
+ ListIter<Ship> s = self_rgn->Ships();
+ while (++s && !farcaster) {
+ if (s->GetFarcaster()) {
+ const Ship* dest = s->GetFarcaster()->GetDest();
+ if (dest && dest->GetRegion() == nav_rgn) {
+ farcaster = s->GetFarcaster();
+ }
+ }
+ }
+
+ if (farcaster) {
+ Point apt = farcaster->ApproachPoint(0);
+ Point npt = farcaster->StartPoint();
+ double r1 = (ship->Location() - npt).length();
+
+ if (r1 > 50e3) {
+ obj_w = apt;
+ distance = r1;
+ objective = Transform(obj_w);
+ }
+
+ else {
+ double r2 = (ship->Location() - apt).length();
+ double r3 = (npt - apt).length();
+
+ if (r1+r2 < 1.2*r3) {
+ obj_w = npt;
+ distance = r1;
+ objective = Transform(obj_w);
+ }
+ else {
+ obj_w = apt;
+ distance = r2;
+ objective = Transform(obj_w);
+ }
+ }
+ }
+ }
+ }
+
+ // PART IV: Deal with quantum jumps:
+ else if (ship->IsStarship()) {
+ quantum_state = 1;
+
+ Point npt = nav_rgn->Location() + navpt->Location();
+ npt -= self_rgn->Location();
+ obj_w = npt.OtherHand();
+
+ // distance from self to navpt:
+ distance = Point(obj_w - ship->Location()).length();
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavAI::Navigator()
+{
+ accumulator.Clear();
+ magnitude = 0;
+ brakes = 0;
+ hold = false;
+
+ if (navpt) {
+ if (navpt->Status() == Instruction::COMPLETE && navpt->HoldTime() > 0) {
+ ship->SetDirectorInfo(Game::GetText("ai.auto-hold"));
+ hold = true;
+ }
+ else {
+ ship->SetDirectorInfo(Game::GetText("ai.auto-nav"));
+ }
+ }
+ else {
+ ship->SetDirectorInfo(Game::GetText("ai.auto-stop"));
+ }
+
+ Accumulate(AvoidTerrain());
+ Accumulate(AvoidCollision());
+
+ if (!hold)
+ accumulator = SeekTarget();
+
+ HelmControl();
+ ThrottleControl();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavAI::HelmControl()
+{
+ // ----------------------------------------------------------
+ // STARSHIP HELM MODE
+ // ----------------------------------------------------------
+
+ if (ship->IsStarship()) {
+ ship->SetFLCSMode(Ship::FLCS_HELM);
+ ship->SetHelmHeading(accumulator.yaw);
+
+ if (accumulator.pitch > 45*DEGREES)
+ ship->SetHelmPitch(45*DEGREES);
+
+ else if (accumulator.pitch < -45*DEGREES)
+ ship->SetHelmPitch(-45*DEGREES);
+
+ else
+ ship->SetHelmPitch(accumulator.pitch);
+ }
+
+ // ----------------------------------------------------------
+ // FIGHTER FLCS AUTO MODE
+ // ----------------------------------------------------------
+
+ else {
+ ship->SetFLCSMode(Ship::FLCS_AUTO);
+
+ // are we being asked to flee?
+ if (fabs(accumulator.yaw) == 1.0 && accumulator.pitch == 0.0) {
+ accumulator.pitch = -0.7f;
+ accumulator.yaw *= 0.25f;
+ }
+
+ self->ApplyRoll((float) (accumulator.yaw * -0.4));
+ self->ApplyYaw((float) (accumulator.yaw * 0.2));
+
+ if (fabs(accumulator.yaw) > 0.5 && fabs(accumulator.pitch) < 0.1)
+ accumulator.pitch -= 0.1f;
+
+ if (accumulator.pitch != 0)
+ self->ApplyPitch((float) accumulator.pitch);
+
+ // if not turning, roll to orient with world coords:
+ if (fabs(accumulator.yaw) < 0.1) {
+ Point vrt = ((Camera*) &(self->Cam()))->vrt();
+ double deflection = vrt.y;
+ if (deflection != 0) {
+ double theta = asin(deflection/vrt.length());
+ self->ApplyRoll(-theta);
+ }
+ }
+
+ if (!ship->IsAirborne() || ship->AltitudeAGL() > 100)
+ ship->RaiseGear();
+ }
+
+ ship->SetTransX(0);
+ ship->SetTransY(0);
+ ship->SetTransZ(0);
+ ship->ExecFLCSFrame();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavAI::ThrottleControl()
+{
+ double ship_speed = ship->Velocity() * ship->Heading();
+ bool augmenter = false;
+
+ if (hold) {
+ throttle = 0;
+ brakes = 1;
+ }
+
+ else if (navpt) {
+ double speed = navpt->Speed();
+
+ if (speed < 10)
+ speed = 250;
+
+ throttle = 0;
+
+ if (Ship::GetFlightModel() > 0) {
+ if (ship_speed > speed + 10)
+ throttle = old_throttle - 0.25;
+
+ else if (ship_speed < speed - 10)
+ throttle = old_throttle + 0.25;
+
+ else
+ throttle = old_throttle;
+ }
+
+ else {
+ if (ship_speed > speed+5)
+ brakes = 0.25;
+
+ else if (ship_speed < speed-5)
+ throttle = 50;
+ }
+ }
+ else {
+ throttle = 0;
+ brakes = 0.25;
+ }
+
+ if (ship->IsAirborne() && ship->Class() < Ship::LCA) {
+ if (ship_speed < 250) {
+ throttle = 100;
+ brakes = 0;
+
+ if (ship_speed < 200)
+ augmenter = true;
+ }
+
+ else if (throttle < 20) {
+ throttle = 20;
+ }
+ }
+
+ old_throttle = throttle;
+ ship->SetThrottle(throttle);
+ ship->SetAugmenter(augmenter);
+
+ if (ship_speed > 1 && brakes > 0)
+ ship->SetTransY(-brakes * ship->Design()->trans_y);
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+NavAI::SeekTarget()
+{
+ if (!ship)
+ return Steer();
+
+ if (takeoff)
+ return Seek(objective);
+
+ if (navpt) {
+ if (quantum_state == 1) {
+ QuantumDrive* q = ship->GetQuantumDrive();
+
+ if (q) {
+ if (q->ActiveState() == QuantumDrive::ACTIVE_READY) {
+ q->SetDestination(navpt->Region(), navpt->Location());
+ q->Engage();
+ }
+
+ else if (q->ActiveState() == QuantumDrive::ACTIVE_POSTWARP) {
+ quantum_state = 0;
+ }
+ }
+ }
+
+ if (distance < 2 * self->Radius()) {
+ ship->SetNavptStatus(navpt, Instruction::COMPLETE);
+
+ return Steer();
+ }
+ else {
+ return Seek(objective);
+ }
+ }
+
+ return Steer();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavAI::Disengage()
+{
+ throttle = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+Point
+NavAI::Transform(const Point& point)
+{
+ if (ship && ship->IsStarship())
+ return point - self->Location();
+
+ return SteerAI::Transform(point);
+}
+
+Steer
+NavAI::Seek(const Point& point)
+{
+ // if ship is starship, the point is in relative world coordinates
+ // x: distance east(-) / west(+)
+ // y: altitude down(-) / up(+)
+ // z: distance north(-) / south(+)
+
+ if (ship && ship->IsStarship()) {
+ Steer result;
+
+ result.yaw = atan2(point.x, point.z) + PI;
+
+ double adjacent = sqrt(point.x * point.x + point.z * point.z);
+ if (fabs(point.y) > ship->Radius() && adjacent > ship->Radius())
+ result.pitch = atan(point.y / adjacent);
+
+ if (_isnan(result.yaw))
+ result.yaw = 0;
+
+ if (_isnan(result.pitch))
+ result.pitch = 0;
+
+ return result;
+ }
+
+ return SteerAI::Seek(point);
+}
+
+Steer
+NavAI::Flee(const Point& point)
+{
+ if (ship && ship->IsStarship()) {
+ Steer result = Seek(point);
+ result.yaw += PI;
+ result.pitch *= -1;
+ return result;
+ }
+
+ return SteerAI::Flee(point);
+}
+
+Steer
+NavAI::Avoid(const Point& point, float radius)
+{
+ if (ship && ship->IsStarship()) {
+ Steer result = Seek(point);
+
+ if (point * ship->BeamLine() > 0)
+ result.yaw -= PI/2;
+ else
+ result.yaw += PI/2;
+
+ return result;
+ }
+
+ return SteerAI::Avoid(point, radius);
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+NavAI::AvoidTerrain()
+{
+ Steer avoid;
+
+ terrain_warning = false;
+
+ if (!ship || !ship->GetRegion() || !ship->GetRegion()->IsActive() ||
+ takeoff || (navpt && navpt->Action() == Instruction::LAUNCH))
+ return avoid;
+
+ if (ship->IsAirborne() && ship->GetFlightPhase() == Ship::ACTIVE) {
+ // too low?
+ if (ship->AltitudeAGL() < 1000) {
+ terrain_warning = true;
+ ship->SetDirectorInfo(Game::GetText("ai.too-low"));
+
+ // way too low?
+ if (ship->AltitudeAGL() < 750) {
+ ship->SetDirectorInfo(Game::GetText("ai.way-too-low"));
+ }
+
+ // where will we be?
+ Point selfpt = ship->Location() + ship->Velocity() + Point(0, 10e3, 0);
+
+ // transform into camera coords:
+ Point obj = Transform(selfpt);
+
+ // pull up!
+ avoid = Seek(obj);
+ }
+ }
+
+ return avoid;
+}
+
+
+
diff --git a/Stars45/NavAI.h b/Stars45/NavAI.h
new file mode 100644
index 0000000..99dc724
--- /dev/null
+++ b/Stars45/NavAI.h
@@ -0,0 +1,74 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NavAI.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Automatic Navigator
+*/
+
+#ifndef NavAI_h
+#define NavAI_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "System.h"
+#include "ShipAI.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Farcaster;
+
+// +--------------------------------------------------------------------+
+
+class NavAI : public ShipAI
+{
+public:
+ NavAI(Ship* s);
+ virtual ~NavAI();
+
+ enum { DIR_TYPE = 2000 };
+ virtual int Type() const { return DIR_TYPE; }
+
+ virtual void ExecFrame(double seconds);
+ virtual int Subframe() const { return true; }
+ void Disengage();
+ bool Complete() const { return complete; }
+
+protected:
+ // behaviors:
+ virtual Steer SeekTarget();
+
+ // steering functions:
+ virtual Point Transform(const Point& pt);
+ virtual Steer Seek(const Point& point);
+ virtual Steer Flee(const Point& point);
+ virtual Steer Avoid(const Point& point, float radius);
+ virtual Steer AvoidTerrain();
+
+ // accumulate behaviors:
+ virtual void Navigator();
+ virtual void FindObjective();
+
+ virtual void HelmControl();
+ virtual void ThrottleControl();
+
+ bool complete;
+ int drop_state;
+ int quantum_state;
+ int terrain_warning;
+ double brakes;
+ Farcaster* farcaster;
+};
+
+// +--------------------------------------------------------------------+
+
+
+#endif NavAI_h
+
diff --git a/Stars45/NavDlg.cpp b/Stars45/NavDlg.cpp
new file mode 100644
index 0000000..5c702c4
--- /dev/null
+++ b/Stars45/NavDlg.cpp
@@ -0,0 +1,1126 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NavDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "NavDlg.h"
+#include "MapView.h"
+#include "MsnElemDlg.h"
+#include "BaseScreen.h"
+#include "Element.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Sim.h"
+#include "Galaxy.h"
+#include "StarSystem.h"
+#include "Instruction.h"
+#include "NavSystem.h"
+#include "FormatUtil.h"
+#include "Campaign.h"
+#include "Contact.h"
+#include "Mission.h"
+
+#include "Game.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ListBox.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+DEF_MAP_CLIENT(NavDlg, OnView);
+DEF_MAP_CLIENT(NavDlg, OnFilter);
+DEF_MAP_CLIENT(NavDlg, OnSelect);
+DEF_MAP_CLIENT(NavDlg, OnCommit);
+DEF_MAP_CLIENT(NavDlg, OnCancel);
+DEF_MAP_CLIENT(NavDlg, OnEngage);
+DEF_MAP_CLIENT(NavDlg, OnMapDown);
+DEF_MAP_CLIENT(NavDlg, OnMapMove);
+DEF_MAP_CLIENT(NavDlg, OnMapClick);
+DEF_MAP_CLIENT(NavDlg, OnClose);
+
+// +--------------------------------------------------------------------+
+
+static char* filter_name[] = {
+ "SYSTEM", "PLANET",
+ "SECTOR", "STATION",
+ "STARSHIP", "FIGHTER"
+};
+
+static char* commit_name = "Commit";
+static char* cancel_name = "Cancel";
+
+static Color commit_color(53,159,67);
+static Color cancel_color(160,8,8);
+
+
+// Supported Selection Modes:
+
+const int SELECT_NONE = -1;
+const int SELECT_SYSTEM = 0;
+const int SELECT_PLANET = 1;
+const int SELECT_REGION = 2;
+const int SELECT_STATION = 3;
+const int SELECT_STARSHIP = 4;
+const int SELECT_FIGHTER = 5;
+
+const int VIEW_GALAXY = 0;
+const int VIEW_SYSTEM = 1;
+const int VIEW_REGION = 2;
+
+// +--------------------------------------------------------------------+
+
+NavDlg::NavDlg(Screen* s, FormDef& def, BaseScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ loc_labels(0), dst_labels(0), loc_data(0), dst_data(0),
+ seln_list(0), info_list(0), seln_mode(SELECT_REGION),
+ nav_edit_mode(NAV_EDIT_NONE), star_map(0), map_win(0),
+ star_system(0), ship(0), mission(0), editor(false)
+{
+ Init(def);
+}
+
+NavDlg::~NavDlg()
+{ }
+
+void
+NavDlg::RegisterControls()
+{
+ int i;
+
+ map_win = FindControl(100);
+
+ if (map_win) {
+ star_map = new(__FILE__,__LINE__) MapView(map_win);
+
+ REGISTER_CLIENT(EID_LBUTTON_DOWN, map_win, NavDlg, OnMapDown);
+ REGISTER_CLIENT(EID_MOUSE_MOVE, map_win, NavDlg, OnMapMove);
+ REGISTER_CLIENT(EID_MAP_CLICK, map_win, NavDlg, OnMapClick);
+ }
+
+ for (i = 0; i < 3; i++) {
+ view_btn[i] = (Button*) FindControl(101 + i);
+ REGISTER_CLIENT(EID_CLICK, view_btn[i], NavDlg, OnView);
+ }
+
+ close_btn = (Button*) FindControl(2);
+
+ if (close_btn)
+ REGISTER_CLIENT(EID_CLICK, close_btn, NavDlg, OnClose);
+
+ zoom_in_btn = (Button*) FindControl(110);
+ zoom_out_btn = (Button*) FindControl(111);
+
+ for (i = 0; i < 6; i++) {
+ filter_btn[i] = (Button*) FindControl(401 + i);
+ REGISTER_CLIENT(EID_CLICK, filter_btn[i], NavDlg, OnFilter);
+ }
+
+ commit_btn = (Button*) FindControl(1);
+
+ if (commit_btn) {
+ REGISTER_CLIENT(EID_CLICK, commit_btn, NavDlg, OnEngage);
+ }
+
+ loc_labels = FindControl(601);
+ dst_labels = FindControl(602);
+ loc_data = FindControl(701);
+ dst_data = FindControl(702);
+
+ seln_list = (ListBox*) FindControl(801);
+
+ if (seln_list) {
+ seln_list->SetSelectedStyle(ListBox::LIST_ITEM_STYLE_FILLED_BOX);
+ REGISTER_CLIENT(EID_SELECT, seln_list, NavDlg, OnSelect);
+ }
+
+ info_list = (ListBox*) FindControl(802);
+
+ if (star_map) {
+ star_map->SetViewMode(VIEW_SYSTEM);
+ view_btn[1]->SetButtonState(1);
+
+ star_map->SetSelectionMode(2);
+ }
+
+ UpdateSelection();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavDlg::SetSystem(StarSystem* s)
+{
+ if (star_system == s)
+ return;
+
+ star_system = s;
+
+ if (star_map) {
+ Campaign* c = Campaign::GetCampaign();
+ Sim* sim = Sim::GetSim();
+
+ if (sim && sim->GetSystemList().size()) {
+ star_map->SetGalaxy(sim->GetSystemList());
+ }
+ else if (mission && mission->GetSystemList().size()) {
+ star_map->SetGalaxy(mission->GetSystemList());
+ }
+ else if (c && c->GetSystemList().size()) {
+ star_map->SetGalaxy(c->GetSystemList());
+ }
+ else {
+ Galaxy* g = Galaxy::GetInstance();
+ if (g)
+ star_map->SetGalaxy(g->GetSystemList());
+ }
+
+ star_map->SetSystem(s);
+ }
+
+ // flush old object pointers:
+ stars.clear();
+ planets.clear();
+ regions.clear();
+ contacts.clear();
+
+ if (star_system) {
+ // insert objects from star system:
+ ListIter<OrbitalBody> star = star_system->Bodies();
+ while (++star) {
+ switch (star->Type()) {
+ case Orbital::STAR: stars.append(star.value());
+ break;
+ case Orbital::PLANET:
+ case Orbital::MOON: planets.append(star.value());
+ break;
+ }
+
+ ListIter<OrbitalBody> planet = star->Satellites();
+ while (++planet) {
+ planets.append(planet.value());
+
+ ListIter<OrbitalBody> moon = planet->Satellites();
+ while (++moon) {
+ planets.append(moon.value());
+ }
+ }
+ }
+
+ ListIter<OrbitalRegion> rgn = star_system->AllRegions();
+ while (++rgn)
+ regions.append(rgn.value());
+ }
+
+ // sort region list by distance from the star:
+ regions.sort();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavDlg::SetShip(Ship* s)
+{
+ if (ship == s)
+ return;
+
+ ship = s;
+
+ if (ship)
+ SetSystem(ship->GetRegion()->System());
+
+ if (star_map) {
+ Sim* sim = Sim::GetSim();
+
+ if (sim && sim->GetSystemList().size())
+ star_map->SetGalaxy(sim->GetSystemList());
+
+ star_map->SetShip(ship);
+
+ if (ship) {
+ star_map->SetRegion(ship->GetRegion()->GetOrbitalRegion());
+ UseViewMode(VIEW_REGION);
+ star_map->SetSelectedShip(ship);
+ }
+ }
+
+ for (int i = 0; i < 6; i++)
+ filter_btn[i]->SetButtonState(0);
+
+ filter_btn[SELECT_REGION]->SetButtonState(1);
+ UseFilter(SELECT_STARSHIP);
+ UpdateSelection();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavDlg::SetMission(Mission* m)
+{
+ if (!m && mission == m)
+ return;
+
+ if (mission == m && star_system == m->GetStarSystem())
+ return;
+
+ mission = m;
+
+ if (mission) {
+ SetSystem(mission->GetStarSystem());
+ }
+
+ if (star_map) {
+ Campaign* c = Campaign::GetCampaign();
+ Sim* sim = Sim::GetSim();
+
+ star_map->SetMission(0); // prevent building map menu twice
+
+ if (sim && sim->GetSystemList().size()) {
+ star_map->SetGalaxy(sim->GetSystemList());
+ }
+ else if (mission && mission->GetSystemList().size()) {
+ star_map->SetGalaxy(mission->GetSystemList());
+ }
+ else if (c && c->GetSystemList().size()) {
+ star_map->SetGalaxy(c->GetSystemList());
+ }
+ else {
+ Galaxy* g = Galaxy::GetInstance();
+ if (g)
+ star_map->SetGalaxy(g->GetSystemList());
+ }
+
+ if (mission) {
+ star_map->SetMission(mission);
+ star_map->SetRegionByName(mission->GetRegion());
+
+ if (star_map->GetViewMode() == VIEW_REGION) {
+ ListIter<MissionElement> elem = mission->GetElements();
+ while (++elem) {
+ MissionElement* e = elem.value();
+
+ if (e->Player())
+ star_map->SetSelectedElem(e);
+ }
+ }
+ }
+ }
+
+ bool updated = false;
+
+ if (mission) {
+ Orbital* rgn = 0;
+ rgn = mission->GetStarSystem()->FindOrbital(mission->GetRegion());
+
+ if (rgn) {
+ SelectRegion((OrbitalRegion*) rgn);
+ updated = true;
+ }
+ }
+
+ if (!updated)
+ UpdateSelection();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavDlg::SetEditorMode(bool e)
+{
+ editor = e;
+
+ if (star_map)
+ star_map->SetEditorMode(editor);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavDlg::ExecFrame()
+{
+ Sim* sim = Sim::GetSim();
+
+ if (loc_labels && ship) {
+ char loc_buf[512];
+ char x[16];
+ char y[16];
+ char z[16];
+ char d[16];
+
+ FormatNumber(x, -ship->Location().x);
+ FormatNumber(y, ship->Location().z);
+ FormatNumber(z, ship->Location().y);
+
+ strcpy(loc_buf, Game::GetText("NavDlg.loc-labels").data());
+ loc_labels->SetText(loc_buf);
+
+ if (sim->GetActiveRegion()) {
+ sprintf(loc_buf, "\n%s\n%s\n%s, %s, %s",
+ (const char*) star_system->Name(),
+ (const char*) sim->GetActiveRegion()->Name(),
+ x, y, z);
+
+ }
+ else {
+ sprintf(loc_buf, "\n%s\nPlanck Space?\n%s, %s, %s",
+ (const char*) star_system->Name(), x, y, z);
+ }
+
+ loc_data->SetText(loc_buf);
+
+ if (ship) {
+ NavSystem* navsys = ship->GetNavSystem();
+
+ if (ship->GetNextNavPoint() == 0 || !navsys) {
+ commit_btn->SetText(Game::GetText("NavDlg.commit"));
+ commit_btn->SetBackColor(commit_color);
+ commit_btn->SetEnabled(false);
+ }
+ else if (navsys) {
+ commit_btn->SetEnabled(true);
+
+ if (navsys->AutoNavEngaged()) {
+ commit_btn->SetText(Game::GetText("NavDlg.cancel"));
+ commit_btn->SetBackColor(cancel_color);
+ }
+ else {
+ commit_btn->SetText(Game::GetText("NavDlg.commit"));
+ commit_btn->SetBackColor(commit_color);
+ }
+ }
+ }
+
+ if (dst_labels) {
+ Instruction* navpt = ship->GetNextNavPoint();
+
+ if (navpt && navpt->Region()) {
+ FormatNumber(x, navpt->Location().x);
+ FormatNumber(y, navpt->Location().y);
+ FormatNumber(z, navpt->Location().z);
+
+ double distance = 0;
+ Point npt = navpt->Region()->Location() + navpt->Location();
+ if (sim->GetActiveRegion())
+ npt -= sim->GetActiveRegion()->Location();
+
+ npt = npt.OtherHand();
+
+ // distance from self to navpt:
+ distance = Point(npt - ship->Location()).length();
+ FormatNumber(d, distance);
+
+ strcpy(loc_buf, Game::GetText("NavDlg.dst-labels").data());
+ dst_labels->SetText(loc_buf);
+
+ sprintf(loc_buf, "\n%s\n%s\n%s, %s, %s\n%s",
+ (const char*) star_system->Name(),
+ (const char*) navpt->Region()->Name(),
+ x, y, z, d);
+ dst_data->SetText(loc_buf);
+ }
+ else {
+ dst_labels->SetText(Game::GetText("NavDlg.destination"));
+ dst_data->SetText(Game::GetText("NavDlg.not-avail"));
+ }
+ }
+ }
+
+ UpdateSelection();
+ UpdateLists();
+
+ if (Keyboard::KeyDown(VK_ADD) ||
+ (zoom_in_btn && zoom_in_btn->GetButtonState() > 0)) {
+ star_map->ZoomIn();
+ }
+ else if (Keyboard::KeyDown(VK_SUBTRACT) ||
+ (zoom_out_btn && zoom_out_btn->GetButtonState() > 0)) {
+ star_map->ZoomOut();
+ }
+
+ else if (star_map->TargetRect().Contains(Mouse::X(),Mouse::Y())) {
+
+ if (Mouse::Wheel() > 0) {
+ star_map->ZoomIn();
+ star_map->ZoomIn();
+ star_map->ZoomIn();
+ }
+
+ else if (Mouse::Wheel() < 0) {
+ star_map->ZoomOut();
+ star_map->ZoomOut();
+ star_map->ZoomOut();
+ }
+ }
+
+ if (nav_edit_mode == NAV_EDIT_NONE)
+ Mouse::SetCursor(Mouse::ARROW);
+ else
+ Mouse::SetCursor(Mouse::CROSS);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavDlg::OnView(AWEvent* event)
+{
+ int use_filter_mode = -1;
+
+ view_btn[VIEW_GALAXY]->SetButtonState(0);
+ view_btn[VIEW_SYSTEM]->SetButtonState(0);
+ view_btn[VIEW_REGION]->SetButtonState(0);
+
+ if (view_btn[0] == event->window) {
+ star_map->SetViewMode(VIEW_GALAXY);
+ view_btn[VIEW_GALAXY]->SetButtonState(1);
+ use_filter_mode = SELECT_SYSTEM;
+ }
+
+ else if (view_btn[VIEW_SYSTEM] == event->window) {
+ star_map->SetViewMode(VIEW_SYSTEM);
+ view_btn[VIEW_SYSTEM]->SetButtonState(1);
+ use_filter_mode = SELECT_REGION;
+ }
+
+ else if (view_btn[VIEW_REGION] == event->window) {
+ star_map->SetViewMode(VIEW_REGION);
+ view_btn[VIEW_REGION]->SetButtonState(1);
+ use_filter_mode = SELECT_STARSHIP;
+ }
+
+ if (use_filter_mode >= 0) {
+ for (int i = 0; i < 6; i++) {
+ if (i == use_filter_mode)
+ filter_btn[i]->SetButtonState(1);
+ else
+ filter_btn[i]->SetButtonState(0);
+ }
+
+ UseFilter(use_filter_mode);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavDlg::OnFilter(AWEvent* event)
+{
+ int filter_index = -1;
+ for (int i = 0; i < 6; i++) {
+ if (filter_btn[i] == event->window) {
+ filter_index = i;
+ filter_btn[i]->SetButtonState(1);
+ }
+ else {
+ filter_btn[i]->SetButtonState(0);
+ }
+ }
+
+ if (filter_index >= 0)
+ UseFilter(filter_index);
+}
+
+void
+NavDlg::UseFilter(int filter_index)
+{
+ seln_mode = filter_index;
+
+ star_map->SetSelectionMode(seln_mode);
+ UpdateSelection();
+ UpdateLists();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavDlg::UseViewMode(int mode)
+{
+ if (mode >= 0 && mode < 3) {
+ int use_filter_mode = -1;
+
+ view_btn[VIEW_GALAXY]->SetButtonState(0);
+ view_btn[VIEW_SYSTEM]->SetButtonState(0);
+ view_btn[VIEW_REGION]->SetButtonState(0);
+
+ if (mode == 0) {
+ star_map->SetViewMode(VIEW_GALAXY);
+ view_btn[VIEW_GALAXY]->SetButtonState(1);
+ use_filter_mode = SELECT_SYSTEM;
+ }
+
+ else if (mode == 1) {
+ star_map->SetViewMode(VIEW_SYSTEM);
+ view_btn[VIEW_SYSTEM]->SetButtonState(1);
+ use_filter_mode = SELECT_REGION;
+ }
+
+ else if (mode == 2) {
+ star_map->SetViewMode(VIEW_REGION);
+ view_btn[VIEW_REGION]->SetButtonState(1);
+ use_filter_mode = SELECT_STARSHIP;
+ }
+
+ if (use_filter_mode >= 0) {
+ for (int i = 0; i < 6; i++) {
+ filter_btn[i]->SetButtonState(i == use_filter_mode);
+ }
+
+ UseFilter(use_filter_mode);
+ }
+ }
+}
+
+void
+NavDlg::SelectStar(Orbital* star)
+{
+ UseViewMode(0);
+
+ if (stars.size()) {
+ int sel = 0;
+
+ ListIter<Orbital> iter = stars;
+ while (++iter) {
+ if (iter.value() == star) {
+ int old_seln_mode = seln_mode;
+ UseFilter(SELECT_SYSTEM);
+ SelectObject(sel);
+ UseFilter(old_seln_mode);
+ return;
+ }
+
+ sel++;
+ }
+ }
+}
+
+void
+NavDlg::SelectPlanet(Orbital* planet)
+{
+ UseViewMode(1);
+
+ if (planets.size()) {
+ int sel = 0;
+
+ ListIter<Orbital> iter = planets;
+ while (++iter) {
+ if (iter.value() == planet) {
+ int old_seln_mode = seln_mode;
+ UseFilter(SELECT_PLANET);
+ SelectObject(sel);
+ UseFilter(old_seln_mode);
+ return;
+ }
+
+ sel++;
+ }
+ }
+}
+
+void
+NavDlg::SelectRegion(OrbitalRegion* rgn)
+{
+ UseViewMode(2);
+
+ if (regions.size()) {
+ int sel = 0;
+
+ ListIter<OrbitalRegion> iter = regions;
+ while (++iter) {
+ if (iter.value() == rgn) {
+ int old_seln_mode = seln_mode;
+ UseFilter(SELECT_REGION);
+ SelectObject(sel);
+ UseFilter(old_seln_mode);
+ return;
+ }
+
+ sel++;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavDlg::OnSelect(AWEvent* event)
+{
+ int index = -1;
+
+ for (int i = 0; i < seln_list->NumItems(); i++)
+ if (seln_list->IsSelected(i))
+ index = i;
+
+ if (index >= 0)
+ SelectObject(index);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavDlg::SelectObject(int index)
+{
+ Text selected = seln_list->GetItemText(index);
+ int selection = seln_list->GetItemData(index);
+
+ star_map->SetSelection(selection);
+ SetSystem(star_map->GetSystem());
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavDlg::UpdateSelection()
+{
+ if (!info_list)
+ return;
+
+ if (!star_map)
+ return;
+
+ info_list->ClearItems();
+
+ Text units_km = Text(" ") + Game::GetText("NavDlg.units.kilometers");
+ Text units_tonnes = Text(" ") + Game::GetText("NavDlg.units.tonnes");
+
+ if (seln_mode <= SELECT_REGION) {
+ Orbital* s = star_map->GetSelection();
+
+ if (s) {
+ char radius[32];
+ char mass[32];
+ char orbit[32];
+ char period[32];
+ char units[32];
+
+ double p = s->Period();
+
+ if (p < 60) {
+ sprintf(units, " %s", Game::GetText("NavDlg.units.seconds").data());
+ }
+ else if (p < 3600) {
+ p /= 60;
+ sprintf(units, " %s", Game::GetText("NavDlg.units.minutes").data());
+ }
+ else if (p < 24 * 3600) {
+ p /= 3600;
+ sprintf(units, " %s", Game::GetText("NavDlg.units.hours").data());
+ }
+ else if (p < 365.25 * 24 * 3600) {
+ p /= 24*3600;
+ sprintf(units, " %s", Game::GetText("NavDlg.units.days").data());
+ }
+ else {
+ p /= 365.25*24*3600;
+ sprintf(units, " %s", Game::GetText("NavDlg.units.years").data());
+ }
+
+ FormatNumberExp(radius, s->Radius()/1000);
+ FormatNumberExp(mass, s->Mass()/1000);
+ FormatNumberExp(orbit, s->Orbit()/1000);
+ FormatNumberExp(period, p);
+
+ strcat(radius, units_km.data());
+ strcat(mass, units_tonnes.data());
+ strcat(orbit, units_km.data());
+ strcat(period, units);
+
+ if (seln_mode >= SELECT_SYSTEM) {
+ info_list->AddItem(Game::GetText(Text("NavDlg.filter.") + filter_name[seln_mode]));
+ info_list->AddItem(Game::GetText("NavDlg.radius"));
+ if (s->Mass() > 0)
+ info_list->AddItem(Game::GetText("NavDlg.mass"));
+ info_list->AddItem(Game::GetText("NavDlg.orbit"));
+ info_list->AddItem(Game::GetText("NavDlg.period"));
+
+ int row = 0;
+ info_list->SetItemText(row++, 1, s->Name());
+ info_list->SetItemText(row++, 1, radius);
+ if (s->Mass() > 0)
+ info_list->SetItemText(row++, 1, mass);
+ info_list->SetItemText(row++, 1, orbit);
+ info_list->SetItemText(row++, 1, period);
+ }
+ }
+ }
+
+ else if (seln_mode == SELECT_STATION ||
+ seln_mode == SELECT_STARSHIP ||
+ seln_mode == SELECT_FIGHTER) {
+
+ Ship* sel_ship = star_map->GetSelectedShip();
+ MissionElement* sel_elem = star_map->GetSelectedElem();
+
+ if (sel_ship) {
+ Text order_desc = Game::GetText("NavDlg.none");
+ char shield[16];
+ char hull[16];
+ char range[32];
+
+ sprintf(shield, "%03d", sel_ship->ShieldStrength());
+ sprintf(hull, "%03d", sel_ship->HullStrength());
+ sprintf(range, Game::GetText("NavDlg.not-avail").data());
+
+ if (ship) {
+ FormatNumberExp(range, Point(sel_ship->Location()-ship->Location()).length()/1000);
+ strcat(range, units_km.data());
+ }
+
+ info_list->AddItem(Game::GetText("NavDlg.name"));
+ info_list->AddItem(Game::GetText("NavDlg.class"));
+ info_list->AddItem(Game::GetText("NavDlg.sector"));
+ info_list->AddItem(Game::GetText("NavDlg.shield"));
+ info_list->AddItem(Game::GetText("NavDlg.hull"));
+ info_list->AddItem(Game::GetText("NavDlg.range"));
+ info_list->AddItem(Game::GetText("NavDlg.orders"));
+
+ int row = 0;
+ info_list->SetItemText(row++, 1, sel_ship->Name());
+ info_list->SetItemText(row++, 1, Text(sel_ship->Abbreviation()) + Text(" ") + Text(sel_ship->Design()->display_name));
+ info_list->SetItemText(row++, 1, sel_ship->GetRegion()->Name());
+ info_list->SetItemText(row++, 1, shield);
+ info_list->SetItemText(row++, 1, hull);
+ info_list->SetItemText(row++, 1, range);
+ info_list->SetItemText(row++, 1, order_desc);
+ }
+
+ else if (sel_elem) {
+ Text order_desc = Game::GetText("NavDlg.none");
+ char range[32];
+
+ MissionElement* self = mission->GetElements()[0];
+ if (self)
+ FormatNumberExp(range, Point(sel_elem->Location()-self->Location()).length()/1000);
+ else
+ strcpy(range, "0");
+
+ strcat(range, units_km.data());
+
+ info_list->AddItem(Game::GetText("NavDlg.name"));
+ info_list->AddItem(Game::GetText("NavDlg.class"));
+ info_list->AddItem(Game::GetText("NavDlg.sector"));
+ info_list->AddItem(Game::GetText("NavDlg.range"));
+ info_list->AddItem(Game::GetText("NavDlg.orders"));
+
+ int row = 0;
+ info_list->SetItemText(row++, 1, sel_elem->Name());
+
+ if (sel_elem->GetDesign())
+ info_list->SetItemText(row++, 1, sel_elem->Abbreviation() + Text(" ") + sel_elem->GetDesign()->name);
+ else
+ info_list->SetItemText(row++, 1, Game::GetText("NavDlg.unknown"));
+
+ info_list->SetItemText(row++, 1, sel_elem->Region());
+ info_list->SetItemText(row++, 1, range);
+ info_list->SetItemText(row++, 1, order_desc);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavDlg::UpdateLists()
+{
+ if (!seln_list)
+ return;
+
+ int seln_index = -1;
+ int top_index = -1;
+
+ if (seln_list->IsSelecting())
+ seln_index = seln_list->GetListIndex();
+
+ top_index = seln_list->GetTopIndex();
+
+ seln_list->ClearItems();
+
+ switch (seln_mode) {
+ case SELECT_SYSTEM:
+ {
+ seln_list->SetColumnTitle(0, Game::GetText(Text("NavDlg.filter.") + filter_name[seln_mode]));
+ int i = 0;
+ ListIter<StarSystem> iter = star_map->GetGalaxy();
+ while (++iter)
+ seln_list->AddItemWithData(iter->Name(), i++);
+ }
+ break;
+
+ case SELECT_PLANET:
+ {
+ seln_list->SetColumnTitle(0, Game::GetText(Text("NavDlg.filter.") + filter_name[seln_mode]));
+ int i = 0;
+ ListIter<Orbital> iter = planets;
+ while (++iter) {
+ if (iter->Type() == Orbital::MOON)
+ seln_list->AddItemWithData(Text("- ") + Text(iter->Name()), i++);
+ else
+ seln_list->AddItemWithData(iter->Name(), i++);
+ }
+ }
+ break;
+
+ case SELECT_REGION:
+ {
+ seln_list->SetColumnTitle(0, Game::GetText(Text("NavDlg.filter.") + filter_name[seln_mode]));
+ int i = 0;
+ ListIter<OrbitalRegion> iter = regions;
+ while (++iter) {
+ seln_list->AddItemWithData(iter->Name(), i++);
+ }
+ }
+ break;
+
+ case SELECT_STATION:
+ case SELECT_STARSHIP:
+ case SELECT_FIGHTER:
+ {
+ seln_list->SetColumnTitle(0, Game::GetText(Text("NavDlg.filter.") + filter_name[seln_mode]));
+ int i = 0;
+
+ if (mission) {
+ ListIter<MissionElement> elem = mission->GetElements();
+ while (++elem) {
+ MissionElement* e = elem.value();
+ bool filter_ok =
+ (seln_mode == SELECT_STATION && e->IsStatic()) ||
+ (seln_mode == SELECT_STARSHIP && e->IsStarship() && !e->IsStatic()) ||
+ (seln_mode == SELECT_FIGHTER && e->IsDropship() && !e->IsSquadron());
+
+ if (filter_ok) {
+ bool visible = editor ||
+ e->GetIFF() == 0 ||
+ e->GetIFF() == mission->Team() ||
+ e->IntelLevel() > Intel::KNOWN;
+
+ if (visible)
+ seln_list->AddItemWithData(e->Name(), e->Identity());
+ }
+ }
+ }
+
+ else if (ship) {
+ Sim* sim = Sim::GetSim();
+ ListIter<SimRegion> r_iter = sim->GetRegions();
+ while (++r_iter) {
+ SimRegion* rgn = r_iter.value();
+
+ ListIter<Ship> s_iter = rgn->Ships();
+ while (++s_iter) {
+ Ship* s = s_iter.value();
+ bool filter_ok =
+ (seln_mode == SELECT_STATION && s->IsStatic()) ||
+ (seln_mode == SELECT_STARSHIP && s->IsStarship() && !s->IsStatic()) ||
+ (seln_mode == SELECT_FIGHTER && s->IsDropship());
+
+
+ if (filter_ok) {
+ bool visible = s->GetIFF() == 0 ||
+ s->GetIFF() == ship->GetIFF() ||
+ s->GetElement() &&
+ s->GetElement()->IntelLevel() > Intel::KNOWN;
+
+ if (visible)
+ seln_list->AddItemWithData(s->Name(), s->Identity());
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (top_index >= 0)
+ seln_list->ScrollTo(top_index);
+
+ if (seln_index >= 0)
+ seln_list->SetSelected(seln_index);
+
+ else
+ CoordinateSelection();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavDlg::OnEngage(AWEvent* event)
+{
+ bool hide = false;
+
+ if (ship) {
+ NavSystem* navsys = ship->GetNavSystem();
+ if (navsys) {
+ if (navsys->AutoNavEngaged()) {
+ navsys->DisengageAutoNav();
+ commit_btn->SetText(Game::GetText("NavDlg.commit"));
+ commit_btn->SetBackColor(commit_color);
+ }
+ else {
+ navsys->EngageAutoNav();
+ commit_btn->SetText(Game::GetText("NavDlg.cancel"));
+ commit_btn->SetBackColor(cancel_color);
+ hide = true;
+ }
+
+ Sim* sim = Sim::GetSim();
+ if (sim)
+ ship->SetControls(sim->GetControls());
+ }
+ }
+
+ if (manager && hide)
+ manager->ShowNavDlg(); // also hides
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavDlg::OnCommit(AWEvent* event)
+{
+ if (manager)
+ manager->ShowNavDlg(); // also hides
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+NavDlg::OnCancel(AWEvent* event)
+{
+ if (manager)
+ manager->ShowNavDlg(); // also hides
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavDlg::SetNavEditMode(int mode)
+{
+ if (nav_edit_mode != mode) {
+ if (mode != NAV_EDIT_NONE) {
+ int map_mode = star_map->GetSelectionMode();
+ if (map_mode != -1)
+ seln_mode = map_mode;
+
+ star_map->SetSelectionMode(-1);
+ }
+ else {
+ star_map->SetSelectionMode(seln_mode);
+ }
+
+ nav_edit_mode = mode;
+ }
+}
+
+int
+NavDlg::GetNavEditMode()
+{
+ return nav_edit_mode;
+}
+
+void
+NavDlg::OnMapDown(AWEvent* event)
+{
+}
+
+void
+NavDlg::OnMapMove(AWEvent* event)
+{
+}
+
+void
+NavDlg::OnMapClick(AWEvent* event)
+{
+ static DWORD click_time = 0;
+
+ SetSystem(star_map->GetSystem());
+ CoordinateSelection();
+
+ // double-click:
+ if (Game::RealTime() - click_time < 350) {
+ MissionElement* elem = star_map->GetSelectedElem();
+ MsnElemDlg* msn_elem_dlg = manager->GetMsnElemDlg();
+
+ if (elem && msn_elem_dlg) {
+ msn_elem_dlg->SetMission(mission);
+ msn_elem_dlg->SetMissionElement(elem);
+ manager->ShowMsnElemDlg();
+ }
+ }
+
+ click_time = Game::RealTime();
+}
+
+void
+NavDlg::CoordinateSelection()
+{
+ if (!seln_list || !star_map)
+ return;
+
+ switch (seln_mode) {
+ default:
+ case SELECT_SYSTEM:
+ case SELECT_PLANET:
+ case SELECT_REGION:
+ {
+ seln_list->ClearSelection();
+
+ Orbital* selected = star_map->GetSelection();
+
+ if (selected) {
+ for (int i = 0; i < seln_list->NumItems(); i++) {
+ if (seln_list->GetItemText(i) == selected->Name() ||
+ seln_list->GetItemText(i) == Text("- ") + selected->Name()) {
+ seln_list->SetSelected(i, true);
+ }
+ }
+ }
+ }
+ break;
+
+ case SELECT_STATION:
+ case SELECT_STARSHIP:
+ case SELECT_FIGHTER:
+ {
+ seln_list->ClearSelection();
+
+ Ship* selected = star_map->GetSelectedShip();
+ MissionElement* elem = star_map->GetSelectedElem();
+
+ if (selected) {
+ for (int i = 0; i < seln_list->NumItems(); i++) {
+ if (seln_list->GetItemText(i) == selected->Name()) {
+ seln_list->SetSelected(i, true);
+ }
+ }
+ }
+
+ else if (elem) {
+ for (int i = 0; i < seln_list->NumItems(); i++) {
+ if (seln_list->GetItemText(i) == elem->Name()) {
+ seln_list->SetSelected(i, true);
+ }
+ }
+ }
+ }
+ break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavDlg::OnClose(AWEvent* event)
+{
+ if (manager)
+ manager->HideNavDlg();
+}
diff --git a/Stars45/NavDlg.h b/Stars45/NavDlg.h
new file mode 100644
index 0000000..0c51b5c
--- /dev/null
+++ b/Stars45/NavDlg.h
@@ -0,0 +1,119 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NavDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Navigation Active Window class
+*/
+
+#ifndef NavDlg_h
+#define NavDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "Font.h"
+
+class BaseScreen;
+class MapView;
+class StarSystem;
+class Ship;
+class SimRegion;
+class Orbital;
+class OrbitalRegion;
+class Mission;
+
+// +--------------------------------------------------------------------+
+
+class NavDlg : public FormWindow
+{
+public:
+ NavDlg(Screen* s, FormDef& def, BaseScreen* mgr);
+ virtual ~NavDlg();
+
+ virtual void RegisterControls();
+
+ // Operations:
+ virtual void OnView(AWEvent* event);
+ virtual void OnFilter(AWEvent* event);
+ virtual void OnSelect(AWEvent* event);
+ virtual void OnCommit(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+ virtual void OnEngage(AWEvent* event);
+ virtual void OnMapDown(AWEvent* event);
+ virtual void OnMapMove(AWEvent* event);
+ virtual void OnMapClick(AWEvent* event);
+ virtual void OnClose(AWEvent* event);
+
+ virtual void ExecFrame();
+ StarSystem* GetSystem() const { return star_system; }
+ void SetSystem(StarSystem* s);
+ Mission* GetMission() const { return mission; }
+ void SetMission(Mission* m);
+ Ship* GetShip() const { return ship; }
+ void SetShip(Ship* s);
+
+ bool GetEditorMode() const { return editor; }
+ void SetEditorMode(bool b);
+
+ void UseViewMode(int mode);
+ void UseFilter(int index);
+ void SelectObject(int index);
+ void UpdateSelection();
+ void UpdateLists();
+ void CoordinateSelection();
+
+ void SelectStar(Orbital* star);
+ void SelectPlanet(Orbital* planet);
+ void SelectRegion(OrbitalRegion* rgn);
+
+ enum NAV_EDIT_MODE { NAV_EDIT_NONE = 0,
+ NAV_EDIT_ADD = 1,
+ NAV_EDIT_DEL = 2,
+ NAV_EDIT_MOVE = 3 };
+
+ void SetNavEditMode(int mode);
+ int GetNavEditMode();
+
+protected:
+ Button* view_btn[3];
+ Button* filter_btn[6];
+ Button* commit_btn;
+ Button* zoom_in_btn;
+ Button* zoom_out_btn;
+ Button* close_btn;
+
+ MapView* star_map;
+ ActiveWindow* map_win;
+ ActiveWindow* loc_labels;
+ ActiveWindow* dst_labels;
+ ActiveWindow* loc_data;
+ ActiveWindow* dst_data;
+
+ ListBox* seln_list;
+ ListBox* info_list;
+
+ BaseScreen* manager;
+ int seln_mode;
+ int nav_edit_mode;
+
+ StarSystem* star_system;
+ List<Orbital> stars;
+ List<Orbital> planets;
+ List<OrbitalRegion> regions;
+ List<Ship> contacts;
+
+ Ship* ship;
+ Mission* mission;
+ bool editor;
+};
+
+#endif NavDlg_h
+
diff --git a/Stars45/NavLight.cpp b/Stars45/NavLight.cpp
new file mode 100644
index 0000000..2a8b447
--- /dev/null
+++ b/Stars45/NavLight.cpp
@@ -0,0 +1,188 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NavLight.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Navigation Light System class
+*/
+
+#include "MemDebug.h"
+#include "NavLight.h"
+
+#include "Game.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+
+// +----------------------------------------------------------------------+
+
+static Bitmap* images[4];
+
+// +----------------------------------------------------------------------+
+
+NavLight::NavLight(double p, double s)
+ : System(COMPUTER, 32, "Navigation Lights", 1, 0),
+ period(p), nlights(0), scale(s), enable(true)
+{
+ name = Game::GetText("sys.nav-light");
+ abrv = Game::GetText("sys.nav-light.abrv");
+
+ ZeroMemory(beacon, sizeof(beacon));
+ ZeroMemory(beacon_type, sizeof(beacon_type));
+ ZeroMemory(pattern, sizeof(pattern));
+}
+
+// +----------------------------------------------------------------------+
+
+NavLight::NavLight(const NavLight& c)
+ : System(c), period(c.period), scale(c.scale),
+ nlights(0), enable(true)
+{
+ Mount(c);
+ SetAbbreviation(c.Abbreviation());
+ ZeroMemory(beacon, sizeof(beacon));
+ ZeroMemory(beacon_type, sizeof(beacon_type));
+
+ nlights = c.nlights;
+
+ for (int i = 0; i < nlights; i++) {
+ loc[i] = c.loc[i];
+ pattern[i] = c.pattern[i];
+ beacon_type[i] = c.beacon_type[i];
+
+ DriveSprite* rep = new(__FILE__,__LINE__) DriveSprite(images[beacon_type[i]]);
+ rep->Scale(c.scale);
+
+ beacon[i] = rep;
+ }
+
+ offset = rand();
+}
+
+// +--------------------------------------------------------------------+
+
+NavLight::~NavLight()
+{
+ for (int i = 0; i < nlights; i++)
+ GRAPHIC_DESTROY(beacon[i]);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavLight::Initialize()
+{
+ static int initialized = 0;
+ if (initialized) return;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->LoadTexture("beacon1.pcx", images[0], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("beacon2.pcx", images[1], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("beacon3.pcx", images[2], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture("beacon4.pcx", images[3], Bitmap::BMP_TRANSLUCENT);
+
+ initialized = 1;
+}
+
+void
+NavLight::Close()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavLight::ExecFrame(double seconds)
+{
+ if (enable && power_on) {
+ double t = (Game::GameTime()+offset) / 1000.0;
+ DWORD n = (int) (fmod(t, period) * 32 / period);
+ DWORD code = 1 << n;
+
+ for (int i = 0; i < nlights; i++) {
+ if (beacon[i]) {
+ if (pattern[i] & code)
+ beacon[i]->SetShade(1);
+ else
+ beacon[i]->SetShade(0);
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < nlights; i++) {
+ if (beacon[i]) {
+ beacon[i]->SetShade(0);
+ }
+ }
+ }
+}
+
+void
+NavLight::Enable()
+{
+ enable = true;
+}
+
+void
+NavLight::Disable()
+{
+ enable = false;
+}
+
+void
+NavLight::AddBeacon(Point l, DWORD ptn, int t)
+{
+ if (nlights < MAX_LIGHTS) {
+ loc[nlights] = l;
+ pattern[nlights] = ptn;
+ beacon_type[nlights] = t;
+
+ DriveSprite* rep = new(__FILE__,__LINE__) DriveSprite(images[t]);
+ rep->Scale(scale);
+
+ beacon[nlights] = rep;
+ nlights++;
+ }
+}
+
+void
+NavLight::SetPeriod(double p)
+{
+ period = p;
+}
+
+void
+NavLight::SetPattern(int index, DWORD ptn)
+{
+ if (index >= 0 && index < nlights)
+ pattern[index] = ptn;
+}
+
+void
+NavLight::SetOffset(DWORD o)
+{
+ offset = o;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavLight::Orient(const Physical* rep)
+{
+ System::Orient(rep);
+
+ const Matrix& orientation = rep->Cam().Orientation();
+ Point ship_loc = rep->Location();
+
+ for (int i = 0; i < nlights; i++) {
+ Point projector = (loc[i] * orientation) + ship_loc;
+ if (beacon[i]) beacon[i]->MoveTo(projector);
+ }
+}
+
+
diff --git a/Stars45/NavLight.h b/Stars45/NavLight.h
new file mode 100644
index 0000000..1d3e4fc
--- /dev/null
+++ b/Stars45/NavLight.h
@@ -0,0 +1,67 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NavLight.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Navigation Lights System class
+*/
+
+#ifndef NavLight_h
+#define NavLight_h
+
+#include "Types.h"
+#include "System.h"
+#include "DriveSprite.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class NavLight : public System
+{
+public:
+ enum Constants { MAX_LIGHTS = 8 };
+
+ NavLight(double period, double scale);
+ NavLight(const NavLight& rhs);
+ virtual ~NavLight();
+
+ static void Initialize();
+ static void Close();
+
+ virtual void ExecFrame(double seconds);
+
+ int NumBeacons() const { return nlights; }
+ Sprite* Beacon(int index) const { return beacon[index]; }
+ bool IsEnabled() const { return enable; }
+
+ virtual void Enable();
+ virtual void Disable();
+ virtual void AddBeacon(Point loc, DWORD pattern, int type=1);
+ virtual void SetPeriod(double p);
+ virtual void SetPattern(int index, DWORD p);
+ virtual void SetOffset(DWORD o);
+
+ virtual void Orient(const Physical* rep);
+
+protected:
+ double period;
+ double scale;
+ bool enable;
+
+ int nlights;
+
+ Point loc[MAX_LIGHTS];
+ DriveSprite* beacon[MAX_LIGHTS];
+ DWORD pattern[MAX_LIGHTS];
+ int beacon_type[MAX_LIGHTS];
+ DWORD offset;
+};
+
+#endif NavLight_h
+
diff --git a/Stars45/NavSystem.cpp b/Stars45/NavSystem.cpp
new file mode 100644
index 0000000..c0a620c
--- /dev/null
+++ b/Stars45/NavSystem.cpp
@@ -0,0 +1,113 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NavSystem.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Navigation System class implementation
+*/
+
+#include "MemDebug.h"
+#include "NavSystem.h"
+#include "Ship.h"
+#include "Sim.h"
+#include "HUDSounds.h"
+#include "Button.h"
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+NavSystem::NavSystem()
+ : System(COMPUTER, 2, "Auto Nav System", 1, 1,1,1),
+ autonav(0)
+{
+ name = Game::GetText("sys.nav-system");
+ abrv = Game::GetText("sys.nav-system.abrv");
+
+ power_flags = POWER_WATTS | POWER_CRITICAL;
+}
+
+// +----------------------------------------------------------------------+
+
+NavSystem::NavSystem(const NavSystem& s)
+ : System(s), autonav(0)
+{
+ Mount(s);
+
+ power_flags = POWER_WATTS | POWER_CRITICAL;
+}
+
+// +--------------------------------------------------------------------+
+
+NavSystem::~NavSystem()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+NavSystem::ExecFrame(double seconds)
+{
+ if (autonav && ship && !ship->GetNextNavPoint())
+ autonav = false;
+
+ energy = 0.0f;
+ System::ExecFrame(seconds);
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+NavSystem::AutoNavEngaged()
+{
+ return ship && autonav && IsPowerOn();
+}
+
+void
+NavSystem::EngageAutoNav()
+{
+ if (IsPowerOn() && !autonav) {
+ if (!ship->GetNextNavPoint()) {
+ Button::PlaySound(Button::SND_REJECT);
+ }
+ else {
+ HUDSounds::PlaySound(HUDSounds::SND_NAV_MODE);
+ autonav = true;
+ }
+ }
+}
+
+void
+NavSystem::DisengageAutoNav()
+{
+ if (autonav)
+ HUDSounds::PlaySound(HUDSounds::SND_NAV_MODE);
+
+ autonav = false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NavSystem::Distribute(double delivered_energy, double seconds)
+{
+ if (IsPowerOn()) {
+ // convert Joules to Watts:
+ energy = (float) (delivered_energy/seconds);
+
+ // brown out:
+ if (energy < capacity*0.75f)
+ power_on = false;
+
+ // spike:
+ else if (energy > capacity*1.5f) {
+ power_on = false;
+ ApplyDamage(50);
+ }
+ }
+}
+
diff --git a/Stars45/NavSystem.h b/Stars45/NavSystem.h
new file mode 100644
index 0000000..1345839
--- /dev/null
+++ b/Stars45/NavSystem.h
@@ -0,0 +1,55 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NavSystem.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Nav Points and so on...
+*/
+
+#ifndef NavSystem_h
+#define NavSystem_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "System.h"
+
+// +--------------------------------------------------------------------+
+
+class StarSystem;
+class Orbital;
+class OrbitalBody;
+class OrbitalRegion;
+class Ship;
+
+// +--------------------------------------------------------------------+
+
+class NavSystem : public System
+{
+public:
+ NavSystem();
+ NavSystem(const NavSystem& rhs);
+ virtual ~NavSystem();
+
+ virtual void ExecFrame(double seconds);
+
+ virtual void Distribute(double delivered_energy, double seconds);
+
+ bool AutoNavEngaged();
+ void EngageAutoNav();
+ void DisengageAutoNav();
+
+protected:
+ bool autonav;
+};
+
+// +--------------------------------------------------------------------+
+
+
+#endif NavSystem_h
+
diff --git a/Stars45/NetAddrDlg.cpp b/Stars45/NetAddrDlg.cpp
new file mode 100644
index 0000000..15156ce
--- /dev/null
+++ b/Stars45/NetAddrDlg.cpp
@@ -0,0 +1,160 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetAddrDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Select Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "NetAddrDlg.h"
+#include "MenuScreen.h"
+#include "NetClientConfig.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "EditBox.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(NetAddrDlg, OnSave);
+DEF_MAP_CLIENT(NetAddrDlg, OnCancel);
+
+// +--------------------------------------------------------------------+
+
+NetAddrDlg::NetAddrDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ btn_save(0), btn_cancel(0), edt_name(0), edt_addr(0), edt_port(0), edt_pass(0)
+{
+ Init(def);
+}
+
+NetAddrDlg::~NetAddrDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetAddrDlg::RegisterControls()
+{
+ btn_save = (Button*) FindControl(1);
+ btn_cancel = (Button*) FindControl(2);
+
+ REGISTER_CLIENT(EID_CLICK, btn_save, NetAddrDlg, OnSave);
+ REGISTER_CLIENT(EID_CLICK, btn_cancel, NetAddrDlg, OnCancel);
+
+ edt_name = (EditBox*) FindControl(200);
+ edt_addr = (EditBox*) FindControl(201);
+ edt_port = (EditBox*) FindControl(202);
+ edt_pass = (EditBox*) FindControl(203);
+
+ if (edt_name) edt_name->SetText("");
+ if (edt_addr) edt_addr->SetText("");
+ if (edt_port) edt_port->SetText("");
+ if (edt_pass) edt_pass->SetText("");
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetAddrDlg::Show()
+{
+ if (!IsShown()) {
+ FormWindow::Show();
+
+ if (edt_name) edt_name->SetText("");
+ if (edt_addr) edt_addr->SetText("");
+ if (edt_port) edt_port->SetText("");
+ if (edt_pass) edt_pass->SetText("");
+
+ if (edt_name) edt_name->SetFocus();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static bool tab_latch = false;
+
+void
+NetAddrDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ OnSave(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetAddrDlg::OnSave(AWEvent* event)
+{
+ NetClientConfig* config = NetClientConfig::GetInstance();
+
+ if (config &&
+ edt_addr && edt_addr->GetText().length() > 0 &&
+ edt_port && edt_port->GetText().length() > 0)
+ {
+ Text name;
+ Text addr;
+ Text pass;
+ int port;
+
+ sscanf(edt_port->GetText().data(), "%d", &port);
+
+ if (edt_name && edt_name->GetText().length() < 250) {
+ char buffer[256];
+ strcpy(buffer, edt_name->GetText().data());
+ char* p = strpbrk(buffer, "\n\r\t");
+ if (p) *p = 0;
+
+ name = SafeQuotes(buffer);
+ }
+
+ if (edt_pass && edt_pass->GetText().length() < 250) {
+ char buffer[256];
+ strcpy(buffer, edt_pass->GetText().data());
+ char* p = strpbrk(buffer, "\n\r\t");
+ if (p) *p = 0;
+
+ pass = SafeQuotes(buffer);
+ }
+
+ if (edt_addr && edt_addr->GetText().length() < 250) {
+ char buffer[256];
+ strcpy(buffer, edt_addr->GetText().data());
+ char* p = strpbrk(buffer, "\n\r\t");
+ if (p) *p = 0;
+
+ addr = SafeQuotes(buffer);
+ }
+
+ config->AddServer(name, addr, port, pass, true);
+ config->Save();
+ }
+
+ if (manager)
+ manager->ShowNetClientDlg();
+}
+
+void
+NetAddrDlg::OnCancel(AWEvent* event)
+{
+ if (manager)
+ manager->ShowNetClientDlg();
+}
diff --git a/Stars45/NetAddrDlg.h b/Stars45/NetAddrDlg.h
new file mode 100644
index 0000000..91b8182
--- /dev/null
+++ b/Stars45/NetAddrDlg.h
@@ -0,0 +1,60 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetAddrDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Server Address/Port Dialog Active Window class
+*/
+
+#ifndef NetAddrDlg_h
+#define NetAddrDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+
+// +--------------------------------------------------------------------+
+
+class NetAddrDlg : public FormWindow
+{
+public:
+ NetAddrDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~NetAddrDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+
+ // Operations:
+ virtual void ExecFrame();
+
+ virtual void OnSave(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+
+protected:
+ MenuScreen* manager;
+
+ Button* btn_save;
+ Button* btn_cancel;
+ EditBox* edt_name;
+ EditBox* edt_addr;
+ EditBox* edt_port;
+ EditBox* edt_pass;
+};
+
+#endif NetAddrDlg_h
+
diff --git a/Stars45/NetAdminChat.cpp b/Stars45/NetAdminChat.cpp
new file mode 100644
index 0000000..f5d2549
--- /dev/null
+++ b/Stars45/NetAdminChat.cpp
@@ -0,0 +1,122 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetAdminServer.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ HTTP Servlet Engine for Multiplayer Admin
+*/
+
+
+#include "MemDebug.h"
+#include "NetAdminChat.h"
+#include "NetLobbyServer.h"
+#include "NetServerConfig.h"
+#include "NetUser.h"
+#include "NetChat.h"
+#include "NetUtil.h"
+
+#include "HttpServlet.h"
+#include "NetLayer.h"
+#include "FormatUtil.h"
+
+// +-------------------------------------------------------------------+
+
+NetAdminChat::NetAdminChat()
+{ }
+
+// +-------------------------------------------------------------------+
+
+bool
+NetAdminChat::DoGet(HttpRequest& request, HttpResponse& response)
+{
+ if (CheckUser(request, response)) {
+ NetLobbyServer* lobby = NetLobbyServer::GetInstance();
+
+ if (lobby) {
+ Text msg = request.GetParam("msg");
+ Text act = request.GetParam("action");
+
+ if (msg.length()) {
+ lobby->AddChat(user, msg);
+
+ if (user)
+ NetUtil::SendChat(0xffff, user->Name(), msg);
+ }
+
+ else if (act.length()) {
+ if (act == "clear")
+ lobby->ClearChat();
+
+ else if (act == "save")
+ lobby->SaveChat();
+ }
+ }
+
+ response.SetStatus(HttpResponse::SC_OK);
+ response.AddHeader("MIME-Version", "1.0");
+ response.AddHeader("Content-Type", "text/html");
+ response.AddHeader("Cache-Control", "no-cache");
+ response.AddHeader("Expires", "-1");
+
+ response.SetContent(GetHead("Chat") +
+ GetTitleBar(GetStatLine(),
+ "onLoad=\"self.focus();document.chatForm.msg.focus();\"") +
+ GetContent() +
+ GetBodyClose());
+ }
+
+ return true;
+}
+
+// +-------------------------------------------------------------------+
+
+Text
+NetAdminChat::GetContent()
+{
+ Text content = "<div style=\"overflow-y:scroll; height:240px; padding-right:4pt; padding-left:4pt; padding-bottom:4pt; padding-top:4pt; margin:4pt;\">\n";
+
+ int nchat = 0;
+ NetLobbyServer* lobby = NetLobbyServer::GetInstance();
+ if (lobby) {
+ content += "\n<table width=\"90%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n";
+
+ ListIter<NetChatEntry> iter = lobby->GetChat();
+ while (++iter) {
+ NetChatEntry* c = iter.value();
+
+ content += " <tr><td nowrap width=\"130\" class=\"tiny\">";
+ content += FormatTimeString(c->GetTime());
+ content += "</td><td nowrap width=\"80\" class=\"tiny\">";
+ content += c->GetUser();
+ content += "</td><td class=\"tiny\">";
+ content += c->GetMessage();
+ content += "</td></tr>\n";
+ }
+
+ content += "</table>\n\n";
+ }
+
+ content += "</div>\n<div class=\"content\">\n\
+ <form name=\"chatForm\" method=\"post\"action=\"/chat\">\n\
+ <table border=\"0\">\n\
+ <tr>\n\
+ <td valign=\"middle\">&nbsp;&nbsp;<input type=\"text\" name=\"msg\" size=\"80\"></td>\n\
+ <td valign=\"middle\">&nbsp;&nbsp;<input type=\"submit\" value=\"Send\"></td>\n\
+ </tr>\n\
+ <tr>\n\
+ <td colspan=\"2\" valign=\"middle\" class=\"std\">&nbsp;&nbsp;<a href=\"/chat\">Refresh</a>\
+&nbsp;&nbsp;&#183;&nbsp;&nbsp;<a href=\"/chat?action=save\">Save</a>&nbsp;&nbsp;&#183;&nbsp;&nbsp;<a href=\"/chat?action=clear\">Clear</a></td>\n\
+ </tr>\n\
+ </table>\n\
+ </form>\n\
+</div>\n\n";
+
+ content += GetCopyright();
+ return content;
+}
diff --git a/Stars45/NetAdminChat.h b/Stars45/NetAdminChat.h
new file mode 100644
index 0000000..233f5f6
--- /dev/null
+++ b/Stars45/NetAdminChat.h
@@ -0,0 +1,33 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetAdminChat.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ HTTP Servlet Engine for Multiplayer Admin
+*/
+
+
+#ifndef NetAdminChat_h
+#define NetAdminChat_h
+
+#include "NetAdminServer.h"
+
+// +-------------------------------------------------------------------+
+
+class NetAdminChat : public NetAdminServlet
+{
+public:
+ NetAdminChat();
+ virtual ~NetAdminChat() { }
+
+ virtual bool DoGet(HttpRequest& request, HttpResponse& response);
+ virtual Text GetContent();
+};
+
+#endif NetAdminChat_h \ No newline at end of file
diff --git a/Stars45/NetAdminServer.cpp b/Stars45/NetAdminServer.cpp
new file mode 100644
index 0000000..7f38ff6
--- /dev/null
+++ b/Stars45/NetAdminServer.cpp
@@ -0,0 +1,887 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetAdminServer.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ HTTP Servlet Engine for Multiplayer Admin
+*/
+
+
+#include "MemDebug.h"
+#include "NetAdminServer.h"
+#include "NetLobbyServer.h"
+#include "NetServerConfig.h"
+#include "NetClientConfig.h"
+#include "NetAdminChat.h"
+#include "NetUser.h"
+#include "NetChat.h"
+
+#include "StarServer.h"
+#include "HttpServlet.h"
+#include "NetLayer.h"
+
+#include "DataLoader.h"
+#include "FormatUtil.h"
+#include "MachineInfo.h"
+
+extern const char* versionInfo;
+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+
+class NetAdminLogin : public NetAdminServlet
+{
+public:
+ NetAdminLogin() { }
+ virtual ~NetAdminLogin() { }
+
+ virtual bool DoGet(HttpRequest& request, HttpResponse& response) {
+ NetServerConfig* config = NetServerConfig::GetInstance();
+
+ Text admin_name = "system";
+ Text admin_pass = "manager";
+
+ if (config) {
+ admin_name = config->GetAdminName();
+ admin_pass = config->GetAdminPass();
+ }
+
+ Text name = request.GetParam("user");
+ Text pass = request.GetParam("pass");
+
+ Sleep(500);
+
+ if (CheckUser(request, response)) {
+ response.SetStatus(HttpResponse::SC_TEMPORARY_REDIRECT);
+ response.SetHeader("MIME-Version", "1.0");
+ response.SetHeader("Content-Type", "text/html");
+ response.SetHeader("Cache-Control", "no-cache");
+ response.SetHeader("Expires", "-1");
+ response.SetHeader("Location", "/home");
+
+ response.SetContent(GetHead("Login") +
+ "<body><br>You are already logged in.<br>" +
+ GetBodyClose());
+ }
+
+ else if (name == admin_name && pass == admin_pass) {
+ user = new(__FILE__,__LINE__) NetUser(name);
+ user->SetAddress(request.GetClientAddr());
+
+ if (session)
+ user->SetSessionID(session->GetID());
+
+ admin->AddUser(user);
+
+ response.SetStatus(HttpResponse::SC_TEMPORARY_REDIRECT);
+ response.SetHeader("MIME-Version", "1.0");
+ response.SetHeader("Content-Type", "text/html");
+ response.SetHeader("Cache-Control", "no-cache");
+ response.SetHeader("Expires", "-1");
+ response.SetHeader("Location", "/home");
+
+ response.SetContent(GetHead("Login") +
+ "<body><br>You have successfully logged in.<br>" +
+ GetBodyClose());
+ }
+
+ else {
+ response.SetStatus(HttpResponse::SC_OK);
+ response.SetHeader("MIME-Version", "1.0");
+ response.SetHeader("Content-Type", "text/html");
+ response.SetHeader("Cache-Control", "no-cache");
+ response.SetHeader("Expires", "-1");
+
+ response.SetContent(GetHead("Login") +
+ GetTitleBar(0, "onLoad=\"self.focus();document.loginForm.user.focus();\"") +
+ GetContent() +
+ GetBodyClose());
+ }
+
+ return true;
+ }
+
+ virtual Text GetContent() { Text content =
+" <table border=\"0\" cellspacing=\"0\" cellpadding=\"4\" align=\"left\" width =\"100%\">\n\
+ <tr>\n\
+ <td width=\"100\">&nbsp;&nbsp;</td>\n\
+ <td valign=\"top\" align=\"left\" width=\"400\"><br><br>\n\
+ <span class=\"subhead\">Welcome to the Starshatter Server!</span><br><br>\n\
+ <span class=\"std\">Login to access the server <b>";
+
+ NetServerConfig* config = NetServerConfig::GetInstance();
+ if (config)
+ content += config->Name();
+ else
+ content += "server";
+
+ content += "</b></span><br>\n\
+ <form name=\"loginForm\" method=\"get\" action=\"/login\">\n\
+ <table border=\"0\" cellspacing=\"0\" cellpadding=\"4\" width=\"100\">\n\
+ <tr>\n\
+ <td align=\"left\" valign=\"middle\" width=\"80\"><span class=\"std\">Username:</span></td>\n\
+ <td align=\"left\" valign=\"middle\"><input type=\"text\" name=\"user\" size=\"25\"></td>\n\
+ </tr>\n\
+ <tr>\n\
+ <td align=\"left\" valign=\"middle\" width=\"80\"><span class=\"std\">Password:</span></td>\n\
+ <td align=\"left\" valign=\"middle\"><input type=\"password\" name=\"pass\" size=\"25\"></td>\n\
+ </tr>\n\
+ <tr>\n\
+ <td>&nbsp;</td>\n\
+ <td align=\"right\"><input type=\"submit\" value=\"Login\"></td>\n\
+ </tr>\n\
+ </table>\n\
+ </form>\n\
+ <td>&nbsp;</td>\n\
+ </tr>\n\
+ </table>\n";
+
+ return content;
+ }
+};
+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+
+class NetAdminServerMgr : public NetAdminServlet
+{
+public:
+ NetAdminServerMgr() { }
+ virtual ~NetAdminServerMgr() { }
+
+ virtual bool DoGet(HttpRequest& request, HttpResponse& response) {
+ if (CheckUser(request, response)) {
+ Text action = request.GetParam("action");
+ action.setSensitive(false);
+
+ bool completed = false;
+
+ if (action == "restart") {
+ StarServer* svr = StarServer::GetInstance();
+
+ if (svr) {
+ svr->Shutdown(true);
+ completed = true;
+
+ response.SetStatus(HttpResponse::SC_OK);
+ response.SetHeader("MIME-Version", "1.0");
+ response.SetHeader("Content-Type", "text/html");
+ response.SetHeader("Cache-Control", "no-cache");
+ response.SetHeader("Expires", "-1");
+
+ response.SetContent(GetHead("Restart") +
+ GetTitleBar() +
+ "<div class=\"content\"><b>The Starshatter Server will restart in three (3) seconds.</b><br></div>" +
+ GetBodyClose());
+ }
+ }
+
+ else if (action == "shutdown") {
+ StarServer* svr = StarServer::GetInstance();
+
+ if (svr) {
+ svr->Shutdown(false);
+ completed = true;
+
+ response.SetStatus(HttpResponse::SC_OK);
+ response.SetHeader("MIME-Version", "1.0");
+ response.SetHeader("Content-Type", "text/html");
+ response.SetHeader("Cache-Control", "no-cache");
+ response.SetHeader("Expires", "-1");
+
+ response.SetContent(GetHead("Restart") +
+ GetTitleBar() +
+ "<div class=\"content\"><b>The Starshatter Server will shutdown in three (3) seconds.</b><br></div>" +
+ GetBodyClose());
+ }
+ }
+
+ if (!completed) {
+ response.SetStatus(HttpResponse::SC_TEMPORARY_REDIRECT);
+ response.SetHeader("MIME-Version", "1.0");
+ response.SetHeader("Content-Type", "text/html");
+ response.SetHeader("Cache-Control", "no-cache");
+ response.SetHeader("Expires", "-1");
+ response.SetHeader("Location", "/home");
+
+ response.SetContent(GetHead("Login") +
+ "<body><br>Unknown Action.<br>" +
+ GetBodyClose());
+ }
+ }
+
+ return true;
+ }
+};
+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+
+class NetAdminFile : public NetAdminServlet
+{
+public:
+ NetAdminFile() { }
+ virtual ~NetAdminFile() { }
+
+ virtual bool DoGet(HttpRequest& request, HttpResponse& response) {
+ if (!CheckUser(request, response))
+ return true;
+
+ Text content;
+ Text path = request.GetParam("path");
+ Text name = request.GetParam("name");
+
+ if (name.length()) {
+ BYTE* buffer = 0;
+ DataLoader* loader = DataLoader::GetLoader();
+
+ if (loader) {
+ bool use_file_system = loader->IsFileSystemEnabled();
+
+ loader->UseFileSystem(true);
+ loader->SetDataPath(path);
+ int len = loader->LoadBuffer(name, buffer);
+
+ if (len) {
+ content = Text((const char*) buffer, len);
+ }
+
+ loader->ReleaseBuffer(buffer);
+ loader->SetDataPath(0);
+ loader->UseFileSystem(use_file_system);
+ }
+ }
+
+ response.SetStatus(HttpResponse::SC_OK);
+ response.AddHeader("MIME-Version", "1.0");
+ response.AddHeader("Cache-Control", "no-cache");
+ response.AddHeader("Expires", "-1");
+ response.AddHeader("Content-Type", "text/plain");
+ response.SetContent(content);
+
+ return true;
+ }
+};
+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+
+class NetAdminUserList : public NetAdminServlet
+{
+public:
+ NetAdminUserList() { }
+ virtual ~NetAdminUserList() { }
+
+ virtual bool DoGet(HttpRequest& request, HttpResponse& response) {
+ if (CheckUser(request, response)) {
+ response.SetStatus(HttpResponse::SC_OK);
+ response.SetHeader("MIME-Version", "1.0");
+ response.SetHeader("Content-Type", "text/html");
+ response.SetHeader("Cache-Control", "no-cache");
+ response.SetHeader("Expires", "-1");
+
+ response.SetContent(GetHead("User List") +
+ GetTitleBar() +
+ GetContent() +
+ GetBodyClose());
+ }
+
+ return true;
+ }
+
+ virtual Text GetContent() {
+ Text content =
+"<script LANGUAGE=\"JavaScript\">\n\
+<!--\n\
+function doConfirm() {\n\
+ return confirm(\"Are you sure you want to ban this player?\");\n\
+}\n\
+// -->\n\
+</script>\n\
+<div class=\"content\">\n\
+ <table border=\"0\" width=\"95%\">\n\
+ <tr class=\"heading\">\n\
+ <td nowrap valign=\"middle\" align=\"left\">\n\
+ <span class=\"heading\">&nbsp;User List</span>\n\
+ </td>\n\
+ </tr>\n\
+ </table>\n\n";
+
+ content +=
+" <table border=\"0\" width=\"95%\" class=\"std\">\n\
+ <tr>\n\
+ <td nowrap width=\"1%\">&nbsp;</td>\n\
+ <td nowrap width=\"20%\" valign=\"middle\" align=\"left\"><b>Name</b></td>\n\
+ <td nowrap width=\"10%\" valign=\"middle\" align=\"left\"><b>Address</b></td>\n\
+ <td nowrap width=\"10%\" valign=\"middle\" align=\"center\"><b>Is Host</b></td>\n\
+ <td nowrap width=\"20%\" valign=\"middle\" align=\"left\"><b>Squadron</b></td>\n\
+ <td nowrap width=\"20%\" valign=\"middle\" align=\"center\"><b>Stats</b></td>\n\
+ <td nowrap width=\"19%\" valign=\"middle\" align=\"center\"><b>Ban</b></td>\n\
+ <td></td>\n\
+ </tr>\n";
+
+ NetLobbyServer* lobby = NetLobbyServer::GetInstance();
+
+ if (lobby) {
+ ListIter<NetUser> u_iter = lobby->GetUsers();
+ while (++u_iter) {
+ NetUser* u = u_iter.value();
+ NetAddr a = u->GetAddress();
+
+ char addr_dotted[32];
+ char addr_hex[16];
+ char user_stats[16];
+
+ sprintf(addr_dotted, "%d.%d.%d.%d", a.B1(), a.B2(), a.B3(), a.B4());
+ sprintf(addr_hex, "%08x", a.IPAddr());
+ sprintf(user_stats, "%d / %d / %d", u->Missions(), u->Kills(), u->Losses());
+
+ content += "<tr>\n<td nowrap width=\"1%\">&nbsp;</td>\n\
+ <td nowrap valign=\"middle\" align=\"left\">";
+ content += u->Name();
+ content += "</td><td nowrap valign=\"middle\" align=\"left\">";
+ content += addr_dotted;
+ content += "</td><td nowrap valign=\"middle\" align=\"center\">";
+ content += u->IsHost() ? "*" : "&nbsp;";
+ content += "</td><td nowrap valign=\"middle\" align=\"left\">";
+ content += u->Squadron();
+ content += "</td><td nowrap valign=\"middle\" align=\"center\">";
+ content += user_stats;
+ content += "</td><td nowrap valign=\"middle\" align=\"center\">";
+ content += "<a onclick=\"return doConfirm()\" href=\"/ban?name=";
+ content += HttpRequest::EncodeParam(u->Name());
+ content += "&addr=";
+ content += addr_hex;
+ content += "\">BAN</a></td></tr>\n";
+ }
+ }
+
+ content += " </table>\n\n";
+
+ content += "</div>\n\n";
+ content += GetCopyright();
+ return content;
+ }
+};
+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+
+class NetAdminBanUser : public NetAdminServlet
+{
+public:
+ NetAdminBanUser() { }
+ virtual ~NetAdminBanUser() { }
+
+ virtual bool DoGet(HttpRequest& request, HttpResponse& response) {
+ if (CheckUser(request, response)) {
+ Text name = request.GetParam("name");
+ bool completed = false;
+
+ NetLobbyServer* lobby = NetLobbyServer::GetInstance();
+
+ if (lobby) {
+ ListIter<NetUser> u_iter = lobby->GetUsers();
+ while (++u_iter && !completed) {
+ NetUser* u = u_iter.value();
+
+ if (u->Name() == name) {
+ NetLobbyServer* nls = NetLobbyServer::GetInstance();
+
+ if (nls) {
+ nls->BanUser(u);
+ completed = true;
+ }
+ }
+ }
+ }
+
+ response.SetStatus(HttpResponse::SC_TEMPORARY_REDIRECT);
+ response.SetHeader("MIME-Version", "1.0");
+ response.SetHeader("Content-Type", "text/html");
+ response.SetHeader("Cache-Control", "no-cache");
+ response.SetHeader("Expires", "-1");
+ response.SetHeader("Location", "/users");
+
+ response.SetContent(GetHead("User List") +
+ GetTitleBar() +
+ "<div class=\"content\">User Banned.<br></div>" +
+ GetBodyClose());
+ }
+
+ return true;
+ }
+};
+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+
+static NetAdminServer* net_Admin_server = 0;
+
+NetAdminServer*
+NetAdminServer::GetInstance(WORD port)
+{
+ if (!net_Admin_server && port > 0)
+ net_Admin_server = new(__FILE__,__LINE__) NetAdminServer(port);
+
+ return net_Admin_server;
+}
+
+NetAdminServer::NetAdminServer(WORD port)
+ : HttpServletExec(port)
+{
+ http_server_name = Text("Starshatter NetAdminServer ") + versionInfo;
+}
+
+NetAdminServer::~NetAdminServer()
+{
+ if (net_Admin_server == this)
+ net_Admin_server = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+HttpServlet*
+NetAdminServer::GetServlet(HttpRequest& request)
+{
+ Text path = request.URI();
+ path.setSensitive(false);
+
+ if (path.indexOf("/login") == 0)
+ return new(__FILE__,__LINE__) NetAdminLogin;
+
+ if (path.indexOf("/chat") == 0)
+ return new(__FILE__,__LINE__) NetAdminChat;
+
+ if (path.indexOf("/server") == 0)
+ return new(__FILE__,__LINE__) NetAdminServerMgr;
+
+ if (path.indexOf("/file") == 0)
+ return new(__FILE__,__LINE__) NetAdminFile;
+
+ if (path.indexOf("/user") == 0)
+ return new(__FILE__,__LINE__) NetAdminUserList;
+
+ if (path.indexOf("/ban") == 0)
+ return new(__FILE__,__LINE__) NetAdminBanUser;
+
+ return new(__FILE__,__LINE__) NetAdminServlet;
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetAdminServer::AddChat(NetUser* user, const char* msg)
+{
+ if (user && msg && *msg) {
+ NetLobbyServer* lobby = NetLobbyServer::GetInstance();
+
+ if (lobby)
+ lobby->AddChat(user, msg);
+ }
+}
+
+ListIter<NetChatEntry>
+NetAdminServer::GetChat()
+{
+ NetLobbyServer* lobby = NetLobbyServer::GetInstance();
+
+ if (lobby)
+ return lobby->GetChat();
+
+ static List<NetChatEntry> idle_chatter;
+ return idle_chatter;
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetAdminServer::AddUser(NetUser* user)
+{
+ if (user && !admin_users.contains(user))
+ admin_users.append(user);
+}
+
+void
+NetAdminServer::DelUser(NetUser* user)
+{
+ if (user) {
+ admin_users.remove(user);
+ delete user;
+ }
+}
+
+int
+NetAdminServer::NumUsers()
+{
+ return admin_users.size();
+}
+
+
+List<NetUser>&
+NetAdminServer::GetUsers()
+{
+ return admin_users;
+}
+
+bool
+NetAdminServer::HasHost()
+{
+ bool result = false;
+
+ NetLobbyServer* lobby = NetLobbyServer::GetInstance();
+
+ if (lobby)
+ result = lobby->HasHost();
+
+ return result;
+}
+
+NetUser*
+NetAdminServer::FindUserBySession(Text id)
+{
+ ListIter<NetUser> iter = admin_users;
+ while (++iter) {
+ NetUser* u = iter.value();
+ if (u->GetSessionID() == id)
+ return u;
+ }
+
+ return 0;
+}
+
+void
+NetAdminServer::DoSyncedCheck()
+{
+ ListIter<NetUser> iter = admin_users;
+ while (++iter) {
+ NetUser* u = iter.value();
+
+ bool found = false;
+
+ ListIter<HttpSession> s_iter = sessions;
+ while (++s_iter && !found) {
+ HttpSession* s = s_iter.value();
+
+ if (s->GetID() == u->GetSessionID())
+ found = true;
+ }
+
+ if (!found)
+ delete iter.removeItem();
+ }
+}
+
+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+
+NetAdminServlet::NetAdminServlet()
+{
+ admin = NetAdminServer::GetInstance();
+ user = 0;
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+NetAdminServlet::DoGet(HttpRequest& request, HttpResponse& response)
+{
+ if (CheckUser(request, response)) {
+
+ if (request.URI() == "/home")
+ response.SetStatus(HttpResponse::SC_OK);
+ else
+ response.SetStatus(HttpResponse::SC_TEMPORARY_REDIRECT);
+
+ response.SetHeader("MIME-Version", "1.0");
+ response.SetHeader("Content-Type", "text/html");
+ response.SetHeader("Cache-Control", "no-cache");
+ response.SetHeader("Expires", "-1");
+ response.SetHeader("Location", "/home");
+
+ response.SetContent(GetHead() +
+ GetTitleBar(GetStatLine()) +
+ GetContent() +
+ GetBodyClose());
+ }
+
+ return true;
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+NetAdminServlet::CheckUser(HttpRequest& request, HttpResponse& response)
+{
+ if (!user) {
+ if (session)
+ user = admin->FindUserBySession(session->GetID());
+
+ if (!user) {
+ response.SetStatus(HttpResponse::SC_TEMPORARY_REDIRECT);
+ response.SetHeader("MIME-Version", "1.0");
+ response.SetHeader("Content-Type", "text/plain");
+ response.SetHeader("Cache-Control", "no-cache");
+ response.SetHeader("Expires", "-1");
+ response.SetHeader("Location", "/login");
+ response.SetContent("You are not logged in.");
+ }
+ }
+
+ return user != 0;
+}
+
+// +-------------------------------------------------------------------+
+
+Text
+NetAdminServlet::GetCSS()
+{
+ return
+
+"body { font-family:arial,helvetica,sans-serif; color:black; background-color:white }\n\
+a:link { text-decoration:none; font-weight:normal; font-size:10pt; color:black }\n\
+a:visited { text-decoration:none; font-weight:normal; font-size:10pt; color:black }\n\
+a:hover { text-decoration:underline; font-weight:normal; font-size:10pt; color:black }\n\
+.std { font-size:10pt }\n\
+.tiny { font-size:8pt }\n\
+.heading { font-size:14pt; font-weight:bold; background-color:#99BBEE }\n\
+.subhead { font-size:11pt; font-weight:bold }\n\
+.status { font-size:10pt; color:white }\n\
+.content { padding-right: 4pt; padding-left: 4pt; padding-bottom: 4pt; padding-top: 4pt; margin: 4pt; }\n\
+.copy { font-size:8pt; }\n\
+.top-bar { color: white; background-color: #336699 }\n\
+.top-line { color: yellow; background-color: black }\n\
+.topbarbig { line-height:24px; color:white; font-size:18px; font-weight:bold; }\n\
+.topbarsmall { line-height:18px; color:white; font-size:14px; }\n";
+}
+
+Text
+NetAdminServlet::GetHead(const char* title)
+{
+ Text head = "<html>\n<head>\n<title>Starshatter Server";
+
+ if (title && *title) {
+ head += " - ";
+ head += title;
+ }
+
+ head += "</title>\n<style type=\"text/css\" media=\"screen\">\n";
+ head += GetCSS();
+ head += "</style>\n</head>\n";
+
+ return head;
+}
+
+Text
+NetAdminServlet::GetBody()
+{
+ return GetTitleBar(GetStatLine()) +
+ GetContent() +
+ GetBodyClose();
+}
+
+Text
+NetAdminServlet::GetTitleBar(const char* statline, const char* onload)
+{
+ Text bar = "<body ";
+
+ if (onload && *onload)
+ bar += onload;
+
+ bar += " leftmargin=\"0\" topmargin=\"0\" marginwidth=\"0\" marginheight=\"0\">\n\
+ <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%\" class=\"top-bar\">\n\
+ <tr height=\"50\">\n\
+ <td>&nbsp;</td>\n\
+ <td valign=\"middle\" align=\"left\">\n\
+ <span class=\"topbarsmall\">Administration Console</span><br>\n";
+
+ if (statline) {
+ bar += "<a href=\"/home\">";
+ }
+
+ bar += "<span class=\"topbarbig\">Starshatter Server ";
+ bar += versionInfo;
+ bar += "</span>";
+
+ if (statline) {
+ bar += "</a>";
+ }
+
+ bar += "\n\
+ </td>\n\
+ </tr>\n\
+ <tr class=\"top-line\">\n\
+ <td colspan=\"2\">";
+
+ if (statline && *statline)
+ bar += statline;
+ else
+ bar += "&nbsp;";
+
+ bar += "</td>\n\
+ </tr>\n\
+ </table>\n\n";
+
+ return bar;
+}
+
+Text
+NetAdminServlet::GetStatLine()
+{
+ NetServerConfig* config = NetServerConfig::GetInstance();
+
+ Text line =
+" <table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" border=\"0\">\n\
+ <tr>\n\
+ <td nowrap width=\"33%\" class=\"top-line\" align=\"left\">\n\
+ <span class=\"status\">&nbsp;&nbsp;Connected to <b>";
+
+ char buffer[256];
+ sprintf(buffer, "%s:%d", config->Name().data(), config->GetAdminPort());
+ line += buffer;
+
+ line += "</b></span>\n\
+ </td>\n\
+ <td nowrap width=\"34%\" class=\"top-line\" align=\"center\">\n\
+ <span class=\"status\">Server Mode: <b>";
+
+ NetLobbyServer* lobby = NetLobbyServer::GetInstance();
+ if (lobby) {
+ switch (lobby->GetStatus()) {
+ default:
+ case NetServerInfo::OFFLINE: line += "Offline"; break;
+ case NetServerInfo::LOBBY: line += "Lobby"; break;
+ case NetServerInfo::BRIEFING: line += "Briefing"; break;
+ case NetServerInfo::ACTIVE: line += "Active"; break;
+ case NetServerInfo::DEBRIEFING: line += "Debriefing"; break;
+ case NetServerInfo::PERSISTENT: line += "PERSISTENT"; break;
+ }
+ }
+ else {
+ line += "Unknown";
+ }
+
+ line += "</b></span>\n\
+ </td>\n\
+ <td nowrap width=\"33%\" class=\"top-line\" align=\"right\">\n\
+ <span class=\"status\">";
+
+ line += FormatTimeString();
+
+ line += "&nbsp;&nbsp;</span>\n\
+ </td>\n\
+ </tr>\n\
+ </table>\n";
+
+ return line;
+}
+
+Text
+NetAdminServlet::GetContent()
+{
+ Text content =
+"<script LANGUAGE=\"JavaScript\">\n\
+<!--\n\
+function doConfirm() {\n\
+ return confirm(\"Are you sure you want to do this?\");\n\
+}\n\
+// -->\n\
+</script>\n\
+<div class=\"content\">\n\
+ <table border=\"0\" width=\"95%\">\n\
+ <tr class=\"heading\">\n\
+ <td nowrap valign=\"middle\" align=\"left\">\n\
+ <span class=\"heading\">&nbsp;Game Admin Functions</span>\n\
+ </td>\n\
+ </tr>\n\
+ </table>\n\n\
+ <table border=\"0\" width=\"95%\">\n\
+ <tr>\n\
+ <td nowrap width=\"1%\">&nbsp;</td>\n\
+ <td nowrap width=\"33%\" valign=\"middle\" align=\"left\">\n\
+ <a href=\"/chat\">Lobby Chat</a>\n\
+ </td>\n\
+ <td nowrap width=\"33%\" valign=\"middle\" align=\"left\">\n\
+ <a href=\"/home\">Mission List</a>\n\
+ </td>\n\
+ <td></td>\n\
+ </tr>\n\
+ <tr>\n\
+ <td nowrap width=\"1%\">&nbsp;</td>\n\
+ <td nowrap width=\"33%\" valign=\"middle\" align=\"left\">\n\
+ <a href=\"/file?name=errlog.txt\">View Error Log</a>\n\
+ </td>\n\
+ <td nowrap width=\"33%\" valign=\"middle\" align=\"left\">\n\
+ <a href=\"/users\">Player List</a>\n\
+ </td>\n\
+ <td></td>\n\
+ </tr>\n\
+ <tr>\n\
+ <td nowrap width=\"1%\">&nbsp;</td>\n\
+ <td nowrap width=\"33%\" valign=\"middle\" align=\"left\">\n\
+ <a href=\"/file?name=serverlog.txt\">View Server Log</a>\n\
+ </td>\n\
+ <td nowrap width=\"33%\" valign=\"middle\" align=\"left\">\n\
+ <a href=\"/home\">Ban List</a>\n\
+ </td>\n\
+ <td></td>\n\
+ </tr>\n\
+ <tr>\n\
+ <td nowrap width=\"1%\">&nbsp;</td>\n\
+ <td nowrap width=\"33%\" valign=\"middle\" align=\"left\"></td>\n\
+ <td nowrap width=\"33%\" valign=\"middle\" align=\"left\"></td>\n\
+ <td></td>\n\
+ </tr>\n\
+ </table>\n\n";
+
+ content +=
+" <table border=\"0\" width=\"95%\">\n\
+ <tr class=\"heading\">\n\
+ <td nowrap valign=\"middle\" align=\"left\">\n\
+ <span class=\"heading\">&nbsp;Server Admin Functions</span>\n\
+ </td>\n\
+ </tr>\n\
+ </table>\n\n\
+ <table border=\"0\" width=\"95%\">\n\
+ <tr>\n\
+ <td nowrap width=\"1%\">&nbsp;</td>\n\
+ <td nowrap width=\"33%\" valign=\"middle\" align=\"left\">\n\
+ <a onclick=\"return doConfirm()\" href=\"/server?action=restart\">Restart Server</a>\n\
+ </td>\n\
+ <td nowrap width=\"33%\" valign=\"middle\" align=\"left\">\n\
+ <a onclick=\"return doConfirm()\" href=\"/server?action=shutdown\">Shutdown Server</a>\n\
+ </td>\n\
+ <td></td>\n\
+ </tr>\n\
+ </table>\n\n";
+
+ content += "</div>\n\n";
+ content += GetCopyright();
+ return content;
+}
+
+Text
+NetAdminServlet::GetBodyClose()
+{
+ return "\n\n</body>\n</html>\n";
+}
+
+Text
+NetAdminServlet::GetCopyright()
+{
+ return "<br><span class=\"copy\">&nbsp;&nbsp;&nbsp;&nbsp;Copyright &copy; 1997-2004 Destroyer Studios. All rights reserved.</span><br>";
+}
diff --git a/Stars45/NetAdminServer.h b/Stars45/NetAdminServer.h
new file mode 100644
index 0000000..a285adf
--- /dev/null
+++ b/Stars45/NetAdminServer.h
@@ -0,0 +1,91 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetAdminServer.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ HTTP Servlet Engine for Multiplayer Admin
+*/
+
+
+#ifndef NetAdminServer_h
+#define NetAdminServer_h
+
+#include "HttpServletExec.h"
+#include "HttpServlet.h"
+
+// +-------------------------------------------------------------------+
+
+class Mission;
+class MissionElement;
+class NetChatEntry;
+class NetUser;
+
+// +-------------------------------------------------------------------+
+
+class NetAdminServer : public HttpServletExec
+{
+public:
+ virtual ~NetAdminServer();
+
+ int operator == (const NetAdminServer& s) const { return this == &s; }
+
+ virtual HttpServlet* GetServlet(HttpRequest& request);
+
+ virtual void AddUser(NetUser* user);
+ virtual void DelUser(NetUser* user);
+ virtual int NumUsers();
+ virtual bool HasHost();
+ virtual List<NetUser>& GetUsers();
+
+ virtual NetUser* FindUserBySession(Text id);
+
+ virtual void AddChat(NetUser* user, const char* msg);
+ ListIter<NetChatEntry> GetChat();
+ DWORD GetStartTime() const { return start_time; }
+
+ virtual void GameOn() { }
+ virtual void GameOff() { }
+
+ // singleton locator:
+ static NetAdminServer* GetInstance(WORD port=0);
+
+protected:
+ NetAdminServer(WORD port);
+ virtual void DoSyncedCheck();
+
+ DWORD start_time;
+ List<NetUser> admin_users;
+};
+
+// +-------------------------------------------------------------------+
+
+class NetAdminServlet : public HttpServlet
+{
+public:
+ NetAdminServlet();
+ virtual ~NetAdminServlet() { }
+
+ virtual bool DoGet(HttpRequest& request, HttpResponse& response);
+ virtual bool CheckUser(HttpRequest& request, HttpResponse& response);
+
+ virtual Text GetCSS();
+ virtual Text GetHead(const char* title=0);
+ virtual Text GetBody();
+ virtual Text GetTitleBar(const char* statline=0, const char* onload=0);
+ virtual Text GetStatLine();
+ virtual Text GetCopyright();
+ virtual Text GetContent();
+ virtual Text GetBodyClose();
+
+protected:
+ NetAdminServer* admin;
+ NetUser* user;
+};
+
+#endif NetAdminServer_h \ No newline at end of file
diff --git a/Stars45/NetAuth.cpp b/Stars45/NetAuth.cpp
new file mode 100644
index 0000000..2e06f0b
--- /dev/null
+++ b/Stars45/NetAuth.cpp
@@ -0,0 +1,216 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetAuth.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ This class represents a user connecting to the multiplayer lobby
+*/
+
+
+#include "MemDebug.h"
+#include "NetAuth.h"
+#include "NetLobby.h"
+#include "NetUser.h"
+#include "ModConfig.h"
+#include "ModInfo.h"
+#include "Random.h"
+#include "sha1.h"
+
+static int auth_level = NetAuth::NET_AUTH_MINIMAL;
+
+// +-------------------------------------------------------------------+
+
+int
+NetAuth::AuthLevel()
+{
+ return auth_level;
+}
+
+
+void
+NetAuth::SetAuthLevel(int n)
+{
+ if (n >= NET_AUTH_MINIMAL && n <= NET_AUTH_SECURE)
+ auth_level = n;
+}
+
+// +-------------------------------------------------------------------+
+
+Text
+NetAuth::CreateAuthRequest(NetUser* u)
+{
+ Text request;
+
+ if (u) {
+ u->SetAuthLevel(auth_level);
+
+ if (auth_level == NET_AUTH_MINIMAL) {
+ u->SetAuthState(NET_AUTH_OK);
+ u->SetSalt("Very Low Sodium");
+ }
+
+ else if (auth_level == NET_AUTH_STANDARD) {
+ u->SetAuthState(NET_AUTH_INITIAL);
+ u->SetSalt("Very Low Sodium");
+
+ request = "level 1";
+ }
+
+ else {
+ char salt[33];
+
+ for (int i = 0; i < 32; i++)
+ salt[i] = (char) ('0' + (int) Random(0, 9.4));
+
+ salt[32] = 0;
+ u->SetSalt(salt);
+ u->SetAuthState(NET_AUTH_INITIAL);
+
+ request = "level 2 salt ";
+ request += salt;
+ }
+ }
+
+ return request;
+}
+
+// +-------------------------------------------------------------------+
+
+static Text Digest(const char* salt, const char* file)
+{
+ int length = 0;
+ int offset = 0;
+ char block[4096];
+ char digest[64];
+
+ ZeroMemory(digest, sizeof(digest));
+
+ if (file) {
+ FILE* f = fopen(file, "rb");
+
+ if (f) {
+ SHA1 sha1;
+
+ if (salt) {
+ sha1.Input(salt, strlen(salt));
+ }
+
+ fseek(f, 0, SEEK_END);
+ length = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ while (offset < length) {
+ int n = fread(block, sizeof(char), 4096, f);
+ sha1.Input(block, n);
+ offset += n;
+ }
+
+ fclose(f);
+
+ unsigned result[5];
+ if (sha1.Result(result)) {
+ sprintf(digest, "SHA1_%08X_%08X_%08X_%08X_%08X",
+ result[0], result[1], result[2], result[3], result[4]);
+ }
+ }
+ }
+
+ return digest;
+}
+
+// +-------------------------------------------------------------------+
+
+Text
+NetAuth::CreateAuthResponse(int level, const char* salt)
+{
+ Text response;
+ ModConfig* config = ModConfig::GetInstance();
+
+ if (level == NET_AUTH_SECURE) {
+
+#ifdef STARSHATTER_DEMO_RELEASE
+
+ response += "exe ";
+ response += Digest(salt, "StarDemo.exe"); // XXX should look up name of this exe
+ response += " ";
+
+#else
+
+ response += "exe ";
+ response += Digest(salt, "stars.exe"); // XXX should look up name of this exe
+ response += " ";
+
+#endif
+
+ response += "dat ";
+ response += Digest(salt, "shatter.dat");
+ response += " ";
+
+ response += "etc ";
+ response += Digest(salt, "start.dat");
+ response += " ";
+ }
+
+ if (level >= NET_AUTH_STANDARD) {
+ List<ModInfo>& mods = config->GetModInfoList();
+ ListIter<ModInfo> mod_iter = mods;
+
+ char buffer[32];
+ sprintf(buffer, "num %d ", mods.size());
+ response += buffer;
+
+ while (++mod_iter) {
+ ModInfo* info = mod_iter.value();
+
+ response += "mod \"";
+ response += info->Name();
+ response += "\" ver \"";
+ response += info->Version();
+ response += "\" ";
+
+ if (level == NET_AUTH_SECURE) {
+ response += "sha ";
+ response += Digest(salt, info->Filename());
+ response += " ";
+ }
+ }
+ }
+
+ return response;
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+NetAuth::AuthUser(NetUser* u, Text response)
+{
+ bool authentic = false;
+
+ if (auth_level == NET_AUTH_MINIMAL) { // (this case should not occur)
+ if (u) {
+ u->SetAuthLevel(auth_level);
+ u->SetAuthState(NET_AUTH_OK);
+ }
+
+ authentic = (u != 0);
+ }
+
+ else if (u) {
+ Text expected_response = CreateAuthResponse(auth_level, u->Salt());
+ if (expected_response == response)
+ authentic = true;
+
+ u->SetAuthState(authentic ? NET_AUTH_OK : NET_AUTH_FAILED);
+ }
+
+ return authentic;
+}
+
+// +-------------------------------------------------------------------+
+
diff --git a/Stars45/NetAuth.h b/Stars45/NetAuth.h
new file mode 100644
index 0000000..8966170
--- /dev/null
+++ b/Stars45/NetAuth.h
@@ -0,0 +1,51 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetAuth.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ This class authenticates a user connecting to the multiplayer lobby
+*/
+
+
+#ifndef NetAuth_h
+#define NetAuth_h
+
+#include "Types.h"
+#include "NetAddr.h"
+#include "NetLobby.h"
+#include "Text.h"
+
+// +-------------------------------------------------------------------+
+
+class NetAuth
+{
+public:
+ enum AUTH_STATE {
+ NET_AUTH_INITIAL = 0,
+ NET_AUTH_FAILED = 1,
+ NET_AUTH_OK = 2
+ };
+
+ enum AUTH_LEVEL {
+ NET_AUTH_MINIMAL = 0,
+ NET_AUTH_STANDARD = 1,
+ NET_AUTH_SECURE = 2
+ };
+
+ static int AuthLevel();
+ static void SetAuthLevel(int n);
+
+ static Text CreateAuthRequest(NetUser* u);
+ static Text CreateAuthResponse(int level, const char* salt);
+ static bool AuthUser(NetUser* u, Text response);
+};
+
+// +-------------------------------------------------------------------+
+
+#endif NetAuth_h \ No newline at end of file
diff --git a/Stars45/NetBrokerClient.cpp b/Stars45/NetBrokerClient.cpp
new file mode 100644
index 0000000..5f5369d
--- /dev/null
+++ b/Stars45/NetBrokerClient.cpp
@@ -0,0 +1,239 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetBrokerClient.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Client for Starshatter.com GameNet Broker
+*/
+
+
+#include "MemDebug.h"
+#include "NetBrokerClient.h"
+
+#include "HttpClient.h"
+#include "HttpServlet.h"
+#include "NetLayer.h"
+
+#include "Game.h"
+
+extern const char* versionInfo;
+const char* HOSTNAME = "www.starshatter.com";
+const WORD HTTPPORT = 80;
+
+bool NetBrokerClient::broker_available = true;
+bool NetBrokerClient::broker_found = false;
+
+// +--------------------------------------------------------------------+
+
+bool
+NetBrokerClient::GameOn(const char* name,
+ const char* type,
+ const char* addr,
+ WORD port,
+ const char* password)
+{
+ bool result = false;
+
+ if (!broker_available)
+ return result;
+
+ Text msg;
+ char buffer[8];
+ NetAddr broker(HOSTNAME, HTTPPORT);
+
+ if (broker.IPAddr() == 0)
+ return result;
+
+ sprintf(buffer, "%d", port);
+
+ msg = "GET http://";
+ msg += HOSTNAME;
+ msg += "/GameNet/GameOn.php?name=";
+ msg += HttpRequest::EncodeParam(name);
+ msg += "&addr=";
+ msg += HttpRequest::EncodeParam(addr);
+ msg += "&game=";
+ msg += HttpRequest::EncodeParam(type);
+ msg += "&vers=";
+ msg += HttpRequest::EncodeParam(versionInfo);
+ msg += "&port=";
+ msg += buffer;
+ msg += "&pass=";
+ msg += HttpRequest::EncodeParam(password);
+ msg += " HTTP/1.1\n\n";
+
+ HttpClient client(broker);
+ HttpRequest request(msg);
+ request.SetHeader("Host", HOSTNAME);
+
+ HttpResponse* response = client.DoRequest(request);
+
+ if (response && response->Status() == 200) {
+ broker_found = true;
+ result = true;
+ }
+ else {
+ ::Print("NetBrokerClient unable to contact GameNet!\n");
+
+ if (response) {
+ ::Print(" Response Status: %d\n", response->Status());
+ ::Print(" Response Content: %s\n", response->Content());
+ }
+ else {
+ ::Print(" No response.\n");
+ }
+
+ if (!broker_found)
+ broker_available = false;
+ }
+
+ delete response;
+ return result;
+}
+
+bool
+NetBrokerClient::GameList(const char* type, List<NetServerInfo>& server_list)
+{
+ bool result = false;
+
+ if (!broker_available)
+ return result;
+
+ Text msg;
+ NetAddr broker(HOSTNAME, HTTPPORT);
+
+ if (broker.IPAddr() == 0)
+ return result;
+
+ msg = "GET http://";
+ msg += HOSTNAME;
+ msg += "/GameNet/GameList.php?game=";
+ msg += HttpRequest::EncodeParam(type);
+ msg += "&vers=";
+ msg += HttpRequest::EncodeParam(versionInfo);
+ msg += " HTTP/1.1\n\n";
+
+ HttpClient client(broker);
+ HttpRequest request(msg);
+ request.SetHeader("Host", HOSTNAME);
+
+ HttpResponse* response = client.DoRequest(request);
+
+ if (response && response->Status() == 200) {
+ result = true;
+
+ Text name;
+ Text type;
+ Text addr;
+ int port;
+ Text pass;
+ Text vers;
+ char buff[1024];
+
+ const char* p = response->Content();
+
+ // skip size
+ while (*p && strncmp(p, "name:", 5))
+ p++;
+
+ while (*p) {
+ if (!strncmp(p, "name:", 5)) {
+ p += 5;
+ ZeroMemory(buff, sizeof(buff));
+ char* d = buff;
+ while (*p && *p != '\n') *d++ = *p++;
+ if (*p) p++;
+
+ name = buff;
+ }
+ else if (!strncmp(p, "addr:", 5)) {
+ p += 5;
+ ZeroMemory(buff, sizeof(buff));
+ char* d = buff;
+ while (*p && *p != '\n') *d++ = *p++;
+ if (*p) p++;
+
+ addr = buff;
+ }
+ else if (!strncmp(p, "port:", 5)) {
+ p += 5;
+ ZeroMemory(buff, sizeof(buff));
+ char* d = buff;
+ while (*p && *p != '\n') *d++ = *p++;
+ if (*p) p++;
+
+ sscanf(buff, "%d", &port);
+ }
+ else if (!strncmp(p, "pass:", 5)) {
+ p += 5;
+ ZeroMemory(buff, sizeof(buff));
+ char* d = buff;
+ while (*p && *p != '\n') *d++ = *p++;
+ if (*p) p++;
+
+ pass = buff;
+ }
+ else if (!strncmp(p, "game:", 5)) {
+ p += 5;
+ ZeroMemory(buff, sizeof(buff));
+ char* d = buff;
+ while (*p && *p != '\n') *d++ = *p++;
+ if (*p) p++;
+
+ type = buff;
+ type.setSensitive(false);
+
+ if (type.contains("lan"))
+ type = "LAN";
+ else
+ type = "Public";
+ }
+ else if (!strncmp(p, "vers:", 5)) {
+ p += 5;
+ ZeroMemory(buff, sizeof(buff));
+ char* d = buff;
+ while (*p && *p != '\n') *d++ = *p++;
+ if (*p) p++;
+
+ vers = buff;
+ }
+ else if (!strncmp(p, "time:", 5)) {
+ while (*p && *p != '\n') p++;
+ if (*p) p++;
+ }
+
+ else if (!strncmp(p, "###", 3)) {
+ NetServerInfo* server = new(__FILE__,__LINE__) NetServerInfo;
+ server->name = name;
+ server->hostname = addr;
+ server->type = type;
+ server->addr = NetAddr(addr, port);
+ server->port = port;
+ server->password = pass;
+ server->version = vers;
+ server->save = false;
+
+ server_list.append(server);
+
+ while (*p && strncmp(p, "name:", 5))
+ p++;
+ }
+ else {
+ while (*p && *p != '\n') p++;
+ if (*p) p++;
+ }
+ }
+ }
+ else if (!broker_found) {
+ broker_available = false;
+ }
+
+ delete response;
+ return result;
+}
diff --git a/Stars45/NetBrokerClient.h b/Stars45/NetBrokerClient.h
new file mode 100644
index 0000000..9dba076
--- /dev/null
+++ b/Stars45/NetBrokerClient.h
@@ -0,0 +1,43 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetBrokerClient.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Client for Starshatter.com GameNet Broker
+*/
+
+
+#ifndef NetBrokerClient_h
+#define NetBrokerClient_h
+
+#include "HttpClient.h"
+#include "NetLobby.h"
+
+// +-------------------------------------------------------------------+
+
+class NetBrokerClient
+{
+public:
+ static void Enable() { broker_available = true; }
+ static void Disable() { broker_available = false; }
+
+ static bool GameOn(const char* name,
+ const char* type,
+ const char* addr,
+ WORD port,
+ const char* password);
+ static bool GameList(const char* type, List<NetServerInfo>& server_list);
+
+protected:
+ static bool broker_available;
+ static bool broker_found;
+};
+
+
+#endif NetBrokerClient_h \ No newline at end of file
diff --git a/Stars45/NetChat.cpp b/Stars45/NetChat.cpp
new file mode 100644
index 0000000..572236b
--- /dev/null
+++ b/Stars45/NetChat.cpp
@@ -0,0 +1,53 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetChat.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Single chat message and sender
+*/
+
+
+#include "MemDebug.h"
+#include "NetChat.h"
+#include "NetLayer.h"
+
+// +-------------------------------------------------------------------+
+
+static int chat_id_key = 1000;
+
+// +-------------------------------------------------------------------+
+
+NetChatEntry::NetChatEntry(const NetUser* u, const char* s)
+ : id(chat_id_key++), msg(s)
+{
+ if (u) {
+ user = u->Name();
+ color = u->GetColor();
+ }
+ else {
+ user = "unknown";
+ color = Color::Gray;
+ }
+
+ time = NetLayer::GetUTC();
+}
+
+NetChatEntry::NetChatEntry(int msg_id, const char* u, const char* s)
+ : id(msg_id), user(u), msg(s)
+{
+ color = Color::Gray;
+ time = NetLayer::GetUTC();
+
+ if (id >= chat_id_key)
+ chat_id_key = id + 1;
+}
+
+NetChatEntry::~NetChatEntry()
+{ }
+
diff --git a/Stars45/NetChat.h b/Stars45/NetChat.h
new file mode 100644
index 0000000..6a0936c
--- /dev/null
+++ b/Stars45/NetChat.h
@@ -0,0 +1,52 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetChat.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Single chat message and sender
+*/
+
+
+#ifndef NetChat_h
+#define NetChat_h
+
+#include "Types.h"
+#include "NetUser.h"
+
+// +-------------------------------------------------------------------+
+
+class NetChatEntry
+{
+public:
+ static const char* TYPENAME() { return "NetChatEntry"; }
+
+ NetChatEntry(const NetUser* user, const char* msg);
+ NetChatEntry(int id, const char* user, const char* msg);
+ ~NetChatEntry();
+
+ int operator == (const NetChatEntry& c) const { return id == c.id; }
+ int operator < (const NetChatEntry& c) const { return id < c.id; }
+
+ int GetID() const { return id; }
+ const Text& GetUser() const { return user; }
+ Color GetColor() const { return color; }
+ const Text& GetMessage() const { return msg; }
+ DWORD GetTime() const { return time; }
+
+private:
+ int id;
+ Text user;
+ Text msg;
+ Color color;
+ DWORD time;
+};
+
+// +-------------------------------------------------------------------+
+
+#endif NetChat_h \ No newline at end of file
diff --git a/Stars45/NetClientConfig.cpp b/Stars45/NetClientConfig.cpp
new file mode 100644
index 0000000..f4986c8
--- /dev/null
+++ b/Stars45/NetClientConfig.cpp
@@ -0,0 +1,332 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars
+ FILE: NetClientConfig.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include "MemDebug.h"
+
+#include "NetClientConfig.h"
+#include "NetLobbyClient.h"
+#include "NetBrokerClient.h"
+
+#include "NetLayer.h"
+#include "NetAddr.h"
+#include "NetHost.h"
+
+#include "Token.h"
+#include "Game.h"
+#include "DataLoader.h"
+#include "ParseUtil.h"
+#include "Resource.h"
+
+// +--------------------------------------------------------------------+
+
+NetClientConfig* NetClientConfig::instance = 0;
+
+// +--------------------------------------------------------------------+
+
+NetClientConfig::NetClientConfig()
+ : server_index(-1), host_request(false), conn(0)
+{
+ instance = this;
+ Load();
+}
+
+NetClientConfig::~NetClientConfig()
+{
+ Logout();
+
+ instance = 0;
+ servers.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetClientConfig::Initialize()
+{
+ if (!instance)
+ instance = new(__FILE__,__LINE__) NetClientConfig();
+}
+
+void
+NetClientConfig::Close()
+{
+ delete instance;
+ instance = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetClientConfig::AddServer(const char* name, const char* addr, WORD port, const char* pass, bool save)
+{
+ if (!addr || !*addr || port < 1024 || port > 48000)
+ return;
+
+ char buffer[1024];
+ if (name && *name)
+ strcpy(buffer, name);
+ else
+ sprintf(buffer, "%s:%d", addr, port);
+
+ NetServerInfo* server = new(__FILE__,__LINE__) NetServerInfo;
+ server->name = buffer;
+ server->hostname = addr;
+ server->addr = NetAddr(addr, port);
+ server->port = port;
+ server->password = pass;
+ server->save = save;
+
+ if (server->addr.IPAddr() == 0) {
+ Print("NetClientConfig::AddServer(%s, %s, %d) failed to resolve IP Addr\n",
+ name, addr, port);
+ }
+
+ servers.append(server);
+}
+
+void
+NetClientConfig::DelServer(int index)
+{
+ if (index >= 0 && index < servers.size()) {
+ delete servers.removeIndex(index);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+NetServerInfo*
+NetClientConfig::GetServerInfo(int n)
+{
+ if (n >= 0 && n < servers.size())
+ return servers.at(n);
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+NetServerInfo*
+NetClientConfig::GetSelectedServer()
+{
+ if (server_index >= 0 && server_index < servers.size())
+ return servers.at(server_index);
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetClientConfig::Download()
+{
+ Load();
+
+ List<NetServerInfo> list;
+ if (NetBrokerClient::GameList("Starshatter", list)) {
+ servers.append(list);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetClientConfig::Load()
+{
+ server_index = -1;
+
+ // read the config file:
+ BYTE* block = 0;
+ int blocklen = 0;
+
+ char filename[64];
+ strcpy(filename, "client.cfg");
+
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ ::fseek(f, 0, SEEK_END);
+ blocklen = ftell(f);
+ ::fseek(f, 0, SEEK_SET);
+
+ block = new(__FILE__,__LINE__) BYTE[blocklen+1];
+ block[blocklen] = 0;
+
+ ::fread(block, blocklen, 1, f);
+ ::fclose(f);
+ }
+
+ if (blocklen == 0)
+ return;
+
+ servers.destroy();
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'.\n", filename);
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "CLIENT_CONFIG") {
+ Print("WARNING: invalid '%s' file. Using defaults\n", filename);
+ return;
+ }
+ }
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "server") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: server struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+
+ Text name;
+ Text addr;
+ Text pass;
+ int port;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "name")
+ GetDefText(name, pdef, filename);
+ else if (pdef->name()->value() == "addr")
+ GetDefText(addr, pdef, filename);
+ else if (pdef->name()->value() == "pass")
+ GetDefText(pass, pdef, filename);
+ else if (pdef->name()->value() == "port")
+ GetDefNumber(port, pdef, filename);
+ }
+ }
+
+ AddServer(name, addr, (WORD) port, pass, true);
+ }
+ }
+ else {
+ Print("WARNING: unknown label '%s' in '%s'\n",
+ def->name()->value().data(), filename);
+ }
+ }
+ }
+ }
+ while (term);
+
+ delete [] block;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetClientConfig::Save()
+{
+ FILE* f = fopen("client.cfg", "w");
+ if (f) {
+ fprintf(f, "CLIENT_CONFIG\n\n");
+
+ ListIter<NetServerInfo> iter = servers;
+ while (++iter) {
+ NetServerInfo* server = iter.value();
+
+ if (server->save) {
+ int port = (int) server->port;
+ fprintf(f, "server: {\n");
+ fprintf(f, " name: \"%s\",\n", (const char*) server->name);
+ fprintf(f, " addr: \"%s\",\n", (const char*) server->hostname);
+ fprintf(f, " port: %d,\n", port);
+
+ if (server->password.length())
+ fprintf(f, " pass: \"%s\",\n", (const char*) server->password);
+
+ fprintf(f, "}\n\n");
+ }
+ }
+
+ fclose(f);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetClientConfig::CreateConnection()
+{
+ NetServerInfo* s = GetSelectedServer();
+
+ if (s) {
+ NetAddr addr = s->addr;
+
+ if (conn) {
+ if (conn->GetServerAddr().IPAddr() != addr.IPAddr() ||
+ conn->GetServerAddr().Port() != addr.Port()) {
+ conn->Logout();
+ DropConnection();
+ }
+ }
+
+ if (addr.IPAddr() && addr.Port() && !conn) {
+ conn = new(__FILE__,__LINE__) NetLobbyClient; // (addr);
+ }
+ }
+
+ else if (conn) {
+ conn->Logout();
+ DropConnection();
+ }
+}
+
+NetLobbyClient*
+NetClientConfig::GetConnection()
+{
+ return conn;
+}
+
+bool
+NetClientConfig::Login()
+{
+ bool result = false;
+
+ if (!conn)
+ CreateConnection();
+
+ if (conn)
+ result = conn->Login(host_request);
+
+ return result;
+}
+
+bool
+NetClientConfig::Logout()
+{
+ bool result = false;
+
+ if (conn) {
+ result = conn->Logout();
+ DropConnection();
+ }
+
+ return result;
+}
+
+void
+NetClientConfig::DropConnection()
+{
+ delete conn;
+ conn = 0;
+}
diff --git a/Stars45/NetClientConfig.h b/Stars45/NetClientConfig.h
new file mode 100644
index 0000000..ba1e340
--- /dev/null
+++ b/Stars45/NetClientConfig.h
@@ -0,0 +1,73 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars
+ FILE: NetClientConfig.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef NetClientConfig_h
+#define NetClientConfig_h
+
+#include "Types.h"
+#include "Game.h"
+#include "Text.h"
+#include "List.h"
+
+#include "NetAddr.h"
+
+// +--------------------------------------------------------------------+
+
+class NetLobbyClient;
+class NetServerInfo;
+
+// +--------------------------------------------------------------------+
+
+class NetClientConfig
+{
+public:
+ NetClientConfig();
+ ~NetClientConfig();
+
+ void AddServer(const char* name,
+ const char* addr,
+ WORD port,
+ const char* password,
+ bool save=false);
+ void DelServer(int index);
+
+ List<NetServerInfo>& GetServerList() { return servers; }
+ NetServerInfo* GetServerInfo(int n);
+ void Download();
+ void Load();
+ void Save();
+
+ void SetServerIndex(int n) { server_index = n; }
+ int GetServerIndex() const { return server_index; }
+ void SetHostRequest(bool n) { host_request = n; }
+ bool GetHostRequest() const { return host_request; }
+ NetServerInfo* GetSelectedServer();
+
+ void CreateConnection();
+ NetLobbyClient* GetConnection();
+ bool Login();
+ bool Logout();
+ void DropConnection();
+
+ static void Initialize();
+ static void Close();
+ static NetClientConfig* GetInstance() { return instance; }
+
+private:
+ List<NetServerInfo> servers;
+ int server_index;
+ bool host_request;
+
+ NetLobbyClient* conn;
+
+ static NetClientConfig* instance;
+};
+
+#endif NetClientConfig_h
diff --git a/Stars45/NetClientDlg.cpp b/Stars45/NetClientDlg.cpp
new file mode 100644
index 0000000..401e412
--- /dev/null
+++ b/Stars45/NetClientDlg.cpp
@@ -0,0 +1,431 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetClientDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "NetClientDlg.h"
+#include "NetClientConfig.h"
+#include "NetLobbyClient.h"
+#include "MenuScreen.h"
+#include "Starshatter.h"
+#include "Ship.h"
+#include "HUDView.h"
+
+#include "NetAddr.h"
+
+#include "DataLoader.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "MachineInfo.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(NetClientDlg, OnSelect);
+DEF_MAP_CLIENT(NetClientDlg, OnAdd);
+DEF_MAP_CLIENT(NetClientDlg, OnDel);
+DEF_MAP_CLIENT(NetClientDlg, OnServer);
+DEF_MAP_CLIENT(NetClientDlg, OnHost);
+DEF_MAP_CLIENT(NetClientDlg, OnJoin);
+DEF_MAP_CLIENT(NetClientDlg, OnCancel);
+
+// +--------------------------------------------------------------------+
+
+NetClientDlg::NetClientDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ server_index(-1), ping_index(0), hnet(0)
+{
+ config = NetClientConfig::GetInstance();
+ Init(def);
+}
+
+NetClientDlg::~NetClientDlg()
+{
+ StopNetProc();
+}
+
+void
+NetClientDlg::StopNetProc()
+{
+ if (hnet != 0) {
+ WaitForSingleObject(hnet, 500);
+ CloseHandle(hnet);
+ hnet = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetClientDlg::RegisterControls()
+{
+ btn_add = (Button*) FindControl(101);
+ btn_del = (Button*) FindControl(102);
+ lst_servers = (ListBox*) FindControl(200);
+ lbl_info = FindControl(210);
+ btn_server = (Button*) FindControl(301);
+ btn_host = (Button*) FindControl(302);
+ btn_join = (Button*) FindControl(303);
+ btn_cancel = (Button*) FindControl(2);
+
+ REGISTER_CLIENT(EID_CLICK, btn_add, NetClientDlg, OnAdd);
+ REGISTER_CLIENT(EID_CLICK, btn_del, NetClientDlg, OnDel);
+ REGISTER_CLIENT(EID_SELECT, lst_servers, NetClientDlg, OnSelect);
+ REGISTER_CLIENT(EID_CLICK, btn_server, NetClientDlg, OnServer);
+ REGISTER_CLIENT(EID_CLICK, btn_host, NetClientDlg, OnHost);
+ REGISTER_CLIENT(EID_CLICK, btn_join, NetClientDlg, OnJoin);
+ REGISTER_CLIENT(EID_CLICK, btn_cancel, NetClientDlg, OnCancel);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetClientDlg::Show()
+{
+ int selected_index = -1;
+
+ if (!IsShown()) {
+ FormWindow::Show();
+
+ // try to retrieve list of active servers from web broker
+ if (config && lst_servers) {
+ selected_index = config->GetServerIndex();
+ lst_servers->ClearItems();
+ config->Download();
+ }
+ }
+
+ if (config && lst_servers) {
+ if (lst_servers->NumItems() != config->GetServerList().size())
+ ShowServers();
+ else
+ UpdateServers();
+
+ if (selected_index >= 0 && selected_index < lst_servers->NumItems()) {
+ config->SetServerIndex(selected_index);
+ lst_servers->SetSelected(selected_index);
+ OnSelect(0);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetClientDlg::ExecFrame()
+{
+ if (!config || !lst_servers) return;
+
+ if (lst_servers->NumItems() != config->GetServerList().size())
+ ShowServers();
+ else
+ UpdateServers();
+
+ NetServerInfo* info = config->GetServerInfo(server_index);
+
+ bool del_enabled = info != 0;
+ bool join_enabled = info != 0 && info->status > NetServerInfo::OFFLINE;
+ bool host_enabled = join_enabled && info->hosted == 0;
+
+ if (btn_host)
+ btn_host->SetEnabled(host_enabled);
+
+ if (btn_join)
+ btn_join->SetEnabled(join_enabled);
+
+ if (btn_del)
+ btn_del->SetEnabled(del_enabled);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetClientDlg::ShowServers()
+{
+ if (!config || !lst_servers) return;
+
+ lst_servers->ClearItems();
+ lst_servers->SetSelectedStyle(ListBox::LIST_ITEM_STYLE_FILLED_BOX);
+ lst_servers->SetLeading(2);
+
+ int i = 0;
+ ListIter<NetServerInfo> iter = config->GetServerList();
+ while (++iter) {
+ NetServerInfo* info = iter.value();
+
+ lst_servers->AddItemWithData(info->name, (DWORD) i);
+ lst_servers->SetItemText(i, 1, info->type);
+ lst_servers->SetItemText(i, 2, info->password);
+ lst_servers->SetItemText(i, 3, Game::GetText("NetClientDlg.offline"));
+ lst_servers->SetItemText(i, 4, "0");
+ lst_servers->SetItemText(i, 5, Game::GetText("NetClientDlg.not-avail"));
+
+ i++;
+ }
+
+ ping_index = 0;
+ server_index = -1;
+
+ if (btn_host) btn_host->SetEnabled(false);
+ if (btn_join) btn_join->SetEnabled(false);
+ if (btn_del) btn_del->SetEnabled(false);
+}
+
+void
+NetClientDlg::UpdateServers()
+{
+ if (!config || !lst_servers || lst_servers->NumItems() < 1) return;
+
+ if (!PingComplete())
+ return;
+
+ PingServer(ping_index);
+
+ for (int i = 0; i < lst_servers->NumItems(); i++) {
+ int n = lst_servers->GetItemData(i);
+
+ NetServerInfo* info = config->GetServerList().at(n);
+ lst_servers->SetItemText(i, 0, info->name);
+
+ Text status = Game::GetText("NetClientDlg.offline");
+
+ if (info->ping_time > 0 && info->ping_time < 10000) {
+ char buffer[32];
+ sprintf(buffer, "%d ms", info->ping_time);
+ lst_servers->SetItemText(i, 5, buffer);
+
+ switch (info->status) {
+ default:
+ case NetServerInfo::OFFLINE: status = Game::GetText("NetClientDlg.offline"); break;
+ case NetServerInfo::LOBBY: status = Game::GetText("NetClientDlg.lobby"); break;
+ case NetServerInfo::BRIEFING: status = Game::GetText("NetClientDlg.briefing"); break;
+ case NetServerInfo::ACTIVE: status = Game::GetText("NetClientDlg.active"); break;
+ case NetServerInfo::DEBRIEFING: status = Game::GetText("NetClientDlg.debriefing"); break;
+ case NetServerInfo::PERSISTENT: status = Game::GetText("NetClientDlg.persistent"); break;
+ }
+ }
+ else {
+ lst_servers->SetItemText(i, 5, Game::GetText("NetClientDlg.not-avail"));
+ }
+
+ lst_servers->SetItemText(i, 3, status);
+
+ char num_users[16];
+ sprintf(num_users, "%d", info->nplayers);
+ lst_servers->SetItemText(i, 4, num_users);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD WINAPI NetClientPingProc(LPVOID link);
+
+void
+NetClientDlg::PingServer(int n)
+{
+ if (hnet == 0) {
+ NetClientConfig* config = NetClientConfig::GetInstance();
+ if (!config) return;
+
+ NetServerInfo* info = config->GetServerInfo(n);
+ if (!info) return;
+
+ // copy info from server list
+ ping_info = *info;
+
+ DWORD thread_id = 0;
+ hnet = CreateThread(0, 4096, NetClientPingProc, (LPVOID) &ping_info, 0, &thread_id);
+
+ if (hnet == 0) {
+ static int report = 10;
+ if (report > 0) {
+ ::Print("WARNING: NetClientDlg() failed to create PING thread for server '%s' (err=%08x)\n", info->name.data(), GetLastError());
+ report--;
+
+ if (report == 0)
+ ::Print(" Further warnings of this type will be supressed.\n");
+ }
+ }
+ }
+}
+
+bool
+NetClientDlg::PingComplete()
+{
+ if (hnet != 0) {
+ DWORD result = 0;
+ GetExitCodeThread(hnet, &result);
+
+ if (result != STILL_ACTIVE) {
+ CloseHandle(hnet);
+ hnet = 0;
+
+ NetClientConfig* config = NetClientConfig::GetInstance();
+ if (config) {
+ NetServerInfo* info = config->GetServerInfo(ping_index);
+ if (info) {
+ // copy result back into server list
+ info->machine_info = ping_info.machine_info;
+ info->gameport = ping_info.gameport;
+ info->status = ping_info.status;
+ info->nplayers = ping_info.nplayers;
+ info->hosted = ping_info.hosted;
+ info->ping_time = ping_info.ping_time;
+ }
+ }
+
+ if (lst_servers && ping_index >= lst_servers->NumItems()-1)
+ ping_index = 0;
+ else
+ ping_index++;
+ }
+ }
+
+ return !hnet;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD WINAPI NetClientPingProc(LPVOID link)
+{
+ NetServerInfo* info = (NetServerInfo*) link;
+ if (!info) {
+ Print("NetClientPingProc() no link\n");
+ Sleep(200);
+ return 1;
+ }
+
+ NetAddr addr = info->addr;
+ if (!addr.IPAddr()) {
+ Sleep(200);
+ return 1;
+ }
+
+ NetLobbyClient conn(addr);
+
+ if (conn.Ping()) {
+ info->machine_info = conn.GetMachineInfo();
+ info->gameport = conn.GetGamePort();
+ info->status = conn.GetStatus();
+ info->nplayers = conn.NumUsers();
+ info->hosted = conn.HasHost();
+ info->ping_time = conn.GetLag();
+ }
+
+ else {
+ info->machine_info = Text();
+ info->nplayers = 0;
+ info->hosted = 0;
+ info->status = NetServerInfo::OFFLINE;
+ info->ping_time = 0;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetClientDlg::OnAdd(AWEvent* event)
+{
+ manager->ShowNetAddrDlg();
+}
+
+void
+NetClientDlg::OnDel(AWEvent* event)
+{
+ if (config && server_index >= 0)
+ config->DelServer(server_index);
+}
+
+void
+NetClientDlg::OnSelect(AWEvent* event)
+{
+ if (lst_servers) {
+ server_index = lst_servers->GetSelection();
+ NetServerInfo* info = config->GetServerInfo(server_index);
+
+ if (lbl_info) {
+ if (info)
+ lbl_info->SetText(info->machine_info);
+ else
+ lbl_info->SetText("");
+ }
+
+ if (btn_host) {
+ btn_host->SetEnabled(info && info->status > NetServerInfo::OFFLINE && info->hosted == 0);
+ }
+
+ if (btn_join) {
+ btn_join->SetEnabled(info && info->status > NetServerInfo::OFFLINE);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetClientDlg::OnServer(AWEvent* event)
+{
+ manager->ShowNetServerDlg();
+}
+
+void
+NetClientDlg::OnHost(AWEvent* event)
+{
+ if (config) {
+ config->SetServerIndex(server_index);
+ config->SetHostRequest(true);
+ config->Save();
+
+ NetServerInfo* info = config->GetServerInfo(server_index);
+ if (info && info->password == "Yes") {
+ manager->ShowNetPassDlg();
+ }
+ else {
+ manager->ShowNetLobbyDlg();
+ }
+ }
+}
+
+void
+NetClientDlg::OnJoin(AWEvent* event)
+{
+ if (config) {
+ config->SetServerIndex(server_index);
+ config->SetHostRequest(false);
+ config->Save();
+
+ NetServerInfo* info = config->GetServerInfo(server_index);
+ if (info && info->password == "Yes") {
+ manager->ShowNetPassDlg();
+ }
+ else {
+ manager->ShowNetLobbyDlg();
+ }
+ }
+}
+
+void
+NetClientDlg::OnCancel(AWEvent* event)
+{
+ if (config) {
+ config->Save();
+ config->SetServerIndex(-1);
+ }
+
+ manager->ShowMenuDlg();
+}
diff --git a/Stars45/NetClientDlg.h b/Stars45/NetClientDlg.h
new file mode 100644
index 0000000..9dfdb60
--- /dev/null
+++ b/Stars45/NetClientDlg.h
@@ -0,0 +1,80 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetClientDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#ifndef NetClientDlg_h
+#define NetClientDlg_h
+
+#include "Types.h"
+#include "NetClientConfig.h"
+#include "NetLobby.h"
+
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+
+// +--------------------------------------------------------------------+
+
+class NetClientDlg : public FormWindow
+{
+public:
+ NetClientDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~NetClientDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnSelect(AWEvent* event);
+ virtual void OnAdd(AWEvent* event);
+ virtual void OnDel(AWEvent* event);
+ virtual void OnServer(AWEvent* event);
+ virtual void OnHost(AWEvent* event);
+ virtual void OnJoin(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+
+ virtual void ShowServers();
+ virtual void UpdateServers();
+ virtual void PingServer(int n);
+ virtual bool PingComplete();
+ virtual void StopNetProc();
+
+protected:
+ MenuScreen* manager;
+ NetClientConfig* config;
+
+ Button* btn_add;
+ Button* btn_del;
+ ListBox* lst_servers;
+ ActiveWindow* lbl_info;
+ int server_index;
+ int ping_index;
+ HANDLE hnet;
+ NetServerInfo ping_info;
+
+ Button* btn_server;
+ Button* btn_host;
+ Button* btn_join;
+ Button* btn_cancel;
+};
+
+#endif NetClientDlg_h
+
diff --git a/Stars45/NetData.cpp b/Stars45/NetData.cpp
new file mode 100644
index 0000000..93d0cba
--- /dev/null
+++ b/Stars45/NetData.cpp
@@ -0,0 +1,1453 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetData.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Payload structures for multiplayer network packets
+*/
+
+#include "MemDebug.h"
+#include "NetData.h"
+#include "NetLink.h"
+#include "NetMsg.h"
+#include "RadioMessage.h"
+#include "Ship.h"
+#include "Shot.h"
+#include "Sim.h"
+#include "Instruction.h"
+#include "Weapon.h"
+#include "Element.h"
+#include "System.h"
+#include "Power.h"
+#include "Shield.h"
+
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+// DATA SIZE OFFSET
+// -------- ----------------- ---------
+// type: 1 0
+// size: 1 1
+// objid: 2 2
+// loc: 9 (3 x 24 bits) 4
+// vel: 6 (3 x 16 bits) 13
+// euler: 4 (3 x 10 bits) 19
+// status: 1 23
+
+const int LOCATION_OFFSET = 8000000;
+const int VELOCITY_OFFSET = 32000;
+const double EULER_SCALE = 2*PI / (1<<10);
+
+BYTE*
+NetObjLoc::Pack()
+{
+ data[ 0] = TYPE;
+ data[ 1] = SIZE;
+
+ // obj id
+ data[ 2] = (BYTE) ((objid & 0xff00) >> 8);
+ data[ 3] = (BYTE) ((objid & 0x00ff) );
+
+ // location
+ DWORD x = (DWORD) (((int) location.x + LOCATION_OFFSET) & 0x00ffffff);
+ DWORD y = (DWORD) (((int) location.y + LOCATION_OFFSET) & 0x00ffffff);
+ DWORD z = (DWORD) (((int) location.z + LOCATION_OFFSET) & 0x00ffffff);
+
+ data[ 4] = (BYTE) ((x & 0x00ff0000) >> 16);
+ data[ 5] = (BYTE) ((x & 0x0000ff00) >> 8);
+ data[ 6] = (BYTE) ((x & 0x000000ff) );
+
+ data[ 7] = (BYTE) ((y & 0x00ff0000) >> 16);
+ data[ 8] = (BYTE) ((y & 0x0000ff00) >> 8);
+ data[ 9] = (BYTE) ((y & 0x000000ff) );
+
+ data[10] = (BYTE) ((z & 0x00ff0000) >> 16);
+ data[11] = (BYTE) ((z & 0x0000ff00) >> 8);
+ data[12] = (BYTE) ((z & 0x000000ff) );
+
+ // velocity
+ WORD vx = (WORD) (((int) velocity.x + VELOCITY_OFFSET) & 0x0000ffff);
+ WORD vy = (WORD) (((int) velocity.y + VELOCITY_OFFSET) & 0x0000ffff);
+ WORD vz = (WORD) (((int) velocity.z + VELOCITY_OFFSET) & 0x0000ffff);
+
+ data[13] = (BYTE) ((vx & 0xff00) >> 8);
+ data[14] = (BYTE) ((vx & 0x00ff));
+
+ data[15] = (BYTE) ((vy & 0xff00) >> 8);
+ data[16] = (BYTE) ((vy & 0x00ff));
+
+ data[17] = (BYTE) ((vz & 0xff00) >> 8);
+ data[18] = (BYTE) ((vz & 0x00ff));
+
+ // orientation
+ if (_finite(euler.x)) {
+ while (euler.x < 0) euler.x += 2*PI;
+ while (euler.x > 2*PI) euler.x -= 2*PI;
+ }
+ else {
+ euler.x = 0;
+ }
+
+ if (_finite(euler.y)) {
+ while (euler.y < 0) euler.y += 2*PI;
+ while (euler.y > 2*PI) euler.y -= 2*PI;
+ }
+ else {
+ euler.y = 0;
+ }
+
+ if (_finite(euler.z)) {
+ while (euler.z < 0) euler.z += 2*PI;
+ while (euler.z > 2*PI) euler.z -= 2*PI;
+ }
+ else {
+ euler.z = 0;
+ }
+
+ WORD ox = (WORD) (((int) (euler.x / EULER_SCALE)) & 0x000003ff);
+ WORD oy = (WORD) (((int) (euler.y / EULER_SCALE)) & 0x000003ff);
+ WORD oz = (WORD) (((int) (euler.z / EULER_SCALE)) & 0x000003ff);
+
+ DWORD o = (ox << 20) | (oy << 10) | (oz);
+
+ data[19] = (BYTE) ((o & 0xff000000) >> 24);
+ data[20] = (BYTE) ((o & 0x00ff0000) >> 16);
+ data[21] = (BYTE) ((o & 0x0000ff00) >> 8);
+ data[22] = (BYTE) ((o & 0x000000ff) );
+
+ // status bits
+ data[23] = throttle << 7 |
+ augmenter << 6 |
+ gear << 5 |
+ (shield >> 2) & 0x1f;
+
+ return data;
+}
+
+bool
+NetObjLoc::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE && p[1] == SIZE) {
+ CopyMemory(data, p, SIZE);
+
+ objid = (data[ 2] << 8) |
+ (data[ 3] );
+
+ int x = (data[ 4] << 16) |
+ (data[ 5] << 8) |
+ (data[ 6] );
+
+ int y = (data[ 7] << 16) |
+ (data[ 8] << 8) |
+ (data[ 9] );
+
+ int z = (data[10] << 16) |
+ (data[11] << 8) |
+ (data[12] );
+
+ int vx = (data[13] << 8) |
+ (data[14] );
+
+ int vy = (data[15] << 8) |
+ (data[16] );
+
+ int vz = (data[17] << 8) |
+ (data[18] );
+
+ DWORD o = (data[19] << 24) |
+ (data[20] << 16) |
+ (data[21] << 8) |
+ (data[22] );
+
+ WORD ox = (WORD) ((o >> 20) & 0x03ff);
+ WORD oy = (WORD) ((o >> 10) & 0x03ff);
+ WORD oz = (WORD) ((o ) & 0x03ff);
+
+ throttle = data[23] & 0x80 ? true : false;
+ augmenter = data[23] & 0x40 ? true : false;
+ gear = data[23] & 0x20 ? true : false;
+ shield = (data[23] & 0x1f) << 2;
+
+ location = Point(x -LOCATION_OFFSET, y -LOCATION_OFFSET, z -LOCATION_OFFSET);
+ velocity = Point(vx-VELOCITY_OFFSET, vy-VELOCITY_OFFSET, vz-VELOCITY_OFFSET);
+ euler = Point(ox*EULER_SCALE, oy*EULER_SCALE, oz*EULER_SCALE);
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+NetJoinRequest::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[0] = TYPE;
+ data[1] = SIZE;
+
+ for (int i = 0; i < name.length() && i < 16; i++)
+ data[2+i] = name[i];
+
+ for (i = 0; i < pass.length() && i < 16; i++)
+ data[18+i] = pass[i];
+
+ for (i = 0; i < elem.length() && i < 31; i++)
+ data[34+i] = elem[i];
+
+ data[65] = (BYTE) index;
+
+ for (i = 0; i < serno.length() && i < 60; i++)
+ data[66+i] = serno[i];
+
+ return data;
+}
+
+bool
+NetJoinRequest::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE && p[1] == SIZE) {
+ CopyMemory(data, p, SIZE);
+
+ char buf[64];
+
+ CopyMemory(buf, data+2, 16);
+ buf[16] = 0;
+ name = buf;
+
+ CopyMemory(buf, data+18, 16);
+ buf[16] = 0;
+ pass = buf;
+
+ CopyMemory(buf, data+34, 31);
+ buf[31] = 0;
+ elem = buf;
+
+ index = data[65];
+
+ CopyMemory(buf, data+66, 60);
+ buf[61] = 0;
+ serno = buf;
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+NetJoinAnnounce::NetJoinAnnounce()
+ : index(0), integrity(0), respawns(0), decoys(0), probes(0), fuel(0),
+ shield(0), nid(0)
+{
+ ZeroMemory(ammo, sizeof(ammo));
+}
+
+void
+NetJoinAnnounce::SetAmmo(const int* a)
+{
+ if (a) {
+ CopyMemory(ammo, a, sizeof(ammo));
+ }
+}
+
+void
+NetJoinAnnounce::SetShip(Ship* s)
+{
+ SetName(s->Name());
+ SetObjID(s->GetObjID());
+
+ if (s->GetElement()) {
+ SetElement(s->GetElement()->Name());
+ SetIndex(s->GetElementIndex());
+ }
+
+ if (s->GetRegion())
+ SetRegion(s->GetRegion()->Name());
+
+ SetLocation(s->Location());
+ SetVelocity(s->Velocity());
+ SetIntegrity(s->Integrity());
+ SetRespawns(s->RespawnCount());
+
+ if (s->GetDecoy())
+ SetDecoys(s->GetDecoy()->Ammo());
+
+ if (s->GetProbeLauncher())
+ SetProbes(s->GetProbeLauncher()->Ammo());
+
+ if (s->Reactors().size())
+ SetFuel(s->Reactors()[0]->Charge());
+
+ Shield* shield = s->GetShield();
+ if (shield)
+ SetShield((int) shield->GetPowerLevel());
+}
+
+BYTE*
+NetJoinAnnounce::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[ 0] = TYPE;
+ data[ 1] = SIZE;
+ data[ 2] = (BYTE) ((objid & 0xff00) >> 8);
+ data[ 3] = (BYTE) ((objid & 0x00ff) );
+
+ float* f = (float*) (data + 4);
+ *f++ = (float) loc.x; // bytes 4 - 7
+ *f++ = (float) loc.y; // bytes 8 - 11
+ *f++ = (float) loc.z; // bytes 12 - 15
+ *f++ = (float) integrity; // bytes 16 - 19
+
+ for (int i = 0; i < name.length() && i < 16; i++)
+ data[20+i] = name[i];
+
+ for (i = 0; i < elem.length() && i < 32; i++)
+ data[36+i] = elem[i];
+
+ for (i = 0; i < region.length() && i < 32; i++)
+ data[68+i] = region[i];
+
+ int* p = (int*) (data + 100);
+ *p++ = respawns; // bytes 100 - 103
+ *p++ = decoys; // bytes 104 - 107
+ *p++ = probes; // bytes 108 - 111
+
+ data[112]= (BYTE) fuel; // byte 112
+ data[113]= (BYTE) shield; // byte 113
+
+ BYTE* a = data + 116;
+ for (i = 0; i < 16; i++) { // bytes 116 - 179
+ if (ammo[i] >= 0) {
+ *a++ = ammo[i];
+ }
+ else {
+ *a++ = 255;
+ }
+ }
+
+ data[180] = (BYTE) index;
+
+ f = (float*) (data + 184);
+ *f++ = (float) velocity.x;
+ *f++ = (float) velocity.y;
+ *f++ = (float) velocity.z;
+
+ return data;
+}
+
+bool
+NetJoinAnnounce::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE && p[1] == SIZE) {
+ CopyMemory(data, p, SIZE);
+
+ objid = (data[2] << 8) | data[3];
+
+ float* f = (float*) (data + 4);
+ loc.x = *f++;
+ loc.y = *f++;
+ loc.z = *f++;
+ integrity = *f++;
+
+ char buf[64];
+ CopyMemory(buf, data+20, 16);
+ buf[16] = 0;
+ name = buf;
+
+ CopyMemory(buf, data+36, 32);
+ buf[16] = 0;
+ elem = buf;
+
+ CopyMemory(buf, data+68, 32);
+ buf[16] = 0;
+ region = buf;
+
+ int* p = (int*) (data + 100);
+ respawns = *p++;
+ decoys = *p++;
+ probes = *p++;
+
+ fuel = data[112];
+ shield = data[113];
+
+ CopyMemory(ammo, data+116, 16);
+
+ index = data[180];
+
+ f = (float*) (data + 184);
+ velocity.x = *f++;
+ velocity.y = *f++;
+ velocity.z = *f++;
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+NetQuitAnnounce::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[0] = TYPE;
+ data[1] = SIZE;
+
+ data[2] = (BYTE) ((objid & 0xff00) >> 8);
+ data[3] = (BYTE) ((objid & 0x00ff) );
+ data[4] = (BYTE) (disconnected);
+
+ return data;
+}
+
+bool
+NetQuitAnnounce::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE && p[1] == SIZE) {
+ CopyMemory(data, p, SIZE);
+
+ objid = (data[2] << 8) | data[3];
+ disconnected = data[4] ? true : false;
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+NetDisconnect::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[0] = TYPE;
+ data[1] = SIZE;
+
+ return data;
+}
+
+bool
+NetDisconnect::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE && p[1] == SIZE) {
+ CopyMemory(data, p, SIZE);
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+NetObjDamage::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[0] = TYPE;
+ data[1] = SIZE;
+
+ data[2] = (BYTE) ((objid & 0xff00) >> 8);
+ data[3] = (BYTE) ((objid & 0x00ff) );
+ data[4] = (BYTE) ((shotid & 0xff00) >> 8);
+ data[5] = (BYTE) ((shotid & 0x00ff) );
+
+ float* p = (float*) (data + 6);
+ *p = damage;
+
+ return data;
+}
+
+bool
+NetObjDamage::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE && p[1] == SIZE) {
+ CopyMemory(data, p, SIZE);
+
+ objid = (data[2] << 8) | data[3];
+ shotid = (data[4] << 8) | data[5];
+ damage = *(float*) (data + 6);
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+NetSysDamage::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[0] = TYPE;
+ data[1] = SIZE;
+
+ data[2] = (BYTE) ((objid & 0xff00) >> 8);
+ data[3] = (BYTE) ((objid & 0x00ff) );
+
+ float* p = (float*) (data + 4);
+ *p = (float) damage;
+
+ data[8] = (BYTE) (sysix+1);
+ data[9] = dmgtype;
+
+ return data;
+}
+
+bool
+NetSysDamage::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE && p[1] == SIZE) {
+ CopyMemory(data, p, SIZE);
+
+ objid = (data[2] << 8) | data[3];
+ damage = *(float*) (data + 4);
+ sysix = data[8];
+ dmgtype = data[9];
+
+ sysix--;
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+NetSysStatus::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[0] = TYPE;
+ data[1] = SIZE;
+
+ data[2] = (BYTE) ((objid & 0xff00) >> 8);
+ data[3] = (BYTE) ((objid & 0x00ff) );
+ data[4] = (BYTE) (sysix+1);
+ data[5] = (BYTE) (status);
+ data[6] = (BYTE) (power);
+ data[7] = (BYTE) (reactor);
+
+ float* f = (float*) (data + 8);
+ *f = (float) avail;
+
+ return data;
+}
+
+bool
+NetSysStatus::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE && p[1] == SIZE) {
+ CopyMemory(data, p, SIZE);
+
+ objid = (data[2] << 8) | data[3];
+ sysix = data[4];
+ status = data[5];
+ power = data[6];
+ reactor = data[7];
+
+ float* f = (float*) (data + 8);
+ avail = *f;
+
+ sysix--;
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+NetObjKill::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[0] = TYPE;
+ data[1] = SIZE;
+
+ data[2] = (BYTE) ((objid & 0xff00) >> 8);
+ data[3] = (BYTE) ((objid & 0x00ff) );
+ data[4] = (BYTE) ((kill_id & 0xff00) >> 8);
+ data[5] = (BYTE) ((kill_id & 0x00ff) );
+ data[6] = (BYTE) killtype;
+ data[7] = (BYTE) respawn;
+
+ float* f = (float*) (data + 8);
+ *f++ = (float) loc.x;
+ *f++ = (float) loc.y;
+ *f++ = (float) loc.z;
+
+ data[20] = (BYTE) deck;
+
+ return data;
+}
+
+bool
+NetObjKill::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE && p[1] == SIZE) {
+ CopyMemory(data, p, SIZE);
+
+ objid = (data[2] << 8) | data[3];
+ kill_id = (data[4] << 8) | data[5];
+ killtype = data[6];
+ respawn = data[7] ? true : false;
+
+ float* f = (float*) (data + 8);
+ loc.x = *f++;
+ loc.y = *f++;
+ loc.z = *f++;
+
+ deck = data[20];
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+NetObjHyper::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[0] = TYPE;
+ data[1] = SIZE;
+
+ data[2] = (BYTE) ((objid & 0xff00) >> 8);
+ data[3] = (BYTE) ((objid & 0x00ff) );
+ data[4] = (BYTE) ((fc_src & 0xff00) >> 8);
+ data[5] = (BYTE) ((fc_src & 0x00ff) );
+ data[6] = (BYTE) ((fc_dst & 0xff00) >> 8);
+ data[7] = (BYTE) ((fc_dst & 0x00ff) );
+ data[8] = (BYTE) transtype;
+
+ float* f = (float*) (data + 12);
+ *f++ = (float) location.x; // bytes 12 - 15
+ *f++ = (float) location.y; // bytes 16 - 19
+ *f++ = (float) location.z; // bytes 20 - 23
+
+ char* p = (char*) (data + 24);
+ strncpy(p, region.data(), 31);
+
+ return data;
+}
+
+bool
+NetObjHyper::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE && p[1] == SIZE) {
+ CopyMemory(data, p, SIZE);
+
+ objid = (data[2] << 8) | data[3];
+ fc_src = (data[4] << 8) | data[5];
+ fc_dst = (data[6] << 8) | data[7];
+ transtype = data[8];
+
+ float* f = (float*) (data + 12);
+ location.x = *f++;
+ location.y = *f++;
+ location.z = *f++;
+
+ region = (char*) (data + 24);
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+NetObjTarget::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[0] = TYPE;
+ data[1] = SIZE;
+
+ data[2] = (BYTE) ((objid & 0xff00) >> 8);
+ data[3] = (BYTE) ((objid & 0x00ff) );
+ data[4] = (BYTE) ((tgtid & 0xff00) >> 8);
+ data[5] = (BYTE) ((tgtid & 0x00ff) );
+ data[6] = (BYTE) (sysix+1);
+
+ return data;
+}
+
+bool
+NetObjTarget::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE && p[1] == SIZE) {
+ CopyMemory(data, p, SIZE);
+
+ objid = (data[2] << 8) | data[3];
+ tgtid = (data[4] << 8) | data[5];
+ sysix = data[6];
+
+ sysix--;
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+NetObjEmcon::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[0] = TYPE;
+ data[1] = SIZE;
+
+ data[2] = (BYTE) ((objid & 0xff00) >> 8);
+ data[3] = (BYTE) ((objid & 0x00ff) );
+ data[4] = (BYTE) (emcon);
+
+ return data;
+}
+
+bool
+NetObjEmcon::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE && p[1] == SIZE) {
+ CopyMemory(data, p, SIZE);
+
+ objid = (data[2] << 8) | data[3];
+ emcon = data[4];
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+NetWepTrigger::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[0] = TYPE;
+ data[1] = SIZE;
+
+ data[2] = (BYTE) ((objid & 0xff00) >> 8);
+ data[3] = (BYTE) ((objid & 0x00ff) );
+ data[4] = (BYTE) ((tgtid & 0xff00) >> 8);
+ data[5] = (BYTE) ((tgtid & 0x00ff) );
+ data[6] = (BYTE) (sysix+1);
+ data[7] = (BYTE) index;
+ data[8] = (BYTE) count;
+ data[9] = ((BYTE) decoy << 1) |
+ ((BYTE) probe );
+
+ return data;
+}
+
+bool
+NetWepTrigger::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE && p[1] == SIZE) {
+ CopyMemory(data, p, SIZE);
+
+ objid = (data[2] << 8) | data[3];
+ tgtid = (data[4] << 8) | data[5];
+ sysix = data[6];
+ index = data[7];
+ count = data[8];
+ decoy = (data[9] & 0x02) ? true : false;
+ probe = (data[9] & 0x01) ? true : false;
+
+ sysix--;
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+NetWepRelease::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[0] = TYPE;
+ data[1] = SIZE;
+
+ data[2] = (BYTE) ((objid & 0xff00) >> 8);
+ data[3] = (BYTE) ((objid & 0x00ff) );
+ data[4] = (BYTE) ((tgtid & 0xff00) >> 8);
+ data[5] = (BYTE) ((tgtid & 0x00ff) );
+ data[6] = (BYTE) ((wepid & 0xff00) >> 8);
+ data[7] = (BYTE) ((wepid & 0x00ff) );
+ data[8] = (BYTE) (sysix+1);
+ data[9] = (BYTE) index;
+ data[10] = ((BYTE) decoy << 1) |
+ ((BYTE) probe );
+
+ return data;
+}
+
+bool
+NetWepRelease::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE && p[1] == SIZE) {
+ CopyMemory(data, p, SIZE);
+
+ objid = (data[2] << 8) | data[3];
+ tgtid = (data[4] << 8) | data[5];
+ wepid = (data[6] << 8) | data[7];
+ sysix = data[8];
+ index = data[9];
+ decoy = (data[10] & 0x02) ? true : false;
+ probe = (data[10] & 0x01) ? true : false;
+
+ sysix--;
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+NetWepDestroy::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[0] = TYPE;
+ data[1] = SIZE;
+ data[2] = (BYTE) ((objid & 0xff00) >> 8);
+ data[3] = (BYTE) ((objid & 0x00ff) );
+
+ return data;
+}
+
+bool
+NetWepDestroy::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE && p[1] == SIZE) {
+ CopyMemory(data, p, SIZE);
+ objid = (data[2] << 8) | data[3];
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+NetCommMsg::~NetCommMsg()
+{
+ delete radio_message;
+}
+
+void
+NetCommMsg::SetRadioMessage(RadioMessage* m)
+{
+ radio_message = new(__FILE__,__LINE__) RadioMessage(*m);
+}
+
+BYTE*
+NetCommMsg::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ if (radio_message) {
+ length = 55 + radio_message->Info().length();
+
+ if (length > SIZE)
+ length = SIZE;
+
+ data[0] = TYPE;
+ data[1] = (BYTE) length;
+
+ if (radio_message->Sender()) {
+ objid = radio_message->Sender()->GetObjID();
+ data[2] = (BYTE) ((objid & 0xff00) >> 8);
+ data[3] = (BYTE) ((objid & 0x00ff) );
+ }
+
+ if (radio_message->DestinationShip()) {
+ DWORD dstid = radio_message->DestinationShip()->GetObjID();
+ data[4] = (BYTE) ((dstid & 0xff00) >> 8);
+ data[5] = (BYTE) ((dstid & 0x00ff) );
+ }
+
+ data[6] = (BYTE) radio_message->Action();
+ data[7] = (BYTE) radio_message->Channel();
+
+ if (radio_message->TargetList().size() > 0) {
+ SimObject* tgt = radio_message->TargetList().at(0);
+ DWORD tgtid = tgt->GetObjID();
+ data[8] = (BYTE) ((tgtid & 0xff00) >> 8);
+ data[9] = (BYTE) ((tgtid & 0x00ff) );
+ }
+
+ float* f = (float*) (data + 10);
+ *f++ = (float) radio_message->Location().x; // bytes 10 - 13
+ *f++ = (float) radio_message->Location().y; // bytes 14 - 17
+ *f++ = (float) radio_message->Location().z; // bytes 18 - 21
+
+ char* p = (char*) (data + 22);
+
+ Element* dst_elem = radio_message->DestinationElem();
+ if (dst_elem)
+ strncpy(p, dst_elem->Name().data(), 31);
+
+ p = (char*) (data + 55);
+ strncpy(p, radio_message->Info().data(), 128);
+
+ data[SIZE-1] = 0;
+ }
+
+ return data;
+}
+
+bool
+NetCommMsg::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE) {
+ length = p[1];
+ ZeroMemory(data, SIZE);
+ CopyMemory(data, p, length);
+
+ DWORD dstid = 0;
+ DWORD tgtid = 0;
+ int action = 0;
+ int channel = 0;
+ Point loc;
+ Text elem_name;
+ Text info;
+
+ objid = (data[2] << 8) | data[3];
+ dstid = (data[4] << 8) | data[5];
+ tgtid = (data[8] << 8) | data[9];
+ action = data[6];
+ channel = data[7];
+
+ float* f = (float*) (data + 10);
+ loc.x = *f++;
+ loc.y = *f++;
+ loc.z = *f++;
+
+ elem_name = (char*) (data + 22);
+
+ if (length > 55)
+ info = (char*) (data + 55);
+
+ Sim* sim = Sim::GetSim();
+ Ship* src = sim->FindShipByObjID(objid);
+ Ship* dst = sim->FindShipByObjID(dstid);
+ Element* elem = sim->FindElement(elem_name);
+
+ delete radio_message;
+ if (elem)
+ radio_message = new(__FILE__,__LINE__) RadioMessage(elem, src, action);
+ else
+ radio_message = new(__FILE__,__LINE__) RadioMessage(dst, src, action);
+
+ radio_message->SetChannel(channel);
+ radio_message->SetLocation(loc);
+ radio_message->SetInfo(info);
+
+ if (tgtid) {
+ SimObject* tgt = sim->FindShipByObjID(tgtid);
+
+ if (!tgt)
+ tgt = sim->FindShotByObjID(tgtid);
+
+ if (tgt)
+ radio_message->AddTarget(tgt);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+NetChatMsg::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ int chatlen = text.length();
+
+ if (chatlen > MAX_CHAT)
+ chatlen = MAX_CHAT;
+
+ length = HDR_LEN + NAME_LEN + chatlen;
+
+ data[0] = TYPE;
+ data[1] = (BYTE) length;
+ data[2] = (BYTE) ((dstid & 0xff00) >> 8);
+ data[3] = (BYTE) ((dstid & 0x00ff) );
+
+ char* p = (char*) (data + HDR_LEN);
+ strncpy(p, name.data(), NAME_LEN);
+
+ p = (char*) (data + HDR_LEN + NAME_LEN);
+ strncpy(p, text.data(), chatlen);
+
+ return data;
+}
+
+bool
+NetChatMsg::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE) {
+ length = p[1];
+ ZeroMemory(data, SIZE);
+ CopyMemory(data, p, length);
+
+ dstid = (data[2] << 8) | data[3];
+
+ char buffer[NAME_LEN+1];
+ ZeroMemory(buffer, NAME_LEN+1);
+ CopyMemory(buffer, data + HDR_LEN, NAME_LEN);
+
+ name = buffer;
+
+ if (length > HDR_LEN + NAME_LEN)
+ text = (char*) (data + HDR_LEN + NAME_LEN);
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+NetElemRequest::NetElemRequest()
+{ }
+
+BYTE*
+NetElemRequest::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[0] = TYPE;
+ data[1] = SIZE;
+
+ strncpy((char*) (data + 8), name.data(), NAME_LEN-1);
+
+ return data;
+}
+
+bool
+NetElemRequest::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE) {
+ ZeroMemory(data, SIZE);
+ CopyMemory(data, p, SIZE);
+
+ name = (const char*) (data + 8);
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+NetElemCreate::NetElemCreate()
+ : iff(0), type(0), intel(0), alert(false), in_flight(false)
+{
+ for (int i = 0; i < 16; i++)
+ load[i] = -1;
+}
+
+void
+NetElemCreate::SetLoadout(int* l)
+{
+ if (l) {
+ CopyMemory(load, l, sizeof(load));
+ }
+ else {
+ for (int i = 0; i < 16; i++)
+ load[i] = -1;
+ }
+}
+
+void
+NetElemCreate::SetSlots(int* s)
+{
+ if (s) {
+ CopyMemory(slots, s, sizeof(slots));
+ }
+ else {
+ for (int i = 0; i < 4; i++)
+ slots[i] = -1;
+ }
+}
+
+BYTE*
+NetElemCreate::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[0] = TYPE;
+ data[1] = SIZE;
+
+ data[2] = (BYTE) iff;
+ data[3] = (BYTE) type;
+ data[4] = (BYTE) intel;
+ data[5] = (BYTE) obj_code;
+
+ for (int i = 0; i < 16; i++)
+ data[6+i] = (BYTE) load[i];
+
+ strncpy((char*) (data + 22), name.data(), NAME_LEN-1);
+ strncpy((char*) (data + 54), commander.data(), NAME_LEN-1);
+ strncpy((char*) (data + 86), objective.data(), NAME_LEN-1);
+ strncpy((char*) (data + 118), carrier.data(), NAME_LEN-1);
+
+ data[150] = (BYTE) squadron;
+ data[151] = (BYTE) slots[0];
+ data[152] = (BYTE) slots[1];
+ data[153] = (BYTE) slots[2];
+ data[154] = (BYTE) slots[3];
+ data[155] = (BYTE) alert;
+ data[156] = (BYTE) in_flight;
+
+ return data;
+}
+
+bool
+NetElemCreate::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE) {
+ ZeroMemory(data, SIZE);
+ CopyMemory(data, p, SIZE);
+
+ iff = data[2];
+ type = data[3];
+ intel = data[4];
+ obj_code = data[5];
+
+ for (int i = 0; i < 16; i++) {
+ load[i] = data[6+i] == 255 ? -1 : data[6+i];
+ }
+
+ name = (const char*) (data + 22);
+ commander = (const char*) (data + 54);
+ objective = (const char*) (data + 86);
+ carrier = (const char*) (data + 118);
+
+ squadron = data[150];
+
+ for (i = 0; i < 4; i++) {
+ slots[i] = data[151+i];
+ if (slots[i] >= 255)
+ slots[i] = -1;
+ }
+
+ alert = data[155] ? true : false;
+ in_flight = data[156] ? true : false;
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+NetShipLaunch::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[ 0] = TYPE;
+ data[ 1] = SIZE;
+
+ DWORD* p = (DWORD*) (data + 4);
+ *p++ = (DWORD) objid;
+ *p++ = (DWORD) squadron;
+ *p++ = (DWORD) slot;
+
+ return data;
+}
+
+bool
+NetShipLaunch::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE) {
+ ZeroMemory(data, SIZE);
+ CopyMemory(data, p, SIZE);
+
+ DWORD* p = (DWORD*) (data + 4);
+ objid = *p++;
+ squadron = (int) *p++;
+ slot = (int) *p++;
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+NetNavData::NetNavData()
+ : objid(0), create(true), index(0), navpoint(0)
+{
+}
+
+NetNavData::~NetNavData()
+{
+ delete navpoint;
+}
+
+void
+NetNavData::SetNavPoint(Instruction* n)
+{
+ if (navpoint) {
+ delete navpoint;
+ navpoint = 0;
+ }
+
+ if (n)
+ navpoint = new(__FILE__,__LINE__) Instruction(*n);
+}
+
+BYTE*
+NetNavData::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[ 0] = TYPE;
+ data[ 1] = SIZE;
+
+ data[ 2] = (BYTE) ((objid & 0xff00) >> 8);
+ data[ 3] = (BYTE) ((objid & 0x00ff) );
+ data[ 4] = (BYTE) create;
+ data[ 5] = (BYTE) index;
+
+ if (!navpoint)
+ return data;
+
+ data[ 6] = (BYTE) navpoint->Action();
+ data[ 7] = (BYTE) navpoint->Formation();
+ data[ 8] = (BYTE) navpoint->Status();
+ data[ 9] = (BYTE) navpoint->EMCON();
+ data[10] = (BYTE) navpoint->WeaponsFree();
+ data[11] = (BYTE) navpoint->Farcast();
+
+ Point loc = navpoint->Location();
+
+ float* f = (float*) (data + 12);
+ *f++ = (float) loc.x; // bytes 12 - 15
+ *f++ = (float) loc.y; // bytes 16 - 19
+ *f++ = (float) loc.z; // bytes 20 - 23
+ *f++ = (float) navpoint->HoldTime(); // bytes 24 - 27
+ *f++ = (float) navpoint->Speed(); // bytes 28 - 31
+
+ WORD tgtid = 0;
+ if (navpoint->GetTarget())
+ tgtid = (WORD) navpoint->GetTarget()->GetObjID();
+
+ data[32] = (BYTE) ((tgtid & 0xff00) >> 8);
+ data[33] = (BYTE) ((tgtid & 0x00ff) );
+
+ strncpy((char*) (data + 34), navpoint->RegionName(), NAME_LEN-1);
+ strncpy((char*) (data + 66), navpoint->TargetName(), NAME_LEN-1);
+ strncpy((char*) (data + 98), elem.data(), NAME_LEN-1);
+
+ return data;
+}
+
+bool
+NetNavData::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE) {
+ ZeroMemory(data, SIZE);
+ CopyMemory(data, p, SIZE);
+
+ int action;
+ int formation;
+ int status;
+ int emcon;
+ int wep_free;
+ int farcast;
+ int speed;
+ float hold_time;
+ Point loc;
+ WORD tgtid = 0;
+
+ const char* rgn_name = 0;
+ const char* tgt_name = 0;
+
+ objid = (data[ 2] << 8) |
+ (data[ 3] );
+
+ tgtid = (data[32] << 8) |
+ (data[33] );
+
+ create = data[ 4] ? true : false;
+ index = data[ 5];
+ action = data[ 6];
+ formation = data[ 7];
+ status = data[ 8];
+ emcon = data[ 9];
+ wep_free = data[10];
+ farcast = data[11];
+
+ float* f = (float*) (data + 12);
+ loc.x = *f++;
+ loc.y = *f++;
+ loc.z = *f++;
+ hold_time = *f++;
+ speed = (int) *f++;
+
+ rgn_name = (const char*) (data + 34);
+ tgt_name = (const char*) (data + 66);
+ elem = (const char*) (data + 98);
+
+ if (navpoint) {
+ delete navpoint;
+ navpoint = 0;
+ }
+
+ Sim* sim = Sim::GetSim();
+ SimRegion* rgn = 0;
+
+ if (sim)
+ rgn = sim->FindRegion(rgn_name);
+
+ if (rgn)
+ navpoint = new(__FILE__,__LINE__) Instruction(rgn, loc, action);
+ else
+ navpoint = new(__FILE__,__LINE__) Instruction(rgn_name, loc, action);
+
+ navpoint->SetFormation(formation);
+ navpoint->SetStatus(status);
+ navpoint->SetEMCON(emcon);
+ navpoint->SetWeaponsFree(wep_free);
+ navpoint->SetFarcast(farcast);
+ navpoint->SetHoldTime(hold_time);
+ navpoint->SetSpeed(speed);
+ navpoint->SetTarget(tgt_name);
+
+ if (tgtid) {
+ Sim* sim = Sim::GetSim();
+ Ship* tgt = sim->FindShipByObjID(tgtid);
+
+ if (tgt)
+ navpoint->SetTarget(tgt);
+ }
+
+ if (index >= 255)
+ index = -1;
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+NetNavDelete::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[ 0] = TYPE;
+ data[ 1] = SIZE;
+
+ data[ 2] = (BYTE) ((objid & 0xff00) >> 8);
+ data[ 3] = (BYTE) ((objid & 0x00ff) );
+ data[ 4] = (BYTE) index;
+
+ strncpy((char*) (data + 6), elem.data(), 31);
+
+ return data;
+}
+
+bool
+NetNavDelete::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE) {
+ ZeroMemory(data, SIZE);
+ CopyMemory(data, p, SIZE);
+ int index = 0;
+
+ objid = (data[ 2] << 8) |
+ (data[ 3] );
+
+ index = data[4];
+ elem = (const char*) (data + 6);
+
+ if (index >= 255)
+ index = -1;
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+NetSelfDestruct::Pack()
+{
+ ZeroMemory(data, SIZE);
+
+ data[0] = TYPE;
+ data[1] = SIZE;
+
+ data[2] = (BYTE) ((objid & 0xff00) >> 8);
+ data[3] = (BYTE) ((objid & 0x00ff) );
+
+ float* p = (float*) (data + 4);
+ *p = damage;
+
+ return data;
+}
+
+bool
+NetSelfDestruct::Unpack(const BYTE* p)
+{
+ if (p && p[0] == TYPE && p[1] == SIZE) {
+ CopyMemory(data, p, SIZE);
+
+ objid = (data[2] << 8) | data[3];
+ damage = *(float*) (data + 4);
+
+ return true;
+ }
+
+ return false;
+}
diff --git a/Stars45/NetData.h b/Stars45/NetData.h
new file mode 100644
index 0000000..bd933d9
--- /dev/null
+++ b/Stars45/NetData.h
@@ -0,0 +1,966 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetData.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Payload structures for multiplayer network packets
+*/
+
+#ifndef NetData_h
+#define NetData_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+// UNRELIABLE: 0x01 - 0x0F
+
+const BYTE NET_PING = 0x01;
+const BYTE NET_PONG = 0x02;
+const BYTE NET_OBJ_LOC = 0x03;
+
+// RELIABLE: 0x10 - 0x7F
+
+const BYTE NET_JOIN_REQUEST = 0x10;
+const BYTE NET_JOIN_ANNOUNCE = 0x11;
+const BYTE NET_QUIT_REQUEST = 0x12;
+const BYTE NET_QUIT_ANNOUNCE = 0x13;
+const BYTE NET_KICK_REQUEST = 0x14;
+const BYTE NET_KICK_ANNOUNCE = 0x15;
+const BYTE NET_GAME_OVER = 0x16;
+const BYTE NET_COMM_MESSAGE = 0x17;
+const BYTE NET_CHAT_MESSAGE = 0x18;
+const BYTE NET_DISCONNECT = 0x19;
+
+const BYTE NET_OBJ_DAMAGE = 0x20;
+const BYTE NET_OBJ_KILL = 0x21;
+const BYTE NET_OBJ_SPAWN = 0x22;
+const BYTE NET_OBJ_HYPER = 0x23;
+const BYTE NET_OBJ_TARGET = 0x24;
+const BYTE NET_OBJ_EMCON = 0x25;
+const BYTE NET_SYS_DAMAGE = 0x26;
+const BYTE NET_SYS_STATUS = 0x27;
+
+const BYTE NET_ELEM_CREATE = 0x28;
+const BYTE NET_SHIP_LAUNCH = 0x29;
+const BYTE NET_NAV_DATA = 0x2A;
+const BYTE NET_NAV_DELETE = 0x2B;
+const BYTE NET_ELEM_REQUEST = 0x2C;
+
+const BYTE NET_WEP_TRIGGER = 0x30;
+const BYTE NET_WEP_RELEASE = 0x31;
+const BYTE NET_WEP_DESTROY = 0x32;
+
+const BYTE NET_SELF_DESTRUCT = 0x3F;
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+
+// +--------------------------------------------------------------------+
+
+class NetData
+{
+public:
+ static const char* TYPENAME() { return "NetData"; }
+
+ NetData() { }
+ virtual ~NetData() { }
+
+ virtual int Type() const { return 0; }
+ virtual int Length() const { return 0; }
+ virtual BYTE* Pack() { return 0; }
+ virtual bool Unpack(const BYTE* data) { return 0; }
+
+ virtual DWORD GetObjID() const { return 0; }
+ virtual void SetObjID(DWORD o) { }
+};
+
+// +--------------------------------------------------------------------+
+
+class NetObjLoc : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetObjLoc"; }
+
+ NetObjLoc() : objid(0), throttle(false), augmenter(false), shield(0) { }
+ NetObjLoc(DWORD oid, const Point& pos, const Point& orient, const Point& vel) :
+ objid(oid), location(pos), euler(orient), velocity(vel),
+ throttle(false), augmenter(false), gear(false), shield(0) { }
+
+ enum { TYPE=NET_OBJ_LOC, SIZE=24 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ virtual void SetObjID(DWORD id) { objid = id; }
+
+ Point GetLocation() const { return location; }
+ Point GetVelocity() const { return velocity; }
+ Point GetOrientation() const { return euler; }
+ bool GetThrottle() const { return throttle; }
+ bool GetAugmenter() const { return augmenter; }
+ bool GetGearDown() const { return gear; }
+ int GetShield() const { return shield; }
+
+ void SetLocation(const Point& loc) { location = loc; }
+ void SetVelocity(const Point& v) { velocity = v; }
+ void SetOrientation(const Point& o) { euler = o; }
+ void SetThrottle(bool t) { throttle = t; }
+ void SetAugmenter(bool a) { augmenter = a; }
+ void SetGearDown(bool g) { gear = g; }
+ void SetShield(int s) { shield = s; }
+
+private:
+ DWORD objid;
+ Point location;
+ Point velocity;
+ Point euler;
+ bool throttle;
+ bool augmenter;
+ bool gear;
+ int shield;
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetJoinRequest : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetJoinRequest"; }
+
+ NetJoinRequest() : index(0) { }
+
+ enum { TYPE=NET_JOIN_REQUEST, SIZE=128 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ const char* GetName() const { return name; }
+ const char* GetPassword() const { return pass; }
+ const char* GetSerialNumber() const { return serno; }
+ const char* GetElement() const { return elem; }
+ int GetIndex() const { return index; }
+
+ void SetName(const char* n) { name = n; }
+ void SetPassword(const char* p) { pass = p; }
+ void SetSerialNumber(const char* s){ serno = s; }
+ void SetElement(const char* n) { elem = n; }
+ void SetIndex(int n) { index = n; }
+
+private:
+ Text name; // callsign
+ Text pass; // password
+ Text serno; // box cdkey
+ Text elem; // element to join
+ int index; // one-based index
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetJoinAnnounce : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetJoinAnnounce"; }
+
+ NetJoinAnnounce();
+
+ enum { TYPE=NET_JOIN_ANNOUNCE, SIZE=200 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ virtual void SetObjID(DWORD o) { objid = o; }
+
+ const char* GetName() const { return name; }
+ const char* GetElement() const { return elem; }
+ const char* GetRegion() const { return region; }
+ const Point& GetLocation() const { return loc; }
+ const Point& GetVelocity() const { return velocity; }
+ int GetIndex() const { return index; }
+ double GetIntegrity() const { return integrity; }
+ int GetRespawns() const { return respawns; }
+ int GetDecoys() const { return decoys; }
+ int GetProbes() const { return probes; }
+ int GetFuel() const { return fuel; }
+ int GetShield() const { return shield; }
+ const int* GetAmmo() const { return ammo; }
+
+ void SetShip(Ship* s);
+
+ void SetName(const char* n) { name = n; }
+ void SetElement(const char* n) { elem = n; }
+ void SetRegion(const char* r) { region = r; }
+ void SetLocation(const Point& l) { loc = l; }
+ void SetVelocity(const Point& v) { velocity = v; }
+ void SetIndex(int n) { index = n; }
+ void SetIntegrity(double n) { integrity = (float) n; }
+ void SetRespawns(int n) { respawns = n; }
+ void SetDecoys(int n) { decoys = n; }
+ void SetProbes(int n) { probes = n; }
+ void SetFuel(int n) { fuel = n; }
+ void SetShield(int n) { shield = n; }
+ void SetAmmo(const int* a);
+
+ virtual DWORD GetNetID() const { return nid; }
+ virtual void SetNetID(DWORD n) { nid = n; }
+
+private:
+ Text name; // callsign
+ Text elem; // element to join
+ Text region; // region ship is in
+ Point loc; // location of ship
+ Point velocity; // velocity of ship
+ int index; // one-based index
+ float integrity; // hull integrity
+ int respawns;
+ int decoys;
+ int probes;
+ int fuel;
+ int shield;
+ int ammo[16];
+ DWORD objid;
+ DWORD nid; // not sent over network
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetQuitAnnounce : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetQuitAnnounce"; }
+
+ NetQuitAnnounce() : objid(0), disconnected(false) { }
+
+ enum { TYPE=NET_QUIT_ANNOUNCE, SIZE=5 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ virtual void SetObjID(DWORD o) { objid = o; }
+ virtual bool GetDisconnected() const { return disconnected; }
+ virtual void SetDisconnected(bool d) { disconnected = d; }
+
+private:
+ DWORD objid;
+ bool disconnected;
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetDisconnect : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetDisconnect"; }
+
+ NetDisconnect() { }
+
+ enum { TYPE=NET_DISCONNECT, SIZE=2 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+private:
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetObjDamage : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetObjDamage"; }
+
+ NetObjDamage() : objid(0), damage(0), shotid(0) { }
+
+ enum { TYPE=NET_OBJ_DAMAGE, SIZE=12 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ virtual void SetObjID(DWORD o) { objid = o; }
+
+ virtual DWORD GetShotID() const { return shotid; }
+ virtual void SetShotID(DWORD o) { shotid = o; }
+
+ float GetDamage() const { return damage; }
+ void SetDamage(float d) { damage = d; }
+
+private:
+ DWORD objid;
+ float damage;
+ DWORD shotid;
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetObjKill : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetObjKill"; }
+
+ NetObjKill() : objid(0), kill_id(0), killtype(0), respawn(false), deck(0) { }
+
+ enum { TYPE=NET_OBJ_KILL, SIZE=24,
+ KILL_MISC = 0,
+ KILL_PRIMARY,
+ KILL_SECONDARY,
+ KILL_COLLISION,
+ KILL_CRASH,
+ KILL_DOCK
+ };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ virtual void SetObjID(DWORD o) { objid = o; }
+ virtual DWORD GetKillerID() const { return kill_id; }
+ virtual void SetKillerID(DWORD o) { kill_id = o; }
+ virtual int GetKillType() const { return killtype;}
+ virtual void SetKillType(int t) { killtype = t; }
+ virtual bool GetRespawn() const { return respawn; }
+ virtual void SetRespawn(bool r) { respawn = r; }
+ virtual Point GetRespawnLoc() const { return loc; }
+ virtual void SetRespawnLoc(const Point& p) { loc = p; }
+ virtual int GetFlightDeck() const { return deck; }
+ virtual void SetFlightDeck(int n) { deck = n; }
+
+private:
+ DWORD objid;
+ DWORD kill_id;
+ int killtype;
+ bool respawn;
+ Point loc;
+ int deck;
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetObjHyper : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetObjHyper"; }
+
+ NetObjHyper() : objid(0), fc_src(0), fc_dst(0), transtype(0) { }
+
+ enum { TYPE=NET_OBJ_HYPER, SIZE=56 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ virtual void SetObjID(DWORD id) { objid = id; }
+
+ const Point& GetLocation() const { return location; }
+ const Text& GetRegion() const { return region; }
+ DWORD GetFarcaster1() const { return fc_src; }
+ DWORD GetFarcaster2() const { return fc_dst; }
+ int GetTransitionType() const { return transtype; }
+
+ void SetLocation(const Point& loc) { location = loc; }
+ void SetRegion(const char* rgn) { region = rgn; }
+ void SetFarcaster1(DWORD f) { fc_src = f; }
+ void SetFarcaster2(DWORD f) { fc_dst = f; }
+ void SetTransitionType(int t) { transtype = t; }
+
+private:
+ DWORD objid;
+ Point location;
+ Text region;
+ DWORD fc_src;
+ DWORD fc_dst;
+ int transtype;
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetObjTarget : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetObjTarget"; }
+
+ NetObjTarget() : objid(0), tgtid(0), sysix(0) { }
+
+ enum { TYPE=NET_OBJ_TARGET, SIZE=7 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ virtual void SetObjID(DWORD o) { objid = o; }
+
+ DWORD GetTgtID() const { return tgtid; }
+ void SetTgtID(DWORD o) { tgtid = o; }
+ int GetSubtarget() const { return sysix; }
+ void SetSubtarget(int n) { sysix = n; }
+
+private:
+ DWORD objid;
+ DWORD tgtid;
+ int sysix;
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetObjEmcon : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetObjEmcon"; }
+
+ NetObjEmcon() : objid(0), emcon(0) { }
+
+ enum { TYPE=NET_OBJ_EMCON, SIZE=5 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ virtual void SetObjID(DWORD o) { objid = o; }
+
+ int GetEMCON() const { return emcon; }
+ void SetEMCON(int n) { emcon = n; }
+
+private:
+ DWORD objid;
+ int emcon;
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetSysDamage : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetSysDamage"; }
+
+ NetSysDamage() : objid(0), sysix(-1), dmgtype(0), damage(0) { }
+
+ enum { TYPE=NET_SYS_DAMAGE, SIZE=12 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ virtual void SetObjID(DWORD o) { objid = o; }
+
+ int GetSystem() const { return sysix; }
+ void SetSystem(int n) { sysix = n; }
+ BYTE GetDamageType() const { return dmgtype; }
+ void SetDamageType(BYTE t) { dmgtype = t; }
+ double GetDamage() const { return damage; }
+ void SetDamage(double d) { damage = d; }
+
+private:
+ DWORD objid;
+ int sysix;
+ BYTE dmgtype;
+ double damage;
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetSysStatus : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetSysStatus"; }
+
+ NetSysStatus() : objid(0), sysix(-1), status(0), power(0), reactor(0),
+ avail(1) { }
+
+ enum { TYPE=NET_SYS_STATUS, SIZE=12 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ virtual void SetObjID(DWORD o) { objid = o; }
+
+ int GetSystem() const { return sysix; }
+ void SetSystem(int n) { sysix = n; }
+ BYTE GetStatus() const { return status; }
+ void SetStatus(BYTE s) { status = s; }
+ int GetPower() const { return power; }
+ void SetPower(int n) { power = n; }
+ int GetReactor() const { return reactor; }
+ void SetReactor(int n) { reactor = n; }
+ double GetAvailability() const { return avail; }
+ void SetAvailablility(double a) { avail = a; }
+
+private:
+ DWORD objid;
+ int sysix;
+ int status;
+ int power;
+ int reactor;
+ double avail;
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetWepTrigger : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetWepTrigger"; }
+
+ NetWepTrigger() : objid(0), tgtid(0), sysix(-1), index(0), count(0),
+ decoy(false), probe(false) { }
+
+ enum { TYPE=NET_WEP_TRIGGER, SIZE=10 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ virtual void SetObjID(DWORD o) { objid = o; }
+
+ DWORD GetTgtID() const { return tgtid; }
+ void SetTgtID(DWORD o) { tgtid = o; }
+ int GetSubtarget() const { return sysix; }
+ void SetSubtarget(int n) { sysix = n; }
+ int GetIndex() const { return index; }
+ void SetIndex(int n) { index = n; }
+ int GetCount() const { return count; }
+ void SetCount(int n) { count = n; }
+ bool GetDecoy() const { return decoy; }
+ void SetDecoy(bool d) { decoy = d; }
+ bool GetProbe() const { return probe; }
+ void SetProbe(bool p) { probe = p; }
+
+private:
+ DWORD objid;
+ DWORD tgtid;
+ int sysix;
+ int index;
+ int count;
+ bool decoy;
+ bool probe;
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetWepRelease : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetWepRelease"; }
+
+ NetWepRelease() : objid(0), tgtid(0), wepid(0), sysix(-1), index(0),
+ decoy(false), probe(false) { }
+
+ enum { TYPE=NET_WEP_RELEASE, SIZE=11 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ virtual void SetObjID(DWORD o) { objid = o; }
+
+ DWORD GetTgtID() const { return tgtid; }
+ void SetTgtID(DWORD o) { tgtid = o; }
+ int GetSubtarget() const { return sysix; }
+ void SetSubtarget(int n) { sysix = n; }
+ DWORD GetWepID() const { return wepid; }
+ void SetWepID(DWORD o) { wepid = o; }
+ int GetIndex() const { return index; }
+ void SetIndex(int n) { index = n; }
+ bool GetDecoy() const { return decoy; }
+ void SetDecoy(bool d) { decoy = d; }
+ bool GetProbe() const { return probe; }
+ void SetProbe(bool p) { probe = p; }
+
+private:
+ DWORD objid;
+ DWORD tgtid;
+ DWORD wepid;
+ int sysix;
+ int index;
+ bool decoy;
+ bool probe;
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetWepDestroy : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetWepDestroy"; }
+
+ NetWepDestroy() : objid(0) { }
+
+ enum { TYPE=NET_WEP_DESTROY, SIZE=4 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ virtual void SetObjID(DWORD o) { objid = o; }
+
+private:
+ DWORD objid;
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class RadioMessage;
+class NetCommMsg : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetCommMsg"; }
+
+ NetCommMsg() : objid(0), radio_message(0), length(0) { }
+ virtual ~NetCommMsg();
+
+ enum { TYPE=NET_COMM_MESSAGE, SIZE=200 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return length; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ virtual void SetObjID(DWORD o) { objid = o; }
+
+ RadioMessage* GetRadioMessage() { return radio_message; }
+ void SetRadioMessage(RadioMessage* m);
+
+private:
+ DWORD objid;
+ RadioMessage* radio_message;
+
+ int length;
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetChatMsg : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetChatMsg"; }
+
+ NetChatMsg() : dstid(0), length(0) { }
+
+ enum { TYPE=NET_CHAT_MESSAGE, SIZE=210, MAX_CHAT=160, HDR_LEN=4, NAME_LEN=32 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return length; }
+
+ virtual DWORD GetDstID() const { return dstid; }
+ virtual void SetDstID(DWORD d) { dstid = d; }
+ const Text& GetName() const { return name; }
+ void SetName(const char* m) { name = m; }
+ const Text& GetText() const { return text; }
+ void SetText(const char* m) { text = m; }
+
+private:
+ DWORD dstid;
+ Text name;
+ Text text;
+
+ int length;
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetElemRequest : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetElemRequest"; }
+
+ NetElemRequest();
+
+ enum { TYPE=NET_ELEM_REQUEST, SIZE=64, NAME_LEN=32 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ const Text& GetName() const { return name; }
+ void SetName(const char* m) { name = m; }
+
+private:
+ Text name;
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetElemCreate : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetElemCreate"; }
+
+ NetElemCreate();
+
+ enum { TYPE=NET_ELEM_CREATE, SIZE=192, NAME_LEN=32 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ const Text& GetName() const { return name; }
+ void SetName(const char* m) { name = m; }
+ const Text& GetCommander() const { return commander; }
+ void SetCommander(const char* m) { commander = m; }
+ const Text& GetObjective() const { return objective; }
+ void SetObjective(const char* m) { objective = m; }
+ const Text& GetCarrier() const { return carrier; }
+ void SetCarrier(const char* m) { carrier = m; }
+
+ int GetIFF() const { return iff; }
+ void SetIFF(int n) { iff = n; }
+ int GetType() const { return type; }
+ void SetType(int n) { type = n; }
+ int GetIntel() const { return intel; }
+ void SetIntel(int n) { intel = n; }
+ int GetObjCode() const { return obj_code; }
+ void SetObjCode(int n) { obj_code = n; }
+ int GetSquadron() const { return squadron; }
+ void SetSquadron(int n) { squadron = n; }
+
+ int* GetLoadout() { return load; }
+ void SetLoadout(int* n);
+ int* GetSlots() { return slots; }
+ void SetSlots(int* n);
+
+ bool GetAlert() const { return alert; }
+ void SetAlert(bool a) { alert = a; }
+
+ bool GetInFlight() const { return in_flight; }
+ void SetInFlight(bool f) { in_flight = f; }
+
+private:
+ Text name;
+ int iff;
+ int type;
+ int intel;
+ int obj_code;
+ int squadron;
+
+ Text commander;
+ Text objective;
+ Text carrier;
+
+ int load[16];
+ int slots[4];
+ bool alert;
+ bool in_flight;
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetShipLaunch : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetShipLaunch"; }
+
+ NetShipLaunch() : objid(0), squadron(0), slot(0) { }
+
+ enum { TYPE=NET_SHIP_LAUNCH, SIZE=16 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ virtual int GetSquadron() const { return squadron; }
+ virtual int GetSlot() const { return slot; }
+
+ virtual void SetObjID(DWORD o) { objid = o; }
+ virtual void SetSquadron(int s) { squadron = s; }
+ virtual void SetSlot(int s) { slot = s; }
+
+private:
+ DWORD objid;
+ int squadron;
+ int slot;
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class Instruction;
+
+class NetNavData : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetNavData"; }
+
+ NetNavData();
+ virtual ~NetNavData();
+
+ enum { TYPE=NET_NAV_DATA, SIZE=144, NAME_LEN=32 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ bool IsAdd() const { return create; }
+ bool IsEdit() const { return !create; }
+ const Text& GetElem() const { return elem; }
+ int GetIndex() const { return index; }
+ Instruction* GetNavPoint() const { return navpoint; }
+
+ virtual void SetObjID(DWORD o) { objid = o; }
+ void SetAdd(bool b) { create = b; }
+ void SetElem(const char* e) { elem = e; }
+ void SetIndex(int n) { index = n; }
+ void SetNavPoint(Instruction* n);
+
+private:
+ DWORD objid;
+ bool create;
+ Text elem;
+ int index;
+ Instruction* navpoint;
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetNavDelete : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetNavDelete"; }
+
+ NetNavDelete() : objid(0), index(0) { }
+
+ enum { TYPE=NET_NAV_DELETE, SIZE=40 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ const Text& GetElem() const { return elem; }
+ int GetIndex() const { return index; }
+
+ virtual void SetObjID(DWORD o) { objid = o; }
+ void SetElem(const char* e) { elem = e; }
+ void SetIndex(int n) { index = n; }
+
+private:
+ DWORD objid;
+ Text elem;
+ int index;
+
+ BYTE data[SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+class NetSelfDestruct : public NetData
+{
+public:
+ static const char* TYPENAME() { return "NetSelfDestruct"; }
+
+ NetSelfDestruct() : objid(0), damage(0) { }
+
+ enum { TYPE=NET_SELF_DESTRUCT, SIZE=8 };
+
+ virtual BYTE* Pack();
+ virtual bool Unpack(const BYTE* data);
+ virtual int Type() const { return TYPE; }
+ virtual int Length() const { return SIZE; }
+
+ virtual DWORD GetObjID() const { return objid; }
+ virtual void SetObjID(DWORD o) { objid = o; }
+
+ float GetDamage() const { return damage; }
+ void SetDamage(float d) { damage = d; }
+
+private:
+ DWORD objid;
+ float damage;
+
+ BYTE data[SIZE];
+};
+
+
+// +--------------------------------------------------------------------+
+
+#endif NetData_h
+
diff --git a/Stars45/NetFileServlet.cpp b/Stars45/NetFileServlet.cpp
new file mode 100644
index 0000000..803347b
--- /dev/null
+++ b/Stars45/NetFileServlet.cpp
@@ -0,0 +1,118 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetFileServlet.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ HTTP Servlet for File Transfer
+*/
+
+
+#include "MemDebug.h"
+#include "NetFileServlet.h"
+#include "NetAdminServer.h"
+#include "NetLayer.h"
+
+#include "DataLoader.h"
+
+// +-------------------------------------------------------------------+
+
+bool
+NetFileServlet::DoGet(HttpRequest& request, HttpResponse& response)
+{
+ if (!CheckUser(request, response))
+ return true;
+
+ Text content;
+ Text path = request.GetParam("path");
+ Text name = request.GetParam("name");
+
+ if (name.length()) {
+ BYTE* buffer = 0;
+ DataLoader* loader = DataLoader::GetLoader();
+
+ if (loader) {
+ loader->SetDataPath(path);
+ int len = loader->LoadBuffer(name, buffer);
+
+ if (len) {
+ content = Text((const char*) buffer, len);
+ }
+
+ loader->ReleaseBuffer(buffer);
+ loader->SetDataPath(0);
+ }
+ }
+
+ response.SetStatus(HttpResponse::SC_OK);
+ response.AddHeader("MIME-Version", "1.0");
+ response.AddHeader("Cache-Control", "no-cache");
+ response.AddHeader("Expires", "-1");
+ response.AddHeader("Content-Type", "text/plain");
+ response.SetContent(content);
+
+ return true;
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+NetWebServlet::DoGet(HttpRequest& request, HttpResponse& response)
+{
+ Text content;
+ Text name = request.URI();
+ bool found = false;
+
+ if (name.length() > 4) {
+ char filename[256];
+ strcpy(filename, name.data() + 1); // skip leading '/'
+
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ ::fseek(f, 0, SEEK_END);
+ int len = ftell(f);
+ ::fseek(f, 0, SEEK_SET);
+
+ BYTE* buf = new(__FILE__,__LINE__) BYTE[len];
+
+ ::fread(buf, len, 1, f);
+ ::fclose(f);
+
+ content = Text((const char*) buf, len);
+ delete [] buf;
+
+ found = true;
+ ::Print("weblog: 200 OK %s %d bytes\n", name.data(), len);
+ }
+ else {
+ ::Print("weblog: 404 Not Found %s\n", name.data());
+ }
+ }
+
+ if (found) {
+ Text content_type = "text/plain";
+
+ if (name.contains(".gif"))
+ content_type = "image/gif";
+ else if (name.contains(".jpg"))
+ content_type = "image/jpeg";
+ else if (name.contains(".htm"))
+ content_type = "text/html";
+
+ response.SetStatus(HttpResponse::SC_OK);
+ response.AddHeader("MIME-Version", "1.0");
+ response.AddHeader("Content-Type", content_type);
+ response.SetContent(content);
+ }
+ else {
+ response.SetStatus(HttpResponse::SC_NOT_FOUND);
+ }
+
+ return true;
+}
diff --git a/Stars45/NetFileServlet.h b/Stars45/NetFileServlet.h
new file mode 100644
index 0000000..c7a3273
--- /dev/null
+++ b/Stars45/NetFileServlet.h
@@ -0,0 +1,50 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetFileServlet.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ HTTP Servlet for File Transfer
+*/
+
+
+#ifndef NetFileServlet_h
+#define NetFileServlet_h
+
+#include "Types.h"
+#include "NetAdminServer.h"
+#include "NetUser.h"
+
+// +-------------------------------------------------------------------+
+
+class Campaign;
+class File;
+
+// +-------------------------------------------------------------------+
+
+class NetFileServlet : public NetAdminServlet
+{
+public:
+ NetFileServlet() { }
+ virtual ~NetFileServlet() { }
+
+ virtual bool DoGet(HttpRequest& request, HttpResponse& response);
+};
+
+// +-------------------------------------------------------------------+
+
+class NetWebServlet : public NetAdminServlet
+{
+public:
+ NetWebServlet() { }
+ virtual ~NetWebServlet() { }
+
+ virtual bool DoGet(HttpRequest& request, HttpResponse& response);
+};
+
+#endif NetFileServlet_h \ No newline at end of file
diff --git a/Stars45/NetGame.cpp b/Stars45/NetGame.cpp
new file mode 100644
index 0000000..2e0e8eb
--- /dev/null
+++ b/Stars45/NetGame.cpp
@@ -0,0 +1,330 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetGame.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Game Manager class
+*/
+
+#include "MemDebug.h"
+#include "NetGame.h"
+#include "NetGameClient.h"
+#include "NetGameServer.h"
+#include "NetClientConfig.h"
+#include "NetServerConfig.h"
+#include "NetPlayer.h"
+
+#include "NetMsg.h"
+#include "NetData.h"
+#include "NetLayer.h"
+
+#include "Player.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Sim.h"
+#include "Element.h"
+#include "HUDView.h"
+
+#include "NetHost.h"
+#include "Game.h"
+#include "Light.h"
+
+// +--------------------------------------------------------------------+
+
+const int MAX_NET_FPS = 20;
+const int MIN_NET_FRAME = 1000 / MAX_NET_FPS;
+
+const DWORD SHIP_ID_START = 0x0010;
+const DWORD SHOT_ID_START = 0x0400;
+
+static NetGame* netgame = 0;
+
+static DWORD ship_id_key = SHIP_ID_START;
+static DWORD shot_id_key = SHOT_ID_START;
+
+static long start_time = 0;
+
+// +--------------------------------------------------------------------+
+
+NetGame::NetGame()
+ : objid(0), netid(0), link(0), local_player(0), last_send_time(0), active(true)
+{
+ netgame = this;
+ sim = Sim::GetSim();
+
+ ship_id_key = SHIP_ID_START;
+ shot_id_key = SHOT_ID_START;
+
+ if (sim)
+ local_player = sim->GetPlayerShip();
+
+ Player* player = Player::GetCurrentPlayer();
+ if (player) {
+ player_name = player->Name();
+ player_pass = player->Password();
+ }
+
+ start_time = NetLayer::GetUTC();
+}
+
+NetGame::~NetGame()
+{
+ netgame = 0;
+ local_player = 0;
+ players.destroy();
+
+ if (link) {
+ double delta = fabs(NetLayer::GetUTC() - start_time);
+ double bandwidth = 10.0 * (link->GetBytesSent() + link->GetBytesRecv()) / delta;
+ double recvrate = link->GetPacketsRecv() / delta;
+
+ Print("NetGame Stats\n-------------\n");
+ Print(" packets sent %d\n", link->GetPacketsSent());
+ Print(" packets recv %d\n", link->GetPacketsRecv());
+ Print(" bytes sent %d\n", link->GetBytesSent());
+ Print(" bytes recv %d\n", link->GetBytesRecv());
+ Print(" retries %d\n", link->GetRetries());
+ Print(" drops %d\n", link->GetDrops());
+ Print(" avg lag %d msec\n", link->GetLag());
+ Print(" time %d sec\n", (int) delta);
+ Print(" bandwidth %d bps\n", (int) bandwidth);
+ Print(" packet rate %d pps in\n\n", (int) recvrate);
+
+ delete link;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+NetGame*
+NetGame::Create()
+{
+ if (!netgame) {
+ if (NetServerConfig::GetInstance())
+ netgame = new(__FILE__,__LINE__) NetGameServer;
+
+ else if (NetClientConfig::GetInstance() && NetClientConfig::GetInstance()->GetSelectedServer())
+ netgame = new(__FILE__,__LINE__) NetGameClient;
+ }
+
+ return netgame;
+}
+
+NetGame*
+NetGame::GetInstance()
+{
+ return netgame;
+}
+
+DWORD
+NetGame::GetObjID() const
+{
+ if (local_player)
+ return local_player->GetObjID();
+
+ return 0;
+}
+
+DWORD
+NetGame::GetNextObjID(int type)
+{
+ if (type == SHIP) {
+ if (ship_id_key >= SHOT_ID_START)
+ ship_id_key = SHIP_ID_START;
+
+ return ship_id_key++;
+ }
+
+ else if (type == SHOT) {
+ if (shot_id_key >= 0xFFFE)
+ shot_id_key = SHOT_ID_START;
+
+ return shot_id_key++;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGame::ExecFrame()
+{
+ Send();
+ Recv();
+}
+
+void
+NetGame::Recv()
+{
+ NetMsg* msg = link->GetMessage();
+
+ while (msg) {
+ if (active) {
+ // For Debug Convenience:
+ // NetPlayer* player = FindPlayerByNetID(msg->NetID());
+
+ switch (msg->Type()) {
+ case NET_JOIN_REQUEST: DoJoinRequest(msg); break;
+ case NET_JOIN_ANNOUNCE: DoJoinAnnounce(msg); break;
+ case NET_QUIT_REQUEST: DoQuitRequest(msg); break;
+ case NET_QUIT_ANNOUNCE: DoQuitAnnounce(msg); break;
+ case NET_GAME_OVER: DoGameOver(msg); break;
+ case NET_DISCONNECT: DoDisconnect(msg); break;
+
+ case NET_OBJ_LOC: DoObjLoc(msg); break;
+ case NET_OBJ_DAMAGE: DoObjDamage(msg); break;
+ case NET_OBJ_KILL: DoObjKill(msg); break;
+ case NET_OBJ_SPAWN: DoObjSpawn(msg); break;
+ case NET_OBJ_HYPER: DoObjHyper(msg); break;
+ case NET_OBJ_TARGET: DoObjTarget(msg); break;
+ case NET_OBJ_EMCON: DoObjEmcon(msg); break;
+ case NET_SYS_DAMAGE: DoSysDamage(msg); break;
+ case NET_SYS_STATUS: DoSysStatus(msg); break;
+
+ case NET_ELEM_CREATE: DoElemCreate(msg); break;
+ case NET_ELEM_REQUEST: DoElemRequest(msg); break;
+ case NET_SHIP_LAUNCH: DoShipLaunch(msg); break;
+ case NET_NAV_DATA: DoNavData(msg); break;
+ case NET_NAV_DELETE: DoNavDelete(msg); break;
+
+ case NET_WEP_TRIGGER: DoWepTrigger(msg); break;
+ case NET_WEP_RELEASE: DoWepRelease(msg); break;
+ case NET_WEP_DESTROY: DoWepDestroy(msg); break;
+
+ case NET_COMM_MESSAGE: DoCommMsg(msg); break;
+ case NET_CHAT_MESSAGE: DoChatMsg(msg); break;
+ case NET_SELF_DESTRUCT: DoSelfDestruct(msg); break;
+ }
+ }
+
+ delete msg;
+ msg = link->GetMessage();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGame::Send()
+{
+}
+
+int
+NetGame::NumPlayers()
+{
+ if (netgame) {
+ int num_players = netgame->players.size();
+
+ if (netgame->local_player)
+ num_players++;
+
+ return num_players;
+ }
+
+ return 0;
+}
+
+NetPlayer*
+NetGame::FindPlayerByName(const char* name)
+{
+ for (int i = 0; i < players.size(); i++) {
+ NetPlayer* p = players[i];
+
+ if (!strcmp(p->Name(), name))
+ return p;
+ }
+
+ return 0;
+}
+
+NetPlayer*
+NetGame::FindPlayerByNetID(DWORD netid)
+{
+ for (int i = 0; i < players.size(); i++) {
+ NetPlayer* p = players[i];
+
+ if (p->GetNetID() == netid)
+ return p;
+ }
+
+ return 0;
+}
+
+NetPlayer*
+NetGame::FindPlayerByObjID(DWORD objid)
+{
+ for (int i = 0; i < players.size(); i++) {
+ NetPlayer* p = players[i];
+
+ if (p->GetObjID() == objid)
+ return p;
+ }
+
+ return 0;
+}
+
+Ship*
+NetGame::FindShipByObjID(DWORD objid)
+{
+ if (sim)
+ return sim->FindShipByObjID(objid);
+
+ return 0;
+}
+
+Shot*
+NetGame::FindShotByObjID(DWORD objid)
+{
+ if (sim)
+ return sim->FindShotByObjID(objid);
+
+ return 0;
+}
+
+NetPeer*
+NetGame::GetPeer(NetPlayer* player)
+{
+ if (player && link) {
+ return link->FindPeer(player->GetNetID());
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGame::Respawn(DWORD objid, Ship* spawn)
+{
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+NetGame::IsNetGame()
+{
+ return netgame != 0;
+}
+
+bool
+NetGame::IsNetGameClient()
+{
+ if (netgame)
+ return netgame->IsClient();
+ return false;
+}
+
+bool
+NetGame::IsNetGameServer()
+{
+ if (netgame)
+ return netgame->IsServer();
+ return false;
+}
diff --git a/Stars45/NetGame.h b/Stars45/NetGame.h
new file mode 100644
index 0000000..249b695
--- /dev/null
+++ b/Stars45/NetGame.h
@@ -0,0 +1,127 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetGame.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Game Manager class
+*/
+
+#ifndef NetGame_h
+#define NetGame_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "NetLink.h"
+#include "Director.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Sim;
+class Ship;
+class Shot;
+class NetData;
+class NetMsg;
+class NetPlayer;
+
+// +--------------------------------------------------------------------+
+
+class NetGame
+{
+public:
+ static const char* TYPENAME() { return "NetGame"; }
+
+ enum { SHIP, SHOT };
+
+ NetGame();
+ virtual ~NetGame();
+
+ virtual bool IsClient() const { return false; }
+ virtual bool IsServer() const { return false; }
+ virtual bool IsActive() const { return active; }
+
+ virtual DWORD GetNetID() const { return netid; }
+ virtual DWORD GetObjID() const;
+
+ virtual void ExecFrame();
+ virtual void Recv();
+ virtual void Send();
+
+ virtual void SendData(NetData* data) { }
+
+ virtual NetPlayer* FindPlayerByName(const char* name);
+ virtual NetPlayer* FindPlayerByNetID(DWORD netid);
+ virtual NetPlayer* FindPlayerByObjID(DWORD objid);
+ virtual Ship* FindShipByObjID(DWORD objid);
+ virtual Shot* FindShotByObjID(DWORD objid);
+
+ virtual NetPeer* GetPeer(NetPlayer* player);
+
+ virtual void Respawn(DWORD objid, Ship* spawn);
+
+ static NetGame* Create();
+ static NetGame* GetInstance();
+ static bool IsNetGame();
+ static bool IsNetGameClient();
+ static bool IsNetGameServer();
+ static int NumPlayers();
+
+ static DWORD GetNextObjID(int type=SHIP);
+
+protected:
+ virtual void DoJoinRequest(NetMsg* msg) { }
+ virtual void DoJoinAnnounce(NetMsg* msg) { }
+ virtual void DoQuitRequest(NetMsg* msg) { }
+ virtual void DoQuitAnnounce(NetMsg* msg) { }
+ virtual void DoGameOver(NetMsg* msg) { }
+ virtual void DoDisconnect(NetMsg* msg) { }
+
+ virtual void DoObjLoc(NetMsg* msg) { }
+ virtual void DoObjDamage(NetMsg* msg) { }
+ virtual void DoObjKill(NetMsg* msg) { }
+ virtual void DoObjSpawn(NetMsg* msg) { }
+ virtual void DoObjHyper(NetMsg* msg) { }
+ virtual void DoObjTarget(NetMsg* msg) { }
+ virtual void DoObjEmcon(NetMsg* msg) { }
+ virtual void DoSysDamage(NetMsg* msg) { }
+ virtual void DoSysStatus(NetMsg* msg) { }
+
+ virtual void DoElemCreate(NetMsg* msg) { }
+ virtual void DoElemRequest(NetMsg* msg) { }
+ virtual void DoShipLaunch(NetMsg* msg) { }
+ virtual void DoNavData(NetMsg* msg) { }
+ virtual void DoNavDelete(NetMsg* msg) { }
+
+ virtual void DoWepTrigger(NetMsg* msg) { }
+ virtual void DoWepRelease(NetMsg* msg) { }
+ virtual void DoWepDestroy(NetMsg* msg) { }
+
+ virtual void DoCommMsg(NetMsg* msg) { }
+ virtual void DoChatMsg(NetMsg* msg) { }
+ virtual void DoSelfDestruct(NetMsg* msg) { }
+
+ List<NetPlayer> players;
+ NetLink* link;
+
+ DWORD objid;
+ DWORD netid;
+ Ship* local_player;
+ Text player_name;
+ Text player_pass;
+ Ship* target;
+ Sim* sim;
+ bool active;
+
+ DWORD last_send_time;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif NetGame_h
+
diff --git a/Stars45/NetGameClient.cpp b/Stars45/NetGameClient.cpp
new file mode 100644
index 0000000..76fb527
--- /dev/null
+++ b/Stars45/NetGameClient.cpp
@@ -0,0 +1,1069 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetGameClient.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Game Manager class
+*/
+
+#include "MemDebug.h"
+#include "NetGameClient.h"
+#include "NetClientConfig.h"
+#include "NetLobby.h"
+#include "NetPlayer.h"
+#include "NetData.h"
+#include "NetUtil.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Shield.h"
+#include "Shot.h"
+#include "Sim.h"
+#include "SimEvent.h"
+#include "Weapon.h"
+#include "Element.h"
+#include "Explosion.h"
+#include "HUDView.h"
+#include "RadioView.h"
+#include "Instruction.h"
+#include "Hangar.h"
+#include "FlightDeck.h"
+#include "Mission.h"
+
+#include "NetMsg.h"
+#include "NetHost.h"
+#include "NetLayer.h"
+#include "NetPeer.h"
+
+#include "Game.h"
+#include "Light.h"
+
+// +--------------------------------------------------------------------+
+
+const int MAX_NET_FPS = 20;
+const int MIN_NET_FRAME = 1000 / MAX_NET_FPS;
+
+const char* FormatGameTime();
+
+// +--------------------------------------------------------------------+
+
+NetGameClient::NetGameClient()
+ : server_id(0), join_req_time(0)
+{
+ Print("Constructing NetGameClient\n");
+
+ NetHost me;
+ Text server_name;
+ WORD port = 11101;
+
+ NetClientConfig* ncc = NetClientConfig::GetInstance();
+ if (ncc) {
+ NetServerInfo* info = ncc->GetSelectedServer();
+
+ if (info) {
+ server_name = info->hostname;
+ port = info->gameport;
+ }
+ }
+
+ if (server_name.length() && port > 0) {
+ Print(" '%s' is a client of '%s'\n", me.Name(), server_name);
+ link = new(__FILE__,__LINE__) NetLink;
+ server_id = link->AddPeer(NetAddr(server_name, port));
+ SendJoinRequest();
+ }
+ else if (port == 0) {
+ Print(" '%s' invalid game port number %d\n", me.Name(), port);
+ }
+ else {
+ Print(" '%s' is a client without a server\n", me.Name());
+ }
+}
+
+NetGameClient::~NetGameClient()
+{
+ link->SendMessage(server_id, NET_QUIT_REQUEST, 0, 0, NetMsg::RELIABLE);
+
+ // wait for message to be delivered before shutting down the link:
+ Sleep(500);
+
+ join_backlog.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGameClient::SendJoinRequest()
+{
+ if ((NetLayer::GetTime() - join_req_time) < 5000)
+ return;
+
+ if (local_player && local_player->GetElement() && server_id) {
+ Print(" sending join request - name: '%s' elem: '%s' index: %d\n",
+ player_name.data(),
+ local_player->GetElement()->Name().data(),
+ local_player->GetElementIndex());
+
+ NetJoinRequest join_req;
+ join_req.SetName(player_name);
+ join_req.SetPassword(player_pass);
+ join_req.SetElement(local_player->GetElement()->Name());
+ join_req.SetIndex(local_player->GetElementIndex());
+
+ link->SendMessage(server_id, join_req.Pack(), NetJoinRequest::SIZE, NetMsg::RELIABLE);
+ join_req_time = NetLayer::GetTime();
+
+ local_player->SetNetObserver(true);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGameClient::DoJoinRequest(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetJoinRequest join_req;
+ if (join_req.Unpack(msg->Data())) {
+ Print("Client received Join Request from '%s'\n", join_req.GetName());
+ }
+}
+
+void
+NetGameClient::DoJoinAnnounce(NetMsg* msg)
+{
+ if (!msg) return;
+
+ Sim* sim = Sim::GetSim();
+ if (!sim) return;
+
+ NetJoinAnnounce* join_ann = new(__FILE__,__LINE__) NetJoinAnnounce;
+ bool saved = false;
+
+ if (join_ann->Unpack(msg->Data())) {
+ DWORD nid = msg->NetID();
+ DWORD oid = join_ann->GetObjID();
+ Text name = join_ann->GetName();
+ Text elem_name = join_ann->GetElement();
+ Text region = join_ann->GetRegion();
+ Point loc = join_ann->GetLocation();
+ Point velocity = join_ann->GetVelocity();
+ int index = join_ann->GetIndex();
+ int shld_lvl = join_ann->GetShield();
+ join_ann->SetNetID(nid);
+ Ship* ship = 0;
+ char ship_name[128];
+
+ strcpy(ship_name, Game::GetText("NetGameClient.no-ship").data());
+
+ if (local_player && player_name == name) {
+ HUDView::Message(Game::GetText("NetGameClient.local-accept"), name.data(), local_player->Name());
+
+ objid = oid;
+ netid = nid;
+ local_player->SetObjID(oid);
+ local_player->SetNetObserver(false);
+ Observe(local_player);
+
+ SimRegion* rgn = local_player->GetRegion();
+ if (rgn && region != rgn->Name()) {
+ SimRegion* dst = sim->FindRegion(region);
+ if (dst) dst->InsertObject(local_player);
+ }
+
+ local_player->MoveTo(loc);
+ local_player->SetVelocity(velocity);
+
+ Shield* shield = local_player->GetShield();
+ if (shield)
+ shield->SetNetShieldLevel(shld_lvl);
+ }
+ else {
+ NetPlayer* remote_player = FindPlayerByObjID(oid);
+ if (remote_player) {
+ remote_player->SetName(name);
+ remote_player->SetObjID(oid);
+
+ if (index > 0)
+ sprintf(ship_name, "%s %d", elem_name.data(), index);
+ else
+ sprintf(ship_name, "%s", elem_name.data());
+ }
+ else {
+ Element* element = sim->FindElement(elem_name);
+
+ if (element) {
+ ship = element->GetShip(index);
+ }
+ else {
+ Print("NetGameClient::DoJoinAnnounce() could not find elem %s for player '%s' objid %d\n",
+ elem_name.data(), name.data(), oid);
+
+ NetUtil::SendElemRequest(elem_name.data());
+ }
+
+ if (!ship) {
+ // save it for later:
+ join_backlog.append(join_ann);
+ saved = true;
+ }
+ else {
+ strcpy(ship_name, ship->Name());
+
+ SimRegion* rgn = ship->GetRegion();
+ if (rgn && region != rgn->Name()) {
+ SimRegion* dst = sim->FindRegion(region);
+ if (dst) dst->InsertObject(ship);
+ }
+
+ ship->MoveTo(loc);
+ ship->SetVelocity(velocity);
+
+ Shield* shield = ship->GetShield();
+ if (shield)
+ shield->SetNetShieldLevel(shld_lvl);
+
+ NetPlayer* remote_player = new(__FILE__,__LINE__) NetPlayer(nid);
+ remote_player->SetName(name);
+ remote_player->SetObjID(oid);
+ remote_player->SetShip(ship);
+
+ players.append(remote_player);
+
+ if (name == "Server A.I. Ship") {
+ Print("Remote Player '%s' has joined as '%s' with ID %d\n", name.data(), ship_name, oid);
+ }
+ else {
+ HUDView::Message(Game::GetText("NetGameClient.remote-join").data(), name.data(), ship_name);
+ }
+ }
+ }
+ }
+ }
+
+ if (!saved)
+ delete join_ann;
+}
+
+bool
+NetGameClient::DoJoinBacklog(NetJoinAnnounce* join_ann)
+{
+ bool finished = false;
+
+ if (!join_ann)
+ return finished;
+
+ Sim* sim = Sim::GetSim();
+ if (!sim)
+ return finished;
+
+ DWORD nid = join_ann->GetNetID();
+ DWORD oid = join_ann->GetObjID();
+ Text name = join_ann->GetName();
+ Text elem_name = join_ann->GetElement();
+ Text region = join_ann->GetRegion();
+ Point loc = join_ann->GetLocation();
+ Point velocity = join_ann->GetVelocity();
+ int index = join_ann->GetIndex();
+ int shld_lvl = join_ann->GetShield();
+ Ship* ship = 0;
+ char ship_name[128];
+
+ strcpy(ship_name, Game::GetText("NetGameClient.no-ship").data());
+
+ if (nid && oid) {
+ NetPlayer* remote_player = FindPlayerByObjID(oid);
+ if (remote_player) {
+ remote_player->SetName(name);
+ remote_player->SetObjID(oid);
+
+ if (index > 0)
+ sprintf(ship_name, "%s %d", elem_name.data(), index);
+ else
+ sprintf(ship_name, "%s", elem_name.data());
+ }
+ else {
+ Element* element = sim->FindElement(elem_name);
+
+ if (element) {
+ ship = element->GetShip(index);
+ }
+
+ if (ship) {
+ strcpy(ship_name, ship->Name());
+
+ SimRegion* rgn = ship->GetRegion();
+ if (rgn && region != rgn->Name()) {
+ SimRegion* dst = sim->FindRegion(region);
+ if (dst) dst->InsertObject(ship);
+ }
+
+ ship->MoveTo(loc);
+ ship->SetVelocity(velocity);
+
+ Shield* shield = ship->GetShield();
+ if (shield)
+ shield->SetNetShieldLevel(shld_lvl);
+
+ NetPlayer* remote_player = new(__FILE__,__LINE__) NetPlayer(nid);
+ remote_player->SetName(name);
+ remote_player->SetObjID(oid);
+ remote_player->SetShip(ship);
+
+ players.append(remote_player);
+ finished = true;
+
+ if (name == "Server A.I. Ship") {
+ Print("NetGameClient::DoJoinBacklog() Remote Player '%s' has joined as '%s' with ID %d\n", name.data(), ship_name, oid);
+ }
+ else {
+ HUDView::Message(Game::GetText("NetGameClient.remote-join").data(), name.data(), ship_name);
+ }
+ }
+ }
+ }
+
+ return finished;
+}
+
+
+void
+NetGameClient::DoQuitRequest(NetMsg* msg)
+{
+ if (!msg) return;
+
+ Print("Client received Quit Request from NetID: %08X\n", msg->NetID());
+}
+
+void
+NetGameClient::DoQuitAnnounce(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetQuitAnnounce quit_ann;
+ quit_ann.Unpack(msg->Data());
+
+ NetPlayer* player = FindPlayerByObjID(quit_ann.GetObjID());
+
+ if (player) {
+ NetPlayer* zombie = players.remove(player);
+
+ // return remote ship to ship pool:
+ Ship* s = zombie->GetShip();
+ if (s) {
+ s->SetNetworkControl(0);
+ zombie->SetShip(0);
+ }
+
+ if (quit_ann.GetDisconnected())
+ HUDView::Message(Game::GetText("NetGameClient.remote-discon").data(), zombie->Name());
+ else
+ HUDView::Message(Game::GetText("NetGameClient.remote-quit").data(), zombie->Name());
+ delete zombie;
+ }
+ else {
+ Print("Quit Announce for unknown player %08X disconnected = %d\n", msg->NetID(), quit_ann.GetDisconnected());
+ }
+}
+
+void
+NetGameClient::DoGameOver(NetMsg* msg)
+{
+ if (!msg) return;
+
+ HUDView::Message(Game::GetText("NetGameClient.game-over").data());
+ players.destroy();
+ active = false;
+}
+
+void
+NetGameClient::DoDisconnect(NetMsg* msg)
+{
+ if (!msg) return;
+
+ HUDView::Message(Game::GetText("NetGameClient.discon-detect").data());
+ HUDView::Message(Game::GetText("NetGameClient.please-exit").data());
+ players.destroy();
+ active = false;
+}
+
+void
+NetGameClient::DoObjLoc(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetObjLoc obj_loc;
+ obj_loc.Unpack(msg->Data());
+
+ NetPlayer* player = FindPlayerByObjID(obj_loc.GetObjID());
+ if (player)
+ player->DoObjLoc(&obj_loc);
+}
+
+void
+NetGameClient::DoObjDamage(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetObjDamage obj_damage;
+ obj_damage.Unpack(msg->Data());
+
+ Ship* ship = FindShipByObjID(obj_damage.GetObjID());
+ if (ship) {
+ Sim* sim = Sim::GetSim();
+ Shot* shot = FindShotByObjID(obj_damage.GetShotID());
+ const Ship* owner = 0;
+ const char* owner_name = "[NET]";
+
+ ship->InflictNetDamage(obj_damage.GetDamage(), shot);
+
+ if (shot && sim) {
+ if (shot->Owner()) {
+ owner = shot->Owner();
+ owner_name = owner->Name();
+ }
+
+ if (shot->IsMissile()) {
+ SimRegion* region = ship->GetRegion();
+ float scale = ship->Design()->explosion_scale;
+
+ if (scale <= 0)
+ scale = ship->Design()->scale;
+
+ if (owner) {
+ const ShipDesign* owner_design = owner->Design();
+ if (owner_design && owner_design->scale < scale)
+ scale = (float) owner_design->scale;
+ }
+
+ sim->CreateExplosion(shot->Location(), Point(), Explosion::SHOT_BLAST, 20.0f * scale, scale, region);
+ }
+
+ if (!shot->IsBeam()) {
+ if (owner) {
+ ShipStats* stats = ShipStats::Find(owner_name);
+
+ if (stats) {
+ if (shot->IsPrimary())
+ stats->AddGunHit();
+ else if (shot->Damage() > 0)
+ stats->AddMissileHit();
+ }
+ }
+
+ shot->Destroy();
+ }
+ }
+ }
+}
+
+void
+NetGameClient::DoObjKill(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetObjKill obj_kill;
+ obj_kill.Unpack(msg->Data());
+
+ Ship* ship = FindShipByObjID(obj_kill.GetObjID());
+ if (ship) {
+ Ship* killer = FindShipByObjID(obj_kill.GetKillerID());
+ Text killer_name = Game::GetText("NetGameClient.unknown");
+
+ if (killer)
+ killer_name = killer->Name();
+
+ // log the kill:
+ switch (obj_kill.GetKillType()) {
+ default:
+ case NetObjKill::KILL_MISC:
+ Print("Ship '%s' destroyed (misc) (%s)\n", ship->Name(), FormatGameTime());
+ break;
+
+ case NetObjKill::KILL_PRIMARY:
+ case NetObjKill::KILL_SECONDARY:
+ Print("Ship '%s' killed by '%s' (%s)\n", ship->Name(), killer_name.data(), FormatGameTime());
+ break;
+
+ case NetObjKill::KILL_COLLISION:
+ Print("Ship '%s' killed in collision with '%s' (%s)\n", ship->Name(), killer_name.data(), FormatGameTime());
+ break;
+
+ case NetObjKill::KILL_CRASH:
+ Print("Ship '%s' destroyed (crash) (%s)\n", ship->Name(), FormatGameTime());
+
+ case NetObjKill::KILL_DOCK:
+ Print("Ship '%s' docked (%s)\n", ship->Name(), FormatGameTime());
+ }
+
+ // record the kill in the stats:
+ if (killer && obj_kill.GetKillType() != NetObjKill::KILL_DOCK) {
+ ShipStats* kstats = ShipStats::Find(killer->Name());
+ if (kstats) {
+ if (obj_kill.GetKillType() == NetObjKill::KILL_PRIMARY)
+ kstats->AddEvent(SimEvent::GUNS_KILL, ship->Name());
+
+ else if (obj_kill.GetKillType() == NetObjKill::KILL_SECONDARY)
+ kstats->AddEvent(SimEvent::MISSILE_KILL, ship->Name());
+ }
+
+ if (killer && killer->GetIFF() != ship->GetIFF()) {
+ if (ship->GetIFF() > 0 || killer->GetIFF() > 1)
+ kstats->AddPoints(ship->Value());
+ }
+ }
+
+ ShipStats* killee = ShipStats::Find(ship->Name());
+ if (killee) {
+ if (obj_kill.GetKillType() == NetObjKill::KILL_DOCK)
+ killee->AddEvent(SimEvent::DOCK, killer_name);
+ else
+ killee->AddEvent(SimEvent::DESTROYED, killer_name);
+ }
+
+ if (obj_kill.GetKillType() == NetObjKill::KILL_DOCK) {
+ FlightDeck* deck = killer->GetFlightDeck(obj_kill.GetFlightDeck());
+ sim->NetDockShip(ship, killer, deck);
+ }
+ else {
+ ship->InflictNetDamage(ship->Integrity());
+ ship->DeathSpiral();
+
+ if (!obj_kill.GetRespawn())
+ ship->SetRespawnCount(0);
+ else
+ ship->SetRespawnLoc(obj_kill.GetRespawnLoc());
+ }
+ }
+
+ // this shouldn't happen in practice,
+ // but if it does, this is what should be done:
+ else {
+ Shot* shot = FindShotByObjID(obj_kill.GetObjID());
+
+ if (shot) {
+ ::Print("NetGameClient::DoObjKill destroying shot '%s'\n", shot->Name());
+ shot->Destroy();
+ }
+ }
+}
+
+void
+NetGameClient::DoObjSpawn(NetMsg* msg)
+{
+}
+
+void
+NetGameClient::DoObjHyper(NetMsg* msg)
+{
+ if (!msg) return;
+ Print("Client received OBJ HYPER from NetID: %08x\n", msg->NetID());
+
+ NetObjHyper obj_hyper;
+ obj_hyper.Unpack(msg->Data());
+
+ NetPlayer* player = FindPlayerByObjID(obj_hyper.GetObjID());
+ if (player && player->GetShip())
+ player->DoObjHyper(&obj_hyper);
+}
+
+void
+NetGameClient::DoObjTarget(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetObjTarget obj_target;
+ obj_target.Unpack(msg->Data());
+
+ NetPlayer* player = FindPlayerByObjID(obj_target.GetObjID());
+ if (player)
+ player->DoObjTarget(&obj_target);
+}
+
+void
+NetGameClient::DoObjEmcon(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetObjEmcon obj_emcon;
+ obj_emcon.Unpack(msg->Data());
+
+ NetPlayer* player = FindPlayerByObjID(obj_emcon.GetObjID());
+ if (player)
+ player->DoObjEmcon(&obj_emcon);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGameClient::DoSysDamage(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetSysDamage sys_damage;
+ sys_damage.Unpack(msg->Data());
+
+ NetPlayer* player = FindPlayerByObjID(sys_damage.GetObjID());
+ if (player && player->GetShip())
+ player->DoSysDamage(&sys_damage);
+}
+
+void
+NetGameClient::DoSysStatus(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetSysStatus sys_status;
+ sys_status.Unpack(msg->Data());
+
+ NetPlayer* player = FindPlayerByObjID(sys_status.GetObjID());
+ if (player && player->GetShip())
+ player->DoSysStatus(&sys_status);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGameClient::DoElemCreate(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetElemCreate elem_create;
+ elem_create.Unpack(msg->Data());
+
+ const char* elem_name = elem_create.GetName().data();
+
+ ::Print("NetGameClient::DoElemCreate name: %s iff: %d type %s\n",
+ elem_name,
+ elem_create.GetIFF(),
+ Mission::RoleName(elem_create.GetType()));
+
+ Sim* sim = Sim::GetSim();
+ Element* elem = sim->FindElement(elem_name);
+ if (elem) {
+ ::Print(" element '%' already exists - ignored\n", elem_name);
+ return;
+ }
+
+ elem = sim->CreateElement(elem_name,
+ elem_create.GetIFF(),
+ elem_create.GetType());
+
+ int* load = elem_create.GetLoadout();
+ int* slots = elem_create.GetSlots();
+ int squadron = elem_create.GetSquadron();
+ int code = elem_create.GetObjCode();
+ Text target = elem_create.GetObjective();
+ bool alert = elem_create.GetAlert();
+ bool active = elem_create.GetInFlight();
+
+ elem->SetIntelLevel(elem_create.GetIntel());
+ elem->SetLoadout(load);
+
+ if (code > Instruction::RTB || target.length() > 0) {
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(code, target);
+ elem->AddObjective(obj);
+ }
+
+ Ship* carrier = sim->FindShip(elem_create.GetCarrier());
+ if (carrier) {
+ elem->SetCarrier(carrier);
+
+ Hangar* hangar = carrier->GetHangar();
+ if (hangar) {
+ Text squadron_name = hangar->SquadronName(squadron);
+ elem->SetSquadron(squadron_name);
+
+ if (active) {
+ for (int i = 0; i < 4; i++) {
+ int slot = slots[i];
+ if (slot > -1) {
+ hangar->GotoActiveFlight(squadron, slot, elem, load);
+ }
+ }
+ }
+
+ else {
+ FlightDeck* deck = 0;
+ int queue = 1000;
+
+ for (int i = 0; i < carrier->NumFlightDecks(); i++) {
+ FlightDeck* d = carrier->GetFlightDeck(i);
+
+ if (d && d->IsLaunchDeck()) {
+ int dq = hangar->PreflightQueue(d);
+
+ if (dq < queue) {
+ queue = dq;
+ deck = d;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ int slot = slots[i];
+ if (slot > -1) {
+ hangar->GotoAlert(squadron, slot, deck, elem, load, !alert);
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+NetGameClient::DoShipLaunch(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetShipLaunch ship_launch;
+ ship_launch.Unpack(msg->Data());
+
+ Sim* sim = Sim::GetSim();
+ int squadron = ship_launch.GetSquadron();
+ int slot = ship_launch.GetSlot();
+
+ Ship* carrier = FindShipByObjID(ship_launch.GetObjID());
+
+ if (carrier) {
+ Hangar* hangar = carrier->GetHangar();
+
+ if (hangar) {
+ hangar->Launch(squadron, slot);
+ }
+ }
+}
+
+void
+NetGameClient::DoWepTrigger(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetWepTrigger trigger;
+ trigger.Unpack(msg->Data());
+
+ NetPlayer* player = FindPlayerByObjID(trigger.GetObjID());
+ if (player)
+ player->DoWepTrigger(&trigger);
+}
+
+void
+NetGameClient::DoWepRelease(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetWepRelease release;
+ release.Unpack(msg->Data());
+
+ NetPlayer* player = FindPlayerByObjID(release.GetObjID());
+ if (player) {
+ player->DoWepRelease(&release);
+ }
+
+ else {
+ Ship* shooter = FindShipByObjID(release.GetObjID());
+ if (shooter) {
+ int index = release.GetIndex();
+ DWORD tgtid = release.GetTgtID();
+ DWORD wepid = release.GetWepID();
+ int subid = release.GetSubtarget();
+ bool decoy = release.GetDecoy();
+ bool probe = release.GetProbe();
+
+ Weapon* w = 0;
+
+ if (decoy) w = shooter->GetDecoy();
+ else if (probe) w = shooter->GetProbeLauncher();
+ else w = shooter->GetWeaponByIndex(index);
+
+ if (w && !w->IsPrimary()) {
+ SimObject* target = FindShipByObjID(tgtid);
+ System* subtgt = 0;
+
+ if (target) {
+ if (subid >= 0) {
+ Ship* tgt_ship = (Ship*) target;
+ subtgt = tgt_ship->Systems().at(subid);
+ }
+ }
+ else {
+ target = FindShotByObjID(tgtid);
+ }
+
+ Shot* shot = w->NetFireSecondary(target, subtgt, wepid);
+
+ if (shot && shot->IsDrone()) {
+ if (probe)
+ shooter->SetProbe((Drone*) shot);
+
+ else if (decoy)
+ shooter->AddActiveDecoy((Drone*) shot);
+ }
+ }
+ }
+ }
+}
+
+void
+NetGameClient::DoNavData(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetNavData nav_data;
+ nav_data.Unpack(msg->Data());
+
+ Element* elem = sim->FindElement(nav_data.GetElem());
+ Ship* ship = FindShipByObjID(nav_data.GetObjID());
+
+ if (elem) {
+ if (nav_data.IsAdd()) {
+ Instruction* navpt = new(__FILE__,__LINE__) Instruction(*nav_data.GetNavPoint());
+ Instruction* after = 0;
+ int index = nav_data.GetIndex();
+
+ if (index >= 0 && index < elem->GetFlightPlan().size())
+ after = elem->GetFlightPlan().at(index);
+
+ elem->AddNavPoint(navpt, after, false);
+ }
+
+ else {
+ Instruction* navpt = nav_data.GetNavPoint();
+ Instruction* exist = 0;
+ int index = nav_data.GetIndex();
+
+ if (navpt && index >= 0 && index < elem->GetFlightPlan().size()) {
+ exist = elem->GetFlightPlan().at(index);
+
+ *exist = *navpt;
+ }
+ }
+ }
+}
+
+void
+NetGameClient::DoNavDelete(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetNavDelete nav_delete;
+ nav_delete.Unpack(msg->Data());
+
+ Element* elem = sim->FindElement(nav_delete.GetElem());
+ Ship* ship = FindShipByObjID(nav_delete.GetObjID());
+
+ if (elem) {
+ int index = nav_delete.GetIndex();
+
+ if (index < 0) {
+ elem->ClearFlightPlan(false);
+ }
+
+ else if (index < elem->FlightPlanLength()) {
+ Instruction* npt = elem->GetFlightPlan().at(index);
+ elem->DelNavPoint(npt, false);
+ }
+ }
+}
+
+void
+NetGameClient::DoWepDestroy(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetWepDestroy destroy;
+ destroy.Unpack(msg->Data());
+
+ Shot* shot = FindShotByObjID(destroy.GetObjID());
+ if (shot) {
+ if (shot->IsBeam())
+ ::Print("NetGameClient::DoWepDestroy shot '%s'\n", shot->Name());
+
+ shot->Destroy();
+ }
+}
+
+void
+NetGameClient::DoCommMsg(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetCommMsg comm_msg;
+ comm_msg.Unpack(msg->Data());
+
+ NetPlayer* player = FindPlayerByObjID(comm_msg.GetObjID());
+ if (player)
+ player->DoCommMessage(&comm_msg);
+}
+
+void
+NetGameClient::DoChatMsg(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetChatMsg chat_msg;
+ chat_msg.Unpack(msg->Data());
+
+ Text name = chat_msg.GetName();
+ if (name.length() < 1)
+ name = Game::GetText("NetGameClient.chat.unknown");
+
+ HUDView::Message("%s> %s", name.data(), chat_msg.GetText().data());
+}
+
+void
+NetGameClient::DoSelfDestruct(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetSelfDestruct self_destruct;
+ self_destruct.Unpack(msg->Data());
+
+ Ship* ship = FindShipByObjID(self_destruct.GetObjID());
+ if (ship) {
+ ship->InflictNetDamage(self_destruct.GetDamage());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGameClient::Send()
+{
+ DWORD time = Game::GameTime();
+
+ // don't flood the network...
+ if (time - last_send_time < MIN_NET_FRAME)
+ return;
+
+ last_send_time = time;
+
+ if (local_player && objid && server_id) {
+ double r, p, y;
+ local_player->Cam().Orientation().ComputeEulerAngles(r,p,y);
+
+ NetObjLoc obj_loc;
+
+ obj_loc.SetObjID(objid);
+ obj_loc.SetLocation(local_player->Location());
+ obj_loc.SetVelocity(local_player->Velocity());
+ obj_loc.SetOrientation(Point(r,p,y));
+ obj_loc.SetThrottle(local_player->Throttle() > 10);
+ obj_loc.SetAugmenter(local_player->Augmenter());
+ obj_loc.SetGearDown(local_player->IsGearDown());
+
+ Shield* shield = local_player->GetShield();
+ if (shield)
+ obj_loc.SetShield((int) shield->GetPowerLevel());
+ else
+ obj_loc.SetShield(0);
+
+ BYTE* obj_loc_data = obj_loc.Pack();
+
+ link->SendMessage(server_id, obj_loc_data, NetObjLoc::SIZE);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGameClient::SendData(NetData* net_data)
+{
+ if (!net_data || !server_id)
+ return;
+
+ if (local_player || net_data->Type() < 0x20) {
+ BYTE* data = net_data->Pack();
+ BYTE flags = 0;
+
+ if (net_data->Type() >= 0x10)
+ flags |= NetMsg::RELIABLE;
+
+ link->SendMessage(server_id, data, net_data->Length(), flags);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGameClient::ExecFrame()
+{
+ if (local_player) {
+ if (local_player->GetObjID() == 0) {
+ SendJoinRequest();
+ }
+
+ else if (active) {
+ // check health of server:
+ NetPeer* server_peer = link->FindPeer(server_id);
+ if (server_peer && (NetLayer::GetUTC() - server_peer->LastReceiveTime() > 15)) {
+ NetMsg net_disco(0, NET_DISCONNECT, 0, 0, 0);
+ DoDisconnect(&net_disco);
+ }
+
+ // if server is still there,
+ else if (server_peer) {
+
+ // check if any old join announcements still need to be processed:
+ ListIter<NetJoinAnnounce> iter = join_backlog;
+ while (++iter) {
+ NetJoinAnnounce* join_ann = iter.value();
+
+ if (DoJoinBacklog(join_ann)) {
+ iter.removeItem();
+ delete join_ann;
+ }
+ }
+ }
+ }
+ }
+
+ NetGame::ExecFrame();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+NetGameClient::Update(SimObject* obj)
+{
+ if (obj->Type() == SimObject::SIM_SHIP) {
+ Ship* s = (Ship*) obj;
+ if (local_player == s)
+ local_player = 0;
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+NetGameClient::GetObserverName() const
+{
+ return "NetGameClient";
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGameClient::Respawn(DWORD oid, Ship* spawn)
+{
+ if (!oid || !spawn) return;
+
+ Print("NetGameClient::Respawn(%d, %s)\n", oid, spawn->Name());
+ spawn->SetObjID(oid);
+ Observe(spawn);
+
+ NetPlayer* p = FindPlayerByObjID(oid);
+ if (p)
+ p->SetShip(spawn);
+
+ if (objid == oid) {
+ Print(" RESPAWN LOCAL PLAYER\n\n");
+ local_player = spawn;
+ }
+}
diff --git a/Stars45/NetGameClient.h b/Stars45/NetGameClient.h
new file mode 100644
index 0000000..71b8b02
--- /dev/null
+++ b/Stars45/NetGameClient.h
@@ -0,0 +1,87 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetGameClient.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Game Manager class
+*/
+
+#ifndef NetGameClient_h
+#define NetGameClient_h
+
+#include "NetGame.h"
+#include "SimObject.h"
+
+// +--------------------------------------------------------------------+
+
+class NetJoinAnnounce;
+
+// +--------------------------------------------------------------------+
+
+class NetGameClient : public NetGame, public SimObserver
+{
+public:
+ NetGameClient();
+ virtual ~NetGameClient();
+
+ virtual bool IsClient() const { return true; }
+ virtual bool IsServer() const { return false; }
+
+ virtual void ExecFrame();
+ virtual void Send();
+ virtual void SendData(NetData* data);
+ virtual void Respawn(DWORD objid, Ship* spawn);
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+protected:
+ virtual void DoJoinRequest(NetMsg* msg);
+ virtual void DoJoinAnnounce(NetMsg* msg);
+ virtual void DoQuitRequest(NetMsg* msg);
+ virtual void DoQuitAnnounce(NetMsg* msg);
+ virtual void DoGameOver(NetMsg* msg);
+ virtual void DoDisconnect(NetMsg* msg);
+
+ virtual void DoObjLoc(NetMsg* msg);
+ virtual void DoObjDamage(NetMsg* msg);
+ virtual void DoObjKill(NetMsg* msg);
+ virtual void DoObjSpawn(NetMsg* msg);
+ virtual void DoObjHyper(NetMsg* msg);
+ virtual void DoObjTarget(NetMsg* msg);
+ virtual void DoObjEmcon(NetMsg* msg);
+ virtual void DoSysDamage(NetMsg* msg);
+ virtual void DoSysStatus(NetMsg* msg);
+
+ virtual void DoElemCreate(NetMsg* msg);
+ virtual void DoShipLaunch(NetMsg* msg);
+ virtual void DoNavData(NetMsg* msg);
+ virtual void DoNavDelete(NetMsg* msg);
+
+ virtual void DoWepTrigger(NetMsg* msg);
+ virtual void DoWepRelease(NetMsg* msg);
+ virtual void DoWepDestroy(NetMsg* msg);
+
+ virtual void DoCommMsg(NetMsg* msg);
+ virtual void DoChatMsg(NetMsg* msg);
+ virtual void DoSelfDestruct(NetMsg* msg);
+
+ virtual void SendJoinRequest();
+
+ virtual bool DoJoinBacklog(NetJoinAnnounce* join_ann);
+
+ DWORD server_id;
+ DWORD join_req_time;
+ List<NetJoinAnnounce> join_backlog;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif NetGameClient_h
+
diff --git a/Stars45/NetGameServer.cpp b/Stars45/NetGameServer.cpp
new file mode 100644
index 0000000..68b4c95
--- /dev/null
+++ b/Stars45/NetGameServer.cpp
@@ -0,0 +1,1199 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetGameServer.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Server-Side Network Game Manager class
+*/
+
+#include "MemDebug.h"
+#include "NetGameServer.h"
+#include "NetServerConfig.h"
+#include "NetLobbyServer.h"
+#include "NetPlayer.h"
+#include "NetUser.h"
+#include "NetMsg.h"
+#include "NetData.h"
+#include "StarServer.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Shield.h"
+#include "Sim.h"
+#include "SimEvent.h"
+#include "Element.h"
+#include "HUDView.h"
+#include "RadioMessage.h"
+#include "RadioView.h"
+#include "Instruction.h"
+#include "Hangar.h"
+#include "FlightDeck.h"
+#include "Mission.h"
+
+#include "NetLayer.h"
+#include "NetHost.h"
+#include "NetPeer.h"
+#include "NetUtil.h"
+#include "Game.h"
+#include "Light.h"
+
+// +--------------------------------------------------------------------+
+
+const int MAX_NET_FPS = 20;
+const int MIN_NET_FRAME = 1000 / MAX_NET_FPS;
+
+// +--------------------------------------------------------------------+
+
+NetGameServer::NetGameServer()
+{
+ Print("Constructing NetGameServer\n");
+
+ WORD server_port = 11101;
+
+ if (NetServerConfig::GetInstance())
+ server_port = NetServerConfig::GetInstance()->GetGamePort();
+
+ NetAddr server(NetHost().Address().IPAddr(), server_port);
+
+ link = new(__FILE__,__LINE__) NetLink(server);
+
+ ListIter<SimRegion> rgn_iter = sim->GetRegions();
+ while (++rgn_iter) {
+ SimRegion* rgn = rgn_iter.value();
+
+ ListIter<Ship> iter = rgn->Ships();
+ while (++iter) {
+ Ship* s = iter.value();
+ s->SetObjID(GetNextObjID());
+ Observe(s);
+ ships.append(s);
+ }
+ }
+
+ if (local_player) {
+ Observe(local_player);
+ objid = local_player->GetObjID();
+ }
+}
+
+NetGameServer::~NetGameServer()
+{
+ ListIter<NetPlayer> player = players;
+ while (++player) {
+ NetPlayer* p = player.value();
+
+ if (p->GetShip())
+ p->GetShip()->SetRespawnCount(0);
+
+ link->SendMessage(p->GetNetID(), NET_GAME_OVER, 0, 0, NetMsg::RELIABLE);
+ }
+
+ Sleep(500);
+
+ ListIter<Ship> iter = ships;
+ while (++iter) {
+ Ship* s = iter.value();
+ s->SetRespawnCount(0);
+ }
+
+ zombies.destroy();
+ ships.clear();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGameServer::ExecFrame()
+{
+ NetGame::ExecFrame();
+ CheckSessions();
+
+ ListIter<SimRegion> rgn_iter = sim->GetRegions();
+ while (++rgn_iter) {
+ SimRegion* rgn = rgn_iter.value();
+
+ ListIter<Ship> iter = rgn->Ships();
+ while (++iter) {
+ Ship* s = iter.value();
+
+ if (s->GetObjID() == 0) {
+ s->SetObjID(GetNextObjID());
+ Observe(s);
+ ships.append(s);
+
+ NetJoinAnnounce join_ann;
+ join_ann.SetShip(s);
+ join_ann.SetName("Server A.I. Ship");
+ SendData(&join_ann);
+ }
+ }
+ }
+
+
+ static DWORD time_mark = 0;
+
+ if (!time_mark) time_mark = Game::RealTime();
+ else if (Game::RealTime() - time_mark > 60000) {
+ time_mark = Game::RealTime();
+
+ if (link && players.size() > 0) {
+ Print("Server Stats\n-------------\n");
+ Print(" packets sent %d\n", link->GetPacketsSent());
+ Print(" packets recv %d\n", link->GetPacketsRecv());
+ Print(" bytes sent %d\n", link->GetBytesSent());
+ Print(" bytes recv %d\n", link->GetBytesRecv());
+ Print(" retries %d\n", link->GetRetries());
+ Print(" drops %d\n", link->GetDrops());
+ Print(" avg lag %d msec\n", link->GetLag());
+ }
+ }
+}
+
+void
+NetGameServer::CheckSessions()
+{
+ if (!link)
+ return;
+
+ ListIter<NetPlayer> iter = players;
+ while (++iter) {
+ NetPlayer* player = iter.value();
+ NetPeer* peer = link->FindPeer(player->GetNetID());
+
+ if (peer && (NetLayer::GetUTC() - peer->LastReceiveTime()) > NET_DISCONNECT_TIME) {
+ // announce drop:
+ NetPlayer* zombie = iter.removeItem();
+ HUDView::Message(Game::GetText("NetGameServer.remote-discon").data(), zombie->Name());
+
+ // tell everyone else:
+ NetQuitAnnounce quit_ann;
+ quit_ann.SetObjID(zombie->GetObjID());
+ quit_ann.SetDisconnected(true);
+ SendData(&quit_ann);
+
+ // return remote ship to ship pool:
+ Ship* s = zombie->GetShip();
+ if (s) {
+ Observe(s);
+ ships.append(s);
+ s->SetNetworkControl(0);
+ zombie->SetShip(0);
+
+ NetJoinAnnounce join_ann;
+ join_ann.SetShip(s);
+ join_ann.SetName("Server A.I. Ship");
+ SendData(&join_ann);
+ }
+
+ zombies.append(zombie);
+ }
+ }
+}
+
+NetPlayer*
+NetGameServer::FindZombieByObjID(DWORD objid)
+{
+ for (int i = 0; i < zombies.size(); i++) {
+ NetPlayer* p = zombies[i];
+
+ if (p->GetObjID() == objid)
+ return p;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGameServer::DoJoinRequest(NetMsg* msg)
+{
+ if (!msg) return;
+
+ bool unpause = players.isEmpty();
+
+ NetJoinRequest join_req;
+ if (join_req.Unpack(msg->Data())) {
+ HUDView::Message(Game::GetText("NetGameServer::join-request").data(), join_req.GetName(), join_req.GetElement(), join_req.GetIndex());
+
+ DWORD nid = msg->NetID();
+ Text name = join_req.GetName();
+ Text serno = join_req.GetSerialNumber();
+ Text elem_name = join_req.GetElement();
+ int index = join_req.GetIndex();
+ Ship* ship = 0;
+ Sim* sim = Sim::GetSim();
+
+ if (sim) {
+ Element* element = sim->FindElement(elem_name);
+
+ if (element)
+ ship = element->GetShip(index);
+ }
+
+ if (!ship) {
+ Print(" JOIN DENIED: could not locate ship for remote player\n");
+ return;
+ }
+
+ if (!ship->GetObjID()) {
+ Print(" JOIN DENIED: remote player requested ship with objid = 0\n");
+ return;
+ }
+
+ NetLobbyServer* lobby = NetLobbyServer::GetInstance();
+
+ if (lobby) {
+ NetUser* user = lobby->FindUserByName(name);
+
+ if (!user)
+ user = lobby->FindUserByNetID(nid);
+
+ if (!user) {
+ Print(" JOIN DENIED: remote player '%s' not found in lobby\n", name.data());
+ return;
+ }
+
+ else if (!user->IsAuthOK()) {
+ Print(" JOIN DENIED: remote player '%s' not authenticated\n", name.data());
+ return;
+ }
+ }
+
+ NetPlayer* remote_player = FindPlayerByNetID(nid);
+ if (remote_player && remote_player->GetShip() != ship) {
+ Print(" disconnecting remote player from ship '%s'\n", ship->Name());
+ players.remove(remote_player);
+ delete remote_player;
+ remote_player = 0;
+ }
+
+ if (!remote_player) {
+ Ignore(ship);
+ ships.remove(ship);
+
+ remote_player = new(__FILE__,__LINE__) NetPlayer(nid);
+ remote_player->SetName(name);
+ remote_player->SetSerialNumber(serno);
+ remote_player->SetObjID(ship->GetObjID());
+ remote_player->SetShip(ship);
+
+ HUDView::Message(Game::GetText("NetGameServer::join-announce").data());
+ Print("remote player name = %s\n", name.data());
+ Print(" obj = %d\n", ship->GetObjID());
+ Print(" ship = %s\n", ship->Name());
+
+ remote_player->SetObjID(ship->GetObjID());
+
+ // tell the new player about the server:
+ if (local_player) {
+ NetJoinAnnounce join_ann;
+ join_ann.SetShip(local_player);
+ join_ann.SetName(player_name);
+ link->SendMessage(remote_player->GetNetID(), join_ann.Pack(), NetJoinAnnounce::SIZE, NetMsg::RELIABLE);
+ }
+
+ // tell the new player about the existing remote players:
+ ListIter<NetPlayer> iter = players;
+ while (++iter) {
+ Ship* s = iter->GetShip();
+
+ if (s) {
+ NetJoinAnnounce join_ann;
+ join_ann.SetShip(s);
+ join_ann.SetName(iter->Name());
+ join_ann.SetObjID(iter->GetObjID());
+ link->SendMessage(remote_player->GetNetID(), join_ann.Pack(), NetJoinAnnounce::SIZE, NetMsg::RELIABLE);
+ }
+ }
+
+ // tell the new player about the A.I. controlled ships:
+ ListIter<Ship> ai_iter = ships;
+ while (++ai_iter) {
+ Ship* s = ai_iter.value();
+ if (s != local_player) {
+ NetJoinAnnounce join_ann;
+ join_ann.SetShip(s);
+ join_ann.SetName("Server A.I. Ship");
+ link->SendMessage(remote_player->GetNetID(), join_ann.Pack(), NetJoinAnnounce::SIZE, NetMsg::RELIABLE);
+ }
+ }
+
+ // make the new player an "existing" remote player:
+ players.append(remote_player);
+
+ // tell existing players about the new player:
+ // NOTE, this also provides the net id to the new player!
+ iter.reset();
+ while (++iter) {
+ Ship* s = remote_player->GetShip();
+
+ NetJoinAnnounce join_ann;
+ join_ann.SetShip(s);
+ join_ann.SetName(remote_player->Name());
+ join_ann.SetObjID(remote_player->GetObjID());
+ link->SendMessage(iter->GetNetID(), join_ann.Pack(), NetJoinAnnounce::SIZE, NetMsg::RELIABLE);
+ }
+
+ if (unpause) {
+ StarServer* s = StarServer::GetInstance();
+ if (s)
+ s->Pause(false);
+ }
+ }
+ }
+}
+
+void
+NetGameServer::DoJoinAnnounce(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetJoinAnnounce join_ann;
+ if (join_ann.Unpack(msg->Data())) {
+ Print("Server received Join Announce from '%s'\n", join_ann.GetName());
+ }
+}
+
+void
+NetGameServer::DoQuitRequest(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetPlayer* player = FindPlayerByNetID(msg->NetID());
+
+ if (player) {
+ NetPlayer* zombie = players.remove(player);
+ HUDView::Message(Game::GetText("NetGameServer.remote-quit").data(), zombie->Name());
+
+ // tell everyone else:
+ NetQuitAnnounce quit_ann;
+ quit_ann.SetObjID(zombie->GetObjID());
+ SendData(&quit_ann);
+
+ // return remote ship to ship pool:
+ Ship* s = zombie->GetShip();
+ if (s) {
+ Observe(s);
+ ships.append(s);
+ s->SetNetworkControl(0);
+ zombie->SetShip(0);
+
+ NetJoinAnnounce join_ann;
+ join_ann.SetShip(s);
+ join_ann.SetName("Server A.I. Ship");
+ SendData(&join_ann);
+ }
+
+ zombies.append(zombie);
+ }
+ else {
+ Print("Quit Request from unknown player NetID: %08X\n", msg->NetID());
+ }
+}
+
+void
+NetGameServer::DoQuitAnnounce(NetMsg* msg)
+{
+ if (!msg) return;
+ Print("Server received Quit Announce from NetID: %08x\n", msg->NetID());
+}
+
+void
+NetGameServer::DoGameOver(NetMsg* msg)
+{
+ if (!msg) return;
+ Print("Server received Game Over from NetID: %08x\n", msg->NetID());
+}
+
+void
+NetGameServer::DoDisconnect(NetMsg* msg)
+{
+ if (!msg) return;
+ Print("Server received Disconnect from NetID: %08x\n", msg->NetID());
+}
+
+void
+NetGameServer::DoObjLoc(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetObjLoc obj_loc;
+ obj_loc.Unpack(msg->Data());
+
+ NetPlayer* player = FindPlayerByObjID(obj_loc.GetObjID());
+ if (player && player->GetShip()) {
+ player->DoObjLoc(&obj_loc);
+ }
+
+ else {
+ player = FindZombieByObjID(obj_loc.GetObjID());
+
+ if (player)
+ SendDisconnect(player);
+ }
+}
+
+void
+NetGameServer::DoObjDamage(NetMsg* msg)
+{
+ if (!msg) return;
+ Print("Server received OBJ DAMAGE from NetID: %08x (ignored)\n", msg->NetID());
+}
+
+void
+NetGameServer::DoObjKill(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetObjKill obj_kill;
+ obj_kill.Unpack(msg->Data());
+
+ if (obj_kill.GetKillType() != NetObjKill::KILL_DOCK) {
+ Print("Server received OBJ KILL from NetID: %08x (ignored)\n", msg->NetID());
+ return;
+ }
+
+ Ship* ship = FindShipByObjID(obj_kill.GetObjID());
+ if (ship) {
+ Ship* killer = FindShipByObjID(obj_kill.GetKillerID());
+ Text killer_name = Game::GetText("NetGameServer.unknown");
+
+ if (killer)
+ killer_name = killer->Name();
+
+ ShipStats* killee = ShipStats::Find(ship->Name());
+ if (killee)
+ killee->AddEvent(SimEvent::DOCK, killer_name);
+
+ FlightDeck* deck = killer->GetFlightDeck(obj_kill.GetFlightDeck());
+ sim->NetDockShip(ship, killer, deck);
+ }
+}
+
+void
+NetGameServer::DoObjSpawn(NetMsg* msg)
+{
+ if (!msg) return;
+ Print("Server received OBJ SPAWN from NetID: %08x (ignored)\n", msg->NetID());
+}
+
+void
+NetGameServer::DoObjHyper(NetMsg* msg)
+{
+ if (!msg) return;
+ Print("Server received OBJ HYPER from NetID: %d\n", msg->NetID());
+
+ NetObjHyper obj_hyper;
+ obj_hyper.Unpack(msg->Data());
+
+ NetPlayer* player = FindPlayerByObjID(obj_hyper.GetObjID());
+ if (player && player->GetShip()) {
+ if (player->DoObjHyper(&obj_hyper)) {
+ SendData(&obj_hyper);
+ }
+ }
+ else {
+ player = FindZombieByObjID(obj_hyper.GetObjID());
+
+ if (player)
+ SendDisconnect(player);
+ }
+}
+
+void
+NetGameServer::DoObjTarget(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetObjTarget obj_target;
+ obj_target.Unpack(msg->Data());
+
+ NetPlayer* player = FindPlayerByObjID(obj_target.GetObjID());
+ if (player) {
+ player->DoObjTarget(&obj_target);
+ }
+ else {
+ player = FindZombieByObjID(obj_target.GetObjID());
+
+ if (player)
+ SendDisconnect(player);
+ }
+}
+
+void
+NetGameServer::DoObjEmcon(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetObjEmcon obj_emcon;
+ obj_emcon.Unpack(msg->Data());
+
+ NetPlayer* player = FindPlayerByObjID(obj_emcon.GetObjID());
+ if (player) {
+ player->DoObjEmcon(&obj_emcon);
+ }
+ else {
+ player = FindZombieByObjID(obj_emcon.GetObjID());
+
+ if (player)
+ SendDisconnect(player);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGameServer::DoSysDamage(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetSysDamage sys_damage;
+ sys_damage.Unpack(msg->Data());
+
+ NetPlayer* player = FindZombieByObjID(sys_damage.GetObjID());
+
+ if (player)
+ SendDisconnect(player);
+}
+
+void
+NetGameServer::DoSysStatus(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetSysStatus sys_status;
+ sys_status.Unpack(msg->Data());
+
+ Ship* ship = FindShipByObjID(sys_status.GetObjID());
+ NetPlayer* player = FindPlayerByNetID(msg->NetID());
+
+ if (ship) {
+ if (!player || ship->GetObjID() != player->GetObjID()) {
+ /**
+ Print("NetGameServer::DoSysStatus - received request for ship '%s' from wrong player %s\n",
+ ship->Name(), player ? player->Name() : "null");
+ **/
+
+ return;
+ }
+
+ player->DoSysStatus(&sys_status);
+
+ // rebroadcast:
+ System* sys = ship->GetSystem(sys_status.GetSystem());
+ NetUtil::SendSysStatus(ship, sys);
+ }
+
+ else {
+ player = FindZombieByObjID(sys_status.GetObjID());
+
+ if (player)
+ SendDisconnect(player);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGameServer::DoElemRequest(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetElemRequest elem_request;
+ elem_request.Unpack(msg->Data());
+
+ Sim* sim = Sim::GetSim();
+ Element* elem = sim->FindElement(elem_request.GetName());
+
+ if (elem) {
+ int squadron = -1;
+ int slots[] = { -1,-1,-1,-1 };
+ Ship* carrier = elem->GetCarrier();
+
+ if (carrier && carrier->GetHangar()) {
+ Hangar* hangar = carrier->GetHangar();
+
+ for (int i = 0; i < 4; i++) {
+ hangar->FindSquadronAndSlot(elem->GetShip(i+1), squadron, slots[i]);
+ }
+ }
+
+ NetUtil::SendElemCreate(elem, squadron, slots, false, true);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGameServer::DoElemCreate(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetElemCreate elem_create;
+ elem_create.Unpack(msg->Data());
+
+ Sim* sim = Sim::GetSim();
+ Element* elem = sim->CreateElement(elem_create.GetName(),
+ elem_create.GetIFF(),
+ elem_create.GetType());
+
+ int* load = elem_create.GetLoadout();
+ int* slots = elem_create.GetSlots();
+ int squadron = elem_create.GetSquadron();
+ int code = elem_create.GetObjCode();
+ Text target = elem_create.GetObjective();
+ bool alert = elem_create.GetAlert();
+
+ elem->SetIntelLevel(elem_create.GetIntel());
+ elem->SetLoadout(load);
+
+ if (code > Instruction::RTB || target.length() > 0) {
+ Instruction* obj = new(__FILE__,__LINE__) Instruction(code, target);
+ elem->AddObjective(obj);
+ }
+
+ Ship* carrier = sim->FindShip(elem_create.GetCarrier());
+ if (carrier) {
+ elem->SetCarrier(carrier);
+
+ Hangar* hangar = carrier->GetHangar();
+ if (hangar) {
+ Text squadron_name = hangar->SquadronName(squadron);
+ elem->SetSquadron(squadron_name);
+
+ FlightDeck* deck = 0;
+ int queue = 1000;
+
+ for (int i = 0; i < carrier->NumFlightDecks(); i++) {
+ FlightDeck* d = carrier->GetFlightDeck(i);
+
+ if (d && d->IsLaunchDeck()) {
+ int dq = hangar->PreflightQueue(d);
+
+ if (dq < queue) {
+ queue = dq;
+ deck = d;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ int slot = slots[i];
+ if (slot > -1) {
+ hangar->GotoAlert(squadron, slot, deck, elem, load, !alert);
+ }
+ }
+ }
+ }
+
+ NetUtil::SendElemCreate(elem,
+ elem_create.GetSquadron(),
+ elem_create.GetSlots(),
+ elem_create.GetAlert());
+}
+
+void
+NetGameServer::DoShipLaunch(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetShipLaunch ship_launch;
+ ship_launch.Unpack(msg->Data());
+
+ Sim* sim = Sim::GetSim();
+ int squadron = ship_launch.GetSquadron();
+ int slot = ship_launch.GetSlot();
+
+ NetPlayer* player = FindPlayerByObjID(ship_launch.GetObjID());
+ if (player) {
+ Ship* carrier = player->GetShip();
+
+ if (carrier) {
+ Hangar* hangar = carrier->GetHangar();
+
+ if (hangar) {
+ hangar->Launch(squadron, slot);
+ }
+
+ NetUtil::SendShipLaunch(carrier, squadron, slot);
+ }
+ }
+}
+
+void
+NetGameServer::DoNavData(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetNavData nav_data;
+ nav_data.Unpack(msg->Data());
+
+ Element* elem = sim->FindElement(nav_data.GetElem());
+
+ if (elem) {
+ if (nav_data.IsAdd()) {
+ Instruction* navpt = new(__FILE__,__LINE__) Instruction(*nav_data.GetNavPoint());
+ Instruction* after = 0;
+ int index = nav_data.GetIndex();
+
+ if (index >= 0 && index < elem->GetFlightPlan().size())
+ after = elem->GetFlightPlan().at(index);
+
+ elem->AddNavPoint(navpt, after, false);
+ }
+
+ else {
+ Instruction* navpt = nav_data.GetNavPoint();
+ Instruction* exist = 0;
+ int index = nav_data.GetIndex();
+
+ if (navpt && index >= 0 && index < elem->GetFlightPlan().size()) {
+ exist = elem->GetFlightPlan().at(index);
+ *exist = *navpt;
+ }
+ }
+
+ SendData(&nav_data);
+ }
+}
+
+void
+NetGameServer::DoNavDelete(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetNavDelete nav_delete;
+ nav_delete.Unpack(msg->Data());
+
+ Element* elem = sim->FindElement(nav_delete.GetElem());
+
+ if (elem) {
+ int index = nav_delete.GetIndex();
+
+ if (index < 0) {
+ elem->ClearFlightPlan(false);
+ }
+
+ else if (index < elem->FlightPlanLength()) {
+ Instruction* npt = elem->GetFlightPlan().at(index);
+ elem->DelNavPoint(npt, false);
+ }
+
+ SendData(&nav_delete);
+ }
+}
+
+void
+NetGameServer::DoWepTrigger(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetWepTrigger trigger;
+ trigger.Unpack(msg->Data());
+
+ NetPlayer* player = FindPlayerByObjID(trigger.GetObjID());
+ if (player) {
+ player->DoWepTrigger(&trigger);
+ }
+ else {
+ player = FindZombieByObjID(trigger.GetObjID());
+
+ if (player)
+ SendDisconnect(player);
+ }
+}
+
+void
+NetGameServer::DoWepRelease(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetWepRelease release;
+ release.Unpack(msg->Data());
+
+ NetPlayer* player = FindPlayerByObjID(release.GetObjID());
+ if (player) {
+ player->DoWepRelease(&release);
+ }
+ else {
+ player = FindZombieByObjID(release.GetObjID());
+
+ if (player)
+ SendDisconnect(player);
+ }
+
+ Print("WEP RELEASE on server? objid = %d\n", release.GetObjID());
+}
+
+void
+NetGameServer::DoWepDestroy(NetMsg* msg)
+{
+}
+
+void
+NetGameServer::DoCommMsg(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetCommMsg comm_msg;
+ comm_msg.Unpack(msg->Data());
+
+ RadioMessage* radio_msg = comm_msg.GetRadioMessage();
+
+ NetPlayer* player = FindPlayerByObjID(comm_msg.GetObjID());
+ if (player && radio_msg) {
+ player->DoCommMessage(&comm_msg);
+
+ int channel = comm_msg.GetRadioMessage()->Channel();
+
+ ListIter<NetPlayer> dst = players;
+ while (++dst) {
+ NetPlayer* remote_player = dst.value();
+ if (remote_player->GetNetID() &&
+ (channel == 0 || channel == remote_player->GetIFF())) {
+
+ BYTE* data = comm_msg.Pack();
+ int size = comm_msg.Length();
+
+ link->SendMessage(remote_player->GetNetID(), data, size, NetMsg::RELIABLE);
+ }
+ }
+ }
+ else {
+ player = FindZombieByObjID(comm_msg.GetObjID());
+
+ if (player)
+ SendDisconnect(player);
+ }
+}
+
+void
+NetGameServer::DoChatMsg(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetChatMsg chat_msg;
+ chat_msg.Unpack(msg->Data());
+
+ RouteChatMsg(chat_msg);
+}
+
+void
+NetGameServer::RouteChatMsg(NetChatMsg& chat_msg)
+{
+ DWORD dst_id = chat_msg.GetDstID();
+
+ // broadcast or team:
+ if (dst_id == 0xffff || dst_id <= 10) {
+ BYTE* data = chat_msg.Pack();
+ int size = chat_msg.Length();
+
+ ListIter<NetPlayer> dst = players;
+ while (++dst) {
+ NetPlayer* remote_player = dst.value();
+ if (remote_player->GetNetID() && chat_msg.GetName() != remote_player->Name()) {
+ if (dst_id == 0xffff || dst_id == 0 || remote_player->GetIFF() == (int) dst_id-1)
+ link->SendMessage(remote_player->GetNetID(), data, size);
+ }
+ }
+
+ if (local_player && (dst_id == 0xffff || dst_id == 0 || local_player->GetIFF() == (int) dst_id-1)) {
+ Text name = chat_msg.GetName();
+ if (name.length() < 1)
+ name = Game::GetText("NetGameServer.chat.unknown");
+
+ // don't echo general messages from the local player.
+ // they are already displayed by the chat entry code
+ // in starshatter.cpp
+
+ if (name != player_name)
+ HUDView::Message("%s> %s", name.data(), chat_msg.GetText().data());
+ }
+ }
+
+ // direct to local player:
+ else if (local_player && local_player->GetObjID() == dst_id) {
+ Text name = chat_msg.GetName();
+ if (name.length() < 1)
+ name = Game::GetText("NetGameServer.chat.unknown");
+
+ HUDView::Message("%s> %s", name.data(), chat_msg.GetText().data());
+ }
+
+ // ship-to-ship, but not to local player:
+ else if (!local_player || local_player->GetObjID() != dst_id) {
+ NetPlayer* remote_player = FindPlayerByObjID(dst_id);
+ if (remote_player && remote_player->GetNetID()) {
+ BYTE* data = chat_msg.Pack();
+ int size = chat_msg.Length();
+ link->SendMessage(remote_player->GetNetID(), data, size);
+ }
+
+ // record message in server log:
+ ::Print("%s> %s\n", chat_msg.GetName().data(), chat_msg.GetText().data());
+ }
+
+ if (dst_id == 0xffff)
+ return;
+
+ // record message in chat log:
+ NetLobbyServer* lobby = NetLobbyServer::GetInstance();
+
+ if (!lobby)
+ return;
+
+ NetUser* user = lobby->FindUserByName(chat_msg.GetName());
+
+ if (user)
+ lobby->AddChat(user, chat_msg.GetText(), false); // don't re-route
+}
+
+// +--------------------------------------------------------------------+
+
+const char* FormatGameTime();
+
+void
+NetGameServer::DoSelfDestruct(NetMsg* msg)
+{
+ if (!msg) return;
+
+ NetSelfDestruct self_destruct;
+ self_destruct.Unpack(msg->Data());
+
+ Ship* ship = FindShipByObjID(self_destruct.GetObjID());
+ NetPlayer* player = FindPlayerByNetID(msg->NetID());
+
+ if (ship) {
+ if (!player || ship->GetObjID() != player->GetObjID()) {
+ Print("NetGameServer::DoSelfDestruct - received request for ship '%s' from wrong player %s\n",
+ ship->Name(), player ? player->Name() : "null");
+
+ return;
+ }
+
+ ship->InflictNetDamage(self_destruct.GetDamage());
+
+ SendData(&self_destruct);
+
+ int ship_destroyed = (!ship->InTransition() && ship->Integrity() < 1.0f);
+
+ // then delete the ship:
+ if (ship_destroyed) {
+ NetUtil::SendObjKill(ship, 0, NetObjKill::KILL_MISC);
+ Print(" %s Self Destruct (%s)\n", ship->Name(), FormatGameTime());
+
+ ShipStats* killee = ShipStats::Find(ship->Name());
+ if (killee)
+ killee->AddEvent(SimEvent::DESTROYED, ship->Name());
+
+ ship->DeathSpiral();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGameServer::Send()
+{
+ if (players.isEmpty())
+ return;
+
+ DWORD time = Game::GameTime();
+
+ // don't flood the network...
+ if (time - last_send_time < MIN_NET_FRAME)
+ return;
+
+ last_send_time = time;
+
+ // tell the remote players where *we* are:
+ if (local_player && !local_player->IsNetObserver() && objid) {
+ double r, p, y;
+ local_player->Cam().Orientation().ComputeEulerAngles(r,p,y);
+
+ NetObjLoc obj_loc;
+ obj_loc.SetObjID(objid);
+ obj_loc.SetLocation(local_player->Location());
+ obj_loc.SetVelocity(local_player->Velocity());
+ obj_loc.SetOrientation(Point(r,p,y));
+ obj_loc.SetThrottle(local_player->Throttle() > 10);
+ obj_loc.SetAugmenter(local_player->Augmenter());
+ obj_loc.SetGearDown(local_player->IsGearDown());
+
+ Shield* shield = local_player->GetShield();
+ if (shield)
+ obj_loc.SetShield((int) shield->GetPowerLevel());
+ else
+ obj_loc.SetShield(0);
+
+ SendData(&obj_loc);
+ }
+
+ // tell each remote player where all the others are:
+ ListIter<NetPlayer> src = players;
+ while (++src) {
+ NetPlayer* player = src.value();
+
+ Ship* player_ship = player->GetShip();
+
+ if (player_ship) {
+ double r, p, y;
+ player_ship->Cam().Orientation().ComputeEulerAngles(r,p,y);
+
+ NetObjLoc obj_loc;
+ obj_loc.SetObjID(player->GetObjID());
+ obj_loc.SetLocation(player_ship->Location());
+ obj_loc.SetVelocity(player_ship->Velocity());
+ obj_loc.SetOrientation(Point(r,p,y));
+ obj_loc.SetThrottle(player_ship->Throttle() > 10);
+ obj_loc.SetAugmenter(player_ship->Augmenter());
+ obj_loc.SetGearDown(player_ship->IsGearDown());
+
+ Shield* shield = player_ship->GetShield();
+ if (shield)
+ obj_loc.SetShield((int) shield->GetPowerLevel());
+ else
+ obj_loc.SetShield(0);
+
+ BYTE* obj_loc_data = obj_loc.Pack();
+
+ ListIter<NetPlayer> dst = players;
+ while (++dst) {
+ NetPlayer* remote_player = dst.value();
+ if (remote_player->GetNetID() && remote_player != player)
+ link->SendMessage(remote_player->GetNetID(), obj_loc_data, NetObjLoc::SIZE);
+ }
+ }
+ }
+
+ // tell each remote player where all the A.I. ships are:
+ ListIter<Ship> ai_iter = ships;
+ while (++ai_iter) {
+ Ship* s = ai_iter.value();
+
+ if (s && !s->IsStatic()) {
+ double r, p, y;
+ s->Cam().Orientation().ComputeEulerAngles(r,p,y);
+
+ NetObjLoc obj_loc;
+ obj_loc.SetObjID(s->GetObjID());
+ obj_loc.SetLocation(s->Location());
+ obj_loc.SetVelocity(s->Velocity());
+ obj_loc.SetOrientation(Point(r,p,y));
+ obj_loc.SetThrottle(s->Throttle() > 10);
+ obj_loc.SetAugmenter(s->Augmenter());
+ obj_loc.SetGearDown(s->IsGearDown());
+
+ Shield* shield = s->GetShield();
+ if (shield)
+ obj_loc.SetShield((int) shield->GetPowerLevel());
+ else
+ obj_loc.SetShield(0);
+
+ SendData(&obj_loc);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGameServer::SendData(NetData* net_data)
+{
+ if (net_data) {
+ BYTE* data = net_data->Pack();
+ int size = net_data->Length();
+ BYTE flags = 0;
+ bool all = true; // include player with objid in net_data?
+ DWORD oid = net_data->GetObjID();
+
+ BYTE msg_type = net_data->Type();
+
+ if (msg_type >= 0x10)
+ flags |= NetMsg::RELIABLE;
+
+ if (msg_type == NET_WEP_TRIGGER ||
+ msg_type == NET_COMM_MESSAGE ||
+ msg_type == NET_CHAT_MESSAGE ||
+ msg_type == NET_OBJ_HYPER ||
+ msg_type == NET_ELEM_CREATE ||
+ msg_type == NET_NAV_DATA ||
+ msg_type == NET_NAV_DELETE) {
+ all = false;
+ }
+
+ ListIter<NetPlayer> dst = players;
+ while (++dst) {
+ NetPlayer* remote_player = dst.value();
+ if (remote_player->GetNetID() && (all || oid != remote_player->GetObjID()))
+ link->SendMessage(remote_player->GetNetID(), data, size, flags);
+ }
+ }
+}
+
+void
+NetGameServer::SendDisconnect(NetPlayer* zombie)
+{
+ if (zombie) {
+ NetDisconnect disconnect;
+ BYTE* data = disconnect.Pack();
+ int size = disconnect.Length();
+ BYTE flags = NetMsg::RELIABLE;
+
+ if (zombie->GetNetID())
+ link->SendMessage(zombie->GetNetID(), data, size, flags);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+NetGameServer::Update(SimObject* obj)
+{
+ if (obj->Type() == SimObject::SIM_SHIP) {
+ Ship* s = (Ship*) obj;
+ if (local_player == s)
+ local_player = 0;
+
+ if (ships.contains(s))
+ ships.remove(s);
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+NetGameServer::GetObserverName() const
+{
+ return "NetGameServer";
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetGameServer::Respawn(DWORD oid, Ship* spawn)
+{
+ if (!oid || !spawn) return;
+
+ Print("NetGameServer::Respawn(%d, %s)\n", oid, spawn->Name());
+ spawn->SetObjID(oid);
+ Observe(spawn);
+
+ NetPlayer* p = FindPlayerByObjID(oid);
+ if (p)
+ p->SetShip(spawn);
+ else
+ ships.append(spawn);
+
+ if (objid == oid) {
+ Print(" RESPAWN LOCAL PLAYER\n\n");
+ local_player = spawn;
+ }
+}
diff --git a/Stars45/NetGameServer.h b/Stars45/NetGameServer.h
new file mode 100644
index 0000000..545aca2
--- /dev/null
+++ b/Stars45/NetGameServer.h
@@ -0,0 +1,90 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetGameServer.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Server-Side Network Game Manager class
+*/
+
+#ifndef NetGameServer_h
+#define NetGameServer_h
+
+#include "NetGame.h"
+#include "SimObject.h"
+
+// +--------------------------------------------------------------------+
+
+class NetChatMsg;
+
+// +--------------------------------------------------------------------+
+
+class NetGameServer : public NetGame, public SimObserver
+{
+public:
+ NetGameServer();
+ virtual ~NetGameServer();
+
+ virtual bool IsClient() const { return false; }
+ virtual bool IsServer() const { return true; }
+
+ virtual void ExecFrame();
+ virtual void CheckSessions();
+
+ virtual void Send();
+ virtual void SendData(NetData* data);
+ virtual void Respawn(DWORD objid, Ship* spawn);
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+ virtual void RouteChatMsg(NetChatMsg& chat_msg);
+
+protected:
+ virtual void DoJoinRequest(NetMsg* msg);
+ virtual void DoJoinAnnounce(NetMsg* msg);
+ virtual void DoQuitRequest(NetMsg* msg);
+ virtual void DoQuitAnnounce(NetMsg* msg);
+ virtual void DoGameOver(NetMsg* msg);
+ virtual void DoDisconnect(NetMsg* msg);
+
+ virtual void DoObjLoc(NetMsg* msg);
+ virtual void DoObjDamage(NetMsg* msg);
+ virtual void DoObjKill(NetMsg* msg);
+ virtual void DoObjSpawn(NetMsg* msg);
+ virtual void DoObjHyper(NetMsg* msg);
+ virtual void DoObjTarget(NetMsg* msg);
+ virtual void DoObjEmcon(NetMsg* msg);
+ virtual void DoSysDamage(NetMsg* msg);
+ virtual void DoSysStatus(NetMsg* msg);
+
+ virtual void DoElemRequest(NetMsg* msg);
+ virtual void DoElemCreate(NetMsg* msg);
+ virtual void DoShipLaunch(NetMsg* msg);
+ virtual void DoNavData(NetMsg* msg);
+ virtual void DoNavDelete(NetMsg* msg);
+
+ virtual void DoWepTrigger(NetMsg* msg);
+ virtual void DoWepRelease(NetMsg* msg);
+ virtual void DoWepDestroy(NetMsg* msg);
+
+ virtual void DoCommMsg(NetMsg* msg);
+ virtual void DoChatMsg(NetMsg* msg);
+ virtual void DoSelfDestruct(NetMsg* msg);
+
+ virtual NetPlayer* FindZombieByObjID(DWORD objid);
+ virtual void SendDisconnect(NetPlayer* zombie);
+
+ List<Ship> ships;
+ List<NetPlayer> zombies;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif NetGameServer_h
+
diff --git a/Stars45/NetLobby.cpp b/Stars45/NetLobby.cpp
new file mode 100644
index 0000000..6258f77
--- /dev/null
+++ b/Stars45/NetLobby.cpp
@@ -0,0 +1,630 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetLobby.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Base Class for Multiplayer Game Lobby classes
+*/
+
+#include "MemDebug.h"
+#include "NetLobby.h"
+#include "NetLobbyClient.h"
+#include "NetLobbyServer.h"
+#include "NetClientConfig.h"
+#include "NetServerConfig.h"
+#include "NetChat.h"
+#include "NetUser.h"
+
+#include "NetMsg.h"
+#include "NetData.h"
+#include "NetLayer.h"
+
+#include "Player.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Campaign.h"
+#include "Element.h"
+#include "Mission.h"
+
+#include "NetHost.h"
+#include "Game.h"
+#include "Light.h"
+#include "parseutil.h"
+
+// +--------------------------------------------------------------------+
+
+const int MAX_NET_FPS = 50;
+const int MIN_NET_FRAME = 1000 / MAX_NET_FPS;
+
+const DWORD SHIP_ID_START = 0x0010;
+const DWORD SHOT_ID_START = 0x0400;
+
+static NetLobby* instance = 0;
+
+static DWORD ship_id_key = SHIP_ID_START;
+static DWORD shot_id_key = SHOT_ID_START;
+
+// +--------------------------------------------------------------------+
+
+NetLobby::NetLobby(bool temporary)
+ : link(0), local_user(0), last_send_time(0), active(true),
+ selected_mission(0), mission(0), status(0)
+{
+ if (!temporary)
+ instance = this;
+}
+
+NetLobby::~NetLobby()
+{
+ if (instance == this)
+ instance = 0;
+
+ unit_map.destroy();
+ users.destroy();
+ campaigns.destroy();
+ chat_log.destroy();
+
+ if (local_user) {
+ delete local_user;
+ local_user = 0;
+ }
+
+ if (link) {
+ delete link;
+ link = 0;
+ }
+
+ mission = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+NetLobby*
+NetLobby::GetInstance()
+{
+ return instance;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobby::ExecFrame()
+{
+ Send();
+ Recv();
+}
+
+void
+NetLobby::Recv()
+{
+ NetMsg* msg = link->GetMessage();
+
+ while (msg) {
+ if (active) {
+ NetPeer* peer = link->FindPeer(msg->NetID());
+
+ static char buffer[256];
+ char* text = 0;
+
+ if (msg->Length() > 2) {
+ if (msg->Length() < 250) {
+ ZeroMemory(buffer, sizeof(buffer));
+ CopyMemory(buffer, msg->Data()+2, msg->Length()-2);
+ text = buffer;
+ }
+ else if (msg->Length() < 1024 * 1024) {
+ text = new(__FILE__,__LINE__) char[msg->Length()];
+ ZeroMemory(text, msg->Length());
+ CopyMemory(text, msg->Data()+2, msg->Length()-2);
+ }
+ }
+
+ switch (msg->Type()) {
+ case NET_LOBBY_PING: DoPing(peer, text); break;
+ case NET_LOBBY_SERVER_INFO: DoServerInfo(peer, text); break;
+ case NET_LOBBY_SERVER_MODS: DoServerMods(peer, text); break;
+ case NET_LOBBY_LOGIN: DoLogin(peer, text); break;
+ case NET_LOBBY_LOGOUT: DoLogout(peer, text); break;
+
+ case NET_LOBBY_AUTH_USER: DoAuthUser(peer, text); break;
+ case NET_LOBBY_USER_AUTH: DoUserAuth(peer, text); break;
+
+ case NET_LOBBY_CHAT: DoChat(peer, text); break;
+ case NET_LOBBY_USER_LIST: DoUserList(peer, text); break;
+ case NET_LOBBY_BAN_USER: DoBanUser(peer, text); break;
+ case NET_LOBBY_MISSION_LIST: DoMissionList(peer, text); break;
+ case NET_LOBBY_MISSION_SELECT: DoMissionSelect(peer, text); break;
+ case NET_LOBBY_MISSION_DATA: DoMissionData(peer, text); break;
+ case NET_LOBBY_UNIT_LIST: DoUnitList(peer, text); break;
+ case NET_LOBBY_MAP_UNIT: DoMapUnit(peer, text); break;
+ case NET_LOBBY_GAME_START: DoGameStart(peer, text); break;
+ case NET_LOBBY_GAME_STOP: DoGameStop(peer, text); break;
+ case NET_LOBBY_EXIT: DoExit(peer, text); break;
+ }
+
+ if (text && text != buffer)
+ delete [] text;
+ }
+
+ delete msg;
+ msg = link->GetMessage();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobby::Send()
+{
+}
+
+// +-------------------------------------------------------------------+
+
+DWORD
+NetLobby::GetLag()
+{
+ if (link)
+ return link->GetLag();
+
+ return 0;
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetLobby::SetLocalUser(NetUser* user)
+{
+ if (user != local_user) {
+ if (local_user) {
+ delete local_user;
+ local_user = 0;
+ }
+
+ if (user) {
+ local_user = user;
+ local_user->SetHost(true);
+ }
+ }
+}
+
+void
+NetLobby::BanUser(NetUser* user)
+{
+}
+
+void
+NetLobby::AddUser(NetUser* user)
+{
+ if (user) {
+ if (user != local_user && !users.contains(user)) {
+ users.append(user);
+ Print("NetLobby User Logged In - name: '%s' id: %d host: %d\n",
+ user->Name().data(),
+ user->GetNetID(),
+ user->IsHost());
+ }
+ }
+}
+
+void
+NetLobby::DelUser(NetUser* user)
+{
+ if (user) {
+ if (user == local_user)
+ local_user = 0;
+
+ else
+ users.remove(user);
+
+ UnmapUnit(user->Name());
+
+ Print("NetLobby User Logged Out - name: '%s' id: %d host: %d\n",
+ user->Name().data(),
+ user->GetNetID(),
+ user->IsHost());
+
+ user->SetHost(false);
+ delete user;
+ }
+}
+
+int
+NetLobby::NumUsers()
+{
+ int num = 0;
+
+ if (local_user)
+ num = 1;
+
+ num += users.size();
+
+ return num;
+}
+
+NetUser*
+NetLobby::GetHost()
+{
+ for (int i = 0; i < users.size(); i++) {
+ NetUser* u = users[i];
+
+ if (u->IsHost())
+ return u;
+ }
+
+ return 0;
+}
+
+bool
+NetLobby::HasHost()
+{
+ bool host = false;
+
+ if (local_user && local_user->IsHost())
+ host = true;
+
+ for (int i = 0; i < users.size() && !host; i++) {
+ if (users[i]->IsHost())
+ host = true;
+ }
+
+ if (status > NetServerInfo::LOBBY)
+ host = true;
+
+ return host;
+}
+
+bool
+NetLobby::SetUserHost(NetUser* user, bool host)
+{
+ bool ok = false;
+
+ if (user && users.contains(user)) {
+ if (host && !HasHost()) {
+ user->SetHost(true);
+ ok = true;
+ }
+ else if (!host) {
+ user->SetHost(false);
+ ok = true;
+ }
+ }
+
+ return ok;
+}
+
+NetUser*
+NetLobby::GetLocalUser()
+{
+ return local_user;
+}
+
+List<NetUser>&
+NetLobby::GetUsers()
+{
+ return users;
+}
+
+List<ModInfo>&
+NetLobby::GetServerMods()
+{
+ return server_mods;
+}
+
+NetUser*
+NetLobby::FindUserByAddr(const NetAddr& addr)
+{
+ for (int i = 0; i < users.size(); i++) {
+ NetUser* u = users[i];
+ if (u->GetAddress().IPAddr() == addr.IPAddr())
+ return u;
+ }
+
+ return 0;
+}
+
+NetUser*
+NetLobby::FindUserByName(const char* name)
+{
+ if (local_user && !stricmp(local_user->Name(), name))
+ return local_user;
+
+ for (int i = 0; i < users.size(); i++) {
+ NetUser* u = users[i];
+ if (!stricmp(u->Name(), name))
+ return u;
+ }
+
+ return 0;
+}
+
+NetUser*
+NetLobby::FindUserByNetID(DWORD id)
+{
+ for (int i = 0; i < users.size(); i++) {
+ NetUser* u = users[i];
+ if (u->GetNetID() == id)
+ return u;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobby::ParseMsg(Text msg, List<NetLobbyParam>& params)
+{
+ params.destroy();
+
+ BlockReader reader(msg.data(), msg.length());
+ Scanner lexer(&reader);
+
+ Token name = lexer.Get();
+
+ while (name.type() == Token::AlphaIdent) {
+ Token value = lexer.Get();
+ if (value.type() != Token::EOT) {
+ Text val = value.symbol();
+
+ if (val[0] == '"' && val[val.length()-1] == '"') {
+ val = val.substring(1, val.length()-2);
+ }
+
+ NetLobbyParam* param = new(__FILE__,__LINE__) NetLobbyParam(name.symbol(), val);
+ params.append(param);
+
+ name = lexer.Get();
+ }
+
+ else {
+ name = Token(Token::EOT);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+NetLobby::Ping()
+{
+ Sleep(500);
+ return false;
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetLobby::AddChat(NetUser* user, const char* msg, bool route)
+{
+ if (user && msg && *msg)
+ chat_log.append(new(__FILE__,__LINE__) NetChatEntry(user, msg));
+}
+
+List<NetChatEntry>&
+NetLobby::GetChat()
+{
+ return chat_log;
+}
+
+void
+NetLobby::ClearChat()
+{
+ chat_log.destroy();
+}
+
+// +-------------------------------------------------------------------+
+
+List<NetCampaignInfo>&
+NetLobby::GetCampaigns()
+{
+ return campaigns;
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetLobby::AddUnitMap(MissionElement* elem, int index)
+{
+ if (elem)
+ unit_map.append(new(__FILE__,__LINE__) NetUnitEntry(elem, index));
+}
+
+List<NetUnitEntry>&
+NetLobby::GetUnitMap()
+{
+ return unit_map;
+}
+
+void
+NetLobby::ClearUnitMap()
+{
+ unit_map.destroy();
+}
+
+void
+NetLobby::MapUnit(int n, const char* user, bool lock)
+{
+ if (n >= 0 && n < unit_map.size()) {
+ NetUnitEntry* unit = unit_map[n];
+
+ if (!lock && unit->GetLocked())
+ return;
+
+ if (user && *user) {
+ for (int i = 0; i < unit_map.size(); i++) {
+ NetUnitEntry* u = unit_map[i];
+ if (u->GetUserName() == user) {
+ if (!lock && u->GetLocked()) return;
+ u->SetUserName("");
+ }
+ }
+ }
+
+ unit->SetUserName(user);
+ unit->SetLock(lock);
+ }
+}
+
+void
+NetLobby::UnmapUnit(const char* user)
+{
+ if (user && *user) {
+ for (int i = 0; i < unit_map.size(); i++) {
+ NetUnitEntry* u = unit_map[i];
+ if (u->GetUserName() == user) {
+ u->SetUserName("");
+ u->SetLock(false);
+ return;
+ }
+ }
+ }
+}
+
+bool
+NetLobby::IsMapped(const char* user)
+{
+ if (user && *user) {
+ for (int i = 0; i < unit_map.size(); i++) {
+ NetUnitEntry* u = unit_map[i];
+ if (u->GetUserName() == user) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobby::SelectMission(DWORD id)
+{
+ if (selected_mission != id) {
+ selected_mission = id;
+ ClearUnitMap();
+
+ int mission_id = selected_mission;
+ int campaign_id = -1;
+ Campaign* campaign = 0;
+ mission = 0;
+
+ campaign_id = mission_id >> NET_CAMPAIGN_SHIFT;
+ mission_id = mission_id & NET_MISSION_MASK;
+
+ ListIter<Campaign> c_iter = Campaign::GetAllCampaigns();
+ while (++c_iter) {
+ campaign = c_iter.value();
+
+ if (campaign->GetCampaignId() == campaign_id) {
+ mission = campaign->GetMission(mission_id);
+ break;
+ }
+ }
+
+ if (campaign && mission) {
+ Campaign::SelectCampaign(campaign->Name());
+ campaign->SetMissionId(mission_id);
+
+ ListIter<MissionElement> iter = mission->GetElements();
+ while (++iter) {
+ MissionElement* elem = iter.value();
+
+ if (elem->IsPlayable()) {
+ if (elem->Count() == 1) {
+ AddUnitMap(elem);
+ }
+ else {
+ for (int i = 0; i < elem->Count(); i++)
+ AddUnitMap(elem, i+1);
+ }
+ }
+ }
+ }
+
+ else {
+ selected_mission = 0;
+ mission = 0;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+NetLobby::IsNetLobbyClient()
+{
+ if (instance)
+ return instance->IsClient();
+ return false;
+}
+
+bool
+NetLobby::IsNetLobbyServer()
+{
+ if (instance)
+ return instance->IsServer();
+ return false;
+}
+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+// +-------------------------------------------------------------------+
+
+NetUnitEntry::NetUnitEntry(MissionElement* e, int n)
+ : index(n), lock(false), lives(1), hull(100), role(0)
+{
+ if (e) {
+ elem = e->Name();
+ iff = e->GetIFF();
+
+ if (e->GetDesign())
+ design = e->GetDesign()->name;
+ }
+}
+
+NetUnitEntry::NetUnitEntry(const char* e, const char* d, int i, int n)
+ : elem(e), design(d), iff(i), index(n), lock(false), lives(1),
+ hull(100), role(0)
+{ }
+
+NetUnitEntry::~NetUnitEntry()
+{ }
+
+Text
+NetUnitEntry::GetDescription() const
+{
+ if (elem) {
+ static char buffer[1024];
+
+ sprintf(buffer, "name \"%s\" index %d design \"%s\" iff %d user \"%s\" lives %d hull %d role %d lock %d ",
+ elem.data(),
+ index,
+ design.data(),
+ iff,
+ user.data(),
+ lives,
+ hull,
+ role,
+ lock);
+
+ return buffer;
+ }
+
+ return "name \"Not Found\" ";
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+NetServerInfo::NetServerInfo()
+ : nplayers(0), hosted(0), status(0), port(0), gameport(0), save(false), ping_time(0)
+{ }
+
diff --git a/Stars45/NetLobby.h b/Stars45/NetLobby.h
new file mode 100644
index 0000000..474ebff
--- /dev/null
+++ b/Stars45/NetLobby.h
@@ -0,0 +1,289 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetLobby.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Base Class for Multiplayer Game Lobby classes
+*/
+
+#ifndef NetLobby_h
+#define NetLobby_h
+
+#include "Types.h"
+#include "NetLink.h"
+#include "List.h"
+
+#define NET_CAMPAIGN_SHIFT 12
+#define NET_MISSION_MASK 0xfff
+#define NET_DISCONNECT_TIME 30
+
+// +-------------------------------------------------------------------+
+
+class Campaign;
+class Mission;
+class MissionElement;
+class MissionInfo;
+class NetCampaignInfo;
+class NetChatEntry;
+class NetUser;
+class NetUnitEntry;
+class NetLobbyParam;
+class ModInfo;
+
+// +--------------------------------------------------------------------+
+
+class NetLobby
+{
+public:
+ static const char* TYPENAME() { return "NetLobby"; }
+
+ NetLobby(bool temporary = false);
+ virtual ~NetLobby();
+
+ virtual bool IsClient() const { return false; }
+ virtual bool IsServer() const { return false; }
+ virtual bool IsActive() const { return active; }
+
+ virtual void ExecFrame();
+ virtual void Recv();
+ virtual void Send();
+ virtual int GetLastError() const { return 0; }
+
+ virtual NetUser* FindUserByAddr(const NetAddr& addr);
+ virtual NetUser* FindUserByName(const char* name);
+ virtual NetUser* FindUserByNetID(DWORD id);
+
+ virtual void BanUser(NetUser* user);
+ virtual void AddUser(NetUser* user);
+ virtual void DelUser(NetUser* user);
+ virtual bool SetUserHost(NetUser* user, bool host);
+ virtual int NumUsers();
+ virtual NetUser* GetHost();
+ virtual bool HasHost();
+ virtual bool IsHost() const { return false; }
+ virtual List<NetUser>& GetUsers();
+
+ virtual List<ModInfo>& GetServerMods();
+
+ virtual NetUser* GetLocalUser();
+ virtual void SetLocalUser(NetUser* user);
+
+ virtual int GetStatus() const { return status; }
+ virtual void SetStatus(int s) { status = s; }
+
+ virtual void AddChat(NetUser* user, const char* msg, bool route=true);
+ virtual List<NetChatEntry>&
+ GetChat();
+ virtual void ClearChat();
+ virtual void SaveChat() { }
+ virtual DWORD GetStartTime() const { return start_time; }
+
+ virtual List<NetCampaignInfo>&
+ GetCampaigns();
+
+ virtual void AddUnitMap(MissionElement* elem, int index=0);
+ virtual List<NetUnitEntry>&
+ GetUnitMap();
+ virtual void ClearUnitMap();
+ virtual void MapUnit(int n, const char* user, bool lock=false);
+ virtual void UnmapUnit(const char* user);
+ virtual bool IsMapped(const char* user);
+
+ virtual Mission* GetSelectedMission() { return mission; }
+ virtual DWORD GetSelectedMissionID() const { return selected_mission; }
+ virtual void SelectMission(DWORD id);
+
+ virtual const Text& GetMachineInfo() { return machine_info; }
+ virtual WORD GetGamePort() { return 0; }
+
+ // actions:
+ virtual bool Ping();
+ virtual void GameStart() { }
+ virtual void GameStop() { }
+ virtual DWORD GetLag();
+
+ // instance management:
+ static NetLobby* GetInstance();
+ static bool IsNetLobbyClient();
+ static bool IsNetLobbyServer();
+
+protected:
+ virtual void DoPing(NetPeer* peer, Text msg) { }
+ virtual void DoServerInfo(NetPeer* peer, Text msg) { }
+ virtual void DoServerMods(NetPeer* peer, Text msg) { }
+ virtual void DoLogin(NetPeer* peer, Text msg) { }
+ virtual void DoLogout(NetPeer* peer, Text msg) { }
+ virtual void DoAuthUser(NetPeer* peer, Text msg) { }
+ virtual void DoUserAuth(NetPeer* peer, Text msg) { }
+ virtual void DoChat(NetPeer* peer, Text msg) { }
+ virtual void DoUserList(NetPeer* peer, Text msg) { }
+ virtual void DoBanUser(NetPeer* peer, Text msg) { }
+ virtual void DoMissionList(NetPeer* peer, Text msg) { }
+ virtual void DoMissionSelect(NetPeer* peer, Text msg) { }
+ virtual void DoMissionData(NetPeer* peer, Text msg) { }
+ virtual void DoUnitList(NetPeer* peer, Text msg) { }
+ virtual void DoMapUnit(NetPeer* peer, Text msg) { }
+ virtual void DoGameStart(NetPeer* peer, Text msg) { }
+ virtual void DoGameStop(NetPeer* peer, Text msg) { }
+ virtual void DoExit(NetPeer* peer, Text msg) { }
+
+ virtual void ParseMsg(Text msg, List<NetLobbyParam>& params);
+
+ NetLink* link;
+ NetUser* local_user;
+ List<NetUser> users;
+ List<NetChatEntry> chat_log;
+ List<NetCampaignInfo> campaigns;
+ List<NetUnitEntry> unit_map;
+ Text machine_info;
+ List<ModInfo> server_mods;
+
+ bool active;
+ DWORD last_send_time;
+ DWORD start_time;
+ DWORD selected_mission;
+ Mission* mission;
+ int status;
+};
+
+// +-------------------------------------------------------------------+
+
+class NetLobbyParam
+{
+public:
+ static const char* TYPENAME() { return "NetLobbyParam"; }
+
+ NetLobbyParam(const char* n, const char* v) : name(n), value(v) { }
+
+ int operator == (const NetLobbyParam& p) const { return name == p.name; }
+
+ Text name;
+ Text value;
+};
+
+// +-------------------------------------------------------------------+
+
+class NetUnitEntry
+{
+public:
+ static const char* TYPENAME() { return "NetUnitEntry"; }
+
+ NetUnitEntry(MissionElement* elem, int index=0);
+ NetUnitEntry(const char* elem, const char* design, int iff, int index=0);
+ ~NetUnitEntry();
+
+ int operator == (const NetUnitEntry& e) const { return (elem == e.elem) && (index == e.index); }
+
+ Text GetDescription() const;
+ const Text& GetUserName() const { return user; }
+ const Text& GetElemName() const { return elem; }
+ const Text& GetDesign() const { return design; }
+ int GetIFF() const { return iff; }
+ int GetIndex() const { return index; }
+ int GetLives() const { return lives; }
+ int GetIntegrity() const { return hull; }
+ int GetMissionRole() const { return role; }
+ bool GetLocked() const { return lock; }
+
+ void SetUserName(const char* u) { user = u; }
+ void SetElemName(const char* e) { elem = e; }
+ void SetDesign(const char* d) { design = d; }
+ void SetIFF(int i) { iff = i; }
+ void SetIndex(int i) { index = i; }
+ void SetLives(int i) { lives = i; }
+ void SetIntegrity(int i) { hull = i; }
+ void SetMissionRole(int i) { role = i; }
+ void SetLock(bool l) { lock = l; }
+
+private:
+ Text user;
+ Text elem;
+ Text design;
+ int iff;
+ int index;
+ int lives;
+ int hull;
+ int role;
+ bool lock;
+};
+
+// +--------------------------------------------------------------------+
+
+class NetServerInfo
+{
+public:
+ static const char* TYPENAME() { return "NetServerInfo"; }
+
+ enum STATUS { OFFLINE, LOBBY, BRIEFING, ACTIVE, DEBRIEFING, PERSISTENT };
+
+ NetServerInfo();
+
+ Text name;
+ Text hostname;
+ Text password;
+ Text type;
+ NetAddr addr;
+ WORD port;
+ WORD gameport;
+ bool save;
+
+ Text version;
+ Text machine_info;
+ int nplayers;
+ int hosted;
+ int status;
+
+ DWORD ping_time;
+};
+
+// +--------------------------------------------------------------------+
+
+class NetCampaignInfo
+{
+public:
+ static const char* TYPENAME() { return "NetCampaignInfo"; }
+
+ NetCampaignInfo() : id(0) { }
+ ~NetCampaignInfo() { }
+
+ int id;
+ Text name;
+
+ List<MissionInfo> missions;
+};
+
+// +--------------------------------------------------------------------+
+
+enum NET_LOBBY_MESSAGES {
+ NET_LOBBY_PING = 0x10,
+ NET_LOBBY_SERVER_INFO,
+ NET_LOBBY_SERVER_MODS,
+ NET_LOBBY_LOGIN,
+ NET_LOBBY_LOGOUT,
+ NET_LOBBY_CHAT,
+ NET_LOBBY_USER_LIST,
+ NET_LOBBY_BAN_USER,
+ NET_LOBBY_MISSION_LIST,
+ NET_LOBBY_MISSION_SELECT,
+ NET_LOBBY_MISSION_DATA,
+ NET_LOBBY_UNIT_LIST,
+ NET_LOBBY_MAP_UNIT,
+
+ NET_LOBBY_AUTH_USER,
+ NET_LOBBY_USER_AUTH,
+
+ NET_LOBBY_GAME_START,
+ NET_LOBBY_GAME_STOP,
+ NET_LOBBY_EXIT
+};
+
+// +--------------------------------------------------------------------+
+
+#endif NetLobby_h
+
diff --git a/Stars45/NetLobbyClient.cpp b/Stars45/NetLobbyClient.cpp
new file mode 100644
index 0000000..6d04add
--- /dev/null
+++ b/Stars45/NetLobbyClient.cpp
@@ -0,0 +1,809 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetLobbyClient.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Stream-oriented network client class
+*/
+
+
+#include "MemDebug.h"
+#include "NetLobbyClient.h"
+#include "NetClientConfig.h"
+#include "NetAuth.h"
+#include "NetChat.h"
+#include "Campaign.h"
+#include "Player.h"
+#include "Starshatter.h"
+#include "ModInfo.h"
+
+#include "NetHost.h"
+#include "NetPeer.h"
+#include "NetLink.h"
+#include "NetMsg.h"
+#include "NetLayer.h"
+#include "FormatUtil.h"
+
+extern const char* versionInfo;
+
+// +-------------------------------------------------------------------+
+
+NetLobbyClient::NetLobbyClient()
+ : NetLobby(false), server_id(0), host(false), exit_code(0), temporary(false)
+{
+ NetHost me;
+ Text server_name;
+ WORD port = 11101;
+
+ ping_req_time = 0;
+ chat_req_time = 0;
+ user_req_time = 0;
+ camp_req_time = 0;
+ unit_req_time = 0;
+ mods_req_time = 0;
+
+ NetClientConfig* ncc = NetClientConfig::GetInstance();
+ if (ncc) {
+ NetServerInfo* info = ncc->GetSelectedServer();
+
+ if (info) {
+ server_name = info->hostname;
+ addr = info->addr;
+ port = info->port;
+ gamepass = info->password;
+ }
+ }
+
+ if (server_name.length() && port > 0) {
+ Print(" '%s' is a client of '%s'\n", me.Name(), server_name);
+ link = new(__FILE__,__LINE__) NetLink;
+ server_id = link->AddPeer(NetAddr(server_name, port));
+ }
+ else if (port == 0) {
+ Print(" '%s' invalid lobby port number %d\n", me.Name(), port);
+ }
+ else {
+ Print(" '%s' is a client without a server\n", me.Name());
+ }
+}
+
+NetLobbyClient::NetLobbyClient(const NetAddr& server_addr)
+ : NetLobby(true), server_id(0), addr(server_addr), host(false), exit_code(0),
+ temporary(true)
+{
+ ping_req_time = 0;
+ chat_req_time = 0;
+ user_req_time = 0;
+ camp_req_time = 0;
+ unit_req_time = 0;
+ mods_req_time = 0;
+
+ if (addr.IPAddr() != 0) {
+ link = new(__FILE__,__LINE__) NetLink;
+ server_id = link->AddPeer(addr);
+ }
+}
+
+NetLobbyClient::~NetLobbyClient()
+{
+ missions.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyClient::SendData(int type, Text msg)
+{
+ if (link && server_id && type > 0 && type < 255) {
+ if (msg.length())
+ link->SendMessage(server_id, (BYTE) type, msg.data(), msg.length(), NetMsg::RELIABLE);
+ else
+ link->SendMessage(server_id, (BYTE) type, 0, 0, NetMsg::RELIABLE);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyClient::ExecFrame()
+{
+ NetLobby::ExecFrame();
+
+ if (!temporary) {
+ // check health of server:
+ NetPeer* s = link->FindPeer(server_id);
+ if (s && (NetLayer::GetUTC() - s->LastReceiveTime() > NET_DISCONNECT_TIME)) {
+ exit_code = 2;
+ }
+
+ // send keep alive ping:
+ if ((NetLayer::GetUTC() - ping_req_time > NET_DISCONNECT_TIME/3)) {
+ SendData(NET_LOBBY_SERVER_INFO, Text());
+ ping_req_time = NetLayer::GetUTC();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+NetLobbyClient::Login(bool host_req)
+{
+ Player* player = Player::GetCurrentPlayer();
+ if (!player)
+ return false;
+
+ host = host_req;
+
+ Text login = "name \"";
+ login += SafeQuotes(player->Name());
+ login += "\" pass \"";
+ login += SafeQuotes(player->Password());
+ login += "\" version \"";
+ login += versionInfo;
+ login += "\" ";
+
+ char buffer[256];
+
+ sprintf(buffer, "host %s rank %d time %d miss %d kill %d loss %d ",
+ host ? "true" : "false",
+ player->Rank(),
+ player->FlightTime(),
+ player->Missions(),
+ player->Kills(),
+ player->Losses());
+
+ login += buffer;
+
+ login += "sig \"";
+ login += SafeQuotes(player->Signature());
+ login += "\" squad \"";
+ login += SafeQuotes(player->Squadron());
+ login += "\" ";
+
+ if (gamepass.length() > 0) {
+ login += "gamepass \"";
+ login += SafeQuotes(gamepass);
+ login += "\" ";
+ }
+
+ SendData(NET_LOBBY_LOGIN, login);
+ ExecFrame();
+ return true;
+}
+
+bool
+NetLobbyClient::Logout()
+{
+ if (host)
+ GameStop();
+
+ SendData(NET_LOBBY_LOGOUT, Text());
+ Sleep(250);
+ ExecFrame();
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+NetLobbyClient::Ping()
+{
+ Text no_data;
+
+ SendData(NET_LOBBY_PING, no_data);
+ Sleep(100);
+
+ SendData(NET_LOBBY_PING, no_data);
+ Sleep(100);
+
+ SendData(NET_LOBBY_PING, no_data);
+ Sleep(100);
+
+ SendData(NET_LOBBY_SERVER_INFO, no_data);
+ Sleep(700);
+ ExecFrame();
+
+ return (server_info.status > NetServerInfo::OFFLINE);
+}
+
+void
+NetLobbyClient::GameStart()
+{
+ SendData(NET_LOBBY_GAME_START, Text());
+ Sleep(100);
+
+ SetStatus(NetServerInfo::ACTIVE);
+ Starshatter::GetInstance()->SetGameMode(Starshatter::PREP_MODE);
+
+ // discard unit map selection data so that
+ // it will be refreshed when we return to
+ // the lobby after the mission:
+
+ ClearUnitMap();
+}
+
+void
+NetLobbyClient::GameStop()
+{
+ SendData(NET_LOBBY_GAME_STOP, Text());
+ ExecFrame();
+ Sleep(100);
+
+ SetStatus(NetServerInfo::LOBBY);
+}
+
+// +--------------------------------------------------------------------+
+
+const Text&
+NetLobbyClient::GetMachineInfo()
+{
+ if (server_info.status > NetServerInfo::OFFLINE)
+ return server_info.machine_info;
+
+ return NetLobby::GetMachineInfo();
+}
+
+int
+NetLobbyClient::GetStatus() const
+{
+ if (server_info.status > NetServerInfo::OFFLINE)
+ return server_info.status;
+
+ return NetLobby::GetStatus();
+}
+
+int
+NetLobbyClient::NumUsers()
+{
+ if (server_info.status > NetServerInfo::OFFLINE)
+ return server_info.nplayers;
+
+ return NetLobby::NumUsers();
+}
+
+bool
+NetLobbyClient::HasHost()
+{
+ if (server_info.status > NetServerInfo::OFFLINE)
+ return server_info.hosted ? true : false;
+
+ return NetLobby::HasHost();
+}
+
+WORD
+NetLobbyClient::GetGamePort()
+{
+ if (server_info.status > NetServerInfo::OFFLINE)
+ return server_info.gameport;
+
+ return NetLobby::GetGamePort();
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetLobbyClient::AddChat(NetUser* user, const char* msg, bool route)
+{
+ if (!msg || !*msg) return;
+
+ char buffer[280];
+ sprintf(buffer, "msg \"%s\"", SafeQuotes(msg));
+
+ SendData(NET_LOBBY_CHAT, buffer);
+ ExecFrame();
+}
+
+List<NetChatEntry>&
+NetLobbyClient::GetChat()
+{
+ if (chat_log.size() < 1 && (NetLayer::GetUTC() - chat_req_time > 3)) {
+ SendData(NET_LOBBY_CHAT, Text());
+ chat_req_time = NetLayer::GetUTC();
+ }
+
+ return chat_log;
+}
+
+List<NetUser>&
+NetLobbyClient::GetUsers()
+{
+ if (users.size() < 1 && (NetLayer::GetUTC() - user_req_time > 2)) {
+ SendData(NET_LOBBY_USER_LIST, Text());
+ user_req_time = NetLayer::GetUTC();
+ }
+
+ return users;
+}
+
+List<ModInfo>&
+NetLobbyClient::GetServerMods()
+{
+ if (server_mods.size() < 1 && (NetLayer::GetUTC() - mods_req_time > 2)) {
+ SendData(NET_LOBBY_SERVER_MODS, Text());
+ mods_req_time = NetLayer::GetUTC();
+ }
+
+ return server_mods;
+}
+
+List<NetUnitEntry>&
+NetLobbyClient::GetUnitMap()
+{
+ bool request = selected_mission &&
+ unit_map.size() < 1 &&
+ (NetLayer::GetUTC() - unit_req_time > 2);
+
+ if (selected_mission && GetStatus() == NetServerInfo::ACTIVE && (NetLayer::GetUTC() - unit_req_time > 5))
+ request = true;
+
+ if (request) {
+ SendData(NET_LOBBY_UNIT_LIST, Text());
+ unit_req_time = NetLayer::GetUTC();
+ }
+
+ return unit_map;
+}
+
+// +--------------------------------------------------------------------+
+
+List<NetCampaignInfo>&
+NetLobbyClient::GetCampaigns()
+{
+ if (campaigns.size() < 1 && (NetLayer::GetUTC() - camp_req_time > 3)) {
+ SendData(NET_LOBBY_MISSION_LIST, Text());
+ camp_req_time = NetLayer::GetUTC();
+ }
+
+ return campaigns;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyClient::BanUser(NetUser* user)
+{
+ char buffer[512];
+ sprintf(buffer, "user \"%s\"", SafeQuotes(user->Name()));
+ SendData(NET_LOBBY_BAN_USER, buffer);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyClient::SelectMission(DWORD id)
+{
+ char buffer[32];
+ sprintf(buffer, "m_id 0x%08x", id);
+ SendData(NET_LOBBY_MISSION_SELECT, buffer);
+}
+
+void
+NetLobbyClient::MapUnit(int n, const char* user, bool lock)
+{
+ if (user && strlen(user) > 250)
+ return;
+
+ char buffer[512];
+ sprintf(buffer, "id %d user \"%s\" lock %s",
+ n, SafeQuotes(user), lock ? "true" : "false");
+ SendData(NET_LOBBY_MAP_UNIT, buffer);
+}
+
+Mission*
+NetLobbyClient::GetSelectedMission()
+{
+ mission = 0;
+
+ // ask server for mission:
+ SendData(NET_LOBBY_MISSION_DATA, Text());
+
+ // wait for answer:
+ int i = 150;
+ while (i-- > 0 && !mission) {
+ Sleep(100);
+ ExecFrame();
+ }
+
+ return mission;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyClient::DoAuthUser(NetPeer* peer, Text msg)
+{
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ int level = NetAuth::NET_AUTH_STANDARD;
+ Text salt;
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ int num = 0;
+ sscanf(p->value, "%d", &num);
+
+ if (p->name == "level") {
+ level = num;
+ }
+
+ else if (p->name == "salt") {
+ salt = p->value;
+ }
+ }
+
+ Text response = NetAuth::CreateAuthResponse(level, salt);
+ if (response.length() > 0)
+ SendData(NET_LOBBY_USER_AUTH, response);
+}
+
+void
+NetLobbyClient::DoServerInfo(NetPeer* peer, Text msg)
+{
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ int num = 0;
+ sscanf(p->value, "%d", &num);
+
+ if (p->name == "info") {
+ server_info.machine_info = p->value;
+ }
+
+ else if (p->name == "version") {
+ server_info.version = p->value;
+ }
+
+ else if (p->name == "mode") {
+ server_info.status = num;
+ }
+
+ else if (p->name == "users") {
+ server_info.nplayers = num;
+ }
+
+ else if (p->name == "host") {
+ server_info.hosted = (p->value == "true");
+ }
+
+ else if (p->name == "port") {
+ server_info.gameport = (WORD) num;
+ }
+ }
+
+ params.destroy();
+}
+
+void
+NetLobbyClient::DoChat(NetPeer* peer, Text msg)
+{
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ int id = 0;
+ Text user_name;
+ Text chat_msg;
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ int num = 0;
+ sscanf(p->value, "%d", &num);
+
+ if (p->name == "id")
+ id = num;
+
+ else if (p->name == "user")
+ user_name = p->value;
+
+ else if (p->name == "msg")
+ chat_msg = p->value;
+ }
+
+ params.destroy();
+
+ // receive chat from server:
+ if (id && chat_msg.length()) {
+ NetChatEntry* entry = new(__FILE__,__LINE__) NetChatEntry(id, user_name, chat_msg);
+
+ if (!chat_log.contains(entry))
+ chat_log.insertSort(entry);
+ else
+ delete entry; // received duplicate
+ }
+}
+
+void
+NetLobbyClient::DoServerMods(NetPeer* peer, Text msg)
+{
+ mods_req_time = NetLayer::GetUTC() + 3600;
+
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+ server_mods.destroy();
+
+ Text name;
+ Text version;
+ Text url;
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ if (p->name == "mod") {
+ name = p->value;
+ }
+
+ else if (p->name == "url") {
+ url = p->value;
+ }
+
+ else if (p->name == "ver") {
+ version = p->value;
+
+ ModInfo* info = new(__FILE__,__LINE__) ModInfo(name, version, url);
+ server_mods.append(info);
+ }
+ }
+
+ params.destroy();
+}
+
+void
+NetLobbyClient::DoUserList(NetPeer* peer, Text msg)
+{
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ users.destroy();
+
+ Text user_name;
+ Text host_flag;
+ Text signature;
+ Text squadron;
+ int rank = 0;
+ int flight_time = 0;
+ int mission_count = 0;
+ int kills = 0;
+ int losses = 0;
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ int num = 0;
+ sscanf(p->value, "%d", &num);
+
+ if (p->name == "name")
+ user_name = p->value;
+
+ else if (p->name == "sig")
+ signature = p->value;
+
+ else if (p->name == "squad")
+ squadron = p->value;
+
+ else if (p->name == "rank")
+ rank = num;
+
+ else if (p->name == "time")
+ flight_time = num;
+
+ else if (p->name == "miss")
+ mission_count = num;
+
+ else if (p->name == "kill")
+ kills = num;
+
+ else if (p->name == "loss")
+ losses = num;
+
+ else if (p->name == "host") {
+ host_flag = p->value;
+
+ NetUser* u = new(__FILE__,__LINE__) NetUser(user_name);
+ u->SetHost((host_flag == "true") ? true : false);
+ u->SetSignature(signature);
+ u->SetSquadron(squadron);
+ u->SetRank(rank);
+ u->SetFlightTime(flight_time);
+ u->SetMissions(mission_count);
+ u->SetKills(kills);
+ u->SetLosses(losses);
+
+ AddUser(u);
+ }
+ }
+
+ params.destroy();
+}
+
+void
+NetLobbyClient::DoMissionList(NetPeer* peer, Text msg)
+{
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ if (params.size() > 2) {
+ campaigns.destroy();
+
+ NetCampaignInfo* c = 0;
+ MissionInfo* m = 0;
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ if (p->name == "c_id") {
+ c = new(__FILE__,__LINE__) NetCampaignInfo;
+ sscanf(p->value, "0x%x", &c->id);
+ campaigns.append(c);
+
+ m = 0;
+ }
+
+ else if (c && p->name == "c_name") {
+ c->name = p->value;
+ }
+
+ else if (p->name == "m_id") {
+ int id = 0;
+ sscanf(p->value, "0x%x", &id);
+
+ int m_id = id & NET_MISSION_MASK;
+ int c_id = id >> NET_CAMPAIGN_SHIFT;
+
+ for (int i = 0; i < campaigns.size(); i++) {
+ NetCampaignInfo* c = campaigns[i];
+ if (c->id == c_id) {
+ m = new(__FILE__,__LINE__) MissionInfo;
+ m->id = m_id;
+ c->missions.append(m);
+ missions.append(m); // for later garbage collection
+ break;
+ }
+ }
+ }
+
+ else if (m && p->name == "m_name") {
+ m->name = p->value;
+ }
+
+ else if (m && p->name == "m_desc") {
+ m->description = p->value;
+ }
+ }
+ }
+
+ params.destroy();
+}
+
+void
+NetLobbyClient::DoMissionSelect(NetPeer* peer, Text msg)
+{
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ int num = 0;
+ sscanf(p->value, "0x%x", &num);
+
+ if (p->name == "m_id") {
+ if (selected_mission != (DWORD) num) {
+ selected_mission = num;
+ ClearUnitMap();
+ }
+ }
+ }
+
+ params.destroy();
+}
+
+void
+NetLobbyClient::DoMissionData(NetPeer* peer, Text msg)
+{
+ Campaign* c = Campaign::SelectCampaign("Multiplayer Missions");
+
+ if (c) {
+ c->LoadNetMission(99999, msg.data());
+ mission = c->GetMission(99999);
+ }
+
+ if (msg.length()) {
+ FILE* f = ::fopen("multi_mission_recv.def", "w");
+ if (f) {
+ ::fwrite(msg.data(), msg.length(), 1, f);
+ ::fclose(f);
+ }
+ }
+}
+
+void
+NetLobbyClient::DoUnitList(NetPeer* peer, Text msg)
+{
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ if (params.size() > 2) {
+ ClearUnitMap();
+
+ Text elem_name;
+ Text design;
+ Text user_name;
+ int iff;
+ int index;
+ int lives = 1;
+ int hull = 100;
+ int role = 0;
+ int lock = 0;
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ if (p->name == "name") {
+ elem_name = p->value;
+ }
+ else if (p->name == "design") {
+ design = p->value;
+ }
+ else if (p->name == "user") {
+ user_name = p->value;
+ }
+ else if (p->name == "index") {
+ sscanf(p->value, "%d", &index);
+ }
+ else if (p->name == "iff") {
+ sscanf(p->value, "%d", &iff);
+ }
+ else if (p->name == "lives") {
+ sscanf(p->value, "%d", &lives);
+ }
+ else if (p->name == "hull") {
+ sscanf(p->value, "%d", &hull);
+ }
+ else if (p->name == "role") {
+ sscanf(p->value, "%d", &role);
+ }
+ else if (p->name == "lock") {
+ sscanf(p->value, "%d", &lock);
+
+ NetUnitEntry* entry = new(__FILE__,__LINE__) NetUnitEntry(elem_name, design, iff, index);
+ entry->SetUserName(user_name);
+ entry->SetLives(lives);
+ entry->SetIntegrity(hull);
+ entry->SetMissionRole(role);
+ entry->SetLock(lock ? true : false);
+
+ unit_map.append(entry);
+ }
+ }
+ }
+
+ params.destroy();
+}
+
+void
+NetLobbyClient::DoMapUnit(NetPeer* peer, Text msg)
+{
+}
+
+void
+NetLobbyClient::DoGameStart(NetPeer* peer, Text msg)
+{
+}
+
+void
+NetLobbyClient::DoExit(NetPeer* peer, Text msg)
+{
+ exit_code = 1;
+}
diff --git a/Stars45/NetLobbyClient.h b/Stars45/NetLobbyClient.h
new file mode 100644
index 0000000..9cb4549
--- /dev/null
+++ b/Stars45/NetLobbyClient.h
@@ -0,0 +1,104 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetLobbyClient.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ UDP-oriented network lobby client class
+*/
+
+
+#ifndef NetLobbyClient_h
+#define NetLobbyClient_h
+
+#include "NetLobby.h"
+#include "NetClientConfig.h"
+
+// +-------------------------------------------------------------------+
+
+class NetLobbyClient : public NetLobby
+{
+public:
+ NetLobbyClient();
+ NetLobbyClient(const NetAddr& server_addr);
+ virtual ~NetLobbyClient();
+
+ int operator == (const NetLobbyClient& c) const { return this == &c; }
+
+ virtual void ExecFrame();
+ virtual bool Login(bool host=false);
+ virtual bool Logout();
+ virtual int GetLastError() const { return exit_code; }
+
+ // actions:
+ virtual bool Ping();
+ virtual void GameStart();
+ virtual void GameStop();
+
+ virtual void BanUser(NetUser* user);
+
+ virtual void AddChat(NetUser* user, const char* msg, bool route=true);
+ virtual List<NetChatEntry>&
+ GetChat();
+
+ NetAddr GetServerAddr() const { return addr; }
+ virtual bool IsHost() const { return host; }
+ virtual bool IsClient() const { return true; }
+
+ virtual List<NetUser>& GetUsers();
+ virtual List<NetCampaignInfo>& GetCampaigns();
+ virtual List<NetUnitEntry>& GetUnitMap();
+ virtual void MapUnit(int n, const char* user, bool lock=false);
+ virtual void SelectMission(DWORD id);
+ virtual Mission* GetSelectedMission();
+
+ virtual List<ModInfo>& GetServerMods();
+
+ // overrides for ping support:
+ virtual const Text& GetMachineInfo();
+ virtual int GetStatus() const;
+ virtual int NumUsers();
+ virtual bool HasHost();
+ virtual WORD GetGamePort();
+
+protected:
+ virtual void SendData(int type, Text msg);
+ virtual void DoServerInfo(NetPeer* peer, Text msg);
+ virtual void DoServerMods(NetPeer* peer, Text msg);
+ virtual void DoAuthUser(NetPeer* peer, Text msg);
+ virtual void DoChat(NetPeer* peer, Text msg);
+ virtual void DoUserList(NetPeer* peer, Text msg);
+ virtual void DoMissionList(NetPeer* peer, Text msg);
+ virtual void DoMissionSelect(NetPeer* peer, Text msg);
+ virtual void DoMissionData(NetPeer* peer, Text msg);
+ virtual void DoUnitList(NetPeer* peer, Text msg);
+ virtual void DoMapUnit(NetPeer* peer, Text msg);
+ virtual void DoGameStart(NetPeer* peer, Text msg);
+ virtual void DoExit(NetPeer* peer, Text msg);
+
+ DWORD server_id;
+ NetAddr addr;
+ bool host;
+ Text gamepass;
+ int exit_code;
+
+ NetServerInfo server_info;
+ List<MissionInfo> missions;
+
+ bool temporary;
+ DWORD ping_req_time;
+ DWORD chat_req_time;
+ DWORD user_req_time;
+ DWORD camp_req_time;
+ DWORD unit_req_time;
+ DWORD mods_req_time;
+};
+
+// +-------------------------------------------------------------------+
+
+#endif NetLobbyClient_h \ No newline at end of file
diff --git a/Stars45/NetLobbyDlg.cpp b/Stars45/NetLobbyDlg.cpp
new file mode 100644
index 0000000..f725b8c
--- /dev/null
+++ b/Stars45/NetLobbyDlg.cpp
@@ -0,0 +1,475 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetLobbyDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "NetLobbyDlg.h"
+#include "NetUnitDlg.h"
+#include "NetClientConfig.h"
+#include "MenuScreen.h"
+#include "Starshatter.h"
+#include "Ship.h"
+#include "Player.h"
+#include "Campaign.h"
+
+#include "NetAddr.h"
+#include "NetLobbyClient.h"
+#include "NetLobbyServer.h"
+#include "NetUser.h"
+#include "NetChat.h"
+
+#include "DataLoader.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "MachineInfo.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(NetLobbyDlg, OnCampaignSelect);
+DEF_MAP_CLIENT(NetLobbyDlg, OnMissionSelect);
+DEF_MAP_CLIENT(NetLobbyDlg, OnApply);
+DEF_MAP_CLIENT(NetLobbyDlg, OnCancel);
+
+// +--------------------------------------------------------------------+
+
+NetLobbyDlg::NetLobbyDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ net_lobby(0)
+{
+ selected_campaign = 0;
+ selected_mission = 0;
+ last_chat = 0;
+ host_mode = false;
+
+ Init(def);
+}
+
+NetLobbyDlg::~NetLobbyDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyDlg::RegisterControls()
+{
+ lst_campaigns = (ComboBox*) FindControl(200);
+ REGISTER_CLIENT(EID_SELECT, lst_campaigns, NetLobbyDlg, OnCampaignSelect);
+
+ lst_missions = (ListBox*) FindControl(201);
+ REGISTER_CLIENT(EID_SELECT, lst_missions, NetLobbyDlg, OnMissionSelect);
+
+ txt_desc = FindControl(202);
+ lst_players = (ListBox*) FindControl(210);
+ lst_chat = (ListBox*) FindControl(211);
+ edt_chat = (EditBox*) FindControl(212);
+
+ if (edt_chat)
+ edt_chat->SetText("");
+
+ apply = (Button*) FindControl(1);
+ REGISTER_CLIENT(EID_CLICK, apply, NetLobbyDlg, OnApply);
+
+ cancel = (Button*) FindControl(2);
+ REGISTER_CLIENT(EID_CLICK, cancel, NetLobbyDlg, OnCancel);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyDlg::Show()
+{
+ if (!IsShown()) {
+ // clear server data:
+ if (lst_chat) lst_chat->ClearItems();
+ if (lst_campaigns) lst_campaigns->ClearItems();
+ if (lst_missions) lst_missions->ClearItems();
+ if (txt_desc) txt_desc->SetText("");
+ if (apply) apply->SetEnabled(false);
+
+ if (lst_missions) {
+ lst_missions->SetSelectedStyle(ListBox::LIST_ITEM_STYLE_FILLED_BOX);
+ lst_missions->SetLeading(2);
+ }
+
+ selected_campaign = 0;
+ selected_mission = 0;
+ last_chat = 0;
+
+ FormWindow::Show();
+
+ net_lobby = NetLobby::GetInstance();
+
+ if (!net_lobby) {
+ Starshatter* stars = Starshatter::GetInstance();
+ if (stars)
+ stars->StartLobby();
+
+ net_lobby = NetLobby::GetInstance();
+ }
+
+ if (net_lobby) {
+ if (net_lobby->IsServer()) {
+ host_mode = true;
+
+ NetUser* user = net_lobby->GetLocalUser();
+
+ if (!user) {
+ Player* player = Player::GetCurrentPlayer();
+ if (player) {
+ user = new(__FILE__,__LINE__) NetUser(player);
+ user->SetHost(true);
+ }
+ else {
+ ::Print("NetLobbyDlg::Show() Host mode - no current player?\n");
+ }
+ }
+
+ net_lobby->SetLocalUser(user);
+ }
+
+ SelectMission();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyDlg::ExecFrame()
+{
+ ExecLobbyFrame();
+
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ if (edt_chat && edt_chat->GetText().length() > 0) {
+ SendChat(edt_chat->GetText());
+ edt_chat->SetText("");
+ }
+ }
+
+ GetPlayers();
+ GetChat();
+
+ if (lst_campaigns) {
+ if (lst_campaigns->NumItems() < 1)
+ GetMissions();
+ else
+ GetSelectedMission();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyDlg::ExecLobbyFrame()
+{
+ if (net_lobby && net_lobby->GetLastError() != 0) {
+ if (net_lobby->IsClient()) {
+ Starshatter* stars = Starshatter::GetInstance();
+ if (stars)
+ stars->StopLobby();
+
+ net_lobby = 0;
+ manager->ShowNetClientDlg();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyDlg::GetPlayers()
+{
+ if (!lst_players) return;
+
+ if (net_lobby) {
+ lst_players->ClearItems();
+
+ NetUser* u = net_lobby->GetLocalUser();
+ if (u) {
+ Text name = Player::RankAbrv(u->Rank());
+ name += " ";
+ name += u->Name();
+
+ int count = lst_players->AddItem(u->IsHost() ? "*" : " ");
+ lst_players->SetItemText(count-1, 1, name);
+ host_mode = true;
+ }
+
+ ListIter<NetUser> iter = net_lobby->GetUsers();
+ while (++iter) {
+ NetUser* u = iter.value();
+ int count = lst_players->AddItem(u->IsHost() ? "*" : " ");
+
+ Text name = Player::RankAbrv(u->Rank());
+ name += " ";
+ name += u->Name();
+
+ lst_players->SetItemText(count-1, 1, name);
+
+ if (Player::GetCurrentPlayer()->Name() == u->Name())
+ host_mode = u->IsHost();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyDlg::GetChat()
+{
+ if (!lst_chat) return;
+
+ if (net_lobby) {
+ int last_item = lst_chat->NumItems() - 1;
+ int count = 0;
+ bool added = false;
+
+ ListIter<NetChatEntry> iter = net_lobby->GetChat();
+ while (++iter) {
+ NetChatEntry* c = iter.value();
+
+ if (count++ > last_item) {
+ int n = lst_chat->AddItem(c->GetUser());
+ lst_chat->SetItemText(n-1, 1, c->GetMessage());
+ added = true;
+ }
+ }
+
+ if (added)
+ lst_chat->EnsureVisible(lst_chat->NumItems()+1);
+ }
+}
+
+void
+NetLobbyDlg::SendChat(Text msg)
+{
+ if (msg.length() < 1) return;
+
+ Player* player = Player::GetCurrentPlayer();
+
+ if (msg[0] >= '0' && msg[0] <= '9') {
+ if (player) {
+ Text macro = player->ChatMacro(msg[0] - '0');
+
+ if (macro.length())
+ msg = macro;
+ }
+ }
+
+ if (net_lobby)
+ net_lobby->AddChat(net_lobby->GetLocalUser(), msg);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyDlg::GetMissions()
+{
+ if (!lst_campaigns || !lst_missions)
+ return;
+
+ if (net_lobby) {
+ lst_campaigns->ClearItems();
+
+ List<NetCampaignInfo>& campaigns = net_lobby->GetCampaigns();
+
+ if (campaigns.size()) {
+ ListIter<NetCampaignInfo> c_iter = campaigns;
+ while (++c_iter) {
+ NetCampaignInfo* c = c_iter.value();
+ lst_campaigns->AddItem(c->name);
+ }
+
+ lst_campaigns->SetSelection(0);
+ NetCampaignInfo* c = campaigns[0];
+
+ lst_missions->ClearItems();
+
+ ListIter<MissionInfo> m_iter = c->missions;
+ while (++m_iter) {
+ MissionInfo* m = m_iter.value();
+ lst_missions->AddItem(m->name);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyDlg::GetSelectedMission()
+{
+ if (!lst_campaigns || !lst_missions) return;
+
+ if (net_lobby) {
+ if (net_lobby->GetSelectedMissionID()) {
+ int id = net_lobby->GetSelectedMissionID();
+
+ selected_campaign = id >> NET_CAMPAIGN_SHIFT;
+ selected_mission = id & NET_MISSION_MASK;
+
+ List<NetCampaignInfo>& campaigns = net_lobby->GetCampaigns();
+
+ for (int i = 0; i < campaigns.size(); i++) {
+ NetCampaignInfo* c = campaigns[i];
+
+ if (c->id == selected_campaign) {
+ lst_campaigns->SetSelection(i);
+ OnCampaignSelect(0);
+
+ for (int j = 0; j < c->missions.size(); j++) {
+ MissionInfo* m = c->missions[j];
+
+ if (m->id == selected_mission) {
+ lst_missions->SetSelected(j);
+ OnMissionSelect(0);
+ }
+ }
+ }
+ }
+ }
+ else if (selected_campaign) {
+ selected_campaign = 0;
+ selected_mission = 0;
+ }
+
+ lst_campaigns->SetEnabled(selected_campaign == 0);
+ lst_missions->SetEnabled(selected_mission == 0);
+
+ if (!host_mode)
+ apply->SetEnabled(selected_mission != 0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyDlg::SelectMission()
+{
+ if (!lst_campaigns || !lst_missions) return;
+
+ bool selected = false;
+
+ if (net_lobby) {
+ int c_index = lst_campaigns->GetSelectedIndex();
+ int m_index = lst_missions->GetSelection();
+
+ List<NetCampaignInfo>& campaigns = net_lobby->GetCampaigns();
+
+ if (c_index >= 0 && c_index < campaigns.size() && m_index >= 0) {
+ NetCampaignInfo* c = campaigns[c_index];
+
+ if (m_index < c->missions.size()) {
+ MissionInfo* m = c->missions[m_index];
+
+ DWORD id = (c->id << NET_CAMPAIGN_SHIFT) +
+ (m->id & NET_MISSION_MASK);
+
+ net_lobby->SelectMission(id);
+ selected = true;
+ }
+ }
+
+ if (!selected)
+ net_lobby->SelectMission(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyDlg::OnCampaignSelect(AWEvent* event)
+{
+ if (net_lobby) {
+ List<NetCampaignInfo>& campaigns = net_lobby->GetCampaigns();
+
+ if (lst_campaigns && lst_missions && campaigns.size()) {
+ int index = lst_campaigns->GetSelectedIndex();
+
+ if (index >= 0 && index < campaigns.size()) {
+ NetCampaignInfo* c = campaigns[index];
+
+ lst_missions->ClearItems();
+ txt_desc->SetText("");
+
+ ListIter<MissionInfo> iter = c->missions;
+ while (++iter) {
+ MissionInfo* m = iter.value();
+ lst_missions->AddItem(m->name);
+ }
+ }
+ }
+ }
+}
+
+void
+NetLobbyDlg::OnMissionSelect(AWEvent* event)
+{
+ if (net_lobby) {
+ List<NetCampaignInfo>& campaigns = net_lobby->GetCampaigns();
+
+ if (lst_campaigns && lst_missions && txt_desc && campaigns.size()) {
+ txt_desc->SetText("");
+
+ if (host_mode && apply)
+ apply->SetEnabled(false);
+
+ int c_index = lst_campaigns->GetSelectedIndex();
+ int m_index = lst_missions->GetSelection();
+
+ if (c_index >= 0 && c_index < campaigns.size()) {
+ NetCampaignInfo* c = campaigns[c_index];
+
+ if (m_index >= 0 && m_index < c->missions.size()) {
+ MissionInfo* m = c->missions[m_index];
+ txt_desc->SetText(m->description);
+
+ if (host_mode && apply)
+ apply->SetEnabled(true);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyDlg::OnApply(AWEvent* event)
+{
+ if (host_mode)
+ SelectMission();
+
+ manager->ShowNetUnitDlg();
+
+ NetUnitDlg* unit_dlg = manager->GetNetUnitDlg();
+ if (unit_dlg)
+ unit_dlg->SetHostMode(host_mode);
+}
+
+void
+NetLobbyDlg::OnCancel(AWEvent* event)
+{
+ if (net_lobby) {
+ net_lobby->SelectMission(0);
+ net_lobby = 0;
+
+ Starshatter* stars = Starshatter::GetInstance();
+ if (stars)
+ stars->StopLobby();
+ }
+
+ manager->ShowNetClientDlg();
+}
diff --git a/Stars45/NetLobbyDlg.h b/Stars45/NetLobbyDlg.h
new file mode 100644
index 0000000..caf7be3
--- /dev/null
+++ b/Stars45/NetLobbyDlg.h
@@ -0,0 +1,89 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetLobbyDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#ifndef NetLobbyDlg_h
+#define NetLobbyDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "EditBox.h"
+#include "Font.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+class NetClientConfig;
+class NetLobby;
+class NetCampaignInfo;
+class MissionInfo;
+class NetChatEntry;
+class NetUser;
+
+// +--------------------------------------------------------------------+
+
+class NetLobbyDlg : public FormWindow
+{
+public:
+ NetLobbyDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~NetLobbyDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnCampaignSelect(AWEvent* event);
+ virtual void OnMissionSelect(AWEvent* event);
+
+ virtual void OnApply(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+
+ virtual void ExecLobbyFrame();
+
+protected:
+ virtual void GetPlayers();
+ virtual void GetChat();
+ virtual void GetMissions();
+ virtual void GetSelectedMission();
+ virtual void SendChat(Text msg);
+ virtual void SelectMission();
+
+ MenuScreen* manager;
+
+ ComboBox* lst_campaigns;
+ ListBox* lst_missions;
+ ActiveWindow* txt_desc;
+ ListBox* lst_players;
+ ListBox* lst_chat;
+ EditBox* edt_chat;
+
+ Button* apply;
+ Button* cancel;
+
+ NetLobby* net_lobby;
+
+ int selected_campaign;
+ int selected_mission;
+ int last_chat;
+ bool host_mode;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif NetLobbyDlg_h
+
diff --git a/Stars45/NetLobbyServer.cpp b/Stars45/NetLobbyServer.cpp
new file mode 100644
index 0000000..d66b209
--- /dev/null
+++ b/Stars45/NetLobbyServer.cpp
@@ -0,0 +1,1359 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetLobbyServer.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ NetLink Engine for Multiplayer Lobby
+*/
+
+#include "MemDebug.h"
+#include "NetLobbyServer.h"
+#include "NetServerConfig.h"
+#include "NetClientConfig.h"
+#include "NetBrokerClient.h"
+#include "NetAuth.h"
+#include "NetChat.h"
+#include "NetUser.h"
+#include "Campaign.h"
+#include "Mission.h"
+#include "StarServer.h"
+#include "Starshatter.h"
+#include "StarServer.h"
+#include "ShipDesign.h"
+#include "Sim.h"
+
+#include "ModConfig.h"
+#include "ModInfo.h"
+
+#include "NetGame.h"
+#include "NetPlayer.h"
+#include "NetUtil.h"
+
+#include "NetPeer.h"
+#include "NetLayer.h"
+#include "NetHost.h"
+#include "NetMsg.h"
+
+#include "MachineInfo.h"
+#include "Game.h"
+#include "FormatUtil.h"
+
+extern const char* versionInfo;
+
+// +-------------------------------------------------------------------+
+
+static NetLobbyServer* net_lobby_server = 0;
+
+NetLobbyServer::NetLobbyServer()
+ : announce_time(0), server_config(0), motd_index(1)
+{
+ status = NetServerInfo::LOBBY;
+ server_name = Text("Starshatter NetLobbyServer ") + versionInfo;
+ start_time = NetLayer::GetUTC();
+
+ selected_mission = 0;
+
+ WORD server_port = 11100;
+
+ server_config = NetServerConfig::GetInstance();
+ if (server_config) {
+ server_name = server_config->Name();
+ server_port = server_config->GetLobbyPort();
+ server_mission = server_config->GetMission();
+
+ NetAuth::SetAuthLevel(server_config->GetAuthLevel());
+
+ server_addr = NetAddr(NetHost().Address().IPAddr(), server_port);
+ link = new(__FILE__,__LINE__) NetLink(server_addr);
+ }
+
+ LoadMOTD();
+
+ StarServer* star_server = StarServer::GetInstance();
+ DWORD mission_id = 0;
+
+ // only one mission:
+ if (star_server && server_mission.length() > 0) {
+ NetCampaignInfo* c = new(__FILE__,__LINE__) NetCampaignInfo;
+ c->id = Campaign::MULTIPLAYER_MISSIONS;
+ c->name = "Persistent Multiplayer";
+ campaigns.append(c);
+
+ ListIter<Campaign> c_iter = Campaign::GetAllCampaigns();
+ while (++c_iter && !mission_id) {
+ Campaign* campaign = c_iter.value();
+
+ if (campaign->GetCampaignId() == Campaign::MULTIPLAYER_MISSIONS) {
+ ListIter<MissionInfo> m_iter = campaign->GetMissionList();
+ while (++m_iter && !mission_id) {
+ MissionInfo* m = m_iter.value();
+
+ if (m->script == server_mission) {
+ c->missions.append(m);
+ mission_id = (Campaign::MULTIPLAYER_MISSIONS << NET_CAMPAIGN_SHIFT) + m->id;
+
+ SelectMission(mission_id);
+ star_server->SetGameMode(StarServer::LOAD_MODE);
+
+ // lock in mission:
+ SetStatus(NetServerInfo::PERSISTENT);
+ }
+ }
+ }
+ }
+ }
+
+ // player host may select mission:
+ if (!mission_id) {
+ campaigns.destroy();
+
+ ListIter<Campaign> c_iter = Campaign::GetAllCampaigns();
+ while (++c_iter) {
+ Campaign* campaign = c_iter.value();
+
+ if (campaign->GetCampaignId() >= Campaign::MULTIPLAYER_MISSIONS) {
+ NetCampaignInfo* c = new(__FILE__,__LINE__) NetCampaignInfo;
+ c->id = campaign->GetCampaignId();
+ c->name = campaign->Name();
+ campaigns.append(c);
+
+ ListIter<MissionInfo> m_iter = campaign->GetMissionList();
+ while (++m_iter) {
+ MissionInfo* m = m_iter.value();
+ c->missions.append(m);
+ }
+ }
+ }
+ }
+
+ ModConfig* config = ModConfig::GetInstance();
+ List<ModInfo>& mods = config->GetModInfoList();
+
+ server_mods.clear();
+ server_mods.append(mods);
+
+ net_lobby_server = this;
+}
+
+NetLobbyServer::~NetLobbyServer()
+{
+ ListIter<NetUser> iter = users;
+ while (++iter) {
+ NetUser* u = iter.value();
+ SendData(u, NET_LOBBY_EXIT, Text());
+ ExecFrame();
+ }
+
+ Sleep(500);
+
+ unit_map.destroy();
+ chat_log.destroy();
+ users.destroy();
+ motd.destroy();
+
+ if (net_lobby_server == this)
+ net_lobby_server = 0;
+}
+
+NetLobbyServer*
+NetLobbyServer::GetInstance()
+{
+ return net_lobby_server;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyServer::LoadMOTD()
+{
+ motd.destroy();
+
+ FILE* f = fopen("motd.txt", "r");
+
+ if (f) {
+ char line[256];
+
+ while (fgets(line, 256, f)) {
+ int n = strlen(line) - 1;
+
+ while (n >= 0 && isspace(line[n]))
+ line[n--] = 0;
+
+ motd.append(new(__FILE__,__LINE__) Text(line));
+ }
+ }
+}
+
+void
+NetLobbyServer::SendMOTD(NetUser* user)
+{
+ if (motd.size() < 1) return;
+
+ char buffer[512];
+
+ for (int i = 0; i < motd.size(); i++) {
+ Text* line = motd[i];
+
+ sprintf(buffer, "id %d user \" \" msg \"%s\"",
+ motd_index++, *line);
+
+ SendData(user, NET_LOBBY_CHAT, buffer);
+ }
+
+ sprintf(buffer, "id %d user \" \" msg \" \"", motd_index++);
+ SendData(user, NET_LOBBY_CHAT, buffer);
+}
+
+void
+NetLobbyServer::SendMods(NetUser* user)
+{
+ char buffer[300];
+
+ ModConfig* config = ModConfig::GetInstance();
+ List<ModInfo>& mods = config->GetModInfoList();
+
+ if (mods.size() < 1) return;
+
+ for (int i = 0; i < mods.size(); i++) {
+ ModInfo* info = mods[i];
+
+ sprintf(buffer, "id %d user \"Enabled Mods:\" msg \"%d. '%s' ",
+ motd_index++, i+1, info->Name().data());
+
+ Text msg = buffer;
+
+ if (info->Version().length() > 0) {
+ msg += "version ";
+ msg += info->Version().data();
+ }
+
+ if (info->URL().length() > 0) {
+ msg += " - ";
+ msg += info->URL().data();
+ }
+
+ msg += "\"";
+
+ SendData(user, NET_LOBBY_CHAT, msg);
+ }
+
+ sprintf(buffer, "id %d user \" \" msg \" \"", motd_index++);
+ SendData(user, NET_LOBBY_CHAT, buffer);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetLobbyServer::ExecFrame()
+{
+ NetLobby::ExecFrame();
+
+ if (announce_time == 0 || Game::RealTime() - announce_time > 200000) {
+ GameOn();
+ announce_time = Game::RealTime();
+ }
+
+ if (GetStatus() == NetServerInfo::BRIEFING) {
+ NetGame* net_game = NetGame::GetInstance();
+
+ if (net_game && net_game->NumPlayers() > 0) {
+ SetStatus(NetServerInfo::ACTIVE);
+ }
+ }
+
+ StarServer* star_server = StarServer::GetInstance();
+ DWORD mission_id = 0;
+
+ // restart persistent mission?
+ if (star_server &&
+ star_server->GetGameMode() == StarServer::MENU_MODE &&
+ server_mission.length() > 0) {
+
+ NetCampaignInfo* c = campaigns.last();
+
+ if (!c || c->name != "Persistent Multiplayer") {
+ c = new(__FILE__,__LINE__) NetCampaignInfo;
+ c->id = Campaign::MULTIPLAYER_MISSIONS;
+ c->name = "Persistent Multiplayer";
+ campaigns.append(c);
+ }
+ else {
+ c->missions.clear();
+ }
+
+ ListIter<Campaign> c_iter = Campaign::GetAllCampaigns();
+ while (++c_iter && !mission_id) {
+ Campaign* campaign = c_iter.value();
+
+ if (campaign->GetCampaignId() == Campaign::MULTIPLAYER_MISSIONS) {
+ ListIter<MissionInfo> m_iter = campaign->GetMissionList();
+ while (++m_iter && !mission_id) {
+ MissionInfo* m = m_iter.value();
+
+ if (m->script == server_mission) {
+ c->missions.append(m);
+ mission_id = (Campaign::MULTIPLAYER_MISSIONS << NET_CAMPAIGN_SHIFT) + m->id;
+
+ // unlock old mission:
+ SetStatus(NetServerInfo::LOBBY);
+
+ SelectMission(mission_id);
+
+ if (star_server->GetGameMode() == StarServer::MENU_MODE) {
+ star_server->SetGameMode(StarServer::LOAD_MODE);
+ }
+
+ // lock in new mission:
+ SetStatus(NetServerInfo::PERSISTENT);
+ }
+ }
+ }
+ }
+ }
+
+ CheckSessions();
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetLobbyServer::SendData(NetUser* dst, int type, Text msg)
+{
+ if (link && dst && type > 0 && type < 255) {
+ if (msg.length())
+ link->SendMessage(dst->GetNetID(), (BYTE) type, msg.data(), msg.length(), NetMsg::RELIABLE);
+ else
+ link->SendMessage(dst->GetNetID(), (BYTE) type, 0, 0, NetMsg::RELIABLE);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetLobbyServer::CheckSessions()
+{
+ if (!link)
+ return;
+
+ bool dropped = false;
+
+ ListIter<NetUser> u_iter = users;
+ while (++u_iter) {
+ NetUser* u = u_iter.value();
+ NetPeer* p = link->FindPeer(u->GetNetID());
+
+ if (p && (NetLayer::GetUTC() - p->LastReceiveTime()) > NET_DISCONNECT_TIME) {
+ // check game peer for activity:
+ NetGame* game = NetGame::GetInstance();
+ NetPlayer* player = 0;
+ NetPeer* p2 = 0;
+
+ if (game) {
+ player = game->FindPlayerByName(u->Name());
+
+ if (player) {
+ p2 = game->GetPeer(player);
+
+ if (p2 && (NetLayer::GetUTC() - p2->LastReceiveTime()) < NET_DISCONNECT_TIME) {
+ p->SetLastReceiveTime(p2->LastReceiveTime());
+ continue;
+ }
+ }
+ else {
+ ::Print("NetLobbyServer::CheckSessions() Could not find player for '%s'\n", u->Name().data());
+ }
+ }
+ else {
+ ::Print("NetLobbyServer::CheckSessions() Could not find net game for '%s'\n", u->Name().data());
+ }
+
+ // announce drop:
+ char timestr[64];
+ FormatTime(timestr, Game::RealTime()/1000);
+ Print("NetLobbyServer: Dropped inactive connection '%s' %s\n",
+ u->Name().data(), timestr);
+
+ if (u->IsHost()) {
+ Print(" User was host - ending net game.\n");
+ GameStop();
+ }
+
+ u_iter.removeItem(); // first remove user from list
+ NetLobby::UnmapUnit(u->Name()); // then unmap unit
+ delete u; // now it is safe to discard the inactive user
+
+ dropped = true;
+ }
+ }
+
+ if (dropped) {
+ SendUsers();
+ SendUnits();
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetLobbyServer::GameStart()
+{
+ if (status < NetServerInfo::ACTIVE) {
+ SetStatus(NetServerInfo::BRIEFING);
+
+ if (Starshatter::GetInstance()) {
+ Starshatter::GetInstance()->SetGameMode(Starshatter::PREP_MODE);
+ }
+ else {
+ StarServer* s = StarServer::GetInstance();
+ if (s && s->GetGameMode() == StarServer::MENU_MODE) {
+ s->SetGameMode(StarServer::LOAD_MODE);
+ }
+ }
+ }
+}
+
+void
+NetLobbyServer::GameStop()
+{
+ if (GetStatus() != NetServerInfo::PERSISTENT) {
+ SetStatus(NetServerInfo::LOBBY);
+
+ StarServer* s = StarServer::GetInstance();
+ if (s && s->GetGameMode() != StarServer::MENU_MODE) {
+ s->SetGameMode(StarServer::MENU_MODE);
+ }
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetLobbyServer::BanUser(NetUser* user)
+{
+ if (user && !user->IsHost()) {
+ ::Print("NetLobbyServer::BanUser name '%s' addr %d.%d.%d.%d\n",
+ user->Name().data(),
+ user->GetAddress().B1(),
+ user->GetAddress().B2(),
+ user->GetAddress().B3(),
+ user->GetAddress().B4());
+
+ SendData(user, NET_LOBBY_EXIT, Text());
+
+ if (server_config)
+ server_config->BanUser(user);
+
+ DelUser(user);
+ }
+}
+
+void
+NetLobbyServer::AddUser(NetUser* user)
+{
+ if (server_config && server_config->IsUserBanned(user)) {
+ delete user;
+ return;
+ }
+
+ NetLobby::AddUser(user);
+ SendUsers();
+}
+
+void
+NetLobbyServer::DelUser(NetUser* user)
+{
+ NetLobby::DelUser(user);
+ SendUsers();
+}
+
+void
+NetLobbyServer::SendUsers()
+{
+ Text content;
+
+ ListIter<NetUser> u_iter = users;
+ while (++u_iter) {
+ NetUser* u = u_iter.value();
+ content += u->GetDescription();
+ }
+
+ u_iter.reset();
+
+ while (++u_iter) {
+ NetUser* u = u_iter.value();
+ SendData(u, NET_LOBBY_USER_LIST, content);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetLobbyServer::RequestAuth(NetUser* user)
+{
+ if (user) {
+ Text request = NetAuth::CreateAuthRequest(user);
+
+ if (request.length() > 0)
+ SendData(user, NET_LOBBY_AUTH_USER, request);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetLobbyServer::AddChat(NetUser* user, const char* msg, bool route)
+{
+ NetChatEntry* entry = 0;
+
+ if (user && msg && *msg) {
+ bool msg_ok = false;
+ const char* p = msg;
+
+ while (*p && !msg_ok) {
+ if (!isspace(*p++))
+ msg_ok = true;
+ }
+
+ if (msg_ok) {
+ entry = new(__FILE__,__LINE__) NetChatEntry(user, msg);
+
+ chat_log.append(entry);
+
+ // forward to all clients:
+ if (users.size()) {
+ char buffer[768];
+ char msg_buf[256];
+ char usr_buf[256];
+
+ // safe quotes uses a static buffer,
+ // so make sure to save copies of the
+ // results when using more than one in
+ // a function call...
+
+ strcpy(msg_buf, SafeQuotes(msg));
+ strcpy(usr_buf, SafeQuotes(user->Name()));
+
+ sprintf(buffer, "id %d user \"%s\" msg \"%s\"",
+ entry->GetID(), usr_buf, msg_buf);
+
+ ListIter<NetUser> iter = users;
+ while (++iter) {
+ NetUser* u = iter.value();
+ SendData(u, NET_LOBBY_CHAT, buffer);
+ }
+
+ if (route) {
+ // send to active game:
+ NetUtil::SendChat(0xffff, usr_buf, msg_buf);
+ }
+ }
+ }
+ }
+}
+
+void
+NetLobbyServer::ClearChat()
+{
+ NetLobby::ClearChat();
+}
+
+void
+NetLobbyServer::SaveChat()
+{
+ FILE* f = fopen("chat.txt", "w");
+ if (f) {
+ for (int i = 0; i < chat_log.size(); i++) {
+ NetChatEntry* c = chat_log[i];
+ fprintf(f, "%08x [%s] %s\n",
+ c->GetTime(),
+ c->GetUser().data(),
+ c->GetMessage().data());
+ }
+
+ fclose(f);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetLobbyServer::SelectMission(DWORD id)
+{
+ if (GetStatus() == NetServerInfo::PERSISTENT)
+ return;
+
+ NetLobby::SelectMission(id);
+
+ // inform all users of the selection:
+ char buffer[32];
+ sprintf(buffer, "m_id 0x%08x", selected_mission);
+
+ ListIter<NetUser> iter = users;
+ while (++iter) {
+ NetUser* u = iter.value();
+ SendData(u, NET_LOBBY_MISSION_SELECT, buffer);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+List<NetUnitEntry>&
+NetLobbyServer::GetUnitMap()
+{
+ if (!mission) {
+ unit_map.destroy();
+ return unit_map;
+ }
+
+ List<NetUnitEntry> units;
+ ListIter<MissionElement> iter = mission->GetElements();
+ int i = 0;
+
+ Sim* sim = Sim::GetSim();
+ if (sim && sim->GetElements().size() > 0)
+ iter = sim->GetMissionElements();
+
+ // create new entries for the playable elements in the mission or simulation:
+ while (++iter) {
+ MissionElement* elem = iter.value();
+
+ if (elem->IsPlayable()) {
+ NetUnitEntry* u = 0;
+ if (elem->Count() == 1) {
+ u = new(__FILE__,__LINE__) NetUnitEntry(elem, 0);
+ u->SetLives(elem->RespawnCount() + 1);
+ u->SetMissionRole(elem->MissionRole());
+ u->SetIFF(elem->GetIFF());
+
+ if (elem->GetDesign())
+ u->SetDesign(elem->GetDesign()->name);
+
+ if (elem->Ships().size() > 0) {
+ MissionShip* s = elem->Ships()[0];
+ u->SetIntegrity((int) s->Integrity());
+ }
+ units.append(u);
+ }
+ else {
+ for (int i = 0; i < elem->Count(); i++) {
+ u = new(__FILE__,__LINE__) NetUnitEntry(elem, i+1);
+ u->SetMissionRole(elem->MissionRole());
+ u->SetIFF(elem->GetIFF());
+
+ if (elem->GetDesign())
+ u->SetDesign(elem->GetDesign()->name);
+
+ if (elem->Ships().size() > i) {
+ MissionShip* s = elem->Ships()[i];
+ u->SetLives(s->Respawns() + 1);
+ u->SetIntegrity((int) s->Integrity());
+ }
+ units.append(u);
+ }
+ }
+ }
+ }
+
+ // match new entries with any existing map entries:
+ if (unit_map.size()) {
+ for (i = 0; i < units.size(); i++) {
+ NetUnitEntry* e_new = units[i];
+ NetUnitEntry* e_old = unit_map.find(e_new);
+
+ if (e_old) {
+ e_new->SetUserName(e_old->GetUserName());
+ e_new->SetLock(e_old->GetLocked());
+ }
+ }
+ }
+
+ // rewrite the unit map with the new entries:
+ ClearUnitMap();
+ for (i = 0; i < units.size(); i++) {
+ unit_map.append(units[i]);
+ }
+
+ return unit_map;
+}
+
+void
+NetLobbyServer::MapUnit(int n, const char* user, bool lock)
+{
+ NetLobby::MapUnit(n, user, lock);
+
+ Text reply;
+
+ ListIter<NetUnitEntry> map_iter = GetUnitMap();
+ while (++map_iter) {
+ NetUnitEntry* unit = map_iter.value();
+ reply += unit->GetDescription();
+ }
+
+ ListIter<NetUser> u_iter = users;
+ while (++u_iter) {
+ NetUser* u = u_iter.value();
+ SendData(u, NET_LOBBY_UNIT_LIST, reply);
+ }
+}
+
+void
+NetLobbyServer::UnmapUnit(const char* user)
+{
+ NetLobby::UnmapUnit(user);
+
+ Text reply;
+
+ ListIter<NetUnitEntry> map_iter = GetUnitMap();
+ while (++map_iter) {
+ NetUnitEntry* unit = map_iter.value();
+ reply += unit->GetDescription();
+ }
+
+ ListIter<NetUser> u_iter = users;
+ while (++u_iter) {
+ NetUser* u = u_iter.value();
+ SendData(u, NET_LOBBY_UNIT_LIST, reply);
+ }
+}
+
+void
+NetLobbyServer::SendUnits()
+{
+ Text content;
+
+ ListIter<NetUnitEntry> map_iter = GetUnitMap();
+ while (++map_iter) {
+ NetUnitEntry* unit = map_iter.value();
+ content += unit->GetDescription();
+ }
+
+ ListIter<NetUser> u_iter = users;
+ while (++u_iter) {
+ NetUser* u = u_iter.value();
+ SendData(u, NET_LOBBY_UNIT_LIST, content);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+Text
+NetLobbyServer::Serialize(Mission* m, NetUser* user)
+{
+ Text s;
+
+ if (!m || !user)
+ return s;
+
+ NetUnitEntry* unit = 0;
+
+ ListIter<NetUnitEntry> u_iter = GetUnitMap();
+ while (++u_iter && !unit) {
+ NetUnitEntry* u = u_iter.value();
+ if (u->GetUserName() == user->Name())
+ unit = u;
+ }
+
+ if (unit)
+ s = m->Serialize(unit->GetElemName(), unit->GetIndex());
+
+ return s;
+}
+
+Mission*
+NetLobbyServer::GetSelectedMission()
+{
+ if (mission) {
+ Text content = Serialize(mission, GetLocalUser());
+ Campaign* c = Campaign::SelectCampaign("Multiplayer Missions");
+
+ if (c) {
+ c->LoadNetMission(99999, content.data());
+ return c->GetMission(99999);
+ }
+ }
+
+ return mission;
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetLobbyServer::GameOn()
+{
+ NetHost host;
+ const char* type = "Starshatter";
+ const char* password = "No";
+ char address[32];
+
+ strcpy(address, "0");
+
+ if (server_config) {
+ if (server_config->GetGameType() == NetServerConfig::NET_GAME_PRIVATE)
+ return;
+
+ if (server_config->GetGameType() == NetServerConfig::NET_GAME_LAN) {
+ type = "Starshatter-LAN";
+ sprintf(address, "%d.%d.%d.%d",
+ host.Address().B1(),
+ host.Address().B2(),
+ host.Address().B3(),
+ host.Address().B4());
+ }
+ else {
+ type = "Starshatter";
+ sprintf(address, "0.0.0.0");
+ }
+
+ if (server_config->GetGamePass().length() > 0)
+ password = "Yes";
+ }
+
+ NetBrokerClient::GameOn(server_name,
+ type,
+ address,
+ server_addr.Port(),
+ password);
+}
+
+void
+NetLobbyServer::GameOff()
+{
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetLobbyServer::DoPing(NetPeer* peer, Text s)
+{ }
+
+void
+NetLobbyServer::DoServerInfo(NetPeer* peer, Text s)
+{
+ if (peer && peer->NetID()) {
+ char buffer[1024];
+ WORD gameport = 11101;
+
+ if (server_config)
+ gameport = server_config->GetGamePort();
+
+ sprintf(buffer, "info \"%s\" version \"%s\" mode %d users %d host %s port %d",
+ MachineInfo::GetShortDescription(),
+ versionInfo,
+ GetStatus(),
+ NumUsers(),
+ HasHost() ? "true" : "false",
+ gameport);
+
+ link->SendMessage(peer->NetID(), (BYTE) NET_LOBBY_SERVER_INFO, buffer, strlen(buffer), NetMsg::RELIABLE);
+ }
+}
+
+void
+NetLobbyServer::DoServerMods(NetPeer* peer, Text s)
+{
+ if (peer && peer->NetID()) {
+ Text response;
+ ModConfig* config = ModConfig::GetInstance();
+ List<ModInfo>& mods = config->GetModInfoList();
+ ListIter<ModInfo> mod_iter = mods;
+
+ char buffer[32];
+ sprintf(buffer, "num %d ", mods.size());
+ response += buffer;
+
+ while (++mod_iter) {
+ ModInfo* info = mod_iter.value();
+
+ response += "mod \"";
+ response += info->Name();
+ response += "\" url \"";
+ response += info->URL();
+ response += "\" ver \"";
+ response += info->Version();
+ response += "\" ";
+ }
+
+ link->SendMessage(peer->NetID(), (BYTE) NET_LOBBY_SERVER_MODS, response, response.length(), NetMsg::RELIABLE);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetLobbyServer::DoLogin(NetPeer* peer, Text msg)
+{
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ Text name;
+ Text pass;
+ Text host;
+ Text gamepass;
+ Text signature;
+ Text squadron;
+ Text version;
+ int rank = 0;
+ int flight_time = 0;
+ int missions = 0;
+ int kills = 0;
+ int losses = 0;
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ int num = 0;
+ sscanf(p->value, "%d", &num);
+
+ if (p->name == "name")
+ name = p->value;
+
+ else if (p->name == "pass")
+ pass = p->value;
+
+ else if (p->name == "gamepass")
+ gamepass = p->value;
+
+ else if (p->name == "host")
+ host = p->value;
+
+ else if (p->name == "sig")
+ signature = p->value;
+
+ else if (p->name == "squad")
+ squadron = p->value;
+
+ else if (p->name == "version")
+ version = p->value;
+
+ else if (p->name == "rank")
+ rank = num;
+
+ else if (p->name == "time")
+ flight_time = num;
+
+ else if (p->name == "miss")
+ missions = num;
+
+ else if (p->name == "kill")
+ kills = num;
+
+ else if (p->name == "loss")
+ losses = num;
+ }
+
+ params.destroy();
+
+ // first check the game version:
+ if (version != versionInfo) {
+ Print("NetLobbyServer - user '%s' tried to login with invalid game version '%s'\n",
+ name.data(), version.data());
+
+ return;
+ }
+
+ // next check the game password:
+ if (server_config && server_config->GetGamePass().length() > 0) {
+ if (gamepass != server_config->GetGamePass()) {
+ Print("NetLobbyServer - user '%s' tried to login with invalid game password '%s'\n",
+ name.data(), gamepass.data());
+
+ return;
+ }
+ }
+
+ // now try to log the user in:
+ NetUser* pre_existing = FindUserByName(name);
+
+ // is user already logged in?
+ if (pre_existing) {
+ if (pre_existing->Pass() == pass &&
+ pre_existing->GetAddress().IPAddr() == peer->Address().IPAddr()) {
+ }
+ }
+
+ // otherwise, create a new user:
+ else {
+ NetUser* user = new(__FILE__,__LINE__) NetUser(name);
+ user->SetAddress(peer->Address());
+ user->SetNetID(peer->NetID());
+ user->SetPass(pass);
+ user->SetSignature(signature);
+ user->SetSquadron(squadron);
+ user->SetRank(rank);
+ user->SetFlightTime(flight_time);
+ user->SetMissions(missions);
+ user->SetKills(kills);
+ user->SetLosses(losses);
+
+ if (host == "true" && !HasHost())
+ user->SetHost(true);
+
+ AddUser(user);
+ RequestAuth(user);
+ SendMOTD(user);
+ SendMods(user);
+ }
+}
+
+void
+NetLobbyServer::DoLogout(NetPeer* peer, Text msg)
+{
+ NetUser* user = FindUserByNetID(peer->NetID());
+
+ if (user) {
+ if (user->IsHost())
+ GameStop();
+
+ DelUser(user);
+ }
+}
+
+void
+NetLobbyServer::DoUserAuth(NetPeer* peer, Text msg)
+{
+ NetUser* user = FindUserByNetID(peer->NetID());
+
+ if (user) {
+ NetAuth::AuthUser(user, msg);
+
+ if (!user->IsAuthOK()) {
+ char buffer[256];
+
+ sprintf(buffer, "id %d user \"SERVER\" msg \"**********\"", motd_index++);
+ SendData(user, NET_LOBBY_CHAT, buffer);
+
+ sprintf(buffer, "id %d user \"SERVER\" msg \"*** Your game configuration does not match the server.\"", motd_index++);
+ SendData(user, NET_LOBBY_CHAT, buffer);
+
+ if (server_mods.size() > 0) {
+ sprintf(buffer, "id %d user \"SERVER\" msg \"*** Please check that you have the proper mods deployed in\"", motd_index++);
+ SendData(user, NET_LOBBY_CHAT, buffer);
+ sprintf(buffer, "id %d user \"SERVER\" msg \"*** the order shown above.\"", motd_index++);
+ SendData(user, NET_LOBBY_CHAT, buffer);
+ }
+
+ else {
+ sprintf(buffer, "id %d user \"SERVER\" msg \"*** Please verify that you have no mods deployed.\"", motd_index++);
+ SendData(user, NET_LOBBY_CHAT, buffer);
+ }
+
+ sprintf(buffer, "id %d user \"SERVER\" msg \"*** You will not be permitted to join the game with an invalid\"", motd_index++);
+ SendData(user, NET_LOBBY_CHAT, buffer);
+
+ sprintf(buffer, "id %d user \"SERVER\" msg \"*** configuration. You may reconnect to this server after you\"", motd_index++);
+ SendData(user, NET_LOBBY_CHAT, buffer);
+
+ sprintf(buffer, "id %d user \"SERVER\" msg \"*** have corrected your mod configuration.\"", motd_index++);
+ SendData(user, NET_LOBBY_CHAT, buffer);
+
+ sprintf(buffer, "id %d user \"SERVER\" msg \"**********\"", motd_index++);
+ SendData(user, NET_LOBBY_CHAT, buffer);
+
+ sprintf(buffer, "id %d user \" \" msg \" \"", motd_index++);
+ SendData(user, NET_LOBBY_CHAT, buffer);
+ }
+ }
+}
+
+void
+NetLobbyServer::DoChat(NetPeer* peer, Text msg)
+{
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ Text chat_msg;
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ int num = 0;
+ sscanf(p->value, "%d", &num);
+
+ if (p->name == "msg") {
+ chat_msg = p->value;
+ }
+ }
+
+ params.destroy();
+
+ NetUser* user = FindUserByNetID(peer->NetID());
+
+ if (user) {
+ // receive chat from client:
+ if (chat_msg.length()) {
+ AddChat(user, chat_msg);
+ }
+
+ // request for chat log:
+ else {
+ ListIter<NetChatEntry> iter = chat_log;
+ while (++iter) {
+ NetChatEntry* entry = iter.value();
+
+ char buffer[512];
+ char msg_buf[256];
+ char usr_buf[256];
+
+ // safe quotes uses a static buffer,
+ // so make sure to save copies of the
+ // results when using more than one in
+ // a function call...
+
+ strcpy(msg_buf, SafeQuotes(entry->GetMessage()));
+ strcpy(usr_buf, SafeQuotes(entry->GetUser()));
+
+ sprintf(buffer, "id %d user \"%s\" msg \"%s\"",
+ entry->GetID(), usr_buf, msg_buf);
+
+ SendData(user, NET_LOBBY_CHAT, buffer);
+ }
+ }
+ }
+}
+
+void
+NetLobbyServer::DoUserList(NetPeer* peer, Text msg)
+{
+ NetUser* user = FindUserByNetID(peer->NetID());
+
+ if (user) {
+ Text content;
+
+ if (local_user)
+ content += local_user->GetDescription();
+
+ ListIter<NetUser> iter = users;
+ while (++iter) {
+ NetUser* u = iter.value();
+ content += u->GetDescription();
+ }
+
+ SendData(user, NET_LOBBY_USER_LIST, content);
+ }
+}
+
+void
+NetLobbyServer::DoBanUser(NetPeer* peer, Text msg)
+{
+ NetUser* user = FindUserByNetID(peer->NetID());
+
+ if (user && user->IsHost() && user->IsAuthOK()) {
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ if (params.size() > 0) {
+ NetLobbyParam* p = params[0];
+
+ if (p->name == "user") {
+ Text user_name = p->value;
+
+ NetUser* u = FindUserByName(user_name);
+ if (u && !u->IsHost())
+ BanUser(u);
+ }
+ }
+
+ params.destroy();
+ }
+}
+
+void
+NetLobbyServer::DoMissionList(NetPeer* peer, Text msg)
+{
+ NetUser* user = FindUserByNetID(peer->NetID());
+
+ if (user) {
+ Text reply;
+ char buffer[4096];
+
+ ListIter<Campaign> c_iter = Campaign::GetAllCampaigns();
+ while (++c_iter) {
+ Campaign* c = c_iter.value();
+
+ if (c->GetCampaignId() >= Campaign::MULTIPLAYER_MISSIONS) {
+ sprintf(buffer, "c_id 0x%08x c_name \"%s\" ",
+ c->GetCampaignId(),
+ SafeQuotes(c->Name()));
+
+ reply += buffer;
+ }
+ }
+
+ c_iter.reset();
+
+ while (++c_iter) {
+ Campaign* c = c_iter.value();
+
+ if (c->GetCampaignId() >= Campaign::MULTIPLAYER_MISSIONS) {
+
+ ListIter<MissionInfo> m_iter = c->GetMissionList();
+ while (++m_iter) {
+ MissionInfo* m = m_iter.value();
+
+ int mission_id = (c->GetCampaignId() << NET_CAMPAIGN_SHIFT) + m->id;
+
+ sprintf(buffer, "m_id 0x%08x ", mission_id);
+ reply += buffer;
+
+ reply += "m_name \"";
+ reply += SafeQuotes(m->name);
+
+ // long version of safe quotes:
+ int n = 0;
+ const char* s = m->description.data();
+
+ while (*s && n < 4090) {
+ if (*s == '"') {
+ buffer[n++] = '\'';
+ s++;
+ }
+ else if (*s == '\n') {
+ buffer[n++] = '\\';
+ buffer[n++] = 'n';
+ s++;
+ }
+ else if (*s == '\t') {
+ buffer[n++] = '\\';
+ buffer[n++] = 't';
+ s++;
+ }
+ else {
+ buffer[n++] = *s++;
+ }
+ }
+
+ // don't forget the null terminator!
+ buffer[n] = 0;
+
+ reply += "\" m_desc \"";
+ reply += buffer;
+
+ reply += "\" ";
+ }
+ }
+ }
+
+ SendData(user, NET_LOBBY_MISSION_LIST, reply);
+
+ sprintf(buffer, "m_id 0x%08x", selected_mission);
+ SendData(user, NET_LOBBY_MISSION_SELECT, buffer);
+ }
+}
+
+void
+NetLobbyServer::DoMissionSelect(NetPeer* peer, Text msg)
+{
+ if (GetStatus() == NetServerInfo::PERSISTENT)
+ return;
+
+ NetUser* user = FindUserByNetID(peer->NetID());
+
+ if (user && user->IsHost() && user->IsAuthOK()) {
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ int num = 0;
+ sscanf(p->value, "0x%x", &num);
+
+ if (p->name == "m_id") {
+ SelectMission(num);
+ }
+ }
+
+ params.destroy();
+ }
+}
+
+void
+NetLobbyServer::DoMissionData(NetPeer* peer, Text msg)
+{
+ NetUser* user = FindUserByNetID(peer->NetID());
+
+ if (user && mission && user->IsAuthOK()) {
+ Text reply = Serialize(mission, user);
+ SendData(user, NET_LOBBY_MISSION_DATA, reply);
+
+ FILE* f = ::fopen("multi_mission_send.def", "w");
+ if (f) {
+ ::fwrite(reply.data(), reply.length(), 1, f);
+ ::fclose(f);
+ }
+ }
+}
+
+void
+NetLobbyServer::DoUnitList(NetPeer* peer, Text msg)
+{
+ NetUser* user = FindUserByNetID(peer->NetID());
+
+ if (user && unit_map.size() && user->IsAuthOK()) {
+ Text reply;
+
+ ListIter<NetUnitEntry> iter = GetUnitMap();
+ while (++iter) {
+ NetUnitEntry* unit = iter.value();
+ reply += unit->GetDescription();
+ }
+
+ SendData(user, NET_LOBBY_UNIT_LIST, reply);
+ }
+}
+
+void
+NetLobbyServer::DoMapUnit(NetPeer* peer, Text msg)
+{
+ NetUser* user = FindUserByNetID(peer->NetID());
+
+ if (user && unit_map.size() && user->IsAuthOK()) {
+ List<NetLobbyParam> params;
+ ParseMsg(msg, params);
+
+ int id = 0;
+ bool lock = false;
+ Text user_name;
+
+ for (int i = 0; i < params.size(); i++) {
+ NetLobbyParam* p = params[i];
+
+ if (p->name == "id") {
+ sscanf(p->value, "%d", &id);
+ }
+
+ else if (p->name == "user") {
+ user_name = p->value;
+ }
+
+ else if (p->name == "lock") {
+ lock = (p->value == "true") ? true : false;
+ }
+ }
+
+ params.destroy();
+
+ MapUnit(id, user_name, lock);
+ }
+}
+
+void
+NetLobbyServer::DoGameStart(NetPeer* peer, Text msg)
+{
+ GameStart();
+}
+
+void
+NetLobbyServer::DoGameStop(NetPeer* peer, Text msg)
+{
+ NetUser* user = FindUserByNetID(peer->NetID());
+
+ if (user && user->IsHost() && user->IsAuthOK())
+ GameStop();
+}
diff --git a/Stars45/NetLobbyServer.h b/Stars45/NetLobbyServer.h
new file mode 100644
index 0000000..0a58230
--- /dev/null
+++ b/Stars45/NetLobbyServer.h
@@ -0,0 +1,119 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetLobbyServer.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ UDP Server Engine for Multiplayer Lobby
+*/
+
+
+#ifndef NetLobbyServer_h
+#define NetLobbyServer_h
+
+// FOR NETWORK MENU TESTING (extra latency in msec):
+// #define EXTRA_LATENCY 300
+
+#include "NetLobby.h"
+
+// +-------------------------------------------------------------------+
+
+class Mission;
+class MissionElement;
+class NetChatEntry;
+class NetServerConfig;
+class NetUser;
+class NetUnitEntry;
+
+// +-------------------------------------------------------------------+
+
+class NetLobbyServer : public NetLobby
+{
+public:
+ NetLobbyServer();
+ virtual ~NetLobbyServer();
+
+ int operator == (const NetLobbyServer& s) const { return this == &s; }
+
+ virtual void ExecFrame();
+ virtual bool IsHost() const { return true; }
+ virtual bool IsServer() const { return true; }
+
+ virtual void BanUser(NetUser* user);
+ virtual void AddUser(NetUser* user);
+ virtual void DelUser(NetUser* user);
+ virtual void SendUsers();
+ virtual void RequestAuth(NetUser* user);
+
+ virtual void AddChat(NetUser* user, const char* msg, bool route=true);
+ virtual void ClearChat();
+ virtual void SaveChat();
+
+ virtual List<NetUnitEntry>&
+ GetUnitMap();
+ virtual void MapUnit(int n, const char* user, bool lock=false);
+ virtual void UnmapUnit(const char* user);
+ virtual void SendUnits();
+
+ virtual void SelectMission(DWORD id);
+ virtual Text Serialize(Mission* m, NetUser* u=0);
+ virtual Mission* GetSelectedMission();
+
+ virtual Text GetServerName() const { return server_name; }
+ virtual void SetServerName(const char* s) { server_name = s; }
+ virtual Text GetServerMission() const { return server_mission; }
+ virtual void SetServerMission(const char* script)
+ { server_mission = script; }
+
+ virtual void GameStart();
+ virtual void GameStop();
+
+ virtual void GameOn();
+ virtual void GameOff();
+
+ // singleton locator:
+ static NetLobbyServer* GetInstance();
+
+protected:
+ virtual void CheckSessions();
+
+ virtual void SendData(NetUser* dst, int type, Text msg);
+ virtual void DoPing(NetPeer* peer, Text msg);
+ virtual void DoServerInfo(NetPeer* peer, Text msg);
+ virtual void DoServerMods(NetPeer* peer, Text msg);
+ virtual void DoLogin(NetPeer* peer, Text msg);
+ virtual void DoLogout(NetPeer* peer, Text msg);
+ virtual void DoUserAuth(NetPeer* peer, Text msg);
+ virtual void DoChat(NetPeer* peer, Text msg);
+ virtual void DoUserList(NetPeer* peer, Text msg);
+ virtual void DoBanUser(NetPeer* peer, Text msg);
+ virtual void DoMissionList(NetPeer* peer, Text msg);
+ virtual void DoMissionSelect(NetPeer* peer, Text msg);
+ virtual void DoMissionData(NetPeer* peer, Text msg);
+ virtual void DoUnitList(NetPeer* peer, Text msg);
+ virtual void DoMapUnit(NetPeer* peer, Text msg);
+ virtual void DoGameStart(NetPeer* peer, Text msg);
+ virtual void DoGameStop(NetPeer* peer, Text msg);
+
+ virtual void LoadMOTD();
+ virtual void SendMOTD(NetUser* user);
+ virtual void SendMods(NetUser* user);
+
+ Text server_name;
+ NetAddr server_addr;
+ DWORD announce_time;
+ NetServerConfig* server_config;
+ Text server_mission;
+ int motd_index;
+
+ List<Text> motd;
+};
+
+// +-------------------------------------------------------------------+
+
+#endif NetLobbyServer_h \ No newline at end of file
diff --git a/Stars45/NetPacket.cpp b/Stars45/NetPacket.cpp
new file mode 100644
index 0000000..35a189d
--- /dev/null
+++ b/Stars45/NetPacket.cpp
@@ -0,0 +1,356 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetGame.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Game Manager and Player classes
+*/
+
+#include "MemDebug.h"
+#include "NetPacket.h"
+#include "NetLink.h"
+#include "NetMsg.h"
+#include "Ship.h"
+
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+
+const int PING_SIZE = 8;
+const int SHIP_LOC_SIZE = 36;
+const int JOIN_REQ_SIZE = 116;
+const int JOIN_ANN_SIZE = 116;
+const int QUIT_ANN_SIZE = 8;
+
+// +--------------------------------------------------------------------+
+
+NetPacket::NetPacket(NetMsg* g)
+ : msg(g)
+{ }
+
+NetPacket::NetPacket(DWORD netid, BYTE type)
+{
+ int len = 0;
+ char buf[256];
+ ZeroMemory(buf, 256);
+
+ switch (type) {
+ case NET_PING: len = PING_SIZE; break;
+ case NET_PONG: len = PING_SIZE; break;
+ case NET_OBJ_LOC: len = SHIP_LOC_SIZE; break;
+
+ case NET_JOIN_REQUEST: len = JOIN_REQ_SIZE; break;
+ case NET_JOIN_ANNOUNCE: len = JOIN_ANN_SIZE; break;
+ case NET_QUIT_ANNOUNCE: len = QUIT_ANN_SIZE; break;
+
+ default: len = JOIN_REQ_SIZE; break;
+ }
+
+ msg = new(__FILE__,__LINE__) NetMsg(netid, type, buf, len);
+}
+
+NetPacket::~NetPacket()
+{
+ delete msg;
+}
+
+bool
+NetPacket::Send(NetLink& link)
+{
+ bool sent = false;
+
+ if (msg)
+ sent = link.SendMessage(msg);
+
+ msg = 0;
+ return sent;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+NetPacket::NetID() const
+{
+ if (msg)
+ return msg->NetID();
+
+ return 0;
+}
+
+BYTE
+NetPacket::Type() const
+{
+ if (msg)
+ return msg->Type();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+NetPacket::GetPingSequence()
+{
+ if (msg && msg->Length() >= PING_SIZE) {
+ DWORD* data = (DWORD*) (msg->Data()+4);
+ return *data;
+ }
+
+ return 0;
+}
+
+void
+NetPacket::SetPingSequence(DWORD seq)
+{
+ if (msg && msg->Length() >= PING_SIZE) {
+ DWORD* data = (DWORD*) (msg->Data()+4);
+ *data = seq;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+NetPacket::GetNetID()
+{
+ if (msg && msg->Length() >= PING_SIZE) {
+ DWORD* data = (DWORD*) (msg->Data()+4);
+ return *data;
+ }
+
+ return 0;
+}
+
+void
+NetPacket::SetNetID(DWORD id)
+{
+ if (msg && msg->Length() >= PING_SIZE) {
+ DWORD* data = (DWORD*) (msg->Data()+4);
+ *data = id;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Point
+NetPacket::GetShipLocation()
+{
+ if (msg && msg->Length() >= SHIP_LOC_SIZE) {
+ long* data = (long*) (msg->Data()+8);
+ long x = *(data + 0);
+ long y = *(data + 1);
+ long z = *(data + 2);
+
+ return Point(x/100.0, y/100.0, z/100.0);
+ }
+
+ return Point();
+}
+
+void
+NetPacket::SetShipLocation(const Point& loc)
+{
+ if (msg && msg->Length() >= SHIP_LOC_SIZE) {
+ long x = (long) (loc.x * 100);
+ long y = (long) (loc.y * 100);
+ long z = (long) (loc.z * 100);
+
+ long* data = (long*) (msg->Data()+8);
+
+ *(data + 0) = x;
+ *(data + 1) = y;
+ *(data + 2) = z;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Point
+NetPacket::GetShipVelocity()
+{
+ if (msg && msg->Length() >= SHIP_LOC_SIZE) {
+ short* data = (short*) (msg->Data()+20);
+
+ short dx = *(data + 0);
+ short dy = *(data + 1);
+ short dz = *(data + 2);
+
+ return Point(dx, dy, dz);
+ }
+
+ return Point();
+}
+
+void
+NetPacket::SetShipVelocity(const Point& vel)
+{
+ if (msg && msg->Length() >= SHIP_LOC_SIZE) {
+ short* data = (short*) (msg->Data()+20);
+
+ *(data + 0) = (short) vel.x;
+ *(data + 1) = (short) vel.y;
+ *(data + 2) = (short) vel.z;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Point
+NetPacket::GetShipOrientation()
+{
+ if (msg && msg->Length() >= SHIP_LOC_SIZE) {
+ short* data = (short*) (msg->Data()+26);
+
+ short r = *(data + 0);
+ short p = *(data + 1);
+ short y = *(data + 2);
+
+ return Point(2*PI*r/32767, 2*PI*p/32767, 2*PI*y/32767);
+ }
+
+ return Point();
+}
+
+void
+NetPacket::SetShipOrientation(const Point& rpy)
+{
+ if (msg && msg->Length() >= SHIP_LOC_SIZE) {
+ short* data = (short*) (msg->Data()+26);
+
+ *(data + 0) = (short) (32767*rpy.x/(2*PI));
+ *(data + 1) = (short) (32767*rpy.y/(2*PI));
+ *(data + 2) = (short) (32767*rpy.z/(2*PI));
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+double
+NetPacket::GetThrottle()
+{
+ if (msg && msg->Length() >= SHIP_LOC_SIZE) {
+ BYTE* data = (BYTE*) msg->Data()+32;
+
+ return (double) *data;
+ }
+
+ return 0;
+}
+
+void
+NetPacket::SetThrottle(double t)
+{
+ if (msg && msg->Length() >= SHIP_LOC_SIZE) {
+ BYTE* data = (BYTE*) msg->Data()+32;
+
+ *data = (BYTE) t;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+NetPacket::GetTrigger(int i)
+{
+ if (i >= 0 && i < 8 && msg && msg->Length() >= SHIP_LOC_SIZE) {
+ BYTE* data = (BYTE*) msg->Data()+33;
+
+ BYTE select = 1 << i;
+ return (*data & select)?true:false;
+ }
+
+ return false;
+}
+
+void
+NetPacket::SetTrigger(int i, bool trigger)
+{
+ if (i >= 0 && i < 8 && msg && msg->Length() >= SHIP_LOC_SIZE) {
+ BYTE* data = (BYTE*) msg->Data()+33;
+
+ BYTE select = 1 << i;
+
+ if (trigger)
+ *data = *data | select;
+ else
+ *data = *data & ~select;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+NetPacket::GetName()
+{
+ if (msg && msg->Length() >= JOIN_REQ_SIZE) {
+ BYTE* data = (BYTE*) msg->Data()+20;
+
+ return (const char*) data;
+ }
+
+ return 0;
+}
+
+void
+NetPacket::SetName(const char* name)
+{
+ if (msg && msg->Length() >= JOIN_REQ_SIZE) {
+ BYTE* data = (BYTE*) msg->Data()+20;
+ strncpy((char*) data, name, 32);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+NetPacket::GetDesign()
+{
+ if (msg && msg->Length() >= JOIN_REQ_SIZE) {
+ BYTE* data = (BYTE*) msg->Data()+52;
+
+ return (const char*) data;
+ }
+
+ return 0;
+}
+
+void
+NetPacket::SetDesign(const char* design)
+{
+ if (msg && msg->Length() >= JOIN_REQ_SIZE) {
+ BYTE* data = (BYTE*) msg->Data()+52;
+ strncpy((char*) data, design, 32);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+NetPacket::GetRegion()
+{
+ if (msg && msg->Length() >= JOIN_REQ_SIZE) {
+ BYTE* data = (BYTE*) msg->Data()+84;
+
+ return (const char*) data;
+ }
+
+ return 0;
+}
+
+void
+NetPacket::SetRegion(const char* rgn_name)
+{
+ if (msg && msg->Length() >= JOIN_REQ_SIZE) {
+ BYTE* data = (BYTE*) msg->Data()+84;
+ strncpy((char*) data, rgn_name, 32);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
diff --git a/Stars45/NetPacket.h b/Stars45/NetPacket.h
new file mode 100644
index 0000000..a3527dc
--- /dev/null
+++ b/Stars45/NetPacket.h
@@ -0,0 +1,74 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetPacket.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Wrapper for low-level datagram class
+*/
+
+#ifndef NetPacket_h
+#define NetPacket_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "NetData.h"
+
+// +--------------------------------------------------------------------+
+
+class NetLink;
+class NetMsg;
+
+// +--------------------------------------------------------------------+
+
+class NetPacket
+{
+public:
+ static const char* TYPENAME() { return "NetPacket"; }
+
+ NetPacket(NetMsg* g);
+ NetPacket(DWORD netid, BYTE type);
+ ~NetPacket();
+
+ bool Send(NetLink& link);
+
+ // various accessors:
+ DWORD NetID() const;
+ BYTE Type() const;
+
+ DWORD GetPingSequence();
+ void SetPingSequence(DWORD seq);
+ DWORD GetNetID();
+ void SetNetID(DWORD id);
+ Point GetShipLocation();
+ void SetShipLocation(const Point& loc);
+ Point GetShipVelocity();
+ void SetShipVelocity(const Point& vel);
+ Point GetShipOrientation();
+ void SetShipOrientation(const Point& rpy);
+ double GetThrottle();
+ void SetThrottle(double t);
+ const char* GetName();
+ void SetName(const char* name);
+ const char* GetDesign();
+ void SetDesign(const char* design);
+ const char* GetRegion();
+ void SetRegion(const char* rgn_name);
+ bool GetTrigger(int i);
+ void SetTrigger(int i, bool trigger);
+
+
+protected:
+ NetMsg* msg;
+};
+
+
+// +--------------------------------------------------------------------+
+
+#endif NetPacket_h
+
diff --git a/Stars45/NetPassDlg.cpp b/Stars45/NetPassDlg.cpp
new file mode 100644
index 0000000..faeccb3
--- /dev/null
+++ b/Stars45/NetPassDlg.cpp
@@ -0,0 +1,143 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetPassDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Server Password Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "NetPassDlg.h"
+#include "MenuScreen.h"
+#include "NetClientConfig.h"
+#include "NetLobby.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "EditBox.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(NetPassDlg, OnApply);
+DEF_MAP_CLIENT(NetPassDlg, OnCancel);
+
+// +--------------------------------------------------------------------+
+
+NetPassDlg::NetPassDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ btn_apply(0), btn_cancel(0), edt_pass(0), lbl_name(0)
+{
+ Init(def);
+}
+
+NetPassDlg::~NetPassDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetPassDlg::RegisterControls()
+{
+ btn_apply = (Button*) FindControl(1);
+ btn_cancel = (Button*) FindControl(2);
+
+ REGISTER_CLIENT(EID_CLICK, btn_apply, NetPassDlg, OnApply);
+ REGISTER_CLIENT(EID_CLICK, btn_cancel, NetPassDlg, OnCancel);
+
+ lbl_name = FindControl(110);
+ edt_pass = (EditBox*) FindControl(200);
+
+ if (edt_pass)
+ edt_pass->SetText("");
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetPassDlg::Show()
+{
+ if (!IsShown()) {
+ FormWindow::Show();
+
+ NetClientConfig* config = NetClientConfig::GetInstance();
+
+ if (config && lbl_name) {
+ NetServerInfo* info = config->GetSelectedServer();
+
+ if (info)
+ lbl_name->SetText(info->name);
+ }
+
+ if (edt_pass) {
+ edt_pass->SetText("");
+ edt_pass->SetFocus();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static bool tab_latch = false;
+
+void
+NetPassDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ OnApply(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetPassDlg::OnApply(AWEvent* event)
+{
+ NetClientConfig* config = NetClientConfig::GetInstance();
+
+ if (config && edt_pass) {
+ NetServerInfo* info = config->GetSelectedServer();
+
+ if (info && edt_pass->GetText().length() < 250) {
+ char buffer[256];
+ strcpy(buffer, edt_pass->GetText().data());
+
+ // trim from first occurrence of invalid character
+ char* p = strpbrk(buffer, "\n\r\t");
+ if (p) *p = 0;
+
+ info->password = SafeQuotes(buffer);
+
+ if (manager) {
+ manager->ShowNetLobbyDlg();
+ return;
+ }
+ }
+ }
+
+ if (manager) {
+ manager->ShowNetClientDlg();
+ }
+}
+
+void
+NetPassDlg::OnCancel(AWEvent* event)
+{
+ if (manager)
+ manager->ShowNetClientDlg();
+}
diff --git a/Stars45/NetPassDlg.h b/Stars45/NetPassDlg.h
new file mode 100644
index 0000000..f91c5b4
--- /dev/null
+++ b/Stars45/NetPassDlg.h
@@ -0,0 +1,58 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetPassDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Server Password Dialog Active Window class
+*/
+
+#ifndef NetPassDlg_h
+#define NetPassDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+
+// +--------------------------------------------------------------------+
+
+class NetPassDlg : public FormWindow
+{
+public:
+ NetPassDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~NetPassDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+
+ // Operations:
+ virtual void ExecFrame();
+
+ virtual void OnApply(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+
+protected:
+ MenuScreen* manager;
+
+ Button* btn_apply;
+ Button* btn_cancel;
+ EditBox* edt_pass;
+ ActiveWindow* lbl_name;
+};
+
+#endif NetPassDlg_h
+
diff --git a/Stars45/NetPlayer.cpp b/Stars45/NetPlayer.cpp
new file mode 100644
index 0000000..31d5c38
--- /dev/null
+++ b/Stars45/NetPlayer.cpp
@@ -0,0 +1,464 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetPlayer.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Player (Director) class
+*/
+
+#include "MemDebug.h"
+#include "NetPlayer.h"
+#include "NetGame.h"
+#include "NetMsg.h"
+#include "NetData.h"
+#include "NetUtil.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Shield.h"
+#include "Shot.h"
+#include "Sim.h"
+#include "SimEvent.h"
+#include "System.h"
+#include "Weapon.h"
+#include "WeaponGroup.h"
+#include "Element.h"
+#include "HUDView.h"
+#include "Explosion.h"
+#include "Farcaster.h"
+#include "RadioMessage.h"
+#include "RadioTraffic.h"
+
+#include "NetHost.h"
+#include "Game.h"
+#include "Light.h"
+
+// +--------------------------------------------------------------------+
+
+NetPlayer::~NetPlayer()
+{
+ if (ship) {
+ ship->SetNetworkControl();
+
+ Sim* sim = Sim::GetSim();
+ sim->DestroyShip(ship);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetPlayer::SetShip(Ship* s)
+{
+ if (ship != s) {
+ if (ship) {
+ ship->EnableRepair(true);
+ Ignore(ship);
+ }
+
+ ship = s;
+
+ if (ship) {
+ Observe(ship);
+ ship->SetNetworkControl(this);
+ ship->SetObjID(objid);
+
+ iff = ship->GetIFF();
+
+ // Turn off auto-repair. All repair data should
+ // come in over the network from the remote player:
+
+ ship->EnableRepair(false);
+
+ // Set all ship weapons back to manual fire control.
+ // All trigger events should come over the network,
+ // not from weapon auto aiming ai:
+
+ ListIter<WeaponGroup> iter = ship->Weapons();
+ while (++iter) {
+ WeaponGroup* group = iter.value();
+
+ ListIter<Weapon> w_iter = group->GetWeapons();
+ while (++w_iter) {
+ Weapon* weapon = w_iter.value();
+
+ weapon->SetFiringOrders(Weapon::MANUAL);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+const double BLEED = 0.5;
+
+bool
+NetPlayer::DoObjLoc(NetObjLoc* obj_loc)
+{
+ if (ship && obj_loc) {
+ loc_error = obj_loc->GetLocation() - ship->Location();
+ bleed_time = BLEED;
+
+ ship->SetVelocity(obj_loc->GetVelocity());
+ Point o = obj_loc->GetOrientation();
+ ship->SetAbsoluteOrientation(o.x, o.y, o.z);
+ ship->SetThrottle(obj_loc->GetThrottle() ? 100 : 0);
+ ship->SetAugmenter(obj_loc->GetAugmenter());
+
+ if (obj_loc->GetGearDown())
+ ship->LowerGear();
+ else
+ ship->RaiseGear();
+
+ Shield* shield = ship->GetShield();
+ if (shield)
+ shield->SetPowerLevel(obj_loc->GetShield());
+
+ return true;
+ }
+
+ return false;
+}
+
+bool
+NetPlayer::DoObjHyper(NetObjHyper* obj_hyper)
+{
+ if (ship && obj_hyper) {
+ Sim* sim = Sim::GetSim();
+ SimRegion* rgn = sim->FindRegion(obj_hyper->GetRegion());
+ DWORD fc1_id = obj_hyper->GetFarcaster1();
+ DWORD fc2_id = obj_hyper->GetFarcaster2();
+ Ship* fc1 = 0;
+ Ship* fc2 = 0;
+ int trans = obj_hyper->GetTransitionType();
+
+ if (ship->GetRegion() == rgn) {
+ ::Print("NetPlayer::DoObjHyper ship: '%s' rgn: '%s' trans: %d (IGNORED)\n\n",
+ ship->Name(), obj_hyper->GetRegion().data(), trans);
+
+ return false;
+ }
+
+ ::Print("NetPlayer::DoObjHyper ship: '%s' rgn: '%s' trans: %d\n\n",
+ ship->Name(), obj_hyper->GetRegion().data(), trans);
+
+ // orbital transition?
+ if (trans == Ship::TRANSITION_DROP_ORBIT) {
+ ship->SetTransition(1.0f, Ship::TRANSITION_DROP_ORBIT, ship->Location());
+ ship->CompleteTransition();
+ }
+
+ else if (trans == Ship::TRANSITION_MAKE_ORBIT) {
+ ship->SetTransition(1.0f, Ship::TRANSITION_MAKE_ORBIT, ship->Location());
+ ship->CompleteTransition();
+ }
+
+ else {
+ if (fc1_id)
+ fc1 = sim->FindShipByObjID(fc1_id);
+
+ if (fc2_id)
+ fc2 = sim->FindShipByObjID(fc2_id);
+
+ sim->CreateExplosion(ship->Location(), Point(0,0,0),
+ Explosion::QUANTUM_FLASH, 1.0f, 0, ship->GetRegion());
+
+ sim->RequestHyperJump(ship, rgn, obj_hyper->GetLocation(), trans, fc1, fc2);
+
+ ShipStats* stats = ShipStats::Find(ship->Name());
+ stats->AddEvent(SimEvent::QUANTUM_JUMP, rgn->Name());
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool
+NetPlayer::DoObjTarget(NetObjTarget* obj_target)
+{
+ if (ship && obj_target) {
+ DWORD tgtid = obj_target->GetTgtID();
+ int subid = obj_target->GetSubtarget();
+ SimObject* target = 0;
+ System* subtgt = 0;
+
+ NetGame* net_game = NetGame::GetInstance();
+ if (net_game && tgtid) {
+ target = net_game->FindShipByObjID(tgtid);
+
+ if (target) {
+ if (subid >= 0) {
+ Ship* tgt_ship = (Ship*) target;
+ subtgt = tgt_ship->Systems().at(subid);
+ }
+ }
+ else {
+ target = net_game->FindShotByObjID(tgtid);
+ }
+ }
+
+ ship->SetTarget(target, subtgt, true); // from net = true (don't resend)
+
+ return true;
+ }
+
+ return false;
+}
+
+bool
+NetPlayer::DoObjEmcon(NetObjEmcon* obj_emcon)
+{
+ if (ship && obj_emcon) {
+ int emcon = obj_emcon->GetEMCON();
+ ship->SetEMCON(emcon, true); // from net = true (don't resend)
+
+ return true;
+ }
+
+ return false;
+}
+
+bool
+NetPlayer::DoWepTrigger(NetWepTrigger* trigger)
+{
+ if (ship && trigger) {
+ int index = trigger->GetIndex();
+ int count = trigger->GetCount();
+ DWORD tgtid = trigger->GetTgtID();
+ int subid = trigger->GetSubtarget();
+ bool decoy = trigger->GetDecoy();
+ bool probe = trigger->GetProbe();
+
+ Weapon* w = 0;
+
+ if (decoy) w = ship->GetDecoy();
+ else if (probe) w = ship->GetProbeLauncher();
+ else w = ship->GetWeaponByIndex(index);
+
+ if (w) {
+ SimObject* target = 0;
+ System* subtgt = 0;
+
+ NetGame* net_game = NetGame::GetInstance();
+ if (net_game) {
+ target = net_game->FindShipByObjID(tgtid);
+
+ if (target) {
+ if (subid >= 0) {
+ Ship* tgt_ship = (Ship*) target;
+ subtgt = tgt_ship->Systems().at(subid);
+ }
+ }
+ else {
+ target = net_game->FindShotByObjID(tgtid);
+ }
+
+ // re-broadcast:
+ if (net_game->IsServer()) {
+ if (w->IsPrimary()) {
+ w->NetFirePrimary(target, subtgt, count);
+ net_game->SendData(trigger);
+ }
+ else {
+ DWORD wepid = NetGame::GetNextObjID(NetGame::SHOT);
+ Shot* shot = w->NetFireSecondary(target, subtgt, wepid);
+
+ if (shot && shot->IsDrone()) {
+ if (probe)
+ ship->SetProbe((Drone*) shot);
+
+ else if (decoy)
+ ship->AddActiveDecoy((Drone*) shot);
+ }
+
+ NetWepRelease release;
+ release.SetObjID(objid);
+ release.SetTgtID(tgtid);
+ release.SetSubtarget(subid);
+ release.SetWepID(wepid);
+ release.SetIndex(index);
+ release.SetDecoy(decoy);
+ release.SetProbe(probe);
+
+ net_game->SendData(&release);
+ }
+ }
+
+ else {
+ if (w->IsPrimary()) {
+ w->NetFirePrimary(target, subtgt, count);
+ }
+ }
+
+ return true;
+ }
+ }
+
+ }
+ return false;
+}
+
+bool
+NetPlayer::DoWepRelease(NetWepRelease* release)
+{
+ if (ship && release) {
+ int index = release->GetIndex();
+ DWORD tgtid = release->GetTgtID();
+ DWORD wepid = release->GetWepID();
+ int subid = release->GetSubtarget();
+ bool decoy = release->GetDecoy();
+ bool probe = release->GetProbe();
+
+ Weapon* w = 0;
+
+ if (decoy) w = ship->GetDecoy();
+ else if (probe) w = ship->GetProbeLauncher();
+ else w = ship->GetWeaponByIndex(index);
+
+ if (w && !w->IsPrimary()) {
+ SimObject* target = 0;
+ System* subtgt = 0;
+
+ NetGame* net_game = NetGame::GetInstance();
+ if (net_game) {
+ target = net_game->FindShipByObjID(tgtid);
+
+ if (target) {
+ if (subid >= 0) {
+ Ship* tgt_ship = (Ship*) target;
+ subtgt = tgt_ship->Systems().at(subid);
+ }
+ }
+ else {
+ target = net_game->FindShotByObjID(tgtid);
+ }
+ }
+
+ Shot* shot = w->NetFireSecondary(target, subtgt, wepid);
+
+ if (shot && shot->IsDrone()) {
+ if (probe)
+ ship->SetProbe((Drone*) shot);
+
+ else if (decoy)
+ ship->AddActiveDecoy((Drone*) shot);
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+NetPlayer::DoCommMessage(NetCommMsg* comm_msg)
+{
+ if (ship && comm_msg) {
+ RadioTraffic* traffic = RadioTraffic::GetInstance();
+ RadioMessage* radio_msg = comm_msg->GetRadioMessage();
+
+ if (traffic && radio_msg) {
+ if (radio_msg->DestinationElem() || radio_msg->DestinationShip()) {
+ // radio traffic owns the sent message,
+ // so we must give it a cloned object that is
+ // safe to delete:
+ traffic->SendMessage(new(__FILE__,__LINE__) RadioMessage(*radio_msg));
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+NetPlayer::DoSysDamage(NetSysDamage* sys_damage)
+{
+ if (ship && sys_damage) {
+ System* sys = ship->GetSystem(sys_damage->GetSystem());
+ ship->InflictNetSystemDamage( sys,
+ sys_damage->GetDamage(),
+ sys_damage->GetDamageType());
+
+ return true;
+ }
+
+ return false;
+}
+
+bool
+NetPlayer::DoSysStatus(NetSysStatus* sys_status)
+{
+ if (ship && sys_status) {
+ System* sys = ship->GetSystem(sys_status->GetSystem());
+ ship->SetNetSystemStatus( sys,
+ sys_status->GetStatus(),
+ sys_status->GetPower(),
+ sys_status->GetReactor(),
+ sys_status->GetAvailability());
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetPlayer::ExecFrame(double seconds)
+{
+ if (ship) {
+ // bleed off the location error:
+ if (loc_error.length() > 0 && bleed_time > 0) {
+ double fragment = min(seconds / BLEED, bleed_time);
+ ship->MoveTo(ship->Location() + loc_error * fragment);
+ bleed_time -= fragment;
+ }
+
+ // update the ship location by dead reckoning:
+ ship->MoveTo(ship->Location() + ship->Velocity() * seconds);
+
+ // let the FLCS run, so that the drive flares will work:
+ ship->ExecFLCSFrame();
+
+ // now update the graphic rep and light sources:
+ if (ship->Rep()) {
+ ship->Rep()->MoveTo(ship->Location());
+ ship->Rep()->SetOrientation(ship->Cam().Orientation());
+ }
+
+ if (ship->LightSrc()) {
+ ship->LightSrc()->MoveTo(ship->Location());
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+NetPlayer::Update(SimObject* obj)
+{
+ if (obj == ship) {
+ ship = 0;
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+NetPlayer::GetObserverName() const
+{
+ return "NetPlayer";
+}
diff --git a/Stars45/NetPlayer.h b/Stars45/NetPlayer.h
new file mode 100644
index 0000000..7c44e40
--- /dev/null
+++ b/Stars45/NetPlayer.h
@@ -0,0 +1,98 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetPlayer.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Network Player (Director) class
+*/
+
+#ifndef NetPlayer_h
+#define NetPlayer_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Director.h"
+#include "SimObject.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Sim;
+class Ship;
+class NetMsg;
+class NetObjLoc;
+class NetObjHyper;
+class NetObjTarget;
+class NetObjEmcon;
+class NetSysDamage;
+class NetSysStatus;
+class NetWepTrigger;
+class NetWepRelease;
+class NetWepDestroy;
+class NetCommMsg;
+
+// +--------------------------------------------------------------------+
+
+class NetPlayer : public Director, public SimObserver
+{
+public:
+ static const char* TYPENAME() { return "NetPlayer"; }
+
+ NetPlayer(DWORD nid) : netid(nid), objid(0), ship(0), iff(0) { }
+ ~NetPlayer();
+
+ int operator == (const NetPlayer& p) const { return netid == p.netid; }
+
+ DWORD GetNetID() const { return netid; }
+ DWORD GetObjID() const { return objid; }
+ void SetObjID(DWORD o) { objid = o; }
+
+ int GetIFF() const { return iff; }
+ Ship* GetShip() const { return ship; }
+ void SetShip(Ship* s);
+
+ const char* Name() const { return name; }
+ void SetName(const char* n) { name = n; }
+ const char* SerialNumber() const { return serno; }
+ void SetSerialNumber(const char* n) { serno = n; }
+
+ virtual void ExecFrame(double seconds);
+
+ bool DoObjLoc(NetObjLoc* obj_loc);
+ bool DoObjHyper(NetObjHyper* obj_hyper);
+ bool DoObjTarget(NetObjTarget* obj_target);
+ bool DoObjEmcon(NetObjEmcon* obj_emcon);
+ bool DoWepTrigger(NetWepTrigger* trigger);
+ bool DoWepRelease(NetWepRelease* release);
+ bool DoCommMessage(NetCommMsg* comm_msg);
+ bool DoSysDamage(NetSysDamage* sys_damage);
+ bool DoSysStatus(NetSysStatus* sys_status);
+
+ virtual int Type() const { return 2; }
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+protected:
+ DWORD netid;
+ DWORD objid;
+ Text name;
+ Text serno;
+ Ship* ship;
+ int iff;
+
+ Point loc_error;
+ double bleed_time;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif NetPlayer_h
+
diff --git a/Stars45/NetServerConfig.cpp b/Stars45/NetServerConfig.cpp
new file mode 100644
index 0000000..bf0652a
--- /dev/null
+++ b/Stars45/NetServerConfig.cpp
@@ -0,0 +1,463 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars
+ FILE: NetServerConfig.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include "MemDebug.h"
+#include "NetServerConfig.h"
+#include "NetUser.h"
+
+#include "NetLayer.h"
+#include "NetAddr.h"
+#include "NetGame.h"
+#include "NetHost.h"
+#include "NetServer.h"
+#include "HttpServer.h"
+#include "HttpServletExec.h"
+#include "NetLobbyServer.h"
+#include "NetAuth.h"
+
+#include "token.h"
+#include "Game.h"
+#include "DataLoader.h"
+#include "ParseUtil.h"
+#include "Resource.h"
+
+// +--------------------------------------------------------------------+
+
+NetServerConfig* NetServerConfig::instance = 0;
+extern const char* versionInfo;
+
+// +--------------------------------------------------------------------+
+
+NetServerConfig::NetServerConfig()
+{
+ instance = this;
+
+ name = "Starshatter ";
+ admin_name = "system";
+ admin_pass = "manager";
+ admin_port = 11111;
+ lobby_port = 11100;
+ game_port = 11101;
+ game_type = NET_GAME_PUBLIC;
+ auth_level = NetAuth::NET_AUTH_STANDARD;
+ poolsize = 8;
+ session_timeout = 300;
+
+ name += versionInfo;
+
+ Load();
+}
+
+NetServerConfig::~NetServerConfig()
+{
+ instance = 0;
+
+ banned_addrs.destroy();
+ banned_names.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetServerConfig::Initialize()
+{
+ if (!instance)
+ instance = new(__FILE__,__LINE__) NetServerConfig();
+}
+
+void
+NetServerConfig::Close()
+{
+ delete instance;
+ instance = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetServerConfig::Load()
+{
+ // read the config file:
+ BYTE* block = 0;
+ int blocklen = 0;
+ int port = 0;
+
+ char filename[64];
+ strcpy(filename, "server.cfg");
+
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ ::fseek(f, 0, SEEK_END);
+ blocklen = ftell(f);
+ ::fseek(f, 0, SEEK_SET);
+
+ block = new(__FILE__,__LINE__) BYTE[blocklen+1];
+ block[blocklen] = 0;
+
+ ::fread(block, blocklen, 1, f);
+ ::fclose(f);
+ }
+
+ if (blocklen == 0) {
+ delete [] block;
+ return;
+ }
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'.\n", filename);
+ delete [] block;
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "SERVER_CONFIG") {
+ Print("WARNING: invalid '%s' file. Using defaults\n", filename);
+ delete [] block;
+ return;
+ }
+ }
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "name") {
+ GetDefText(instance->name, def, filename);
+ }
+
+ else if (def->name()->value() == "admin_name") {
+ GetDefText(instance->admin_name, def, filename);
+ }
+
+ else if (def->name()->value() == "admin_pass") {
+ GetDefText(instance->admin_pass, def, filename);
+ }
+
+ else if (def->name()->value() == "game_pass") {
+ GetDefText(instance->game_pass, def, filename);
+ }
+
+ else if (def->name()->value() == "mission") {
+ GetDefText(instance->mission, def, filename);
+ }
+
+ else if (def->name()->value() == "auth_level") {
+ Text level;
+
+ if (def->term() && def->term()->isText()) {
+ GetDefText(level, def, filename);
+
+ level.toLower();
+
+ if (level.indexOf("min") == 0)
+ instance->auth_level = NetAuth::NET_AUTH_MINIMAL;
+
+ else if (level == "standard" || level == "std")
+ instance->auth_level = NetAuth::NET_AUTH_STANDARD;
+
+ else if (level == "secure")
+ instance->auth_level = NetAuth::NET_AUTH_SECURE;
+ }
+
+ else {
+ GetDefNumber(instance->auth_level, def, filename);
+ }
+ }
+
+ else if (def->name()->value() == "admin_port") {
+ GetDefNumber(port, def, filename);
+ if (port > 1024 && port < 48000)
+ instance->admin_port = (WORD) port;
+ }
+
+ else if (def->name()->value() == "lobby_port") {
+ GetDefNumber(port, def, filename);
+ if (port > 1024 && port < 48000)
+ instance->lobby_port = (WORD) port;
+ }
+
+ else if (def->name()->value() == "game_port") {
+ GetDefNumber(port, def, filename);
+ if (port > 1024 && port < 48000)
+ instance->game_port = (WORD) port;
+ }
+
+ else if (def->name()->value() == "game_type") {
+ Text type;
+ GetDefText(type, def, filename);
+ type.setSensitive(false);
+
+ if (type == "LAN")
+ instance->game_type = NET_GAME_LAN;
+
+ else if (type == "private")
+ instance->game_type = NET_GAME_PRIVATE;
+
+ else
+ instance->game_type = NET_GAME_PUBLIC;
+ }
+
+ else if (def->name()->value() == "poolsize") {
+ GetDefNumber(instance->poolsize, def, filename);
+ }
+
+ else if (def->name()->value() == "session_timeout") {
+ GetDefNumber(instance->session_timeout, def, filename);
+ }
+
+ else
+ Print("WARNING: unknown label '%s' in '%s'\n",
+ def->name()->value().data(), filename);
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ term->print();
+ }
+ }
+ }
+ while (term);
+
+ delete [] block;
+
+ LoadBanList();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetServerConfig::Save()
+{
+ FILE* f = fopen("server.cfg", "w");
+ if (f) {
+ fprintf(f, "SERVER_CONFIG\n\n");
+ fprintf(f, "name: \"%s\"\n", instance->name.data());
+ fprintf(f, "admin_name: \"%s\"\n", instance->admin_name.data());
+ fprintf(f, "admin_pass: \"%s\"\n", instance->admin_pass.data());
+ fprintf(f, "game_pass: \"%s\"\n", instance->game_pass.data());
+ fprintf(f, "\n");
+ fprintf(f, "admin_port: %d\n", instance->admin_port);
+ fprintf(f, "lobby_port: %d\n", instance->lobby_port);
+ fprintf(f, "game_port: %d\n", instance->game_port);
+
+ switch (instance->game_type) {
+ case NET_GAME_LAN:
+ fprintf(f, "game_type: LAN\n");
+ break;
+
+ case NET_GAME_PRIVATE:
+ fprintf(f, "game_type: private\n");
+ break;
+
+ case NET_GAME_PUBLIC:
+ default:
+ fprintf(f, "game_type: public\n");
+ break;
+ }
+
+ switch (instance->auth_level) {
+ case NetAuth::NET_AUTH_MINIMAL:
+ fprintf(f, "auth_level: minimal\n");
+ break;
+
+ case NetAuth::NET_AUTH_STANDARD:
+ default:
+ fprintf(f, "auth_level: standard\n");
+ break;
+
+ case NetAuth::NET_AUTH_SECURE:
+ fprintf(f, "auth_level: secure\n");
+ break;
+ }
+
+ fprintf(f, "\n");
+ fprintf(f, "poolsize: %d\n", instance->poolsize);
+ fprintf(f, "session_timeout: %d\n", instance->session_timeout);
+
+ if (mission.length() > 0) {
+ fprintf(f, "\nmission: \"%s\"\n", instance->mission.data());
+ }
+
+ fclose(f);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+NetServerConfig::Clean(const char* s)
+{
+ if (!s || !*s) return Text();
+
+ int len = strlen(s);
+ char* buff = new(__FILE__,__LINE__) char[len+1];
+ ZeroMemory(buff, len+1);
+
+ char* p = buff;
+
+ for (int i = 0; i < len; i++) {
+ char c = s[i];
+
+ if (c >= 32 && c < 127)
+ *p++ = c;
+ }
+
+ Text result(buff);
+ delete [] buff;
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetServerConfig::LoadBanList()
+{
+ // read the config file:
+ BYTE* block = 0;
+ int blocklen = 0;
+ int port = 0;
+
+ char filename[64];
+ strcpy(filename, "banned.cfg");
+
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ ::fseek(f, 0, SEEK_END);
+ blocklen = ftell(f);
+ ::fseek(f, 0, SEEK_SET);
+
+ block = new(__FILE__,__LINE__) BYTE[blocklen+1];
+ block[blocklen] = 0;
+
+ ::fread(block, blocklen, 1, f);
+ ::fclose(f);
+ }
+
+ if (blocklen == 0) {
+ delete [] block;
+ return;
+ }
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'.\n", filename);
+ delete [] block;
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "BANNED_CONFIG") {
+ Print("WARNING: invalid '%s' file.\n", filename);
+ delete [] block;
+ return;
+ }
+ }
+
+ banned_addrs.destroy();
+ banned_names.destroy();
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "name") {
+ Text name;
+ GetDefText(name, def, filename);
+ banned_names.append(new(__FILE__,__LINE__) Text(name));
+ }
+
+ else if (def->name()->value() == "addr") {
+ DWORD addr;
+ GetDefNumber(addr, def, filename);
+ banned_addrs.append(new(__FILE__,__LINE__) NetAddr(addr));
+ }
+ }
+
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ term->print();
+ }
+ }
+ }
+ while (term);
+
+ delete [] block;
+}
+
+void
+NetServerConfig::BanUser(NetUser* user)
+{
+ if (!user || IsUserBanned(user))
+ return;
+
+ NetAddr* user_addr = new(__FILE__,__LINE__) NetAddr(user->GetAddress().IPAddr());
+ Text* user_name = new(__FILE__,__LINE__) Text(user->Name());
+
+ banned_addrs.append(user_addr);
+ banned_names.append(user_name);
+
+ FILE* f = fopen("banned.cfg", "w");
+ if (f) {
+ fprintf(f, "BANNED_CONFIG\n\n");
+
+ ListIter<NetAddr> a_iter = banned_addrs;
+ while (++a_iter) {
+ NetAddr* addr = a_iter.value();
+ fprintf(f, "addr: 0x%08x // %d.%d.%d.%d\n",
+ addr->IPAddr(),
+ addr->B1(),
+ addr->B2(),
+ addr->B3(),
+ addr->B4());
+ }
+
+ fprintf(f, "\n");
+
+ ListIter<Text> n_iter = banned_names;
+ while (++n_iter) {
+ Text* name = n_iter.value();
+ fprintf(f, "name: \"%s\"\n", name->data());
+ }
+
+ fclose(f);
+ }
+}
+
+bool
+NetServerConfig::IsUserBanned(NetUser* user)
+{
+ if (user) {
+ NetAddr user_addr = user->GetAddress();
+ Text user_name = user->Name();
+
+ user_addr.SetPort(0);
+
+ return banned_addrs.contains(&user_addr) ||
+ banned_names.contains(&user_name);
+ }
+
+ return false;
+}
+
diff --git a/Stars45/NetServerConfig.h b/Stars45/NetServerConfig.h
new file mode 100644
index 0000000..14e8484
--- /dev/null
+++ b/Stars45/NetServerConfig.h
@@ -0,0 +1,99 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars
+ FILE: NetServerConfig.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef NetServerConfig_h
+#define NetServerConfig_h
+
+#include "Types.h"
+#include "Game.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class NetAddr;
+class NetUser;
+
+// +--------------------------------------------------------------------+
+
+class NetServerConfig
+{
+public:
+ static const char* TYPENAME() { return "NetServerConfig"; }
+
+ NetServerConfig();
+ ~NetServerConfig();
+
+ enum GAME_TYPE {
+ NET_GAME_LAN,
+ NET_GAME_PRIVATE,
+ NET_GAME_PUBLIC
+ };
+
+ const Text& Name() const { return name; }
+ const Text& GetAdminName() const { return admin_name; }
+ const Text& GetAdminPass() const { return admin_pass; }
+ const Text& GetGamePass() const { return game_pass; }
+ const Text& GetMission() const { return mission; }
+ WORD GetAdminPort() const { return admin_port; }
+ WORD GetLobbyPort() const { return lobby_port; }
+ WORD GetGamePort() const { return game_port; }
+ int GetPoolsize() const { return poolsize; }
+ int GetSessionTimeout() const { return session_timeout; }
+ int GetGameType() const { return game_type; }
+ int GetAuthLevel() const { return auth_level; }
+
+ void SetName(const char* s) { name = Clean(s); }
+ void SetAdminName(const char* s){ admin_name = Clean(s); }
+ void SetAdminPass(const char* s){ admin_pass = Clean(s); }
+ void SetGamePass(const char* s) { game_pass = Clean(s); }
+ void SetMission(const char* s) { mission = Clean(s); }
+ void SetGameType(int t) { game_type = t; }
+ void SetAdminPort(WORD p) { admin_port = p; }
+ void SetLobbyPort(WORD p) { lobby_port = p; }
+ void SetGamePort(WORD p) { game_port = p; }
+ void SetPoolsize(int s) { poolsize = s; }
+ void SetSessionTimeout(int t) { session_timeout = t; }
+ void SetAuthLevel(int n) { auth_level = n; }
+
+ void Load();
+ void Save();
+
+ bool IsUserBanned(NetUser* user);
+ void BanUser(NetUser* user);
+
+ static void Initialize();
+ static void Close();
+ static NetServerConfig* GetInstance() { return instance; }
+
+private:
+ void LoadBanList();
+ Text Clean(const char* s);
+
+ Text name;
+ Text admin_name;
+ Text admin_pass;
+ Text game_pass;
+ Text mission;
+
+ WORD admin_port;
+ WORD lobby_port;
+ WORD game_port;
+ int poolsize;
+ int session_timeout;
+ int game_type;
+ int auth_level;
+
+ List<NetAddr> banned_addrs;
+ List<Text> banned_names;
+
+ static NetServerConfig* instance;
+};
+
+#endif NetServerConfig_h
diff --git a/Stars45/NetServerDlg.cpp b/Stars45/NetServerDlg.cpp
new file mode 100644
index 0000000..0135b34
--- /dev/null
+++ b/Stars45/NetServerDlg.cpp
@@ -0,0 +1,179 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetServerDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "NetServerDlg.h"
+#include "NetServerConfig.h"
+#include "MenuScreen.h"
+#include "Starshatter.h"
+
+#include "NetAddr.h"
+#include "HttpClient.h"
+#include "HttpServer.h"
+
+#include "DataLoader.h"
+#include "Button.h"
+#include "EditBox.h"
+#include "ComboBox.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "MachineInfo.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(NetServerDlg, OnApply);
+DEF_MAP_CLIENT(NetServerDlg, OnCancel);
+
+// +--------------------------------------------------------------------+
+
+NetServerDlg::NetServerDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr)
+{
+ config = NetServerConfig::GetInstance();
+ Init(def);
+}
+
+NetServerDlg::~NetServerDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetServerDlg::RegisterControls()
+{
+ edt_name = (EditBox*) FindControl(200);
+ cmb_type = (ComboBox*) FindControl(201);
+ edt_game_port = (EditBox*) FindControl(202);
+ edt_admin_port = (EditBox*) FindControl(203);
+ edt_game_pass = (EditBox*) FindControl(204);
+ edt_admin_name = (EditBox*) FindControl(205);
+ edt_admin_pass = (EditBox*) FindControl(206);
+
+ btn_apply = (Button*) FindControl(1);
+ btn_cancel = (Button*) FindControl(2);
+
+ REGISTER_CLIENT(EID_CLICK, btn_apply, NetServerDlg, OnApply);
+ REGISTER_CLIENT(EID_CLICK, btn_cancel, NetServerDlg, OnCancel);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetServerDlg::Show()
+{
+ if (!IsShown())
+ FormWindow::Show();
+
+ NetServerConfig::Initialize();
+ config = NetServerConfig::GetInstance();
+
+ if (config) {
+ config->Load();
+
+ char buff[32];
+
+ if (edt_name) {
+ edt_name->SetText(config->Name());
+ edt_name->SetFocus();
+ }
+
+ if (cmb_type)
+ cmb_type->SetSelection(config->GetGameType());
+
+ if (edt_game_port) {
+ sprintf(buff, "%d", config->GetLobbyPort());
+ edt_game_port->SetText(buff);
+ }
+
+ if (edt_admin_port) {
+ sprintf(buff, "%d", config->GetAdminPort());
+ edt_admin_port->SetText(buff);
+ }
+
+ if (edt_game_pass)
+ edt_game_pass->SetText(config->GetGamePass());
+
+ if (edt_admin_name)
+ edt_admin_name->SetText(config->GetAdminName());
+
+ if (edt_admin_pass)
+ edt_admin_pass->SetText(config->GetAdminPass());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetServerDlg::ExecFrame()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetServerDlg::OnApply(AWEvent* event)
+{
+ if (config) {
+ if (edt_name)
+ config->SetName(edt_name->GetText());
+
+ if (cmb_type)
+ config->SetGameType(cmb_type->GetSelectedIndex());
+
+ if (edt_game_port) {
+ int port = 0;
+ sscanf(edt_game_port->GetText(), "%d", &port);
+ config->SetLobbyPort((WORD) port);
+ config->SetGamePort((WORD) port+1);
+ }
+
+ if (edt_admin_port) {
+ int port = 0;
+ sscanf(edt_admin_port->GetText(), "%d", &port);
+ config->SetAdminPort((WORD) port);
+ }
+
+ if (edt_game_pass)
+ config->SetGamePass(edt_game_pass->GetText());
+
+ if (edt_admin_name)
+ config->SetAdminName(edt_admin_name->GetText());
+
+ if (edt_admin_pass)
+ config->SetAdminPass(edt_admin_pass->GetText());
+
+
+ config->Save();
+ }
+
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars) {
+ ::Print("\nSTART LOCAL SERVER\n\n");
+ stars->SetLobbyMode(Starshatter::NET_LOBBY_SERVER);
+ manager->ShowNetLobbyDlg();
+ }
+ else {
+ manager->ShowMenuDlg();
+ }
+}
+
+void
+NetServerDlg::OnCancel(AWEvent* event)
+{
+ NetServerConfig::Close();
+ manager->ShowNetClientDlg();
+}
diff --git a/Stars45/NetServerDlg.h b/Stars45/NetServerDlg.h
new file mode 100644
index 0000000..57d64f4
--- /dev/null
+++ b/Stars45/NetServerDlg.h
@@ -0,0 +1,64 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetServerDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#ifndef NetServerDlg_h
+#define NetServerDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+class NetServerConfig;
+
+// +--------------------------------------------------------------------+
+
+class NetServerDlg : public FormWindow
+{
+public:
+ NetServerDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~NetServerDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnApply(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+
+protected:
+ MenuScreen* manager;
+ NetServerConfig* config;
+
+ EditBox* edt_name;
+ ComboBox* cmb_type;
+ EditBox* edt_game_port;
+ EditBox* edt_admin_port;
+ EditBox* edt_game_pass;
+ EditBox* edt_admin_name;
+ EditBox* edt_admin_pass;
+
+ Button* btn_apply;
+ Button* btn_cancel;
+};
+
+#endif NetServerDlg_h
+
diff --git a/Stars45/NetUnitDlg.cpp b/Stars45/NetUnitDlg.cpp
new file mode 100644
index 0000000..4d227e6
--- /dev/null
+++ b/Stars45/NetUnitDlg.cpp
@@ -0,0 +1,641 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetUnitDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "NetUnitDlg.h"
+#include "NetClientConfig.h"
+#include "ConfirmDlg.h"
+#include "MenuScreen.h"
+#include "Starshatter.h"
+#include "Campaign.h"
+#include "Mission.h"
+#include "Ship.h"
+#include "Player.h"
+#include "Campaign.h"
+#include "ShipDesign.h"
+
+#include "NetAddr.h"
+#include "NetLobbyClient.h"
+#include "NetLobbyServer.h"
+#include "NetUser.h"
+#include "NetChat.h"
+
+#include "DataLoader.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "MachineInfo.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(NetUnitDlg, OnSelect);
+DEF_MAP_CLIENT(NetUnitDlg, OnUnit);
+DEF_MAP_CLIENT(NetUnitDlg, OnMap);
+DEF_MAP_CLIENT(NetUnitDlg, OnUnMap);
+DEF_MAP_CLIENT(NetUnitDlg, OnBan);
+DEF_MAP_CLIENT(NetUnitDlg, OnBanConfirm);
+DEF_MAP_CLIENT(NetUnitDlg, OnApply);
+DEF_MAP_CLIENT(NetUnitDlg, OnCancel);
+
+// +--------------------------------------------------------------------+
+
+NetUnitDlg::NetUnitDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ net_lobby(0), unit_index(-1)
+{
+ last_chat = 0;
+ host_mode = false;
+
+ Init(def);
+}
+
+NetUnitDlg::~NetUnitDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetUnitDlg::RegisterControls()
+{
+ lst_players = (ListBox*) FindControl(201);
+ lst_units = (ListBox*) FindControl(202);
+ lst_chat = (ListBox*) FindControl(211);
+ edt_chat = (EditBox*) FindControl(212);
+
+ REGISTER_CLIENT(EID_SELECT, lst_units, NetUnitDlg, OnUnit);
+
+ if (edt_chat)
+ edt_chat->SetText("");
+
+ btn_select = (Button*) FindControl(206);
+ REGISTER_CLIENT(EID_CLICK, btn_select, NetUnitDlg, OnSelect);
+
+ btn_map = (Button*) FindControl(203);
+ REGISTER_CLIENT(EID_CLICK, btn_map, NetUnitDlg, OnMap);
+
+ btn_unmap = (Button*) FindControl(204);
+ REGISTER_CLIENT(EID_CLICK, btn_unmap, NetUnitDlg, OnUnMap);
+
+ btn_ban = (Button*) FindControl(205);
+
+ if (btn_ban) {
+ REGISTER_CLIENT(EID_CLICK, btn_ban, NetUnitDlg, OnBan);
+ REGISTER_CLIENT(EID_USER_1, btn_ban, NetUnitDlg, OnBanConfirm);
+ }
+
+ btn_apply = (Button*) FindControl(1);
+ REGISTER_CLIENT(EID_CLICK, btn_apply, NetUnitDlg, OnApply);
+
+ btn_cancel = (Button*) FindControl(2);
+ REGISTER_CLIENT(EID_CLICK, btn_cancel, NetUnitDlg, OnCancel);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetUnitDlg::Show()
+{
+ if (!IsShown()) {
+ FormWindow::Show();
+
+ // clear server data:
+ if (lst_players) {
+ lst_players->ClearItems();
+ lst_players->SetSelectedStyle(ListBox::LIST_ITEM_STYLE_FILLED_BOX);
+ lst_players->SetLeading(2);
+ }
+
+ if (lst_units) {
+ lst_units->ClearItems();
+ lst_units->SetSelectedStyle(ListBox::LIST_ITEM_STYLE_FILLED_BOX);
+ lst_units->SetLeading(2);
+ }
+
+ if (lst_chat) lst_chat->ClearItems();
+ last_chat = 0;
+
+ if (btn_apply)
+ btn_apply->SetEnabled(false);
+
+ net_lobby = NetLobby::GetInstance();
+ host_mode = false;
+
+ if (net_lobby) {
+ host_mode = net_lobby->IsHost();
+ }
+
+ if (host_mode) {
+ btn_select->Hide();
+ btn_map->Show();
+ btn_unmap->Show();
+ btn_ban->Show();
+ }
+ else {
+ btn_select->Show();
+ btn_map->Hide();
+ btn_unmap->Hide();
+ btn_ban->Hide();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetUnitDlg::ExecFrame()
+{
+ ExecLobbyFrame();
+
+ if (!net_lobby)
+ return;
+
+ Text player_name;
+
+ if (Player::GetCurrentPlayer())
+ player_name = Player::GetCurrentPlayer()->Name();
+
+ if (btn_select) {
+ bool enable = false;
+
+ if (lst_players && lst_units && btn_select->IsVisible()) {
+ int sel_unit = lst_units->GetSelection();
+
+ enable = sel_unit >= 0 &&
+ lst_units->GetItemText(sel_unit).length() == 0;
+ }
+
+ btn_select->SetEnabled(enable);
+ }
+
+ if (btn_map) {
+ bool enable = false;
+
+ if (lst_players && lst_units && btn_map->IsVisible()) {
+ int sel_play = lst_players->GetSelection();
+ int sel_unit = lst_units->GetSelection();
+
+ enable = sel_unit >= 0 && sel_play >= 0 &&
+ lst_units->GetItemText(sel_unit).length() == 0;
+
+ if (enable && !host_mode) {
+ NetUser* u = (NetUser*) lst_players->GetItemData(sel_play);
+ if (!u || u->Name() != player_name)
+ enable = false;
+ }
+ }
+
+ btn_map->SetEnabled(enable);
+ }
+
+ if (btn_unmap) {
+ bool enable = false;
+
+ if (lst_players && lst_units && btn_unmap->IsVisible()) {
+ int sel_play = lst_players->GetSelection();
+ int sel_unit = lst_units->GetSelection();
+
+ enable = sel_unit >= 0 && lst_units->GetItemText(sel_unit).length() > 0;
+
+ if (enable && !host_mode) {
+ NetUser* u = (NetUser*) lst_units->GetItemData(sel_unit);
+ if (!u || u->Name() != Player::GetCurrentPlayer()->Name())
+ enable = false;
+ }
+ }
+
+ btn_unmap->SetEnabled(enable);
+ }
+
+ if (btn_ban) {
+ bool enable = false;
+
+ if (lst_players && lst_units && host_mode && btn_ban->IsVisible()) {
+ int sel_play = lst_players->GetSelection();
+ int sel_unit = lst_units->GetSelection();
+
+ enable = sel_play >= 0 && sel_unit < 0;
+
+ if (enable) {
+ NetUser* u = (NetUser*) lst_players->GetItemData(sel_play);
+ if (u && u->Name() == player_name)
+ enable = false;
+ }
+ }
+
+ btn_ban->SetEnabled(enable);
+ }
+
+ if (btn_apply) {
+ bool ok_to_start = net_lobby->IsMapped(player_name);
+
+ NetUser* host = net_lobby->GetHost();
+ if (host && !net_lobby->IsMapped(host->Name()))
+ ok_to_start = false;
+
+ btn_apply->SetEnabled(ok_to_start);
+ }
+
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ if (edt_chat && edt_chat->GetText().length() > 0) {
+ SendChat(edt_chat->GetText());
+ edt_chat->SetText("");
+ }
+ }
+
+ CheckUnitMapping();
+ GetChat();
+ GetUnits();
+ GetAvailable();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetUnitDlg::ExecLobbyFrame()
+{
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (!net_lobby) {
+ manager->ShowNetClientDlg();
+ }
+
+ else if (net_lobby->GetLastError() != 0) {
+ if (net_lobby->IsClient()) {
+ if (stars)
+ stars->StopLobby();
+
+ net_lobby = 0;
+ manager->ShowNetClientDlg();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static bool assignment_change = false;
+static int num_users = 0;
+
+void
+NetUnitDlg::CheckUnitMapping()
+{
+ if (net_lobby && lst_units) {
+ ListIter<NetUnitEntry> iter = net_lobby->GetUnitMap();
+ List<NetUnitEntry>& units = iter.container();
+ List<NetUser>& users = net_lobby->GetUsers();
+
+ if (users.size() != num_users) {
+ assignment_change = true;
+ num_users = users.size();
+ }
+
+ if (units.size() != lst_units->NumItems()) {
+ assignment_change = true;
+ }
+
+ else if (lst_units->NumItems()) {
+ for (int i = 0; i < units.size(); i++) {
+ NetUnitEntry* e = units.at(i);
+ Text user_name = e->GetUserName();
+ NetUser* u = net_lobby->FindUserByName(user_name);
+
+ if (lst_units->GetItemData(i) != (DWORD) u)
+ assignment_change = true;
+ }
+ }
+ }
+}
+
+void
+NetUnitDlg::GetAvailable()
+{
+ if (!lst_players) return;
+
+ Text player_name;
+
+ if (Player::GetCurrentPlayer())
+ player_name = Player::GetCurrentPlayer()->Name();
+
+ if (net_lobby) {
+ List<NetUser> available_users;
+
+ NetUser* u = net_lobby->GetLocalUser();
+ if (u) {
+ bool assigned = false;
+ ListIter<NetUnitEntry> iter = net_lobby->GetUnitMap();
+ while (++iter) {
+ NetUnitEntry* unit = iter.value();
+ if (unit->GetUserName() == u->Name())
+ assigned = true;
+ }
+
+ if (!assigned)
+ available_users.append(u);
+ }
+
+ ListIter<NetUser> iter = net_lobby->GetUsers();
+ while (++iter) {
+ NetUser* u = iter.value();
+ bool assigned = false;
+ ListIter<NetUnitEntry> iter = net_lobby->GetUnitMap();
+ while (++iter) {
+ NetUnitEntry* unit = iter.value();
+ if (unit->GetUserName() == u->Name())
+ assigned = true;
+ }
+
+ if (!assigned) {
+ available_users.append(u);
+ }
+ }
+
+ if (available_users.size() != lst_players->NumItems()) {
+ assignment_change = true;
+ lst_players->ClearItems();
+
+ for (int i = 0; i < available_users.size(); i++) {
+ NetUser* u = available_users[i];
+
+ Text name = Player::RankAbrv(u->Rank());
+ name += " ";
+ name += u->Name();
+
+ lst_players->AddItemWithData(name.data(), (DWORD) u);
+
+ if (!host_mode && u->Name() == player_name) {
+ lst_players->SetSelected(lst_players->NumItems()-1);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetUnitDlg::GetUnits()
+{
+ if (!lst_units) return;
+
+ if (net_lobby) {
+ ListIter<NetUnitEntry> iter = net_lobby->GetUnitMap();
+ List<NetUnitEntry>& units = iter.container();
+ List<NetUser>& users = net_lobby->GetUsers();
+
+ if (assignment_change) {
+ lst_units->ClearItems();
+
+ for (int i = 0; i < units.size(); i++) {
+ NetUnitEntry* e = units.at(i);
+
+ char name[64];
+ char team[16];
+
+ if (e->GetIndex())
+ sprintf(name, "%s %d", e->GetElemName().data(), e->GetIndex());
+ else
+ strcpy(name, e->GetElemName().data());
+
+ sprintf(team, "%d", e->GetIFF());
+
+ Text user_name = e->GetUserName();
+
+ NetUser* u = net_lobby->FindUserByName(user_name);
+ if (u) {
+ user_name = Player::RankAbrv(u->Rank());
+ user_name += " ";
+ user_name += u->Name();
+ }
+
+ int count = lst_units->AddItemWithData(user_name, (DWORD) u);
+ lst_units->SetItemText(count-1, 1, name);
+ lst_units->SetItemText(count-1, 2, e->GetDesign());
+
+ if (lst_units->NumColumns() > 4) {
+ lst_units->SetItemText(count-1, 3, Mission::RoleName(e->GetMissionRole()));
+ lst_units->SetItemText(count-1, 4, team);
+ }
+ else if (lst_units->NumColumns() > 3) {
+ lst_units->SetItemText(count-1, 3, team);
+ }
+ }
+ }
+ }
+
+ assignment_change = false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetUnitDlg::GetChat()
+{
+ if (!lst_chat) return;
+
+ if (net_lobby) {
+ int last_item = lst_chat->NumItems() - 1;
+ int count = 0;
+ bool added = false;
+
+ ListIter<NetChatEntry> iter = net_lobby->GetChat();
+ while (++iter) {
+ NetChatEntry* c = iter.value();
+
+ if (count++ > last_item) {
+ int n = lst_chat->AddItem(c->GetUser());
+ lst_chat->SetItemText(n-1, 1, c->GetMessage());
+ added = true;
+ }
+ }
+
+ if (added)
+ lst_chat->EnsureVisible(lst_chat->NumItems()+1);
+ }
+}
+
+
+void
+NetUnitDlg::SendChat(Text msg)
+{
+ if (msg.length() < 1) return;
+
+ Player* player = Player::GetCurrentPlayer();
+
+ if (msg[0] >= '0' && msg[0] <= '9') {
+ if (player) {
+ Text macro = player->ChatMacro(msg[0] - '0');
+
+ if (macro.length())
+ msg = macro;
+ }
+ }
+
+ if (net_lobby)
+ net_lobby->AddChat(0, msg);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetUnitDlg::OnUnit(AWEvent* event)
+{
+ if (!lst_units || host_mode) return;
+
+ static DWORD unit_click_time = 0;
+
+ int list_index = lst_units->GetListIndex();
+
+ // double-click:
+ if (list_index == unit_index && Game::RealTime() - unit_click_time < 350) {
+ OnSelect(0);
+ }
+
+ unit_click_time = Game::RealTime();
+ unit_index = list_index;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetUnitDlg::OnSelect(AWEvent* event)
+{
+ if (!lst_players || !lst_units) return;
+
+ Text player_name;
+
+ if (Player::GetCurrentPlayer())
+ player_name = Player::GetCurrentPlayer()->Name();
+
+ int sel_unit = lst_units->GetSelection();
+
+ if (net_lobby) {
+ net_lobby->MapUnit(sel_unit, player_name, host_mode);
+ lst_units->ClearItems();
+ }
+
+ assignment_change = true;
+
+ GetUnits();
+ GetAvailable();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+NetUnitDlg::OnMap(AWEvent* event)
+{
+ if (!lst_players || !lst_units) return;
+
+ int sel_player = lst_players->GetSelection();
+ int sel_unit = lst_units->GetSelection();
+
+ if (net_lobby) {
+ NetUser* u = (NetUser*) lst_players->GetItemData(sel_player);
+ net_lobby->MapUnit(sel_unit, u->Name(), host_mode);
+ lst_units->ClearItems();
+ }
+
+ assignment_change = true;
+
+ GetUnits();
+ GetAvailable();
+}
+
+void
+NetUnitDlg::OnUnMap(AWEvent* event)
+{
+ if (!lst_players || !lst_units) return;
+
+ if (net_lobby) {
+ net_lobby->MapUnit(lst_units->GetSelection(), 0, host_mode);
+ lst_units->ClearItems();
+ }
+
+ assignment_change = true;
+
+ GetUnits();
+ GetAvailable();
+}
+
+void
+NetUnitDlg::OnBan(AWEvent* event)
+{
+ if (!lst_players) return;
+
+ int sel_player = lst_players->GetSelection();
+
+ if (net_lobby) {
+ NetUser* u = (NetUser*) lst_players->GetItemData(sel_player);
+
+ ConfirmDlg* confirm = manager->GetConfirmDlg();
+ if (confirm) {
+ char msg[512];
+ sprintf(msg, Game::GetText("NetUnitDlg.are-you-sure").data(), u->Name());
+ confirm->SetMessage(msg);
+ confirm->SetTitle(Game::GetText("NetUnitDlg.confirm-ban"));
+ confirm->SetParentControl(btn_ban);
+
+ manager->ShowConfirmDlg();
+ }
+
+ else {
+ OnBanConfirm(event);
+ }
+ }
+}
+
+void
+NetUnitDlg::OnBanConfirm(AWEvent* event)
+{
+ if (!lst_players) return;
+
+ int sel_player = lst_players->GetSelection();
+
+ if (net_lobby) {
+ NetUser* u = (NetUser*) lst_players->GetItemData(sel_player);
+ net_lobby->BanUser(u);
+ lst_units->ClearItems();
+ }
+
+ GetUnits();
+ GetAvailable();
+}
+
+void
+NetUnitDlg::OnApply(AWEvent* event)
+{
+ bool ok = false;
+
+ if (net_lobby) {
+ Mission* mission = net_lobby->GetSelectedMission();
+
+ if (mission) {
+ net_lobby->GameStart();
+ ok = true;
+ }
+ }
+
+ if (!ok) OnCancel(0);
+}
+
+void
+NetUnitDlg::OnCancel(AWEvent* event)
+{
+ if (net_lobby) {
+ net_lobby->SelectMission(0);
+ net_lobby = 0;
+ }
+
+ manager->ShowNetLobbyDlg();
+}
diff --git a/Stars45/NetUnitDlg.h b/Stars45/NetUnitDlg.h
new file mode 100644
index 0000000..5b8b4fd
--- /dev/null
+++ b/Stars45/NetUnitDlg.h
@@ -0,0 +1,93 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetUnitDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Multiplayer Unit Selection Dialog Active Window class
+*/
+
+#ifndef NetUnitDlg_h
+#define NetUnitDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "EditBox.h"
+#include "Font.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+class NetClientConfig;
+class NetLobby;
+class NetChatEntry;
+class NetUser;
+
+// +--------------------------------------------------------------------+
+
+class NetUnitDlg : public FormWindow
+{
+public:
+ NetUnitDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~NetUnitDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnSelect(AWEvent* event);
+ virtual void OnUnit(AWEvent* event);
+ virtual void OnMap(AWEvent* event);
+ virtual void OnUnMap(AWEvent* event);
+ virtual void OnBan(AWEvent* event);
+ virtual void OnBanConfirm(AWEvent* event);
+ virtual void OnApply(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+
+ virtual void ExecLobbyFrame();
+
+ virtual bool GetHostMode() const { return host_mode; }
+ virtual void SetHostMode(bool h) { host_mode = h; }
+
+protected:
+ virtual void GetAvailable();
+ virtual void GetUnits();
+ virtual void GetChat();
+ virtual void SendChat(Text msg);
+ virtual void CheckUnitMapping();
+
+ MenuScreen* manager;
+
+ ListBox* lst_players;
+ ListBox* lst_units;
+ ListBox* lst_chat;
+ EditBox* edt_chat;
+
+ Button* btn_select;
+ Button* btn_map;
+ Button* btn_unmap;
+ Button* btn_ban;
+ Button* btn_apply;
+ Button* btn_cancel;
+
+ NetLobby* net_lobby;
+
+ int last_chat;
+ int unit_index;
+ bool host_mode;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif NetUnitDlg_h
+
diff --git a/Stars45/NetUser.cpp b/Stars45/NetUser.cpp
new file mode 100644
index 0000000..ce9217f
--- /dev/null
+++ b/Stars45/NetUser.cpp
@@ -0,0 +1,117 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetUser.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ This class represents a user connecting to the multiplayer lobby
+*/
+
+
+#include "MemDebug.h"
+#include "NetUser.h"
+#include "NetAuth.h"
+#include "NetLobby.h"
+#include "Player.h"
+#include "FormatUtil.h"
+
+// +-------------------------------------------------------------------+
+
+static Color user_colors[] = {
+ Color::BrightBlue,
+ Color::BrightRed,
+ Color::BrightGreen,
+ Color::Cyan,
+ Color::Orange,
+ Color::Magenta,
+ Color::Yellow,
+ Color::White,
+ Color::Gray,
+ Color::DarkRed,
+ Color::Tan,
+ Color::Violet
+};
+
+static int user_color_index = 0;
+
+// +-------------------------------------------------------------------+
+
+NetUser::NetUser(const char* n)
+ : name(n), netid(0), host(false),
+ rank(0), flight_time(0), missions(0), kills(0), losses(0),
+ auth_state(NetAuth::NET_AUTH_INITIAL),
+ auth_level(NetAuth::NET_AUTH_MINIMAL)
+{
+ if (user_color_index < 0 || user_color_index >= 12)
+ user_color_index = 0;
+
+ color = user_colors[user_color_index++];
+
+ ZeroMemory(salt, sizeof(salt));
+}
+
+NetUser::NetUser(const Player* player)
+ : netid(0), host(false),
+ rank(0), flight_time(0), missions(0), kills(0), losses(0),
+ auth_state(NetAuth::NET_AUTH_INITIAL),
+ auth_level(NetAuth::NET_AUTH_MINIMAL)
+{
+ if (user_color_index < 0 || user_color_index >= 12)
+ user_color_index = 0;
+
+ color = user_colors[user_color_index++];
+
+ if (player) {
+ name = player->Name();
+ pass = player->Password();
+ signature = player->Signature();
+ squadron = player->Squadron();
+ rank = player->Rank();
+ flight_time = player->FlightTime();
+ missions = player->Missions();
+ kills = player->Kills();
+ losses = player->Losses();
+ }
+
+ ZeroMemory(salt, sizeof(salt));
+}
+
+NetUser::~NetUser()
+{ }
+
+// +-------------------------------------------------------------------+
+
+Text
+NetUser::GetDescription()
+{
+ Text content;
+
+ content += "name \"";
+ content += SafeQuotes(name);
+ content += "\" sig \"";
+ content += SafeQuotes(signature);
+ content += "\" squad \"";
+ content += SafeQuotes(squadron);
+
+ char buffer[256];
+ sprintf(buffer, "\" rank %d time %d miss %d kill %d loss %d host %s ",
+ rank, flight_time, missions, kills, losses,
+ host ? "true" : "false");
+
+ content += buffer;
+
+ return content;
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+NetUser::IsAuthOK() const
+{
+ return auth_state == NetAuth::NET_AUTH_OK;
+} \ No newline at end of file
diff --git a/Stars45/NetUser.h b/Stars45/NetUser.h
new file mode 100644
index 0000000..6d8c9eb
--- /dev/null
+++ b/Stars45/NetUser.h
@@ -0,0 +1,112 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetUser.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ This class represents a user connecting to the multiplayer lobby
+*/
+
+
+#ifndef NetUser_h
+#define NetUser_h
+
+#include "Types.h"
+#include "NetAddr.h"
+#include "NetLobby.h"
+#include "Color.h"
+
+// +-------------------------------------------------------------------+
+
+class Player;
+
+// +-------------------------------------------------------------------+
+
+class NetUser
+{
+public:
+ static const char* TYPENAME() { return "NetUser"; }
+
+ NetUser(const char* name);
+ NetUser(const Player* player);
+ virtual ~NetUser();
+
+ int operator == (const NetUser& u) const { return this == &u; }
+
+ const Text& Name() const { return name; }
+ const Text& Pass() const { return pass; }
+ const NetAddr& GetAddress() const { return addr; }
+ Color GetColor() const { return color; }
+ const Text& GetSessionID() const { return session_id; }
+ DWORD GetNetID() const { return netid; }
+ bool IsHost() const { return host; }
+
+ int AuthLevel() const { return auth_level; }
+ int AuthState() const { return auth_state; }
+ const char* Salt() const { return salt; }
+ bool IsAuthOK() const;
+
+ const Text& Squadron() const { return squadron; }
+ const Text& Signature() const { return signature; }
+ int Rank() const { return rank; }
+ int FlightTime() const { return flight_time; }
+ int Missions() const { return missions; }
+ int Kills() const { return kills; }
+ int Losses() const { return losses; }
+
+ void SetName(const char* n) { name = n; }
+ void SetPass(const char* p) { pass = p; }
+ void SetAddress(const NetAddr& a)
+ { addr = a; }
+
+ void SetColor(Color c) { color = c; }
+ void SetNetID(DWORD id) { netid = id; }
+ void SetSessionID(Text s) { session_id = s; }
+ void SetHost(bool h) { host = h; }
+
+ void SetAuthLevel(int n) { auth_level = n; }
+ void SetAuthState(int n) { auth_state = n; }
+ void SetSalt(const char* s) { strcpy(salt, s);}
+
+ void SetSquadron(const char* s) { squadron = s; }
+ void SetSignature(const char* s){ signature = s; }
+ void SetRank(int n) { rank = n; }
+ void SetFlightTime(int n) { flight_time = n;}
+ void SetMissions(int n) { missions = n; }
+ void SetKills(int n) { kills = n; }
+ void SetLosses(int n) { losses = n; }
+
+ Text GetDescription();
+
+protected:
+ Text name;
+ Text pass;
+ Text session_id;
+ NetAddr addr;
+ DWORD netid;
+ Color color;
+ bool host;
+
+ // authentication:
+ int auth_state;
+ int auth_level;
+ char salt[33];
+
+ // stats:
+ Text squadron;
+ Text signature;
+ int rank;
+ int flight_time;
+ int missions;
+ int kills;
+ int losses;
+};
+
+// +-------------------------------------------------------------------+
+
+#endif NetUser_h \ No newline at end of file
diff --git a/Stars45/NetUtil.cpp b/Stars45/NetUtil.cpp
new file mode 100644
index 0000000..d0360f7
--- /dev/null
+++ b/Stars45/NetUtil.cpp
@@ -0,0 +1,424 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetUtil.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Utility class to simplify sending NetData messages.
+*/
+
+
+#include "MemDebug.h"
+#include "NetUtil.h"
+#include "NetData.h"
+#include "NetGame.h"
+#include "NetGameServer.h"
+
+#include "Element.h"
+#include "Instruction.h"
+#include "Random.h"
+#include "Ship.h"
+#include "Shot.h"
+#include "System.h"
+#include "Weapon.h"
+
+// +-------------------------------------------------------------------+
+
+void
+NetUtil::SendObjDamage(SimObject* obj, double dmg, Shot* shot)
+{
+ NetGame* net_game = NetGame::GetInstance();
+ if (!net_game || !obj) return;
+
+ if (net_game->IsServer() && obj->GetObjID()) {
+ NetObjDamage damage;
+ damage.SetObjID(obj->GetObjID());
+ damage.SetDamage((float) dmg);
+
+ if (shot)
+ damage.SetShotID(shot->GetObjID());
+
+ net_game->SendData(&damage);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetUtil::SendSysDamage(Ship* obj, System* sys, double dmg)
+{
+ NetGame* net_game = NetGame::GetInstance();
+ if (!net_game || !obj || !sys) return;
+
+ if (net_game->IsServer() && obj->GetObjID()) {
+ NetSysDamage damage;
+ damage.SetObjID(obj->GetObjID());
+ damage.SetDamage(dmg);
+ damage.SetSystem(sys->GetID());
+
+ net_game->SendData(&damage);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetUtil::SendSysStatus(Ship* obj, System* sys)
+{
+ NetGame* net_game = NetGame::GetInstance();
+ if (!net_game || !obj || !sys) return;
+
+ if (obj->GetObjID()) {
+ NetSysStatus status;
+ status.SetObjID(obj->GetObjID());
+ status.SetSystem( (int) sys->GetID());
+ status.SetStatus( (int) sys->Status());
+ status.SetPower( (int) sys->GetPowerLevel());
+ status.SetReactor((int) sys->GetSourceIndex());
+ status.SetAvailablility(sys->Availability());
+
+ net_game->SendData(&status);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetUtil::SendObjKill(Ship* obj, const Ship* killer, int type, int deck)
+{
+ NetGame* net_game = NetGame::GetInstance();
+ if (!net_game || !obj) return;
+
+ if (type == NetObjKill::KILL_DOCK || (net_game->IsServer() && obj->GetObjID())) {
+ NetObjKill kill;
+ kill.SetObjID(obj->GetObjID());
+ kill.SetFlightDeck(deck);
+
+ if (killer)
+ kill.SetKillerID(killer->GetObjID());
+
+ kill.SetKillType(type);
+
+ if (type != NetObjKill::KILL_DOCK && obj->RespawnCount() > 0) {
+ Print("NetObjKill preparing respawn for %s\n", obj->Name());
+
+ Point respawn_loc = RandomPoint() * 1.75;
+ kill.SetRespawn(true);
+ kill.SetRespawnLoc(respawn_loc);
+ obj->SetRespawnLoc(respawn_loc);
+ }
+ else {
+ Print("NetObjKill no respawn for %s\n", obj->Name());
+ }
+
+ net_game->SendData(&kill);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetUtil::SendObjHyper(Ship* obj, const char* rgn, const Point& loc,
+ const Ship* fc1, const Ship* fc2, int transtype)
+{
+ NetGame* net_game = NetGame::GetInstance();
+ if (!net_game || !obj) return;
+
+ if (obj->GetObjID()) {
+ NetObjHyper obj_hyper;
+ obj_hyper.SetObjID(obj->GetObjID());
+ obj_hyper.SetRegion(rgn);
+ obj_hyper.SetLocation(loc);
+ obj_hyper.SetTransitionType(transtype);
+
+ if (fc1)
+ obj_hyper.SetFarcaster1(fc1->GetObjID());
+
+ if (fc2)
+ obj_hyper.SetFarcaster2(fc2->GetObjID());
+
+ net_game->SendData(&obj_hyper);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetUtil::SendObjTarget(Ship* obj)
+{
+ NetGame* net_game = NetGame::GetInstance();
+ if (!net_game || !obj) return;
+
+ if (obj->GetObjID()) {
+ NetObjTarget obj_target;
+ obj_target.SetObjID(obj->GetObjID());
+
+ SimObject* target = obj->GetTarget();
+ if (target) {
+ obj_target.SetTgtID(target->GetObjID());
+
+ System* subtarget = obj->GetSubTarget();
+
+ if (subtarget) {
+ Ship* s = (Ship*) target;
+ obj_target.SetSubtarget(subtarget->GetID());
+ }
+ }
+
+ net_game->SendData(&obj_target);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetUtil::SendObjEmcon(Ship* obj)
+{
+ NetGame* net_game = NetGame::GetInstance();
+ if (!net_game || !obj) return;
+
+ if (obj->GetObjID()) {
+ NetObjEmcon obj_emcon;
+ obj_emcon.SetObjID(obj->GetObjID());
+ obj_emcon.SetEMCON(obj->GetEMCON());
+ net_game->SendData(&obj_emcon);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetUtil::SendWepTrigger(Weapon* wep, int count)
+{
+ NetGame* net_game = NetGame::GetInstance();
+ if (!net_game || !wep) return;
+
+ if (wep->IsPrimary() || net_game->IsClient()) {
+ NetWepTrigger trigger;
+ trigger.SetObjID(wep->Owner()->GetObjID());
+
+ SimObject* target = wep->GetTarget();
+ if (target) {
+ trigger.SetTgtID(target->GetObjID());
+
+ System* subtarget = wep->GetSubTarget();
+
+ if (subtarget) {
+ Ship* s = (Ship*) target;
+ trigger.SetSubtarget(subtarget->GetID());
+ }
+ }
+
+ trigger.SetIndex(wep->GetIndex());
+ trigger.SetCount(count);
+ trigger.SetDecoy(wep->IsDecoy());
+ trigger.SetProbe(wep->IsProbe());
+
+ net_game->SendData(&trigger);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetUtil::SendWepRelease(Weapon* wep, Shot* shot)
+{
+ NetGame* net_game = NetGame::GetInstance();
+ if (!net_game || !wep || !shot) return;
+
+ if (net_game->IsServer() && wep->IsMissile()) {
+ DWORD wepid = NetGame::GetNextObjID(NetGame::SHOT);
+ shot->SetObjID(wepid);
+
+ NetWepRelease release;
+ release.SetObjID(wep->Owner()->GetObjID());
+
+ SimObject* target = wep->GetTarget();
+ if (target)
+ release.SetTgtID(target->GetObjID());
+
+ System* subtarget = wep->GetSubTarget();
+ if (target && subtarget && target->Type() == SimObject::SIM_SHIP) {
+ Ship* tgt = (Ship*) target;
+ release.SetSubtarget(subtarget->GetID());
+ }
+
+ release.SetWepID(wepid);
+ release.SetIndex(wep->GetIndex());
+ release.SetDecoy(shot->IsDecoy());
+ release.SetProbe(shot->IsProbe());
+
+ net_game->SendData(&release);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetUtil::SendWepDestroy(Shot* shot)
+{
+ NetGame* net_game = NetGame::GetInstance();
+ if (!net_game || !shot) return;
+
+ if (net_game->IsServer() && shot->GetObjID()) {
+ NetWepDestroy destroy;
+
+ destroy.SetObjID(shot->GetObjID());
+
+ net_game->SendData(&destroy);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetUtil::SendChat(DWORD dst, const char* name, const char* text)
+{
+ NetGame* net_game = NetGame::GetInstance();
+ if (!net_game || !name || !*name || !text || !*text) return;
+
+ NetChatMsg chat_msg;
+ chat_msg.SetDstID(dst);
+ chat_msg.SetName(name);
+ chat_msg.SetText(text);
+
+ if (net_game->IsClient()) {
+ net_game->SendData(&chat_msg);
+ }
+
+ else {
+ NetGameServer* net_game_server = (NetGameServer*) net_game;
+ net_game_server->RouteChatMsg(chat_msg);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetUtil::SendElemRequest(const char* name)
+{
+ NetGame* net_game = NetGame::GetInstance();
+ if (!net_game || !name) return;
+
+ NetElemRequest elem_request;
+ elem_request.SetName(name);
+
+ ::Print("NetUtil::SendElemRequest name: '%s'\n", name);
+ net_game->SendData(&elem_request);
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetUtil::SendElemCreate(Element* elem, int squadron, int* slots, bool alert, bool in_flight)
+{
+ NetGame* net_game = NetGame::GetInstance();
+ if (!net_game || !elem) return;
+
+ NetElemCreate elem_create;
+ elem_create.SetName(elem->Name());
+ elem_create.SetType(elem->Type());
+ elem_create.SetIFF(elem->GetIFF());
+ elem_create.SetIntel(elem->IntelLevel());
+ elem_create.SetLoadout(elem->Loadout());
+ elem_create.SetSquadron(squadron);
+ elem_create.SetSlots(slots);
+ elem_create.SetAlert(alert);
+ elem_create.SetInFlight(in_flight);
+
+ if (elem->NumObjectives() > 0) {
+ Instruction* obj = elem->GetObjective(0);
+
+ if (obj) {
+ elem_create.SetObjCode(obj->Action());
+ elem_create.SetObjective(obj->TargetName());
+ }
+ }
+
+ if (elem->GetCarrier())
+ elem_create.SetCarrier(elem->GetCarrier()->Name());
+
+ if (elem->GetCommander())
+ elem_create.SetCommander(elem->GetCommander()->Name());
+
+ ::Print("NetUtil::SendElemCreate iff: %d name: '%s'\n", elem->GetIFF(), elem->Name().data());
+ net_game->SendData(&elem_create);
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetUtil::SendShipLaunch(Ship* carrier, int squadron, int slot)
+{
+ NetGame* net_game = NetGame::GetInstance();
+ if (!net_game || !carrier) return;
+
+ if (carrier->GetObjID()) {
+ NetShipLaunch ship_launch;
+
+ ship_launch.SetObjID(carrier->GetObjID());
+ ship_launch.SetSquadron(squadron);
+ ship_launch.SetSlot(slot);
+
+ net_game->SendData(&ship_launch);
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetUtil::SendNavData(bool add, Element* elem, int index, Instruction* navpt)
+{
+ NetGame* net_game = NetGame::GetInstance();
+ if (!net_game || !elem || !navpt) return;
+
+ // resolve rloc before copying the navpoint into the net nav data structure:
+ Point loc = navpt->Location();
+
+ NetNavData nav_data;
+ nav_data.SetObjID(net_game->GetObjID());
+ nav_data.SetAdd(add);
+ nav_data.SetElem(elem->Name());
+ nav_data.SetIndex(index);
+ nav_data.SetNavPoint(navpt);
+
+ net_game->SendData(&nav_data);
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetUtil::SendNavDelete(Element* elem, int index)
+{
+ NetGame* net_game = NetGame::GetInstance();
+ if (!net_game || !elem) return;
+
+ NetNavDelete nav_delete;
+ nav_delete.SetObjID(net_game->GetObjID());
+ nav_delete.SetElem(elem->Name());
+ nav_delete.SetIndex(index);
+
+ net_game->SendData(&nav_delete);
+}
+
+// +-------------------------------------------------------------------+
+
+void
+NetUtil::SendSelfDestruct(Ship* obj, double dmg)
+{
+ NetGame* net_game = NetGame::GetInstance();
+ if (!net_game || !obj) return;
+
+ if (obj->GetObjID()) {
+ NetSelfDestruct sd;
+ sd.SetObjID(obj->GetObjID());
+ sd.SetDamage((float) dmg);
+
+ net_game->SendData(&sd);
+ }
+}
diff --git a/Stars45/NetUtil.h b/Stars45/NetUtil.h
new file mode 100644
index 0000000..d0b277e
--- /dev/null
+++ b/Stars45/NetUtil.h
@@ -0,0 +1,56 @@
+/* Project STARSHATTER
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: NetUtil.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Utility class to simplify sending NetData messages.
+*/
+
+
+#ifndef NetUtil_h
+#define NetUtil_h
+
+#include "Types.h"
+#include "Geometry.h"
+
+// +-------------------------------------------------------------------+
+
+class Element;
+class Instruction;
+class SimObject;
+class Ship;
+class Shot;
+class System;
+class Weapon;
+
+// +-------------------------------------------------------------------+
+
+class NetUtil
+{
+public:
+ static void SendObjDamage(SimObject* obj, double damage, Shot* shot=0);
+ static void SendObjKill(Ship* obj, const Ship* killer, int type=0, int deck=0);
+ static void SendObjHyper(Ship* obj, const char* rgn, const Point& loc, const Ship* fc1=0, const Ship* fc2=0, int ttype=0);
+ static void SendObjTarget(Ship* obj);
+ static void SendObjEmcon(Ship* obj);
+ static void SendSysDamage(Ship* obj, System* sys, double damage);
+ static void SendSysStatus(Ship* obj, System* sys);
+ static void SendWepTrigger(Weapon* wep, int count=1);
+ static void SendWepRelease(Weapon* wep, Shot* shot);
+ static void SendWepDestroy(Shot* shot);
+ static void SendChat(DWORD dst, const char* name, const char* text);
+ static void SendElemRequest(const char* name);
+ static void SendElemCreate(Element* elem, int squadron, int* slots, bool alert, bool in_flight=false);
+ static void SendShipLaunch(Ship* carrier, int squadron, int slot);
+ static void SendNavData(bool add, Element* elem, int index, Instruction* navpt);
+ static void SendNavDelete(Element* elem, int index);
+ static void SendSelfDestruct(Ship* ship, double damage);
+};
+
+#endif NetUtil_h \ No newline at end of file
diff --git a/Stars45/OptDlg.cpp b/Stars45/OptDlg.cpp
new file mode 100644
index 0000000..e750a9b
--- /dev/null
+++ b/Stars45/OptDlg.cpp
@@ -0,0 +1,322 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: OptDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "OptDlg.h"
+#include "MenuScreen.h"
+#include "Starshatter.h"
+#include "Ship.h"
+#include "HUDView.h"
+#include "Player.h"
+
+#include "DataLoader.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "MachineInfo.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(OptDlg, OnApply);
+DEF_MAP_CLIENT(OptDlg, OnCancel);
+DEF_MAP_CLIENT(OptDlg, OnEnter);
+DEF_MAP_CLIENT(OptDlg, OnExit);
+DEF_MAP_CLIENT(OptDlg, OnAudio);
+DEF_MAP_CLIENT(OptDlg, OnVideo);
+DEF_MAP_CLIENT(OptDlg, OnOptions);
+DEF_MAP_CLIENT(OptDlg, OnControls);
+DEF_MAP_CLIENT(OptDlg, OnMod);
+
+// +--------------------------------------------------------------------+
+
+OptDlg::OptDlg(Screen* s, FormDef& def, BaseScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ apply(0), cancel(0), vid_btn(0), aud_btn(0), ctl_btn(0), opt_btn(0), mod_btn(0),
+ closed(true)
+{
+ Init(def);
+}
+
+OptDlg::~OptDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+OptDlg::RegisterControls()
+{
+ if (apply)
+ return;
+
+ flight_model = (ComboBox*) FindControl(201);
+ flying_start = (ComboBox*) FindControl(211);
+ landings = (ComboBox*) FindControl(202);
+ ai_difficulty = (ComboBox*) FindControl(203);
+ hud_mode = (ComboBox*) FindControl(204);
+ hud_color = (ComboBox*) FindControl(205);
+ ff_mode = (ComboBox*) FindControl(206);
+ grid_mode = (ComboBox*) FindControl(207);
+ gunsight = (ComboBox*) FindControl(208);
+ description = FindControl(500);
+ apply = (Button*) FindControl(1);
+ cancel = (Button*) FindControl(2);
+ vid_btn = (Button*) FindControl(901);
+ aud_btn = (Button*) FindControl(902);
+ ctl_btn = (Button*) FindControl(903);
+ opt_btn = (Button*) FindControl(904);
+ mod_btn = (Button*) FindControl(905);
+
+ if (flight_model) {
+ REGISTER_CLIENT(EID_MOUSE_ENTER, flight_model, OptDlg, OnEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, flight_model, OptDlg, OnExit);
+ }
+
+ if (flying_start) {
+ REGISTER_CLIENT(EID_MOUSE_ENTER, flying_start, OptDlg, OnEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, flying_start, OptDlg, OnExit);
+ }
+
+ if (landings) {
+ REGISTER_CLIENT(EID_MOUSE_ENTER, landings, OptDlg, OnEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, landings, OptDlg, OnExit);
+ }
+
+ if (ai_difficulty) {
+ REGISTER_CLIENT(EID_MOUSE_ENTER, ai_difficulty, OptDlg, OnEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, ai_difficulty, OptDlg, OnExit);
+ }
+
+ if (hud_mode) {
+ REGISTER_CLIENT(EID_MOUSE_ENTER, hud_mode, OptDlg, OnEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, hud_mode, OptDlg, OnExit);
+ }
+
+ if (hud_color) {
+ REGISTER_CLIENT(EID_MOUSE_ENTER, hud_color, OptDlg, OnEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, hud_color, OptDlg, OnExit);
+ }
+
+ if (ff_mode) {
+ REGISTER_CLIENT(EID_MOUSE_ENTER, ff_mode, OptDlg, OnEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, ff_mode, OptDlg, OnExit);
+ }
+
+ if (grid_mode) {
+ REGISTER_CLIENT(EID_MOUSE_ENTER, grid_mode, OptDlg, OnEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, grid_mode, OptDlg, OnExit);
+ }
+
+ if (gunsight) {
+ REGISTER_CLIENT(EID_MOUSE_ENTER, gunsight, OptDlg, OnEnter);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, gunsight, OptDlg, OnExit);
+ }
+
+ if (apply)
+ REGISTER_CLIENT(EID_CLICK, apply, OptDlg, OnApply);
+
+ if (cancel)
+ REGISTER_CLIENT(EID_CLICK, cancel, OptDlg, OnCancel);
+
+ if (vid_btn)
+ REGISTER_CLIENT(EID_CLICK, vid_btn, OptDlg, OnVideo);
+
+ if (aud_btn)
+ REGISTER_CLIENT(EID_CLICK, aud_btn, OptDlg, OnAudio);
+
+ if (ctl_btn)
+ REGISTER_CLIENT(EID_CLICK, ctl_btn, OptDlg, OnControls);
+
+ if (opt_btn)
+ REGISTER_CLIENT(EID_CLICK, opt_btn, OptDlg, OnOptions);
+
+ if (mod_btn)
+ REGISTER_CLIENT(EID_CLICK, mod_btn, OptDlg, OnMod);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+OptDlg::Show()
+{
+ FormWindow::Show();
+
+ if (closed) {
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars) {
+ if (flight_model)
+ flight_model->SetSelection(Ship::GetFlightModel());
+
+ if (landings)
+ landings->SetSelection(Ship::GetLandingModel());
+
+ if (hud_mode)
+ hud_mode->SetSelection(HUDView::IsArcade() ? 1 : 0);
+
+ if (hud_color)
+ hud_color->SetSelection(HUDView::DefaultColorSet());
+
+ if (ff_mode)
+ ff_mode->SetSelection((int) (Ship::GetFriendlyFireLevel() * 4));
+ }
+
+ Player* player = Player::GetCurrentPlayer();
+ if (player) {
+ if (flying_start)
+ flying_start->SetSelection(player->FlyingStart());
+
+ if (ai_difficulty)
+ ai_difficulty->SetSelection(ai_difficulty->NumItems() - player->AILevel() - 1);
+
+ if (grid_mode)
+ grid_mode->SetSelection(player->GridMode());
+
+ if (gunsight)
+ gunsight->SetSelection(player->Gunsight());
+ }
+ }
+
+ if (vid_btn) vid_btn->SetButtonState(0);
+ if (aud_btn) aud_btn->SetButtonState(0);
+ if (ctl_btn) ctl_btn->SetButtonState(0);
+ if (opt_btn) opt_btn->SetButtonState(1);
+ if (mod_btn) mod_btn->SetButtonState(0);
+
+ closed = false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+OptDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ OnApply(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void OptDlg::OnAudio(AWEvent* event) { manager->ShowAudDlg(); }
+void OptDlg::OnVideo(AWEvent* event) { manager->ShowVidDlg(); }
+void OptDlg::OnOptions(AWEvent* event) { manager->ShowOptDlg(); }
+void OptDlg::OnControls(AWEvent* event) { manager->ShowCtlDlg(); }
+void OptDlg::OnMod(AWEvent* event) { manager->ShowModDlg(); }
+
+// +--------------------------------------------------------------------+
+
+void
+OptDlg::OnApply(AWEvent* event)
+{
+ manager->ApplyOptions();
+}
+
+void
+OptDlg::OnCancel(AWEvent* event)
+{
+ manager->CancelOptions();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+OptDlg::OnEnter(AWEvent* event)
+{
+ ActiveWindow* src = event->window;
+
+ if (src && description)
+ description->SetText(src->GetAltText());
+}
+
+void
+OptDlg::OnExit(AWEvent* event)
+{
+ ComboBox* cmb = (ComboBox*) event->window;
+
+ if (!cmb || cmb->IsListShowing())
+ return;
+
+ if (description)
+ description->SetText("");
+}
+
+// +--------------------------------------------------------------------+
+
+void
+OptDlg::Apply()
+{
+ if (closed) return;
+
+ Player* player = Player::GetCurrentPlayer();
+ if (player) {
+ if (flight_model)
+ player->SetFlightModel(flight_model->GetSelectedIndex());
+
+ if (flying_start)
+ player->SetFlyingStart(flying_start->GetSelectedIndex());
+
+ if (landings)
+ player->SetLandingModel(landings->GetSelectedIndex());
+
+ if (ai_difficulty)
+ player->SetAILevel(ai_difficulty->NumItems() - ai_difficulty->GetSelectedIndex() - 1);
+
+ if (hud_mode)
+ player->SetHUDMode(hud_mode->GetSelectedIndex());
+
+ if (hud_color)
+ player->SetHUDColor(hud_color->GetSelectedIndex());
+
+ if (ff_mode)
+ player->SetFriendlyFire(ff_mode->GetSelectedIndex());
+
+ if (grid_mode)
+ player->SetGridMode(grid_mode->GetSelectedIndex());
+
+ if (gunsight)
+ player->SetGunsight(gunsight->GetSelectedIndex());
+
+ Player::Save();
+ }
+
+ if (flight_model)
+ Ship::SetFlightModel(flight_model->GetSelectedIndex());
+
+ if (landings)
+ Ship::SetLandingModel(landings->GetSelectedIndex());
+
+ if (hud_mode)
+ HUDView::SetArcade(hud_mode->GetSelectedIndex() > 0);
+
+ if (hud_color)
+ HUDView::SetDefaultColorSet(hud_color->GetSelectedIndex());
+
+ if (ff_mode)
+ Ship::SetFriendlyFireLevel(ff_mode->GetSelectedIndex() / 4.0);
+
+ HUDView* hud = HUDView::GetInstance();
+ if (hud) hud->SetHUDColorSet(hud_color->GetSelectedIndex());
+
+ closed = true;
+}
+
+void
+OptDlg::Cancel()
+{
+ closed = true;
+}
diff --git a/Stars45/OptDlg.h b/Stars45/OptDlg.h
new file mode 100644
index 0000000..dfc8f84
--- /dev/null
+++ b/Stars45/OptDlg.h
@@ -0,0 +1,87 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: OptDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#ifndef OptDlg_h
+#define OptDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+
+// +--------------------------------------------------------------------+
+
+class BaseScreen;
+
+// +--------------------------------------------------------------------+
+
+class OptDlg : public FormWindow
+{
+public:
+ OptDlg(Screen* s, FormDef& def, BaseScreen* mgr);
+ virtual ~OptDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void Apply();
+ virtual void Cancel();
+
+ virtual void OnApply(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+
+ virtual void OnEnter(AWEvent* event);
+ virtual void OnExit(AWEvent* event);
+
+ virtual void OnAudio(AWEvent* event);
+ virtual void OnVideo(AWEvent* event);
+ virtual void OnOptions(AWEvent* event);
+ virtual void OnControls(AWEvent* event);
+ virtual void OnMod(AWEvent* event);
+
+protected:
+ BaseScreen* manager;
+
+ ComboBox* flight_model;
+ ComboBox* flying_start;
+ ComboBox* landings;
+ ComboBox* ai_difficulty;
+ ComboBox* hud_mode;
+ ComboBox* hud_color;
+ ComboBox* joy_mode;
+ ComboBox* ff_mode;
+ ComboBox* grid_mode;
+ ComboBox* gunsight;
+
+ ActiveWindow* description;
+
+ Button* aud_btn;
+ Button* vid_btn;
+ Button* opt_btn;
+ Button* ctl_btn;
+ Button* mod_btn;
+
+ Button* apply;
+ Button* cancel;
+
+ bool closed;
+};
+
+#endif OptDlg_h
+
diff --git a/Stars45/PlanScreen.cpp b/Stars45/PlanScreen.cpp
new file mode 100644
index 0000000..77c08f4
--- /dev/null
+++ b/Stars45/PlanScreen.cpp
@@ -0,0 +1,395 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars
+ FILE: PlanScreen.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include "MemDebug.h"
+#include "PlanScreen.h"
+#include "FormDef.h"
+#include "MsnObjDlg.h"
+#include "MsnPkgDlg.h"
+#include "MsnWepDlg.h"
+#include "MsnNavDlg.h"
+#include "DebriefDlg.h"
+#include "AwardDlg.h"
+#include "Campaign.h"
+#include "Mission.h"
+#include "Sim.h"
+#include "Starshatter.h"
+#include "StarSystem.h"
+
+#include "Game.h"
+#include "Video.h"
+#include "Screen.h"
+#include "ActiveWindow.h"
+#include "Mouse.h"
+#include "Keyboard.h"
+#include "FadeView.h"
+#include "Color.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "FontMgr.h"
+#include "EventDispatch.h"
+#include "DataLoader.h"
+#include "Resource.h"
+
+// +--------------------------------------------------------------------+
+
+PlanScreen::PlanScreen()
+ : screen(0), navdlg(0), award_dlg(0), debrief_dlg(0),
+ objdlg(0), pkgdlg(0), wepdlg(0), isShown(false)
+{
+ loader = DataLoader::GetLoader();
+}
+
+PlanScreen::~PlanScreen()
+{
+ TearDown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlanScreen::Setup(Screen* s)
+{
+ if (!s)
+ return;
+
+ screen = s;
+
+ // create windows
+ loader->UseFileSystem(true);
+
+ FormDef msn_obj_def("MsnObjDlg", 0);
+ msn_obj_def.Load("MsnObjDlg");
+ objdlg = new(__FILE__,__LINE__) MsnObjDlg(screen, msn_obj_def, this);
+
+ FormDef msn_pkg_def("MsnPkgDlg", 0);
+ msn_pkg_def.Load("MsnPkgDlg");
+ pkgdlg = new(__FILE__,__LINE__) MsnPkgDlg(screen, msn_pkg_def, this);
+
+ FormDef msn_nav_def("MsnNavDlg", 0);
+ msn_nav_def.Load("MsnNavDlg");
+ navdlg = new(__FILE__,__LINE__) MsnNavDlg(screen, msn_nav_def, this);
+
+ FormDef msn_wep_def("MsnWepDlg", 0);
+ msn_wep_def.Load("MsnWepDlg");
+ wepdlg = new(__FILE__,__LINE__) MsnWepDlg(screen, msn_wep_def, this);
+
+ FormDef award_def("AwardDlg", 0);
+ award_def.Load("AwardDlg");
+ award_dlg = new(__FILE__,__LINE__) AwardDlg(screen, award_def, this);
+
+ FormDef debrief_def("DebriefDlg", 0);
+ debrief_def.Load("DebriefDlg");
+ debrief_dlg = new(__FILE__,__LINE__) DebriefDlg(screen, debrief_def, this);
+
+ loader->UseFileSystem(Starshatter::UseFileSystem());
+ ShowMsnDlg();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlanScreen::TearDown()
+{
+ if (screen) {
+ screen->DelWindow(objdlg);
+ screen->DelWindow(pkgdlg);
+ screen->DelWindow(wepdlg);
+ screen->DelWindow(navdlg);
+ screen->DelWindow(debrief_dlg);
+ screen->DelWindow(award_dlg);
+ }
+
+ delete objdlg;
+ delete pkgdlg;
+ delete wepdlg;
+ delete navdlg;
+ delete debrief_dlg;
+ delete award_dlg;
+
+ objdlg = 0;
+ pkgdlg = 0;
+ wepdlg = 0;
+ navdlg = 0;
+ debrief_dlg = 0;
+ award_dlg = 0;
+ screen = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlanScreen::ExecFrame()
+{
+ Game::SetScreenColor(Color::Black);
+
+ Mission* mission = 0;
+ Campaign* campaign = Campaign::GetCampaign();
+
+ if (campaign)
+ mission = campaign->GetMission();
+
+ if (navdlg) {
+ navdlg->SetMission(mission);
+
+ if (navdlg->IsShown())
+ navdlg->ExecFrame();
+ }
+
+ if (objdlg && objdlg->IsShown()) {
+ objdlg->ExecFrame();
+ }
+
+ if (pkgdlg && pkgdlg->IsShown()) {
+ pkgdlg->ExecFrame();
+ }
+
+ if (wepdlg && wepdlg->IsShown()) {
+ wepdlg->ExecFrame();
+ }
+
+ if (award_dlg && award_dlg->IsShown()) {
+ award_dlg->ExecFrame();
+ }
+
+ if (debrief_dlg && debrief_dlg->IsShown()) {
+ debrief_dlg->ExecFrame();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+PlanScreen::CloseTopmost()
+{
+ if (debrief_dlg->IsShown()) {
+ debrief_dlg->OnClose(0);
+ }
+
+ if (award_dlg->IsShown()) {
+ return true;
+ }
+
+ return false;
+}
+
+void
+PlanScreen::Show()
+{
+ if (!isShown) {
+ ShowMsnDlg();
+ isShown = true;
+ }
+}
+
+void
+PlanScreen::Hide()
+{
+ HideAll();
+ isShown = false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlanScreen::ShowMsnDlg()
+{
+ HideAll();
+ Mouse::Show(true);
+ objdlg->Show();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlanScreen::HideMsnDlg()
+{
+ HideAll();
+ Mouse::Show(true);
+ objdlg->Show();
+}
+
+bool
+PlanScreen::IsMsnShown()
+{
+ return IsMsnObjShown() || IsMsnPkgShown() || IsMsnWepShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlanScreen::ShowMsnObjDlg()
+{
+ HideAll();
+ Mouse::Show(true);
+ objdlg->Show();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlanScreen::HideMsnObjDlg()
+{
+ HideAll();
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+PlanScreen::IsMsnObjShown()
+{
+ return objdlg && objdlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlanScreen::ShowMsnPkgDlg()
+{
+ HideAll();
+ Mouse::Show(true);
+ pkgdlg->Show();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlanScreen::HideMsnPkgDlg()
+{
+ HideAll();
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+PlanScreen::IsMsnPkgShown()
+{
+ return pkgdlg && pkgdlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlanScreen::ShowMsnWepDlg()
+{
+ HideAll();
+ Mouse::Show(true);
+ wepdlg->Show();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlanScreen::HideMsnWepDlg()
+{
+ HideAll();
+ Mouse::Show(true);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+PlanScreen::IsMsnWepShown()
+{
+ return wepdlg && wepdlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlanScreen::ShowNavDlg()
+{
+ if (navdlg && !navdlg->IsShown()) {
+ HideAll();
+ Mouse::Show(true);
+ navdlg->Show();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlanScreen::HideNavDlg()
+{
+ if (navdlg && navdlg->IsShown()) {
+ HideAll();
+ Mouse::Show(true);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+PlanScreen::IsNavShown()
+{
+ return navdlg && navdlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlanScreen::ShowDebriefDlg()
+{
+ HideAll();
+ Mouse::Show(true);
+ debrief_dlg->Show();
+}
+
+void
+PlanScreen::HideDebriefDlg()
+{
+ HideAll();
+ Mouse::Show(true);
+}
+
+bool
+PlanScreen::IsDebriefShown()
+{
+ return debrief_dlg && debrief_dlg->IsShown();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlanScreen::ShowAwardDlg()
+{
+ HideAll();
+ Mouse::Show(true);
+ award_dlg->Show();
+}
+
+void
+PlanScreen::HideAwardDlg()
+{
+ HideAll();
+ Mouse::Show(true);
+}
+
+bool
+PlanScreen::IsAwardShown()
+{
+ return award_dlg && award_dlg->IsShown();
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+PlanScreen::HideAll()
+{
+ if (objdlg) objdlg->Hide();
+ if (pkgdlg) pkgdlg->Hide();
+ if (wepdlg) wepdlg->Hide();
+ if (navdlg) navdlg->Hide();
+ if (award_dlg) award_dlg->Hide();
+ if (debrief_dlg) debrief_dlg->Hide();
+} \ No newline at end of file
diff --git a/Stars45/PlanScreen.h b/Stars45/PlanScreen.h
new file mode 100644
index 0000000..25dcb52
--- /dev/null
+++ b/Stars45/PlanScreen.h
@@ -0,0 +1,108 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: PlanScreen.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef PlanScreen_h
+#define PlanScreen_h
+
+#include "Types.h"
+#include "Bitmap.h"
+#include "Screen.h"
+#include "BaseScreen.h"
+
+// +--------------------------------------------------------------------+
+
+class MsnObjDlg;
+class MsnPkgDlg;
+class MsnWepDlg;
+class MsnNavDlg;
+class DebriefDlg;
+class AwardDlg;
+
+class Bitmap;
+class DataLoader;
+class Font;
+class Screen;
+class Video;
+class VideoFactory;
+
+// +--------------------------------------------------------------------+
+
+class PlanScreen : public BaseScreen
+{
+public:
+ PlanScreen();
+ virtual ~PlanScreen();
+
+ virtual void Setup(Screen* screen);
+ virtual void TearDown();
+ virtual bool CloseTopmost();
+
+ virtual bool IsShown() const { return isShown; }
+ virtual void Show();
+ virtual void Hide();
+
+ virtual void ShowMsnDlg();
+ virtual void HideMsnDlg();
+ virtual bool IsMsnShown();
+
+ virtual void ShowMsnObjDlg();
+ virtual void HideMsnObjDlg();
+ virtual bool IsMsnObjShown();
+ virtual MsnObjDlg* GetMsnObjDlg() { return objdlg; }
+
+ virtual void ShowMsnPkgDlg();
+ virtual void HideMsnPkgDlg();
+ virtual bool IsMsnPkgShown();
+ virtual MsnPkgDlg* GetMsnPkgDlg() { return pkgdlg; }
+
+ virtual void ShowMsnWepDlg();
+ virtual void HideMsnWepDlg();
+ virtual bool IsMsnWepShown();
+ virtual MsnWepDlg* GetMsnWepDlg() { return wepdlg; }
+
+ virtual void ShowNavDlg();
+ virtual void HideNavDlg();
+ virtual bool IsNavShown();
+ virtual NavDlg* GetNavDlg() { return (NavDlg*) navdlg; }
+
+ virtual void ShowDebriefDlg();
+ virtual void HideDebriefDlg();
+ virtual bool IsDebriefShown();
+ virtual DebriefDlg* GetDebriefDlg() { return debrief_dlg; }
+
+ virtual void ShowAwardDlg();
+ virtual void HideAwardDlg();
+ virtual bool IsAwardShown();
+ virtual AwardDlg* GetAwardDlg() { return award_dlg; }
+
+ virtual void ExecFrame();
+ virtual void HideAll();
+
+
+private:
+ Screen* screen;
+
+ MsnObjDlg* objdlg;
+ MsnPkgDlg* pkgdlg;
+ MsnWepDlg* wepdlg;
+ MsnNavDlg* navdlg;
+ DebriefDlg* debrief_dlg;
+ AwardDlg* award_dlg;
+
+ DataLoader* loader;
+
+ int wc, hc;
+ bool isShown;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif PlanScreen_h
+
diff --git a/Stars45/Player.cpp b/Stars45/Player.cpp
new file mode 100644
index 0000000..4a7c8aa
--- /dev/null
+++ b/Stars45/Player.cpp
@@ -0,0 +1,1552 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Player.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Player / Logbook class
+*/
+
+
+#include "MemDebug.h"
+#include "Player.h"
+#include "NetLobbyServer.h"
+#include "NetLayer.h"
+#include "Ship.h"
+#include "SimEvent.h"
+#include "Campaign.h"
+#include "CampaignSaveGame.h"
+#include "Random.h"
+#include "HUDView.h"
+#include "MFD.h"
+
+#include "DataLoader.h"
+#include "Encrypt.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+#include "Bitmap.h"
+#include "Game.h"
+
+// +-------------------------------------------------------------------+
+
+class AwardInfo
+{
+public:
+ static const char* TYPENAME() { return "AwardInfo"; }
+
+ AwardInfo() :
+ type(RANK),
+ id(0),
+ large_insignia(0),
+ small_insignia(0),
+ granted_ship_classes(0x7),
+ total_points(0),
+ mission_points(0),
+ total_missions(0),
+ kills(0),
+ lost(0),
+ collision(0),
+ campaign_id(0),
+ campaign_complete(false),
+ dynamic_campaign(false),
+ ceremony(true),
+ required_awards(0),
+ lottery(0),
+ min_rank(0),
+ max_rank((int) 1e9),
+ min_ship_class(0),
+ max_ship_class((int) 1e9)
+ { }
+ ~AwardInfo() { }
+
+ enum TYPE { RANK, MEDAL };
+
+ int type;
+ int id;
+ Text name;
+ Text abrv;
+ Text desc;
+ Text grant;
+ Text desc_sound;
+ Text grant_sound;
+ Bitmap* large_insignia;
+ Bitmap* small_insignia;
+ int granted_ship_classes;
+
+ int total_points;
+ int mission_points;
+ int total_missions;
+ int kills;
+ int lost;
+ int collision;
+ int campaign_id;
+ bool campaign_complete;
+ bool dynamic_campaign;
+ bool ceremony;
+
+ int required_awards;
+ int lottery;
+ int min_rank;
+ int max_rank;
+ int min_ship_class;
+ int max_ship_class;
+};
+
+static List<AwardInfo> rank_table;
+static List<AwardInfo> medal_table;
+static bool config_exists = false;
+
+// +-------------------------------------------------------------------+
+
+Player::Player(const char* n)
+ : uid(0), name(n), create_date(0), points(0), medals(0), flight_time(0),
+ missions(0), kills(0), losses(0), campaigns(0), trained(0),
+ flight_model(0), flying_start(0), landing_model(0),
+ ai_level(1), hud_mode(0), hud_color(1),
+ ff_level(4), grid(1), gunsight(0), award(0)
+{
+ name.setSensitive(false);
+
+ mfd[0] = -1;
+ mfd[1] = -1;
+ mfd[2] = -1;
+ mfd[3] = -1;
+}
+
+Player::Player()
+ : uid(0), create_date(0), points(0), medals(0), flight_time(0),
+ missions(0), kills(0), losses(0), campaigns(0), trained(0),
+ flight_model(0), flying_start(0), landing_model(0),
+ ai_level(1), hud_mode(0), hud_color(1),
+ ff_level(4), grid(1), gunsight(0), award(0)
+{
+ name.setSensitive(false);
+
+ mfd[0] = -1;
+ mfd[1] = -1;
+ mfd[2] = -1;
+ mfd[3] = -1;
+}
+
+Player::~Player()
+{ }
+
+// +-------------------------------------------------------------------+
+
+void
+Player::SetName(const char* n)
+{
+ if (n && *n)
+ name = n;
+}
+
+void
+Player::SetPassword(const char* p)
+{
+ if (p && *p) {
+ pass = p;
+
+ if (pass.length() > 16)
+ pass = pass.substring(0, 16);
+ }
+}
+
+void
+Player::SetSquadron(const char* s)
+{
+ if (s && *s)
+ squadron = s;
+}
+
+void
+Player::SetSignature(const char* s)
+{
+ if (s && *s)
+ signature = s;
+}
+
+const Text&
+Player::ChatMacro(int n) const
+{
+ if (n >= 0 && n < 10)
+ return chat_macros[n];
+
+ return chat_macros[0];
+}
+
+void
+Player::SetChatMacro(int n, const char* m)
+{
+ if (n >= 0 && n < 10 && m && *m)
+ chat_macros[n] = m;
+}
+
+void
+Player::SetPoints(int p)
+{
+ if (p >= 0)
+ points = p;
+}
+
+void
+Player::SetMedals(int m)
+{
+ medals = m;
+}
+
+void
+Player::SetCampaigns(int n)
+{
+ campaigns = n;
+}
+
+void
+Player::SetTrained(int n)
+{
+ if (n == 0)
+ trained = 0;
+
+ else if (n > 0 && n <= 20)
+ trained = trained | (1 << (n-1));
+
+ else if (n > 20)
+ trained = n;
+}
+
+bool
+Player::HasTrained(int n) const
+{
+ if (n > 0 && n <= 20)
+ return (trained & (1 << (n-1))) ? true : false;
+
+ return false;
+}
+
+bool
+Player::HasCompletedCampaign(int id) const
+{
+ if (id > 0 && id < 30)
+ return (campaigns & (1 << id)) ? true : false;
+
+ return false;
+}
+
+void
+Player::SetCampaignComplete(int id)
+{
+ if (id > 0 && id < 30) {
+ campaigns = campaigns | (1 << id);
+ Save();
+ }
+}
+
+void
+Player::SetCreateDate(int d)
+{
+ if (d >= 0)
+ create_date = d;
+}
+
+void
+Player::SetFlightTime(int t)
+{
+ if (t >= 0)
+ flight_time = t;
+}
+
+void
+Player::SetMissions(int m)
+{
+ if (m >= 0)
+ missions = m;
+}
+
+void
+Player::SetKills(int k)
+{
+ if (k >= 0)
+ kills = k;
+}
+
+void
+Player::SetLosses(int l)
+{
+ if (l >= 0)
+ losses = l;
+}
+
+// +-------------------------------------------------------------------+
+
+void
+Player::AddFlightTime(int t)
+{
+ if (t > 0)
+ flight_time += t;
+}
+
+void
+Player::AddPoints(int p)
+{
+ if (p > 0)
+ points += p;
+}
+
+void
+Player::AddMissions(int m)
+{
+ if (m > 0)
+ missions += m;
+}
+
+void
+Player::AddKills(int k)
+{
+ if (k > 0)
+ kills += k;
+}
+
+void
+Player::AddLosses(int l)
+{
+ if (l > 0)
+ losses += l;
+}
+
+// +-------------------------------------------------------------------+
+
+void
+Player::SetFlightModel(int n)
+{
+ if (n >= Ship::FM_STANDARD && n <= Ship::FM_ARCADE) {
+ flight_model = n;
+ Ship::SetFlightModel(n);
+ }
+}
+
+void
+Player::SetFlyingStart(int n)
+{
+ flying_start = n;
+}
+
+void
+Player::SetLandingModel(int n)
+{
+ if (n >= Ship::LM_STANDARD && n <= Ship::LM_EASIER) {
+ landing_model = n;
+ Ship::SetLandingModel(landing_model);
+ }
+}
+
+void
+Player::SetAILevel(int n)
+{
+ ai_level = n;
+}
+
+void
+Player::SetHUDMode(int n)
+{
+ hud_mode = n;
+ HUDView::SetArcade(n > 0);
+}
+
+void
+Player::SetHUDColor(int n)
+{
+ hud_color = n;
+ HUDView::SetDefaultColorSet(n);
+}
+
+void
+Player::SetFriendlyFire(int n)
+{
+ if (n >= 0 && n <= 4) {
+ ff_level = n;
+ Ship::SetFriendlyFireLevel(n/4.0);
+ }
+}
+
+void
+Player::SetGridMode(int n)
+{
+ if (n >= 0 && n <= 1) {
+ grid = n;
+ }
+}
+
+void
+Player::SetGunsight(int n)
+{
+ if (n >= 0 && n <= 1) {
+ gunsight = n;
+ }
+}
+
+void
+Player::ClearShowAward()
+{
+ award = 0;
+}
+
+Text
+Player::AwardName() const
+{
+ if (award)
+ return award->name;
+
+ return Text();
+}
+
+Text
+Player::AwardDesc() const
+{
+ if (award)
+ return award->grant;
+
+ return Text();
+}
+
+Bitmap*
+Player::AwardImage() const
+{
+ if (award)
+ return award->large_insignia;
+
+ return 0;
+}
+
+Sound*
+Player::AwardSound() const
+{
+ if (award && award->grant_sound.length()) {
+ DataLoader* loader = DataLoader::GetLoader();
+ Sound* result = 0;
+
+ loader->LoadSound(award->grant_sound, result);
+ return result;
+ }
+
+ return 0;
+}
+
+// +-------------------------------------------------------------------+
+
+const char*
+Player::RankName(int rank)
+{
+ ListIter<AwardInfo> iter = rank_table;
+ while (++iter) {
+ AwardInfo* award = iter.value();
+ if (award->id == rank)
+ return award->name;
+ }
+
+ return "Conscript";
+}
+
+const char*
+Player::RankAbrv(int rank)
+{
+ ListIter<AwardInfo> iter = rank_table;
+ while (++iter) {
+ AwardInfo* award = iter.value();
+ if (award->id == rank)
+ return award->abrv;
+ }
+
+ return "";
+}
+
+Bitmap*
+Player::RankInsignia(int rank, int size)
+{
+ ListIter<AwardInfo> iter = rank_table;
+ while (++iter) {
+ AwardInfo* award = iter.value();
+ if (award->id == rank) {
+ if (size == 0)
+ return award->small_insignia;
+
+ if (size == 1)
+ return award->large_insignia;
+ }
+ }
+
+ return 0;
+}
+
+const char*
+Player::RankDescription(int rank)
+{
+ ListIter<AwardInfo> iter = rank_table;
+ while (++iter) {
+ AwardInfo* award = iter.value();
+ if (award->id == rank)
+ return award->desc;
+ }
+
+ return "";
+}
+
+int
+Player::RankFromName(const char* name)
+{
+ ListIter<AwardInfo> iter = rank_table;
+ while (++iter) {
+ AwardInfo* award = iter.value();
+ if (award->name == name)
+ return award->id;
+ }
+
+ return 0;
+}
+
+int
+Player::Rank() const
+{
+ for (int i = rank_table.size()-1; i >= 0; i--) {
+ AwardInfo* award = rank_table[i];
+ if (points >= award->total_points)
+ return award->id;
+ }
+
+ return 0;
+}
+
+void
+Player::SetRank(int r)
+{
+ ListIter<AwardInfo> iter = rank_table;
+ while (++iter) {
+ AwardInfo* award = iter.value();
+ if (r == award->id)
+ points = award->total_points;
+ }
+}
+
+int
+Player::Medal(int n) const
+{
+ if (n < 0)
+ return 0;
+
+ for (int i = 0; i < 16; i++) {
+ int selector = 1 << (15-i);
+
+ // found a medal:
+ if (medals & selector) {
+
+ // and it's the nth medal!
+ if (n == 0) {
+ return selector;
+ }
+
+ n--;
+ }
+ }
+
+ return 0;
+}
+
+// +-------------------------------------------------------------------+
+
+const char*
+Player::MedalName(int medal)
+{
+ ListIter<AwardInfo> iter = medal_table;
+ while (++iter) {
+ AwardInfo* award = iter.value();
+ if (award->id == medal)
+ return award->name;
+ }
+
+ return "";
+}
+
+Bitmap*
+Player::MedalInsignia(int medal, int size)
+{
+ ListIter<AwardInfo> iter = medal_table;
+ while (++iter) {
+ AwardInfo* award = iter.value();
+ if (award->id == medal) {
+ if (size == 0)
+ return award->small_insignia;
+
+ if (size == 1)
+ return award->large_insignia;
+ }
+ }
+
+ return 0;
+}
+
+const char*
+Player::MedalDescription(int medal)
+{
+ ListIter<AwardInfo> iter = medal_table;
+ while (++iter) {
+ AwardInfo* award = iter.value();
+ if (award->id == medal)
+ return award->desc;
+ }
+
+ return "";
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+Player::CanCommand(int ship_class)
+{
+ if (ship_class <= Ship::ATTACK)
+ return true;
+
+ for (int i = rank_table.size()-1; i >= 0; i--) {
+ AwardInfo* award = rank_table[i];
+ if (points > award->total_points) {
+ return (ship_class & award->granted_ship_classes) != 0;
+ }
+ }
+
+ return false;
+}
+
+int
+Player::CommandRankRequired(int ship_class)
+{
+ for (int i = 0; i < rank_table.size(); i++) {
+ AwardInfo* award = rank_table[i];
+ if ((ship_class & award->granted_ship_classes) != 0) {
+ return i;
+ }
+ }
+
+ return rank_table.size()-1;
+}
+
+// +-------------------------------------------------------------------+
+
+int
+Player::GetMissionPoints(ShipStats* s, DWORD start_time)
+{
+ int result = 0;
+
+ if (s) {
+ result = s->GetPoints();
+
+ int flight_time = (Game::GameTime() - start_time) / 1000;
+
+ // if player survived mission, award one experience point
+ // for each minute of action, in ten point blocks:
+ if (!s->GetDeaths() && !s->GetColls()) {
+ int minutes = flight_time / 60;
+ minutes /= 10;
+ minutes *= 10;
+ result += minutes;
+
+ if (s->HasEvent(SimEvent::DOCK))
+ result += 100;
+ }
+ else {
+ result -= (int) (2.5 * Ship::Value(s->GetShipClass()));
+ }
+
+ if (result < 0)
+ result = 0;
+ }
+
+ return result;
+}
+
+// +-------------------------------------------------------------------+
+
+void
+Player::ProcessStats(ShipStats* s, DWORD start_time)
+{
+ if (!s) return;
+
+ int old_rank = Rank();
+ int pts = GetMissionPoints(s, start_time);
+
+ AddPoints(pts);
+ AddPoints(s->GetCommandPoints());
+ AddKills(s->GetGunKills());
+ AddKills(s->GetMissileKills());
+ AddLosses(s->GetDeaths());
+ AddLosses(s->GetColls());
+ AddMissions(1);
+ AddFlightTime((Game::GameTime() - start_time) / 1000);
+
+ int rank = Rank();
+
+ // did the player earn a promotion?
+ if (old_rank != rank) {
+ ListIter<AwardInfo> iter = rank_table;
+ while (++iter) {
+ AwardInfo* a = iter.value();
+ if (rank == a->id) {
+ award = a;
+ }
+ }
+ }
+
+ // if not, did the player earn a medal?
+ else {
+ ListIter<AwardInfo> iter = medal_table;
+ while (++iter) {
+ AwardInfo* a = iter.value();
+
+ if (EarnedAward(a, s) && a->ceremony) {
+ award = a;
+ break;
+ }
+ }
+ }
+
+ // persist all stats, promotions, and medals:
+ Save();
+}
+
+bool
+Player::EarnedAward(AwardInfo* a, ShipStats* s)
+{
+ if (!a || !s)
+ return false;
+
+ // already earned this medal?
+ if (a->id & medals)
+ return false;
+
+ // eligible for this medal?
+ int rank = Rank();
+ if (a->min_rank > rank || a->max_rank < rank)
+ return false;
+
+ if ((a->required_awards & medals) < a->required_awards)
+ return false;
+
+ if (a->min_ship_class > s->GetShipClass() || a->max_ship_class < s->GetShipClass())
+ return false;
+
+ if (a->total_points > points)
+ return false;
+
+ if (a->total_missions > missions)
+ return false;
+
+ if (a->campaign_id && a->campaign_complete) {
+ if (!HasCompletedCampaign(a->campaign_id))
+ return false;
+ }
+
+ else {
+ // campaign related requirements
+ Campaign* c = Campaign::GetCampaign();
+
+ if (c) {
+ if (a->dynamic_campaign && !c->IsDynamic())
+ return false;
+ }
+ }
+
+ // sufficient merit for this medal?
+ if (a->mission_points > s->GetPoints())
+ return false;
+
+ if (a->kills > s->GetGunKills() + s->GetMissileKills())
+ return false;
+
+ if (a->mission_points > s->GetPoints())
+ return false;
+
+ // player must survive mission if lost = -1
+ if (a->lost < 0 && (s->GetDeaths() || s->GetColls()))
+ return false;
+
+ // do we need to be wounded in battle?
+ if (a->lost > s->GetDeaths() || a->collision > s->GetColls())
+ return false;
+
+ // final lottery check:
+ if (a->lottery < 2 || RandomChance(1, a->lottery)) {
+ medals |= a->id;
+ return true;
+ }
+
+ // what do we have for the losers, judge?
+ return false;
+}
+
+// +-------------------------------------------------------------------+
+
+static List<Player> player_roster;
+static Player* current_player = 0;
+
+List<Player>&
+Player::GetRoster()
+{
+ return player_roster;
+}
+
+Player*
+Player::GetCurrentPlayer()
+{
+ return current_player;
+}
+
+void
+Player::SelectPlayer(Player* p)
+{
+ HUDView* hud = HUDView::GetInstance();
+
+ if (current_player && current_player != p) {
+ if (hud) {
+ for (int i = 0; i < 3; i++) {
+ MFD* mfd = hud->GetMFD(i);
+
+ if (mfd)
+ current_player->mfd[i] = mfd->GetMode();
+ }
+ }
+ }
+
+ if (player_roster.contains(p)) {
+ current_player = p;
+
+ Ship::SetFlightModel(p->flight_model);
+ Ship::SetLandingModel(p->landing_model);
+ HUDView::SetArcade(p->hud_mode > 0);
+ HUDView::SetDefaultColorSet(p->hud_color);
+
+ if (hud) {
+ for (int i = 0; i < 3; i++) {
+ if (p->mfd[i] >= 0) {
+ MFD* mfd = hud->GetMFD(i);
+
+ if (mfd)
+ mfd->SetMode(p->mfd[i]);
+ }
+ }
+ }
+ }
+}
+
+Player*
+Player::Find(const char* name)
+{
+ for (int i = 0; i < player_roster.size(); i++) {
+ Player* p = player_roster.at(i);
+ if (p->Name() == name)
+ return p;
+ }
+
+ return 0;
+}
+
+Player*
+Player::Create(const char* name)
+{
+ if (name && *name) {
+ // check for existence:
+ if (Find(name))
+ return 0;
+
+ Player* newbie = new(__FILE__,__LINE__) Player(name);
+ newbie->SetCreateDate(NetLayer::GetUTC());
+
+ player_roster.append(newbie);
+ newbie->CreateUniqueID();
+ return newbie;
+ }
+
+ return 0;
+}
+
+void
+Player::Destroy(Player* p)
+{
+ if (p) {
+ player_roster.remove(p);
+
+ if (p == current_player) {
+ current_player = 0;
+
+ if (player_roster.size())
+ current_player = player_roster.at(0);
+ }
+
+ CampaignSaveGame::RemovePlayer(p);
+ delete p;
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+Player::Initialize()
+{
+ LoadAwardTables();
+ Load();
+
+ if (!current_player) {
+ if (!player_roster.size()) {
+ Create("Pilot");
+ }
+
+ SelectPlayer(player_roster.at(0));
+ }
+}
+
+void
+Player::Close()
+{
+ if (current_player && !player_roster.contains(current_player))
+ delete current_player;
+
+ player_roster.destroy();
+ current_player = 0;
+
+ rank_table.destroy();
+ medal_table.destroy();
+}
+
+// +-------------------------------------------------------------------+
+
+bool
+Player::ConfigExists()
+{
+ return config_exists;
+}
+
+// +-------------------------------------------------------------------+
+
+#define GET_DEF_BOOL(x) if(pdef->name()->value()==(#x))GetDefBool(player->x,pdef,filename)
+#define GET_DEF_TEXT(x) if(pdef->name()->value()==(#x))GetDefText(player->x,pdef,filename)
+#define GET_DEF_NUM(x) if(pdef->name()->value()==(#x))GetDefNumber(player->x,pdef,filename)
+
+void
+Player::Load()
+{
+ config_exists = false;
+
+ // read the config file:
+ BYTE* block = 0;
+ int blocklen = 0;
+
+ char filename[64];
+ strcpy(filename, "player.cfg");
+
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ config_exists = true;
+
+ ::fseek(f, 0, SEEK_END);
+ blocklen = ftell(f);
+ ::fseek(f, 0, SEEK_SET);
+
+ block = new(__FILE__,__LINE__) BYTE[blocklen+1];
+ block[blocklen] = 0;
+
+ ::fread(block, blocklen, 1, f);
+ ::fclose(f);
+ }
+
+ if (blocklen == 0)
+ return;
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'.\n", filename);
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "PLAYER_CONFIG") {
+ Print("WARNING: invalid '%s' file. Using defaults\n", filename);
+ return;
+ }
+ }
+
+ if (current_player && !player_roster.contains(current_player))
+ delete current_player;
+ player_roster.destroy();
+ current_player = 0;
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "player") {
+
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: player structure missing in '%s'\n", filename);
+ }
+ else {
+ Player* player = new(__FILE__,__LINE__) Player;
+ bool current = false;
+ TermStruct* val = def->term()->isStruct();
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ GET_DEF_TEXT(name);
+ else GET_DEF_TEXT(squadron);
+ else GET_DEF_TEXT(signature);
+
+ else GET_DEF_NUM(uid);
+ else GET_DEF_NUM(flight_model);
+ else GET_DEF_NUM(flying_start);
+ else GET_DEF_NUM(landing_model);
+ else GET_DEF_NUM(ai_level);
+ else GET_DEF_NUM(hud_mode);
+ else GET_DEF_NUM(hud_color);
+ else GET_DEF_NUM(ff_level);
+ else GET_DEF_NUM(grid);
+ else GET_DEF_NUM(gunsight);
+
+ else if (pdef->name()->value() == ("chat_0"))
+ GetDefText(player->chat_macros[0], pdef, filename);
+
+ else if (pdef->name()->value() == ("chat_1"))
+ GetDefText(player->chat_macros[1], pdef, filename);
+
+ else if (pdef->name()->value() == ("chat_2"))
+ GetDefText(player->chat_macros[2], pdef, filename);
+
+ else if (pdef->name()->value() == ("chat_3"))
+ GetDefText(player->chat_macros[3], pdef, filename);
+
+ else if (pdef->name()->value() == ("chat_4"))
+ GetDefText(player->chat_macros[4], pdef, filename);
+
+ else if (pdef->name()->value() == ("chat_5"))
+ GetDefText(player->chat_macros[5], pdef, filename);
+
+ else if (pdef->name()->value() == ("chat_6"))
+ GetDefText(player->chat_macros[6], pdef, filename);
+
+ else if (pdef->name()->value() == ("chat_7"))
+ GetDefText(player->chat_macros[7], pdef, filename);
+
+ else if (pdef->name()->value() == ("chat_8"))
+ GetDefText(player->chat_macros[8], pdef, filename);
+
+ else if (pdef->name()->value() == ("chat_9"))
+ GetDefText(player->chat_macros[9], pdef, filename);
+
+ else if (pdef->name()->value() == ("mfd0"))
+ GetDefNumber(player->mfd[0], pdef, filename);
+
+ else if (pdef->name()->value() == ("mfd1"))
+ GetDefNumber(player->mfd[1], pdef, filename);
+
+ else if (pdef->name()->value() == ("mfd2"))
+ GetDefNumber(player->mfd[2], pdef, filename);
+
+ else if (pdef->name()->value() == ("current"))
+ GetDefBool(current, pdef, filename);
+
+ else if (pdef->name()->value() == ("trained"))
+ GetDefNumber(player->trained, pdef, filename);
+
+ else if (pdef->name()->value() == ("stats")) {
+ Text stats;
+ GetDefText(stats, pdef, filename);
+ player->DecodeStats(stats);
+ }
+
+ else if (pdef->name()->value().indexOf("XXX_CHEAT_A1B2C3_") == 0) {
+ if (pdef->name()->value().contains("points"))
+ GetDefNumber(player->points, pdef, filename);
+
+ else if (pdef->name()->value().contains("rank")) {
+ int rank=0;
+ GetDefNumber(rank, pdef, filename);
+ player->SetRank(rank);
+ }
+
+ else if (pdef->name()->value().contains("medals"))
+ GetDefNumber(player->medals, pdef, filename);
+
+ else if (pdef->name()->value().contains("campaigns"))
+ GetDefNumber(player->campaigns, pdef, filename);
+
+ else if (pdef->name()->value().contains("missions"))
+ GetDefNumber(player->missions, pdef, filename);
+
+ else if (pdef->name()->value().contains("kills"))
+ GetDefNumber(player->kills, pdef, filename);
+
+ else if (pdef->name()->value().contains("losses"))
+ GetDefNumber(player->losses, pdef, filename);
+
+ else if (pdef->name()->value().contains("flight_time"))
+ GetDefNumber(player->flight_time, pdef, filename);
+ }
+ }
+ }
+
+ player_roster.append(player);
+ player->CreateUniqueID();
+
+ if (current)
+ SelectPlayer(player);
+ }
+
+ }
+ else {
+ Print("WARNING: unknown label '%s' in '%s'\n",
+ def->name()->value().data(), filename);
+ }
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ term->print();
+ }
+ }
+ }
+ while (term);
+
+ delete [] block;
+}
+
+// +-------------------------------------------------------------------+
+
+void
+Player::Save()
+{
+ HUDView* hud = HUDView::GetInstance();
+ if (hud && current_player) {
+ for (int i = 0; i < 3; i++) {
+ MFD* mfd = hud->GetMFD(i);
+
+ if (mfd)
+ current_player->mfd[i] = mfd->GetMode();
+ }
+ }
+
+ FILE* f = fopen("player.cfg", "w");
+ if (f) {
+ fprintf(f, "PLAYER_CONFIG\n\n");
+
+ ListIter<Player> iter = player_roster;
+ while (++iter) {
+ Player* p = iter.value();
+
+ fprintf(f, "player: {\n");
+ fprintf(f, " uid: %d,\n", p->uid);
+ fprintf(f, " name: \"%s\",\n", SafeQuotes(p->name.data()));
+ fprintf(f, " squadron: \"%s\",\n", SafeQuotes(p->squadron.data()));
+ fprintf(f, " signature: \"%s\",\n", SafeQuotes(p->signature.data()));
+
+ Text stat_data = p->EncodeStats();
+
+ if (stat_data.length() > 32) {
+ char tmp[64];
+ int len = stat_data.length();
+
+ for (int n = 0; n < len; n += 32) {
+ ZeroMemory(tmp, sizeof(tmp));
+ const char* p = stat_data.data() + n;
+ strncpy(tmp, p, 32);
+
+ if (n == 0)
+ fprintf(f, " stats: \"%s\"\n", tmp);
+ else if (n < len-32)
+ fprintf(f, " \"%s\"\n", tmp);
+ else
+ fprintf(f, " \"%s\",\n", tmp);
+ }
+ }
+
+ if (p == current_player)
+ fprintf(f, " current: true,\n");
+ else
+ fprintf(f, " current: false,\n");
+
+ fprintf(f, " trained: %d,\n", p->trained);
+ fprintf(f, " flight_model: %d,\n", p->flight_model);
+ fprintf(f, " flying_start: %d,\n", p->flying_start);
+ fprintf(f, " landing_model: %d,\n", p->landing_model);
+ fprintf(f, " ai_level: %d,\n", p->ai_level);
+ fprintf(f, " hud_mode: %d,\n", p->hud_mode);
+ fprintf(f, " hud_color: %d,\n", p->hud_color);
+ fprintf(f, " ff_level: %d,\n", p->ff_level);
+ fprintf(f, " grid: %d,\n", p->grid);
+ fprintf(f, " gunsight: %d,\n", p->gunsight);
+
+ for (int i = 0; i < 10; i++) {
+ fprintf(f, " chat_%d: \"%s\",\n", i, SafeQuotes(p->chat_macros[i].data()));
+ }
+
+ for (i = 0; i < 3; i++) {
+ if (p->mfd[i] >= 0) {
+ fprintf(f, " mfd%d: %d,\n", i, p->mfd[i]);
+ }
+ }
+
+ fprintf(f, "}\n\n");
+ }
+
+ fclose(f);
+
+ config_exists = true;
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+static char stat_buf[280];
+static char code_buf[280];
+
+Text
+Player::EncodeStats()
+{
+ ZeroMemory(stat_buf, 280);
+ ZeroMemory(code_buf, 280);
+
+ sprintf(stat_buf, "%-16s%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
+ pass.data(),
+ create_date,
+ points,
+ flight_time,
+ missions,
+ kills,
+ losses,
+ medals,
+ campaigns,
+ 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32);
+
+ for (int i = 0; i < 16; i++)
+ for (int j = 0; j < 16; j++)
+ code_buf[i*16 + j] = stat_buf[j*16 + i];
+
+ return Encryption::Encode(Encryption::Encrypt(code_buf));
+}
+
+void
+Player::DecodeStats(const char* stats)
+{
+ ZeroMemory(stat_buf, 280);
+ ZeroMemory(code_buf, 280);
+
+ if (!stats || !*stats) {
+ Print("Player::DecodeStats() invalid or missing stats\n");
+ create_date = NetLayer::GetUTC();
+ return;
+ }
+
+ Text plain = Encryption::Decrypt(Encryption::Decode(stats));
+
+ if (plain.length() == 64) {
+ for (int i = 0; i < 8; i++)
+ for (int j = 0; j < 8; j++)
+ stat_buf[j*8 + i] = plain[i*8 +j];
+ }
+
+ else if (plain.length() == 256) {
+ for (int i = 0; i < 16; i++)
+ for (int j = 0; j < 16; j++)
+ stat_buf[j*16 + i] = plain[i*16 +j];
+ }
+
+ else {
+ Print("Player::DecodeStats() invalid plain text length %d\n", plain.length());
+ create_date = NetLayer::GetUTC();
+ return;
+ }
+
+ char work[32];
+ ZeroMemory(work, 32);
+ CopyMemory(work, stat_buf, 16);
+ for (int i = 15; i > 0; i--)
+ if (work[i] == ' ') work[i] = 0;
+ else break;
+ pass = work;
+
+ ZeroMemory(work, 16);
+ CopyMemory(work, stat_buf+16, 8);
+ sscanf(work, "%x", &create_date);
+
+ ZeroMemory(work, 16);
+ CopyMemory(work, stat_buf+24, 8);
+ sscanf(work, "%x", &points);
+ if (points < 0) points = 0;
+
+ ZeroMemory(work, 16);
+ CopyMemory(work, stat_buf+32, 8);
+ sscanf(work, "%x", &flight_time);
+ if (flight_time < 0) flight_time = 0;
+
+ ZeroMemory(work, 16);
+ CopyMemory(work, stat_buf+40, 8);
+ sscanf(work, "%x", &missions);
+ if (missions < 0) missions = 0;
+
+ ZeroMemory(work, 16);
+ CopyMemory(work, stat_buf+48, 8);
+ sscanf(work, "%x", &kills);
+ if (kills < 0) kills = 0;
+
+ ZeroMemory(work, 16);
+ CopyMemory(work, stat_buf+56, 8);
+ sscanf(work, "%x", &losses);
+ if (losses < 0) losses = 0;
+
+ if (plain.length() > 64) {
+ ZeroMemory(work, 16);
+ CopyMemory(work, stat_buf+64, 8);
+ sscanf(work, "%x", &medals);
+
+ ZeroMemory(work, 16);
+ CopyMemory(work, stat_buf+72, 8);
+ sscanf(work, "%x", &campaigns);
+ }
+
+ if (create_date == 0) {
+ ::Print("WARNING - loaded player with zero stats '%s'\n", name.data());
+ create_date = NetLayer::GetUTC();
+ }
+}
+
+// +-------------------------------------------------------------------+
+
+void
+Player::LoadAwardTables()
+{
+ DataLoader* loader = DataLoader::GetLoader();
+
+ if (!loader) return;
+
+ BYTE* block = 0;
+ const char* filename = "awards.def";
+
+ loader->SetDataPath("Awards/");
+ loader->LoadBuffer(filename, block, true);
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
+
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "AWARDS") {
+ return;
+ }
+ }
+
+ rank_table.destroy();
+ medal_table.destroy();
+
+ ::Print("Loading Ranks and Medals\n");
+
+ do {
+ delete term; term = 0;
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "award") {
+
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: award structure missing in '%s'\n", filename);
+ }
+ else {
+ AwardInfo* award = new(__FILE__,__LINE__) AwardInfo;
+ TermStruct* val = def->term()->isStruct();
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == ("name")) {
+ GetDefText(award->name, pdef, filename);
+ award->name = Game::GetText(award->name);
+ }
+
+ else if (pdef->name()->value() == ("abrv")) {
+ GetDefText(award->abrv, pdef, filename);
+ award->abrv = Game::GetText(award->abrv);
+ }
+
+ else if (pdef->name()->value() == ("desc")) {
+ GetDefText(award->desc, pdef, filename);
+ if (award->desc.length() <= 40)
+ award->desc = Game::GetText(award->desc);
+ }
+
+ else if (pdef->name()->value() == ("award")) {
+ GetDefText(award->grant, pdef, filename);
+ if (award->grant.length() <= 40)
+ award->grant = Game::GetText(award->grant);
+ }
+
+ else if (pdef->name()->value() == ("desc_sound"))
+ GetDefText(award->desc_sound, pdef, filename);
+
+ else if (pdef->name()->value() == ("award_sound"))
+ GetDefText(award->grant_sound, pdef, filename);
+
+ else if (pdef->name()->value().indexOf("large") == 0) {
+ Text txt;
+ GetDefText(txt, pdef, filename);
+ txt.setSensitive(false);
+
+ if (!txt.contains(".pcx"))
+ txt.append(".pcx");
+
+ loader->CacheBitmap(txt, award->large_insignia);
+ }
+
+ else if (pdef->name()->value().indexOf("small") == 0) {
+ Text txt;
+ GetDefText(txt, pdef, filename);
+ txt.setSensitive(false);
+
+ if (!txt.contains(".pcx"))
+ txt.append(".pcx");
+
+ loader->CacheBitmap(txt, award->small_insignia);
+
+ if (award->small_insignia)
+ award->small_insignia->AutoMask();
+ }
+
+ else if (pdef->name()->value() == ("type")) {
+ Text txt;
+ GetDefText(txt, pdef, filename);
+ txt.setSensitive(false);
+
+ if (txt == "rank")
+ award->type = AwardInfo::RANK;
+
+ else if (txt == "medal")
+ award->type = AwardInfo::MEDAL;
+ }
+
+ else if (pdef->name()->value() == ("id"))
+ GetDefNumber(award->id, pdef, filename);
+
+ else if (pdef->name()->value() == ("total_points"))
+ GetDefNumber(award->total_points, pdef, filename);
+
+ else if (pdef->name()->value() == ("mission_points"))
+ GetDefNumber(award->mission_points, pdef, filename);
+
+ else if (pdef->name()->value() == ("total_missions"))
+ GetDefNumber(award->total_missions, pdef, filename);
+
+ else if (pdef->name()->value() == ("kills"))
+ GetDefNumber(award->kills, pdef, filename);
+
+ else if (pdef->name()->value() == ("lost"))
+ GetDefNumber(award->lost, pdef, filename);
+
+ else if (pdef->name()->value() == ("collision"))
+ GetDefNumber(award->collision, pdef, filename);
+
+ else if (pdef->name()->value() == ("campaign_id"))
+ GetDefNumber(award->campaign_id, pdef, filename);
+
+ else if (pdef->name()->value() == ("campaign_complete"))
+ GetDefBool(award->campaign_complete, pdef, filename);
+
+ else if (pdef->name()->value() == ("dynamic_campaign"))
+ GetDefBool(award->dynamic_campaign, pdef, filename);
+
+ else if (pdef->name()->value() == ("ceremony"))
+ GetDefBool(award->ceremony, pdef, filename);
+
+ else if (pdef->name()->value() == ("required_awards"))
+ GetDefNumber(award->required_awards, pdef, filename);
+
+ else if (pdef->name()->value() == ("lottery"))
+ GetDefNumber(award->lottery, pdef, filename);
+
+ else if (pdef->name()->value() == ("min_rank"))
+ GetDefNumber(award->min_rank, pdef, filename);
+
+ else if (pdef->name()->value() == ("max_rank"))
+ GetDefNumber(award->max_rank, pdef, filename);
+
+ else if (pdef->name()->value() == ("min_ship_class")) {
+ Text classname;
+ GetDefText(classname, pdef, filename);
+ award->min_ship_class = Ship::ClassForName(classname);
+ }
+
+ else if (pdef->name()->value() == ("max_ship_class")) {
+ Text classname;
+ GetDefText(classname, pdef, filename);
+ award->max_ship_class = Ship::ClassForName(classname);
+ }
+
+ else if (pdef->name()->value().indexOf("grant") == 0)
+ GetDefNumber(award->granted_ship_classes, pdef, filename);
+ }
+ }
+
+ if (award->type == AwardInfo::RANK) {
+ rank_table.append(award);
+ }
+
+ else if (award->type == AwardInfo::MEDAL) {
+ medal_table.append(award);
+ }
+
+ else {
+ delete award;
+ }
+ }
+ }
+ else {
+ Print("WARNING: unknown label '%s' in '%s'\n",
+ def->name()->value().data(), filename);
+ }
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ term->print();
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+ loader->SetDataPath(0);
+}
+
+// +-------------------------------------------------------------------+
+
+void
+Player::CreateUniqueID()
+{
+ ListIter<Player> iter = player_roster;
+ while (++iter) {
+ Player* p = iter.value();
+
+ if (p != this && p->uid >= uid)
+ uid = p->uid + 1;
+ }
+
+ if (uid < 1)
+ uid = 1;
+}
diff --git a/Stars45/Player.h b/Stars45/Player.h
new file mode 100644
index 0000000..39dbfcd
--- /dev/null
+++ b/Stars45/Player.h
@@ -0,0 +1,187 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Player.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Player / Logbook class
+*/
+
+
+#ifndef Player_h
+#define Player_h
+
+#include "Types.h"
+#include "List.h"
+#include "Text.h"
+
+// +-------------------------------------------------------------------+
+
+class Player;
+class Bitmap;
+class ShipStats;
+class AwardInfo;
+class Sound;
+
+// +-------------------------------------------------------------------+
+
+class Player
+{
+public:
+ static const char* TYPENAME() { return "Player"; }
+
+ Player(const char* name);
+ virtual ~Player();
+
+ int operator == (const Player& u) const { return name == u.name; }
+
+ int Identity() const { return uid; }
+ const Text& Name() const { return name; }
+ const Text& Password() const { return pass; }
+ const Text& Squadron() const { return squadron; }
+ const Text& Signature() const { return signature; }
+ const Text& ChatMacro(int n) const;
+ int CreateDate() const { return create_date; }
+ int Rank() const;
+ int Medal(int n) const;
+ int Points() const { return points; }
+ int Medals() const { return medals; }
+ int FlightTime() const { return flight_time; }
+ int Missions() const { return missions; }
+ int Kills() const { return kills; }
+ int Losses() const { return losses; }
+ int Campaigns() const { return campaigns; }
+ int Trained() const { return trained; }
+
+ int FlightModel() const { return flight_model; }
+ int FlyingStart() const { return flying_start; }
+ int LandingModel() const { return landing_model; }
+ int AILevel() const { return ai_level; }
+ int HUDMode() const { return hud_mode; }
+ int HUDColor() const { return hud_color; }
+ int FriendlyFire() const { return ff_level; }
+ int GridMode() const { return grid; }
+ int Gunsight() const { return gunsight; }
+
+ bool ShowAward() const { return award != 0; }
+ Text AwardName() const;
+ Text AwardDesc() const;
+ Bitmap* AwardImage() const;
+ Sound* AwardSound() const;
+
+ bool CanCommand(int ship_class);
+
+ void SetName(const char* n);
+ void SetPassword(const char* p);
+ void SetSquadron(const char* s);
+ void SetSignature(const char* s);
+ void SetChatMacro(int n, const char* m);
+ void SetCreateDate(int d);
+ void SetRank(int r);
+ void SetPoints(int p);
+ void SetMedals(int m);
+ void SetCampaigns(int n);
+ void SetTrained(int n);
+ void SetFlightTime(int t);
+ void SetMissions(int m);
+ void SetKills(int k);
+ void SetLosses(int l);
+
+ void AddFlightTime(int t);
+ void AddPoints(int p);
+ void AddMedal(int m);
+ void AddMissions(int m);
+ void AddKills(int k);
+ void AddLosses(int l);
+
+ bool HasTrained(int n) const;
+ bool HasCompletedCampaign(int id) const;
+ void SetCampaignComplete(int id);
+
+ void SetFlightModel(int n);
+ void SetFlyingStart(int n);
+ void SetLandingModel(int n);
+ void SetAILevel(int n);
+ void SetHUDMode(int n);
+ void SetHUDColor(int n);
+ void SetFriendlyFire(int n);
+ void SetGridMode(int n);
+ void SetGunsight(int n);
+
+ void ClearShowAward();
+
+ Text EncodeStats();
+ void DecodeStats(const char* stats);
+
+ int GetMissionPoints(ShipStats* stats, DWORD start_time);
+ void ProcessStats(ShipStats* stats, DWORD start_time);
+ bool EarnedAward(AwardInfo* a, ShipStats* s);
+
+ static const char* RankName(int rank);
+ static const char* RankAbrv(int rank);
+ static int RankFromName(const char* name);
+ static Bitmap* RankInsignia(int rank, int size);
+ static const char* RankDescription(int rank);
+ static const char* MedalName(int medal);
+ static Bitmap* MedalInsignia(int medal, int size);
+ static const char* MedalDescription(int medal);
+ static int CommandRankRequired(int ship_class);
+
+ static List<Player>& GetRoster();
+ static Player* GetCurrentPlayer();
+ static void SelectPlayer(Player* p);
+ static Player* Create(const char* name);
+ static void Destroy(Player* p);
+ static Player* Find(const char* name);
+ static void Initialize();
+ static void Close();
+ static void Load();
+ static void Save();
+ static bool ConfigExists();
+ static void LoadAwardTables();
+
+protected:
+ Player();
+
+ void CreateUniqueID();
+
+ int uid;
+ Text name;
+ Text pass;
+ Text squadron;
+ Text signature;
+ Text chat_macros[10];
+ int mfd[4];
+
+ // stats:
+ int create_date;
+ int points;
+ int medals; // bitmap of earned medals
+ int flight_time;
+ int missions;
+ int kills;
+ int losses;
+ int campaigns; // bitmap of completed campaigns
+ int trained; // id of highest training mission completed
+
+ // gameplay options:
+ int flight_model;
+ int flying_start;
+ int landing_model;
+ int ai_level;
+ int hud_mode;
+ int hud_color;
+ int ff_level;
+ int grid;
+ int gunsight;
+
+ // transient:
+ AwardInfo* award;
+};
+
+#endif Player_h \ No newline at end of file
diff --git a/Stars45/PlayerDlg.cpp b/Stars45/PlayerDlg.cpp
new file mode 100644
index 0000000..d0eb8d9
--- /dev/null
+++ b/Stars45/PlayerDlg.cpp
@@ -0,0 +1,389 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: PlayerDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "PlayerDlg.h"
+#include "AwardShowDlg.h"
+#include "ConfirmDlg.h"
+#include "MenuScreen.h"
+#include "Player.h"
+
+#include "FormatUtil.h"
+
+#include "Game.h"
+#include "DataLoader.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "EditBox.h"
+#include "ImageBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Keyboard.h"
+#include "MachineInfo.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(PlayerDlg, OnApply);
+DEF_MAP_CLIENT(PlayerDlg, OnCancel);
+DEF_MAP_CLIENT(PlayerDlg, OnSelectPlayer);
+DEF_MAP_CLIENT(PlayerDlg, OnAdd);
+DEF_MAP_CLIENT(PlayerDlg, OnDel);
+DEF_MAP_CLIENT(PlayerDlg, OnDelConfirm);
+DEF_MAP_CLIENT(PlayerDlg, OnRank);
+DEF_MAP_CLIENT(PlayerDlg, OnMedal);
+
+// +--------------------------------------------------------------------+
+
+PlayerDlg::PlayerDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ lst_roster(0), btn_add(0), btn_del(0),
+ txt_name(0), txt_password(0), txt_squadron(0), txt_signature(0),
+ img_rank(0)
+{
+ Init(def);
+}
+
+PlayerDlg::~PlayerDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlayerDlg::RegisterControls()
+{
+ lst_roster = (ListBox*) FindControl(200);
+ btn_add = (Button*) FindControl(101);
+ btn_del = (Button*) FindControl(102);
+ txt_name = (EditBox*) FindControl(201);
+ txt_password = (EditBox*) FindControl(202);
+ txt_squadron = (EditBox*) FindControl(203);
+ txt_signature = (EditBox*) FindControl(204);
+
+ lbl_createdate = FindControl(205);
+ lbl_rank = FindControl(206);
+ lbl_flighttime = FindControl(207);
+ lbl_missions = FindControl(208);
+ lbl_kills = FindControl(209);
+ lbl_losses = FindControl(210);
+ lbl_points = FindControl(211);
+
+ img_rank = (ImageBox*) FindControl(220);
+ REGISTER_CLIENT(EID_CLICK, img_rank, PlayerDlg, OnRank);
+
+ for (int i = 0; i < 15; i++) {
+ medals[i] = -1;
+ img_medals[i] = (ImageBox*) FindControl(230 + i);
+ if (img_medals[i])
+ REGISTER_CLIENT(EID_CLICK, img_medals[i], PlayerDlg, OnMedal);
+ }
+
+ for (i = 0; i < 10; i++) {
+ txt_chat[i] = (EditBox*) FindControl(300 + i);
+ }
+
+ REGISTER_CLIENT(EID_SELECT, lst_roster, PlayerDlg, OnSelectPlayer);
+ REGISTER_CLIENT(EID_CLICK, btn_add, PlayerDlg, OnAdd);
+ REGISTER_CLIENT(EID_CLICK, btn_del, PlayerDlg, OnDel);
+ REGISTER_CLIENT(EID_USER_1, btn_del, PlayerDlg, OnDelConfirm);
+
+ apply = (Button*) FindControl(1);
+ REGISTER_CLIENT(EID_CLICK, apply, PlayerDlg, OnApply);
+
+ cancel = (Button*) FindControl(2);
+ REGISTER_CLIENT(EID_CLICK, cancel, PlayerDlg, OnCancel);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlayerDlg::Show()
+{
+ FormWindow::Show();
+
+ if (!lst_roster) return;
+ lst_roster->ClearItems();
+ lst_roster->SetSelectedStyle(ListBox::LIST_ITEM_STYLE_FILLED_BOX);
+ lst_roster->SetLeading(2);
+
+ int current_index = 0;
+
+ List<Player>& roster = Player::GetRoster();
+ for (int i = 0; i < roster.size(); i++) {
+ Player* p = roster[i];
+ lst_roster->AddItem(p->Name());
+ if (p == Player::GetCurrentPlayer())
+ current_index = i;
+ }
+
+ lst_roster->SetSelected(current_index);
+ ShowPlayer();
+
+ apply->SetEnabled(roster.size() > 0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlayerDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ OnApply(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlayerDlg::ShowPlayer()
+{
+ Player* p = Player::GetCurrentPlayer();
+
+ if (p) {
+ if (txt_name) txt_name->SetText(p->Name());
+ if (txt_password) txt_password->SetText(p->Password());
+ if (txt_squadron) txt_squadron->SetText(p->Squadron());
+ if (txt_signature) txt_signature->SetText(p->Signature());
+
+ char flight_time[64], missions[16], kills[16], losses[16], points[16];
+ FormatTime(flight_time, p->FlightTime());
+ sprintf(missions, "%d", p->Missions());
+ sprintf(kills, "%d", p->Kills());
+ sprintf(losses, "%d", p->Losses());
+ sprintf(points, "%d", p->Points());
+
+ if (lbl_createdate) lbl_createdate->SetText(FormatTimeString(p->CreateDate()));
+ if (lbl_rank) lbl_rank->SetText(Player::RankName(p->Rank()));
+ if (lbl_flighttime) lbl_flighttime->SetText(flight_time);
+ if (lbl_missions) lbl_missions->SetText(missions);
+ if (lbl_kills) lbl_kills->SetText(kills);
+ if (lbl_losses) lbl_losses->SetText(losses);
+ if (lbl_points) lbl_points->SetText(points);
+
+ if (img_rank) {
+ img_rank->SetPicture(*Player::RankInsignia(p->Rank(), 0));
+ img_rank->Show();
+ }
+
+ for (int i = 0; i < 15; i++) {
+ if (img_medals[i]) {
+ int medal = p->Medal(i);
+ if (medal) {
+ medals[i] = medal;
+ img_medals[i]->SetPicture(*Player::MedalInsignia(medal, 0));
+ img_medals[i]->Show();
+ }
+ else {
+ medals[i] = -1;
+ img_medals[i]->Hide();
+ }
+ }
+ }
+
+ for (i = 0; i < 10; i++) {
+ if (txt_chat[i])
+ txt_chat[i]->SetText(p->ChatMacro(i));
+ }
+ }
+ else {
+ if (txt_name) txt_name->SetText("");
+ if (txt_password) txt_password->SetText("");
+ if (txt_squadron) txt_squadron->SetText("");
+ if (txt_signature) txt_signature->SetText("");
+
+ if (lbl_createdate) lbl_createdate->SetText("");
+ if (lbl_rank) lbl_rank->SetText("");
+ if (lbl_flighttime) lbl_flighttime->SetText("");
+ if (lbl_missions) lbl_missions->SetText("");
+ if (lbl_kills) lbl_kills->SetText("");
+ if (lbl_losses) lbl_losses->SetText("");
+ if (lbl_points) lbl_points->SetText("");
+
+ if (img_rank) img_rank->Hide();
+
+ for (int i = 0; i < 15; i++) {
+ medals[i] = -1;
+ if (img_medals[i])
+ img_medals[i]->Hide();
+ }
+
+ for (i = 0; i < 10; i++) {
+ if (txt_chat[i])
+ txt_chat[i]->SetText("");
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlayerDlg::UpdatePlayer()
+{
+ Player* p = Player::GetCurrentPlayer();
+
+ if (p) {
+ if (txt_name) p->SetName(txt_name->GetText());
+ if (txt_password) p->SetPassword(txt_password->GetText());
+ if (txt_squadron) p->SetSquadron(txt_squadron->GetText());
+ if (txt_signature) p->SetSignature(txt_signature->GetText());
+
+ for (int i = 0; i < 10; i++) {
+ if (txt_chat[i])
+ p->SetChatMacro(i, txt_chat[i]->GetText());
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlayerDlg::OnSelectPlayer(AWEvent* event)
+{
+ if (!lst_roster) return;
+
+ UpdatePlayer();
+
+ int index = lst_roster->GetSelection();
+
+ List<Player>& roster = Player::GetRoster();
+ if (index >= 0 && index < roster.size()) {
+ Player::SelectPlayer(roster.at(index));
+ }
+
+ ShowPlayer();
+
+ apply->SetEnabled(roster.size() > 0);
+}
+
+void
+PlayerDlg::OnAdd(AWEvent* event)
+{
+ Player::Create("Pilot");
+ ShowPlayer();
+
+ if (!lst_roster) return;
+ lst_roster->ClearItems();
+
+ List<Player>& roster = Player::GetRoster();
+ for (int i = 0; i < roster.size(); i++) {
+ Player* p = roster[i];
+ lst_roster->AddItem(p->Name());
+ lst_roster->SetSelected(i, (p == Player::GetCurrentPlayer()));
+ }
+
+ apply->SetEnabled(roster.size() > 0);
+}
+
+void
+PlayerDlg::OnDel(AWEvent* event)
+{
+ if (!Player::GetCurrentPlayer())
+ return;
+
+ ConfirmDlg* confirm = manager->GetConfirmDlg();
+ if (confirm) {
+ char msg[256];
+ sprintf(msg, Game::GetText("PlayerDlg.are-you-sure").data(),
+ Player::GetCurrentPlayer()->Name().data());
+ confirm->SetMessage(msg);
+ confirm->SetTitle(Game::GetText("PlayerDlg.confirm-delete"));
+ confirm->SetParentControl(btn_del);
+
+ manager->ShowConfirmDlg();
+ }
+
+ else {
+ OnDelConfirm(event);
+ }
+}
+
+void
+PlayerDlg::OnDelConfirm(AWEvent* event)
+{
+ Player::Destroy(Player::GetCurrentPlayer());
+ ShowPlayer();
+
+ if (!lst_roster) return;
+ lst_roster->ClearItems();
+
+ List<Player>& roster = Player::GetRoster();
+ for (int i = 0; i < roster.size(); i++) {
+ Player* p = roster[i];
+ lst_roster->AddItem(p->Name());
+ lst_roster->SetSelected(i, (p == Player::GetCurrentPlayer()));
+ }
+
+ apply->SetEnabled(roster.size() > 0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlayerDlg::OnRank(AWEvent* event)
+{
+ Player* p = Player::GetCurrentPlayer();
+ AwardShowDlg* award_dlg = manager->GetAwardDlg();
+
+ if (p && award_dlg) {
+ award_dlg->SetRank(p->Rank());
+ manager->ShowAwardDlg();
+ }
+}
+
+void
+PlayerDlg::OnMedal(AWEvent* event)
+{
+ Player* p = Player::GetCurrentPlayer();
+ AwardShowDlg* award_dlg = manager->GetAwardDlg();
+
+ if (p && award_dlg) {
+ int m = -1;
+
+ for (int i = 0; i < 15; i++) {
+ if (event->window == img_medals[i]) {
+ m = i;
+ break;
+ }
+ }
+
+ if (m >= 0) {
+ award_dlg->SetMedal(medals[i]);
+ manager->ShowAwardDlg();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlayerDlg::OnApply(AWEvent* event)
+{
+ Player* player = Player::GetCurrentPlayer();
+ if (player) {
+ UpdatePlayer();
+ Player::Save();
+ }
+
+ FlushKeys();
+ manager->ShowMenuDlg();
+}
+
+void
+PlayerDlg::OnCancel(AWEvent* event)
+{
+ Player::Load();
+ FlushKeys();
+ manager->ShowMenuDlg();
+}
diff --git a/Stars45/PlayerDlg.h b/Stars45/PlayerDlg.h
new file mode 100644
index 0000000..ff0af40
--- /dev/null
+++ b/Stars45/PlayerDlg.h
@@ -0,0 +1,86 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: PlayerDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#ifndef PlayerDlg_h
+#define PlayerDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Font.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+class Player;
+
+// +--------------------------------------------------------------------+
+
+class PlayerDlg : public FormWindow
+{
+public:
+ PlayerDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~PlayerDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnApply(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+ virtual void OnSelectPlayer(AWEvent* event);
+ virtual void OnAdd(AWEvent* event);
+ virtual void OnDel(AWEvent* event);
+ virtual void OnDelConfirm(AWEvent* event);
+ virtual void OnRank(AWEvent* event);
+ virtual void OnMedal(AWEvent* event);
+
+ virtual void UpdatePlayer();
+ virtual void ShowPlayer();
+
+protected:
+ MenuScreen* manager;
+
+ ListBox* lst_roster;
+ Button* btn_add;
+ Button* btn_del;
+
+ EditBox* txt_name;
+ EditBox* txt_password;
+ EditBox* txt_squadron;
+ EditBox* txt_signature;
+
+ EditBox* txt_chat[10];
+
+ ActiveWindow* lbl_createdate;
+ ActiveWindow* lbl_rank;
+ ActiveWindow* lbl_flighttime;
+ ActiveWindow* lbl_missions;
+ ActiveWindow* lbl_kills;
+ ActiveWindow* lbl_losses;
+ ActiveWindow* lbl_points;
+ ImageBox* img_rank;
+ ImageBox* img_medals[15];
+ int medals[15];
+
+ Button* apply;
+ Button* cancel;
+};
+
+#endif PlayerDlg_h
+
diff --git a/Stars45/Power.cpp b/Stars45/Power.cpp
new file mode 100644
index 0000000..7090d2f
--- /dev/null
+++ b/Stars45/Power.cpp
@@ -0,0 +1,300 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Power.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Power generation and usage classes
+*/
+
+#include "MemDebug.h"
+#include "Power.h"
+#include "Ship.h"
+#include "NetUtil.h"
+
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+static char* source_type[] = {
+ "sys.power.battery", "sys.power.auxilliary", "sys.power.fusion"
+};
+
+static int source_value[] = {
+ 1, 2, 4
+};
+
+PowerSource::PowerSource(SUBTYPE s, double max_out, double f_ratio)
+ : System(POWER_SOURCE, (int) s, "Power", source_value[s], 0),
+ max_output((float) max_out), fuel_ratio((float) f_ratio),
+ route_changed(false), requested_power_level(1.0f)
+{
+ name = Game::GetText(source_type[s]);
+ abrv = Game::GetText(Text(source_type[s]) + ".abrv");
+
+ if (fuel_ratio < 1) {
+ switch (subtype) { // enough to last for [n] hours at full power
+ case BATTERY: fuel_ratio = max_output * 5 * 3600 / 100; break;
+ case AUX: fuel_ratio = max_output * 50 * 3600 / 100; break;
+ case FUSION: fuel_ratio = max_output * 100 * 3600 / 100; break;
+ }
+ }
+
+ capacity = 100.0f;
+
+ if (subtype != BATTERY) {
+ emcon_power[0] = 10;
+ emcon_power[1] = 50;
+ emcon_power[2] = 100;
+ }
+}
+
+PowerSource::PowerSource(const PowerSource& p)
+ : System(p),
+ max_output(p.max_output), fuel_ratio(p.fuel_ratio),
+ route_changed(false), requested_power_level(1.0f)
+{
+ Mount(p);
+ SetAbbreviation(p.Abbreviation());
+}
+
+// +----------------------------------------------------------------------+
+
+void
+PowerSource::AddClient(System* client)
+{
+ if (client) {
+ int old_src_index = client->GetSourceIndex();
+
+ client->SetSourceIndex(GetID());
+ clients.append(client);
+ route_changed = true;
+
+ if (ship && old_src_index != GetID())
+ NetUtil::SendSysStatus(ship, client);
+ }
+}
+
+void
+PowerSource::RemoveClient(System* client)
+{
+ if (client && clients.contains(client)) {
+ client->SetSourceIndex(-1);
+ clients.remove(client);
+ route_changed = true;
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+int
+PowerSource::Charge() const
+{
+ return (int) capacity;
+}
+
+void
+PowerSource::SetFuelRange(double hours)
+{
+ if (hours > 0)
+ fuel_ratio = (float) (max_output * hours * 3600 / 100);
+}
+
+// +----------------------------------------------------------------------+
+
+void
+PowerSource::ExecFrame(double seconds)
+{
+ if (seconds < 0.001)
+ seconds = 0.001;
+
+ if (capacity <= 0)
+ capacity = 0;
+
+ System::ExecFrame(seconds);
+
+ double energy_requested = 0;
+ double energy_avail = 0;
+ double total_distrib = 0;
+
+ // fuel leak?
+ if (availability < 0.4 && capacity > 0)
+ capacity -= (float) (0.03 * seconds);
+
+ if (IsPowerOn() && capacity > 0) {
+ energy_avail = max_output * seconds * power_level * availability;
+
+ if (power_level < requested_power_level) {
+ power_level += (float) (seconds * 0.03); // thirty seconds to charge up
+
+ if (power_level > requested_power_level)
+ power_level = (float) requested_power_level;
+ }
+ else if (power_level > requested_power_level) {
+ power_level -= (float) (seconds * 0.10); // ten seconds to power down
+
+ if (power_level < requested_power_level)
+ power_level = (float) requested_power_level;
+ }
+ }
+
+ ListIter<System> iter = clients;
+ while (++iter) {
+ System* sink = iter.value();
+
+ if (sink->IsPowerOn()) {
+ double joules = sink->GetRequest(seconds);
+
+ if (joules > 0) {
+ if (sink->IsPowerCritical()) {
+
+ if (joules > energy_avail)
+ joules = energy_avail;
+
+ energy_avail -= joules;
+ total_distrib += joules;
+ sink->Distribute(joules, seconds);
+ }
+ else {
+ energy_requested += joules;
+ }
+ }
+ }
+ else {
+ sink->Distribute(-0.2 * sink->GetCapacity() * seconds, seconds);
+ }
+ }
+
+ if (energy_avail > 0) {
+
+ // enough to go around:
+ if (energy_requested <= energy_avail) {
+ iter.reset();
+
+ while (++iter) {
+ System* sink = iter.value();
+
+ if (sink->IsPowerOn() && !sink->IsPowerCritical()) {
+ double joules = sink->GetRequest(seconds);
+ total_distrib += joules;
+ sink->Distribute(joules, seconds);
+ }
+ }
+ }
+
+ // load balancing:
+ else {
+ iter.reset();
+ while (++iter) {
+ System* sink = iter.value();
+
+ if (sink->IsPowerOn() && !sink->IsPowerCritical()) {
+ double request = sink->GetRequest(seconds);
+ double delivery = 0;
+
+ if (request > 0)
+ delivery = energy_avail * (request/energy_requested);
+
+ if (delivery > 0) {
+ total_distrib += delivery;
+ sink->Distribute(delivery, seconds);
+ }
+ }
+ }
+ }
+ }
+
+ if (IsPowerOn() && capacity > 0 && !Game::Paused()) {
+ // reactors always burn at least 10% of max (to maintain operating temp):
+ if (subtype != BATTERY) {
+ double baseline = 0.1 * max_output * seconds;
+
+ if (total_distrib < baseline)
+ total_distrib = baseline;
+ }
+
+ // expend fuel:
+ if (total_distrib > 0) {
+ double effective_fuel_ratio = fuel_ratio;
+
+ switch (Ship::GetFlightModel()) {
+ default:
+ case Ship::FM_STANDARD:
+ effective_fuel_ratio = 1 * fuel_ratio * (0.25 + 0.75 * availability);
+ break;
+
+ case Ship::FM_RELAXED:
+ effective_fuel_ratio = 3 * fuel_ratio * (0.25 + 0.75 * availability);
+ break;
+
+ case Ship::FM_ARCADE:
+ effective_fuel_ratio = 4 * fuel_ratio * (0.25 + 0.75 * availability);
+ break;
+ }
+
+ capacity -= (float) (total_distrib / effective_fuel_ratio);
+ }
+ }
+
+ else if (capacity <= 0) {
+ capacity = 0.0f;
+ PowerOff();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PowerSource::SetPowerLevel(double level)
+{
+ if (level > 100)
+ level = 100;
+ else if (level < 0)
+ level = 0;
+
+ level /= 100;
+
+ if (requested_power_level != level) {
+ // if the system is on emergency override power,
+ // do not let the EMCON system use this method
+ // to drop it back to normal power:
+ if (requested_power_level > 1 && level == 1) {
+ requested_power_level = 1.2f;
+ }
+
+ else {
+ requested_power_level = (float) level;
+ }
+ }
+}
+
+void
+PowerSource::SetOverride(bool over)
+{
+ bool changed = false;
+
+ if (over && requested_power_level != 1.2f) {
+ requested_power_level = 1.2f;
+ changed = true;
+ }
+
+ else if (!over && requested_power_level > 1) {
+ requested_power_level = 1.0f;
+ changed = true;
+ }
+
+ if (changed)
+ NetUtil::SendSysStatus(ship, this);
+}
+
+void
+PowerSource::DrainPower(double to_level)
+{
+ if (to_level >= 0 && to_level < power_level)
+ power_level = (float) to_level;
+}
diff --git a/Stars45/Power.h b/Stars45/Power.h
new file mode 100644
index 0000000..893095a
--- /dev/null
+++ b/Stars45/Power.h
@@ -0,0 +1,62 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Power.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Power generation and usage classes
+*/
+
+#ifndef Power_h
+#define Power_h
+
+#include "Types.h"
+#include "System.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class PowerSource : public System
+{
+public:
+ enum SUBTYPE { BATTERY, AUX, FUSION };
+
+ PowerSource(SUBTYPE s, double max_output, double fuel_ratio=0);
+ PowerSource(const PowerSource& rhs);
+
+ virtual void ExecFrame(double seconds);
+
+ void AddClient(System* client);
+ void RemoveClient(System* client);
+
+ List<System>& Clients() { return clients; }
+
+ virtual int Charge() const;
+
+ virtual void SetFuelRange(double hours);
+
+ bool RouteChanged() const { return route_changed; }
+ void RouteScanned() { route_changed = false; }
+
+ // override from System:
+ virtual void SetPowerLevel(double level);
+ virtual void SetOverride(bool over);
+
+ // for power drain damage:
+ virtual void DrainPower(double to_level);
+
+protected:
+ float max_output;
+ float fuel_ratio;
+ List<System> clients;
+ bool route_changed;
+ float requested_power_level;
+};
+
+#endif Power_h
+
diff --git a/Stars45/QuantumDrive.cpp b/Stars45/QuantumDrive.cpp
new file mode 100644
index 0000000..57e2c94
--- /dev/null
+++ b/Stars45/QuantumDrive.cpp
@@ -0,0 +1,246 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: QuantumDrive.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Quantum Drive class
+*/
+
+#include "MemDebug.h"
+#include "QuantumDrive.h"
+#include "Ship.h"
+#include "Explosion.h"
+#include "Drive.h"
+#include "Sim.h"
+#include "SimEvent.h"
+#include "StarSystem.h"
+
+#include "Game.h"
+#include "Random.h"
+
+// +----------------------------------------------------------------------+
+
+static int drive_value = 3;
+
+// +----------------------------------------------------------------------+
+
+QuantumDrive::QuantumDrive(SUBTYPE s, double cap, double rate)
+ : System(DRIVE, s, "Quantum", drive_value, (float) cap, (float) cap, (float) rate),
+ dst_rgn(0), active_state(ACTIVE_READY), warp_fov(1), jump_time(0), countdown(5)
+{
+ name = Game::GetText("sys.quantum");
+ abrv = Game::GetText("sys.quantum.abrv");
+
+ emcon_power[0] = 0;
+ emcon_power[1] = 0;
+ emcon_power[2] = 100;
+}
+
+// +----------------------------------------------------------------------+
+
+QuantumDrive::QuantumDrive(const QuantumDrive& d)
+ : System(d),
+ dst_rgn(0), active_state(ACTIVE_READY), warp_fov(1), jump_time(0),
+ countdown(d.countdown)
+{
+ Mount(d);
+ SetAbbreviation(d.Abbreviation());
+
+ energy = capacity;
+}
+
+// +--------------------------------------------------------------------+
+
+QuantumDrive::~QuantumDrive()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+QuantumDrive::SetDestination(SimRegion* rgn, const Point& loc)
+{
+ dst_rgn = rgn;
+ dst_loc = loc;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+QuantumDrive::Engage(bool immediate)
+{
+ if (active_state == ACTIVE_READY && ship != 0 &&
+ IsPowerOn() && Status() == NOMINAL && energy == capacity) {
+
+ active_state = ACTIVE_COUNTDOWN;
+ if (immediate) {
+ jump_time = 1;
+ return true;
+ }
+
+ jump_time = countdown;
+
+ SimRegion* rgn = ship->GetRegion();
+
+ ListIter<Ship> s_iter = rgn->Ships();
+ while (++s_iter) {
+ Ship* s = s_iter.value();
+
+ if (s != ship) {
+ double dist = Point(s->Location() - ship->Location()).length();
+
+ if (dist < 25e3)
+ jump_time += 5;
+
+ else if (dist < 50e3)
+ jump_time += 2;
+
+ else if (dist < 100e3)
+ jump_time += 1;
+
+ else if (dist < 200e3)
+ jump_time += 0.5;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+void
+QuantumDrive::PowerOff()
+{
+ System::PowerOff();
+ AbortJump();
+}
+
+void
+QuantumDrive::AbortJump()
+{
+ active_state = ACTIVE_READY;
+ jump_time = 0;
+ energy = 0;
+ warp_fov = 1;
+
+ ship->SetWarp(warp_fov);
+
+ SimRegion* r = ship->GetRegion();
+ ListIter<Ship> neighbor = r->Ships();
+
+ while (++neighbor) {
+ if (neighbor->IsDropship()) {
+ Ship* s = neighbor.value();
+ Point delta = s->Location() - ship->Location();
+
+ if (delta.length() < 5e3)
+ s->SetWarp(warp_fov);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+QuantumDrive::ExecFrame(double seconds)
+{
+ System::ExecFrame(seconds);
+
+ if (active_state == ACTIVE_READY)
+ return;
+
+ if (ship) {
+ bool warping = false;
+
+ if (active_state == ACTIVE_COUNTDOWN) {
+ if (jump_time > 0) {
+ jump_time -= seconds;
+ }
+
+ else {
+ jump_time = 0;
+ active_state = ACTIVE_PREWARP;
+ }
+ }
+
+ else if (active_state == ACTIVE_PREWARP) {
+ if (warp_fov < 5000) {
+ warp_fov *= 1.5;
+ }
+ else {
+ Jump();
+ energy = 0.0f;
+ }
+
+ warping = true;
+ }
+
+ else if (active_state == ACTIVE_POSTWARP) {
+ if (warp_fov > 1) {
+ warp_fov *= 0.75;
+ }
+ else {
+ warp_fov = 1;
+ active_state = ACTIVE_READY;
+ }
+
+ warping = true;
+ }
+
+ if (warping) {
+ ship->SetWarp(warp_fov);
+
+ SimRegion* r = ship->GetRegion();
+ ListIter<Ship> neighbor = r->Ships();
+
+ while (++neighbor) {
+ if (neighbor->IsDropship()) {
+ Ship* s = neighbor.value();
+ Point delta = s->Location() - ship->Location();
+
+ if (delta.length() < 5e3)
+ s->SetWarp(warp_fov);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+QuantumDrive::Jump()
+{
+ Sim* sim = Sim::GetSim();
+
+ if (ship && sim) {
+ double dist = 150e3 + Random(0, 60e3);
+ Point esc_vec = dst_rgn->GetOrbitalRegion()->Location() -
+ dst_rgn->GetOrbitalRegion()->Primary()->Location();
+
+ esc_vec.Normalize();
+ esc_vec *= dist;
+ esc_vec += RandomDirection() * Random(15e3, 22e3);
+
+ if (subtype == HYPER)
+ sim->CreateExplosion(ship->Location(), Point(0,0,0), Explosion::HYPER_FLASH, 1, 1, ship->GetRegion());
+ else
+ sim->CreateExplosion(ship->Location(), Point(0,0,0), Explosion::QUANTUM_FLASH, 1, 0, ship->GetRegion());
+
+ sim->RequestHyperJump(ship, dst_rgn, esc_vec);
+
+ ShipStats* stats = ShipStats::Find(ship->Name());
+ stats->AddEvent(SimEvent::QUANTUM_JUMP, dst_rgn->Name());
+ }
+
+ dst_rgn = 0;
+ dst_loc = Point();
+
+ active_state = ACTIVE_POSTWARP;
+}
diff --git a/Stars45/QuantumDrive.h b/Stars45/QuantumDrive.h
new file mode 100644
index 0000000..e2c41b7
--- /dev/null
+++ b/Stars45/QuantumDrive.h
@@ -0,0 +1,73 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: QuantumDrive.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Quantum (JUMP) Drive (system) class
+*/
+
+#ifndef QuantumDrive_h
+#define QuantumDrive_h
+
+#include "Types.h"
+#include "System.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+class SimRegion;
+
+// +--------------------------------------------------------------------+
+
+class QuantumDrive : public System
+{
+public:
+ enum SUBTYPE { QUANTUM, HYPER };
+
+ QuantumDrive(SUBTYPE s, double capacity, double sink_rate);
+ QuantumDrive(const QuantumDrive& rhs);
+ virtual ~QuantumDrive();
+
+ enum ACTIVE_STATES {
+ ACTIVE_READY, ACTIVE_COUNTDOWN, ACTIVE_PREWARP, ACTIVE_POSTWARP
+ };
+
+ void SetDestination(SimRegion* rgn, const Point& loc);
+ bool Engage(bool immediate=false);
+ int ActiveState() const { return active_state; }
+ double WarpFactor() const { return warp_fov; }
+ double JumpTime() const { return jump_time; }
+ virtual void PowerOff();
+
+ virtual void ExecFrame(double seconds);
+
+ void SetShip(Ship* s) { ship = s; }
+ Ship* GetShip() const { return ship; }
+
+ double GetCountdown() const { return countdown; }
+ void SetCountdown(double d) { countdown = d; }
+
+protected:
+ void Jump();
+ void AbortJump();
+
+ int active_state;
+
+ Ship* ship;
+ double warp_fov;
+ double jump_time;
+ double countdown;
+
+ SimRegion* dst_rgn;
+ Point dst_loc;
+};
+
+#endif // QuantumDrive_h
+
diff --git a/Stars45/QuantumFlash.cpp b/Stars45/QuantumFlash.cpp
new file mode 100644
index 0000000..f47b3b5
--- /dev/null
+++ b/Stars45/QuantumFlash.cpp
@@ -0,0 +1,175 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: QuantumFlash.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Quantum Warp Out special effect class
+*/
+
+#include "MemDebug.h"
+#include "QuantumFlash.h"
+
+#include "Light.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Game.h"
+#include "Random.h"
+#include "Scene.h"
+
+// +--------------------------------------------------------------------+
+
+static Bitmap* quantum_flash_texture = 0;
+
+// +--------------------------------------------------------------------+
+
+QuantumFlash::QuantumFlash()
+ : nverts(64), npolys(16), mtl(0), verts(0), polys(0), beams(0),
+ texture(quantum_flash_texture), length(8000), width(0),
+ shade(1.0)
+{
+ trans = true;
+ luminous = true;
+
+ if (!texture || texture->Width() < 1) {
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Explosions/");
+ loader->LoadTexture("quantum.pcx", quantum_flash_texture, Bitmap::BMP_TRANSLUCENT);
+ loader->SetDataPath(0);
+ texture = quantum_flash_texture;
+ }
+
+ loc = Vec3(0.0f, 0.0f, 1000.0f);
+
+ mtl = new(__FILE__,__LINE__) Material;
+ verts = new(__FILE__,__LINE__) VertexSet(nverts);
+ polys = new(__FILE__,__LINE__) Poly[npolys];
+ beams = new(__FILE__,__LINE__) Matrix[npolys];
+
+ mtl->Kd = Color::White;
+ mtl->tex_diffuse = texture;
+ mtl->tex_emissive = texture;
+ mtl->blend = Video::BLEND_ADDITIVE;
+ mtl->luminous = true;
+
+ verts->nverts = nverts;
+
+ for (int i = 0; i < npolys; i++) {
+ verts->loc[4*i+0] = Point( width, 0, 1000);
+ verts->loc[4*i+1] = Point( width, -length, 1000);
+ verts->loc[4*i+2] = Point(-width, -length, 1000);
+ verts->loc[4*i+3] = Point(-width, 0, 1000);
+
+ for (int n = 0; n < 4; n++) {
+ verts->diffuse[4*i+n] = D3DCOLOR_RGBA(255,255,255,255);
+ verts->specular[4*i+n] = D3DCOLOR_RGBA( 0, 0, 0,255);
+ verts->tu[4*i+n] = (n < 2) ? 0.0f : 1.0f;
+ verts->tv[4*i+n] = (n > 0 && n < 3) ? 1.0f : 0.0f;
+ }
+
+ beams[i].Roll( Random(-2*PI, 2*PI));
+ beams[i].Pitch(Random(-2*PI, 2*PI));
+ beams[i].Yaw( Random(-2*PI, 2*PI));
+
+ polys[i].nverts = 4;
+ polys[i].visible = 1;
+ polys[i].sortval = 0;
+ polys[i].vertex_set = verts;
+ polys[i].material = mtl;
+
+ polys[i].verts[0] = 4*i+0;
+ polys[i].verts[1] = 4*i+1;
+ polys[i].verts[2] = 4*i+2;
+ polys[i].verts[3] = 4*i+3;
+ }
+
+ radius = (float) length;
+ length = 0;
+ strcpy(name, "QuantumFlash");
+}
+
+// +--------------------------------------------------------------------+
+
+QuantumFlash::~QuantumFlash()
+{
+ delete mtl;
+ delete verts;
+ delete [] polys;
+ delete [] beams;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+QuantumFlash::Render(Video* video, DWORD flags)
+{
+ if (hidden || !visible || !video || ((flags & RENDER_ADDITIVE) == 0))
+ return;
+
+ const Camera* cam = video->GetCamera();
+
+ if (cam) {
+ UpdateVerts(cam->Pos());
+ video->DrawPolys(npolys, polys);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+QuantumFlash::UpdateVerts(const Point& cam_pos)
+{
+ if (length < radius) {
+ length += radius/80;
+ width += 1;
+ }
+
+ for (int i = 0; i < npolys; i++) {
+ Matrix& m = beams[i];
+
+ m.Yaw(0.05);
+ Point vpn = Point(m(2,0), m(2,1), m(2,2));
+
+ Point head = loc;
+ Point tail = loc - vpn * length;
+ Point vtail = tail - head;
+ Point vcam = cam_pos - loc;
+ Point vtmp = vcam.cross(vtail);
+ vtmp.Normalize();
+ Point vlat = vtmp * -width;
+
+ verts->loc[4*i+0] = head + vlat;
+ verts->loc[4*i+1] = tail + vlat * 8;
+ verts->loc[4*i+2] = tail - vlat * 8;
+ verts->loc[4*i+3] = head - vlat;
+
+ DWORD color = D3DCOLOR_RGBA((BYTE) (255*shade), (BYTE) (255*shade), (BYTE) (255*shade), 255);
+
+ for (int n = 0; n < 4; n++) {
+ verts->diffuse[4*i+n] = color;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+QuantumFlash::SetOrientation(const Matrix& o)
+{
+ orientation = o;
+}
+
+void
+QuantumFlash::SetShade(double s)
+{
+ if (s < 0) s = 0;
+ else if (s > 1) s = 1;
+
+ shade = s;
+}
+
diff --git a/Stars45/QuantumFlash.h b/Stars45/QuantumFlash.h
new file mode 100644
index 0000000..0e07306
--- /dev/null
+++ b/Stars45/QuantumFlash.h
@@ -0,0 +1,60 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: QuantumFlash.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Quantum Warp Out special effect class
+*/
+
+#ifndef QuantumFlash_h
+#define QuantumFlash_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Graphic.h"
+#include "Polygon.h"
+#include "SimObject.h"
+
+// +--------------------------------------------------------------------+
+
+class QuantumFlash : public Graphic
+{
+public:
+ QuantumFlash();
+ virtual ~QuantumFlash();
+
+ // operations
+ virtual void Render(Video* video, DWORD flags);
+
+ // accessors / mutators
+ virtual void SetOrientation(const Matrix& o);
+ void SetDirection(const Point& v);
+ void SetEndPoints(const Point& from, const Point& to);
+
+ double Shade() const { return shade; }
+ void SetShade(double s);
+
+protected:
+ void UpdateVerts(const Point& cam_pos);
+
+ double length;
+ double width;
+ double shade;
+
+ int npolys, nverts;
+ Material* mtl;
+ VertexSet* verts;
+ Poly* polys;
+ Matrix* beams;
+ Bitmap* texture;
+
+ Matrix orientation;
+};
+
+#endif QuantumFlash_h
diff --git a/Stars45/QuantumView.cpp b/Stars45/QuantumView.cpp
new file mode 100644
index 0000000..07f5154
--- /dev/null
+++ b/Stars45/QuantumView.cpp
@@ -0,0 +1,318 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: QuantumView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ View class for Quantum Destination HUD Overlay
+*/
+
+#include "MemDebug.h"
+#include "QuantumView.h"
+#include "QuantumDrive.h"
+#include "HUDView.h"
+#include "Ship.h"
+#include "Element.h"
+#include "Sim.h"
+#include "StarSystem.h"
+#include "FormatUtil.h"
+
+#include "Color.h"
+#include "Window.h"
+#include "Video.h"
+#include "Screen.h"
+#include "DataLoader.h"
+#include "Scene.h"
+#include "Font.h"
+#include "FontMgr.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "Game.h"
+#include "Menu.h"
+
+static Color hud_color = Color::Black;
+
+// +====================================================================+
+//
+// QUANTUM DRIVE DESTINATION MENU:
+//
+
+static Menu* quantum_menu = 0;
+static bool show_menu = false;
+
+void
+QuantumView::Initialize()
+{
+ static int initialized = 0;
+ if (initialized) return;
+
+ quantum_menu = new(__FILE__,__LINE__) Menu(Game::GetText("QuantumView.menu"));
+
+ initialized = 1;
+}
+
+void
+QuantumView::Close()
+{
+ delete quantum_menu;
+}
+
+// +====================================================================+
+
+QuantumView* QuantumView::quantum_view = 0;
+
+QuantumView::QuantumView(Window* c)
+ : View(c), sim(0), ship(0), font(0)
+{
+ quantum_view = this;
+ sim = Sim::GetSim();
+
+ width = window->Width();
+ height = window->Height();
+ xcenter = (width / 2.0) - 0.5;
+ ycenter = (height / 2.0) + 0.5;
+ font = FontMgr::Find("HUD");
+
+ HUDView* hud = HUDView::GetInstance();
+ if (hud)
+ SetColor(hud->GetTextColor());
+}
+
+QuantumView::~QuantumView()
+{
+ quantum_view = 0;
+}
+
+void
+QuantumView::OnWindowMove()
+{
+ width = window->Width();
+ height = window->Height();
+ xcenter = (width / 2.0) - 0.5;
+ ycenter = (height / 2.0) + 0.5;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+QuantumView::Update(SimObject* obj)
+{
+ if (obj == ship)
+ ship = 0;
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+QuantumView::GetObserverName() const
+{
+ return "QuantumView";
+}
+
+// +--------------------------------------------------------------------+
+
+void
+QuantumView::Refresh()
+{
+ sim = Sim::GetSim();
+
+ if (sim && ship != sim->GetPlayerShip()) {
+ ship = sim->GetPlayerShip();
+
+ if (ship) {
+ if (ship->Life() == 0 || ship->IsDying() || ship->IsDead()) {
+ ship = 0;
+ }
+ else {
+ Observe(ship);
+ }
+ }
+ }
+
+ if (IsMenuShown()) {
+ Rect menu_rect(width-115, 10, 115, 12);
+
+ font->SetColor(hud_color);
+ font->SetAlpha(1);
+ font->DrawText(quantum_menu->GetTitle(), 0, menu_rect, DT_CENTER);
+
+ menu_rect.y += 15;
+
+ ListIter<MenuItem> item = quantum_menu->GetItems();
+ while (++item) {
+ item->SetEnabled(true);
+
+ font->DrawText(item->GetText(), 0, menu_rect, DT_LEFT);
+ menu_rect.y += 10;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+QuantumView::ExecFrame()
+{
+ HUDView* hud = HUDView::GetInstance();
+ if (hud) {
+ if (hud_color != hud->GetTextColor()) {
+ hud_color = hud->GetTextColor();
+ SetColor(hud_color);
+ }
+ }
+
+ static double time_til_change = 0;
+
+ if (time_til_change > 0)
+ time_til_change -= Game::GUITime();
+
+ if (time_til_change <= 0) {
+ time_til_change = 0;
+
+ if (show_menu) {
+ QuantumDrive* quantum_drive = 0;
+
+ if (ship)
+ quantum_drive = ship->GetQuantumDrive();
+
+ if (quantum_drive && quantum_drive->ActiveState() != QuantumDrive::ACTIVE_READY) {
+ show_menu = false;
+ return;
+ }
+
+ int max_items = quantum_menu->NumItems();
+
+ for (int i = 0; i < max_items; i++) {
+ if (Keyboard::KeyDown('1' + i)) {
+ MenuItem* item = quantum_menu->GetItem(i);
+ if (item && item->GetEnabled()) {
+
+ SimRegion* rgn = (SimRegion*) item->GetData();
+
+ if (rgn) {
+ quantum_drive->SetDestination(rgn, Point(0,0,0));
+ quantum_drive->Engage();
+ }
+
+ show_menu = false;
+ time_til_change = 0.3;
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+QuantumView::SetColor(Color c)
+{
+ hud_color = c;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+QuantumView::IsMenuShown()
+{
+ return show_menu;
+}
+
+void
+QuantumView::ShowMenu()
+{
+ if (!ship) return;
+
+ if (!show_menu) {
+ if (ship->IsStarship() && ship->GetQuantumDrive()) {
+ GetQuantumMenu(ship);
+ show_menu = true;
+ }
+
+ for (int i = 0; i < 10; i++) {
+ if (Keyboard::KeyDown('1' + i)) {
+ // just need to clear the key down flag
+ // so we don't process old keystrokes
+ // as valid menu picks...
+ }
+ }
+ }
+}
+
+void
+QuantumView::CloseMenu()
+{
+ show_menu = false;
+}
+
+// +--------------------------------------------------------------------+
+
+Menu*
+QuantumView::GetQuantumMenu(Ship* s)
+{
+ if (s && sim) {
+ if (s->IsStarship()) {
+ quantum_menu->ClearItems();
+
+ SimRegion* current_region = ship->GetRegion();
+
+ if (!current_region) return 0;
+
+ StarSystem* current_system = current_region->System();
+
+ List<SimRegion> rgn_list;
+
+ ListIter<SimRegion> iter = sim->GetRegions();
+ while (++iter) {
+ SimRegion* rgn = iter.value();
+ StarSystem* rgn_system = rgn->System();
+
+ if (rgn != ship->GetRegion() && !rgn->IsAirSpace() &&
+ rgn_system == current_system) {
+ rgn_list.append(rgn);
+ }
+ }
+
+ // sort local regions by distance from star:
+ rgn_list.sort();
+
+ // now add regions in other star systems:
+ iter.reset();
+ while (++iter) {
+ SimRegion* rgn = iter.value();
+ StarSystem* rgn_system = rgn->System();
+
+ if (rgn != ship->GetRegion() && rgn->Type() != SimRegion::AIR_SPACE &&
+ rgn_system != current_system && current_region->Links().contains(rgn)) {
+ rgn_list.append(rgn);
+ }
+ }
+
+ int n = 1;
+ iter.attach(rgn_list);
+ while (++iter) {
+ SimRegion* rgn = iter.value();
+ StarSystem* rgn_system = rgn->System();
+ char text[64];
+
+ if (rgn_system != current_system)
+ sprintf(text, "%d. %s/%s", n++, rgn_system->Name(), rgn->Name());
+ else
+ sprintf(text, "%d. %s", n++, rgn->Name());
+
+ quantum_menu->AddItem(text, (DWORD) rgn);
+ }
+
+ return quantum_menu;
+ }
+ }
+
+ return 0;
+}
diff --git a/Stars45/QuantumView.h b/Stars45/QuantumView.h
new file mode 100644
index 0000000..07d074c
--- /dev/null
+++ b/Stars45/QuantumView.h
@@ -0,0 +1,73 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: QuantumView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ View class for Radio Communications HUD Overlay
+*/
+
+#ifndef QuantumView_h
+#define QuantumView_h
+
+#include "Types.h"
+#include "View.h"
+#include "SimObject.h"
+#include "Color.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+class RadioMessage;
+class HUDView;
+class Menu;
+class Font;
+
+// +--------------------------------------------------------------------+
+
+class QuantumView : public View,
+ public SimObserver
+{
+public:
+ QuantumView(Window* c);
+ virtual ~QuantumView();
+
+ // Operations:
+ virtual void Refresh();
+ virtual void OnWindowMove();
+ virtual void ExecFrame();
+
+ virtual Menu* GetQuantumMenu(Ship* ship);
+ virtual bool IsMenuShown();
+ virtual void ShowMenu();
+ virtual void CloseMenu();
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+ static void SetColor(Color c);
+
+ static void Initialize();
+ static void Close();
+
+ static QuantumView* GetInstance() { return quantum_view; }
+
+protected:
+ int width, height;
+ double xcenter, ycenter;
+
+ Font* font;
+ Sim* sim;
+ Ship* ship;
+
+ static QuantumView* quantum_view;
+};
+
+#endif QuantumView_h
+
diff --git a/Stars45/QuitView.cpp b/Stars45/QuitView.cpp
new file mode 100644
index 0000000..35670ac
--- /dev/null
+++ b/Stars45/QuitView.cpp
@@ -0,0 +1,302 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: QuitView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ View class for End Mission menu
+*/
+
+#include "MemDebug.h"
+#include "QuitView.h"
+#include "HUDView.h"
+#include "RadioView.h"
+#include "Ship.h"
+#include "Contact.h"
+#include "Sim.h"
+#include "SimEvent.h"
+#include "Campaign.h"
+#include "Starshatter.h"
+#include "GameScreen.h"
+
+#include "CameraView.h"
+#include "Color.h"
+#include "Window.h"
+#include "Video.h"
+#include "Screen.h"
+#include "DataLoader.h"
+#include "Scene.h"
+#include "FontMgr.h"
+#include "Button.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "MouseController.h"
+#include "Game.h"
+#include "Menu.h"
+
+// +====================================================================+
+
+static bool show_menu = false;
+static Bitmap* menu_bmp = 0;
+static MouseController* mouse_con = 0;
+static bool mouse_active = false;
+static const int w2 = 200;
+static const int h2 = 128;
+
+void
+QuitView::Initialize()
+{
+ if (!menu_bmp) {
+ menu_bmp = new(__FILE__,__LINE__) Bitmap;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Screens/");
+ loader->LoadBitmap("QuitWin.pcx", *menu_bmp, Bitmap::BMP_TRANSPARENT);
+ loader->SetDataPath(0);
+ }
+}
+
+void
+QuitView::Close()
+{
+ delete menu_bmp;
+}
+
+// +====================================================================+
+
+QuitView* QuitView::quit_view = 0;
+
+QuitView::QuitView(Window* c)
+ : View(c), mouse_latch(false)
+{
+ quit_view = this;
+ sim = Sim::GetSim();
+
+ width = window->Width();
+ height = window->Height();
+ xcenter = width / 2;
+ ycenter = height / 2;
+
+ mouse_con = MouseController::GetInstance();
+}
+
+QuitView::~QuitView()
+{
+ quit_view = 0;
+}
+
+void
+QuitView::OnWindowMove()
+{
+ width = window->Width();
+ height = window->Height();
+ xcenter = width / 2;
+ ycenter = height / 2;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+QuitView::Refresh()
+{
+ if (show_menu && menu_bmp) {
+ Rect clip_rect;
+
+ clip_rect.x = xcenter - w2;
+ clip_rect.y = ycenter - h2;
+ clip_rect.w = w2 * 2;
+ clip_rect.h = h2 * 2;
+
+ window->ClipBitmap(xcenter - w2,
+ ycenter - h2,
+ xcenter - w2 + menu_bmp->Width(),
+ ycenter - h2 + menu_bmp->Height(),
+ menu_bmp,
+ Color::White,
+ Video::BLEND_SOLID,
+ clip_rect);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+QuitView::ExecFrame()
+{
+ sim = Sim::GetSim();
+
+ if (show_menu) {
+ Color::SetFade(1, Color::Black, 0);
+ int action = 0;
+
+ if (Mouse::LButton()) {
+ mouse_latch = true;
+ }
+ else if (mouse_latch) {
+ mouse_latch = false;
+
+ if (Mouse::X() > xcenter - w2 && Mouse::X() < xcenter + w2) {
+ int y0 = ycenter - h2;
+
+ for (int i = 0; i < 4; i++)
+ if (Mouse::Y() >= y0 + 75 + i * 30 && Mouse::Y() <= y0 + 105 + i * 30)
+ action = i+1;
+ }
+ }
+
+ for (int i = 1; i <= 4; i++) {
+ if (Keyboard::KeyDown('0' + i))
+ action = i;
+ }
+
+ // was mission long enough to accept?
+ if (action == 1 && !CanAccept()) {
+ Button::PlaySound(Button::SND_REJECT);
+ action = 3;
+ }
+
+ // exit and accept:
+ if (action == 1) {
+ CloseMenu();
+ Game::SetTimeCompression(1);
+
+ Starshatter* stars = Starshatter::GetInstance();
+ stars->SetGameMode(Starshatter::PLAN_MODE);
+ }
+
+ // quit and discard results:
+ else if (action == 2) {
+ CloseMenu();
+ Game::SetTimeCompression(1);
+
+ Starshatter* stars = Starshatter::GetInstance();
+ Campaign* campaign = Campaign::GetCampaign();
+
+ // discard mission and events:
+ if (sim) sim->UnloadMission();
+ else ShipStats::Initialize();
+
+ if (campaign && campaign->GetCampaignId() < Campaign::SINGLE_MISSIONS) {
+ campaign->RollbackMission();
+ stars->SetGameMode(Starshatter::CMPN_MODE);
+ }
+
+ else {
+ stars->SetGameMode(Starshatter::MENU_MODE);
+ }
+ }
+
+ // resume:
+ else if (action == 3) {
+ CloseMenu();
+ }
+
+ // controls:
+ else if (action == 4) {
+ GameScreen* game_screen = GameScreen::GetInstance();
+
+ if (game_screen)
+ game_screen->ShowCtlDlg();
+ else
+ CloseMenu();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+QuitView::IsMenuShown()
+{
+ return show_menu;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+QuitView::CanAccept()
+{
+ sim = Sim::GetSim();
+
+ if (!sim || sim->IsNetGame())
+ return true;
+
+ Ship* player_ship = sim->GetPlayerShip();
+
+ if (player_ship->MissionClock() < 60000) {
+ RadioView::Message(Game::GetText("QuitView.too-soon"));
+ RadioView::Message(Game::GetText("QuitView.abort"));
+ return false;
+ }
+
+ ListIter<Contact> iter = player_ship->ContactList();
+ while (++iter) {
+ Contact* c = iter.value();
+ Ship* cship = c->GetShip();
+ int ciff = c->GetIFF(player_ship);
+
+ if (c->Threat(player_ship)) {
+ RadioView::Message(Game::GetText("QuitView.threats-present"));
+ RadioView::Message(Game::GetText("QuitView.abort"));
+ return false;
+ }
+
+ else if (cship && ciff > 0 && ciff != player_ship->GetIFF()) {
+ Point delta = c->Location() - player_ship->Location();
+ double dist = delta.length();
+
+ if (cship->IsDropship() && dist < 50e3) {
+ RadioView::Message(Game::GetText("QuitView.threats-present"));
+ RadioView::Message(Game::GetText("QuitView.abort"));
+ return false;
+ }
+
+ else if (cship->IsStarship() && dist < 100e3) {
+ RadioView::Message(Game::GetText("QuitView.threats-present"));
+ RadioView::Message(Game::GetText("QuitView.abort"));
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+void
+QuitView::ShowMenu()
+{
+ if (!show_menu) {
+ show_menu = true;
+
+ for (int i = 0; i < 10; i++) {
+ if (Keyboard::KeyDown('1' + i)) {
+ // just need to clear the key down flag
+ // so we don't process old keystrokes
+ // as valid menu picks...
+ }
+ }
+
+ Button::PlaySound(Button::SND_CONFIRM);
+ Starshatter::GetInstance()->Pause(true);
+
+ if (mouse_con) {
+ mouse_active = mouse_con->Active();
+ mouse_con->SetActive(false);
+ }
+ }
+}
+
+void
+QuitView::CloseMenu()
+{
+ show_menu = false;
+ Starshatter::GetInstance()->Pause(false);
+
+ if (mouse_con)
+ mouse_con->SetActive(mouse_active);
+}
diff --git a/Stars45/QuitView.h b/Stars45/QuitView.h
new file mode 100644
index 0000000..c9a75da
--- /dev/null
+++ b/Stars45/QuitView.h
@@ -0,0 +1,64 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: QuitView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ View class for End Mission menu
+*/
+
+#ifndef QuitView_h
+#define QuitView_h
+
+#include "Types.h"
+#include "View.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "SimObject.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class HUDView;
+class Menu;
+
+// +--------------------------------------------------------------------+
+
+class QuitView : public View
+{
+public:
+ QuitView(Window* c);
+ virtual ~QuitView();
+
+ // Operations:
+ virtual void Refresh();
+ virtual void OnWindowMove();
+ virtual void ExecFrame();
+
+ virtual bool CanAccept();
+ virtual bool IsMenuShown();
+ virtual void ShowMenu();
+ virtual void CloseMenu();
+
+ static void Initialize();
+ static void Close();
+
+ static QuitView* GetInstance() { return quit_view; }
+
+protected:
+ int width, height;
+ int xcenter, ycenter;
+ bool mouse_latch;
+
+ Sim* sim;
+
+ static QuitView* quit_view;
+};
+
+#endif QuitView_h
+
diff --git a/Stars45/RLoc.cpp b/Stars45/RLoc.cpp
new file mode 100644
index 0000000..4388844
--- /dev/null
+++ b/Stars45/RLoc.cpp
@@ -0,0 +1,88 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: RLoc.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Navigation Point class implementation
+*/
+
+#include "MemDebug.h"
+#include "RLoc.h"
+#include "Random.h"
+
+// +----------------------------------------------------------------------+
+
+RLoc::RLoc()
+ : rloc(0), dex(0), dex_var(5.0e3f), az(0), az_var(3.1415f), el(0), el_var(0.1f)
+{ }
+
+RLoc::RLoc(const Point& l, double d, double dv)
+ : loc(l), base_loc(l), rloc(0), dex((float) d), dex_var((float) dv),
+ az(0), az_var(3.1415f), el(0), el_var(0.1f)
+{ }
+
+RLoc::RLoc(RLoc* l, double d, double dv)
+ : rloc(l), dex((float) d), dex_var((float) dv),
+ az(0), az_var(3.1415f), el(0), el_var(0.1f)
+{ }
+
+RLoc::RLoc(const RLoc& r)
+ : loc(r.loc), base_loc(r.base_loc), rloc(r.rloc),
+ dex(r.dex), dex_var(r.dex_var),
+ az(r.az), az_var(r.az_var),
+ el(r.el), el_var(r.el_var)
+{ }
+
+RLoc::~RLoc()
+{ }
+
+// +----------------------------------------------------------------------+
+
+const Point&
+RLoc::Location()
+{
+ if (rloc || dex > 0) Resolve();
+ return loc;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+RLoc::Resolve()
+{
+ if (rloc) {
+ base_loc = rloc->Location();
+ rloc = 0;
+ }
+
+ if (dex > 0) {
+ double d = dex + Random(-dex_var, dex_var);
+ double a = az + Random(-az_var, az_var);
+ double e = el + Random(-el_var, el_var);
+
+ Point p = Point(d * sin(a),
+ d * -cos(a),
+ d * sin(e));
+
+ loc = base_loc + p;
+ dex = 0;
+ }
+ else {
+ loc = base_loc;
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+RLoc::SetBaseLocation(const Point& l)
+{
+ base_loc = l;
+ loc = l;
+}
diff --git a/Stars45/RLoc.h b/Stars45/RLoc.h
new file mode 100644
index 0000000..39cded4
--- /dev/null
+++ b/Stars45/RLoc.h
@@ -0,0 +1,71 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: RLoc.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Relative Location (RLoc) class declaration
+*/
+
+#ifndef RLoc_h
+#define RLoc_h
+
+#include "Types.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class RLoc
+{
+public:
+ RLoc();
+ RLoc(const Point& loc, double d, double dv=5e3);
+ RLoc(RLoc* rloc, double d, double dv=5e3);
+ RLoc(const RLoc& r);
+ ~RLoc();
+
+ // accessors:
+ const Point& Location();
+ const Point& BaseLocation() const { return base_loc; }
+ RLoc* ReferenceLoc() const { return rloc; }
+ double Distance() const { return dex; }
+ double DistanceVar() const { return dex_var; }
+ double Azimuth() const { return az; }
+ double AzimuthVar() const { return az_var; }
+ double Elevation() const { return el; }
+ double ElevationVar() const { return el_var; }
+
+ void Resolve();
+
+ // mutators:
+ void SetBaseLocation(const Point& l);
+ void SetReferenceLoc(RLoc* r) { rloc = r; }
+ void SetDistance(double d) { dex = (float) d; }
+ void SetDistanceVar(double dv) { dex_var = (float) dv; }
+ void SetAzimuth(double a) { az = (float) a; }
+ void SetAzimuthVar(double av) { az_var = (float) av; }
+ void SetElevation(double e) { el = (float) e; }
+ void SetElevationVar(double ev) { el_var = (float) ev; }
+
+private:
+ Point loc;
+ Point base_loc;
+ RLoc* rloc;
+
+ float dex;
+ float dex_var;
+ float az;
+ float az_var;
+ float el;
+ float el_var;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif RLoc_h
+
diff --git a/Stars45/RadioHandler.cpp b/Stars45/RadioHandler.cpp
new file mode 100644
index 0000000..e56c850
--- /dev/null
+++ b/Stars45/RadioHandler.cpp
@@ -0,0 +1,561 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: RadioHandler.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Radio message handler class implementation
+*/
+
+#include "MemDebug.h"
+#include "RadioHandler.h"
+#include "RadioMessage.h"
+#include "RadioTraffic.h"
+#include "Instruction.h"
+
+#include "Contact.h"
+#include "Element.h"
+#include "Mission.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Sim.h"
+#include "StarSystem.h"
+#include "Power.h"
+#include "Drive.h"
+#include "Shield.h"
+#include "Hangar.h"
+#include "FlightDeck.h"
+#include "WeaponGroup.h"
+#include "SteerAI.h"
+
+#include "Text.h"
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+RadioHandler::RadioHandler()
+{ }
+
+RadioHandler::~RadioHandler()
+{ }
+
+// +----------------------------------------------------------------------+
+
+bool
+RadioHandler::ProcessMessage(RadioMessage* msg, Ship* s)
+{
+ if (!s || !msg || !msg->Sender())
+ return false;
+
+ if (s->Class() >= Ship::FARCASTER && s->Class() <= Ship::C3I)
+ return false;
+
+ if (msg->Sender()->IsRogue()) {
+ Ship* sender = (Ship*) msg->Sender(); // cast-away const
+ RadioMessage* nak = new(__FILE__,__LINE__) RadioMessage(sender, s, RadioMessage::NACK);
+ RadioTraffic::Transmit(nak);
+ return false;
+ }
+
+ bool respond = (s != msg->Sender());
+
+ // SPECIAL CASE:
+ // skip navpoint must be processed by elem leader,
+ // even if the elem leader sent the message:
+
+ if (msg->Action() == RadioMessage::SKIP_NAVPOINT && !respond)
+ ProcessMessageAction(msg, s);
+
+ if (ProcessMessageOrders(msg, s))
+ respond = respond && true;
+
+ else
+ respond = respond && ProcessMessageAction(msg, s);
+
+ return respond;
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+RadioHandler::IsOrder(int action)
+{
+ bool result = false;
+
+ switch (action) {
+ default:
+ case RadioMessage::NONE:
+ case RadioMessage::ACK:
+ case RadioMessage::NACK: result = false; break;
+
+// target mgt:
+ case RadioMessage::ATTACK:
+ case RadioMessage::ESCORT:
+ case RadioMessage::BRACKET:
+ case RadioMessage::IDENTIFY: result = true; break;
+
+// combat mgt:
+ case RadioMessage::COVER_ME:
+ case RadioMessage::WEP_HOLD:
+ case RadioMessage::FORM_UP: result = true; break;
+
+ case RadioMessage::WEP_FREE:
+ case RadioMessage::SAY_POSITION:
+ case RadioMessage::LAUNCH_PROBE: result = false; break;
+
+// formation mgt:
+ case RadioMessage::GO_DIAMOND:
+ case RadioMessage::GO_SPREAD:
+ case RadioMessage::GO_BOX:
+ case RadioMessage::GO_TRAIL: result = true; break;
+
+// mission mgt:
+ case RadioMessage::MOVE_PATROL: result = true; break;
+ case RadioMessage::SKIP_NAVPOINT: result = false; break;
+ case RadioMessage::RESUME_MISSION: result = true; break;
+
+ case RadioMessage::RTB:
+ case RadioMessage::DOCK_WITH:
+ case RadioMessage::QUANTUM_TO:
+ case RadioMessage::FARCAST_TO: result = true; break;
+
+// sensor mgt:
+ case RadioMessage::GO_EMCON1:
+ case RadioMessage::GO_EMCON2:
+ case RadioMessage::GO_EMCON3: result = true; break;
+
+// support:
+ case RadioMessage::REQUEST_PICTURE:
+ case RadioMessage::REQUEST_SUPPORT:
+ case RadioMessage::PICTURE: result = false; break;
+
+// traffic control:
+ case RadioMessage::CALL_INBOUND:
+ case RadioMessage::CALL_APPROACH:
+ case RadioMessage::CALL_CLEARANCE:
+ case RadioMessage::CALL_FINALS:
+ case RadioMessage::CALL_WAVE_OFF: result = false; break;
+ }
+
+ return result;
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+RadioHandler::ProcessMessageOrders(RadioMessage* msg, Ship* ship)
+{
+ Instruction* instruction = ship->GetRadioOrders();
+ int action = 0;
+
+ if (msg && msg->Action() == RadioMessage::RESUME_MISSION) {
+ instruction->SetAction(RadioMessage::NONE);
+ instruction->SetFormation(-1);
+ instruction->SetWeaponsFree(true);
+ if (instruction->GetTarget()) {
+ instruction->ClearTarget();
+ ship->DropTarget();
+ }
+ return true;
+ }
+
+ if (msg && IsOrder(msg->Action())) {
+ int posture_only = false;
+
+ action = msg->Action();
+
+ if (action == RadioMessage::FORM_UP)
+ action = RadioMessage::WEP_HOLD;
+
+ // target orders => drop current target:
+ if (action >= RadioMessage::ATTACK &&
+ action <= RadioMessage::COVER_ME ||
+ action == RadioMessage::WEP_HOLD ||
+ action >= RadioMessage::DOCK_WITH &&
+ action <= RadioMessage::FARCAST_TO) {
+
+ if (ship != msg->Sender())
+ ship->DropTarget();
+
+ Director* dir = ship->GetDirector();
+ if (dir && dir->Type() >= SteerAI::SEEKER && dir->Type() <= SteerAI::GROUND) {
+ SteerAI* ai = (SteerAI*) dir;
+ ai->SetTarget(0);
+ }
+
+ // farcast and quantum jump radio messages:
+ if (action >= RadioMessage::QUANTUM_TO) {
+ Sim* sim = Sim::GetSim();
+
+ if (sim) {
+ SimRegion* rgn = sim->FindRegion(msg->Info());
+
+ if (rgn) {
+ instruction->SetAction(action);
+ instruction->SetLocation(Point(0,0,0));
+ instruction->SetRegion(rgn);
+ instruction->SetFarcast(action == RadioMessage::FARCAST_TO);
+ instruction->SetWeaponsFree(false);
+ return true;
+ }
+ }
+ }
+ }
+
+ // formation orders => set formation:
+ if (action >= RadioMessage::GO_DIAMOND &&
+ action <= RadioMessage::GO_TRAIL) {
+
+ switch (action) {
+ case RadioMessage::GO_DIAMOND: instruction->SetFormation(Instruction::DIAMOND); break;
+ case RadioMessage::GO_SPREAD: instruction->SetFormation(Instruction::SPREAD); break;
+ case RadioMessage::GO_BOX: instruction->SetFormation(Instruction::BOX); break;
+ case RadioMessage::GO_TRAIL: instruction->SetFormation(Instruction::TRAIL); break;
+ }
+
+ posture_only = true;
+ }
+
+ // emcon orders => set emcon:
+ if (action >= RadioMessage::GO_EMCON1 &&
+ action <= RadioMessage::GO_EMCON3) {
+
+ switch (msg->Action()) {
+ case RadioMessage::GO_EMCON1: instruction->SetEMCON(1); break;
+ case RadioMessage::GO_EMCON2: instruction->SetEMCON(2); break;
+ case RadioMessage::GO_EMCON3: instruction->SetEMCON(3); break;
+ }
+
+ posture_only = true;
+ }
+
+ if (!posture_only) {
+ instruction->SetAction(action);
+ instruction->ClearTarget();
+
+ if (msg->TargetList().size() > 0) {
+ SimObject* msg_tgt = msg->TargetList().at(0);
+ instruction->SetTarget(msg_tgt);
+ instruction->SetLocation(msg_tgt->Location());
+ }
+
+ else if (action == RadioMessage::COVER_ME) {
+ instruction->SetTarget((Ship*) msg->Sender());
+ instruction->SetLocation(msg->Sender()->Location());
+ }
+
+ else if (action == RadioMessage::MOVE_PATROL) {
+ instruction->SetLocation(msg->Location());
+ }
+
+ // handle element engagement:
+ if (action == RadioMessage::ATTACK && msg->TargetList().size() > 0) {
+ Element* elem = msg->DestinationElem();
+
+ if (!elem && msg->DestinationShip())
+ elem = msg->DestinationShip()->GetElement();
+
+ if (elem) {
+ SimObject* msg_tgt = msg->TargetList().at(0);
+ if (msg_tgt && msg_tgt->Type() == SimObject::SIM_SHIP) {
+ Element* tgt = ((Ship*) msg_tgt)->GetElement();
+ elem->SetAssignment(tgt);
+
+ if (msg->TargetList().size() > 1)
+ instruction->SetTarget(tgt->Name().data());
+ else
+ instruction->SetTarget(msg_tgt);
+ }
+ else {
+ elem->ResumeAssignment();
+ }
+ }
+ }
+
+ else if (action == RadioMessage::RESUME_MISSION) {
+ Element* elem = msg->DestinationElem();
+
+ if (!elem && msg->DestinationShip())
+ elem = msg->DestinationShip()->GetElement();
+
+ if (elem) {
+ elem->ResumeAssignment();
+ }
+ }
+ }
+
+ instruction->SetWeaponsFree(action <= RadioMessage::WEP_FREE);
+ return true;
+ }
+
+ return false;
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+RadioHandler::ProcessMessageAction(RadioMessage* msg, Ship* ship)
+{
+ if (!msg) return false;
+
+ if (msg->Action() == RadioMessage::CALL_INBOUND)
+ return Inbound(msg, ship);
+
+ if (msg->Action() == RadioMessage::CALL_FINALS)
+ return true; // acknowledge
+
+ if (msg->Action() == RadioMessage::REQUEST_PICTURE)
+ return Picture(msg, ship);
+
+ if (msg->Action() == RadioMessage::REQUEST_SUPPORT)
+ return Support(msg, ship);
+
+ if (msg->Action() == RadioMessage::SKIP_NAVPOINT)
+ return SkipNavpoint(msg, ship);
+
+ if (msg->Action() == RadioMessage::LAUNCH_PROBE)
+ return LaunchProbe(msg, ship);
+
+ return false;
+}
+
+bool
+RadioHandler::SkipNavpoint(RadioMessage* msg, Ship* ship)
+{
+ // Find next Instruction:
+ Instruction* navpt = ship->GetNextNavPoint();
+ int elem_index = ship->GetElementIndex();
+
+ if (navpt && elem_index < 2) {
+ ship->SetNavptStatus(navpt, Instruction::SKIPPED);
+ }
+
+ return true;
+}
+
+bool
+RadioHandler::LaunchProbe(RadioMessage* msg, Ship* ship)
+{
+ if (ship && ship->GetProbeLauncher()) {
+ ship->LaunchProbe();
+ return ship->GetProbe() != 0;
+ }
+
+ return false;
+}
+
+bool
+RadioHandler::Inbound(RadioMessage* msg, Ship* ship)
+{
+ Ship* inbound = (Ship*) msg->Sender();
+ Hangar* hangar = ship->GetHangar();
+ FlightDeck* deck = 0;
+ int squadron = -1;
+ int slot = -1;
+ bool same_rgn = false;
+
+ if (inbound && inbound->GetRegion() == ship->GetRegion())
+ same_rgn = true;
+
+ // is the sender already inbound to us?
+ if (inbound->GetInbound() &&
+ inbound->GetInbound()->GetDeck() &&
+ inbound->GetInbound()->GetDeck()->GetCarrier() == ship) {
+ InboundSlot* islot = inbound->GetInbound();
+ deck = islot->GetDeck();
+ squadron = islot->Squadron();
+ slot = islot->Index();
+ }
+
+ // otherwise, find space for sender:
+ else {
+ if (hangar && same_rgn) {
+ if (hangar->FindSlot(inbound, squadron, slot)) {
+ int shortest_queue = 1000;
+
+ for (int i = 0; i < ship->NumFlightDecks(); i++) {
+ FlightDeck* d = ship->GetFlightDeck(i);
+ if (d->IsRecoveryDeck()) {
+ int nwaiting = d->GetRecoveryQueue().size();
+
+ if (nwaiting < shortest_queue) {
+ deck = d;
+ shortest_queue = nwaiting;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // if no space (or not a carrier!) wave sender off:
+ if (!deck || !same_rgn || squadron < 0 || slot < 0) {
+ RadioMessage* wave_off = new(__FILE__,__LINE__) RadioMessage(inbound, ship, RadioMessage::NACK);
+ if (!hangar)
+ wave_off->SetInfo(Game::GetText("RadioHandler.no-hangar"));
+
+ else if (!same_rgn) {
+ char info[256];
+ sprintf(info, Game::GetText("RadioHandler.too-far-away").data(), ship->GetRegion()->Name());
+ wave_off->SetInfo(info);
+ }
+
+ else
+ wave_off->SetInfo(Game::GetText("RadioHandler.all-full"));
+
+ RadioTraffic::Transmit(wave_off);
+ return false;
+ }
+
+ // put sender in recovery queue, if not already there:
+ InboundSlot* inbound_slot = inbound->GetInbound();
+ int sequence = 0;
+
+ if (!inbound_slot) {
+ inbound_slot = new(__FILE__,__LINE__) InboundSlot(inbound, deck, squadron, slot);
+ sequence = deck->Inbound(inbound_slot);
+ }
+ else {
+ sequence = inbound_slot->Index();
+ }
+
+ // inform sender of status:
+ RadioMessage* approach = new(__FILE__,__LINE__) RadioMessage(inbound, ship, RadioMessage::CALL_APPROACH);
+
+ if (inbound_slot->Cleared()) {
+ char info[256];
+ sprintf(info, Game::GetText("RadioHandler.cleared").data(), deck->Name());
+ approach->SetInfo(info);
+ }
+ else if (sequence) {
+ char info[256];
+ sprintf(info, Game::GetText("RadioHandler.sequenced").data(), sequence, deck->Name());
+ approach->SetInfo(info);
+ }
+
+ RadioTraffic::Transmit(approach);
+
+ return false;
+}
+
+bool
+RadioHandler::Picture(RadioMessage* msg, Ship* ship)
+{
+ if (!ship) return false;
+
+ // try to find some enemy fighters in the area:
+ Ship* tgt = 0;
+ double range = 1e9;
+
+ ListIter<Contact> iter = ship->ContactList();
+ while (++iter) {
+ Contact* c = iter.value();
+ int iff = c->GetIFF(ship);
+ Ship* s = c->GetShip();
+
+ if (s && s->IsDropship() && s->IsHostileTo(ship)) {
+ double s_range = Point(msg->Sender()->Location() - s->Location()).length();
+ if (!tgt || s_range < range) {
+ tgt = s;
+ range = s_range;
+ }
+ }
+ }
+
+ // found some:
+ if (tgt) {
+ Element* sender = msg->Sender()->GetElement();
+ Element* tgt_elem = tgt->GetElement();
+ RadioMessage* response = new(__FILE__,__LINE__) RadioMessage(sender, ship, RadioMessage::ATTACK);
+
+ if (tgt_elem) {
+ for (int i = 1; i <= tgt_elem->NumShips(); i++)
+ response->AddTarget(tgt_elem->GetShip(i));
+ }
+ else {
+ response->AddTarget(tgt);
+ }
+
+ RadioTraffic::Transmit(response);
+ }
+
+ // nobody worth killin':
+ else {
+ Ship* sender = (Ship*) msg->Sender(); // cast-away const
+ RadioMessage* response = new(__FILE__,__LINE__) RadioMessage(sender, ship, RadioMessage::PICTURE);
+ RadioTraffic::Transmit(response);
+ }
+
+ return false;
+}
+
+bool
+RadioHandler::Support(RadioMessage* msg, Ship* ship)
+{
+ if (!ship) return false;
+
+ // try to find some fighters with time on their hands...
+ Element* help = 0;
+ Element* cmdr = ship->GetElement();
+ Element* baby = msg->Sender()->GetElement();
+ SimRegion* rgn = msg->Sender()->GetRegion();
+
+ for (int i = 0; i < rgn->Ships().size(); i++) {
+ Ship* s = rgn->Ships().at(i);
+ Element* e = s->GetElement();
+
+ if (e && s->IsDropship() &&
+ e->Type() == Mission::PATROL &&
+ e != baby &&
+ cmdr->CanCommand(e) &&
+ s->GetRadioOrders()->Action() == RadioMessage::NONE) {
+ help = e;
+ break;
+ }
+ }
+
+ // found some:
+ if (help) {
+ RadioMessage* escort = new(__FILE__,__LINE__) RadioMessage(help, ship, RadioMessage::ESCORT);
+ escort->TargetList().append(msg->Sender());
+ RadioTraffic::Transmit(escort);
+
+ Text ok = Game::GetText("RadioHandler.help-enroute");
+ Ship* sender = (Ship*) msg->Sender(); // cast-away const
+ RadioMessage* response = new(__FILE__,__LINE__) RadioMessage(sender, ship, RadioMessage::ACK);
+ response->SetInfo(ok);
+ RadioTraffic::Transmit(response);
+ }
+
+ // no help in sight:
+ else {
+ Text nope = Game::GetText("RadioHandler.no-help-for-you");
+ Ship* sender = (Ship*) msg->Sender(); // cast-away const
+ RadioMessage* response = new(__FILE__,__LINE__) RadioMessage(sender, ship, RadioMessage::NACK);
+ response->SetInfo(nope);
+ RadioTraffic::Transmit(response);
+ }
+
+ return false;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+RadioHandler::AcknowledgeMessage(RadioMessage* msg, Ship* s)
+{
+ if (s && msg && msg->Sender() && msg->Action()) {
+ if (msg->Action() >= RadioMessage::ACK && msg->Action() <= RadioMessage::NACK)
+ return; // nothing to say here
+
+ Ship* sender = (Ship*) msg->Sender(); // cast-away const
+ RadioMessage* ack = new(__FILE__,__LINE__) RadioMessage(sender, s, RadioMessage::ACK);
+ RadioTraffic::Transmit(ack);
+ }
+}
+
diff --git a/Stars45/RadioHandler.h b/Stars45/RadioHandler.h
new file mode 100644
index 0000000..151514e
--- /dev/null
+++ b/Stars45/RadioHandler.h
@@ -0,0 +1,53 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: RadioHandler.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ RadioHandler (radio comms) class declaration
+*/
+
+#ifndef RadioHandler_h
+#define RadioHandler_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "SimObject.h"
+
+// +--------------------------------------------------------------------+
+
+class RadioMessage;
+class Ship;
+
+// +--------------------------------------------------------------------+
+
+class RadioHandler
+{
+public:
+ RadioHandler();
+ virtual ~RadioHandler();
+
+ virtual bool ProcessMessage(RadioMessage* msg, Ship* s);
+ virtual void AcknowledgeMessage(RadioMessage* msg, Ship* s);
+
+protected:
+ virtual bool IsOrder(int action);
+ virtual bool ProcessMessageOrders(RadioMessage* msg, Ship* s);
+ virtual bool ProcessMessageAction(RadioMessage* msg, Ship* s);
+
+ virtual bool Inbound(RadioMessage* msg, Ship* s);
+ virtual bool Picture(RadioMessage* msg, Ship* s);
+ virtual bool Support(RadioMessage* msg, Ship* s);
+ virtual bool SkipNavpoint(RadioMessage* msg, Ship* s);
+ virtual bool LaunchProbe(RadioMessage* msg, Ship* s);
+};
+
+// +--------------------------------------------------------------------+
+
+#endif RadioHandler_h
+
diff --git a/Stars45/RadioMessage.cpp b/Stars45/RadioMessage.cpp
new file mode 100644
index 0000000..4072897
--- /dev/null
+++ b/Stars45/RadioMessage.cpp
@@ -0,0 +1,165 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: RadioMessage.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Radio communication message class implementation
+*/
+
+#include "MemDebug.h"
+#include "RadioMessage.h"
+#include "Ship.h"
+#include "Text.h"
+
+// +----------------------------------------------------------------------+
+
+RadioMessage::RadioMessage(Ship* dst, const Ship* s, int a)
+ : dst_ship(dst), dst_elem(0), sender(s), action(a), channel(0)
+{
+ if (s)
+ channel = s->GetIFF();
+}
+
+RadioMessage::RadioMessage(Element* dst, const Ship* s, int a)
+ : dst_ship(0), dst_elem(dst), sender(s), action(a), channel(0)
+{
+ if (s)
+ channel = s->GetIFF();
+}
+
+RadioMessage::RadioMessage(const RadioMessage& rm)
+ : dst_ship(rm.dst_ship), dst_elem(rm.dst_elem),
+ sender(rm.sender), action(rm.action), channel(rm.channel),
+ info(rm.info), location(rm.location)
+{
+ if (rm.target_list.size() > 0) {
+ for (int i = 0; i < rm.target_list.size(); i++) {
+ SimObject* obj = rm.target_list.at(i);
+ target_list.append(obj);
+ }
+ }
+}
+
+RadioMessage::~RadioMessage()
+{ }
+
+// +----------------------------------------------------------------------+
+
+const char*
+RadioMessage::ActionName(int a)
+{
+ if (a == ACK) {
+ int coin = rand();
+ if (coin < 10000) return "Acknowledged";
+ if (coin < 17000) return "Roger that";
+ if (coin < 20000) return "Understood";
+ if (coin < 22000) return "Copy that";
+ return "Affirmative";
+ }
+
+ if (a == DISTRESS) {
+ int coin = rand();
+ if (coin < 15000) return "Mayday! Mayday!";
+ if (coin < 18000) return "She's breaking up!";
+ if (coin < 21000) return "Checking out!";
+ return "We're going down!";
+ }
+
+ if (a == WARN_ACCIDENT) {
+ int coin = rand();
+ if (coin < 15000) return "Check your fire!";
+ if (coin < 18000) return "Watch it!";
+ if (coin < 21000) return "Hey! We're on your side!";
+ return "Confirm your targets!";
+ }
+
+ if (a == WARN_TARGETED) {
+ int coin = rand();
+ if (coin < 15000) return "Break off immediately!";
+ if (coin < 20000) return "Buddy spike!";
+ return "Abort! Abort!";
+ }
+
+ switch (a) {
+ case NONE: return "";
+
+ case NACK: return "Negative, Unable";
+
+ case ATTACK: return "Engage";
+ case ESCORT: return "Escort";
+ case BRACKET: return "Bracket";
+ case IDENTIFY: return "Identify";
+
+ case COVER_ME: return "Cover Me";
+ case MOVE_PATROL: return "Vector";
+ case SKIP_NAVPOINT: return "Skip Navpoint";
+ case RESUME_MISSION: return "Resume Mission";
+ case RTB: return "Return to Base";
+ case DOCK_WITH: return "Dock With";
+ case QUANTUM_TO: return "Jump to";
+ case FARCAST_TO: return "Farcast to";
+
+ case GO_DIAMOND: return "Goto Diamond Formation";
+ case GO_SPREAD: return "Goto Spread Formation";
+ case GO_BOX: return "Goto Box Formation";
+ case GO_TRAIL: return "Goto Trail Formation";
+
+ case WEP_FREE: return "Break and Attack";
+ case WEP_HOLD: return "Hold All Weapons";
+ case FORM_UP: return "Return to Formation";
+ case SAY_POSITION: return "Say Your Position";
+
+ case LAUNCH_PROBE: return "Launch Probe";
+ case GO_EMCON1: return "Goto EMCON 1";
+ case GO_EMCON2: return "Goto EMCON 2";
+ case GO_EMCON3: return "Goto EMCON 3";
+
+ case REQUEST_PICTURE: return "Request Picture";
+ case REQUEST_SUPPORT: return "Request Support";
+ case PICTURE: return "Picture is clear";
+
+ case CALL_INBOUND: return "Calling Inbound";
+ case CALL_APPROACH: return "Roger your approach";
+ case CALL_CLEARANCE: return "You have clearance";
+ case CALL_FINALS: return "On final approach";
+ case CALL_WAVE_OFF: return "Wave off - Runway is closed";
+
+ case DECLARE_ROGUE: return "Prepare to be destroyed!";
+
+ case CALL_ENGAGING: return "Engaging";
+ case FOX_1: return "Fox One!";
+ case FOX_2: return "Fox Two!";
+ case FOX_3: return "Fox Three!";
+ case SPLASH_1: return "Splash One!";
+ case SPLASH_2: return "Splash Two!";
+ case SPLASH_3: return "Splash Three!";
+ case SPLASH_4: return "Splash Four!";
+ case SPLASH_5: return "Target Destroyed!";
+ case SPLASH_6: return "Enemy Destroyed!";
+ case SPLASH_7: return "Confirmed Kill!";
+ case BREAK_ORBIT: return "Breaking Orbit";
+ case MAKE_ORBIT: return "Heading for Orbit";
+ case QUANTUM_JUMP: return "Going Quantum";
+
+ default: return "Unknown";
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+RadioMessage::AddTarget(SimObject* obj)
+{
+ if (obj && !target_list.contains(obj)) {
+ target_list.append(obj);
+ }
+}
+
+
+
diff --git a/Stars45/RadioMessage.h b/Stars45/RadioMessage.h
new file mode 100644
index 0000000..93d010e
--- /dev/null
+++ b/Stars45/RadioMessage.h
@@ -0,0 +1,155 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: RadioMessage.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ RadioMessage (radio comms) class declaration
+*/
+
+#ifndef RadioMessage_h
+#define RadioMessage_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "SimObject.h"
+#include "Instruction.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Element;
+class Ship;
+class SimObject;
+
+// +--------------------------------------------------------------------+
+
+class RadioMessage
+{
+public:
+ enum ACTION
+ {
+ NONE = 0,
+
+ DOCK_WITH = Instruction::DOCK,
+ RTB = Instruction::RTB,
+ QUANTUM_TO = Instruction::NUM_ACTIONS,
+ FARCAST_TO,
+
+ // protocol:
+ ACK,
+ NACK,
+
+ // target mgt:
+ ATTACK,
+ ESCORT,
+ BRACKET,
+ IDENTIFY,
+
+ // combat mgt:
+ COVER_ME,
+ WEP_FREE,
+ WEP_HOLD,
+ FORM_UP, // alias for wep_hold
+ SAY_POSITION,
+
+ // sensor mgt:
+ LAUNCH_PROBE,
+ GO_EMCON1,
+ GO_EMCON2,
+ GO_EMCON3,
+
+ // formation mgt:
+ GO_DIAMOND,
+ GO_SPREAD,
+ GO_BOX,
+ GO_TRAIL,
+
+ // mission mgt:
+ MOVE_PATROL,
+ SKIP_NAVPOINT,
+ RESUME_MISSION,
+
+ // misc announcements:
+ CALL_ENGAGING,
+ FOX_1,
+ FOX_2,
+ FOX_3,
+ SPLASH_1,
+ SPLASH_2,
+ SPLASH_3,
+ SPLASH_4,
+ SPLASH_5, // target destroyed
+ SPLASH_6, // enemy destroyed
+ SPLASH_7, // confirmed kill
+ DISTRESS,
+ BREAK_ORBIT,
+ MAKE_ORBIT,
+ QUANTUM_JUMP,
+
+ // friendly fire:
+ WARN_ACCIDENT,
+ WARN_TARGETED,
+ DECLARE_ROGUE,
+
+ // support:
+ PICTURE,
+ REQUEST_PICTURE,
+ REQUEST_SUPPORT,
+
+ // traffic control:
+ CALL_INBOUND,
+ CALL_APPROACH,
+ CALL_CLEARANCE,
+ CALL_FINALS,
+ CALL_WAVE_OFF,
+
+ NUM_ACTIONS
+ };
+
+ RadioMessage(Ship* dst, const Ship* sender, int action);
+ RadioMessage(Element* dst, const Ship* sender, int action);
+ RadioMessage(const RadioMessage& rm);
+ virtual ~RadioMessage();
+
+ // accessors:
+ static const char* ActionName(int a);
+
+ const Ship* Sender() const { return sender; }
+ Ship* DestinationShip() const { return dst_ship; }
+ Element* DestinationElem() const { return dst_elem; }
+ int Action() const { return action; }
+ List<SimObject>& TargetList() { return target_list; }
+ const Point& Location() const { return location; }
+ const Text& Info() const { return info; }
+ int Channel() const { return channel; }
+
+ // mutators:
+ void SetDestinationShip(Ship* s) { dst_ship = s; }
+ void SetDestinationElem(Element* e) { dst_elem = e; }
+ void AddTarget(SimObject* s);
+ void SetLocation(const Point& l) { location = l; }
+ void SetInfo(Text msg) { info = msg; }
+ void SetChannel(int c) { channel = c; }
+
+protected:
+ const Ship* sender;
+ Ship* dst_ship;
+ Element* dst_elem;
+ int action;
+ List<SimObject> target_list;
+ Point location;
+ Text info;
+ int channel;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif RadioMessage_h
+
diff --git a/Stars45/RadioTraffic.cpp b/Stars45/RadioTraffic.cpp
new file mode 100644
index 0000000..b0c558e
--- /dev/null
+++ b/Stars45/RadioTraffic.cpp
@@ -0,0 +1,391 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: RadioTraffic.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Radio message handler class implementation
+*/
+
+#include "MemDebug.h"
+#include "RadioTraffic.h"
+#include "RadioMessage.h"
+#include "RadioView.h"
+#include "RadioVox.h"
+#include "Instruction.h"
+#include "Ship.h"
+#include "Contact.h"
+#include "Element.h"
+#include "Sim.h"
+#include "NetGame.h"
+#include "NetData.h"
+
+#include "Game.h"
+#include "Text.h"
+
+// +----------------------------------------------------------------------+
+
+RadioTraffic* RadioTraffic::radio_traffic = 0;
+
+// +----------------------------------------------------------------------+
+
+RadioTraffic::RadioTraffic()
+{
+ radio_traffic = this;
+}
+
+RadioTraffic::~RadioTraffic()
+{
+ traffic.destroy();
+}
+
+// +----------------------------------------------------------------------+
+
+void
+RadioTraffic::Initialize()
+{
+ if (!radio_traffic)
+ radio_traffic = new(__FILE__,__LINE__) RadioTraffic;
+}
+
+void
+RadioTraffic::Close()
+{
+ delete radio_traffic;
+ radio_traffic = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+RadioTraffic::SendQuickMessage(Ship* ship, int action)
+{
+ if (!ship) return;
+
+ Element* elem = ship->GetElement();
+
+ if (elem) {
+ if (action >= RadioMessage::REQUEST_PICTURE) {
+ Ship* controller = ship->GetController();
+
+ if (controller && !ship->IsStarship()) {
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(controller, ship, action);
+ Transmit(msg);
+ }
+ }
+ else if (action >= RadioMessage::SPLASH_1 && action <= RadioMessage::DISTRESS) {
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage((Element*) 0, ship, action);
+ Transmit(msg);
+ }
+ else {
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(elem, ship, action);
+
+ if (action == RadioMessage::ATTACK || action == RadioMessage::ESCORT)
+ msg->AddTarget(ship->GetTarget());
+
+ Transmit(msg);
+ }
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+RadioTraffic::Transmit(RadioMessage* msg)
+{
+ if (msg && radio_traffic) {
+ NetGame* net_game = NetGame::GetInstance();
+ if (net_game) {
+ NetCommMsg net_msg;
+ net_msg.SetRadioMessage(msg);
+ net_game->SendData(&net_msg);
+ }
+
+ radio_traffic->SendMessage(msg);
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+RadioTraffic::SendMessage(RadioMessage* msg)
+{
+ if (!msg) return;
+
+ Sim* sim = Sim::GetSim();
+ Ship* player = sim->GetPlayerShip();
+ int iff = 0;
+
+ if (player)
+ iff = player->GetIFF();
+
+ if (msg->DestinationShip()) {
+ traffic.append(msg);
+
+ if (msg->Channel() == 0 || msg->Channel() == iff)
+ DisplayMessage(msg);
+
+ if (!NetGame::IsNetGameClient())
+ msg->DestinationShip()->HandleRadioMessage(msg);
+ }
+
+ else if (msg->DestinationElem()) {
+ traffic.append(msg);
+
+ if (msg->Channel() == 0 || msg->Channel() == iff)
+ DisplayMessage(msg);
+
+ if (!NetGame::IsNetGameClient())
+ msg->DestinationElem()->HandleRadioMessage(msg);
+ }
+
+ else {
+ if (msg->Channel() == 0 || msg->Channel() == iff)
+ DisplayMessage(msg);
+
+ delete msg;
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+Text
+RadioTraffic::TranslateVox(const char* phrase)
+{
+ Text vox = "vox.";
+ vox += phrase;
+ vox.toLower();
+ vox = Game::GetText(vox);
+
+ if (vox.contains("vox.")) // failed to translate
+ return Text(phrase); // return the original text
+
+ return vox;
+}
+
+void
+RadioTraffic::DisplayMessage(RadioMessage* msg)
+{
+ if (!msg) return;
+
+ char txt_buf[256]; txt_buf[0] = 0;
+ char msg_buf[128]; msg_buf[0] = 0;
+ char src_buf[64]; src_buf[0] = 0;
+ char dst_buf[64]; dst_buf[0] = 0;
+ char act_buf[64]; act_buf[0] = 0;
+ int vox_channel = 0;
+
+ Ship* dst_ship = msg->DestinationShip();
+ Element* dst_elem = msg->DestinationElem();
+
+ // BUILD SRC AND DST BUFFERS -------------------
+
+ if (msg->Sender()) {
+ const Ship* sender = msg->Sender();
+
+ // orders to self?
+ if (dst_elem && dst_elem->NumShips() == 1 && dst_elem->GetShip(1) == sender) {
+ if (msg->Action() >= RadioMessage::CALL_ENGAGING) {
+ sprintf(src_buf, "%s", sender->Name());
+
+ if (sender->IsStarship())
+ vox_channel = (sender->Identity()%3) + 5;
+ }
+ }
+
+ // orders to other ships:
+ else {
+ if (sender->IsStarship()) {
+ vox_channel = (sender->Identity()%3) + 5;
+ }
+ else {
+ vox_channel = sender->GetElementIndex();
+ }
+
+ if (msg->Action() >= RadioMessage::CALL_ENGAGING) {
+ sprintf(src_buf, "%s", sender->Name());
+ }
+ else {
+ sprintf(src_buf, "This is %s", sender->Name());
+
+ if (dst_ship) {
+ // internal announcement
+ if (dst_ship->GetElement() == sender->GetElement()) {
+ dst_elem = sender->GetElement();
+ int index = sender->GetElementIndex();
+
+ if (index > 1 && dst_elem) {
+ sprintf(dst_buf, "%s Leader", (const char*) dst_elem->Name());
+ sprintf(src_buf, "this is %s %d", (const char*) dst_elem->Name(), index);
+ }
+ else {
+ sprintf(src_buf, "this is %s leader", (const char*) dst_elem->Name());
+ }
+ }
+
+ else {
+ strcpy(dst_buf, (const char*) dst_ship->Name());
+ src_buf[0] = tolower(src_buf[0]);
+ }
+ }
+
+ else if (dst_elem) {
+ // flight
+ if (dst_elem->NumShips() > 1) {
+ sprintf(dst_buf, "%s Flight", (const char*) dst_elem->Name());
+
+ // internal announcement
+ if (sender->GetElement() == dst_elem) {
+ int index = sender->GetElementIndex();
+
+ if (index > 1) {
+ sprintf(dst_buf, "%s Leader", (const char*) dst_elem->Name());
+ sprintf(src_buf, "this is %s %d", (const char*) dst_elem->Name(), index);
+ }
+ else {
+ sprintf(src_buf, "this is %s leader", (const char*) dst_elem->Name());
+ }
+ }
+ }
+
+ // solo
+ else {
+ strcpy(dst_buf, (const char*) dst_elem->Name());
+ src_buf[0] = tolower(src_buf[0]);
+ }
+ }
+ }
+ }
+ }
+
+ // BUILD ACTION AND TARGET BUFFERS -------------------
+
+ SimObject* target = 0;
+
+ strcpy(act_buf, RadioMessage::ActionName(msg->Action()));
+
+ if (msg->TargetList().size() > 0)
+ target = msg->TargetList()[0];
+
+ if (msg->Action() == RadioMessage::ACK ||
+ msg->Action() == RadioMessage::NACK) {
+
+ if (dst_ship == msg->Sender()) {
+ src_buf[0] = 0;
+ dst_buf[0] = 0;
+
+ if (msg->Action() == RadioMessage::ACK)
+ sprintf(msg_buf, "%s.", TranslateVox("Acknowledged").data());
+ else
+ sprintf(msg_buf, "%s.", TranslateVox("Unable").data());
+ }
+ else if (msg->Sender()) {
+ dst_buf[0] = 0;
+
+ if (msg->Info().length()) {
+ sprintf(msg_buf, "%s. %s",
+ TranslateVox(act_buf).data(),
+ (const char*) msg->Info());
+ }
+ else {
+ sprintf(msg_buf, "%s.", TranslateVox(act_buf).data());
+ }
+ }
+ else {
+ if (msg->Info().length()) {
+ sprintf(msg_buf, "%s. %s",
+ TranslateVox(act_buf).data(),
+ (const char*) msg->Info());
+ }
+ else {
+ sprintf(msg_buf, "%s.", TranslateVox(act_buf).data());
+ }
+ }
+ }
+
+ else if (msg->Action() == RadioMessage::MOVE_PATROL) {
+ sprintf(msg_buf, TranslateVox("Move patrol.").data());
+ }
+
+ else if (target && dst_ship && msg->Sender()) {
+ Contact* c = msg->Sender()->FindContact(target);
+
+ if (c && c->GetIFF(msg->Sender()) > 10) {
+ sprintf(msg_buf, "%s %s.", TranslateVox(act_buf).data(), TranslateVox("unknown contact").data());
+ }
+
+ else {
+ sprintf(msg_buf, "%s %s.",
+ TranslateVox(act_buf).data(),
+ target->Name());
+ }
+ }
+
+ else if (target) {
+ sprintf(msg_buf, "%s %s.",
+ TranslateVox(act_buf).data(),
+ target->Name());
+ }
+
+ else if (msg->Info().length()) {
+ sprintf(msg_buf, "%s %s",
+ TranslateVox(act_buf).data(),
+ (const char*) msg->Info());
+ }
+
+ else {
+ strcpy(msg_buf, TranslateVox(act_buf).data());
+ }
+
+ char last_char = msg_buf[strlen(msg_buf)-1];
+ if (last_char != '!' && last_char != '.' && last_char != '?')
+ strcat(msg_buf, ".");
+
+ // final format:
+ if (dst_buf[0] && src_buf[0]) {
+ sprintf(txt_buf, "%s %s. %s", TranslateVox(dst_buf).data(), TranslateVox(src_buf).data(), msg_buf);
+ txt_buf[0] = toupper(txt_buf[0]);
+ }
+
+ else if (src_buf[0]) {
+ sprintf(txt_buf, "%s. %s", TranslateVox(src_buf).data(), msg_buf);
+ txt_buf[0] = toupper(txt_buf[0]);
+ }
+
+ else if (dst_buf[0]) {
+ sprintf(txt_buf, "%s %s", TranslateVox(dst_buf).data(), msg_buf);
+ txt_buf[0] = toupper(txt_buf[0]);
+ }
+
+ else {
+ strcpy(txt_buf, msg_buf);
+ }
+
+ // vox:
+ const char* path[8] = { "1", "1", "2", "3", "4", "5", "6", "7" };
+
+ RadioVox* vox = new(__FILE__,__LINE__) RadioVox(vox_channel, path[vox_channel], txt_buf);
+
+ vox->AddPhrase(dst_buf);
+ vox->AddPhrase(src_buf);
+ vox->AddPhrase(act_buf);
+
+ if (vox && !vox->Start()) {
+ RadioView::Message(txt_buf);
+ delete vox;
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+RadioTraffic::DiscardMessages()
+{
+ if (radio_traffic)
+ radio_traffic->traffic.destroy();
+}
diff --git a/Stars45/RadioTraffic.h b/Stars45/RadioTraffic.h
new file mode 100644
index 0000000..93a925c
--- /dev/null
+++ b/Stars45/RadioTraffic.h
@@ -0,0 +1,65 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: RadioTraffic.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ RadioTraffic maintains a history of all messages sent between ships
+ in the simulation. This class also handles displaying relevant
+ traffic to the player.
+*/
+
+#ifndef RadioTraffic_h
+#define RadioTraffic_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "SimObject.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Element;
+class RadioMessage;
+class Ship;
+class SimObject;
+
+// +--------------------------------------------------------------------+
+
+class RadioTraffic
+{
+public:
+ RadioTraffic();
+ ~RadioTraffic();
+
+ // accessors:
+ static void Initialize();
+ static void Close();
+
+ static RadioTraffic* GetInstance() { return radio_traffic; }
+
+ static void SendQuickMessage(Ship* ship, int msg);
+ static void Transmit(RadioMessage* msg);
+ static void DiscardMessages();
+ static Text TranslateVox(const char* phrase);
+
+ void SendMessage(RadioMessage* msg);
+ void DisplayMessage(RadioMessage* msg);
+
+
+protected:
+ List<RadioMessage> traffic;
+
+ static RadioTraffic* radio_traffic;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif RadioTraffic_h
+
diff --git a/Stars45/RadioView.cpp b/Stars45/RadioView.cpp
new file mode 100644
index 0000000..cdb5f84
--- /dev/null
+++ b/Stars45/RadioView.cpp
@@ -0,0 +1,640 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: RadioView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ View class for Radio Communications HUD Overlay
+*/
+
+#include "MemDebug.h"
+#include "RadioView.h"
+#include "RadioMessage.h"
+#include "RadioTraffic.h"
+#include "QuantumView.h"
+#include "HUDView.h"
+#include "Ship.h"
+#include "Element.h"
+#include "Sim.h"
+#include "Starshatter.h"
+
+#include "CameraView.h"
+#include "Color.h"
+#include "Window.h"
+#include "Video.h"
+#include "Screen.h"
+#include "DataLoader.h"
+#include "Scene.h"
+#include "FontMgr.h"
+#include "FormatUtil.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "Game.h"
+#include "Menu.h"
+
+static Color hud_color = Color::Black;
+static Color txt_color = Color::White;
+
+// +====================================================================+
+//
+// RADIO COMMUNICATIONS MENU:
+//
+
+static Menu* fighter_menu = 0;
+static Menu* starship_menu = 0;
+static Menu* target_menu = 0;
+static Menu* combat_menu = 0;
+static Menu* formation_menu = 0;
+static Menu* sensors_menu = 0;
+static Menu* mission_menu = 0;
+static Menu* wing_menu = 0;
+static Menu* elem_menu = 0;
+static Menu* control_menu = 0;
+
+static int starship_page = 0;
+static int num_pages = 0;
+const int PAGE_SIZE = 9;
+
+static MenuHistory history;
+
+void
+RadioView::Initialize()
+{
+ static int initialized = 0;
+ if (initialized) return;
+
+ target_menu = new(__FILE__,__LINE__) Menu(Game::GetText("RadioView.menu.TARGET"));
+ target_menu->AddItem(Game::GetText("RadioView.item.attack"), RadioMessage::ATTACK);
+ target_menu->AddItem(Game::GetText("RadioView.item.bracket"), RadioMessage::BRACKET);
+ target_menu->AddItem(Game::GetText("RadioView.item.escort"), RadioMessage::ESCORT);
+
+ combat_menu = new(__FILE__,__LINE__) Menu(Game::GetText("RadioView.menu.COMBAT"));
+ combat_menu->AddItem(Game::GetText("RadioView.item.cover"), RadioMessage::COVER_ME);
+ combat_menu->AddItem(Game::GetText("RadioView.item.break-attack"), RadioMessage::WEP_FREE);
+ combat_menu->AddItem(Game::GetText("RadioView.item.form-up"), RadioMessage::FORM_UP);
+
+ formation_menu = new(__FILE__,__LINE__) Menu(Game::GetText("RadioView.menu.FORMATION"));
+ formation_menu->AddItem(Game::GetText("RadioView.item.diamond"), RadioMessage::GO_DIAMOND);
+ formation_menu->AddItem(Game::GetText("RadioView.item.spread"), RadioMessage::GO_SPREAD);
+ formation_menu->AddItem(Game::GetText("RadioView.item.box"), RadioMessage::GO_BOX);
+ formation_menu->AddItem(Game::GetText("RadioView.item.trail"), RadioMessage::GO_TRAIL);
+
+ sensors_menu = new(__FILE__,__LINE__) Menu(Game::GetText("RadioView.menu.SENSORS"));
+ sensors_menu->AddItem(Game::GetText("RadioView.item.emcon-1"), RadioMessage::GO_EMCON1);
+ sensors_menu->AddItem(Game::GetText("RadioView.item.emcon-2"), RadioMessage::GO_EMCON2);
+ sensors_menu->AddItem(Game::GetText("RadioView.item.emcon-3"), RadioMessage::GO_EMCON3);
+ sensors_menu->AddItem(Game::GetText("RadioView.item.probe"), RadioMessage::LAUNCH_PROBE);
+
+ mission_menu = new(__FILE__,__LINE__) Menu(Game::GetText("RadioView.menu.MISSION"));
+ mission_menu->AddItem(Game::GetText("RadioView.item.skip-navpt"), RadioMessage::SKIP_NAVPOINT);
+ mission_menu->AddItem(Game::GetText("RadioView.item.resume"), RadioMessage::RESUME_MISSION);
+ mission_menu->AddItem(Game::GetText("RadioView.item.rtb"), RadioMessage::RTB);
+
+ wing_menu = new(__FILE__,__LINE__) Menu(Game::GetText("RadioView.menu.WINGMAN"));
+ wing_menu->AddMenu(Game::GetText("RadioView.item.target"), target_menu);
+ wing_menu->AddMenu(Game::GetText("RadioView.item.combat"), combat_menu);
+ wing_menu->AddMenu(Game::GetText("RadioView.item.formation"), formation_menu);
+ wing_menu->AddMenu(Game::GetText("RadioView.item.mission"), mission_menu);
+ wing_menu->AddMenu(Game::GetText("RadioView.item.sensors"), sensors_menu);
+
+ elem_menu = new(__FILE__,__LINE__) Menu(Game::GetText("RadioView.menu.ELEMENT"));
+ elem_menu->AddMenu(Game::GetText("RadioView.item.target"), target_menu);
+ elem_menu->AddMenu(Game::GetText("RadioView.item.combat"), combat_menu);
+ elem_menu->AddMenu(Game::GetText("RadioView.item.formation"), formation_menu);
+ elem_menu->AddMenu(Game::GetText("RadioView.item.mission"), mission_menu);
+ elem_menu->AddMenu(Game::GetText("RadioView.item.sensors"), sensors_menu);
+
+ control_menu = new(__FILE__,__LINE__) Menu(Game::GetText("RadioView.menu.CONTROL"));
+ control_menu->AddItem(Game::GetText("RadioView.item.picture"), RadioMessage::REQUEST_PICTURE);
+ control_menu->AddItem(Game::GetText("RadioView.item.backup"), RadioMessage::REQUEST_SUPPORT);
+ control_menu->AddItem(Game::GetText("RadioView.item.call-inbound"), RadioMessage::CALL_INBOUND);
+ control_menu->AddItem(Game::GetText("RadioView.item.call-finals"), RadioMessage::CALL_FINALS);
+
+ fighter_menu = new(__FILE__,__LINE__) Menu(Game::GetText("RadioView.menu.RADIO"));
+ fighter_menu->AddMenu(Game::GetText("RadioView.item.wingman"), wing_menu);
+ fighter_menu->AddMenu(Game::GetText("RadioView.item.element"), elem_menu);
+ fighter_menu->AddMenu(Game::GetText("RadioView.item.control"), control_menu);
+
+ starship_menu = new(__FILE__,__LINE__) Menu(Game::GetText("RadioView.menu.RADIO"));
+
+ initialized = 1;
+}
+
+void
+RadioView::Close()
+{
+ history.Clear();
+
+ delete fighter_menu;
+ delete starship_menu;
+ delete target_menu;
+ delete combat_menu;
+ delete formation_menu;
+ delete sensors_menu;
+ delete mission_menu;
+ delete wing_menu;
+ delete elem_menu;
+ delete control_menu;
+}
+
+static bool TargetRequired(const MenuItem* item)
+{
+ if (item) {
+ switch (item->GetData()) {
+ case RadioMessage::ATTACK:
+ case RadioMessage::BRACKET:
+ case RadioMessage::ESCORT:
+ return true;
+
+ default:
+ if (item->GetData() == (DWORD) target_menu)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// +====================================================================+
+
+RadioView* RadioView::radio_view = 0;
+ThreadSync RadioView::sync;
+
+RadioView::RadioView(Window* c)
+ : View(c), sim(0), ship(0), font(0), dst_elem(0)
+{
+ radio_view = this;
+ sim = Sim::GetSim();
+
+ width = window->Width();
+ height = window->Height();
+ xcenter = (width / 2.0) - 0.5;
+ ycenter = (height / 2.0) + 0.5;
+ font = FontMgr::Find("HUD");
+
+ HUDView* hud = HUDView::GetInstance();
+ if (hud)
+ SetColor(hud->GetTextColor());
+
+ for (int i = 0; i < MAX_MSG; i++)
+ msg_time[i] = 0;
+}
+
+RadioView::~RadioView()
+{
+ radio_view = 0;
+}
+
+void
+RadioView::OnWindowMove()
+{
+ width = window->Width();
+ height = window->Height();
+ xcenter = (width / 2.0) - 0.5;
+ ycenter = (height / 2.0) + 0.5;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+RadioView::Update(SimObject* obj)
+{
+ if (obj == ship) {
+ ship = 0;
+ history.Clear();
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+RadioView::GetObserverName() const
+{
+ return "RadioView";
+}
+
+// +--------------------------------------------------------------------+
+
+void
+RadioView::Refresh()
+{
+ sim = Sim::GetSim();
+
+ if (!font)
+ return;
+
+ font->SetColor(txt_color);
+ font->SetAlpha(1);
+
+ if (sim && ship != sim->GetPlayerShip()) {
+ ship = sim->GetPlayerShip();
+ history.Clear();
+
+ if (ship) {
+ if (ship->Life() == 0 || ship->IsDying() || ship->IsDead()) {
+ ship = 0;
+ }
+ else {
+ Observe(ship);
+ }
+ }
+ }
+
+ QuantumView* qv = QuantumView::GetInstance();
+
+ if (!qv || !qv->IsMenuShown()) {
+ Menu* menu = history.GetCurrent();
+
+ if (menu) {
+ Rect menu_rect(width-115, 10, 115, 12);
+
+ font->DrawText(menu->GetTitle(), 0, menu_rect, DT_CENTER);
+ menu_rect.y += 15;
+
+ ListIter<MenuItem> item = menu->GetItems();
+ while (++item) {
+ if (ship->GetEMCON() < 2 ||
+ (TargetRequired(item.value()) && !ship->GetTarget()) ||
+ item->GetText().contains("KIA")) {
+ item->SetEnabled(false);
+ font->SetAlpha(0.35);
+ }
+ else {
+ item->SetEnabled(true);
+ font->SetAlpha(1);
+ }
+
+ font->DrawText(item->GetText(), 0, menu_rect, DT_LEFT);
+ menu_rect.y += 10;
+ }
+ }
+ }
+
+ int message_queue_empty = true;
+
+ // age messages:
+ for (int i = 0; i < MAX_MSG; i++) {
+ if (msg_time[i] > 0) {
+ msg_time[i] -= Game::GUITime();
+
+ if (msg_time[i] <= 0) {
+ msg_time[i] = 0;
+ msg_text[i] = "";
+ }
+
+ message_queue_empty = false;
+ }
+ }
+
+ if (!message_queue_empty) {
+ // advance message pipeline:
+ for (i = 0; i < MAX_MSG; i++) {
+ if (msg_time[0] == 0) {
+ for (int j = 0; j < MAX_MSG-1; j++) {
+ msg_time[j] = msg_time[j+1];
+ msg_text[j] = msg_text[j+1];
+ }
+
+ msg_time[MAX_MSG-1] = 0;
+ msg_text[MAX_MSG-1] = "";
+ }
+ }
+
+ bool hud_off = false;
+
+ if (HUDView::GetInstance())
+ hud_off = (HUDView::GetInstance()->GetHUDMode() == HUDView::HUD_MODE_OFF);
+
+ // draw messages:
+ if (!hud_off) {
+ for (i = 0; i < MAX_MSG; i++) {
+ if (msg_time[i] > 0) {
+ Rect msg_rect(0, 95 + i*10, width, 12);
+
+ if (msg_time[i] > 1)
+ font->SetAlpha(1);
+ else
+ font->SetAlpha(0.5 + 0.5*msg_time[i]);
+
+ font->DrawText(msg_text[i].data(), msg_text[i].length(), msg_rect, DT_CENTER);
+ }
+ }
+
+ font->SetAlpha(1);
+ }
+ }
+
+ Starshatter* stars = Starshatter::GetInstance();
+ if (stars && stars->GetChatMode()) {
+ Text chat;
+
+ switch (stars->GetChatMode()) {
+ case 1: chat = "ALL: "; break;
+ case 2: chat = "TEAM: "; break;
+ case 3: chat = "WING: "; break;
+ case 4: chat = "UNIT: "; break;
+ }
+
+ chat += stars->GetChatText();
+
+ Rect chat_rect(width/2 - 250, height-150, 500, 12);
+ font->DrawText(chat, 0, chat_rect, DT_LEFT);
+
+ chat_rect.Inflate(2,2);
+ window->DrawRect(chat_rect, hud_color);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+RadioView::SendRadioMessage(Ship* ship, MenuItem* item)
+{
+ if (!ship || !item) return;
+ Element* elem = ship->GetElement();
+ if (!elem) return;
+
+ // check destination:
+ if (dst_elem) {
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(dst_elem, ship, item->GetData());
+
+ if (TargetRequired(item))
+ msg->AddTarget(ship->GetTarget());
+
+ RadioTraffic::Transmit(msg);
+ dst_elem = 0;
+ }
+
+ else if (history.Find(Game::GetText("RadioView.menu.WINGMAN"))) { // wingman menu
+ int index = ship->GetElementIndex();
+ int wing = 0;
+
+ switch (index) {
+ case 1: wing = 2; break;
+ case 2: wing = 1; break;
+ case 3: wing = 4; break;
+ case 4: wing = 3; break;
+ }
+
+ if (wing) {
+ Ship* dst = elem->GetShip(wing);
+ if (dst) {
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(dst, ship, item->GetData());
+
+ if (TargetRequired(item))
+ msg->AddTarget(ship->GetTarget());
+
+ RadioTraffic::Transmit(msg);
+ }
+ }
+ }
+
+ else if (history.Find(Game::GetText("RadioView.menu.ELEMENT"))) { // element menu
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(elem, ship, item->GetData());
+
+ if (TargetRequired(item))
+ msg->AddTarget(ship->GetTarget());
+
+ RadioTraffic::Transmit(msg);
+ }
+
+ else if (history.Find(Game::GetText("RadioView.menu.CONTROL"))) { // control menu
+ RadioMessage* msg = 0;
+ Ship* controller = ship->GetController();
+
+ if (controller) {
+ msg = new(__FILE__,__LINE__) RadioMessage(controller, ship, item->GetData());
+ RadioTraffic::Transmit(msg);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+RadioView::ExecFrame()
+{
+ HUDView* hud = HUDView::GetInstance();
+ if (hud) {
+ if (txt_color != hud->GetTextColor()) {
+ txt_color = hud->GetTextColor();
+ SetColor(txt_color);
+ }
+
+ hud_color = hud->GetHUDColor();
+ }
+
+ static int current_key = 0;
+
+ if (current_key > 0 && Keyboard::KeyDown(current_key))
+ return;
+
+ current_key = 0;
+
+ Menu* menu = history.GetCurrent();
+ if (menu) {
+ int max_items = menu->NumItems();
+
+ if (menu == starship_menu && Keyboard::KeyDown('0')) {
+ current_key = '0';
+ if (++starship_page >= num_pages)
+ starship_page = 0;
+
+ history.Pop();
+ history.Push(GetRadioMenu(ship));
+ }
+ else {
+ for (int i = 0; i < max_items; i++) {
+ if (Keyboard::KeyDown('1' + i)) {
+ current_key = '1' + i;
+ MenuItem* item = menu->GetItem(i);
+ if (item && item->GetEnabled()) {
+ if (item->GetSubmenu()) {
+ if (history.GetCurrent() == starship_menu)
+ dst_elem = (Element*) item->GetData();
+
+ history.Push(item->GetSubmenu());
+ }
+ else {
+ // execute radio message:
+ SendRadioMessage(ship, item);
+
+ // clear radio menu:
+ history.Clear();
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+RadioView::SetColor(Color c)
+{
+ HUDView* hud = HUDView::GetInstance();
+
+ if (hud) {
+ hud_color = hud->GetHUDColor();
+ txt_color = hud->GetTextColor();
+ }
+ else {
+ hud_color = c;
+ txt_color = c;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+RadioView::IsMenuShown()
+{
+ return history.GetCurrent() != 0;
+}
+
+void
+RadioView::ShowMenu()
+{
+ if (!ship) return;
+
+ if (!history.GetCurrent()) {
+ history.Push(GetRadioMenu(ship));
+
+ for (int i = 0; i < 10; i++) {
+ if (Keyboard::KeyDown('1' + i)) {
+ // just need to clear the key down flag
+ // so we don't process old keystrokes
+ // as valid menu picks...
+ }
+ }
+ }
+}
+
+void
+RadioView::CloseMenu()
+{
+ history.Clear();
+ dst_elem = 0;
+ starship_page = 0;
+ num_pages = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+Menu*
+RadioView::GetRadioMenu(Ship* s)
+{
+ dst_elem = 0;
+
+ if (s && sim) {
+ if (s->IsStarship()) {
+ starship_menu->ClearItems();
+
+ int n = 0;
+ int page_offset = starship_page*PAGE_SIZE;
+
+ ListIter<Element> elem = sim->GetElements();
+
+ if (num_pages == 0) {
+ while (++elem) {
+ if (elem->IsFinished() || elem->IsSquadron() || elem->IsStatic())
+ continue;
+
+ if (ship->GetIFF() == elem->GetIFF() && ship->GetElement() != elem.value())
+ n++;
+ }
+
+ num_pages = (n/PAGE_SIZE) + (n%PAGE_SIZE > 0);
+ n = 0;
+ elem.reset();
+ }
+
+ while (++elem) {
+ if (elem->IsFinished() || elem->IsSquadron() || elem->IsStatic())
+ continue;
+
+ if (ship->GetIFF() == elem->GetIFF() && ship->GetElement() != elem.value()) {
+ if (n >= page_offset && n < page_offset+PAGE_SIZE) {
+ char text[64];
+ sprintf(text, "%d. %s", n+1 - page_offset, (const char*) elem->Name());
+
+ if (elem->IsActive()) {
+ starship_menu->AddMenu(text, elem_menu, (DWORD) elem.value());
+ }
+ else {
+ strcat(text, " ");
+ strcat(text, Game::GetText("RadioView.item.not-avail").data());
+ starship_menu->AddItem(text, 0, false);
+ }
+ }
+ n++;
+ }
+ }
+
+ if (num_pages > 1) {
+ char text[64];
+ sprintf(text, Game::GetText("RadioView.item.next-page").data(), starship_page + 1, num_pages);
+ starship_menu->AddItem(text);
+ }
+
+ return starship_menu;
+ }
+ else if (s->IsDropship()) {
+ return fighter_menu;
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+RadioView::Message(const char* msg)
+{
+ AutoThreadSync a(sync);
+
+ if (radio_view) {
+ int index = -1;
+
+ for (int i = 0; i < MAX_MSG; i++) {
+ if (radio_view->msg_time[i] <= 0) {
+ index = i;
+ break;
+ }
+ }
+
+ // no space; advance pipeline:
+ if (index < 0) {
+ for (i = 0; i < MAX_MSG-1; i++) {
+ radio_view->msg_text[i] = radio_view->msg_text[i+1];
+ radio_view->msg_time[i] = radio_view->msg_time[i+1];
+ }
+
+ index = MAX_MSG-1;
+ }
+
+ radio_view->msg_text[index] = msg;
+ radio_view->msg_time[index] = 10;
+ }
+}
+
+void
+RadioView::ClearMessages()
+{
+ if (radio_view) {
+ for (int i = 0; i < MAX_MSG-1; i++) {
+ radio_view->msg_text[i] = Text();
+ radio_view->msg_time[i] = 0;
+ }
+ }
+}
diff --git a/Stars45/RadioView.h b/Stars45/RadioView.h
new file mode 100644
index 0000000..8bcdf03
--- /dev/null
+++ b/Stars45/RadioView.h
@@ -0,0 +1,87 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: RadioView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ View class for Radio Communications HUD Overlay
+*/
+
+#ifndef RadioView_h
+#define RadioView_h
+
+#include "Types.h"
+#include "View.h"
+#include "Color.h"
+#include "SimObject.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Font;
+class Element;
+class Ship;
+class RadioMessage;
+class CameraView;
+class HUDView;
+class Menu;
+class MenuItem;
+
+// +--------------------------------------------------------------------+
+
+class RadioView : public View,
+ public SimObserver
+{
+public:
+ RadioView(Window* c);
+ virtual ~RadioView();
+
+ // Operations:
+ virtual void Refresh();
+ virtual void OnWindowMove();
+ virtual void ExecFrame();
+
+ virtual Menu* GetRadioMenu(Ship* ship);
+ virtual bool IsMenuShown();
+ virtual void ShowMenu();
+ virtual void CloseMenu();
+
+ static void Message(const char* msg);
+ static void ClearMessages();
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+ static void SetColor(Color c);
+
+ static void Initialize();
+ static void Close();
+
+ static RadioView* GetInstance() { return radio_view; }
+
+protected:
+ void SendRadioMessage(Ship* ship, MenuItem* item);
+
+ int width, height;
+ double xcenter, ycenter;
+
+ Font* font;
+ Sim* sim;
+ Ship* ship;
+ Element* dst_elem;
+
+ enum { MAX_MSG=6 };
+ Text msg_text[MAX_MSG];
+ double msg_time[MAX_MSG];
+
+ static RadioView* radio_view;
+ static ThreadSync sync;
+};
+
+#endif RadioView_h
+
diff --git a/Stars45/RadioVox.cpp b/Stars45/RadioVox.cpp
new file mode 100644
index 0000000..6974dc0
--- /dev/null
+++ b/Stars45/RadioVox.cpp
@@ -0,0 +1,249 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: RadioVox.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ View class for Radio Communications HUD Overlay
+*/
+
+#include "MemDebug.h"
+#include "RadioVox.h"
+#include "RadioView.h"
+#include "AudioConfig.h"
+
+#include "DataLoader.h"
+#include "Game.h"
+#include "Sound.h"
+#include "ThreadSync.h"
+
+// +====================================================================+
+//
+// RADIO VOX CONTROLLER:
+//
+
+DWORD WINAPI VoxUpdateProc(LPVOID link);
+
+class RadioVoxController
+{
+public:
+ enum { MAX_QUEUE = 5 };
+
+ RadioVoxController();
+ ~RadioVoxController();
+
+ bool Add(RadioVox* vox);
+ void Update();
+ DWORD UpdateThread();
+
+ bool shutdown;
+ HANDLE hthread;
+ List<RadioVox> queue;
+ ThreadSync sync;
+};
+
+static RadioVoxController* controller = 0;
+
+// +--------------------------------------------------------------------+
+
+RadioVoxController::RadioVoxController()
+ : hthread(0), shutdown(false)
+{
+ DWORD thread_id = 0;
+ hthread = CreateThread(0, 4096, VoxUpdateProc,
+ (LPVOID) this, 0, &thread_id);
+}
+
+// +--------------------------------------------------------------------+
+
+RadioVoxController::~RadioVoxController()
+{
+ shutdown = true;
+
+ WaitForSingleObject(hthread, 500);
+ CloseHandle(hthread);
+ hthread = 0;
+
+ queue.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD WINAPI VoxUpdateProc(LPVOID link)
+{
+ RadioVoxController* controller = (RadioVoxController*) link;
+
+ if (controller)
+ return controller->UpdateThread();
+
+ return (DWORD) E_POINTER;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+RadioVoxController::UpdateThread()
+{
+ while (!shutdown) {
+ Update();
+ Sleep(50);
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+RadioVoxController::Update()
+{
+ AutoThreadSync a(sync);
+
+ if (queue.size()) {
+ RadioVox* vox = queue.first();
+
+ if (!vox->Update())
+ delete queue.removeIndex(0);
+ }
+}
+
+bool
+RadioVoxController::Add(RadioVox* vox)
+{
+ if (!vox || vox->sounds.isEmpty())
+ return false;
+
+ AutoThreadSync a(sync);
+
+ if (queue.size() < MAX_QUEUE) {
+ queue.append(vox);
+ return true;
+ }
+
+ return false;
+}
+
+// +====================================================================+
+//
+// RADIO VOX MESSAGE:
+//
+
+void
+RadioVox::Initialize()
+{
+ if (!controller) {
+ controller = new(__FILE__,__LINE__) RadioVoxController;
+ }
+}
+
+void
+RadioVox::Close()
+{
+ delete controller;
+ controller = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+RadioVox::RadioVox(int n, const char* p, const char* m)
+ : path(p), message(m), index(0), channel(n)
+{
+}
+
+RadioVox::~RadioVox()
+{
+ sounds.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+RadioVox::AddPhrase(const char* key)
+{
+ if (AudioConfig::VoxVolume() <= AudioConfig::Silence())
+ return false;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ if (!loader)
+ return false;
+
+ if (key && *key) {
+ char datapath[256];
+ char filename[256];
+
+ sprintf(datapath, "Vox/%s/", path.data());
+ sprintf(filename, "%s.wav", key);
+
+ bool use_fs = loader->IsFileSystemEnabled();
+ Sound* sound = 0;
+
+ loader->UseFileSystem(true);
+ loader->SetDataPath(datapath);
+ loader->LoadSound(filename, sound, Sound::LOCALIZED, true); // optional sound
+ loader->SetDataPath(0);
+ loader->UseFileSystem(use_fs);
+
+ if (sound) {
+ sound->SetVolume(AudioConfig::VoxVolume());
+ sound->SetFlags(Sound::LOCALIZED | Sound::LOCKED);
+ sound->SetFilename(filename);
+ sounds.append(sound);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+RadioVox::Start()
+{
+ if (controller)
+ return controller->Add(this);
+
+ return false;
+}
+
+bool
+RadioVox::Update()
+{
+ if (message.length()) {
+ RadioView::Message(message);
+ message = "";
+ }
+
+ bool active = false;
+
+ while (!active && index < sounds.size()) {
+ Sound* s = sounds[index];
+
+ if (s->IsReady()) {
+ if (channel & 1)
+ s->SetPan(channel * -3000);
+ else
+ s->SetPan(channel * 3000);
+
+ s->Play();
+ active = true;
+ }
+
+ else if (s->IsPlaying()) {
+ s->Update();
+ active = true;
+ }
+
+ else {
+ index++;
+ }
+ }
+
+ return active;
+}
diff --git a/Stars45/RadioVox.h b/Stars45/RadioVox.h
new file mode 100644
index 0000000..b68430a
--- /dev/null
+++ b/Stars45/RadioVox.h
@@ -0,0 +1,59 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: RadioVox.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ View class for Radio Communications HUD Overlay
+*/
+
+#ifndef RadioVox_h
+#define RadioVox_h
+
+#include "Types.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Element;
+class Ship;
+class RadioMessage;
+class Sound;
+
+// +--------------------------------------------------------------------+
+
+class RadioVox
+{
+ friend class RadioVoxController;
+
+public:
+ static const char* TYPENAME() { return "RadioVox"; }
+
+ RadioVox(int channel, const char* path, const char* message=0);
+ virtual ~RadioVox();
+
+ // Operations:
+ virtual bool AddPhrase(const char* key);
+ virtual bool Start();
+
+ static void Initialize();
+ static void Close();
+
+protected:
+ virtual bool Update();
+
+ Text path;
+ Text message;
+ List<Sound> sounds;
+ int index;
+ int channel;
+};
+
+#endif RadioVox_h
+
diff --git a/Stars45/SeekerAI.cpp b/Stars45/SeekerAI.cpp
new file mode 100644
index 0000000..c57219c
--- /dev/null
+++ b/Stars45/SeekerAI.cpp
@@ -0,0 +1,257 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: SeekerAI.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Seeker Missile (low-level) Artificial Intelligence class
+*/
+
+#include "MemDebug.h"
+#include "SeekerAI.h"
+#include "Ship.h"
+#include "Shot.h"
+#include "System.h"
+#include "WeaponDesign.h"
+
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+SeekerAI::SeekerAI(SimObject* s)
+ : SteerAI(s), shot((Shot*) s), orig_target(0),
+ pursuit(1), delay(0), overshot(false)
+{
+ ai_type = SEEKER;
+
+ seek_gain = 25;
+ seek_damp = 0.55;
+}
+
+
+// +--------------------------------------------------------------------+
+
+SeekerAI::~SeekerAI()
+{
+ if (shot) {
+ if (shot->Owner())
+ ((Ship*) shot->Owner())->SetMissileEta(shot->Identity(), 0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SeekerAI::ExecFrame(double seconds)
+{
+ // setup:
+ FindObjective();
+
+ // adaptive behavior:
+ Navigator();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SeekerAI::Navigator()
+{
+ if (delay > 0) {
+ delay -= Game::FrameTime();
+ }
+ else {
+ Steer s = SeekTarget();
+ self->ApplyYaw((float) s.yaw);
+ self->ApplyPitch((float) s.pitch);
+ }
+}
+
+void
+SeekerAI::SetTarget(SimObject* targ, System* sub)
+{
+ if (!orig_target && targ && targ->Type() == SimObject::SIM_SHIP) {
+ orig_target = (Ship*) targ;
+ Observe(orig_target);
+ }
+
+ SteerAI::SetTarget(targ, sub);
+
+ if (!target) {
+ shot->SetEta(0);
+
+ if (shot->Owner())
+ ((Ship*) shot->Owner())->SetMissileEta(shot->Identity(), 0);
+ }
+}
+
+void
+SeekerAI::FindObjective()
+{
+ if (!shot || !target) return;
+
+ if (target->Life() == 0) {
+ if (target != orig_target)
+ SetTarget(orig_target,0);
+ else
+ SetTarget(0,0);
+
+ return;
+ }
+
+ Point tloc = target->Location();
+ tloc = Transform(tloc);
+
+ // seeker head limit of 45 degrees:
+ if (tloc.z < 0 || tloc.z < fabs(tloc.x) || tloc.z < fabs(tloc.y)) {
+ overshot = true;
+ SetTarget(0,0);
+ return;
+ }
+
+ // distance from self to target:
+ distance = Point(target->Location() - self->Location()).length();
+
+ // are we being spoofed?
+ CheckDecoys(distance);
+
+ Point cv = ClosingVelocity();
+
+ // time to reach target:
+ double time = distance / cv.length();
+ double predict = time;
+ if (predict > 15)
+ predict = 15;
+
+ // pure pursuit:
+ if (pursuit == 1 || time < 0.1) {
+ obj_w = target->Location();
+ }
+
+ // lead pursuit:
+ else {
+ // where the target will be when we reach it:
+ Point run_vec = target->Velocity();
+ obj_w = target->Location() + (run_vec * predict);
+ }
+
+ // subsystem offset:
+ if (subtarget) {
+ Point offset = target->Location() - subtarget->MountLocation();
+ obj_w -= offset;
+ }
+ else if (target->Type() == SimObject::SIM_SHIP) {
+ Ship* tgt_ship = (Ship*) target;
+
+ if (tgt_ship->IsGroundUnit())
+ obj_w += Point(0,150,0);
+ }
+
+
+ distance = Point(obj_w - self->Location()).length();
+ time = distance / cv.length();
+
+ // where we will be when the target gets there:
+ if (predict > 0.1 && predict < 15) {
+ Point self_dest = self->Location() + cv * predict;
+ Point err = obj_w - self_dest;
+
+ obj_w += err;
+ }
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+ objective.Normalize();
+
+ shot->SetEta((int) time);
+
+ if (shot->Owner())
+ ((Ship*) shot->Owner())->SetMissileEta(shot->Identity(), (int) time);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SeekerAI::CheckDecoys(double target_distance)
+{
+ // if the assigned target has the burner lit,
+ // ignore the decoys:
+ if (orig_target && orig_target->Augmenter()) {
+ SetTarget(orig_target);
+ return;
+ }
+
+ if (target &&
+ target == orig_target &&
+ orig_target->GetActiveDecoys().size()) {
+
+ ListIter<Shot> decoy = orig_target->GetActiveDecoys();
+
+ while (++decoy) {
+ double decoy_distance = Point(decoy->Location() - self->Location()).length();
+
+ if (decoy_distance < target_distance) {
+ if (rand() < 1600) {
+ SetTarget(decoy.value(), 0);
+ return;
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SeekerAI::Overshot()
+{
+ return overshot;
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+SeekerAI::AvoidCollision()
+{
+ return Steer();
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+SeekerAI::SeekTarget()
+{
+ if (!self || !target || Overshot())
+ return Steer();
+
+ return Seek(objective);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SeekerAI::Update(SimObject* obj)
+{
+ if (obj == target) {
+ if (obj->Type() == SimObject::SIM_SHOT && orig_target != 0)
+ target = orig_target;
+ }
+
+ if (obj == orig_target)
+ orig_target = 0;
+
+ return SteerAI::Update(obj);
+}
+
+const char*
+SeekerAI::GetObserverName() const
+{
+ static char name[64];
+ sprintf(name, "SeekerAI(%s)", self->Name());
+ return name;
+}
+
diff --git a/Stars45/SeekerAI.h b/Stars45/SeekerAI.h
new file mode 100644
index 0000000..c72e54f
--- /dev/null
+++ b/Stars45/SeekerAI.h
@@ -0,0 +1,73 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: SeekerAI.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Seeker Missile (low-level) Artifical Intelligence class
+*/
+
+#ifndef SeekerAI_h
+#define SeekerAI_h
+
+#include "Types.h"
+#include "SteerAI.h"
+#include "SimObject.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+class Shot;
+
+class SeekerAI : public SteerAI
+{
+public:
+ SeekerAI(SimObject* s);
+ virtual ~SeekerAI();
+
+ virtual int Type() const { return 1001; }
+ virtual int Subframe() const { return true; }
+
+ virtual void ExecFrame(double seconds);
+ virtual void FindObjective();
+ virtual void SetTarget(SimObject* targ, System* sub=0);
+ virtual bool Overshot();
+
+ virtual void SetPursuit(int p) { pursuit = p; }
+ virtual int GetPursuit() const { return pursuit; }
+
+ virtual void SetDelay(double d) { delay = d; }
+ virtual double GetDelay() const { return delay; }
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+protected:
+ // behaviors:
+ virtual Steer AvoidCollision();
+ virtual Steer SeekTarget();
+
+ // accumulate behaviors:
+ virtual void Navigator();
+
+ virtual void CheckDecoys(double distance);
+
+ Ship* orig_target;
+ Shot* shot;
+ int pursuit; // type of pursuit curve
+ // 1: pure pursuit
+ // 2: lead pursuit
+
+ double delay; // don't start seeking until then
+ bool overshot;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif SeekerAI_h
+
diff --git a/Stars45/Sensor.cpp b/Stars45/Sensor.cpp
new file mode 100644
index 0000000..315d214
--- /dev/null
+++ b/Stars45/Sensor.cpp
@@ -0,0 +1,850 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Sensor.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Integrated (Passive and Active) Sensor Package class implementation
+*/
+
+#include "MemDebug.h"
+#include "Sensor.h"
+#include "Contact.h"
+#include "Element.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Shot.h"
+#include "Drone.h"
+#include "WeaponDesign.h"
+#include "Sim.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+const double SENSOR_THRESHOLD = 0.25;
+
+// +----------------------------------------------------------------------+
+
+Sensor::Sensor()
+ : System(SENSOR, 1, "Dual Sensor Pkg", 1, 10, 10, 10),
+ mode(STD), target(0),
+ nsettings(0), range_index(0)
+{
+ name = Game::GetText("sys.sensor");
+ abrv = Game::GetText("sys.sensor.abrv");
+
+ SetMode(mode);
+ power_flags = POWER_WATTS;
+
+ ZeroMemory(range_settings, sizeof(range_settings));
+}
+
+// +----------------------------------------------------------------------+
+
+Sensor::Sensor(const Sensor& s)
+ : System(s), mode(STD), target(0),
+ nsettings(s.nsettings), range_index(0)
+{
+ Mount(s);
+
+ SetMode(mode);
+ power_flags = POWER_WATTS;
+
+ CopyMemory(range_settings, s.range_settings, sizeof(range_settings));
+
+ if (nsettings)
+ range_index = nsettings-1;
+}
+
+// +--------------------------------------------------------------------+
+
+Sensor::~Sensor()
+{
+ ClearAllContacts();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sensor::ClearAllContacts()
+{
+ contacts.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Sensor::GetBeamLimit() const
+{
+ if (mode == ACM)
+ return 15*DEGREES;
+
+ if (mode <= GM)
+ return 45*DEGREES;
+
+ return 175*DEGREES;
+}
+
+double
+Sensor::GetBeamRange() const
+{
+ return (double) range_settings[range_index];
+}
+
+void
+Sensor::IncreaseRange()
+{
+ if (range_index < nsettings-1)
+ range_index++;
+ else
+ range_index = nsettings-1;
+}
+
+void
+Sensor::DecreaseRange()
+{
+ if (range_index > 0)
+ range_index--;
+ else
+ range_index = 0;
+}
+
+void
+Sensor::AddRange(double r)
+{
+ if (nsettings < 8)
+ range_settings[nsettings++] = (float) r;
+
+ range_index = nsettings-1;
+}
+
+void
+Sensor::SetMode(Mode m)
+{
+ if (mode != m) {
+ // dump the contact list when changing in/out of GM:
+ if (mode == GM || m == GM)
+ ClearAllContacts();
+
+ // dump the current target on mode changes:
+ if (m <= GM) {
+ if (ship)
+ ship->DropTarget();
+
+ Ignore(target);
+ target = 0;
+ }
+ }
+
+ mode = m;
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+Sensor::Update(SimObject* obj)
+{
+ if (obj == target) {
+ target = 0;
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+Sensor::GetObserverName() const
+{
+ return "Sensor";
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sensor::ExecFrame(double seconds)
+{
+ if (Game::Paused())
+ return;
+
+ System::ExecFrame(seconds);
+
+ if (!IsPowerOn() || energy <= 0) {
+ ClearAllContacts();
+ return;
+ }
+
+ if (ship && ship->GetAIMode() < 2) {
+ // just pretend to work this frame:
+ energy = 0.0f;
+ return;
+ }
+
+ if (ship && ship->GetRegion()) {
+ const Camera* cam = &ship->Cam();
+ double az1 = -45*DEGREES;
+ double az2 = 45*DEGREES;
+
+ if (mode > GM) {
+ az1 = -175*DEGREES;
+ az2 = 175*DEGREES;
+ }
+
+ ListIter<Ship> ship_iter(ship->GetRegion()->Ships());
+ while (++ship_iter) {
+ Ship* c_ship = ship_iter.value();
+
+ if (c_ship != ship) {
+ ProcessContact(c_ship, az1, az2);
+ }
+ else {
+ Contact* c = FindContact(c_ship);
+
+ if (!c) {
+ c = new(__FILE__,__LINE__) Contact(c_ship, 0.0f, 0.0f);
+ contacts.append(c);
+ }
+
+ // update track:
+ if (c) {
+ c->loc = c_ship->Location();
+ c->d_pas = 2.0f;
+ c->d_act = 2.0f;
+
+ c->UpdateTrack();
+ }
+ }
+ }
+
+ ListIter<Shot> threat_iter(ship->GetThreatList());
+ while (++threat_iter) {
+ ProcessContact(threat_iter.value(), az1, az2);
+ }
+
+ ListIter<Drone> drone_iter(ship->GetRegion()->Drones());
+ while (++drone_iter) {
+ ProcessContact(drone_iter.value(), az1, az2);
+ }
+
+ List<Contact>& track_list = ship->GetRegion()->TrackList(ship->GetIFF());
+ ListIter<Contact> c_iter(contacts);
+
+ while (++c_iter) {
+ Contact* contact = c_iter.value();
+ Ship* c_ship = contact->GetShip();
+ Shot* c_shot = contact->GetShot();
+ double c_life = -1;
+
+ if (c_ship) {
+ c_life = c_ship->Life();
+
+ // look for quantum jumps and orbit transitions:
+ if (c_ship->GetRegion() != ship->GetRegion())
+ c_life = 0;
+ }
+
+ else if (c_shot) {
+ c_life = c_shot->Life();
+ }
+
+ else {
+ c_life = 0;
+ }
+
+ if (contact->Age() < 0 || c_life == 0) {
+ delete c_iter.removeItem();
+ }
+
+ else if (ship && ship->GetIFF() >= 0 && ship->GetIFF() < 5) {
+ // update shared track database:
+ Contact* t = track_list.find(contact);
+
+ if (c_ship) {
+ if (!t) {
+ Contact* track = new(__FILE__,__LINE__) Contact(c_ship, contact->d_pas, contact->d_act);
+ track->loc = c_ship->Location();
+ track_list.append(track);
+ }
+
+ else {
+ t->loc = c_ship->Location();
+ t->Merge(contact);
+ t->UpdateTrack();
+ }
+ }
+
+ else if (c_shot) {
+ if (!t) {
+ Contact* track = new(__FILE__,__LINE__) Contact(c_shot, contact->d_pas, contact->d_act);
+ track->loc = c_shot->Location();
+ track_list.append(track);
+ }
+
+ else {
+ t->loc = c_shot->Location();
+ t->Merge(contact);
+ t->UpdateTrack();
+ }
+ }
+ }
+ }
+
+
+ if (mode == ACM) {
+ if (!ship->GetTarget())
+ ship->LockTarget(SimObject::SIM_SHIP, true, true);
+ }
+ }
+
+ energy = 0.0f;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sensor::ProcessContact(Ship* c_ship, double az1, double az2)
+{
+ if (c_ship->IsNetObserver())
+ return;
+
+ double sensor_range = GetBeamRange();
+
+ // translate:
+ const Camera* cam = &ship->Cam();
+ Point targ_pt = c_ship->Location() - ship->Location();
+
+ // rotate:
+ double tx = targ_pt * cam->vrt();
+ double ty = targ_pt * cam->vup();
+ double tz = targ_pt * cam->vpn();
+
+ // convert to spherical coords:
+ double rng = targ_pt.length();
+ double az = asin(fabs(tx) / rng);
+ double el = asin(fabs(ty) / rng);
+ if (tx < 0) az = -az;
+ if (ty < 0) el = -el;
+
+ double min_range = rng;
+ Drone* probe = ship->GetProbe();
+ bool probescan = false;
+
+ if (ship->GetIFF() == c_ship->GetIFF()) {
+ min_range = 1;
+ }
+
+ else if (probe) {
+ Point probe_pt = c_ship->Location() - probe->Location();
+ double prng = probe_pt.length();
+
+ if (prng < probe->Design()->lethal_radius && prng < rng) {
+ min_range = prng;
+ probescan = true;
+ }
+ }
+
+ bool vis = tz > 1 && (c_ship->Radius()/rng > 0.001);
+ bool threat = (c_ship->Life() != 0 &&
+ c_ship->GetIFF() &&
+ c_ship->GetIFF() != ship->GetIFF() &&
+ c_ship->GetEMCON() > 2 &&
+ c_ship->IsTracking(ship));
+
+ if (!threat) {
+ if (mode == GM && !c_ship->IsGroundUnit())
+ return;
+
+ if (mode != GM && c_ship->IsGroundUnit())
+ return;
+
+ if (min_range > sensor_range || min_range > c_ship->Design()->detet) {
+ if (c_ship == target) {
+ ship->DropTarget();
+ Ignore(target);
+ target = 0;
+ }
+
+ return;
+ }
+ }
+
+ // clip:
+ if (threat || vis || mode >= PST || tz > 1) {
+
+ // correct az/el for back hemisphere:
+ if (tz < 0) {
+ if (az < 0) az = -PI - az;
+ else az = PI - az;
+ }
+
+ double d_pas = 0;
+ double d_act = 0;
+ double effectivity = energy/capacity * availability;
+
+ // did this contact get scanned this frame?
+ if (effectivity > SENSOR_THRESHOLD) {
+ if (az >= az1 && az <= az2 && (mode >= PST || fabs(el) < 45*DEGREES)) {
+ double passive_range_limit = 500e3;
+ if (c_ship->Design()->detet > passive_range_limit)
+ passive_range_limit = c_ship->Design()->detet;
+
+ d_pas = c_ship->PCS() * effectivity * (1 - min_range/passive_range_limit);
+
+ if (d_pas < 0)
+ d_pas = 0;
+
+ if (probescan) {
+ double max_range = probe->Design()->lethal_radius;
+ d_act = c_ship->ACS() * (1 - min_range/max_range);
+ }
+
+ else if (mode != PAS && mode != PST) {
+ double max_range = sensor_range;
+ d_act = c_ship->ACS() * effectivity * (1 - min_range/max_range);
+ }
+
+ if (d_act < 0)
+ d_act = 0;
+ }
+ }
+
+ // yes, update or add new contact:
+ if (threat || vis || d_pas > SENSOR_THRESHOLD || d_act > SENSOR_THRESHOLD) {
+ Element* elem = c_ship->GetElement();
+ CombatUnit* unit = c_ship->GetCombatUnit();
+
+ if (elem && ship && elem->GetIFF() != ship->GetIFF() && elem->IntelLevel() < Intel::LOCATED) {
+ elem->SetIntelLevel(Intel::LOCATED);
+ }
+
+ if (unit && ship && unit->GetIFF() != ship->GetIFF()) {
+ CombatGroup* group = unit->GetCombatGroup();
+
+ if (group && group->IntelLevel() < Intel::LOCATED &&
+ group->IntelLevel() > Intel::RESERVE) {
+ group->SetIntelLevel(Intel::LOCATED);
+ }
+ }
+
+ Contact* c = FindContact(c_ship);
+
+ if (!c) {
+ c = new(__FILE__,__LINE__) Contact(c_ship, 0.0f, 0.0f);
+ contacts.append(c);
+ }
+
+ // update track:
+ if (c) {
+ c->loc = c_ship->Location();
+ c->d_pas = (float) d_pas;
+ c->d_act = (float) d_act;
+ c->probe = probescan;
+
+ c->UpdateTrack();
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sensor::ProcessContact(Shot* c_shot, double az1, double az2)
+{
+ double sensor_range = GetBeamRange();
+
+ if (c_shot->IsPrimary() || c_shot->IsDecoy())
+ return;
+
+ // translate:
+ const Camera* cam = &ship->Cam();
+ Point targ_pt = c_shot->Location() - ship->Location();
+
+ // rotate:
+ double tx = targ_pt * cam->vrt();
+ double ty = targ_pt * cam->vup();
+ double tz = targ_pt * cam->vpn();
+
+ // convert to spherical coords:
+ double rng = targ_pt.length();
+ double az = asin(fabs(tx) / rng);
+ double el = asin(fabs(ty) / rng);
+ if (tx < 0) az = -az;
+ if (ty < 0) el = -el;
+
+ bool vis = tz > 1 && (c_shot->Radius()/rng > 0.001);
+ bool threat = (c_shot->IsTracking(ship));
+
+ // clip:
+ if (threat || vis || ((mode >= PST || tz > 1) && rng <= sensor_range)) {
+
+ // correct az/el for back hemisphere:
+ if (tz < 0) {
+ if (az < 0) az = -PI - az;
+ else az = PI - az;
+ }
+
+ double d_pas = 0;
+ double d_act = 0;
+ double effectivity = energy/capacity * availability;
+
+ // did this contact get scanned this frame?
+ if (effectivity > SENSOR_THRESHOLD) {
+ if (az >= az1 && az <= az2 && (mode >= PST || fabs(el) < 45*DEGREES)) {
+ if (rng < sensor_range/2)
+ d_pas = 1.5;
+ else
+ d_pas = 0.5;
+
+ if (mode != PAS && mode != PST)
+ d_act = effectivity * (1 - rng/sensor_range);
+
+ if (d_act < 0)
+ d_act = 0;
+ }
+ }
+
+ // yes, update or add new contact:
+ if (threat || vis || d_pas > SENSOR_THRESHOLD || d_act > SENSOR_THRESHOLD) {
+ Contact* c = FindContact(c_shot);
+
+ if (!c) {
+ c = new(__FILE__,__LINE__) Contact(c_shot, 0.0f, 0.0f);
+ contacts.append(c);
+ }
+
+ // update track:
+ if (c) {
+ c->loc = c_shot->Location();
+ c->d_pas = (float) d_pas;
+ c->d_act = (float) d_act;
+
+ c->UpdateTrack();
+ }
+ }
+ }
+}
+
+Contact*
+Sensor::FindContact(Ship* s)
+{
+ ListIter<Contact> iter(contacts);
+ while (++iter) {
+ Contact* c = iter.value();
+ if (c->GetShip() == s)
+ return c;
+ }
+
+ return 0;
+}
+
+Contact*
+Sensor::FindContact(Shot* s)
+{
+ ListIter<Contact> iter(contacts);
+ while (++iter) {
+ Contact* c = iter.value();
+ if (c->GetShot() == s)
+ return c;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Sensor::IsTracking(SimObject* tgt)
+{
+ if (tgt && mode != GM && mode != PAS && mode != PST && IsPowerOn()) {
+ if (tgt == target)
+ return true;
+
+ Contact* c = 0;
+
+ if (tgt->Type() == SimObject::SIM_SHIP) {
+ c = FindContact((Ship*) tgt);
+ }
+ else {
+ c = FindContact((Shot*) tgt);
+ }
+
+ return (c != 0 && c->ActReturn() > SENSOR_THRESHOLD && !c->IsProbed());
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+const double sensor_lock_threshold = 0.5;
+
+struct TargetOffset {
+ static const char* TYPENAME() { return "TargetOffset"; }
+
+ SimObject* target;
+ double offset;
+
+ TargetOffset() : target(0), offset(10) { }
+ TargetOffset(SimObject* t, double o) : target(t), offset(o) { }
+ int operator< (const TargetOffset& o) const { return offset < o.offset; }
+ int operator<=(const TargetOffset& o) const { return offset <= o.offset; }
+ int operator==(const TargetOffset& o) const { return offset == o.offset; }
+};
+
+SimObject*
+Sensor::LockTarget(int type, bool closest, bool hostile)
+{
+ if (!ship || ship->GetEMCON() < 3) {
+ Ignore(target);
+ target = 0;
+ return target;
+ }
+
+ SimObject* test = 0;
+ ListIter<Contact> contact(ship->ContactList());
+
+ List<TargetOffset> targets;
+
+ while (++contact) {
+ if (type == SimObject::SIM_SHIP)
+ test = contact->GetShip();
+ else
+ test = contact->GetShot();
+
+ if (!test)
+ continue;
+
+ // do not target own missiles:
+ if (contact->GetShot() && contact->GetShot()->Owner() == ship)
+ continue;
+
+ double tgt_range = contact->Range(ship);
+
+ // do not target ships outside of detet range:
+ if (contact->GetShip() && contact->GetShip()->Design()->detet < tgt_range)
+ continue;
+
+ double d_pas = contact->PasReturn();
+ double d_act = contact->ActReturn();
+ bool vis = contact->Visible(ship) || contact->Threat(ship);
+
+ if (!vis && d_pas < sensor_lock_threshold && d_act < sensor_lock_threshold)
+ continue;
+
+ if (closest) {
+ if (hostile && (contact->GetIFF(ship) == 0 || contact->GetIFF(ship) == ship->GetIFF()))
+ continue;
+
+ targets.append(new(__FILE__,__LINE__) TargetOffset(test, tgt_range));
+ }
+
+ // clip:
+ else if (contact->InFront(ship)) {
+ double az, el, rng;
+
+ contact->GetBearing(ship, az, el, rng);
+ az = fabs(az / PI);
+ el = fabs(el / PI);
+
+ if (az <= 0.2 && el <= 0.2)
+ targets.append(new(__FILE__,__LINE__) TargetOffset(test, az+el));
+ }
+ }
+
+ targets.sort();
+
+ if (target) {
+ int index = 100000;
+ int i = 0;
+
+ if (targets.size() > 0) {
+ ListIter<TargetOffset> iter(targets);
+ while (++iter) {
+ if (iter->target == target) {
+ index = i;
+ break;
+ }
+
+ i++;
+ }
+
+ if (index < targets.size()-1)
+ index++;
+ else
+ index = 0;
+
+ target = targets[index]->target;
+ Observe(target);
+ }
+ }
+ else if (targets.size() > 0) {
+ target = targets[0]->target;
+ Observe(target);
+ }
+ else {
+ target = 0;
+ }
+
+ targets.destroy();
+
+ if (target && mode < STD)
+ mode = STD;
+
+ return target;
+}
+
+// +--------------------------------------------------------------------+
+
+SimObject*
+Sensor::LockTarget(SimObject* candidate)
+{
+ Ignore(target);
+ target = 0;
+
+ if (ship->GetEMCON() < 3)
+ return target;
+
+ if (!candidate)
+ return target;
+
+ int type = candidate->Type();
+ SimObject* test = 0;
+ ListIter<Contact> contact(ship->ContactList());
+
+ while (++contact) {
+ if (type == SimObject::SIM_SHIP)
+ test = contact->GetShip();
+ else
+ test = contact->GetShot();
+
+ if (test == candidate) {
+ double d_pas = contact->PasReturn();
+ double d_act = contact->ActReturn();
+ bool vis = contact->Visible(ship) || contact->Threat(ship);
+
+ if (vis || d_pas > sensor_lock_threshold || d_act > sensor_lock_threshold) {
+ target = test;
+ Observe(target);
+ }
+
+ break;
+ }
+ }
+
+ if (target && mode < STD)
+ mode = STD;
+
+ return target;
+}
+
+// +--------------------------------------------------------------------+
+
+SimObject*
+Sensor::AcquirePassiveTargetForMissile()
+{
+ SimObject* pick = 0;
+ double min_off = 2;
+
+ ListIter<Contact> contact(ship->ContactList());
+
+ while (++contact) {
+ SimObject* test = contact->GetShip();
+ double d = contact->PasReturn();
+
+ if (d < 1) continue;
+
+ // clip:
+ if (contact->InFront(ship)) {
+ double az, el, rng;
+
+ contact->GetBearing(ship, az, el, rng);
+ az = fabs(az / PI);
+ el = fabs(el / PI);
+
+ if (az + el < min_off) {
+ min_off = az + el;
+ pick = test;
+ }
+ }
+ }
+
+ return pick;
+}
+
+// +--------------------------------------------------------------------+
+
+SimObject*
+Sensor::AcquireActiveTargetForMissile()
+{
+ SimObject* pick = 0;
+ double min_off = 2;
+
+ ListIter<Contact> contact(ship->ContactList());
+
+ while (++contact) {
+ SimObject* test = contact->GetShip();
+ double d = contact->ActReturn();
+
+ if (d < 1) continue;
+
+ if (contact->InFront(ship)) {
+ double az, el, rng;
+
+ contact->GetBearing(ship, az, el, rng);
+ az = fabs(az / PI);
+ el = fabs(el / PI);
+
+ if (az + el < min_off) {
+ min_off = az + el;
+ pick = test;
+ }
+ }
+ }
+
+ return pick;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sensor::DoEMCON(int index)
+{
+ int e = GetEMCONPower(index);
+
+ if (power_level * 100 > e || emcon != index) {
+ if (e == 0) {
+ PowerOff();
+ }
+ else if (emcon != index) {
+ PowerOn();
+
+ if (power_level * 100 > e) {
+ SetPowerLevel(e);
+ }
+
+ if (emcon == 3) {
+ if (GetMode() < PST)
+ SetMode(STD);
+ else
+ SetMode(CST);
+ }
+ else {
+ int m = GetMode();
+ if (m < PST && m > PAS)
+ SetMode(Sensor::PAS);
+ else if (m == CST)
+ SetMode(PST);
+ }
+ }
+ }
+
+ emcon = index;
+}
+
diff --git a/Stars45/Sensor.h b/Stars45/Sensor.h
new file mode 100644
index 0000000..5bb4dc0
--- /dev/null
+++ b/Stars45/Sensor.h
@@ -0,0 +1,86 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Sensor.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Integrated (Passive and Active) Sensor Package class
+*/
+
+#ifndef Sensor_h
+#define Sensor_h
+
+#include "Types.h"
+#include "SimObject.h"
+#include "System.h"
+#include "Geometry.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Shot;
+class Contact;
+
+// +--------------------------------------------------------------------+
+
+class Sensor : public System, public SimObserver
+{
+public:
+ enum Mode {
+ PAS, STD, ACM, GM, // fighter modes
+ PST, CST // starship modes
+ };
+
+ Sensor();
+ Sensor(const Sensor& rhs);
+ virtual ~Sensor();
+
+ virtual void ExecFrame(double seconds);
+ virtual SimObject* LockTarget(int type=SimObject::SIM_SHIP,
+ bool closest=false,
+ bool hostile=false);
+ virtual SimObject* LockTarget(SimObject* candidate);
+ virtual bool IsTracking(SimObject* tgt);
+ virtual void DoEMCON(int emcon);
+
+ virtual void ClearAllContacts();
+
+ virtual Mode GetMode() const { return mode; }
+ virtual void SetMode(Mode m);
+ virtual double GetBeamLimit() const;
+ virtual double GetBeamRange() const;
+ virtual void IncreaseRange();
+ virtual void DecreaseRange();
+ virtual void AddRange(double r);
+
+ Contact* FindContact(Ship* s);
+ Contact* FindContact(Shot* s);
+
+ // borrow this sensor for missile seeker
+ SimObject* AcquirePassiveTargetForMissile();
+ SimObject* AcquireActiveTargetForMissile();
+
+ // SimObserver:
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+protected:
+ void ProcessContact(Ship* contact, double az1, double az2);
+ void ProcessContact(Shot* contact, double az1, double az2);
+
+ Mode mode;
+ int nsettings;
+ int range_index;
+ float range_settings[8];
+ SimObject* target;
+
+ List<Contact> contacts;
+};
+
+#endif Sensor_h
+
diff --git a/Stars45/Shield.cpp b/Stars45/Shield.cpp
new file mode 100644
index 0000000..58d3776
--- /dev/null
+++ b/Stars45/Shield.cpp
@@ -0,0 +1,259 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Shield.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Weapon class
+*/
+
+#include "MemDebug.h"
+#include "Shield.h"
+#include "Shot.h"
+#include "WeaponDesign.h"
+
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+static char* shield_name[] = {
+ "sys.shield.none",
+ "sys.shield.deflector",
+ "sys.shield.grav",
+ "sys.shield.hyper"
+};
+
+static int shield_value[] = {
+ 0, 2, 2, 3
+};
+
+// +----------------------------------------------------------------------+
+
+Shield::Shield(SUBTYPE shield_type)
+ : System(SHIELD, shield_type, "shield", shield_value[shield_type], 100, 0),
+ shield_cutoff(0.0f), shield_capacitor(false), shield_bubble(false),
+ deflection_cost(1.0f), shield_curve(0.05f)
+{
+ name = Game::GetText(shield_name[shield_type]);
+ abrv = Game::GetText("sys.shield.abrv");
+
+ power_flags = POWER_WATTS | POWER_CRITICAL;
+ energy = 0.0f;
+ power_level = 0.0f;
+ shield_level = 0.0f;
+
+ switch (shield_type) {
+ default:
+ case DEFLECTOR:
+ capacity = sink_rate = 2.0e3f;
+ shield_factor = 0.05f;
+ break;
+
+ case GRAV_SHIELD:
+ capacity = sink_rate = 7.0e3f;
+ shield_factor = 0.01f;
+ break;
+
+ case HYPER_SHIELD:
+ capacity = sink_rate = 10.0e3f;
+ shield_factor = 0.003f;
+ break;
+ }
+
+ emcon_power[0] = 0;
+ emcon_power[1] = 0;
+ emcon_power[2] = 100;
+}
+
+// +----------------------------------------------------------------------+
+
+Shield::Shield(const Shield& s)
+ : System(s), shield_factor(s.shield_factor), requested_power_level(0.0f),
+ shield_cutoff(s.shield_cutoff), shield_capacitor(s.shield_capacitor),
+ shield_bubble(s.shield_bubble), deflection_cost(s.deflection_cost),
+ shield_curve(s.shield_curve)
+{
+ power_flags = s.power_flags;
+ energy = 0.0f;
+ power_level = 0.0f;
+ shield_level = 0.0f;
+
+ Mount(s);
+}
+
+// +--------------------------------------------------------------------+
+
+Shield::~Shield()
+{ }
+
+void
+Shield::SetShieldCapacitor(bool c)
+{
+ shield_capacitor = c;
+
+ if (shield_capacitor) {
+ power_flags = POWER_CRITICAL;
+ shield_curve = 0.05f;
+ }
+ else {
+ power_flags = POWER_WATTS | POWER_CRITICAL;
+ shield_curve = 0.25f;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Shield::ExecFrame(double seconds)
+{
+ System::ExecFrame(seconds);
+
+ if (power_level < requested_power_level) {
+ power_level += (float) (seconds * 0.10); // ten seconds to charge up
+
+ if (power_level > requested_power_level)
+ power_level = (float) requested_power_level;
+ }
+ else if (power_level > requested_power_level) {
+ power_level -= (float) (seconds * 0.20); // five seconds to power down
+
+ if (power_level < requested_power_level)
+ power_level = (float) requested_power_level;
+ }
+
+ if (power_level < 0.01 && !shield_capacitor) {
+ shield_level = 0.0f;
+ energy = 0.0f;
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Shield::Distribute(double delivered_energy, double seconds)
+{
+ System::Distribute(delivered_energy, seconds);
+
+ if (shield_capacitor) {
+ if (shield_cutoff > 0 && shield_cutoff < 0.999) {
+ float cutoff = shield_cutoff * capacity;
+
+ if (energy > cutoff)
+ shield_level = (energy-cutoff)/(capacity-cutoff);
+ else
+ shield_level = 0.0f;
+ }
+
+ else {
+ shield_level = energy/capacity;
+ }
+ }
+ else {
+ shield_level = energy/sink_rate;
+ energy = 0.0f;
+ }
+
+ if (shield_level < 0)
+ shield_level = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Shield::DeflectDamage(Shot* shot, double damage)
+{
+ double filter = 1;
+ double penetration = 5;
+ double leak = 0;
+
+ if (shot)
+ penetration = shot->Design()->penetration;
+
+ filter = 1 - shield_factor * penetration;
+
+ if (filter < 0)
+ filter = 0;
+
+ else if (filter > 1)
+ filter = 1;
+
+ if (shield_capacitor) {
+ if (shield_cutoff > 0 && shield_level < 1e-6) {
+ leak = damage;
+ energy -= (float) (damage * deflection_cost);
+ }
+
+ else {
+ leak = damage * (1 - pow(shield_level, shield_curve) * filter * availability);
+
+ double deflected = damage - leak;
+ energy -= (float) deflected * deflection_cost;
+ }
+
+ }
+ else {
+ leak = damage * (1 - pow(shield_level, shield_curve) * filter * availability);
+ }
+
+ return leak;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Shield::SetPowerLevel(double level)
+{
+ if (level > 100)
+ level = 100;
+ else if (level < 0)
+ level = 0;
+
+ level /= 100;
+
+ if (requested_power_level != level) {
+ // if the system is on emergency override power,
+ // do not let the EMCON system use this method
+ // to drop it back to normal power:
+ if (power_level > 1 && level == 1) {
+ requested_power_level = (float) power_level;
+ return;
+ }
+
+ requested_power_level = (float) level;
+ }
+}
+
+void
+Shield::SetNetShieldLevel(int level)
+{
+ if (level > 100) level = 100;
+ else if (level < 0) level = 0;
+
+ requested_power_level = (float) (level/100.0);
+ power_level = requested_power_level;
+}
+
+void
+Shield::DoEMCON(int index)
+{
+ int e = GetEMCONPower(index);
+
+ if (power_level * 100 > e || emcon != index) {
+ if (e == 0) {
+ PowerOff();
+ }
+ else if (emcon != index) {
+ PowerOn();
+
+ if (power_level * 100 > e)
+ SetPowerLevel(e);
+ }
+ }
+
+ emcon = index;
+}
diff --git a/Stars45/Shield.h b/Stars45/Shield.h
new file mode 100644
index 0000000..0fa16c3
--- /dev/null
+++ b/Stars45/Shield.h
@@ -0,0 +1,77 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Shield.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Conventional Shield (system) class
+*/
+
+#ifndef Shield_h
+#define Shield_h
+
+#include "Types.h"
+#include "System.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class Shot;
+class Sound;
+
+// +--------------------------------------------------------------------+
+
+class Shield : public System
+{
+public:
+ enum SUBTYPE { DEFLECTOR = 1, GRAV_SHIELD, HYPER_SHIELD };
+
+ Shield(SUBTYPE s);
+ Shield(const Shield& rhs);
+ virtual ~Shield();
+
+ virtual void ExecFrame(double seconds);
+ double DeflectDamage(Shot* shot, double shot_damage);
+
+ double ShieldLevel() const { return shield_level * 100; }
+ double ShieldFactor() const { return shield_factor; }
+ double ShieldCurve() const { return shield_curve; }
+ void SetShieldFactor(double f) { shield_factor = (float) f; }
+ void SetShieldCurve(double c) { shield_curve = (float) c; }
+ double ShieldCutoff() const { return shield_cutoff; }
+ void SetShieldCutoff(double f) { shield_cutoff = (float) f; }
+ double Capacity() const { return capacity; }
+ double Consumption() const { return sink_rate; }
+ void SetConsumption(double r) { sink_rate = (float)r; }
+ bool ShieldCapacitor() const { return shield_capacitor; }
+ void SetShieldCapacitor(bool c);
+ bool ShieldBubble() const { return shield_bubble; }
+ void SetShieldBubble(bool b) { shield_bubble = b; }
+ double DeflectionCost() const { return deflection_cost; }
+ void SetDeflectionCost(double c) { deflection_cost = (float) c; }
+
+ // override from System:
+ virtual void SetPowerLevel(double level);
+ virtual void SetNetShieldLevel(int level);
+
+ virtual void Distribute(double delivered_energy, double seconds);
+ virtual void DoEMCON(int emcon);
+
+protected:
+ bool shield_capacitor;
+ bool shield_bubble;
+ float shield_factor;
+ float shield_level;
+ float shield_curve;
+ float shield_cutoff;
+ float requested_power_level;
+ float deflection_cost;
+};
+
+#endif Shield_h
+
diff --git a/Stars45/ShieldRep.cpp b/Stars45/ShieldRep.cpp
new file mode 100644
index 0000000..31ef1f4
--- /dev/null
+++ b/Stars45/ShieldRep.cpp
@@ -0,0 +1,250 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ShieldRep.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ShieldRep Solid class
+*/
+
+#include "MemDebug.h"
+#include "ShieldRep.h"
+#include "Random.h"
+
+#include "Game.h"
+#include "Light.h"
+#include "Solid.h"
+#include "Bitmap.h"
+#include "Color.h"
+#include "DataLoader.h"
+
+// +--------------------------------------------------------------------+
+
+const int MAX_SHIELD_HITS = 16;
+
+// +--------------------------------------------------------------------+
+
+struct ShieldHit
+{
+ Vec3 hitloc;
+ double damage;
+ double age;
+ Shot* shot;
+
+ ShieldHit() : damage(0), age(0), shot(0) { }
+};
+
+// +--------------------------------------------------------------------+
+
+ShieldRep::ShieldRep()
+{
+ bubble = false;
+ luminous = true;
+ trans = true;
+ nhits = 0;
+
+ hits = new(__FILE__,__LINE__) ShieldHit[MAX_SHIELD_HITS];
+}
+
+ShieldRep::~ShieldRep()
+{
+ delete [] hits;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShieldRep::Hit(Vec3 impact, Shot* shot, double damage)
+{
+ if (!model || model->GetSurfaces().size() < 1)
+ return;
+
+ // transform impact into object space:
+ Matrix xform(Orientation());
+
+ Vec3 tmp = impact - loc;
+
+ impact.x = tmp * Vec3(xform(0,0), xform(0,1), xform(0,2));
+ impact.y = tmp * Vec3(xform(1,0), xform(1,1), xform(1,2));
+ impact.z = tmp * Vec3(xform(2,0), xform(2,1), xform(2,2));
+
+ // find slot to store the hit:
+ int i;
+ int slot = -1;
+ double age = -1;
+
+ for (i = 0; i < MAX_SHIELD_HITS; i++) {
+ if (hits[i].shot == shot) {
+ slot = i;
+ break;
+ }
+ }
+
+ if (slot < 0) {
+ for (i = 0; i < MAX_SHIELD_HITS; i++) {
+ if (hits[i].damage <= 0) {
+ slot = i;
+ break;
+ }
+
+ if (hits[i].age > age) {
+ slot = i;
+ age = hits[i].age;
+ }
+ }
+ }
+
+ if (slot >= 0 && slot < MAX_SHIELD_HITS) {
+ // record the hit in the slot:
+ hits[slot].hitloc = impact;
+ hits[slot].damage = damage;
+ hits[slot].age = 1;
+ hits[slot].shot = shot;
+
+ if (nhits < MAX_SHIELD_HITS)
+ nhits++;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShieldRep::Energize(double seconds, bool b)
+{
+ bubble = b;
+
+ if (nhits < 1) return;
+
+ nhits = 0;
+
+ for (int i = 0; i < MAX_SHIELD_HITS; i++) {
+ if (hits[i].damage > 0) {
+ // age the hit:
+ hits[i].age += seconds;
+ hits[i].damage -= (hits[i].damage * 4 * seconds);
+
+ // collect garbage:
+ if (hits[i].damage < 10) {
+ hits[i].age = 0;
+ hits[i].damage = 0;
+ hits[i].shot = 0;
+ }
+ else {
+ nhits++;
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShieldRep::TranslateBy(const Point& ref)
+{
+ true_eye_point = ref;
+ Solid::TranslateBy(ref);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShieldRep::Illuminate()
+{
+ if (!model) return;
+
+ Surface* surf = model->GetSurfaces().first();
+ VertexSet* vset = surf->GetVertexSet();
+ int nverts = vset->nverts;
+
+ for (int i = 0; i < nverts; i++) {
+ vset->diffuse[i] = 0;
+ vset->specular[i] = 0;
+ }
+
+ double all_damage = 0;
+
+ if (nhits < 1) return;
+
+ for (i = 0; i < MAX_SHIELD_HITS; i++) {
+ if (hits[i].damage > 0) {
+ // add the hit's contribution to the shield verts:
+ Vec3 hitloc = hits[i].hitloc;
+ double hitdam = hits[i].damage * 2000;
+
+ all_damage += hits[i].damage;
+
+ if (!bubble) {
+
+ double limit = radius * radius;
+ if (hitdam > limit)
+ hitdam = limit;
+
+ for (int v = 0; v < nverts; v++) {
+ double dist = (vset->loc[v] - hitloc).length();
+
+ if (dist < 1)
+ dist = 1; // can't divide by zero!
+
+ else
+ dist = pow(dist, 2.7);
+
+ double pert = Random(0.1, 1.5);
+ double intensity = pert*hitdam/dist;
+
+ if (intensity > 0.003)
+ vset->diffuse[v] = ((Color::White * intensity) + vset->diffuse[v]).Value();
+ }
+
+ }
+ }
+ }
+
+ if (bubble) {
+ double shield_gain = 1;
+
+ if (all_damage < 1000) {
+ shield_gain = all_damage / 1000;
+ }
+
+ for (int i = 0; i < nverts; i++) {
+ Vec3 vloc = (vset->loc[i] * orientation) + loc;
+ Vec3 vnrm = (vset->nrm[i] * orientation);
+
+ Vec3 V = vloc * -1.0f;
+ V.Normalize();
+
+ double intensity = 1 - V*vnrm;
+
+ if (intensity > 0) {
+ intensity *= intensity;
+
+ if (intensity > 1) intensity = 1;
+
+ intensity *= (shield_gain * Random(0.75, 1.0));
+
+ Color vs = Color::White * intensity;
+ vset->diffuse[i] = vs.Value();
+ }
+ }
+ }
+
+ InvalidateSurfaceData();
+}
+
+void
+ShieldRep::Render(Video* video, DWORD flags)
+{
+ if ((flags & RENDER_ADDITIVE) == 0)
+ return;
+
+ if (nhits > 0) {
+ Illuminate();
+ Solid::Render(video, RENDER_ALPHA); // have to lie about the render flag
+ // or the engine will reject the solid
+ }
+} \ No newline at end of file
diff --git a/Stars45/ShieldRep.h b/Stars45/ShieldRep.h
new file mode 100644
index 0000000..07c2bbd
--- /dev/null
+++ b/Stars45/ShieldRep.h
@@ -0,0 +1,48 @@
+/* Project STARSHATTER
+ John DiCamillo
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ShieldRep.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ShieldRep Solid class
+*/
+
+#ifndef ShieldRep_h
+#define ShieldRep_h
+
+#include "Types.h"
+#include "Solid.h"
+
+// +--------------------------------------------------------------------+
+
+struct ShieldHit;
+class Shot;
+
+class ShieldRep : public Solid
+{
+public:
+ ShieldRep();
+ virtual ~ShieldRep();
+
+ // operations
+ virtual void Render(Video* video, DWORD flags);
+ virtual void Energize(double seconds, bool bubble=false);
+ int ActiveHits() const { return nhits; }
+ virtual void Hit(Vec3 impact, Shot* shot, double damage=0);
+ virtual void TranslateBy(const Point& ref);
+ virtual void Illuminate();
+
+protected:
+ int nhits;
+ ShieldHit* hits;
+ Point true_eye_point;
+ bool bubble;
+};
+
+#endif ShieldRep_h
+
diff --git a/Stars45/Ship.cpp b/Stars45/Ship.cpp
new file mode 100644
index 0000000..5753b00
--- /dev/null
+++ b/Stars45/Ship.cpp
@@ -0,0 +1,5305 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Ship.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Starship class
+*/
+
+#include "MemDebug.h"
+#include "Ship.h"
+#include "ShipAI.h"
+#include "ShipCtrl.h"
+#include "ShipDesign.h"
+#include "ShipKiller.h"
+#include "Shot.h"
+#include "Drone.h"
+#include "SeekerAI.h"
+#include "HardPoint.h"
+#include "Weapon.h"
+#include "WeaponGroup.h"
+#include "Shield.h"
+#include "ShieldRep.h"
+#include "Computer.h"
+#include "FlightComp.h"
+#include "Drive.h"
+#include "QuantumDrive.h"
+#include "Farcaster.h"
+#include "Thruster.h"
+#include "Power.h"
+#include "FlightDeck.h"
+#include "LandingGear.h"
+#include "Hangar.h."
+#include "Sensor.h"
+#include "Contact.h"
+#include "CombatUnit.h"
+#include "Element.h"
+#include "Instruction.h"
+#include "RadioMessage.h"
+#include "RadioHandler.h"
+#include "RadioTraffic.h"
+#include "NavLight.h"
+#include "NavSystem.h"
+#include "NavAI.h"
+#include "DropShipAI.h"
+#include "Explosion.h"
+#include "MissionEvent.h"
+#include "ShipSolid.h"
+#include "Sim.h"
+#include "SimEvent.h"
+#include "StarSystem.h"
+#include "TerrainRegion.h"
+#include "Terrain.h"
+#include "System.h"
+#include "Component.h"
+#include "KeyMap.h"
+#include "RadioView.h"
+#include "AudioConfig.h"
+#include "CameraDirector.h"
+#include "HUDView.h"
+#include "Random.h"
+#include "RadioVox.h"
+
+#include "NetGame.h"
+#include "NetUtil.h"
+
+#include "MotionController.h"
+#include "Keyboard.h"
+#include "Joystick.h"
+#include "Bolt.h"
+#include "Game.h"
+#include "Solid.h"
+#include "Shadow.h"
+#include "Skin.h"
+#include "Sprite.h"
+#include "Light.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "Sound.h"
+#include "DataLoader.h"
+
+#include "Parser.h"
+#include "Reader.h"
+
+// +----------------------------------------------------------------------+
+
+static int base_contact_id = 0;
+static double range_min = 0;
+static double range_max = 250e3;
+
+int Ship::control_model = 0; // standard
+int Ship::flight_model = 0; // standard
+int Ship::landing_model = 0; // standard
+double Ship::friendly_fire_level = 1; // 100%
+
+const int HIT_NOTHING = 0;
+const int HIT_HULL = 1;
+const int HIT_SHIELD = 2;
+const int HIT_BOTH = 3;
+const int HIT_TURRET = 4;
+
+// +----------------------------------------------------------------------+
+
+Ship::Ship(const char* ship_name, const char* reg_num, ShipDesign* ship_dsn, int IFF, int cmd_ai, const int* load)
+ : IFF_code(IFF), killer(0), throttle(0), augmenter(false), throttle_request(0),
+ shield(0), shieldRep(0), main_drive(0), quantum_drive(0), farcaster(0),
+ check_fire(false), probe(0), sensor_drone(0), primary(0), secondary(1),
+ cmd_chain_index(0), target(0), subtarget(0), radio_orders(0), launch_point(0),
+ g_force(0.0f), sensor(0), navsys(0), flcs(0), hangar(0), respawns(0), invulnerable(false),
+ thruster(0), decoy(0), ai_mode(2), command_ai_level(cmd_ai), flcs_mode(FLCS_AUTO), loadout(0),
+ emcon(3), old_emcon(3), master_caution(false), cockpit(0), gear(0), skin(0),
+ auto_repair(true), last_repair_time(0), last_eval_time(0), last_beam_time(0), last_bolt_time(0),
+ warp_fov(1), flight_phase(LAUNCH), launch_time(0), carrier(0), dock(0), ff_count(0),
+ inbound(0), element(0), director_info("Init"), combat_unit(0), net_control(0),
+ track(0), ntrack(0), track_time(0), helm_heading(0.0f), helm_pitch(0.0f),
+ altitude_agl(-1.0e6f), transition_time(0.0f), transition_type(TRANSITION_NONE),
+ friendly_fire_time(0), ward(0), net_observer_mode(false), orig_elem_index(-1)
+{
+ sim = Sim::GetSim();
+
+ strcpy(name, ship_name);
+ if (reg_num && *reg_num)
+ strcpy(regnum, reg_num);
+ else regnum[0] = 0;
+
+ design = ship_dsn;
+
+ if (!design) {
+ char msg[256];
+ sprintf(msg, "No ship design found for '%s'\n", ship_name);
+ Game::Panic(msg);
+ }
+
+ obj_type = SimObject::SIM_SHIP;
+
+ radius = design->radius;
+ mass = design->mass;
+ integrity = design->integrity;
+ vlimit = design->vlimit;
+
+ agility = design->agility;
+ wep_mass = 0.0f;
+ wep_resist = 0.0f;
+
+ CL = design->CL;
+ CD = design->CD;
+ stall = design->stall;
+
+ chase_vec = design->chase_vec;
+ bridge_vec = design->bridge_vec;
+
+ acs = design->acs;
+ pcs = design->acs;
+
+ auto_repair = design->repair_auto;
+
+ while (!base_contact_id)
+ base_contact_id = rand() % 1000;
+
+ contact_id = base_contact_id++;
+ int sys_id = 0;
+
+ for (int i = 0; i < design->reactors.size(); i++) {
+ PowerSource* reactor = new(__FILE__,__LINE__) PowerSource(*design->reactors[i]);
+ reactor->SetShip(this);
+ reactor->SetID(sys_id++);
+ reactors.append(reactor);
+ systems.append(reactor);
+ }
+
+ for (i = 0; i < design->drives.size(); i++) {
+ Drive* drive = new(__FILE__,__LINE__) Drive(*design->drives[i]);
+ drive->SetShip(this);
+ drive->SetID(sys_id++);
+
+ int src_index = drive->GetSourceIndex();
+ if (src_index >= 0 && src_index < reactors.size())
+ reactors[src_index]->AddClient(drive);
+
+ drives.append(drive);
+ systems.append(drive);
+ }
+
+ if (design->quantum_drive) {
+ quantum_drive = new(__FILE__,__LINE__) QuantumDrive(*design->quantum_drive);
+ quantum_drive->SetShip(this);
+ quantum_drive->SetID(sys_id++);
+
+ int src_index = quantum_drive->GetSourceIndex();
+ if (src_index >= 0 && src_index < reactors.size())
+ reactors[src_index]->AddClient(quantum_drive);
+
+ quantum_drive->SetShip(this);
+ systems.append(quantum_drive);
+ }
+
+ if (design->farcaster) {
+ farcaster = new(__FILE__,__LINE__) Farcaster(*design->farcaster);
+ farcaster->SetShip(this);
+ farcaster->SetID(sys_id++);
+
+ int src_index = farcaster->GetSourceIndex();
+ if (src_index >= 0 && src_index < reactors.size())
+ reactors[src_index]->AddClient(farcaster);
+
+ farcaster->SetShip(this);
+ systems.append(farcaster);
+ }
+
+ if (design->thruster) {
+ thruster = new(__FILE__,__LINE__) Thruster(*design->thruster);
+ thruster->SetShip(this);
+ thruster->SetID(sys_id++);
+
+ int src_index = thruster->GetSourceIndex();
+ if (src_index >= 0 && src_index < reactors.size())
+ reactors[src_index]->AddClient(thruster);
+
+ thruster->SetShip(this);
+ systems.append(thruster);
+ }
+
+ if (design->shield) {
+ shield = new(__FILE__,__LINE__) Shield(*design->shield);
+ shield->SetShip(this);
+ shield->SetID(sys_id++);
+
+ int src_index = shield->GetSourceIndex();
+ if (src_index >= 0 && src_index < reactors.size())
+ reactors[src_index]->AddClient(shield);
+
+ if (design->shield_model) {
+ shieldRep = new(__FILE__,__LINE__) ShieldRep;
+ shieldRep->UseModel(design->shield_model);
+ }
+
+ systems.append(shield);
+ }
+
+ for (i = 0; i < design->flight_decks.size(); i++) {
+ FlightDeck* deck = new(__FILE__,__LINE__) FlightDeck(*design->flight_decks[i]);
+ deck->SetShip(this);
+ deck->SetCarrier(this);
+ deck->SetID(sys_id++);
+ deck->SetIndex(i);
+
+ int src_index = deck->GetSourceIndex();
+ if (src_index >= 0 && src_index < reactors.size())
+ reactors[src_index]->AddClient(deck);
+
+ flight_decks.append(deck);
+ systems.append(deck);
+ }
+
+ if (design->flight_decks.size() > 0) {
+ if (!hangar) {
+ hangar = new(__FILE__,__LINE__) Hangar;
+ hangar->SetShip(this);
+ }
+ }
+
+ if (design->squadrons.size() > 0) {
+ if (!hangar) {
+ hangar = new(__FILE__,__LINE__) Hangar;
+ hangar->SetShip(this);
+ }
+
+ for (i = 0; i < design->squadrons.size(); i++) {
+ ShipSquadron* s = design->squadrons[i];
+ hangar->CreateSquadron(s->name, 0, s->design, s->count, GetIFF(), 0, 0, s->avail);
+ }
+ }
+
+ if (design->gear) {
+ gear = new(__FILE__,__LINE__) LandingGear(*design->gear);
+ gear->SetShip(this);
+ gear->SetID(sys_id++);
+
+ int src_index = gear->GetSourceIndex();
+ if (src_index >= 0 && src_index < reactors.size())
+ reactors[src_index]->AddClient(gear);
+
+ systems.append(gear);
+ }
+
+ if (design->sensor) {
+ sensor = new(__FILE__,__LINE__) Sensor(*design->sensor);
+ sensor->SetShip(this);
+ sensor->SetID(sys_id++);
+
+ int src_index = sensor->GetSourceIndex();
+ if (src_index >= 0 && src_index < reactors.size())
+ reactors[src_index]->AddClient(sensor);
+
+ if (IsStarship() || IsStatic() || !strncmp(design->name, "Camera", 6))
+ sensor->SetMode(Sensor::CST);
+
+ systems.append(sensor);
+ }
+
+ int wep_index = 1;
+
+ for (i = 0; i < design->weapons.size(); i++) {
+ Weapon* gun = new(__FILE__,__LINE__) Weapon(*design->weapons[i]);
+ gun->SetID(sys_id++);
+ gun->SetOwner(this);
+ gun->SetIndex(wep_index++);
+
+ int src_index = gun->GetSourceIndex();
+ if (src_index >= 0 && src_index < reactors.size())
+ reactors[src_index]->AddClient(gun);
+
+ WeaponGroup* group = FindWeaponGroup(gun->Group());
+ group->AddWeapon(gun);
+ group->SetAbbreviation(gun->Abbreviation());
+
+ systems.append(gun);
+
+ if (IsDropship() && gun->GetTurret())
+ gun->SetFiringOrders(Weapon::POINT_DEFENSE);
+ else
+ gun->SetFiringOrders(Weapon::MANUAL);
+ }
+
+ int loadout_size = design->hard_points.size();
+
+ if (load && loadout_size > 0) {
+ loadout = new(__FILE__,__LINE__) int[loadout_size];
+
+ for (i = 0; i < loadout_size; i++) {
+ int mounted_weapon = loadout[i] = load[i];
+
+ if (mounted_weapon < 0)
+ continue;
+
+ Weapon* missile = design->hard_points[i]->CreateWeapon(mounted_weapon);
+
+ if (missile) {
+ missile->SetID(sys_id++);
+ missile->SetOwner(this);
+ missile->SetIndex(wep_index++);
+
+ WeaponGroup* group = FindWeaponGroup(missile->Group());
+ group->AddWeapon(missile);
+ group->SetAbbreviation(missile->Abbreviation());
+
+ systems.append(missile);
+ }
+ }
+ }
+
+ if (weapons.size() > 1) {
+ primary = -1;
+ secondary = -1;
+
+ for (i = 0; i < weapons.size(); i++) {
+ WeaponGroup* group = weapons[i];
+ if (group->IsPrimary() && primary < 0) {
+ primary = i;
+
+ // turrets on fighters are set to point defense by default,
+ // this forces the primary turret back to manual control
+ group->SetFiringOrders(Weapon::MANUAL);
+ }
+
+ else if (group->IsMissile() && secondary < 0) {
+ secondary = i;
+ }
+ }
+
+ if (primary < 0) primary = 0;
+ if (secondary < 0) secondary = 1;
+
+ if (weapons.size() > 4) {
+ ::Print("WARNING: Ship '%s' type '%s' has %d wep groups (max=4)\n",
+ Name(), DesignName(), weapons.size());
+ }
+ }
+
+ if (design->decoy) {
+ decoy = new(__FILE__,__LINE__) Weapon(*design->decoy);
+ decoy->SetOwner(this);
+ decoy->SetID(sys_id++);
+ decoy->SetIndex(wep_index++);
+
+ int src_index = decoy->GetSourceIndex();
+ if (src_index >= 0 && src_index < reactors.size())
+ reactors[src_index]->AddClient(decoy);
+
+ systems.append(decoy);
+ }
+
+ for (i = 0; i < design->navlights.size(); i++) {
+ NavLight* navlight = new(__FILE__,__LINE__) NavLight(*design->navlights[i]);
+ navlight->SetShip(this);
+ navlight->SetID(sys_id++);
+ navlight->SetOffset(((DWORD) this) << 2);
+ navlights.append(navlight);
+ systems.append(navlight);
+ }
+
+ if (design->navsys) {
+ navsys = new(__FILE__,__LINE__) NavSystem(*design->navsys);
+ navsys->SetShip(this);
+ navsys->SetID(sys_id++);
+
+ int src_index = navsys->GetSourceIndex();
+ if (src_index >= 0 && src_index < reactors.size())
+ reactors[src_index]->AddClient(navsys);
+
+ systems.append(navsys);
+ }
+
+ if (design->probe) {
+ probe = new(__FILE__,__LINE__) Weapon(*design->probe);
+ probe->SetOwner(this);
+ probe->SetID(sys_id++);
+ probe->SetIndex(wep_index++);
+
+ int src_index = probe->GetSourceIndex();
+ if (src_index >= 0 && src_index < reactors.size())
+ reactors[src_index]->AddClient(probe);
+
+ systems.append(probe);
+ }
+
+ for (i = 0; i < design->computers.size(); i++) {
+ Computer* comp = 0;
+
+ if (design->computers[i]->Subtype() == Computer::FLIGHT) {
+ flcs = new(__FILE__,__LINE__) FlightComp(*design->computers[i]);
+
+ flcs->SetShip(this);
+ flcs->SetMode(flcs_mode);
+ flcs->SetVelocityLimit(vlimit);
+
+ if (thruster)
+ flcs->SetTransLimit(thruster->TransXLimit(),
+ thruster->TransYLimit(),
+ thruster->TransZLimit());
+ else
+ flcs->SetTransLimit(design->trans_x,
+ design->trans_y,
+ design->trans_z);
+
+ comp = flcs;
+ }
+ else {
+ comp = new(__FILE__,__LINE__) Computer(*design->computers[i]);
+ }
+
+ comp->SetShip(this);
+ comp->SetID(sys_id++);
+ int src_index = comp->GetSourceIndex();
+ if (src_index >= 0 && src_index < reactors.size())
+ reactors[src_index]->AddClient(comp);
+
+ computers.append(comp);
+ systems.append(comp);
+ }
+
+ radio_orders = new(__FILE__,__LINE__) Instruction("", Point(0,0,0));
+
+ // Load Detail Set:
+ for (i = 0; i < DetailSet::MAX_DETAIL; i++) {
+ if (design->models[i].size() > 0) {
+ Solid* solid = new(__FILE__,__LINE__) ShipSolid(this);
+ solid->UseModel(design->models[i].at(0));
+ solid->CreateShadows(1);
+
+ Point* offset = 0;
+ Point* spin = 0;
+
+ if (design->offsets[i].size() > 0)
+ offset = new(__FILE__,__LINE__) Point(*design->offsets[i].at(0));
+
+ if (design->spin_rates.size() > 0)
+ spin = new(__FILE__,__LINE__) Point(*design->spin_rates.at(0));
+
+ detail_level = detail.DefineLevel(design->feature_size[i], solid, offset, spin);
+ }
+
+ if (design->models[i].size() > 1) {
+ for (int n = 1; n < design->models[i].size(); n++) {
+ Solid* solid = new(__FILE__,__LINE__) ShipSolid(this); //Solid;
+ solid->UseModel(design->models[i].at(n));
+ solid->CreateShadows(1);
+
+ Point* offset = 0;
+ Point* spin = 0;
+
+ if (design->offsets[i].size() > n)
+ offset = new(__FILE__,__LINE__) Point(*design->offsets[i].at(n));
+
+ if (design->spin_rates.size() > n)
+ spin = new(__FILE__,__LINE__) Point(*design->spin_rates.at(n));
+
+ detail.AddToLevel(detail_level, solid, offset, spin);
+ }
+ }
+ }
+
+ // start with lowest available detail:
+ detail_level = 0; // this is highest -> detail.NumLevels()-1);
+ rep = detail.GetRep(detail_level);
+
+ if (design->cockpit_model) {
+ cockpit = new(__FILE__,__LINE__) Solid;
+ cockpit->UseModel(design->cockpit_model);
+ cockpit->SetForeground(true);
+ }
+
+ if (design->main_drive >= 0 && design->main_drive < drives.size())
+ main_drive = drives[design->main_drive];
+
+ // only use light from drives:
+ light = 0;
+
+ // setup starship helm stuff:
+ if (IsStarship()) {
+ flcs_mode = FLCS_HELM;
+ }
+
+ // initialize the AI:
+ dir = 0;
+ SetControls(0);
+
+ for (i = 0; i < 4; i++) {
+ missile_id[i] = 0;
+ missile_eta[i] = 0;
+ trigger[i] = false;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Ship::~Ship()
+{
+ // the loadout can not be cleared during Destroy, because it
+ // is needed after Destroy to create the re-spawned ship
+
+ delete [] loadout;
+ loadout = 0;
+
+ Destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::Destroy()
+{
+ // destroy fighters on deck:
+ ListIter<FlightDeck> deck = flight_decks;
+ while (++deck) {
+ for (int i = 0; i < deck->NumSlots(); i++) {
+ Ship* s = deck->GetShip(i);
+
+ if (s && !s->IsDying() && !s->IsDead()) {
+ if (sim && sim->IsActive()) {
+ s->DeathSpiral();
+ }
+ else {
+ s->transition_type = TRANSITION_DEAD;
+ s->Destroy();
+ }
+ }
+ }
+ }
+
+ if (element) {
+ // mission ending for this ship, evaluate objectives one last time:
+ for (int i = 0; i < element->NumObjectives(); i++) {
+ Instruction* obj = element->GetObjective(i);
+
+ if (obj->Status() <= Instruction::ACTIVE) {
+ obj->Evaluate(this);
+ }
+ }
+
+ combat_unit = element->GetCombatUnit();
+ SetElement(0);
+ }
+
+ delete [] track;
+ track = 0;
+
+ delete shield;
+ shield = 0;
+ delete sensor;
+ sensor = 0;
+ delete navsys;
+ navsys = 0;
+ delete thruster;
+ thruster = 0;
+ delete farcaster;
+ farcaster = 0;
+ delete quantum_drive;
+ quantum_drive = 0;
+ delete decoy;
+ decoy = 0;
+ delete probe;
+ probe = 0;
+ delete gear;
+ gear = 0;
+
+ main_drive = 0;
+ flcs = 0;
+
+ // repair queue does not own the systems under repair:
+ repair_queue.clear();
+
+ navlights.destroy();
+ flight_decks.destroy();
+ computers.destroy();
+ weapons.destroy();
+ drives.destroy();
+ reactors.destroy();
+
+ // this is now a list of dangling pointers:
+ systems.clear();
+
+ delete hangar;
+ hangar = 0;
+
+ // this also destroys the rep:
+ detail.Destroy();
+ rep = 0;
+
+ GRAPHIC_DESTROY(cockpit);
+ GRAPHIC_DESTROY(shieldRep);
+ LIGHT_DESTROY(light);
+
+ delete launch_point;
+ launch_point = 0;
+
+ delete radio_orders;
+ radio_orders = 0;
+
+ delete dir;
+ dir = 0;
+
+ delete killer;
+ killer = 0;
+
+ // inbound slot is deleted by flight deck:
+ inbound = 0;
+
+ life = 0;
+ Notify();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::Initialize()
+{
+ ShipDesign::Initialize();
+ Thruster::Initialize();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::Close()
+{
+ ShipDesign::Close();
+ Thruster::Close();
+}
+
+void
+Ship::SetupAgility()
+{
+ const float ROLL_SPEED = (float)(PI * 0.1500);
+ const float PITCH_SPEED = (float)(PI * 0.0250);
+ const float YAW_SPEED = (float)(PI * 0.0250);
+
+ drag = design->drag;
+ dr_drg = design->roll_drag;
+ dp_drg = design->pitch_drag;
+ dy_drg = design->yaw_drag;
+
+ if (IsDying()) {
+ drag = 0.0f;
+ dr_drg *= 0.25f;
+ dp_drg *= 0.25f;
+ dy_drg *= 0.25f;
+ }
+
+ if (flight_model > 0) {
+ drag = design->arcade_drag;
+ thrust *= 10.0f;
+ }
+
+ float yaw_air_factor = 1.0f;
+
+ if (IsAirborne()) {
+ bool grounded = AltitudeAGL() < Radius()/2;
+
+ if (flight_model > 0) {
+ drag *= 2.0f;
+
+ if (gear && gear->GetState() != LandingGear::GEAR_UP)
+ drag *= 2.0f;
+
+ if (grounded)
+ drag *= 3.0f;
+ }
+
+ else {
+ if (Class() != LCA)
+ yaw_air_factor = 0.3f;
+
+ double rho = GetDensity();
+ double speed = Velocity().length();
+
+ agility = design->air_factor * rho * speed - wep_resist;
+
+ if (grounded && agility < 0)
+ agility = 0;
+
+ else if (!grounded && agility < 0.5 * design->agility)
+ agility = 0.5 * design->agility;
+
+ else if (agility > 2 * design->agility)
+ agility = 2 * design->agility;
+
+ // undercarriage aerodynamic drag
+ if (gear && gear->GetState() != LandingGear::GEAR_UP)
+ drag *= 5.0f;
+
+ // wheel rolling friction
+ if (grounded)
+ drag *= 10.0f;
+
+ // dead engine drag ;-)
+ if (thrust < 10)
+ drag *= 5.0f;
+ }
+ }
+
+ else {
+ agility = design->agility - wep_resist;
+
+ if (agility < 0.5 * design->agility)
+ agility = 0.5 * design->agility;
+
+ if (flight_model == 0)
+ drag = 0.0f;
+ }
+
+ float rr = (float) (design->roll_rate * PI / 180);
+ float pr = (float) (design->pitch_rate * PI / 180);
+ float yr = (float) (design->yaw_rate * PI / 180);
+
+ if (rr == 0) rr = (float) agility * ROLL_SPEED;
+ if (pr == 0) pr = (float) agility * PITCH_SPEED;
+ if (yr == 0) yr = (float) agility * YAW_SPEED * yaw_air_factor;
+
+ SetAngularRates(rr, pr, yr);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::SetRegion(SimRegion* rgn)
+{
+ SimObject::SetRegion(rgn);
+
+ const double GRAV = 6.673e-11;
+
+ if (IsGroundUnit()) {
+ // glue buildings to the terrain:
+ Point loc = Location();
+ Terrain* terrain = region->GetTerrain();
+
+ if (terrain) {
+ loc.y = terrain->Height(loc.x, loc.z);
+ MoveTo(loc);
+ }
+ }
+
+ else if (IsAirborne()) {
+ Orbital* primary = GetRegion()->GetOrbitalRegion()->Primary();
+
+ double m0 = primary->Mass();
+ double r = primary->Radius();
+
+ SetGravity((float) (GRAV * m0 / (r*r)));
+ SetBaseDensity(1.0f);
+ }
+
+ else {
+ SetGravity(0.0f);
+ SetBaseDensity(0.0f);
+
+ if (IsStarship())
+ flcs_mode = FLCS_HELM;
+ else
+ flcs_mode = FLCS_AUTO;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Ship::GetTextureList(List<Bitmap>& textures)
+{
+ textures.clear();
+
+ for (int d = 0; d < detail.NumLevels(); d++) {
+ for (int i = 0; i < detail.NumModels(d); i++) {
+ Graphic* g = detail.GetRep(d, i);
+
+ if (g->IsSolid()) {
+ Solid* solid = (Solid*) g;
+ Model* model = solid->GetModel();
+
+ if (model) {
+ for (int n = 0; n < model->NumMaterials(); n++) {
+ //textures.append(model->textures[n]);
+ }
+ }
+ }
+ }
+ }
+
+ return textures.size();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::Activate(Scene& scene)
+{
+ int i = 0;
+ SimObject::Activate(scene);
+
+ for (i = 0; i < detail.NumModels(detail_level); i++) {
+ Graphic* g = detail.GetRep(detail_level, i);
+ scene.AddGraphic(g);
+ }
+
+ for (i = 0; i < flight_decks.size(); i++)
+ scene.AddLight(flight_decks[i]->GetLight());
+
+ if (shieldRep)
+ scene.AddGraphic(shieldRep);
+
+ if (cockpit) {
+ scene.AddForeground(cockpit);
+ cockpit->Hide();
+ }
+
+ Drive* drive = GetDrive();
+ if (drive) {
+ for (i = 0; i < drive->NumEngines(); i++) {
+ Graphic* flare = drive->GetFlare(i);
+ if (flare) {
+ scene.AddGraphic(flare);
+ }
+
+ Graphic* trail = drive->GetTrail(i);
+ if (trail) {
+ scene.AddGraphic(trail);
+ }
+ }
+ }
+
+ Thruster* thruster = GetThruster();
+ if (thruster) {
+ for (i = 0; i < thruster->NumThrusters(); i++) {
+ Graphic* flare = thruster->Flare(i);
+ if (flare) {
+ scene.AddGraphic(flare);
+ }
+
+ Graphic* trail = thruster->Trail(i);
+ if (trail) {
+ scene.AddGraphic(trail);
+ }
+ }
+ }
+
+ for (int n = 0; n < navlights.size(); n++) {
+ NavLight* navlight = navlights[n];
+ for (i = 0; i < navlight->NumBeacons(); i++) {
+ Graphic* beacon = navlight->Beacon(i);
+ if (beacon)
+ scene.AddGraphic(beacon);
+ }
+ }
+
+ ListIter<WeaponGroup> g = weapons;
+ while (++g) {
+ ListIter<Weapon> w = g->GetWeapons();
+ while (++w) {
+ Solid* turret = w->GetTurret();
+ if (turret) {
+ scene.AddGraphic(turret);
+
+ Solid* turret_base = w->GetTurretBase();
+ if (turret_base)
+ scene.AddGraphic(turret_base);
+ }
+ if (w->IsMissile()) {
+ for (i = 0; i < w->Ammo(); i++) {
+ Solid* store = w->GetVisibleStore(i);
+ if (store)
+ scene.AddGraphic(store);
+ }
+ }
+ }
+ }
+
+ if (gear && gear->GetState() != LandingGear::GEAR_UP) {
+ for (int i = 0; i < gear->NumGear(); i++) {
+ scene.AddGraphic(gear->GetGear(i));
+ }
+ }
+}
+
+void
+Ship::Deactivate(Scene& scene)
+{
+ int i = 0;
+ SimObject::Deactivate(scene);
+
+ for (i = 0; i < detail.NumModels(detail_level); i++) {
+ Graphic* g = detail.GetRep(detail_level, i);
+ scene.DelGraphic(g);
+ }
+
+ for (i = 0; i < flight_decks.size(); i++)
+ scene.DelLight(flight_decks[i]->GetLight());
+
+ if (shieldRep)
+ scene.DelGraphic(shieldRep);
+
+ if (cockpit)
+ scene.DelForeground(cockpit);
+
+ Drive* drive = GetDrive();
+ if (drive) {
+ for (i = 0; i < drive->NumEngines(); i++) {
+ Graphic* flare = drive->GetFlare(i);
+ if (flare) {
+ scene.DelGraphic(flare);
+ }
+
+ Graphic* trail = drive->GetTrail(i);
+ if (trail) {
+ scene.DelGraphic(trail);
+ }
+ }
+ }
+
+ Thruster* thruster = GetThruster();
+ if (thruster) {
+ for (i = 0; i < thruster->NumThrusters(); i++) {
+ Graphic* flare = thruster->Flare(i);
+ if (flare) {
+ scene.DelGraphic(flare);
+ }
+
+ Graphic* trail = thruster->Trail(i);
+ if (trail) {
+ scene.DelGraphic(trail);
+ }
+ }
+ }
+
+ for (int n = 0; n < navlights.size(); n++) {
+ NavLight* navlight = navlights[n];
+ for (i = 0; i < navlight->NumBeacons(); i++) {
+ Graphic* beacon = navlight->Beacon(i);
+ if (beacon)
+ scene.DelGraphic(beacon);
+ }
+ }
+
+ ListIter<WeaponGroup> g = weapons;
+ while (++g) {
+ ListIter<Weapon> w = g->GetWeapons();
+ while (++w) {
+ Solid* turret = w->GetTurret();
+ if (turret) {
+ scene.DelGraphic(turret);
+
+ Solid* turret_base = w->GetTurretBase();
+ if (turret_base)
+ scene.DelGraphic(turret_base);
+ }
+ if (w->IsMissile()) {
+ for (i = 0; i < w->Ammo(); i++) {
+ Solid* store = w->GetVisibleStore(i);
+ if (store)
+ scene.DelGraphic(store);
+ }
+ }
+ }
+ }
+
+ if (gear) {
+ for (int i = 0; i < gear->NumGear(); i++) {
+ scene.DelGraphic(gear->GetGear(i));
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::MatchOrientation(const Ship& s)
+{
+ Point pos = cam.Pos();
+ cam.Clone(s.cam);
+ cam.MoveTo(pos);
+
+ if (rep)
+ rep->SetOrientation(cam.Orientation());
+
+ if (cockpit)
+ cockpit->SetOrientation(cam.Orientation());
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::ClearTrack()
+{
+ const int DEFAULT_TRACK_LENGTH = 20; // 10 seconds
+
+ if (!track) {
+ track = new(__FILE__,__LINE__) Point[DEFAULT_TRACK_LENGTH];
+ }
+
+ track[0] = Location();
+ ntrack = 1;
+ track_time = Game::GameTime();
+}
+
+void
+Ship::UpdateTrack()
+{
+ const int DEFAULT_TRACK_UPDATE = 500; // milliseconds
+ const int DEFAULT_TRACK_LENGTH = 20; // 10 seconds
+
+ DWORD time = Game::GameTime();
+
+ if (!track) {
+ track = new(__FILE__,__LINE__) Point[DEFAULT_TRACK_LENGTH];
+ track[0] = Location();
+ ntrack = 1;
+ track_time = time;
+ }
+
+ else if (time - track_time > DEFAULT_TRACK_UPDATE) {
+ if (Location() != track[0]) {
+ for (int i = DEFAULT_TRACK_LENGTH-2; i >= 0; i--)
+ track[i+1] = track[i];
+
+ track[0] = Location();
+ if (ntrack < DEFAULT_TRACK_LENGTH) ntrack++;
+ }
+
+ track_time = time;
+ }
+}
+
+Point
+Ship::TrackPoint(int i) const
+{
+ if (track && i < ntrack)
+ return track[i];
+
+ return Point();
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+Ship::Abbreviation() const
+{
+ return design->abrv;
+}
+
+const char*
+Ship::DesignName() const
+{
+ return design->DisplayName();
+}
+
+const char*
+Ship::DesignFileName() const
+{
+ return design->filename;
+}
+
+const char*
+Ship::ClassName() const
+{
+ return ShipDesign::ClassName(design->type);
+}
+
+const char*
+Ship::ClassName(int c)
+{
+ return ShipDesign::ClassName(c);
+}
+
+int
+Ship::ClassForName(const char* name)
+{
+ return ShipDesign::ClassForName(name);
+}
+
+Ship::CLASSIFICATION
+Ship::Class() const
+{
+ return (CLASSIFICATION) design->type;
+}
+
+bool
+Ship::IsGroundUnit() const
+{
+ return (design->type & GROUND_UNITS) ? true : false;
+}
+
+bool
+Ship::IsStarship() const
+{
+ return (design->type & STARSHIPS) ? true : false;
+}
+
+bool
+Ship::IsDropship() const
+{
+ return (design->type & DROPSHIPS) ? true : false;
+}
+
+bool
+Ship::IsStatic() const
+{
+ return design->type >= STATION;
+}
+
+bool
+Ship::IsRogue() const
+{
+ return ff_count >= 50;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Ship::IsHostileTo(const SimObject* o) const
+{
+ if (o) {
+ if (IsRogue())
+ return true;
+
+ if (o->Type() == SIM_SHIP) {
+ Ship* s = (Ship*) o;
+
+ if (s->IsRogue())
+ return true;
+
+ if (GetIFF() == 0) {
+ if (s->GetIFF() > 1)
+ return true;
+ }
+ else {
+ if (s->GetIFF() > 0 && s->GetIFF() != GetIFF())
+ return true;
+ }
+ }
+
+ else if (o->Type() == SIM_SHOT || o->Type() == SIM_DRONE) {
+ Shot* s = (Shot*) o;
+
+ if (GetIFF() == 0) {
+ if (s->GetIFF() > 1)
+ return true;
+ }
+ else {
+ if (s->GetIFF() > 0 && s->GetIFF() != GetIFF())
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Ship::RepairSpeed() const
+{
+ return design->repair_speed;
+}
+
+int
+Ship::RepairTeams() const
+{
+ return design->repair_teams;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Ship::NumContacts() const
+{
+ // cast-away const:
+ return ((Ship*)this)->ContactList().size();
+}
+
+List<Contact>&
+Ship::ContactList()
+{
+ if (region)
+ return region->TrackList(GetIFF());
+
+ static List<Contact> empty_contact_list;
+ return empty_contact_list;
+}
+
+Contact*
+Ship::FindContact(SimObject* s) const
+{
+ if (!s) return 0;
+
+ ListIter<Contact> c_iter = ((Ship*) this)->ContactList();
+ while (++c_iter) {
+ Contact* c = c_iter.value();
+
+ if (c->GetShip() == s)
+ return c;
+
+ if (c->GetShot() == s)
+ return c;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+Ship*
+Ship::GetController() const
+{
+ Ship* controller = 0;
+
+ if (carrier) {
+ // are we in same region as carrier?
+ if (carrier->GetRegion() == GetRegion()) {
+ return carrier;
+ }
+
+ // if not, figure out who our control unit is:
+ else {
+ double distance = 10e6;
+
+ ListIter<Ship> iter = GetRegion()->Carriers();
+ while (++iter) {
+ Ship* test = iter.value();
+ if (test->GetIFF() == GetIFF()) {
+ double d = Point(Location() - test->Location()).length();
+ if (d < distance) {
+ controller = test;
+ distance = d;
+ }
+ }
+ }
+ }
+ }
+
+ if (!controller) {
+ if (element && element->GetCommander())
+ controller = element->GetCommander()->GetShip(1);
+ }
+
+ return controller;
+}
+
+int
+Ship::NumInbound() const
+{
+ int result = 0;
+
+ for (int i = 0; i < flight_decks.size(); i++) {
+ result += flight_decks[i]->GetRecoveryQueue().size();
+ }
+
+ return result;
+}
+
+int
+Ship::NumFlightDecks() const
+{
+ return flight_decks.size();
+}
+
+FlightDeck*
+Ship::GetFlightDeck(int i) const
+{
+ if (i >= 0 && i < flight_decks.size())
+ return flight_decks[i];
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::SetFlightPhase(OP_MODE phase)
+{
+ if (phase == ACTIVE && !launch_time) {
+ launch_time = Game::GameTime() + 1;
+ dock = 0;
+
+ if (element)
+ element->SetLaunchTime(launch_time);
+ }
+
+ flight_phase = phase;
+
+ if (flight_phase == ACTIVE)
+ dock = 0;
+}
+
+void
+Ship::SetCarrier(Ship* c, FlightDeck* d)
+{
+ carrier = c;
+ dock = d;
+
+ if (carrier)
+ Observe(carrier);
+}
+
+void
+Ship::SetInbound(InboundSlot* s)
+{
+ inbound = s;
+
+ if (inbound && flight_phase == ACTIVE) {
+ flight_phase = APPROACH;
+
+ SetCarrier((Ship*) inbound->GetDeck()->GetCarrier(), inbound->GetDeck());
+
+ HUDView* hud = HUDView::GetInstance();
+
+ if (hud && hud->GetShip() == this)
+ hud->SetHUDMode(HUDView::HUD_MODE_ILS);
+ }
+}
+
+void
+Ship::Stow()
+{
+ if (carrier && carrier->GetHangar())
+ carrier->GetHangar()->Stow(this);
+}
+
+bool
+Ship::IsGearDown()
+{
+ if (gear && gear->GetState() == LandingGear::GEAR_DOWN)
+ return true;
+
+ return false;
+}
+
+void
+Ship::LowerGear()
+{
+ if (gear && gear->GetState() != LandingGear::GEAR_DOWN) {
+ gear->SetState(LandingGear::GEAR_LOWER);
+ Scene* scene = 0;
+
+ if (rep)
+ scene = rep->GetScene();
+
+ if (scene) {
+ for (int i = 0; i < gear->NumGear(); i++) {
+ Solid* g = gear->GetGear(i);
+ if (g) {
+ if (detail_level == 0)
+ scene->DelGraphic(g);
+ else
+ scene->AddGraphic(g);
+ }
+ }
+ }
+ }
+}
+
+void
+Ship::RaiseGear()
+{
+ if (gear && gear->GetState() != LandingGear::GEAR_UP)
+ gear->SetState(LandingGear::GEAR_RAISE);
+}
+
+void
+Ship::ToggleGear()
+{
+ if (gear) {
+ if (gear->GetState() == LandingGear::GEAR_UP ||
+ gear->GetState() == LandingGear::GEAR_RAISE) {
+ LowerGear();
+ }
+ else {
+ RaiseGear();
+ }
+ }
+}
+
+void
+Ship::ToggleNavlights()
+{
+ bool enable = false;
+
+ for (int i = 0; i < navlights.size(); i++) {
+ if (i == 0)
+ enable = !navlights[0]->IsEnabled();
+
+ if (enable)
+ navlights[i]->Enable();
+ else
+ navlights[i]->Disable();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Ship::CollidesWith(Physical& o)
+{
+ // bounding spheres test:
+ Point delta_loc = Location() - o.Location();
+ if (delta_loc.length() > radius + o.Radius())
+ return 0;
+
+ if (!o.Rep())
+ return 1;
+
+ for (int i = 0; i < detail.NumModels(detail_level); i++) {
+ Graphic* g = detail.GetRep(detail_level, i);
+
+ if (o.Type() == SimObject::SIM_SHIP) {
+ Ship* o_ship = (Ship*) &o;
+ int o_det = o_ship->detail_level;
+
+ for (int j = 0; j < o_ship->detail.NumModels(o_det); j++) {
+ Graphic* o_g = o_ship->detail.GetRep(o_det, j);
+
+ if (g->CollidesWith(*o_g))
+ return 1;
+ }
+ }
+ else {
+ // representation collision test (will do bounding spheres first):
+ if (g->CollidesWith(*o.Rep()))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+static DWORD ff_warn_time = 0;
+
+int
+Ship::HitBy(Shot* shot, Point& impact)
+{
+ if (shot->Owner() == this || IsNetObserver())
+ return HIT_NOTHING;
+
+ if (shot->IsFlak())
+ return HIT_NOTHING;
+
+ if (InTransition())
+ return HIT_NOTHING;
+
+ Point shot_loc = shot->Location();
+ Point delta = shot_loc - Location();
+ double dlen = delta.length();
+
+ Point hull_impact;
+ int hit_type = HIT_NOTHING;
+ double dscale = 1;
+ float scale = design->explosion_scale;
+ Weapon* wep = 0;
+
+ if (!shot->IsMissile() && !shot->IsBeam()) {
+ if (dlen > Radius() * 2)
+ return HIT_NOTHING;
+ }
+
+ if (scale <= 0)
+ scale = design->scale;
+
+ if (shot->Owner()) {
+ const ShipDesign* owner_design = shot->Owner()->Design();
+ if (owner_design && owner_design->scale < scale)
+ scale = (float) owner_design->scale;
+ }
+
+
+ // MISSILE PROCESSING ------------------------------------------------
+
+ if (shot->IsMissile() && rep) {
+ if (dlen < rep->Radius()) {
+ hull_impact = impact = shot_loc;
+
+ hit_type = CheckShotIntersection(shot, impact, hull_impact, &wep);
+
+ if (hit_type) {
+ if (shot->Damage() > 0) {
+ DWORD flash = Explosion::HULL_FLASH;
+
+ if ((hit_type & HIT_SHIELD) != 0)
+ flash = Explosion::SHIELD_FLASH;
+
+ sim->CreateExplosion(impact, Velocity(), flash, 0.3f * scale, scale, region);
+ sim->CreateExplosion(impact, Point(), Explosion::SHOT_BLAST, 2.0f, scale, region);
+ }
+ }
+ }
+
+ if (hit_type == HIT_NOTHING && shot->IsArmed()) {
+ SeekerAI* seeker = (SeekerAI*) shot->GetDirector();
+
+ // if the missile overshot us, take damage proportional to distance
+ double damage_radius = shot->Design()->lethal_radius;
+ if (dlen < (damage_radius + Radius())) {
+ if (seeker && seeker->Overshot()) {
+ dscale = 1.0 - (dlen / (damage_radius + Radius()));
+
+ if (dscale > 1)
+ dscale = 1;
+
+ if (ShieldStrength() > 5) {
+ hull_impact = impact = shot_loc;
+
+ if (shot->Damage() > 0) {
+ if (shieldRep)
+ shieldRep->Hit(impact, shot, shot->Damage()*dscale);
+ sim->CreateExplosion(impact, Velocity(), Explosion::SHIELD_FLASH, 0.20f * scale, scale, region);
+ sim->CreateExplosion(impact, Point(), Explosion::SHOT_BLAST, 20.0f * scale, scale, region);
+ }
+
+ hit_type = HIT_BOTH;
+ }
+ else {
+ hull_impact = impact = shot_loc;
+
+ if (shot->Damage() > 0) {
+ sim->CreateExplosion(impact, Velocity(), Explosion::HULL_FLASH, 0.30f * scale, scale, region);
+ sim->CreateExplosion(impact, Point(), Explosion::SHOT_BLAST, 20.0f * scale, scale, region);
+ }
+
+ hit_type = HIT_HULL;
+ }
+ }
+ }
+ }
+ }
+
+ // ENERGY WEP PROCESSING ---------------------------------------------
+
+ else {
+ hit_type = CheckShotIntersection(shot, impact, hull_impact, &wep);
+
+ // impact:
+ if (hit_type) {
+
+ if (hit_type & HIT_SHIELD) {
+ if (shieldRep)
+ shieldRep->Hit(impact, shot, shot->Damage());
+ sim->CreateExplosion(impact, Velocity(), Explosion::SHIELD_FLASH, 0.20f * scale, scale, region);
+ }
+
+ else {
+ if (shot->IsBeam())
+ sim->CreateExplosion(impact, Velocity(), Explosion::BEAM_FLASH, 0.30f * scale, scale, region);
+ else
+ sim->CreateExplosion(impact, Velocity(), Explosion::HULL_FLASH, 0.30f * scale, scale, region);
+
+ if (IsStarship()) {
+ Point burst_vel = hull_impact - Location();
+ burst_vel.Normalize();
+ burst_vel *= Radius() * 0.5;
+ burst_vel += Velocity();
+
+ sim->CreateExplosion(hull_impact, burst_vel, Explosion::HULL_BURST, 0.50f * scale, scale, region, this);
+ }
+ }
+ }
+ }
+
+ // DAMAGE RESOLUTION -------------------------------------------------
+
+ if (hit_type != HIT_NOTHING && shot->IsArmed()) {
+ double effective_damage = shot->Damage() * dscale;
+
+ // FRIENDLY FIRE --------------------------------------------------
+
+ if (shot->Owner()) {
+ Ship* s = (Ship*) shot->Owner();
+
+ if (!IsRogue() && s->GetIFF() == GetIFF() &&
+ s->GetDirector() && s->GetDirector()->Type() < 1000) {
+ bool was_rogue = s->IsRogue();
+
+ // only count beam hits once
+ if (shot->Damage() && !shot->HitTarget() && GetFriendlyFireLevel() > 0) {
+ int penalty = 1;
+
+ if (shot->IsBeam()) penalty = 5;
+ else if (shot->IsDrone()) penalty = 7;
+
+ if (s->GetTarget() == this) penalty *= 3;
+
+ s->IncFriendlyFire(penalty);
+ }
+
+ effective_damage *= GetFriendlyFireLevel();
+
+ if (Class() > DRONE && s->Class() > DRONE) {
+ if (s->IsRogue() && !was_rogue) {
+ RadioMessage* warn = new(__FILE__,__LINE__) RadioMessage(s, this, RadioMessage::DECLARE_ROGUE);
+ RadioTraffic::Transmit(warn);
+ }
+ else if (!s->IsRogue() && (Game::GameTime() - ff_warn_time) > 5000) {
+ ff_warn_time = Game::GameTime();
+
+ RadioMessage* warn = 0;
+ if (s->GetTarget() == this)
+ warn = new(__FILE__,__LINE__) RadioMessage(s, this, RadioMessage::WARN_TARGETED);
+ else
+ warn = new(__FILE__,__LINE__) RadioMessage(s, this, RadioMessage::WARN_ACCIDENT);
+
+ RadioTraffic::Transmit(warn);
+ }
+ }
+ }
+ }
+
+ if (effective_damage > 0) {
+ if (!shot->IsBeam() && shot->Design()->damage_type == WeaponDesign::DMG_NORMAL)
+ ApplyTorque(shot->Velocity() * (float) effective_damage * 1e-6f);
+
+ if (!NetGame::IsNetGameClient()) {
+ InflictDamage(effective_damage, shot, hit_type, hull_impact);
+ }
+ }
+ }
+
+ return hit_type;
+}
+
+static bool CheckRaySphereIntersection(Point loc, double radius, Point Q, Point w, double len)
+{
+ Point d0 = loc - Q;
+ Point d1 = d0.cross(w);
+ double dlen = d1.length(); // distance of point from line
+
+ if (dlen > radius) // clean miss
+ return false; // (no impact)
+
+ // possible collision course...
+ // find the point on the ray that is closest
+ // to the sphere's location:
+ Point closest = Q + w * (d0 * w);
+
+ // find the leading edge, and it's distance from the location:
+ Point leading_edge = Q + w*len;
+ Point leading_delta = leading_edge - loc;
+ double leading_dist = leading_delta.length();
+
+ // if the leading edge is not within the sphere,
+ if (leading_dist > radius) {
+ // check to see if the closest point is between the
+ // ray's endpoints:
+ Point delta1 = closest - Q;
+ Point delta2 = leading_edge - Q; // this is w*len
+
+ // if the closest point is not between the leading edge
+ // and the origin, this ray does not intersect:
+ if (delta1 * delta2 < 0 || delta1.length() > len) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int
+Ship::CheckShotIntersection(Shot* shot, Point& ipt, Point& hpt, Weapon** wep)
+{
+ int hit_type = HIT_NOTHING;
+ Point shot_loc = shot->Location();
+ Point shot_org = shot->Origin();
+ Point shot_vpn = shot_loc - shot_org;
+ double shot_len = shot_vpn.Normalize();
+ double blow_len = shot_len;
+ bool hit_hull = false;
+ bool easy = false;
+
+ if (shot_len <= 0)
+ return hit_type;
+
+ if (shot_len < 1000)
+ shot_len = 1000;
+
+ Point hull_impact;
+ Point shield_impact;
+ Point turret_impact;
+ Point closest;
+ double d0 = 1e9;
+ double d1 = 1e9;
+ double ds = 1e9;
+
+ if (dir && dir->Type() == SteerAI::FIGHTER) {
+ ShipAI* shipAI = (ShipAI*) dir;
+ easy = shipAI->GetAILevel() < 2;
+ }
+
+ if (shieldRep && ShieldStrength() > 5) {
+ if (shieldRep->CheckRayIntersection(shot_org, shot_vpn, shot_len, shield_impact)) {
+ hit_type = HIT_SHIELD;
+ closest = shield_impact;
+ d0 = Point(closest - shot_org).length();
+ ds = d0;
+
+ ipt = shield_impact;
+ }
+ }
+
+ if (shieldRep && hit_type == HIT_SHIELD && !shot->IsBeam())
+ blow_len = shieldRep->Radius() * 2;
+
+ for (int i = 0; i < detail.NumModels(detail_level) && !hit_hull; i++) {
+ Solid* s = (Solid*) detail.GetRep(detail_level, i);
+ if (s) {
+ if (easy) {
+ hit_hull = CheckRaySphereIntersection(s->Location(), s->Radius(), shot_org, shot_vpn, shot_len);
+ }
+ else {
+ hit_hull = s->CheckRayIntersection(shot_org, shot_vpn, blow_len, hull_impact)?true:false;
+ }
+ }
+ }
+
+ if (hit_hull) {
+ if (ShieldStrength() > 5 && !shieldRep)
+ hit_type = HIT_SHIELD;
+
+ hit_type = hit_type | HIT_HULL;
+ hpt = hull_impact;
+
+ d1 = Point(hull_impact - shot_org).length();
+
+ if (d1 < d0) {
+ closest = hull_impact;
+ d0 = d1;
+ }
+ }
+
+ if (IsStarship() || IsStatic()) {
+ ListIter<WeaponGroup> g_iter = Weapons();
+ while (++g_iter) {
+ WeaponGroup* g = g_iter.value();
+
+ if (g->GetDesign() && g->GetDesign()->turret_model) {
+ double tsize = g->GetDesign()->turret_model->Radius();
+
+ ListIter<Weapon> w_iter = g->GetWeapons();
+ while (++w_iter) {
+ Weapon* w = w_iter.value();
+
+ Point tloc = w->GetTurret()->Location();
+
+ if (CheckRaySphereIntersection(tloc, tsize, shot_org, shot_vpn, shot_len)) {
+ Point delta = tloc - shot_org;
+ d1 = delta.length();
+
+ if (d1 < d0) {
+ if (wep) *wep = w;
+ hit_type = hit_type | HIT_TURRET;
+ turret_impact = tloc;
+
+ d0 = d1;
+
+ closest = turret_impact;
+ hull_impact = turret_impact;
+ hpt = turret_impact;
+
+ if (d1 < ds)
+ ipt = turret_impact;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // trim beam shots to closest impact point:
+ if (hit_type && shot->IsBeam()) {
+ shot->SetBeamPoints(shot_org, closest);
+ }
+
+ return hit_type;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::InflictNetDamage(double damage, Shot* shot)
+{
+ if (damage > 0 && !IsNetObserver()) {
+ Physical::InflictDamage(damage, 0);
+
+ // shake by percentage of maximum damage
+ double newshake = 50 * damage/design->integrity;
+ const double MAX_SHAKE = 7;
+
+ if (shake < MAX_SHAKE) shake += (float) newshake;
+ if (shake > MAX_SHAKE) shake = (float) MAX_SHAKE;
+ }
+}
+
+void
+Ship::InflictNetSystemDamage(System* system, double damage, BYTE dmg_type)
+{
+ if (system && damage > 0 && !IsNetObserver()) {
+ bool dmg_normal = dmg_type == WeaponDesign::DMG_NORMAL;
+ bool dmg_power = dmg_type == WeaponDesign::DMG_POWER;
+ bool dmg_emp = dmg_type == WeaponDesign::DMG_EMP;
+
+ double sys_damage = damage;
+ double avail = system->Availability();
+
+ if (dmg_normal || system->IsPowerCritical() && dmg_emp) {
+ system->ApplyDamage(sys_damage);
+ master_caution = true;
+
+ if (system->GetExplosionType() && (avail - system->Availability()) >= 50) {
+ float scale = design->explosion_scale;
+ if (scale <= 0)
+ scale = design->scale;
+
+ sim->CreateExplosion(system->MountLocation(), Velocity() * 0.7f, system->GetExplosionType(), 0.2f * scale, scale, region, this, system);
+ }
+ }
+ }
+}
+
+void
+Ship::SetNetSystemStatus(System* system, int status, int power, int reactor, double avail)
+{
+ if (system && !IsNetObserver()) {
+ if (system->GetPowerLevel() != power)
+ system->SetPowerLevel(power);
+
+ if (system->GetSourceIndex() != reactor) {
+ System* s = GetSystem(reactor);
+
+ if (s && s->Type() == System::POWER_SOURCE) {
+ PowerSource* reac = (PowerSource*) s;
+ reac->AddClient(system);
+ }
+ }
+
+ if (system->Status() != status) {
+ if (status == System::MAINT) {
+ ListIter<Component> comp = system->GetComponents();
+ while (++comp) {
+ Component* c = comp.value();
+
+ if (c->Status() < Component::NOMINAL && c->Availability() < 75) {
+ if (c->SpareCount() &&
+ c->ReplaceTime() <= 300 &&
+ (c->Availability() < 50 ||
+ c->ReplaceTime() < c->RepairTime())) {
+
+ c->Replace();
+ }
+
+ else if (c->Availability() >= 50 || c->NumJerried() < 5) {
+ c->Repair();
+ }
+ }
+ }
+
+ RepairSystem(system);
+ }
+ }
+
+ if (system->Availability() < avail) {
+ system->SetNetAvail(avail);
+ }
+ else {
+ system->SetNetAvail(-1);
+ }
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+bool IsWeaponBlockedFriendly(Weapon* w, const SimObject* test)
+{
+ if (w->GetTarget()) {
+ Point tgt = w->GetTarget()->Location();
+ Point obj = test->Location();
+ Point wep = w->MountLocation();
+
+ Point dir = tgt - wep;
+ double d = dir.Normalize();
+ Point rho = obj - wep;
+ double r = rho.Normalize();
+
+ // if target is much closer than obstacle,
+ // don't worry about friendly fire...
+ if (d < 1.5 * r)
+ return false;
+
+ Point dst = dir * r + wep;
+ double err = (obj - dst).length();
+
+ if (err < test->Radius() * 1.5)
+ return true;
+ }
+
+ return false;
+}
+
+void
+Ship::CheckFriendlyFire()
+{
+ // if no weapons, there is no worry about friendly fire...
+ if (weapons.size() < 1)
+ return;
+
+ // only check once each second
+ if (Game::GameTime() - friendly_fire_time < 1000)
+ return;
+
+ List<Weapon> w_list;
+ int i, j;
+
+ // clear the FF blocked flag on all weapons
+ for (i = 0; i < weapons.size(); i++) {
+ WeaponGroup* g = weapons[i];
+
+ for (j = 0; j < g->NumWeapons(); j++) {
+ Weapon* w = g->GetWeapon(j);
+ w_list.append(w);
+ w->SetBlockedFriendly(false);
+ }
+ }
+
+ // for each friendly ship within some kind of weapons range,
+ ListIter<Contact> c_iter = ContactList();
+ while (++c_iter) {
+ Contact* c = c_iter.value();
+ Ship* cship = c->GetShip();
+ Shot* cshot = c->GetShot();
+
+ if (cship && cship != this && (cship->GetIFF() == 0 || cship->GetIFF() == GetIFF())) {
+ double range = (cship->Location() - Location()).length();
+
+ if (range > 100e3)
+ continue;
+
+ // check each unblocked weapon to see if it is blocked by that ship
+ ListIter<Weapon> iter = w_list;
+ while (++iter) {
+ Weapon* w = iter.value();
+
+ if (!w->IsBlockedFriendly())
+ w->SetBlockedFriendly(IsWeaponBlockedFriendly(w, cship));
+ }
+ }
+
+ else if (cshot && cshot->GetIFF() == GetIFF()) {
+ double range = (cshot->Location() - Location()).length();
+
+ if (range > 30e3)
+ continue;
+
+ // check each unblocked weapon to see if it is blocked by that shot
+ ListIter<Weapon> iter = w_list;
+ while (++iter) {
+ Weapon* w = iter.value();
+
+ if (!w->IsBlockedFriendly())
+ w->SetBlockedFriendly(IsWeaponBlockedFriendly(w, cshot));
+ }
+ }
+ }
+
+ friendly_fire_time = Game::GameTime() + (DWORD) Random(0, 500);
+}
+
+// +----------------------------------------------------------------------+
+
+Ship*
+Ship::GetLeader() const
+{
+ if (element)
+ return element->GetShip(1);
+
+ return (Ship*) this;
+}
+
+int
+Ship::GetElementIndex() const
+{
+ if (element)
+ return element->FindIndex(this);
+
+ return 0;
+}
+
+int
+Ship::GetOrigElementIndex() const
+{
+ return orig_elem_index;
+}
+
+void
+Ship::SetElement(Element* e)
+{
+ element = e;
+
+ if (element) {
+ combat_unit = element->GetCombatUnit();
+
+ if (combat_unit) {
+ integrity = (float) (design->integrity - combat_unit->GetSustainedDamage());
+ }
+
+ orig_elem_index = element->FindIndex(this);
+ }
+}
+
+void
+Ship::SetLaunchPoint(Instruction* pt)
+{
+ if (pt && !launch_point)
+ launch_point = pt;
+}
+
+void
+Ship::AddNavPoint(Instruction* pt, Instruction* after)
+{
+ if (GetElementIndex() == 1)
+ element->AddNavPoint(pt, after);
+}
+
+void
+Ship::DelNavPoint(Instruction* pt)
+{
+ if (GetElementIndex() == 1)
+ element->DelNavPoint(pt);
+}
+
+void
+Ship::ClearFlightPlan()
+{
+ if (GetElementIndex() == 1)
+ element->ClearFlightPlan();
+}
+
+// +----------------------------------------------------------------------+
+
+bool
+Ship::IsAutoNavEngaged()
+{
+ if (navsys && navsys->AutoNavEngaged())
+ return true;
+
+ return false;
+}
+
+void
+Ship::SetAutoNav(bool engage)
+{
+ if (navsys) {
+ if (navsys->AutoNavEngaged()) {
+ if (!engage)
+ navsys->DisengageAutoNav();
+ }
+ else {
+ if (engage)
+ navsys->EngageAutoNav();
+ }
+
+ if (sim)
+ SetControls(sim->GetControls());
+ }
+}
+
+void
+Ship::CommandMode()
+{
+ if (!dir || dir->Type() != ShipCtrl::DIR_TYPE) {
+ const char* msg = "Captain on the bridge";
+ RadioVox* vox = new(__FILE__,__LINE__) RadioVox(0, "1", msg);
+ vox->AddPhrase(msg);
+
+ if (vox && !vox->Start()) {
+ RadioView::Message( RadioTraffic::TranslateVox(msg) );
+ delete vox;
+ }
+
+ SetControls(sim->GetControls());
+ }
+
+ else {
+ const char* msg = "Exec, you have the conn";
+ RadioVox* vox = new(__FILE__,__LINE__) RadioVox(0, "1", msg);
+ vox->AddPhrase(msg);
+
+ if (vox && !vox->Start()) {
+ RadioView::Message( RadioTraffic::TranslateVox(msg) );
+ delete vox;
+ }
+
+ SetControls(0);
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+Instruction*
+Ship::GetNextNavPoint()
+{
+ if (launch_point && launch_point->Status() <= Instruction::ACTIVE)
+ return launch_point;
+
+ if (element)
+ return element->GetNextNavPoint();
+
+ return 0;
+}
+
+int
+Ship::GetNavIndex(const Instruction* n)
+{
+ if (element)
+ return element->GetNavIndex(n);
+
+ return 0;
+}
+
+double
+Ship::RangeToNavPoint(const Instruction* n)
+{
+ double distance = 0;
+
+ if (n && n->Region()) {
+ Point npt = n->Region()->Location() + n->Location();
+ npt -= GetRegion()->Location();
+ npt = npt.OtherHand(); // convert from map to sim coords
+
+ distance = Point(npt - Location()).length();
+ }
+
+ return distance;
+}
+
+void
+Ship::SetNavptStatus(Instruction* navpt, int status)
+{
+ if (navpt && navpt->Status() != status) {
+ if (status == Instruction::COMPLETE) {
+ if (navpt->Action() == Instruction::ASSAULT)
+ ::Print("Completed Assault\n");
+
+ else if (navpt->Action() == Instruction::STRIKE)
+ ::Print("Completed Strike\n");
+ }
+
+ navpt->SetStatus(status);
+
+ if (status == Instruction::COMPLETE)
+ sim->ProcessEventTrigger(MissionEvent::TRIGGER_NAVPT, 0, Name(), GetNavIndex(navpt));
+
+ if (element) {
+ int index = element->GetNavIndex(navpt);
+
+ if (index >= 0)
+ NetUtil::SendNavData(false, element, index-1, navpt);
+ }
+ }
+}
+
+List<Instruction>&
+Ship::GetFlightPlan()
+{
+ if (element)
+ return element->GetFlightPlan();
+
+ static List<Instruction> dummy_flight_plan;
+ return dummy_flight_plan;
+}
+
+int
+Ship::FlightPlanLength()
+{
+ if (element)
+ return element->FlightPlanLength();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::SetWard(Ship* s)
+{
+ if (ward == s)
+ return;
+
+ ward = s;
+
+ if (ward)
+ Observe(ward);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::SetTarget(SimObject* targ, System* sub, bool from_net)
+{
+ if (targ && targ->Type() == SimObject::SIM_SHIP) {
+ Ship* targ_ship = (Ship*) targ;
+
+ if (targ_ship && targ_ship->IsNetObserver())
+ return;
+ }
+
+ if (target != targ) {
+ // DON'T IGNORE TARGET, BECAUSE IT MAY BE IN THREAT LIST
+ target = targ;
+ if (target) Observe(target);
+
+ if (sim && target)
+ sim->ProcessEventTrigger(MissionEvent::TRIGGER_TARGET, 0, target->Name());
+ }
+
+ subtarget = sub;
+
+ ListIter<WeaponGroup> weapon = weapons;
+ while (++weapon) {
+ if (weapon->GetFiringOrders() != Weapon::POINT_DEFENSE) {
+ weapon->SetTarget(target, subtarget);
+
+ if (sub || !IsStarship())
+ weapon->SetSweep(Weapon::SWEEP_NONE);
+ else
+ weapon->SetSweep(Weapon::SWEEP_TIGHT);
+ }
+ }
+
+ if (!from_net && NetGame::GetInstance())
+ NetUtil::SendObjTarget(this);
+
+ // track engagement:
+ if (target && target->Type() == SimObject::SIM_SHIP) {
+ Element* elem = GetElement();
+ Element* tgt_elem = ((Ship*) target)->GetElement();
+
+ if (elem)
+ elem->SetAssignment(tgt_elem);
+ }
+}
+
+void
+Ship::DropTarget()
+{
+ target = 0;
+ subtarget = 0;
+
+ SetTarget(target, subtarget);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::CycleSubTarget(int dir)
+{
+ if (!target || target->Type() != SimObject::SIM_SHIP)
+ return;
+
+ Ship* tgt = (Ship*) target;
+
+ if (tgt->IsDropship())
+ return;
+
+ System* subtgt = 0;
+
+ ListIter<System> sys = tgt->Systems();
+
+ if (dir > 0) {
+ int latch = (subtarget == 0);
+ while (++sys) {
+ if (sys->Type() == System::COMPUTER || // computers are not targetable
+ sys->Type() == System::SENSOR) // sensors are not targetable
+ continue;
+
+ if (sys.value() == subtarget) {
+ latch = 1;
+ }
+
+ else if (latch) {
+ subtgt = sys.value();
+ break;
+ }
+ }
+ }
+
+ else {
+ System* prev = 0;
+
+ while (++sys) {
+ if (sys->Type() == System::COMPUTER || // computers are not targetable
+ sys->Type() == System::SENSOR) // sensors are not targetable
+ continue;
+
+ if (sys.value() == subtarget) {
+ subtgt = prev;
+ break;
+ }
+
+ prev = sys.value();
+ }
+
+ if (!subtarget)
+ subtgt = prev;
+ }
+
+ SetTarget(tgt, subtgt);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::ExecFrame(double seconds)
+{
+ ZeroMemory(trigger, sizeof(trigger));
+ altitude_agl = -1.0e6f;
+
+ if (flight_phase < LAUNCH) {
+ DockFrame(seconds);
+ return;
+ }
+
+ if (flight_phase == LAUNCH ||
+ (flight_phase == TAKEOFF && AltitudeAGL() > Radius())) {
+ SetFlightPhase(ACTIVE);
+ }
+
+ if (transition_time > 0) {
+ transition_time -= (float) seconds;
+
+ if (transition_time <= 0) {
+ CompleteTransition();
+ return;
+ }
+
+ if (rep && IsDying() && killer) {
+ killer->ExecFrame(seconds);
+ }
+ }
+
+ // observers do not run out of power:
+ if (IsNetObserver()) {
+ for (int i = 0; i < reactors.size(); i++)
+ reactors[i]->SetFuelRange(1e6);
+ }
+
+ if (IsStatic()) {
+ StatFrame(seconds);
+ return;
+ }
+
+ CheckFriendlyFire();
+ ExecNavFrame(seconds);
+ ExecEvalFrame(seconds);
+
+ if (IsAirborne()) {
+ // are we trying to make orbit?
+ if (Location().y >= TERRAIN_ALTITUDE_LIMIT)
+ MakeOrbit();
+ }
+
+ if (!InTransition()) {
+ ExecSensors(seconds);
+ ExecThrottle(seconds);
+ }
+
+ else if (IsDropping() || IsAttaining() || IsSkipping()) {
+ throttle = 100;
+ }
+
+ if (target && target->Life() == 0) {
+ DropTarget();
+ }
+
+ ExecPhysics(seconds);
+
+ if (!InTransition()) {
+ UpdateTrack();
+ }
+
+ // are we docking?
+ if (IsDropship()) {
+ ListIter<Ship> iter = GetRegion()->Carriers();
+
+ while (++iter) {
+ Ship* carrier_target = iter.value();
+
+ double range = (Location() - carrier_target->Location()).length();
+ if (range > carrier_target->Radius() * 1.5)
+ continue;
+
+ if (carrier_target->GetIFF() == GetIFF() || carrier_target->GetIFF() == 0) {
+ for (int i = 0; i < carrier_target->NumFlightDecks(); i++) {
+ if (carrier_target->GetFlightDeck(i)->Recover(this))
+ break;
+ }
+ }
+ }
+ }
+
+ ExecSystems(seconds);
+ ExecMaintFrame(seconds);
+
+ if (flight_decks.size() > 0) {
+ Camera* global_cam = CameraDirector::GetInstance()->GetCamera();
+ Point global_cam_loc = global_cam->Pos();
+ bool disable_shadows = false;
+
+ for (int i = 0; i < flight_decks.size(); i++) {
+ if (flight_decks[i]->ContainsPoint(global_cam_loc))
+ disable_shadows = true;
+ }
+
+ EnableShadows(!disable_shadows);
+ }
+
+ if (!_finite(Location().x)) {
+ DropTarget();
+ }
+
+ if (!IsStatic() && !IsGroundUnit() && GetFlightModel() < 2)
+ CalcFlightPath();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::LaunchProbe()
+{
+ if (net_observer_mode)
+ return;
+
+ if (sensor_drone) {
+ sensor_drone = 0;
+ }
+
+ if (probe) {
+ sensor_drone = (Drone*) probe->Fire();
+
+ if (sensor_drone)
+ Observe(sensor_drone);
+
+ else if (sim->GetPlayerShip() == this)
+ Button::PlaySound(Button::SND_REJECT);
+ }
+}
+
+void
+Ship::SetProbe(Drone* d)
+{
+ if (sensor_drone != d) {
+ sensor_drone = d;
+
+ if (sensor_drone)
+ Observe(sensor_drone);
+ }
+}
+
+void
+Ship::ExecSensors(double seconds)
+{
+ // how visible are we?
+ DoEMCON();
+
+ // what can we see?
+ if (sensor)
+ sensor->ExecFrame(seconds);
+
+ // can we still see our target?
+ if (target) {
+ int target_found = 0;
+ ListIter<Contact> c_iter = ContactList();
+ while (++c_iter) {
+ Contact* c = c_iter.value();
+
+ if (target == c->GetShip() || target == c->GetShot()) {
+ target_found = 1;
+
+ bool vis = c->Visible(this) || c->Threat(this);
+
+ if (!vis && !c->PasLock() && !c->ActLock())
+ DropTarget();
+ }
+ }
+
+ if (!target_found)
+ DropTarget();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::ExecNavFrame(double seconds)
+{
+ bool auto_pilot = false;
+
+ // update director info string:
+ SetFLCSMode(flcs_mode);
+
+ if (navsys) {
+ navsys->ExecFrame(seconds);
+
+ if (navsys->AutoNavEngaged()) {
+ if (dir && dir->Type() == NavAI::DIR_TYPE) {
+ NavAI* navai = (NavAI*) dir;
+
+ if (navai->Complete()) {
+ navsys->DisengageAutoNav();
+ SetControls(sim->GetControls());
+ }
+ else {
+ auto_pilot = true;
+ }
+ }
+ }
+ }
+
+ // even if we are not on auto pilot,
+ // have we completed the next navpoint?
+
+ Instruction* navpt = GetNextNavPoint();
+ if (navpt && !auto_pilot) {
+ if (navpt->Region() == GetRegion()) {
+ double distance = 0;
+
+ Point npt = navpt->Location();
+
+ if (navpt->Region())
+ npt += navpt->Region()->Location();
+
+ Sim* sim = Sim::GetSim();
+ if (sim->GetActiveRegion())
+ npt -= sim->GetActiveRegion()->Location();
+
+ npt = npt.OtherHand();
+
+ // distance from self to navpt:
+ distance = Point(npt - Location()).length();
+
+ if (distance < 10 * Radius())
+ SetNavptStatus(navpt, Instruction::COMPLETE);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::ExecEvalFrame(double seconds)
+{
+ // is it already too late?
+ if (life == 0 || integrity < 1) return;
+
+ const DWORD EVAL_FREQUENCY = 1000; // once every second
+ static DWORD last_eval_frame = 0; // one ship per game frame
+
+ if (element && element->NumObjectives() > 0 &&
+ Game::GameTime() - last_eval_time > EVAL_FREQUENCY &&
+ last_eval_frame != Game::Frame()) {
+
+ last_eval_time = Game::GameTime();
+ last_eval_frame = Game::Frame();
+
+ for (int i = 0; i < element->NumObjectives(); i++) {
+ Instruction* obj = element->GetObjective(i);
+
+ if (obj->Status() <= Instruction::ACTIVE) {
+ obj->Evaluate(this);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::ExecPhysics(double seconds)
+{
+ if (net_control) {
+ net_control->ExecFrame(seconds);
+ Thrust(seconds); // drive flare
+ }
+ else {
+ thrust = (float) Thrust(seconds);
+ SetupAgility();
+
+ if (seconds > 0) {
+ g_force = 0.0f;
+ }
+
+ if (IsAirborne()) {
+ Point v1 = velocity;
+ AeroFrame(seconds);
+ Point v2 = velocity;
+ Point dv = v2 - v1 + Point(0, g_accel*seconds, 0);
+
+ if (seconds > 0) {
+ g_force = (float) (dv * cam.vup() / seconds) / 9.8f;
+ }
+ }
+
+ else if (IsDying() || flight_model < 2) { // standard and relaxed modes
+ Physical::ExecFrame(seconds);
+ }
+
+ else { // arcade mode
+ Physical::ArcadeFrame(seconds);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::ExecThrottle(double seconds)
+{
+ double spool = 75 * seconds;
+
+ if (throttle < throttle_request)
+ if (throttle_request-throttle < spool)
+ throttle = throttle_request;
+ else
+ throttle += spool;
+
+ else if (throttle > throttle_request)
+ if (throttle - throttle_request < spool)
+ throttle = throttle_request;
+ else
+ throttle -= spool;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::ExecSystems(double seconds)
+{
+ if (!rep)
+ return;
+
+ int i;
+
+ ListIter<System> iter = systems;
+ while (++iter) {
+ System* sys = iter.value();
+
+ sys->Orient(this);
+
+ // sensors have already been executed,
+ // they can not be run twice in a frame!
+ if (sys->Type() != System::SENSOR)
+ sys->ExecFrame(seconds);
+ }
+
+ // hangars and weapon groups are not systems
+ // they must be executed separately from above
+ if (hangar)
+ hangar->ExecFrame(seconds);
+
+ wep_mass = 0.0f;
+ wep_resist = 0.0f;
+
+ bool winchester_cycle = false;
+
+ for (i = 0; i < weapons.size(); i++) {
+ WeaponGroup* w_group = weapons[i];
+ w_group->ExecFrame(seconds);
+
+ if (w_group->GetTrigger() && w_group->GetFiringOrders() == Weapon::MANUAL) {
+
+ Weapon* gun = w_group->GetSelected();
+
+ SimObject* gun_tgt = gun->GetTarget();
+
+ // if no target has been designated for this
+ // weapon, let it guide on the contact closest
+ // to its boresight. this must be done before
+ // firing the weapon.
+
+ if (sensor && gun->Guided() && !gun->Design()->beam && !gun_tgt) {
+ gun->SetTarget(sensor->AcquirePassiveTargetForMissile(), 0);
+ }
+
+ gun->Fire();
+
+ w_group->SetTrigger(false);
+ w_group->CycleWeapon();
+ w_group->CheckAmmo();
+
+ // was that the last shot from this missile group?
+ if (w_group->IsMissile() && w_group->Ammo() < 1) {
+
+ // is this the current secondary weapon group?
+ if (weapons[secondary] == w_group) {
+ winchester_cycle = true;
+ }
+ }
+ }
+
+ wep_mass += w_group->Mass();
+ wep_resist += w_group->Resistance();
+ }
+
+ // if we just fired the last shot in the current secondary
+ // weapon group, auto cycle to another secondary weapon:
+ if (winchester_cycle) {
+ int old_secondary = secondary;
+
+ CycleSecondary();
+
+ // do not winchester-cycle to an A2G missile type,
+ // or a missile that is also out of ammo,
+ // keep going!
+
+ while (secondary != old_secondary) {
+ Weapon* missile = GetSecondary();
+ if (missile && missile->CanTarget(Ship::GROUND_UNITS))
+ CycleSecondary();
+
+ else if (weapons[secondary]->Ammo() < 1)
+ CycleSecondary();
+
+ else
+ break;
+ }
+ }
+
+ mass = (float) design->mass + wep_mass;
+
+ if (IsDropship())
+ agility = (float) design->agility - wep_resist;
+
+ if (shieldRep) {
+ Solid* solid = (Solid*) rep;
+ shieldRep->MoveTo(solid->Location());
+ shieldRep->SetOrientation(solid->Orientation());
+
+ bool bubble = false;
+ if (shield)
+ bubble = shield->ShieldBubble();
+
+ if (shieldRep->ActiveHits()) {
+ shieldRep->Energize(seconds, bubble);
+ shieldRep->Show();
+ }
+ else {
+ shieldRep->Hide();
+ }
+ }
+
+ if (cockpit) {
+ Solid* solid = (Solid*) rep;
+
+ Point cpos = cam.Pos() +
+ cam.vrt() * bridge_vec.x +
+ cam.vpn() * bridge_vec.y +
+ cam.vup() * bridge_vec.z;
+
+ cockpit->MoveTo(cpos);
+ cockpit->SetOrientation(solid->Orientation());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::AeroFrame(double seconds)
+{
+ float g_save = g_accel;
+
+ if (Class() == LCA) {
+ lat_thrust = true;
+ SetGravity(0.0f);
+ }
+
+ if (AltitudeAGL() < Radius()) {
+ SetGravity(0.0f);
+
+ // on the ground/runway?
+ double bottom = 1e9;
+ double tlevel = Location().y - AltitudeAGL();
+
+ // taking off or landing?
+ if (flight_phase < ACTIVE || flight_phase > APPROACH) {
+ if (dock)
+ tlevel = dock->MountLocation().y;
+ }
+
+ if (tlevel < 0)
+ tlevel = 0;
+
+ if (gear)
+ bottom = gear->GetTouchDown()-1;
+ else
+ bottom = Location().y-6;
+
+ if (bottom < tlevel)
+ TranslateBy(Point(0, bottom-tlevel, 0));
+ }
+
+ // MODEL 2: ARCADE
+ if (flight_model >= 2) {
+ Physical::ArcadeFrame(seconds);
+ }
+
+ // MODEL 1: RELAXED
+ else if (flight_model == 1) {
+ Physical::ExecFrame(seconds);
+ }
+
+ // MODEL 0: STANDARD
+ else {
+ // apply drag-torque (i.e. turn ship into
+ // velocity vector to minimize drag):
+
+ Point vnrm = velocity;
+ double v = vnrm.Normalize();
+ double pitch_deflection = vnrm * cam.vup();
+ double yaw_deflection = vnrm * cam.vrt();
+
+ if (lat_thrust && v < 250) {
+ }
+
+ else {
+ if (v < 250) {
+ double factor = 1.2 + (250 - v) / 100;
+
+ ApplyPitch(pitch_deflection * -factor);
+ ApplyYaw(yaw_deflection * factor);
+
+ dp += (float) (dp_acc * seconds);
+ dy += (float) (dy_acc * seconds);
+ }
+
+ else {
+ if (fabs(pitch_deflection) > stall) {
+ ApplyPitch(pitch_deflection * -1.2);
+ dp += (float) (dp_acc * seconds);
+ }
+
+ ApplyYaw(yaw_deflection * 2);
+ dy += (float) (dy_acc * seconds);
+ }
+ }
+
+ // compute rest of physics:
+ Physical::AeroFrame(seconds);
+ }
+
+ SetGravity(g_save);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::LinearFrame(double seconds)
+{
+ Physical::LinearFrame(seconds);
+
+ if (!IsAirborne() || Class() != LCA)
+ return;
+
+ // damp lateral movement in atmosphere:
+
+ // side-to-side
+ if (!trans_x) {
+ Point transvec = cam.vrt();
+ transvec *= (transvec * velocity) * seconds * 0.5;
+ velocity -= transvec;
+ }
+
+ // fore-and-aft
+ if (!trans_y && fabs(thrust < 1)) {
+ Point transvec = cam.vpn();
+ transvec *= (transvec * velocity) * seconds * 0.25;
+ velocity -= transvec;
+ }
+
+ // up-and-down
+ if (!trans_z) {
+ Point transvec = cam.vup();
+ transvec *= (transvec * velocity) * seconds * 0.5;
+ velocity -= transvec;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::DockFrame(double seconds)
+{
+ SelectDetail(seconds);
+
+ if (sim->GetPlayerShip() == this) {
+ // Make sure the thruster sound is diabled
+ // when the player is on the runway or catapult
+ if (thruster) {
+ thruster->ExecTrans(0,0,0);
+ }
+ }
+
+ if (rep) {
+ // Update the graphic rep and light sources:
+ // (This is usually done by the physics class,
+ // but when the ship is in dock, we skip the
+ // standard physics processing):
+ rep->MoveTo(cam.Pos());
+ rep->SetOrientation(cam.Orientation());
+
+ if (light)
+ light->MoveTo(cam.Pos());
+
+ ListIter<System> iter = systems;
+ while (++iter)
+ iter->Orient(this);
+
+ double spool = 75 * seconds;
+
+ if (flight_phase == DOCKING) {
+ throttle_request = 0;
+ throttle = 0;
+ }
+
+ else if (throttle < throttle_request)
+ if (throttle_request-throttle < spool)
+ throttle = throttle_request;
+ else
+ throttle += spool;
+
+ else if (throttle > throttle_request)
+ if (throttle - throttle_request < spool)
+ throttle = throttle_request;
+ else
+ throttle -= spool;
+
+ // make sure there is power to run the drive:
+ for (int i = 0; i < reactors.size(); i++)
+ reactors[i]->ExecFrame(seconds);
+
+ // count up weapon ammo for status mfd:
+ for (i = 0; i < weapons.size(); i++)
+ weapons[i]->ExecFrame(seconds);
+
+ // show drive flare while on catapult:
+ if (main_drive) {
+ main_drive->SetThrottle(throttle);
+
+ if (throttle > 0)
+ main_drive->Thrust(seconds); // show drive flare
+ }
+ }
+
+ if (cockpit && !cockpit->Hidden()) {
+ Solid* solid = (Solid*) rep;
+
+ Point cpos = cam.Pos() +
+ cam.vrt() * bridge_vec.x +
+ cam.vpn() * bridge_vec.y +
+ cam.vup() * bridge_vec.z;
+
+ cockpit->MoveTo(cpos);
+ cockpit->SetOrientation(solid->Orientation());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::StatFrame(double seconds)
+{
+ if (flight_phase != ACTIVE) {
+ flight_phase = ACTIVE;
+ launch_time = Game::GameTime() + 1;
+
+ if (element)
+ element->SetLaunchTime(launch_time);
+ }
+
+ if (IsGroundUnit()) {
+ // glue buildings to the terrain:
+ Point loc = Location();
+ Terrain* terrain = region->GetTerrain();
+
+ if (terrain) {
+ loc.y = terrain->Height(loc.x, loc.z);
+ MoveTo(loc);
+ }
+ }
+
+ if (rep) {
+ rep->MoveTo(cam.Pos());
+ rep->SetOrientation(cam.Orientation());
+ }
+
+ if (light) {
+ light->MoveTo(cam.Pos());
+ }
+
+ ExecSensors(seconds);
+
+ if (target && target->Life() == 0) {
+ DropTarget();
+ }
+
+ if (dir) dir->ExecFrame(seconds);
+
+ SelectDetail(seconds);
+
+ int i = 0;
+
+ if (rep) {
+ ListIter<System> iter = systems;
+ while (++iter)
+ iter->Orient(this);
+
+ for (i = 0; i < reactors.size(); i++)
+ reactors[i]->ExecFrame(seconds);
+
+ for (i = 0; i < navlights.size(); i++)
+ navlights[i]->ExecFrame(seconds);
+
+ for (i = 0; i < weapons.size(); i++)
+ weapons[i]->ExecFrame(seconds);
+
+ if (farcaster) {
+ farcaster->ExecFrame(seconds);
+
+ if (navlights.size() == 2) {
+ if (farcaster->Charge() > 99) {
+ navlights[0]->Enable();
+ navlights[1]->Disable();
+ }
+ else {
+ navlights[0]->Disable();
+ navlights[1]->Enable();
+ }
+ }
+ }
+
+ if (shield)
+ shield->ExecFrame(seconds);
+
+ if (hangar)
+ hangar->ExecFrame(seconds);
+
+ if (flight_decks.size() > 0) {
+ Camera* global_cam = CameraDirector::GetInstance()->GetCamera();
+ Point global_cam_loc = global_cam->Pos();
+ bool disable_shadows = false;
+
+ for (i = 0; i < flight_decks.size(); i++) {
+ flight_decks[i]->ExecFrame(seconds);
+
+ if (flight_decks[i]->ContainsPoint(global_cam_loc))
+ disable_shadows = true;
+ }
+
+ EnableShadows(!disable_shadows);
+ }
+ }
+
+ if (shieldRep) {
+ Solid* solid = (Solid*) rep;
+ shieldRep->MoveTo(solid->Location());
+ shieldRep->SetOrientation(solid->Orientation());
+
+ bool bubble = false;
+ if (shield)
+ bubble = shield->ShieldBubble();
+
+ if (shieldRep->ActiveHits()) {
+ shieldRep->Energize(seconds, bubble);
+ shieldRep->Show();
+ }
+ else {
+ shieldRep->Hide();
+ }
+ }
+
+ if (!_finite(Location().x)) {
+ DropTarget();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Graphic*
+Ship::Cockpit() const
+{
+ return cockpit;
+}
+
+void
+Ship::ShowCockpit()
+{
+ if (cockpit) {
+ cockpit->Show();
+
+ ListIter<WeaponGroup> g = weapons;
+ while (++g) {
+ ListIter<Weapon> w = g->GetWeapons();
+ while (++w) {
+ Solid* turret = w->GetTurret();
+ if (turret) {
+ turret->Show();
+
+ Solid* turret_base = w->GetTurretBase();
+ if (turret_base)
+ turret_base->Show();
+ }
+
+ if (w->IsMissile()) {
+ for (int i = 0; i < w->Ammo(); i++) {
+ Solid* store = w->GetVisibleStore(i);
+ if (store) {
+ store->Show();
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+Ship::HideCockpit()
+{
+ if (cockpit)
+ cockpit->Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::SelectDetail(double seconds)
+{
+ detail.ExecFrame(seconds);
+ detail.SetLocation(GetRegion(), Location());
+
+ int new_level = detail.GetDetailLevel();
+
+ if (detail_level != new_level) {
+ Scene* scene = 0;
+
+ // remove current rep from scene (if necessary):
+ for (int i = 0; i < detail.NumModels(detail_level); i++) {
+ Graphic* g = detail.GetRep(detail_level, i);
+ if (g) {
+ scene = g->GetScene();
+ if (scene)
+ scene->DelGraphic(g);
+ }
+ }
+
+ // switch to new rep:
+ detail_level = new_level;
+ rep = detail.GetRep(detail_level);
+
+ // add new rep to scene (if necessary):
+ if (scene) {
+ for (int i = 0; i < detail.NumModels(detail_level); i++) {
+ Graphic* g = detail.GetRep(detail_level, i);
+ Point s = detail.GetSpin(detail_level, i);
+ Matrix m = cam.Orientation();
+
+ m.Pitch(s.x);
+ m.Yaw(s.z);
+ m.Roll(s.y);
+
+ scene->AddGraphic(g);
+ g->MoveTo(cam.Pos() + detail.GetOffset(detail_level, i));
+ g->SetOrientation(m);
+ }
+
+ // show/hide external stores and landing gear...
+ if (detail.NumLevels() > 0) {
+ if (gear && (gear->GetState() != LandingGear::GEAR_UP)) {
+ for (int i = 0; i < gear->NumGear(); i++) {
+ Solid* g = gear->GetGear(i);
+
+ if (g) {
+ if (detail_level == 0)
+ scene->DelGraphic(g);
+ else
+ scene->AddGraphic(g);
+ }
+ }
+ }
+
+ ListIter<WeaponGroup> g = weapons;
+ while (++g) {
+ ListIter<Weapon> w = g->GetWeapons();
+ while (++w) {
+ Solid* turret = w->GetTurret();
+ if (turret) {
+ if (detail_level == 0)
+ scene->DelGraphic(turret);
+ else
+ scene->AddGraphic(turret);
+
+ Solid* turret_base = w->GetTurretBase();
+ if (turret_base) {
+ if (detail_level == 0)
+ scene->DelGraphic(turret_base);
+ else
+ scene->AddGraphic(turret_base);
+ }
+ }
+ if (w->IsMissile()) {
+ for (i = 0; i < w->Ammo(); i++) {
+ Solid* store = w->GetVisibleStore(i);
+ if (store) {
+ if (detail_level == 0)
+ scene->DelGraphic(store);
+ else
+ scene->AddGraphic(store);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ else {
+ int nmodels = detail.NumModels(detail_level);
+
+ if (nmodels > 1) {
+ for (int i = 0; i < nmodels; i++) {
+ Graphic* g = detail.GetRep(detail_level, i);
+ Point s = detail.GetSpin(detail_level, i);
+ Matrix m = cam.Orientation();
+
+ m.Pitch(s.x);
+ m.Yaw(s.z);
+ m.Roll(s.y);
+
+ g->MoveTo(cam.Pos() + detail.GetOffset(detail_level, i));
+ g->SetOrientation(m);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::ShowRep()
+{
+ for (int i = 0; i < detail.NumModels(detail_level); i++) {
+ Graphic* g = detail.GetRep(detail_level, i);
+ g->Show();
+ }
+
+ if (gear && (gear->GetState() != LandingGear::GEAR_UP)) {
+ for (int i = 0; i < gear->NumGear(); i++) {
+ Solid* g = gear->GetGear(i);
+ if (g) g->Show();
+ }
+ }
+
+ ListIter<WeaponGroup> g = weapons;
+ while (++g) {
+ ListIter<Weapon> w = g->GetWeapons();
+ while (++w) {
+ Solid* turret = w->GetTurret();
+ if (turret) {
+ turret->Show();
+
+ Solid* turret_base = w->GetTurretBase();
+ if (turret_base)
+ turret_base->Show();
+ }
+
+ if (w->IsMissile()) {
+ for (i = 0; i < w->Ammo(); i++) {
+ Solid* store = w->GetVisibleStore(i);
+ if (store) {
+ store->Show();
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+Ship::HideRep()
+{
+ for (int i = 0; i < detail.NumModels(detail_level); i++) {
+ Graphic* g = detail.GetRep(detail_level, i);
+ g->Hide();
+ }
+
+ if (gear && (gear->GetState() != LandingGear::GEAR_UP)) {
+ for (int i = 0; i < gear->NumGear(); i++) {
+ Solid* g = gear->GetGear(i);
+ if (g) g->Hide();
+ }
+ }
+
+ ListIter<WeaponGroup> g = weapons;
+ while (++g) {
+ ListIter<Weapon> w = g->GetWeapons();
+ while (++w) {
+ Solid* turret = w->GetTurret();
+ if (turret) {
+ turret->Hide();
+
+ Solid* turret_base = w->GetTurretBase();
+ if (turret_base)
+ turret_base->Hide();
+ }
+
+ if (w->IsMissile()) {
+ for (i = 0; i < w->Ammo(); i++) {
+ Solid* store = w->GetVisibleStore(i);
+ if (store) {
+ store->Hide();
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+Ship::EnableShadows(bool enable)
+{
+ for (int i = 0; i < detail.NumModels(detail_level); i++) {
+ Graphic* g = detail.GetRep(detail_level, i);
+
+ if (g->IsSolid()) {
+ Solid* s = (Solid*) g;
+
+ ListIter<Shadow> iter = s->GetShadows();
+ while (++iter) {
+ Shadow* shadow = iter.value();
+ shadow->SetEnabled(enable);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Ship::Update(SimObject* obj)
+{
+ if (obj == ward)
+ ward = 0;
+
+ if (obj == target) {
+ target = 0;
+ subtarget = 0;
+ }
+
+ if (obj == carrier) {
+ carrier = 0;
+ dock = 0;
+ inbound = 0;
+ }
+
+ if (obj->Type() == SimObject::SIM_SHOT ||
+ obj->Type() == SimObject::SIM_DRONE) {
+ Shot* s = (Shot*) obj;
+
+ if (sensor_drone == s)
+ sensor_drone = 0;
+
+ if (decoy_list.contains(s))
+ decoy_list.remove(s);
+
+ if (threat_list.contains(s))
+ threat_list.remove(s);
+ }
+
+ return SimObserver::Update(obj);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Ship::GetFuelLevel() const
+{
+ if (reactors.size() > 0) {
+ PowerSource* reactor = reactors[0];
+ if (reactor)
+ return reactor->Charge();
+ }
+
+ return 0;
+}
+
+void
+Ship::SetThrottle(double percent)
+{
+ throttle_request = percent;
+
+ if (throttle_request < 0) throttle_request = 0;
+ else if (throttle_request > 100) throttle_request = 100;
+
+ if (throttle_request < 50)
+ augmenter = false;
+}
+
+void
+Ship::SetAugmenter(bool enable)
+{
+ if (throttle <= 50)
+ enable = false;
+
+ if (main_drive && main_drive->MaxAugmenter() <= 0)
+ enable = false;
+
+ augmenter = enable;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::SetTransition(double trans_time, int trans_type, const Point& trans_loc)
+{
+ transition_time = (float) trans_time;
+ transition_type = trans_type;
+ transition_loc = trans_loc;
+}
+
+void
+Ship::DropOrbit()
+{
+ if (IsDropship() && transition_type == TRANSITION_NONE && !IsAirborne()) {
+ SimRegion* dst_rgn = sim->FindNearestTerrainRegion(this);
+
+ if (dst_rgn &&
+ dst_rgn->GetOrbitalRegion()->Primary() ==
+ GetRegion()->GetOrbitalRegion()->Primary()) {
+
+ transition_time = 10.0f;
+ transition_type = TRANSITION_DROP_ORBIT;
+ transition_loc = Location() + Heading() * (-2*Radius());
+
+ RadioTraffic::SendQuickMessage(this, RadioMessage::BREAK_ORBIT);
+ SetControls(0);
+ }
+ }
+}
+
+void
+Ship::MakeOrbit()
+{
+ if (IsDropship() && transition_type == TRANSITION_NONE && IsAirborne()) {
+ transition_time = 5.0f;
+ transition_type = TRANSITION_MAKE_ORBIT;
+ transition_loc = Location() + Heading() * (-2*Radius());
+
+ RadioTraffic::SendQuickMessage(this, RadioMessage::MAKE_ORBIT);
+ SetControls(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Ship::IsInCombat()
+{
+ if (IsRogue())
+ return true;
+
+ bool combat = false;
+
+ ListIter<Contact> c_iter = ContactList();
+ while (++c_iter) {
+ Contact* c = c_iter.value();
+ Ship* cship = c->GetShip();
+ int ciff = c->GetIFF(this);
+ Point delta = c->Location() - Location();
+ double dist = delta.length();
+
+ if (c->Threat(this) && !cship) {
+ if (IsStarship())
+ combat = dist < 120e3;
+ else
+ combat = dist < 60e3;
+ }
+
+ else if (cship && ciff > 0 && ciff != GetIFF()) {
+ if (IsStarship() && cship->IsStarship())
+ combat = dist < 120e3;
+ else
+ combat = dist < 60e3;
+ }
+ }
+
+ return combat;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Ship::CanTimeSkip()
+{
+ bool go = false;
+ Instruction* navpt = GetNextNavPoint();
+
+ if (MissionClock() < 10000 || NetGame::IsNetGame())
+ return go;
+
+ if (navpt) {
+ go = true;
+
+ if (navpt->Region() != GetRegion())
+ go = false;
+
+ else if (Point(navpt->Location().OtherHand() - Location()).length() < 30e3)
+ go = false;
+ }
+
+ if (go)
+ go = !IsInCombat();
+
+ return go;
+}
+
+void
+Ship::TimeSkip()
+{
+ if (CanTimeSkip()) {
+ // go back to regular time before performing the skip:
+ Game::SetTimeCompression(1);
+
+ transition_time = 7.5f;
+ transition_type = TRANSITION_TIME_SKIP;
+ transition_loc = Location() + Heading() * (Velocity().length() * 4);
+ // 2500; //(8*Radius());
+
+ if (rand() < 16000)
+ transition_loc += BeamLine() * (2.5*Radius());
+ else
+ transition_loc += BeamLine() * (-2 *Radius());
+
+ if (rand() < 8000)
+ transition_loc += LiftLine() * (-1*Radius());
+ else
+ transition_loc += LiftLine() * (1.8*Radius());
+
+ SetControls(0);
+ }
+
+ else if (sim->GetPlayerShip() == this) {
+ SetAutoNav(true);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::DropCam(double time, double range)
+{
+ transition_type = TRANSITION_DROP_CAM;
+
+ if (time > 0)
+ transition_time = (float) time;
+ else
+ transition_time = 10.0f;
+
+ Point offset = Heading() * (Velocity().length() * 5);
+ double lateral_offset = 2 * Radius();
+ double vertical_offset = Radius();
+
+ if (vertical_offset > 300)
+ vertical_offset = 300;
+
+ if (rand() < 16000)
+ lateral_offset *= -1;
+
+ if (rand() < 8000)
+ vertical_offset *= -1;
+
+ offset += BeamLine() * lateral_offset;
+ offset += LiftLine() * vertical_offset;
+
+ if (range > 0)
+ offset *= range;
+
+ transition_loc = Location() + offset;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::DeathSpiral()
+{
+ if (!killer)
+ killer = new(__FILE__,__LINE__) ShipKiller(this);
+
+ ListIter<System> iter = systems;
+ while (++iter)
+ iter->PowerOff();
+
+ // transfer arcade velocity to newtonian velocity:
+ if (flight_model >= 2) {
+ velocity += arcade_velocity;
+ }
+
+ if (GetIFF() < 100 && !IsGroundUnit()) {
+ RadioTraffic::SendQuickMessage(this, RadioMessage::DISTRESS);
+ }
+
+ transition_type = TRANSITION_DEATH_SPIRAL;
+
+ killer->BeginDeathSpiral();
+
+ transition_time = killer->TransitionTime();
+ transition_loc = killer->TransitionLoc();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::CompleteTransition()
+{
+ int old_type = transition_type;
+ transition_time = 0.0f;
+ transition_type = TRANSITION_NONE;
+
+ switch (old_type) {
+ case TRANSITION_NONE:
+ case TRANSITION_DROP_CAM:
+ default:
+ return;
+
+ case TRANSITION_DROP_ORBIT: {
+ SetControls(0);
+ SimRegion* dst_rgn = sim->FindNearestTerrainRegion(this);
+ Point dst_loc = Location().OtherHand() * 0.20;
+ dst_loc.x += 6000 * GetElementIndex();
+ dst_loc.z = TERRAIN_ALTITUDE_LIMIT * 0.95;
+ dst_loc += RandomDirection() * 2e3;
+
+ sim->RequestHyperJump(this, dst_rgn, dst_loc, TRANSITION_DROP_ORBIT);
+
+ ShipStats* stats = ShipStats::Find(Name());
+ stats->AddEvent(SimEvent::BREAK_ORBIT, dst_rgn->Name());
+ }
+ break;
+
+ case TRANSITION_MAKE_ORBIT: {
+ SetControls(0);
+ SimRegion* dst_rgn = sim->FindNearestSpaceRegion(this);
+ double dist = 200.0e3 + 10.0e3 * GetElementIndex();
+ Point esc_vec = dst_rgn->GetOrbitalRegion()->Location() -
+ dst_rgn->GetOrbitalRegion()->Primary()->Location();
+
+ esc_vec.z = -100 * GetElementIndex();
+ esc_vec.Normalize();
+ esc_vec *= -dist;
+ esc_vec += RandomDirection() * 2e3;
+
+ sim->RequestHyperJump(this, dst_rgn, esc_vec, TRANSITION_MAKE_ORBIT);
+
+ ShipStats* stats = ShipStats::Find(Name());
+ stats->AddEvent(SimEvent::MAKE_ORBIT, dst_rgn->Name());
+ }
+ break;
+
+ case TRANSITION_TIME_SKIP: {
+ Instruction* navpt = GetNextNavPoint();
+
+ if (navpt) {
+ Point delta = navpt->Location().OtherHand() - Location();
+ Point unit = delta; unit.Normalize();
+ Point trans = delta + unit * -20e3;
+ double dist = trans.length();
+ double speed = navpt->Speed();
+
+ if (speed < 50) speed = 500;
+
+ double etr = dist / speed;
+
+ sim->ResolveTimeSkip(etr);
+ }
+ }
+ break;
+
+ case TRANSITION_DEATH_SPIRAL:
+ SetControls(0);
+ transition_type = TRANSITION_DEAD;
+ break;
+ }
+
+}
+
+bool
+Ship::IsAirborne() const
+{
+ if (region)
+ return region->Type() == SimRegion::AIR_SPACE;
+
+ return false;
+}
+
+double
+Ship::CompassHeading() const
+{
+ Point heading = Heading();
+ double compass_heading = atan2(fabs(heading.x), heading.z);
+
+ if (heading.x < 0)
+ compass_heading *= -1;
+
+ double result = compass_heading + PI;
+
+ if (result >= 2*PI)
+ result -= 2*PI;
+
+ return result;
+}
+
+double
+Ship::CompassPitch() const
+{
+ Point heading = Heading();
+ return asin(heading.y);
+}
+
+double
+Ship::AltitudeMSL() const
+{
+ return Location().y;
+}
+
+double
+Ship::AltitudeAGL() const
+{
+ if (altitude_agl < -1000) {
+ Ship* pThis = (Ship*) this; // cast-away const
+ Point loc = Location();
+
+ Terrain* terrain = region->GetTerrain();
+
+ if (terrain)
+ pThis->altitude_agl = (float) (loc.y - terrain->Height(loc.x, loc.z));
+
+ else
+ pThis->altitude_agl = (float) loc.y;
+
+ if (!_finite(altitude_agl)) {
+ pThis->altitude_agl = 0.0f;
+ }
+ }
+
+ return altitude_agl;
+}
+
+double
+Ship::GForce() const
+{
+ return g_force;
+}
+
+// +--------------------------------------------------------------------+
+
+WeaponGroup*
+Ship::FindWeaponGroup(const char* name)
+{
+ WeaponGroup* group = 0;
+
+ ListIter<WeaponGroup> iter = weapons;
+ while (!group && ++iter)
+ if (!stricmp(iter->Name(), name))
+ group = iter.value();
+
+ if (!group) {
+ group = new(__FILE__,__LINE__) WeaponGroup(name);
+ weapons.append(group);
+ }
+
+ return group;
+}
+
+void
+Ship::SelectWeapon(int n, int w)
+{
+ if (n < weapons.size())
+ weapons[n]->SelectWeapon(w);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::CyclePrimary()
+{
+ if (weapons.isEmpty())
+ return;
+
+ if (IsDropship() && primary < weapons.size()) {
+ WeaponGroup* p = weapons[primary];
+ Weapon* w = p->GetSelected();
+
+ if (w && w->GetTurret()) {
+ p->SetFiringOrders(Weapon::POINT_DEFENSE);
+ }
+ }
+
+ int n = primary + 1;
+ while (n != primary) {
+ if (n >= weapons.size())
+ n = 0;
+
+ if (weapons[n]->IsPrimary()) {
+ weapons[n]->SetFiringOrders(Weapon::MANUAL);
+ break;
+ }
+
+ n++;
+ }
+
+ primary = n;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::CycleSecondary()
+{
+ if (weapons.isEmpty())
+ return;
+
+ int n = secondary + 1;
+ while (n != secondary) {
+ if (n >= weapons.size())
+ n = 0;
+
+ if (weapons[n]->IsMissile())
+ break;
+
+ n++;
+ }
+
+ secondary = n;
+
+ // automatically switch sensors to appropriate mode:
+ if (IsAirborne()) {
+ Weapon* missile = GetSecondary();
+ if (missile && missile->CanTarget(Ship::GROUND_UNITS))
+ SetSensorMode(Sensor::GM);
+ else if (sensor && sensor->GetMode() == Sensor::GM)
+ SetSensorMode(Sensor::STD);
+ }
+}
+
+int
+Ship::GetMissileEta(int index) const
+{
+ if (index >= 0 && index < 4)
+ return missile_eta[index];
+
+ return 0;
+}
+
+void
+Ship::SetMissileEta(int id, int eta)
+{
+ int index = -1;
+
+ // are we tracking this missile's eta?
+ for (int i = 0; i < 4; i++)
+ if (id == missile_id[i])
+ index = i;
+
+ // if not, can we find an open slot to track it in?
+ if (index < 0) {
+ for (int i = 0; i < 4 && index < 0; i++) {
+ if (missile_eta[i] == 0) {
+ index = i;
+ missile_id[i] = id;
+ }
+ }
+ }
+
+ // track the eta:
+ if (index >= 0 && index < 4) {
+ if (eta > 3599)
+ eta = 3599;
+
+ missile_eta[index] = (BYTE) eta;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::DoEMCON()
+{
+ ListIter<System> iter = systems;
+ while (++iter) {
+ System* s = iter.value();
+ s->DoEMCON(emcon);
+ }
+
+ old_emcon = emcon;
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Ship::Thrust(double seconds) const
+{
+ double total_thrust = 0;
+
+ if (main_drive) {
+ // velocity limiter:
+ Point H = Heading();
+ Point V = Velocity();
+ double vmag = V.Normalize();
+ double eff_throttle = throttle;
+ double thrust_factor = 1;
+ double vfwd = H * V;
+ bool aug_on = main_drive->IsAugmenterOn();
+
+ if (vmag > vlimit && vfwd > 0) {
+ double vmax = vlimit;
+ if (aug_on)
+ vmax *= 1.5;
+
+ vfwd = 0.5 * vfwd + 0.5;
+
+ // reduce drive efficiency at high fwd speed:
+ thrust_factor = (vfwd * pow(vmax,3) / pow(vmag,3)) + (1-vfwd);
+ }
+
+ if (flcs)
+ eff_throttle = flcs->Throttle();
+
+ // square-law throttle curve to increase sensitivity
+ // at lower throttle settings:
+ if (flight_model > 1) {
+ eff_throttle /= 100;
+ eff_throttle *= eff_throttle;
+ eff_throttle *= 100;
+ }
+
+ main_drive->SetThrottle(eff_throttle, augmenter);
+ total_thrust += thrust_factor * main_drive->Thrust(seconds);
+
+ if (aug_on && shake < 1.5)
+ ((Ship*) this)->shake = 1.5f;
+ }
+
+ return total_thrust;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::CycleFLCSMode()
+{
+ switch (flcs_mode) {
+ case FLCS_MANUAL: SetFLCSMode(FLCS_HELM); break;
+ case FLCS_AUTO: SetFLCSMode(FLCS_MANUAL); break;
+ case FLCS_HELM: SetFLCSMode(FLCS_AUTO); break;
+
+ default:
+ if (IsStarship())
+ flcs_mode = (BYTE) FLCS_HELM;
+ else
+ flcs_mode = (BYTE) FLCS_AUTO;
+ break;
+ }
+
+ // reset helm heading to compass heading when switching
+ // back to helm mode from manual mode:
+
+ if (flcs_mode == FLCS_HELM) {
+ if (IsStarship()) {
+ SetHelmHeading(CompassHeading());
+ SetHelmPitch(CompassPitch());
+ }
+ else {
+ flcs_mode = (BYTE) FLCS_AUTO;
+ }
+ }
+}
+
+void
+Ship::SetFLCSMode(int mode)
+{
+ flcs_mode = (BYTE) mode;
+
+ if (IsAirborne())
+ flcs_mode = (BYTE) FLCS_MANUAL;
+
+ if (dir && dir->Type() < SteerAI::SEEKER) {
+ switch (flcs_mode) {
+ case FLCS_MANUAL: director_info = Game::GetText("flcs.manual"); break;
+ case FLCS_AUTO: director_info = Game::GetText("flcs.auto"); break;
+ case FLCS_HELM: director_info = Game::GetText("flcs.helm"); break;
+ default: director_info = Game::GetText("flcs.fault"); break;
+ }
+
+ if (!flcs || !flcs->IsPowerOn())
+ director_info = Game::GetText("flcs.offline");
+
+ else if (IsAirborne())
+ director_info = Game::GetText("flcs.atmospheric");
+ }
+
+ if (flcs)
+ flcs->SetMode(mode);
+}
+
+int
+Ship::GetFLCSMode() const
+{
+ return (int) flcs_mode;
+}
+
+void
+Ship::SetTransX(double t)
+{
+ float limit = design->trans_x;
+
+ if (thruster)
+ limit = (float) thruster->TransXLimit();
+
+ trans_x = (float) t;
+
+ if (trans_x) {
+ if (trans_x > limit)
+ trans_x = limit;
+ else if (trans_x < -limit)
+ trans_x = -limit;
+
+ // reduce thruster efficiency at high fwd speed:
+ double vfwd = cam.vrt() * Velocity();
+ double vmag = fabs(vfwd);
+ if (vmag > vlimit) {
+ if (trans_x > 0 && vfwd > 0 || trans_x < 0 && vfwd < 0)
+ trans_x *= (float) (pow(vlimit,4) / pow(vmag,4));
+ }
+ }
+}
+
+void
+Ship::SetTransY(double t)
+{
+ float limit = design->trans_y;
+
+ if (thruster)
+ limit = (float) thruster->TransYLimit();
+
+ trans_y = (float) t;
+
+ if (trans_y) {
+ double vmag = Velocity().length();
+
+ if (trans_y > limit)
+ trans_y = limit;
+ else if (trans_y < -limit)
+ trans_y = -limit;
+
+ // reduce thruster efficiency at high fwd speed:
+ if (vmag > vlimit) {
+ double vfwd = cam.vpn() * Velocity();
+
+ if (trans_y > 0 && vfwd > 0 || trans_y < 0 && vfwd < 0)
+ trans_y *= (float) (pow(vlimit,4) / pow(vmag,4));
+ }
+ }
+}
+
+void
+Ship::SetTransZ(double t)
+{
+ float limit = design->trans_z;
+
+ if (thruster)
+ limit = (float) thruster->TransZLimit();
+
+ trans_z = (float) t;
+
+ if (trans_z) {
+
+ if (trans_z > limit)
+ trans_z = limit;
+ else if (trans_z < -limit)
+ trans_z = -limit;
+
+ // reduce thruster efficiency at high fwd speed:
+ double vfwd = cam.vup() * Velocity();
+ double vmag = fabs(vfwd);
+ if (vmag > vlimit) {
+ if (trans_z > 0 && vfwd > 0 || trans_z < 0 && vfwd < 0)
+ trans_z *= (float) (pow(vlimit,4) / pow(vmag,4));
+ }
+ }
+}
+
+void
+Ship::ExecFLCSFrame()
+{
+ if (flcs)
+ flcs->ExecSubFrame();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::SetHelmHeading(double h)
+{
+ while (h < 0)
+ h += 2*PI;
+
+ while (h >= 2*PI)
+ h -= 2*PI;
+
+ helm_heading = (float) h;
+}
+
+void
+Ship::SetHelmPitch(double p)
+{
+ const double PITCH_LIMIT = 80 * DEGREES;
+
+ if (p < -PITCH_LIMIT)
+ p = -PITCH_LIMIT;
+
+ else if (p > PITCH_LIMIT)
+ p = PITCH_LIMIT;
+
+ helm_pitch = (float) p;
+}
+
+void
+Ship::ApplyHelmYaw(double y)
+{
+ // rotate compass into helm-relative orientation:
+ double compass = CompassHeading() - helm_heading;
+ double turn = y * PI/4;
+
+ if (compass > PI)
+ compass -= 2*PI;
+ else if (compass < -PI)
+ compass += 2*PI;
+
+ // if requested turn is more than 170, reject it:
+ if (fabs(compass + turn) > 170*DEGREES)
+ return;
+
+ SetHelmHeading(helm_heading + turn);
+}
+
+void
+Ship::ApplyHelmPitch(double p)
+{
+ SetHelmPitch(helm_pitch - p * PI/4);
+}
+
+void
+Ship::ApplyPitch(double p)
+{
+ if (flight_model == 0) { // standard flight model
+ if (IsAirborne())
+ p *= 0.5;
+
+ // command for pitch up is negative
+ if (p < 0) {
+ if (alpha > PI/6) {
+ p *= 0.05;
+ }
+ else if (g_force > 12.0) {
+ double limit = 0.5 - (g_force - 12.0)/10.0;
+
+ if (limit < 0)
+ p = 0;
+ else
+ p *= limit;
+ }
+ }
+
+ // command for pitch down is positive
+ else if (p > 0) {
+ if (alpha < -PI/8) {
+ p *= 0.05;
+ }
+ else if (g_force < -3) {
+ p *= 0.1;
+ }
+ }
+ }
+
+ Physical::ApplyPitch(p);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Ship::FireWeapon(int n)
+{
+ bool fired = false;
+
+ if (n >= 0 && !CheckFire()) {
+ if (n < 4)
+ trigger[n] = true;
+
+ if (n < weapons.size()) {
+ weapons[n]->SetTrigger(true);
+ fired = weapons[n]->GetTrigger();
+ }
+ }
+
+ if (!fired && sim->GetPlayerShip() == this)
+ Button::PlaySound(Button::SND_REJECT);
+
+ return fired;
+}
+
+bool
+Ship::FireDecoy()
+{
+ Shot* drone = 0;
+
+ if (decoy && !CheckFire()) {
+ drone = decoy->Fire();
+
+ if (drone) {
+ Observe(drone);
+ decoy_list.append(drone);
+ }
+ }
+
+ if (sim->GetPlayerShip() == this) {
+ if (NetGame::IsNetGame()) {
+ if (decoy && decoy->Ammo() < 1)
+ Button::PlaySound(Button::SND_REJECT);
+ }
+
+ else if (!drone) {
+ Button::PlaySound(Button::SND_REJECT);
+ }
+ }
+
+ return drone != 0;
+}
+
+void
+Ship::AddActiveDecoy(Drone* drone)
+{
+ if (drone) {
+ Observe(drone);
+ decoy_list.append(drone);
+ }
+}
+
+Weapon*
+Ship::GetPrimary() const
+{
+ if (weapons.size() > primary)
+ return weapons[primary]->GetSelected();
+ return 0;
+}
+
+Weapon*
+Ship::GetSecondary() const
+{
+ if (weapons.size() > secondary)
+ return weapons[secondary]->GetSelected();
+ return 0;
+}
+
+Weapon*
+Ship::GetWeaponByIndex(int n)
+{
+ for (int i = 0; i < weapons.size(); i++) {
+ WeaponGroup* g = weapons[i];
+
+ List<Weapon>& wlist = g->GetWeapons();
+ for (int j = 0; j < wlist.size(); j++) {
+ Weapon* w = wlist[j];
+
+ if (w->GetIndex() == n) {
+ return w;
+ }
+ }
+ }
+
+ return 0;
+}
+
+WeaponGroup*
+Ship::GetPrimaryGroup() const
+{
+ if (weapons.size() > primary)
+ return weapons[primary];
+ return 0;
+}
+
+WeaponGroup*
+Ship::GetSecondaryGroup() const
+{
+ if (weapons.size() > secondary)
+ return weapons[secondary];
+ return 0;
+}
+
+WeaponDesign*
+Ship::GetPrimaryDesign() const
+{
+ if (weapons.size() > primary)
+ return (WeaponDesign*) weapons[primary]->GetSelected()->Design();
+ return 0;
+}
+
+WeaponDesign*
+Ship::GetSecondaryDesign() const
+{
+ if (weapons.size() > secondary)
+ return (WeaponDesign*) weapons[secondary]->GetSelected()->Design();
+ return 0;
+}
+
+Weapon*
+Ship::GetDecoy() const
+{
+ return decoy;
+}
+
+List<Shot>&
+Ship::GetActiveDecoys()
+{
+ return decoy_list;
+}
+
+List<Shot>&
+Ship::GetThreatList()
+{
+ return threat_list;
+}
+
+void
+Ship::AddThreat(Shot* s)
+{
+ if (!threat_list.contains(s)) {
+ Observe(s);
+ threat_list.append(s);
+ }
+}
+
+void
+Ship::DropThreat(Shot* s)
+{
+ if (threat_list.contains(s)) {
+ threat_list.remove(s);
+ }
+}
+
+bool
+Ship::GetTrigger(int i) const
+{
+ if (i >= 0) {
+ if (i < 4)
+ return trigger[i];
+
+ else if (i < weapons.size())
+ return weapons[i]->GetTrigger();
+ }
+
+ return false;
+}
+
+void
+Ship::SetTrigger(int i)
+{
+ if (i >= 0 && !CheckFire()) {
+ if (i < 4)
+ trigger[i] = true;
+
+ if (i < weapons.size())
+ weapons[i]->SetTrigger();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::SetSensorMode(int mode)
+{
+ if (sensor)
+ sensor->SetMode((Sensor::Mode) mode);
+}
+
+int
+Ship::GetSensorMode() const
+{
+ if (sensor)
+ return (int) sensor->GetMode();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Ship::IsTracking(SimObject* tgt)
+{
+ if (tgt && sensor)
+ return sensor->IsTracking(tgt);
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::LockTarget(int type, bool closest, bool hostile)
+{
+ if (sensor)
+ SetTarget(sensor->LockTarget(type, closest, hostile));
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::LockTarget(SimObject* candidate)
+{
+ if (sensor)
+ SetTarget(sensor->LockTarget(candidate));
+ else
+ SetTarget(candidate);
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Ship::InflictDamage(double damage, Shot* shot, int hit_type, Point impact)
+{
+ double damage_applied = 0;
+
+ if (Game::Paused() || IsNetObserver() || IsInvulnerable())
+ return damage_applied;
+
+ if (Integrity() == 0) // already dead?
+ return damage_applied;
+
+ const double MAX_SHAKE = 7;
+ double hull_damage = damage;
+ bool hit_shield = (hit_type & HIT_SHIELD) != 0;
+ bool hit_hull = (hit_type & HIT_HULL) != 0;
+ bool hit_turret = (hit_type & HIT_TURRET) != 0;
+
+ if (impact == Point(0,0,0))
+ impact = Location();
+
+ if (hit_shield && ShieldStrength() > 0) {
+ hull_damage = shield->DeflectDamage(shot, damage);
+
+ if (shot) {
+ if (shot->IsBeam()) {
+ if (design->beam_hit_sound_resource) {
+ if (Game::RealTime() - last_beam_time > 400) {
+ Sound* s = design->beam_hit_sound_resource->Duplicate();
+ s->SetLocation(impact);
+ s->SetVolume(AudioConfig::EfxVolume());
+ s->Play();
+
+ last_beam_time = Game::RealTime();
+ }
+ }
+ }
+
+ else {
+ if (design->bolt_hit_sound_resource) {
+ if (Game::RealTime() - last_bolt_time > 400) {
+ Sound* s = design->bolt_hit_sound_resource->Duplicate();
+ s->SetLocation(impact);
+ s->SetVolume(AudioConfig::EfxVolume());
+ s->Play();
+
+ last_bolt_time = Game::RealTime();
+ }
+ }
+ }
+ }
+ }
+
+ if (hit_hull) {
+ hull_damage = InflictSystemDamage(hull_damage, shot, impact);
+
+ int damage_type = WeaponDesign::DMG_NORMAL;
+
+ if (shot && shot->Design())
+ damage_type = shot->Design()->damage_type;
+
+ if (damage_type == WeaponDesign::DMG_NORMAL) {
+ damage_applied = hull_damage;
+ Physical::InflictDamage(damage_applied, 0);
+ NetUtil::SendObjDamage(this, damage_applied, shot);
+ }
+ }
+
+ else if (hit_turret) {
+ hull_damage = InflictSystemDamage(hull_damage, shot, impact) * 0.3;
+
+ int damage_type = WeaponDesign::DMG_NORMAL;
+
+ if (shot && shot->Design())
+ damage_type = shot->Design()->damage_type;
+
+ if (damage_type == WeaponDesign::DMG_NORMAL) {
+ damage_applied = hull_damage;
+ Physical::InflictDamage(damage_applied, 0);
+ NetUtil::SendObjDamage(this, damage_applied, shot);
+ }
+ }
+
+ // shake by percentage of maximum damage
+ double newshake = 50 * damage/design->integrity;
+
+ if (shake < MAX_SHAKE) shake += (float) newshake;
+ if (shake > MAX_SHAKE) shake = (float) MAX_SHAKE;
+
+ // start fires as needed:
+ if ((IsStarship() || IsGroundUnit() || RandomChance(1,3)) && hit_hull && damage_applied > 0) {
+ int old_integrity = (int) ((integrity + damage_applied)/design->integrity * 10);
+ int new_integrity = (int) ((integrity )/design->integrity * 10);
+
+ if (new_integrity < 5 && new_integrity < old_integrity) {
+ // need accurate hull impact for starships,
+ if (rep) {
+ Point detonation = impact*2 - Location();
+ Point direction = Location() - detonation;
+ double distance = direction.Normalize() * 3;
+ rep->CheckRayIntersection(detonation, direction, distance, impact);
+
+ // pull fire back into hull a bit:
+ direction = Location() - impact;
+ impact += direction * 0.2;
+
+ float scale = (float) design->scale;
+
+ if (IsDropship())
+ sim->CreateExplosion(impact, Velocity(), Explosion::SMOKE_TRAIL, 0.01f * scale, 0.5f * scale, region, this);
+ else
+ sim->CreateExplosion(impact, Velocity(), Explosion::HULL_FIRE, 0.10f * scale, scale, region, this);
+ }
+ }
+ }
+
+ return damage_applied;
+}
+
+double
+Ship::InflictSystemDamage(double damage, Shot* shot, Point impact)
+{
+ if (IsNetObserver())
+ return 0;
+
+ // find the system that is closest to the impact point:
+ System* system = 0;
+ double distance = 1e6;
+ double blast_radius = 0;
+ int dmg_type = 0;
+
+ if (shot)
+ dmg_type = shot->Design()->damage_type;
+
+ bool dmg_normal = dmg_type == WeaponDesign::DMG_NORMAL;
+ bool dmg_power = dmg_type == WeaponDesign::DMG_POWER;
+ bool dmg_emp = dmg_type == WeaponDesign::DMG_EMP;
+ double to_level = 0;
+
+ if (dmg_power) {
+ to_level = 1 - damage / 1e4;
+
+ if (to_level < 0)
+ to_level = 0;
+ }
+
+ // damage caused by weapons applies to closest system:
+ if (shot) {
+ if (shot->IsMissile())
+ blast_radius = 300;
+
+ ListIter<System> iter = systems;
+ while (++iter) {
+ System* candidate = iter.value();
+ double sysrad = candidate->Radius();
+
+ if (dmg_power)
+ candidate->DrainPower(to_level);
+
+ if (sysrad > 0 || dmg_emp && candidate->IsPowerCritical()) {
+ double test_distance = (impact - candidate->MountLocation()).length();
+
+ if ((test_distance-blast_radius) < sysrad || dmg_emp && candidate->IsPowerCritical()) {
+ if (test_distance < distance) {
+ system = candidate;
+ distance = test_distance;
+ }
+ }
+ }
+ }
+
+ // if a system was in range of the blast, assess the damage:
+ if (system) {
+ double hull_damage = damage * system->HullProtection();
+ double sys_damage = damage - hull_damage;
+ double avail = system->Availability();
+
+ if (dmg_normal || system->IsPowerCritical() && dmg_emp) {
+ system->ApplyDamage(sys_damage);
+ NetUtil::SendSysDamage(this, system, sys_damage);
+
+ master_caution = true;
+
+ if (dmg_normal) {
+ if (sys_damage < 100)
+ damage -= sys_damage;
+ else
+ damage -= 100;
+ }
+
+ if (system->GetExplosionType() && (avail - system->Availability()) >= 50) {
+ float scale = design->explosion_scale;
+ if (scale <= 0)
+ scale = design->scale;
+
+ sim->CreateExplosion(system->MountLocation(), Velocity() * 0.7f, system->GetExplosionType(), 0.2f * scale, scale, region, this, system);
+ }
+ }
+ }
+ }
+
+ // damage caused by collision applies to all systems:
+ else {
+ // ignore incidental bumps:
+ if (damage < 100)
+ return damage;
+
+ ListIter<System> iter = systems;
+ while (++iter) {
+ System* sys = iter.value();
+
+ if (rand() > 24000) {
+ double base_damage = 33.0 + rand()/1000.0;
+ double sys_damage = base_damage * (1.0 - sys->HullProtection());
+ sys->ApplyDamage(sys_damage);
+ NetUtil::SendSysDamage(this, system, sys_damage);
+ damage -= sys_damage;
+
+ master_caution = true;
+ }
+ }
+
+ // just in case this ship has lots of systems...
+ if (damage < 0)
+ damage = 0;
+ }
+
+ // return damage remaining
+ return damage;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Ship::ShieldStrength() const
+{
+ if (!shield) return 0;
+
+ return (int) shield->ShieldLevel();
+}
+
+int
+Ship::HullStrength() const
+{
+ if (design)
+ return (int) (Integrity() / design->integrity * 100);
+
+ return 10;
+}
+
+// +--------------------------------------------------------------------+
+
+System*
+Ship::GetSystem(int sys_id)
+{
+ System* s = 0;
+
+ if (sys_id >= 0) {
+ if (sys_id < systems.size()) {
+ s = systems[sys_id];
+ if (s->GetID() == sys_id)
+ return s;
+ }
+
+ ListIter<System> iter = systems;
+ while (++iter) {
+ s = iter.value();
+
+ if (s->GetID() == sys_id)
+ return s;
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::RepairSystem(System* sys)
+{
+ if (!repair_queue.contains(sys)) {
+ repair_queue.append(sys);
+ sys->Repair();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::IncreaseRepairPriority(int task_index)
+{
+ if (task_index > 0 && task_index < repair_queue.size()) {
+ System* task1 = repair_queue.at(task_index-1);
+ System* task2 = repair_queue.at(task_index);
+
+ repair_queue.at(task_index-1) = task2;
+ repair_queue.at(task_index) = task1;
+ }
+}
+
+void
+Ship::DecreaseRepairPriority(int task_index)
+{
+ if (task_index >= 0 && task_index < repair_queue.size()-1) {
+ System* task1 = repair_queue.at(task_index);
+ System* task2 = repair_queue.at(task_index+1);
+
+ repair_queue.at(task_index) = task2;
+ repair_queue.at(task_index+1) = task1;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::ExecMaintFrame(double seconds)
+{
+ // is it already too late?
+ if (life == 0 || integrity < 1) return;
+
+ const DWORD REPAIR_FREQUENCY = 5000; // once every five seconds
+ static DWORD last_repair_frame = 0; // one ship per game frame
+
+ if (auto_repair &&
+ Game::GameTime() - last_repair_time > REPAIR_FREQUENCY &&
+ last_repair_frame != Game::Frame()) {
+
+ last_repair_time = Game::GameTime();
+ last_repair_frame = Game::Frame();
+
+ ListIter<System> iter = systems;
+ while (++iter) {
+ System* sys = iter.value();
+
+ if (sys->Status() != System::NOMINAL) {
+ bool started_repairs = false;
+
+ // emergency power routing:
+ if (sys->Type() == System::POWER_SOURCE && sys->Availability() < 33) {
+ PowerSource* src = (PowerSource*) sys;
+ PowerSource* dst = 0;
+
+ for (int i = 0; i < reactors.size(); i++) {
+ PowerSource* pwr = reactors[i];
+
+ if (pwr != src && pwr->Availability() > src->Availability()) {
+ if (!dst ||
+ (pwr->Availability() > dst->Availability() &&
+ pwr->Charge() > dst->Charge()))
+ dst = pwr;
+ }
+ }
+
+ if (dst) {
+ while (src->Clients().size() > 0) {
+ System* s = src->Clients().at(0);
+ src->RemoveClient(s);
+ dst->AddClient(s);
+ }
+ }
+ }
+
+ ListIter<Component> comp = sys->GetComponents();
+ while (++comp) {
+ Component* c = comp.value();
+
+ if (c->Status() < Component::NOMINAL && c->Availability() < 75) {
+ if (c->SpareCount() &&
+ c->ReplaceTime() <= 300 &&
+ (c->Availability() < 50 ||
+ c->ReplaceTime() < c->RepairTime())) {
+
+ c->Replace();
+ started_repairs = true;
+ }
+
+ else if (c->Availability() >= 50 || c->NumJerried() < 5) {
+ c->Repair();
+ started_repairs = true;
+ }
+ }
+ }
+
+ if (started_repairs)
+ RepairSystem(sys);
+ }
+ }
+ }
+
+ if (repair_queue.size() > 0 && RepairTeams() > 0) {
+ int team = 0;
+ ListIter<System> iter = repair_queue;
+ while (++iter && team < RepairTeams()) {
+ System* sys = iter.value();
+
+ sys->ExecMaintFrame(seconds * RepairSpeed());
+ team++;
+
+ if (sys->Status() != System::MAINT) {
+ iter.removeItem();
+
+ // emergency power routing (restore):
+ if (sys->Type() == System::POWER_SOURCE &&
+ sys->Status() == System::NOMINAL) {
+ PowerSource* src = (PowerSource*) sys;
+ int isrc = reactors.index(src);
+
+ for (int i = 0; i < reactors.size(); i++) {
+ PowerSource* pwr = reactors[i];
+
+ if (pwr != src) {
+ List<System> xfer;
+
+ for (int i = 0; i < pwr->Clients().size(); i++) {
+ System* s = pwr->Clients().at(i);
+
+ if (s->GetSourceIndex() == isrc) {
+ xfer.append(s);
+ }
+ }
+
+ for (i = 0; i < xfer.size(); i++) {
+ System* s = xfer.at(i);
+ pwr->RemoveClient(s);
+ src->AddClient(s);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::SetNetworkControl(Director* net)
+{
+ net_control = net;
+
+ delete dir;
+ dir = 0;
+
+ if (!net_control && GetIFF() < 100) {
+ if (IsStatic())
+ dir = 0;
+ else if (IsStarship())
+ dir = SteerAI::Create(this, SteerAI::STARSHIP);
+ else
+ dir = SteerAI::Create(this, SteerAI::FIGHTER);
+ }
+}
+
+void
+Ship::SetControls(MotionController* m)
+{
+ if (IsDropping() || IsAttaining()) {
+ if (dir && dir->Type() != DropShipAI::DIR_TYPE) {
+ delete dir;
+ dir = new(__FILE__,__LINE__) DropShipAI(this);
+ }
+
+ return;
+ }
+
+ else if (IsSkipping()) {
+ if (navsys && sim->GetPlayerShip() == this)
+ navsys->EngageAutoNav();
+ }
+
+ else if (IsDying() || IsDead()) {
+ if (dir) {
+ delete dir;
+ dir = 0;
+ }
+
+ if (navsys && navsys->AutoNavEngaged()) {
+ navsys->DisengageAutoNav();
+ }
+
+ return;
+ }
+
+ else if (life == 0) {
+ if (dir || navsys) {
+ ::Print("Warning: dying ship '%' still has not been destroyed!\n", name);
+ delete dir;
+ dir = 0;
+
+ if (navsys && navsys->AutoNavEngaged())
+ navsys->DisengageAutoNav();
+ }
+
+ return;
+ }
+
+ if (navsys && navsys->AutoNavEngaged()) {
+ NavAI* nav = 0;
+
+ if (dir) {
+ if (dir->Type() != NavAI::DIR_TYPE) {
+ delete dir;
+ dir = 0;
+ }
+ else {
+ nav = (NavAI*) dir;
+ }
+ }
+
+ if (!nav) {
+ nav = new(__FILE__,__LINE__) NavAI(this);
+ dir = nav;
+ return;
+ }
+
+ else if (!nav->Complete()) {
+ return;
+ }
+ }
+
+ if (dir) {
+ delete dir;
+ dir = 0;
+ }
+
+ if (m) {
+ Keyboard::FlushKeys();
+ m->Acquire();
+ dir = new(__FILE__,__LINE__) ShipCtrl(this, m);
+ director_info = Game::GetText("flcs.auto");
+ }
+ else if (GetIFF() < 100) {
+ if (IsStatic())
+ dir = SteerAI::Create(this, SteerAI::GROUND);
+
+ else if (IsStarship() && !IsAirborne())
+ dir = SteerAI::Create(this, SteerAI::STARSHIP);
+
+ else
+ dir = SteerAI::Create(this, SteerAI::FIGHTER);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Ship::IFFColor(int iff)
+{
+ Color c;
+
+ switch (iff) {
+ case 0: // NEUTRAL, NON-COMBAT
+ c = Color(192,192,192);
+ break;
+
+ case 1: // TERELLIAN ALLIANCE
+ c = Color(70,70,220);
+ break;
+
+ case 2: // MARAKAN HEGEMONY
+ c = Color(220,20,20);
+ break;
+
+ case 3: // BROTHERHOOD OF IRON
+ c = Color(200,180,20);
+ break;
+
+ case 4: // ZOLON EMPIRE
+ c = Color(20,200,20);
+ break;
+
+ case 5:
+ c = Color(128, 0, 128);
+ break;
+
+ case 6:
+ c = Color(40,192,192);
+ break;
+
+ default:
+ c = Color(128,128,128);
+ break;
+ }
+
+ return c;
+}
+
+Color
+Ship::MarkerColor() const
+{
+ return IFFColor(IFF_code);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::SetIFF(int iff)
+{
+ IFF_code = iff;
+
+ if (hangar)
+ hangar->SetAllIFF(iff);
+
+ DropTarget();
+
+ if (dir && dir->Type() >= 1000) {
+ SteerAI* ai = (SteerAI*) dir;
+ ai->DropTarget();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::SetRogue(bool r)
+{
+ bool rogue = IsRogue();
+
+ ff_count = r ? 1000 : 0;
+
+ if (!rogue && IsRogue()) {
+ Print("Ship '%s' has been made rogue\n", Name());
+ }
+ else if (rogue && !IsRogue()) {
+ Print("Ship '%s' is no longer rogue\n", Name());
+ }
+}
+
+void
+Ship::SetFriendlyFire(int f)
+{
+ bool rogue = IsRogue();
+
+ ff_count = f;
+
+ if (!rogue && IsRogue()) {
+ Print("Ship '%s' has been made rogue with ff_count = %d\n", Name(), ff_count);
+ }
+ else if (rogue && !IsRogue()) {
+ Print("Ship '%s' is no longer rogue\n", Name());
+ }
+}
+
+void
+Ship::IncFriendlyFire(int f)
+{
+ if (f > 0) {
+ bool rogue = IsRogue();
+
+ ff_count += f;
+
+ if (!rogue && IsRogue()) {
+ Print("Ship '%s' has been made rogue with ff_count = %d\n", Name(), ff_count);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Ship::SetEMCON(int e, bool from_net)
+{
+ if (e < 1) emcon = 1;
+ else if (e > 3) emcon = 3;
+ else emcon = (BYTE) e;
+
+ if (emcon != old_emcon && !from_net && NetGame::GetInstance())
+ NetUtil::SendObjEmcon(this);
+}
+
+double
+Ship::PCS() const
+{
+ double e_factor = design->e_factor[emcon-1];
+
+ if (IsAirborne() && !IsGroundUnit()) {
+ if (AltitudeAGL() < 40)
+ return 0;
+
+ if (AltitudeAGL() < 200) {
+ double clutter = AltitudeAGL() / 200;
+ return clutter * e_factor;
+ }
+ }
+
+ return e_factor * pcs;
+}
+
+double
+Ship::ACS() const
+{
+ if (IsAirborne() && !IsGroundUnit()) {
+ if (AltitudeAGL() < 40)
+ return 0;
+
+ if (AltitudeAGL() < 200) {
+ double clutter = AltitudeAGL() / 200;
+ return clutter * acs;
+ }
+ }
+
+ return acs;
+}
+
+DWORD
+Ship::MissionClock() const
+{
+ if (launch_time > 0)
+ return Game::GameTime() + 1 - launch_time;
+
+ return 0;
+}
+
+// +----------------------------------------------------------------------+
+
+Instruction*
+Ship::GetRadioOrders() const
+{
+ return radio_orders;
+}
+
+void
+Ship::ClearRadioOrders()
+{
+ if (radio_orders) {
+ radio_orders->SetAction(0);
+ radio_orders->ClearTarget();
+ radio_orders->SetLocation(Point());
+ }
+}
+
+void
+Ship::HandleRadioMessage(RadioMessage* msg)
+{
+ if (!msg) return;
+
+ static RadioHandler rh;
+
+ if (rh.ProcessMessage(msg, this))
+ rh.AcknowledgeMessage(msg, this);
+}
+
+// +----------------------------------------------------------------------+
+
+int
+Ship::Value() const
+{
+ return Value(design->type);
+}
+
+// +----------------------------------------------------------------------+
+
+int
+Ship::Value(int type)
+{
+ int value = 0;
+
+ switch (type) {
+ case DRONE: value = 10; break;
+ case FIGHTER: value = 20; break;
+ case ATTACK: value = 40; break;
+ case LCA: value = 50; break;
+
+ case COURIER: value = 100; break;
+ case CARGO: value = 100; break;
+ case CORVETTE: value = 100; break;
+ case FREIGHTER: value = 250; break;
+ case FRIGATE: value = 200; break;
+ case DESTROYER: value = 500; break;
+ case CRUISER: value = 800; break;
+ case BATTLESHIP: value = 1000; break;
+ case CARRIER: value = 1500; break;
+ case DREADNAUGHT: value = 1500; break;
+
+ case STATION: value = 2500; break;
+ case FARCASTER: value = 5000; break;
+
+ case MINE: value = 20; break;
+ case COMSAT: value = 200; break;
+ case DEFSAT: value = 300; break;
+ case SWACS: value = 500; break;
+
+ case BUILDING: value = 100; break;
+ case FACTORY: value = 250; break;
+ case SAM: value = 100; break;
+ case EWR: value = 200; break;
+ case C3I: value = 500; break;
+ case STARBASE: value = 2000; break;
+
+ default: value = 100; break;
+ }
+
+ return value;
+}
+
+// +----------------------------------------------------------------------+
+
+double
+Ship::AIValue() const
+{
+ int i = 0;
+ double value = 0;
+
+ for (i = 0; i < reactors.size(); i++) {
+ const PowerSource* r = reactors[i];
+ value += r->Value();
+ }
+
+ for (i = 0; i < drives.size(); i++) {
+ const Drive* d = drives[i];
+ value += d->Value();
+ }
+
+ for (i = 0; i < weapons.size(); i++) {
+ const WeaponGroup* w = weapons[i];
+ value += w->Value();
+ }
+
+ return value;
+}
diff --git a/Stars45/Ship.h b/Stars45/Ship.h
new file mode 100644
index 0000000..a1057d8
--- /dev/null
+++ b/Stars45/Ship.h
@@ -0,0 +1,583 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Ship.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Starship (or space/ground station) class
+*/
+
+#ifndef Ship_h
+#define Ship_h
+
+#include "Types.h"
+#include "SimObject.h"
+#include "DetailSet.h"
+#include "Director.h"
+#include "Geometry.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+class Shot;
+class Drone;
+
+class Bitmap;
+class CombatUnit;
+class Computer;
+class Contact;
+class Drive;
+class Element;
+class Farcaster;
+class FlightComp;
+class FlightDeck;
+class Hangar;
+class InboundSlot;
+class Instruction;
+class LandingGear;
+class MotionController;
+class NavLight;
+class NavSystem;
+class PowerSource;
+class QuantumDrive;
+class RadioMessage;
+class Shield;
+class ShieldRep;
+class ShipDesign;
+class ShipKiller;
+class Solid;
+class Skin;
+class Sound;
+class Sensor;
+class System;
+class Thruster;
+class Weapon;
+class WeaponDesign;
+class WeaponGroup;
+
+// +--------------------------------------------------------------------+
+
+class Ship : public SimObject,
+ public SimObserver
+{
+public:
+ static const char* TYPENAME() { return "Ship"; }
+
+ enum CLASSIFICATION {
+ DRONE = 0x0001,
+ FIGHTER = 0x0002,
+ ATTACK = 0x0004,
+ LCA = 0x0008,
+ COURIER = 0x0010,
+ CARGO = 0x0020,
+ CORVETTE = 0x0040,
+ FREIGHTER = 0x0080,
+ FRIGATE = 0x0100,
+ DESTROYER = 0x0200,
+ CRUISER = 0x0400,
+ BATTLESHIP = 0x0800,
+ CARRIER = 0x1000,
+ DREADNAUGHT = 0x2000,
+
+ STATION = 0x4000,
+ FARCASTER = 0x8000,
+
+ MINE = 0x00010000,
+ COMSAT = 0x00020000,
+ DEFSAT = 0x00040000,
+ SWACS = 0x00080000,
+
+ BUILDING = 0x00100000,
+ FACTORY = 0x00200000,
+ SAM = 0x00400000,
+ EWR = 0x00800000,
+ C3I = 0x01000000,
+ STARBASE = 0x02000000,
+
+ DROPSHIPS = 0x0000000f,
+ STARSHIPS = 0x0000fff0,
+ SPACE_UNITS = 0x000f0000,
+ GROUND_UNITS = 0xfff00000
+ };
+
+ enum OP_MODE { DOCKED, ALERT, LOCKED, LAUNCH, TAKEOFF, ACTIVE, APPROACH, RECOVERY, DOCKING };
+ enum FLCS_MODE { FLCS_MANUAL, FLCS_AUTO, FLCS_HELM };
+ enum TRAN_TYPE { TRANSITION_NONE,
+ TRANSITION_DROP_CAM,
+ TRANSITION_DROP_ORBIT,
+ TRANSITION_MAKE_ORBIT,
+ TRANSITION_TIME_SKIP,
+ TRANSITION_DEATH_SPIRAL,
+ TRANSITION_DEAD };
+
+ enum FLIGHT_MODEL { FM_STANDARD, FM_RELAXED, FM_ARCADE };
+ enum LANDING_MODEL { LM_STANDARD, LM_EASIER };
+
+ // CONSTRUCTORS:
+ Ship(const char* ship_name, const char* reg_num, ShipDesign* design, int IFF=0, int cmd_ai=0, const int* loadout=0);
+ virtual ~Ship();
+
+ int operator == (const Ship& s) const { return id == s.id; }
+
+ static void Initialize();
+ static void Close();
+
+ virtual void ExecFrame(double seconds);
+ virtual void AeroFrame(double seconds);
+ virtual void StatFrame(double seconds);
+ virtual void DockFrame(double seconds);
+ virtual void LinearFrame(double seconds);
+ virtual void ExecSensors(double seconds);
+
+ void ExecNavFrame(double seconds);
+ void ExecPhysics(double seconds);
+ void ExecThrottle(double seconds);
+ void ExecSystems(double seconds);
+
+ virtual void Activate(Scene& scene);
+ virtual void Deactivate(Scene& scene);
+ virtual void SelectDetail(double seconds);
+ virtual void SetRegion(SimRegion* rgn);
+ virtual int GetTextureList(List<Bitmap>& textures);
+
+ // DIRECTION:
+ virtual void SetControls(MotionController* m);
+ virtual void SetNetworkControl(Director* net_ctrl=0);
+ void SetDirectorInfo(const char* msg) { director_info = msg; }
+ const char* GetDirectorInfo() const { return director_info; }
+ void SetAIMode(int n) { ai_mode = (BYTE) n; }
+ int GetAIMode() const { return (int) ai_mode; }
+ void SetCommandAILevel(int n) { command_ai_level = (BYTE) n; }
+ int GetCommandAILevel() const { return command_ai_level; }
+ virtual int GetFlightPhase() const { return flight_phase; }
+ virtual void SetFlightPhase(OP_MODE phase);
+ bool IsNetObserver() const { return net_observer_mode; }
+ void SetNetObserver(bool n) { net_observer_mode = n; }
+
+ bool IsInvulnerable() const { return invulnerable; }
+ void SetInvulnerable(bool n) { invulnerable = n; }
+
+ double GetHelmHeading() const { return helm_heading; }
+ double GetHelmPitch() const { return helm_pitch; }
+ void SetHelmHeading(double h);
+ void SetHelmPitch(double p);
+ virtual void ApplyHelmYaw(double y);
+ virtual void ApplyHelmPitch(double p);
+ virtual void ApplyPitch(double pitch_acc); // override for G limiter
+
+ void ArcadeStop() { arcade_velocity *= 0; }
+
+ // CAMERA:
+ Point BridgeLocation() const { return bridge_vec; }
+ Point ChaseLocation() const { return chase_vec; }
+ Point TransitionLocation() const { return transition_loc; }
+
+ // FLIGHT DECK:
+ Ship* GetController() const;
+ int NumInbound() const;
+ int NumFlightDecks() const;
+ FlightDeck* GetFlightDeck(int i=0) const;
+ Ship* GetCarrier() const { return carrier; }
+ FlightDeck* GetDock() const { return dock; }
+ void SetCarrier(Ship* c, FlightDeck* d);
+ void Stow();
+ InboundSlot* GetInbound() const { return inbound; }
+ void SetInbound(InboundSlot* s);
+
+ // DRIVE SYSTEMS:
+ int GetFuelLevel() const; // (0-100) percent of full tank
+ void SetThrottle(double percent);
+ void SetAugmenter(bool enable);
+ double Thrust(double seconds) const;
+ double VelocityLimit() const { return vlimit; }
+ Drive* GetDrive() const { return main_drive; }
+ double Throttle() const { return throttle; }
+ bool Augmenter() const { return augmenter; }
+ QuantumDrive* GetQuantumDrive() const { return quantum_drive; }
+ Farcaster* GetFarcaster() const { return farcaster; }
+
+ bool IsAirborne() const;
+ bool IsDropCam() const { return transition_type == TRANSITION_DROP_CAM; }
+ bool IsDropping() const { return transition_type == TRANSITION_DROP_ORBIT; }
+ bool IsAttaining() const { return transition_type == TRANSITION_MAKE_ORBIT; }
+ bool IsSkipping() const { return transition_type == TRANSITION_TIME_SKIP; }
+ bool IsDying() const { return transition_type == TRANSITION_DEATH_SPIRAL; }
+ bool IsDead() const { return transition_type == TRANSITION_DEAD; }
+ bool InTransition() const { return transition_type != TRANSITION_NONE; }
+ void DropOrbit();
+ void MakeOrbit();
+ bool CanTimeSkip();
+ bool IsInCombat();
+ void TimeSkip();
+ void DropCam(double time=10, double range=0);
+ void DeathSpiral();
+ void CompleteTransition();
+ void SetTransition(double trans_time, int trans_type, const Point& trans_loc);
+
+ double CompassHeading() const;
+ double CompassPitch() const;
+ double AltitudeMSL() const;
+ double AltitudeAGL() const;
+ double GForce() const;
+
+ virtual void SetupAgility();
+
+ // FLIGHT CONTROL SYSTEM (FLCS):
+ void ExecFLCSFrame();
+ void CycleFLCSMode();
+ void SetFLCSMode(int mode);
+ int GetFLCSMode() const;
+ void SetTransX(double t);
+ void SetTransY(double t);
+ void SetTransZ(double t);
+
+ bool IsGearDown();
+ void LowerGear();
+ void RaiseGear();
+ void ToggleGear();
+ void ToggleNavlights();
+
+ // WEAPON SYSTEMS:
+ virtual void CheckFriendlyFire();
+ virtual void CheckFire(bool c) { check_fire = c; }
+ virtual bool CheckFire() const { return (check_fire||net_observer_mode)?true:false; }
+ virtual void SelectWeapon(int n, int w);
+ virtual bool FireWeapon(int n);
+ virtual bool FirePrimary() { return FireWeapon(primary); }
+ virtual bool FireSecondary() { return FireWeapon(secondary); }
+ virtual bool FireDecoy();
+ virtual void CyclePrimary();
+ virtual void CycleSecondary();
+ virtual Weapon* GetPrimary() const;
+ virtual Weapon* GetSecondary() const;
+ virtual Weapon* GetWeaponByIndex(int n);
+ virtual WeaponGroup* GetPrimaryGroup() const;
+ virtual WeaponGroup* GetSecondaryGroup() const;
+ virtual Weapon* GetDecoy() const;
+ virtual List<Shot>& GetActiveDecoys();
+ virtual void AddActiveDecoy(Drone* d);
+ virtual int* GetLoadout() { return loadout; }
+
+ List<Shot>& GetThreatList();
+ void AddThreat(Shot* s);
+ void DropThreat(Shot* s);
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const { return name; }
+
+ virtual int GetMissileEta(int index) const;
+ virtual void SetMissileEta(int id, int eta);
+
+ virtual WeaponDesign* GetPrimaryDesign() const;
+ virtual WeaponDesign* GetSecondaryDesign() const;
+
+ virtual void SetTarget(SimObject* t, System* sub=0, bool from_net=false);
+ virtual SimObject* GetTarget() const { return target; }
+ virtual System* GetSubTarget() const { return subtarget; }
+ virtual void CycleSubTarget(int dir=1);
+ virtual void DropTarget();
+ virtual void LockTarget(int type=SimObject::SIM_SHIP,
+ bool closest=false,
+ bool hostile=false);
+ virtual void LockTarget(SimObject* candidate);
+ virtual bool IsTracking(SimObject* tgt);
+ virtual bool GetTrigger(int i) const;
+ virtual void SetTrigger(int i);
+
+ Ship* GetWard() const { return ward; }
+ void SetWard(Ship* s);
+
+ // SHIELD SYSTEMS:
+ virtual double InflictDamage(double damage,
+ Shot* shot = 0,
+ int hit_type = 3,
+ Point hull_impact = Point(0,0,0));
+
+ virtual double InflictSystemDamage(double damage, Shot* shot, Point impact);
+
+ virtual void InflictNetDamage(double damage, Shot* shot=0);
+ virtual void InflictNetSystemDamage(System* system, double damage, BYTE type);
+ virtual void SetNetSystemStatus(System* system, int status, int power, int reactor, double avail);
+ virtual void SetIntegrity(float n) { integrity = n; }
+
+ virtual void Destroy();
+ virtual int ShieldStrength() const;
+ virtual int HullStrength() const;
+ virtual int HitBy(Shot* shot, Point& impact);
+ virtual int CollidesWith(Physical& o);
+
+ // SENSORS AND VISIBILITY:
+ virtual int GetContactID() const { return contact_id; }
+ virtual int GetIFF() const { return IFF_code; }
+ virtual void SetIFF(int iff);
+ virtual Color MarkerColor() const;
+ static Color IFFColor(int iff);
+ virtual void DoEMCON();
+ virtual double PCS() const;
+ virtual double ACS() const;
+ int NumContacts() const; // actual sensor contacts
+ List<Contact>& ContactList();
+ virtual int GetSensorMode() const;
+ virtual void SetSensorMode(int mode);
+ virtual void LaunchProbe();
+ virtual Weapon* GetProbeLauncher() const { return probe; }
+ virtual Drone* GetProbe() const { return sensor_drone; }
+ virtual void SetProbe(Drone* d);
+ virtual int GetEMCON() const { return emcon; }
+ virtual void SetEMCON(int e, bool from_net=false);
+ virtual Contact* FindContact(SimObject* s) const;
+ virtual bool IsHostileTo(const SimObject* o) const;
+
+ // GENERAL ACCESSORS:
+ const char* Registry() const { return regnum; }
+ void SetName(const char* ident) { strcpy(name, ident); }
+ const ShipDesign* Design() const { return design; }
+ const char* Abbreviation() const;
+ const char* DesignName() const;
+ const char* DesignFileName() const;
+ static const char* ClassName(int c);
+ static int ClassForName(const char* name);
+ const char* ClassName() const;
+ CLASSIFICATION Class() const;
+ bool IsGroundUnit() const;
+ bool IsStarship() const;
+ bool IsDropship() const;
+ bool IsStatic() const;
+ bool IsRogue() const;
+ void SetRogue(bool r=true);
+ int GetFriendlyFire() const { return ff_count; }
+ void SetFriendlyFire(int f);
+ void IncFriendlyFire(int f=1);
+ double Agility() const { return agility; }
+ DWORD MissionClock() const;
+ Graphic* Cockpit() const;
+ void ShowCockpit();
+ void HideCockpit();
+ int Value() const;
+ double AIValue() const;
+ static int Value(int type);
+
+ const Skin* GetSkin() const { return skin; }
+ void UseSkin(const Skin* s) { skin = s; }
+ void ShowRep();
+ void HideRep();
+ void EnableShadows(bool enable);
+
+ int RespawnCount() const { return respawns; }
+ void SetRespawnCount(int r) { respawns = r; }
+ const Point& RespawnLoc() const { return respawn_loc; }
+ void SetRespawnLoc(const Point& rl)
+ { respawn_loc = rl; }
+
+ double WarpFactor() const { return warp_fov; }
+ void SetWarp(double w) { warp_fov = (float) w; }
+
+ void MatchOrientation(const Ship& s);
+
+ // ORDERS AND NAVIGATION:
+ void ExecEvalFrame(double seconds);
+ void SetLaunchPoint(Instruction* pt);
+ void AddNavPoint(Instruction* pt, Instruction* afterPoint=0);
+ void DelNavPoint(Instruction* pt);
+ void ClearFlightPlan();
+ Instruction* GetNextNavPoint();
+ int GetNavIndex(const Instruction* n);
+ double RangeToNavPoint(const Instruction* n);
+ void SetNavptStatus(Instruction* n, int status);
+ List<Instruction>& GetFlightPlan();
+ int FlightPlanLength();
+ CombatUnit* GetCombatUnit() const { return combat_unit; }
+ Element* GetElement() const { return element; }
+ Ship* GetLeader() const;
+ int GetElementIndex() const;
+ int GetOrigElementIndex() const;
+ void SetElement(Element* e);
+
+ Instruction* GetRadioOrders() const;
+ void ClearRadioOrders();
+ void HandleRadioMessage(RadioMessage* msg);
+ bool IsAutoNavEngaged();
+ void SetAutoNav(bool engage=true);
+ void CommandMode();
+
+ void ClearTrack();
+ void UpdateTrack();
+ int TrackLength() const { return ntrack; }
+ Point TrackPoint(int i) const;
+
+ // DAMAGE CONTROL AND ENGINEERING:
+ List<System>& RepairQueue() { return repair_queue; }
+ double RepairSpeed() const;
+ int RepairTeams() const;
+ void RepairSystem(System* sys);
+ void IncreaseRepairPriority(int task_index);
+ void DecreaseRepairPriority(int task_index);
+ void ExecMaintFrame(double seconds);
+ bool AutoRepair() const { return auto_repair; }
+ void EnableRepair(bool e) { auto_repair = e; }
+ bool MasterCaution() const { return master_caution; }
+ void ClearCaution() { master_caution = 0; }
+
+ // SYSTEM ACCESSORS:
+ List<System>& Systems() { return systems; }
+ List<WeaponGroup>& Weapons() { return weapons; }
+ List<Drive>& Drives() { return drives; }
+ List<Computer>& Computers() { return computers; }
+ List<FlightDeck>& FlightDecks() { return flight_decks; }
+ List<PowerSource>& Reactors() { return reactors; }
+ List<NavLight>& NavLights() { return navlights; }
+ Shield* GetShield() { return shield; }
+ Solid* GetShieldRep() { return (Solid*) shieldRep; }
+ Sensor* GetSensor() { return sensor; }
+ NavSystem* GetNavSystem() { return navsys; }
+ FlightComp* GetFLCS() { return flcs; }
+ Thruster* GetThruster() { return thruster; }
+ Hangar* GetHangar() { return hangar; }
+ LandingGear* GetGear() { return gear; }
+
+ System* GetSystem(int sys_id);
+
+ static int GetControlModel() { return control_model; }
+ static void SetControlModel(int n) { control_model = n; }
+ static int GetFlightModel() { return flight_model; }
+ static void SetFlightModel(int f) { flight_model = f; }
+ static int GetLandingModel() { return landing_model; }
+ static void SetLandingModel(int f) { landing_model = f; }
+ static double GetFriendlyFireLevel() { return friendly_fire_level; }
+ static void SetFriendlyFireLevel(double f)
+ { friendly_fire_level = f; }
+
+protected:
+ int CheckShotIntersection(Shot* shot, Point& ipt, Point& hpt, Weapon** wep=0);
+ WeaponGroup* FindWeaponGroup(const char* name);
+
+ char regnum[16];
+ ShipDesign* design;
+ ShipKiller* killer;
+ DetailSet detail;
+ int detail_level;
+ Sim* sim;
+ double vlimit;
+ double agility;
+ double throttle;
+ double throttle_request;
+ bool augmenter;
+ float wep_mass;
+ float wep_resist;
+
+ int IFF_code;
+ int cmd_chain_index;
+ int ff_count;
+ OP_MODE flight_phase;
+
+ SimObject* target;
+ System* subtarget;
+ Ship* ward;
+ int check_fire;
+ int primary;
+ int secondary;
+
+ const Skin* skin;
+ Solid* cockpit;
+ Drive* main_drive;
+ QuantumDrive* quantum_drive;
+ Farcaster* farcaster;
+ Shield* shield;
+ ShieldRep* shieldRep;
+ NavSystem* navsys;
+ FlightComp* flcs;
+ Sensor* sensor;
+ LandingGear* gear;
+ Thruster* thruster;
+ Weapon* decoy;
+ Weapon* probe;
+ Drone* sensor_drone;
+ Hangar* hangar;
+ List<Shot> decoy_list;
+ List<Shot> threat_list;
+
+ List<System> systems;
+ List<PowerSource> reactors;
+ List<WeaponGroup> weapons;
+ List<Drive> drives;
+ List<Computer> computers;
+ List<FlightDeck> flight_decks;
+ List<NavLight> navlights;
+ List<System> repair_queue;
+
+ CombatUnit* combat_unit;
+ Element* element;
+ int orig_elem_index;
+ Instruction* radio_orders;
+ Instruction* launch_point;
+
+ Vec3 chase_vec;
+ Vec3 bridge_vec;
+
+ const char* director_info;
+ BYTE ai_mode;
+ BYTE command_ai_level;
+ BYTE flcs_mode;
+ bool net_observer_mode;
+
+ float pcs; // passive sensor cross section
+ float acs; // active sensor cross section
+ BYTE emcon;
+ BYTE old_emcon;
+ bool invulnerable;
+
+ DWORD launch_time;
+ DWORD friendly_fire_time;
+
+ Ship* carrier;
+ FlightDeck* dock;
+ InboundSlot* inbound;
+
+ Director* net_control;
+
+ Point* track;
+ int ntrack;
+ DWORD track_time;
+
+ float helm_heading;
+ float helm_pitch;
+
+ float altitude_agl;
+ float g_force;
+
+ float warp_fov;
+
+ float transition_time;
+ int transition_type;
+ Point transition_loc;
+ Point respawn_loc;
+ int respawns;
+
+ bool master_caution;
+ bool auto_repair;
+ DWORD last_repair_time;
+ DWORD last_eval_time;
+ DWORD last_beam_time;
+ DWORD last_bolt_time;
+
+ int missile_id[4];
+ BYTE missile_eta[4];
+ bool trigger[4];
+ int* loadout;
+
+ int contact_id;
+
+ static int control_model;
+ static int flight_model;
+ static int landing_model;
+ static double friendly_fire_level;
+};
+
+#endif Ship_h
+
diff --git a/Stars45/ShipAI.cpp b/Stars45/ShipAI.cpp
new file mode 100644
index 0000000..1118a8a
--- /dev/null
+++ b/Stars45/ShipAI.cpp
@@ -0,0 +1,1358 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ShipAI.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Starship (low-level) Artificial Intelligence class
+*/
+
+#include "MemDebug.h"
+#include "ShipAI.h"
+#include "TacticalAI.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Shot.h"
+#include "Element.h"
+#include "NavLight.h"
+#include "Instruction.h"
+#include "RadioMessage.h"
+#include "RadioTraffic.h"
+#include "Contact.h"
+#include "WeaponGroup.h"
+#include "Drive.h"
+#include "Shield.h"
+#include "Sim.h"
+#include "Player.h"
+#include "StarSystem.h"
+#include "FlightComp.h"
+#include "Farcaster.h"
+#include "QuantumDrive.h"
+#include "Debris.h"
+#include "Asteroid.h"
+
+#include "Game.h"
+#include "Random.h"
+
+// +----------------------------------------------------------------------+
+
+ShipAI::ShipAI(SimObject* s)
+ : SteerAI(s),
+ support(0), rumor(0), threat(0), threat_missile(0), drop_time(0),
+ too_close(0), navpt(0), patrol(0), engaged_ship_id(0),
+ bracket(false), identify(false), hold(false), takeoff(false),
+ throttle(0), old_throttle(0), element_index(1), splash_count(0),
+ tactical(0), farcaster(0), ai_level(2), last_avoid_time(0),
+ last_call_time(0)
+{
+ ship = (Ship*) self;
+
+ Sim* sim = Sim::GetSim();
+ Ship* pship = sim->GetPlayerShip();
+ int player_team = 1;
+
+ if (pship)
+ player_team = pship->GetIFF();
+
+ Player* player = Player::GetCurrentPlayer();
+ if (player) {
+ if (ship && ship->GetIFF() && ship->GetIFF() != player_team) {
+ ai_level = player->AILevel();
+ }
+ else if (player->AILevel() == 0) {
+ ai_level = 1;
+ }
+ }
+
+ // evil alien ships are *always* smart:
+ if (ship && ship->GetIFF() > 1 && ship->Design()->auto_roll > 1) {
+ ai_level = 2;
+ }
+}
+
+
+// +--------------------------------------------------------------------+
+
+ShipAI::~ShipAI()
+{
+ delete tactical;
+}
+
+void
+ShipAI::ClearTactical()
+{
+ delete tactical;
+ tactical = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+Ship*
+ShipAI::GetWard() const
+{
+ return ship->GetWard();
+}
+
+void
+ShipAI::SetWard(Ship* s)
+{
+ if (s == ship->GetWard())
+ return;
+
+ ship->SetWard(s);
+
+ Point form = RandomDirection();
+ form.SwapYZ();
+
+ if (fabs(form.x) < 0.5) {
+ if (form.x < 0)
+ form.x = -0.5;
+ else
+ form.x = 0.5;
+ }
+
+ if (ship && ship->IsStarship()) {
+ form *= 30e3;
+ }
+ else {
+ form *= 15e3;
+ form.y = 500;
+ }
+
+ SetFormationDelta(form);
+}
+
+void
+ShipAI::SetSupport(Ship* s)
+{
+ if (support == s)
+ return;
+
+ support = s;
+
+ if (support)
+ Observe(support);
+}
+
+void
+ShipAI::SetRumor(Ship* s)
+{
+ if (!s || rumor == s)
+ return;
+
+ rumor = s;
+
+ if (rumor)
+ Observe(rumor);
+}
+
+void
+ShipAI::ClearRumor()
+{
+ rumor = 0;
+}
+
+void
+ShipAI::SetThreat(Ship* s)
+{
+ if (threat == s)
+ return;
+
+ threat = s;
+
+ if (threat)
+ Observe(threat);
+}
+
+void
+ShipAI::SetThreatMissile(Shot* s)
+{
+ if (threat_missile == s)
+ return;
+
+ threat_missile = s;
+
+ if (threat_missile)
+ Observe(threat_missile);
+}
+
+bool
+ShipAI::Update(SimObject* obj)
+{
+ if (obj == support)
+ support = 0;
+
+ if (obj == threat)
+ threat = 0;
+
+ if (obj == threat_missile)
+ threat_missile = 0;
+
+ if (obj == rumor)
+ rumor = 0;
+
+ return SteerAI::Update(obj);
+}
+
+const char*
+ShipAI::GetObserverName() const
+{
+ static char name[64];
+ sprintf(name, "ShipAI(%s)", self->Name());
+ return name;
+}
+
+// +--------------------------------------------------------------------+
+
+Point
+ShipAI::GetPatrol() const
+{
+ return patrol_loc;
+}
+
+void
+ShipAI::SetPatrol(const Point& p)
+{
+ patrol = 1;
+ patrol_loc = p;
+}
+
+void
+ShipAI::ClearPatrol()
+{
+ patrol = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipAI::ExecFrame(double secs)
+{
+ seconds = secs;
+
+ if (drop_time > 0) drop_time -= seconds;
+ if (!ship) return;
+
+ ship->SetDirectorInfo(" ");
+
+ // check to make sure current navpt is still valid:
+ if (navpt)
+ navpt = ship->GetNextNavPoint();
+
+ if (ship->GetFlightPhase() == Ship::TAKEOFF || ship->GetFlightPhase() == Ship::LAUNCH)
+ takeoff = true;
+
+ if (takeoff) {
+ FindObjective();
+ Navigator();
+
+ if (ship->MissionClock() > 10000)
+ takeoff = false;
+
+ return;
+ }
+
+ // initial assessment:
+ if (ship->MissionClock() < 5000)
+ return;
+
+ element_index = ship->GetElementIndex();
+
+ NavlightControl();
+ CheckTarget();
+
+ if (tactical)
+ tactical->ExecFrame(seconds);
+
+ if (target && target != ship->GetTarget()) {
+ ship->LockTarget(target);
+
+ // if able to lock target, and target is a ship (not a shot)...
+ if (target == ship->GetTarget() && target->Type() == SimObject::SIM_SHIP) {
+
+ // if this isn't the same ship we last called out:
+ if (target->Identity() != engaged_ship_id && Game::GameTime() - last_call_time > 10000) {
+ // call engaging:
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(ship->GetElement(), ship, RadioMessage::CALL_ENGAGING);
+ msg->AddTarget(target);
+ RadioTraffic::Transmit(msg);
+ last_call_time = Game::GameTime();
+
+ engaged_ship_id = target->Identity();
+ }
+ }
+ }
+
+ else if (!target) {
+ target = ship->GetTarget();
+
+ if (engaged_ship_id && !target) {
+ engaged_ship_id = 0;
+
+ /***
+ *** XXX
+ *** Not the right place to make this decision.
+ ***
+ *** There is a brief wait between killing a target and
+ *** selecting a new one, so this message is sent after
+ *** every kill.
+ ***
+ *** Need to track when the entire element has been
+ *** put down.
+
+ if (element_index == 1) {
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(ship->GetElement(), ship, RadioMessage::RESUME_MISSION);
+ RadioTraffic::Transmit(msg);
+ }
+
+ ***
+ ***/
+ }
+ }
+
+ FindObjective();
+ Navigator();
+}
+
+// +--------------------------------------------------------------------+
+
+Point
+ShipAI::ClosingVelocity()
+{
+ if (ship && target) {
+ if (ship->GetPrimaryDesign()) {
+ WeaponDesign* guns = ship->GetPrimaryDesign();
+ Point delta = target->Location() - ship->Location();
+
+ // fighters need to aim the ship so that the guns will hit the target
+ if (guns->firing_cone < 10*DEGREES && guns->max_range <= delta.length()) {
+ Point aim_vec = ship->Heading();
+ aim_vec.Normalize();
+
+ Point shot_vel = ship->Velocity() + aim_vec * guns->speed;
+ return shot_vel - target->Velocity();
+ }
+
+ // ships with turreted weapons just need to worry about actual closing speed
+ else {
+ return ship->Velocity() - target->Velocity();
+ }
+ }
+
+ else {
+ return ship->Velocity();
+ }
+ }
+
+ return Point(1,0,0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipAI::FindObjective()
+{
+ distance = 0;
+
+ int order = ship->GetRadioOrders()->Action();
+
+ if (order == RadioMessage::QUANTUM_TO ||
+ order == RadioMessage::FARCAST_TO) {
+
+ FindObjectiveQuantum();
+ objective = Transform(obj_w);
+ return;
+ }
+
+ bool form = (order == RadioMessage::WEP_HOLD) ||
+ (order == RadioMessage::FORM_UP) ||
+ (order == RadioMessage::MOVE_PATROL) ||
+ (order == RadioMessage::RTB) ||
+ (order == RadioMessage::DOCK_WITH) ||
+ (!order && !target) ||
+ (farcaster);
+
+ Ship* ward = ship->GetWard();
+
+ // if not the element leader, stay in formation:
+ if (form && element_index > 1) {
+ ship->SetDirectorInfo(Game::GetText("ai.formation"));
+
+ if (navpt && navpt->Action() == Instruction::LAUNCH) {
+ FindObjectiveNavPoint();
+ }
+ else {
+ navpt = 0;
+ FindObjectiveFormation();
+ }
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+ return;
+ }
+
+ // under orders?
+ bool directed = false;
+ if (tactical)
+ directed = (tactical->RulesOfEngagement() == TacticalAI::DIRECTED);
+
+ // threat processing:
+ if (threat && !directed) {
+ double d_threat = Point(threat->Location() - ship->Location()).length();
+
+ // seek support:
+ if (support) {
+ double d_support = Point(support->Location() - ship->Location()).length();
+ if (d_support > 35e3) {
+ ship->SetDirectorInfo(Game::GetText("ai.regroup"));
+ FindObjectiveTarget(support);
+ objective = Transform(obj_w);
+ return;
+ }
+ }
+
+ // run away:
+ else if (threat != target) {
+ ship->SetDirectorInfo(Game::GetText("ai.retreat"));
+ obj_w = ship->Location() + Point(ship->Location() - threat->Location()) * 100;
+ objective = Transform(obj_w);
+ return;
+ }
+ }
+
+ // normal processing:
+ if (target) {
+ ship->SetDirectorInfo(Game::GetText("ai.seek-target"));
+ FindObjectiveTarget(target);
+ objective = AimTransform(obj_w);
+ }
+
+ else if (patrol) {
+ ship->SetDirectorInfo(Game::GetText("ai.patrol"));
+ FindObjectivePatrol();
+ objective = Transform(obj_w);
+ }
+
+ else if (ward) {
+ ship->SetDirectorInfo(Game::GetText("ai.seek-ward"));
+ FindObjectiveFormation();
+ objective = Transform(obj_w);
+ }
+
+ else if (navpt && form) {
+ ship->SetDirectorInfo(Game::GetText("ai.seek-navpt"));
+ FindObjectiveNavPoint();
+ objective = Transform(obj_w);
+ }
+
+ else if (rumor) {
+ ship->SetDirectorInfo(Game::GetText("ai.search"));
+ FindObjectiveTarget(rumor);
+ objective = Transform(obj_w);
+ }
+
+ else {
+ obj_w = Point();
+ objective = Point();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipAI::FindObjectiveTarget(SimObject* tgt)
+{
+ if (!tgt) {
+ obj_w = Point();
+ return;
+ }
+
+ navpt = 0; // this tells fire control that we are chasing a target,
+ // instead of a navpoint!
+
+ Point cv = ClosingVelocity();
+ double cvl = cv.length();
+ double time = 0;
+
+ if (cvl > 50) {
+ // distance from self to target:
+ distance = Point(tgt->Location() - self->Location()).length();
+
+ // time to reach target:
+ time = distance / cvl;
+
+ // where the target will be when we reach it:
+ if (time < 15) {
+ Point run_vec = tgt->Velocity();
+ obj_w = tgt->Location() + (run_vec * time);
+
+
+ if (time < 10)
+ obj_w += (tgt->Acceleration() * 0.33 * time * time);
+ }
+ else {
+ obj_w = tgt->Location();
+ }
+ }
+
+ else {
+ obj_w = tgt->Location();
+ }
+
+ distance = Point(obj_w - self->Location()).length();
+
+ if (cvl > 50) {
+ time = distance / cvl;
+
+ // where we will be when the target gets there:
+ if (time < 15) {
+ Point self_dest = self->Location() + cv * time;
+ Point err = obj_w - self_dest;
+
+ obj_w += err;
+ }
+ }
+
+ Point approach = obj_w - self->Location();
+ distance = approach.length();
+
+ if (bracket && distance > 25e3) {
+ Point offset = approach.cross(Point(0,1,0));
+ offset.Normalize();
+ offset *= 15e3;
+
+ Ship* s = (Ship*) self;
+ if (s->GetElementIndex() & 1)
+ obj_w -= offset;
+ else
+ obj_w += offset;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipAI::FindObjectivePatrol()
+{
+ navpt = 0;
+
+ Point npt = patrol_loc;
+ obj_w = npt;
+
+ // distance from self to navpt:
+ distance = Point(obj_w - self->Location()).length();
+
+ if (distance < 1000) {
+ ship->ClearRadioOrders();
+ ClearPatrol();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipAI::FindObjectiveNavPoint()
+{
+ SimRegion* self_rgn = ship->GetRegion();
+ SimRegion* nav_rgn = navpt->Region();
+ QuantumDrive* qdrive = ship->GetQuantumDrive();
+
+ if (!self_rgn)
+ return;
+
+ if (!nav_rgn) {
+ nav_rgn = self_rgn;
+ navpt->SetRegion(nav_rgn);
+ }
+
+ bool use_farcaster = self_rgn != nav_rgn &&
+ (navpt->Farcast() ||
+ !qdrive ||
+ !qdrive->IsPowerOn() ||
+ qdrive->Status() < System::DEGRADED
+ );
+
+ if (use_farcaster) {
+ FindObjectiveFarcaster(self_rgn, nav_rgn);
+ }
+
+ else {
+ if (farcaster) {
+ if (farcaster->GetShip()->GetRegion() != self_rgn)
+ farcaster = farcaster->GetDest()->GetFarcaster();
+
+ obj_w = farcaster->EndPoint();
+ }
+
+ else {
+ // transform from starsystem to world coordinates:
+ Point npt = navpt->Region()->Location() + navpt->Location();
+
+ SimRegion* active_region = ship->GetRegion();
+
+ if (active_region)
+ npt -= active_region->Location();
+
+ npt = npt.OtherHand();
+
+ obj_w = npt;
+ }
+
+ // distance from self to navpt:
+ distance = Point(obj_w - ship->Location()).length();
+
+ if (farcaster && distance < 1000)
+ farcaster = 0;
+
+ if (distance < 1000 || (navpt->Action() == Instruction::LAUNCH && distance > 25000))
+ ship->SetNavptStatus(navpt, Instruction::COMPLETE);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipAI::FindObjectiveQuantum()
+{
+ Instruction* orders = ship->GetRadioOrders();
+ SimRegion* self_rgn = ship->GetRegion();
+ SimRegion* nav_rgn = orders->Region();
+ QuantumDrive* qdrive = ship->GetQuantumDrive();
+
+ if (!self_rgn || !nav_rgn)
+ return;
+
+ bool use_farcaster = self_rgn != nav_rgn &&
+ (orders->Farcast() ||
+ !qdrive ||
+ !qdrive->IsPowerOn() ||
+ qdrive->Status() < System::DEGRADED
+ );
+
+ if (use_farcaster) {
+ FindObjectiveFarcaster(self_rgn, nav_rgn);
+ }
+
+ else {
+ if (farcaster) {
+ if (farcaster->GetShip()->GetRegion() != self_rgn)
+ farcaster = farcaster->GetDest()->GetFarcaster();
+
+ obj_w = farcaster->EndPoint();
+ }
+
+ else {
+ // transform from starsystem to world coordinates:
+ Point npt = orders->Region()->Location() + orders->Location();
+
+ SimRegion* active_region = ship->GetRegion();
+
+ if (active_region)
+ npt -= active_region->Location();
+
+ npt = npt.OtherHand();
+
+ obj_w = npt;
+
+ if (qdrive && qdrive->ActiveState() == QuantumDrive::ACTIVE_READY) {
+ qdrive->SetDestination(nav_rgn, orders->Location());
+ qdrive->Engage();
+ return;
+ }
+ }
+
+ // distance from self to navpt:
+ distance = Point(obj_w - ship->Location()).length();
+
+ if (farcaster) {
+ if (distance < 1000) {
+ farcaster = 0;
+ ship->ClearRadioOrders();
+ }
+ }
+ else if (self_rgn == nav_rgn) {
+ ship->ClearRadioOrders();
+ }
+ }
+}
+
+void
+ShipAI::FindObjectiveFarcaster(SimRegion* src_rgn, SimRegion* dst_rgn)
+{
+ if (!farcaster) {
+ ListIter<Ship> s = src_rgn->Ships();
+ while (++s && !farcaster) {
+ if (s->GetFarcaster()) {
+ const Ship* dest = s->GetFarcaster()->GetDest();
+ if (dest && dest->GetRegion() == dst_rgn) {
+ farcaster = s->GetFarcaster();
+ }
+ }
+ }
+ }
+
+ if (farcaster) {
+ Point apt = farcaster->ApproachPoint(0);
+ Point npt = farcaster->StartPoint();
+ double r1 = (ship->Location() - npt).length();
+
+ if (r1 > 50e3) {
+ obj_w = apt;
+ distance = r1;
+ }
+
+ else {
+ double r2 = (ship->Location() - apt).length();
+ double r3 = (npt - apt).length();
+
+ if (r1+r2 < 1.2*r3) {
+ obj_w = npt;
+ distance = r1;
+ }
+ else {
+ obj_w = apt;
+ distance = r2;
+ }
+ }
+
+ objective = Transform(obj_w);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipAI::SetFormationDelta(const Point& point)
+{
+ formation_delta = point;
+}
+
+void
+ShipAI::FindObjectiveFormation()
+{
+ const double prediction = 5;
+
+ // find the base position:
+ Element* elem = ship->GetElement();
+ Ship* lead = elem->GetShip(1);
+ Ship* ward = ship->GetWard();
+
+ if (!lead || lead == ship) {
+ lead = ward;
+
+ distance = (lead->Location() - self->Location()).length();
+ if (distance < 30e3 && lead->Velocity().length() < 50) {
+ obj_w = self->Location() + lead->Heading() * 1e6;
+ distance = -1;
+ return;
+ }
+ }
+
+ obj_w = lead->Location() + lead->Velocity() * prediction;
+ Matrix m; m.Rotate(0, 0, lead->CompassHeading() - PI);
+ Point fd = formation_delta * m;
+ obj_w += fd;
+
+ // try to avoid smacking into the ground...
+ if (ship->IsAirborne()) {
+ if (ship->AltitudeAGL() < 3000 || lead->AltitudeAGL() < 3000) {
+ obj_w.y += 500;
+ }
+ }
+
+ Point dst_w = self->Location() + self->Velocity() * prediction;
+ Point dlt_w = obj_w - dst_w;
+
+ distance = dlt_w.length();
+
+ // get slot z distance:
+ dlt_w += ship->Location();
+ slot_dist = Transform(dlt_w).z;
+
+ Director* lead_dir = lead->GetDirector();
+ if (lead_dir && (lead_dir->Type() == FIGHTER || lead_dir->Type() == STARSHIP)) {
+ ShipAI* lead_ai = (ShipAI*) lead_dir;
+ farcaster = lead_ai->GetFarcaster();
+ }
+ else {
+ Instruction* navpt = elem->GetNextNavPoint();
+ if (!navpt) {
+ farcaster = 0;
+ return;
+ }
+
+ SimRegion* self_rgn = ship->GetRegion();
+ SimRegion* nav_rgn = navpt->Region();
+ QuantumDrive* qdrive = ship->GetQuantumDrive();
+
+ if (self_rgn && !nav_rgn) {
+ nav_rgn = self_rgn;
+ navpt->SetRegion(nav_rgn);
+ }
+
+ bool use_farcaster = self_rgn != nav_rgn &&
+ (navpt->Farcast() ||
+ !qdrive ||
+ !qdrive->IsPowerOn() ||
+ qdrive->Status() < System::DEGRADED
+ );
+
+ if (use_farcaster) {
+ ListIter<Ship> s = self_rgn->Ships();
+ while (++s && !farcaster) {
+ if (s->GetFarcaster()) {
+ const Ship* dest = s->GetFarcaster()->GetDest();
+ if (dest && dest->GetRegion() == nav_rgn) {
+ farcaster = s->GetFarcaster();
+ }
+ }
+ }
+ }
+ else if (farcaster) {
+ if (farcaster->GetShip()->GetRegion() != self_rgn)
+ farcaster = farcaster->GetDest()->GetFarcaster();
+
+ obj_w = farcaster->EndPoint();
+ distance = Point(obj_w - ship->Location()).length();
+
+ if (distance < 1000)
+ farcaster = 0;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipAI::Splash(const Ship* targ)
+{
+ if (splash_count > 6)
+ splash_count = 4;
+
+ // call splash:
+ RadioTraffic::SendQuickMessage(ship, RadioMessage::SPLASH_1 + splash_count);
+ splash_count++;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipAI::SetTarget(SimObject* targ, System* sub)
+{
+ if (targ != target) {
+ bracket = false;
+ }
+
+ SteerAI::SetTarget(targ, sub);
+}
+
+void
+ShipAI::DropTarget(double dtime)
+{
+ SetTarget(0);
+ drop_time = dtime; // seconds until we can re-acquire
+
+ ship->DropTarget();
+}
+
+void
+ShipAI::SetBracket(bool b)
+{
+ bracket = b;
+ identify = false;
+}
+
+void
+ShipAI::SetIdentify(bool i)
+{
+ identify = i;
+ bracket = false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipAI::Navigator()
+{
+ accumulator.Clear();
+ magnitude = 0;
+
+ hold = false;
+ if ((ship->GetElement() && ship->GetElement()->GetHoldTime() > 0) ||
+ (navpt && navpt->Status() == Instruction::COMPLETE && navpt->HoldTime() > 0))
+ hold = true;
+
+ ship->SetFLCSMode(Ship::FLCS_HELM);
+
+ if (target)
+ ship->SetDirectorInfo(Game::GetText("ai.seek-target"));
+ else if (rumor)
+ ship->SetDirectorInfo(Game::GetText("ai.seek-rumor"));
+ else
+ ship->SetDirectorInfo(Game::GetText("ai.none"));
+
+ Accumulate(AvoidCollision());
+ Accumulate(AvoidTerrain());
+
+ if (!hold)
+ Accumulate(SeekTarget());
+
+ HelmControl();
+ ThrottleControl();
+ FireControl();
+ AdjustDefenses();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipAI::HelmControl()
+{
+ double trans_x = 0;
+ double trans_y = 0;
+ double trans_z = 0;
+
+ ship->SetHelmHeading(accumulator.yaw);
+
+ if (fabs(accumulator.pitch) < 5*DEGREES || fabs(accumulator.pitch) > 45*DEGREES) {
+ trans_z = objective.y;
+ ship->SetHelmPitch(0);
+ }
+
+ else {
+ ship->SetHelmPitch(accumulator.pitch);
+ }
+
+ ship->SetTransX(trans_x);
+ ship->SetTransY(trans_y);
+ ship->SetTransZ(trans_z);
+
+ ship->ExecFLCSFrame();
+}
+
+/*****************************************
+ **
+ ** NOTE:
+ ** No one is really using this method.
+ ** It is overridden by both StarshipAI
+ ** and FighterAI.
+ **
+ *****************************************/
+
+void
+ShipAI::ThrottleControl()
+{
+ if (navpt && !threat && !target) { // lead only, get speed from navpt
+ double speed = navpt->Speed();
+
+ if (speed > 0)
+ throttle = speed / ship->VelocityLimit() * 100;
+ else
+ throttle = 50;
+ }
+
+ else if (patrol && !threat && !target) { // lead only, get speed from navpt
+ double speed = 200;
+
+ if (distance > 5000)
+ speed = 500;
+
+ if (ship->Velocity().length() > speed)
+ throttle = 0;
+ else
+ throttle = 50;
+ }
+
+ else {
+ if (threat || target || element_index < 2) { // element lead
+ throttle = 100;
+
+ if (!threat && !target)
+ throttle = 50;
+
+ if (accumulator.brake > 0) {
+ throttle *= (1 - accumulator.brake);
+ }
+ }
+
+ else { // wingman
+ Ship* lead = ship->GetElement()->GetShip(1);
+ double lv = lead->Velocity().length();
+ double sv = ship->Velocity().length();
+ double dv = lv-sv;
+ double dt = 0;
+
+ if (dv > 0) dt = dv * 1e-2 * seconds;
+ else if (dv < 0) dt = dv * 1e-2 * seconds;
+
+ throttle = old_throttle + dt;
+ }
+ }
+
+ old_throttle = throttle;
+ ship->SetThrottle((int) throttle);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipAI::NavlightControl()
+{
+ Ship* leader = ship->GetLeader();
+
+ if (leader && leader != ship) {
+ bool navlight_enabled = false;
+
+ if (leader->NavLights().size() > 0)
+ navlight_enabled = leader->NavLights().at(0)->IsEnabled();
+
+ for (int i = 0; i < ship->NavLights().size(); i++) {
+ if (navlight_enabled)
+ ship->NavLights().at(i)->Enable();
+ else
+ ship->NavLights().at(i)->Disable();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+ShipAI::AvoidTerrain()
+{
+ Steer avoid;
+ return avoid;
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+ShipAI::AvoidCollision()
+{
+ Steer avoid;
+
+ if (!ship || !ship->GetRegion() || !ship->GetRegion()->IsActive())
+ return avoid;
+
+ if (other && (other->Life() == 0 || other->Integrity() < 1)) {
+ other = 0;
+ last_avoid_time = 0; // check for a new obstacle immediately
+ }
+
+ if (!other && Game::GameTime() - last_avoid_time < 500)
+ return avoid;
+
+ brake = 0;
+
+ // don't get closer than this:
+ double avoid_dist = 5 * self->Radius();
+
+ if (avoid_dist < 1e3) avoid_dist = 1e3;
+ else if (avoid_dist > 12e3) avoid_dist = 12e3;
+
+ // find the soonest potential collision,
+ // ignore any that occur after this:
+ double avoid_time = 15;
+
+ if (ship->Design()->avoid_time > 0)
+ avoid_time = ship->Design()->avoid_time;
+ else if (ship->IsStarship())
+ avoid_time *= 1.5;
+
+ Point bearing = self->Velocity();
+ bearing.Normalize();
+
+ bool found = false;
+ int num_contacts = ship->NumContacts();
+ ListIter<Contact> contact = ship->ContactList();
+
+ // check current obstacle first:
+ if (other) {
+ found = AvoidTestSingleObject(other, bearing, avoid_dist, avoid_time, avoid);
+ }
+
+ if (!found) {
+ // avoid ships:
+ while (++contact && !found) {
+ Ship* c_ship = contact->GetShip();
+
+ if (c_ship && c_ship != ship && c_ship->IsStarship()) {
+ found = AvoidTestSingleObject(c_ship, bearing, avoid_dist, avoid_time, avoid);
+ }
+ }
+
+ // also avoid large pieces of debris:
+ if (!found) {
+ ListIter<Debris> iter = ship->GetRegion()->Rocks();
+ while (++iter && !found) {
+ Debris* debris = iter.value();
+
+ if (debris->Mass() > ship->Mass())
+ found = AvoidTestSingleObject(debris, bearing, avoid_dist, avoid_time, avoid);
+ }
+ }
+
+ // and asteroids:
+ if (!found) {
+ // give asteroids a wider berth -
+ avoid_dist *= 8;
+
+ ListIter<Asteroid> iter = ship->GetRegion()->Roids();
+ while (++iter && !found) {
+ Asteroid* roid = iter.value();
+ found = AvoidTestSingleObject(roid, bearing, avoid_dist, avoid_time, avoid);
+ }
+
+ if (!found)
+ avoid_dist /= 8;
+ }
+
+ // if found, steer to avoid:
+ if (other) {
+ avoid = Avoid(obstacle, (float) (ship->Radius() + other->Radius() + avoid_dist * 0.9));
+ avoid.brake = brake;
+
+ ship->SetDirectorInfo(Game::GetText("ai.avoid-collision"));
+ }
+ }
+
+ last_avoid_time = Game::GameTime();
+ return avoid;
+}
+
+bool
+ShipAI::AvoidTestSingleObject(SimObject* obj,
+ const Point& bearing,
+ double avoid_dist,
+ double& avoid_time,
+ Steer& avoid)
+{
+ if (too_close == obj->Identity()) {
+ double dist = (ship->Location() - obj->Location()).length();
+ double closure = (ship->Velocity() - obj->Velocity()) * bearing;
+
+ if (closure > 1 && dist < avoid_dist) {
+ avoid = AvoidCloseObject(obj);
+ return true;
+ }
+ else {
+ too_close = 0;
+ }
+ }
+
+ // will we get close?
+ double time = ClosestApproachTime(ship->Location(), ship->Velocity(),
+ obj->Location(), obj->Velocity());
+
+ // already past the obstacle:
+ if (time <= 0) {
+ if (other == obj) other = 0;
+ return false;
+ }
+
+ // how quickly could we collide?
+ Point current_relation = ship->Location() - obj->Location();
+ double current_distance = current_relation.length() - ship->Radius() - obj->Radius();
+
+ // are we really far away?
+ if (current_distance > 25e3) {
+ if (other == obj) other = 0;
+ return false;
+ }
+
+ // is the obstacle a farcaster?
+ if (obj->Type() == SimObject::SIM_SHIP) {
+ Ship* c_ship = (Ship*) obj;
+
+ if (c_ship->GetFarcaster()) {
+ // are we on a safe vector?
+ Point dir = ship->Velocity();
+ dir.Normalize();
+
+ double angle_off = fabs(acos(dir * obj->Cam().vpn()));
+
+ if (angle_off > 90*DEGREES)
+ angle_off = 180*DEGREES - angle_off;
+
+ if (angle_off < 35*DEGREES) {
+ // will we pass through the center?
+ Point d = ship->Location() + dir * (current_distance + ship->Radius() + obj->Radius());
+ double err = (obj->Location() - d).length();
+
+ if (err < 0.667 * obj->Radius()) {
+ return false;
+ }
+ }
+ }
+ }
+
+ // rate of closure:
+ double closing_velocity = (ship->Velocity() - obj->Velocity()) * bearing;
+
+ // are we too close already?
+ if (current_distance < (avoid_dist * 0.35)) {
+ if (closing_velocity > 1 || current_distance < ship->Radius()) {
+ avoid = AvoidCloseObject(obj);
+ return true;
+ }
+ }
+
+ // too far away to worry about:
+ double separation = (avoid_dist + obj->Radius());
+ if ((current_distance-separation) / closing_velocity > avoid_time) {
+ if (other == obj) other = 0;
+ return false;
+ }
+
+ // where will we be?
+ Point selfpt = ship->Location() + ship->Velocity() * time;
+ Point testpt = obj->Location() + obj->Velocity() * time;
+
+ // how close will we get?
+ double dist = (selfpt - testpt).length()
+ - ship->Radius()
+ - obj->Radius();
+
+ // that's too close:
+ if (dist < avoid_dist) {
+ if (dist < avoid_dist * 0.25 && time < avoid_time * 0.5) {
+ avoid = AvoidCloseObject(obj);
+ return true;
+ }
+
+ obstacle = Transform(testpt);
+
+ if (obstacle.z > 0) {
+ other = obj;
+ avoid_time = time;
+ brake = 0.5;
+
+ Observe(other);
+ }
+ }
+
+ // hysteresis:
+ else if (other == obj && dist > avoid_dist * 1.25) {
+ other = 0;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+ShipAI::AvoidCloseObject(SimObject* obj)
+{
+ too_close = obj->Identity();
+ obstacle = Transform(obj->Location());
+ other = obj;
+
+ Observe(other);
+
+ Steer avoid = Flee(obstacle);
+ avoid.brake = 0.3;
+
+ ship->SetDirectorInfo(Game::GetText("ai.avoid-collision"));
+ return avoid;
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+ShipAI::SeekTarget()
+{
+ Ship* ward = ship->GetWard();
+
+ if (!target && !ward && !navpt && !patrol) {
+ if (element_index > 1) {
+ // wingmen keep in formation:
+ return Seek(objective);
+ }
+
+ if (farcaster) {
+ return Seek(objective);
+ }
+
+ if (rumor) {
+ return Seek(objective);
+ }
+
+ return Steer();
+ }
+
+ if (patrol) {
+ Steer result = Seek(objective);
+
+ if (distance < 2000) {
+ result.brake = 1;
+ }
+
+ return result;
+ }
+
+ if (target && too_close == target->Identity()) {
+ drop_time = 4;
+ return Avoid(objective, 0.0f);
+ }
+ else if (drop_time > 0) {
+ return Steer();
+ }
+
+ return Seek(objective);
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+ShipAI::EvadeThreat()
+{
+ return Steer();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipAI::FireControl()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipAI::AdjustDefenses()
+{
+ Shield* shield = ship->GetShield();
+
+ if (shield) {
+ double desire = 50;
+
+ if (threat_missile || threat)
+ desire = 100;
+
+ shield->SetPowerLevel(desire);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipAI::CheckTarget()
+{
+ if (target) {
+ if (target->Life() == 0)
+ target = 0;
+
+ else if (target->Type() == SimObject::SIM_SHIP) {
+ Ship* tgt_ship = (Ship*) target;
+
+ if (tgt_ship->GetIFF() == ship->GetIFF() && !tgt_ship->IsRogue())
+ target = 0;
+ }
+ }
+}
diff --git a/Stars45/ShipAI.h b/Stars45/ShipAI.h
new file mode 100644
index 0000000..05f0555
--- /dev/null
+++ b/Stars45/ShipAI.h
@@ -0,0 +1,153 @@
+/* Project STARSHATTER
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ShipAI.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Common base class and interface for low-level ship AI
+*/
+
+#ifndef ShipAI_h
+#define ShipAI_h
+
+#include "Types.h"
+#include "SteerAI.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+class Shot;
+class Instruction;
+class TacticalAI;
+class Farcaster;
+
+// +--------------------------------------------------------------------+
+
+class ShipAI : public SteerAI
+{
+public:
+ ShipAI(SimObject* s);
+ virtual ~ShipAI();
+
+ virtual void ExecFrame(double seconds);
+ virtual int Subframe() const { return true; }
+
+ virtual Ship* GetShip() const { return ship; }
+
+ virtual Ship* GetWard() const;
+ virtual void SetWard(Ship* s);
+ virtual Ship* GetThreat() const { return threat; }
+ virtual void SetThreat(Ship* s);
+ virtual Ship* GetSupport() const { return support; }
+ virtual void SetSupport(Ship* s);
+ virtual Ship* GetRumor() const { return rumor; }
+ virtual void SetRumor(Ship* s);
+ virtual Shot* GetThreatMissile() const { return threat_missile; }
+ virtual void SetThreatMissile(Shot* s);
+ virtual Instruction* GetNavPoint() const { return navpt; }
+ virtual void SetNavPoint(Instruction* n) { navpt = n; }
+ virtual Point GetPatrol() const;
+ virtual void SetPatrol(const Point& p);
+ virtual void ClearPatrol();
+ virtual void ClearRumor();
+ virtual void ClearTactical();
+
+ virtual Farcaster* GetFarcaster() { return farcaster; }
+
+ // convert the goal point from world to local coords:
+ virtual void FindObjective();
+
+ virtual void Splash(const Ship* targ);
+ virtual void SetTarget(SimObject* targ, System* sub=0);
+ virtual void DropTarget(double drop_time=1.5);
+ virtual double DropTime() const { return drop_time; }
+ virtual void SetBracket(bool bracket);
+ virtual void SetIdentify(bool identify);
+
+ virtual void SetFormationDelta(const Point& point);
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+ virtual int GetAILevel() const { return ai_level; }
+
+protected:
+ // accumulate behaviors:
+ virtual void Navigator();
+
+ // behaviors:
+ virtual bool AvoidTestSingleObject(SimObject* obj,
+ const Point& bearing,
+ double avoid_dist,
+ double& avoid_time,
+ Steer& steer);
+
+ virtual Steer AvoidCloseObject(SimObject* obj);
+ virtual Steer AvoidCollision();
+ virtual Steer AvoidTerrain();
+ virtual Steer SeekTarget();
+ virtual Steer EvadeThreat();
+
+ virtual Point ClosingVelocity();
+
+ // compute the goal point in world coords based on current ai state:
+ virtual void FindObjectiveTarget(SimObject* tgt);
+ virtual void FindObjectiveNavPoint();
+ virtual void FindObjectiveFormation();
+ virtual void FindObjectivePatrol();
+ virtual void FindObjectiveQuantum();
+ virtual void FindObjectiveFarcaster(SimRegion* src, SimRegion* dst);
+
+ // fire on target if appropriate:
+ virtual void AdjustDefenses();
+ virtual void FireControl();
+ virtual void HelmControl();
+ virtual void ThrottleControl();
+ virtual void NavlightControl();
+
+ virtual void CheckTarget();
+
+ Ship* ship;
+ Ship* support;
+ Ship* rumor;
+ Ship* threat;
+ Shot* threat_missile;
+ Instruction* navpt;
+ Point obstacle;
+ TacticalAI* tactical;
+ Farcaster* farcaster;
+ int engaged_ship_id;
+ int splash_count;
+
+ Point formation_delta;
+ double slot_dist;
+
+ double throttle;
+ double old_throttle;
+ double seconds;
+ double drop_time;
+ double brake;
+ DWORD last_avoid_time;
+ DWORD last_call_time;
+
+ int element_index;
+ int too_close;
+ bool bracket;
+ bool identify;
+ bool hold;
+ bool takeoff;
+
+ int patrol;
+ Point patrol_loc;
+ int ai_level;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif ShipAI_h
+
diff --git a/Stars45/ShipCtrl.cpp b/Stars45/ShipCtrl.cpp
new file mode 100644
index 0000000..0da5012
--- /dev/null
+++ b/Stars45/ShipCtrl.cpp
@@ -0,0 +1,341 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ShipCtrl.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Ship Controller class
+*/
+
+#include "MemDebug.h"
+#include "ShipCtrl.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Shield.h"
+#include "Sensor.h"
+#include "NavSystem.h"
+#include "FlightComp.h"
+#include "LandingGear.h"
+#include "Sim.h"
+#include "StarSystem.h"
+#include "Starshatter.h"
+#include "HUDView.h"
+#include "HUDSounds.h"
+#include "KeyMap.h"
+#include "RadioMessage.h"
+#include "RadioTraffic.h"
+
+#include "MouseController.h"
+#include "Keyboard.h"
+#include "Joystick.h"
+#include "Game.h"
+#include "DataLoader.h"
+
+// +--------------------------------------------------------------------+
+
+ShipCtrl::ShipCtrl(Ship* s, MotionController* ctrl)
+ : ship(s), controller(ctrl), throttle_active(false), launch_latch(false),
+ pickle_latch(false), target_latch(false)
+{
+ for (int i = 0; i < 10; i++)
+ GetAsyncKeyState('0'+i);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+ShipCtrl::KeyDown(int action)
+{
+ int k = Joystick::KeyDownMap(action) ||
+ Keyboard::KeyDownMap(action);
+
+ return k;
+}
+
+int
+ShipCtrl::Toggled(int action)
+{
+ static double last_toggle_time = 0;
+
+ if (KeyDown(action)) {
+ if ((Game::RealTime() - last_toggle_time) > 250) {
+ last_toggle_time = Game::RealTime();
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipCtrl::Launch()
+{
+ if (controller) {
+ ship->SetThrottle(100);
+ throttle_active = false;
+ launch_latch = true;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipCtrl::ExecFrame(double seconds)
+{
+ Starshatter* stars = Starshatter::GetInstance();
+ if (stars && stars->GetChatMode()) return;
+ if (!ship) return;
+
+ const int DELTA_THROTTLE = 5;
+
+ controller->Acquire();
+
+ if (ship->IsStarship() && ship->GetFLCSMode() == Ship::FLCS_HELM) {
+ ship->ApplyHelmPitch(controller->Pitch() * seconds);
+ ship->ApplyHelmYaw(controller->Yaw() * seconds);
+ }
+ else {
+ ship->ApplyRoll(controller->Roll());
+ ship->ApplyPitch(controller->Pitch());
+ ship->ApplyYaw(controller->Yaw());
+ }
+
+ ship->SetTransX(controller->X() * ship->Design()->trans_x);
+ ship->SetTransY(controller->Y() * ship->Design()->trans_y);
+ ship->SetTransZ(controller->Z() * ship->Design()->trans_z);
+
+ bool augmenter = false;
+
+ if (controller->Throttle() > 0.05) {
+ if (throttle_active) {
+ ship->SetThrottle(controller->Throttle() * 100);
+
+ if (controller->Throttle() >= 0.99)
+ augmenter = true;
+ }
+ else if (!launch_latch || controller->Throttle() > 0.5) {
+ throttle_active = true;
+ launch_latch = false;
+ }
+ }
+ else if (throttle_active) {
+ ship->SetThrottle(0);
+ throttle_active = false;
+ }
+
+ ship->ExecFLCSFrame();
+
+ static double time_til_change = 0.0;
+
+ if (time_til_change < 0.001) {
+ if (KeyDown(KEY_THROTTLE_UP)) {
+ ship->SetThrottle(ship->Throttle() + DELTA_THROTTLE);
+ time_til_change = 0.05;
+ }
+
+ else if (KeyDown(KEY_THROTTLE_DOWN)) {
+ ship->SetThrottle(ship->Throttle() - DELTA_THROTTLE);
+ time_til_change = 0.05;
+ }
+
+ else if (KeyDown(KEY_THROTTLE_ZERO)) {
+ ship->SetThrottle(0);
+ if (ship->GetFLCS())
+ ship->GetFLCS()->FullStop();
+ time_til_change = 0.05;
+ }
+
+ else if (KeyDown(KEY_THROTTLE_FULL)) {
+ ship->SetThrottle(100);
+ time_til_change = 0.05;
+ }
+
+ else if (KeyDown(KEY_FLCS_MODE_AUTO)) {
+ ship->CycleFLCSMode();
+ time_til_change = 0.5f;
+ }
+
+ else if (KeyDown(KEY_CYCLE_PRIMARY)) {
+ HUDSounds::PlaySound(HUDSounds::SND_WEP_MODE);
+ ship->CyclePrimary();
+ time_til_change = 0.5f;
+ }
+
+ else if (KeyDown(KEY_CYCLE_SECONDARY)) {
+ HUDSounds::PlaySound(HUDSounds::SND_WEP_MODE);
+ ship->CycleSecondary();
+ time_til_change = 0.5f;
+ }
+
+ if (ship->GetShield()) {
+ Shield* shield = ship->GetShield();
+ double level = shield->GetPowerLevel();
+
+ if (KeyDown(KEY_SHIELDS_FULL)) {
+ HUDSounds::PlaySound(HUDSounds::SND_SHIELD_LEVEL);
+ shield->SetPowerLevel(100);
+ time_til_change = 0.5f;
+ }
+
+ else if (KeyDown(KEY_SHIELDS_ZERO)) {
+ HUDSounds::PlaySound(HUDSounds::SND_SHIELD_LEVEL);
+ shield->SetPowerLevel(0);
+ time_til_change = 0.5f;
+ }
+
+ else if (KeyDown(KEY_SHIELDS_UP)) {
+ if (level < 25) level = 25;
+ else if (level < 50) level = 50;
+ else if (level < 75) level = 75;
+ else level = 100;
+
+ HUDSounds::PlaySound(HUDSounds::SND_SHIELD_LEVEL);
+ shield->SetPowerLevel(level);
+ time_til_change = 0.5f;
+ }
+
+ else if (KeyDown(KEY_SHIELDS_DOWN)) {
+ if (level > 75) level = 75;
+ else if (level > 50) level = 50;
+ else if (level > 25) level = 25;
+ else level = 0;
+
+ HUDSounds::PlaySound(HUDSounds::SND_SHIELD_LEVEL);
+ shield->SetPowerLevel(level);
+ time_til_change = 0.5f;
+ }
+
+ }
+
+ if (ship->GetSensor()) {
+ Sensor* sensor = ship->GetSensor();
+
+ if (sensor->GetMode() < Sensor::PST) {
+ if (KeyDown(KEY_SENSOR_MODE)) {
+ int sensor_mode = sensor->GetMode() + 1;
+ if (sensor_mode > Sensor::GM)
+ sensor_mode = Sensor::PAS;
+
+ sensor->SetMode((Sensor::Mode) sensor_mode);
+ time_til_change = 0.5f;
+ }
+
+ else if (KeyDown(KEY_SENSOR_GROUND_MODE)) {
+ if (ship->IsAirborne()) {
+ sensor->SetMode(Sensor::GM);
+ time_til_change = 0.5f;
+ }
+ }
+ }
+ else {
+ // manual "return to search" command for starships:
+ if (KeyDown(KEY_SENSOR_MODE)) {
+ ship->DropTarget();
+ }
+ }
+
+ if (KeyDown(KEY_SENSOR_RANGE_PLUS)) {
+ sensor->IncreaseRange();
+ time_til_change = 0.5f;
+ }
+
+ else if (KeyDown(KEY_SENSOR_RANGE_MINUS)) {
+ sensor->DecreaseRange();
+ time_til_change = 0.5f;
+ }
+ }
+
+ if (KeyDown(KEY_EMCON_PLUS)) {
+ ship->SetEMCON(ship->GetEMCON()+1);
+ time_til_change = 0.5f;
+ }
+
+ else if (KeyDown(KEY_EMCON_MINUS)) {
+ ship->SetEMCON(ship->GetEMCON()-1);
+ time_til_change = 0.5f;
+ }
+ }
+ else
+ time_til_change -= seconds;
+
+ if (controller->ActionMap(KEY_ACTION_0))
+ ship->FirePrimary();
+
+ if (controller->ActionMap(KEY_ACTION_1)) {
+ if (!pickle_latch)
+ ship->FireSecondary();
+
+ pickle_latch = true;
+ }
+ else {
+ pickle_latch = false;
+ }
+
+ if (controller->ActionMap(KEY_ACTION_3)) {
+ if (!target_latch)
+ ship->LockTarget(SimObject::SIM_SHIP);
+
+ target_latch = true;
+ }
+ else {
+ target_latch = false;
+ }
+
+ ship->SetAugmenter(augmenter || (KeyDown(KEY_AUGMENTER) ? true : false));
+
+ if (Toggled(KEY_DECOY))
+ ship->FireDecoy();
+
+ if (Toggled(KEY_LAUNCH_PROBE))
+ ship->LaunchProbe();
+
+ if (Toggled(KEY_GEAR_TOGGLE))
+ ship->ToggleGear();
+
+ if (Toggled(KEY_NAVLIGHT_TOGGLE))
+ ship->ToggleNavlights();
+
+ if (Toggled(KEY_LOCK_TARGET))
+ ship->LockTarget(SimObject::SIM_SHIP, false, true);
+
+ else if (Toggled(KEY_LOCK_THREAT))
+ ship->LockTarget(SimObject::SIM_DRONE);
+
+ else if (Toggled(KEY_LOCK_CLOSEST_SHIP))
+ ship->LockTarget(SimObject::SIM_SHIP, true, false);
+
+ else if (Toggled(KEY_LOCK_CLOSEST_THREAT))
+ ship->LockTarget(SimObject::SIM_DRONE, true, false);
+
+ else if (Toggled(KEY_LOCK_HOSTILE_SHIP))
+ ship->LockTarget(SimObject::SIM_SHIP, true, true);
+
+ else if (Toggled(KEY_LOCK_HOSTILE_THREAT))
+ ship->LockTarget(SimObject::SIM_DRONE, true, true);
+
+ else if (Toggled(KEY_CYCLE_SUBTARGET))
+ ship->CycleSubTarget(1);
+
+ else if (Toggled(KEY_PREV_SUBTARGET))
+ ship->CycleSubTarget(-1);
+
+ if (Toggled(KEY_AUTO_NAV)) {
+ ship->SetAutoNav(true);
+ // careful: this object has just been deleted!
+ return;
+ }
+
+ if (Toggled(KEY_DROP_ORBIT)) {
+ ship->DropOrbit();
+ // careful: this object has just been deleted!
+ return;
+ }
+}
+
diff --git a/Stars45/ShipCtrl.h b/Stars45/ShipCtrl.h
new file mode 100644
index 0000000..f694832
--- /dev/null
+++ b/Stars45/ShipCtrl.h
@@ -0,0 +1,59 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ShipCtrl.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Starship (or space/ground station) class
+*/
+
+#ifndef ShipCtrl_h
+#define ShipCtrl_h
+
+#include "Types.h"
+#include "SimObject.h"
+#include "MotionController.h"
+#include "Director.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+class ShipDesign;
+class KeyMap;
+
+// +--------------------------------------------------------------------+
+
+class ShipCtrl : public Director
+{
+public:
+ enum TYPE { DIR_TYPE = 1 };
+
+ ShipCtrl(Ship* s, MotionController* m);
+
+ virtual void ExecFrame(double seconds);
+ virtual int Subframe() const { return true; }
+ virtual void Launch();
+
+ static int KeyDown(int action);
+ static int Toggled(int action);
+
+ virtual int Type() const { return DIR_TYPE; }
+
+protected:
+ Ship* ship;
+ MotionController* controller;
+
+ bool throttle_active;
+ bool launch_latch;
+ bool pickle_latch;
+ bool target_latch;
+};
+
+#endif ShipCtrl_h
+
diff --git a/Stars45/ShipDesign.cpp b/Stars45/ShipDesign.cpp
new file mode 100644
index 0000000..8c1da09
--- /dev/null
+++ b/Stars45/ShipDesign.cpp
@@ -0,0 +1,3700 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ShipDesign.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Starship Design parameters class
+*/
+
+#include "MemDebug.h"
+#include "ShipDesign.h"
+#include "Ship.h"
+#include "Shot.h"
+#include "Power.h"
+#include "HardPoint.h"
+#include "Weapon.h"
+#include "WeaponDesign.h"
+#include "Shield.h"
+#include "Sensor.h"
+#include "NavLight.h"
+#include "NavSystem.h"
+#include "Drive.h"
+#include "QuantumDrive.h"
+#include "Farcaster.h"
+#include "Thruster.h"
+#include "FlightDeck.h"
+#include "LandingGear.h"
+#include "Computer.h"
+#include "SystemDesign.h"
+#include "Component.h"
+
+#include "Game.h"
+#include "Solid.h"
+#include "Skin.h"
+#include "Sprite.h"
+#include "Light.h"
+#include "Bitmap.h"
+#include "Sound.h"
+#include "DataLoader.h"
+#include "ParseUtil.h"
+
+// +--------------------------------------------------------------------+
+
+const char* ship_design_class_name[32] = {
+ "Drone", "Fighter",
+ "Attack", "LCA",
+ "Courier", "Cargo",
+ "Corvette", "Freighter",
+
+ "Frigate", "Destroyer",
+ "Cruiser", "Battleship",
+ "Carrier", "Dreadnaught",
+
+ "Station", "Farcaster",
+
+ "Mine", "DEFSAT",
+ "COMSAT", "SWACS",
+
+ "Building", "Factory",
+ "SAM", "EWR",
+ "C3I", "Starbase",
+
+ "0x04000000", "0x08000000",
+ "0x10000000", "0x20000000",
+ "0x40000000", "0x80000000"
+};
+
+// +--------------------------------------------------------------------+
+
+static const int NAMELEN = 64;
+static bool degrees = false;
+
+struct ShipCatalogEntry {
+ static const char* TYPENAME() { return "ShipCatalogEntry"; }
+
+ ShipCatalogEntry() : hide(false), design(0) {}
+
+ ShipCatalogEntry(const char* n, const char* t, const char* p, const char* f, bool h=false) :
+ name(n), type(t), path(p), file(f), hide(h), design(0) {}
+
+ ~ShipCatalogEntry() { delete design; }
+
+ Text name;
+ Text type;
+ Text path;
+ Text file;
+ bool hide;
+
+ ShipDesign* design;
+};
+
+static List<ShipCatalogEntry> catalog;
+static List<ShipCatalogEntry> mod_catalog;
+
+// +--------------------------------------------------------------------+
+
+#define GET_DEF_BOOL(n) if (defname==(#n)) GetDefBool((n), def, filename)
+#define GET_DEF_TEXT(n) if (defname==(#n)) GetDefText((n), def, filename)
+#define GET_DEF_NUM(n) if (defname==(#n)) GetDefNumber((n), def, filename)
+#define GET_DEF_VEC(n) if (defname==(#n)) GetDefVec((n), def, filename)
+
+static char cockpit_name[80];
+static List<Text> detail[4];
+static List<Point> offset[4];
+
+static char errmsg[256];
+
+// +--------------------------------------------------------------------+
+
+ShipLoad::ShipLoad()
+{
+ ZeroMemory(name, sizeof(name));
+ ZeroMemory(load, sizeof(load));
+ mass = 0;
+}
+
+ShipSquadron::ShipSquadron()
+{
+ name[0] = 0;
+ design = 0;
+ count = 4;
+ avail = 4;
+}
+
+static void PrepareModel(Model& model)
+{
+ bool uses_bumps = false;
+
+ ListIter<Material> iter = model.GetMaterials();
+ while (++iter && !uses_bumps) {
+ Material* mtl = iter.value();
+ if (mtl->tex_bumpmap != 0 && mtl->bump != 0)
+ uses_bumps = true;
+ }
+
+ if (uses_bumps)
+ model.ComputeTangents();
+}
+
+// +--------------------------------------------------------------------+
+
+ShipDesign::ShipDesign()
+ : sensor(0), navsys(0), shield(0), type(0), decoy(0),
+ probe(0), gear(0), valid(false), secret(false), auto_roll(1), cockpit_model(0),
+ bolt_hit_sound_resource(0), beam_hit_sound_resource(0), lod_levels(0)
+{
+ ZeroMemory(filename, sizeof(filename));
+ ZeroMemory(path_name, sizeof(path_name));
+ ZeroMemory(name, sizeof(name));
+ ZeroMemory(display_name, sizeof(display_name));
+ ZeroMemory(abrv, sizeof(abrv));
+
+ for (int i = 0; i < 4; i++)
+ feature_size[i] = 0.0f;
+}
+
+// +--------------------------------------------------------------------+
+
+ShipDesign::ShipDesign(const char* n, const char* p, const char* fname, bool s)
+ : sensor(0), navsys(0), shield(0), type(0),
+ quantum_drive(0), farcaster(0), thruster(0), shield_model(0), decoy(0),
+ probe(0), gear(0), valid(false), secret(s), auto_roll(1), cockpit_model(0),
+ bolt_hit_sound_resource(0), beam_hit_sound_resource(0), lod_levels(0)
+{
+ ZeroMemory(filename, sizeof(filename));
+ ZeroMemory(path_name, sizeof(path_name));
+ ZeroMemory(name, sizeof(name));
+ ZeroMemory(display_name, sizeof(display_name));
+ ZeroMemory(abrv, sizeof(abrv));
+
+ strcpy(name, n);
+
+ if (!strstr(fname, ".def"))
+ sprintf(filename, "%s.def", fname);
+ else
+ strcpy(filename, fname);
+
+ for (int i = 0; i < 4; i++)
+ feature_size[i] = 0.0f;
+
+ scale = 1.0f;
+
+ agility = 2e2f;
+ air_factor = 0.1f;
+ vlimit = 8e3f;
+ drag = 2.5e-5f;
+ arcade_drag = 1.0f;
+ roll_drag = 5.0f;
+ pitch_drag = 5.0f;
+ yaw_drag = 5.0f;
+
+ roll_rate = 0.0f;
+ pitch_rate = 0.0f;
+ yaw_rate = 0.0f;
+
+ trans_x = 0.0f;
+ trans_y = 0.0f;
+ trans_z = 0.0f;
+
+ turn_bank = (float) (PI/8);
+
+ CL = 0.0f;
+ CD = 0.0f;
+ stall = 0.0f;
+
+ prep_time = 30.0f;
+ avoid_time = 0.0f;
+ avoid_fighter = 0.0f;
+ avoid_strike = 0.0f;
+ avoid_target = 0.0f;
+ commit_range = 0.0f;
+
+ splash_radius = -1.0f;
+ scuttle = 5e3f;
+ repair_speed = 1.0f;
+ repair_teams = 2;
+ repair_auto = true;
+ repair_screen = true;
+ wep_screen = true;
+
+ chase_vec = Vec3(0, -100, 20);
+ bridge_vec = Vec3(0, 0, 0);
+ beauty_cam = Vec3(0, 0, 0);
+ cockpit_scale = 1.0f;
+
+ radius = 1.0f;
+ integrity = 500.0f;
+
+ primary = 0;
+ secondary = 1;
+ main_drive = -1;
+
+ pcs = 3.0f;
+ acs = 1.0f;
+ detet = 250.0e3f;
+ e_factor[0] = 0.1f;
+ e_factor[1] = 0.3f;
+ e_factor[2] = 1.0f;
+
+ explosion_scale = 0.0f;
+ death_spiral_time = 3.0f;
+
+ if (!secret)
+ Print("Loading ShipDesign '%s'\n", name);
+
+ strcpy(path_name, p);
+ if (path_name[strlen(path_name)-1] != '/')
+ strcat(path_name, "/");
+
+ // Load Design File:
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath(path_name);
+
+ BYTE* block;
+ int blocklen = loader->LoadBuffer(filename, block, true);
+
+ // file not found:
+ if (blocklen <= 4) {
+ valid = false;
+ return;
+ }
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'\n", filename);
+ valid = false;
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "SHIP") {
+ Print("ERROR: invalid ship design file '%s'\n", filename);
+ valid = false;
+ return;
+ }
+ }
+
+ cockpit_name[0] = 0;
+ valid = true;
+ degrees = false;
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ ParseShip(def);
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ term->print();
+ }
+ }
+ }
+ while (term);
+
+ for (i = 0; i < 4; i++) {
+ int n = 0;
+ ListIter<Text> iter = detail[i];
+ while (++iter) {
+ const char* model_name = iter.value()->data();
+
+ Model* model = new(__FILE__,__LINE__) Model;
+ if (!model->Load(model_name, scale)) {
+ Print("ERROR: Could not load detail %d, model '%s'\n", i, model_name);
+ delete model;
+ model = 0;
+ valid = false;
+ }
+
+ else {
+ lod_levels = i+1;
+
+ if (model->Radius() > radius)
+ radius = (float) model->Radius();
+
+ models[i].append(model);
+ PrepareModel(*model);
+
+ if (offset[i].size()) {
+ *offset[i].at(n) *= scale;
+ offsets[i].append(offset[i].at(n)); // transfer ownership
+ }
+ else
+ offsets[i].append(new(__FILE__,__LINE__) Point);
+
+ n++;
+ }
+ }
+
+ detail[i].destroy();
+ }
+
+ if (!secret)
+ Print(" Ship Design Radius = %f\n", radius);
+
+ if (cockpit_name[0]) {
+ const char* model_name = cockpit_name;
+
+ cockpit_model = new(__FILE__,__LINE__) Model;
+ if (!cockpit_model->Load(model_name, cockpit_scale)) {
+ Print("ERROR: Could not load cockpit model '%s'\n", model_name);
+ delete cockpit_model;
+ cockpit_model = 0;
+ }
+ else {
+ if (!secret)
+ Print(" Loaded cockpit model '%s', preparing tangents\n", model_name);
+ PrepareModel(*cockpit_model);
+ }
+ }
+
+ if (beauty.Width() < 1 && loader->FindFile("beauty.pcx"))
+ loader->LoadBitmap("beauty.pcx", beauty);
+
+ if (hud_icon.Width() < 1 && loader->FindFile("hud_icon.pcx"))
+ loader->LoadBitmap("hud_icon.pcx", hud_icon);
+
+ loader->ReleaseBuffer(block);
+ loader->SetDataPath(0);
+
+ if (abrv[0] == 0) {
+ switch (type) {
+ case Ship::DRONE: strcpy(abrv, "DR"); break;
+ case Ship::FIGHTER: strcpy(abrv, "F"); break;
+ case Ship::ATTACK: strcpy(abrv, "F/A"); break;
+ case Ship::LCA: strcpy(abrv, "LCA"); break;
+ case Ship::CORVETTE: strcpy(abrv, "FC"); break;
+ case Ship::COURIER:
+ case Ship::CARGO:
+ case Ship::FREIGHTER: strcpy(abrv, "MV"); break;
+ case Ship::FRIGATE: strcpy(abrv, "FF"); break;
+ case Ship::DESTROYER: strcpy(abrv, "DD"); break;
+ case Ship::CRUISER: strcpy(abrv, "CA"); break;
+ case Ship::BATTLESHIP: strcpy(abrv, "BB"); break;
+ case Ship::CARRIER: strcpy(abrv, "CV"); break;
+ case Ship::DREADNAUGHT: strcpy(abrv, "DN"); break;
+ case Ship::MINE: strcpy(abrv, "MINE"); break;
+ case Ship::COMSAT: strcpy(abrv, "COMS"); break;
+ case Ship::DEFSAT: strcpy(abrv, "DEFS"); break;
+ case Ship::SWACS: strcpy(abrv, "SWAC"); break;
+ default: break;
+ }
+ }
+
+ if (scuttle < 1)
+ scuttle = 1;
+
+ if (splash_radius < 0)
+ splash_radius = radius * 12.0f;
+
+ if (repair_speed <= 1e-6)
+ repair_speed = 1.0e-6f;
+
+ if (commit_range <= 0) {
+ if (type <= Ship::LCA)
+ commit_range = 80.0e3f;
+ else
+ commit_range = 200.0e3f;
+ }
+
+ // calc standard loadout weights:
+ ListIter<ShipLoad> sl = loadouts;
+ while (++sl) {
+ for (int i = 0; i < hard_points.size(); i++) {
+ HardPoint* hp = hard_points[i];
+ sl->mass += hp->GetCarryMass(sl->load[i]);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+ShipDesign::~ShipDesign()
+{
+ delete bolt_hit_sound_resource;
+ delete beam_hit_sound_resource;
+ delete cockpit_model;
+ delete navsys;
+ delete sensor;
+ delete shield;
+ delete thruster;
+ delete farcaster;
+ delete quantum_drive;
+ delete decoy;
+ delete probe;
+ delete gear;
+
+ navlights.destroy();
+ flight_decks.destroy();
+ hard_points.destroy();
+ computers.destroy();
+ weapons.destroy();
+ drives.destroy();
+ reactors.destroy();
+ loadouts.destroy();
+ map_sprites.destroy();
+
+ delete shield_model;
+ for (int i = 0; i < 4; i++) {
+ models[i].destroy();
+ offsets[i].destroy();
+ }
+
+ spin_rates.destroy();
+
+ for (i = 0; i < 10; i++) {
+ delete debris[i].model;
+ }
+}
+
+const char*
+ShipDesign::DisplayName() const
+{
+ if (display_name[0])
+ return display_name;
+
+ return name;
+}
+
+// +--------------------------------------------------------------------+
+
+void AddModCatalogEntry(const char* design_name, const char* design_path)
+{
+ if (!design_name || !*design_name)
+ return;
+
+ ShipCatalogEntry* entry = 0;
+
+ for (int i = 0; i < catalog.size(); i++) {
+ ShipCatalogEntry* e = catalog[i];
+ if (e->name == design_name) {
+ if (design_path && *design_path && e->path != design_path)
+ continue;
+ entry = e;
+ return;
+ }
+ }
+
+ for (i = 0; i < mod_catalog.size(); i++) {
+ ShipCatalogEntry* e = mod_catalog[i];
+ if (e->name == design_name) {
+ if (design_path && *design_path) {
+ Text full_path = "Mods/Ships/";
+ full_path += design_path;
+
+ if (e->path != full_path)
+ continue;
+ }
+
+ entry = e;
+ return;
+ }
+ }
+
+ // still here? not found yet:
+ Text file = Text(design_name) + ".def";
+ Text path = Text("Mods/Ships/");
+ Text name;
+ Text type;
+ bool valid = false;
+
+ if (design_path && *design_path)
+ path += design_path;
+ else
+ path += design_name;
+
+ path += "/";
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath(path);
+
+ BYTE* block;
+ int blocklen = loader->LoadBuffer(file, block, true);
+
+ // file not found:
+ if (blocklen <= 4) {
+ return;
+ }
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'\n", file);
+ delete block;
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "SHIP") {
+ Print("ERROR: invalid ship design file '%s'\n", file);
+ delete block;
+ return;
+ }
+ }
+
+ valid = true;
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ Text defname = def->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "class") {
+ if (!GetDefText(type, def, file)) {
+ Print("WARNING: invalid or missing ship class in '%s'\n", file);
+ valid = false;
+ }
+ }
+
+ else if (defname == "name") {
+ if (!GetDefText(name, def, file)) {
+ Print("WARNING: invalid or missing ship name in '%s'\n", file);
+ valid = false;
+ }
+ }
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", file);
+ term->print();
+ }
+ }
+ }
+ while (term && valid && (name.length() < 1 || type.length() < 1));
+
+ delete block;
+
+ if (valid && name.length() && type.length()) {
+ Print("Add Mod Catalog Entry '%s' Class '%s'\n", name.data(), type.data());
+
+ ShipCatalogEntry* entry = new(__FILE__,__LINE__) ShipCatalogEntry(name, type, path, file);
+ mod_catalog.append(entry);
+ }
+}
+
+void
+ShipDesign::Initialize()
+{
+ if (catalog.size()) return;
+
+ LoadCatalog("Ships/", "catalog.def");
+ LoadSkins("Mods/Skins/");
+
+ List<Text> mod_designs;
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Mods/Ships/");
+ loader->ListFiles("*.def", mod_designs, true);
+
+ for (int i = 0; i < mod_designs.size(); i++) {
+ Text full_name = *mod_designs[i];
+ full_name.setSensitive(false);
+
+ if (full_name.contains('/') && !full_name.contains("catalog")) {
+ char path[1024];
+ strcpy(path, full_name.data());
+
+ char* name = path + full_name.length();
+ while (*name != '/')
+ name--;
+
+ *name++ = 0;
+
+ char* p = strrchr(name, '.');
+ if (p && strlen(p) > 3) {
+ if ((p[1] == 'd' || p[1] == 'D') &&
+ (p[2] == 'e' || p[2] == 'E') &&
+ (p[3] == 'f' || p[3] == 'F')) {
+
+ *p = 0;
+ }
+ }
+
+ // Just do a quick parse of the def file and add the
+ // info to the catalog. DON'T preload all of the models,
+ // textures, and weapons at this time. That takes way
+ // too long with some of the larger user mods.
+
+ AddModCatalogEntry(name, path);
+ }
+ }
+
+ mod_designs.destroy();
+ loader->SetDataPath(0);
+}
+
+void
+ShipDesign::Close()
+{
+ mod_catalog.destroy();
+ catalog.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+int
+ShipDesign::LoadCatalog(const char* path, const char* fname, bool mod)
+{
+ int result = 0;
+
+ // Load Design Catalog File:
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath(path);
+
+ char filename[NAMELEN];
+ ZeroMemory(filename, NAMELEN);
+ strncpy(filename, fname, NAMELEN-1);
+
+ Print("Loading ship design catalog: %s%s\n", path, filename);
+
+ BYTE* block;
+ int blocklen = loader->LoadBuffer(filename, block, true);
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'\n", filename);
+ loader->ReleaseBuffer(block);
+ loader->SetDataPath(0);
+ return result;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "SHIPCATALOG") {
+ Print("ERROR: invalid ship catalog file '%s'\n", filename);
+ loader->ReleaseBuffer(block);
+ loader->SetDataPath(0);
+ return result;
+ }
+ }
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ Text name, type, fname, path;
+ bool hide = false;
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def && def->term() && def->term()->isStruct()) {
+ TermStruct* val = def->term()->isStruct();
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "name") {
+ if (!GetDefText(name, pdef, filename))
+ Print("WARNING: invalid or missing ship name in '%s'\n", filename);
+ }
+ else if (defname == "type") {
+ if (!GetDefText(type, pdef, filename))
+ Print("WARNING: invalid or missing ship type in '%s'\n", filename);
+ }
+ else if (defname == "path") {
+ if (!GetDefText(path, pdef, filename))
+ Print("WARNING: invalid or missing ship path in '%s'\n", filename);
+ }
+ else if (defname == "file") {
+ if (!GetDefText(fname, pdef, filename))
+ Print("WARNING: invalid or missing ship file in '%s'\n", filename);
+ }
+ else if (defname == "hide" || defname == "secret") {
+ GetDefBool(hide, pdef, filename);
+ }
+ }
+ }
+
+ ShipCatalogEntry* entry = new(__FILE__,__LINE__) ShipCatalogEntry(name, type, path, fname, hide);
+
+ if (mod) mod_catalog.append(entry);
+ else catalog.append(entry);
+
+ result++;
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ term->print();
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+ loader->SetDataPath(0);
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::LoadSkins(const char* path, const char* archive)
+{
+ // Load MOD Skin Files:
+ List<Text> list;
+ DataLoader* loader = DataLoader::GetLoader();
+ bool oldfs = loader->IsFileSystemEnabled();
+
+ loader->UseFileSystem(true);
+ loader->SetDataPath(path);
+ loader->ListArchiveFiles(archive, "*.def", list);
+
+ ListIter<Text> iter = list;
+ while (++iter) {
+ Text filename = *iter.value();
+ BYTE* block;
+ int blocklen = loader->LoadBuffer(filename, block, true);
+
+ // file not found:
+ if (blocklen <= 4) {
+ continue;
+ }
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+ ShipDesign* design = 0;
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'\n", filename);
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "SKIN") {
+ Print("ERROR: invalid skin file '%s'\n", filename);
+ return;
+ }
+ }
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ Text defname = def->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "name") {
+ Text name;
+ GetDefText(name, def, filename);
+ design = Get(name);
+ }
+
+ else if (defname == "skin" && design != 0) {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: skin struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ Skin* skin = design->ParseSkin(val);
+
+ if (skin)
+ skin->SetPath(archive);
+ }
+ }
+ }
+ }
+ }
+ while (term);
+ }
+
+ loader->UseFileSystem(oldfs);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+ShipDesign::StandardCatalogSize()
+{
+ return catalog.size();
+}
+
+void
+ShipDesign::PreloadCatalog(int index)
+{
+ if (index >= 0 && index < catalog.size()) {
+ ShipCatalogEntry* entry = catalog[index];
+
+ if (entry->hide)
+ return;
+
+ int ship_class = ClassForName(entry->type);
+ if (ship_class > Ship::STARSHIPS)
+ return;
+
+ if (!entry->path.contains("Alliance_"))
+ return;
+
+ if (!entry->design) {
+ entry->design = new(__FILE__,__LINE__) ShipDesign(entry->name,
+ entry->path,
+ entry->file,
+ entry->hide);
+ }
+ }
+
+ else {
+ ListIter<ShipCatalogEntry> iter = catalog;
+ while (++iter) {
+ ShipCatalogEntry* entry = iter.value();
+
+ if (!entry->design) {
+ entry->design = new(__FILE__,__LINE__) ShipDesign(entry->name,
+ entry->path,
+ entry->file,
+ entry->hide);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+ShipDesign::CheckName(const char* design_name)
+{
+ ShipCatalogEntry* entry = 0;
+
+ for (int i = 0; i < catalog.size(); i++) {
+ if (catalog.at(i)->name == design_name) {
+ entry = catalog.at(i);
+ break;
+ }
+ }
+
+ if (!entry) {
+ for (int i = 0; i < mod_catalog.size(); i++) {
+ if (mod_catalog.at(i)->name == design_name) {
+ entry = mod_catalog.at(i);
+ break;
+ }
+ }
+ }
+
+ return entry != 0;
+}
+
+// +--------------------------------------------------------------------+
+
+ShipDesign*
+ShipDesign::Get(const char* design_name, const char* design_path)
+{
+ if (!design_name || !*design_name)
+ return 0;
+
+ ShipCatalogEntry* entry = 0;
+
+ for (int i = 0; i < catalog.size(); i++) {
+ ShipCatalogEntry* e = catalog[i];
+ if (e->name == design_name) {
+ if (design_path && *design_path && e->path != design_path)
+ continue;
+ entry = e;
+ break;
+ }
+ }
+
+ if (!entry) {
+ for (int i = 0; i < mod_catalog.size(); i++) {
+ ShipCatalogEntry* e = mod_catalog[i];
+ if (e->name == design_name) {
+ if (design_path && *design_path) {
+ Text full_path = "Mods/Ships/";
+ full_path += design_path;
+
+ if (e->path != full_path)
+ continue;
+ }
+
+ entry = e;
+ break;
+ }
+ }
+ }
+
+ if (entry) {
+ if (!entry->design) {
+ entry->design = new(__FILE__,__LINE__) ShipDesign(entry->name,
+ entry->path,
+ entry->file,
+ entry->hide);
+ }
+ return entry->design;
+ }
+ else {
+ Print("ShipDesign: no catalog entry for design '%s', checking mods...\n", design_name);
+ return ShipDesign::FindModDesign(design_name, design_path);
+ }
+}
+
+ShipDesign*
+ShipDesign::FindModDesign(const char* design_name, const char* design_path)
+{
+ Text file = Text(design_name) + ".def";
+ Text path = Text("Mods/Ships/");
+
+ if (design_path && *design_path)
+ path += design_path;
+ else
+ path += design_name;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath(path);
+
+ ShipDesign* design = new(__FILE__,__LINE__) ShipDesign(design_name, path, file);
+
+ if (design->valid) {
+ Print("ShipDesign: found mod design '%s'\n", design->name);
+
+ ShipCatalogEntry* entry = new(__FILE__,__LINE__) ShipCatalogEntry(design->name,
+ ClassName(design->type),
+ path,
+ file);
+ mod_catalog.append(entry);
+ entry->design = design;
+ return entry->design;
+ }
+ else {
+ delete design;
+ }
+
+ return 0;
+}
+
+void
+ShipDesign::ClearModCatalog()
+{
+ mod_catalog.destroy();
+
+ for (int i = 0; i < catalog.size(); i++) {
+ ShipCatalogEntry* e = catalog[i];
+
+ if (e && e->design) {
+ ListIter<Skin> iter = e->design->skins;
+
+ while (++iter) {
+ Skin* skin = iter.value();
+ if (*skin->Path())
+ iter.removeItem();
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+ShipDesign::GetDesignList(int type, List<Text>& designs)
+{
+ designs.clear();
+
+ for (int i = 0; i < catalog.size(); i++) {
+ ShipCatalogEntry* e = catalog[i];
+
+ int etype = ClassForName(e->type);
+ if (etype & type) {
+ if (!e->design)
+ e->design = new(__FILE__,__LINE__) ShipDesign(e->name,
+ e->path,
+ e->file,
+ e->hide);
+
+ if (e->hide || !e->design || !e->design->valid || e->design->secret)
+ continue;
+
+ designs.append(&e->name);
+ }
+ }
+
+ for (i = 0; i < mod_catalog.size(); i++) {
+ ShipCatalogEntry* e = mod_catalog[i];
+
+ int etype = ClassForName(e->type);
+ if (etype & type) {
+ designs.append(&e->name);
+ }
+ }
+
+ return designs.size();
+}
+
+// +--------------------------------------------------------------------+
+
+int
+ShipDesign::ClassForName(const char* name)
+{
+ if (!name || !name[0])
+ return 0;
+
+ for (int i = 0; i < 32; i++) {
+ if (!stricmp(name, ship_design_class_name[i])) {
+ return 1 << i;
+ }
+ }
+
+ return 0;
+}
+
+const char*
+ShipDesign::ClassName(int type)
+{
+ if (type != 0) {
+ int index = 0;
+
+ while (!(type & 1)) {
+ type >>= 1;
+ index++;
+ }
+
+ if (index >= 0 && index < 32)
+ return ship_design_class_name[index];
+ }
+
+ return "Unknown";
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseShip(TermDef* def)
+{
+ char detail_name[NAMELEN];
+ Vec3 off_loc;
+ Vec3 spin;
+ Text defname = def->name()->value();
+
+ defname.setSensitive(false);
+
+ if (defname == "cockpit_model") {
+ if (!GetDefText(cockpit_name, def, filename))
+ Print("WARNING: invalid or missing cockpit_model in '%s'\n", filename);
+ }
+
+ else if (defname == "model" || defname == "detail_0") {
+ if (!GetDefText(detail_name, def, filename))
+ Print("WARNING: invalid or missing model in '%s'\n", filename);
+
+ detail[0].append(new(__FILE__,__LINE__) Text(detail_name));
+ }
+
+ else if (defname == "detail_1") {
+ if (!GetDefText(detail_name, def, filename))
+ Print("WARNING: invalid or missing detail_1 in '%s'\n", filename);
+
+ detail[1].append(new(__FILE__,__LINE__) Text(detail_name));
+ }
+
+ else if (defname == "detail_2") {
+ if (!GetDefText(detail_name, def, filename))
+ Print("WARNING: invalid or missing detail_2 in '%s'\n", filename);
+
+ detail[2].append(new(__FILE__,__LINE__) Text(detail_name));
+ }
+
+ else if (defname == "detail_3") {
+ if (!GetDefText(detail_name, def, filename))
+ Print("WARNING: invalid or missing detail_3 in '%s'\n", filename);
+
+ detail[3].append(new(__FILE__,__LINE__) Text(detail_name));
+ }
+
+ else if (defname == "spin") {
+ if (!GetDefVec(spin, def, filename))
+ Print("WARNING: invalid or missing spin in '%s'\n", filename);
+
+ spin_rates.append(new(__FILE__,__LINE__) Point(spin));
+ }
+
+ else if (defname == "offset_0") {
+ if (!GetDefVec(off_loc, def, filename))
+ Print("WARNING: invalid or missing offset_0 in '%s'\n", filename);
+
+ offset[0].append(new(__FILE__,__LINE__) Point(off_loc));
+ }
+
+ else if (defname == "offset_1") {
+ if (!GetDefVec(off_loc, def, filename))
+ Print("WARNING: invalid or missing offset_1 in '%s'\n", filename);
+
+ offset[1].append(new(__FILE__,__LINE__) Point(off_loc));
+ }
+
+ else if (defname == "offset_2") {
+ if (!GetDefVec(off_loc, def, filename))
+ Print("WARNING: invalid or missing offset_2 in '%s'\n", filename);
+
+ offset[2].append(new(__FILE__,__LINE__) Point(off_loc));
+ }
+
+ else if (defname == "offset_3") {
+ if (!GetDefVec(off_loc, def, filename))
+ Print("WARNING: invalid or missing offset_3 in '%s'\n", filename);
+
+ offset[3].append(new(__FILE__,__LINE__) Point(off_loc));
+ }
+
+ else if (defname == "beauty") {
+ if (def->term() && def->term()->isArray()) {
+ GetDefVec(beauty_cam, def, filename);
+
+ if (degrees) {
+ beauty_cam.x *= (float) DEGREES;
+ beauty_cam.y *= (float) DEGREES;
+ }
+ }
+
+ else {
+ char beauty_name[64];
+ if (!GetDefText(beauty_name, def, filename))
+ Print("WARNING: invalid or missing beauty in '%s'\n", filename);
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->LoadBitmap(beauty_name, beauty);
+ }
+ }
+
+ else if (defname == "hud_icon") {
+ char hud_icon_name[64];
+ if (!GetDefText(hud_icon_name, def, filename))
+ Print("WARNING: invalid or missing hud_icon in '%s'\n", filename);
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->LoadBitmap(hud_icon_name, hud_icon);
+ }
+
+ else if (defname == "feature_0") {
+ if (!GetDefNumber(feature_size[0], def, filename))
+ Print("WARNING: invalid or missing feature_0 in '%s'\n", filename);
+ }
+
+ else if (defname == "feature_1") {
+ if (!GetDefNumber(feature_size[1], def, filename))
+ Print("WARNING: invalid or missing feature_1 in '%s'\n", filename);
+ }
+
+ else if (defname == "feature_2") {
+ if (!GetDefNumber(feature_size[2], def, filename))
+ Print("WARNING: invalid or missing feature_2 in '%s'\n", filename);
+ }
+
+ else if (defname == "feature_3") {
+ if (!GetDefNumber(feature_size[3], def, filename))
+ Print("WARNING: invalid or missing feature_3 in '%s'\n", filename);
+ }
+
+
+ else if (defname == "class") {
+ char typestr[64];
+ if (!GetDefText(typestr, def, filename))
+ Print("WARNING: invalid or missing ship class in '%s'\n", filename);
+
+ type = ClassForName(typestr);
+
+ if (type <= Ship::LCA) {
+ repair_auto = false;
+ repair_screen = false;
+ wep_screen = false;
+ }
+ }
+
+ else GET_DEF_TEXT(name);
+ else GET_DEF_TEXT(description);
+ else GET_DEF_TEXT(display_name);
+ else GET_DEF_TEXT(abrv);
+ else GET_DEF_NUM(pcs);
+ else GET_DEF_NUM(acs);
+ else GET_DEF_NUM(detet);
+ else GET_DEF_NUM(scale);
+ else GET_DEF_NUM(explosion_scale);
+ else GET_DEF_NUM(mass);
+ else GET_DEF_NUM(vlimit);
+ else GET_DEF_NUM(agility);
+ else GET_DEF_NUM(air_factor);
+ else GET_DEF_NUM(roll_rate);
+ else GET_DEF_NUM(pitch_rate);
+ else GET_DEF_NUM(yaw_rate);
+ else GET_DEF_NUM(integrity);
+ else GET_DEF_NUM(drag);
+ else GET_DEF_NUM(arcade_drag);
+ else GET_DEF_NUM(roll_drag);
+ else GET_DEF_NUM(pitch_drag);
+ else GET_DEF_NUM(yaw_drag);
+ else GET_DEF_NUM(trans_x);
+ else GET_DEF_NUM(trans_y);
+ else GET_DEF_NUM(trans_z);
+ else GET_DEF_NUM(turn_bank);
+ else GET_DEF_NUM(cockpit_scale);
+ else GET_DEF_NUM(auto_roll);
+
+ else GET_DEF_NUM(CL);
+ else GET_DEF_NUM(CD);
+ else GET_DEF_NUM(stall);
+
+ else GET_DEF_NUM(prep_time);
+ else GET_DEF_NUM(avoid_time);
+ else GET_DEF_NUM(avoid_fighter);
+ else GET_DEF_NUM(avoid_strike);
+ else GET_DEF_NUM(avoid_target);
+ else GET_DEF_NUM(commit_range);
+
+ else GET_DEF_NUM(splash_radius);
+ else GET_DEF_NUM(scuttle);
+ else GET_DEF_NUM(repair_speed);
+ else GET_DEF_NUM(repair_teams);
+ else GET_DEF_BOOL(secret);
+ else GET_DEF_BOOL(repair_auto);
+ else GET_DEF_BOOL(repair_screen);
+ else GET_DEF_BOOL(wep_screen);
+ else GET_DEF_BOOL(degrees);
+
+ else if (defname == "emcon_1") {
+ GetDefNumber(e_factor[0], def, filename);
+ }
+
+ else if (defname == "emcon_2") {
+ GetDefNumber(e_factor[1], def, filename);
+ }
+
+ else if (defname == "emcon_3") {
+ GetDefNumber(e_factor[2], def, filename);
+ }
+
+ else if (defname == "chase") {
+ if (!GetDefVec(chase_vec, def, filename))
+ Print("WARNING: invalid or missing chase cam loc in '%s'\n", filename);
+
+ chase_vec *= (float) scale;
+ }
+
+ else if (defname == "bridge") {
+ if (!GetDefVec(bridge_vec, def, filename))
+ Print("WARNING: invalid or missing bridge cam loc in '%s'\n", filename);
+
+ bridge_vec *= (float) scale;
+ }
+
+ else if (defname == "power") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: power source struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParsePower(val);
+ }
+ }
+
+ else if (defname == "main_drive" || defname == "drive") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: main drive struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseDrive(val);
+ }
+ }
+
+ else if (defname == "quantum" || defname == "quantum_drive") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: quantum_drive struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseQuantumDrive(val);
+ }
+ }
+
+ else if (defname == "sender" || defname == "farcaster") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: farcaster struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseFarcaster(val);
+ }
+ }
+
+ else if (defname == "thruster") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: thruster struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseThruster(val);
+ }
+ }
+
+ else if (defname == "navlight") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: navlight struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseNavlight(val);
+ }
+ }
+
+ else if (defname == "flightdeck") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: flightdeck struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseFlightDeck(val);
+ }
+ }
+
+ else if (defname == "gear") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: gear struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseLandingGear(val);
+ }
+ }
+
+ else if (defname == "weapon") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: weapon struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseWeapon(val);
+ }
+ }
+
+ else if (defname == "hardpoint") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: hardpoint struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseHardPoint(val);
+ }
+ }
+
+ else if (defname == "loadout") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: loadout struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseLoadout(val);
+ }
+ }
+
+ else if (defname == "decoy") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: decoy struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseWeapon(val);
+ }
+ }
+
+ else if (defname == "probe") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: probe struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseWeapon(val);
+ }
+ }
+
+ else if (defname == "sensor") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: sensor struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseSensor(val);
+ }
+ }
+
+ else if (defname == "nav") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: nav struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseNavsys(val);
+ }
+ }
+
+ else if (defname == "computer") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: computer struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseComputer(val);
+ }
+ }
+
+ else if (defname == "shield") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: shield struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseShield(val);
+ }
+ }
+
+ else if (defname == "death_spiral") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: death spiral struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseDeathSpiral(val);
+ }
+ }
+
+ else if (defname == "map") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: map struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseMap(val);
+ }
+ }
+
+ else if (defname == "squadron") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: squadron struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseSquadron(val);
+ }
+ }
+
+ else if (defname == "skin") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: skin struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseSkin(val);
+ }
+ }
+
+ else {
+ Print("WARNING: unknown parameter '%s' in '%s'\n",
+ defname.data(), filename);
+ }
+
+ if (description.length())
+ description = Game::GetText(description);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParsePower(TermStruct* val)
+{
+ int stype = 0;
+ float output = 1000.0f;
+ float fuel = 0.0f;
+ Vec3 loc(0.0f, 0.0f, 0.0f);
+ float size = 0.0f;
+ float hull = 0.5f;
+ Text design_name;
+ Text pname;
+ Text pabrv;
+ int etype = 0;
+ int emcon_1 = -1;
+ int emcon_2 = -1;
+ int emcon_3 = -1;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "type") {
+ TermText* tname = pdef->term()->isText();
+
+ if (tname) {
+ if (tname->value()[0] == 'B') stype = PowerSource::BATTERY;
+ else if (tname->value()[0] == 'A') stype = PowerSource::AUX;
+ else if (tname->value()[0] == 'F') stype = PowerSource::FUSION;
+ else Print("WARNING: unknown power source type '%s' in '%s'\n", tname->value().data(), filename);
+ }
+ }
+
+ else if (defname == "name") {
+ GetDefText(pname, pdef, filename);
+ }
+
+ else if (defname == "abrv") {
+ GetDefText(pabrv, pdef, filename);
+ }
+
+ else if (defname == "design") {
+ GetDefText(design_name, pdef, filename);
+ }
+
+ else if (defname == "max_output") {
+ GetDefNumber(output, pdef, filename);
+ }
+ else if (defname == "fuel_range") {
+ GetDefNumber(fuel, pdef, filename);
+ }
+
+ else if (defname == "loc") {
+ GetDefVec(loc, pdef, filename);
+ loc *= (float) scale;
+ }
+ else if (defname == "size") {
+ GetDefNumber(size, pdef, filename);
+ size *= (float) scale;
+ }
+ else if (defname == "hull_factor") {
+ GetDefNumber(hull, pdef, filename);
+ }
+
+ else if (defname == "explosion") {
+ GetDefNumber(etype, pdef, filename);
+ }
+
+ else if (defname == "emcon_1") {
+ GetDefNumber(emcon_1, pdef, filename);
+ }
+
+ else if (defname == "emcon_2") {
+ GetDefNumber(emcon_2, pdef, filename);
+ }
+
+ else if (defname == "emcon_3") {
+ GetDefNumber(emcon_3, pdef, filename);
+ }
+ }
+ }
+
+ PowerSource* source = new(__FILE__,__LINE__) PowerSource((PowerSource::SUBTYPE) stype, output);
+ if (pname.length()) source->SetName(pname);
+ if (pabrv.length()) source->SetName(pabrv);
+ source->SetFuelRange(fuel);
+ source->Mount(loc, size, hull);
+ source->SetExplosionType(etype);
+
+ if (design_name.length()) {
+ SystemDesign* sd = SystemDesign::Find(design_name);
+ if (sd)
+ source->SetDesign(sd);
+ }
+
+ if (emcon_1 >= 0 && emcon_1 <= 100)
+ source->SetEMCONPower(1, emcon_1);
+
+ if (emcon_2 >= 0 && emcon_2 <= 100)
+ source->SetEMCONPower(1, emcon_2);
+
+ if (emcon_3 >= 0 && emcon_3 <= 100)
+ source->SetEMCONPower(1, emcon_3);
+
+ reactors.append(source);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseDrive(TermStruct* val)
+{
+ Text dname;
+ Text dabrv;
+ int dtype = 0;
+ int etype = 0;
+ float dthrust = 1.0f;
+ float daug = 0.0f;
+ float dscale = 1.0f;
+ Vec3 loc(0.0f, 0.0f, 0.0f);
+ float size = 0.0f;
+ float hull = 0.5f;
+ Text design_name;
+ int emcon_1 = -1;
+ int emcon_2 = -1;
+ int emcon_3 = -1;
+ bool trail = true;
+ Drive* drive = 0;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "type") {
+ TermText* tname = pdef->term()->isText();
+
+ if (tname) {
+ Text tval = tname->value();
+ tval.setSensitive(false);
+
+ if (tval == "Plasma") dtype = Drive::PLASMA;
+ else if (tval == "Fusion") dtype = Drive::FUSION;
+ else if (tval == "Alien") dtype = Drive::GREEN;
+ else if (tval == "Green") dtype = Drive::GREEN;
+ else if (tval == "Red") dtype = Drive::RED;
+ else if (tval == "Blue") dtype = Drive::BLUE;
+ else if (tval == "Yellow") dtype = Drive::YELLOW;
+ else if (tval == "Stealth") dtype = Drive::STEALTH;
+
+ else Print("WARNING: unknown drive type '%s' in '%s'\n", tname->value().data(), filename);
+ }
+ }
+ else if (defname == "name") {
+ if (!GetDefText(dname, pdef, filename))
+ Print("WARNING: invalid or missing name for drive in '%s'\n", filename);
+ }
+
+ else if (defname == "abrv") {
+ if (!GetDefText(dabrv, pdef, filename))
+ Print("WARNING: invalid or missing abrv for drive in '%s'\n", filename);
+ }
+
+ else if (defname == "design") {
+ if (!GetDefText(design_name, pdef, filename))
+ Print("WARNING: invalid or missing design for drive in '%s'\n", filename);
+ }
+
+ else if (defname == "thrust") {
+ if (!GetDefNumber(dthrust, pdef, filename))
+ Print("WARNING: invalid or missing thrust for drive in '%s'\n", filename);
+ }
+
+ else if (defname == "augmenter") {
+ if (!GetDefNumber(daug, pdef, filename))
+ Print("WARNING: invalid or missing augmenter for drive in '%s'\n", filename);
+ }
+
+ else if (defname == "scale") {
+ if (!GetDefNumber(dscale, pdef, filename))
+ Print("WARNING: invalid or missing scale for drive in '%s'\n", filename);
+ }
+
+ else if (defname == "port") {
+ Vec3 port;
+ float flare_scale = 0;
+
+ if (pdef->term()->isArray()) {
+ GetDefVec(port, pdef, filename);
+ port *= scale;
+ flare_scale = dscale;
+ }
+
+ else if (pdef->term()->isStruct()) {
+ TermStruct* val = pdef->term()->isStruct();
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef2 = val->elements()->at(i)->isDef();
+ if (pdef2) {
+ if (pdef2->name()->value() == "loc") {
+ GetDefVec(port, pdef2, filename);
+ port *= scale;
+ }
+
+ else if (pdef2->name()->value() == "scale") {
+ GetDefNumber(flare_scale, pdef2, filename);
+ }
+ }
+ }
+
+ if (flare_scale <= 0)
+ flare_scale = dscale;
+ }
+
+ if (!drive)
+ drive = new(__FILE__,__LINE__) Drive((Drive::SUBTYPE) dtype, dthrust, daug, trail);
+
+ drive->AddPort(port, flare_scale);
+ }
+
+ else if (defname == "loc") {
+ if (!GetDefVec(loc, pdef, filename))
+ Print("WARNING: invalid or missing loc for drive in '%s'\n", filename);
+ loc *= (float) scale;
+ }
+
+ else if (defname == "size") {
+ if (!GetDefNumber(size, pdef, filename))
+ Print("WARNING: invalid or missing size for drive in '%s'\n", filename);
+ size *= (float) scale;
+ }
+
+ else if (defname == "hull_factor") {
+ if (!GetDefNumber(hull, pdef, filename))
+ Print("WARNING: invalid or missing hull_factor for drive in '%s'\n", filename);
+ }
+
+ else if (defname == "explosion") {
+ if (!GetDefNumber(etype, pdef, filename))
+ Print("WARNING: invalid or missing explosion for drive in '%s'\n", filename);
+ }
+
+ else if (defname == "emcon_1") {
+ GetDefNumber(emcon_1, pdef, filename);
+ }
+
+ else if (defname == "emcon_2") {
+ GetDefNumber(emcon_2, pdef, filename);
+ }
+
+ else if (defname == "emcon_3") {
+ GetDefNumber(emcon_3, pdef, filename);
+ }
+
+ else if (defname == "trail" || defname == "show_trail") {
+ GetDefBool(trail, pdef, filename);
+ }
+ }
+ }
+
+ if (!drive)
+ drive = new(__FILE__,__LINE__) Drive((Drive::SUBTYPE) dtype, dthrust, daug, trail);
+
+ drive->SetSourceIndex(reactors.size()-1);
+ drive->Mount(loc, size, hull);
+ if (dname.length()) drive->SetName(dname);
+ if (dabrv.length()) drive->SetAbbreviation(dabrv);
+ drive->SetExplosionType(etype);
+
+ if (design_name.length()) {
+ SystemDesign* sd = SystemDesign::Find(design_name);
+ if (sd)
+ drive->SetDesign(sd);
+ }
+
+ if (emcon_1 >= 0 && emcon_1 <= 100)
+ drive->SetEMCONPower(1, emcon_1);
+
+ if (emcon_2 >= 0 && emcon_2 <= 100)
+ drive->SetEMCONPower(1, emcon_2);
+
+ if (emcon_3 >= 0 && emcon_3 <= 100)
+ drive->SetEMCONPower(1, emcon_3);
+
+ main_drive = drives.size();
+ drives.append(drive);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseQuantumDrive(TermStruct* val)
+{
+ double capacity = 250e3;
+ double consumption = 1e3;
+ Vec3 loc(0.0f, 0.0f, 0.0f);
+ float size = 0.0f;
+ float hull = 0.5f;
+ float countdown = 5.0f;
+ Text design_name;
+ Text type_name;
+ Text abrv;
+ int subtype = QuantumDrive::QUANTUM;
+ int emcon_1 = -1;
+ int emcon_2 = -1;
+ int emcon_3 = -1;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "design") {
+ GetDefText(design_name, pdef, filename);
+ }
+ else if (defname == "abrv") {
+ GetDefText(abrv, pdef, filename);
+ }
+ else if (defname == "type") {
+ GetDefText(type_name, pdef, filename);
+ type_name.setSensitive(false);
+
+ if (type_name.contains("hyper")) {
+ subtype = QuantumDrive::HYPER;
+ }
+ }
+ else if (defname == "capacity") {
+ GetDefNumber(capacity, pdef, filename);
+ }
+ else if (defname == "consumption") {
+ GetDefNumber(consumption, pdef, filename);
+ }
+ else if (defname == "loc") {
+ GetDefVec(loc, pdef, filename);
+ loc *= (float) scale;
+ }
+ else if (defname == "size") {
+ GetDefNumber(size, pdef, filename);
+ size *= (float) scale;
+ }
+ else if (defname == "hull_factor") {
+ GetDefNumber(hull, pdef, filename);
+ }
+ else if (defname == "jump_time") {
+ GetDefNumber(countdown, pdef, filename);
+ }
+ else if (defname == "countdown") {
+ GetDefNumber(countdown, pdef, filename);
+ }
+
+ else if (defname == "emcon_1") {
+ GetDefNumber(emcon_1, pdef, filename);
+ }
+
+ else if (defname == "emcon_2") {
+ GetDefNumber(emcon_2, pdef, filename);
+ }
+
+ else if (defname == "emcon_3") {
+ GetDefNumber(emcon_3, pdef, filename);
+ }
+ }
+ }
+
+ QuantumDrive* drive = new(__FILE__,__LINE__) QuantumDrive((QuantumDrive::SUBTYPE) subtype, capacity, consumption);
+ drive->SetSourceIndex(reactors.size()-1);
+ drive->Mount(loc, size, hull);
+ drive->SetCountdown(countdown);
+
+ if (design_name.length()) {
+ SystemDesign* sd = SystemDesign::Find(design_name);
+ if (sd)
+ drive->SetDesign(sd);
+ }
+
+ if (abrv.length())
+ drive->SetAbbreviation(abrv);
+
+ if (emcon_1 >= 0 && emcon_1 <= 100)
+ drive->SetEMCONPower(1, emcon_1);
+
+ if (emcon_2 >= 0 && emcon_2 <= 100)
+ drive->SetEMCONPower(1, emcon_2);
+
+ if (emcon_3 >= 0 && emcon_3 <= 100)
+ drive->SetEMCONPower(1, emcon_3);
+
+ quantum_drive = drive;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseFarcaster(TermStruct* val)
+{
+ Text design_name;
+ double capacity = 300e3;
+ double consumption = 15e3; // twenty second recharge
+ int napproach = 0;
+ Vec3 approach[Farcaster::NUM_APPROACH_PTS];
+ Vec3 loc(0.0f, 0.0f, 0.0f);
+ Vec3 start(0.0f, 0.0f, 0.0f);
+ Vec3 end(0.0f, 0.0f, 0.0f);
+ float size = 0.0f;
+ float hull = 0.5f;
+ int emcon_1 = -1;
+ int emcon_2 = -1;
+ int emcon_3 = -1;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "design") {
+ GetDefText(design_name, pdef, filename);
+ }
+ else if (defname == "capacity") {
+ GetDefNumber(capacity, pdef, filename);
+ }
+ else if (defname == "consumption") {
+ GetDefNumber(consumption, pdef, filename);
+ }
+ else if (defname == "loc") {
+ GetDefVec(loc, pdef, filename);
+ loc *= (float) scale;
+ }
+ else if (defname == "size") {
+ GetDefNumber(size, pdef, filename);
+ size *= (float) scale;
+ }
+ else if (defname == "hull_factor") {
+ GetDefNumber(hull, pdef, filename);
+ }
+
+ else if (defname == "start") {
+ GetDefVec(start, pdef, filename);
+ start *= (float) scale;
+ }
+ else if (defname == "end") {
+ GetDefVec(end, pdef, filename);
+ end *= (float) scale;
+ }
+ else if (defname == "approach") {
+ if (napproach < Farcaster::NUM_APPROACH_PTS) {
+ GetDefVec(approach[napproach], pdef, filename);
+ approach[napproach++] *= (float) scale;
+ }
+ else {
+ Print("WARNING: farcaster approach point ignored in '%s' (max=%d)\n",
+ filename, Farcaster::NUM_APPROACH_PTS);
+ }
+ }
+
+ else if (defname == "emcon_1") {
+ GetDefNumber(emcon_1, pdef, filename);
+ }
+
+ else if (defname == "emcon_2") {
+ GetDefNumber(emcon_2, pdef, filename);
+ }
+
+ else if (defname == "emcon_3") {
+ GetDefNumber(emcon_3, pdef, filename);
+ }
+ }
+ }
+
+ Farcaster* caster = new(__FILE__,__LINE__) Farcaster(capacity, consumption);
+ caster->SetSourceIndex(reactors.size()-1);
+ caster->Mount(loc, size, hull);
+
+ if (design_name.length()) {
+ SystemDesign* sd = SystemDesign::Find(design_name);
+ if (sd)
+ caster->SetDesign(sd);
+ }
+
+ caster->SetStartPoint(start);
+ caster->SetEndPoint(end);
+
+ for (i = 0; i < napproach; i++)
+ caster->SetApproachPoint(i, approach[i]);
+
+ if (emcon_1 >= 0 && emcon_1 <= 100)
+ caster->SetEMCONPower(1, emcon_1);
+
+ if (emcon_2 >= 0 && emcon_2 <= 100)
+ caster->SetEMCONPower(1, emcon_2);
+
+ if (emcon_3 >= 0 && emcon_3 <= 100)
+ caster->SetEMCONPower(1, emcon_3);
+
+ farcaster = caster;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseThruster(TermStruct* val)
+{
+ if (thruster) {
+ Print("WARNING: additional thruster ignored in '%s'\n", filename);
+ return;
+ }
+
+ double thrust = 100;
+
+ Vec3 loc(0.0f, 0.0f, 0.0f);
+ float size = 0.0f;
+ float hull = 0.5f;
+ Text design_name;
+ float tscale = 1.0f;
+ int emcon_1 = -1;
+ int emcon_2 = -1;
+ int emcon_3 = -1;
+ int dtype = 0;
+
+ Thruster* drive = 0;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+
+ if (defname == "type") {
+ TermText* tname = pdef->term()->isText();
+
+ if (tname) {
+ Text tval = tname->value();
+ tval.setSensitive(false);
+
+ if (tval == "Plasma") dtype = Drive::PLASMA;
+ else if (tval == "Fusion") dtype = Drive::FUSION;
+ else if (tval == "Alien") dtype = Drive::GREEN;
+ else if (tval == "Green") dtype = Drive::GREEN;
+ else if (tval == "Red") dtype = Drive::RED;
+ else if (tval == "Blue") dtype = Drive::BLUE;
+ else if (tval == "Yellow") dtype = Drive::YELLOW;
+ else if (tval == "Stealth") dtype = Drive::STEALTH;
+
+ else Print("WARNING: unknown thruster type '%s' in '%s'\n", tname->value().data(), filename);
+ }
+ }
+
+ else if (defname == "thrust") {
+ GetDefNumber(thrust, pdef, filename);
+ }
+
+ else if (defname == "design") {
+ GetDefText(design_name, pdef, filename);
+ }
+
+ else if (defname == "loc") {
+ GetDefVec(loc, pdef, filename);
+ loc *= (float) scale;
+ }
+ else if (defname == "size") {
+ GetDefNumber(size, pdef, filename);
+ size *= (float) scale;
+ }
+ else if (defname == "hull_factor") {
+ GetDefNumber(hull, pdef, filename);
+ }
+ else if (defname == "scale") {
+ GetDefNumber(tscale, pdef, filename);
+ }
+ else if (defname.contains("port") && pdef->term()) {
+ Vec3 port;
+ float port_scale = 0;
+ DWORD fire = 0;
+
+ if (pdef->term()->isArray()) {
+ GetDefVec(port, pdef, filename);
+ port *= scale;
+ port_scale = tscale;
+ }
+
+ else if (pdef->term()->isStruct()) {
+ TermStruct* val = pdef->term()->isStruct();
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef2 = val->elements()->at(i)->isDef();
+ if (pdef2) {
+ if (pdef2->name()->value() == "loc") {
+ GetDefVec(port, pdef2, filename);
+ port *= scale;
+ }
+
+ else if (pdef2->name()->value() == "fire") {
+ GetDefNumber(fire, pdef2, filename);
+ }
+
+ else if (pdef2->name()->value() == "scale") {
+ GetDefNumber(port_scale, pdef2, filename);
+ }
+ }
+ }
+
+ if (port_scale <= 0)
+ port_scale = tscale;
+ }
+
+ if (!drive)
+ drive = new(__FILE__,__LINE__) Thruster(dtype, thrust, tscale);
+
+ if (defname == "port" || defname == "port_bottom")
+ drive->AddPort(Thruster::BOTTOM, port, fire, port_scale);
+
+ else if (defname == "port_top")
+ drive->AddPort(Thruster::TOP, port, fire, port_scale);
+
+ else if (defname == "port_left")
+ drive->AddPort(Thruster::LEFT, port, fire, port_scale);
+
+ else if (defname == "port_right")
+ drive->AddPort(Thruster::RIGHT, port, fire, port_scale);
+
+ else if (defname == "port_fore")
+ drive->AddPort(Thruster::FORE, port, fire, port_scale);
+
+ else if (defname == "port_aft")
+ drive->AddPort(Thruster::AFT, port, fire, port_scale);
+ }
+
+ else if (defname == "emcon_1") {
+ GetDefNumber(emcon_1, pdef, filename);
+ }
+
+ else if (defname == "emcon_2") {
+ GetDefNumber(emcon_2, pdef, filename);
+ }
+
+ else if (defname == "emcon_3") {
+ GetDefNumber(emcon_3, pdef, filename);
+ }
+ }
+ }
+
+ if (!drive)
+ drive = new(__FILE__,__LINE__) Thruster(dtype, thrust, tscale);
+ drive->SetSourceIndex(reactors.size()-1);
+ drive->Mount(loc, size, hull);
+
+ if (design_name.length()) {
+ SystemDesign* sd = SystemDesign::Find(design_name);
+ if (sd)
+ drive->SetDesign(sd);
+ }
+
+ if (emcon_1 >= 0 && emcon_1 <= 100)
+ drive->SetEMCONPower(1, emcon_1);
+
+ if (emcon_2 >= 0 && emcon_2 <= 100)
+ drive->SetEMCONPower(1, emcon_2);
+
+ if (emcon_3 >= 0 && emcon_3 <= 100)
+ drive->SetEMCONPower(1, emcon_3);
+
+ thruster = drive;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseNavlight(TermStruct* val)
+{
+ Text dname;
+ Text dabrv;
+ Text design_name;
+ int nlights = 0;
+ float dscale = 1.0f;
+ float period = 10.0f;
+ Vec3 bloc[NavLight::MAX_LIGHTS];
+ int btype[NavLight::MAX_LIGHTS];
+ DWORD pattern[NavLight::MAX_LIGHTS];
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "name")
+ GetDefText(dname, pdef, filename);
+ else if (defname == "abrv")
+ GetDefText(dabrv, pdef, filename);
+
+ else if (defname == "design") {
+ GetDefText(design_name, pdef, filename);
+ }
+
+ else if (defname == "scale") {
+ GetDefNumber(dscale, pdef, filename);
+ }
+ else if (defname == "period") {
+ GetDefNumber(period, pdef, filename);
+ }
+ else if (defname == "light") {
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: light struct missing for ship '%s' in '%s'\n", name, filename);
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+
+ Vec3 loc;
+ int t = 0;
+ DWORD ptn = 0;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "type") {
+ GetDefNumber(t, pdef, filename);
+ }
+ else if (defname == "loc") {
+ GetDefVec(loc, pdef, filename);
+ }
+ else if (defname == "pattern") {
+ GetDefNumber(ptn, pdef, filename);
+ }
+ }
+ }
+
+ if (t < 1 || t > 4)
+ t = 1;
+
+ if (nlights < NavLight::MAX_LIGHTS) {
+ bloc[nlights] = loc * scale;
+ btype[nlights] = t-1;
+ pattern[nlights] = ptn;
+ nlights++;
+ }
+ else {
+ Print("WARNING: Too many lights ship '%s' in '%s'\n", name, filename);
+ }
+ }
+ }
+ }
+ }
+
+ NavLight* nav = new(__FILE__,__LINE__) NavLight(period, dscale);
+ if (dname.length()) nav->SetName(dname);
+ if (dabrv.length()) nav->SetAbbreviation(dabrv);
+
+ if (design_name.length()) {
+ SystemDesign* sd = SystemDesign::Find(design_name);
+ if (sd)
+ nav->SetDesign(sd);
+ }
+
+ for (i = 0; i < nlights; i++)
+ nav->AddBeacon(bloc[i], pattern[i], btype[i]);
+
+ navlights.append(nav);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseFlightDeck(TermStruct* val)
+{
+ Text dname;
+ Text dabrv;
+ Text design_name;
+ float dscale = 1.0f;
+ float az = 0.0f;
+ int etype = 0;
+
+ bool launch = false;
+ bool recovery = false;
+ int nslots = 0;
+ int napproach = 0;
+ int nrunway = 0;
+ DWORD filters[10];
+ Vec3 spots[10];
+ Vec3 approach[FlightDeck::NUM_APPROACH_PTS];
+ Vec3 runway[2];
+ Vec3 loc(0,0,0);
+ Vec3 start(0,0,0);
+ Vec3 end(0,0,0);
+ Vec3 cam(0,0,0);
+ Vec3 box(0,0,0);
+ float cycle_time = 0.0f;
+ float size = 0.0f;
+ float hull = 0.5f;
+
+ float light = 0.0f;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "name")
+ GetDefText(dname, pdef, filename);
+ else if (defname == "abrv")
+ GetDefText(dabrv, pdef, filename);
+ else if (defname == "design")
+ GetDefText(design_name, pdef, filename);
+
+ else if (defname == "start") {
+ GetDefVec(start, pdef, filename);
+ start *= (float) scale;
+ }
+ else if (defname == "end") {
+ GetDefVec(end, pdef, filename);
+ end *= (float) scale;
+ }
+ else if (defname == "cam") {
+ GetDefVec(cam, pdef, filename);
+ cam *= (float) scale;
+ }
+ else if (defname == "box" || defname == "bounding_box") {
+ GetDefVec(box, pdef, filename);
+ box *= (float) scale;
+ }
+ else if (defname == "approach") {
+ if (napproach < FlightDeck::NUM_APPROACH_PTS) {
+ GetDefVec(approach[napproach], pdef, filename);
+ approach[napproach++] *= (float) scale;
+ }
+ else {
+ Print("WARNING: flight deck approach point ignored in '%s' (max=%d)\n",
+ filename, FlightDeck::NUM_APPROACH_PTS);
+ }
+ }
+ else if (defname == "runway") {
+ GetDefVec(runway[nrunway], pdef, filename);
+ runway[nrunway++] *= (float) scale;
+ }
+ else if (defname == "spot") {
+ if (pdef->term()->isStruct()) {
+ TermStruct* s = pdef->term()->isStruct();
+ for (int i = 0; i < s->elements()->size(); i++) {
+ TermDef* d = s->elements()->at(i)->isDef();
+ if (d) {
+ if (d->name()->value() == "loc") {
+ GetDefVec(spots[nslots], d, filename);
+ spots[nslots] *= (float) scale;
+ }
+ else if (d->name()->value() == "filter") {
+ GetDefNumber(filters[nslots], d, filename);
+ }
+ }
+ }
+
+ nslots++;
+ }
+
+ else if (pdef->term()->isArray()) {
+ GetDefVec(spots[nslots], pdef, filename);
+ spots[nslots] *= (float) scale;
+ filters[nslots++] = 0xf;
+ }
+ }
+
+ else if (defname == "light") {
+ GetDefNumber(light, pdef, filename);
+ }
+
+ else if (defname == "cycle_time") {
+ GetDefNumber(cycle_time, pdef, filename);
+ }
+
+ else if (defname == "launch") {
+ GetDefBool(launch, pdef, filename);
+ }
+
+ else if (defname == "recovery") {
+ GetDefBool(recovery, pdef, filename);
+ }
+
+ else if (defname == "azimuth") {
+ GetDefNumber(az, pdef, filename);
+ if (degrees) az *= (float) DEGREES;
+ }
+
+ else if (defname == "loc") {
+ GetDefVec(loc, pdef, filename);
+ loc *= (float) scale;
+ }
+ else if (defname == "size") {
+ GetDefNumber(size, pdef, filename);
+ size *= (float) scale;
+ }
+ else if (defname == "hull_factor") {
+ GetDefNumber(hull, pdef, filename);
+ }
+ else if (defname == "explosion") {
+ GetDefNumber(etype, pdef, filename);
+ }
+ }
+ }
+
+ FlightDeck* deck = new(__FILE__,__LINE__) FlightDeck();
+ deck->Mount(loc, size, hull);
+ if (dname.length()) deck->SetName(dname);
+ if (dabrv.length()) deck->SetAbbreviation(dabrv);
+
+ if (design_name.length()) {
+ SystemDesign* sd = SystemDesign::Find(design_name);
+ if (sd)
+ deck->SetDesign(sd);
+ }
+
+ if (launch)
+ deck->SetLaunchDeck();
+ else if (recovery)
+ deck->SetRecoveryDeck();
+
+ deck->SetAzimuth(az);
+ deck->SetBoundingBox(box);
+ deck->SetStartPoint(start);
+ deck->SetEndPoint(end);
+ deck->SetCamLoc(cam);
+ deck->SetExplosionType(etype);
+
+ if (light > 0)
+ deck->SetLight(light);
+
+ for (i = 0; i < napproach; i++)
+ deck->SetApproachPoint(i, approach[i]);
+
+ for (i = 0; i < nrunway; i++)
+ deck->SetRunwayPoint(i, runway[i]);
+
+ for (i = 0; i < nslots; i++)
+ deck->AddSlot(spots[i], filters[i]);
+
+ if (cycle_time > 0)
+ deck->SetCycleTime(cycle_time);
+
+ flight_decks.append(deck);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseLandingGear(TermStruct* val)
+{
+ Text dname;
+ Text dabrv;
+ Text design_name;
+ int ngear = 0;
+ Vec3 start[LandingGear::MAX_GEAR];
+ Vec3 end[LandingGear::MAX_GEAR];
+ Model* model[LandingGear::MAX_GEAR];
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "name")
+ GetDefText(dname, pdef, filename);
+ else if (defname == "abrv")
+ GetDefText(dabrv, pdef, filename);
+
+ else if (defname == "design") {
+ GetDefText(design_name, pdef, filename);
+ }
+
+ else if (defname == "gear") {
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: gear struct missing for ship '%s' in '%s'\n", name, filename);
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+
+ Vec3 v1, v2;
+ char mod_name[256];
+
+ ZeroMemory(mod_name, sizeof(mod_name));
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "model") {
+ GetDefText(mod_name, pdef, filename);
+ }
+ else if (defname == "start") {
+ GetDefVec(v1, pdef, filename);
+ }
+ else if (defname == "end") {
+ GetDefVec(v2, pdef, filename);
+ }
+ }
+ }
+
+ if (ngear < LandingGear::MAX_GEAR) {
+ Model* m = new(__FILE__,__LINE__) Model;
+ if (!m->Load(mod_name, scale)) {
+ Print("WARNING: Could not load landing gear model '%s'\n", mod_name);
+ delete m;
+ m = 0;
+ }
+ else {
+ model[ngear] = m;
+ start[ngear] = v1 * scale;
+ end[ngear] = v2 * scale;
+ ngear++;
+ }
+ }
+ else {
+ Print("WARNING: Too many landing gear ship '%s' in '%s'\n", name, filename);
+ }
+ }
+ }
+ }
+ }
+
+ gear = new(__FILE__,__LINE__) LandingGear();
+ if (dname.length()) gear->SetName(dname);
+ if (dabrv.length()) gear->SetAbbreviation(dabrv);
+
+ if (design_name.length()) {
+ SystemDesign* sd = SystemDesign::Find(design_name);
+ if (sd)
+ gear->SetDesign(sd);
+ }
+
+ for (i = 0; i < ngear; i++)
+ gear->AddGear(model[i], start[i], end[i]);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseWeapon(TermStruct* val)
+{
+ Text wtype;
+ Text wname;
+ Text wabrv;
+ Text design_name;
+ Text group_name;
+ int nmuz = 0;
+ Vec3 muzzles[Weapon::MAX_BARRELS];
+ Vec3 loc(0.0f, 0.0f, 0.0f);
+ float size = 0.0f;
+ float hull = 0.5f;
+ float az = 0.0f;
+ float el = 0.0f;
+ float az_max = 1e6f;
+ float az_min = 1e6f;
+ float el_max = 1e6f;
+ float el_min = 1e6f;
+ float az_rest = 1e6f;
+ float el_rest = 1e6f;
+ int etype = 0;
+ int emcon_1 = -1;
+ int emcon_2 = -1;
+ int emcon_3 = -1;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "type")
+ GetDefText(wtype, pdef, filename);
+ else if (defname == "name")
+ GetDefText(wname, pdef, filename);
+ else if (defname == "abrv")
+ GetDefText(wabrv, pdef, filename);
+ else if (defname == "design")
+ GetDefText(design_name, pdef, filename);
+ else if (defname == "group")
+ GetDefText(group_name, pdef, filename);
+
+ else if (defname == "muzzle") {
+ if (nmuz < Weapon::MAX_BARRELS) {
+ GetDefVec(muzzles[nmuz], pdef, filename);
+ nmuz++;
+ }
+ else {
+ Print("WARNING: too many muzzles (max=%d) for weapon in '%s'\n", filename, Weapon::MAX_BARRELS);
+ }
+ }
+ else if (defname == "loc") {
+ GetDefVec(loc, pdef, filename);
+ loc *= (float) scale;
+ }
+ else if (defname == "size") {
+ GetDefNumber(size, pdef, filename);
+ size *= (float) scale;
+ }
+ else if (defname == "hull_factor") {
+ GetDefNumber(hull, pdef, filename);
+ }
+ else if (defname == "azimuth") {
+ GetDefNumber(az, pdef, filename);
+ if (degrees) az *= (float) DEGREES;
+ }
+ else if (defname == "elevation") {
+ GetDefNumber(el, pdef, filename);
+ if (degrees) el *= (float) DEGREES;
+ }
+
+ else if (defname==("aim_az_max")) {
+ GetDefNumber(az_max,pdef,filename);
+ if (degrees) az_max *= (float) DEGREES;
+ az_min = 0.0f - az_max;
+ }
+
+ else if (defname==("aim_el_max")) {
+ GetDefNumber(el_max,pdef,filename);
+ if (degrees) el_max *= (float) DEGREES;
+ el_min = 0.0f - el_max;
+ }
+
+ else if (defname==("aim_az_min")) {
+ GetDefNumber(az_min,pdef,filename);
+ if (degrees) az_min *= (float) DEGREES;
+ }
+
+ else if (defname==("aim_el_min")) {
+ GetDefNumber(el_min,pdef,filename);
+ if (degrees) el_min *= (float) DEGREES;
+ }
+
+ else if (defname==("aim_az_rest")) {
+ GetDefNumber(az_rest,pdef,filename);
+ if (degrees) az_rest *= (float) DEGREES;
+ }
+
+ else if (defname==("aim_el_rest")) {
+ GetDefNumber(el_rest,pdef,filename);
+ if (degrees) el_rest *= (float) DEGREES;
+ }
+
+ else if (defname == "rest_azimuth") {
+ GetDefNumber(az_rest, pdef, filename);
+ if (degrees) az_rest *= (float) DEGREES;
+ }
+ else if (defname == "rest_elevation") {
+ GetDefNumber(el_rest, pdef, filename);
+ if (degrees) el_rest *= (float) DEGREES;
+ }
+ else if (defname == "explosion") {
+ GetDefNumber(etype, pdef, filename);
+ }
+
+ else if (defname == "emcon_1") {
+ GetDefNumber(emcon_1, pdef, filename);
+ }
+
+ else if (defname == "emcon_2") {
+ GetDefNumber(emcon_2, pdef, filename);
+ }
+
+ else if (defname == "emcon_3") {
+ GetDefNumber(emcon_3, pdef, filename);
+ }
+ else {
+ Print("WARNING: unknown weapon parameter '%s' in '%s'\n",
+ defname.data(), filename);
+ }
+ }
+ }
+
+ WeaponDesign* meta = WeaponDesign::Find(wtype);
+ if (!meta) {
+ Print("WARNING: unusual weapon name '%s' in '%s'\n", (const char*) wtype, filename);
+ }
+ else {
+ // non-turret weapon muzzles are relative to ship scale:
+ if (meta->turret_model == 0) {
+ for (int i = 0; i < nmuz; i++)
+ muzzles[i] *= (float) scale;
+ }
+
+ // turret weapon muzzles are relative to weapon scale:
+ else {
+ for (int i = 0; i < nmuz; i++)
+ muzzles[i] *= (float) meta->scale;
+ }
+
+ Weapon* gun = new(__FILE__,__LINE__) Weapon(meta, nmuz, muzzles, az, el);
+ gun->SetSourceIndex(reactors.size()-1);
+ gun->Mount(loc, size, hull);
+
+ if (az_max < 1e6) gun->SetAzimuthMax(az_max);
+ if (az_min < 1e6) gun->SetAzimuthMin(az_min);
+ if (az_rest < 1e6) gun->SetRestAzimuth(az_rest);
+
+ if (el_max < 1e6) gun->SetElevationMax(el_max);
+ if (el_min < 1e6) gun->SetElevationMin(el_min);
+ if (el_rest < 1e6) gun->SetRestElevation(el_rest);
+
+ if (emcon_1 >= 0 && emcon_1 <= 100)
+ gun->SetEMCONPower(1, emcon_1);
+
+ if (emcon_2 >= 0 && emcon_2 <= 100)
+ gun->SetEMCONPower(1, emcon_2);
+
+ if (emcon_3 >= 0 && emcon_3 <= 100)
+ gun->SetEMCONPower(1, emcon_3);
+
+ if (wname.length()) gun->SetName(wname);
+ if (wabrv.length()) gun->SetAbbreviation(wabrv);
+
+ if (design_name.length()) {
+ SystemDesign* sd = SystemDesign::Find(design_name);
+ if (sd)
+ gun->SetDesign(sd);
+ }
+
+ if (group_name.length()) {
+ gun->SetGroup(group_name);
+ }
+
+ gun->SetExplosionType(etype);
+
+ if (meta->decoy_type && !decoy)
+ decoy = gun;
+ else if (meta->probe && !probe)
+ probe = gun;
+ else
+ weapons.append(gun);
+ }
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath(path_name);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseHardPoint(TermStruct* val)
+{
+ Text wtypes[8];
+ Text wname;
+ Text wabrv;
+ Text design;
+ Vec3 muzzle;
+ Vec3 loc(0.0f, 0.0f, 0.0f);
+ float size = 0.0f;
+ float hull = 0.5f;
+ float az = 0.0f;
+ float el = 0.0f;
+ int ntypes = 0;
+ int emcon_1 = -1;
+ int emcon_2 = -1;
+ int emcon_3 = -1;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "type")
+ GetDefText(wtypes[ntypes++], pdef, filename);
+ else if (defname == "name")
+ GetDefText(wname, pdef, filename);
+ else if (defname == "abrv")
+ GetDefText(wabrv, pdef, filename);
+ else if (defname == "design")
+ GetDefText(design, pdef, filename);
+
+ else if (defname == "muzzle") {
+ GetDefVec(muzzle, pdef, filename);
+ muzzle *= (float) scale;
+ }
+ else if (defname == "loc") {
+ GetDefVec(loc, pdef, filename);
+ loc *= (float) scale;
+ }
+ else if (defname == "size") {
+ GetDefNumber(size, pdef, filename);
+ size *= (float) scale;
+ }
+ else if (defname == "hull_factor") {
+ GetDefNumber(hull, pdef, filename);
+ }
+ else if (defname == "azimuth") {
+ GetDefNumber(az, pdef, filename);
+ if (degrees) az *= (float) DEGREES;
+ }
+ else if (defname == "elevation") {
+ GetDefNumber(el, pdef, filename);
+ if (degrees) el *= (float) DEGREES;
+ }
+
+ else if (defname == "emcon_1") {
+ GetDefNumber(emcon_1, pdef, filename);
+ }
+
+ else if (defname == "emcon_2") {
+ GetDefNumber(emcon_2, pdef, filename);
+ }
+
+ else if (defname == "emcon_3") {
+ GetDefNumber(emcon_3, pdef, filename);
+ }
+ else {
+ Print("WARNING: unknown weapon parameter '%s' in '%s'\n",
+ defname.data(), filename);
+ }
+ }
+ }
+
+ HardPoint* hp = new(__FILE__,__LINE__) HardPoint(muzzle, az, el);
+ if (hp) {
+ for (int i = 0; i < ntypes; i++) {
+ WeaponDesign* meta = WeaponDesign::Find(wtypes[i]);
+ if (!meta) {
+ Print("WARNING: unusual weapon name '%s' in '%s'\n", (const char*) wtypes[i], filename);
+ }
+ else {
+ hp->AddDesign(meta);
+ }
+ }
+
+ hp->Mount(loc, size, hull);
+ if (wname.length()) hp->SetName(wname);
+ if (wabrv.length()) hp->SetAbbreviation(wabrv);
+ if (design.length()) hp->SetDesign(design);
+
+ hard_points.append(hp);
+ }
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath(path_name);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseLoadout(TermStruct* val)
+{
+ ShipLoad* load = new(__FILE__,__LINE__) ShipLoad;
+
+ if (!load) return;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "name")
+ GetDefText(load->name, pdef, filename);
+
+ else if (defname == "stations")
+ GetDefArray(load->load, 16, pdef, filename);
+
+ else
+ Print("WARNING: unknown loadout parameter '%s' in '%s'\n",
+ defname.data(), filename);
+ }
+ }
+
+ loadouts.append(load);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseSensor(TermStruct* val)
+{
+ Text design_name;
+ Vec3 loc(0.0f, 0.0f, 0.0f);
+ float size = 0.0f;
+ float hull = 0.5f;
+ int nranges = 0;
+ float ranges[8];
+ int emcon_1 = -1;
+ int emcon_2 = -1;
+ int emcon_3 = -1;
+
+ ZeroMemory(ranges, sizeof(ranges));
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "range") {
+ GetDefNumber(ranges[nranges++], pdef, filename);
+ }
+ else if (defname == "loc") {
+ GetDefVec(loc, pdef, filename);
+ loc *= (float) scale;
+ }
+ else if (defname == "size") {
+ size *= (float) scale;
+ GetDefNumber(size, pdef, filename);
+ }
+ else if (defname == "hull_factor") {
+ GetDefNumber(hull, pdef, filename);
+ }
+ else if (defname == "design") {
+ GetDefText(design_name, pdef, filename);
+ }
+ else if (defname == "emcon_1") {
+ GetDefNumber(emcon_1, pdef, filename);
+ }
+
+ else if (defname == "emcon_2") {
+ GetDefNumber(emcon_2, pdef, filename);
+ }
+
+ else if (defname == "emcon_3") {
+ GetDefNumber(emcon_3, pdef, filename);
+ }
+ }
+ }
+
+ if (!sensor) {
+ sensor = new(__FILE__,__LINE__) Sensor();
+
+ if (design_name.length()) {
+ SystemDesign* sd = SystemDesign::Find(design_name);
+ if (sd)
+ sensor->SetDesign(sd);
+ }
+
+ for (int i = 0; i < nranges; i++)
+ sensor->AddRange(ranges[i]);
+
+ if (emcon_1 >= 0 && emcon_1 <= 100)
+ sensor->SetEMCONPower(1, emcon_1);
+
+ if (emcon_2 >= 0 && emcon_2 <= 100)
+ sensor->SetEMCONPower(1, emcon_2);
+
+ if (emcon_3 >= 0 && emcon_3 <= 100)
+ sensor->SetEMCONPower(1, emcon_3);
+
+ sensor->Mount(loc, size, hull);
+ sensor->SetSourceIndex(reactors.size()-1);
+ }
+ else {
+ Print("WARNING: additional sensor ignored in '%s'\n", filename);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseNavsys(TermStruct* val)
+{
+ Text design_name;
+ Vec3 loc(0.0f, 0.0f, 0.0f);
+ float size = 0.0f;
+ float hull = 0.5f;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "loc") {
+ GetDefVec(loc, pdef, filename);
+ loc *= (float) scale;
+ }
+ else if (defname == "size") {
+ size *= (float) scale;
+ GetDefNumber(size, pdef, filename);
+ }
+ else if (defname == "hull_factor") {
+ GetDefNumber(hull, pdef, filename);
+ }
+ else if (defname == "design")
+ GetDefText(design_name, pdef, filename);
+ }
+ }
+
+ if (!navsys) {
+ navsys = new(__FILE__,__LINE__) NavSystem;
+
+ if (design_name.length()) {
+ SystemDesign* sd = SystemDesign::Find(design_name);
+ if (sd)
+ navsys->SetDesign(sd);
+ }
+
+ navsys->Mount(loc, size, hull);
+ navsys->SetSourceIndex(reactors.size()-1);
+ }
+ else {
+ Print("WARNING: additional nav system ignored in '%s'\n", filename);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseComputer(TermStruct* val)
+{
+ Text comp_name("Computer");
+ Text comp_abrv("Comp");
+ Text design_name;
+ int comp_type = 1;
+ Vec3 loc(0.0f, 0.0f, 0.0f);
+ float size = 0.0f;
+ float hull = 0.5f;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "name") {
+ GetDefText(comp_name, pdef, filename);
+ }
+ else if (defname == "abrv") {
+ GetDefText(comp_abrv, pdef, filename);
+ }
+ else if (defname == "design") {
+ GetDefText(design_name, pdef, filename);
+ }
+ else if (defname == "type") {
+ GetDefNumber(comp_type, pdef, filename);
+ }
+ else if (defname == "loc") {
+ GetDefVec(loc, pdef, filename);
+ loc *= (float) scale;
+ }
+ else if (defname == "size") {
+ size *= (float) scale;
+ GetDefNumber(size, pdef, filename);
+ }
+ else if (defname == "hull_factor") {
+ GetDefNumber(hull, pdef, filename);
+ }
+ }
+ }
+
+ Computer* comp = new(__FILE__,__LINE__) Computer(comp_type, comp_name);
+ comp->Mount(loc, size, hull);
+ comp->SetAbbreviation(comp_abrv);
+ comp->SetSourceIndex(reactors.size()-1);
+
+ if (design_name.length()) {
+ SystemDesign* sd = SystemDesign::Find(design_name);
+ if (sd)
+ comp->SetDesign(sd);
+ }
+
+ computers.append(comp);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseShield(TermStruct* val)
+{
+ Text dname;
+ Text dabrv;
+ Text design_name;
+ Text model_name;
+ double factor = 0;
+ double capacity = 0;
+ double consumption = 0;
+ double cutoff = 0;
+ double curve = 0;
+ double def_cost = 1;
+ int shield_type = 0;
+ Vec3 loc(0.0f, 0.0f, 0.0f);
+ float size = 0.0f;
+ float hull = 0.5f;
+ int etype = 0;
+ bool shield_capacitor = false;
+ bool shield_bubble = false;
+ int emcon_1 = -1;
+ int emcon_2 = -1;
+ int emcon_3 = -1;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "type") {
+ GetDefNumber(shield_type, pdef, filename);
+ }
+ else if (defname == "name")
+ GetDefText(dname, pdef, filename);
+ else if (defname == "abrv")
+ GetDefText(dabrv, pdef, filename);
+ else if (defname == "design")
+ GetDefText(design_name, pdef, filename);
+ else if (defname == "model")
+ GetDefText(model_name, pdef, filename);
+
+ else if (defname == "loc") {
+ GetDefVec(loc, pdef, filename);
+ loc *= (float) scale;
+ }
+ else if (defname == "size") {
+ GetDefNumber(size, pdef, filename);
+ size *= (float) scale;
+ }
+ else if (defname == "hull_factor")
+ GetDefNumber(hull, pdef, filename);
+
+ else if (defname.contains("factor"))
+ GetDefNumber(factor, pdef, filename);
+ else if (defname.contains("cutoff"))
+ GetDefNumber(cutoff, pdef, filename);
+ else if (defname.contains("curve"))
+ GetDefNumber(curve, pdef, filename);
+ else if (defname.contains("capacitor"))
+ GetDefBool(shield_capacitor, pdef, filename);
+ else if (defname.contains("bubble"))
+ GetDefBool(shield_bubble, pdef, filename);
+ else if (defname == "capacity")
+ GetDefNumber(capacity, pdef, filename);
+ else if (defname == "consumption")
+ GetDefNumber(consumption, pdef, filename);
+ else if (defname == "deflection_cost")
+ GetDefNumber(def_cost, pdef, filename);
+ else if (defname == "explosion")
+ GetDefNumber(etype, pdef, filename);
+
+ else if (defname == "emcon_1") {
+ GetDefNumber(emcon_1, pdef, filename);
+ }
+
+ else if (defname == "emcon_2") {
+ GetDefNumber(emcon_2, pdef, filename);
+ }
+
+ else if (defname == "emcon_3") {
+ GetDefNumber(emcon_3, pdef, filename);
+ }
+
+ else if (defname == "bolt_hit_sound") {
+ GetDefText(bolt_hit_sound, pdef, filename);
+ }
+
+ else if (defname == "beam_hit_sound") {
+ GetDefText(beam_hit_sound, pdef, filename);
+ }
+ }
+ }
+
+ if (!shield) {
+ if (shield_type) {
+ shield = new(__FILE__,__LINE__) Shield((Shield::SUBTYPE) shield_type);
+ shield->SetSourceIndex(reactors.size()-1);
+ shield->Mount(loc, size, hull);
+ if (dname.length()) shield->SetName(dname);
+ if (dabrv.length()) shield->SetAbbreviation(dabrv);
+
+ if (design_name.length()) {
+ SystemDesign* sd = SystemDesign::Find(design_name);
+ if (sd)
+ shield->SetDesign(sd);
+ }
+
+ shield->SetExplosionType(etype);
+ shield->SetShieldCapacitor(shield_capacitor);
+ shield->SetShieldBubble(shield_bubble);
+
+ if (factor > 0) shield->SetShieldFactor(factor);
+ if (capacity > 0) shield->SetCapacity(capacity);
+ if (cutoff > 0) shield->SetShieldCutoff(cutoff);
+ if (consumption > 0) shield->SetConsumption(consumption);
+ if (def_cost > 0) shield->SetDeflectionCost(def_cost);
+ if (curve > 0) shield->SetShieldCurve(curve);
+
+ if (emcon_1 >= 0 && emcon_1 <= 100)
+ shield->SetEMCONPower(1, emcon_1);
+
+ if (emcon_2 >= 0 && emcon_2 <= 100)
+ shield->SetEMCONPower(1, emcon_2);
+
+ if (emcon_3 >= 0 && emcon_3 <= 100)
+ shield->SetEMCONPower(1, emcon_3);
+
+ if (model_name.length()) {
+ shield_model = new(__FILE__,__LINE__) Model;
+ if (!shield_model->Load(model_name, scale)) {
+ Print("ERROR: Could not load shield model '%s'\n", model_name);
+ delete shield_model;
+ shield_model = 0;
+ valid = false;
+ }
+ else {
+ shield_model->SetDynamic(true);
+ shield_model->SetLuminous(true);
+ }
+ }
+
+ DataLoader* loader = DataLoader::GetLoader();
+ DWORD SOUND_FLAGS = Sound::LOCALIZED | Sound::LOC_3D;
+
+ if (bolt_hit_sound.length()) {
+ if (!loader->LoadSound(bolt_hit_sound, bolt_hit_sound_resource, SOUND_FLAGS, true)) {
+ loader->SetDataPath("Sounds/");
+ loader->LoadSound(bolt_hit_sound, bolt_hit_sound_resource, SOUND_FLAGS);
+ loader->SetDataPath(path_name);
+ }
+ }
+
+ if (beam_hit_sound.length()) {
+ if (!loader->LoadSound(beam_hit_sound, beam_hit_sound_resource, SOUND_FLAGS, true)) {
+ loader->SetDataPath("Sounds/");
+ loader->LoadSound(beam_hit_sound, beam_hit_sound_resource, SOUND_FLAGS);
+ loader->SetDataPath(path_name);
+ }
+ }
+ }
+ else {
+ Print("WARNING: invalid shield type in '%s'\n", filename);
+ }
+ }
+ else {
+ Print("WARNING: additional shield ignored in '%s'\n", filename);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseDeathSpiral(TermStruct* val)
+{
+ int exp_index = -1;
+ int debris_index = -1;
+ int fire_index = -1;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* def = val->elements()->at(i)->isDef();
+ if (def) {
+ Text defname = def->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "time") {
+ GetDefNumber(death_spiral_time, def, filename);
+ }
+
+ else if (defname == "explosion") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: explosion struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseExplosion(val, ++exp_index);
+ }
+ }
+
+ // BACKWARD COMPATIBILITY:
+ else if (defname == "explosion_type") {
+ GetDefNumber(explosion[++exp_index].type, def, filename);
+ }
+
+ else if (defname == "explosion_time") {
+ GetDefNumber(explosion[exp_index].time, def, filename);
+ }
+
+ else if (defname == "explosion_loc") {
+ GetDefVec(explosion[exp_index].loc, def, filename);
+ explosion[exp_index].loc *= (float) scale;
+ }
+
+ else if (defname == "final_type") {
+ GetDefNumber(explosion[++exp_index].type, def, filename);
+ explosion[exp_index].final = true;
+ }
+
+ else if (defname == "final_loc") {
+ GetDefVec(explosion[exp_index].loc, def, filename);
+ explosion[exp_index].loc *= (float) scale;
+ }
+
+
+ else if (defname == "debris") {
+ if (def->term() && def->term()->isText()) {
+ Text model_name;
+ GetDefText(model_name, def, filename);
+ Model* model = new(__FILE__,__LINE__) Model;
+ if (!model->Load(model_name, scale)) {
+ Print("Could not load debris model '%s'\n", model_name);
+ delete model;
+ return;
+ }
+
+ PrepareModel(*model);
+ debris[++debris_index].model = model;
+ fire_index = -1;
+ }
+ else if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: debris struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseDebris(val, ++debris_index);
+ }
+ }
+
+ else if (defname == "debris_mass") {
+ GetDefNumber(debris[debris_index].mass, def, filename);
+ }
+
+ else if (defname == "debris_speed") {
+ GetDefNumber(debris[debris_index].speed, def, filename);
+ }
+
+ else if (defname == "debris_drag") {
+ GetDefNumber(debris[debris_index].drag, def, filename);
+ }
+
+ else if (defname == "debris_loc") {
+ GetDefVec(debris[debris_index].loc, def, filename);
+ debris[debris_index].loc *= (float) scale;
+ }
+
+ else if (defname == "debris_count") {
+ GetDefNumber(debris[debris_index].count, def, filename);
+ }
+
+ else if (defname == "debris_life") {
+ GetDefNumber(debris[debris_index].life, def, filename);
+ }
+
+ else if (defname == "debris_fire") {
+ if (++fire_index < 5) {
+ GetDefVec(debris[debris_index].fire_loc[fire_index], def, filename);
+ debris[debris_index].fire_loc[fire_index] *= (float) scale;
+ }
+ }
+
+ else if (defname == "debris_fire_type") {
+ GetDefNumber(debris[debris_index].fire_type, def, filename);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseExplosion(TermStruct* val, int index)
+{
+ ShipExplosion* exp = &explosion[index];
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* def = val->elements()->at(i)->isDef();
+ if (def) {
+ Text defname = def->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "time") {
+ GetDefNumber(exp->time, def, filename);
+ }
+
+ else if (defname == "type") {
+ GetDefNumber(exp->type, def, filename);
+ }
+
+ else if (defname == "loc") {
+ GetDefVec(exp->loc, def, filename);
+ exp->loc *= (float) scale;
+ }
+
+ else if (defname == "final") {
+ GetDefBool(exp->final, def, filename);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseDebris(TermStruct* val, int index)
+{
+ char model_name[NAMELEN];
+ int fire_index = 0;
+ ShipDebris* deb = &debris[index];
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* def = val->elements()->at(i)->isDef();
+ if (def) {
+ Text defname = def->name()->value();
+
+ if (defname == "model") {
+ GetDefText(model_name, def, filename);
+ Model* model = new(__FILE__,__LINE__) Model;
+ if (!model->Load(model_name, scale)) {
+ Print("Could not load debris model '%s'\n", model_name);
+ delete model;
+ return;
+ }
+
+ PrepareModel(*model);
+ deb->model = model;
+ }
+
+ else if (defname == "mass") {
+ GetDefNumber(deb->mass, def, filename);
+ }
+
+ else if (defname == "speed") {
+ GetDefNumber(deb->speed, def, filename);
+ }
+
+ else if (defname == "drag") {
+ GetDefNumber(deb->drag, def, filename);
+ }
+
+ else if (defname == "loc") {
+ GetDefVec(deb->loc, def, filename);
+ deb->loc *= (float) scale;
+ }
+
+ else if (defname == "count") {
+ GetDefNumber(deb->count, def, filename);
+ }
+
+ else if (defname == "life") {
+ GetDefNumber(deb->life, def, filename);
+ }
+
+ else if (defname == "fire") {
+ if (fire_index < 5) {
+ GetDefVec(deb->fire_loc[fire_index], def, filename);
+ deb->fire_loc[fire_index] *= (float) scale;
+ fire_index++;
+ }
+ }
+
+ else if (defname == "fire_type") {
+ GetDefNumber(deb->fire_type, def, filename);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseMap(TermStruct* val)
+{
+ char sprite_name[NAMELEN];
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "sprite") {
+ GetDefText(sprite_name, pdef, filename);
+
+ Bitmap* sprite = new(__FILE__,__LINE__) Bitmap();
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->LoadBitmap(sprite_name, *sprite, Bitmap::BMP_TRANSLUCENT);
+
+ map_sprites.append(sprite);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipDesign::ParseSquadron(TermStruct* val)
+{
+ char name[NAMELEN];
+ char design[NAMELEN];
+ int count = 4;
+ int avail = 4;
+
+ name[0] = 0;
+ design[0] = 0;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ Text defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "name") {
+ GetDefText(name, pdef, filename);
+ }
+ else if (defname == "design") {
+ GetDefText(design, pdef, filename);
+ }
+ else if (defname == "count") {
+ GetDefNumber(count, pdef, filename);
+ }
+ else if (defname == "avail") {
+ GetDefNumber(avail, pdef, filename);
+ }
+ }
+ }
+
+ ShipSquadron* s = new(__FILE__,__LINE__) ShipSquadron;
+ strcpy(s->name, name);
+
+ s->design = Get(design);
+ s->count = count;
+ s->avail = avail;
+
+ squadrons.append(s);
+}
+
+// +--------------------------------------------------------------------+
+
+Skin*
+ShipDesign::ParseSkin(TermStruct* val)
+{
+ Skin* skin = 0;
+ char name[NAMELEN];
+
+ name[0] = 0;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* def = val->elements()->at(i)->isDef();
+ if (def) {
+ Text defname = def->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "name") {
+ GetDefText(name, def, filename);
+
+ skin = new(__FILE__,__LINE__) Skin(name);
+ }
+ else if (defname == "material" || defname == "mtl") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: skin struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseSkinMtl(val, skin);
+ }
+ }
+ }
+ }
+
+ if (skin && skin->NumCells()) {
+ skins.append(skin);
+ }
+
+ else if (skin) {
+ delete skin;
+ skin = 0;
+ }
+
+ return skin;
+}
+
+void
+ShipDesign::ParseSkinMtl(TermStruct* val, Skin* skin)
+{
+ Material* mtl = new(__FILE__,__LINE__) Material;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* def = val->elements()->at(i)->isDef();
+ if (def) {
+ Text defname = def->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "name") {
+ GetDefText(mtl->name, def, filename);
+ }
+ else if (defname == "Ka") {
+ GetDefColor(mtl->Ka, def, filename);
+ }
+ else if (defname == "Kd") {
+ GetDefColor(mtl->Kd, def, filename);
+ }
+ else if (defname == "Ks") {
+ GetDefColor(mtl->Ks, def, filename);
+ }
+ else if (defname == "Ke") {
+ GetDefColor(mtl->Ke, def, filename);
+ }
+ else if (defname == "Ns" || defname == "power") {
+ GetDefNumber(mtl->power, def, filename);
+ }
+ else if (defname == "bump") {
+ GetDefNumber(mtl->bump, def, filename);
+ }
+ else if (defname == "luminous") {
+ GetDefBool(mtl->luminous, def, filename);
+ }
+
+ else if (defname == "blend") {
+ if (def->term() && def->term()->isNumber())
+ GetDefNumber(mtl->blend, def, filename);
+
+ else if (def->term() && def->term()->isText()) {
+ Text val;
+ GetDefText(val, def, filename);
+ val.setSensitive(false);
+
+ if (val == "alpha" || val == "translucent")
+ mtl->blend = Material::MTL_TRANSLUCENT;
+
+ else if (val == "additive")
+ mtl->blend = Material::MTL_ADDITIVE;
+
+ else
+ mtl->blend = Material::MTL_SOLID;
+ }
+ }
+
+ else if (defname.indexOf("tex_d") == 0) {
+ char tex_name[64];
+ if (!GetDefText(tex_name, def, filename))
+ Print("WARNING: invalid or missing tex_diffuse in '%s'\n", filename);
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->LoadTexture(tex_name, mtl->tex_diffuse);
+ }
+
+ else if (defname.indexOf("tex_s") == 0) {
+ char tex_name[64];
+ if (!GetDefText(tex_name, def, filename))
+ Print("WARNING: invalid or missing tex_specular in '%s'\n", filename);
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->LoadTexture(tex_name, mtl->tex_specular);
+ }
+
+ else if (defname.indexOf("tex_b") == 0) {
+ char tex_name[64];
+ if (!GetDefText(tex_name, def, filename))
+ Print("WARNING: invalid or missing tex_bumpmap in '%s'\n", filename);
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->LoadTexture(tex_name, mtl->tex_bumpmap);
+ }
+
+ else if (defname.indexOf("tex_e") == 0) {
+ char tex_name[64];
+ if (!GetDefText(tex_name, def, filename))
+ Print("WARNING: invalid or missing tex_emissive in '%s'\n", filename);
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->LoadTexture(tex_name, mtl->tex_emissive);
+ }
+ }
+ }
+
+ if (skin && mtl)
+ skin->AddMaterial(mtl);
+}
+
+const Skin*
+ShipDesign::FindSkin(const char* skin_name) const
+{
+ int n = skins.size();
+
+ for (int i = 0; i < n; i++) {
+ Skin* s = skins[n-1-i];
+
+ if (!strcmp(s->Name(), skin_name))
+ return s;
+ }
+
+ return 0;
+}
diff --git a/Stars45/ShipDesign.h b/Stars45/ShipDesign.h
new file mode 100644
index 0000000..6ae85dc
--- /dev/null
+++ b/Stars45/ShipDesign.h
@@ -0,0 +1,292 @@
+/* Project Starshatter 4.6
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ShipDesign.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Starship Design parameters class
+*/
+
+#ifndef ShipDesign_h
+#define ShipDesign_h
+
+#include "Types.h"
+#include "Bitmap.h"
+#include "Geometry.h"
+#include "term.h"
+#include "List.h"
+
+// +----------------------------------------------------------------------+
+
+class ShipDesign;
+class Model;
+class Skin;
+class PowerSource;
+class Weapon;
+class HardPoint;
+class Computer;
+class Drive;
+class QuantumDrive;
+class Farcaster;
+class Thruster;
+class Sensor;
+class NavLight;
+class NavSystem;
+class Shield;
+class FlightDeck;
+class LandingGear;
+class System;
+class Sound;
+
+// +====================================================================+
+
+class ShipLoad
+{
+public:
+ static const char* TYPENAME() { return "ShipLoad"; }
+
+ ShipLoad();
+
+ char name[64];
+ int load[16];
+ double mass;
+};
+
+class ShipSquadron
+{
+public:
+ static const char* TYPENAME() { return "ShipSquadron"; }
+
+ ShipSquadron();
+
+ char name[64];
+ ShipDesign* design;
+ int count;
+ int avail;
+};
+
+class ShipExplosion
+{
+public:
+ static const char* TYPENAME() { return "ShipExplosion"; }
+
+ ShipExplosion() { ZeroMemory(this, sizeof(ShipExplosion)); }
+
+ int type;
+ float time;
+ Vec3 loc;
+ bool final;
+};
+
+class ShipDebris
+{
+public:
+ static const char* TYPENAME() { return "ShipDebris"; }
+
+ ShipDebris() { ZeroMemory(this, sizeof(ShipDebris)); }
+
+ Model* model;
+ int count;
+ int life;
+ Vec3 loc;
+ float mass;
+ float speed;
+ float drag;
+ int fire_type;
+ Vec3 fire_loc[5];
+};
+
+// +====================================================================+
+// Used to share common information about ships of a single type.
+// ShipDesign objects are loaded from a text file and stored in a
+// static list (catalog) member for use by the Ship.
+
+class ShipDesign
+{
+public:
+ static const char* TYPENAME() { return "ShipDesign"; }
+
+ enum CONSTANTS {
+ MAX_DEBRIS = 10,
+ MAX_EXPLOSIONS = 10
+ };
+
+ ShipDesign();
+ ShipDesign(const char* name, const char* path, const char* filename, bool secret=false);
+ ~ShipDesign();
+
+ // public interface:
+ static void Initialize();
+ static void Close();
+ static bool CheckName(const char* name);
+ static ShipDesign* Get(const char* design_name, const char* design_path=0);
+ static ShipDesign* FindModDesign(const char* design_name, const char* design_path=0);
+ static void ClearModCatalog();
+ static int GetDesignList(int type, List<Text>& designs); // never destroy the design list!
+
+ static int ClassForName(const char* name);
+ static const char* ClassName(int type);
+
+ static int LoadCatalog(const char* path, const char* file, bool mod=false);
+ static void LoadSkins(const char* path, const char* archive=0);
+ static void PreloadCatalog(int index=-1);
+ static int StandardCatalogSize();
+
+ int operator == (const ShipDesign& s) const { return !strncmp(name, s.name, 31); }
+
+ // Parser:
+ void ParseShip(TermDef* def);
+
+ void ParsePower(TermStruct* val);
+ void ParseDrive(TermStruct* val);
+ void ParseQuantumDrive(TermStruct* val);
+ void ParseFarcaster(TermStruct* val);
+ void ParseThruster(TermStruct* val);
+ void ParseNavlight(TermStruct* val);
+ void ParseFlightDeck(TermStruct* val);
+ void ParseLandingGear(TermStruct* val);
+ void ParseWeapon(TermStruct* val);
+ void ParseHardPoint(TermStruct* val);
+ void ParseSensor(TermStruct* val);
+ void ParseNavsys(TermStruct* val);
+ void ParseComputer(TermStruct* val);
+ void ParseShield(TermStruct* val);
+ void ParseDeathSpiral(TermStruct* val);
+ void ParseExplosion(TermStruct* val, int index);
+ void ParseDebris(TermStruct* val, int index);
+ void ParseLoadout(TermStruct* val);
+ void ParseMap(TermStruct* val);
+ void ParseSquadron(TermStruct* val);
+ Skin* ParseSkin(TermStruct* val);
+ void ParseSkinMtl(TermStruct* val, Skin* skin);
+
+ // general information:
+ const char* DisplayName() const;
+
+ char filename[64];
+ char path_name[64];
+ char name[64];
+ char display_name[64];
+ char abrv[16];
+ int type;
+ float scale;
+ int auto_roll;
+ bool valid;
+ bool secret; // don't display in editor
+ Text description; // background info for tactical reference
+
+ // LOD representation:
+ int lod_levels;
+ List<Model> models[4];
+ List<Point> offsets[4];
+ float feature_size[4];
+ List<Point> spin_rates;
+
+ // player selectable skins:
+ List<Skin> skins;
+ const Skin* FindSkin(const char* skin_name) const;
+
+ // virtual cockpit:
+ Model* cockpit_model;
+ float cockpit_scale;
+
+ // performance:
+ float vlimit;
+ float agility;
+ float air_factor;
+ float roll_rate;
+ float pitch_rate;
+ float yaw_rate;
+ float trans_x;
+ float trans_y;
+ float trans_z;
+ float turn_bank;
+ Vec3 chase_vec;
+ Vec3 bridge_vec;
+ Vec3 beauty_cam;
+
+ float prep_time;
+
+ // physical data:
+ float drag, roll_drag, pitch_drag, yaw_drag;
+ float arcade_drag;
+ float mass, integrity, radius;
+
+ // aero data:
+ float CL, CD, stall;
+
+ // weapons:
+ int primary;
+ int secondary;
+
+ // drives:
+ int main_drive;
+
+ // visibility:
+ float pcs; // passive sensor cross section
+ float acs; // active sensor cross section
+ float detet; // maximum detection range
+ float e_factor[3]; // pcs scaling by emcon setting
+
+ // ai settings:
+ float avoid_time;
+ float avoid_fighter;
+ float avoid_strike;
+ float avoid_target;
+ float commit_range;
+
+ // death spriral sequence:
+ float death_spiral_time;
+ float explosion_scale;
+ ShipExplosion explosion[MAX_EXPLOSIONS];
+ ShipDebris debris[MAX_DEBRIS];
+
+ List<PowerSource> reactors;
+ List<Weapon> weapons;
+ List<HardPoint> hard_points;
+ List<Drive> drives;
+ List<Computer> computers;
+ List<FlightDeck> flight_decks;
+ List<NavLight> navlights;
+ QuantumDrive* quantum_drive;
+ Farcaster* farcaster;
+ Thruster* thruster;
+ Sensor* sensor;
+ NavSystem* navsys;
+ Shield* shield;
+ Model* shield_model;
+ Weapon* decoy;
+ Weapon* probe;
+ LandingGear* gear;
+
+ float splash_radius;
+ float scuttle;
+ float repair_speed;
+ int repair_teams;
+ bool repair_auto;
+ bool repair_screen;
+ bool wep_screen;
+
+ Text bolt_hit_sound;
+ Text beam_hit_sound;
+
+ Sound* bolt_hit_sound_resource;
+ Sound* beam_hit_sound_resource;
+
+ List<ShipLoad> loadouts;
+ List<Bitmap> map_sprites;
+ List<ShipSquadron> squadrons;
+
+ Bitmap beauty;
+ Bitmap hud_icon;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif ShipDesign_h
+
diff --git a/Stars45/ShipKiller.cpp b/Stars45/ShipKiller.cpp
new file mode 100644
index 0000000..47f3ad8
--- /dev/null
+++ b/Stars45/ShipKiller.cpp
@@ -0,0 +1,263 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars
+ FILE: ShipKiller.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ShipKiller class implementation
+*/
+
+#include "MemDebug.h"
+#include "ShipKiller.h"
+#include "Sim.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "System.h"
+#include "Weapon.h"
+#include "Shot.h"
+#include "Explosion.h"
+#include "Debris.h"
+#include "HUDSounds.h"
+
+#include "Solid.h"
+#include "Random.h"
+
+// +----------------------------------------------------------------------+
+
+ShipKiller::ShipKiller(Ship* s)
+ : ship(s), DEATH_CAM_LINGER(5.0f), time(0.0f), exp_time(0.0f), exp_index(0)
+{
+}
+
+ShipKiller::~ShipKiller()
+{
+}
+
+// +----------------------------------------------------------------------+
+
+inline int random_index()
+{
+ return (int) (rand()/3277);
+}
+
+// +----------------------------------------------------------------------+
+
+void
+ShipKiller::BeginDeathSpiral()
+{
+ if (!ship) return;
+
+ // shut down all ship systems:
+ ListIter<System> iter = ship->Systems();
+ while (++iter) {
+ iter->PowerOff();
+ iter->SetPowerLevel(0);
+
+ if (iter->Type() == System::WEAPON) {
+ Weapon* gun = (Weapon*) iter.value();
+
+ for (int i = 0; i < Weapon::MAX_BARRELS; i++) {
+ Shot* beam = gun->GetBeam(i);
+ if (beam)
+ beam->Destroy();
+ }
+ }
+ }
+
+ if (ship->GetShieldRep())
+ ship->GetShieldRep()->Hide();
+
+ Sim* sim = Sim::GetSim();
+ const ShipDesign* design = ship->Design();
+
+ float time_to_go = design->death_spiral_time;
+ time = DEATH_CAM_LINGER + time_to_go;
+ loc = ship->Location() + ship->Velocity() * (time_to_go-1.0f);
+
+ if (rand() < 16000)
+ loc += ship->BeamLine() * -3 * ship->Radius();
+ else
+ loc += ship->BeamLine() * 3 * ship->Radius();
+
+ if (rand() < 8000)
+ loc += ship->LiftLine() * -1 * ship->Radius();
+ else
+ loc += ship->LiftLine() * 2 * ship->Radius();
+
+ // stop on crash:
+ if (ship->IsGroundUnit() || (ship->IsAirborne() && ship->AltitudeAGL() < ship->Radius()*2)) {
+ time = DEATH_CAM_LINGER;
+ loc = ship->Location() + Point(6*ship->Radius(), 7*ship->Radius(), 8*ship->Radius());
+ ship->SetVelocity(Point(0,0,0));
+ }
+ // else, slow tumble:
+ else {
+ Point torque = RandomVector(ship->Mass()/7);
+ ship->ApplyTorque(torque);
+
+ for (int i = 0; i < 5; i++) {
+ exp_index = random_index() % ShipDesign::MAX_EXPLOSIONS;
+ if (design->explosion[exp_index].type > 0 && !design->explosion[exp_index].final)
+ break;
+ }
+
+ float exp_scale = design->explosion_scale;
+ if (exp_scale <= 0)
+ exp_scale = design->scale;
+
+ exp_time = design->explosion[exp_index].time;
+
+ if (design->explosion[exp_index].type > 0) {
+ Point exp_loc = ship->Location() + (design->explosion[exp_index].loc * ship->Cam().Orientation());
+ sim->CreateExplosion(exp_loc,
+ ship->Velocity(),
+ design->explosion[exp_index].type,
+ (float) ship->Radius(),
+ exp_scale,
+ ship->GetRegion(),
+ ship);
+ }
+ }
+
+ ship->SetControls(0);
+ ship->SetupAgility();
+}
+
+// +----------------------------------------------------------------------+
+
+void
+ShipKiller::ExecFrame(double seconds)
+{
+ Sim* sim = Sim::GetSim();
+ const ShipDesign* design = ship->Design();
+
+ time -= (float) seconds;
+ exp_time -= (float) seconds;
+
+ float exp_scale = design->explosion_scale;
+ if (exp_scale <= 0)
+ exp_scale = design->scale;
+
+ if (exp_time < 0) {
+ exp_index++;
+ if (exp_index >= ShipDesign::MAX_EXPLOSIONS || design->explosion[exp_index].final)
+ exp_index = 0;
+
+ exp_time = design->explosion[exp_index].time;
+
+ if (design->explosion[exp_index].type > 0) {
+ Point exp_loc = ship->Location() + (design->explosion[exp_index].loc * ship->Cam().Orientation());
+ sim->CreateExplosion(exp_loc,
+ ship->Velocity(),
+ design->explosion[exp_index].type,
+ (float) ship->Radius(),
+ exp_scale,
+ ship->GetRegion(),
+ ship);
+ }
+ }
+
+ if (time < DEATH_CAM_LINGER) {
+ for (int i = 0; i < ShipDesign::MAX_EXPLOSIONS; i++) {
+ if (design->explosion[i].final) {
+ Point exp_loc = ship->Location() + (design->explosion[i].loc * ship->Cam().Orientation());
+ sim->CreateExplosion(exp_loc,
+ ship->Velocity(),
+ design->explosion[i].type,
+ (float) ship->Radius(),
+ exp_scale,
+ ship->GetRegion());
+ }
+ }
+
+ for (i = 0; i < ShipDesign::MAX_DEBRIS; i++) {
+ if (design->debris[i].model) {
+ Point debris_loc = ship->Location() + (design->debris[i].loc * ship->Cam().Orientation());
+ Point debris_vel = debris_loc - ship->Location();
+ debris_vel.Normalize();
+
+ if (design->debris[i].speed > 0)
+ debris_vel *= design->debris[i].speed;
+ else
+ debris_vel *= 200;
+
+ if (ship->IsGroundUnit()) {
+ debris_vel *= 2;
+
+ if (debris_vel.y < 0)
+ debris_vel.y *= -1;
+ }
+
+ for (int n = 0; n < design->debris[i].count; n++) {
+ Debris* debris = sim->CreateDebris(debris_loc,
+ debris_vel + ship->Velocity(),
+ design->debris[i].model,
+ design->debris[i].mass,
+ ship->GetRegion());
+
+ debris->SetLife(design->debris[i].life);
+ debris->SetDrag(design->debris[i].drag);
+
+ if (n == 0) {
+ debris->CloneCam(ship->Cam());
+ debris->MoveTo(debris_loc);
+ }
+
+ for (int fire = 0; fire < 5; fire++) {
+ if (design->debris[i].fire_loc[fire] == Vec3(0,0,0))
+ continue;
+
+ Point fire_loc = debris->Location() + (design->debris[i].fire_loc[fire] * debris->Cam().Orientation());
+
+ if (design->debris[i].fire_type > 0) {
+ sim->CreateExplosion(fire_loc,
+ ship->Velocity(),
+ design->debris[i].fire_type,
+ exp_scale,
+ exp_scale,
+ ship->GetRegion(),
+ debris);
+ }
+ else {
+ sim->CreateExplosion(fire_loc,
+ ship->Velocity(),
+ Explosion::SMALL_FIRE,
+ exp_scale,
+ exp_scale,
+ ship->GetRegion(),
+ debris);
+
+ sim->CreateExplosion(fire_loc,
+ ship->Velocity(),
+ Explosion::SMOKE_TRAIL,
+ exp_scale * 0.25f,
+ exp_scale * 0.25f,
+ ship->GetRegion(),
+ debris);
+ }
+ }
+
+ if (n+1 < design->debris[i].count) {
+ debris_vel = RandomVector(1);
+
+ if (design->debris[i].speed > 0)
+ debris_vel *= design->debris[i].speed * Random(0.8, 1.2);
+ else
+ debris_vel *= 300 + rand()/50;
+ }
+ }
+ }
+ }
+
+ if (ship == sim->GetPlayerShip())
+ HUDSounds::StopSound(HUDSounds::SND_RED_ALERT);
+
+ sim->CreateSplashDamage(ship);
+ ship->Destroy(); // CAREFUL!!! This will also delete this object!
+ }
+}
diff --git a/Stars45/ShipKiller.h b/Stars45/ShipKiller.h
new file mode 100644
index 0000000..3cb9b35
--- /dev/null
+++ b/Stars45/ShipKiller.h
@@ -0,0 +1,55 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ShipKiller.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ShipKiller (i.e. death spiral) class
+*/
+
+#ifndef ShipKiller_h
+#define ShipKiller_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+
+// +--------------------------------------------------------------------+
+
+class ShipKiller
+{
+public:
+ const float DEATH_CAM_LINGER;
+
+ // CONSTRUCTORS:
+ ShipKiller(Ship* ship);
+ virtual ~ShipKiller();
+
+ virtual void BeginDeathSpiral();
+ virtual void ExecFrame(double seconds);
+
+ // GENERAL ACCESSORS:
+ virtual float TransitionTime() const { return time; }
+ virtual Point TransitionLoc() const { return loc; }
+
+protected:
+ Ship* ship;
+
+ float time;
+ Point loc;
+
+ float exp_time;
+ int exp_index;
+};
+
+#endif ShipKiller_h
+
diff --git a/Stars45/ShipSolid.cpp b/Stars45/ShipSolid.cpp
new file mode 100644
index 0000000..8691d21
--- /dev/null
+++ b/Stars45/ShipSolid.cpp
@@ -0,0 +1,96 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ShipSolid.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "ShipSolid.h"
+#include "Ship.h"
+#include "Sim.h"
+#include "StarSystem.h"
+#include "TerrainRegion.h"
+
+#include "Game.h"
+#include "Skin.h"
+
+// +--------------------------------------------------------------------+
+
+ShipSolid::ShipSolid(Ship* s)
+ : ship(s), skin(0), in_soup(false)
+{
+}
+
+// +--------------------------------------------------------------------+
+
+ShipSolid::~ShipSolid()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipSolid::TranslateBy(const Point& ref)
+{
+ true_eye_point = ref;
+ Solid::TranslateBy(ref);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipSolid::Render(Video* video, DWORD flags)
+{
+ if (hidden || !visible || !video || Depth() > 5e6)
+ return;
+
+ const Skin* s = 0;
+
+ if (ship)
+ s = ship->GetSkin();
+ else
+ s = skin;
+
+ if (s)
+ s->ApplyTo(model);
+
+ bool fog = false;
+
+ if (ship && ship->IsAirborne()) {
+ fog = true;
+
+ TerrainRegion* rgn = (TerrainRegion*) ship->GetRegion()->GetOrbitalRegion();
+ double visibility = rgn->GetWeather().Visibility();
+ FLOAT fog_density = (FLOAT) (rgn->FogDensity() * 2.5e-5 * 1/visibility);
+ Color fog_color = rgn->FogColor();
+
+ // Use BLACK fog on secondary lighting pass
+ // This will effectively "filter out" the highlights
+ // with distance...
+
+ if (flags & Graphic::RENDER_ADD_LIGHT)
+ fog_color = Color::Black;
+
+ video->SetRenderState(Video::FOG_ENABLE, true);
+ video->SetRenderState(Video::FOG_COLOR, fog_color.Value());
+ video->SetRenderState(Video::FOG_DENSITY, *((DWORD*) &fog_density));
+ }
+
+ if (!fog) video->SetRenderState(Video::FOG_ENABLE, false);
+
+ Solid::Render(video, flags);
+
+ if (fog) video->SetRenderState(Video::FOG_ENABLE, false);
+
+ if (s)
+ s->Restore(model);
+}
+
+
diff --git a/Stars45/ShipSolid.h b/Stars45/ShipSolid.h
new file mode 100644
index 0000000..48a1bae
--- /dev/null
+++ b/Stars45/ShipSolid.h
@@ -0,0 +1,52 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: ShipSolid.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ 3D Solid (Polygon) Object
+*/
+
+#ifndef ShipSolid_h
+#define ShipSolid_h
+
+#include "Solid.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+class Skin;
+
+// +--------------------------------------------------------------------+
+
+class ShipSolid : public Solid
+{
+public:
+ static const char* TYPENAME() { return "ShipSolid"; }
+
+ ShipSolid(Ship* s);
+ virtual ~ShipSolid();
+
+ virtual void Render(Video* video, DWORD flags);
+ virtual void TranslateBy(const Point& ref);
+
+ const Skin* GetSkin() const { return skin; }
+ void SetSkin(const Skin* s) { skin = s; }
+
+protected:
+ Ship* ship;
+ const Skin* skin;
+ Point true_eye_point;
+ Point fog_loc;
+ bool in_soup;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif ShipSolid_h
+
diff --git a/Stars45/Shot.cpp b/Stars45/Shot.cpp
new file mode 100644
index 0000000..21980a8
--- /dev/null
+++ b/Stars45/Shot.cpp
@@ -0,0 +1,626 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Shot.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Laser and Missile class
+*/
+
+#include "MemDebug.h"
+#include "Shot.h"
+#include "Weapon.h"
+#include "DriveSprite.h"
+#include "SeekerAI.h"
+#include "Sim.h"
+#include "Ship.h"
+#include "Trail.h"
+#include "Random.h"
+#include "AudioConfig.h"
+#include "TerrainRegion.h"
+#include "Terrain.h"
+
+#include "Game.h"
+#include "Bolt.h"
+#include "Sprite.h"
+#include "Solid.h"
+#include "Light.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Sound.h"
+
+// +--------------------------------------------------------------------+
+
+Shot::Shot(const Point& pos, const Camera& shot_cam, WeaponDesign* dsn, const Ship* ship)
+ : first_frame(true), owner(ship), flash(0), flare(0), trail(0), sound(0), eta(0),
+ charge(1.0f), design(dsn), offset(1.0e5f), altitude_agl(-1.0e6f), hit_target(false)
+{
+ obj_type = SimObject::SIM_SHOT;
+ type = design->type;
+ primary = design->primary;
+ beam = design->beam;
+ base_damage = design->damage;
+ armed = false;
+
+ radius = 10.0f;
+
+ if (primary || design->decoy_type || !design->guided) {
+ straight = true;
+ armed = true;
+ }
+
+ cam.Clone(shot_cam);
+
+ life = design->life;
+ velocity = cam.vpn() * (double) design->speed;
+
+ MoveTo(pos);
+
+ if (beam)
+ origin = pos + (shot_cam.vpn() * -design->length);
+
+ switch (design->graphic_type) {
+ case Graphic::BOLT: {
+ Bolt* s = new(__FILE__,__LINE__) Bolt(design->length, design->width, design->shot_img, 1);
+ s->SetDirection(cam.vpn());
+ rep = s;
+ }
+ break;
+
+ case Graphic::SPRITE: {
+ Sprite* s = 0;
+
+ if (design->animation)
+ s = new(__FILE__,__LINE__) DriveSprite(design->animation, design->anim_length);
+ else
+ s = new(__FILE__,__LINE__) DriveSprite(design->shot_img);
+
+ s->Scale((double) design->scale);
+ rep = s;
+ }
+ break;
+
+ case Graphic::SOLID: {
+ Solid* s = new(__FILE__,__LINE__) Solid;
+ s->UseModel(design->shot_model);
+ rep = s;
+
+ radius = rep->Radius();
+ }
+ break;
+ }
+
+ if (rep)
+ rep->MoveTo(pos);
+
+ light = 0;
+
+ if (design->light > 0) {
+ light = new(__FILE__,__LINE__) Light(design->light);
+ light->SetColor(design->light_color);
+ }
+
+ mass = design->mass;
+ drag = design->drag;
+ thrust = 0.0f;
+
+ dr_drg = design->roll_drag;
+ dp_drg = design->pitch_drag;
+ dy_drg = design->yaw_drag;
+
+ SetAngularRates((float) design->roll_rate, (float) design->pitch_rate, (float) design->yaw_rate);
+
+ if (design->flash_img != 0) {
+ flash = new(__FILE__,__LINE__) Sprite(design->flash_img);
+ flash->Scale((double) design->flash_scale);
+ flash->MoveTo(pos - cam.vpn() * design->length);
+ flash->SetLuminous(true);
+ }
+
+ if (design->flare_img != 0) {
+ flare = new(__FILE__,__LINE__) DriveSprite(design->flare_img);
+ flare->Scale((double) design->flare_scale);
+ flare->MoveTo(pos);
+ }
+
+ if (owner) {
+ iff_code = (BYTE) owner->GetIFF();
+ Observe((SimObject*) owner);
+ }
+
+ sprintf(name, "Shot(%s)", design->name);
+}
+
+// +--------------------------------------------------------------------+
+
+Shot::~Shot()
+{
+ GRAPHIC_DESTROY(flash);
+ GRAPHIC_DESTROY(flare);
+ GRAPHIC_DESTROY(trail);
+
+ if (sound) {
+ sound->Stop();
+ sound->Release();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+Shot::DesignName() const
+{
+ return design->name;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Shot::SetCharge(float c)
+{
+ charge = c;
+
+ // trim beam life to amount of energy available:
+ if (beam)
+ life = design->life * charge / design->charge;
+}
+
+void
+Shot::SetFuse(double seconds)
+{
+ if (seconds > 0 && !beam)
+ life = seconds;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Shot::SeekTarget(SimObject* target, System* sub)
+{
+ if (dir && !primary) {
+ SeekerAI* seeker = (SeekerAI*) dir;
+ SimObject* old_target = seeker->GetTarget();
+
+ if (old_target->Type()==SimObject::SIM_SHIP) {
+ Ship* tgt_ship = (Ship*) old_target;
+ tgt_ship->DropThreat(this);
+ }
+ }
+
+ delete dir;
+ dir = 0;
+
+ if (target) {
+ SeekerAI* seeker = new(__FILE__,__LINE__) SeekerAI(this);
+ seeker->SetTarget(target, sub);
+ seeker->SetPursuit(design->guided);
+ seeker->SetDelay(1);
+
+ dir = seeker;
+
+ if (!primary && target->Type()==SimObject::SIM_SHIP) {
+ Ship* tgt_ship = (Ship*) target;
+ tgt_ship->AddThreat(this);
+ }
+ }
+}
+
+bool
+Shot::IsTracking(Ship* tgt) const
+{
+ return tgt && (GetTarget() == tgt);
+}
+
+SimObject*
+Shot::GetTarget() const
+{
+ if (dir) {
+ SeekerAI* seeker = (SeekerAI*) dir;
+
+ if (seeker->GetDelay() <= 0)
+ return seeker->GetTarget();
+ }
+
+ return 0;
+}
+
+bool
+Shot::IsFlak() const
+{
+ return design && design->flak;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Shot::IsHostileTo(const SimObject* o) const
+{
+ if (o) {
+ if (o->Type() == SIM_SHIP) {
+ Ship* s = (Ship*) o;
+
+ if (s->IsRogue())
+ return true;
+
+ if (s->GetIFF() > 0 && s->GetIFF() != GetIFF())
+ return true;
+ }
+
+ else if (o->Type() == SIM_SHOT || o->Type() == SIM_DRONE) {
+ Shot* s = (Shot*) o;
+
+ if (s->GetIFF() > 0 && s->GetIFF() != GetIFF())
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Shot::ExecFrame(double seconds)
+{
+ altitude_agl = -1.0e6f;
+
+ // add random flickering effect:
+ double flicker = 0.75 + (double) rand() / 8e4;
+ if (flicker > 1) flicker = 1;
+
+ if (flare) {
+ flare->SetShade(flicker);
+ }
+ else if (beam) {
+ Bolt* blob = (Bolt*) rep;
+ blob->SetShade(flicker);
+ offset -= (float) (seconds * 10);
+ }
+
+ if (Game::Paused())
+ return;
+
+ if (beam) {
+ if (!first_frame) {
+ if (life > 0) {
+ life -= seconds;
+
+ if (life < 0)
+ life = 0;
+ }
+ }
+ }
+ else {
+ origin = Location();
+
+ if (!first_frame)
+ Physical::ExecFrame(seconds);
+ else
+ Physical::ExecFrame(0);
+
+ double len = design->length;
+ if (len < 50) len = 50;
+
+ if (!trail && life > 0 && design->life - life > 0.2) {
+ if (design->trail.length()) {
+ trail = new(__FILE__,__LINE__) Trail(design->trail_img, design->trail_length);
+
+ if (design->trail_width > 0)
+ trail->SetWidth(design->trail_width);
+
+ if (design->trail_dim > 0)
+ trail->SetDim(design->trail_dim);
+
+ trail->AddPoint(Location() + Heading() * -100);
+
+ Scene* scene = 0;
+
+ if (rep)
+ scene = rep->GetScene();
+
+ if (scene)
+ scene->AddGraphic(trail);
+ }
+ }
+
+ if (trail)
+ trail->AddPoint(Location());
+
+ if (!armed) {
+ SeekerAI* seeker = (SeekerAI*) dir;
+
+ if (seeker && seeker->GetDelay() <= 0)
+ armed = true;
+ }
+
+ // handle submunitions:
+ else if (design->det_range > 0 && design->det_count > 0) {
+ if (dir && !primary) {
+ SeekerAI* seeker = (SeekerAI*) dir;
+ SimObject* target = seeker->GetTarget();
+
+ if (target) {
+ double range = Point(Location() - target->Location()).length();
+
+ if (range < design->det_range) {
+ life = 0;
+
+ Sim* sim = Sim::GetSim();
+ WeaponDesign* child_design = WeaponDesign::Find(design->det_child);
+
+ if (sim && child_design) {
+ double spread = design->det_spread;
+
+ Camera aim_cam;
+ aim_cam.Clone(Cam());
+ aim_cam.LookAt(target->Location());
+
+ for (int i = 0; i < design->det_count; i++) {
+ Shot* child = sim->CreateShot(Location(), aim_cam, child_design,
+ owner, owner->GetRegion());
+
+ child->SetCharge(child_design->charge);
+
+ if (child_design->guided)
+ child->SeekTarget(target, seeker->GetSubTarget());
+
+ if (child_design->beam)
+ child->SetBeamPoints(Location(), target->Location());
+
+ if (i) aim_cam.LookAt(target->Location());
+ aim_cam.Pitch(Random(-spread, spread));
+ aim_cam.Yaw(Random(-spread, spread));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (flash && !first_frame)
+ GRAPHIC_DESTROY(flash);
+
+ if (thrust < design->thrust)
+ thrust += (float) (seconds * 5.0e3);
+ else
+ thrust = design->thrust;
+ }
+
+ first_frame = 0;
+
+ if (flare)
+ flare->MoveTo(Location());
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Shot::Disarm()
+{
+ if (armed && !primary) {
+ armed = false;
+ delete dir;
+ dir = 0;
+ }
+}
+
+void
+Shot::Destroy()
+{
+ life = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Shot::SetBeamPoints(const Point& from, const Point& to)
+{
+ if (beam) {
+ MoveTo(to);
+ origin = from;
+
+ if (sound) {
+ sound->SetLocation(from);
+ }
+
+ if (rep) {
+ Bolt* s = (Bolt*) rep;
+ s->SetEndPoints(from, to);
+
+ double len = Point(to - from).length() / 500;
+ s->SetTextureOffset(offset, offset + len);
+ }
+ }
+
+ if (flash) {
+ flash->MoveTo(origin);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Shot::AltitudeMSL() const
+{
+ return Location().y;
+}
+
+double
+Shot::AltitudeAGL() const
+{
+ if (altitude_agl < -1000) {
+ Shot* pThis = (Shot*) this; // cast-away const
+ Point loc = Location();
+ Terrain* terrain = region->GetTerrain();
+
+ if (terrain)
+ pThis->altitude_agl = (float) (loc.y - terrain->Height(loc.x, loc.z));
+
+ else
+ pThis->altitude_agl = (float) loc.y;
+
+ if (!_finite(altitude_agl)) {
+ pThis->altitude_agl = 0.0f;
+ }
+ }
+
+ return altitude_agl;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Shot::Initialize()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Shot::Close()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Shot::Damage() const
+{
+ double damage = 0;
+
+ // beam damage based on length:
+ if (beam) {
+ double fade = 1;
+
+ if (design) {
+ // linear fade with distance:
+ double len = Point(origin - Location()).length();
+
+ if (len > design->min_range)
+ fade = (design->length - len) / (design->length - design->min_range);
+ }
+
+ damage = base_damage * charge * fade * Game::FrameTime();
+ }
+
+ // energy wep damage based on time:
+ else if (primary) {
+ damage = base_damage * charge * life;
+ }
+
+ // missile damage is constant:
+ else {
+ damage = base_damage * charge;
+ }
+
+ return damage;
+}
+
+double
+Shot::Length() const
+{
+ if (design)
+ return design->length;
+
+ return 500;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Shot::Activate(Scene& scene)
+{
+ SimObject::Activate(scene);
+
+ if (trail)
+ scene.AddGraphic(trail);
+
+ if (flash)
+ scene.AddGraphic(flash);
+
+ if (flare)
+ scene.AddGraphic(flare);
+
+ if (first_frame) {
+ if (design->sound_resource) {
+ sound = design->sound_resource->Duplicate();
+
+ if (sound) {
+ long max_vol = AudioConfig::EfxVolume();
+ long volume = -1000;
+
+ if (volume > max_vol)
+ volume = max_vol;
+
+ if (beam) {
+ sound->SetLocation(origin);
+ sound->SetVolume(volume);
+ sound->Play();
+ }
+ else {
+ sound->SetLocation(Location());
+ sound->SetVolume(volume);
+ sound->Play();
+ sound = 0; // fire and forget:
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Shot::Deactivate(Scene& scene)
+{
+ SimObject::Deactivate(scene);
+
+ if (trail)
+ scene.DelGraphic(trail);
+
+ if (flash)
+ scene.DelGraphic(flash);
+
+ if (flare)
+ scene.DelGraphic(flare);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Shot::GetIFF() const
+{
+ return iff_code;
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Shot::MarkerColor() const
+{
+ return Ship::IFFColor(GetIFF());
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+Shot::GetObserverName() const
+{
+ return name;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Shot::Update(SimObject* obj)
+{
+ if (obj == (SimObject*) owner)
+ owner = 0;
+
+ return SimObserver::Update(obj);
+}
diff --git a/Stars45/Shot.h b/Stars45/Shot.h
new file mode 100644
index 0000000..52f5a6c
--- /dev/null
+++ b/Stars45/Shot.h
@@ -0,0 +1,127 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Shot.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Laser and Missile class
+*/
+
+#ifndef Shot_h
+#define Shot_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "SimObject.h"
+#include "Color.h"
+
+// +--------------------------------------------------------------------+
+
+class Camera;
+class Ship;
+class Trail;
+class System;
+class WeaponDesign;
+class Sprite;
+class Sound;
+
+// +--------------------------------------------------------------------+
+
+class Shot : public SimObject,
+ public SimObserver
+{
+public:
+ static const char* TYPENAME() { return "Shot"; }
+
+ Shot(const Point& pos, const Camera& cam, WeaponDesign* design, const Ship* ship=0);
+ virtual ~Shot();
+
+ virtual void SeekTarget(SimObject* target, System* sub=0);
+ virtual void ExecFrame(double factor);
+ static void Initialize();
+ static void Close();
+
+ virtual void Activate(Scene& scene);
+ virtual void Deactivate(Scene& scene);
+
+ const Ship* Owner() const { return owner; }
+ double Damage() const;
+ int ShotType() const { return type; }
+ virtual SimObject* GetTarget() const;
+
+ virtual bool IsPrimary() const { return primary; }
+ virtual bool IsDrone() const { return false; }
+ virtual bool IsDecoy() const { return false; }
+ virtual bool IsProbe() const { return false; }
+ virtual bool IsMissile() const { return !primary; }
+ virtual bool IsArmed() const { return armed; }
+ virtual bool IsBeam() const { return beam; }
+ virtual bool IsFlak() const;
+ virtual bool IsHostileTo(const SimObject* o) const;
+
+ bool HitTarget() const { return hit_target; }
+ void SetHitTarget(bool h) { hit_target = h; }
+
+ virtual bool IsTracking(Ship* tgt) const;
+ virtual double PCS() const { return 0; }
+ virtual double ACS() const { return 0; }
+ virtual int GetIFF() const;
+ virtual Color MarkerColor() const;
+
+ const Point& Origin() const { return origin; }
+ float Charge() const { return charge; }
+ void SetCharge(float c);
+ double Length() const;
+ Graphic* GetTrail() const { return (Graphic*) trail; }
+ void SetFuse(double seconds);
+
+ void SetBeamPoints(const Point& from, const Point& to);
+ virtual void Disarm();
+ virtual void Destroy();
+
+ const WeaponDesign* Design() const { return design; }
+ const char* DesignName() const;
+ int GetEta() const { return eta; }
+ void SetEta(int t) { eta = t; }
+
+ double AltitudeMSL() const;
+ double AltitudeAGL() const;
+
+ // SimObserver interface:
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+protected:
+ const Ship* owner;
+
+ int type;
+ float base_damage;
+ float charge;
+ float offset;
+ float altitude_agl;
+ short eta;
+ BYTE iff_code;
+ bool first_frame;
+ bool primary;
+ bool beam;
+ bool armed;
+ bool hit_target;
+
+ Sprite* flash; // muzzle flash
+ Sprite* flare; // drive flare
+ Trail* trail; // exhaust trail
+
+ Sound* sound;
+ WeaponDesign* design;
+
+ // for beam weapons:
+ Point origin;
+};
+
+#endif Shot_h
+
diff --git a/Stars45/Sim.cpp b/Stars45/Sim.cpp
new file mode 100644
index 0000000..90986a4
--- /dev/null
+++ b/Stars45/Sim.cpp
@@ -0,0 +1,3810 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Sim.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Simulation Universe and Region classes
+*/
+
+#include "MemDebug.h"
+#include "Sim.h"
+#include "SimEvent.h"
+#include "SimObject.h"
+#include "Starshatter.h"
+#include "StarSystem.h"
+#include "Contact.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Element.h"
+#include "Instruction.h"
+#include "RadioTraffic.h"
+#include "Shot.h"
+#include "Drone.h"
+#include "Explosion.h"
+#include "Debris.h"
+#include "Asteroid.h"
+#include "Drive.h"
+#include "QuantumDrive.h"
+#include "Sensor.h"
+#include "NavLight.h"
+#include "Shield.h"
+#include "Weapon.h"
+#include "WeaponGroup.h"
+#include "Hangar.h"
+#include "FlightDeck.h"
+#include "Sky.h"
+#include "Grid.h"
+#include "MFD.h"
+#include "AudioConfig.h"
+#include "Mission.h"
+#include "MissionEvent.h"
+#include "CameraDirector.h"
+#include "MusicDirector.h"
+#include "Combatant.h"
+#include "CombatGroup.h"
+#include "CombatUnit.h"
+#include "HUDView.h"
+#include "SeekerAI.h"
+#include "ShipAI.h"
+#include "Power.h"
+#include "Callsign.h"
+#include "GameScreen.h"
+#include "Terrain.h"
+#include "TerrainPatch.h"
+
+#include "NetGame.h"
+#include "NetClientConfig.h"
+#include "NetServerConfig.h"
+#include "NetPlayer.h"
+#include "NetUtil.h"
+#include "NetData.h"
+
+#include "Game.h"
+#include "Sound.h"
+#include "Bolt.h"
+#include "Solid.h"
+#include "Sprite.h"
+#include "Light.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "ParseUtil.h"
+#include "MouseController.h"
+#include "Player.h"
+#include "Random.h"
+#include "Video.h"
+
+const char* FormatGameTime();
+
+// +--------------------------------------------------------------------+
+
+class SimHyper
+{
+public:
+ SimHyper(Ship* o, SimRegion* r, const Point& l, int t, bool h, Ship* fc1, Ship* fc2)
+ : ship(o), rgn(r), loc(l), type(t), hyperdrive(h), fc_src(fc1), fc_dst(fc2) { }
+
+ Ship* ship;
+ SimRegion* rgn;
+ Point loc;
+ int type;
+ bool hyperdrive;
+ Ship* fc_src;
+ Ship* fc_dst;
+};
+
+// +--------------------------------------------------------------------+
+
+class SimSplash
+{
+public:
+ SimSplash(SimRegion* r, const Point& l, double d, double n)
+ : rgn(r), loc(l), damage(d), range(n),
+ owner_name("Collateral Damage"), missile(false) { }
+
+ SimRegion* rgn;
+ Point loc;
+ double damage;
+ double range;
+ Text owner_name;
+ bool missile;
+};
+
+// +--------------------------------------------------------------------+
+
+static bool first_frame = true;
+Sim* Sim::sim = 0;
+
+Sim::Sim(MotionController* c)
+ : ctrl(c), test_mode(false), grid_shown(false), dust(0),
+ star_system(0), active_region(0), mission(0), netgame(0),
+ start_time(0)
+{
+ Drive::Initialize();
+ Explosion::Initialize();
+ FlightDeck::Initialize();
+ NavLight::Initialize();
+ Shot::Initialize();
+ MFD::Initialize();
+ Asteroid::Initialize();
+
+ if (!sim)
+ sim = this;
+
+ cam_dir = CameraDirector::GetInstance();
+}
+
+Sim::~Sim()
+{
+ UnloadMission();
+
+ Shot::Close();
+ FlightDeck::Close();
+ NavLight::Close();
+ Token::close();
+ Asteroid::Close();
+
+ if (sim == this)
+ sim = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sim::CommitMission()
+{
+ for (int i = 0; i < regions.size(); i++)
+ regions[i]->CommitMission();
+
+ if (ShipStats::NumStats() > 0) {
+ Print("\n\nFINAL SCORE '%s'\n", (const char*) mission->Name());
+ Print("Name Kill1 Kill2 Died Colls Points Cmd Pts\n");
+ Print("---------------- ----- ----- ----- ----- ------ ------\n");
+
+ int tk1 = 0;
+ int tk2 = 0;
+ int td = 0;
+ int tc = 0;
+
+ for (int i = 0; i < ShipStats::NumStats(); i++) {
+ ShipStats* s = ShipStats::GetStats(i);
+ s->Summarize();
+
+ Print("%-16s %5d %5d %5d %5d %6d %6d\n",
+ s->GetName(),
+ s->GetGunKills(),
+ s->GetMissileKills(),
+ s->GetDeaths(),
+ s->GetColls(),
+ s->GetPoints(),
+ s->GetCommandPoints());
+
+ tk1 += s->GetGunKills();
+ tk2 += s->GetMissileKills();
+ td += s->GetDeaths();
+ tc += s->GetColls();
+
+ CombatGroup* group = s->GetCombatGroup();
+
+ if (group) {
+ Combatant* c = group->GetCombatant();
+
+ if (c)
+ c->AddScore(s->GetPoints());
+
+ if (s->GetElementIndex() == 1)
+ group->SetSorties(group->Sorties() + 1);
+
+ group->SetKills(group->Kills() + s->GetGunKills() + s->GetMissileKills());
+ group->SetPoints(group->Points() + s->GetPoints());
+ }
+
+ if (s->IsPlayer()) {
+ Player* p = Player::GetCurrentPlayer();
+ p->ProcessStats(s, start_time);
+
+ if (mission && mission->Type() == Mission::TRAINING &&
+ s->GetDeaths() == 0 && s->GetColls() == 0)
+ p->SetTrained(mission->Identity());
+
+ Player::Save(); // save training state right now before we forget!
+ }
+ }
+
+ Print("--------------------------------------------\n");
+ Print("TOTAL %5d %5d %5d %5d\n\n", tk1, tk2, td, tc);
+
+ ShipStats::Initialize();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sim::UnloadMission()
+{
+ if (netgame) {
+ delete netgame;
+ netgame = 0;
+ }
+
+ HUDView* hud = HUDView::GetInstance();
+ if (hud)
+ hud->HideAll();
+
+ ShipStats::Initialize();
+
+ events.destroy();
+ mission_elements.destroy();
+ elements.destroy();
+ finished.destroy();
+
+ if (active_region)
+ active_region->Deactivate();
+
+ if (star_system)
+ star_system->Deactivate();
+
+ if (mission) {
+ mission->SetActive(false);
+ mission->SetComplete(true);
+ }
+
+ regions.destroy();
+ scene.Collect();
+
+ GRAPHIC_DESTROY(dust);
+
+ star_system = 0;
+ active_region = 0;
+ mission = 0;
+
+ // reclaim memory used by radio traffic:
+ RadioTraffic::DiscardMessages();
+
+ // release texture memory for 2D screens:
+ Starshatter* stars = Starshatter::GetInstance();
+ if (stars)
+ stars->InvalidateTextureCache();
+
+ cam_dir = CameraDirector::GetInstance();
+ if (cam_dir)
+ cam_dir->SetShip(0);
+
+ AudioConfig::SetTraining(false);
+}
+
+bool
+Sim::IsActive() const
+{
+ return mission && mission->IsActive();
+}
+
+bool
+Sim::IsComplete() const
+{
+ return mission && mission->IsComplete();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sim::LoadMission(Mission* m, bool preload_textures)
+{
+ cam_dir = CameraDirector::GetInstance();
+
+ if (!mission) {
+ mission = m;
+ mission->SetActive(true);
+
+ if (preload_textures) {
+ Video* video = Game::GetVideo();
+ List<Model> all_models;
+ //List<Bitmap> all_textures;
+
+ ListIter<MissionElement> elem_iter = mission->GetElements();
+ while (++elem_iter) {
+ MissionElement* elem = elem_iter.value();
+ const ShipDesign* design = elem->GetDesign();
+
+ if (design) {
+ for (int i = 0; i < 4; i++) {
+ List<Model>& models = (List<Model>&) design->models[i]; // cast-away const
+
+ ListIter<Model> model_iter = models;
+ while (++model_iter) {
+ Model* model = model_iter.value();
+ if (!all_models.contains(model)) {
+ all_models.append(model);
+ //model->GetAllTextures(all_textures);
+
+ ListIter<Surface> surf_iter = model->GetSurfaces();
+ while (++surf_iter) {
+ Surface* surface = surf_iter.value();
+ video->PreloadSurface(surface);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ if (video && all_textures.size() > 0) {
+ ::Print("Preloading %d textures into video texture cache\n", all_textures.size());
+ ListIter<Bitmap> bmp_iter = all_textures;
+ while (++bmp_iter) {
+ Bitmap* bmp = bmp_iter.value();
+ video->PreloadTexture(bmp);
+ }
+ }
+ */
+ }
+ }
+}
+
+void
+Sim::ExecMission()
+{
+ cam_dir = CameraDirector::GetInstance();
+
+ if (!mission) {
+ Print("Sim::ExecMission() - No mission to execute.\n");
+ return;
+ }
+
+ if (elements.size() || finished.size()) {
+ Print("Sim::ExecMission(%s) mission is already executing.\n", mission->Name());
+ return;
+ }
+
+ Print("\nExec Mission: '%s'\n", (const char*) mission->Name());
+
+ if (cam_dir)
+ cam_dir->Reset();
+
+ if (mission->Stardate() > 0)
+ StarSystem::SetBaseTime(mission->Stardate(), true);
+
+ star_system = mission->GetStarSystem();
+ star_system->Activate(scene);
+
+ int dust_factor = 0;
+
+ if (Starshatter::GetInstance())
+ dust_factor = Starshatter::GetInstance()->Dust();
+
+ if (star_system->NumDust() * dust_factor) {
+ dust = new(__FILE__,__LINE__) Dust(star_system->NumDust() * 2*(dust_factor+1), dust_factor > 1);
+ scene.AddGraphic(dust);
+ }
+
+ CreateRegions();
+ BuildLinks();
+ CreateElements();
+ CopyEvents();
+
+ if (netgame) {
+ delete netgame;
+ netgame = 0;
+ }
+
+ first_frame = true;
+ start_time = Game::GameTime();
+
+ AudioConfig::SetTraining(mission->Type() == Mission::TRAINING);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sim::CreateRegions()
+{
+ const char* active_region_name = 0;
+
+ if (mission)
+ active_region_name = mission->GetRegion();
+
+ ListIter<StarSystem> iter = mission->GetSystemList();
+ while (++iter) {
+ StarSystem* sys = iter.value();
+
+ // insert objects from star system:
+ ListIter<OrbitalBody> star = sys->Bodies();
+ while (++star) {
+ ListIter<OrbitalBody> planet = star->Satellites();
+ while (++planet) {
+ ListIter<OrbitalBody> moon = planet->Satellites();
+ while (++moon) {
+ ListIter<OrbitalRegion> rgn = moon->Regions();
+ while (++rgn) {
+ SimRegion* sim_region = new(__FILE__,__LINE__) SimRegion(this, rgn.value());
+ regions.append(sim_region);
+ if (!strcmp(active_region_name, sim_region->Name())) {
+ ActivateRegion(sim_region);
+ }
+ }
+ }
+
+ ListIter<OrbitalRegion> rgn = planet->Regions();
+ while (++rgn) {
+ SimRegion* sim_region = new(__FILE__,__LINE__) SimRegion(this, rgn.value());
+ regions.append(sim_region);
+ if (!strcmp(active_region_name, sim_region->Name())) {
+ ActivateRegion(sim_region);
+ }
+ }
+ }
+
+ ListIter<OrbitalRegion> rgn = star->Regions();
+ while (++rgn) {
+ SimRegion* sim_region = new(__FILE__,__LINE__) SimRegion(this, rgn.value());
+ regions.append(sim_region);
+ if (!strcmp(active_region_name, sim_region->Name())) {
+ ActivateRegion(sim_region);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sim::BuildLinks()
+{
+ ListIter<SimRegion> iter = regions;
+ while (++iter) {
+ SimRegion* rgn = iter.value();
+ OrbitalRegion* orb = rgn->GetOrbitalRegion();
+
+ if (orb) {
+ ListIter<Text> lnk_iter = orb->Links();
+ while (++lnk_iter) {
+ Text* t = lnk_iter.value();
+
+ SimRegion* tgt = FindRegion(*t);
+
+ if (tgt && !rgn->Links().contains(tgt))
+ rgn->Links().append(tgt);
+ }
+ }
+ }
+}
+
+void
+Sim::CreateElements()
+{
+ ListIter<MissionElement> e_iter = mission->GetElements();
+ while (++e_iter) {
+ MissionElement* msn_elem = e_iter.value();
+
+ // add element to a carrier?
+ if (msn_elem->IsSquadron()) {
+ Ship* carrier = FindShip(msn_elem->Carrier());
+ if (carrier) {
+ Hangar* hangar = carrier->GetHangar();
+
+ if (hangar) {
+ int* def_load = 0;
+
+ if (msn_elem->Loadouts().size()) {
+ MissionLoad* m = msn_elem->Loadouts().at(0);
+
+ if (m->GetName().length()) {
+ ShipDesign* dsn = (ShipDesign*) msn_elem->GetDesign();
+ ListIter<ShipLoad> sl_iter = dsn->loadouts;
+ while (++sl_iter) {
+ ShipLoad* sl = sl_iter.value();
+
+ if (m->GetName() == sl->name)
+ def_load = sl->load;
+ }
+ }
+
+ if (!def_load) {
+ def_load = m->GetStations();
+ }
+ }
+
+ hangar->CreateSquadron(msn_elem->Name(), msn_elem->GetCombatGroup(),
+ msn_elem->GetDesign(), msn_elem->Count(),
+ msn_elem->GetIFF(),
+ def_load, msn_elem->MaintCount(), msn_elem->DeadCount());
+
+ Element* element = CreateElement(msn_elem->Name(),
+ msn_elem->GetIFF(),
+ msn_elem->MissionRole());
+
+ element->SetCarrier(carrier);
+ element->SetCombatGroup(msn_elem->GetCombatGroup());
+ element->SetCombatUnit(msn_elem->GetCombatUnit());
+ element->SetCount(msn_elem->Count());
+ element->SetRogue(false);
+ element->SetPlayable(false);
+ element->SetLoadout(def_load);
+ }
+ }
+ }
+
+ // create the element in space:
+ else {
+ Ship* carrier = 0;
+ Hangar* hangar = 0;
+ int squadron = -1;
+ int slot = 0;
+
+ // first create the package element:
+ Element* element = CreateElement(msn_elem->Name(),
+ msn_elem->GetIFF(),
+ msn_elem->MissionRole());
+
+ element->SetPlayer(msn_elem->Player());
+ element->SetCombatGroup(msn_elem->GetCombatGroup());
+ element->SetCombatUnit(msn_elem->GetCombatUnit());
+ element->SetCommandAILevel(msn_elem->CommandAI());
+ element->SetHoldTime(msn_elem->HoldTime());
+ element->SetZoneLock(msn_elem->ZoneLock() ? true : false);
+ element->SetRogue(msn_elem->IsRogue());
+ element->SetPlayable(msn_elem->IsPlayable());
+ element->SetIntelLevel(msn_elem->IntelLevel());
+
+ // if this is the player's element, make sure to activate the region:
+ if (msn_elem->Player()) {
+ SimRegion* rgn = FindRegion(msn_elem->Region());
+
+ if (rgn && rgn != active_region)
+ ActivateRegion(rgn);
+ }
+
+ // if element belongs to a squadron,
+ // find the carrier, squadron, flight deck, etc.:
+ if (msn_elem->Squadron().length() > 0) {
+ MissionElement* squadron_elem = mission->FindElement(msn_elem->Squadron());
+
+ if (squadron_elem) {
+ element->SetSquadron(msn_elem->Squadron());
+
+ Element* cmdr = FindElement(squadron_elem->Carrier());
+
+ if (cmdr) {
+ element->SetCommander(cmdr);
+ carrier = cmdr->GetShip(1);
+
+ if (carrier) {
+ element->SetCarrier(carrier);
+ hangar = carrier->GetHangar();
+
+ for (int s = 0; s < hangar->NumSquadrons(); s++) {
+ if (hangar->SquadronName(s) == msn_elem->Squadron()) {
+ squadron = s;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ else if (msn_elem->Commander().length() > 0) {
+ Element* cmdr = FindElement(msn_elem->Commander());
+
+ if (cmdr) {
+ element->SetCommander(cmdr);
+ }
+ }
+
+ ListIter<Instruction> obj = msn_elem->Objectives();
+ while (++obj) {
+ Instruction* o = obj.value();
+ Instruction* instr = 0;
+
+ instr = new(__FILE__,__LINE__) Instruction(*o);
+
+ element->AddObjective(instr);
+ }
+
+ if (msn_elem->Instructions().size() > 0) {
+ ListIter<Text> instr = msn_elem->Instructions();
+ while (++instr) {
+ element->AddInstruction(*instr);
+ }
+ }
+
+ ListIter<Instruction> nav = msn_elem->NavList();
+ while (++nav) {
+ SimRegion* rgn = FindRegion(nav->RegionName());
+
+ if (!rgn)
+ rgn = FindRegion(msn_elem->Region());
+
+ if (rgn) {
+ Instruction* npt = new(__FILE__,__LINE__)
+ Instruction(rgn, nav->Location(), nav->Action());
+
+ npt->SetStatus(nav->Status());
+ npt->SetEMCON(nav->EMCON());
+ npt->SetFormation(nav->Formation());
+ npt->SetSpeed(nav->Speed());
+ npt->SetTarget(nav->TargetName());
+ npt->SetHoldTime(nav->HoldTime());
+ npt->SetFarcast(nav->Farcast());
+
+ element->AddNavPoint(npt);
+ }
+ }
+
+ bool alertPrep = false;
+ int* loadout = 0;
+ int respawns = msn_elem->RespawnCount();
+
+ // if ships are to start on alert,
+ // spot them onto the appropriate launch deck:
+ if (hangar && element && msn_elem->Count() > 0 && msn_elem->IsAlert()) {
+ FlightDeck* deck = 0;
+ int queue = 1000;
+ const ShipDesign* dsn = msn_elem->GetDesign();
+
+ if (dsn) {
+ for (int i = 0; i < carrier->NumFlightDecks(); i++) {
+ FlightDeck* d = carrier->GetFlightDeck(i);
+ int dq = hangar->PreflightQueue(d);
+
+ if (d && d->IsLaunchDeck() && d->SpaceLeft(dsn->type) && dq < queue) {
+ queue = dq;
+ deck = d;
+ }
+ }
+ }
+
+ if (deck) {
+ alertPrep = true;
+
+ // choose best loadout:
+ if (msn_elem->Loadouts().size()) {
+ MissionLoad* l = msn_elem->Loadouts().at(0);
+ if (l->GetName().length()) {
+ ListIter<ShipLoad> sl = ((ShipDesign*) dsn)->loadouts;
+ while (++sl) {
+ if (!stricmp(sl->name, l->GetName()))
+ loadout = sl->load;
+ }
+ }
+
+ else {
+ loadout = l->GetStations();
+ }
+ }
+
+ element->SetLoadout(loadout);
+
+ for (int i = 0; i < msn_elem->Count(); i++) {
+ int squadron = -1;
+ int slot = -1;
+
+ if (hangar->FindAvailSlot(msn_elem->GetDesign(), squadron, slot)) {
+ alertPrep = alertPrep &&
+ hangar->GotoAlert(squadron,
+ slot,
+ deck,
+ element,
+ loadout,
+ true, // package for launch
+ true); // expedite
+
+ HangarSlot* s = (HangarSlot*) hangar->GetSlot(squadron, slot);
+ Ship* alertShip = hangar->GetShip(s);
+
+ if (alertShip) {
+ alertShip->SetRespawnCount(respawns);
+
+ if (msn_elem->Player() == i+1) {
+ if (alertShip->GetRegion()) {
+ alertShip->GetRegion()->SetPlayerShip(alertShip);
+ }
+ else {
+ ::Print("WARNING: alert ship '%s' region is null\n", alertShip->Name());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!alertPrep) {
+ // then, create the ships:
+ for (int i = 0; i < msn_elem->Count(); i++) {
+ MissionShip* msn_ship = 0;
+ Text sname = msn_elem->GetShipName(i);
+ Text rnum = msn_elem->GetRegistry(i);
+ Text rgn_name = msn_elem->Region();
+
+ if (msn_elem->Ships().size() > i) {
+ msn_ship = msn_elem->Ships()[i];
+ sname = msn_ship->Name();
+ rnum = msn_ship->RegNum();
+ rgn_name = msn_ship->Region();
+ }
+
+ Point l2 = msn_elem->Location();
+
+ if (msn_ship && fabs(msn_ship->Location().x) < 1e9) {
+ l2 = msn_ship->Location();
+ }
+ else if (i) {
+ Point offset = RandomPoint();
+ offset.z = Random(-1e3, 1e3);
+
+ if (msn_elem->Count() < 5)
+ offset *= 0.3;
+
+ l2 += offset;
+ }
+
+ // choose best loadout:
+ ListIter<MissionLoad> l = msn_elem->Loadouts();
+ while (++l) {
+ if ((l->GetShip() == i) || (l->GetShip() < 0 && loadout == 0)) {
+ if (l->GetName().length()) {
+ ListIter<ShipLoad> sl = ((ShipDesign*) msn_elem->GetDesign())->loadouts;
+ while (++sl) {
+ if (!stricmp(sl->name, l->GetName()))
+ loadout = sl->load;
+ }
+ }
+
+ else {
+ loadout = l->GetStations();
+ }
+ }
+ }
+
+ element->SetLoadout(loadout);
+
+ Ship* ship = CreateShip(sname, rnum,
+ (ShipDesign*) msn_elem->GetDesign(),
+ rgn_name, l2,
+ msn_elem->GetIFF(),
+ msn_elem->CommandAI(),
+ loadout);
+
+ if (ship) {
+ double heading = msn_elem->Heading();
+ const Skin* skin = msn_elem->GetSkin();
+
+ if (msn_ship) {
+ heading = msn_ship->Heading();
+
+ if (msn_ship->GetSkin())
+ skin = msn_ship->GetSkin();
+ }
+
+ ship->SetRogue(msn_elem->IsRogue());
+ ship->SetInvulnerable(msn_elem->IsInvulnerable());
+ ship->SetHeading(0, 0, heading + PI);
+ ship->SetRespawnCount(respawns);
+ ship->UseSkin(skin);
+
+ if (!netgame)
+ ship->SetRespawnLoc(RandomPoint() * 2);
+
+ if (ship->IsStarship())
+ ship->SetHelmHeading(heading);
+
+ else if (ship->IsAirborne() && ship->AltitudeAGL() > 25)
+ ship->SetVelocity(ship->Heading() * 250);
+
+ if (element)
+ element->AddShip(ship);
+
+ if (hangar)
+ hangar->FindSlot(ship, squadron, slot, Hangar::ACTIVE);
+
+ if (ship->GetRegion() && msn_elem->Player() == i+1)
+ ship->GetRegion()->SetPlayerShip(ship);
+
+ if (ship->NumFlightDecks()) {
+ for (int i = 0; i < ship->NumFlightDecks(); i++) {
+ FlightDeck* deck = ship->GetFlightDeck(i);
+ if (deck)
+ deck->Orient(ship);
+ }
+ }
+
+ if (msn_ship) {
+ ship->SetVelocity(msn_ship->Velocity().OtherHand());
+ ship->SetIntegrity((float) msn_ship->Integrity());
+ ship->SetRespawnCount(msn_ship->Respawns());
+
+ if (msn_ship->Ammo()[0] > -10) {
+ for (int i = 0; i < 64; i++) {
+ Weapon* w = ship->GetWeaponByIndex(i+1);
+ if (w)
+ w->SetAmmo(msn_ship->Ammo()[i]);
+ else
+ break;
+ }
+ }
+
+ if (msn_ship->Fuel()[0] > -10) {
+ for (int i = 0; i < 4; i++) {
+ if (ship->Reactors().size() > i) {
+ PowerSource* p = ship->Reactors()[i];
+ p->SetCapacity(msn_ship->Fuel()[i]);
+ }
+ }
+ }
+
+ if (msn_ship->Decoys() > -10) {
+ Weapon* w = ship->GetDecoy();
+ if (w)
+ w->SetAmmo(msn_ship->Decoys());
+ }
+
+ if (msn_ship->Probes() > -10) {
+ Weapon* w = ship->GetProbeLauncher();
+ if (w)
+ w->SetAmmo(msn_ship->Probes());
+ }
+ }
+
+ Shield* shield = ship->GetShield();
+
+ if (shield) {
+ shield->SetPowerLevel(50);
+ }
+
+ if (ship->Class() > Ship::FRIGATE) {
+ ListIter<WeaponGroup> iter = ship->Weapons();
+ while (++iter) {
+ WeaponGroup* weapon = iter.value();
+
+ // anti-air weapon?
+ if (weapon->GetDesign()->target_type & Ship::DRONE) {
+ weapon->SetFiringOrders(Weapon::POINT_DEFENSE);
+ }
+ else {
+ weapon->SetFiringOrders(Weapon::MANUAL);
+ }
+ }
+ }
+
+ if (ship->Class() > Ship::DRONE && ship->Class() < Ship::STATION) {
+ ShipStats* stats = ShipStats::Find(sname);
+ if (stats) {
+ char design[64];
+ sprintf(design, "%s %s", ship->Abbreviation(), ship->Design()->display_name);
+ stats->SetType(design);
+ stats->SetShipClass(ship->Class());
+ stats->SetRole(Mission::RoleName(msn_elem->MissionRole()));
+ stats->SetIFF(ship->GetIFF());
+ stats->SetRegion(msn_elem->Region());
+ stats->SetCombatGroup(msn_elem->GetCombatGroup());
+ stats->SetCombatUnit(msn_elem->GetCombatUnit());
+ stats->SetPlayer(msn_elem->Player() == i+1);
+ stats->SetElementIndex(ship->GetElementIndex());
+ }
+ }
+ } // ship
+ } // count
+ }
+ }
+ }
+}
+
+void
+Sim::CopyEvents()
+{
+ events.destroy();
+
+ if (mission) {
+ ListIter<MissionEvent> iter = mission->GetEvents();
+ while (++iter) {
+ MissionEvent* orig = iter.value();
+ MissionEvent* event = new(__FILE__,__LINE__) MissionEvent(*orig);
+ events.append(event);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+Sim::FindAvailCallsign(int IFF)
+{
+ const char* call = "Unidentified";
+
+ for (int i = 0; i < 32; i++) {
+ call = Callsign::GetCallsign(IFF);
+
+ if (!FindElement(call))
+ break;
+ }
+
+ return call;
+}
+
+Element*
+Sim::CreateElement(const char* callsign, int IFF, int type)
+{
+ Element* elem = new(__FILE__,__LINE__) Element(callsign, IFF, type);
+ elements.append(elem);
+ return elem;
+}
+
+void
+Sim::DestroyElement(Element* elem)
+{
+ if (elements.contains(elem))
+ elements.remove(elem);
+
+ delete elem;
+}
+
+Element*
+Sim::FindElement(const char* name)
+{
+ ListIter<Element> iter = elements;
+
+ while (++iter) {
+ Element* elem = iter.value();
+ Text ename = elem->Name();
+
+ if (ename == name)
+ return elem;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Sim::GetAssignedElements(Element* elem, List<Element>& assigned)
+{
+ assigned.clear();
+
+ if (elem) {
+ for (int i = 0; i < elements.size(); i++) {
+ Element* e = elements.at(i);
+ if (!e->IsSquadron() && e->GetAssignment() == elem)
+ assigned.append(e);
+ }
+ }
+
+ return assigned.size();
+}
+
+// +--------------------------------------------------------------------+
+
+Ship*
+Sim::CreateShip(const char* name, const char* reg_num, ShipDesign* design, const char* rgn_name, const Point& loc, int IFF, int cmd_ai, const int* loadout)
+{
+ if (!design) {
+ Print("WARNING: CreateShip(%s): invalid design\n", name);
+ return 0;
+ }
+
+ SimRegion* rgn = FindRegion(rgn_name);
+
+ if (!rgn) {
+ return 0;
+ }
+
+ Ship* ship = new(__FILE__,__LINE__) Ship(name, reg_num, design, IFF, cmd_ai, loadout);
+ ship->MoveTo(loc.OtherHand());
+
+ if (rgn) {
+ Print("Inserting Ship(%s) into Region(%s) (%s)\n", ship->Name(), rgn->Name(), FormatGameTime());
+ rgn->InsertObject(ship);
+
+ if (ship->IsAirborne() && ship->AltitudeAGL() > 25)
+ ship->SetVelocity(ship->Heading() * 250);
+ }
+
+ return ship;
+}
+
+Ship*
+Sim::FindShip(const char* name, const char* rgn_name)
+{
+ Ship* ship = 0;
+
+ if (rgn_name) {
+ SimRegion* rgn = FindRegion(rgn_name);
+ if (rgn)
+ ship = rgn->FindShip(name);
+ }
+
+ if (!ship) {
+ ListIter<SimRegion> rgn = regions;
+ while (++rgn && !ship)
+ ship = rgn->FindShip(name);
+ }
+
+ return ship;
+}
+
+void
+Sim::DestroyShip(Ship* ship)
+{
+ SimRegion* rgn = ship->GetRegion();
+ if (rgn)
+ rgn->DestroyShip(ship);
+}
+
+void
+Sim::NetDockShip(Ship* ship, Ship* carrier, FlightDeck* deck)
+{
+ SimRegion* rgn = ship->GetRegion();
+ if (rgn)
+ rgn->NetDockShip(ship, carrier, deck);
+}
+
+Ship*
+Sim::FindShipByObjID(DWORD objid)
+{
+ Ship* ship = 0;
+
+ ListIter<SimRegion> rgn = regions;
+ while (++rgn && !ship)
+ ship = rgn->FindShipByObjID(objid);
+
+ return ship;
+}
+
+Shot*
+Sim::FindShotByObjID(DWORD objid)
+{
+ Shot* shot = 0;
+
+ ListIter<SimRegion> rgn = regions;
+ while (++rgn && !shot)
+ shot = rgn->FindShotByObjID(objid);
+
+ return shot;
+}
+
+// +--------------------------------------------------------------------+
+
+Orbital*
+Sim::FindOrbitalBody(const char* name)
+{
+ Orbital* body = 0;
+
+ if (mission) {
+ ListIter<StarSystem> iter = mission->GetSystemList();
+ while (++iter && !body) {
+ StarSystem* sys = iter.value();
+ body = sys->FindOrbital(name);
+ }
+ }
+
+ return body;
+}
+
+
+// +--------------------------------------------------------------------+
+
+Shot*
+Sim::CreateShot(const Point& pos, const Camera& shot_cam, WeaponDesign* design, const Ship* ship, SimRegion* rgn)
+{
+ Shot* shot = 0;
+
+ if (design->drone)
+ shot = new(__FILE__,__LINE__) Drone(pos, shot_cam, design, ship);
+ else
+ shot = new(__FILE__,__LINE__) Shot( pos, shot_cam, design, ship);
+
+ if (rgn)
+ rgn->InsertObject(shot);
+
+ else if (active_region)
+ active_region->InsertObject(shot);
+
+ return shot;
+}
+
+// +--------------------------------------------------------------------+
+
+Explosion*
+Sim::CreateExplosion(const Point& pos, const Point& vel, int type, float exp_scale, float part_scale, SimRegion* rgn, SimObject* source, System* sys)
+{
+ // don't bother creating explosions that can't be seen:
+ if (!rgn || !active_region || rgn != active_region)
+ return 0;
+
+ Explosion* exp = new(__FILE__,__LINE__) Explosion(type, pos, vel, exp_scale, part_scale, rgn, source);
+
+ if (rgn)
+ rgn->InsertObject(exp);
+
+ else if (active_region)
+ active_region->InsertObject(exp);
+
+ return exp;
+}
+
+// +--------------------------------------------------------------------+
+
+Debris*
+Sim::CreateDebris(const Point& pos, const Point& vel, Model* model, double mass, SimRegion* rgn)
+{
+ Debris* debris = new(__FILE__,__LINE__) Debris(model, pos, vel, mass);
+
+ if (rgn)
+ rgn->InsertObject(debris);
+
+ else if (active_region)
+ active_region->InsertObject(debris);
+
+ return debris;
+}
+
+// +--------------------------------------------------------------------+
+
+Asteroid*
+Sim::CreateAsteroid(const Point& pos, int t, double mass, SimRegion* rgn)
+{
+ Asteroid* asteroid = new(__FILE__,__LINE__) Asteroid(t, pos, mass);
+
+ if (rgn)
+ rgn->InsertObject(asteroid);
+
+ else if (active_region)
+ active_region->InsertObject(asteroid);
+
+ return asteroid;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sim::CreateSplashDamage(Ship* ship)
+{
+ if (ship && ship->GetRegion() && ship->Design()->splash_radius > 1) {
+ SimSplash* splash = new(__FILE__,__LINE__)
+ SimSplash(ship->GetRegion(),
+ ship->Location(),
+ ship->Design()->integrity / 4,
+ ship->Design()->splash_radius);
+
+ splash->owner_name = ship->Name();
+ splashlist.append(splash);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sim::CreateSplashDamage(Shot* shot)
+{
+ if (shot && shot->GetRegion()) {
+ double damage = shot->Damage();
+ if (damage < shot->Design()->damage)
+ damage = shot->Design()->damage;
+
+ SimSplash* splash = new(__FILE__,__LINE__)
+ SimSplash(shot->GetRegion(),
+ shot->Location(),
+ damage,
+ shot->Design()->lethal_radius);
+
+ if (shot->Owner())
+ splash->owner_name = shot->Owner()->Name();
+
+ splash->missile = shot->IsMissile();
+
+ splashlist.append(splash);
+ CreateExplosion(shot->Location(), Point(), Explosion::SHOT_BLAST, 20.0f, 1.0f, shot->GetRegion());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sim::ShowGrid(int show)
+{
+ Player* player = Player::GetCurrentPlayer();
+
+ if (player && player->GridMode() == 0) {
+ show = 0;
+ grid_shown = false;
+ }
+
+ ListIter<SimRegion> rgn = regions;
+ while (++rgn) {
+ rgn->ShowGrid(show);
+ }
+
+ grid_shown = show?true:false;
+}
+
+bool
+Sim::GridShown() const
+{
+ return grid_shown;
+}
+
+// +--------------------------------------------------------------------+
+
+List<StarSystem>&
+Sim::GetSystemList()
+{
+ if (mission)
+ return mission->GetSystemList();
+
+ static List<StarSystem> dummy_system_list;
+ return dummy_system_list;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sim::NextView()
+{
+ if (active_region)
+ active_region->NextView();
+}
+
+Ship*
+Sim::GetPlayerShip()
+{
+ if (active_region)
+ return active_region->GetPlayerShip();
+
+ Starshatter* stars = Starshatter::GetInstance();
+ if (stars && stars->InCutscene()) {
+ Ship* player = 0;
+
+ ListIter<SimRegion> rgn = regions;
+ while (++rgn && !player) {
+ player = rgn->GetPlayerShip();
+ }
+
+ return player;
+ }
+
+ return 0;
+}
+
+Element*
+Sim::GetPlayerElement()
+{
+ Element* elem = 0;
+
+ for (int i = 0; i < elements.size(); i++) {
+ Element* e = elements[i];
+
+ if (e->Player() > 0)
+ elem = e;
+ }
+
+ return elem;
+}
+
+bool
+Sim::IsSelected(Ship* s)
+{
+ if (active_region)
+ return active_region->IsSelected(s);
+
+ return false;
+}
+
+ListIter<Ship>
+Sim::GetSelection()
+{
+ if (active_region)
+ return active_region->GetSelection();
+
+ static List<Ship> empty;
+ return empty;
+}
+
+void
+Sim::ClearSelection()
+{
+ if (active_region)
+ active_region->ClearSelection();
+}
+
+void
+Sim::AddSelection(Ship* s)
+{
+ if (active_region)
+ active_region->AddSelection(s);
+}
+
+void
+Sim::SetSelection(Ship* newsel)
+{
+ if (active_region)
+ active_region->SetSelection(newsel);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sim::SetTestMode(bool t)
+{
+ test_mode = t;
+ Ship* pship = GetPlayerShip();
+
+ if (pship)
+ if (IsTestMode())
+ pship->SetControls(0);
+ else
+ pship->SetControls(ctrl);
+}
+
+// +--------------------------------------------------------------------+
+
+SimRegion*
+Sim::FindRegion(const char* name)
+{
+ ListIter<SimRegion> rgn = regions;
+ while (++rgn)
+ if (rgn->name == name)
+ return rgn.value();
+
+ return 0;
+}
+
+SimRegion*
+Sim::FindRegion(OrbitalRegion* orgn)
+{
+ ListIter<SimRegion> rgn = regions;
+ while (++rgn)
+ if (rgn->orbital_region == orgn)
+ return rgn.value();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+SimRegion*
+Sim::FindNearestSpaceRegion(SimObject* object)
+{
+ return FindNearestRegion(object, REAL_SPACE);
+}
+
+SimRegion*
+Sim::FindNearestTerrainRegion(SimObject* object)
+{
+ return FindNearestRegion(object, AIR_SPACE);
+}
+
+SimRegion*
+Sim::FindNearestRegion(SimObject* object, int type)
+{
+ if (!object) return 0;
+
+ SimRegion* result = 0;
+ double distance = 1.0e40;
+ Point objloc = object->Location();
+
+ objloc = objloc.OtherHand();
+
+ if (object->GetRegion())
+ objloc += object->GetRegion()->Location();
+
+ ListIter<SimRegion> rgn = regions;
+ while (++rgn) {
+ if (rgn->Type() == type) {
+ OrbitalRegion* orgn = rgn->GetOrbitalRegion();
+ if (orgn) {
+ double test = fabs((orgn->Location() - objloc).length());
+ if (test < distance) {
+ result = rgn.value();
+ distance = test;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+SimRegion*
+Sim::FindNearestSpaceRegion(Orbital* body)
+{
+ SimRegion* result = 0;
+
+ if (!body)
+ return result;
+
+ ListIter<SimRegion> rgn = regions;
+ while (++rgn && !result) {
+ if (rgn->IsOrbital()) {
+ OrbitalRegion* orgn = rgn->GetOrbitalRegion();
+ if (orgn) {
+ ListIter<OrbitalRegion> iter = body->Regions();
+ while (++iter) {
+ if (iter.value() == orgn)
+ result = rgn.value();
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Sim::ActivateRegion(SimRegion* rgn)
+{
+ if (rgn && active_region != rgn && regions.contains(rgn)) {
+ if (active_region)
+ active_region->Deactivate();
+
+ if (!active_region || active_region->System() != rgn->System()) {
+ if (active_region)
+ active_region->System()->Deactivate();
+ rgn->System()->Activate(scene);
+ }
+
+ active_region = rgn;
+ star_system = active_region->System();
+
+ if (star_system) {
+ star_system->SetActiveRegion(active_region->orbital_region);
+ }
+ else {
+ ::Print("WARNING: Sim::ActivateRegion() No star system found for rgn '%s'", rgn->Name());
+ }
+
+ active_region->Activate();
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sim::RequestHyperJump(Ship* obj, SimRegion* rgn, const Point& loc,
+ int type, Ship* fc1, Ship* fc2)
+{
+ bool hyperdrive = false;
+
+ if (obj->GetQuantumDrive() && obj->GetQuantumDrive()->Subtype() == QuantumDrive::HYPER)
+ hyperdrive = true;
+
+ jumplist.append(new(__FILE__,__LINE__) SimHyper(obj, rgn, loc, type, hyperdrive, fc1, fc2));
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sim::ExecFrame(double seconds)
+{
+ if (first_frame) {
+ first_frame = false;
+ netgame = NetGame::Create();
+ }
+
+ if (netgame)
+ netgame->ExecFrame();
+
+ if (regions.isEmpty()) {
+ active_region = 0;
+ rgn_queue.clear();
+ jumplist.destroy();
+ scene.Collect();
+ return;
+ }
+
+ ListIter<Element> elem = elements;
+ while (++elem)
+ if (!elem->IsSquadron())
+ elem->ExecFrame(seconds);
+
+ ListIter<SimRegion> rgn = regions;
+ while (++rgn)
+ if (rgn.value() != active_region && rgn->NumShips() && !rgn_queue.contains(rgn.value()))
+ rgn_queue.append(rgn.value());
+
+ // execframe for one inactive sim region:
+ if (rgn_queue.size()) {
+ SimRegion* exec_rgn = rgn_queue.removeIndex(0);
+
+ while (exec_rgn && (exec_rgn->NumShips() == 0 || exec_rgn == active_region))
+ if (rgn_queue.size())
+ exec_rgn = rgn_queue.removeIndex(0);
+ else
+ exec_rgn = 0;
+
+ if (exec_rgn)
+ exec_rgn->ExecFrame(seconds);
+ }
+
+ if (active_region)
+ active_region->ExecFrame(seconds);
+
+ ExecEvents(seconds);
+ ResolveHyperList();
+ ResolveSplashList();
+
+ // GC all the dead objects:
+ scene.Collect();
+
+ if (!IsTestMode()) {
+ ListIter<Element> e_iter = elements;
+ while (++e_iter) {
+ Element* elem = e_iter.value();
+ if (!elem->IsSquadron() && elem->IsFinished()) {
+ finished.append(e_iter.removeItem());
+ }
+ }
+ }
+
+ // setup music
+ if (!MusicDirector::IsNoMusic()) {
+ Starshatter* stars = Starshatter::GetInstance();
+ if (stars && stars->GetGameMode() == Starshatter::PLAY_MODE) {
+ Ship* player_ship = GetPlayerShip();
+ if (player_ship) {
+ int phase = player_ship->GetFlightPhase();
+
+ if (phase < Ship::ACTIVE) {
+ MusicDirector::SetMode(MusicDirector::LAUNCH);
+ }
+
+ else if (phase > Ship::ACTIVE) {
+ MusicDirector::SetMode(MusicDirector::RECOVERY);
+ }
+
+ else {
+ if (player_ship->IsInCombat()) {
+ MusicDirector::SetMode(MusicDirector::COMBAT);
+ }
+
+ else {
+ MusicDirector::SetMode(MusicDirector::FLIGHT);
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+Sim::ExecEvents(double seconds)
+{
+ ListIter<MissionEvent> iter = events;
+ while (++iter) {
+ MissionEvent* event = iter.value();
+ event->ExecFrame(seconds);
+ }
+}
+
+void
+Sim::ResolveHyperList()
+{
+ // resolve the hyper space transitions:
+ if (jumplist.size()) {
+ Ship* pship = GetPlayerShip();
+
+ ListIter<SimHyper> j_iter = jumplist;
+ while (++j_iter) {
+ SimHyper* jump = j_iter.value();
+ Ship* jumpship = jump->ship;
+
+ if (jumpship) {
+ SimRegion* dest = jump->rgn;
+
+ if (!dest)
+ dest = FindNearestSpaceRegion(jumpship);
+
+ if (dest) {
+ // bring along fighters on deck:
+ ListIter<FlightDeck> deck = jumpship->FlightDecks();
+ while (++deck) {
+ for (int i = 0; i < deck->NumSlots(); i++) {
+ Ship* s = deck->GetShip(i);
+
+ if (s) {
+ dest->InsertObject(s);
+ s->ClearTrack();
+ }
+ }
+ }
+
+ if (jump->type == 0 && !jump->hyperdrive) {
+ // bring along nearby ships:
+ // have to do it in two parts, because inserting the ships
+ // into the destination corrupts the iter over the current
+ // region's list of ships...
+
+ // part one: gather the ships that will be jumping:
+ List<Ship> riders;
+ ListIter<Ship> neighbor = jumpship->GetRegion()->Ships();
+ while (++neighbor) {
+ if (neighbor->IsDropship()) {
+ Ship* s = neighbor.value();
+ if (s == jumpship) continue;
+
+ Point delta = s->Location() - jumpship->Location();
+
+ if (delta.length() < 5e3) {
+ riders.append(s);
+ }
+ }
+ }
+
+ // part two: now transfer the list to the destination:
+ for (int i = 0; i < riders.size(); i++) {
+ Ship* s = riders[i];
+ Point delta = s->Location() - jumpship->Location();
+ dest->InsertObject(s);
+ s->MoveTo(jump->loc.OtherHand() + delta);
+ s->ClearTrack();
+
+ if (jump->fc_dst) {
+ double r = jump->fc_dst->Roll();
+ double p = jump->fc_dst->Pitch();
+ double w = jump->fc_dst->Yaw();
+
+ s->SetAbsoluteOrientation(r, p, w);
+ s->SetVelocity(jump->fc_dst->Heading() * 500);
+ }
+
+ ProcessEventTrigger(MissionEvent::TRIGGER_JUMP, 0, s->Name());
+ }
+ }
+
+ // now it is safe to move the main jump ship:
+ dest->InsertObject(jumpship);
+ jumpship->MoveTo(jump->loc.OtherHand());
+ jumpship->ClearTrack();
+
+ ProcessEventTrigger(MissionEvent::TRIGGER_JUMP, 0, jumpship->Name());
+ NetUtil::SendObjHyper(jumpship, dest->Name(), jump->loc, jump->fc_src, jump->fc_dst, jump->type);
+
+ // if using farcaster:
+ if (jump->fc_src) {
+ ::Print("Ship '%s' farcast to '%s'\n", jumpship->Name(), dest->Name());
+ CreateExplosion(jumpship->Location(), Point(0,0,0), Explosion::QUANTUM_FLASH, 1.0f, 0, dest);
+
+ if (jump->fc_dst) {
+ double r = jump->fc_dst->Roll();
+ double p = jump->fc_dst->Pitch();
+ double w = jump->fc_dst->Yaw();
+
+ jumpship->SetAbsoluteOrientation(r, p, w);
+ jumpship->SetVelocity(jump->fc_dst->Heading() * 500);
+ }
+
+ jumpship->SetHelmHeading(jumpship->CompassHeading());
+ jumpship->SetHelmPitch(0);
+ }
+
+ // break orbit:
+ else if (jump->type == Ship::TRANSITION_DROP_ORBIT) {
+ ::Print("Ship '%s' broke orbit to '%s'\n", jumpship->Name(), dest->Name());
+ jumpship->SetAbsoluteOrientation(0,PI/4,0);
+ jumpship->SetVelocity(jumpship->Heading() * 1.0e3);
+ }
+
+ // make orbit:
+ else if (jump->type == Ship::TRANSITION_MAKE_ORBIT) {
+ ::Print("Ship '%s' achieved orbit '%s'\n", jumpship->Name(), dest->Name());
+ jumpship->LookAt(Point(0,0,0));
+ jumpship->SetVelocity(jumpship->Heading() * 500.0);
+ }
+
+ // hyper jump:
+ else {
+ ::Print("Ship '%s' quantum to '%s'\n", jumpship->Name(), dest->Name());
+
+ if (jump->hyperdrive)
+ CreateExplosion(jumpship->Location(), Point(0,0,0), Explosion::HYPER_FLASH, 1, 1, dest);
+ else
+ CreateExplosion(jumpship->Location(), Point(0,0,0), Explosion::QUANTUM_FLASH, 1, 0, dest);
+
+ jumpship->LookAt(Point(0,0,0));
+ jumpship->SetVelocity(jumpship->Heading() * 500.0);
+ jumpship->SetHelmHeading(jumpship->CompassHeading());
+ jumpship->SetHelmPitch(0);
+ }
+ }
+
+ else if (regions.size() > 1) {
+ ::Print("Warning: Unusual jump request for ship '%s'\n", jumpship->Name());
+ regions[1]->InsertObject(jumpship);
+ }
+
+ Sensor* sensor = jumpship->GetSensor();
+ if (sensor)
+ sensor->ClearAllContacts();
+ }
+ }
+
+ jumplist.destroy();
+
+ if (pship && pship->GetRegion()) {
+ if (active_region != pship->GetRegion()) {
+ pship->GetRegion()->SetPlayerShip(pship);
+ }
+ }
+ }
+}
+
+void
+Sim::ResolveSplashList()
+{
+ if (splashlist.size()) {
+ ListIter<SimSplash> iter = splashlist;
+ while (++iter) {
+ SimSplash* splash = iter.value();
+
+ if (!splash->rgn)
+ continue;
+
+ // damage ships:
+ ListIter<Ship> s_iter = splash->rgn->Ships();
+ while (++s_iter) {
+ Ship* ship = s_iter.value();
+
+ double distance = (ship->Location() - splash->loc).length();
+
+ if (distance > 1 && distance < splash->range) {
+ double damage = splash->damage * (1 - distance/splash->range);
+ if (!NetGame::IsNetGameClient()) {
+ ship->InflictDamage(damage);
+ }
+
+ int ship_destroyed = (!ship->InTransition() && ship->Integrity() < 1.0f);
+
+ // then delete the ship:
+ if (ship_destroyed) {
+ NetUtil::SendObjKill(ship, 0, NetObjKill::KILL_MISC);
+ Print(" %s Killed %s (%s)\n", (const char*) splash->owner_name, ship->Name(), FormatGameTime());
+
+ // record the kill
+ ShipStats* killer = ShipStats::Find(splash->owner_name);
+ if (killer) {
+ if (splash->missile)
+ killer->AddEvent(SimEvent::MISSILE_KILL, ship->Name());
+ else
+ killer->AddEvent(SimEvent::GUNS_KILL, ship->Name());
+ }
+
+ Ship* owner = FindShip(splash->owner_name, splash->rgn->Name());
+ if (owner && owner->GetIFF() != ship->GetIFF()) {
+ if (ship->GetIFF() > 0 || owner->GetIFF() > 1) {
+ killer->AddPoints(ship->Value());
+
+ Element* elem = owner->GetElement();
+ if (elem) {
+ if (owner->GetElementIndex() > 1) {
+ Ship* s = elem->GetShip(1);
+
+ if (s) {
+ ShipStats* cmdr_stats = ShipStats::Find(s->Name());
+ if (cmdr_stats) {
+ cmdr_stats->AddCommandPoints(ship->Value()/2);
+ }
+ }
+ }
+
+ Element* cmdr = elem->GetCommander();
+ if (cmdr) {
+ Ship* s = cmdr->GetShip(1);
+
+ if (s) {
+ ShipStats* cmdr_stats = ShipStats::Find(s->Name());
+ if (cmdr_stats) {
+ cmdr_stats->AddCommandPoints(ship->Value()/2);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ShipStats* killee = ShipStats::Find(ship->Name());
+ if (killee)
+ killee->AddEvent(SimEvent::DESTROYED, splash->owner_name);
+
+ ship->DeathSpiral();
+ }
+ }
+ }
+
+ // damage drones:
+ ListIter<Drone> drone_iter = splash->rgn->Drones();
+ while (++drone_iter) {
+ Drone* drone = drone_iter.value();
+
+ double distance = (drone->Location() - splash->loc).length();
+
+ if (distance > 1 && distance < splash->range) {
+ double damage = splash->damage * (1 - distance/splash->range);
+ drone->InflictDamage(damage);
+
+ int destroyed = (drone->Integrity() < 1.0f);
+
+ // then mark the drone for deletion:
+ if (destroyed) {
+ NetUtil::SendWepDestroy(drone);
+ sim->CreateExplosion(drone->Location(), drone->Velocity(), 21 /* was LARGE_EXP */, 1.0f, 1.0f, splash->rgn);
+ drone->SetLife(0);
+ }
+ }
+ }
+ }
+
+ splashlist.destroy();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sim::ProcessEventTrigger(int type, int event_id, const char* ship, int param)
+{
+ Text ship_name = ship;
+
+ ListIter<MissionEvent> iter = events;
+ while (++iter) {
+ MissionEvent* event = iter.value();
+
+ if (event->IsPending() && event->Trigger() == type) {
+ switch (type) {
+ case MissionEvent::TRIGGER_DAMAGE:
+ case MissionEvent::TRIGGER_DESTROYED:
+ case MissionEvent::TRIGGER_JUMP:
+ case MissionEvent::TRIGGER_LAUNCH:
+ case MissionEvent::TRIGGER_DOCK:
+ case MissionEvent::TRIGGER_TARGET:
+ if (event->TriggerParam() <= param) {
+ if (ship_name.indexOf(event->TriggerShip()) == 0)
+ event->Activate();
+ }
+ break;
+
+ case MissionEvent::TRIGGER_NAVPT:
+ if (event->TriggerParam() == param) {
+ if (ship_name.indexOf(event->TriggerShip()) == 0)
+ event->Activate();
+ }
+ break;
+
+ case MissionEvent::TRIGGER_EVENT:
+ case MissionEvent::TRIGGER_SKIPPED:
+ if (event->TriggerParam() == event_id)
+ event->Activate();
+ break;
+ }
+ }
+ }
+}
+
+double
+Sim::MissionClock() const
+{
+ return (Game::GameTime() - start_time) / 1000.0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sim::SkipCutscene()
+{
+ Starshatter* stars = Starshatter::GetInstance();
+ if (stars && stars->InCutscene()) {
+ ListIter<MissionEvent> iter = events;
+ bool end = false;
+ double end_time = 0;
+
+ while (++iter && !end) {
+ MissionEvent* event = iter.value();
+
+ if (event->IsPending() || event->IsActive()) {
+ if (event->Event() == MissionEvent::END_SCENE ||
+ event->Event() == MissionEvent::END_MISSION) {
+ end = true;
+ end_time = event->Time();
+ }
+
+ if (event->Event() == MissionEvent::FIRE_WEAPON) {
+ event->Skip();
+ }
+
+ else {
+ event->Activate();
+ event->Execute(true);
+ }
+ }
+ }
+
+ double skip_time = end_time - MissionClock();
+ if (skip_time > 0) {
+ Game::SkipGameTime(skip_time);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sim::ResolveTimeSkip(double seconds)
+{
+ double skipped = 0;
+
+ // allow elements to process hold time, and release as needed:
+ ListIter<Element> elem = elements;
+ while (++elem)
+ elem->ExecFrame(seconds);
+
+ // step through the skip, ten seconds at a time:
+ if (active_region) {
+ double total_skip = seconds;
+ double frame_skip = 10;
+ Ship* player = GetPlayerShip();
+
+ while (total_skip > frame_skip) {
+ if (active_region->CanTimeSkip()) {
+ active_region->ResolveTimeSkip(frame_skip);
+ total_skip -= frame_skip;
+ skipped += frame_skip;
+ }
+ // break out early if player runs into bad guys...
+ else {
+ total_skip = 0;
+ }
+ }
+
+ if (total_skip > 0)
+ active_region->ResolveTimeSkip(total_skip);
+ skipped += total_skip;
+ }
+
+ // give player control after time skip:
+ Ship* player_ship = GetPlayerShip();
+ if (player_ship) {
+ player_ship->SetAutoNav(false);
+ player_ship->SetThrottle(75);
+
+ HUDView* hud = HUDView::GetInstance();
+ if (hud)
+ hud->SetHUDMode(HUDView::HUD_MODE_TAC);
+
+ if (IsTestMode())
+ player_ship->SetControls(0);
+ }
+
+ Game::SkipGameTime(skipped);
+ CameraDirector::SetCameraMode(CameraDirector::MODE_COCKPIT);
+}
+
+// +--------------------------------------------------------------------+
+
+ListIter<MissionElement>
+Sim::GetMissionElements()
+{
+ mission_elements.destroy();
+
+ ListIter<Element> iter = elements;
+ while (++iter) {
+ Element* elem = iter.value();
+
+ int num_live_ships = 0;
+
+ for (int i = 0; i < elem->NumShips(); i++) {
+ Ship* s = elem->GetShip(i+1);
+
+ if (s && !s->IsDying() && !s->IsDead())
+ num_live_ships++;
+ }
+
+ if (elem->IsSquadron() || num_live_ships > 0) {
+ MissionElement* msn_elem = CreateMissionElement(elem);
+
+ if (msn_elem)
+ mission_elements.append(msn_elem);
+ }
+ }
+
+ return mission_elements;
+}
+
+MissionElement*
+Sim::CreateMissionElement(Element* elem)
+{
+ MissionElement* msn_elem = 0;
+
+ if (elem->IsSquadron()) {
+ if (!elem->GetCarrier() || elem->GetCarrier()->Integrity() < 1)
+ return msn_elem;
+ }
+
+ if (elem && !elem->IsNetObserver()) {
+ msn_elem = new(__FILE__,__LINE__) MissionElement;
+
+ msn_elem->SetName(elem->Name());
+ msn_elem->SetIFF(elem->GetIFF());
+ msn_elem->SetMissionRole(elem->Type());
+
+ if (elem->IsSquadron() && elem->GetCarrier()) {
+ Ship* carrier = elem->GetCarrier();
+
+ msn_elem->SetCarrier(carrier->Name());
+ msn_elem->SetCount(elem->GetCount());
+ msn_elem->SetLocation(carrier->Location().OtherHand());
+
+ if (carrier->GetRegion())
+ msn_elem->SetRegion(carrier->GetRegion()->Name());
+
+ int squadron_index = 0;
+ Hangar* hangar = FindSquadron(elem->Name(), squadron_index);
+
+ if (hangar) {
+ msn_elem->SetDeadCount(hangar->NumShipsDead(squadron_index));
+ msn_elem->SetMaintCount(hangar->NumShipsMaint(squadron_index));
+
+ const ShipDesign* design = hangar->SquadronDesign(squadron_index);
+ msn_elem->SetDesign(design);
+
+ Text design_path = design->path_name;
+ design_path.setSensitive(false);
+
+ if (design_path.indexOf("/Mods/Ships") == 0) {
+ design_path = design_path.substring(11, 1000);
+ msn_elem->SetPath(design_path);
+ }
+ }
+ }
+
+ else {
+ msn_elem->SetSquadron(elem->GetSquadron());
+ msn_elem->SetCount(elem->NumShips());
+ }
+
+ if (elem->GetCommander())
+ msn_elem->SetCommander(elem->GetCommander()->Name());
+
+ msn_elem->SetCombatGroup(elem->GetCombatGroup());
+ msn_elem->SetCombatUnit(elem->GetCombatUnit());
+
+ Ship* ship = elem->GetShip(1);
+ if (ship) {
+ if (ship->GetRegion())
+ msn_elem->SetRegion(ship->GetRegion()->Name());
+
+ msn_elem->SetLocation(ship->Location().OtherHand());
+ msn_elem->SetDesign(ship->Design());
+
+ msn_elem->SetPlayer(elem->Player());
+ msn_elem->SetCommandAI(elem->GetCommandAILevel());
+ msn_elem->SetHoldTime((int) elem->GetHoldTime());
+ msn_elem->SetZoneLock(elem->GetZoneLock());
+ msn_elem->SetHeading(ship->CompassHeading());
+
+ msn_elem->SetPlayable(elem->IsPlayable());
+ msn_elem->SetRogue(elem->IsRogue());
+ msn_elem->SetIntelLevel(elem->IntelLevel());
+
+ Text design_path = ship->Design()->path_name;
+ design_path.setSensitive(false);
+
+ if (design_path.indexOf("/Mods/Ships") == 0) {
+ design_path = design_path.substring(11, 1000);
+ msn_elem->SetPath(design_path);
+ }
+
+ msn_elem->SetRespawnCount(ship->RespawnCount());
+ }
+
+ MissionLoad* loadout = new(__FILE__,__LINE__) MissionLoad;
+ CopyMemory(loadout->GetStations(), elem->Loadout(), 16 * sizeof(int));
+
+ msn_elem->Loadouts().append(loadout);
+
+ int num_obj = elem->NumObjectives();
+ for (int i = 0; i < num_obj; i++) {
+ Instruction* o = elem->GetObjective(i);
+ Instruction* instr = 0;
+
+ instr = new(__FILE__,__LINE__) Instruction(*o);
+
+ msn_elem->AddObjective(instr);
+ }
+
+ int num_inst = elem->NumInstructions();
+ for (i = 0; i < num_inst; i++) {
+ Text instr = elem->GetInstruction(i);
+ msn_elem->AddInstruction(instr);
+ }
+
+ ListIter<Instruction> nav_iter = elem->GetFlightPlan();
+ while (++nav_iter) {
+ Instruction* nav = nav_iter.value();
+ Instruction* npt = new(__FILE__,__LINE__)
+ Instruction(nav->RegionName(), nav->Location(), nav->Action());
+
+ npt->SetFormation(nav->Formation());
+ npt->SetSpeed(nav->Speed());
+ npt->SetTarget(nav->TargetName());
+ npt->SetHoldTime(nav->HoldTime());
+ npt->SetFarcast(nav->Farcast());
+ npt->SetStatus(nav->Status());
+
+ msn_elem->AddNavPoint(npt);
+ }
+
+ for (i = 0; i < elem->NumShips(); i++) {
+ ship = elem->GetShip(i+1);
+
+ if (ship) {
+ MissionShip* s = new(__FILE__,__LINE__) MissionShip;
+
+ s->SetName(ship->Name());
+ s->SetRegNum(ship->Registry());
+ s->SetRegion(ship->GetRegion()->Name());
+ s->SetLocation(ship->Location().OtherHand());
+ s->SetVelocity(ship->Velocity().OtherHand());
+
+ s->SetRespawns(ship->RespawnCount());
+ s->SetHeading(ship->CompassHeading());
+ s->SetIntegrity(ship->Integrity());
+
+ if (ship->GetDecoy())
+ s->SetDecoys(ship->GetDecoy()->Ammo());
+
+ if (ship->GetProbeLauncher())
+ s->SetProbes(ship->GetProbeLauncher()->Ammo());
+
+ int n;
+ int ammo[16];
+ int fuel[4];
+
+ for (n = 0; n < 16; n++) {
+ Weapon* w = ship->GetWeaponByIndex(n+1);
+
+ if (w)
+ ammo[n] = w->Ammo();
+ else
+ ammo[n] = -10;
+ }
+
+ for (n = 0; n < 4; n++) {
+ if (ship->Reactors().size() > n)
+ fuel[n] = ship->Reactors()[n]->Charge();
+ else
+ fuel[n] = -10;
+ }
+
+ s->SetAmmo(ammo);
+ s->SetFuel(fuel);
+
+ msn_elem->Ships().append(s);
+ }
+ }
+ }
+
+ return msn_elem;
+}
+
+Hangar*
+Sim::FindSquadron(const char* name, int& index)
+{
+ Hangar* hangar = 0;
+
+ ListIter<SimRegion> iter = regions;
+ while (++iter && !hangar) {
+ SimRegion* rgn = iter.value();
+
+ ListIter<Ship> s_iter = rgn->Carriers();
+ while (++s_iter && !hangar) {
+ Ship* carrier = s_iter.value();
+ Hangar* h = carrier->GetHangar();
+
+ for (int i = 0; i < h->NumSquadrons() && !hangar; i++) {
+ if (h->SquadronName(i) == name) {
+ hangar = h;
+ index = i;
+ }
+ }
+ }
+ }
+
+ return hangar;
+}
+
+// +===================================================================-+
+
+SimRegion::SimRegion(Sim* s, const char* n, int t)
+ : sim(s), name(n), type(t), orbital_region(0), star_system(0)
+ , player_ship(0), grid(0), active(false), current_view(0), sim_time(0)
+ , ai_index(0), terrain(0)
+{
+ if (sim) {
+ star_system = sim->GetStarSystem();
+ }
+}
+
+SimRegion::SimRegion(Sim* s, OrbitalRegion* r)
+ : sim(s), orbital_region(r), type(REAL_SPACE), star_system(0)
+ , player_ship(0), grid(0), active(false), current_view(0), sim_time(0)
+ , ai_index(0), terrain(0)
+{
+ if (r) {
+ star_system = r->System();
+ }
+
+ if (orbital_region) {
+ name = orbital_region->Name();
+ grid = new(__FILE__,__LINE__) Grid((int) orbital_region->Radius(),
+ (int) orbital_region->GridSpace());
+
+
+ if (orbital_region->Type() == Orbital::TERRAIN) {
+ TerrainRegion* trgn = (TerrainRegion*) orbital_region;
+ terrain = new(__FILE__,__LINE__) Terrain(trgn);
+
+ type = AIR_SPACE;
+ }
+
+ else if (orbital_region->Asteroids() > 0) {
+ int asteroids = orbital_region->Asteroids();
+
+ for (int i = 0; i < asteroids; i++) {
+ Point init_loc((rand()-16384.0f) * 30,
+ (rand()-16384.0f) * 3,
+ (rand()-16384.0f) * 30);
+ sim->CreateAsteroid(init_loc, i, Random(1e7, 1e8), this);
+ }
+ }
+ }
+ else {
+ name = Game::GetText("Unknown");
+ }
+}
+
+SimRegion::~SimRegion()
+{
+ GRAPHIC_DESTROY(grid);
+ delete terrain;
+ explosions.destroy();
+ shots.destroy();
+ ships.destroy();
+ debris.destroy();
+ asteroids.destroy();
+ dead_ships.destroy();
+
+ for (int i = 0; i < 5; i++)
+ track_database[i].destroy();
+}
+
+int
+SimRegion::operator < (const SimRegion& r) const
+{
+ return (orbital_region && r.orbital_region && *orbital_region < *r.orbital_region);
+}
+
+int
+SimRegion::operator <= (const SimRegion& r) const
+{
+ return (orbital_region && r.orbital_region && *orbital_region <= *r.orbital_region);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::SetPlayerShip(Ship* ship)
+{
+ // there can only be a player ship when playing the game locally
+ if (Starshatter::GetInstance()) {
+ int player_index = ships.index(ship);
+
+ if (player_index >= 0) {
+ if (sim->GetActiveRegion() != this)
+ sim->ActivateRegion(this);
+
+ AttachPlayerShip(player_index);
+ }
+
+ else {
+ Print("SimRegion %s could not set player ship '%s' - not in region\n",
+ name.data(), ship ? ship->Name() : "(null)");
+ }
+ }
+
+ // if this is a stand-alone server, set player ship to null
+ else {
+ if (player_ship)
+ player_ship->SetControls(0);
+
+ current_view = -1;
+ player_ship = 0;
+ }
+}
+
+void
+SimRegion::AttachPlayerShip(int index)
+{
+ if (player_ship)
+ player_ship->SetControls(0);
+
+ current_view = index;
+ player_ship = ships[current_view];
+
+ CameraDirector* cam_dir = CameraDirector::GetInstance();
+ if (cam_dir)
+ cam_dir->SetShip(player_ship);
+
+ if (sim->dust)
+ sim->dust->Reset(player_ship->Location());
+
+ if (!sim->IsTestMode())
+ player_ship->SetControls(sim->ctrl);
+
+ MouseController* mouse_con = MouseController::GetInstance();
+ if (mouse_con)
+ mouse_con->SetActive(false);
+}
+
+void
+SimRegion::NextView()
+{
+ if (ships.size()) {
+ int original_view = current_view;
+
+ do {
+ current_view++;
+ if (current_view >= ships.size()) {
+ current_view = 0;
+ }
+ }
+ while (ships[current_view]->Life() == 0 && current_view != original_view);
+
+ if (current_view != original_view) {
+ ClearSelection();
+
+ if (!sim->IsTestMode())
+ player_ship->SetControls(0);
+
+ if (player_ship->Rep())
+ player_ship->Rep()->Show();
+
+ AttachPlayerShip(current_view);
+ }
+ }
+}
+
+bool
+SimRegion::IsSelected(Ship* s)
+{
+ return selection.contains(s);
+}
+
+ListIter<Ship>
+SimRegion::GetSelection()
+{
+ return selection;
+}
+
+void
+SimRegion::SetSelection(Ship* newsel)
+{
+ selection.clear();
+ selection.append(newsel);
+}
+
+void
+SimRegion::ClearSelection()
+{
+ selection.clear();
+}
+
+void
+SimRegion::AddSelection(Ship* newsel)
+{
+ if (!newsel ||
+ newsel->GetFlightPhase() < Ship::ACTIVE ||
+ newsel->GetFlightPhase() >= Ship::RECOVERY)
+ return;
+
+ if (!selection.contains(newsel))
+ selection.append(newsel);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::Activate()
+{
+ if (!sim) return;
+
+ ListIter<Ship> ship = ships;
+ while (++ship)
+ ship->Activate(sim->scene);
+
+ ListIter<Shot> shot = shots;
+ while (++shot)
+ shot->Activate(sim->scene);
+
+ ListIter<Explosion> exp = explosions;
+ while (++exp)
+ exp->Activate(sim->scene);
+
+ ListIter<Debris> deb = debris;
+ while (++deb)
+ deb->Activate(sim->scene);
+
+ ListIter<Asteroid> a = asteroids;
+ while (++a)
+ a->Activate(sim->scene);
+
+ if (grid)
+ sim->scene.AddGraphic(grid);
+
+ if (terrain)
+ terrain->Activate(sim->scene);
+
+ player_ship = 0;
+ active = true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::Deactivate()
+{
+ if (!sim) return;
+
+ ListIter<Ship> ship = ships;
+ while (++ship)
+ ship->Deactivate(sim->scene);
+
+ ListIter<Shot> shot = shots;
+ while (++shot)
+ shot->Deactivate(sim->scene);
+
+ ListIter<Explosion> exp = explosions;
+ while (++exp)
+ exp->Deactivate(sim->scene);
+
+ ListIter<Debris> deb = debris;
+ while (++deb)
+ deb->Deactivate(sim->scene);
+
+ ListIter<Asteroid> a = asteroids;
+ while (++a)
+ a->Deactivate(sim->scene);
+
+ if (grid)
+ sim->scene.DelGraphic(grid);
+
+ if (terrain)
+ terrain->Deactivate(sim->scene);
+
+ player_ship = 0;
+ active = false;
+
+ for (int i = 0; i < 5; i++)
+ track_database[i].destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::ExecFrame(double secs)
+{
+ if (!sim) return;
+
+ double seconds = secs;
+
+ // DON'T REALLY KNOW WHAT PURPOSE THIS SERVES....
+ if (!active) {
+ double max_frame = 3 * Game::GetMaxFrameLength();
+ long new_time = Game::GameTime();
+ double delta = new_time - sim_time;
+ seconds = delta / 1000.0;
+
+ if (seconds > max_frame)
+ seconds = max_frame;
+ }
+
+ sim_time = Game::GameTime();
+
+ if (orbital_region)
+ location = orbital_region->Location();
+
+ CameraDirector* cam_dir = CameraDirector::GetInstance();
+
+ Point ref;
+
+ if (active && cam_dir) {
+ ref = cam_dir->GetCamera()->Pos();
+ UpdateSky(seconds, ref);
+ }
+
+ if (terrain)
+ terrain->ExecFrame(seconds);
+
+ UpdateTracks(seconds);
+ UpdateShips(seconds);
+ UpdateShots(seconds);
+ UpdateExplosions(seconds);
+
+ if (!Game::Paused()) {
+ DamageShips();
+ DockShips();
+
+ if (active) {
+ CollideShips();
+ CrashShips();
+ }
+
+ DestroyShips();
+ }
+
+ if (active && cam_dir && player_ship) {
+ Sound::SetListener(*(cam_dir->GetCamera()), player_ship->Velocity());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::ShowGrid(int show)
+{
+ if (grid) {
+ if (show)
+ grid->Show();
+ else
+ grid->Hide();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::UpdateSky(double seconds, const Point& ref)
+{
+ Dust* dust = sim->dust;
+
+ if (dust) {
+ if (orbital_region && orbital_region->Type() == Orbital::TERRAIN) {
+ dust->Hide();
+ }
+ else {
+ dust->Show();
+
+ dust->ExecFrame(seconds, ref);
+
+ if (player_ship && dust->Hidden()) {
+ dust->Reset(player_ship->Location());
+ dust->Show();
+ }
+ }
+ }
+
+ ListIter<Asteroid> a = asteroids;
+ while (++a) {
+ a->ExecFrame(seconds);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::UpdateShips(double seconds)
+{
+ int ship_index = 0;
+ if (ai_index > ships.size())
+ ai_index = 0;
+
+ ListIter<Ship> ship_iter = ships;
+ Ship* ship = 0;
+
+ while (++ship_iter) {
+ ship = ship_iter.value();
+
+ if (ship_index == ai_index || ship == player_ship)
+ ship->SetAIMode(2);
+ else
+ ship->SetAIMode(1);
+
+ ship->ExecFrame(seconds);
+ ship_index++;
+ }
+
+ ai_index++;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::UpdateShots(double seconds)
+{
+ ListIter<Shot> shot_iter = shots;
+ while (++shot_iter) {
+ Shot* shot = shot_iter.value();
+ shot->ExecFrame(seconds);
+
+ if (shot->Design()->flak) {
+ SeekerAI* seeker = (SeekerAI*) shot->GetDirector();
+
+ if (shot->Life() < 0.02 || seeker && seeker->Overshot()) {
+ shot->SetFuse(0.001); // set lifetime to ~zero
+ sim->CreateSplashDamage(shot);
+ }
+ }
+
+ if (shot->Life() < 0.01) { // died of old age
+ NetUtil::SendWepDestroy(shot);
+
+ if (shot->IsDrone())
+ drones.remove((Drone*) shot);
+
+ shot_iter.removeItem();
+ delete shot;
+ shot = 0;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::UpdateExplosions(double seconds)
+{
+ ListIter<Explosion> exp_iter = explosions;
+ while (++exp_iter) {
+ Explosion* exp = exp_iter.value();
+ exp->ExecFrame(seconds);
+
+ if (exp->Life() < 0.01) { // died of old age
+ exp_iter.removeItem();
+ delete exp;
+ }
+ }
+
+ ListIter<Debris> debris_iter = debris;
+ while (++debris_iter) {
+ Debris* d = debris_iter.value();
+ d->ExecFrame(seconds);
+
+ if (d->Life() < 0.01) { // died of old age
+ debris_iter.removeItem();
+ delete d;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+// Check for collisions between ships and shots, and apply damage.
+// Also look for damage to drones and debris.
+
+void
+SimRegion::DamageShips()
+{
+ if (ships.size() == 0 || shots.size() == 0)
+ return;
+
+ Point impact;
+
+ // FOR EACH SHOT IN THE REGION:
+ ListIter<Shot> shot_iter = shots;
+ while (++shot_iter) {
+ Shot* shot = shot_iter.value();
+ const Ship* owner = shot->Owner();
+ const char* owner_name;
+
+ if (owner)
+ owner_name = owner->Name();
+ else
+ owner_name = "[KIA]";
+
+ // CHECK FOR COLLISION WITH A SHIP:
+ ListIter<Ship> ship_iter = ships;
+ while (shot && ++ship_iter) {
+ Ship* ship = ship_iter.value();
+ int hit = ship->HitBy(shot, impact);
+
+ if (hit) {
+ // recon imager:
+ if (shot->Damage() < 0) {
+ ShipStats* shooter = ShipStats::Find(owner_name);
+ if (shooter) {
+ shooter->AddEvent(SimEvent::SCAN_TARGET, ship->Name());
+ }
+ }
+
+ // live round:
+ else if (shot->Damage() > 0) {
+ int ship_destroyed = (!ship->InTransition() && ship->Integrity() < 1.0f);
+
+ // then delete the ship:
+ if (ship_destroyed) {
+ NetUtil::SendObjKill(ship, owner, shot->IsMissile() ? NetObjKill::KILL_SECONDARY : NetObjKill::KILL_PRIMARY);
+
+ Print(" %s Killed %s (%s)\n", owner_name, ship->Name(), FormatGameTime());
+
+ // alert the killer
+ Director* director = owner->GetDirector();
+ if (director && director->Type() > SteerAI::SEEKER && director->Type() < SteerAI::GROUND) {
+ ShipAI* shipAI = (ShipAI*) director;
+ shipAI->Splash(ship);
+ }
+
+ // record the kill
+ ShipStats* killer = ShipStats::Find(owner_name);
+ if (killer) {
+ if (shot->IsMissile())
+ killer->AddEvent(SimEvent::MISSILE_KILL, ship->Name());
+ else
+ killer->AddEvent(SimEvent::GUNS_KILL, ship->Name());
+ }
+
+ if (owner && owner->GetIFF() != ship->GetIFF()) {
+ if (ship->GetIFF() > 0 || owner->GetIFF() > 1) {
+ killer->AddPoints(ship->Value());
+
+ Element* elem = owner->GetElement();
+ if (elem) {
+ if (owner->GetElementIndex() > 1) {
+ Ship* s = elem->GetShip(1);
+
+ if (s) {
+ ShipStats* cmdr_stats = ShipStats::Find(s->Name());
+ if (cmdr_stats) {
+ cmdr_stats->AddCommandPoints(ship->Value()/2);
+ }
+ }
+ }
+
+ Element* cmdr = elem->GetCommander();
+ if (cmdr) {
+ Ship* s = cmdr->GetShip(1);
+
+ if (s) {
+ ShipStats* cmdr_stats = ShipStats::Find(s->Name());
+ if (cmdr_stats) {
+ cmdr_stats->AddCommandPoints(ship->Value()/2);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ShipStats* killee = ShipStats::Find(ship->Name());
+ if (killee)
+ killee->AddEvent(SimEvent::DESTROYED, owner_name);
+
+ ship->DeathSpiral();
+ }
+ }
+
+ // finally, consume the shot:
+ if (!shot->IsBeam()) {
+ if (owner) {
+ ShipStats* stats = ShipStats::Find(owner_name);
+ if (shot->Design()->primary)
+ stats->AddGunHit();
+ else if (shot->Damage() > 0)
+ stats->AddMissileHit();
+ }
+
+ NetUtil::SendWepDestroy(shot);
+
+ if (shot->IsDrone())
+ drones.remove((Drone*) shot);
+
+ shot_iter.removeItem();
+ delete shot;
+ shot = 0;
+ }
+ else if (!shot->HitTarget()) {
+ shot->SetHitTarget(true);
+
+ if (owner) {
+ ShipStats* stats = ShipStats::Find(owner_name);
+ if (shot->Design()->primary)
+ stats->AddGunHit();
+ }
+ }
+ }
+ }
+
+ // CHECK FOR COLLISION WITH A DRONE:
+ if (shot && shot->Design()->target_type & Ship::DRONE) {
+ ListIter<Drone> drone_iter = drones;
+ while (shot && ++drone_iter) {
+ Drone* d = drone_iter.value();
+
+ if (d == shot || d->Owner() == owner)
+ continue;
+
+ int hit = d->HitBy(shot, impact);
+ if (hit) {
+ int destroyed = (d->Integrity() < 1.0f);
+
+ // then mark the drone for deletion:
+ if (destroyed) {
+ NetUtil::SendWepDestroy(d);
+ sim->CreateExplosion(d->Location(), d->Velocity(), 21, 1.0f, 1.0f, this);
+ d->SetLife(0);
+ }
+
+ // finally, consume the shot:
+ if (!shot->IsBeam()) {
+ if (owner) {
+ ShipStats* stats = ShipStats::Find(owner_name);
+ if (shot->Design()->primary)
+ stats->AddGunHit();
+ else
+ stats->AddMissileHit();
+ }
+
+ NetUtil::SendWepDestroy(shot);
+
+ if (shot->IsDrone())
+ drones.remove((Drone*) shot);
+
+ shot_iter.removeItem();
+ delete shot;
+ shot = 0;
+ }
+ }
+ }
+ }
+
+ // CHECK FOR COLLISION WITH DEBRIS:
+ ListIter<Debris> debris_iter = debris;
+ while (shot && ++debris_iter) {
+ Debris* d = debris_iter.value();
+
+ if (d->Radius() < 50)
+ continue;
+
+ int hit = d->HitBy(shot, impact);
+ if (hit) {
+ int destroyed = (d->Integrity() < 1.0f);
+
+ // then delete the debris:
+ if (destroyed) {
+ sim->CreateExplosion(d->Location(), d->Velocity(), Explosion::LARGE_EXPLOSION, 1.0f, 1.0f, this);
+ debris_iter.removeItem();
+ delete d;
+ }
+
+ // finally, consume the shot:
+ if (!shot->IsBeam()) {
+ NetUtil::SendWepDestroy(shot);
+ if (shot->IsDrone())
+ drones.remove((Drone*) shot);
+
+ shot_iter.removeItem();
+ delete shot;
+ shot = 0;
+ }
+ }
+ }
+
+ // CHECK FOR COLLISION WITH ASTEROIDS:
+ ListIter<Asteroid> a_iter = asteroids;
+ while (shot && ++a_iter) {
+ Asteroid* a = a_iter.value();
+
+ int hit = a->HitBy(shot, impact);
+ if (hit) {
+ if (!shot->IsBeam()) {
+ if (shot->IsDrone())
+ drones.remove((Drone*) shot);
+
+ shot_iter.removeItem();
+ delete shot;
+ shot = 0;
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::CollideShips()
+{
+ if (ships.size() < 2 && debris.size() < 1)
+ return;
+
+ List<Ship> kill_list;
+
+ int s_index = 0;
+
+ ListIter<Ship> ship_iter = ships;
+ while (++ship_iter) {
+ Ship* ship = ship_iter.value();
+
+ if (ship->InTransition() ||
+ ship->GetFlightPhase() < Ship::ACTIVE ||
+ ship->MissionClock() < 10000 ||
+ ship->IsNetObserver())
+ continue;
+
+ int t_index = 0;
+ ListIter<Ship> targ_iter = ships;
+ while (++targ_iter) {
+ Ship* targ = targ_iter.value();
+
+ if (t_index++ <= s_index) continue;
+
+ if (targ == ship) continue;
+
+ if (targ->InTransition() ||
+ targ->GetFlightPhase() < Ship::ACTIVE ||
+ targ->MissionClock() < 10000 ||
+ targ->IsNetObserver())
+ continue;
+
+ // ignore AI fighter collisions:
+ if (ship->IsDropship() &&
+ ship != player_ship &&
+ targ->IsDropship() &&
+ targ != player_ship)
+ continue;
+
+ // don't collide with own runway!
+ if (ship->IsAirborne() && ship->GetCarrier() == targ)
+ continue;
+ if (targ->IsAirborne() && targ->GetCarrier() == ship)
+ continue;
+
+ // impact:
+ if (ship->CollidesWith(*targ)) {
+ Vec3 tv1 = targ->Velocity();
+ Vec3 sv1 = ship->Velocity();
+
+ Physical::SemiElasticCollision(*ship, *targ);
+
+ Vec3 tv2 = targ->Velocity();
+ Vec3 sv2 = ship->Velocity();
+
+ double dvs = (sv2-sv1).length();
+ double dvt = (tv2-tv1).length();
+
+ if (dvs > 20) dvs *= dvs;
+ if (dvt > 20) dvt *= dvt;
+
+ if (!NetGame::IsNetGameClient()) {
+ double old_integrity = ship->Integrity();
+ ship->InflictDamage(dvs);
+ double hull_damage = old_integrity - ship->Integrity();
+ NetUtil::SendObjDamage(ship, hull_damage);
+
+ old_integrity = targ->Integrity();
+ targ->InflictDamage(dvt);
+ hull_damage = old_integrity - targ->Integrity();
+ NetUtil::SendObjDamage(targ, hull_damage);
+ }
+
+ // then delete the ship:
+ if (targ->Integrity() < 1.0f) {
+ NetUtil::SendObjKill(targ, ship, NetObjKill::KILL_COLLISION);
+ Print(" ship %s died in collision with %s (%s)\n", targ->Name(), ship->Name(), FormatGameTime());
+ if (!kill_list.contains(targ)) {
+ ShipStats* r = ShipStats::Find(targ->Name());
+ if (r) r->AddEvent(SimEvent::COLLIDE, ship->Name());
+
+ if (targ->GetIFF() > 0 && ship->GetIFF() != targ->GetIFF()) {
+ r = ShipStats::Find(ship->Name());
+ if (r) r->AddPoints(targ->Value());
+ }
+
+ kill_list.insert(targ);
+ }
+ }
+
+ if (ship->Integrity() < 1.0f) {
+ NetUtil::SendObjKill(ship, targ, NetObjKill::KILL_COLLISION);
+ Print(" ship %s died in collision with %s (%s)\n", ship->Name(), targ->Name(), FormatGameTime());
+ if (!kill_list.contains(ship)) {
+ ShipStats* r = ShipStats::Find(ship->Name());
+ if (r) r->AddEvent(SimEvent::COLLIDE, targ->Name());
+
+ if (ship->GetIFF() > 0 && ship->GetIFF() != targ->GetIFF()) {
+ r = ShipStats::Find(targ->Name());
+ if (r) r->AddPoints(ship->Value());
+ }
+
+ kill_list.insert(ship);
+ }
+ }
+ }
+ }
+
+ ListIter<Debris> debris_iter = debris;
+ while (++debris_iter) {
+ Debris* d = debris_iter.value();
+
+ if (d->Radius() < 50)
+ continue;
+
+ if (ship->CollidesWith(*d)) {
+ Vec3 tv1 = d->Velocity();
+ Vec3 sv1 = ship->Velocity();
+
+ Physical::SemiElasticCollision(*ship, *d);
+
+ Vec3 tv2 = d->Velocity();
+ Vec3 sv2 = ship->Velocity();
+
+ if (!NetGame::IsNetGameClient()) {
+ ship->InflictDamage((sv2-sv1).length());
+ }
+
+ d->InflictDamage((tv2-tv1).length());
+
+ // then delete the debris:
+ if (d->Integrity() < 1.0f) {
+ sim->CreateExplosion(d->Location(), d->Velocity(), Explosion::LARGE_EXPLOSION, 1.0f, 1.0f, this);
+ debris_iter.removeItem();
+ delete d;
+ }
+
+ if (ship->Integrity() < 1.0f) {
+ if (!kill_list.contains(ship)) {
+ ShipStats* r = ShipStats::Find(ship->Name());
+ if (r) r->AddEvent(SimEvent::COLLIDE, Game::GetText("DEBRIS"));
+
+ kill_list.insert(ship);
+ }
+ }
+ }
+ }
+
+ ListIter<Asteroid> a_iter = asteroids;
+ while (++a_iter) {
+ Asteroid* a = a_iter.value();
+
+ if (ship->CollidesWith(*a)) {
+ Vec3 sv1 = ship->Velocity();
+ Physical::SemiElasticCollision(*ship, *a);
+ Vec3 sv2 = ship->Velocity();
+
+ if (!NetGame::IsNetGameClient()) {
+ ship->InflictDamage((sv2-sv1).length() * 10);
+ }
+
+ if (ship->Integrity() < 1.0f) {
+ if (!kill_list.contains(ship)) {
+ ShipStats* r = ShipStats::Find(ship->Name());
+ if (r) r->AddEvent(SimEvent::COLLIDE, Game::GetText("ASTEROID"));
+
+ kill_list.insert(ship);
+ }
+ }
+ }
+ }
+
+ s_index++;
+ }
+
+ ListIter<Ship> killed(kill_list);
+ while (++killed) {
+ Ship* kill = killed.value();
+ kill->DeathSpiral();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::CrashShips()
+{
+ if (type != AIR_SPACE || NetGame::IsNetGameClient())
+ return;
+
+ ListIter<Ship> ship_iter = ships;
+ while (++ship_iter) {
+ Ship* ship = ship_iter.value();
+
+ if (!ship->IsGroundUnit() &&
+ !ship->InTransition() &&
+ ship->Class() != Ship::LCA &&
+ ship->AltitudeAGL() < ship->Radius()/2) {
+ if (ship->GetFlightPhase() == Ship::ACTIVE || ship->GetFlightPhase() == Ship::APPROACH) {
+ ship->InflictDamage(1e6);
+
+ if (ship->Integrity() < 1.0f) {
+ Print(" ship destroyed by crash: %s (%s)\n", ship->Name(), FormatGameTime());
+ ShipStats* r = ShipStats::Find(ship->Name());
+ if (r) r->AddEvent(SimEvent::CRASH);
+
+ ship->DeathSpiral();
+ }
+ }
+ }
+ }
+
+ ListIter<Shot> shot_iter = shots;
+ while (++shot_iter) {
+ Shot* shot = shot_iter.value();
+
+ if (shot->IsBeam() || shot->IsDecoy())
+ continue;
+
+ if (shot->AltitudeMSL() < 5e3 &&
+ shot->AltitudeAGL() < 5) {
+
+ // shot hit the ground, destroy it:
+ NetUtil::SendWepDestroy(shot);
+
+ if (shot->IsDrone())
+ drones.remove((Drone*) shot);
+
+ shot_iter.removeItem();
+ delete shot;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::DestroyShips()
+{
+ ListIter<Ship> ship_iter = ships;
+ while (++ship_iter) {
+ Ship* ship = ship_iter.value();
+
+ if (ship->IsDead()) {
+ // must use the iterator to remove the current
+ // item from the container:
+ ship_iter.removeItem();
+ DestroyShip(ship);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::DestroyShip(Ship* ship)
+{
+ if (!ship) return;
+
+ Ship* spawn = 0;
+
+ ships.remove(ship);
+ carriers.remove(ship);
+ selection.remove(ship);
+
+ Text rgn_name;
+ if (ship->GetRegion())
+ rgn_name = ship->GetRegion()->Name();
+
+ bool player_destroyed = (player_ship == ship);
+
+ char ship_name[64];
+ char ship_reg[64];
+ strcpy(ship_name, ship->Name());
+ strcpy(ship_reg, ship->Registry());
+
+ ShipDesign* ship_design = (ShipDesign*) ship->Design();
+ int ship_iff = ship->GetIFF();
+ int cmd_ai = ship->GetCommandAILevel();
+ bool respawn = sim->IsTestMode() && !ship->IsGroundUnit();
+ bool observe = false;
+
+ if (!respawn)
+ respawn = ship->RespawnCount() > 0;
+
+ if (sim->netgame) {
+ if (!respawn)
+ observe = player_destroyed;
+ }
+
+ if (respawn || observe) {
+ if (!sim->netgame || !respawn)
+ ship->SetRespawnLoc(RandomPoint() * 2);
+
+ Point spawn_loc = ship->RespawnLoc();
+
+ if (ship->IsAirborne() && spawn_loc.z < 5e3)
+ spawn_loc.z = Random(8e3, 10e3);
+
+ spawn = sim->CreateShip(ship_name, ship_reg, ship_design, rgn_name, spawn_loc, ship_iff, cmd_ai, observe ? 0 : ship->GetLoadout());
+ spawn->SetRespawnCount(ship->RespawnCount() - 1);
+ spawn->SetNetObserver(observe);
+
+ if (sim->netgame && respawn)
+ sim->netgame->Respawn(ship->GetObjID(), spawn);
+
+ int n = strlen(ship_name);
+ if (ship_name[n-2] == ' ' && isdigit(ship_name[n-1]))
+ ship_name[n-2] = 0;
+
+ Element* elem = sim->FindElement(ship_name);
+ if (elem)
+ elem->AddShip(spawn, ship->GetOrigElementIndex());
+ else
+ Print("Warning: No Element found for '%s' on respawn.\n", ship_name);
+
+ if (player_destroyed)
+ SetPlayerShip(spawn);
+ }
+ else {
+ // close mission, return to menu:
+ if (player_destroyed) {
+ Starshatter* stars = Starshatter::GetInstance();
+ if (stars)
+ stars->SetGameMode(Starshatter::PLAN_MODE);
+ }
+ }
+
+ sim->ProcessEventTrigger(MissionEvent::TRIGGER_DESTROYED, 0, ship->Name());
+
+ dead_ships.insert(ship);
+ ship->Destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::NetDockShip(Ship* ship, Ship* carrier, FlightDeck* deck)
+{
+ if (!ship || !carrier || !deck) return;
+
+ deck->Dock(ship);
+}
+
+// +--------------------------------------------------------------------+
+
+Ship*
+SimRegion::FindShip(const char* ship_name)
+{
+ Ship* ship = 0;
+
+ if (ship_name && *ship_name) {
+ int name_len = strlen(ship_name);
+
+ ListIter<Ship> ship_iter = ships;
+ while (++ship_iter && !ship) {
+ Ship* test = ship_iter.value();
+ if (!strncmp(test->Name(), ship_name, name_len)) {
+ int test_len = strlen(test->Name());
+
+ // The only fuzzy match is for element indices.
+ // The desired name "Alpha" matches "Alpha 1" and "Alpha 2"
+ // but not "Alpha-Centauri"
+
+ if (test_len > name_len && test->Name()[name_len] != ' ')
+ continue;
+
+ ship = test;
+ }
+ }
+ }
+
+ return ship;
+}
+
+Ship*
+SimRegion::FindShipByObjID(DWORD objid)
+{
+ Ship* ship = 0;
+
+ ListIter<Ship> ship_iter = ships;
+ while (++ship_iter && !ship) {
+ Ship* test = ship_iter.value();
+ if (test->GetObjID() == objid)
+ ship = test;
+ }
+
+ return ship;
+}
+
+Shot*
+SimRegion::FindShotByObjID(DWORD objid)
+{
+ Shot* shot = 0;
+
+ ListIter<Shot> shot_iter = shots;
+ while (++shot_iter && !shot) {
+ Shot* test = shot_iter.value();
+ if (test->GetObjID() == objid)
+ shot = test;
+ }
+
+ if (!shot) {
+ ListIter<Drone> drone_iter = drones;
+ while (++drone_iter && !shot) {
+ Drone* test = drone_iter.value();
+ if (test->GetObjID() == objid)
+ shot = test;
+ }
+ }
+
+ return shot;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::DockShips()
+{
+ if (ships.size() == 0)
+ return;
+
+ ListIter<Ship> ship_iter = ships;
+ while (++ship_iter) {
+ Ship* ship = ship_iter.value();
+ int docked = (ship->GetFlightPhase() == Ship::DOCKED);
+
+ if (docked) {
+ sim->ProcessEventTrigger(MissionEvent::TRIGGER_DOCK, 0, ship->Name());
+
+ // who did this ship dock with?
+ Ship* carrier = ship->GetCarrier();
+
+ if (carrier) {
+ ShipStats* s = ShipStats::Find(ship->Name());
+ if (s) {
+ if (ship->IsAirborne())
+ s->AddEvent(SimEvent::LAND, carrier->Name());
+ else
+ s->AddEvent(SimEvent::DOCK, carrier->Name());
+ }
+
+ ShipStats* c = ShipStats::Find(carrier->Name());
+ if (c) c->AddEvent(SimEvent::RECOVER_SHIP, ship->Name());
+ }
+
+ // then delete the ship:
+ int player_docked = (player_ship == ship);
+ char ship_name[33];
+ strcpy(ship_name, ship->Name());
+
+ selection.remove(ship);
+ dead_ships.insert(ship_iter.removeItem());
+ ship->Destroy();
+
+ if (player_docked) {
+ // close mission, return to menu:
+ Starshatter* stars = Starshatter::GetInstance();
+ if (stars)
+ stars->SetGameMode(Starshatter::PLAN_MODE);
+ }
+
+ if (carrier)
+ Print(" %s Docked with %s\n", ship_name, carrier->Name());
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::InsertObject(Ship* ship)
+{
+ if (!ship) return;
+
+ SimRegion* orig = ship->GetRegion();
+
+ if (orig != this) {
+ if (orig != 0) {
+ if (orig->active)
+ ship->Deactivate(sim->scene);
+
+ orig->ships.remove(ship);
+ orig->carriers.remove(ship);
+ orig->selection.remove(ship);
+ }
+
+ ships.append(ship);
+
+ if (ship->NumFlightDecks())
+ carriers.append(ship);
+
+ TranslateObject(ship);
+ ship->SetRegion(this);
+
+ if (active)
+ ship->Activate(sim->scene);
+ }
+}
+
+void
+SimRegion::InsertObject(Shot* shot)
+{
+ if (!shot) return;
+
+ SimRegion* orig = shot->GetRegion();
+
+ if (orig != this) {
+ if (orig != 0)
+ orig->shots.remove(shot);
+
+ shots.append(shot);
+ if (shot->IsDrone())
+ drones.append((Drone*) shot);
+
+ TranslateObject(shot);
+ shot->SetRegion(this);
+
+ if (active)
+ shot->Activate(sim->scene);
+ }
+}
+
+void
+SimRegion::InsertObject(Explosion* exp)
+{
+ if (!exp) return;
+
+ SimRegion* orig = exp->GetRegion();
+
+ if (orig != this) {
+ if (orig != 0)
+ orig->explosions.remove(exp);
+
+ explosions.append(exp);
+ TranslateObject(exp);
+ exp->SetRegion(this);
+
+ if (active)
+ exp->Activate(sim->scene);
+ }
+}
+
+void
+SimRegion::InsertObject(Debris* d)
+{
+ if (!d) return;
+
+ SimRegion* orig = d->GetRegion();
+
+ if (orig != this) {
+ if (orig != 0)
+ orig->debris.remove(d);
+
+ debris.append(d);
+ TranslateObject(d);
+ d->SetRegion(this);
+
+ if (active)
+ d->Activate(sim->scene);
+ }
+}
+
+void
+SimRegion::InsertObject(Asteroid* a)
+{
+ if (!a) return;
+
+ SimRegion* orig = a->GetRegion();
+
+ if (orig != this) {
+ if (orig != 0)
+ orig->asteroids.remove(a);
+
+ asteroids.append(a);
+ TranslateObject(a);
+ a->SetRegion(this);
+
+ if (active)
+ a->Activate(sim->scene);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::TranslateObject(SimObject* object)
+{
+ if (orbital_region)
+ location = orbital_region->Location();
+
+ if (object) {
+ SimRegion* orig = object->GetRegion();
+ if (orig) {
+ Point delta = Location() - orig->Location();
+ delta = delta.OtherHand();
+ object->TranslateBy(delta);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+List<Contact>&
+SimRegion::TrackList(int iff)
+{
+ if (iff >= 0 && iff < 5)
+ return track_database[iff];
+
+ static List<Contact> empty;
+ return empty;
+}
+
+void
+SimRegion::UpdateTracks(double seconds)
+{
+ for (int i = 0; i < 5; i++) {
+ ListIter<Contact> track_iter = track_database[i];
+
+ while (++track_iter) {
+ Contact* t = track_iter.value();
+ Ship* c_ship = t->GetShip();
+ Shot* c_shot = t->GetShot();
+ double c_life = 0;
+
+ if (c_ship) {
+ c_life = c_ship->Life();
+
+ // look for quantum jumps and orbit transitions:
+ if (c_ship->GetRegion() != this || c_ship->IsNetObserver())
+ c_life = 0;
+ }
+
+ else if (c_shot)
+ c_life = c_shot->Life();
+
+ if (t->Age() < 0 || c_life == 0) {
+ track_iter.removeItem();
+ delete t;
+ }
+
+ else {
+ t->Reset();
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SimRegion::CanTimeSkip() const
+{
+ bool ok = false;
+
+ if (player_ship) {
+ ok = true;
+
+ for (int i = 0; ok && i < ships.size(); i++) {
+ Ship* s = ships[i];
+
+ if (s != player_ship && s->GetIFF() && s->GetIFF() != player_ship->GetIFF()) {
+ double dist = Point(s->Location() - player_ship->Location()).length();
+
+ if (s->IsStarship())
+ ok = dist > 60e3;
+ else
+ ok = dist > 30e3;
+ }
+ }
+ }
+
+ return ok;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::ResolveTimeSkip(double seconds)
+{
+ for (int i = 0; i < ships.size(); i++) {
+ Ship* ship = ships[i];
+ Ship* ward = ship->GetWard();
+
+ // remember to burn fuel and fix stuff...
+ ship->ExecSystems(seconds);
+ ship->ExecMaintFrame(seconds);
+
+ ship->ClearTrack();
+ ListIter<Contact> contact = ship->ContactList();
+ while (++contact)
+ contact->ClearTrack();
+
+ if (ship->IsStatic())
+ continue;
+
+ // if ship is cleared inbound, land him:
+ InboundSlot* inbound = ship->GetInbound();
+ if (inbound) {
+ if (inbound->Cleared()) {
+ FlightDeck* deck = inbound->GetDeck();
+
+ if (deck) {
+ ship->SetCarrier((Ship*) deck->GetCarrier(), deck);
+ ship->SetFlightPhase(Ship::DOCKED);
+ ship->Stow();
+ deck->Clear(inbound->Index());
+ }
+ }
+
+ // cleared or not, once you're inbound, don't seek navpoints:
+ continue;
+ }
+
+ if (ship->GetHangar()) {
+ ship->GetHangar()->ExecFrame(seconds);
+
+ List<FlightDeck>& flight_decks = ship->FlightDecks();
+ for (int n = 0; n < flight_decks.size(); n++)
+ flight_decks[n]->ExecFrame(seconds);
+ }
+
+ Instruction* navpt = ship->GetNextNavPoint();
+ Point dest = ship->Location();
+ double speed = 500;
+ double space = 2.0e3 * (ship->GetElementIndex() - 1);
+
+ if (ship->IsStarship())
+ space *= 5;
+
+ if (navpt && navpt->Action() == Instruction::LAUNCH) {
+ ship->SetNavptStatus(navpt, Instruction::COMPLETE);
+ navpt = ship->GetNextNavPoint();
+ }
+
+ if (navpt) {
+ dest = navpt->Location().OtherHand();
+ speed = navpt->Speed();
+ }
+
+ else if (ward) {
+ Point delta = ship->Location() - ward->Location();
+ delta.y = 0;
+
+ if (delta.length() > 25e3) {
+ delta.Normalize();
+ dest = ward->Location() + delta * 25e3;
+ }
+ }
+
+ Point delta = dest - ship->Location();
+ Point unit = delta;
+ double dist = unit.Normalize() - space;
+
+ if (dist > 1e3) {
+ if (speed < 50)
+ speed = 500;
+
+ double etr = dist / speed;
+
+ if (etr > seconds)
+ etr = seconds;
+
+ Point trans = unit * (speed * etr);
+
+ if (ship->GetFuelLevel() > 1) {
+ ship->MoveTo(ship->Location() + trans);
+ ship->SetVelocity(unit * speed);
+ }
+ ship->LookAt(dest);
+
+ if (ship->IsStarship()) {
+ ship->SetFLCSMode(Ship::FLCS_HELM);
+ ship->SetHelmHeading(ship->CompassHeading());
+ ship->SetHelmPitch(ship->CompassPitch());
+ }
+ }
+
+ else if (navpt && navpt->Status() <= Instruction::ACTIVE) {
+ ship->SetNavptStatus(navpt, Instruction::COMPLETE);
+ }
+
+ if (ward) {
+ Point ward_heading = ward->Heading();
+ ward_heading.y = 0;
+ ward_heading.Normalize();
+
+ if (ship->GetFuelLevel() > 1) {
+ ship->SetVelocity(ward->Velocity());
+ }
+ ship->LookAt(ship->Location() + ward_heading * 1e6);
+
+ if (ship->IsStarship()) {
+ ship->SetFLCSMode(Ship::FLCS_HELM);
+ ship->SetHelmHeading(ship->CompassHeading());
+ ship->SetHelmPitch(ship->CompassPitch());
+ }
+ }
+
+ if (dist > 1 || ward) {
+ for (int j = 0; j < ships.size(); j++) {
+ Ship* test = ships[j];
+
+ if (ship != test && test->Mass() >= ship->Mass()) {
+ Point delta = ship->Location() - test->Location();
+
+ if (delta.length() < ship->Radius() * 2 + test->Radius() * 2) {
+ ship->MoveTo(test->Location() + RandomPoint().OtherHand());
+ }
+ }
+ }
+ }
+ }
+
+ DockShips();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimRegion::CommitMission()
+{
+ for (int i = 0; i < dead_ships.size(); i++) {
+ Ship* s = dead_ships[i];
+
+ if (s->GetCombatUnit() && s->GetFlightPhase() != Ship::DOCKED)
+ s->GetCombatUnit()->Kill(1);
+ }
+
+ for (i = 0; i < ships.size(); i++) {
+ Ship* s = ships[i];
+ CombatUnit* u = s->GetCombatUnit();
+
+ if (u) {
+ Point u_loc = s->Location().OtherHand();
+ if (u_loc.z > 20e3)
+ u_loc.z = 20e3;
+ else if (u_loc.z < -20e3)
+ u_loc.z = -20e3;
+
+ if (u->IsStarship()) {
+ u->SetRegion(s->GetRegion()->Name());
+ u->MoveTo(u_loc);
+ }
+
+ if (!u->IsDropship()) {
+ if (s->Integrity() < 1)
+ u->Kill(1);
+ else
+ u->SetSustainedDamage(s->Design()->integrity - s->Integrity());
+ }
+
+ CombatGroup* g = u->GetCombatGroup();
+ if (g && g->Type() > CombatGroup::FLEET && g->GetFirstUnit() == u) {
+ if (!g->IsZoneLocked())
+ g->SetRegion(s->GetRegion()->Name());
+ else
+ u->SetRegion(g->GetRegion());
+
+ g->MoveTo(u_loc);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+const char* FormatGameTime()
+{
+ static char txt[64];
+
+ int t = Game::GameTime();
+
+ int h = ( t / 3600000);
+ int m = ((t - h*3600000) / 60000);
+ int s = ((t - h*3600000 - m*60000) / 1000);
+ int e = ( t - h*3600000 - m*60000 - s*1000);
+
+ if (h > 0)
+ sprintf(txt, "%02d:%02d:%02d.%03d", h,m,s,e);
+ else
+ sprintf(txt, "%02d:%02d.%03d", m,s,e);
+
+ return txt;
+}
diff --git a/Stars45/Sim.h b/Stars45/Sim.h
new file mode 100644
index 0000000..dd2be8f
--- /dev/null
+++ b/Stars45/Sim.h
@@ -0,0 +1,330 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Sim.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Simulation Universe and Region classes
+*/
+
+#ifndef Sim_h
+#define Sim_h
+
+#include "Types.h"
+#include "Universe.h"
+#include "Scene.h"
+#include "Physical.h"
+#include "Geometry.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Sim;
+class SimRegion;
+class SimObject;
+class SimObserver;
+class SimHyper;
+class SimSplash;
+
+class StarSystem;
+class Orbital;
+class OrbitalRegion;
+class Asteroid;
+
+class NetGame;
+
+class CameraDirector;
+class Contact;
+class Ship;
+class ShipDesign;
+class System;
+class Element;
+class Shot;
+class Drone;
+class Explosion;
+class Debris;
+class WeaponDesign;
+class MotionController;
+class Dust;
+class Grid;
+class Mission;
+class MissionElement;
+class MissionEvent;
+class Hangar;
+class FlightDeck;
+
+class Terrain;
+class TerrainPatch;
+
+class Model;
+
+// +--------------------------------------------------------------------+
+
+class Sim : public Universe
+{
+ friend class SimRegion;
+
+public:
+ enum { REAL_SPACE, AIR_SPACE };
+
+ Sim(MotionController* ctrl);
+ virtual ~Sim();
+
+ static Sim* GetSim() { return sim; }
+
+ virtual void ExecFrame(double seconds);
+
+ void LoadMission(Mission* msn, bool preload_textures=false);
+ void ExecMission();
+ void CommitMission();
+ void UnloadMission();
+
+ void NextView();
+ void ShowGrid(int show = true);
+ bool GridShown() const;
+
+ const char* FindAvailCallsign(int IFF);
+ Element* CreateElement(const char* callsign, int IFF, int type=0/*PATROL*/);
+ void DestroyElement(Element* elem);
+ Ship* CreateShip(const char* name,
+ const char* reg_num,
+ ShipDesign* design,
+ const char* rgn_name,
+ const Point& loc,
+ int IFF=0,
+ int cmd_ai=0,
+ const int* loadout=0);
+ Ship* FindShip(const char* name, const char* rgn_name=0);
+ Shot* CreateShot(const Point& pos, const Camera& shot_cam, WeaponDesign* d, const Ship* ship=0, SimRegion* rgn=0);
+ Explosion* CreateExplosion(const Point& pos, const Point& vel, int type, float exp_scale, float part_scale, SimRegion* rgn=0, SimObject* source=0, System* sys=0);
+ Debris* CreateDebris(const Point& pos, const Point& vel, Model* model, double mass, SimRegion* rgn=0);
+ Asteroid* CreateAsteroid(const Point& pos, int type, double mass, SimRegion* rgn=0);
+ void CreateSplashDamage(Ship* ship);
+ void CreateSplashDamage(Shot* shot);
+ void DestroyShip(Ship* ship);
+ void NetDockShip(Ship* ship, Ship* carrier, FlightDeck* deck);
+
+ virtual Ship* FindShipByObjID(DWORD objid);
+ virtual Shot* FindShotByObjID(DWORD objid);
+
+ Mission* GetMission() { return mission; }
+ List<MissionEvent>& GetEvents() { return events; }
+ List<SimRegion>& GetRegions() { return regions; }
+ SimRegion* FindRegion(const char* name);
+ SimRegion* FindRegion(OrbitalRegion* rgn);
+ SimRegion* FindNearestSpaceRegion(SimObject* object);
+ SimRegion* FindNearestSpaceRegion(Orbital* body);
+ SimRegion* FindNearestTerrainRegion(SimObject* object);
+ SimRegion* FindNearestRegion(SimObject* object, int type);
+ bool ActivateRegion(SimRegion* rgn);
+
+ void RequestHyperJump(Ship* obj,
+ SimRegion* rgn,
+ const Point& loc,
+ int type=0,
+ Ship* fc_src=0,
+ Ship* fc_dst=0);
+
+ SimRegion* GetActiveRegion() { return active_region; }
+ StarSystem* GetStarSystem() { return star_system; }
+ List<StarSystem>& GetSystemList();
+ Scene* GetScene() { return &scene; }
+ Ship* GetPlayerShip();
+ Element* GetPlayerElement();
+ Orbital* FindOrbitalBody(const char* name);
+
+ void SetSelection(Ship* s);
+ bool IsSelected(Ship* s);
+ ListIter<Ship> GetSelection();
+ void ClearSelection();
+ void AddSelection(Ship* s);
+
+ void SetTestMode(bool t=true);
+
+ bool IsTestMode() const { return test_mode; }
+ bool IsNetGame() const { return netgame != 0; }
+ bool IsActive() const;
+ bool IsComplete() const;
+
+ MotionController* GetControls() const { return ctrl; }
+
+ Element* FindElement(const char* name);
+ List<Element>& GetElements() { return elements; }
+
+ int GetAssignedElements(Element* elem, List<Element>& assigned);
+
+ void SkipCutscene();
+ void ResolveTimeSkip(double seconds);
+ void ResolveHyperList();
+ void ResolveSplashList();
+
+ void ExecEvents(double seconds);
+ void ProcessEventTrigger(int type, int event_id=0, const char* ship=0, int param=0);
+ double MissionClock() const;
+ DWORD StartTime() const { return start_time; }
+
+ // Create a list of mission elements based on the current
+ // state of the simulation. Used for multiplayer join in progress.
+ ListIter<MissionElement> GetMissionElements();
+
+protected:
+ void CreateRegions();
+ void CreateElements();
+ void CopyEvents();
+ void BuildLinks();
+
+ // Convert a single live element into a mission element
+ // that can be serialized over the net.
+ MissionElement* CreateMissionElement(Element* elem);
+ Hangar* FindSquadron(const char* name, int& index);
+
+ static Sim* sim;
+ SimRegion* active_region;
+ StarSystem* star_system;
+ Scene scene;
+ Dust* dust;
+ CameraDirector* cam_dir;
+
+ List<SimRegion> regions;
+ List<SimRegion> rgn_queue;
+ List<SimHyper> jumplist;
+ List<SimSplash> splashlist;
+ List<Element> elements;
+ List<Element> finished;
+ List<MissionEvent> events;
+ List<MissionElement> mission_elements;
+
+ MotionController* ctrl;
+
+ bool test_mode;
+ bool grid_shown;
+ Mission* mission;
+
+ NetGame* netgame;
+ DWORD start_time;
+};
+
+// +--------------------------------------------------------------------+
+
+class SimRegion
+{
+ friend class Sim;
+
+public:
+ static const char* TYPENAME() { return "SimRegion"; }
+
+ enum { REAL_SPACE, AIR_SPACE };
+
+ SimRegion(Sim* sim, const char* name, int type);
+ SimRegion(Sim* sim, OrbitalRegion* rgn);
+ virtual ~SimRegion();
+
+ int operator == (const SimRegion& r) const { return (sim==r.sim) && (name==r.name); }
+ int operator < (const SimRegion& r) const;
+ int operator <= (const SimRegion& r) const;
+
+ virtual void Activate();
+ virtual void Deactivate();
+ virtual void ExecFrame(double seconds);
+ void ShowGrid(int show = true);
+ void NextView();
+ Ship* FindShip(const char* name);
+ Ship* GetPlayerShip() { return player_ship; }
+ void SetPlayerShip(Ship* ship);
+ OrbitalRegion* GetOrbitalRegion() { return orbital_region; }
+ Terrain* GetTerrain() { return terrain; }
+ bool IsActive() const { return active; }
+ bool IsAirSpace() const { return type == AIR_SPACE; }
+ bool IsOrbital() const { return type == REAL_SPACE; }
+ bool CanTimeSkip()const;
+
+ virtual Ship* FindShipByObjID(DWORD objid);
+ virtual Shot* FindShotByObjID(DWORD objid);
+
+ virtual void InsertObject(Ship* ship);
+ virtual void InsertObject(Shot* shot);
+ virtual void InsertObject(Explosion* explosion);
+ virtual void InsertObject(Debris* debris);
+ virtual void InsertObject(Asteroid* asteroid);
+
+ const char* Name() const { return name; }
+ int Type() const { return type; }
+ int NumShips() { return ships.size(); }
+ List<Ship>& Ships() { return ships; }
+ List<Ship>& Carriers() { return carriers; }
+ List<Shot>& Shots() { return shots; }
+ List<Drone>& Drones() { return drones; }
+ List<Debris>& Rocks() { return debris; }
+ List<Asteroid>& Roids() { return asteroids; }
+ List<Explosion>& Explosions() { return explosions; }
+ List<SimRegion>& Links() { return links; }
+ StarSystem* System() { return star_system; }
+
+ Point Location() const { return location; }
+
+ void SetSelection(Ship* s);
+ bool IsSelected(Ship* s);
+ ListIter<Ship> GetSelection();
+ void ClearSelection();
+ void AddSelection(Ship* s);
+
+ List<Contact>& TrackList(int iff);
+
+ void ResolveTimeSkip(double seconds);
+
+protected:
+ void CommitMission();
+ void TranslateObject(SimObject* object);
+
+ void AttachPlayerShip(int index);
+ void DestroyShips();
+ void DestroyShip(Ship* ship);
+ void NetDockShip(Ship* ship, Ship* carrier, FlightDeck* deck);
+
+ void UpdateSky(double seconds, const Point& ref);
+ void UpdateShips(double seconds);
+ void UpdateShots(double seconds);
+ void UpdateExplosions(double seconds);
+ void UpdateTracks(double seconds);
+
+ void DamageShips();
+ void CollideShips();
+ void CrashShips();
+ void DockShips();
+
+ Sim* sim;
+ Text name;
+ int type;
+ StarSystem* star_system;
+ OrbitalRegion* orbital_region;
+ Point location;
+ Grid* grid;
+ Terrain* terrain;
+ bool active;
+
+ Ship* player_ship;
+ int current_view;
+ List<Ship> ships;
+ List<Ship> carriers;
+ List<Ship> selection;
+ List<Ship> dead_ships;
+ List<Shot> shots;
+ List<Drone> drones;
+ List<Explosion> explosions;
+ List<Debris> debris;
+ List<Asteroid> asteroids;
+ List<Contact> track_database[5];
+ List<SimRegion> links;
+
+ DWORD sim_time;
+ int ai_index;
+};
+
+#endif Sim_h
+
diff --git a/Stars45/SimEvent.cpp b/Stars45/SimEvent.cpp
new file mode 100644
index 0000000..1cec64e
--- /dev/null
+++ b/Stars45/SimEvent.cpp
@@ -0,0 +1,262 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: SimEvent.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Simulation Events for mission summary
+*/
+
+#include "MemDebug.h"
+#include "SimEvent.h"
+#include "Sim.h"
+#include "Game.h"
+
+// +====================================================================+
+
+List<ShipStats> records;
+
+// +====================================================================+
+
+SimEvent::SimEvent(int e, const char* t, const char* i)
+ : event(e), count(0)
+{
+ Sim* sim = Sim::GetSim();
+ if (sim) {
+ time = (int) sim->MissionClock();
+ }
+ else {
+ time = (int) (Game::GameTime()/1000);
+ }
+
+ SetTarget(t);
+ SetInfo(i);
+}
+
+SimEvent::~SimEvent()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimEvent::SetTime(int t)
+{
+ time = t;
+}
+
+void
+SimEvent::SetTarget(const char* t)
+{
+ if (t && t[0])
+ target = t;
+}
+
+void
+SimEvent::SetInfo(const char* i)
+{
+ if (i && i[0])
+ info = i;
+}
+
+void
+SimEvent::SetCount(int c)
+{
+ count = c;
+}
+
+Text
+SimEvent::GetEventDesc() const
+{
+ switch (event) {
+ case LAUNCH: return Game::GetText("sim.event.Launch");
+ case DOCK: return Game::GetText("sim.event.Dock");
+ case LAND: return Game::GetText("sim.event.Land");
+ case EJECT: return Game::GetText("sim.event.Eject");
+ case CRASH: return Game::GetText("sim.event.Crash");
+ case COLLIDE: return Game::GetText("sim.event.Collision With");
+ case DESTROYED: return Game::GetText("sim.event.Destroyed By");
+ case MAKE_ORBIT: return Game::GetText("sim.event.Make Orbit");
+ case BREAK_ORBIT: return Game::GetText("sim.event.Break Orbit");
+ case QUANTUM_JUMP: return Game::GetText("sim.event.Quantum Jump");
+ case LAUNCH_SHIP: return Game::GetText("sim.event.Launch Ship");
+ case RECOVER_SHIP: return Game::GetText("sim.event.Recover Ship");
+ case FIRE_GUNS: return Game::GetText("sim.event.Fire Guns");
+ case FIRE_MISSILE: return Game::GetText("sim.event.Fire Missile");
+ case DROP_DECOY: return Game::GetText("sim.event.Drop Decoy");
+ case GUNS_KILL: return Game::GetText("sim.event.Guns Kill");
+ case MISSILE_KILL: return Game::GetText("sim.event.Missile Kill");
+ case LAUNCH_PROBE: return Game::GetText("sim.event.Launch Probe");
+ case SCAN_TARGET: return Game::GetText("sim.event.Scan Target");
+ default: return Game::GetText("sim.event.no event");
+ }
+}
+
+// +====================================================================+
+
+ShipStats::ShipStats(const char* n, int i)
+ : name(n), iff(i), kill1(0), kill2(0), lost(0), coll(0), points(0),
+ cmd_points(0), gun_shots(0), gun_hits(0), missile_shots(0), missile_hits(0),
+ combat_group(0), combat_unit(0), player(false), ship_class(0), elem_index(-1)
+{
+ if (!n || !n[0])
+ name = Game::GetText("[unknown]");
+}
+
+ShipStats::~ShipStats()
+{
+ events.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipStats::SetType(const char* t)
+{
+ if (t && t[0])
+ type = t;
+}
+
+void
+ShipStats::SetRole(const char* r)
+{
+ if (r && r[0])
+ role = r;
+}
+
+void
+ShipStats::SetRegion(const char* r)
+{
+ if (r && r[0])
+ region = r;
+}
+
+void
+ShipStats::SetCombatGroup(CombatGroup* g)
+{
+ combat_group = g;
+}
+
+void
+ShipStats::SetCombatUnit(CombatUnit* u)
+{
+ combat_unit = u;
+}
+
+void
+ShipStats::SetElementIndex(int n)
+{
+ elem_index = n;
+}
+
+void
+ShipStats::SetPlayer(bool p)
+{
+ player = p;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ShipStats::Summarize()
+{
+ kill1 = 0;
+ kill2 = 0;
+ lost = 0;
+ coll = 0;
+
+ ListIter<SimEvent> iter = events;
+ while (++iter) {
+ SimEvent* event = iter.value();
+ int code = event->GetEvent();
+
+ if (code == SimEvent::GUNS_KILL)
+ kill1++;
+
+ else if (code == SimEvent::MISSILE_KILL)
+ kill2++;
+
+ else if (code == SimEvent::DESTROYED)
+ lost++;
+
+ else if (code == SimEvent::CRASH)
+ coll++;
+
+ else if (code == SimEvent::COLLIDE)
+ coll++;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+SimEvent*
+ShipStats::AddEvent(SimEvent* e)
+{
+ events.append(e);
+ return e;
+}
+
+SimEvent*
+ShipStats::AddEvent(int event, const char* tgt, const char* info)
+{
+ SimEvent* e = new(__FILE__,__LINE__) SimEvent(event, tgt, info);
+ events.append(e);
+ return e;
+}
+
+bool
+ShipStats::HasEvent(int event)
+{
+ for (int i = 0; i < events.size(); i++)
+ if (events[i]->GetEvent() == event)
+ return true;
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+void ShipStats::Initialize() { records.destroy(); }
+void ShipStats::Close() { records.destroy(); }
+
+// +--------------------------------------------------------------------+
+
+int
+ShipStats::NumStats()
+{
+ return records.size();
+}
+
+ShipStats*
+ShipStats::GetStats(int i)
+{
+ if (i >= 0 && i < records.size())
+ return records.at(i);
+
+ return 0;
+}
+
+ShipStats*
+ShipStats::Find(const char* name)
+{
+ if (name && name[0]) {
+ ListIter<ShipStats> iter = records;
+ while (++iter) {
+ ShipStats* stats = iter.value();
+ if (!strcmp(stats->GetName(), name))
+ return stats;
+ }
+
+ ShipStats* stats = new(__FILE__,__LINE__) ShipStats(name);
+ records.append(stats);
+ return stats;
+ }
+
+ return 0;
+}
+
diff --git a/Stars45/SimEvent.h b/Stars45/SimEvent.h
new file mode 100644
index 0000000..779fe00
--- /dev/null
+++ b/Stars45/SimEvent.h
@@ -0,0 +1,163 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: SimEvent.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Simulation Universe and Region classes
+*/
+
+#ifndef SimEvent_h
+#define SimEvent_h
+
+#include "Types.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Sim;
+class SimRegion;
+class SimObject;
+class SimObserver;
+class SimHyper;
+class CombatGroup;
+class CombatUnit;
+
+// +--------------------------------------------------------------------+
+
+class SimEvent
+{
+public:
+ static const char* TYPENAME() { return "SimEvent"; }
+
+ enum EVENT { LAUNCH=1, DOCK, LAND, EJECT, CRASH, COLLIDE, DESTROYED,
+ MAKE_ORBIT, BREAK_ORBIT, QUANTUM_JUMP,
+ LAUNCH_SHIP, RECOVER_SHIP,
+ FIRE_GUNS, FIRE_MISSILE, DROP_DECOY,
+ GUNS_KILL, MISSILE_KILL,
+ LAUNCH_PROBE, SCAN_TARGET
+ };
+
+ SimEvent(int event, const char* tgt=0, const char* info=0);
+ ~SimEvent();
+
+ int GetEvent() const { return event; }
+ int GetTime() const { return time; }
+ Text GetEventDesc() const;
+ const char* GetTarget() const { return target; }
+ const char* GetInfo() const { return info; }
+ int GetCount() const { return count; }
+
+ void SetTarget(const char* tgt);
+ void SetInfo(const char* info);
+ void SetCount(int count);
+ void SetTime(int time);
+
+private:
+ int event;
+ int time;
+ Text target;
+ Text info;
+ int count;
+};
+
+// +--------------------------------------------------------------------+
+
+class ShipStats
+{
+public:
+ static const char* TYPENAME() { return "ShipStats"; }
+
+ ShipStats(const char* name, int iff=0);
+ ~ShipStats();
+
+ static void Initialize();
+ static void Close();
+ static ShipStats* Find(const char* name);
+ static int NumStats();
+ static ShipStats* GetStats(int i);
+
+ void Summarize();
+
+ const char* GetName() const { return name; }
+ const char* GetType() const { return type; }
+ const char* GetRole() const { return role; }
+ const char* GetRegion() const { return region; }
+ CombatGroup* GetCombatGroup() const { return combat_group; }
+ CombatUnit* GetCombatUnit() const { return combat_unit; }
+ int GetElementIndex() const { return elem_index; }
+ int GetShipClass() const { return ship_class; }
+ int GetIFF() const { return iff; }
+ int GetGunKills() const { return kill1; }
+ int GetMissileKills() const { return kill2; }
+ int GetDeaths() const { return lost; }
+ int GetColls() const { return coll; }
+ int GetPoints() const { return points; }
+ int GetCommandPoints()const { return cmd_points; }
+
+ int GetGunShots() const { return gun_shots; }
+ int GetGunHits() const { return gun_hits; }
+ int GetMissileShots() const { return missile_shots; }
+ int GetMissileHits() const { return missile_hits; }
+
+ bool IsPlayer() const { return player; }
+
+ List<SimEvent>&
+ GetEvents() { return events; }
+ SimEvent* AddEvent(SimEvent* e);
+ SimEvent* AddEvent(int event, const char* tgt=0, const char* info=0);
+ bool HasEvent(int event);
+
+ void SetShipClass(int c) { ship_class = c; }
+ void SetIFF(int i) { iff = i; }
+ void SetType(const char* t);
+ void SetRole(const char* r);
+ void SetRegion(const char* r);
+ void SetCombatGroup(CombatGroup* g);
+ void SetCombatUnit(CombatUnit* u);
+ void SetElementIndex(int n);
+ void SetPlayer(bool p);
+
+ void AddGunShot() { gun_shots++; }
+ void AddGunHit() { gun_hits++; }
+ void AddMissileShot() { missile_shots++; }
+ void AddMissileHit() { missile_hits++; }
+ void AddPoints(int p) { points += p; }
+ void AddCommandPoints(int p) { cmd_points += p; }
+
+private:
+ Text name;
+ Text type;
+ Text role;
+ Text region;
+ CombatGroup* combat_group;
+ CombatUnit* combat_unit;
+ bool player;
+ int elem_index;
+ int ship_class;
+ int iff;
+ int kill1;
+ int kill2;
+ int lost;
+ int coll;
+
+ int gun_shots;
+ int gun_hits;
+
+ int missile_shots;
+ int missile_hits;
+
+ int points;
+ int cmd_points;
+
+ List<SimEvent> events;
+};
+
+#endif SimEvent_h
+
diff --git a/Stars45/SimObject.cpp b/Stars45/SimObject.cpp
new file mode 100644
index 0000000..01a69f6
--- /dev/null
+++ b/Stars45/SimObject.cpp
@@ -0,0 +1,150 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: SimObject.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Simulation Object and Observer classes
+*/
+
+#include "MemDebug.h"
+#include "SimObject.h"
+
+#include "Graphic.h"
+#include "Light.h"
+#include "Scene.h"
+
+// +--------------------------------------------------------------------+
+
+SimObserver::~SimObserver()
+{
+ ListIter<SimObject> observed = observe_list;
+ while (++observed)
+ observed->Unregister(this);
+}
+
+void
+SimObserver::Observe(SimObject* obj)
+{
+ if (obj) {
+ obj->Register(this);
+
+ if (!observe_list.contains(obj))
+ observe_list.append(obj);
+ }
+}
+
+void
+SimObserver::Ignore(SimObject* obj)
+{
+ if (obj) {
+ obj->Unregister(this);
+ observe_list.remove(obj);
+ }
+}
+
+bool
+SimObserver::Update(SimObject* obj)
+{
+ if (obj)
+ observe_list.remove(obj);
+
+ return true;
+}
+
+const char*
+SimObserver::GetObserverName() const
+{
+ static char name[32];
+ sprintf(name, "SimObserver 0x%08x", (DWORD) this);
+ return name;
+}
+
+// +--------------------------------------------------------------------+
+
+SimObject::~SimObject()
+{
+ Notify();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimObject::Notify()
+{
+ if (!notifying) {
+ notifying = true;
+
+ int nobservers = observers.size();
+ int nupdate = 0;
+
+ if (nobservers > 0) {
+ ListIter<SimObserver> iter = observers;
+ while (++iter) {
+ SimObserver* observer = iter.value();
+ observer->Update(this);
+ nupdate++;
+ }
+
+ observers.clear();
+ }
+
+ if (nobservers != nupdate) {
+ ::Print("WARNING: incomplete notify sim object '%s' - %d of %d notified\n",
+ Name(), nupdate, nobservers);
+ }
+
+ notifying = false;
+ }
+ else {
+ ::Print("WARNING: double notify on sim object '%s'\n", Name());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimObject::Register(SimObserver* observer)
+{
+ if (!notifying && !observers.contains(observer))
+ observers.append(observer);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimObject::Unregister(SimObserver* observer)
+{
+ if (!notifying)
+ observers.remove(observer);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SimObject::Activate(Scene& scene)
+{
+ if (rep)
+ scene.AddGraphic(rep);
+ if (light)
+ scene.AddLight(light);
+
+ active = true;
+}
+
+void
+SimObject::Deactivate(Scene& scene)
+{
+ if (rep)
+ scene.DelGraphic(rep);
+ if (light)
+ scene.DelLight(light);
+
+ active = false;
+}
+
diff --git a/Stars45/SimObject.h b/Stars45/SimObject.h
new file mode 100644
index 0000000..1d3e645
--- /dev/null
+++ b/Stars45/SimObject.h
@@ -0,0 +1,99 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: SimObject.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Simulation Object and Observer classes
+*/
+
+#ifndef SimObject_h
+#define SimObject_h
+
+#include "Types.h"
+#include "Physical.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Sim;
+class SimRegion;
+class SimObject;
+class SimObserver;
+class Scene;
+
+// +--------------------------------------------------------------------+
+
+class SimObject : public Physical
+{
+ friend class SimRegion;
+
+public:
+ static const char* TYPENAME() { return "SimObject"; }
+
+ enum TYPES {
+ SIM_SHIP=100,
+ SIM_SHOT,
+ SIM_DRONE,
+ SIM_EXPLOSION,
+ SIM_DEBRIS,
+ SIM_ASTEROID
+ };
+
+ SimObject() : region(0), objid(0), active(0), notifying(0) { }
+ SimObject(const char* n, int t=0) : Physical(n,t), region(0), objid(0), active(0), notifying(0) { }
+ virtual ~SimObject();
+
+ virtual SimRegion* GetRegion() const { return region; }
+ virtual void SetRegion(SimRegion* rgn) { region = rgn; }
+
+ virtual void Notify();
+ virtual void Register(SimObserver* obs);
+ virtual void Unregister(SimObserver* obs);
+
+ virtual void Activate(Scene& scene);
+ virtual void Deactivate(Scene& scene);
+
+ virtual DWORD GetObjID() const { return objid; }
+ virtual void SetObjID(DWORD id) { objid = id; }
+
+ virtual bool IsHostileTo(const SimObject* o)
+ const { return false; }
+
+protected:
+ SimRegion* region;
+ List<SimObserver> observers;
+ DWORD objid;
+ bool active;
+ bool notifying;
+};
+
+// +--------------------------------------------------------------------+
+
+class SimObserver
+{
+public:
+ static const char* TYPENAME() { return "SimObserver"; }
+
+ virtual ~SimObserver();
+
+ int operator == (const SimObserver& o) const { return this == &o; }
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+ virtual void Observe(SimObject* obj);
+ virtual void Ignore(SimObject* obj);
+
+
+protected:
+ List<SimObject> observe_list;
+};
+
+#endif SimObject_h
+
diff --git a/Stars45/Sky.cpp b/Stars45/Sky.cpp
new file mode 100644
index 0000000..0460924
--- /dev/null
+++ b/Stars45/Sky.cpp
@@ -0,0 +1,726 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Sky.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Celestial sphere, stars, planets, space dust...
+*/
+
+#include "MemDebug.h"
+#include "Sky.h"
+#include "StarSystem.h"
+
+#include "Game.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Light.h"
+#include "Random.h"
+
+void Print(const char* ftm, ...);
+
+
+// +====================================================================+
+
+Stars::Stars(int nstars)
+{
+ infinite = true;
+ luminous = true;
+ shadow = false;
+
+ vset = new(__FILE__,__LINE__) VertexSet(nstars);
+ colors = new(__FILE__,__LINE__) Color[nstars];
+
+ for (int i = 0; i < nstars; i++) {
+ vset->loc[i] = RandomVector(1000);
+
+ ColorValue val = ColorValue((float) Random(0.7, 0.8),
+ (float) Random(0.7, 0.8),
+ (float) Random(0.7, 0.8));
+ Color c = val.ToColor();
+
+ colors[i] = c;
+ vset->diffuse[i] = c.Value();
+ vset->specular[i] = 0;
+ }
+
+ strcpy(name, "Stars");
+}
+
+Stars::~Stars()
+{
+ delete [] colors;
+ delete vset;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Stars::Illuminate(double scale)
+{
+ if (!vset)
+ return;
+
+ for (int i = 0; i < vset->nverts; i++) {
+ Color c = colors[i] * scale;
+ vset->diffuse[i] = c.Value();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Stars::Render(Video* video, DWORD flags)
+{
+ if (!vset || !video || (flags & Graphic::RENDER_ADDITIVE) == 0)
+ return;
+
+ video->SetBlendType(Video::BLEND_ADDITIVE);
+ video->DrawPoints(vset);
+}
+
+// +====================================================================+
+
+static const double BOUNDARY = 3000;
+static const double BOUNDARYx2 = BOUNDARY * 2;
+
+Dust::Dust(int ndust, bool b)
+ : really_hidden(false), bright(b)
+{
+ radius = (float) BOUNDARYx2;
+ luminous = true;
+ vset = new(__FILE__,__LINE__) VertexSet(ndust);
+
+ Reset(Point(0, 0, 0));
+ strcpy(name, "Dust");
+}
+
+// +--------------------------------------------------------------------+
+
+Dust::~Dust()
+{
+ delete vset;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Dust::Reset(const Point& ref)
+{
+ BYTE c = 0;
+
+ for (int i = 0; i < vset->nverts; i++) {
+ vset->loc[i] = Vec3( Random(-BOUNDARY, BOUNDARY),
+ Random(-BOUNDARY, BOUNDARY),
+ Random(-BOUNDARY, BOUNDARY) );
+
+ if (bright)
+ c = (BYTE) Random(96,200);
+ else
+ c = (BYTE) Random(64,156);
+
+ vset->diffuse[i] = Color(c,c,c).Value();
+ vset->specular[i] = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Dust::ExecFrame(double factor, const Point& ref)
+{
+ if (Game::TimeCompression() > 4) {
+ Hide();
+ return;
+ }
+
+ Show();
+
+ Point delta = ref - loc;
+ double dlen = delta.length();
+
+ if (dlen < 0.0001)
+ return;
+
+ if (dlen > BOUNDARY) {
+ Reset(ref);
+ }
+ else {
+ // wrap around if necessary to keep in view
+ for (int i = 0; i < vset->nverts; i++) {
+ Vec3 v = vset->loc[i];
+
+ v -= delta;
+
+ if (v.x > BOUNDARY) v.x -= (float) BOUNDARYx2;
+ if (v.x < -BOUNDARY) v.x += (float) BOUNDARYx2;
+ if (v.y > BOUNDARY) v.y -= (float) BOUNDARYx2;
+ if (v.y < -BOUNDARY) v.y += (float) BOUNDARYx2;
+ if (v.z > BOUNDARY) v.z -= (float) BOUNDARYx2;
+ if (v.z < -BOUNDARY) v.z += (float) BOUNDARYx2;
+
+ vset->loc[i] = v;
+ }
+ }
+
+ MoveTo(ref);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Dust::Render(Video* video, DWORD flags)
+{
+ if (hidden || really_hidden)
+ return;
+
+ if (!vset || !video || (flags & Graphic::RENDER_SOLID) == 0 || (flags & Graphic::RENDER_ADD_LIGHT) != 0)
+ return;
+
+ video->SetBlendType(Video::BLEND_SOLID);
+ video->SetRenderState(Video::Z_ENABLE, false);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, false);
+
+ video->DrawPoints(vset);
+
+ video->SetRenderState(Video::Z_ENABLE, true);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, true);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Dust::Hide()
+{
+ hidden = true;
+ really_hidden = true;
+}
+
+void
+Dust::Show()
+{
+ hidden = false;
+ really_hidden = false;
+}
+
+// +====================================================================+
+
+PlanetRep::PlanetRep(const char* surface_name, const char* glow_name,
+ double rad, const Vec3& pos, double tscale,
+ const char* rngname, double minrad, double maxrad,
+ Color atmos, const char* gloss_name)
+ : mtl_surf(0), mtl_limb(0), mtl_ring(0), star_system(0)
+{
+ loc = pos;
+
+ radius = (float) rad;
+ has_ring = 0;
+ ring_verts = -1;
+ ring_polys = -1;
+ ring_rad = 0;
+ body_rad = rad;
+ daytime = false;
+ atmosphere = atmos;
+ star_system = 0;
+
+ if (!surface_name || !*surface_name) {
+ Print(" invalid Planet patch - no surface texture specified\n");
+ return;
+ }
+
+ Print(" constructing Planet patch %s\n", surface_name);
+ strncpy(name, surface_name, 31);
+ name[31] = 0;
+
+ Bitmap* bmp_surf = 0;
+ Bitmap* bmp_spec = 0;
+ Bitmap* bmp_glow = 0;
+ Bitmap* bmp_ring = 0;
+ Bitmap* bmp_limb = 0;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->LoadTexture(surface_name, bmp_surf, Bitmap::BMP_SOLID, true);
+
+ if (glow_name && *glow_name) {
+ Print(" loading glow texture %s\n", glow_name);
+ loader->LoadTexture(glow_name, bmp_glow, Bitmap::BMP_SOLID, true);
+ }
+
+ if (gloss_name && *gloss_name) {
+ Print(" loading gloss texture %s\n", gloss_name);
+ loader->LoadTexture(gloss_name, bmp_spec, Bitmap::BMP_SOLID, true);
+ }
+
+ mtl_surf = new(__FILE__,__LINE__) Material;
+
+ mtl_surf->Ka = Color::LightGray;
+ mtl_surf->Kd = Color::White;
+ mtl_surf->Ke = bmp_glow ? Color::White : Color::Black;
+ mtl_surf->Ks = bmp_spec ? Color::LightGray : Color::Black;
+ mtl_surf->power = 25.0f;
+ mtl_surf->tex_diffuse = bmp_surf;
+ mtl_surf->tex_specular = bmp_spec;
+ mtl_surf->tex_emissive = bmp_glow;
+ mtl_surf->blend = Material::MTL_SOLID;
+
+ if (bmp_spec && Video::GetInstance()->IsSpecMapEnabled()) {
+ if (glow_name && strstr(glow_name, "light"))
+ strcpy(mtl_surf->shader, "SimplePix/PlanetSurfNightLight");
+
+ else if (glow_name)
+ strcpy(mtl_surf->shader, "SimplePix/PlanetSurf");
+ }
+
+ if (atmosphere != Color::Black) {
+ mtl_limb = new(__FILE__,__LINE__) Material;
+
+ mtl_limb->Ka = atmosphere;
+
+ strcpy(mtl_limb->shader, "PlanetLimb");
+
+ Print(" loading atmospheric limb texture PlanetLimb.pcx\n");
+ loader->LoadTexture("PlanetLimb.pcx", bmp_limb, Bitmap::BMP_TRANSLUCENT, true);
+ mtl_limb->tex_diffuse = bmp_limb;
+ mtl_limb->blend = Material::MTL_TRANSLUCENT;
+ }
+
+ if (maxrad > 0 && minrad > 0) {
+ has_ring = 1;
+ radius = (float) maxrad;
+ ring_rad = (maxrad + minrad)/2;
+ loader->LoadTexture(rngname, bmp_ring, Bitmap::BMP_SOLID, true);
+
+ mtl_ring = new(__FILE__,__LINE__) Material;
+
+ mtl_ring->Ka = Color::LightGray;
+ mtl_ring->Kd = Color::White;
+ mtl_ring->Ks = Color::Gray;
+ mtl_ring->Ke = Color::Black;
+ mtl_ring->power = 30.0f;
+ mtl_ring->tex_diffuse = bmp_ring;
+ mtl_ring->blend = Material::MTL_TRANSLUCENT;
+ }
+
+ if (rad > 2e6 && rad < 1e8)
+ CreateSphere(rad, 24, 32, minrad, maxrad, 48, tscale);
+ else
+ CreateSphere(rad, 16, 24, minrad, maxrad, 48, tscale);
+}
+
+// +--------------------------------------------------------------------+
+
+PlanetRep::~PlanetRep()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlanetRep::CreateSphere(double radius, int nrings, int nsections,
+ double minrad, double maxrad, int rsections,
+ double tscale)
+{
+ const int sect_verts = nsections + 1;
+
+ model = new(__FILE__,__LINE__) Model;
+ own_model = 1;
+
+ Surface* surface = new(__FILE__,__LINE__) Surface;
+
+ int i, j, m, n;
+
+ int npolys = (nrings + 2) * nsections;
+ int nverts = (nrings + 3) * sect_verts;
+
+ int ppolys = npolys;
+ int pverts = nverts;
+
+ int apolys = 0;
+ int averts = 0;
+
+ if (atmosphere != Color::Black) {
+ apolys = npolys;
+ averts = nverts;
+
+ npolys *= 2;
+ nverts *= 2;
+ }
+
+ if (has_ring) {
+ ring_verts = nverts;
+ ring_polys = npolys;
+
+ npolys += rsections * 3; // top, bottom, edge
+ nverts += rsections * 6;
+ }
+
+ surface->SetName(name);
+ surface->CreateVerts(nverts);
+ surface->CreatePolys(npolys);
+
+ VertexSet* vset = surface->GetVertexSet();
+
+ if (!vset || vset->nverts < nverts) {
+ ::Print("WARNING: insufficient memory for planet '%s'\n", name);
+ return;
+ }
+
+ Poly* polys = surface->GetPolys();
+
+ if (!polys) {
+ ::Print("WARNING: insufficient memory for planet '%s'\n", name);
+ return;
+ }
+
+ ZeroMemory(polys, sizeof(Poly) * npolys);
+
+ // Generate vertex points for planetary rings:
+ double dtheta = PI / (nrings + 2);
+ double dphi = 2 * PI / nsections;
+ double theta = 0;
+ n = 0; // vertex being generated
+
+ for (i = 0; i < nrings+3; i++) {
+ double y = radius * cos(theta); // y is the same for entire ring
+ double v = theta / PI; // v is the same for entire ring
+ double rsintheta = radius * sin(theta);
+ double phi = 0;
+
+ for (j = 0; j < sect_verts; j++) {
+ double x = rsintheta * sin(phi);
+ double z = rsintheta * cos(phi);
+
+ vset->loc[n] = Vec3(x, y, z);
+ vset->nrm[n] = Vec3(x, y, z);
+ vset->tu[n] = (float) (tscale * (1 - (phi/(2.0*PI))));
+ vset->tv[n] = (float) (tscale * v);
+
+ vset->nrm[n].Normalize();
+
+ phi += dphi;
+ n++;
+ }
+
+ theta += dtheta;
+ }
+
+ // Generate vertex points for rings:
+ if (has_ring) {
+ n = ring_verts;
+
+ double dphi = 2.0 * PI / rsections;
+ double y = 0; // y is the same for entire ring
+
+ // top of ring:
+ double phi = 0;
+ for (j = 0; j < rsections; j++) {
+ double x = minrad * sin(phi);
+ double z = minrad * cos(phi);
+
+ vset->loc[n] = Vec3(x, y, z);
+ vset->nrm[n] = Vec3(0, 1, 0);
+ vset->tu[n] = (j & 1) ? 1.0f : 0.0f;
+ vset->tv[n] = 0.0f;
+ n++;
+
+ x = maxrad * sin(phi);
+ z = maxrad * cos(phi);
+
+ vset->loc[n] = Vec3(x, y, z);
+ vset->nrm[n] = Vec3(0, 1, 0);
+ vset->tu[n] = (j & 1) ? 1.0f : 0.0f;
+ vset->tv[n] = 1.0f;
+ n++;
+
+ phi += dphi;
+ }
+
+ // bottom of ring:
+ phi = 0;
+ for (j = 0; j < rsections; j++) {
+ double x = minrad * sin(phi);
+ double z = minrad * cos(phi);
+
+ vset->loc[n] = Vec3(x, y, z);
+ vset->nrm[n] = Vec3(0, -1, 0);
+ vset->tu[n] = (j & 1) ? 1.0f : 0.0f;
+ vset->tv[n] = 0.0f;
+ n++;
+
+ x = maxrad * sin(phi);
+ z = maxrad * cos(phi);
+
+ vset->loc[n] = Vec3(x, y, z);
+ vset->nrm[n] = Vec3(0, -1, 0);
+ vset->tu[n] = (j & 1) ? 1.0f : 0.0f;
+ vset->tv[n] = 1.0f;
+ n++;
+
+ phi += dphi;
+ }
+
+ // edge of ring:
+ phi = 0;
+ for (j = 0; j < rsections; j++) {
+ double x = maxrad * sin(phi);
+ double z = maxrad * cos(phi);
+
+ Point normal = Point(x,0,z);
+ normal.Normalize();
+
+ double thickness = maxrad/333;
+
+ vset->loc[n] = Vec3(x, y+thickness, z);
+ vset->nrm[n] = normal;
+ vset->tu[n] = (j & 1) ? 1.0f : 0.0f;
+ vset->tv[n] = 1.0f;
+ n++;
+
+ vset->loc[n] = Vec3(x, y-thickness, z);
+ vset->nrm[n] = normal;
+ vset->tu[n] = (j & 1) ? 1.0f : 0.0f;
+ vset->tv[n] = 1.0f;
+ n++;
+
+ phi += dphi;
+ }
+ }
+
+ for (i = 0; i < npolys; i++) {
+ polys[i].nverts = 3;
+ polys[i].vertex_set = vset;
+ polys[i].material = mtl_surf;
+ }
+
+ // Generate triangles for top and bottom caps.
+ for (i = 0; i < nsections; i++) {
+ Poly& p0 = polys[i];
+ p0.verts[2] = i;
+ p0.verts[1] = sect_verts + i;
+ p0.verts[0] = sect_verts + ((i+1) % sect_verts);
+
+ Poly& p1 = polys[ppolys - nsections + i];
+ p1.verts[2] = pverts - 1 - i;
+ p1.verts[1] = pverts - 1 - sect_verts - i;
+ p1.verts[0] = pverts - 2 - sect_verts - i;
+
+ surface->AddIndices(6);
+ }
+
+ // Generate triangles for the planetary rings
+ m = sect_verts; // first vertex in current ring
+ n = nsections; // triangle being generated, skip the top cap
+
+ for (i = 0; i < nrings; i++) {
+ for (j = 0; j < nsections; j++) {
+ Poly& p0 = polys[n];
+ p0.nverts = 4;
+ p0.verts[3] = m + j;
+ p0.verts[2] = m + (sect_verts) + j;
+ p0.verts[1] = m + (sect_verts) + ((j + 1) % (sect_verts));
+ p0.verts[0] = m + ((j + 1) % (sect_verts));
+ n++;
+
+ surface->AddIndices(6);
+ }
+
+ m += sect_verts;
+ }
+
+ if (averts && apolys && mtl_limb) {
+ for (i = 0; i < pverts; i++) {
+ vset->loc[averts + i] = vset->loc[i];
+ vset->nrm[averts + i] = vset->nrm[i];
+ }
+
+ for (i = 0; i < ppolys; i++) {
+ Poly& p0 = polys[i];
+ Poly& p1 = polys[apolys + i];
+
+ p1.vertex_set = vset;
+ p1.material = mtl_limb;
+
+ p1.nverts = p0.nverts;
+ p1.verts[0] = p0.verts[0];
+ p1.verts[1] = p0.verts[1];
+ p1.verts[2] = p0.verts[2];
+ p1.verts[3] = p0.verts[3];
+
+ surface->AddIndices(p1.nverts == 3 ? 3 : 6);
+ }
+ }
+
+ if (has_ring) {
+ // Generate quads for the rings
+ m = ring_verts; // first vertex in top of ring, after planet verts
+ n = ring_polys; // quad being generated, after planet polys
+
+ // top of ring:
+ for (j = 0; j < rsections; j++) {
+ Poly& p0 = polys[n];
+ p0.nverts = 4;
+ p0.material = mtl_ring;
+
+ p0.verts[3] = m + 2*j;
+ p0.verts[2] = m + 2*j + 1;
+ p0.verts[1] = m + ((2*j + 3) % (rsections*2));
+ p0.verts[0] = m + ((2*j + 2) % (rsections*2));
+
+ surface->AddIndices(6);
+
+ n++;
+ }
+
+ // bottom of ring:
+ // first vertex in bottom of ring, after top ring verts
+ m = ring_verts + 2*rsections;
+
+ for (j = 0; j < rsections; j++) {
+ Poly& p0 = polys[n];
+ p0.nverts = 4;
+ p0.material = mtl_ring;
+
+ p0.verts[0] = m + 2*j;
+ p0.verts[1] = m + 2*j + 1;
+ p0.verts[2] = m + ((2*j + 3) % (rsections*2));
+ p0.verts[3] = m + ((2*j + 2) % (rsections*2));
+
+ surface->AddIndices(6);
+
+ n++;
+ }
+
+ // edge of ring:
+ // first vertex in edge of ring, after bottom ring verts
+ m = ring_verts + 4*rsections;
+
+ for (j = 0; j < rsections; j++) {
+ Poly& p0 = polys[n];
+ p0.nverts = 4;
+ p0.material = mtl_ring;
+
+ p0.verts[3] = m + 2*j;
+ p0.verts[2] = m + 2*j + 1;
+ p0.verts[1] = m + ((2*j + 3) % (rsections*2));
+ p0.verts[0] = m + ((2*j + 2) % (rsections*2));
+
+ surface->AddIndices(6);
+
+ n++;
+ }
+ }
+
+ // then assign them to cohesive segments:
+ Segment* segment = 0;
+
+ for (n = 0; n < npolys; n++) {
+ Poly& poly = polys[n];
+ poly.plane = Plane(vset->loc[poly.verts[0]],
+ vset->loc[poly.verts[2]],
+ vset->loc[poly.verts[1]]);
+
+ if (segment && segment->material == polys[n].material) {
+ segment->npolys++;
+ }
+ else {
+ segment = new(__FILE__,__LINE__) Segment;
+
+ segment->npolys = 1;
+ segment->polys = &polys[n];
+ segment->material = segment->polys->material;
+
+ surface->GetSegments().append(segment);
+ }
+ }
+
+ model->AddSurface(surface);
+}
+
+
+int
+PlanetRep::CheckRayIntersection(Point Q, Point w, double len, Point& ipt,
+ bool treat_translucent_polys_as_solid)
+{
+ // compute leading edge of ray:
+ Point dst = Q + w*len;
+
+ // check right angle spherical distance:
+ Point d0 = loc - Q;
+ Point d1 = d0.cross(w);
+ double dlen = d1.length(); // distance of point from line
+
+ if (dlen > body_rad) // clean miss
+ return 0; // (no impact)
+
+ // possible collision course...
+ Point d2 = Q + w * (d0 * w);
+
+ // so check the leading edge:
+ Point delta0 = dst - loc;
+
+ if (delta0.length() > radius) {
+ // and the endpoints:
+ Point delta1 = d2 - Q;
+ Point delta2 = dst - Q;
+
+ // if d2 is not between Q and dst, we missed:
+ if (delta1 * delta2 < 0 ||
+ delta1.length() > delta2.length()) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+void
+PlanetRep::SetDaytime(bool d)
+{
+ daytime = d;
+
+ if (daytime) {
+ if (mtl_surf) mtl_surf->blend = Material::MTL_ADDITIVE;
+ if (mtl_ring) mtl_ring->blend = Material::MTL_ADDITIVE;
+ }
+
+ else {
+ if (mtl_surf) mtl_surf->blend = Material::MTL_SOLID;
+ if (mtl_ring) mtl_ring->blend = Material::MTL_TRANSLUCENT;
+ }
+}
+
+void
+PlanetRep::SetStarSystem(StarSystem* system)
+{
+ star_system = system;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+PlanetRep::Render(Video* video, DWORD flags)
+{
+ Solid::Render(video, flags);
+
+ /***
+ *** DEBUG
+ ***
+
+ Matrix orient = Orientation();
+ orient.Transpose();
+
+ video->SetObjTransform(orient, Location());
+
+ Surface* surf = model->GetSurfaces().first();
+ Poly* polys = surf->GetPolys();
+
+ for (int i = 0; i < 5; i++)
+ video->DrawPolyOutline(polys + i);
+ /***/
+}
+
diff --git a/Stars45/Sky.h b/Stars45/Sky.h
new file mode 100644
index 0000000..92f731b
--- /dev/null
+++ b/Stars45/Sky.h
@@ -0,0 +1,105 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Sky.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Celestial sphere, stars, planets, space dust...
+*/
+
+#ifndef Sky_h
+#define Sky_h
+
+#include "Types.h"
+#include "Solid.h"
+#include "Bitmap.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class StarSystem;
+
+// +--------------------------------------------------------------------+
+
+class Stars : public Graphic
+{
+public:
+ Stars(int nstars);
+ virtual ~Stars();
+
+ virtual void Illuminate(double scale);
+ virtual void Render(Video* video, DWORD flags);
+
+protected:
+ VertexSet* vset;
+ Color* colors;
+};
+
+// +--------------------------------------------------------------------+
+
+class Dust : public Graphic
+{
+public:
+ Dust(int ndust, bool bright=false);
+ virtual ~Dust();
+
+ virtual void Render(Video* video, DWORD flags);
+ virtual void Reset(const Point& ref);
+ virtual void ExecFrame(double factor, const Point& ref);
+
+ virtual void Hide();
+ virtual void Show();
+
+protected:
+ bool really_hidden;
+ bool bright;
+ VertexSet* vset;
+};
+
+// +--------------------------------------------------------------------+
+
+class PlanetRep : public Solid
+{
+public:
+ PlanetRep(const char* img_west, const char* img_glow,
+ double rad, const Vec3& pos, double tscale = 1,
+ const char* rngname=0, double minrad = 0, double maxrad = 0,
+ Color atmos = Color::Black, const char* img_gloss=0);
+ virtual ~PlanetRep();
+
+ virtual Color Atmosphere() const { return atmosphere; }
+ virtual void SetAtmosphere(Color a) { atmosphere = a; }
+ virtual void SetDaytime(bool d);
+ virtual void SetStarSystem(StarSystem* system);
+
+ virtual void Render(Video* video, DWORD flags);
+
+ virtual int CheckRayIntersection(Point pt, Point vpn, double len, Point& ipt,
+ bool treat_translucent_polys_as_solid=true);
+
+protected:
+ void CreateSphere(double radius, int nrings, int nsections,
+ double minrad, double maxrad, int rsections,
+ double tscale);
+
+ Material* mtl_surf;
+ Material* mtl_limb;
+ Material* mtl_ring;
+ int has_ring;
+ int ring_verts;
+ int ring_polys;
+ double ring_rad;
+ double body_rad;
+ Color atmosphere;
+ bool daytime;
+
+ StarSystem* star_system;
+};
+
+#endif Sky_h
+
diff --git a/Stars45/StarServer.cpp b/Stars45/StarServer.cpp
new file mode 100644
index 0000000..672057a
--- /dev/null
+++ b/Stars45/StarServer.cpp
@@ -0,0 +1,545 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars
+ FILE: StarServer.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include "MemDebug.h"
+
+#include "StarServer.h"
+#include "Campaign.h"
+#include "CombatRoster.h"
+#include "Galaxy.h"
+#include "Mission.h"
+#include "Sim.h"
+#include "SimEvent.h"
+#include "Ship.h"
+#include "Contact.h"
+#include "QuantumDrive.h"
+#include "Power.h"
+#include "SystemDesign.h"
+#include "WeaponDesign.h"
+#include "Shot.h"
+#include "Drive.h"
+#include "Explosion.h"
+#include "FlightDeck.h"
+#include "RadioMessage.h"
+#include "RadioTraffic.h"
+#include "Random.h"
+#include "ModConfig.h"
+
+#include "NetLayer.h"
+#include "NetGame.h"
+#include "NetHost.h"
+#include "NetServer.h"
+#include "HttpServer.h"
+#include "HttpServletExec.h"
+#include "NetAdminServer.h"
+#include "NetLobbyServer.h"
+#include "NetServerConfig.h"
+
+#include "Token.h"
+#include "MachineInfo.h"
+#include "Game.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "EventDispatch.h"
+#include "MultiController.h"
+#include "DataLoader.h"
+#include "ParseUtil.h"
+#include "Resource.h"
+
+// +--------------------------------------------------------------------+
+
+StarServer* StarServer::instance = 0;
+
+static Mission* current_mission = 0;
+static double time_til_change = 0;
+static bool exit_latch = true;
+
+extern const char* versionInfo;
+
+// +--------------------------------------------------------------------+
+
+StarServer::StarServer()
+ : loader(0), time_mark(0), minutes(0), game_mode(MENU_MODE),
+ admin_server(0), lobby_server(0)
+{
+ if (!instance)
+ instance = this;
+
+ app_name = "Starserver 5.0";
+ title_text = "Starserver";
+ palette_name = "alpha";
+
+ Game::server = true;
+ Game::show_mouse = true;
+
+ DataLoader::Initialize();
+ loader = DataLoader::GetLoader();
+ int loadstat = loader->EnableDatafile("shatter.dat");
+
+ if (loadstat != DataLoader::DATAFILE_OK) {
+ const char* err_msg = loadstat == DataLoader::DATAFILE_INVALID ?
+ "The file 'shatter.dat' appears to have been damaged. Please re-install Starshatter." :
+ "Starshatter cannot open the file 'shatter.dat'. Please re-install Starshatter.";
+
+ ::MessageBox(hwnd, err_msg, "Starshatter - Error", MB_OK);
+ ::Print(err_msg);
+ ::Print("\n\nFATAL ERROR: EXIT.");
+ exit(-1);
+ }
+
+#ifndef STARSHATTER_DEMO_RELEASE
+ if (loader->FindFile("start.dat"))
+ loader->EnableDatafile("start.dat");
+#endif
+
+ // no images or sounds in server mode:
+ loader->EnableMedia(false);
+}
+
+StarServer::~StarServer()
+{
+ delete admin_server;
+ delete lobby_server;
+
+ admin_server = 0;
+ lobby_server = 0;
+
+ // delete all the ships and stuff
+ // BEFORE getting rid of the system
+ // and weapons catalogs!
+ delete world;
+ world = 0; // don't let base class double delete the world
+
+ Drive::Close();
+ Explosion::Close();
+ FlightDeck::Close();
+ Campaign::Close();
+ CombatRoster::Close();
+ Galaxy::Close();
+ RadioTraffic::Close();
+ Ship::Close();
+ WeaponDesign::Close();
+ SystemDesign::Close();
+ DataLoader::Close();
+ NetServerConfig::Close();
+ ModConfig::Close();
+
+ instance = 0;
+
+ Game::server = false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+StarServer::Init(HINSTANCE hi, HINSTANCE hpi, LPSTR cmdline, int nCmdShow)
+{
+ if (loader)
+ loader->UseFileSystem(false);
+
+ return Game::Init(hi, hpi, cmdline, nCmdShow);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+StarServer::InitGame()
+{
+ if (!Game::InitGame())
+ return false;
+
+ RandomInit();
+ ModConfig::Initialize();
+ NetServerConfig::Initialize();
+ SystemDesign::Initialize("sys.def");
+ WeaponDesign::Initialize("wep.def");
+ Ship::Initialize();
+ Galaxy::Initialize();
+ CombatRoster::Initialize();
+ Campaign::Initialize();
+
+ Drive::Initialize();
+ Explosion::Initialize();
+ FlightDeck::Initialize();
+ Ship::Initialize();
+ Shot::Initialize();
+ RadioTraffic::Initialize();
+
+ time_mark = Game::GameTime();
+ minutes = 0;
+
+ NetServerConfig* server_config = NetServerConfig::GetInstance();
+ if (!server_config)
+ return false;
+
+ ::Print("\n\n\nStarshatter Server Init\n");
+ ::Print("-----------------------\n");
+ ::Print("Server Name: %s\n", (const char*) server_config->Name());
+ ::Print("Server Type: %d\n", server_config->GetGameType());
+
+ if (server_config->GetMission().length() > 0)
+ ::Print("Server Mission: %s\n", (const char*) server_config->GetMission());
+
+ ::Print("Lobby Server Port: %d\n", server_config->GetLobbyPort());
+ ::Print("Admin Server Port: %d\n", server_config->GetAdminPort());
+ ::Print("-----------------------\n");
+
+ NetLobbyServer* nls = new(__FILE__,__LINE__) NetLobbyServer;
+ NetAdminServer* nas = NetAdminServer::GetInstance(server_config->GetAdminPort());
+ nas->SetServerName(server_config->Name());
+
+ lobby_server = nls;
+ admin_server = nas;
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarServer::SetGameMode(int m)
+{
+ if (game_mode == m)
+ return;
+
+ if (m == LOAD_MODE) {
+ Print(" game_mode = LOAD_MODE\n");
+ paused = true;
+ }
+
+ else if (m == PLAY_MODE) {
+ Print(" game_mode = PLAY_MODE\n");
+
+ if (!world) {
+ CreateWorld();
+ InstantiateMission();
+ }
+
+ // stand alone server should wait for players to connect
+ // before unpausing the simulation...
+ SetTimeCompression(1);
+ Pause(true);
+ }
+
+ else if (m == MENU_MODE) {
+ Print(" game_mode = MENU_MODE\n");
+ paused = true;
+
+ Sim* sim = (Sim*) world;
+
+ if (sim)
+ sim->UnloadMission();
+ }
+
+ game_mode = m;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarServer::SetNextMission(const char* script)
+{
+ if (lobby_server)
+ lobby_server->SetServerMission(script);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarServer::CreateWorld()
+{
+ RadioTraffic::Initialize();
+
+ // create world
+ if (!world) {
+ Sim* sim = new(__FILE__,__LINE__) Sim(0);
+ world = sim;
+ Print(" World Created.\n");
+ }
+}
+
+void
+StarServer::InstantiateMission()
+{
+ Memory::Check();
+
+ current_mission = 0;
+
+ if (Campaign::GetCampaign()) {
+ current_mission = Campaign::GetCampaign()->GetMission();
+ }
+
+ Sim* sim = (Sim*) world;
+
+ if (sim) {
+ sim->UnloadMission();
+
+ if (current_mission) {
+ sim->LoadMission(current_mission);
+ sim->ExecMission();
+ sim->SetTestMode(false);
+
+ Print(" Mission Instantiated.\n");
+ }
+
+ else {
+ Print(" *** WARNING: StarServer::InstantiateMission() - no mission selected ***\n");
+ }
+ }
+
+ Memory::Check();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+StarServer::GameLoop()
+{
+ if (active && paused) {
+ UpdateWorld();
+ GameState();
+ }
+
+ else if (!active) {
+ UpdateWorld();
+ GameState();
+ Sleep(10);
+ }
+
+ Game::GameLoop();
+ return false; // must return false to keep processing
+ // true tells the outer loop to sleep until a
+ // windows event is available
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarServer::UpdateWorld()
+{
+ long new_time = real_time;
+ double delta = new_time - frame_time;
+ seconds = max_frame_length;
+ gui_seconds = delta * 0.001;
+
+ if (frame_time == 0)
+ gui_seconds = 0;
+
+ time_comp = 1;
+
+ if (delta < max_frame_length * 1000)
+ seconds = delta * 0.001;
+
+ frame_time = new_time;
+
+ Galaxy* galaxy = Galaxy::GetInstance();
+ if (galaxy) galaxy->ExecFrame();
+
+ Campaign* campaign = Campaign::GetCampaign();
+ if (campaign) campaign->ExecFrame();
+
+ if (paused) {
+ if (world)
+ world->ExecFrame(0);
+ }
+
+ else {
+ game_time += (DWORD) (seconds * 1000);
+
+ Drive::StartFrame();
+
+ if (world)
+ world->ExecFrame(seconds);
+ }
+
+ static DWORD refresh_time = 0;
+ if (RealTime() - refresh_time > 1000) {
+ refresh_time = RealTime();
+ RedrawWindow(hwnd, 0, 0, RDW_ERASE|RDW_INVALIDATE);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarServer::GameState()
+{
+ if (lobby_server) {
+ lobby_server->ExecFrame();
+
+ if (lobby_server->GetStatus() == NetServerInfo::PERSISTENT)
+ paused = NetGame::NumPlayers() < 1;
+ }
+
+ if (game_mode == MENU_MODE) {
+ Sleep(30);
+ }
+
+ else if (game_mode == LOAD_MODE) {
+ CreateWorld();
+ InstantiateMission();
+
+ SetGameMode(PLAY_MODE);
+ }
+
+ else if (game_mode == PLAY_MODE) {
+ if (Game::GameTime() - time_mark > 60000) {
+ time_mark = Game::GameTime();
+ minutes++;
+ if (minutes > 60)
+ Print(" TIME %2d:%02d:00\n", minutes/60, minutes%60);
+ else
+ Print(" TIME %2d:00\n", minutes);
+ }
+
+ Sleep(10);
+ }
+
+ Memory::Check();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+StarServer::OnPaint()
+{
+ PAINTSTRUCT paintstruct;
+ HDC hdc = BeginPaint(hwnd, &paintstruct);
+
+ Text txt_title = title_text;
+ Text txt_mode;
+ Text txt_users = Game::GetText("server.no-users");
+ char buf[256];
+
+ txt_title += " ";
+ txt_title += versionInfo;
+
+ switch (game_mode) {
+ case LOAD_MODE:
+ case MENU_MODE:
+ txt_mode = Game::GetText("server.mode.lobby");
+
+ if (lobby_server) {
+ sprintf(buf, Game::GetText("server.users").data(), lobby_server->NumUsers());
+ txt_users = buf;
+ }
+ break;
+
+ case PLAY_MODE:
+ txt_mode = Game::GetText("server.mode.active");
+ if (lobby_server) {
+ sprintf(buf, Game::GetText("server.users-and-players").data(), lobby_server->NumUsers(), NetGame::NumPlayers());
+ }
+ else {
+ sprintf(buf, Game::GetText("server.players").data(), NetGame::NumPlayers());
+ }
+ txt_users = buf;
+ break;
+
+ default:
+ txt_mode = Game::GetText("server.mode.other");
+ break;
+ }
+
+ if (lobby_server && lobby_server->GetStatus() == NetServerInfo::PERSISTENT)
+ txt_mode += " " + Game::GetText("server.alt.persistent");
+
+ if (paused)
+ txt_mode += " " + Game::GetText("server.alt.paused");
+
+ TextOut(hdc, 4, 4, txt_title, txt_title.length());
+ TextOut(hdc, 4, 22, txt_mode, txt_mode.length());
+ TextOut(hdc, 4, 40, txt_users, txt_users.length());
+
+ Sim* sim = Sim::GetSim();
+ if (sim && sim->GetMission()) {
+ Mission* mission = sim->GetMission();
+ Text txt_msn = Game::GetText("server.mission");
+ txt_msn += mission->Name();
+ TextOut(hdc, 4, 58, txt_msn, txt_msn.length());
+ }
+
+ EndPaint(hwnd, &paintstruct);
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD WINAPI StarServerShutdownProc(LPVOID link)
+{
+ StarServer* stars = (StarServer*) link;
+
+ Sleep(3000);
+
+ if (stars) {
+ stars->Exit();
+ return 0;
+ }
+
+ return (DWORD) E_POINTER;
+}
+
+DWORD WINAPI StarServerRestartProc(LPVOID link)
+{
+ StarServer* stars = (StarServer*) link;
+
+ Sleep(3000);
+
+#ifdef STARSHATTER_DEMO_RELEASE
+
+ if (stars) {
+ char cmdline[256];
+ strcpy(cmdline, "StarDemo -server");
+
+ STARTUPINFO s;
+ ZeroMemory(&s, sizeof(s));
+ s.cb = sizeof(s);
+
+ PROCESS_INFORMATION pi;
+ ZeroMemory(&pi, sizeof(pi));
+
+ CreateProcess("StarDemo.exe", cmdline, 0, 0, 0, 0, 0, 0, &s, &pi);
+ stars->Exit();
+ return 0;
+ }
+
+#else
+
+ if (stars) {
+ char cmdline[256];
+ strcpy(cmdline, "stars -server");
+
+ STARTUPINFO s;
+ ZeroMemory(&s, sizeof(s));
+ s.cb = sizeof(s);
+
+ PROCESS_INFORMATION pi;
+ ZeroMemory(&pi, sizeof(pi));
+
+ CreateProcess("stars.exe", cmdline, 0, 0, 0, 0, 0, 0, &s, &pi);
+ stars->Exit();
+ return 0;
+ }
+
+#endif
+
+ return (DWORD) E_POINTER;
+}
+
+void
+StarServer::Shutdown(bool restart)
+{
+ DWORD thread_id = 0;
+
+ if (restart)
+ CreateThread(0, 4096, StarServerRestartProc, (LPVOID) this, 0, &thread_id);
+ else
+ CreateThread(0, 4096, StarServerShutdownProc, (LPVOID) this, 0, &thread_id);
+} \ No newline at end of file
diff --git a/Stars45/StarServer.h b/Stars45/StarServer.h
new file mode 100644
index 0000000..62a8b0c
--- /dev/null
+++ b/Stars45/StarServer.h
@@ -0,0 +1,76 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars
+ FILE: StarServer.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef StarServer_h
+#define StarServer_h
+
+#include "Types.h"
+#include "Game.h"
+#include "Bitmap.h"
+#include "KeyMap.h"
+
+// +--------------------------------------------------------------------+
+
+class Campaign;
+class Ship;
+class Sim;
+class FadeView;
+class CameraDirector;
+class MultiController;
+class MouseController;
+class DataLoader;
+
+class NetServer;
+class NetLobbyServer;
+
+// +--------------------------------------------------------------------+
+
+class StarServer : public Game
+{
+public:
+ StarServer();
+ virtual ~StarServer();
+
+ virtual bool Init(HINSTANCE hi, HINSTANCE hpi, LPSTR cmdline, int nCmdShow);
+ virtual bool InitGame();
+ virtual void GameState();
+ virtual bool OnPaint();
+
+ enum MODE { MENU_MODE, // main menu
+ LOAD_MODE, // loading mission into simulator
+ PLAY_MODE // active simulation
+ };
+
+ int GetGameMode() { return game_mode; }
+ void SetGameMode(int mode);
+ void SetNextMission(const char* script);
+
+ void CreateWorld();
+ void Shutdown(bool restart=false);
+
+ static StarServer* GetInstance() { return instance; }
+
+
+protected:
+ virtual bool GameLoop();
+ virtual void UpdateWorld();
+ virtual void InstantiateMission();
+
+ static StarServer* instance;
+ NetServer* admin_server;
+ NetLobbyServer* lobby_server;
+ DataLoader* loader;
+
+ int game_mode;
+ DWORD time_mark;
+ DWORD minutes;
+};
+
+#endif StarServer_h
diff --git a/Stars45/StarSystem.cpp b/Stars45/StarSystem.cpp
new file mode 100644
index 0000000..b403cdb
--- /dev/null
+++ b/Stars45/StarSystem.cpp
@@ -0,0 +1,1974 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: StarSystem.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Various Heavenly Bodies
+*/
+
+#include "MemDebug.h"
+#include "StarSystem.h"
+#include "Galaxy.h"
+#include "Sky.h"
+#include "Starshatter.h"
+#include "TerrainRegion.h"
+#include "TerrainHaze.h"
+#include "Weather.h"
+
+#include "Game.h"
+#include "Sound.h"
+#include "Solid.h"
+#include "Light.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Scene.h"
+#include "ParseUtil.h"
+
+const double epoch = 0.5e9;
+ double StarSystem::stardate = 0;
+
+// +====================================================================+
+
+static double base_time = 0;
+static WORD oldcw = 0;
+static WORD fpcw = 0;
+
+void FPU2Extended()
+{
+ _asm { fstcw oldcw }
+ fpcw = oldcw | 0x0300; // set 80-bit precision mode
+ _asm { fldcw fpcw }
+}
+
+void FPURestore()
+{
+ // well, I don't actually WANT single-precision mode
+ // so I think I'll just ignore this...
+
+ //_asm { fldcw oldcw }
+}
+
+void StarSystem::SetBaseTime(double t, bool absolute)
+{
+ FPU2Extended();
+
+ if (absolute) {
+ base_time = t;
+ CalcStardate();
+ }
+
+ else if (t > 0) {
+ if (t > epoch) t -= epoch;
+ base_time = t;
+ CalcStardate();
+ }
+}
+
+double StarSystem::GetBaseTime()
+{
+ return base_time;
+}
+
+void StarSystem::CalcStardate()
+{
+ if (base_time < 1) {
+ time_t clock_seconds;
+ time(&clock_seconds);
+
+ base_time = clock_seconds;
+
+ while (base_time < 0)
+ base_time += epoch;
+ }
+
+ FPU2Extended();
+
+ double gtime = (double) Game::GameTime() / 1000.0;
+ double sdate = gtime + base_time + epoch;
+
+ stardate = sdate;
+
+ FPURestore();
+}
+
+static const double GRAV = 6.673e-11;
+static const int NAMELEN = 64;
+
+// +====================================================================+
+
+StarSystem::StarSystem(const char* sys_name, Point l, int iff, int s)
+ : name(sys_name), affiliation(iff), sky_stars(0), sky_dust(0), loc(l), seq(s),
+ active_region(0), instantiated(false), ambient(0,0,0),
+ sun_color(255,255,255), sun_scale(1), point_stars(0), poly_stars(0),
+ nebula(0), haze(0)
+{
+ center = new(__FILE__,__LINE__) Orbital(this, "CG", Orbital::NOTHING, 1.0e35f, 0.0f, 0.0f, 0);
+ radius = 0.0f;
+}
+
+// +--------------------------------------------------------------------+
+
+StarSystem::~StarSystem()
+{
+ Print(" Destroying Star System %s\n", (const char*) name);
+
+ if (instantiated) {
+ Deactivate();
+ Destroy();
+ }
+
+ bodies.destroy();
+ regions.destroy();
+ all_regions.clear(); // do not destroy these!
+
+ delete center;
+}
+
+// +--------------------------------------------------------------------+
+
+static OrbitalBody* primary_star = 0;
+static OrbitalBody* primary_planet = 0;
+static OrbitalBody* primary_moon = 0;
+
+void
+StarSystem::Load()
+{
+ CalcStardate();
+ active_region = 0;
+
+ BYTE* block = 0;
+ DataLoader* loader = DataLoader::GetLoader();
+ datapath = loader->GetDataPath();
+
+ sprintf(filename, "%s/%s.def", (const char*) name, (const char*) name);
+
+ Print("Loading StarSystem: %s\n", filename);
+ loader->LoadBuffer(filename, block, true);
+
+ if (!block) {
+ Print("ERROR: invalid star system file '%s'\n", filename);
+ exit(-2);
+ return;
+ }
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block));
+
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'\n", filename);
+ exit(-3);
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "STARSYSTEM") {
+ Print("ERROR: invalid star system file '%s'\n", filename);
+ term->print(10);
+ exit(-4);
+ return;
+ }
+ }
+
+ // parse the system:
+ do {
+ delete term;
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "name") {
+ char namebuf[NAMELEN];
+ namebuf[0] = 0;
+ GetDefText(namebuf, def, filename);
+
+ if (namebuf[0])
+ name = namebuf;
+ }
+
+ else if (def->name()->value() == "sky") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: sky struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+
+ char imgname[NAMELEN];
+ char magname[NAMELEN];
+ char hazname[NAMELEN];
+
+ imgname[0] = 0;
+ magname[0] = 0;
+ hazname[0] = 0;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "poly_stars")
+ GetDefText(imgname, pdef, filename);
+
+ else if (pdef->name()->value() == "nebula")
+ GetDefText(magname, pdef, filename);
+
+ else if (pdef->name()->value() == "haze")
+ GetDefText(hazname, pdef, filename);
+ }
+ }
+
+ if (imgname[0])
+ sky_poly_stars = imgname;
+
+ if (magname[0])
+ sky_nebula = magname;
+
+ if (hazname[0])
+ sky_haze = hazname;
+ }
+ }
+
+ else if (def->name()->value() == "stars") {
+ GetDefNumber(sky_stars, def, filename);
+ }
+
+ else if (def->name()->value() == "ambient") {
+ Vec3 a;
+ GetDefVec(a, def, filename);
+
+ ambient = Color((BYTE) a.x, (BYTE) a.y, (BYTE) a.z) * 2.5;
+ }
+
+ else if (def->name()->value() == "dust") {
+ GetDefNumber(sky_dust, def, filename);
+ }
+
+ else if (def->name()->value() == "star") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: star struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseStar(val);
+ }
+ }
+
+ else if (def->name()->value() == "planet") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: planet struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParsePlanet(val);
+ }
+ }
+
+ else if (def->name()->value() == "moon") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: moon struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseMoon(val);
+ }
+ }
+
+ else if (def->name()->value() == "region") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: region struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseRegion(val);
+ }
+ }
+
+ else if (def->name()->value() == "terrain") {
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: terrain struct missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ ParseTerrain(val);
+ }
+ }
+
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarSystem::ParseStar(TermStruct* val)
+{
+ char star_name[NAMELEN];
+ char img_name[NAMELEN];
+ char map_name[NAMELEN];
+ double light = 0.0;
+ double radius = 0.0;
+ double rot = 0.0;
+ double mass = 0.0;
+ double orbit = 0.0;
+ double tscale = 1.0;
+ bool retro = false;
+ Color color;
+ Color back;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "name")
+ GetDefText(star_name, pdef, filename);
+
+ else if (pdef->name()->value() == "map" || pdef->name()->value() == "icon")
+ GetDefText(map_name, pdef, filename);
+
+ else if (pdef->name()->value() == "image")
+ GetDefText(img_name, pdef, filename);
+
+ else if (pdef->name()->value() == "mass")
+ GetDefNumber(mass, pdef, filename);
+
+ else if (pdef->name()->value() == "orbit")
+ GetDefNumber(orbit, pdef, filename);
+
+ else if (pdef->name()->value() == "radius")
+ GetDefNumber(radius, pdef, filename);
+
+ else if (pdef->name()->value() == "rotation")
+ GetDefNumber(rot, pdef, filename);
+
+ else if (pdef->name()->value() == "tscale")
+ GetDefNumber(tscale, pdef, filename);
+
+ else if (pdef->name()->value() == "light")
+ GetDefNumber(light, pdef, filename);
+
+ else if (pdef->name()->value() == "retro")
+ GetDefBool(retro, pdef, filename);
+
+ else if (pdef->name()->value() == "color") {
+ Vec3 a;
+ GetDefVec(a, pdef, filename);
+ color = Color((BYTE) a.x, (BYTE) a.y, (BYTE) a.z);
+ }
+
+ else if (pdef->name()->value() == "back" || pdef->name()->value() == "back_color") {
+ Vec3 a;
+ GetDefVec(a, pdef, filename);
+ back = Color((BYTE) a.x, (BYTE) a.y, (BYTE) a.z);
+ }
+ }
+ }
+
+ OrbitalBody* star = new(__FILE__,__LINE__) OrbitalBody(this, star_name, Orbital::STAR, mass, radius, orbit, center);
+ star->map_name = map_name;
+ star->tex_name = img_name;
+ star->light = light;
+ star->tscale = tscale;
+ star->subtype = Star::G;
+ star->retro = retro;
+ star->rotation = rot * 3600;
+ star->color = color;
+ star->back = back;
+
+ // map icon:
+ if (*map_name) {
+ DataLoader::GetLoader()->LoadBitmap(map_name, star->map_icon, Bitmap::BMP_TRANSLUCENT, true);
+ }
+
+ bodies.append(star);
+ primary_star = star;
+ primary_planet = 0;
+ primary_moon = 0;
+
+ if (orbit > StarSystem::radius)
+ StarSystem::radius = orbit;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarSystem::ParsePlanet(TermStruct* val)
+{
+ char pln_name[NAMELEN];
+ char img_name[NAMELEN];
+ char map_name[NAMELEN];
+ char hi_name[NAMELEN];
+ char img_ring[NAMELEN];
+ char glo_name[NAMELEN];
+ char glo_hi_name[NAMELEN];
+ char gloss_name[NAMELEN];
+
+ double radius = 0.0;
+ double mass = 0.0;
+ double orbit = 0.0;
+ double rot = 0.0;
+ double minrad = 0.0;
+ double maxrad = 0.0;
+ double tscale = 1.0;
+ double tilt = 0.0;
+ bool retro = false;
+ bool lumin = false;
+ Color atmos = Color::Black;
+
+ ZeroMemory(pln_name, NAMELEN);
+ ZeroMemory(hi_name, NAMELEN);
+ ZeroMemory(img_ring, NAMELEN);
+ ZeroMemory(glo_name, NAMELEN);
+ ZeroMemory(glo_hi_name, NAMELEN);
+ ZeroMemory(gloss_name, NAMELEN);
+ ZeroMemory(map_name, NAMELEN);
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "name")
+ GetDefText(pln_name, pdef, filename);
+
+ else if (pdef->name()->value() == "map" || pdef->name()->value() == "icon")
+ GetDefText(map_name, pdef, filename);
+
+ else if (pdef->name()->value() == "image")
+ GetDefText(img_name, pdef, filename);
+
+ else if (pdef->name()->value() == "image_west")
+ GetDefText(img_name, pdef, filename);
+
+ else if (pdef->name()->value() == "image_east")
+ GetDefText(img_name, pdef, filename);
+
+ else if (pdef->name()->value() == "glow")
+ GetDefText(glo_name, pdef, filename);
+
+ else if (pdef->name()->value() == "gloss")
+ GetDefText(gloss_name, pdef, filename);
+
+ else if (pdef->name()->value() == "high_res")
+ GetDefText(hi_name, pdef, filename);
+
+ else if (pdef->name()->value() == "high_res_west")
+ GetDefText(hi_name, pdef, filename);
+
+ else if (pdef->name()->value() == "high_res_east")
+ GetDefText(hi_name, pdef, filename);
+
+ else if (pdef->name()->value() == "glow_high_res")
+ GetDefText(glo_hi_name, pdef, filename);
+
+ else if (pdef->name()->value() == "mass")
+ GetDefNumber(mass, pdef, filename);
+
+ else if (pdef->name()->value() == "orbit")
+ GetDefNumber(orbit, pdef, filename);
+
+ else if (pdef->name()->value() == "retro")
+ GetDefBool(retro, pdef, filename);
+
+ else if (pdef->name()->value() == "luminous")
+ GetDefBool(lumin, pdef, filename);
+
+ else if (pdef->name()->value() == "rotation")
+ GetDefNumber(rot, pdef, filename);
+
+ else if (pdef->name()->value() == "radius")
+ GetDefNumber(radius, pdef, filename);
+
+ else if (pdef->name()->value() == "ring")
+ GetDefText(img_ring, pdef, filename);
+
+ else if (pdef->name()->value() == "minrad")
+ GetDefNumber(minrad, pdef, filename);
+
+ else if (pdef->name()->value() == "maxrad")
+ GetDefNumber(maxrad, pdef, filename);
+
+ else if (pdef->name()->value() == "tscale")
+ GetDefNumber(tscale, pdef, filename);
+
+ else if (pdef->name()->value() == "tilt")
+ GetDefNumber(tilt, pdef, filename);
+
+ else if (pdef->name()->value() == "atmosphere") {
+ Vec3 a;
+ GetDefVec(a, pdef, filename);
+ atmos = Color((BYTE) a.x, (BYTE) a.y, (BYTE) a.z);
+ }
+ }
+ }
+
+ OrbitalBody* planet = new(__FILE__,__LINE__) OrbitalBody(this, pln_name, Orbital::PLANET, mass, radius, orbit, primary_star);
+ planet->map_name = map_name;
+ planet->tex_name = img_name;
+ planet->tex_high_res = hi_name;
+ planet->tex_ring = img_ring;
+ planet->tex_glow = glo_name;
+ planet->tex_glow_high_res = glo_hi_name;
+ planet->tex_gloss = gloss_name;
+ planet->ring_min = minrad;
+ planet->ring_max = maxrad;
+ planet->tscale = tscale;
+ planet->tilt = tilt;
+ planet->retro = retro;
+ planet->luminous = lumin;
+ planet->rotation = rot * 3600;
+ planet->atmosphere = atmos;
+
+ if (primary_star)
+ primary_star->satellites.append(planet);
+ else
+ bodies.append(planet);
+
+ primary_planet = planet;
+ primary_moon = 0;
+
+ if (orbit > StarSystem::radius)
+ StarSystem::radius = orbit;
+
+ // map icon:
+ if (map_name[0]) {
+ DataLoader::GetLoader()->LoadBitmap(map_name, planet->map_icon, Bitmap::BMP_TRANSLUCENT, true);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarSystem::ParseMoon(TermStruct* val)
+{
+ char map_name[NAMELEN];
+ char pln_name[NAMELEN];
+ char img_name[NAMELEN];
+ char hi_name[NAMELEN];
+ char glo_name[NAMELEN];
+ char glo_hi_name[NAMELEN];
+ char gloss_name[NAMELEN];
+
+ double radius = 0.0;
+ double mass = 0.0;
+ double orbit = 0.0;
+ double rot = 0.0;
+ double tscale = 1.0;
+ double tilt = 0.0;
+ bool retro = false;
+ Color atmos = Color::Black;
+
+ ZeroMemory(map_name, NAMELEN);
+ ZeroMemory(pln_name, NAMELEN);
+ ZeroMemory(hi_name, NAMELEN);
+ ZeroMemory(img_name, NAMELEN);
+ ZeroMemory(glo_name, NAMELEN);
+ ZeroMemory(glo_hi_name, NAMELEN);
+ ZeroMemory(gloss_name, NAMELEN);
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "name")
+ GetDefText(pln_name, pdef, filename);
+
+ else if (pdef->name()->value() == "map" || pdef->name()->value() == "icon")
+ GetDefText(map_name, pdef, filename);
+
+ else if (pdef->name()->value() == "image")
+ GetDefText(img_name, pdef, filename);
+
+ else if (pdef->name()->value() == "glow")
+ GetDefText(glo_name, pdef, filename);
+
+ else if (pdef->name()->value() == "high_res")
+ GetDefText(hi_name, pdef, filename);
+
+ else if (pdef->name()->value() == "glow_high_res")
+ GetDefText(glo_hi_name, pdef, filename);
+
+ else if (pdef->name()->value() == "gloss")
+ GetDefText(gloss_name, pdef, filename);
+
+ else if (pdef->name()->value() == "mass")
+ GetDefNumber(mass, pdef, filename);
+
+ else if (pdef->name()->value() == "orbit")
+ GetDefNumber(orbit, pdef, filename);
+
+ else if (pdef->name()->value() == "rotation")
+ GetDefNumber(rot, pdef, filename);
+
+ else if (pdef->name()->value() == "retro")
+ GetDefBool(retro, pdef, filename);
+
+ else if (pdef->name()->value() == "radius")
+ GetDefNumber(radius, pdef, filename);
+
+ else if (pdef->name()->value() == "tscale")
+ GetDefNumber(tscale, pdef, filename);
+
+ else if (pdef->name()->value() == "inclination")
+ GetDefNumber(tilt, pdef, filename);
+
+ else if (pdef->name()->value() == "atmosphere") {
+ Vec3 a;
+ GetDefVec(a, pdef, filename);
+ atmos = Color((BYTE) a.x, (BYTE) a.y, (BYTE) a.z);
+ }
+ }
+ }
+
+ OrbitalBody* moon = new(__FILE__,__LINE__) OrbitalBody(this, pln_name, Orbital::MOON, mass, radius, orbit, primary_planet);
+ moon->map_name = map_name;
+ moon->tex_name = img_name;
+ moon->tex_high_res = hi_name;
+ moon->tex_glow = glo_name;
+ moon->tex_glow_high_res = glo_hi_name;
+ moon->tex_gloss = gloss_name;
+ moon->tscale = tscale;
+ moon->retro = retro;
+ moon->rotation = rot * 3600;
+ moon->tilt = tilt;
+ moon->atmosphere = atmos;
+
+ if (primary_planet)
+ primary_planet->satellites.append(moon);
+ else {
+ Print("WARNING: no planet for moon %s in '%s', deleted.\n", pln_name, filename);
+ delete moon;
+ moon = 0;
+ }
+
+ primary_moon = moon;
+
+ // map icon:
+ if (map_name[0]) {
+ DataLoader::GetLoader()->LoadBitmap(map_name, moon->map_icon, Bitmap::BMP_TRANSLUCENT, true);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarSystem::ParseRegion(TermStruct* val)
+{
+ char rgn_name[NAMELEN];
+ char lnk_name[NAMELEN];
+ double size = 1.0e6;
+ double orbit = 0.0;
+ double grid = 25000;
+ double inclination = 0.0;
+ int asteroids = 0;
+
+ List<Text> links;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "name")
+ GetDefText(rgn_name, pdef, filename);
+
+ else if (pdef->name()->value() == "link") {
+ GetDefText(lnk_name, pdef, filename);
+ if (lnk_name[0]) {
+ links.append(new(__FILE__,__LINE__) Text(lnk_name));
+ }
+ }
+
+ else if (pdef->name()->value() == "orbit")
+ GetDefNumber(orbit, pdef, filename);
+
+ else if (pdef->name()->value() == "size")
+ GetDefNumber(size, pdef, filename);
+
+ else if (pdef->name()->value() == "radius")
+ GetDefNumber(size, pdef, filename);
+
+ else if (pdef->name()->value() == "grid")
+ GetDefNumber(grid, pdef, filename);
+
+ else if (pdef->name()->value() == "inclination")
+ GetDefNumber(inclination, pdef, filename);
+
+ else if (pdef->name()->value() == "asteroids")
+ GetDefNumber(asteroids, pdef, filename);
+ }
+ }
+
+ Orbital* primary = primary_moon;
+ if (!primary) primary = primary_planet;
+ if (!primary) primary = primary_star;
+
+ OrbitalRegion* region = new(__FILE__,__LINE__) OrbitalRegion(this, rgn_name, 0, size, orbit, primary);
+ region->grid = grid;
+ region->inclination = inclination;
+ region->asteroids = asteroids;
+ region->links.append(links);
+
+ if (primary)
+ primary->regions.append(region);
+ else
+ regions.append(region);
+
+ all_regions.append(region);
+
+ if (orbit > StarSystem::radius)
+ StarSystem::radius = orbit;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarSystem::ParseTerrain(TermStruct* val)
+{
+ Orbital* primary = primary_moon;
+ if (!primary) primary = primary_planet;
+
+ if (!primary) {
+ Print("WARNING: Terrain region with no primary ignored in '%s'\n", filename);
+ return;
+ }
+
+ TerrainRegion* region = 0;
+
+ Text rgn_name;
+ Text patch_name;
+ Text patch_texture;
+ Text noise_tex0;
+ Text noise_tex1;
+ Text apron_name;
+ Text apron_texture;
+ Text water_texture;
+ Text env_texture_positive_x;
+ Text env_texture_negative_x;
+ Text env_texture_positive_y;
+ Text env_texture_negative_y;
+ Text env_texture_positive_z;
+ Text env_texture_negative_z;
+ Text haze_name;
+ Text sky_name;
+ Text clouds_high;
+ Text clouds_low;
+ Text shades_high;
+ Text shades_low;
+
+ double size = 1.0e6;
+ double grid = 25000;
+ double inclination = 0.0;
+ double scale = 10e3;
+ double mtnscale = 1e3;
+ double fog_density = 0;
+ double fog_scale = 0;
+ double haze_fade = 0;
+ double clouds_alt_high= 0;
+ double clouds_alt_low= 0;
+ double w_period = 0;
+ double w_chances[Weather::NUM_STATES];
+
+ ZeroMemory(w_chances, sizeof(w_chances));
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "name")
+ GetDefText(rgn_name, pdef, filename);
+
+ else if (pdef->name()->value() == "patch")
+ GetDefText(patch_name, pdef, filename);
+
+ else if (pdef->name()->value() == "patch_texture")
+ GetDefText(patch_texture, pdef, filename);
+
+ else if (pdef->name()->value() == "detail_texture_0")
+ GetDefText(noise_tex0, pdef, filename);
+
+ else if (pdef->name()->value() == "detail_texture_1")
+ GetDefText(noise_tex1, pdef, filename);
+
+ else if (pdef->name()->value() == "apron")
+ GetDefText(apron_name, pdef, filename);
+
+ else if (pdef->name()->value() == "apron_texture")
+ GetDefText(apron_texture, pdef, filename);
+
+ else if (pdef->name()->value() == "water_texture")
+ GetDefText(water_texture, pdef, filename);
+
+ else if (pdef->name()->value() == "env_texture_positive_x")
+ GetDefText(env_texture_positive_x, pdef, filename);
+
+ else if (pdef->name()->value() == "env_texture_negative_x")
+ GetDefText(env_texture_negative_x, pdef, filename);
+
+ else if (pdef->name()->value() == "env_texture_positive_y")
+ GetDefText(env_texture_positive_y, pdef, filename);
+
+ else if (pdef->name()->value() == "env_texture_negative_y")
+ GetDefText(env_texture_negative_y, pdef, filename);
+
+ else if (pdef->name()->value() == "env_texture_positive_z")
+ GetDefText(env_texture_positive_z, pdef, filename);
+
+ else if (pdef->name()->value() == "env_texture_negative_z")
+ GetDefText(env_texture_negative_z, pdef, filename);
+
+ else if (pdef->name()->value() == "clouds_high")
+ GetDefText(clouds_high, pdef, filename);
+
+ else if (pdef->name()->value() == "shades_high")
+ GetDefText(shades_high, pdef, filename);
+
+ else if (pdef->name()->value() == "clouds_low")
+ GetDefText(clouds_low, pdef, filename);
+
+ else if (pdef->name()->value() == "shades_low")
+ GetDefText(shades_low, pdef, filename);
+
+ else if (pdef->name()->value() == "haze")
+ GetDefText(haze_name, pdef, filename);
+
+ else if (pdef->name()->value() == "sky_color")
+ GetDefText(sky_name, pdef, filename);
+
+ else if (pdef->name()->value() == "size" ||
+ pdef->name()->value() == "radius")
+ GetDefNumber(size, pdef, filename);
+
+ else if (pdef->name()->value() == "grid")
+ GetDefNumber(grid, pdef, filename);
+
+ else if (pdef->name()->value() == "inclination")
+ GetDefNumber(inclination, pdef, filename);
+
+ else if (pdef->name()->value() == "scale")
+ GetDefNumber(scale, pdef, filename);
+
+ else if (pdef->name()->value() == "mtnscale" ||
+ pdef->name()->value() == "mtn_scale")
+ GetDefNumber(mtnscale, pdef, filename);
+
+ else if (pdef->name()->value() == "fog_density")
+ GetDefNumber(fog_density, pdef, filename);
+
+ else if (pdef->name()->value() == "fog_scale")
+ GetDefNumber(fog_scale, pdef, filename);
+
+ else if (pdef->name()->value() == "haze_fade")
+ GetDefNumber(haze_fade, pdef, filename);
+
+ else if (pdef->name()->value() == "clouds_alt_high")
+ GetDefNumber(clouds_alt_high, pdef, filename);
+
+ else if (pdef->name()->value() == "clouds_alt_low")
+ GetDefNumber(clouds_alt_low, pdef, filename);
+
+ else if (pdef->name()->value() == "weather_period")
+ GetDefNumber(w_period, pdef, filename);
+
+ else if (pdef->name()->value() == "weather_clear")
+ GetDefNumber(w_chances[0], pdef, filename);
+ else if (pdef->name()->value() == "weather_high_clouds")
+ GetDefNumber(w_chances[1], pdef, filename);
+ else if (pdef->name()->value() == "weather_moderate_clouds")
+ GetDefNumber(w_chances[2], pdef, filename);
+ else if (pdef->name()->value() == "weather_overcast")
+ GetDefNumber(w_chances[3], pdef, filename);
+ else if (pdef->name()->value() == "weather_fog")
+ GetDefNumber(w_chances[4], pdef, filename);
+ else if (pdef->name()->value() == "weather_storm")
+ GetDefNumber(w_chances[5], pdef, filename);
+
+ else if (pdef->name()->value() == "layer") {
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: terrain layer struct missing in '%s'\n", filename);
+ }
+ else {
+
+ if (!region)
+ region = new(__FILE__,__LINE__) TerrainRegion(this, rgn_name, size, primary);
+
+ TermStruct* val = pdef->term()->isStruct();
+ ParseLayer(region, val);
+ }
+ }
+ }
+ }
+
+ if (!region)
+ region = new(__FILE__,__LINE__) TerrainRegion(this, rgn_name, size, primary);
+
+ region->grid = grid;
+ region->inclination = inclination;
+ region->patch_name = patch_name;
+ region->patch_texture = patch_texture;
+ region->noise_tex0 = noise_tex0;
+ region->noise_tex1 = noise_tex1;
+ region->apron_name = apron_name;
+ region->apron_texture = apron_texture;
+ region->water_texture = water_texture;
+ region->haze_name = haze_name;
+ region->clouds_high = clouds_high;
+ region->shades_high = shades_high;
+ region->clouds_low = clouds_low;
+ region->shades_low = shades_low;
+ region->scale = scale;
+ region->mtnscale = mtnscale;
+ region->fog_density = fog_density;
+ region->fog_scale = fog_scale;
+ region->haze_fade = haze_fade;
+ region->clouds_alt_high = clouds_alt_high;
+ region->clouds_alt_low = clouds_alt_low;
+
+ region->env_texture_positive_x = env_texture_positive_x;
+ region->env_texture_negative_x = env_texture_negative_x;
+ region->env_texture_positive_y = env_texture_positive_y;
+ region->env_texture_negative_y = env_texture_negative_y;
+ region->env_texture_positive_z = env_texture_positive_z;
+ region->env_texture_negative_z = env_texture_negative_z;
+
+ Weather& weather = region->GetWeather();
+
+ weather.SetPeriod(w_period);
+
+ for (i = 0; i < Weather::NUM_STATES; i++)
+ weather.SetChance(i, w_chances[i]);
+
+ region->LoadSkyColors(sky_name);
+
+ primary->regions.append(region);
+ all_regions.append(region);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarSystem::ParseLayer(TerrainRegion* rgn, TermStruct* val)
+{
+ Text tile_name;
+ Text detail_name;
+ double height = 0;
+
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value().indexOf("tile") == 0)
+ GetDefText(tile_name, pdef, filename);
+
+ else if (pdef->name()->value().indexOf("detail") == 0)
+ GetDefText(detail_name, pdef, filename);
+
+ else if (pdef->name()->value().contains("height") ||
+ pdef->name()->value().contains("alt"))
+ GetDefNumber(height, pdef, filename);
+ }
+ }
+
+ if (rgn)
+ rgn->AddLayer(height, tile_name, detail_name);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarSystem::Create()
+{
+ if (Game::Server())
+ return;
+
+ if (!instantiated) {
+ Print("Creating Star System %s\n", (const char*) name);
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath(datapath);
+
+ // poly star shell:
+ if (sky_poly_stars.length()) {
+ poly_stars = new(__FILE__,__LINE__) Solid;
+ poly_stars->Load(sky_poly_stars, 120);
+ poly_stars->SetLuminous(true);
+ poly_stars->SetInfinite(true);
+
+ Print("Celestial Sphere '%s' loaded\n", (const char*) sky_poly_stars);
+ Print(" radius: %f\n", poly_stars->Radius());
+ }
+
+ if (sky_stars) {
+ Print("Point Stars: %d\n", sky_stars);
+ point_stars = new(__FILE__,__LINE__) Stars(sky_stars);
+ }
+
+ loader->SetDataPath(datapath);
+
+ // nebula:
+ if (sky_nebula.length()) {
+ nebula = new(__FILE__,__LINE__) Solid;
+ nebula->Load(sky_nebula, 100);
+ nebula->SetLuminous(true);
+ nebula->SetInfinite(true);
+
+ Print("Nebular Sky '%s' loaded\n", (const char*) sky_nebula);
+ Print(" radius: %f\n", nebula->Radius());
+ }
+
+ // atmospheric haze:
+ if (sky_haze.length()) {
+ loader->SetDataPath(datapath);
+ haze = new(__FILE__,__LINE__) TerrainHaze();
+ haze->Load(sky_haze, 120);
+
+ haze->SetInfinite(true);
+
+ Print("Atmospheric Haze '%s' loaded\n", (const char*) sky_haze);
+ Print(" radius: %f\n", haze->Radius());
+
+ haze->Hide();
+ }
+
+ loader->SetDataPath(0);
+
+ ListIter<OrbitalBody> star = bodies;
+ while (++star) {
+ CreateBody(*star);
+
+ ListIter<OrbitalBody> planet = star->Satellites();
+ while (++planet) {
+ CreateBody(*planet);
+
+ ListIter<OrbitalBody> moon = planet->Satellites();
+ while (++moon) {
+ CreateBody(*moon);
+ }
+ }
+ }
+ }
+
+ instantiated = true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarSystem::CreateBody(OrbitalBody& body)
+{
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath(datapath);
+
+ // stars:
+ if (body.type == Orbital::STAR) {
+ Point starloc = body.loc;
+ starloc = starloc.OtherHand();
+
+ PlanetRep* rep = new(__FILE__,__LINE__) PlanetRep(body.tex_name,
+ 0,
+ body.radius,
+ starloc,
+ body.tscale);
+
+ rep->SetLuminous(true);
+ rep->MoveTo(loc);
+ body.rep = rep;
+
+ sun_brightness = body.light;
+ sun_color = body.color;
+
+ Light* sun_light = new(__FILE__,__LINE__) Light(1.1f);
+ sun_light->SetColor(sun_color);
+ sun_light->SetShadow(true);
+ sun_light->MoveTo(body.loc);
+ sun_light->SetType(Light::LIGHT_DIRECTIONAL);
+
+ sun_lights.append(sun_light);
+ body.light_rep = sun_light;
+
+ if (body.back != Color::Black) {
+ Light* back_light = new(__FILE__,__LINE__) Light(0.6f);
+ back_light->SetColor(body.back);
+ back_light->SetShadow(false);
+ back_light->MoveTo(body.loc * -1);
+ back_light->SetType(Light::LIGHT_DIRECTIONAL);
+
+ back_lights.append(back_light);
+ body.back_light = back_light;
+ }
+ }
+
+ // planets and moons:
+ else {
+ Point planetloc = body.loc;
+ planetloc = planetloc.OtherHand();
+
+ double rmax = 0;
+ double rmin = 0;
+
+ if (body.ring_max > 0) {
+ rmax = body.ring_max*body.radius;
+ rmin = body.ring_min*body.radius;
+ }
+
+ Text surface = body.tex_name;
+ Text glow = body.tex_glow;
+
+ if (Game::MaxTexSize() >= 512) {
+ if (body.tex_high_res.length())
+ surface = body.tex_high_res;
+
+ if (body.tex_glow_high_res.length())
+ glow = body.tex_glow_high_res;
+ }
+
+ PlanetRep* rep = new(__FILE__,__LINE__) PlanetRep(surface,
+ glow,
+ body.radius,
+ planetloc,
+ body.tscale,
+ body.tex_ring,
+ rmin,
+ rmax,
+ body.atmosphere,
+ body.tex_gloss);
+
+ rep->SetStarSystem(this);
+
+ /***
+ if (body.luminous) {
+ rep->SetLuminous(1);
+ }
+ ***/
+
+ if (body.tilt != 0) {
+ Matrix m;
+ m.Pitch(body.tilt);
+
+ rep->SetOrientation(m);
+ }
+
+ body.rep = rep;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarSystem::Destroy()
+{
+ if (instantiated) {
+ ListIter<OrbitalBody> star_iter = bodies;
+ while (++star_iter) {
+ OrbitalBody* star = star_iter.value();
+
+ GRAPHIC_DESTROY(star->rep);
+ LIGHT_DESTROY(star->light_rep);
+ LIGHT_DESTROY(star->back_light);
+
+ ListIter<OrbitalBody> planet = star->Satellites();
+ while (++planet) {
+ GRAPHIC_DESTROY(planet->rep);
+
+ ListIter<OrbitalBody> moon = planet->Satellites();
+ while (++moon) {
+ GRAPHIC_DESTROY(moon->rep);
+ }
+ }
+ }
+
+ GRAPHIC_DESTROY(point_stars);
+ GRAPHIC_DESTROY(poly_stars);
+ GRAPHIC_DESTROY(nebula);
+ GRAPHIC_DESTROY(haze);
+
+ sun_lights.clear();
+ back_lights.clear();
+ }
+
+ instantiated = false;
+ sun_scale = 1;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarSystem::Activate(Scene& scene)
+{
+ if (!instantiated)
+ Create();
+
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (!stars)
+ return;
+
+ if (point_stars) {
+ scene.AddBackground(point_stars);
+ point_stars->Hide();
+ }
+
+ if (poly_stars) {
+ scene.AddBackground(poly_stars);
+ }
+
+ if (stars->Nebula() && nebula) {
+ scene.AddBackground(nebula);
+ }
+
+ if (haze) {
+ scene.AddBackground(haze);
+ haze->Hide();
+ }
+
+ ListIter<OrbitalBody> star_iter = bodies;
+ while (++star_iter) {
+ OrbitalBody* star = star_iter.value();
+ scene.AddGraphic(star->rep);
+ scene.AddLight(star->light_rep);
+ if (nebula && stars && stars->Nebula()) {
+ star->back_light->SetColor(star->back);
+ }
+ else {
+ Color c = Color(60,60,65);
+ star->back_light->SetColor(c);
+ }
+ scene.AddLight(star->back_light);
+
+ if (nebula && stars && stars->Nebula()) {
+ scene.SetAmbient(ambient);
+ }
+ else {
+ Color c = ambient;
+ int n = (c.Red() + c.Green() + c.Blue()) / 3;
+
+ c = Color(n,n,n);
+ scene.SetAmbient(c);
+ }
+
+ ListIter<OrbitalBody> planet = star->Satellites();
+ while (++planet) {
+ scene.AddGraphic(planet->rep);
+
+ ListIter<OrbitalBody> moon = planet->Satellites();
+ while (++moon) {
+ scene.AddGraphic(moon->rep);
+ }
+ }
+ }
+
+ ExecFrame();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarSystem::Deactivate()
+{
+ if (!instantiated)
+ return;
+
+ active_region = 0;
+
+ if (point_stars && point_stars->GetScene())
+ point_stars->GetScene()->DelBackground(point_stars);
+
+ if (poly_stars && poly_stars->GetScene())
+ poly_stars->GetScene()->DelBackground(poly_stars);
+
+ if (nebula && nebula->GetScene())
+ nebula->GetScene()->DelBackground(nebula);
+
+ if (haze && haze->GetScene()) {
+ haze->GetScene()->DelBackground(haze);
+ haze->Hide();
+ }
+
+ ListIter<OrbitalBody> star = bodies;
+ while (++star) {
+ if (star->rep && star->rep->GetScene())
+ star->rep->GetScene()->DelGraphic(star->rep);
+
+ if (star->light_rep && star->light_rep->GetScene())
+ star->light_rep->GetScene()->DelLight(star->light_rep);
+
+ if (star->back_light && star->back_light->GetScene())
+ star->back_light->GetScene()->DelLight(star->back_light);
+
+ ListIter<OrbitalBody> planet = star->Satellites();
+ while (++planet) {
+ if (planet->rep && planet->rep->GetScene())
+ planet->rep->GetScene()->DelGraphic(planet->rep);
+
+ ListIter<OrbitalBody> moon = planet->Satellites();
+ while (++moon) {
+ if (moon->rep && moon->rep->GetScene())
+ moon->rep->GetScene()->DelGraphic(moon->rep);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarSystem::SetActiveRegion(OrbitalRegion* rgn)
+{
+ Scene* scene = 0;
+
+ if (active_region != rgn) {
+ active_region = rgn;
+
+ if (active_region) {
+ if (active_region->Type() != Orbital::TERRAIN) {
+ if (point_stars) point_stars->Hide();
+ if (poly_stars) poly_stars->Show();
+ if (nebula) nebula->Show();
+ if (haze) haze->Hide();
+ }
+ else {
+ if (point_stars) point_stars->Show();
+ if (poly_stars) poly_stars->Hide();
+ if (nebula) nebula->Hide();
+ if (haze) haze->Show();
+ }
+
+ if (poly_stars) {
+ scene = poly_stars->GetScene();
+ if (scene)
+ scene->SetAmbient(ambient);
+ }
+ else if (nebula) {
+ scene = nebula->GetScene();
+ if (scene)
+ scene->SetAmbient(ambient);
+ }
+
+ ListIter<OrbitalBody> star = bodies;
+ while (++star) {
+ if (star->rep)
+ star->rep->Show();
+ }
+
+ ExecFrame();
+ }
+ else {
+ if (point_stars) point_stars->Hide();
+ if (poly_stars) poly_stars->Hide();
+ if (nebula) nebula->Hide();
+ if (haze) haze->Hide();
+
+
+ ListIter<OrbitalBody> star = bodies;
+ while (++star) {
+ if (star->rep)
+ star->rep->Hide();
+
+ if (star->light_rep) {
+ scene = star->light_rep->GetScene();
+ if (scene)
+ scene->DelLight(star->light_rep);
+ }
+
+ if (star->back_light) {
+ scene = star->back_light->GetScene();
+ if (scene)
+ scene->DelLight(star->back_light);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static BYTE max3(BYTE a, BYTE b, BYTE c)
+{
+ if (a > b)
+ if (a > c) return a;
+ else return c;
+ else
+ if (b > c) return b;
+ else return c;
+}
+
+static BYTE min3(BYTE a, BYTE b, BYTE c)
+{
+ if (a < b)
+ if (a < c) return a;
+ else return c;
+ else
+ if (b < c) return b;
+ else return c;
+}
+
+void
+StarSystem::ExecFrame()
+{
+ CalcStardate();
+
+ ListIter<OrbitalBody> star = bodies;
+ while (++star)
+ star->Update();
+
+ ListIter<OrbitalRegion> region = regions;
+ while (++region)
+ region->Update();
+
+ // update the graphic reps, relative to the active region:
+ if (instantiated && active_region) {
+ Point active_loc = active_region->Location();
+ active_loc = active_loc.OtherHand();
+
+ Scene* scene = 0;
+ TerrainRegion* trgn = 0;
+ bool terrain = (active_region->Type() == Orbital::TERRAIN);
+ Matrix terrain_orientation;
+ Matrix terrain_transformation;
+
+ if (terrain) {
+ trgn = (TerrainRegion*) active_region;
+ Color tmp = trgn->SkyColor();
+ Game::SetScreenColor(tmp);
+
+ tvpn = (active_region->Location() - active_region->Primary()->Location());
+ tvpn.Normalize();
+ tvup = Point(0,0,-1);
+ tvrt = tvpn.cross(tvup);
+ tvrt.Normalize();
+
+ terrain_orientation.Rotate(0, PI/2, 0);
+ terrain_transformation = Matrix(tvrt, tvup, tvpn);
+
+ if (point_stars) {
+ point_stars->SetOrientation(terrain_transformation);
+
+ Color sky_color = trgn->SkyColor();
+ BYTE sky_red = (BYTE) sky_color.Red();
+ BYTE sky_green = (BYTE) sky_color.Green();
+ BYTE sky_blue = (BYTE) sky_color.Blue();
+
+ BYTE Max = max3(sky_red, sky_green, sky_blue);
+ BYTE Min = min3(sky_red, sky_green, sky_blue);
+
+ BYTE lum = (BYTE) (240.0 * (Max + Min) / 510.0);
+
+ if (lum > 50) {
+ point_stars->Hide();
+ }
+ else {
+ Stars* pstars = (Stars*) point_stars;
+
+ pstars->Illuminate(1.0 - lum / 50.0);
+ pstars->Show();
+ }
+
+ scene = point_stars->GetScene();
+ }
+
+ if (haze) {
+ ((TerrainHaze*)haze)->UseTerrainRegion(trgn);
+ scene = haze->GetScene();
+ }
+
+ if (scene) {
+ scene->SetAmbient(ambient);
+ }
+
+ }
+ else {
+ Game::SetScreenColor(Color::Black);
+ }
+
+ double star_alt = 0;
+
+ ListIter<OrbitalBody> star_iter = bodies;
+ while (++star_iter) {
+ OrbitalBody* star = star_iter.value();
+
+ if (active_region->Inclination() != 0) {
+ double distance = (active_region->Location() - star->Location()).length();
+ star_alt = sin(active_region->Inclination()) * distance;
+ }
+
+ if (terrain) {
+ Point sloc = TerrainTransform(star->Location());
+
+ if (star->rep) {
+ star->rep->MoveTo(sloc);
+
+ PlanetRep* pr = (PlanetRep*) star->rep;
+ pr->SetDaytime(true);
+ }
+
+ if (star->light_rep) {
+ star->light_rep->MoveTo(sloc);
+ star->light_rep->SetActive(sloc.y > -100);
+ }
+
+ if (star->back_light) {
+ star->back_light->MoveTo(sloc * -1);
+ star->back_light->SetActive(sloc.y > -100);
+ }
+
+ if (trgn && star->rep) {
+ if (trgn->IsEclipsed())
+ star->rep->Hide();
+ else
+ star->rep->Show();
+ }
+ }
+
+ else {
+ Point sloc = Point(star->Location() - active_region->Location()).OtherHand();
+ sloc.y = star_alt;
+
+ if (star->rep) {
+ star->rep->MoveTo(sloc);
+
+ PlanetRep* pr = (PlanetRep*) star->rep;
+ pr->SetDaytime(false);
+ }
+
+ if (star->light_rep) {
+ star->light_rep->MoveTo(sloc);
+ star->light_rep->SetActive(true);
+ }
+
+ if (star->back_light) {
+ star->back_light->MoveTo(sloc * -1);
+ star->back_light->SetActive(true);
+ }
+ }
+
+ ListIter<OrbitalBody> planet_iter = star->Satellites();
+ while (++planet_iter) {
+ OrbitalBody* planet = planet_iter.value();
+
+ if (planet->rep) {
+ PlanetRep* pr = (PlanetRep*) planet->rep;
+
+ if (terrain) {
+ pr->MoveTo(TerrainTransform(planet->Location()));
+ pr->SetOrientation(terrain_orientation);
+
+ if (planet == active_region->Primary()) {
+ pr->Hide();
+ }
+
+ else {
+ pr->Show();
+ pr->SetDaytime(true);
+ }
+ }
+ else {
+ pr->Show();
+ pr->TranslateBy(active_loc);
+ pr->SetDaytime(false);
+ }
+ }
+
+ ListIter<OrbitalBody> moon_iter = planet->Satellites();
+ while (++moon_iter) {
+ OrbitalBody* moon = moon_iter.value();
+
+ if (moon->rep) {
+ PlanetRep* pr = (PlanetRep*) moon->rep;
+
+ if (terrain) {
+ pr->MoveTo(TerrainTransform(moon->Location()));
+ pr->SetOrientation(terrain_orientation);
+
+ if (moon == active_region->Primary()) {
+ pr->Hide();
+ }
+
+ else {
+ pr->Show();
+ pr->SetDaytime(true);
+ }
+ }
+ else {
+ pr->Show();
+ pr->TranslateBy(active_loc);
+ pr->SetDaytime(false);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Orbital*
+StarSystem::FindOrbital(const char* name)
+{
+ if (!name || !name[0])
+ return 0;
+
+ ListIter<OrbitalBody> star = bodies;
+ while (++star) {
+ if (!stricmp(star->Name(), name))
+ return star.value();
+
+ ListIter<OrbitalRegion> star_rgn = star->Regions();
+ while (++star_rgn) {
+ if (!stricmp(star_rgn->Name(), name))
+ return star_rgn.value();
+ }
+
+ ListIter<OrbitalBody> planet = star->Satellites();
+ while (++planet) {
+ if (!stricmp(planet->Name(), name))
+ return planet.value();
+
+ ListIter<OrbitalRegion> planet_rgn = planet->Regions();
+ while (++planet_rgn) {
+ if (!stricmp(planet_rgn->Name(), name))
+ return planet_rgn.value();
+ }
+
+ ListIter<OrbitalBody> moon = planet->Satellites();
+ while (++moon) {
+ if (!stricmp(moon->Name(), name))
+ return moon.value();
+
+ ListIter<OrbitalRegion> moon_rgn = moon->Regions();
+ while (++moon_rgn) {
+ if (!stricmp(moon_rgn->Name(), name))
+ return moon_rgn.value();
+ }
+ }
+ }
+ }
+
+ ListIter<OrbitalRegion> region = regions;
+ while (++region) {
+ if (!stricmp(region->Name(), name))
+ return region.value();
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+OrbitalRegion*
+StarSystem::FindRegion(const char* name)
+{
+ if (!name || !name[0])
+ return 0;
+
+ ListIter<OrbitalRegion> region = all_regions;
+ while (++region) {
+ if (!stricmp(region->Name(), name))
+ return region.value();
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+StarSystem::HasLinkTo(StarSystem* s) const
+{
+ ListIter<OrbitalRegion> iter = ((StarSystem*) this)->all_regions;
+ while (++iter) {
+ OrbitalRegion* rgn = iter.value();
+
+ ListIter<Text> lnk_iter = rgn->Links();
+ while (++lnk_iter) {
+ Text* t = lnk_iter.value();
+
+ if (s->FindRegion(*t))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+Point
+StarSystem::TerrainTransform(const Point& loc)
+{
+ Point result;
+
+ Point tmp = loc - active_region->Location();
+
+ result.x = tmp * tvrt;
+ result.z = tmp * tvup;
+ result.y = tmp * tvpn;
+
+ return result;
+}
+
+Color
+StarSystem::Ambient() const
+{
+ Color result = ambient;
+ bool terrain = (active_region && active_region->Type() == Orbital::TERRAIN);
+
+ if (terrain) {
+ TerrainRegion* trgn = (TerrainRegion*) active_region;
+ result = trgn->Ambient();
+
+ if (trgn->IsEclipsed())
+ result = result * 0.3;
+ }
+
+ return result;
+}
+
+void
+StarSystem::SetSunlight(Color color, double brightness)
+{
+ sun_color = color;
+ sun_scale = brightness;
+
+ ListIter<Light> sun_iter = sun_lights;
+ while (++sun_iter) {
+ Light* sun_light = sun_iter.value();
+ sun_light->SetColor(color);
+ sun_light->SetIntensity((float) (1.1 * sun_scale));
+ }
+
+ ListIter<Light> back_iter = back_lights;
+ while (++back_iter) {
+ Light* back_light = back_iter.value();
+ back_light->SetIntensity((float) (0.5 * sun_scale));
+ }
+}
+
+void
+StarSystem::SetBacklight(Color color, double brightness)
+{
+ ListIter<Light> back_iter = back_lights;
+ while (++back_iter) {
+ Light* back_light = back_iter.value();
+ back_light->SetColor(color);
+ back_light->SetIntensity((float) (0.5 * brightness));
+ }
+}
+
+void
+StarSystem::RestoreTrueSunColor()
+{
+ ListIter<OrbitalBody> iter = bodies;
+ while (++iter) {
+ OrbitalBody* star = iter.value();
+
+ if (star) {
+ if (star->light_rep) {
+ star->light_rep->SetColor(star->LightColor());
+ star->light_rep->SetIntensity(1.1f);
+ }
+
+ if (star->back_light) {
+ star->back_light->SetColor(star->back);
+ star->back_light->SetIntensity(0.5f);
+ }
+ }
+ }
+}
+
+// +====================================================================+
+
+Color
+Star::GetColor() const
+{
+ return GetColor(seq);
+}
+
+int
+Star::GetSize() const
+{
+ return GetSize(seq);
+}
+
+Color
+Star::GetColor(int s)
+{
+ switch (s) {
+ case O: return Color(128,128,255); break;
+ case B: return Color(192,192,255); break;
+ case A: return Color(220,220,255); break;
+ case F: return Color(255,255,255); break;
+ case G: return Color(255,255,128); break;
+ case K: return Color(255,192,100); break;
+ case M: return Color(255,100,100); break;
+
+ case RED_GIANT: return Color(255, 80, 80); break;
+ case WHITE_DWARF: return Color(255,255,255); break;
+ case BLACK_HOLE: return Color( 0, 0, 0); break;
+ }
+
+ return Color::White;
+}
+
+int
+Star::GetSize(int s)
+{
+ switch (s) {
+ case O: return 4; break;
+ case B: return 4; break;
+ case A: return 3; break;
+ case F: return 3; break;
+ case G: return 2; break;
+ case K: return 2; break;
+ case M: return 1; break;
+
+ case RED_GIANT: return 4; break;
+ case WHITE_DWARF: return 1; break;
+ case BLACK_HOLE: return 3; break;
+ }
+
+ return 3;
+}
+
+// +====================================================================+
+
+Orbital::Orbital(StarSystem* s, const char* n, OrbitalType t, double m, double r, double o, Orbital* p)
+ : name(n), type(t), subtype(0), radius(r), mass(m), orbit(o),
+ phase(0), period(0), rotation(0), retro(false),
+ system(s), primary(p), loc(0, 0, 0), rep(0), velocity(0)
+{
+ if (system && primary && orbit > 0) {
+ velocity = sqrt(GRAV * primary->Mass() / orbit);
+ period = 2 * PI * orbit / velocity;
+ }
+
+ Update();
+}
+
+// +--------------------------------------------------------------------+
+
+Orbital::~Orbital()
+{
+ delete rep;
+ regions.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Orbital::Update()
+{
+ if (system && primary && orbit > 0) {
+ double grade = (retro) ? -1 : 1;
+
+ // orbits are counter clockwise:
+ phase = -2 * PI * grade * StarSystem::Stardate() / period;
+
+ loc = primary->Location() + Point((double) (orbit * cos(phase)),
+ (double) (orbit * sin(phase)),
+ 0);
+ }
+
+ ListIter<OrbitalRegion> region = regions;
+ while (++region)
+ region->Update();
+
+ if (rep)
+ rep->MoveTo(loc.OtherHand());
+}
+
+// +--------------------------------------------------------------------+
+
+Point
+Orbital::PredictLocation(double delta_t)
+{
+ Point predicted_loc = Location();
+
+ if (system && primary && orbit > 0) {
+ predicted_loc = primary->PredictLocation(delta_t);
+
+ double grade = (retro) ? -1 : 1;
+
+ // orbits are(?) counter clockwise:
+ double predicted_phase = (double) (-2 * PI * grade * (StarSystem::Stardate()+delta_t) / period);
+
+ predicted_loc += Point((double) (orbit * cos(predicted_phase)),
+ (double) (orbit * sin(predicted_phase)),
+ 0);
+ }
+
+ return predicted_loc;
+}
+
+void
+Orbital::SetMapIcon(const Bitmap& img)
+{
+ if (img.Width() >=64 && img.Height() >= 64) {
+ map_icon.CopyBitmap(img);
+ map_icon.AutoMask();
+ map_icon.MakeTexture();
+ }
+}
+
+// +====================================================================+
+
+OrbitalBody::OrbitalBody(StarSystem* s, const char* n, OrbitalType t, double m, double r, double o, Orbital* p)
+ : Orbital(s, n, t, m, r, o, p), light(0), light_rep(0), back_light(0),
+ ring_min(0), ring_max(0), tilt(0), luminous(false)
+{
+ Update();
+}
+
+// +--------------------------------------------------------------------+
+
+OrbitalBody::~OrbitalBody()
+{
+ satellites.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+OrbitalBody::Update()
+{
+ Orbital::Update();
+
+ theta = 0;
+
+ if (rotation > 0)
+ theta = -2 * PI * StarSystem::Stardate() / rotation;
+
+ ListIter<OrbitalBody> body = satellites;
+ while (++body)
+ body->Update();
+
+ if (rep && theta != 0) {
+ Matrix m;
+ m.Pitch(tilt);
+ m.Roll(tilt/2);
+ m.Yaw(theta);
+ rep->SetOrientation(m);
+ }
+
+ if (light_rep) {
+ Point bodyloc = loc;
+ bodyloc = bodyloc.OtherHand();
+ light_rep->MoveTo(bodyloc);
+ }
+}
+
+// +====================================================================+
+
+OrbitalRegion::OrbitalRegion(StarSystem* s, const char* n, double m, double r, double o, Orbital* p)
+ : Orbital(s, n, REGION, m, r, o, p), grid(25.0e3f), inclination(0)
+{
+}
+
+// +--------------------------------------------------------------------+
+
+OrbitalRegion::~OrbitalRegion()
+{
+ links.destroy();
+}
+
diff --git a/Stars45/StarSystem.h b/Stars45/StarSystem.h
new file mode 100644
index 0000000..ab22c28
--- /dev/null
+++ b/Stars45/StarSystem.h
@@ -0,0 +1,322 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: StarSystem.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Various heavenly bodies
+*/
+
+#ifndef StarSystem_h
+#define StarSystem_h
+
+#include "Types.h"
+#include "Solid.h"
+#include "Bitmap.h"
+#include "Geometry.h"
+#include "Text.h"
+#include "term.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class StarSystem;
+class Orbital;
+class OrbitalBody;
+class OrbitalRegion;
+class TerrainRegion;
+
+class Graphic;
+class Light;
+class Scene;
+
+// +--------------------------------------------------------------------+
+
+class StarSystem
+{
+public:
+ static const char* TYPENAME() { return "StarSystem"; }
+
+ StarSystem(const char* name, Point loc, int iff=0, int s=4);
+ virtual ~StarSystem();
+
+ int operator == (const StarSystem& s) const { return name == s.name; }
+
+ // operations:
+ virtual void Load();
+ virtual void Create();
+ virtual void Destroy();
+
+ virtual void Activate(Scene& scene);
+ virtual void Deactivate();
+
+ virtual void ExecFrame();
+
+ // accessors:
+ const char* Name() const { return name; }
+ const char* Govt() const { return govt; }
+ const char* Description() const { return description; }
+ int Affiliation() const { return affiliation; }
+ int Sequence() const { return seq; }
+ Point Location() const { return loc; }
+ int NumStars() const { return sky_stars; }
+ int NumDust() const { return sky_dust; }
+ Color Ambient() const;
+
+ List<OrbitalBody>& Bodies() { return bodies; }
+ List<OrbitalRegion>& Regions() { return regions; }
+ List<OrbitalRegion>& AllRegions() { return all_regions; }
+ OrbitalRegion* ActiveRegion() { return active_region; }
+
+ Orbital* FindOrbital(const char* name);
+ OrbitalRegion* FindRegion(const char* name);
+
+ void SetActiveRegion(OrbitalRegion* rgn);
+
+ static void SetBaseTime(double t, bool absolute=false);
+ static double GetBaseTime();
+ static double Stardate() { return stardate; }
+ static void CalcStardate();
+ double Radius() const { return radius; }
+
+ void SetSunlight(Color color, double brightness=1);
+ void SetBacklight(Color color, double brightness=1);
+ void RestoreTrueSunColor();
+ bool HasLinkTo(StarSystem* s) const;
+ const Text& GetDataPath() const { return datapath; }
+
+protected:
+ void ParseStar(TermStruct* val);
+ void ParsePlanet(TermStruct* val);
+ void ParseMoon(TermStruct* val);
+ void ParseRegion(TermStruct* val);
+ void ParseTerrain(TermStruct* val);
+ void ParseLayer(TerrainRegion* rgn, TermStruct* val);
+
+ void CreateBody(OrbitalBody& body);
+ Point TerrainTransform(const Point& loc);
+
+ char filename[64];
+ Text name;
+ Text govt;
+ Text description;
+ Text datapath;
+ int affiliation;
+ int seq;
+ Point loc;
+ static double stardate;
+ double radius;
+ bool instantiated;
+
+ int sky_stars;
+ int sky_dust;
+ Text sky_poly_stars;
+ Text sky_nebula;
+ Text sky_haze;
+ double sky_uscale;
+ double sky_vscale;
+ Color ambient;
+ Color sun_color;
+ double sun_brightness;
+ double sun_scale;
+ List<Light> sun_lights;
+ List<Light> back_lights;
+
+ Graphic* point_stars;
+ Solid* poly_stars;
+ Solid* nebula;
+ Solid* haze;
+
+ List<OrbitalBody> bodies;
+ List<OrbitalRegion> regions;
+ List<OrbitalRegion> all_regions;
+
+ Orbital* center;
+ OrbitalRegion* active_region;
+
+ Point tvpn, tvup, tvrt;
+};
+
+// +--------------------------------------------------------------------+
+
+class Star
+{
+public:
+ static const char* TYPENAME() { return "Star"; }
+
+ Star(const char* n, const Point& l, int s) : name(n), loc(l), seq(s) { }
+ virtual ~Star() { }
+
+ enum SPECTRAL_CLASS { BLACK_HOLE, WHITE_DWARF, RED_GIANT,
+ O, B, A, F, G, K, M };
+
+ int operator == (const Star& s) const { return name == s.name; }
+
+ // accessors:
+ const char* Name() const { return name; }
+ const Point& Location() const { return loc; }
+ int Sequence() const { return seq; }
+ Color GetColor() const;
+ int GetSize() const;
+
+ static Color GetColor(int spectral_class);
+ static int GetSize(int spectral_class);
+
+protected:
+ Text name;
+ Point loc;
+ int seq;
+};
+
+// +--------------------------------------------------------------------+
+
+class Orbital
+{
+ friend class StarSystem;
+
+public:
+ static const char* TYPENAME() { return "Orbital"; }
+
+ enum OrbitalType { NOTHING, STAR, PLANET, MOON, REGION, TERRAIN };
+
+ Orbital(StarSystem* sys, const char* n, OrbitalType t, double m, double r, double o, Orbital* p=0);
+ virtual ~Orbital();
+
+ int operator == (const Orbital& o) const { return type == o.type && name == o.name && system == o.system; }
+ int operator < (const Orbital& o) const { return loc.length() < o.loc.length(); }
+ int operator <= (const Orbital& o) const { return loc.length() <= o.loc.length(); }
+
+ // operations:
+ virtual void Update();
+ Point PredictLocation(double delta_t);
+
+ // accessors:
+ const char* Name() const { return name; }
+ OrbitalType Type() const { return type; }
+ int SubType() const { return subtype; }
+
+ const char* Description() const { return description; }
+ double Mass() const { return mass; }
+ double Radius() const { return radius; }
+ double Rotation() const { return rotation; }
+ double RotationPhase()const { return theta; }
+ double Orbit() const { return orbit; }
+ bool Retrograde() const { return retro; }
+ double Phase() const { return phase; }
+ double Period() const { return period; }
+ Point Location() const { return loc; }
+ Graphic* Rep() const { return rep; }
+
+ const Bitmap& GetMapIcon() const { return map_icon; }
+ void SetMapIcon(const Bitmap& img);
+
+ StarSystem* System() const { return system; }
+ Orbital* Primary() const { return primary; }
+ ListIter<OrbitalRegion> Regions() { return regions; }
+
+protected:
+ Text name;
+ OrbitalType type;
+ int subtype;
+
+ Text description;
+ double mass;
+ double radius;
+ double rotation;
+ double theta;
+ double orbit;
+ double phase;
+ double period;
+ double velocity;
+ Point loc;
+ bool retro;
+ Graphic* rep;
+ Bitmap map_icon;
+
+ StarSystem* system;
+ Orbital* primary;
+
+ List<OrbitalRegion> regions;
+};
+
+// +--------------------------------------------------------------------+
+
+class OrbitalBody : public Orbital
+{
+ friend class StarSystem;
+
+public:
+ static const char* TYPENAME() { return "OrbitalBody"; }
+
+ OrbitalBody(StarSystem* sys, const char* n, OrbitalType t, double m, double r, double o, Orbital* prime=0);
+ virtual ~OrbitalBody();
+
+ // operations:
+ virtual void Update();
+
+ // accessors:
+ ListIter<OrbitalBody> Satellites() { return satellites; }
+
+ double Tilt() const { return tilt; }
+ double RingMin() const { return ring_min; }
+ double RingMax() const { return ring_max; }
+
+ double LightIntensity() const { return light; }
+ Color LightColor() const { return color; }
+ bool Luminous() const { return luminous; }
+
+protected:
+ Text map_name;
+ Text tex_name;
+ Text tex_high_res;
+ Text tex_ring;
+ Text tex_glow;
+ Text tex_glow_high_res;
+ Text tex_gloss;
+
+ double tscale;
+ double light;
+ double ring_min;
+ double ring_max;
+ double tilt;
+ Light* light_rep;
+ Light* back_light;
+ Color color;
+ Color back;
+ Color atmosphere;
+ bool luminous;
+
+ List<OrbitalBody> satellites;
+};
+
+// +--------------------------------------------------------------------+
+
+class OrbitalRegion : public Orbital
+{
+ friend class StarSystem;
+
+public:
+ static const char* TYPENAME() { return "OrbitalRegion"; }
+
+ OrbitalRegion(StarSystem* sys, const char* n, double m, double r, double o, Orbital* prime=0);
+ virtual ~OrbitalRegion();
+
+ double GridSpace() const { return grid; }
+ double Inclination() const { return inclination; }
+ int Asteroids() const { return asteroids; }
+ List<Text>& Links() { return links; }
+
+protected:
+ double grid;
+ double inclination;
+ int asteroids;
+ List<Text> links;
+};
+
+#endif StarSystem_h
+
diff --git a/Stars45/Stars.dsp b/Stars45/Stars.dsp
new file mode 100644
index 0000000..8933134
--- /dev/null
+++ b/Stars45/Stars.dsp
@@ -0,0 +1,1585 @@
+# Microsoft Developer Studio Project File - Name="Stars" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=Stars - 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 "Stars.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 "Stars.mak" CFG="Stars - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "Stars - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "Stars - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "Stars - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "../FoundationEx" /I "../nGenEx" /I "../NetEx" /I "../Parser" /I "../Opcode/OpcodeLib" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 ..\Opcode\OpcodeLib\Release\OpcodeLib.lib ..\ngenex\release\ngenex.lib ..\netex\release\netex.lib ..\zlib\release\zlib.lib ..\libpng\release\libpng.lib wsock32.lib dinput.lib dsound.lib d3d9.lib d3dx9.lib dxguid.lib winmm.lib version.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib ogg_static.lib vorbis_static.lib vorbisfile_static.lib Vfw32.lib /nologo /subsystem:windows /profile /map:"Game/Stars.map" /debug /machine:I386 /out:"Game/Stars.exe"
+
+!ELSEIF "$(CFG)" == "Stars - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../FoundationEx" /I "../nGenEx" /I "../NetEx" /I "../Parser" /I "../Opcode/OpcodeLib" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 ..\Opcode\OpcodeLib\Debug\OpcodeLib.lib ..\ngenex\debug\ngenex.lib ..\netex\debug\netex.lib ..\zlib\debug\zlib.lib ..\libpng\debug\libpng.lib wsock32.lib dinput.lib dsound.lib d3d9.lib d3dx9.lib dxguid.lib winmm.lib version.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib ogg_static.lib vorbis_static.lib vorbisfile_static.lib Vfw32.lib /nologo /subsystem:windows /map /debug /machine:I386 /out:"Game/Stars_d.exe" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "Stars - Win32 Release"
+# Name "Stars - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\Asteroid.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\AudDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\AudioConfig.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Authorization.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\AwardDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\AwardShowDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Callsign.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CameraDirector.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\CameraView.cpp
+
+!IF "$(CFG)" == "Stars - Win32 Release"
+
+# ADD CPP /FAcs
+
+!ELSEIF "$(CFG)" == "Stars - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\Campaign.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignMissionFighter.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignMissionRequest.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignMissionStarship.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignPlanAssignment.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignPlanEvent.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignPlanMission.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignPlanMovement.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignPlanStrategic.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignSaveGame.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignSituationReport.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CarrierAI.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmdDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmdForceDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmdIntelDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmdMissionsDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmdMsgDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmdOrdersDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmdTheaterDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmdTitleDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmpCompleteDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmpFileDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmpLoadDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmpnScreen.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmpSceneDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmpSelectDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CombatAction.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Combatant.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CombatAssignment.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CombatEvent.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CombatGroup.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CombatRoster.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CombatUnit.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CombatZone.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Component.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Computer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConfirmDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Contact.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CtlDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\DebriefDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Debris.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\DetailSet.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\DisplayView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Drive.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\DriveSprite.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Drone.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\DropShipAI.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Element.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\EngDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ExceptionHandler.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ExitDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Explosion.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Farcaster.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\FighterAI.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\FighterTacticalAI.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\FirstTimeDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\FlightComp.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\FlightDeck.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\FlightPlanner.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\FltDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Font.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\FormDef.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Galaxy.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\GameScreen.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Grid.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\GroundAI.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Hangar.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\HardPoint.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Hoop.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\HUDSounds.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\HUDView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Instruction.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Intel.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\JoyDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\KeyDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\KeyMap.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\LandingGear.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\LoadDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\LoadScreen.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Main.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MapView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MenuDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MenuScreen.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MenuView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Mfd.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Mission.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MissionEvent.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MissionTemplate.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModConfig.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModInfo.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModInfoDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Mouse.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnEditDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnEditNavDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnElemDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnEventDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnNavDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnObjDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnPkgDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnSelectDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnWepDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MusicDirector.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MusicTrack.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NavAI.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NavDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NavLight.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NavSystem.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetAddrDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetAdminChat.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetAdminServer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetAuth.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetBrokerClient.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetChat.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetClientConfig.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetClientDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetData.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetFileServlet.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetGame.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetGameClient.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetGameServer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetLobby.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetLobbyClient.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetLobbyDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetLobbyServer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetPacket.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetPassDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetPlayer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetServerConfig.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetServerDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetUnitDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetUser.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetUtil.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NPClientWraps.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\OptDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Particles.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\PlanScreen.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Player.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\PlayerDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Power.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\QuantumDrive.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\QuantumFlash.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\QuantumView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\QuitView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\RadioHandler.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\RadioMessage.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\RadioTraffic.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\RadioView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\RadioVox.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\RLoc.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\SeekerAI.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sensor.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Shield.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ShieldRep.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Ship.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ShipAI.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ShipCtrl.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ShipDesign.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ShipKiller.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ShipSolid.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Shot.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sim.cpp
+
+!IF "$(CFG)" == "Stars - Win32 Release"
+
+# ADD CPP /FAcs
+
+!ELSEIF "$(CFG)" == "Stars - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\SimEvent.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\SimObject.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sky.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Stars.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\StarServer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Starshatter.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\StarshipAI.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\StarshipTacticalAI.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\StarSystem.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\SteerAI.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\System.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\SystemDesign.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\TacRefDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\TacticalAI.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\TacticalView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Terrain.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\TerrainApron.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\TerrainClouds.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\TerrainHaze.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\TerrainPatch.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\TerrainRegion.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Thruster.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\TrackIR.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Trail.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\VidDlg.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\Water.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Weapon.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\WeaponDesign.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\WeaponGroup.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Weather.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\WepView.cpp
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\Asteroid.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\AudDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\AudioConfig.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Authorization.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\AwardDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\AwardShowDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\BaseScreen.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Callsign.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CameraDirector.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Campaign.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignMissionFighter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignMissionRequest.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignMissionStarship.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignPlan.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignPlanAssignment.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignPlanEvent.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignPlanMission.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignPlanMovement.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignPlanStrategic.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CampaignSituationReport.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmdDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmdForceDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmdIntelDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmdMissionsDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmdMsgDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmdOrdersDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmdTheaterDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmdTitleDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmpCompleteDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmpFileDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmpLoadDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmpnScreen.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmpSceneDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CmpSelectDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CombatAction.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CombatEvent.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CombatGroup.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CombatRoster.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CombatUnit.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CombatZone.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Component.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConfirmDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CtlDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\nGenEx\DataLoader.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DebriefDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Debris.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DetailSet.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DisplayView.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DriveSprite.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Drone.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DropShipAI.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Element.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\EngDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ExitDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Explosion.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FighterAI.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FighterTacticalAI.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FlightPlanner.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FltDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Galaxy.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\GameScreen.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Grid.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\GroundAI.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Hoop.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HUDSounds.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HUDView.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Instruction.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Intel.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\JoyDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\KeyDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\KeyMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LoadDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LoadScreen.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MapView.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MenuDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MenuScreen.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MenuView.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Mfd.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Mission.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MissionEvent.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MissionTemplate.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModConfig.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModInfo.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ModInfoDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnEditDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnElemDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnEventDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnNavDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnObjDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnPkgDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnSelectDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MsnWepDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MusicDirector.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MusicTrack.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NavAI.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NavDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NavLight.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetAddrDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetAdminChat.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetAdminServer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetAuth.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetBrokerClient.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetChat.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetClientConfig.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetClientDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetData.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetFileServlet.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetGame.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetGameClient.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetGameServer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetLobby.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetLobbyClient.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetLobbyDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetLobbyServer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetPacket.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetPassDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetPlayer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetServerConfig.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetServerDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetUnitDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetUtil.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\NPClientWraps.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OptDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\PlanScreen.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Player.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\PlayerDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Power.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\QuantumFlash.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\QuantumView.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RadioHandler.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RadioMessage.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RadioView.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RadioVox.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RLoc.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SeekerAI.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ShieldRep.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ShipCtrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ShipKiller.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\StarServer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Starshatter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\StarshipAI.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\StarshipTacticalAI.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\StarSystem.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SteerAI.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SystemDesign.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TacRefDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TacticalAI.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Terrain.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TerrainApron.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TerrainClouds.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TerrainHaze.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TerrainLayer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TerrainPatch.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Thruster.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TrackIR.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Trail.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VidDlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WeaponDesign.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Weather.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WepView.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\Stars.ico
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/Stars45/Stars.dsw b/Stars45/Stars.dsw
new file mode 100644
index 0000000..f7bf4fe
--- /dev/null
+++ b/Stars45/Stars.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Stars"=.\Stars.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/Stars45/Stars.ico b/Stars45/Stars.ico
new file mode 100644
index 0000000..69980ca
--- /dev/null
+++ b/Stars45/Stars.ico
Binary files differ
diff --git a/Stars45/Stars.rc b/Stars45/Stars.rc
new file mode 100644
index 0000000..4eaa114
--- /dev/null
+++ b/Stars45/Stars.rc
@@ -0,0 +1,99 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+Stars ICON DISCARDABLE "Stars.ico"
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 4,5,0,0
+ PRODUCTVERSION 4,5,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "Comments", "\0"
+ VALUE "CompanyName", "Destroyer Studios\0"
+ VALUE "FileDescription", "Stars\0"
+ VALUE "FileVersion", "5, 0, 0, 0\0"
+ VALUE "InternalName", "Stars\0"
+ VALUE "LegalCopyright", "Copyright © 2006\0"
+ VALUE "LegalTrademarks", "\0"
+ VALUE "OriginalFilename", "Stars.exe\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "Starshatter\0"
+ VALUE "ProductVersion", "5, 0, 0, 0\0"
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // !_MAC
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
+
diff --git a/Stars45/Stars.vcxproj b/Stars45/Stars.vcxproj
new file mode 100644
index 0000000..37a5cc1
--- /dev/null
+++ b/Stars45/Stars.vcxproj
@@ -0,0 +1,522 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Template|Win32">
+ <Configuration>Template</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <SccProjectName />
+ <SccLocalPath />
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Template|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Template|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>.\Debug\</OutDir>
+ <IntDir>.\Debug\</IntDir>
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>.\Release\</OutDir>
+ <IntDir>.\Release\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+ <FunctionLevelLinking>false</FunctionLevelLinking>
+ <Optimization>Disabled</Optimization>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <WarningLevel>Level3</WarningLevel>
+ <MinimalRebuild>true</MinimalRebuild>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <AdditionalIncludeDirectories>../FoundationEx;../nGenEx;../NetEx;../Parser;../Opcode/OpcodeLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AssemblerListingLocation>.\Debug\</AssemblerListingLocation>
+ <BrowseInformation>true</BrowseInformation>
+ <PrecompiledHeaderOutputFile>.\Debug\Stars.pch</PrecompiledHeaderOutputFile>
+ <ObjectFileName>.\Debug\</ObjectFileName>
+ <ProgramDataBaseFileName>.\Debug\</ProgramDataBaseFileName>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ </ClCompile>
+ <Midl>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <TypeLibraryName>.\Debug\Stars.tlb</TypeLibraryName>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ </Midl>
+ <ResourceCompile>
+ <Culture>0x0409</Culture>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug\Stars.bsc</OutputFile>
+ </Bscmake>
+ <Link>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <OutputFile>Game/Stars_d.exe</OutputFile>
+ <AdditionalDependencies>..\Opcode\OpcodeLib\Debug\OpcodeLib.lib;..\ngenex\debug\ngenex.lib;..\netex\debug\netex.lib;..\zlib\debug\zlib.lib;..\libpng\debug\libpng.lib;wsock32.lib;dinput.lib;dsound.lib;d3d9.lib;d3dx9.lib;dxguid.lib;winmm.lib;version.lib;ogg_static.lib;vorbis_static.lib;vorbisfile_static.lib;Vfw32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <StringPooling>true</StringPooling>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <Optimization>MaxSpeed</Optimization>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <WarningLevel>Level3</WarningLevel>
+ <AdditionalIncludeDirectories>../FoundationEx;../nGenEx;../NetEx;../Parser;../Opcode/OpcodeLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AssemblerListingLocation>.\Release\</AssemblerListingLocation>
+ <PrecompiledHeaderOutputFile>.\Release\Stars.pch</PrecompiledHeaderOutputFile>
+ <ObjectFileName>.\Release\</ObjectFileName>
+ <ProgramDataBaseFileName>.\Release\</ProgramDataBaseFileName>
+ </ClCompile>
+ <Midl>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <TypeLibraryName>.\Release\Stars.tlb</TypeLibraryName>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ </Midl>
+ <ResourceCompile>
+ <Culture>0x0409</Culture>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release\Stars.bsc</OutputFile>
+ </Bscmake>
+ <Link>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <OutputFile>Game/Stars.exe</OutputFile>
+ <AdditionalDependencies>..\Opcode\OpcodeLib\Release\OpcodeLib.lib;..\ngenex\release\ngenex.lib;..\netex\release\netex.lib;..\zlib\release\zlib.lib;..\libpng\release\libpng.lib;wsock32.lib;dinput.lib;dsound.lib;d3d9.lib;d3dx9.lib;dxguid.lib;winmm.lib;version.lib;ogg_static.lib;vorbis_static.lib;vorbisfile_static.lib;Vfw32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="Asteroid.cpp" />
+ <ClCompile Include="AudDlg.cpp" />
+ <ClCompile Include="AudioConfig.cpp" />
+ <ClCompile Include="Authorization.cpp" />
+ <ClCompile Include="AwardDlg.cpp" />
+ <ClCompile Include="AwardShowDlg.cpp" />
+ <ClCompile Include="Callsign.cpp" />
+ <ClCompile Include="CameraDirector.cpp" />
+ <ClCompile Include="..\nGenEx\CameraView.cpp">
+ <AssemblerOutput Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">All</AssemblerOutput>
+ </ClCompile>
+ <ClCompile Include="Campaign.cpp" />
+ <ClCompile Include="CampaignMissionFighter.cpp" />
+ <ClCompile Include="CampaignMissionRequest.cpp" />
+ <ClCompile Include="CampaignMissionStarship.cpp" />
+ <ClCompile Include="CampaignPlanAssignment.cpp" />
+ <ClCompile Include="CampaignPlanEvent.cpp" />
+ <ClCompile Include="CampaignPlanMission.cpp" />
+ <ClCompile Include="CampaignPlanMovement.cpp" />
+ <ClCompile Include="CampaignPlanStrategic.cpp" />
+ <ClCompile Include="CampaignSaveGame.cpp" />
+ <ClCompile Include="CampaignSituationReport.cpp" />
+ <ClCompile Include="CarrierAI.cpp" />
+ <ClCompile Include="CmdDlg.cpp" />
+ <ClCompile Include="CmdForceDlg.cpp" />
+ <ClCompile Include="CmdIntelDlg.cpp" />
+ <ClCompile Include="CmdMissionsDlg.cpp" />
+ <ClCompile Include="CmdMsgDlg.cpp" />
+ <ClCompile Include="CmdOrdersDlg.cpp" />
+ <ClCompile Include="CmdTheaterDlg.cpp" />
+ <ClCompile Include="CmdTitleDlg.cpp" />
+ <ClCompile Include="CmpCompleteDlg.cpp" />
+ <ClCompile Include="CmpFileDlg.cpp" />
+ <ClCompile Include="CmpLoadDlg.cpp" />
+ <ClCompile Include="CmpnScreen.cpp" />
+ <ClCompile Include="CmpSceneDlg.cpp" />
+ <ClCompile Include="CmpSelectDlg.cpp" />
+ <ClCompile Include="CombatAction.cpp" />
+ <ClCompile Include="Combatant.cpp" />
+ <ClCompile Include="CombatAssignment.cpp" />
+ <ClCompile Include="CombatEvent.cpp" />
+ <ClCompile Include="CombatGroup.cpp" />
+ <ClCompile Include="CombatRoster.cpp" />
+ <ClCompile Include="CombatUnit.cpp" />
+ <ClCompile Include="CombatZone.cpp" />
+ <ClCompile Include="Component.cpp" />
+ <ClCompile Include="Computer.cpp" />
+ <ClCompile Include="ConfirmDlg.cpp" />
+ <ClCompile Include="Contact.cpp" />
+ <ClCompile Include="CtlDlg.cpp" />
+ <ClCompile Include="DebriefDlg.cpp" />
+ <ClCompile Include="Debris.cpp" />
+ <ClCompile Include="DetailSet.cpp" />
+ <ClCompile Include="DisplayView.cpp" />
+ <ClCompile Include="Drive.cpp" />
+ <ClCompile Include="DriveSprite.cpp" />
+ <ClCompile Include="Drone.cpp" />
+ <ClCompile Include="DropShipAI.cpp" />
+ <ClCompile Include="Element.cpp" />
+ <ClCompile Include="EngDlg.cpp" />
+ <ClCompile Include="ExceptionHandler.cpp" />
+ <ClCompile Include="ExitDlg.cpp" />
+ <ClCompile Include="Explosion.cpp" />
+ <ClCompile Include="Farcaster.cpp" />
+ <ClCompile Include="FighterAI.cpp" />
+ <ClCompile Include="FighterTacticalAI.cpp" />
+ <ClCompile Include="FirstTimeDlg.cpp" />
+ <ClCompile Include="FlightComp.cpp" />
+ <ClCompile Include="FlightDeck.cpp" />
+ <ClCompile Include="FlightPlanner.cpp" />
+ <ClCompile Include="FltDlg.cpp" />
+ <ClCompile Include="..\nGenEx\Font.cpp" />
+ <ClCompile Include="..\nGenEx\FormDef.cpp" />
+ <ClCompile Include="Galaxy.cpp" />
+ <ClCompile Include="GameScreen.cpp" />
+ <ClCompile Include="Grid.cpp" />
+ <ClCompile Include="GroundAI.cpp" />
+ <ClCompile Include="Hangar.cpp" />
+ <ClCompile Include="HardPoint.cpp" />
+ <ClCompile Include="Hoop.cpp" />
+ <ClCompile Include="HUDSounds.cpp" />
+ <ClCompile Include="HUDView.cpp" />
+ <ClCompile Include="Instruction.cpp" />
+ <ClCompile Include="Intel.cpp" />
+ <ClCompile Include="JoyDlg.cpp" />
+ <ClCompile Include="KeyDlg.cpp" />
+ <ClCompile Include="KeyMap.cpp" />
+ <ClCompile Include="LandingGear.cpp" />
+ <ClCompile Include="LoadDlg.cpp" />
+ <ClCompile Include="LoadScreen.cpp" />
+ <ClCompile Include="Main.cpp" />
+ <ClCompile Include="MapView.cpp" />
+ <ClCompile Include="MenuDlg.cpp" />
+ <ClCompile Include="MenuScreen.cpp" />
+ <ClCompile Include="MenuView.cpp" />
+ <ClCompile Include="Mfd.cpp" />
+ <ClCompile Include="Mission.cpp" />
+ <ClCompile Include="MissionEvent.cpp" />
+ <ClCompile Include="MissionTemplate.cpp" />
+ <ClCompile Include="ModConfig.cpp" />
+ <ClCompile Include="ModDlg.cpp" />
+ <ClCompile Include="ModInfo.cpp" />
+ <ClCompile Include="ModInfoDlg.cpp" />
+ <ClCompile Include="..\nGenEx\Mouse.cpp" />
+ <ClCompile Include="MsnDlg.cpp" />
+ <ClCompile Include="MsnEditDlg.cpp" />
+ <ClCompile Include="MsnEditNavDlg.cpp" />
+ <ClCompile Include="MsnElemDlg.cpp" />
+ <ClCompile Include="MsnEventDlg.cpp" />
+ <ClCompile Include="MsnNavDlg.cpp" />
+ <ClCompile Include="MsnObjDlg.cpp" />
+ <ClCompile Include="MsnPkgDlg.cpp" />
+ <ClCompile Include="MsnSelectDlg.cpp" />
+ <ClCompile Include="MsnWepDlg.cpp" />
+ <ClCompile Include="MusicDirector.cpp" />
+ <ClCompile Include="MusicTrack.cpp" />
+ <ClCompile Include="NavAI.cpp" />
+ <ClCompile Include="NavDlg.cpp" />
+ <ClCompile Include="NavLight.cpp" />
+ <ClCompile Include="NavSystem.cpp" />
+ <ClCompile Include="NetAddrDlg.cpp" />
+ <ClCompile Include="NetAdminChat.cpp" />
+ <ClCompile Include="NetAdminServer.cpp" />
+ <ClCompile Include="NetAuth.cpp" />
+ <ClCompile Include="NetBrokerClient.cpp" />
+ <ClCompile Include="NetChat.cpp" />
+ <ClCompile Include="NetClientConfig.cpp" />
+ <ClCompile Include="NetClientDlg.cpp" />
+ <ClCompile Include="NetData.cpp" />
+ <ClCompile Include="NetFileServlet.cpp" />
+ <ClCompile Include="NetGame.cpp" />
+ <ClCompile Include="NetGameClient.cpp" />
+ <ClCompile Include="NetGameServer.cpp" />
+ <ClCompile Include="NetLobby.cpp" />
+ <ClCompile Include="NetLobbyClient.cpp" />
+ <ClCompile Include="NetLobbyDlg.cpp" />
+ <ClCompile Include="NetLobbyServer.cpp" />
+ <ClCompile Include="NetPacket.cpp" />
+ <ClCompile Include="NetPassDlg.cpp" />
+ <ClCompile Include="NetPlayer.cpp" />
+ <ClCompile Include="NetServerConfig.cpp" />
+ <ClCompile Include="NetServerDlg.cpp" />
+ <ClCompile Include="NetUnitDlg.cpp" />
+ <ClCompile Include="NetUser.cpp" />
+ <ClCompile Include="NetUtil.cpp" />
+ <ClCompile Include="NPClientWraps.cpp" />
+ <ClCompile Include="OptDlg.cpp" />
+ <ClCompile Include="..\nGenEx\Particles.cpp" />
+ <ClCompile Include="PlanScreen.cpp" />
+ <ClCompile Include="Player.cpp" />
+ <ClCompile Include="PlayerDlg.cpp" />
+ <ClCompile Include="Power.cpp" />
+ <ClCompile Include="QuantumDrive.cpp" />
+ <ClCompile Include="QuantumFlash.cpp" />
+ <ClCompile Include="QuantumView.cpp" />
+ <ClCompile Include="QuitView.cpp" />
+ <ClCompile Include="RadioHandler.cpp" />
+ <ClCompile Include="RadioMessage.cpp" />
+ <ClCompile Include="RadioTraffic.cpp" />
+ <ClCompile Include="RadioView.cpp" />
+ <ClCompile Include="RadioVox.cpp" />
+ <ClCompile Include="RLoc.cpp" />
+ <ClCompile Include="SeekerAI.cpp" />
+ <ClCompile Include="Sensor.cpp" />
+ <ClCompile Include="Shield.cpp" />
+ <ClCompile Include="ShieldRep.cpp" />
+ <ClCompile Include="Ship.cpp" />
+ <ClCompile Include="ShipAI.cpp" />
+ <ClCompile Include="ShipCtrl.cpp" />
+ <ClCompile Include="ShipDesign.cpp" />
+ <ClCompile Include="ShipKiller.cpp" />
+ <ClCompile Include="ShipSolid.cpp" />
+ <ClCompile Include="Shot.cpp" />
+ <ClCompile Include="Sim.cpp">
+ <AssemblerOutput Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">All</AssemblerOutput>
+ </ClCompile>
+ <ClCompile Include="SimEvent.cpp" />
+ <ClCompile Include="SimObject.cpp" />
+ <ClCompile Include="Sky.cpp" />
+ <ClCompile Include="StarServer.cpp" />
+ <ClCompile Include="Starshatter.cpp" />
+ <ClCompile Include="StarshipAI.cpp" />
+ <ClCompile Include="StarshipTacticalAI.cpp" />
+ <ClCompile Include="StarSystem.cpp" />
+ <ClCompile Include="SteerAI.cpp" />
+ <ClCompile Include="System.cpp" />
+ <ClCompile Include="SystemDesign.cpp" />
+ <ClCompile Include="TacRefDlg.cpp" />
+ <ClCompile Include="TacticalAI.cpp" />
+ <ClCompile Include="TacticalView.cpp" />
+ <ClCompile Include="Terrain.cpp" />
+ <ClCompile Include="TerrainApron.cpp" />
+ <ClCompile Include="TerrainClouds.cpp" />
+ <ClCompile Include="TerrainHaze.cpp" />
+ <ClCompile Include="TerrainPatch.cpp" />
+ <ClCompile Include="TerrainRegion.cpp" />
+ <ClCompile Include="Thruster.cpp" />
+ <ClCompile Include="TrackIR.cpp" />
+ <ClCompile Include="Trail.cpp" />
+ <ClCompile Include="VidDlg.cpp" />
+ <ClCompile Include="..\nGenEx\Water.cpp" />
+ <ClCompile Include="Weapon.cpp" />
+ <ClCompile Include="WeaponDesign.cpp" />
+ <ClCompile Include="WeaponGroup.cpp" />
+ <ClCompile Include="Weather.cpp" />
+ <ClCompile Include="WepView.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="Stars.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="Asteroid.h" />
+ <ClInclude Include="AudDlg.h" />
+ <ClInclude Include="AudioConfig.h" />
+ <ClInclude Include="Authorization.h" />
+ <ClInclude Include="AwardDlg.h" />
+ <ClInclude Include="AwardShowDlg.h" />
+ <ClInclude Include="BaseScreen.h" />
+ <ClInclude Include="Callsign.h" />
+ <ClInclude Include="CameraDirector.h" />
+ <ClInclude Include="Campaign.h" />
+ <ClInclude Include="CampaignMissionFighter.h" />
+ <ClInclude Include="CampaignMissionRequest.h" />
+ <ClInclude Include="CampaignMissionStarship.h" />
+ <ClInclude Include="CampaignPlan.h" />
+ <ClInclude Include="CampaignPlanAssignment.h" />
+ <ClInclude Include="CampaignPlanEvent.h" />
+ <ClInclude Include="CampaignPlanMission.h" />
+ <ClInclude Include="CampaignPlanMovement.h" />
+ <ClInclude Include="CampaignPlanStrategic.h" />
+ <ClInclude Include="CampaignSituationReport.h" />
+ <ClInclude Include="CmdDlg.h" />
+ <ClInclude Include="CmdForceDlg.h" />
+ <ClInclude Include="CmdIntelDlg.h" />
+ <ClInclude Include="CmdMissionsDlg.h" />
+ <ClInclude Include="CmdMsgDlg.h" />
+ <ClInclude Include="CmdOrdersDlg.h" />
+ <ClInclude Include="CmdTheaterDlg.h" />
+ <ClInclude Include="CmdTitleDlg.h" />
+ <ClInclude Include="CmpCompleteDlg.h" />
+ <ClInclude Include="CmpFileDlg.h" />
+ <ClInclude Include="CmpLoadDlg.h" />
+ <ClInclude Include="CmpnScreen.h" />
+ <ClInclude Include="CmpSceneDlg.h" />
+ <ClInclude Include="CmpSelectDlg.h" />
+ <ClInclude Include="CombatAction.h" />
+ <ClInclude Include="CombatEvent.h" />
+ <ClInclude Include="CombatGroup.h" />
+ <ClInclude Include="CombatRoster.h" />
+ <ClInclude Include="CombatUnit.h" />
+ <ClInclude Include="CombatZone.h" />
+ <ClInclude Include="Component.h" />
+ <ClInclude Include="ConfirmDlg.h" />
+ <ClInclude Include="CtlDlg.h" />
+ <ClInclude Include="..\nGenEx\DataLoader.h" />
+ <ClInclude Include="DebriefDlg.h" />
+ <ClInclude Include="Debris.h" />
+ <ClInclude Include="DetailSet.h" />
+ <ClInclude Include="DisplayView.h" />
+ <ClInclude Include="DriveSprite.h" />
+ <ClInclude Include="Drone.h" />
+ <ClInclude Include="DropShipAI.h" />
+ <ClInclude Include="Element.h" />
+ <ClInclude Include="EngDlg.h" />
+ <ClInclude Include="ExitDlg.h" />
+ <ClInclude Include="Explosion.h" />
+ <ClInclude Include="FighterAI.h" />
+ <ClInclude Include="FighterTacticalAI.h" />
+ <ClInclude Include="FlightPlanner.h" />
+ <ClInclude Include="FltDlg.h" />
+ <ClInclude Include="Galaxy.h" />
+ <ClInclude Include="GameScreen.h" />
+ <ClInclude Include="Grid.h" />
+ <ClInclude Include="GroundAI.h" />
+ <ClInclude Include="Hoop.h" />
+ <ClInclude Include="HUDSounds.h" />
+ <ClInclude Include="HUDView.h" />
+ <ClInclude Include="Instruction.h" />
+ <ClInclude Include="Intel.h" />
+ <ClInclude Include="JoyDlg.h" />
+ <ClInclude Include="KeyDlg.h" />
+ <ClInclude Include="KeyMap.h" />
+ <ClInclude Include="LoadDlg.h" />
+ <ClInclude Include="LoadScreen.h" />
+ <ClInclude Include="MapView.h" />
+ <ClInclude Include="MenuDlg.h" />
+ <ClInclude Include="MenuScreen.h" />
+ <ClInclude Include="MenuView.h" />
+ <ClInclude Include="Mfd.h" />
+ <ClInclude Include="Mission.h" />
+ <ClInclude Include="MissionEvent.h" />
+ <ClInclude Include="MissionTemplate.h" />
+ <ClInclude Include="ModConfig.h" />
+ <ClInclude Include="ModDlg.h" />
+ <ClInclude Include="ModInfo.h" />
+ <ClInclude Include="ModInfoDlg.h" />
+ <ClInclude Include="MsnDlg.h" />
+ <ClInclude Include="MsnEditDlg.h" />
+ <ClInclude Include="MsnElemDlg.h" />
+ <ClInclude Include="MsnEventDlg.h" />
+ <ClInclude Include="MsnNavDlg.h" />
+ <ClInclude Include="MsnObjDlg.h" />
+ <ClInclude Include="MsnPkgDlg.h" />
+ <ClInclude Include="MsnSelectDlg.h" />
+ <ClInclude Include="MsnWepDlg.h" />
+ <ClInclude Include="MusicDirector.h" />
+ <ClInclude Include="MusicTrack.h" />
+ <ClInclude Include="NavAI.h" />
+ <ClInclude Include="NavDlg.h" />
+ <ClInclude Include="NavLight.h" />
+ <ClInclude Include="NetAddrDlg.h" />
+ <ClInclude Include="NetAdminChat.h" />
+ <ClInclude Include="NetAdminServer.h" />
+ <ClInclude Include="NetAuth.h" />
+ <ClInclude Include="NetBrokerClient.h" />
+ <ClInclude Include="NetChat.h" />
+ <ClInclude Include="NetClientConfig.h" />
+ <ClInclude Include="NetClientDlg.h" />
+ <ClInclude Include="NetData.h" />
+ <ClInclude Include="NetFileServlet.h" />
+ <ClInclude Include="NetGame.h" />
+ <ClInclude Include="NetGameClient.h" />
+ <ClInclude Include="NetGameServer.h" />
+ <ClInclude Include="NetLobby.h" />
+ <ClInclude Include="NetLobbyClient.h" />
+ <ClInclude Include="NetLobbyDlg.h" />
+ <ClInclude Include="NetLobbyServer.h" />
+ <ClInclude Include="NetPacket.h" />
+ <ClInclude Include="NetPassDlg.h" />
+ <ClInclude Include="NetPlayer.h" />
+ <ClInclude Include="NetServerConfig.h" />
+ <ClInclude Include="NetServerDlg.h" />
+ <ClInclude Include="NetUnitDlg.h" />
+ <ClInclude Include="NetUtil.h" />
+ <ClInclude Include="NPClientWraps.h" />
+ <ClInclude Include="OptDlg.h" />
+ <ClInclude Include="PlanScreen.h" />
+ <ClInclude Include="Player.h" />
+ <ClInclude Include="PlayerDlg.h" />
+ <ClInclude Include="Power.h" />
+ <ClInclude Include="QuantumFlash.h" />
+ <ClInclude Include="QuantumView.h" />
+ <ClInclude Include="RadioHandler.h" />
+ <ClInclude Include="RadioMessage.h" />
+ <ClInclude Include="RadioView.h" />
+ <ClInclude Include="RadioVox.h" />
+ <ClInclude Include="RLoc.h" />
+ <ClInclude Include="SeekerAI.h" />
+ <ClInclude Include="ShieldRep.h" />
+ <ClInclude Include="ShipCtrl.h" />
+ <ClInclude Include="ShipKiller.h" />
+ <ClInclude Include="StarServer.h" />
+ <ClInclude Include="Starshatter.h" />
+ <ClInclude Include="StarshipAI.h" />
+ <ClInclude Include="StarshipTacticalAI.h" />
+ <ClInclude Include="StarSystem.h" />
+ <ClInclude Include="SteerAI.h" />
+ <ClInclude Include="SystemDesign.h" />
+ <ClInclude Include="TacRefDlg.h" />
+ <ClInclude Include="TacticalAI.h" />
+ <ClInclude Include="Terrain.h" />
+ <ClInclude Include="TerrainApron.h" />
+ <ClInclude Include="TerrainClouds.h" />
+ <ClInclude Include="TerrainHaze.h" />
+ <ClInclude Include="TerrainLayer.h" />
+ <ClInclude Include="TerrainPatch.h" />
+ <ClInclude Include="Thruster.h" />
+ <ClInclude Include="TrackIR.h" />
+ <ClInclude Include="Trail.h" />
+ <ClInclude Include="VidDlg.h" />
+ <ClInclude Include="WeaponDesign.h" />
+ <ClInclude Include="Weather.h" />
+ <ClInclude Include="WepView.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="Stars.ico" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Stars45/Stars.vcxproj.filters b/Stars45/Stars.vcxproj.filters
new file mode 100644
index 0000000..eea4571
--- /dev/null
+++ b/Stars45/Stars.vcxproj.filters
@@ -0,0 +1,1123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{b8a66b66-1e1d-446a-891f-0e137a6a2a75}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{3d0b1a1b-1131-48e1-85d3-b5f35ce9219c}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{8540b9f2-52cf-4590-be61-3db9ea2de9f0}</UniqueIdentifier>
+ <Extensions>ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="Asteroid.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="AudDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="AudioConfig.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Authorization.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="AwardDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="AwardShowDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Callsign.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CameraDirector.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\CameraView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Campaign.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CampaignMissionFighter.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CampaignMissionRequest.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CampaignMissionStarship.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CampaignPlanAssignment.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CampaignPlanEvent.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CampaignPlanMission.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CampaignPlanMovement.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CampaignPlanStrategic.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CampaignSaveGame.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CampaignSituationReport.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CarrierAI.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CmdDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CmdForceDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CmdIntelDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CmdMissionsDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CmdMsgDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CmdOrdersDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CmdTheaterDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CmdTitleDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CmpCompleteDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CmpFileDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CmpLoadDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CmpnScreen.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CmpSceneDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CmpSelectDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CombatAction.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Combatant.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CombatAssignment.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CombatEvent.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CombatGroup.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CombatRoster.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CombatUnit.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CombatZone.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Component.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Computer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ConfirmDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Contact.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CtlDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="DebriefDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Debris.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="DetailSet.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="DisplayView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Drive.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="DriveSprite.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Drone.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="DropShipAI.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Element.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="EngDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ExceptionHandler.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ExitDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Explosion.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Farcaster.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FighterAI.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FighterTacticalAI.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FirstTimeDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FlightComp.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FlightDeck.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FlightPlanner.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FltDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Font.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\FormDef.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Galaxy.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="GameScreen.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Grid.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="GroundAI.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Hangar.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="HardPoint.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Hoop.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="HUDSounds.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="HUDView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Instruction.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Intel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="JoyDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="KeyDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="KeyMap.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="LandingGear.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="LoadDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="LoadScreen.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MapView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MenuDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MenuScreen.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MenuView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Mfd.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Mission.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MissionEvent.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MissionTemplate.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ModConfig.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ModDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ModInfo.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ModInfoDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Mouse.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MsnDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MsnEditDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MsnEditNavDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MsnElemDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MsnEventDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MsnNavDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MsnObjDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MsnPkgDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MsnSelectDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MsnWepDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MusicDirector.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MusicTrack.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NavAI.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NavDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NavLight.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NavSystem.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetAddrDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetAdminChat.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetAdminServer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetAuth.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetBrokerClient.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetChat.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetClientConfig.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetClientDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetData.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetFileServlet.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetGame.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetGameClient.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetGameServer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetLobby.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetLobbyClient.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetLobbyDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetLobbyServer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetPacket.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetPassDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetPlayer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetServerConfig.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetServerDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetUnitDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetUser.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NetUtil.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NPClientWraps.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="OptDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Particles.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="PlanScreen.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Player.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="PlayerDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Power.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="QuantumDrive.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="QuantumFlash.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="QuantumView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="QuitView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="RadioHandler.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="RadioMessage.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="RadioTraffic.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="RadioView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="RadioVox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="RLoc.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SeekerAI.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Sensor.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Shield.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ShieldRep.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Ship.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ShipAI.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ShipCtrl.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ShipDesign.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ShipKiller.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ShipSolid.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Shot.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Sim.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SimEvent.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SimObject.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Sky.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="StarServer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Starshatter.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="StarshipAI.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="StarshipTacticalAI.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="StarSystem.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SteerAI.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="System.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SystemDesign.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TacRefDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TacticalAI.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TacticalView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Terrain.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TerrainApron.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TerrainClouds.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TerrainHaze.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TerrainPatch.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TerrainRegion.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Thruster.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TrackIR.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Trail.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VidDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\nGenEx\Water.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Weapon.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WeaponDesign.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WeaponGroup.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Weather.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WepView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="Stars.rc">
+ <Filter>Source Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="Asteroid.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="AudDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="AudioConfig.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Authorization.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="AwardDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="AwardShowDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="BaseScreen.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Callsign.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CameraDirector.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Campaign.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CampaignMissionFighter.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CampaignMissionRequest.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CampaignMissionStarship.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CampaignPlan.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CampaignPlanAssignment.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CampaignPlanEvent.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CampaignPlanMission.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CampaignPlanMovement.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CampaignPlanStrategic.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CampaignSituationReport.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CmdDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CmdForceDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CmdIntelDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CmdMissionsDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CmdMsgDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CmdOrdersDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CmdTheaterDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CmdTitleDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CmpCompleteDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CmpFileDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CmpLoadDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CmpnScreen.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CmpSceneDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CmpSelectDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CombatAction.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CombatEvent.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CombatGroup.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CombatRoster.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CombatUnit.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CombatZone.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Component.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ConfirmDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CtlDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\nGenEx\DataLoader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="DebriefDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Debris.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="DetailSet.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="DisplayView.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="DriveSprite.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Drone.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="DropShipAI.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Element.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="EngDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ExitDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Explosion.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="FighterAI.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="FighterTacticalAI.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="FlightPlanner.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="FltDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Galaxy.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="GameScreen.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Grid.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="GroundAI.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Hoop.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="HUDSounds.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="HUDView.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Instruction.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Intel.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="JoyDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="KeyDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="KeyMap.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="LoadDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="LoadScreen.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MapView.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MenuDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MenuScreen.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MenuView.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Mfd.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Mission.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MissionEvent.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MissionTemplate.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ModConfig.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ModDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ModInfo.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ModInfoDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MsnDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MsnEditDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MsnElemDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MsnEventDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MsnNavDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MsnObjDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MsnPkgDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MsnSelectDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MsnWepDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MusicDirector.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MusicTrack.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NavAI.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NavDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NavLight.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetAddrDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetAdminChat.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetAdminServer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetAuth.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetBrokerClient.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetChat.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetClientConfig.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetClientDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetData.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetFileServlet.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetGame.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetGameClient.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetGameServer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetLobby.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetLobbyClient.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetLobbyDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetLobbyServer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetPacket.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetPassDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetPlayer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetServerConfig.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetServerDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetUnitDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NetUtil.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="NPClientWraps.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="OptDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="PlanScreen.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Player.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="PlayerDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Power.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="QuantumFlash.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="QuantumView.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="RadioHandler.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="RadioMessage.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="RadioView.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="RadioVox.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="RLoc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SeekerAI.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ShieldRep.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ShipCtrl.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ShipKiller.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="StarServer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Starshatter.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="StarshipAI.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="StarshipTacticalAI.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="StarSystem.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SteerAI.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SystemDesign.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="TacRefDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="TacticalAI.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Terrain.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="TerrainApron.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="TerrainClouds.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="TerrainHaze.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="TerrainLayer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="TerrainPatch.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Thruster.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="TrackIR.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Trail.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="VidDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="WeaponDesign.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Weather.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="WepView.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="Stars.ico">
+ <Filter>Resource Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Stars45/Starshatter.cpp b/Stars45/Starshatter.cpp
new file mode 100644
index 0000000..9b6fc38
--- /dev/null
+++ b/Stars45/Starshatter.cpp
@@ -0,0 +1,2903 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Starshatter.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+
+#include "MemDebug.h"
+#include "Starshatter.h"
+
+#include "MenuScreen.h"
+#include "LoadScreen.h"
+#include "PlanScreen.h"
+#include "CmpnScreen.h"
+
+#include "AudioConfig.h"
+#include "MusicDirector.h"
+#include "HUDSounds.h"
+#include "Player.h"
+
+#include "Shot.h"
+#include "Drive.h"
+#include "LandingGear.h"
+#include "Explosion.h"
+#include "FlightDeck.h"
+#include "NavLight.h"
+#include "Debris.h"
+#include "Contact.h"
+#include "QuantumDrive.h"
+#include "Sensor.h"
+#include "Power.h"
+#include "SystemDesign.h"
+#include "WeaponDesign.h"
+
+#include "Campaign.h"
+#include "CampaignSaveGame.h"
+#include "CombatRoster.h"
+#include "CombatZone.h"
+#include "CampaignPlan.h"
+
+#include "Galaxy.h"
+#include "StarSystem.h"
+#include "Mission.h"
+#include "Sim.h"
+#include "SimEvent.h"
+#include "Element.h"
+#include "Ship.h"
+#include "ShipCtrl.h"
+#include "ShipDesign.h"
+#include "HUDView.h"
+#include "MFD.h"
+#include "RadioMessage.h"
+#include "RadioTraffic.h"
+#include "RadioVox.h"
+#include "CameraDirector.h"
+#include "ModConfig.h"
+#include "KeyMap.h"
+
+#include "GameScreen.h"
+#include "QuantumView.h"
+#include "QuitView.h"
+#include "RadioView.h"
+#include "TacticalView.h"
+#include "DisplayView.h"
+
+#include "LoadDlg.h"
+#include "TacRefDlg.h"
+#include "CmpLoadDlg.h"
+#include "Terrain.h"
+
+#include "NetClientConfig.h"
+#include "NetServerConfig.h"
+#include "NetLayer.h"
+#include "NetLobbyClient.h"
+#include "NetLobbyServer.h"
+#include "NetGame.h"
+#include "NetUtil.h"
+
+#include "ParseUtil.h"
+#include "Token.h"
+
+#include "MachineInfo.h"
+#include "Game.h"
+#include "VideoFactory.h"
+#include "Screen.h"
+#include "Window.h"
+#include "ActiveWindow.h"
+#include "Button.h"
+#include "CameraView.h"
+#include "ImgView.h"
+#include "FadeView.h"
+#include "Color.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "FontMgr.h"
+#include "Keyboard.h"
+#include "Joystick.h"
+#include "MouseController.h"
+#include "Mouse.h"
+#include "TrackIR.h"
+#include "EventDispatch.h"
+#include "MultiController.h"
+#include "Archive.h"
+#include "DataLoader.h"
+#include "Random.h"
+#include "Resource.h"
+#include "Universe.h"
+#include "Video.h"
+#include "VideoSettings.h"
+#include "WebBrowser.h"
+
+// +--------------------------------------------------------------------+
+
+int quick_mode = 0;
+char quick_mission_name[64];
+Mission* quick_mission = 0;
+
+int Starshatter::keymap[256];
+int Starshatter::keyalt[256];
+Starshatter* Starshatter::instance = 0;
+
+static Mission* current_mission = 0;
+static Mission* cutscene_mission = 0;
+static double cutscene_basetime = 0;
+static int cut_efx_volume = 100;
+static int cut_wrn_volume = 100;
+static double time_til_change = 0;
+static bool exit_latch = true;
+static bool show_missions = false;
+static bool use_file_system = false;
+static bool no_splash = false;
+
+enum CHAT_MODES {
+ CHAT_BROADCAST = 1,
+ CHAT_TEAM = 2,
+ CHAT_WING = 3,
+ CHAT_UNIT = 4
+};
+
+// +--------------------------------------------------------------------+
+
+Starshatter::Starshatter()
+ : gamewin(0), menuscreen(0), loadscreen(0), planscreen(0),
+ cmpnscreen(0), gamescreen(0), splash(0), splash_index(0),
+ input(0), loader(0), cam_dir(0), music_dir(0),
+ field_of_view(2), time_mark(0), minutes(0),
+ player_ship(0), net_lobby(0),
+ spinning(false), tactical(false), mouse_x(0), mouse_y(0),
+ game_mode(MENU_MODE), mouse_input(0), head_tracker(0),
+ terminal(0), verdana(0), limerick18(0), limerick12(0),
+ HUDfont(0), GUIfont(0), GUI_small_font(0), title_font(0),
+ ocrb(0), req_change_video(0), video_changed(0),
+ lens_flare(true), corona(true), nebula(true), dust(0),
+ load_step(0), load_progress(0),
+ chat_mode(0), exit_time(1.2), cutscene(0)
+{
+ if (!instance)
+ instance = this;
+
+ app_name = "Starshatter: The Gathering Storm";
+ title_text = "STARSHATTER";
+ palette_name = "alpha";
+
+ gamma = 128; // default - flat gamma ramp
+
+ if (!DataLoader::GetLoader())
+ DataLoader::Initialize();
+
+ loader = DataLoader::GetLoader();
+ int loadstat = loader->EnableDatafile("shatter.dat");
+
+ if (loadstat != DataLoader::DATAFILE_OK) {
+ const char* err_msg = loadstat == DataLoader::DATAFILE_INVALID ?
+ "The file 'shatter.dat' appears to have been damaged. Please re-install Starshatter." :
+ "Starshatter cannot open the file 'shatter.dat'. Please re-install Starshatter.";
+
+ ::MessageBox(hwnd, err_msg, "Starshatter - Error", MB_OK);
+ ::Print(err_msg);
+ ::Print("\n\nFATAL ERROR: EXIT.");
+ exit(-1);
+ }
+
+ if (loader->FindFile("vox.dat"))
+ loader->EnableDatafile("vox.dat");
+
+#ifndef STARSHATTER_DEMO_RELEASE
+ if (loader->FindFile("start.dat"))
+ loader->EnableDatafile("start.dat");
+#endif
+
+ loadstat = loader->EnableDatafile("content.dat");
+
+ if (loadstat != DataLoader::DATAFILE_OK) {
+ const char* err_msg = loadstat == DataLoader::DATAFILE_INVALID ?
+ "The file 'content.dat' appears to have been damaged. Please re-install the latest Starshatter update." :
+ "Starshatter cannot open the file 'content.dat'. Please re-install the latest Starshatter update.";
+
+ ::MessageBox(hwnd, err_msg, "Starshatter - Error", MB_OK);
+ ::Print(err_msg);
+ ::Print("\n\nFATAL ERROR: EXIT.");
+ exit(-1);
+ }
+
+ LoadVideoConfig("video.cfg");
+
+ // create the fonts
+ loader->SetDataPath("Fonts/");
+
+ HUDfont = new(__FILE__,__LINE__) Font("HUDfont");
+ FontMgr::Register("HUD", HUDfont);
+
+ GUIfont = new(__FILE__,__LINE__) Font("GUIfont");
+ FontMgr::Register("GUI", GUIfont);
+
+ GUI_small_font = new(__FILE__,__LINE__) Font("GUIsmall");
+ FontMgr::Register("GUIsmall", GUI_small_font);
+
+ limerick12 = new(__FILE__,__LINE__) Font("Limerick12");
+ limerick18 = new(__FILE__,__LINE__) Font("Limerick18");
+ terminal = new(__FILE__,__LINE__) Font("Terminal");
+ verdana = new(__FILE__,__LINE__) Font("Verdana");
+ ocrb = new(__FILE__,__LINE__) Font("OCRB");
+
+ FontMgr::Register("Limerick12", limerick12);
+ FontMgr::Register("Limerick18", limerick18);
+ FontMgr::Register("Terminal", terminal);
+ FontMgr::Register("Verdana", verdana);
+ FontMgr::Register("OCRB", ocrb);
+
+ loader->SetDataPath(0);
+
+ ZeroMemory(keymap, sizeof(keymap));
+ ZeroMemory(keyalt, sizeof(keyalt));
+}
+
+Starshatter::~Starshatter()
+{
+ if (video_changed) {
+ SaveVideoConfig("video.cfg");
+ }
+
+ DeleteFile("video2.cfg");
+ StopLobby();
+
+ if (Status() <= EXIT)
+ Player::Save();
+
+ delete menuscreen;
+ delete loadscreen;
+ delete planscreen;
+ delete gamescreen;
+ delete cmpnscreen;
+
+ menuscreen = 0;
+ loadscreen = 0;
+ planscreen = 0;
+ gamescreen = 0;
+ cmpnscreen = 0;
+
+ music_dir = 0;
+
+ // delete all the ships and stuff
+ // BEFORE getting rid of the system
+ // and weapons catalogs!
+ delete world;
+ world = 0; // don't let base class double delete the world
+
+ delete quick_mission;
+
+ AudioConfig::Close();
+ HUDSounds::Close();
+ MusicDirector::Close();
+
+ Player::Close();
+ Drive::Close();
+ LandingGear::Close();
+ MFD::Close();
+ Explosion::Close();
+ FlightDeck::Close();
+ Campaign::Close();
+ CombatRoster::Close();
+ Galaxy::Close();
+ RadioTraffic::Close();
+ RadioVox::Close();
+ Ship::Close();
+ WeaponDesign::Close();
+ SystemDesign::Close();
+ ModConfig::Close();
+ NetClientConfig::Close();
+ TacticalView::Close();
+ QuantumView::Close();
+ QuitView::Close();
+ RadioView::Close();
+ NetServerConfig::Close();
+
+ Mouse::Close();
+ EventDispatch::Close();
+ FontMgr::Close();
+ Button::Close();
+ DataLoader::Close();
+
+ delete ocrb;
+ delete limerick12;
+ delete limerick18;
+ delete verdana;
+ delete terminal;
+ delete HUDfont;
+ delete GUIfont;
+ delete GUI_small_font;
+ delete input;
+ delete head_tracker;
+
+ instance = 0;
+}
+
+void
+Starshatter::Exit()
+{
+ MusicDirector::SetMode(MusicDirector::NONE);
+ SetGameMode(EXIT_MODE);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Starshatter::OnHelp()
+{
+ WebBrowser browser;
+ browser.OpenURL("http://matrixgames.com/support");
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::MapKeys()
+{
+ int nkeys = keycfg.GetNumKeys();
+
+ if (nkeys > 0) {
+ Starshatter::MapKeys(&keycfg, nkeys);
+ input->MapKeys(keycfg.GetMapping(), nkeys);
+ }
+}
+
+void
+Starshatter::MapKeys(KeyMap* mapping, int nkeys)
+{
+ for (int i = 0; i < nkeys; i++) {
+ KeyMapEntry* k = mapping->GetKeyMap(i);
+
+ if (k->act >= KEY_MAP_FIRST && k->act <= KEY_MAP_LAST)
+ MapKey(k->act, k->key, k->alt);
+ }
+}
+
+void
+Starshatter::MapKey(int act, int key, int alt)
+{
+ keymap[act] = key;
+ keyalt[act] = alt;
+
+ GetAsyncKeyState(key);
+ GetAsyncKeyState(alt);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Starshatter::Init(HINSTANCE hi, HINSTANCE hpi, LPSTR cmdline, int nCmdShow)
+{
+ if (strstr(cmdline, "-win") || strstr(cmdline, "-dbg")) {
+ if (video_settings) {
+ video_settings->is_windowed = true;
+
+ /***
+ // XXX temporary for video capture
+ video_settings->window_width = 720; //800;
+ video_settings->window_height = 386; //431; // 450 - 19
+ /***/
+
+ Print(" STARSHATTER RUNNING IN WINDOW MODE\n");
+ }
+ }
+
+ if (strstr(cmdline, "-filesys")) {
+ use_file_system = true;
+ Print(" FILE SYSTEM ENABLED\n");
+ }
+
+ if (strstr(cmdline, "-nosplash")) {
+ no_splash = true;
+ }
+
+ if (loader)
+ loader->UseFileSystem(use_file_system);
+
+ return Game::Init(hi, hpi, cmdline, nCmdShow);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Starshatter::InitGame()
+{
+ if (!Game::InitGame())
+ return false;
+
+ RandomInit();
+
+ AudioConfig::Initialize();
+ ModConfig::Initialize();
+
+ InitMouse();
+
+ Button::Initialize();
+ EventDispatch::Create();
+ NetClientConfig::Initialize();
+ Player::Initialize();
+ HUDSounds::Initialize();
+
+ int nkeys = keycfg.LoadKeyMap("key.cfg", 256);
+
+ if (nkeys)
+ Print(" Loaded key.cfg\n\n");
+
+ // create the appropriate motion controller and player_ship
+ input = new(__FILE__,__LINE__) MultiController;
+ Keyboard* k = new(__FILE__,__LINE__) Keyboard;
+ input->AddController(k);
+
+ mouse_input = new(__FILE__,__LINE__) MouseController;
+ input->AddController(mouse_input);
+
+ ::Print("\nStarshatter::InitGame() create joystick\n");
+ Joystick* j = new(__FILE__,__LINE__) Joystick;
+ j->SetSensitivity(15, 5000);
+ input->AddController(j);
+
+ Joystick::EnumerateDevices();
+ ::Print("\n");
+
+ head_tracker = new(__FILE__,__LINE__) TrackIR();
+ MapKeys();
+
+ SystemDesign::Initialize("sys.def");
+ WeaponDesign::Initialize("wep.def");
+ MusicDirector::Initialize();
+
+ // if no splashes, we need to initialize the campaign engine now
+ if (no_splash) {
+ Ship::Initialize();
+ Galaxy::Initialize();
+ CombatRoster::Initialize();
+ Campaign::Initialize();
+ }
+
+ // otherwise, the campaign engine will get initialized during the splashes
+ else {
+ SetupSplash();
+ }
+
+ time_mark = Game::GameTime();
+ minutes = 0;
+
+ return true;
+}
+
+void
+Starshatter::InitMouse()
+{
+ if (loader) {
+ loader->SetDataPath(0);
+
+ Mouse::Create(screen);
+ Mouse::Show(false);
+ Mouse::LoadCursor(Mouse::ARROW, "MouseArrow.pcx", Mouse::HOTSPOT_NW);
+ Mouse::LoadCursor(Mouse::CROSS, "MouseCross.pcx", Mouse::HOTSPOT_CTR);
+ Mouse::LoadCursor(Mouse::DRAG, "MouseDrag.pcx", Mouse::HOTSPOT_NW);
+ Mouse::SetCursor(Mouse::ARROW);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::RequestChangeVideo()
+{
+ req_change_video = true;
+}
+
+int
+Starshatter::GetScreenWidth()
+{
+ if (video_settings)
+ return video_settings->GetWidth();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Starshatter::GetScreenHeight()
+{
+ if (video_settings)
+ return video_settings->GetHeight();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::StartOrResumeGame()
+{
+ if (game_mode != MENU_MODE && game_mode != CMPN_MODE)
+ return;
+
+ Player* p = Player::GetCurrentPlayer();
+ if (!p)
+ return;
+
+ List<Campaign>& list = Campaign::GetAllCampaigns();
+ Campaign* c = 0;
+ Text saved = CampaignSaveGame::GetResumeFile();
+
+
+ // resume saved game?
+ if (saved.length()) {
+ CampaignSaveGame savegame;
+ savegame.Load(saved);
+ c = savegame.GetCampaign();
+ }
+
+ // start training campaign?
+ else if (p->Trained() < 255) {
+ c = list[0];
+ c->Load();
+ }
+
+ // start new dynamic campaign sequence?
+ else {
+ c = list[1];
+ c->Load();
+ }
+
+ if (c)
+ Campaign::SelectCampaign(c->Name());
+
+ Mouse::Show(false);
+ SetGameMode(CLOD_MODE);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Starshatter::UseFileSystem()
+{
+ return use_file_system;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::OpenTacticalReference()
+{
+ if (menuscreen && game_mode == MENU_MODE) {
+ menuscreen->ShowLoadDlg();
+
+ LoadDlg* load_dlg = menuscreen->GetLoadDlg();
+
+ if (load_dlg && load_dlg->IsShown()) {
+ load_activity = Game::GetText("Starshatter.load.tac-ref");
+ load_progress = 1;
+ catalog_index = 0;
+ }
+ else {
+ menuscreen->ShowTacRefDlg();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::SetGameMode(int m)
+{
+ if (game_mode == m)
+ return;
+
+ const char* mode_name[] = {
+ "MENU_MODE", // main menu
+ "CLOD_MODE", // loading campaign
+ "CMPN_MODE", // operational command for dynamic campaign
+ "PREP_MODE", // loading mission info for planning
+ "PLAN_MODE", // mission briefing
+ "LOAD_MODE", // loading mission into simulator
+ "PLAY_MODE", // active simulation
+ "EXIT_MODE" // shutting down
+ };
+
+ if (m >= MENU_MODE && m <= EXIT_MODE)
+ Print(">>> Starshatter::SetGameMode(%d) (%s)\n", m, mode_name[m]);
+ else
+ Print(">>> Starshatter::SetGameMode(%d) (UNKNOWN MODE)\n", m);
+
+ MouseController* mouse_con = MouseController::GetInstance();
+ if (mouse_con)
+ mouse_con->SetActive(false);
+
+ if (m == CLOD_MODE || m == PREP_MODE || m == LOAD_MODE) {
+ load_step = 0;
+ load_progress = 0;
+ load_activity = Game::GetText("Starshatter.load.general");
+ paused = true;
+ }
+
+ else if (m == CMPN_MODE) {
+ load_step = 0;
+ load_progress = 100;
+ load_activity = Game::GetText("Starshatter.load.complete");
+ paused = false;
+ }
+
+ else if (m == PLAY_MODE) {
+ Print(" Starting Game...\n");
+
+ player_ship = 0;
+ load_progress = 100;
+ load_activity = Game::GetText("Starshatter.load.complete");
+
+ if (!world) {
+ CreateWorld();
+ InstantiateMission();
+ }
+
+ if (gamescreen)
+ gamescreen->SetFieldOfView(field_of_view);
+
+ HUDView::ClearMessages();
+ RadioView::ClearMessages();
+
+ SetTimeCompression(1);
+ Pause(false);
+
+ Print(" Stardate: %.1f\n", StarSystem::GetBaseTime());
+ }
+
+ else if (m == PLAN_MODE) {
+ if (game_mode == PLAY_MODE) {
+ Print(" Returning to Plan Mode...\n");
+ if (soundcard)
+ soundcard->StopSoundEffects();
+
+ StopNetGame();
+ Pause(true);
+ Print(" Stardate: %.1f\n", StarSystem::GetBaseTime());
+ }
+ }
+
+ else if (m == MENU_MODE) {
+ Print(" Returning to Main Menu...\n");
+
+ if (game_mode == PLAN_MODE || game_mode == PLAY_MODE) {
+ if (soundcard)
+ soundcard->StopSoundEffects();
+
+ StopNetGame();
+ }
+
+ paused = true;
+ }
+
+ if (m == EXIT_MODE) {
+ Print(" Shutting Down (Returning to Windows)...\n");
+
+ if (game_mode == PLAN_MODE || game_mode == PLAY_MODE) {
+ if (soundcard)
+ soundcard->StopSoundEffects();
+
+ StopNetGame();
+ }
+
+ Print(" Stardate: %.1f\n", StarSystem::GetBaseTime());
+ Print(" Bitmap Cache Footprint: %d KB\n", Bitmap::CacheMemoryFootprint() / 1024);
+
+ paused = true;
+ }
+
+ FlushKeys();
+ game_mode = m;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Starshatter::ChangeVideo()
+{
+ bool result = false;
+
+ if (menuscreen) {
+ delete menuscreen;
+ menuscreen = 0;
+ }
+
+ if (loadscreen) {
+ delete loadscreen;
+ loadscreen = 0;
+ }
+
+ if (planscreen) {
+ delete planscreen;
+ planscreen = 0;
+ }
+
+ if (gamescreen) {
+ delete gamescreen;
+ gamescreen = 0;
+ }
+
+ if (cmpnscreen) {
+ delete cmpnscreen;
+ cmpnscreen = 0;
+ }
+
+ loader->SetDataPath(0);
+
+ LoadVideoConfig("video2.cfg");
+
+ result = ResetVideo();
+
+ InitMouse();
+
+ req_change_video = false;
+ video_changed = true;
+
+ return result;
+}
+
+bool
+Starshatter::ResizeVideo()
+{
+ if (Game::ResizeVideo()) {
+ InitMouse();
+ Mouse::Show(true);
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::CreateWorld()
+{
+ RadioTraffic::Initialize();
+ RadioView::Initialize();
+ RadioVox::Initialize();
+ QuantumView::Initialize();
+ QuitView::Initialize();
+ TacticalView::Initialize();
+
+ // create world
+ if (!world) {
+ Sim* sim = new(__FILE__,__LINE__) Sim(input);
+ world = sim;
+ Print(" World Created.\n");
+ }
+
+ cam_dir = CameraDirector::GetInstance();
+}
+
+void
+Starshatter::InstantiateMission()
+{
+ Memory::Check();
+
+ current_mission = 0;
+
+ if (Campaign::GetCampaign()) {
+ current_mission = Campaign::GetCampaign()->GetMission();
+ }
+
+ Sim* sim = (Sim*) world;
+
+ if (sim) {
+ bool dynamic = false;
+ Campaign* campaign = Campaign::GetCampaign();
+
+ if (campaign && campaign->IsDynamic())
+ dynamic = true;
+
+ sim->UnloadMission();
+ sim->LoadMission(current_mission);
+ sim->ExecMission();
+ sim->SetTestMode(test_mode && !dynamic ? true : false);
+
+ Print(" Mission Instantiated.\n");
+ }
+
+ Memory::Check();
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Starshatter::KeyDown(int action) const
+{
+ int k = Joystick::KeyDownMap(action) ||
+ Keyboard::KeyDownMap(action);
+
+ return k;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Starshatter::GameLoop()
+{
+ cam_dir = CameraDirector::GetInstance();
+
+ if (active && paused) {
+ // Route Events to EventTargets
+ EventDispatch* ed = EventDispatch::GetInstance();
+ if (ed)
+ ed->Dispatch();
+
+ UpdateWorld();
+ GameState();
+ UpdateScreen();
+ CollectStats();
+
+ /***
+ static DWORD vmf_time = 0;
+
+ if (real_time() - vmf_time > 5000) {
+ vmf_time = real_time();
+ DWORD vmf = video->VidMemFree() / (1024 * 1024);
+ ::Print("\n###### %02d:%02d - Video Memory Free: %d MB\n\n",
+ vmf_time / 60000,
+ vmf_time / 1000,
+ vmf);
+ }
+ ***/
+ }
+
+ Game::GameLoop();
+ return false; // must return false to keep processing
+ // true tells the outer loop to sleep until a
+ // windows event is available
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::UpdateWorld()
+{
+ long new_time = real_time;
+ double delta = new_time - frame_time; // in milliseconds
+ seconds = max_frame_length; // in seconds
+ gui_seconds = delta * 0.001;
+
+ if (frame_time == 0)
+ gui_seconds = 0;
+
+ if (delta < time_comp * max_frame_length * 1000) {
+ seconds = time_comp * delta * 0.001;
+ }
+ else {
+ seconds = time_comp * max_frame_length;
+ }
+
+ frame_time = new_time;
+
+ Galaxy* galaxy = Galaxy::GetInstance();
+ if (galaxy) galaxy->ExecFrame();
+
+ // Cutscene missions have a tendency to mess with the stardate
+ // to manage time-of-day and camera effects. It's a bad idea to
+ // evaluate campaign actions and events while the cutscene is
+ // changing the time base.
+ if (!cutscene_mission) {
+ Campaign* campaign = Campaign::GetCampaign();
+ if (campaign) campaign->ExecFrame();
+ }
+
+ if (paused) {
+ if (world)
+ world->ExecFrame(0);
+ }
+
+ else {
+ game_time += (DWORD) (seconds * 1000);
+
+ Drive::StartFrame();
+
+ if (world)
+ world->ExecFrame(seconds);
+ }
+
+ if (game_mode == PLAY_MODE || InCutscene()) {
+ if (cam_dir) {
+ if (head_tracker && head_tracker->IsRunning() && !InCutscene()) {
+ head_tracker->ExecFrame();
+ cam_dir->VirtualHead(head_tracker->GetAzimuth(), head_tracker->GetElevation());
+ cam_dir->VirtualHeadOffset(head_tracker->GetX(), head_tracker->GetY(), head_tracker->GetZ());
+ }
+
+ cam_dir->ExecFrame(gui_seconds);
+ }
+
+ Sim* sim = Sim::GetSim();
+ SimRegion* rgn = sim ? sim->GetActiveRegion() : 0;
+
+ if (rgn) {
+ ListIter<Ship> iter = rgn->Ships();
+ while (++iter) {
+ Ship* s = iter.value();
+ s->SelectDetail(seconds);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::GameState()
+{
+ if (splash) {
+ static bool quick_splash = false;
+
+ if (GetKey() != 0)
+ quick_splash = true;
+
+ if (quick_splash) {
+ splash->FadeIn(0);
+ splash->StopHold();
+ splash->FadeOut(0);
+ }
+
+ if (splash->Done()) {
+ splash = 0; // this will get deleted along with gamewin
+ splash_index++;
+
+ if (gamewin) {
+ screen->DelWindow(gamewin);
+ delete gamewin;
+ gamewin = 0;
+ }
+
+ if (splash_index < 2) {
+ Ship::Initialize();
+ Galaxy::Initialize();
+ SetupSplash();
+ }
+ else {
+ CombatRoster::Initialize();
+ Campaign::Initialize();
+ SetupMenuScreen();
+ }
+
+ FlushKeys();
+ }
+ }
+
+ else if (game_mode == MENU_MODE) {
+ bool campaign_select = false;
+
+ if (cmpnscreen) {
+ campaign_select = cmpnscreen->IsShown();
+ cmpnscreen->Hide();
+ }
+
+ if (gamescreen)
+ gamescreen->Hide();
+
+ if (planscreen)
+ planscreen->Hide();
+
+ if (loadscreen)
+ loadscreen->Hide();
+
+ if (!menuscreen) {
+ SetupMenuScreen();
+ }
+ else {
+ menuscreen->Show();
+
+ if (campaign_select)
+ menuscreen->ShowCmpSelectDlg();
+ }
+
+ if (MusicDirector::GetInstance() &&
+ MusicDirector::GetInstance()->GetMode() != MusicDirector::CREDITS)
+ MusicDirector::SetMode(MusicDirector::MENU);
+
+ DoMenuScreenFrame();
+ }
+
+ else if (game_mode == CLOD_MODE ||
+ game_mode == PREP_MODE ||
+ game_mode == LOAD_MODE) {
+ if (menuscreen)
+ menuscreen->Hide();
+
+ if (planscreen)
+ planscreen->Hide();
+
+ if (cmpnscreen)
+ cmpnscreen->Hide();
+
+ if (!loadscreen)
+ SetupLoadScreen();
+ else
+ loadscreen->Show();
+
+ if (game_mode == CLOD_MODE)
+ MusicDirector::SetMode(MusicDirector::MENU);
+ else
+ MusicDirector::SetMode(MusicDirector::BRIEFING);
+
+ DoLoadScreenFrame();
+ }
+
+ else if (game_mode == PLAN_MODE) {
+ if (menuscreen)
+ menuscreen->Hide();
+
+ if (cmpnscreen)
+ menuscreen->Hide();
+
+ if (loadscreen)
+ loadscreen->Hide();
+
+ if (gamescreen)
+ gamescreen->Hide();
+
+ if (!planscreen)
+ SetupPlanScreen();
+ else
+ planscreen->Show();
+
+ Player* p = Player::GetCurrentPlayer();
+ if (p && p->ShowAward()) {
+ if (!planscreen->IsAwardShown())
+ planscreen->ShowAwardDlg();
+ }
+
+ else if (ShipStats::NumStats()) {
+ if (!planscreen->IsDebriefShown()) {
+ planscreen->ShowDebriefDlg();
+ show_missions = true;
+ }
+ }
+
+ else {
+ if (!planscreen->IsMsnShown() && !planscreen->IsNavShown())
+ planscreen->ShowMsnDlg();
+ }
+
+ MusicDirector::SetMode(MusicDirector::BRIEFING);
+
+ DoPlanScreenFrame();
+ }
+
+
+ else if (game_mode == CMPN_MODE) {
+ if (menuscreen)
+ menuscreen->Hide();
+
+ if (planscreen)
+ planscreen->Hide();
+
+ if (loadscreen)
+ loadscreen->Hide();
+
+ if (gamescreen)
+ gamescreen->Hide();
+
+ if (!cmpnscreen)
+ SetupCmpnScreen();
+ else
+ cmpnscreen->Show();
+
+ DoCmpnScreenFrame();
+ }
+
+ else if (game_mode == PLAY_MODE) {
+ if (menuscreen)
+ menuscreen->Hide();
+
+ if (cmpnscreen)
+ cmpnscreen->Hide();
+
+ if (planscreen)
+ planscreen->Hide();
+
+ if (loadscreen)
+ loadscreen->Hide();
+
+ if (!gamescreen)
+ SetupGameScreen();
+ else
+ gamescreen->Show();
+
+ DoGameScreenFrame();
+ }
+
+ if (game_mode == EXIT_MODE) {
+ exit_time -= Game::GUITime();
+
+ if (exit_time <= 0)
+ Game::Exit();
+ }
+
+ if (net_lobby)
+ net_lobby->ExecFrame();
+
+ if (music_dir)
+ music_dir->ExecFrame();
+
+ Memory::Check();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::DoMenuScreenFrame()
+{
+ if (!Mouse::RButton()) {
+ Mouse::SetCursor(Mouse::ARROW);
+ Mouse::Show(true);
+ }
+
+ if (time_til_change > 0)
+ time_til_change -= Game::GUITime();
+
+ if (!menuscreen)
+ return;
+
+ if (KeyDown(KEY_EXIT)) {
+ if (time_til_change <= 0) {
+ time_til_change = 0.5;
+
+ if (!exit_latch && !menuscreen->CloseTopmost()) {
+ menuscreen->ShowExitDlg();
+ }
+ }
+
+ exit_latch = true;
+ }
+ else {
+ exit_latch = false;
+ }
+
+ LoadDlg* load_dlg = menuscreen->GetLoadDlg();
+
+ if (load_dlg && load_dlg->IsShown()) {
+ // load all ship designs in the standard catalog
+ if (catalog_index < ShipDesign::StandardCatalogSize()) {
+ ShipDesign::PreloadCatalog(catalog_index++);
+ load_activity = Game::GetText("Starshatter.load.tac-ref");
+
+ if (load_progress < 95)
+ load_progress++;
+ }
+
+ else {
+ menuscreen->ShowTacRefDlg();
+ }
+ }
+
+ if (show_missions) {
+ if (net_lobby)
+ menuscreen->ShowNetLobbyDlg();
+ else
+ menuscreen->ShowMsnSelectDlg();
+
+ show_missions = false;
+ }
+
+ menuscreen->ExecFrame();
+
+ if (req_change_video) {
+ ChangeVideo();
+ SetupMenuScreen();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::DoPlanScreenFrame()
+{
+ Mouse::SetCursor(Mouse::ARROW);
+
+ if (time_til_change > 0)
+ time_til_change -= Game::GUITime();
+
+ if (KeyDown(KEY_EXIT)) {
+ if (time_til_change <= 0) {
+ time_til_change = 1;
+
+ if (!exit_latch && !planscreen->CloseTopmost()) {
+ Campaign* campaign = Campaign::GetCampaign();
+ if (campaign && (campaign->IsDynamic() || campaign->IsTraining()))
+ SetGameMode(CMPN_MODE);
+ else
+ SetGameMode(MENU_MODE);
+ }
+ }
+
+ exit_latch = true;
+ }
+ else {
+ exit_latch = false;
+ }
+
+ planscreen->ExecFrame();
+ show_missions = true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::DoCmpnScreenFrame()
+{
+ Mouse::SetCursor(Mouse::ARROW);
+
+ if (time_til_change > 0)
+ time_til_change -= Game::GUITime();
+
+ exit_latch = KeyDown(KEY_EXIT) ? true : false;
+
+ if (InCutscene() && player_ship) {
+ // warp effect:
+ if (player_ship->WarpFactor() > 1) {
+ if (player_ship->WarpFactor() > field_of_view)
+ cmpnscreen->SetFieldOfView(player_ship->WarpFactor());
+ else
+ cmpnscreen->SetFieldOfView(field_of_view);
+ }
+
+ else {
+ if (cmpnscreen->GetFieldOfView() != field_of_view)
+ cmpnscreen->SetFieldOfView(field_of_view);
+ }
+ }
+
+ if (InCutscene() && exit_latch) {
+ time_til_change = 1;
+ EndCutscene();
+ EndMission();
+ cmpnscreen->SetFieldOfView(field_of_view);
+ }
+
+ else if (time_til_change <= 0 && exit_latch) {
+ time_til_change = 1;
+
+ if (!cmpnscreen || !cmpnscreen->CloseTopmost()) {
+ SetGameMode(MENU_MODE);
+ }
+ }
+
+ // time control for campaign mode:
+ else if (game_mode == CMPN_MODE) {
+ if (time_til_change <= 0) {
+ if (KeyDown(KEY_PAUSE)) {
+ time_til_change = 1;
+ Pause(!paused);
+ }
+
+ else if (KeyDown(KEY_TIME_COMPRESS)) {
+ time_til_change = 1;
+
+ switch (TimeCompression()) {
+ case 1: SetTimeCompression(2); break;
+ case 2: SetTimeCompression(4); break;
+ case 4: SetTimeCompression(8); break;
+ }
+ }
+
+ else if (KeyDown(KEY_TIME_EXPAND)) {
+ time_til_change = 1;
+
+ switch (TimeCompression()) {
+ case 8: SetTimeCompression( 4); break;
+ case 4: SetTimeCompression( 2); break;
+ default: SetTimeCompression( 1); break;
+ }
+ }
+ }
+ }
+
+ if (show_missions && !InCutscene()) {
+ cmpnscreen->ShowCmdMissionsDlg();
+ show_missions = false;
+ }
+
+ cmpnscreen->ExecFrame();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::DoLoadScreenFrame()
+{
+ Mouse::Show(false);
+ Mouse::SetCursor(Mouse::ARROW);
+
+ if (game_mode == CLOD_MODE) {
+ CmpLoadDlg* dlg = loadscreen->GetCmpLoadDlg();
+
+ switch (load_step) {
+ case 0:
+ load_activity = Game::GetText("Starshatter.load.campaign");
+ load_progress = 1;
+ catalog_index = 0;
+ break;
+
+ case 1:
+ // load all ship designs in the standard catalog
+ if (catalog_index < ShipDesign::StandardCatalogSize()) {
+ ShipDesign::PreloadCatalog(catalog_index++);
+ load_activity = Game::GetText("Starshatter.load.campaign");
+
+ if (load_progress < 80)
+ load_progress++;
+
+ load_step = 0; // force return to current step on next frame
+ }
+ else {
+ load_activity = Game::GetText("Starshatter.load.start");
+ load_progress = 80;
+ }
+ break;
+
+ case 2:
+ if (Campaign::GetCampaign())
+ Campaign::GetCampaign()->Start();
+ break;
+
+ default:
+ if (dlg && load_progress < 100) {
+ load_progress++;
+ }
+ else {
+ load_activity = Game::GetText("Starshatter.load.ready");
+ load_progress = 100;
+ SetGameMode(CMPN_MODE);
+ }
+ break;
+ }
+ }
+ else {
+ switch (load_step) {
+ case 0:
+ load_activity = Game::GetText("Starshatter.load.drives");
+ load_progress = 0;
+ break;
+
+ case 1:
+ Drive::Initialize();
+ LandingGear::Initialize();
+ load_activity = Game::GetText("Starshatter.load.explosions");
+ load_progress = 5;
+ break;
+
+ case 2:
+ Explosion::Initialize();
+ load_activity = Game::GetText("Starshatter.load.systems");
+ load_progress = 10;
+ break;
+
+ case 3:
+ FlightDeck::Initialize();
+ NavLight::Initialize();
+ load_activity = Game::GetText("Starshatter.load.ships");
+ load_progress = 15;
+ break;
+
+ case 4:
+ Ship::Initialize();
+ Shot::Initialize();
+ load_activity = Game::GetText("Starshatter.load.hud");
+ load_progress = 20;
+ break;
+
+ case 5:
+ MFD::Initialize();
+ load_activity = Game::GetText("Starshatter.load.menus");
+ load_progress = 25;
+ break;
+
+ case 6:
+ RadioTraffic::Initialize();
+ RadioView::Initialize();
+ TacticalView::Initialize();
+
+ if (!gamescreen && game_mode != PREP_MODE)
+ SetupGameScreen();
+
+ load_activity = Game::GetText("Starshatter.load.mission");
+ load_progress = 65;
+ break;
+
+ case 7:
+ if (game_mode == PREP_MODE) {
+ if (Campaign::GetCampaign())
+ Campaign::GetCampaign()->GetMission();
+ SetGameMode(PLAN_MODE);
+ load_activity = Game::GetText("Starshatter.load.loaded");
+ load_progress = 100;
+ Button::PlaySound(4);
+ }
+ else {
+ CreateWorld();
+ load_activity = Game::GetText("Starshatter.load.simulation");
+ load_progress = 75;
+ }
+ break;
+
+ case 8:
+ InstantiateMission();
+ load_activity = Game::GetText("Starshatter.load.viewscreen");
+ load_progress = 90;
+ break;
+
+ default:
+ SetGameMode(PLAY_MODE);
+ load_activity = Game::GetText("Starshatter.load.ready");
+ load_progress = 100;
+ break;
+ }
+ }
+
+ load_step++;
+ loadscreen->ExecFrame();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::PlayerCam(int mode)
+{
+ if (player_ship && !player_ship->InTransition() && player_ship->GetFlightPhase() >= Ship::LOCKED) {
+ gamescreen->CloseTopmost();
+ if (mode == CameraDirector::MODE_DROP) {
+ player_ship->DropCam(15, 0);
+ cam_dir->SetShip(player_ship);
+ cam_dir->SetMode(CameraDirector::MODE_DROP, 0);
+ }
+
+ else {
+ cam_dir->SetMode(mode);
+
+ if (mode == CameraDirector::MODE_ORBIT) {
+ MouseController* mouse_con = MouseController::GetInstance();
+ if (mouse_con)
+ mouse_con->SetActive(false);
+ }
+ }
+ }
+}
+
+void
+Starshatter::ViewSelection()
+{
+ Sim* sim = (Sim*) world;
+ List<Ship>& seln = sim->GetSelection().container();
+
+ if (cam_dir) {
+ if (seln.isEmpty())
+ cam_dir->SetViewObject(player_ship);
+
+ else if (seln.size() == 1)
+ cam_dir->SetViewObject(seln[0]);
+
+ else
+ cam_dir->SetViewObjectGroup(seln);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::DoGameScreenFrame()
+{
+ Sim* sim = (Sim*) world;
+
+ if (!gamescreen || !sim)
+ return;
+
+ if (InCutscene()) {
+ if (player_ship) {
+ // warp effect:
+ if (player_ship->WarpFactor() > 1) {
+ if (player_ship->WarpFactor() > field_of_view)
+ gamescreen->SetFieldOfView(player_ship->WarpFactor());
+ else
+ gamescreen->SetFieldOfView(field_of_view);
+ }
+
+ else {
+ if (gamescreen->GetFieldOfView() != field_of_view)
+ gamescreen->SetFieldOfView(field_of_view);
+ }
+ }
+
+ gamescreen->FrameRate(frame_rate);
+ gamescreen->ExecFrame();
+
+ if (KeyDown(KEY_EXIT)) {
+ gamescreen->SetFieldOfView(field_of_view);
+ exit_latch = true;
+ time_til_change = 1;
+
+ sim->SkipCutscene();
+ }
+
+ return;
+ }
+
+ if (time_til_change > 0)
+ time_til_change -= Game::GUITime();
+
+ if (exit_latch && !KeyDown(KEY_EXIT))
+ exit_latch = false;
+
+ DoMouseFrame();
+
+ HUDView* hud_view = HUDView::GetInstance();
+
+ // changing to a new ship?
+ if (player_ship != sim->GetPlayerShip()) {
+ gamescreen->HideNavDlg();
+ gamescreen->HideFltDlg();
+ gamescreen->HideEngDlg();
+
+ Ship* new_player = sim->GetPlayerShip();
+
+ if (new_player) {
+ if (new_player->IsDropship() && Ship::GetFlightModel() < 2 && Ship::GetControlModel() < 1)
+ input->SwapYawRoll(true);
+ else
+ input->SwapYawRoll(false);
+
+ if (hud_view) {
+ hud_view->SetHUDMode(HUDView::HUD_MODE_TAC);
+ hud_view->HideHUDWarn();
+ }
+ }
+ }
+
+ player_ship = sim->GetPlayerShip();
+
+ if (player_ship) {
+ // warp effect:
+ if (player_ship->WarpFactor() > 1) {
+ if (player_ship->WarpFactor() > field_of_view)
+ gamescreen->SetFieldOfView(player_ship->WarpFactor());
+ else
+ gamescreen->SetFieldOfView(field_of_view);
+ }
+
+ else {
+ if (gamescreen->GetFieldOfView() != field_of_view)
+ gamescreen->SetFieldOfView(field_of_view);
+ }
+
+ gamescreen->ShowExternal();
+
+ if (CameraDirector::GetCameraMode() >= CameraDirector::MODE_ORBIT && !player_ship->InTransition())
+ tactical = true;
+ else
+ tactical = false;
+
+ if (player_ship->InTransition())
+ gamescreen->CloseTopmost();
+ }
+
+ if (chat_mode) {
+ DoChatMode();
+ }
+
+ else {
+ DoGameKeys();
+ }
+
+ gamescreen->FrameRate(frame_rate);
+ gamescreen->ExecFrame();
+
+ if (Game::GameTime() - time_mark > 60000) {
+ time_mark = Game::GameTime();
+ minutes++;
+ if (minutes > 60)
+ Print(" TIME %2d:%02d:00\n", minutes/60, minutes%60);
+ else
+ Print(" TIME %2d:00\n", minutes);
+ }
+}
+
+void
+Starshatter::DoGameKeys()
+{
+ Sim* sim = (Sim*) world;
+ HUDView* hud_view = HUDView::GetInstance();
+
+ if (time_til_change <= 0) {
+ if (KeyDown(KEY_CAM_BRIDGE)) {
+ PlayerCam(CameraDirector::MODE_COCKPIT);
+ time_til_change = 0.5;
+ }
+
+ else if (KeyDown(KEY_CAM_VIRT)) {
+ PlayerCam(CameraDirector::MODE_VIRTUAL);
+ time_til_change = 0.5;
+ }
+
+ else if (KeyDown(KEY_CAM_CHASE)) {
+ PlayerCam(CameraDirector::MODE_CHASE);
+ time_til_change = 0.5;
+ }
+
+ else if (KeyDown(KEY_CAM_DROP)) {
+ PlayerCam(CameraDirector::MODE_DROP);
+ time_til_change = 0.5;
+ }
+
+ else if (KeyDown(KEY_CAM_EXTERN)) {
+ PlayerCam(CameraDirector::MODE_ORBIT);
+ time_til_change = 0.5;
+ }
+
+ else if (KeyDown(KEY_TARGET_PADLOCK)) {
+ PlayerCam(CameraDirector::MODE_TARGET);
+ time_til_change = 0.5;
+ }
+
+ else if (KeyDown(KEY_SWAP_ROLL_YAW)) {
+ input->SwapYawRoll(!input->GetSwapYawRoll());
+ time_til_change = 0.5;
+ }
+
+ else if (KeyDown(KEY_ZOOM_WIDE)) {
+ time_til_change = 0.5;
+ if (gamescreen->GetFieldOfView() <= 2)
+ field_of_view = 3; // wide
+ else
+ field_of_view = 2; // normal
+
+ // don't mess with fov during warp:
+ if (player_ship && player_ship->WarpFactor() <= 1)
+ gamescreen->SetFieldOfView(field_of_view);
+ }
+
+ else if (!exit_latch && KeyDown(KEY_EXIT)) {
+ exit_latch = true;
+ time_til_change = 0.5;
+
+ if (!gamescreen->CloseTopmost()) {
+ QuitView* quit = QuitView::GetInstance();
+ if (quit)
+ quit->ShowMenu();
+ else
+ SetGameMode(Starshatter::PLAN_MODE);
+ }
+ }
+
+ else if (KeyDown(KEY_PAUSE)) {
+ Pause(!paused);
+ time_til_change = 0.5;
+ }
+
+
+ else if (KeyDown(KEY_TIME_COMPRESS)) {
+ time_til_change = 0.5;
+ if (NetGame::IsNetGame())
+ SetTimeCompression(1);
+ else
+ switch (TimeCompression()) {
+ case 1: SetTimeCompression(2); break;
+ default: SetTimeCompression(4); break;
+ }
+ }
+
+ else if (KeyDown(KEY_TIME_EXPAND)) {
+ time_til_change = 0.5;
+
+ if (NetGame::IsNetGame())
+ SetTimeCompression(1);
+ else
+ switch (TimeCompression()) {
+ case 4: SetTimeCompression( 2); break;
+ default: SetTimeCompression( 1); break;
+ }
+ }
+
+ else if (KeyDown(KEY_TIME_SKIP)) {
+ time_til_change = 0.5;
+
+ if (player_ship && !NetGame::IsNetGame()) {
+ player_ship->TimeSkip();
+ }
+ }
+
+ else if (KeyDown(KEY_COMMAND_MODE)) {
+ if (player_ship) {
+ time_til_change = 0.5;
+ player_ship->CommandMode();
+ }
+ }
+
+/*** For Debug Convenience Only: ***/
+ else if (KeyDown(KEY_INC_STARDATE)) {
+ StarSystem::SetBaseTime(StarSystem::GetBaseTime() + 600, true);
+ }
+
+ else if (KeyDown(KEY_DEC_STARDATE)) {
+ StarSystem::SetBaseTime(StarSystem::GetBaseTime() - 600, true);
+ }
+/***/
+ }
+
+ if (gamescreen && time_til_change <= 0) {
+ if (KeyDown(KEY_MFD1)) {
+ gamescreen->CycleMFDMode(0);
+ time_til_change = 0.5;
+ }
+
+ else if (KeyDown(KEY_MFD2)) {
+ gamescreen->CycleMFDMode(1);
+ time_til_change = 0.5;
+ }
+
+ else if (KeyDown(KEY_MFD3)) {
+ gamescreen->CycleMFDMode(2);
+ time_til_change = 0.5;
+ }
+
+ else if (KeyDown(KEY_MFD4)) {
+ gamescreen->CycleMFDMode(3);
+ time_til_change = 0.5;
+ }
+
+ else if (KeyDown(KEY_RADIO_MENU)) {
+ RadioView* radio_view = RadioView::GetInstance();
+ if (radio_view) {
+ if (radio_view->IsMenuShown())
+ radio_view->CloseMenu();
+ else
+ radio_view->ShowMenu();
+ }
+ time_til_change = 0.5;
+ }
+
+ else if (KeyDown(KEY_QUANTUM_MENU)) {
+ QuantumView* quantum_view = QuantumView::GetInstance();
+ if (quantum_view) {
+ if (quantum_view->IsMenuShown())
+ quantum_view->CloseMenu();
+ else
+ quantum_view->ShowMenu();
+ }
+ time_til_change = 0.5;
+ }
+
+ else if (KeyDown(KEY_CAM_VIEW_SELECTION)) {
+ time_til_change = 0.5;
+ ViewSelection();
+ }
+
+ else if (KeyDown(KEY_HUD_MODE)) {
+ time_til_change = 0.5;
+ if (hud_view)
+ hud_view->CycleHUDMode();
+ }
+
+ else if (KeyDown(KEY_HUD_COLOR)) {
+ time_til_change = 0.5;
+ if (hud_view)
+ hud_view->CycleHUDColor();
+ }
+
+ else if (KeyDown(KEY_HUD_WARN)) {
+ time_til_change = 0.5;
+ if (hud_view)
+ hud_view->CycleHUDWarn();
+ }
+
+ else if (KeyDown(KEY_HUD_INST)) {
+ time_til_change = 0.5;
+ if (hud_view)
+ hud_view->CycleHUDInst();
+ }
+
+ else if (KeyDown(KEY_SELF_DESTRUCT)) {
+ time_til_change = 0.5;
+
+ if (player_ship && !player_ship->InTransition()) {
+ double damage = player_ship->Design()->scuttle;
+
+ if (NetGame::IsNetGameClient()) {
+ NetUtil::SendSelfDestruct(player_ship, damage);
+ }
+ else {
+ Point scuttle_loc = player_ship->Location() + RandomDirection() * player_ship->Radius();
+ player_ship->InflictDamage(damage, 0, 1, scuttle_loc);
+ }
+
+ if (player_ship->Integrity() < 1) {
+ ::Print(" %s 0-0-0-Destruct-0\n\n", player_ship->Name());
+
+ ShipStats* s = ShipStats::Find(player_ship->Name());
+ if (s)
+ s->AddEvent(SimEvent::DESTROYED, player_ship->Name());
+
+ player_ship->DeathSpiral();
+ }
+ }
+ }
+ }
+
+ if (gamescreen && player_ship && time_til_change <= 0 && !::GetAsyncKeyState(VK_SHIFT) && !::GetAsyncKeyState(VK_MENU)) {
+ if (KeyDown(KEY_NAV_DLG)) {
+ gamescreen->ShowNavDlg();
+ time_til_change = 0.5;
+ }
+
+ else if (KeyDown(KEY_WEP_DLG)) {
+ if (player_ship && player_ship->Design()->wep_screen)
+ gamescreen->ShowWeaponsOverlay();
+ time_til_change = 0.5;
+ }
+
+ else if (KeyDown(KEY_FLT_DLG)) {
+ if (player_ship && player_ship->NumFlightDecks() > 0)
+ gamescreen->ShowFltDlg();
+ time_til_change = 0.5;
+ }
+
+ else if (KeyDown(KEY_ENG_DLG)) {
+ if (player_ship && player_ship->Design()->repair_screen)
+ gamescreen->ShowEngDlg();
+ time_til_change = 0.5;
+ }
+ }
+
+ if (cam_dir) {
+ double spin = (PI/2) * Game::FrameTime(); // Game::GUITime();
+
+ if (avi_file)
+ spin /= 6;
+
+ if (KeyDown(KEY_CAM_EXT_PLUS_AZ))
+ cam_dir->ExternalAzimuth(spin);
+
+ else if (KeyDown(KEY_CAM_EXT_MINUS_AZ))
+ cam_dir->ExternalAzimuth(-spin);
+
+ if (KeyDown(KEY_CAM_EXT_PLUS_EL))
+ cam_dir->ExternalElevation(spin);
+
+ else if (KeyDown(KEY_CAM_EXT_MINUS_EL))
+ cam_dir->ExternalElevation(-spin);
+
+ if (KeyDown(KEY_CAM_VIRT_PLUS_AZ))
+ cam_dir->VirtualAzimuth(-spin);
+
+ else if (KeyDown(KEY_CAM_VIRT_MINUS_AZ))
+ cam_dir->VirtualAzimuth(spin);
+
+ if (KeyDown(KEY_CAM_VIRT_PLUS_EL))
+ cam_dir->VirtualElevation(spin);
+
+ else if (KeyDown(KEY_CAM_VIRT_MINUS_EL))
+ cam_dir->VirtualElevation(-spin);
+
+ if (KeyDown(KEY_CAM_EXT_PLUS_RANGE)){
+ if (!gamescreen->IsNavShown()) {
+ cam_dir->ExternalRange((float) (1 + 1.5 * Game::FrameTime())); // 1.1f);
+ }
+ }
+
+ else if (KeyDown(KEY_CAM_EXT_MINUS_RANGE)) {
+ if (!gamescreen->IsNavShown()) {
+ cam_dir->ExternalRange((float) (1 - 1.5 * Game::FrameTime())); // 0.9f);
+ }
+ }
+
+ if (tactical && !gamescreen->IsFormShown()) {
+ if (Mouse::Wheel()) {
+ int w = Mouse::Wheel();
+
+ if (w < 0) {
+ while (w < 0) {
+ cam_dir->ExternalRange(1.25f);
+ w += 120;
+ }
+ }
+ else {
+ while (w > 0) {
+ cam_dir->ExternalRange(0.75f);
+ w -= 120;
+ }
+ }
+ }
+
+ else if (Mouse::LButton() && Mouse::RButton()) {
+ if (mouse_dy < 0)
+ cam_dir->ExternalRange(0.85f);
+ else if (mouse_dy > 0)
+ cam_dir->ExternalRange(1.15f);
+ }
+
+ else if (Mouse::MButton() && time_til_change <= 0) {
+ time_til_change = 0.5;
+ ViewSelection();
+ }
+
+ else {
+ if (mouse_dx || mouse_dy) {
+ if (CameraDirector::GetCameraMode() == CameraDirector::MODE_VIRTUAL) {
+ cam_dir->VirtualAzimuth( mouse_dx * 0.2 * DEGREES);
+ cam_dir->VirtualElevation(mouse_dy * 0.2 * DEGREES);
+ }
+ else {
+ cam_dir->ExternalAzimuth( mouse_dx * 0.2 * DEGREES);
+ cam_dir->ExternalElevation(mouse_dy * 0.2 * DEGREES);
+ }
+ }
+ }
+ }
+ }
+
+ // radio message hot keys:
+ static bool comm_key = false;
+
+ if (KeyDown(KEY_COMM_ATTACK_TGT)) {
+ if (!comm_key)
+ RadioTraffic::SendQuickMessage(player_ship, RadioMessage::ATTACK);
+ comm_key = true;
+ }
+
+ else if (KeyDown(KEY_COMM_ESCORT_TGT)) {
+ if (!comm_key)
+ RadioTraffic::SendQuickMessage(player_ship, RadioMessage::ESCORT);
+ comm_key = true;
+ }
+
+ else if (KeyDown(KEY_COMM_WEP_FREE)) {
+ if (!comm_key)
+ RadioTraffic::SendQuickMessage(player_ship, RadioMessage::WEP_FREE);
+ comm_key = true;
+ }
+
+ else if (KeyDown(KEY_COMM_WEP_HOLD)) {
+ if (!comm_key)
+ RadioTraffic::SendQuickMessage(player_ship, RadioMessage::FORM_UP);
+ comm_key = true;
+ }
+
+ else if (KeyDown(KEY_COMM_COVER_ME)) {
+ if (!comm_key)
+ RadioTraffic::SendQuickMessage(player_ship, RadioMessage::COVER_ME);
+ comm_key = true;
+ }
+
+ else if (KeyDown(KEY_COMM_SKIP_NAV)) {
+ if (!comm_key)
+ RadioTraffic::SendQuickMessage(player_ship, RadioMessage::SKIP_NAVPOINT);
+ comm_key = true;
+ }
+
+ else if (KeyDown(KEY_COMM_RETURN_TO_BASE)) {
+ if (!comm_key)
+ RadioTraffic::SendQuickMessage(player_ship, RadioMessage::RTB);
+ comm_key = true;
+ }
+
+ else if (KeyDown(KEY_COMM_CALL_INBOUND)) {
+ if (!comm_key)
+ RadioTraffic::SendQuickMessage(player_ship, RadioMessage::CALL_INBOUND);
+ comm_key = true;
+ }
+
+ else if (KeyDown(KEY_COMM_REQUEST_PICTURE)) {
+ if (!comm_key)
+ RadioTraffic::SendQuickMessage(player_ship, RadioMessage::REQUEST_PICTURE);
+ comm_key = true;
+ }
+
+ else if (KeyDown(KEY_COMM_REQUEST_SUPPORT)) {
+ if (!comm_key)
+ RadioTraffic::SendQuickMessage(player_ship, RadioMessage::REQUEST_SUPPORT);
+ comm_key = true;
+ }
+
+ else if (KeyDown(KEY_CHAT_BROADCAST)) {
+ if (!comm_key)
+ SetChatMode(CHAT_BROADCAST);
+ comm_key = true;
+ }
+
+ else if (KeyDown(KEY_CHAT_TEAM)) {
+ if (!comm_key)
+ SetChatMode(CHAT_TEAM);
+ comm_key = true;
+ }
+
+ else if (KeyDown(KEY_CHAT_WING)) {
+ if (!comm_key)
+ SetChatMode(CHAT_WING);
+ comm_key = true;
+ }
+
+ else if (KeyDown(KEY_CHAT_UNIT) && sim->GetSelection().container().size()) {
+ if (!comm_key)
+ SetChatMode(CHAT_UNIT);
+ comm_key = true;
+ }
+
+ else {
+ comm_key = false;
+ }
+}
+
+void
+Starshatter::DoChatMode()
+{
+ Player* p = Player::GetCurrentPlayer();
+ bool send_chat = false;
+ Text name = "Player";
+
+ if (player_ship)
+ name = player_ship->Name();
+
+ if (p)
+ name = p->Name();
+
+ if (chat_text.length()) {
+ if (chat_text[0] >= '0' && chat_text[0] <= '9') {
+ if (p) {
+ chat_text = p->ChatMacro(chat_text[0] - '0');
+ send_chat = true;
+ }
+ }
+ }
+
+ if (KeyDown(KEY_EXIT)) {
+ SetChatMode(0);
+ time_til_change = 0.5;
+ }
+
+ else if (send_chat || Keyboard::KeyDown(VK_RETURN)) {
+ switch (chat_mode) {
+ default:
+ case CHAT_BROADCAST:
+ {
+ NetUtil::SendChat(0, name, chat_text);
+ }
+ break;
+
+ case CHAT_TEAM:
+ if (player_ship) {
+ NetUtil::SendChat(player_ship->GetIFF()+1, name, chat_text);
+ }
+ break;
+
+ case CHAT_WING:
+ if (player_ship) {
+ Element* elem = player_ship->GetElement();
+ if (elem) {
+ for (int i = 1; i <= elem->NumShips(); i++) {
+ Ship* s = elem->GetShip(i);
+ if (s && s != player_ship)
+ NetUtil::SendChat(s->GetObjID(), name, chat_text);
+ }
+ }
+ }
+ break;
+
+ case CHAT_UNIT:
+ {
+ Sim* sim = Sim::GetSim();
+ ListIter<Ship> seln = sim->GetSelection();
+
+ while (++seln) {
+ Ship* s = seln.value();
+ if (s != player_ship)
+ NetUtil::SendChat(s->GetObjID(), name, chat_text);
+ }
+ }
+ break;
+ }
+
+ HUDView::Message("%s> %s", name.data(), chat_text.data());
+
+ SetChatMode(0);
+ time_til_change = 0.5;
+ }
+
+ else {
+ int key = 0;
+ int shift = 0;
+
+ while (GetKeyPlus(key, shift)) {
+ if (key >= 'A' && key <= 'Z') {
+ if (shift & 1)
+ chat_text += (char) key;
+ else
+ chat_text += (char) tolower(key);
+ }
+ else {
+ switch (key) {
+ case VK_BACK:
+ chat_text = chat_text.substring(0, chat_text.length()-1);
+ break;
+
+ case VK_SPACE: chat_text += ' '; break;
+ case '0': if (shift & 1) chat_text += ')'; else chat_text += '0'; break;
+ case '1': if (shift & 1) chat_text += '!'; else chat_text += '1'; break;
+ case '2': if (shift & 1) chat_text += '@'; else chat_text += '2'; break;
+ case '3': if (shift & 1) chat_text += '#'; else chat_text += '3'; break;
+ case '4': if (shift & 1) chat_text += '$'; else chat_text += '4'; break;
+ case '5': if (shift & 1) chat_text += '%'; else chat_text += '5'; break;
+ case '6': if (shift & 1) chat_text += '^'; else chat_text += '6'; break;
+ case '7': if (shift & 1) chat_text += '&'; else chat_text += '7'; break;
+ case '8': if (shift & 1) chat_text += '*'; else chat_text += '8'; break;
+ case '9': if (shift & 1) chat_text += '('; else chat_text += '9'; break;
+ case 186: if (shift & 1) chat_text += ':'; else chat_text += ';'; break;
+ case 187: if (shift & 1) chat_text += '+'; else chat_text += '='; break;
+ case 188: if (shift & 1) chat_text += '<'; else chat_text += ','; break;
+ case 189: if (shift & 1) chat_text += '_'; else chat_text += '-'; break;
+ case 190: if (shift & 1) chat_text += '>'; else chat_text += '.'; break;
+ case 191: if (shift & 1) chat_text += '?'; else chat_text += '/'; break;
+ case 192: if (shift & 1) chat_text += '~'; else chat_text += '`'; break;
+ case 219: if (shift & 1) chat_text += '{'; else chat_text += '['; break;
+ case 221: if (shift & 1) chat_text += '}'; else chat_text += ']'; break;
+ case 220: if (shift & 1) chat_text += '|'; else chat_text += '\\'; break;
+ case 222: if (shift & 1) chat_text += '"'; else chat_text += '\''; break;
+ }
+ }
+ }
+ }
+}
+
+void
+Starshatter::SetChatMode(int mode)
+{
+ if (mode >= 0 && mode <= 4 && mode != chat_mode) {
+ chat_text = "";
+
+ if (!NetGame::IsNetGame()) {
+ chat_mode = 0;
+ }
+ else {
+ chat_mode = mode;
+
+ // flush input before reading chat message:
+ if (chat_mode) {
+ FlushKeys();
+ }
+
+ // flush input before sampling flight controls:
+ else {
+ for (int i = 1; i < 255; i++) {
+ Keyboard::KeyDown(i);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::DoMouseFrame()
+{
+ EventDispatch* event_dispatch = EventDispatch::GetInstance();
+ if (event_dispatch && event_dispatch->GetCapture())
+ return;
+
+ mouse_dx = 0;
+ mouse_dy = 0;
+
+ static int old_mouse_x = 0;
+ static int old_mouse_y = 0;
+
+ if (!spinning && Mouse::RButton()) {
+ spinning = true;
+ old_mouse_x = Mouse::X();
+ old_mouse_y = Mouse::Y();
+ mouse_x = Mouse::X();
+ mouse_y = Mouse::Y();
+ Mouse::Show(false);
+ }
+
+ else if (spinning) {
+ if (!Mouse::RButton()) {
+ spinning = false;
+
+ if (tactical) {
+ Mouse::Show(true);
+ Mouse::SetCursor(Mouse::ARROW);
+ }
+
+ Mouse::SetCursorPos(old_mouse_x, old_mouse_y);
+ }
+
+ else {
+ mouse_dx = Mouse::X() - mouse_x;
+ mouse_dy = Mouse::Y() - mouse_y;
+
+ Mouse::SetCursorPos(mouse_x, mouse_y);
+ }
+ }
+
+ else if (cutscene || !player_ship) {
+ Mouse::Show(false);
+ return;
+ }
+
+ // hide mouse cursor when mouse controller is actively steering:
+ else if (mouse_input && mouse_input->Active()) {
+ if (mouse_input->Selector() == 1) {
+ Mouse::Show(false);
+ }
+
+ else if (mouse_input->Selector() == 2) {
+ Mouse::Show(true);
+ Mouse::SetCursor(Mouse::CROSS);
+ }
+ }
+
+ else {
+ HUDView* hud_view = HUDView::GetInstance();
+
+ if (hud_view && hud_view->GetHUDMode() != HUDView::HUD_MODE_OFF) {
+ Mouse::Show(true);
+ Mouse::SetCursor(Mouse::ARROW);
+ }
+ }
+
+ if (gamescreen && gamescreen->IsFormShown())
+ return;
+
+ TacticalView* tac_view = TacticalView::GetInstance();
+
+ if (tac_view)
+ tac_view->DoMouseFrame();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::SetupSplash()
+{
+ Color::SetFade(0);
+
+ if (gamewin) {
+ screen->DelWindow(gamewin);
+ delete gamewin;
+ gamewin = 0;
+ }
+
+ switch (splash_index) {
+ case 0:
+ splash_image.ClearImage();
+ loader->SetDataPath(0);
+ loader->LoadBitmap("matrix.pcx", splash_image);
+ break;
+
+ case 1:
+ default:
+ splash_image.ClearImage();
+ loader->SetDataPath(0);
+ loader->LoadBitmap("studio.pcx", splash_image);
+ break;
+ }
+
+ int screen_width = GetScreenWidth();
+ int screen_height = GetScreenHeight();
+
+ gamewin = new(__FILE__,__LINE__) Window(screen, 0, 0, screen_width, screen_height);
+ splash = new(__FILE__,__LINE__) FadeView(gamewin, 2, 2, 2);
+
+ gamewin->AddView(splash);
+ gamewin->AddView(new(__FILE__,__LINE__) ImgView(gamewin, &splash_image));
+ screen->AddWindow(gamewin);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::SetupMenuScreen()
+{
+ if (menuscreen) {
+ delete menuscreen;
+ menuscreen = 0;
+ }
+
+ menuscreen = new(__FILE__,__LINE__) MenuScreen();
+ menuscreen->Setup(screen);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::SetupCmpnScreen()
+{
+ if (cmpnscreen) {
+ delete cmpnscreen;
+ cmpnscreen = 0;
+ }
+
+ cmpnscreen = new(__FILE__,__LINE__) CmpnScreen();
+ cmpnscreen->Setup(screen);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::SetupPlanScreen()
+{
+ if (planscreen) {
+ delete planscreen;
+ planscreen = 0;
+ }
+
+ planscreen = new(__FILE__,__LINE__) PlanScreen();
+ planscreen->Setup(screen);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::SetupLoadScreen()
+{
+ if (loadscreen) {
+ delete loadscreen;
+ loadscreen = 0;
+ }
+
+ loadscreen = new(__FILE__,__LINE__) LoadScreen();
+ loadscreen->Setup(screen);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::SetupGameScreen()
+{
+ if (gamescreen) {
+ delete gamescreen;
+ gamescreen = 0;
+ }
+
+ gamescreen = new(__FILE__,__LINE__) GameScreen();
+ gamescreen->Setup(screen);
+ gamescreen->SetFieldOfView(field_of_view);
+
+ // initialize player_ship's MFD choices:
+ Player::SelectPlayer(Player::GetCurrentPlayer());
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::LoadVideoConfig(const char* filename)
+{
+ // set up defaults:
+ int screen_width = 1024;
+ int screen_height = 768;
+ int screen_depth = 32;
+ int terrain_detail_level = 3;
+ bool shadows_enabled = true;
+ bool spec_maps_enabled = true;
+ bool bump_maps_enabled = true;
+ bool vertex_shader = true;
+ bool pixel_shader = true;
+ float depth_bias = video_settings->depth_bias;
+
+ max_tex_size = 2048;
+
+ if (MachineInfo::GetCpuSpeed() >= 1000 && MachineInfo::GetTotalRam() > 128)
+ terrain_detail_level = 4;
+
+ Terrain::SetDetailLevel(terrain_detail_level);
+
+ // read the config file:
+ BYTE* block = 0;
+ int blocklen = 0;
+
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ ::fseek(f, 0, SEEK_END);
+ blocklen = ftell(f);
+ ::fseek(f, 0, SEEK_SET);
+
+ block = new(__FILE__,__LINE__) BYTE[blocklen+1];
+ block[blocklen] = 0;
+
+ ::fread(block, blocklen, 1, f);
+ ::fclose(f);
+ }
+
+ if (blocklen == 0)
+ return;
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'.\n", filename);
+ exit(-3);
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "VIDEO") {
+ Print("WARNING: invalid %s file. Using defaults\n", filename);
+ return;
+ }
+ }
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "width") {
+ int w;
+ GetDefNumber(w, def, filename);
+
+ switch (w) {
+ case 800:
+ screen_width = 800;
+ screen_height = 600;
+ break;
+
+ case 1024:
+ screen_width = 1024;
+ screen_height = 768;
+ break;
+
+ case 1152:
+ screen_width = 1152;
+ screen_height = 864;
+ break;
+
+ case 1280:
+ screen_width = 1280;
+ screen_height = 960;
+ break;
+
+ case 1440:
+ screen_width = 1440;
+ screen_height = 900;
+ break;
+
+ default:
+ screen_width = w;
+ screen_height = (w*3)/4;
+ break;
+ }
+ }
+
+ else if (def->name()->value() == "height") {
+ int h;
+ GetDefNumber(h, def, filename);
+
+ if (screen_width == 1280 && (h == 800 || h == 1024))
+ screen_height = h;
+ else if (screen_width == 1600 && (h == 900 || h == 1200))
+ screen_height = h;
+ }
+
+ else if (def->name()->value() == "depth" ||
+ def->name()->value() == "bpp") {
+
+ int bpp;
+ GetDefNumber(bpp, def, filename);
+
+ switch (bpp) {
+ case 8:
+ case 16:
+ case 24:
+ case 32:
+ screen_depth = bpp;
+ break;
+
+ default:
+ Print("WARNING: Invalid screen bpp (%d) in '%s'\n", bpp, filename);
+ screen_depth = 8;
+ break;
+ }
+ }
+
+ else if (def->name()->value() == "max_tex") {
+ int n;
+ GetDefNumber(n, def, filename);
+ if (n >= 64 && n <= 4096)
+ max_tex_size = n;
+ }
+
+ else if (def->name()->value() == "gamma") {
+ int level;
+ GetDefNumber(level, def, filename);
+ if (level >= 0 && level <= 255)
+ gamma = level;
+ }
+
+ else if (def->name()->value() == "terrain_detail_level") {
+ GetDefNumber(terrain_detail_level, def, filename);
+ Terrain::SetDetailLevel(terrain_detail_level);
+ }
+
+ else if (def->name()->value() == "terrain_texture_enable") {
+ bool enable;
+ GetDefBool(enable, def, filename);
+
+ // no longer supported!
+ }
+
+ else if (def->name()->value() == "shadows") {
+ GetDefBool(shadows_enabled, def, filename);
+ }
+
+ else if (def->name()->value() == "spec_maps") {
+ GetDefBool(spec_maps_enabled, def, filename);
+ }
+
+ else if (def->name()->value() == "bump_maps") {
+ GetDefBool(bump_maps_enabled, def, filename);
+ }
+
+ else if (def->name()->value() == "vertex_shader") {
+ GetDefBool(vertex_shader, def, filename);
+ }
+
+ else if (def->name()->value() == "pixel_shader") {
+ GetDefBool(pixel_shader, def, filename);
+ }
+
+ else if (def->name()->value().contains("bias")) {
+ GetDefNumber(depth_bias, def, filename);
+ }
+
+ else if (def->name()->value() == "flare") {
+ bool b;
+ GetDefBool(b, def, filename);
+ lens_flare = b;
+ }
+
+ else if (def->name()->value() == "corona") {
+ bool b;
+ GetDefBool(b, def, filename);
+ corona = b;
+ }
+
+ else if (def->name()->value() == "nebula") {
+ bool b;
+ GetDefBool(b, def, filename);
+ nebula = b;
+ }
+
+ else if (def->name()->value() == "dust") {
+ GetDefNumber(dust, def, filename);
+ }
+
+ else if (def->name()->value().indexOf("cam_range") == 0) {
+ double range_max = 0;
+ GetDefNumber(range_max, def, filename);
+ CameraDirector::SetRangeLimit(range_max);
+ }
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ term->print();
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+
+ if (video_settings) {
+ video_settings->fullscreen_mode.width = screen_width;
+ video_settings->fullscreen_mode.height = screen_height;
+
+ if (screen_depth == 16)
+ video_settings->fullscreen_mode.format = VideoMode::FMT_R5G6B5;
+ else
+ video_settings->fullscreen_mode.format = VideoMode::FMT_X8R8G8B8;
+
+ video_settings->shadows = shadows_enabled;
+ video_settings->specmaps = spec_maps_enabled;
+ video_settings->bumpmaps = bump_maps_enabled;
+ video_settings->enable_vs = vertex_shader;
+ video_settings->enable_ps = pixel_shader;
+ video_settings->depth_bias = depth_bias;
+ }
+
+ if (video) {
+ video->SetShadowEnabled(shadows_enabled);
+ video->SetSpecMapEnabled(spec_maps_enabled);
+ video->SetBumpMapEnabled(bump_maps_enabled);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::SaveVideoConfig(const char* filename)
+{
+ if (!video_settings)
+ return;
+
+ FILE* f = fopen(filename, "w");
+ if (f) {
+ fprintf(f, "VIDEO\n\n");
+ fprintf(f, "width: %4d\n", video_settings->fullscreen_mode.width);
+ fprintf(f, "height: %4d\n", video_settings->fullscreen_mode.height);
+ fprintf(f, "depth: %4d\n", video_settings->fullscreen_mode.format == VideoMode::FMT_R5G6B5 ? 16 : 32);
+ fprintf(f, "\n");
+ fprintf(f, "max_tex: %4d\n", max_tex_size);
+ fprintf(f, "primary3D: %s\n", "true");
+ fprintf(f, "gamma: %4d\n", gamma);
+ fprintf(f, "\n");
+ fprintf(f, "terrain_detail_level: %d\n", Terrain::DetailLevel());
+ fprintf(f, "terrain_texture_enable: %true\n");
+ fprintf(f, "\n");
+
+ fprintf(f, "shadows: %s\n", video_settings->shadows ? "true" : "false");
+ fprintf(f, "spec_maps: %s\n", video_settings->specmaps ? "true" : "false");
+ fprintf(f, "bump_maps: %s\n", video_settings->bumpmaps ? "true" : "false");
+ fprintf(f, "bias: %f\n", video_settings->depth_bias);
+ fprintf(f, "\n");
+
+ fprintf(f, "flare: %s\n", lens_flare ? "true" : "false");
+ fprintf(f, "corona: %s\n", corona ? "true" : "false");
+ fprintf(f, "nebula: %s\n", nebula ? "true" : "false");
+ fprintf(f, "dust: %d\n", dust);
+
+ if (CameraDirector::GetRangeLimit() != 300e3)
+ fprintf(f, " cam_range_max: %f,\n", CameraDirector::GetRangeLimit());
+
+ fclose(f);
+ }
+
+ video_changed = false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::InvalidateTextureCache()
+{
+ if (video)
+ video->InvalidateCache();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Starshatter::ExecCutscene(const char* msn_file, const char* path)
+{
+ if (InCutscene() || !msn_file || !*msn_file) return;
+
+ if (!world)
+ CreateWorld();
+
+ cutscene_mission = new(__FILE__,__LINE__) Mission(0);
+ cutscene_basetime = StarSystem::GetBaseTime();
+
+ if (cutscene_mission->Load(msn_file, path)) {
+ Sim* sim = (Sim*) world;
+
+ if (sim) {
+ bool dynamic = false;
+ Campaign* campaign = Campaign::GetCampaign();
+
+ if (campaign && campaign->IsDynamic())
+ dynamic = true;
+
+ sim->UnloadMission();
+ sim->LoadMission(cutscene_mission, true); // attempt to preload the tex cache
+ sim->ExecMission();
+ sim->ShowGrid(false);
+ player_ship = sim->GetPlayerShip();
+
+ Print(" Cutscene Instantiated.\n");
+
+ UpdateWorld();
+ }
+ }
+ else {
+ delete cutscene_mission;
+ cutscene_mission = 0;
+ cutscene_basetime = 0;
+ }
+}
+
+void
+Starshatter::BeginCutscene()
+{
+ Sim* sim = Sim::GetSim();
+
+ if (cutscene == 0 && !sim->IsNetGame()) {
+ HUDView* hud_view = HUDView::GetInstance();
+ if (hud_view)
+ hud_view->SetHUDMode(HUDView::HUD_MODE_OFF);
+
+ if (sim->GetPlayerShip())
+ sim->GetPlayerShip()->SetControls(0);
+
+ AudioConfig* audio_cfg = AudioConfig::GetInstance();
+
+ if (audio_cfg) {
+ cut_efx_volume = audio_cfg->GetEfxVolume();
+ cut_wrn_volume = audio_cfg->GetWrnVolume();
+ }
+
+ Ship::SetFlightModel(Ship::FM_ARCADE);
+ }
+
+ cutscene++;
+}
+
+void
+Starshatter::EndCutscene()
+{
+ cutscene--;
+
+ if (cutscene == 0) {
+ DisplayView* disp_view = DisplayView::GetInstance();
+ if (disp_view)
+ disp_view->ClearDisplay();
+
+ HUDView* hud_view = HUDView::GetInstance();
+ if (hud_view)
+ hud_view->SetHUDMode(HUDView::HUD_MODE_TAC);
+
+ Sim* sim = Sim::GetSim();
+ if (sim->GetPlayerShip())
+ sim->GetPlayerShip()->SetControls(sim->GetControls());
+
+ if (cam_dir) {
+ cam_dir->SetViewOrbital(0);
+ CameraDirector::SetRangeLimits(10, CameraDirector::GetRangeLimit());
+ cam_dir->SetOrbitPoint(PI/4,PI/4,1);
+ cam_dir->SetOrbitRates(0,0,0);
+ }
+
+ AudioConfig* audio_cfg = AudioConfig::GetInstance();
+
+ if (audio_cfg) {
+ audio_cfg->SetEfxVolume(cut_efx_volume);
+ audio_cfg->SetWrnVolume(cut_wrn_volume);
+ }
+
+ Player* p = Player::GetCurrentPlayer();
+ if (p)
+ Ship::SetFlightModel(p->FlightModel());
+ }
+}
+
+void
+Starshatter::EndMission()
+{
+ if (cutscene_mission) {
+ Sim* sim = Sim::GetSim();
+
+ if (sim && sim->GetMission() == cutscene_mission) {
+ ShipStats::Initialize();
+ sim->UnloadMission();
+
+ // restore world clock (true => absolute time reference)
+ if (cutscene_basetime != 0)
+ StarSystem::SetBaseTime(cutscene_basetime, true);
+
+ delete cutscene_mission;
+ cutscene_mission = 0;
+ cutscene_basetime = 0;
+
+ return;
+ }
+ }
+
+ SetGameMode(Starshatter::PLAN_MODE);
+}
+
+Mission*
+Starshatter::GetCutsceneMission() const
+{
+ return cutscene_mission;
+}
+
+const char*
+Starshatter::GetSubtitles() const
+{
+ if (cutscene_mission)
+ return cutscene_mission->Subtitles();
+
+ return "";
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Starshatter::GetLobbyMode()
+{
+ return lobby_mode;
+}
+
+void
+Starshatter::SetLobbyMode(int mode)
+{
+ lobby_mode = mode;
+}
+
+void
+Starshatter::StartLobby()
+{
+ if (!net_lobby) {
+ if (lobby_mode == NET_LOBBY_SERVER) {
+ NetServerConfig::Initialize();
+ NetServerConfig* server_config = NetServerConfig::GetInstance();
+
+ if (server_config)
+ net_lobby = new(__FILE__,__LINE__) NetLobbyServer;
+ }
+
+ else {
+ NetClientConfig* client_config = NetClientConfig::GetInstance();
+ if (client_config)
+ client_config->Login();
+
+ net_lobby = NetLobby::GetInstance();
+ }
+ }
+
+ lobby_mode = NET_LOBBY_CLIENT;
+}
+
+void
+Starshatter::StopLobby()
+{
+ if (net_lobby) {
+ if (net_lobby->IsServer()) {
+ delete net_lobby;
+ NetServerConfig::Close();
+ }
+
+ else {
+ NetClientConfig* client_config = NetClientConfig::GetInstance();
+ if (client_config)
+ client_config->Logout();
+ }
+
+ net_lobby = 0;
+ }
+
+ lobby_mode = NET_LOBBY_CLIENT;
+}
+
+void
+Starshatter::StopNetGame()
+{
+ // local server:
+ NetLobby* lobby = NetLobby::GetInstance();
+
+ if (lobby && lobby->IsServer()) {
+ lobby->GameStop();
+ }
+
+ // client connected to remote server:
+ else {
+ NetClientConfig* config = NetClientConfig::GetInstance();
+ if (config && config->GetHostRequest()) {
+ config->Login();
+
+ NetLobbyClient* conn = config->GetConnection();
+
+ if (conn) {
+ conn->GameStop();
+ conn->SelectMission(0);
+ }
+ }
+ }
+}
diff --git a/Stars45/Starshatter.h b/Stars45/Starshatter.h
new file mode 100644
index 0000000..023408b
--- /dev/null
+++ b/Stars45/Starshatter.h
@@ -0,0 +1,225 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Starshatter.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef Starshatter_h
+#define Starshatter_h
+
+#include "Types.h"
+#include "Game.h"
+#include "Bitmap.h"
+#include "KeyMap.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Campaign;
+class MenuScreen;
+class CmpnScreen;
+class PlanScreen;
+class LoadScreen;
+class GameScreen;
+class Ship;
+class Sim;
+class FadeView;
+class CameraDirector;
+class MultiController;
+class MouseController;
+class MusicDirector;
+class DataLoader;
+class Font;
+class TrackIR;
+class Mission;
+
+class NetServer;
+class NetLobby;
+
+// +--------------------------------------------------------------------+
+
+class Starshatter : public Game
+{
+public:
+ Starshatter();
+ virtual ~Starshatter();
+
+ virtual bool Init(HINSTANCE hi, HINSTANCE hpi, LPSTR cmdline, int nCmdShow);
+ virtual bool InitGame();
+ virtual bool ChangeVideo();
+ virtual void GameState();
+ virtual void Exit();
+ virtual bool OnHelp();
+
+ enum MODE { MENU_MODE, // main menu
+ CLOD_MODE, // loading campaign
+ CMPN_MODE, // operational command for dynamic campaign
+ PREP_MODE, // loading mission info for planning
+ PLAN_MODE, // mission briefing
+ LOAD_MODE, // loading mission into simulator
+ PLAY_MODE, // active simulation
+ EXIT_MODE // shutting down
+ };
+
+ enum LOBBY { NET_LOBBY_CLIENT,
+ NET_LOBBY_SERVER
+ };
+
+ int GetGameMode() { return game_mode; }
+ void SetGameMode(int mode);
+ void RequestChangeVideo();
+ void LoadVideoConfig(const char* filename);
+ void SaveVideoConfig(const char* filename);
+ void SetupSplash();
+ void SetupMenuScreen();
+ void SetupCmpnScreen();
+ void SetupPlanScreen();
+ void SetupLoadScreen();
+ void SetupGameScreen();
+ void OpenTacticalReference();
+ void CreateWorld();
+ int KeyDown(int action) const;
+
+ void PlayerCam(int mode);
+ void ViewSelection();
+
+ void MapKeys();
+ static void MapKeys(KeyMap* mapping, int nkeys);
+ static void MapKey(int act, int key, int alt=0);
+
+ void SetTestMode(int t) { test_mode = t; }
+
+ static Starshatter* GetInstance() { return instance; }
+
+ int GetScreenWidth();
+ int GetScreenHeight();
+
+ // graphic options:
+ int LensFlare() { return lens_flare; }
+ int Corona() { return corona; }
+ int Nebula() { return nebula; }
+ int Dust() { return dust; }
+
+ KeyMap& GetKeyMap() { return keycfg; }
+
+ int GetLoadProgress() { return load_progress; }
+ const char* GetLoadActivity() { return load_activity; }
+
+ void InvalidateTextureCache();
+
+ int GetChatMode() const { return chat_mode; }
+ void SetChatMode(int c);
+ const char* GetChatText() const { return chat_text.data(); }
+
+ void StopNetGame();
+
+ int GetLobbyMode();
+ void SetLobbyMode(int mode = NET_LOBBY_CLIENT);
+ void StartLobby();
+ void StopLobby();
+
+ void ExecCutscene(const char* msn_file, const char* path);
+ void BeginCutscene();
+ void EndCutscene();
+ bool InCutscene() const { return cutscene > 0; }
+ Mission* GetCutsceneMission() const;
+ const char* GetSubtitles() const;
+ void EndMission();
+
+ void StartOrResumeGame();
+
+ static bool UseFileSystem();
+
+protected:
+ virtual void DoMenuScreenFrame();
+ virtual void DoCmpnScreenFrame();
+ virtual void DoPlanScreenFrame();
+ virtual void DoLoadScreenFrame();
+ virtual void DoGameScreenFrame();
+ virtual void DoMouseFrame();
+
+ virtual void DoChatMode();
+ virtual void DoGameKeys();
+
+ virtual bool GameLoop();
+ virtual void UpdateWorld();
+ virtual void InstantiateMission();
+ virtual bool ResizeVideo();
+ virtual void InitMouse();
+
+ static Starshatter* instance;
+ Window* gamewin;
+ MenuScreen* menuscreen;
+ LoadScreen* loadscreen;
+ PlanScreen* planscreen;
+ GameScreen* gamescreen;
+ CmpnScreen* cmpnscreen;
+
+ FadeView* splash;
+ int splash_index;
+ Bitmap splash_image;
+ MultiController* input;
+ MouseController* mouse_input;
+ TrackIR* head_tracker;
+ DataLoader* loader;
+
+ Ship* player_ship;
+ CameraDirector* cam_dir;
+ MusicDirector* music_dir;
+
+ Font* HUDfont;
+ Font* GUIfont;
+ Font* GUI_small_font;
+ Font* terminal;
+ Font* verdana;
+ Font* title_font;
+ Font* limerick18;
+ Font* limerick12;
+ Font* ocrb;
+
+ DWORD time_mark;
+ DWORD minutes;
+
+ double field_of_view;
+ double orig_fov;
+
+ static int keymap[256];
+ static int keyalt[256];
+ KeyMap keycfg;
+
+ bool tactical;
+ bool spinning;
+ int mouse_x;
+ int mouse_y;
+ int mouse_dx;
+ int mouse_dy;
+
+ int game_mode;
+ int test_mode;
+ int req_change_video;
+ int video_changed;
+
+ int lens_flare;
+ int corona;
+ int nebula;
+ int dust;
+
+ double exit_time;
+
+ int load_step;
+ int load_progress;
+ Text load_activity;
+ int catalog_index;
+
+ int cutscene;
+ int lobby_mode;
+ NetLobby* net_lobby;
+ int chat_mode;
+ Text chat_text;
+};
+
+#endif Starshatter_h
diff --git a/Stars45/StarshipAI.cpp b/Stars45/StarshipAI.cpp
new file mode 100644
index 0000000..6b78fee
--- /dev/null
+++ b/Stars45/StarshipAI.cpp
@@ -0,0 +1,822 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: StarshipAI.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Starship (low-level) Artificial Intelligence class
+*/
+
+#include "MemDebug.h"
+#include "StarshipAI.h"
+#include "StarshipTacticalAI.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Element.h"
+#include "Mission.h"
+#include "Instruction.h"
+#include "RadioMessage.h"
+#include "Contact.h"
+#include "WeaponGroup.h"
+#include "Drive.h"
+#include "Sim.h"
+#include "StarSystem.h"
+#include "FlightComp.h"
+#include "Farcaster.h"
+#include "QuantumDrive.h"
+
+#include "Game.h"
+#include "Random.h"
+
+// +----------------------------------------------------------------------+
+
+StarshipAI::StarshipAI(SimObject* s)
+ : ShipAI(s), sub_select_time(0), subtarget(0), tgt_point_defense(false)
+{
+ ai_type = STARSHIP;
+
+ // signifies this ship is a dead hulk:
+ if (ship && ship->Design()->auto_roll < 0) {
+ Point torque(rand()-16000, rand()-16000, rand()-16000);
+ torque.Normalize();
+ torque *= ship->Mass() / 10;
+
+ ship->SetFLCSMode(0);
+ if (ship->GetFLCS())
+ ship->GetFLCS()->PowerOff();
+
+ ship->ApplyTorque(torque);
+ ship->SetVelocity(RandomDirection() * Random(20, 50));
+
+ for (int i = 0; i < 64; i++) {
+ Weapon* w = ship->GetWeaponByIndex(i+1);
+ if (w)
+ w->DrainPower(0);
+ else
+ break;
+ }
+ }
+
+ else {
+ tactical = new(__FILE__,__LINE__) StarshipTacticalAI(this);
+ }
+
+ sub_select_time = Game::GameTime() + (DWORD) Random(0, 2000);
+ point_defense_time = sub_select_time;
+}
+
+
+// +--------------------------------------------------------------------+
+
+StarshipAI::~StarshipAI()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+StarshipAI::FindObjective()
+{
+ distance = 0;
+
+ int order = ship->GetRadioOrders()->Action();
+
+ if (order == RadioMessage::QUANTUM_TO ||
+ order == RadioMessage::FARCAST_TO) {
+
+ FindObjectiveQuantum();
+ objective = Transform(obj_w);
+ return;
+ }
+
+ bool hold = order == RadioMessage::WEP_HOLD ||
+ order == RadioMessage::FORM_UP;
+
+ bool form = hold ||
+ (!order && !target) ||
+ (farcaster);
+
+ // if not the element leader, stay in formation:
+ if (form && element_index > 1) {
+ ship->SetDirectorInfo(Game::GetText("ai.formation"));
+
+ if (navpt && navpt->Action() == Instruction::LAUNCH) {
+ FindObjectiveNavPoint();
+ }
+ else {
+ navpt = 0;
+ FindObjectiveFormation();
+ }
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+ return;
+ }
+
+ // under orders?
+ bool directed = false;
+ double threat_level = 0;
+ double support_level = 1;
+ Ship* ward = ship->GetWard();
+
+ if (tactical) {
+ directed = (tactical->RulesOfEngagement() == TacticalAI::DIRECTED);
+ threat_level = tactical->ThreatLevel();
+ support_level = tactical->SupportLevel();
+ }
+
+ // threat processing:
+ if (hold || !directed && threat_level >= 2*support_level) {
+
+ // seek support:
+ if (support) {
+ double d_support = Point(support->Location() - ship->Location()).length();
+ if (d_support > 35e3) {
+ ship->SetDirectorInfo(Game::GetText("ai.regroup"));
+ FindObjectiveTarget(support);
+ objective = Transform(obj_w);
+ return;
+ }
+ }
+
+ // run away:
+ else if (threat && threat != target) {
+ ship->SetDirectorInfo(Game::GetText("ai.retreat"));
+ obj_w = ship->Location() + Point(ship->Location() - threat->Location()) * 100;
+ objective = Transform(obj_w);
+ return;
+ }
+ }
+
+ // weapons hold:
+ if (hold) {
+ if (navpt) {
+ ship->SetDirectorInfo(Game::GetText("ai.seek-navpt"));
+ FindObjectiveNavPoint();
+ }
+
+ else if (patrol) {
+ ship->SetDirectorInfo(Game::GetText("ai.patrol"));
+ FindObjectivePatrol();
+ }
+
+ else {
+ ship->SetDirectorInfo(Game::GetText("ai.holding"));
+ objective = Point();
+ }
+ }
+
+ // normal processing:
+ else if (target) {
+ ship->SetDirectorInfo(Game::GetText("ai.seek-target"));
+ FindObjectiveTarget(target);
+ }
+
+ else if (patrol) {
+ ship->SetDirectorInfo(Game::GetText("ai.patrol"));
+ FindObjectivePatrol();
+ }
+
+ else if (ward) {
+ ship->SetDirectorInfo(Game::GetText("ai.seek-ward"));
+ FindObjectiveFormation();
+ }
+
+ else if (navpt) {
+ ship->SetDirectorInfo(Game::GetText("ai.seek-navpt"));
+ FindObjectiveNavPoint();
+ }
+
+ else if (rumor) {
+ ship->SetDirectorInfo(Game::GetText("ai.search"));
+ FindObjectiveTarget(rumor);
+ }
+
+ else {
+ objective = Point();
+ }
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarshipAI::Navigator()
+{
+ // signifies this ship is a dead hulk:
+ if (ship && ship->Design()->auto_roll < 0) {
+ ship->SetDirectorInfo(Game::GetText("ai.dead"));
+ return;
+ }
+
+ accumulator.Clear();
+ magnitude = 0;
+
+ hold = false;
+ if ((ship->GetElement() && ship->GetElement()->GetHoldTime() > 0) ||
+ (navpt && navpt->Status() == Instruction::COMPLETE && navpt->HoldTime() > 0))
+ hold = true;
+
+ ship->SetFLCSMode(Ship::FLCS_HELM);
+
+ if (!ship->GetDirectorInfo()) {
+ if (target)
+ ship->SetDirectorInfo(Game::GetText("ai.seek-target"));
+ else if (ship->GetWard())
+ ship->SetDirectorInfo(Game::GetText("ai.seek-ward"));
+ else
+ ship->SetDirectorInfo(Game::GetText("ai.patrol"));
+ }
+
+ if (farcaster && distance < 25e3) {
+ accumulator = SeekTarget();
+ }
+ else {
+ accumulator = AvoidCollision();
+
+ if (!other && !hold)
+ accumulator = SeekTarget();
+ }
+
+ HelmControl();
+ ThrottleControl();
+ FireControl();
+ AdjustDefenses();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarshipAI::HelmControl()
+{
+ // signifies this ship is a dead hulk:
+ if (ship && ship->Design()->auto_roll < 0) {
+ return;
+ }
+
+ double trans_x = 0;
+ double trans_y = 0;
+ double trans_z = 0;
+
+ bool station_keeping = distance < 0;
+
+ if (station_keeping) {
+ accumulator.brake = 1;
+ accumulator.stop = 1;
+
+ ship->SetHelmPitch(0);
+ }
+
+ else {
+ Element* elem = ship->GetElement();
+
+ Ship* ward = ship->GetWard();
+ Ship* s_threat = 0;
+ if (threat && threat->Class() >= ship->Class())
+ s_threat = threat;
+
+ if (other || target || ward || s_threat || navpt || patrol || farcaster || element_index > 1) {
+ ship->SetHelmHeading(accumulator.yaw);
+
+ if (elem->Type() == Mission::FLIGHT_OPS) {
+ ship->SetHelmPitch(0);
+
+ if (ship->NumInbound() > 0) {
+ ship->SetHelmHeading(ship->CompassHeading());
+ }
+ }
+
+ else if (accumulator.pitch > 60*DEGREES) {
+ ship->SetHelmPitch(60*DEGREES);
+ }
+
+ else if (accumulator.pitch < -60*DEGREES) {
+ ship->SetHelmPitch(-60*DEGREES);
+ }
+
+ else {
+ ship->SetHelmPitch(accumulator.pitch);
+ }
+
+ }
+ else {
+ ship->SetHelmPitch(0);
+ }
+ }
+
+ ship->SetTransX(trans_x);
+ ship->SetTransY(trans_y);
+ ship->SetTransZ(trans_z);
+
+ ship->ExecFLCSFrame();
+}
+
+void
+StarshipAI::ThrottleControl()
+{
+ // signifies this ship is a dead hulk:
+ if (ship && ship->Design()->auto_roll < 0) {
+ return;
+ }
+
+ // station keeping:
+
+ if (distance < 0) {
+ old_throttle = 0;
+ throttle = 0;
+
+ ship->SetThrottle(0);
+
+ if (ship->GetFLCS())
+ ship->GetFLCS()->FullStop();
+
+ return;
+ }
+
+ // normal throttle processing:
+
+ double ship_speed = ship->Velocity() * ship->Heading();
+ double brakes = 0;
+ Ship* ward = ship->GetWard();
+ Ship* s_threat = 0;
+
+ if (threat && threat->Class() >= ship->Class())
+ s_threat = threat;
+
+ if (target || s_threat) { // target pursuit, or retreat
+ throttle = 100;
+
+ if (target && distance < 50e3) {
+ double closing_speed = ship_speed;
+
+ if (target) {
+ Point delta = target->Location() - ship->Location();
+ delta.Normalize();
+
+ closing_speed = ship->Velocity() * delta;
+ }
+
+ if (closing_speed > 300) {
+ throttle = 30;
+ brakes = 0.25;
+ }
+ }
+
+ throttle *= (1 - accumulator.brake);
+
+ if (throttle < 1 && ship->GetFLCS() != 0)
+ ship->GetFLCS()->FullStop();
+ }
+
+ else if (ward) { // escort, match speed of ward
+ double speed = ward->Velocity().length();
+ throttle = old_throttle;
+
+ if (speed == 0) {
+ double d = (ship->Location() - ward->Location()).length();
+
+ if (d > 30e3)
+ speed = (d - 30e3) / 100;
+ }
+
+ if (speed > 0) {
+ if (ship_speed > speed) {
+ throttle = old_throttle - 1;
+ brakes = 0.2;
+ }
+ else if (ship_speed < speed - 10) {
+ throttle = old_throttle + 1;
+ }
+ }
+ else {
+ throttle = 0;
+ brakes = 0.5;
+ }
+ }
+
+ else if (patrol || farcaster) { // seek patrol point
+ throttle = 100;
+
+ if (distance < 10 * ship_speed) {
+ if (ship->Velocity().length() > 200)
+ throttle = 5;
+ else
+ throttle = 50;
+ }
+ }
+
+ else if (navpt) { // lead only, get speed from navpt
+ double speed = navpt->Speed();
+ throttle = old_throttle;
+
+ if (hold) {
+ throttle = 0;
+ brakes = 1;
+ }
+
+ else {
+ if (speed <= 0)
+ speed = 300;
+
+ if (ship_speed > speed) {
+ if (throttle > 0 && old_throttle > 1)
+ throttle = old_throttle - 1;
+
+ brakes = 0.25;
+ }
+ else if (ship_speed < speed - 10) {
+ throttle = old_throttle + 1;
+ }
+ }
+ }
+
+ else if (element_index > 1) { // wingman
+ Ship* lead = ship->GetElement()->GetShip(1);
+ double lv = lead->Velocity().length();
+ double sv = ship_speed;
+ double dv = lv-sv;
+ double dt = 0;
+
+ if (dv > 0) dt = dv * 1e-2 * seconds;
+ else if (dv < 0) dt = dv * 1e-2 * seconds;
+
+ throttle = old_throttle + dt;
+ }
+
+ else {
+ throttle = 0;
+ }
+
+ old_throttle = throttle;
+ ship->SetThrottle(throttle);
+
+ if (ship_speed > 1 && brakes > 0)
+ ship->SetTransY(-brakes * ship->Design()->trans_y);
+
+ else if (throttle > 10 && (ship->GetEMCON() < 2 || ship->GetFuelLevel() < 10))
+ ship->SetTransY(ship->Design()->trans_y);
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+StarshipAI::SeekTarget()
+{
+ if (navpt) {
+ SimRegion* self_rgn = ship->GetRegion();
+ SimRegion* nav_rgn = navpt->Region();
+ QuantumDrive* qdrive = ship->GetQuantumDrive();
+
+ if (self_rgn && !nav_rgn) {
+ nav_rgn = self_rgn;
+ navpt->SetRegion(nav_rgn);
+ }
+
+ bool use_farcaster = self_rgn != nav_rgn &&
+ (navpt->Farcast() ||
+ !qdrive ||
+ !qdrive->IsPowerOn() ||
+ qdrive->Status() < System::DEGRADED
+ );
+
+ if (use_farcaster) {
+ if (!farcaster) {
+ ListIter<Ship> s = self_rgn->Ships();
+ while (++s && !farcaster) {
+ if (s->GetFarcaster()) {
+ const Ship* dest = s->GetFarcaster()->GetDest();
+ if (dest && dest->GetRegion() == nav_rgn) {
+ farcaster = s->GetFarcaster();
+ }
+ }
+ }
+ }
+
+ if (farcaster) {
+ if (farcaster->GetShip()->GetRegion() != self_rgn)
+ farcaster = farcaster->GetDest()->GetFarcaster();
+
+ obj_w = farcaster->EndPoint();
+ distance = Point(obj_w - ship->Location()).length();
+
+ if (distance < 1000)
+ farcaster = 0;
+ }
+ }
+ else if (self_rgn != nav_rgn) {
+ QuantumDrive* q = ship->GetQuantumDrive();
+
+ if (q) {
+ if (q->ActiveState() == QuantumDrive::ACTIVE_READY) {
+ q->SetDestination(navpt->Region(), navpt->Location());
+ q->Engage();
+ }
+ }
+ }
+ }
+
+ return ShipAI::SeekTarget();
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+StarshipAI::AvoidCollision()
+{
+ if (!ship || ship->Velocity().length() < 25)
+ return Steer();
+
+ return ShipAI::AvoidCollision();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarshipAI::FireControl()
+{
+ // identify unknown contacts:
+ if (identify) {
+ if (fabs(ship->GetHelmHeading() - ship->CompassHeading()) < 10*DEGREES) {
+ Contact* contact = ship->FindContact(target);
+
+ if (contact && !contact->ActLock()) {
+ if (!ship->GetProbe()) {
+ ship->LaunchProbe();
+ }
+ }
+ }
+
+ return;
+ }
+
+ // investigate last known location of enemy ship:
+ if (rumor && !target && ship->GetProbeLauncher() && !ship->GetProbe()) {
+ // is rumor in basket?
+ Point rmr = Transform(rumor->Location());
+ rmr.Normalize();
+
+ double dx = fabs(rmr.x);
+ double dy = fabs(rmr.y);
+
+ if (dx < 10*DEGREES && dy < 10*DEGREES && rmr.z > 0) {
+ ship->LaunchProbe();
+ }
+ }
+
+ // Corvettes and Frigates are anti-air platforms. They need to
+ // target missile threats even when the threat is aimed at another
+ // friendly ship. Forward facing weapons must be on auto fire,
+ // while lateral and aft facing weapons are set to point defense.
+
+ if (ship->Class() == Ship::CORVETTE || ship->Class() == Ship::FRIGATE) {
+ ListIter<WeaponGroup> iter = ship->Weapons();
+ while (++iter) {
+ WeaponGroup* group = iter.value();
+
+ ListIter<Weapon> w_iter = group->GetWeapons();
+ while (++w_iter) {
+ Weapon* weapon = w_iter.value();
+
+ double az = weapon->GetAzimuth();
+ if (fabs(az) < 45*DEGREES) {
+ weapon->SetFiringOrders(Weapon::AUTO);
+ weapon->SetTarget(target, 0);
+ }
+
+ else {
+ weapon->SetFiringOrders(Weapon::POINT_DEFENSE);
+ }
+ }
+ }
+ }
+
+ // All other starships are free to engage ship targets. Weapon
+ // fire control is managed by the type of weapon.
+
+ else {
+ System* subtgt = SelectSubtarget();
+
+ ListIter<WeaponGroup> iter = ship->Weapons();
+ while (++iter) {
+ WeaponGroup* weapon = iter.value();
+
+ if (weapon->GetDesign()->target_type & Ship::DROPSHIPS) { // anti-air weapon?
+ weapon->SetFiringOrders(Weapon::POINT_DEFENSE);
+ }
+ else if (weapon->IsDrone()) { // torpedoes
+ weapon->SetFiringOrders(Weapon::MANUAL);
+ weapon->SetTarget(target, 0);
+
+ if (target && target->GetRegion() == ship->GetRegion()) {
+ Point delta = target->Location() - ship->Location();
+ double range = delta.length();
+
+ if (range < weapon->GetDesign()->max_range * 0.9 &&
+ !AssessTargetPointDefense())
+ weapon->SetFiringOrders(Weapon::AUTO);
+
+ else if (range < weapon->GetDesign()->max_range * 0.5)
+ weapon->SetFiringOrders(Weapon::AUTO);
+ }
+ }
+ else { // anti-ship weapon
+ weapon->SetFiringOrders(Weapon::AUTO);
+ weapon->SetTarget(target, subtgt);
+ weapon->SetSweep(subtgt ? Weapon::SWEEP_NONE : Weapon::SWEEP_TIGHT);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+System*
+StarshipAI::SelectSubtarget()
+{
+ if (Game::GameTime() - sub_select_time < 2345)
+ return subtarget;
+
+ subtarget = 0;
+
+ if (!target || target->Type() != SimObject::SIM_SHIP || GetAILevel() < 1)
+ return subtarget;
+
+ Ship* tgt_ship = (Ship*) target;
+
+ if (!tgt_ship->IsStarship())
+ return subtarget;
+
+ Weapon* subtgt = 0;
+ double dist = 50e3;
+ Point svec = ship->Location() - tgt_ship->Location();
+
+ sub_select_time = Game::GameTime();
+
+ // first pass: turrets
+ ListIter<WeaponGroup> g_iter = tgt_ship->Weapons();
+ while (++g_iter) {
+ WeaponGroup* g = g_iter.value();
+
+ if (g->GetDesign() && g->GetDesign()->turret_model) {
+ ListIter<Weapon> w_iter = g->GetWeapons();
+ while (++w_iter) {
+ Weapon* w = w_iter.value();
+
+ if (w->Availability() < 35)
+ continue;
+
+ if (w->GetAimVector() * svec < 0)
+ continue;
+
+ if (w->GetTurret()) {
+ Point tloc = w->GetTurret()->Location();
+ Point delta = tloc - ship->Location();
+ double dlen = delta.length();
+
+ if (dlen < dist) {
+ subtgt = w;
+ dist = dlen;
+ }
+ }
+ }
+ }
+ }
+
+ // second pass: major weapons
+ if (!subtgt) {
+ g_iter.reset();
+ while (++g_iter) {
+ WeaponGroup* g = g_iter.value();
+
+ if (g->GetDesign() && !g->GetDesign()->turret_model) {
+ ListIter<Weapon> w_iter = g->GetWeapons();
+ while (++w_iter) {
+ Weapon* w = w_iter.value();
+
+ if (w->Availability() < 35)
+ continue;
+
+ if (w->GetAimVector() * svec < 0)
+ continue;
+
+ Point tloc = w->MountLocation();
+ Point delta = tloc - ship->Location();
+ double dlen = delta.length();
+
+ if (dlen < dist) {
+ subtgt = w;
+ dist = dlen;
+ }
+ }
+ }
+ }
+ }
+
+ subtarget = subtgt;
+ return subtarget;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+StarshipAI::AssessTargetPointDefense()
+{
+ if (Game::GameTime() - point_defense_time < 3500)
+ return tgt_point_defense;
+
+ tgt_point_defense = false;
+
+ if (!target || target->Type() != SimObject::SIM_SHIP || GetAILevel() < 2)
+ return tgt_point_defense;
+
+ Ship* tgt_ship = (Ship*) target;
+
+ if (!tgt_ship->IsStarship())
+ return tgt_point_defense;
+
+ Weapon* subtgt = 0;
+ Point svec = ship->Location() - tgt_ship->Location();
+
+ point_defense_time = Game::GameTime();
+
+ // first pass: turrets
+ ListIter<WeaponGroup> g_iter = tgt_ship->Weapons();
+ while (++g_iter && !tgt_point_defense) {
+ WeaponGroup* g = g_iter.value();
+
+ if (g->CanTarget(1)) {
+ ListIter<Weapon> w_iter = g->GetWeapons();
+ while (++w_iter && !tgt_point_defense) {
+ Weapon* w = w_iter.value();
+
+ if (w->Availability() > 35 && w->GetAimVector() * svec > 0)
+ tgt_point_defense = true;
+ }
+ }
+ }
+
+ return tgt_point_defense;
+}
+
+
+// +--------------------------------------------------------------------+
+
+Point
+StarshipAI::Transform(const Point& point)
+{
+ return point - self->Location();
+}
+
+Steer
+StarshipAI::Seek(const Point& point)
+{
+ // the point is in relative world coordinates
+ // x: distance east(-) / west(+)
+ // y: altitude down(-) / up(+)
+ // z: distance north(-) / south(+)
+
+ Steer result;
+
+ result.yaw = atan2(point.x, point.z) + PI;
+
+ double adjacent = sqrt(point.x*point.x + point.z*point.z);
+ if (fabs(point.y) > ship->Radius() && adjacent > ship->Radius())
+ result.pitch = atan(point.y / adjacent);
+
+ if (!_finite(result.yaw))
+ result.yaw = 0;
+
+ if (!_finite(result.pitch))
+ result.pitch = 0;
+
+ return result;
+}
+
+Steer
+StarshipAI::Flee(const Point& point)
+{
+ Steer result = Seek(point);
+ result.yaw += PI;
+ return result;
+}
+
+Steer
+StarshipAI::Avoid(const Point& point, float radius)
+{
+ Steer result = Seek(point);
+
+ if (point * ship->BeamLine() > 0)
+ result.yaw -= PI/2;
+ else
+ result.yaw += PI/2;
+
+ return result;
+}
+
+
diff --git a/Stars45/StarshipAI.h b/Stars45/StarshipAI.h
new file mode 100644
index 0000000..1a20ec0
--- /dev/null
+++ b/Stars45/StarshipAI.h
@@ -0,0 +1,62 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: FighterAI.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Starship (low-level) Artifical Intelligence class
+*/
+
+#ifndef StarshipAI_h
+#define StarshipAI_h
+
+#include "Types.h"
+#include "ShipAI.h"
+
+// +--------------------------------------------------------------------+
+
+class StarshipAI : public ShipAI
+{
+public:
+ StarshipAI(SimObject* s);
+ virtual ~StarshipAI();
+
+ // convert the goal point from world to local coords:
+ virtual void FindObjective();
+
+protected:
+ // accumulate behaviors:
+ virtual void Navigator();
+ virtual Steer SeekTarget();
+ virtual Steer AvoidCollision();
+
+ // steering functions:
+ virtual Steer Seek(const Point& point);
+ virtual Steer Flee(const Point& point);
+ virtual Steer Avoid(const Point& point, float radius);
+
+ virtual Point Transform(const Point& pt);
+
+ // fire on target if appropriate:
+ virtual void FireControl();
+ virtual void HelmControl();
+ virtual void ThrottleControl();
+
+ System* SelectSubtarget();
+ bool AssessTargetPointDefense();
+
+ DWORD sub_select_time;
+ DWORD point_defense_time;
+ System* subtarget;
+ bool tgt_point_defense;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif StarshipAI_h
+
diff --git a/Stars45/StarshipTacticalAI.cpp b/Stars45/StarshipTacticalAI.cpp
new file mode 100644
index 0000000..c6756b1
--- /dev/null
+++ b/Stars45/StarshipTacticalAI.cpp
@@ -0,0 +1,276 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: StarshipTacticalAI.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Starship-specific mid-level (tactical) AI
+*/
+
+#include "MemDebug.h"
+#include "StarshipTacticalAI.h"
+#include "ShipAI.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Shot.h"
+#include "Element.h"
+#include "Instruction.h"
+#include "RadioMessage.h"
+#include "RadioTraffic.h"
+#include "Contact.h"
+#include "WeaponGroup.h"
+#include "Drive.h"
+#include "QuantumDrive.h"
+#include "Sim.h"
+#include "StarSystem.h"
+#include "Starshatter.h"
+#include "Random.h"
+
+#include "Game.h"
+
+const double STARSHIP_TACTICAL_DROP_TIME = 15;
+
+// +----------------------------------------------------------------------+
+
+StarshipTacticalAI::StarshipTacticalAI(ShipAI* ai)
+ : TacticalAI(ai), drop_time(1.0e9), bugout(false), ai_level(0), initial_integrity(0)
+{
+ if (ai && ai->GetShip()) {
+ ai_level = ai->GetAILevel();
+ initial_integrity = ai->GetShip()->Integrity();
+ }
+
+ switch (ai_level) {
+ default:
+ case 2: THREAT_REACTION_TIME = 500; break;
+ case 1: THREAT_REACTION_TIME = 1000; break;
+ case 0: THREAT_REACTION_TIME = 2500;
+ drop_time = STARSHIP_TACTICAL_DROP_TIME +
+ Random(0, STARSHIP_TACTICAL_DROP_TIME);
+ break;
+ }
+}
+
+
+// +--------------------------------------------------------------------+
+
+StarshipTacticalAI::~StarshipTacticalAI()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+StarshipTacticalAI::ExecFrame(double seconds)
+{
+ TacticalAI::ExecFrame(seconds);
+
+ drop_time -= seconds;
+
+ if (drop_time <= 0) {
+ drop_time = STARSHIP_TACTICAL_DROP_TIME;
+
+ ship_ai->DropTarget(STARSHIP_TACTICAL_DROP_TIME/4);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarshipTacticalAI::FindThreat()
+{
+ // pick the closest contact on Threat Warning System:
+ Ship* threat_ship = 0;
+ Shot* threat_missile = 0;
+ Ship* rumor = 0;
+ double threat_dist = 1e9;
+ double CELL_SIZE = 20e3;
+
+ threat_level = 0;
+ support_level = ship->AIValue() / CELL_SIZE;
+
+ ListIter<Contact> iter = ship->ContactList();
+
+ while (++iter) {
+ Contact* contact = iter.value();
+ Ship* c_ship = contact->GetShip();
+ Shot* c_shot = contact->GetShot();
+
+ if (!c_ship && !c_shot)
+ continue;
+
+ if (c_ship && c_ship != ship) {
+ double basis = max(contact->Range(ship), CELL_SIZE);
+ double ai_value = c_ship->AIValue() / basis;
+
+ if (c_ship->GetIFF() == ship->GetIFF()) {
+ support_level += ai_value;
+ }
+ else if (ship->GetIFF() > 0 && c_ship->GetIFF() > 0) {
+ threat_level += ai_value;
+ }
+ else if (c_ship->GetIFF() > 1) { // neutrals should not be afraid of alliance
+ threat_level += ai_value;
+ }
+ }
+
+ if (contact->Threat(ship) &&
+ (Game::GameTime() - contact->AcquisitionTime()) > THREAT_REACTION_TIME) {
+
+ if (c_shot) {
+ threat_missile = c_shot;
+ rumor = (Ship* ) threat_missile->Owner();
+ }
+ else {
+ double rng = contact->Range(ship);
+
+ if (c_ship &&
+ c_ship->Class() != Ship::FREIGHTER &&
+ c_ship->Class() != Ship::FARCASTER) {
+
+
+ if (c_ship->GetTarget() == ship) {
+ if (!threat_ship || c_ship->Class() > threat_ship->Class()) {
+ threat_ship = c_ship;
+ threat_dist = 0;
+ }
+ }
+ else if (rng < threat_dist) {
+ threat_ship = c_ship;
+ threat_dist = rng;
+ }
+
+ CheckBugOut(c_ship, rng);
+ }
+ }
+ }
+ }
+
+ if (rumor) {
+ iter.reset();
+
+ while (++iter) {
+ if (iter->GetShip() == rumor) {
+ rumor = 0;
+ ship_ai->ClearRumor();
+ break;
+ }
+ }
+ }
+
+ ship_ai->SetRumor(rumor);
+ ship_ai->SetThreat(threat_ship);
+ ship_ai->SetThreatMissile(threat_missile);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+StarshipTacticalAI::FindSupport()
+{
+ if (threat_level < 0.01) {
+ ship_ai->SetSupport(0);
+ return;
+ }
+
+ // pick the biggest friendly contact in the sector:
+ Ship* support = 0;
+ double support_dist = 1e9;
+
+ ListIter<Contact> c_iter = ship->ContactList();
+
+ while (++c_iter) {
+ Contact* contact = c_iter.value();
+ if (contact->GetShip() && contact->GetIFF(ship) == ship->GetIFF()) {
+ Ship* c_ship = contact->GetShip();
+
+ if (c_ship != ship && c_ship->Class() >= ship->Class()) {
+ if (!support || c_ship->Class() > support->Class())
+ support = c_ship;
+ }
+ }
+ }
+
+ ship_ai->SetSupport(support);
+}
+
+void
+StarshipTacticalAI::CheckBugOut(Ship* c_ship, double rng)
+{
+ // see if carrier should bug out...
+ if (!ship || !c_ship || ship->Class() != Ship::CARRIER)
+ return;
+
+ if (bugout)
+ return;
+
+ if (ship->GetElement() && ship->GetElement()->GetZoneLock())
+ return;
+
+ if (c_ship->Class() < Ship::DESTROYER || c_ship->Class() > Ship::STATION)
+ return;
+
+ Starshatter* stars = Starshatter::GetInstance();
+ if (stars && stars->InCutscene())
+ return;
+
+ double sustained_damage = initial_integrity - ship->Integrity();
+ double allowable_damage = ship->Design()->integrity * 0.25;
+
+ if (rng > 50e3 && sustained_damage < allowable_damage)
+ return;
+
+ // still here? we must need to bug out!
+
+ Sim* sim = Sim::GetSim();
+ SimRegion* dst = 0;
+
+ List<SimRegion>& regions = sim->GetRegions();
+
+ if (regions.size() > 1) {
+ int tries = 10;
+ while (!dst && tries--) {
+ int n = RandomIndex() % regions.size();
+ dst = regions[n];
+
+ if (dst == ship->GetRegion() || dst->IsAirSpace())
+ dst = 0;
+ }
+ }
+
+ if (dst) {
+ // bug out!
+ QuantumDrive* quantum = ship->GetQuantumDrive();
+ if (quantum) {
+ quantum->SetDestination(dst, Point(0,0,0));
+ quantum->Engage();
+ }
+
+ // ask highest ranking escort to go with you:
+ Element* escort = 0;
+
+ ListIter<Element> iter = sim->GetElements();
+ while (++iter) {
+ Element* elem = iter.value();
+
+ if (!escort || elem->GetShipClass() > escort->GetShipClass()) {
+ if (ship->GetElement()->CanCommand(elem))
+ escort = elem;
+ }
+ }
+
+ if (escort) {
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(escort, ship, RadioMessage::QUANTUM_TO);
+ if (msg) {
+ msg->SetInfo(dst->Name());
+ RadioTraffic::Transmit(msg);
+ }
+ }
+
+ bugout = true;
+ }
+}
diff --git a/Stars45/StarshipTacticalAI.h b/Stars45/StarshipTacticalAI.h
new file mode 100644
index 0000000..007c9ad
--- /dev/null
+++ b/Stars45/StarshipTacticalAI.h
@@ -0,0 +1,47 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: StarshipTacticalAI.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Starship-specific mid-level (tactical) AI
+*/
+
+#ifndef StarshipTacticalAI_h
+#define StarshipTacticalAI_h
+
+#include "Types.h"
+#include "TacticalAI.h"
+
+// +--------------------------------------------------------------------+
+
+class StarshipTacticalAI : public TacticalAI
+{
+public:
+ StarshipTacticalAI(ShipAI* ai);
+ virtual ~StarshipTacticalAI();
+
+ virtual void ExecFrame(double seconds);
+
+protected:
+ virtual void FindThreat();
+ virtual void FindSupport();
+
+ virtual void CheckBugOut(Ship* c_ship, double range);
+
+ DWORD THREAT_REACTION_TIME;
+ int ai_level;
+ double drop_time;
+ double initial_integrity;
+ bool bugout;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif StarshipTacticalAI_h
+
diff --git a/Stars45/SteerAI.cpp b/Stars45/SteerAI.cpp
new file mode 100644
index 0000000..7287949
--- /dev/null
+++ b/Stars45/SteerAI.cpp
@@ -0,0 +1,453 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: SteerAI.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Steering (low-level) Artificial Intelligence class
+*/
+
+#include "MemDebug.h"
+#include "SteerAI.h"
+#include "SeekerAI.h"
+#include "FighterAI.h"
+#include "StarshipAI.h"
+#include "GroundAI.h"
+#include "System.h"
+
+#include "Game.h"
+#include "Physical.h"
+
+// +----------------------------------------------------------------------+
+
+Steer Steer::operator+(const Steer& s) const
+{
+ return Steer(yaw+s.yaw, pitch+s.pitch, roll+s.roll, (brake>s.brake)?brake:s.brake);
+}
+
+Steer Steer::operator-(const Steer& s) const
+{
+ return Steer(yaw-s.yaw, pitch-s.pitch, roll-s.roll, (brake<s.brake)?brake:s.brake);
+}
+
+Steer Steer::operator*(double f) const
+{
+ return Steer(yaw*f, pitch*f, roll*f, brake);
+}
+
+Steer Steer::operator/(double f) const
+{
+ return Steer(yaw/f, pitch/f, roll/f, brake);
+}
+
+
+Steer& Steer::operator+=(const Steer& s)
+{
+ yaw += s.yaw;
+ pitch += s.pitch;
+ roll += s.roll;
+
+ if (s.brake > brake)
+ brake = s.brake;
+
+ if (s.stop)
+ stop = 1;
+
+ return *this;
+}
+
+Steer& Steer::operator-=(const Steer& s)
+{
+ yaw -= s.yaw;
+ pitch -= s.pitch;
+ roll -= s.roll;
+
+ if (s.brake < brake)
+ brake = s.brake;
+
+ if (s.stop)
+ stop = 1;
+
+ return *this;
+}
+
+
+double
+Steer::Magnitude() const
+{
+ return sqrt(yaw*yaw + pitch*pitch);
+}
+
+// +--------------------------------------------------------------------+
+
+Director*
+SteerAI::Create(SimObject* self, int type)
+{
+ switch (type) {
+ case SEEKER: return new(__FILE__,__LINE__) SeekerAI(self);
+ break;
+
+ case STARSHIP: return new(__FILE__,__LINE__) StarshipAI(self);
+ break;
+
+ case GROUND: return new(__FILE__,__LINE__) GroundAI(self);
+ break;
+
+ default:
+ case FIGHTER: return new(__FILE__,__LINE__) FighterAI(self);
+ break;
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+SteerAI::SteerAI(SimObject* ship)
+ : self(ship),
+ target(0), subtarget(0), other(0), distance(0.0), evade_time(0),
+ objective(0.0f, 0.0f, 0.0f)
+{
+ seek_gain = 20;
+ seek_damp = 0.5;
+
+ for (int i = 0; i < 3; i++)
+ az[i] = el[i] = 0;
+}
+
+
+// +--------------------------------------------------------------------+
+
+SteerAI::~SteerAI()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+SteerAI::SetTarget(SimObject* targ, System* sub)
+{
+ if (target != targ) {
+ target = targ;
+
+ if (target)
+ Observe(target);
+ }
+
+ subtarget = sub;
+}
+
+void
+SteerAI::DropTarget(double dtime)
+{
+ SetTarget(0);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SteerAI::Update(SimObject* obj)
+{
+ if (obj == target) {
+ target = 0;
+ subtarget = 0;
+ }
+
+ if (obj == other) {
+ other = 0;
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+SteerAI::GetObserverName() const
+{
+ static char name[64];
+ sprintf(name, "SteerAI(%s)", self->Name());
+ return name;
+}
+
+// +--------------------------------------------------------------------+
+
+Point
+SteerAI::ClosingVelocity()
+{
+ if (self) {
+ if (target)
+ return self->Velocity() - target->Velocity();
+ else
+ return self->Velocity();
+ }
+
+ return Point(1, 0, 0);
+}
+
+void
+SteerAI::FindObjective()
+{
+ if (!self || !target) return;
+
+ Point cv = ClosingVelocity();
+ double cvl = cv.length();
+ double time = 0;
+
+ if (cvl > 5) {
+ // distance from self to target:
+ distance = Point(target->Location() - self->Location()).length();
+
+ // time to reach target:
+ time = distance / cvl;
+
+ // where the target will be when we reach it:
+ Point run_vec = target->Velocity();
+ obj_w = target->Location() + (run_vec * time);
+ }
+
+ else {
+ obj_w = target->Location();
+ }
+
+ // subsystem offset:
+ if (subtarget) {
+ Point offset = target->Location() - subtarget->MountLocation();
+ obj_w -= offset;
+ }
+
+ distance = Point(obj_w - self->Location()).length();
+
+ if (cvl > 5)
+ time = distance / cvl;
+
+ // where we will be when the target gets there:
+ Point self_dest = self->Location() + cv * time;
+ Point err = obj_w - self_dest;
+
+ obj_w += err;
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+ objective.Normalize();
+
+ distance = Point(obj_w - self->Location()).length();
+}
+
+Point
+SteerAI::Transform(const Point& pt)
+{
+ Point obj_t = pt - self->Location();
+ Point result;
+
+ if (self->FlightPathYawAngle() != 0 || self->FlightPathPitchAngle() != 0) {
+ double az = self->FlightPathYawAngle();
+ double el = self->FlightPathPitchAngle();
+
+ const double MAX_ANGLE = 15*DEGREES;
+ const double MIN_ANGLE = 3*DEGREES;
+
+ if (az > MAX_ANGLE)
+ az = MAX_ANGLE;
+ else if (az < -MAX_ANGLE)
+ az = -MAX_ANGLE;
+ else if (az > MIN_ANGLE)
+ az = MIN_ANGLE + (az-MIN_ANGLE)/2;
+ else if (az < -MIN_ANGLE)
+ az = -MIN_ANGLE + (az+MIN_ANGLE)/2;
+
+ if (el > MAX_ANGLE)
+ el = MAX_ANGLE;
+ else if (el < -MAX_ANGLE)
+ el = -MAX_ANGLE;
+ else if (el > MIN_ANGLE)
+ el = MIN_ANGLE + (el-MIN_ANGLE)/2;
+ else if (el < -MIN_ANGLE)
+ el = -MIN_ANGLE + (el+MIN_ANGLE)/2;
+
+ Camera cam;
+ cam.Clone(self->Cam());
+ cam.Yaw(az);
+ cam.Pitch(-el);
+
+ result = Point(obj_t * cam.vrt(),
+ obj_t * cam.vup(),
+ obj_t * cam.vpn());
+ }
+ else {
+ Camera& cam = (Camera&) self->Cam(); // cast away const
+
+ result = Point(obj_t * cam.vrt(),
+ obj_t * cam.vup(),
+ obj_t * cam.vpn());
+ }
+
+ return result;
+}
+
+Point
+SteerAI::AimTransform(const Point& pt)
+{
+ Camera& cam = (Camera&) self->Cam(); // cast away const
+ Point obj_t = pt - self->Location();
+
+ Point result = Point(obj_t * cam.vrt(),
+ obj_t * cam.vup(),
+ obj_t * cam.vpn());
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SteerAI::Navigator()
+{
+ accumulator.Clear();
+ magnitude = 0;
+}
+
+int
+SteerAI::Accumulate(const Steer& steer)
+{
+ int overflow = 0;
+
+ double mag = steer.Magnitude();
+
+ if (magnitude + mag > 1) {
+ overflow = 1;
+ double scale = (1 - magnitude) / mag;
+
+ accumulator += steer * scale;
+ magnitude = 1;
+
+ if (seeking) {
+ az[0] *= scale;
+ el[0] *= scale;
+ seeking = 0;
+ }
+ }
+ else {
+ accumulator += steer;
+ magnitude += mag;
+ }
+
+ return overflow;
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+SteerAI::Seek(const Point& point)
+{
+ Steer s;
+
+ // advance memory pipeline:
+ az[2] = az[1]; az[1] = az[0];
+ el[2] = el[1]; el[1] = el[0];
+
+ // approach
+ if (point.z > 0.0f) {
+ az[0] = atan2(fabs(point.x), point.z) * seek_gain;
+ el[0] = atan2(fabs(point.y), point.z) * seek_gain;
+
+ if (point.x < 0) az[0] = -az[0];
+ if (point.y > 0) el[0] = -el[0];
+
+ s.yaw = az[0] - seek_damp * (az[1] + az[2] * 0.5);
+ s.pitch = el[0] - seek_damp * (el[1] + el[2] * 0.5);
+ }
+
+ // reverse
+ else {
+ if (point.x > 0) s.yaw = 1.0f;
+ else s.yaw = -1.0f;
+
+ s.pitch = -point.y * 0.5f;
+ }
+
+ seeking = 1;
+
+ return s;
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+SteerAI::Flee(const Point& pt)
+{
+ Steer s;
+
+ Point point = pt;
+ point.Normalize();
+
+ // approach
+ if (point.z > 0.0f) {
+ if (point.x > 0) s.yaw = -1.0f;
+ else s.yaw = 1.0f;
+ }
+
+ // flee
+ else {
+ s.yaw = -point.x;
+ s.pitch = point.y;
+ }
+
+ return s;
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+SteerAI::Avoid(const Point& point, float radius)
+{
+ Steer s;
+
+ if (point.z > 0) {
+ double ax = radius - fabs(point.x);
+ double ay = radius - fabs(point.y);
+
+ // go around?
+ if (ax < ay) {
+ s.yaw = atan2(ax, point.z) * seek_gain;
+ if (point.x > 0) s.yaw = -s.yaw;
+ }
+
+ // go over/under:
+ else {
+ s.pitch = atan2(ay, point.z) * seek_gain;
+ if (point.y < 0) s.pitch = -s.pitch;
+ }
+ }
+
+ return s;
+}
+
+// +--------------------------------------------------------------------+
+
+Steer
+SteerAI::Evade(const Point& point, const Point& vel)
+{
+ Steer evade;
+
+ if (Game::GameTime() - evade_time > 1250) {
+ evade_time = Game::GameTime();
+
+ int direction = (rand()>>9) & 0x07;
+
+ switch (direction) {
+ default:
+ case 0: evade.yaw = 0; evade.pitch = -0.5; break;
+ case 1: evade.yaw = 0; evade.pitch = -1.0; break;
+ case 2: evade.yaw = 1; evade.pitch = -0.3; break;
+ case 3: evade.yaw = 1; evade.pitch = -0.6; break;
+ case 4: evade.yaw = 1; evade.pitch = -1.0; break;
+ case 5: evade.yaw = -1; evade.pitch = -0.3; break;
+ case 6: evade.yaw = -1; evade.pitch = -0.6; break;
+ case 7: evade.yaw = -1; evade.pitch = -1.0; break;
+ }
+ }
+
+ return evade;
+}
+
diff --git a/Stars45/SteerAI.h b/Stars45/SteerAI.h
new file mode 100644
index 0000000..7a107d5
--- /dev/null
+++ b/Stars45/SteerAI.h
@@ -0,0 +1,125 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: SteerAI.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Steering (low-level) Artifical Intelligence class
+*/
+
+#ifndef SteerAI_h
+#define SteerAI_h
+
+#include "Types.h"
+#include "SimObject.h"
+#include "Director.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class System;
+
+// +--------------------------------------------------------------------+
+
+struct Steer
+{
+ Steer() : yaw(0), pitch(0), roll(0), brake(0), stop(0) { }
+ Steer(double y, double p, double r, double b=0) : yaw(y), pitch(p), roll(r), brake(b), stop(0) { }
+ Steer(const Steer& s) : yaw(s.yaw), pitch(s.pitch), roll(s.roll), brake(s.brake), stop(s.stop) { }
+
+ Steer& operator = (const Steer& s) { yaw=s.yaw; pitch=s.pitch; roll=s.roll; brake = s.brake; stop = s.stop; return *this; }
+
+ Steer operator+(const Steer& s) const;
+ Steer operator-(const Steer& s) const;
+ Steer operator*(double f) const;
+ Steer operator/(double f) const;
+
+ Steer& operator+=(const Steer& s);
+ Steer& operator-=(const Steer& s);
+
+ double Magnitude() const;
+
+ void Clear() { yaw=0; pitch=0; roll=0; brake=0; stop=0; }
+
+ double yaw, pitch, roll;
+ double brake;
+ int stop;
+};
+
+// +--------------------------------------------------------------------+
+
+class SteerAI : public Director, public SimObserver
+{
+public:
+ enum Type { SEEKER = 1000, FIGHTER, STARSHIP, GROUND };
+
+ SteerAI(SimObject* self);
+ virtual ~SteerAI();
+
+ static Director* Create(SimObject*, int type);
+
+ virtual void SetTarget(SimObject* targ, System* sub=0);
+ virtual SimObject* GetTarget() const { return target; }
+ virtual System* GetSubTarget() const { return subtarget; }
+ virtual void DropTarget(double drop_time=1.5);
+ virtual int Type() const { return ai_type; }
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+ // debug:
+ virtual Point GetObjective() const { return obj_w; }
+ virtual SimObject* GetOther() const { return other; }
+
+protected:
+
+ // accumulate behaviors:
+ virtual void Navigator();
+ virtual int Accumulate(const Steer& steer);
+
+ // steering functions:
+ virtual Steer Seek(const Point& point);
+ virtual Steer Flee(const Point& point);
+ virtual Steer Avoid(const Point& point, float radius);
+ virtual Steer Evade(const Point& point, const Point& vel);
+
+ // compute the goal point based on target stats:
+ virtual void FindObjective();
+ virtual Point ClosingVelocity();
+
+ virtual Point Transform(const Point& pt);
+ virtual Point AimTransform(const Point& pt);
+
+ int seeking;
+
+ SimObject* self;
+ SimObject* target;
+ System* subtarget;
+ SimObject* other;
+
+ Point obj_w;
+ Point objective;
+
+ double distance;
+ double az[3], el[3];
+
+ Steer accumulator;
+ double magnitude;
+ DWORD evade_time;
+
+ double seek_gain;
+ double seek_damp;
+
+ int ai_type;
+};
+
+
+// +--------------------------------------------------------------------+
+
+#endif SteerAI_h
+
diff --git a/Stars45/System.cpp b/Stars45/System.cpp
new file mode 100644
index 0000000..2dc552a
--- /dev/null
+++ b/Stars45/System.cpp
@@ -0,0 +1,399 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: System.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Generic Ship Subsystem class
+*/
+
+#include "MemDebug.h"
+#include "System.h"
+#include "SystemDesign.h"
+#include "Component.h"
+#include "NetUtil.h"
+
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+System::System(System::CATEGORY t, int s, const char* n, int maxv,
+ double e, double c, double r)
+ : type(t), id(0), ship(0), subtype(s), status(NOMINAL), availability(1.0f),
+ safety(1.0f), stability(1.0f), crit_level(0.5f), net_avail(-1.0f),
+ mount_rel(0.0f, 0.0f, 0.0f), radius(0.0f), safety_overload(0.0f),
+ hull_factor(0.5f), energy((float) e), capacity((float) c), sink_rate((float) r),
+ power_level(1.0f), power_flags(0), source_index(0), power_on(true),
+ explosion_type(0), name(n), abrv(name), design(0), emcon(3)
+{
+ if (maxv < 100)
+ max_value = maxv;
+ else
+ max_value = (int) (maxv/100.0);
+
+ emcon_power[0] = 100;
+ emcon_power[1] = 100;
+ emcon_power[2] = 100;
+}
+
+// +----------------------------------------------------------------------+
+
+System::System(const System& s)
+ : type(s.type), id(s.id), ship(0), subtype(s.subtype), status(s.status),
+ availability(s.availability), safety(s.safety), stability(s.stability),
+ crit_level(s.crit_level), net_avail(-1.0f),
+ mount_rel(s.mount_rel), radius(s.radius), safety_overload(0.0f),
+ hull_factor(s.hull_factor), energy(s.energy), capacity(s.capacity),
+ sink_rate(s.sink_rate), power_level(s.power_level), power_flags(s.power_flags),
+ source_index(s.source_index), power_on(s.power_on), max_value(s.max_value),
+ explosion_type(s.explosion_type), name(s.name), abrv(s.abrv), design(s.design),
+ emcon(s.emcon)
+{
+ if (design) {
+ // cast-away const
+ ListIter<Component> c = (List<Component>&) s.components;
+ while (++c) {
+ Component* comp = new(__FILE__,__LINE__) Component(*(c.value()));
+ comp->SetSystem(this);
+ components.append(comp);
+ }
+ }
+
+ emcon_power[0] = s.emcon_power[0];
+ emcon_power[1] = s.emcon_power[1];
+ emcon_power[2] = s.emcon_power[2];
+}
+
+// +--------------------------------------------------------------------+
+
+System::~System()
+{
+ components.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+System::SetDesign(SystemDesign* d)
+{
+ if (design) {
+ design = 0;
+ components.destroy();
+ }
+
+ if (d) {
+ design = d;
+
+ ListIter<ComponentDesign> cd = design->components;
+ while (++cd) {
+ Component* comp = new(__FILE__,__LINE__) Component(cd.value(), this);
+ components.append(comp);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+System::SetPowerLevel(double level)
+{
+ if (level > 100)
+ level = 100;
+ else if (level < 0)
+ level = 0;
+
+ level /= 100;
+
+ if (power_level != level) {
+ // if the system is on emergency override power,
+ // do not let the EMCON system use this method
+ // to drop it back to normal power:
+ if (power_level > 1 && level == 1)
+ return;
+
+ power_level = (float) level;
+
+ NetUtil::SendSysStatus(ship, this);
+ }
+}
+
+void
+System::SetOverride(bool over)
+{
+ bool changed = false;
+
+ if (over && power_level != 1.2f) {
+ power_level = 1.2f;
+ changed = true;
+ }
+
+ else if (!over && power_level > 1) {
+ power_level = 1.0f;
+ changed = true;
+ }
+
+ if (changed)
+ NetUtil::SendSysStatus(ship, this);
+}
+
+void
+System::SetEMCONPower(int index, int power_level)
+{
+ if (index >= 1 && index <= 3) {
+ emcon_power[index-1] = (BYTE) power_level;
+ }
+}
+
+int
+System::GetEMCONPower(int index)
+{
+ if (index >= 1 && index <= 3) {
+ return emcon_power[index-1];
+ }
+
+ return 100;
+}
+
+void
+System::DoEMCON(int index)
+{
+ int e = GetEMCONPower(index);
+
+ if (power_level * 100 > e || emcon != index) {
+ if (e == 0) {
+ PowerOff();
+ }
+ else {
+ if (emcon != index)
+ PowerOn();
+
+ SetPowerLevel(e);
+ }
+ }
+
+ emcon = index;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+System::ExecFrame(double seconds)
+{
+ if (seconds < 0.01)
+ seconds = 0.01;
+
+ STATUS s = DESTROYED;
+
+ if (availability > 0.99)
+ s = NOMINAL;
+ else if (availability > crit_level)
+ s = DEGRADED;
+ else
+ s = CRITICAL;
+
+ bool repair = false;
+
+ if (components.size() > 0) {
+ ListIter<Component> comp = components;
+ while (++comp) {
+ if (comp->Status() > Component::NOMINAL) {
+ repair = true;
+ break;
+ }
+ }
+ }
+
+ if (repair) {
+ Repair();
+ }
+
+ else {
+ if (status != s) {
+ status = s;
+ NetUtil::SendSysStatus(ship, this);
+ }
+
+ // collateral damage due to unsafe operation:
+ if (power_on && power_level > safety) {
+ safety_overload += (float) seconds;
+
+ // inflict some damage now:
+ if (safety_overload > 60) {
+ safety_overload -= (float) (rand() / (1000 * (power_level-safety)));
+ ApplyDamage(15);
+
+ NetUtil::SendSysStatus(ship, this);
+ }
+ }
+
+ else if (safety_overload > 0) {
+ safety_overload -= (float) seconds;
+ }
+ }
+}
+
+void
+System::Repair()
+{
+ if (status != MAINT) {
+ status = MAINT;
+ safety_overload = 0.0f;
+
+ NetUtil::SendSysStatus(ship, this);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+System::ExecMaintFrame(double seconds)
+{
+ if (components.size() > 0) {
+ ListIter<Component> comp = components;
+ while (++comp) {
+ if (comp->Status() > Component::NOMINAL) {
+ comp->ExecMaintFrame(seconds);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+System::ApplyDamage(double damage)
+{
+ if (!power_on)
+ damage /= 10;
+
+ if (components.size() > 0) {
+ int index = rand() % components.size();
+
+ if (damage > 50) {
+ damage/=2;
+ components[index]->ApplyDamage(damage);
+
+ index = rand() % components.size();
+ }
+
+ components[index]->ApplyDamage(damage);
+
+ if (safety < 0.5)
+ SetPowerLevel(50);
+
+ else if (safety < 1.0)
+ SetPowerLevel(safety * 100);
+ }
+ else {
+ availability -= (float) (damage/100.0f);
+ if (availability < 0.01) availability = 0.0f;
+ }
+}
+
+void
+System::CalcStatus()
+{
+ if (components.size() > 0) {
+ availability = 1.0f;
+ safety = 1.0f;
+ stability = 1.0f;
+
+ ListIter<Component> comp = components;
+ while (++comp) {
+ if (comp->DamageEfficiency())
+ availability *= comp->Availability() / 100.0f;
+
+ if (comp->DamageSafety())
+ safety *= comp->Availability() / 100.0f;
+
+ if (comp->DamageStability())
+ stability *= comp->Availability() / 100.0f;
+
+ if (comp->IsJerried()) {
+ safety *= 0.95f;
+ stability *= 0.95f;
+ }
+ }
+
+ if (net_avail >= 0 && availability < net_avail)
+ availability = net_avail;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+System::Mount(Point loc, float rad, float hull)
+{
+ mount_rel = loc;
+ radius = rad;
+ hull_factor = hull;
+}
+
+void
+System::Mount(const System& system)
+{
+ mount_rel = system.mount_rel;
+ radius = system.radius;
+ hull_factor = system.hull_factor;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+System::Orient(const Physical* rep)
+{
+ const Matrix& orientation = rep->Cam().Orientation();
+ Point loc = rep->Location();
+
+ mount_loc = (mount_rel * orientation) + loc;
+}
+
+// +----------------------------------------------------------------------+
+
+double
+System::GetRequest(double seconds) const
+{
+ if (!power_on || capacity == energy)
+ return 0;
+
+ else
+ return power_level * sink_rate * seconds;
+}
+
+// +----------------------------------------------------------------------+
+
+void
+System::Distribute(double delivered_energy, double seconds)
+{
+ if (UsesWatts()) {
+ if (seconds < 0.01)
+ seconds = 0.01;
+
+ // convert Joules to Watts:
+ energy = (float) (delivered_energy/seconds);
+ }
+
+ else if (!Game::Paused()) {
+ energy += (float) delivered_energy;
+
+ if (energy > capacity)
+ energy = capacity;
+
+ else if (energy < 0)
+ energy = 0.0f;
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+System::DrainPower(double to_level)
+{
+ energy = 0.0f;
+}
diff --git a/Stars45/System.h b/Stars45/System.h
new file mode 100644
index 0000000..61cefbe
--- /dev/null
+++ b/Stars45/System.h
@@ -0,0 +1,174 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: System.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Generic ship System class
+*/
+
+#ifndef System_h
+#define System_h
+
+#include "Types.h"
+#include "Physical.h"
+#include "Geometry.h"
+
+#include "List.h"
+#include "text.h"
+
+// +--------------------------------------------------------------------+
+
+class Component;
+class Ship;
+class SystemDesign;
+
+// +--------------------------------------------------------------------+
+
+class System
+{
+ friend Component;
+
+public:
+ static const char* TYPENAME() { return "System"; }
+
+ enum CATEGORY { MISC_SYSTEM=0, DRIVE=1, WEAPON, SHIELD, SENSOR,
+ COMPUTER, POWER_SOURCE, FLIGHT_DECK, FARCASTER };
+ enum STATUS { DESTROYED, CRITICAL, DEGRADED, NOMINAL, MAINT };
+ enum POWER_FLAGS { POWER_WATTS=1, POWER_CRITICAL=2 };
+
+ System(CATEGORY t, int s, const char* n, int maxv,
+ double energy=0, double capacity=100, double sink_rate=1);
+ System(const System& s);
+ virtual ~System();
+
+ int operator==(const System& s) const { return this == &s; }
+
+ CATEGORY Type() const { return type; }
+ int Subtype() const { return subtype; }
+ const char* Name() const { return name; }
+ const char* Abbreviation() const { return abrv; }
+
+ void SetName(const char* n) { name = n; }
+ void SetAbbreviation(const char* a) { abrv = a; }
+ void SetDesign(SystemDesign* d);
+
+ virtual int Value() const { return (int) (max_value*availability*100); }
+ int MaxValue() const { return (int) (max_value*100); }
+ STATUS Status() const { return status; }
+ double Availability() const { return availability*100; }
+ double Safety() const { return safety*100; }
+ double Stability() const { return stability*100; }
+ virtual void CalcStatus();
+ virtual void Repair();
+
+ double NetAvail() const { return net_avail; }
+ void SetNetAvail(double d){ net_avail = (float) d; }
+
+ List<Component>& GetComponents() { return components; }
+
+ virtual void ApplyDamage(double damage);
+ virtual void ExecFrame(double seconds);
+ virtual void ExecMaintFrame(double seconds);
+ virtual void DoEMCON(int emcon);
+
+ // PHYSICAL LOCATION (for inflicting system damage):
+ virtual void Orient(const Physical* rep);
+ virtual void Mount(Point loc, float radius, float hull_factor=0.5f);
+ virtual void Mount(const System& system);
+
+ Point MountLocation() const { return mount_loc; }
+ double Radius() const { return radius; }
+ double HullProtection() const { return hull_factor; }
+
+ // POWER UTILIZATION:
+ bool IsPowerCritical() const { return (power_flags & POWER_CRITICAL)?true:false; }
+ bool UsesWatts() const { return (power_flags & POWER_WATTS)?true:false; }
+
+ virtual double GetRequest(double seconds) const;
+ virtual void Distribute(double delivered_energy, double seconds);
+
+ int GetSourceIndex() const { return source_index; }
+ void SetSourceIndex(int i) { source_index = i; }
+
+ virtual int Charge() const { return (int) (100 * energy/capacity); }
+
+ bool IsPowerOn() const { return power_on; }
+ virtual void PowerOn() { power_on = true; }
+ virtual void PowerOff() { power_on = false; }
+
+ // percentage, but stored as 0-1
+ virtual double GetPowerLevel() const { return power_level * 100; }
+ virtual void SetPowerLevel(double level);
+ virtual void SetOverride(bool over);
+
+ // for power drain damage:
+ virtual void DrainPower(double to_level);
+
+ void SetCapacity(double c) { capacity = (float) c; }
+ double GetCapacity() const { return capacity; }
+ double GetEnergy() const { return energy; }
+ double GetSinkRate() const { return sink_rate; }
+ void SetEMCONPower(int emcon, int power_level);
+ int GetEMCONPower(int emcon);
+
+ int GetExplosionType() const { return explosion_type; }
+ void SetExplosionType(int t) { explosion_type = t; }
+
+ Ship* GetShip() const { return ship; }
+ void SetShip(Ship* s) { ship = s; }
+ int GetID() const { return id; }
+ void SetID(int n) { id = n; }
+
+protected:
+ // AI information:
+ CATEGORY type;
+ Ship* ship;
+ int id;
+ int subtype;
+ int max_value;
+
+ // Displayable name:
+ Text name;
+ Text abrv;
+
+ // System health status:
+ STATUS status;
+ float crit_level;
+ float availability;
+ float safety;
+ float stability;
+ float safety_overload;
+ float net_avail;
+
+ // Mounting:
+ Point mount_loc; // world space
+ Point mount_rel; // object space
+ float radius;
+ float hull_factor;
+
+ // Power Sink:
+ float energy;
+ float capacity;
+ float sink_rate;
+ float power_level;
+ int source_index;
+ DWORD power_flags;
+ bool power_on;
+ BYTE emcon_power[3];
+ BYTE emcon;
+
+ int explosion_type;
+
+ // Subcomponents:
+ SystemDesign* design;
+ List<Component> components;
+};
+
+#endif System_h
+
diff --git a/Stars45/SystemDesign.cpp b/Stars45/SystemDesign.cpp
new file mode 100644
index 0000000..8b33524
--- /dev/null
+++ b/Stars45/SystemDesign.cpp
@@ -0,0 +1,168 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: SystemDesign.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Weapon Design parameters class
+*/
+
+#include "MemDebug.h"
+#include "SystemDesign.h"
+#include "Component.h"
+
+#include "Game.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "ParseUtil.h"
+
+// +--------------------------------------------------------------------+
+
+List<SystemDesign> SystemDesign::catalog;
+
+#define GET_DEF_TEXT(p,d,x) if(p->name()->value()==(#x))GetDefText(d->x,p,filename)
+#define GET_DEF_NUM(p,d,x) if(p->name()->value()==(#x))GetDefNumber(d->x,p,filename)
+
+// +--------------------------------------------------------------------+
+
+SystemDesign::SystemDesign()
+{ }
+
+SystemDesign::~SystemDesign()
+{
+ components.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SystemDesign::Initialize(const char* filename)
+{
+ Print("Loading System Designs '%s'\n", filename);
+
+ // Load Design File:
+ DataLoader* loader = DataLoader::GetLoader();
+ BYTE* block;
+
+ int blocklen = loader->LoadBuffer(filename, block, true);
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'\n", filename);
+ exit(-3);
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "SYSTEM") {
+ Print("ERROR: invalid system design file '%s'\n", filename);
+ exit(-4);
+ }
+ }
+
+ int type = 1;
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "system") {
+
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: system structure missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ SystemDesign* design = new(__FILE__,__LINE__) SystemDesign;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ GET_DEF_TEXT(pdef, design, name);
+
+ else if (pdef->name()->value()==("component")) {
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: component structure missing in system '%s' in '%s'\n", (const char*) design->name, filename);
+ }
+ else {
+ TermStruct* val2 = pdef->term()->isStruct();
+ ComponentDesign* comp_design = new(__FILE__,__LINE__) ComponentDesign;
+
+ for (int i = 0; i < val2->elements()->size(); i++) {
+ TermDef* pdef2 = val2->elements()->at(i)->isDef();
+ if (pdef2) {
+ GET_DEF_TEXT(pdef2, comp_design, name);
+ else GET_DEF_TEXT(pdef2, comp_design, abrv);
+ else GET_DEF_NUM (pdef2, comp_design, repair_time);
+ else GET_DEF_NUM (pdef2, comp_design, replace_time);
+ else GET_DEF_NUM (pdef2, comp_design, spares);
+ else GET_DEF_NUM (pdef2, comp_design, affects);
+
+ else {
+ Print("WARNING: parameter '%s' ignored in '%s'\n",
+ pdef2->name()->value().data(), filename);
+ }
+ }
+ }
+
+ design->components.append(comp_design);
+ }
+ }
+
+ else {
+ Print("WARNING: parameter '%s' ignored in '%s'\n",
+ pdef->name()->value().data(), filename);
+ }
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ val->elements()->at(i)->print();
+ }
+ }
+
+ catalog.append(design);
+ }
+ }
+
+ else
+ Print("WARNING: unknown definition '%s' in '%s'\n",
+ def->name()->value().data(), filename);
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ term->print();
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SystemDesign::Close()
+{
+ catalog.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+SystemDesign*
+SystemDesign::Find(const char* name)
+{
+ SystemDesign test;
+ test.name = name;
+ return catalog.find(&test);
+}
+
diff --git a/Stars45/SystemDesign.h b/Stars45/SystemDesign.h
new file mode 100644
index 0000000..4e7bfc2
--- /dev/null
+++ b/Stars45/SystemDesign.h
@@ -0,0 +1,54 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: SystemDesign.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Generic ship System Design class
+*/
+
+#ifndef SystemDesign_h
+#define SystemDesign_h
+
+#include "Types.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class ComponentDesign;
+
+// +--------------------------------------------------------------------+
+
+class SystemDesign
+{
+public:
+ static const char* TYPENAME() { return "SystemDesign"; }
+
+ SystemDesign();
+ ~SystemDesign();
+ int operator == (const SystemDesign& rhs) const { return name == rhs.name; }
+
+ static void Initialize(const char* filename);
+ static void Close();
+ static SystemDesign* Find(const char* name);
+
+ // Unique ID:
+ Text name;
+
+ // Sub-components:
+ List<ComponentDesign> components;
+
+ static List<SystemDesign> catalog;
+};
+
+// +--------------------------------------------------------------------+
+
+
+#endif SystemDesign_h
+
diff --git a/Stars45/TacRefDlg.cpp b/Stars45/TacRefDlg.cpp
new file mode 100644
index 0000000..a492bc2
--- /dev/null
+++ b/Stars45/TacRefDlg.cpp
@@ -0,0 +1,689 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TacRefDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Tactical Reference Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "TacRefDlg.h"
+#include "MenuScreen.h"
+#include "Campaign.h"
+#include "Mission.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "WeaponDesign.h"
+#include "WeaponGroup.h"
+
+#include "Game.h"
+#include "EventDispatch.h"
+#include "Mouse.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "ParseUtil.h"
+#include "FormatUtil.h"
+#include "Light.h"
+#include "Solid.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+DEF_MAP_CLIENT(TacRefDlg, OnClose);
+DEF_MAP_CLIENT(TacRefDlg, OnSelect);
+DEF_MAP_CLIENT(TacRefDlg, OnMode);
+DEF_MAP_CLIENT(TacRefDlg, OnCamRButtonDown);
+DEF_MAP_CLIENT(TacRefDlg, OnCamRButtonUp);
+DEF_MAP_CLIENT(TacRefDlg, OnCamMove);
+DEF_MAP_CLIENT(TacRefDlg, OnCamZoom);
+
+// +--------------------------------------------------------------------+
+
+TacRefDlg::TacRefDlg(Screen* s, FormDef& def, MenuScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ beauty(0), camview(0), imgview(0),
+ txt_caption(0), txt_stats(0), txt_description(0),
+ lst_designs(0), btn_close(0), btn_ships(0), btn_weaps(0),
+ mode(MODE_NONE), radius(100), mouse_x(0), mouse_y(0),
+ cam_zoom(2.5), cam_az(-PI/6), cam_el(PI/7), captured(false),
+ ship_index(0), weap_index(0)
+{
+ Init(def);
+}
+
+TacRefDlg::~TacRefDlg()
+{
+ if (beauty) {
+ beauty->DelView(camview);
+ beauty->DelView(imgview);
+ }
+
+ delete camview;
+ delete imgview;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacRefDlg::RegisterControls()
+{
+ btn_close = (Button*) FindControl(1);
+ btn_ships = (Button*) FindControl(101);
+ btn_weaps = (Button*) FindControl(102);
+ lst_designs = (ListBox*) FindControl(200);
+ txt_caption = FindControl(301);
+ beauty = FindControl(401);
+ txt_stats = (RichTextBox*) FindControl(402);
+ txt_description = (RichTextBox*) FindControl(403);
+
+ if (btn_close) {
+ REGISTER_CLIENT(EID_CLICK, btn_close, TacRefDlg, OnClose);
+ }
+
+ if (btn_ships) {
+ btn_ships->SetButtonState(mode == MODE_SHIPS);
+ REGISTER_CLIENT(EID_CLICK, btn_ships, TacRefDlg, OnMode);
+ }
+
+ if (btn_weaps) {
+ btn_weaps->SetButtonState(mode == MODE_WEAPONS);
+ REGISTER_CLIENT(EID_CLICK, btn_weaps, TacRefDlg, OnMode);
+ }
+
+ if (lst_designs) {
+ REGISTER_CLIENT(EID_SELECT, lst_designs, TacRefDlg, OnSelect);
+ }
+
+ if (beauty) {
+ REGISTER_CLIENT(EID_RBUTTON_DOWN, beauty, TacRefDlg, OnCamRButtonDown);
+ REGISTER_CLIENT(EID_RBUTTON_UP, beauty, TacRefDlg, OnCamRButtonUp);
+ REGISTER_CLIENT(EID_MOUSE_MOVE, beauty, TacRefDlg, OnCamMove);
+ REGISTER_CLIENT(EID_MOUSE_WHEEL, beauty, TacRefDlg, OnCamZoom);
+
+ scene.SetAmbient(Color(60,60,60));
+
+ Point light_pos(3e6, 5e6, 4e6);
+
+ Light* main_light = new Light(1.0f); //1.25f);
+ main_light->MoveTo(light_pos);
+ main_light->SetType(Light::LIGHT_DIRECTIONAL);
+ main_light->SetColor(Color::White);
+ main_light->SetShadow(true);
+
+ scene.AddLight(main_light);
+
+ Light* back_light = new Light(0.5f);
+ back_light->MoveTo(light_pos * -1);
+ back_light->SetType(Light::LIGHT_DIRECTIONAL);
+ back_light->SetColor(Color::White);
+ back_light->SetShadow(false);
+
+ scene.AddLight(back_light);
+
+ camview = new(__FILE__,__LINE__) CameraView(beauty, &cam, &scene);
+ camview->SetProjectionType(Video::PROJECTION_PERSPECTIVE);
+ camview->SetFieldOfView(2);
+
+ beauty->AddView(camview);
+
+ imgview = new(__FILE__,__LINE__) ImgView(beauty, 0);
+ imgview->SetBlend(Video::BLEND_ALPHA);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacRefDlg::Show()
+{
+ update_scene = !shown;
+
+ FormWindow::Show();
+
+ if (update_scene) {
+ AWEvent event(btn_ships, EID_CLICK);
+ OnMode(&event);
+ }
+}
+
+struct WepGroup {
+ Text name;
+ int count;
+
+ WepGroup() : count(0) { }
+};
+WepGroup* FindWepGroup(WepGroup* weapons, const char* name);
+
+
+void
+TacRefDlg::SelectShip(const ShipDesign* design)
+{
+ if (beauty && camview) {
+ scene.Graphics().clear();
+
+ if (design) {
+ radius = design->radius;
+
+ UpdateCamera();
+
+ int level = design->lod_levels-1;
+ int n = design->models[level].size();
+
+ for (int i = 0; i < n; i++) {
+ Model* model = design->models[level].at(i);
+
+ Solid* s = new(__FILE__,__LINE__) Solid;
+ s->UseModel(model);
+ s->CreateShadows(1);
+ s->MoveTo(*design->offsets[level].at(i));
+
+ scene.Graphics().append(s);
+ }
+ }
+ }
+
+ if (txt_caption) {
+ txt_caption->SetText("");
+
+ if (design) {
+ char txt[256];
+ sprintf(txt, "%s %s", design->abrv, design->DisplayName());
+ txt_caption->SetText(txt);
+ }
+ }
+
+ if (txt_stats) {
+ txt_stats->SetText("");
+
+ if (design) {
+ Text desc;
+ char txt[256];
+
+ sprintf(txt, "%s\t\t\t%s\n", Game::GetText("tacref.type").data(), Ship::ClassName(design->type));
+ desc += txt;
+
+ sprintf(txt, "%s\t\t\t%s\n", Game::GetText("tacref.class").data(), design->DisplayName());
+ desc += txt;
+ desc += Game::GetText("tacref.length");
+ desc += "\t\t";
+
+ if (design->type < Ship::STATION)
+ FormatNumber(txt, design->radius/2);
+ else
+ FormatNumber(txt, design->radius*2);
+
+ strcat(txt, " m\n");
+ desc += txt;
+ desc += Game::GetText("tacref.mass");
+ desc += "\t\t\t";
+
+ FormatNumber(txt, design->mass);
+ strcat(txt, " T\n");
+ desc += txt;
+ desc += Game::GetText("tacref.hull");
+ desc += "\t\t\t";
+
+ FormatNumber(txt, design->integrity);
+ strcat(txt, "\n");
+ desc += txt;
+
+ if (design->weapons.size()) {
+ desc += Game::GetText("tacref.weapons");
+
+ WepGroup groups[8];
+ for (int w = 0; w < design->weapons.size(); w++) {
+ Weapon* gun = design->weapons[w];
+ WepGroup* group = FindWepGroup(groups, gun->Group());
+
+ if (group)
+ group->count++;
+ }
+
+ for (int g = 0; g < 8; g++) {
+ WepGroup* group = &groups[g];
+ if (group && group->count) {
+ sprintf(txt, "\t\t%s (%d)\n\t\t", group->name.data(), group->count);
+ desc += txt;
+
+ for (int w = 0; w < design->weapons.size(); w++) {
+ Weapon* gun = design->weapons[w];
+
+ if (group->name == gun->Group()) {
+ sprintf(txt, "\t\t\t%s\n\t\t", (const char*) gun->Design()->name);
+ desc += txt;
+ }
+ }
+ }
+ }
+
+ desc += "\n";
+ }
+
+ txt_stats->SetText(desc);
+ }
+ }
+
+ if (txt_description) {
+ if (design && design->description.length()) {
+ txt_description->SetText(design->description);
+ }
+ else {
+ txt_description->SetText(Game::GetText("tacref.mass"));
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacRefDlg::SelectWeapon(const WeaponDesign* design)
+{
+ if (beauty && imgview) {
+ imgview->SetPicture(0);
+
+ if (design)
+ imgview->SetPicture(design->beauty_img);
+ }
+
+ if (txt_caption) {
+ txt_caption->SetText("");
+
+ if (design)
+ txt_caption->SetText(design->name);
+ }
+
+ if (txt_stats) {
+ txt_stats->SetText("");
+
+ if (design) {
+ Text desc;
+ char txt[256];
+
+ desc = Game::GetText("tacref.name");
+ desc += "\t";
+ desc += design->name;
+ desc += "\n";
+ desc += Game::GetText("tacref.type");
+ desc += "\t\t";
+
+ if (design->damage < 1)
+ desc += Game::GetText("tacref.wep.other");
+ else if (design->beam)
+ desc += Game::GetText("tacref.wep.beam");
+ else if (design->primary)
+ desc += Game::GetText("tacref.wep.bolt");
+ else if (design->drone)
+ desc += Game::GetText("tacref.wep.drone");
+ else if (design->guided)
+ desc += Game::GetText("tacref.wep.guided");
+ else
+ desc += Game::GetText("tacref.wep.missile");
+
+ if (design->turret_model && design->damage >= 1) {
+ desc += " ";
+ desc += Game::GetText("tacref.wep.turret");
+ desc += "\n";
+ }
+ else {
+ desc += "\n";
+ }
+
+ desc += Game::GetText("tacref.targets");
+ desc += "\t";
+
+ if ((design->target_type & Ship::DROPSHIPS) != 0) {
+ if ((design->target_type & Ship::STARSHIPS) != 0) {
+ if ((design->target_type & Ship::GROUND_UNITS) != 0) {
+ desc += Game::GetText("tacref.targets.fsg");
+ }
+ else {
+ desc += Game::GetText("tacref.targets.fs");
+ }
+ }
+ else {
+ if ((design->target_type & Ship::GROUND_UNITS) != 0) {
+ desc += Game::GetText("tacref.targets.fg");
+ }
+ else {
+ desc += Game::GetText("tacref.targets.f");
+ }
+ }
+ }
+
+ else if ((design->target_type & Ship::STARSHIPS) != 0) {
+ if ((design->target_type & Ship::GROUND_UNITS) != 0) {
+ desc += Game::GetText("tacref.targets.sg");
+ }
+ else {
+ desc += Game::GetText("tacref.targets.s");
+ }
+ }
+
+ else if ((design->target_type & Ship::GROUND_UNITS) != 0) {
+ desc += Game::GetText("tacref.targets.g");
+ }
+
+ desc += "\n";
+ desc += Game::GetText("tacref.speed");
+ desc += "\t";
+
+ FormatNumber(txt, design->speed);
+ desc += txt;
+ desc += "m/s\n";
+ desc += Game::GetText("tacref.range");
+ desc += "\t";
+
+ FormatNumber(txt, design->max_range);
+ desc += txt;
+ desc += "m\n";
+ desc += Game::GetText("tacref.damage");
+ desc += "\t";
+
+ if (design->damage > 0) {
+ FormatNumber(txt, design->damage * design->charge);
+ desc += txt;
+ if (design->beam)
+ desc += "/s";
+ }
+ else {
+ desc += Game::GetText("tacref.none");
+ }
+
+ desc += "\n";
+
+ if (!design->primary && design->damage > 0) {
+ desc += Game::GetText("tacref.kill-radius");
+ desc += "\t";
+ FormatNumber(txt, design->lethal_radius);
+ desc += txt;
+ desc += " m\n";
+ }
+
+ txt_stats->SetText(desc);
+ }
+ }
+
+ if (txt_description) {
+ if (design && design->description.length()) {
+ txt_description->SetText(design->description);
+ }
+ else {
+ txt_description->SetText(Game::GetText("tacref.no-info"));
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacRefDlg::ExecFrame()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+TacRefDlg::SetCaptureBeauty()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch && beauty)
+ return dispatch->CaptureMouse(beauty) ? true : false;
+
+ return 0;
+}
+
+bool
+TacRefDlg::ReleaseCaptureBeauty()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch && beauty)
+ return dispatch->ReleaseMouse(beauty) ? true : false;
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacRefDlg::UpdateAzimuth(double a)
+{
+ cam_az += a;
+
+ if (cam_az > PI)
+ cam_az = -2*PI + cam_az;
+
+ else if (cam_az < -PI)
+ cam_az = 2*PI + cam_az;
+}
+
+void
+TacRefDlg::UpdateElevation(double e)
+{
+ cam_el += e;
+
+ const double limit = (0.43 * PI);
+
+ if (cam_el > limit)
+ cam_el = limit;
+ else if (cam_el < -limit)
+ cam_el = -limit;
+}
+
+void
+TacRefDlg::UpdateZoom(double delta)
+{
+ cam_zoom *= delta;
+
+ if (cam_zoom < 1.2)
+ cam_zoom = 1.2;
+
+ else if (cam_zoom > 10)
+ cam_zoom = 10;
+}
+
+void
+TacRefDlg::UpdateCamera()
+{
+ double x = cam_zoom * radius * sin(cam_az) * cos(cam_el);
+ double y = cam_zoom * radius * cos(cam_az) * cos(cam_el);
+ double z = cam_zoom * radius * sin(cam_el);
+
+ cam.LookAt(Point(0,0,0), Point(x,z,y), Point(0,1,0));
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacRefDlg::OnSelect(AWEvent* event)
+{
+ if (lst_designs) {
+ int seln = lst_designs->GetSelection();
+ DWORD dsn = lst_designs->GetItemData(seln);
+
+ if (mode == MODE_SHIPS) {
+ ship_index = seln;
+
+ if (dsn) {
+ SelectShip((ShipDesign*) dsn);
+ }
+ }
+
+ else if (mode == MODE_WEAPONS) {
+ weap_index = seln;
+
+ if (dsn) {
+ SelectWeapon((WeaponDesign*) dsn);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacRefDlg::OnCamRButtonDown(AWEvent* event)
+{
+ captured = SetCaptureBeauty();
+ mouse_x = event->x;
+ mouse_y = event->y;
+}
+
+void
+TacRefDlg::OnCamRButtonUp(AWEvent* event)
+{
+ if (captured)
+ ReleaseCaptureBeauty();
+
+ captured = false;
+ mouse_x = 0;
+ mouse_y = 0;
+}
+
+void
+TacRefDlg::OnCamMove(AWEvent* event)
+{
+ if (captured) {
+ int mouse_dx = event->x - mouse_x;
+ int mouse_dy = event->y - mouse_y;
+
+ UpdateAzimuth( mouse_dx * 0.3 * DEGREES);
+ UpdateElevation( mouse_dy * 0.3 * DEGREES);
+ UpdateCamera();
+
+ mouse_x = event->x;
+ mouse_y = event->y;
+ }
+}
+
+void
+TacRefDlg::OnCamZoom(AWEvent* event)
+{
+ int w = Mouse::Wheel();
+
+ if (w < 0) {
+ while (w < 0) {
+ UpdateZoom(1.25);
+ w += 120;
+ }
+ }
+ else {
+ while (w > 0) {
+ UpdateZoom(0.75f);
+ w -= 120;
+ }
+ }
+
+ UpdateCamera();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacRefDlg::OnMode(AWEvent* event)
+{
+ if (event->window == btn_ships && mode != MODE_SHIPS) {
+ mode = MODE_SHIPS;
+
+ if (lst_designs) {
+ lst_designs->ClearItems();
+
+ List<Text> designs;
+
+ for (int n = 0; n < 16; n++) {
+ int type = 1 << n;
+ ShipDesign::GetDesignList(type, designs);
+
+ ListIter<Text> iter = designs;
+ while (++iter) {
+ Text* val = iter.value();
+
+ const ShipDesign* dsn = ShipDesign::Get(*val);
+
+ if (dsn) {
+ char txt[256];
+ sprintf(txt, "%s %s", dsn->abrv, dsn->DisplayName());
+
+ lst_designs->AddItemWithData(txt, (DWORD) dsn);
+ }
+ else {
+ lst_designs->AddItemWithData(*val, 0);
+ }
+ }
+ }
+
+ lst_designs->SetSelected(ship_index);
+ }
+
+ if (beauty) {
+ beauty->AddView(camview);
+ beauty->DelView(imgview);
+ }
+
+ DWORD dsn = lst_designs->GetItemData(ship_index);
+
+ if (dsn) {
+ SelectShip((ShipDesign*) dsn);
+ }
+ }
+
+ else if (event->window == btn_weaps && mode != MODE_WEAPONS) {
+ mode = MODE_WEAPONS;
+
+ const WeaponDesign* design = 0;
+
+ if (lst_designs) {
+ lst_designs->ClearItems();
+ List<Text> designs;
+
+ WeaponDesign::GetDesignList(designs);
+
+ ListIter<Text> iter = designs;
+ while (++iter) {
+ Text* val = iter.value();
+
+ if (val->contains("Zolon") || val->contains("Inverted"))
+ continue;
+
+ const WeaponDesign* dsn = WeaponDesign::Find(*val);
+
+ if (dsn && !dsn->secret) {
+ lst_designs->AddItemWithData(*val, (DWORD) dsn);
+
+ if (!design)
+ design = dsn;
+ }
+ }
+
+ lst_designs->SetSelected(weap_index);
+ }
+
+ if (beauty) {
+ beauty->DelView(camview);
+ beauty->AddView(imgview);
+ }
+
+ DWORD dsn = lst_designs->GetItemData(weap_index);
+
+ if (dsn) {
+ SelectWeapon((WeaponDesign*) dsn);
+ }
+ }
+
+ btn_ships->SetButtonState(mode == MODE_SHIPS);
+ btn_weaps->SetButtonState(mode == MODE_WEAPONS);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacRefDlg::OnClose(AWEvent* event)
+{
+ manager->ShowMenuDlg();
+}
+
diff --git a/Stars45/TacRefDlg.h b/Stars45/TacRefDlg.h
new file mode 100644
index 0000000..a0be975
--- /dev/null
+++ b/Stars45/TacRefDlg.h
@@ -0,0 +1,101 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TacRefDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mission Briefing Dialog Active Window class
+*/
+
+#ifndef TacRefDlg_h
+#define TacRefDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "MsnDlg.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "CameraView.h"
+#include "ImgView.h"
+#include "Scene.h"
+#include "Font.h"
+#include "Text.h"
+#include "ListBox.h"
+#include "RichTextBox.h"
+
+// +--------------------------------------------------------------------+
+
+class MenuScreen;
+class ShipDesign;
+class WeaponDesign;
+
+// +--------------------------------------------------------------------+
+
+class TacRefDlg : public FormWindow
+{
+public:
+ enum MODES { MODE_NONE, MODE_SHIPS, MODE_WEAPONS };
+
+ TacRefDlg(Screen* s, FormDef& def, MenuScreen* mgr);
+ virtual ~TacRefDlg();
+
+ virtual void RegisterControls();
+ virtual void ExecFrame();
+ virtual void Show();
+
+ // Operations:
+ virtual void OnClose(AWEvent* event);
+ virtual void OnMode(AWEvent* event);
+ virtual void OnSelect(AWEvent* event);
+ virtual void OnCamRButtonDown(AWEvent* event);
+ virtual void OnCamRButtonUp(AWEvent* event);
+ virtual void OnCamMove(AWEvent* event);
+ virtual void OnCamZoom(AWEvent* event);
+
+protected:
+ virtual void SelectShip(const ShipDesign* dsn);
+ virtual void SelectWeapon(const WeaponDesign* dsn);
+
+ virtual void UpdateZoom(double r);
+ virtual void UpdateAzimuth(double a);
+ virtual void UpdateElevation(double e);
+ virtual void UpdateCamera();
+ virtual bool SetCaptureBeauty();
+ virtual bool ReleaseCaptureBeauty();
+
+ MenuScreen* manager;
+ ActiveWindow* beauty;
+ ListBox* lst_designs;
+ ActiveWindow* txt_caption;
+ RichTextBox* txt_stats;
+ RichTextBox* txt_description;
+ Button* btn_ships;
+ Button* btn_weaps;
+ Button* btn_close;
+
+ ImgView* imgview;
+ CameraView* camview;
+ Scene scene;
+ Camera cam;
+
+ int mode;
+ double radius;
+ double cam_zoom;
+ double cam_az;
+ double cam_el;
+ int mouse_x;
+ int mouse_y;
+ bool update_scene;
+ bool captured;
+
+ int ship_index;
+ int weap_index;
+};
+
+#endif TacRefDlg_h
+
diff --git a/Stars45/TacticalAI.cpp b/Stars45/TacticalAI.cpp
new file mode 100644
index 0000000..c9217aa
--- /dev/null
+++ b/Stars45/TacticalAI.cpp
@@ -0,0 +1,958 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TacticalAI.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Generic Ship Tactical Level AI class
+*/
+
+#include "MemDebug.h"
+#include "TacticalAI.h"
+#include "ShipAI.h"
+#include "CarrierAI.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Element.h"
+#include "Instruction.h"
+#include "RadioMessage.h"
+#include "RadioTraffic.h"
+#include "Contact.h"
+#include "WeaponGroup.h"
+#include "Drive.h"
+#include "Hangar.h"
+#include "Sim.h"
+#include "Shot.h"
+#include "Drone.h"
+#include "StarSystem.h"
+
+#include "Game.h"
+#include "Random.h"
+
+// +----------------------------------------------------------------------+
+
+static int exec_time_seed = 0;
+
+// +----------------------------------------------------------------------+
+
+TacticalAI::TacticalAI(ShipAI* ai)
+ : ship(0), ship_ai(0), carrier_ai(0), navpt(0), orders(0),
+ action(0), threat_level(0), support_level(1),
+ directed_tgtid(0)
+{
+ if (ai) {
+ ship_ai = ai;
+ ship = ai->GetShip();
+
+ Sim* sim = Sim::GetSim();
+
+ if (ship && ship->GetHangar() && ship->GetCommandAILevel() > 0 &&
+ ship != sim->GetPlayerShip())
+ carrier_ai = new(__FILE__,__LINE__) CarrierAI(ship, ship_ai->GetAILevel());
+ }
+
+ agression = 0;
+ roe = FLEXIBLE;
+ element_index = 1;
+ exec_time = exec_time_seed;
+ exec_time_seed += 17;
+}
+
+TacticalAI::~TacticalAI()
+{
+ delete carrier_ai;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalAI::ExecFrame(double secs)
+{
+ const int exec_period = 1000;
+
+ if (!ship || !ship_ai)
+ return;
+
+ navpt = ship->GetNextNavPoint();
+ orders = ship->GetRadioOrders();
+
+ if ((int) Game::GameTime() - exec_time > exec_period) {
+ element_index = ship->GetElementIndex();
+
+ CheckOrders();
+ SelectTarget();
+ FindThreat();
+ FindSupport();
+
+ if (element_index > 1) {
+ int formation = 0;
+
+ if (orders && orders->Formation() >= 0)
+ formation = orders->Formation();
+
+ else if (navpt)
+ formation = navpt->Formation();
+
+ FindFormationSlot(formation);
+ }
+
+ ship_ai->SetNavPoint(navpt);
+
+ if (carrier_ai)
+ carrier_ai->ExecFrame(secs);
+
+ exec_time += exec_period;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalAI::CheckOrders()
+{
+ directed_tgtid = 0;
+
+ if (CheckShipOrders())
+ return;
+
+ if (CheckFlightPlan())
+ return;
+
+ if (CheckObjectives())
+ return;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+TacticalAI::CheckShipOrders()
+{
+ return ProcessOrders();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+TacticalAI::CheckObjectives()
+{
+ bool processed = false;
+ Ship* ward = 0;
+ Element* elem = ship->GetElement();
+
+ if (elem) {
+ Instruction* obj = elem->GetTargetObjective();
+
+ if (obj) {
+ ship_ai->ClearPatrol();
+
+ if (obj->Action()) {
+ switch (obj->Action()) {
+ case Instruction::INTERCEPT:
+ case Instruction::STRIKE:
+ case Instruction::ASSAULT:
+ {
+ SimObject* tgt = obj->GetTarget();
+ if (tgt && tgt->Type() == SimObject::SIM_SHIP) {
+ roe = DIRECTED;
+ SelectTargetDirected((Ship*) tgt);
+ }
+ }
+ break;
+
+ case Instruction::DEFEND:
+ case Instruction::ESCORT:
+ {
+ SimObject* tgt = obj->GetTarget();
+ if (tgt && tgt->Type() == SimObject::SIM_SHIP) {
+ roe = DEFENSIVE;
+ ward = (Ship*) tgt;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ orders = obj;
+ processed = true;
+ }
+ }
+
+ ship_ai->SetWard(ward);
+ return processed;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+TacticalAI::ProcessOrders()
+{
+ ship_ai->ClearPatrol();
+
+ if (orders && orders->EMCON() > 0) {
+ int desired_emcon = orders->EMCON();
+
+ if (ship_ai && (ship_ai->GetThreat() || ship_ai->GetThreatMissile()))
+ desired_emcon = 3;
+
+ if (ship->GetEMCON() != desired_emcon)
+ ship->SetEMCON(desired_emcon);
+ }
+
+ if (orders && orders->Action()) {
+ switch (orders->Action()) {
+ case RadioMessage::ATTACK:
+ case RadioMessage::BRACKET:
+ case RadioMessage::IDENTIFY:
+ {
+ bool tgt_ok = false;
+ SimObject* tgt = orders->GetTarget();
+
+ if (tgt && tgt->Type() == SimObject::SIM_SHIP) {
+ Ship* tgt_ship = (Ship*) tgt;
+
+ if (CanTarget(tgt_ship)) {
+ roe = DIRECTED;
+ SelectTargetDirected((Ship*) tgt);
+
+ ship_ai->SetBracket(orders->Action() == RadioMessage::BRACKET);
+ ship_ai->SetIdentify(orders->Action() == RadioMessage::IDENTIFY);
+ ship_ai->SetNavPoint(0);
+
+ tgt_ok = true;
+ }
+ }
+
+ if (!tgt_ok)
+ ClearRadioOrders();
+ }
+ break;
+
+ case RadioMessage::ESCORT:
+ case RadioMessage::COVER_ME:
+ {
+ SimObject* tgt = orders->GetTarget();
+ if (tgt && tgt->Type() == SimObject::SIM_SHIP) {
+ roe = DEFENSIVE;
+ ship_ai->SetWard((Ship*) tgt);
+ ship_ai->SetNavPoint(0);
+ }
+ else {
+ ClearRadioOrders();
+ }
+ }
+ break;
+
+ case RadioMessage::WEP_FREE:
+ roe = AGRESSIVE;
+ ship_ai->DropTarget(0.1);
+ break;
+
+ case RadioMessage::WEP_HOLD:
+ case RadioMessage::FORM_UP:
+ roe = NONE;
+ ship_ai->DropTarget(5);
+ break;
+
+ case RadioMessage::MOVE_PATROL:
+ roe = SELF_DEFENSIVE;
+ ship_ai->SetPatrol(orders->Location());
+ ship_ai->SetNavPoint(0);
+ ship_ai->DropTarget(Random(5, 10));
+ break;
+
+ case RadioMessage::RTB:
+ case RadioMessage::DOCK_WITH:
+ roe = NONE;
+
+ ship_ai->DropTarget(10);
+
+ if (!ship->GetInbound()) {
+ RadioMessage* msg = 0;
+ Ship* controller = ship->GetController();
+
+ if (orders->Action() == RadioMessage::DOCK_WITH && orders->GetTarget()) {
+ controller = (Ship*) orders->GetTarget();
+ }
+
+ if (!controller) {
+ Element* elem = ship->GetElement();
+ if (elem && elem->GetCommander()) {
+ Element* cmdr = elem->GetCommander();
+ controller = cmdr->GetShip(1);
+ }
+ }
+
+ if (controller && controller->GetHangar() &&
+ controller->GetHangar()->CanStow(ship)) {
+ SimRegion* self_rgn = ship->GetRegion();
+ SimRegion* rtb_rgn = controller->GetRegion();
+
+ if (self_rgn == rtb_rgn) {
+ double range = Point(controller->Location() - ship->Location()).length();
+
+ if (range < 50e3) {
+ msg = new(__FILE__,__LINE__) RadioMessage(controller, ship, RadioMessage::CALL_INBOUND);
+ RadioTraffic::Transmit(msg);
+ }
+ }
+ }
+ else {
+ ship->ClearRadioOrders();
+ }
+
+ ship_ai->SetNavPoint(0);
+ }
+ break;
+
+ case RadioMessage::QUANTUM_TO:
+ case RadioMessage::FARCAST_TO:
+ roe = NONE;
+ ship_ai->DropTarget(10);
+ break;
+
+ }
+
+ action = orders->Action();
+ return true;
+ }
+
+ // if we had an action before, this must be a "cancel orders"
+ else if (action) {
+ ClearRadioOrders();
+ }
+
+ return false;
+}
+
+void
+TacticalAI::ClearRadioOrders()
+{
+ action = 0;
+ roe = FLEXIBLE;
+
+ if (ship_ai)
+ ship_ai->DropTarget(0.1);
+
+ if (ship)
+ ship->ClearRadioOrders();
+
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+TacticalAI::CheckFlightPlan()
+{
+ Ship* ward = 0;
+
+ // Find next Instruction:
+ navpt = ship->GetNextNavPoint();
+
+ roe = FLEXIBLE;
+
+ if (navpt) {
+ switch (navpt->Action()) {
+ case Instruction::LAUNCH:
+ case Instruction::DOCK:
+ case Instruction::RTB: roe = NONE;
+ break;
+
+ case Instruction::VECTOR: roe = SELF_DEFENSIVE;
+ break;
+
+ case Instruction::DEFEND:
+ case Instruction::ESCORT: roe = DEFENSIVE;
+ break;
+
+ case Instruction::INTERCEPT:
+ roe = DIRECTED;
+ break;
+
+ case Instruction::RECON:
+ case Instruction::STRIKE:
+ case Instruction::ASSAULT: roe = DIRECTED;
+ break;
+
+ case Instruction::PATROL:
+ case Instruction::SWEEP: roe = FLEXIBLE;
+ break;
+
+ default: break;
+ }
+
+ if (roe == DEFENSIVE) {
+ SimObject* tgt = navpt->GetTarget();
+
+ if (tgt && tgt->Type() == SimObject::SIM_SHIP)
+ ward = (Ship*) tgt;
+ }
+
+
+ if (navpt->EMCON() > 0) {
+ int desired_emcon = navpt->EMCON();
+
+ if (ship_ai && (ship_ai->GetThreat() || ship_ai->GetThreatMissile()))
+ desired_emcon = 3;
+
+ if (ship->GetEMCON() != desired_emcon)
+ ship->SetEMCON(desired_emcon);
+ }
+ }
+
+ if (ship_ai)
+ ship_ai->SetWard(ward);
+
+ return (navpt != 0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalAI::SelectTarget()
+{
+ if (!ship) {
+ roe = NONE;
+ return;
+ }
+
+ // unarmed vessels should never engage an enemy:
+ if (ship->Weapons().size() < 1)
+ roe = NONE;
+
+ SimObject* target = ship_ai->GetTarget();
+ SimObject* ward = ship_ai->GetWard();
+
+ // if not allowed to engage, drop and return:
+ if (roe == NONE) {
+ if (target)
+ ship_ai->DropTarget();
+ return;
+ }
+
+ // if we have abandoned our ward, drop and return:
+ if (ward && roe != AGRESSIVE) {
+ double d = (ward->Location() - ship->Location()).length();
+ double safe_zone = 50e3;
+
+ if (target) {
+ if (ship->IsStarship())
+ safe_zone = 100e3;
+
+ if (d > safe_zone) {
+ ship_ai->DropTarget();
+ return;
+ }
+ }
+ else {
+ if (d > safe_zone) {
+ return;
+ }
+ }
+ }
+
+ // already have a target, keep it:
+ if (target) {
+ if (target->Life()) {
+ CheckTarget();
+
+ // frigates need to be ready to abandon ship-type targets
+ // in favor of drone-type targets, others should just go
+ // with what they have:
+ if (ship->Class() != Ship::CORVETTE && ship->Class() != Ship::FRIGATE)
+ return;
+
+ // in case the check decided to drop the target:
+ target = ship_ai->GetTarget();
+ }
+
+ // if the old target is dead, forget it:
+ else {
+ ship_ai->DropTarget();
+ target = 0;
+ }
+ }
+
+ // if not allowed to acquire, forget it:
+ if (ship_ai->DropTime() > 0)
+ return;
+
+ if (roe == DIRECTED) {
+ if (target && target->Type() == SimObject::SIM_SHIP)
+ SelectTargetDirected((Ship*) target);
+ else if (navpt && navpt->GetTarget() && navpt->GetTarget()->Type() == SimObject::SIM_SHIP)
+ SelectTargetDirected((Ship*) navpt->GetTarget());
+ else
+ SelectTargetDirected();
+ }
+
+ else {
+ SelectTargetOpportunity();
+
+ // don't switch one ship target for another...
+ if (ship->Class() == Ship::CORVETTE || ship->Class() == Ship::FRIGATE) {
+ SimObject* potential_target = ship_ai->GetTarget();
+ if (target && potential_target && target != potential_target) {
+ if (target->Type() == SimObject::SIM_SHIP &&
+ potential_target->Type() == SimObject::SIM_SHIP) {
+
+ ship_ai->SetTarget(target);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalAI::SelectTargetDirected(Ship* tgt)
+{
+ Ship* potential_target = tgt;
+
+ // try to target one of the element's objectives
+ // (if it shows up in the contact list)
+
+ if (!tgt) {
+ Element* elem = ship->GetElement();
+
+ if (elem) {
+ Instruction* objective = elem->GetTargetObjective();
+
+ if (objective) {
+ SimObject* obj_sim_obj = objective->GetTarget();
+ Ship* obj_tgt = 0;
+
+ if (obj_sim_obj && obj_sim_obj->Type() == SimObject::SIM_SHIP)
+ obj_tgt = (Ship*) obj_sim_obj;
+
+ if (obj_tgt) {
+ ListIter<Contact> contact = ship->ContactList();
+ while (++contact && !potential_target) {
+ Ship* test = contact->GetShip();
+
+ if (obj_tgt == test) {
+ potential_target = test;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!CanTarget(potential_target))
+ potential_target = 0;
+
+ ship_ai->SetTarget(potential_target);
+
+ if (tgt && tgt == ship_ai->GetTarget())
+ directed_tgtid = tgt->Identity();
+ else
+ directed_tgtid = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+TacticalAI::CanTarget(Ship* tgt)
+{
+ bool result = false;
+
+ if (tgt && !tgt->InTransition()) {
+ if (tgt->IsRogue() || tgt->GetIFF() != ship->GetIFF())
+ result = true;
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalAI::SelectTargetOpportunity()
+{
+ // NON-COMBATANTS do not pick targets of opportunity:
+ if (ship->GetIFF() == 0)
+ return;
+
+ SimObject* potential_target = 0;
+
+ // pick the closest combatant ship with a different IFF code:
+ double target_dist = ship->Design()->commit_range;
+
+ SimObject* ward = ship_ai->GetWard();
+
+ // FRIGATES are primarily anti-air platforms, but may
+ // also attack smaller starships:
+
+ if (ship->Class() == Ship::CORVETTE || ship->Class() == Ship::FRIGATE) {
+ Ship* current_ship_target = 0;
+ Shot* current_shot_target = 0;
+
+ // if we are escorting a larger warship, it is good to attack
+ // the same target as our ward:
+
+ if (ward) {
+ Ship* s = (Ship*) ward;
+
+ if (s->Class() > ship->Class()) {
+ SimObject* obj = s->GetTarget();
+
+ if (obj && obj->Type() == SimObject::SIM_SHIP) {
+ current_ship_target = (Ship*) obj;
+ target_dist = (ship->Location() - obj->Location()).length();
+ }
+ }
+ }
+
+ ListIter<Contact> contact = ship->ContactList();
+ while (++contact) {
+ Ship* c_ship = contact->GetShip();
+ Shot* c_shot = contact->GetShot();
+
+ if (!c_ship && !c_shot)
+ continue;
+
+ int c_iff = contact->GetIFF(ship);
+ bool rogue = c_ship && c_ship->IsRogue();
+ bool tgt_ok = c_iff > 0 &&
+ c_iff != ship->GetIFF() &&
+ c_iff < 1000;
+
+ if (rogue || tgt_ok) {
+ if (c_ship && c_ship != ship && !c_ship->InTransition()) {
+ if (c_ship->Class() < Ship::DESTROYER ||
+ (c_ship->Class() >= Ship::MINE && c_ship->Class() <= Ship::SWACS)) {
+ // found an enemy, check distance:
+ double dist = (ship->Location() - c_ship->Location()).length();
+
+ if (dist < 0.75 * target_dist &&
+ (!current_ship_target || c_ship->Class() <= current_ship_target->Class())) {
+ current_ship_target = c_ship;
+ target_dist = dist;
+ }
+ }
+ }
+
+ else if (c_shot) {
+ // found an enemy shot, is there enough time to engage?
+ if (c_shot->GetEta() < 3)
+ continue;
+
+ // found an enemy shot, check distance:
+ double dist = (ship->Location() - c_shot->Location()).length();
+
+ if (!current_shot_target) {
+ current_shot_target = c_shot;
+ target_dist = dist;
+ }
+
+ // is this shot a better target than the one we've found?
+ else {
+ Ship* ward = ship_ai->GetWard();
+
+ if ((c_shot->IsTracking(ward) || c_shot->IsTracking(ship)) &&
+ (!current_shot_target->IsTracking(ward) ||
+ !current_shot_target->IsTracking(ship))) {
+ current_shot_target = c_shot;
+ target_dist = dist;
+ }
+ else if (dist < target_dist) {
+ current_shot_target = c_shot;
+ target_dist = dist;
+ }
+ }
+ }
+ }
+ }
+
+ if (current_shot_target)
+ potential_target = current_shot_target;
+ else
+ potential_target = current_ship_target;
+ }
+
+ // ALL OTHER SHIP CLASSES ignore fighters and only engage
+ // other starships:
+
+ else {
+ List<Ship> ward_threats;
+
+ ListIter<Contact> contact = ship->ContactList();
+ while (++contact) {
+ Ship* c_ship = contact->GetShip();
+
+ if (!c_ship)
+ continue;
+
+ int c_iff = contact->GetIFF(ship);
+ bool rogue = c_ship->IsRogue();
+ bool tgt_ok = c_ship != ship &&
+ c_iff > 0 &&
+ c_iff != ship->GetIFF() &&
+ !c_ship->InTransition();
+
+ if (rogue || tgt_ok) {
+ if (c_ship->IsStarship() || c_ship->IsStatic()) {
+ // found an enemy, check distance:
+ double dist = (ship->Location() - c_ship->Location()).length();
+
+ if (dist < 0.75 * target_dist) {
+ potential_target = c_ship;
+ target_dist = dist;
+ }
+
+ if (ward && c_ship->IsTracking(ward)) {
+ ward_threats.append(c_ship);
+ }
+ }
+ }
+ }
+
+ // if this ship is protecting a ward,
+ // prefer targets that are threatening that ward:
+ if (potential_target && ward_threats.size() && !ward_threats.contains((Ship*)potential_target)) {
+ target_dist *= 2;
+
+ ListIter<Ship> iter = ward_threats;
+ while (++iter) {
+ Ship* threat = iter.value();
+
+ double dist = (ward->Location() - threat->Location()).length();
+
+ if (dist < target_dist) {
+ potential_target = threat;
+ target_dist = dist;
+ }
+ }
+ }
+ }
+
+ if (ship->Class() != Ship::CARRIER)
+ ship_ai->SetTarget(potential_target);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalAI::CheckTarget()
+{
+ SimObject* tgt = ship_ai->GetTarget();
+
+ if (!tgt) return;
+
+ if (tgt->GetRegion() != ship->GetRegion()) {
+ ship_ai->DropTarget();
+ return;
+ }
+
+ if (tgt->Type() == SimObject::SIM_SHIP) {
+ Ship* target = (Ship*) tgt;
+
+ // has the target joined our side?
+ if (target->GetIFF() == ship->GetIFF() && !target->IsRogue()) {
+ ship_ai->DropTarget();
+ return;
+ }
+
+ // is the target already jumping/breaking/dying?
+ if (target->InTransition()) {
+ ship_ai->DropTarget();
+ return;
+ }
+
+ // have we been ordered to pursue the target?
+ if (directed_tgtid) {
+ if (directed_tgtid != target->Identity()) {
+ ship_ai->DropTarget();
+ }
+
+ return;
+ }
+
+ // can we catch the target?
+ if (target->Design()->vlimit <= ship->Design()->vlimit ||
+ ship->Velocity().length() <= ship->Design()->vlimit)
+ return;
+
+ // is the target now out of range?
+ WeaponDesign* wep_dsn = ship->GetPrimaryDesign();
+ if (!wep_dsn)
+ return;
+
+ // compute the "give up" range:
+ double drop_range = 3 * wep_dsn->max_range;
+ if (drop_range > 0.75 * ship->Design()->commit_range)
+ drop_range = 0.75 * ship->Design()->commit_range;
+
+ double range = Point(target->Location() - ship->Location()).length();
+ if (range < drop_range)
+ return;
+
+ // is the target closing or separating?
+ Point delta = (target->Location() + target->Velocity()) -
+ (ship->Location() + ship->Velocity());
+
+ if (delta.length() < range)
+ return;
+
+ ship_ai->DropTarget();
+ }
+
+ else if (tgt->Type() == SimObject::SIM_DRONE) {
+ Drone* drone = (Drone*) tgt;
+
+ // is the target still a threat?
+ if (drone->GetEta() < 1 || drone->GetTarget() == 0)
+ ship_ai->DropTarget();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalAI::FindThreat()
+{
+ // pick the closest contact on Threat Warning System:
+ Ship* threat = 0;
+ Shot* threat_missile = 0;
+ Ship* rumor = 0;
+ double threat_dist = 1e9;
+ const DWORD THREAT_REACTION_TIME = 1000; // 1 second
+
+ ListIter<Contact> iter = ship->ContactList();
+
+ while (++iter) {
+ Contact* contact = iter.value();
+
+ if (contact->Threat(ship) &&
+ (Game::GameTime() - contact->AcquisitionTime()) > THREAT_REACTION_TIME) {
+
+ if (contact->GetShot()) {
+ threat_missile = contact->GetShot();
+ rumor = (Ship*) threat_missile->Owner();
+ }
+ else {
+ double rng = contact->Range(ship);
+
+ Ship* c_ship = contact->GetShip();
+ if (c_ship && !c_ship->InTransition() &&
+ c_ship->Class() != Ship::FREIGHTER &&
+ c_ship->Class() != Ship::FARCASTER) {
+
+ if (c_ship->GetTarget() == ship) {
+ if (!threat || c_ship->Class() > threat->Class()) {
+ threat = c_ship;
+ threat_dist = 0;
+ }
+ }
+ else if (rng < threat_dist) {
+ threat = c_ship;
+ threat_dist = rng;
+ }
+ }
+ }
+ }
+ }
+
+ if (rumor && !rumor->InTransition()) {
+ iter.reset();
+
+ while (++iter) {
+ if (iter->GetShip() == rumor) {
+ rumor = 0;
+ ship_ai->ClearRumor();
+ break;
+ }
+ }
+ }
+ else {
+ rumor = 0;
+ ship_ai->ClearRumor();
+ }
+
+ ship_ai->SetRumor(rumor);
+ ship_ai->SetThreat(threat);
+ ship_ai->SetThreatMissile(threat_missile);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalAI::FindSupport()
+{
+ if (!ship_ai->GetThreat()) {
+ ship_ai->SetSupport(0);
+ return;
+ }
+
+ // pick the biggest friendly contact in the sector:
+ Ship* support = 0;
+ double support_dist = 1e9;
+
+ ListIter<Contact> contact = ship->ContactList();
+
+ while (++contact) {
+ if (contact->GetShip() && contact->GetIFF(ship) == ship->GetIFF()) {
+ Ship* c_ship = contact->GetShip();
+
+ if (c_ship != ship && c_ship->Class() >= ship->Class() && !c_ship->InTransition()) {
+ if (!support || c_ship->Class() > support->Class())
+ support = c_ship;
+ }
+ }
+ }
+
+ ship_ai->SetSupport(support);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalAI::FindFormationSlot(int formation)
+{
+ // find the formation delta:
+ int s = element_index - 1;
+ Point delta(10*s, 0, 10*s);
+
+ // diamond:
+ if (formation == Instruction::DIAMOND) {
+ switch (element_index) {
+ case 2: delta = Point( 10, 0, -12); break;
+ case 3: delta = Point(-10, 0, -12); break;
+ case 4: delta = Point( 0, 0, -24); break;
+ }
+ }
+
+ // spread:
+ if (formation == Instruction::SPREAD) {
+ switch (element_index) {
+ case 2: delta = Point( 15, 0, 0); break;
+ case 3: delta = Point(-15, 0, 0); break;
+ case 4: delta = Point(-30, 0, 0); break;
+ }
+ }
+
+ // box:
+ if (formation == Instruction::BOX) {
+ switch (element_index) {
+ case 2: delta = Point(15, 0, 0); break;
+ case 3: delta = Point( 0, -1, -15); break;
+ case 4: delta = Point(15, -1, -15); break;
+ }
+ }
+
+ // trail:
+ if (formation == Instruction::TRAIL) {
+ delta = Point(0, 0, -15*s);
+ }
+
+ ship_ai->SetFormationDelta(delta * ship->Radius() * 2);
+}
diff --git a/Stars45/TacticalAI.h b/Stars45/TacticalAI.h
new file mode 100644
index 0000000..3a56136
--- /dev/null
+++ b/Stars45/TacticalAI.h
@@ -0,0 +1,91 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TacticalAI.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Common base class and interface for mid-level (tactical) AI
+*/
+
+#ifndef TacticalAI_h
+#define TacticalAI_h
+
+#include "Types.h"
+#include "Director.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+class ShipAI;
+class Instruction;
+class CarrierAI;
+
+// +--------------------------------------------------------------------+
+
+class TacticalAI : public Director
+{
+public:
+ TacticalAI(ShipAI* ai);
+ virtual ~TacticalAI();
+
+ enum ROE {
+ NONE,
+ SELF_DEFENSIVE,
+ DEFENSIVE,
+ DIRECTED,
+ FLEXIBLE,
+ AGRESSIVE
+ };
+
+ virtual void ExecFrame(double seconds);
+
+ virtual ROE RulesOfEngagement() const { return roe; }
+ virtual double ThreatLevel() const { return threat_level; }
+ virtual double SupportLevel() const { return support_level; }
+
+protected:
+ // pick the best target if we don't have one yet:
+ virtual void CheckOrders();
+ virtual bool CheckShipOrders();
+ virtual bool ProcessOrders();
+ virtual bool CheckFlightPlan();
+ virtual bool CheckObjectives();
+
+ virtual void SelectTarget();
+ virtual void SelectTargetDirected(Ship* tgt=0);
+ virtual void SelectTargetOpportunity();
+ virtual void CheckTarget();
+ virtual void FindThreat();
+ virtual void FindSupport();
+ virtual void FindFormationSlot(int formation);
+
+ virtual bool CanTarget(Ship* tgt);
+ virtual void ClearRadioOrders();
+
+ Ship* ship;
+ ShipAI* ship_ai;
+ CarrierAI* carrier_ai;
+
+ Instruction* navpt;
+ Instruction* orders;
+
+ double agression;
+ ROE roe;
+ int element_index;
+ int action;
+ int exec_time;
+ int directed_tgtid;
+
+ double threat_level;
+ double support_level;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif TacticalAI_h
+
diff --git a/Stars45/TacticalView.cpp b/Stars45/TacticalView.cpp
new file mode 100644
index 0000000..e27c8f1
--- /dev/null
+++ b/Stars45/TacticalView.cpp
@@ -0,0 +1,1495 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TacticalView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ View class for Tactical Data Readout HUD Overlay
+*/
+
+#include "MemDebug.h"
+#include "TacticalView.h"
+#include "QuantumView.h"
+#include "RadioView.h"
+#include "RadioMessage.h"
+#include "RadioTraffic.h"
+#include "HUDSounds.h"
+#include "HUDView.h"
+#include "WepView.h"
+#include "CameraDirector.h"
+#include "Ship.h"
+#include "ShipCtrl.h"
+#include "ShipDesign.h"
+#include "QuantumDrive.h"
+#include "Farcaster.h"
+#include "Instruction.h"
+#include "Element.h"
+#include "Contact.h"
+#include "Sim.h"
+#include "Starshatter.h"
+#include "GameScreen.h"
+#include "MenuView.h"
+
+#include "Projector.h"
+#include "Color.h"
+#include "Window.h"
+#include "Video.h"
+#include "DataLoader.h"
+#include "Scene.h"
+#include "FontMgr.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "MouseController.h"
+#include "Menu.h"
+#include "Game.h"
+#include "FormatUtil.h"
+
+static Color hud_color = Color::Black;
+static Color txt_color = Color::Black;
+
+// +--------------------------------------------------------------------+
+
+TacticalView* TacticalView::tac_view = 0;
+
+// +--------------------------------------------------------------------+
+
+TacticalView::TacticalView(Window* c, GameScreen* parent)
+ : View(c), gamescreen(parent), ship(0), camview(0), projector(0),
+ mouse_down(0), right_down(0), shift_down(0),
+ show_move(0), show_action(0), active_menu(0), menu_view(0),
+ msg_ship(0), base_alt(0), move_alt(0)
+{
+ tac_view = this;
+ sim = Sim::GetSim();
+
+ width = window->Width();
+ height = window->Height();
+ xcenter = (width / 2.0) - 0.5;
+ ycenter = (height / 2.0) + 0.5;
+ font = FontMgr::Find("HUD");
+
+ SetColor(Color::White);
+
+ mouse_start.x = 0;
+ mouse_start.y = 0;
+ mouse_action.x = 0;
+ mouse_action.y = 0;
+
+ menu_view = new(__FILE__,__LINE__) MenuView(window);
+}
+
+TacticalView::~TacticalView()
+{
+ delete menu_view;
+ tac_view = 0;
+}
+
+void
+TacticalView::OnWindowMove()
+{
+ width = window->Width();
+ height = window->Height();
+ xcenter = (width / 2.0) - 0.5;
+ ycenter = (height / 2.0) + 0.5;
+
+ if (menu_view)
+ menu_view->OnWindowMove();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+TacticalView::Update(SimObject* obj)
+{
+ if (obj == ship) {
+ ship = 0;
+ }
+
+ if (obj == msg_ship) {
+ msg_ship = 0;
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+TacticalView::GetObserverName() const
+{
+ return "TacticalView";
+}
+
+void
+TacticalView::UseProjector(Projector* p)
+{
+ projector = p;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalView::Refresh()
+{
+ sim = Sim::GetSim();
+
+ if (sim) {
+ bool rebuild = false;
+
+ if (ship != sim->GetPlayerShip()) {
+ ship = sim->GetPlayerShip();
+
+ if (ship) {
+ if (ship->Life() == 0 || ship->IsDying() || ship->IsDead()) {
+ ship = 0;
+ }
+ else {
+ Observe(ship);
+ }
+ }
+
+ rebuild = true;
+ }
+
+ if (ship) {
+ if (current_sector != ship->GetRegion()->Name())
+ rebuild = true;
+
+ if (rebuild) {
+ BuildMenu();
+ current_sector = ship->GetRegion()->Name();
+ }
+ }
+ }
+
+ if (!ship || ship->InTransition())
+ return;
+
+ DrawMouseRect();
+
+ if (sim) {
+ ListIter<Ship> sel = sim->GetSelection();
+
+ if (sel.size()) {
+ while (++sel) {
+ Ship* selection = sel.value();
+
+ // draw selection rect on selected ship:
+ if (selection && selection->Rep())
+ DrawSelection(selection);
+ }
+
+ RadioView* rv = RadioView::GetInstance();
+ QuantumView* qv = QuantumView::GetInstance();
+
+ if ((!rv || !rv->IsMenuShown()) && (!qv || !qv->IsMenuShown())) {
+ sel.reset();
+
+ if (sel.size() == 1) {
+ DrawSelectionInfo(sel.next());
+ }
+ else {
+ DrawSelectionList(sel);
+ }
+ }
+ }
+ }
+
+ DrawMenu();
+
+ if (show_move) {
+ Mouse::Show(false);
+ DrawMove();
+ }
+ else if (show_action) {
+ Mouse::Show(false);
+ DrawAction();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalView::ExecFrame()
+{
+ HUDView* hud = HUDView::GetInstance();
+ if (hud) {
+ if (hud_color != hud->GetTextColor()) {
+ hud_color = hud->GetTextColor();
+ SetColor(hud_color);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalView::SetColor(Color c)
+{
+ HUDView* hud = HUDView::GetInstance();
+
+ if (hud) {
+ hud_color = hud->GetHUDColor();
+ txt_color = hud->GetTextColor();
+ }
+ else {
+ hud_color = c;
+ txt_color = c;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalView::DrawMouseRect()
+{
+ if (mouse_rect.w > 0 && mouse_rect.h > 0) {
+ Color c = hud_color * 0.66;
+
+ if (shift_down)
+ c = Color::Orange;
+
+ window->DrawRect(mouse_rect, c);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalView::DrawSelection(Ship* seln)
+{
+ Graphic* g = seln->Rep();
+ Rect r = g->ScreenRect();
+
+ Point mark_pt;
+
+ if (seln)
+ mark_pt = seln->Location();
+
+ projector->Transform(mark_pt);
+
+ // clip:
+ if (mark_pt.z > 1.0) {
+ projector->Project(mark_pt);
+
+ int x = (int) mark_pt.x;
+ int y = r.y;
+
+ if (y >= 2000)
+ y = (int) mark_pt.y;
+
+ if (x > 4 && x < width-4 &&
+ y > 4 && y < height-4) {
+
+ const int BAR_LENGTH = 40;
+
+ // life bars:
+ int sx = x - BAR_LENGTH/2;
+ int sy = y - 8;
+
+ double hull_strength = seln->HullStrength() / 100.0;
+
+ int hw = (int) (BAR_LENGTH * hull_strength);
+ int sw = (int) (BAR_LENGTH * (seln->ShieldStrength() / 100.0));
+
+ if (hw < 0) hw = 0;
+ if (sw < 0) sw = 0;
+
+ System::STATUS s = System::NOMINAL;
+
+ if (hull_strength < 0.30) s = System::CRITICAL;
+ else if (hull_strength < 0.60) s = System::DEGRADED;
+
+ Color hc = HUDView::GetStatusColor(s);
+ Color sc = hud_color;
+
+ window->FillRect(sx, sy, sx+hw, sy+1, hc);
+ window->FillRect(sx, sy+3, sx+sw, sy+4, sc);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalView::DrawSelectionInfo(Ship* seln)
+{
+ if (!ship || !seln) return;
+
+ Rect label_rect(width-140, 10, 90, 12);
+ Rect info_rect(width-100, 10, 90, 12);
+
+ if (width >= 800) {
+ label_rect.x -= 20;
+ info_rect.x -= 20;
+ info_rect.w += 20;
+ }
+
+ static char name[64];
+ static char design[64];
+ static char shield[32];
+ static char hull[32];
+ static char range[32];
+ static char heading[32];
+ static char speed[32];
+ static char orders[64];
+ static char psv[32];
+ static char act[32];
+
+ int show_labels = width > 640;
+ int full_info = true;
+ int shield_val = seln->ShieldStrength();
+ int hull_val = seln->HullStrength();
+
+ if (shield_val < 0) shield_val = 0;
+ if (hull_val < 0) hull_val = 0;
+
+ sprintf(name, "%s", seln->Name());
+
+ if (show_labels) {
+ sprintf(shield, "%s %03d", Game::GetText("HUDView.symbol.shield").data(), shield_val);
+ sprintf(hull, "%s %03d", Game::GetText("HUDView.symbol.hull").data(), hull_val);
+ }
+ else {
+ sprintf(shield, "%03d", shield_val);
+ sprintf(hull, "%03d", hull_val);
+ }
+
+ FormatNumberExp(range, Point(seln->Location()-ship->Location()).length()/1000);
+ strcat(range, " km");
+ sprintf(heading, "%03d %s", (int) (seln->CompassHeading() / DEGREES), Game::GetText("HUDView.symbol.degrees").data());
+
+ double ss = seln->Velocity().length();
+ if (seln->Velocity() * seln->Heading() < 0)
+ ss = -ss;
+
+ FormatNumberExp(speed, ss);
+ strcat(speed, " m/s");
+
+ Contact* contact = 0;
+
+ // always recognize ownside:
+ if (seln->GetIFF() != ship->GetIFF()) {
+ ListIter<Contact> c = ship->ContactList();
+ while (++c) {
+ if (c->GetShip() == seln) {
+ contact = c.value();
+ if (c->GetIFF(ship) > seln->GetIFF()) {
+ sprintf(name, "%s %04d", Game::GetText("TacView.contact").data(), seln->GetContactID());
+ full_info = false;
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (show_labels) {
+ font->SetColor(txt_color);
+ font->SetAlpha(1);
+
+ font->DrawText(Game::GetText("TacView.name"), 5, label_rect, DT_LEFT);
+ label_rect.y += 10;
+ font->DrawText(Game::GetText("TacView.type"), 5, label_rect, DT_LEFT);
+ label_rect.y += 10;
+
+ if (full_info) {
+ font->DrawText(Game::GetText("TacView.shield"), 5, label_rect, DT_LEFT);
+ label_rect.y += 10;
+ font->DrawText(Game::GetText("TacView.hull"), 5, label_rect, DT_LEFT);
+ label_rect.y += 10;
+ }
+
+ font->DrawText(Game::GetText("TacView.range"), 4, label_rect, DT_LEFT);
+ label_rect.y += 10;
+
+ if (full_info) {
+ font->DrawText(Game::GetText("TacView.speed"), 4, label_rect, DT_LEFT);
+ label_rect.y += 10;
+ font->DrawText(Game::GetText("TacView.heading"), 4, label_rect, DT_LEFT);
+ label_rect.y += 10;
+ }
+ else {
+ font->DrawText(Game::GetText("TacView.passive"), 4, label_rect, DT_LEFT);
+ label_rect.y += 10;
+ font->DrawText(Game::GetText("TacView.active"), 4, label_rect, DT_LEFT);
+ label_rect.y += 10;
+ }
+ }
+
+ font->DrawText(name, 0, info_rect, DT_LEFT);
+ info_rect.y += 10;
+
+ if (full_info) {
+ sprintf(design, "%s %s", seln->Abbreviation(), seln->Design()->display_name);
+ font->DrawText(design, 0, info_rect, DT_LEFT);
+ info_rect.y += 10;
+ }
+ else {
+ if (seln->IsStarship())
+ font->DrawText(Game::GetText("TacView.starship"), 8, info_rect, DT_LEFT);
+ else
+ font->DrawText(Game::GetText("TacView.fighter"), 7, info_rect, DT_LEFT);
+
+ info_rect.y += 10;
+ }
+
+ if (full_info) {
+ font->DrawText(shield, 0, info_rect, DT_LEFT);
+ info_rect.y += 10;
+
+ font->DrawText(hull, 0, info_rect, DT_LEFT);
+ info_rect.y += 10;
+ }
+
+ font->DrawText(range, 0, info_rect, DT_LEFT);
+ info_rect.y += 10;
+
+ if (full_info) {
+ font->DrawText(speed, 0, info_rect, DT_LEFT);
+ info_rect.y += 10;
+
+ font->DrawText(heading, 0, info_rect, DT_LEFT);
+ info_rect.y += 10;
+
+ if (seln->GetIFF() == ship->GetIFF()) {
+ Instruction* instr = seln->GetRadioOrders();
+ if (instr && instr->Action()) {
+ strcpy(orders, RadioMessage::ActionName(instr->Action()));
+
+ if (instr->Action() == RadioMessage::QUANTUM_TO) {
+ strcat(orders, " ");
+ strcat(orders, instr->RegionName());
+ }
+ }
+ else {
+ *orders = 0;
+ }
+
+ if (*orders) {
+ if (show_labels) {
+ font->DrawText(Game::GetText("TacView.orders"), 5, label_rect, DT_LEFT);
+ label_rect.y += 10;
+ }
+
+ font->DrawText(orders, 0, info_rect, DT_LEFT);
+ info_rect.y += 10;
+ }
+ }
+ }
+ else {
+ sprintf(psv, "%03d", (int) (contact->PasReturn() * 100.0));
+ sprintf(act, "%03d", (int) (contact->ActReturn() * 100.0));
+
+ if (contact->Threat(ship))
+ strcat(psv, " !");
+
+ font->DrawText(psv, 0, info_rect, DT_LEFT);
+ info_rect.y += 10;
+ font->DrawText(act, 0, info_rect, DT_LEFT);
+ info_rect.y += 10;
+ }
+
+ /*** XXX DEBUG
+ font->DrawText(seln->GetDirectorInfo(), 0, info_rect, DT_LEFT);
+ info_rect.y += 10;
+ /***/
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalView::DrawSelectionList(ListIter<Ship> seln)
+{
+ int index = 0;
+ Rect info_rect(width-100, 10, 90, 12);
+
+ while (++seln) {
+ char name[64];
+ sprintf(name, "%s", seln->Name());
+
+ // always recognize ownside:
+ if (seln->GetIFF() != ship->GetIFF()) {
+ ListIter<Contact> c = ship->ContactList();
+ while (++c) {
+ if (c->GetShip() == seln.value()) {
+ if (c->GetIFF(ship) > seln->GetIFF()) {
+ sprintf(name, "%s %04d", Game::GetText("TacView.contact").data(), seln->GetContactID());
+ }
+
+ break;
+ }
+ }
+ }
+
+ font->DrawText(name, 0, info_rect, DT_LEFT);
+ info_rect.y += 10;
+ index++;
+
+ if (index >= 10)
+ break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalView::DoMouseFrame()
+{
+ static DWORD rbutton_latch = 0;
+
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars->InCutscene())
+ return;
+
+ if (Mouse::RButton()) {
+ MouseController* mouse_con = MouseController::GetInstance();
+ if (!right_down && (!mouse_con || !mouse_con->Active())) {
+ rbutton_latch = Game::RealTime();
+ right_down = true;
+ }
+ }
+ else {
+ if (sim && right_down && (Game::RealTime() - rbutton_latch < 250)) {
+ Ship* seln = WillSelectAt(Mouse::X(), Mouse::Y());
+
+ if (seln && sim->IsSelected(seln) &&
+ seln->GetIFF() == ship->GetIFF() &&
+ ship->GetElement()->CanCommand(seln->GetElement())) {
+
+ msg_ship = seln;
+ Observe(msg_ship);
+ }
+
+ else if (ship && seln == ship &&
+ (!ship->GetDirector() ||
+ ship->GetDirector()->Type() != ShipCtrl::DIR_TYPE)) {
+
+ msg_ship = seln;
+ }
+
+ else {
+ msg_ship = 0;
+ }
+ }
+
+ right_down = false;
+ }
+
+ if (menu_view)
+ menu_view->DoMouseFrame();
+
+ MouseController* mouse_con = MouseController::GetInstance();
+
+ if (!mouse_con || !mouse_con->Active()) {
+ if (Mouse::LButton()) {
+ if (!mouse_down) {
+ mouse_start.x = Mouse::X();
+ mouse_start.y = Mouse::Y();
+
+ shift_down = Keyboard::KeyDown(VK_SHIFT);
+ }
+
+ else {
+ if (Mouse::X() < mouse_start.x) {
+ mouse_rect.x = Mouse::X();
+ mouse_rect.w = mouse_start.x - Mouse::X();
+ }
+ else {
+ mouse_rect.x = mouse_start.x;
+ mouse_rect.w = Mouse::X() - mouse_start.x;
+ }
+
+ if (Mouse::Y() < mouse_start.y) {
+ mouse_rect.y = Mouse::Y();
+ mouse_rect.h = mouse_start.y - Mouse::Y();
+ }
+ else {
+ mouse_rect.y = mouse_start.y;
+ mouse_rect.h = Mouse::Y() - mouse_start.y;
+ }
+
+ // don't draw seln rectangle while zooming:
+ if (Mouse::RButton() || show_move || show_action) {
+ mouse_rect.w = 0;
+ mouse_rect.h = 0;
+ }
+
+ else {
+ SelectRect(mouse_rect);
+ }
+ }
+
+ mouse_down = true;
+ }
+
+ else {
+ if (mouse_down) {
+ int mouse_x = Mouse::X();
+ int mouse_y = Mouse::Y();
+
+ if (menu_view && menu_view->GetAction()) {
+ ProcessMenuItem(menu_view->GetAction());
+ Mouse::Show(true);
+ }
+ else if (show_move) {
+ SendMove();
+ show_move = false;
+ Mouse::Show(true);
+ }
+ else if (show_action) {
+ SendAction();
+ show_action = false;
+ Mouse::Show(true);
+ }
+ else {
+ if (!HUDView::IsMouseLatched() && !WepView::IsMouseLatched()) {
+ int dx = (int) fabs((double) (mouse_x - mouse_start.x));
+ int dy = (int) fabs((double) (mouse_y - mouse_start.y));
+
+ static DWORD click_time = 0;
+
+ if (dx < 3 && dy < 3) {
+ bool hit = SelectAt(mouse_x, mouse_y);
+
+ if (ship->IsStarship() && Game::RealTime() - click_time < 350)
+ SetHelm(hit);
+
+ click_time = Game::RealTime();
+ }
+ }
+ }
+
+ mouse_rect = Rect();
+ mouse_down = false;
+ }
+ }
+ }
+
+ if (show_action && !mouse_down && !right_down) {
+ mouse_action.x = Mouse::X();
+ mouse_action.y = Mouse::Y();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+TacticalView::SelectAt(int x, int y)
+{
+ if (!ship) return false;
+
+ Ship* selection = WillSelectAt(x,y);
+
+ if (selection && shift_down)
+ ship->SetTarget(selection);
+
+ else if (sim && selection)
+ sim->SetSelection(selection);
+
+ return selection != 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+TacticalView::SelectRect(const Rect& rect)
+{
+ bool result = false;
+
+ if (!ship || !sim) return result;
+
+ if (rect.w > 8 || rect.h > 8)
+ sim->ClearSelection();
+
+ // check distance to each contact:
+ List<Contact>& contact_list = ship->ContactList();
+
+ for (int i = 0; i < ship->NumContacts(); i++) {
+ Ship* test = contact_list[i]->GetShip();
+
+ if (test && test != ship) {
+
+ Point test_loc = test->Location();
+ projector->Transform(test_loc);
+
+ if (test_loc.z > 1) {
+ projector->Project(test_loc);
+
+ if (rect.Contains((int) test_loc.x, (int) test_loc.y)) {
+ // shift-select targets:
+ if (shift_down) {
+ if (test->GetIFF() == 0 || test->GetIFF() == ship->GetIFF())
+ continue;
+
+ ship->SetTarget(test);
+ result = true;
+ }
+ else {
+ sim->AddSelection(test);
+ result = true;
+ }
+ }
+ }
+ }
+ }
+
+ // select self only in orbit cam
+ if (!shift_down && CameraDirector::GetCameraMode() == CameraDirector::MODE_ORBIT) {
+ Point test_loc = ship->Location();
+ projector->Transform(test_loc);
+
+ if (test_loc.z > 1) {
+ projector->Project(test_loc);
+
+ if (rect.Contains((int) test_loc.x, (int) test_loc.y)) {
+ sim->AddSelection(ship);
+ result = true;
+ }
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+Ship*
+TacticalView::WillSelectAt(int x, int y)
+{
+ Ship* selection = 0;
+
+ if (ship) {
+ // check distance to each contact:
+ List<Contact>& contact_list = ship->ContactList();
+
+ for (int i = 0; i < ship->NumContacts(); i++) {
+ Ship* test = contact_list[i]->GetShip();
+
+ if (test) {
+ // shift-select targets:
+ if (shift_down) {
+ if (test->GetIFF() == 0 || test->GetIFF() == ship->GetIFF())
+ continue;
+ }
+
+ Graphic* g = test->Rep();
+ if (g) {
+ Rect r = g->ScreenRect();
+
+ if (r.x == 2000 && r.y == 2000 && r.w == 0 && r.h == 0) {
+ if (projector) {
+ Point loc = test->Location();
+ projector->Transform(loc);
+ projector->Project(loc);
+
+ r.x = (int) loc.x;
+ r.y = (int) loc.y;
+ }
+ }
+
+ if (r.w < 20 || r.h < 20)
+ r.Inflate(20,20);
+ else
+ r.Inflate(10,10);
+
+ if (r.Contains(x,y)) {
+ selection = test;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!selection && !shift_down) {
+ Graphic* g = ship->Rep();
+ if (g) {
+ Rect r = g->ScreenRect();
+
+ if (r.Contains(x,y)) {
+ selection = ship;
+ }
+ }
+ }
+ }
+
+ if (selection == ship && CameraDirector::GetCameraMode() != CameraDirector::MODE_ORBIT)
+ selection = 0;
+
+ return selection;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalView::SetHelm(bool approach)
+{
+ Point delta;
+
+ // double-click on ship: set helm to approach
+ if (sim && approach) {
+ ListIter<Ship> iter = sim->GetSelection();
+ ++iter;
+ Ship* selection = iter.value();
+
+ if (selection != ship) {
+ delta = selection->Location() - ship->Location();
+ delta.Normalize();
+ }
+ }
+
+ // double-click on space: set helm in direction
+ if (delta.length() < 1) {
+ int mx = Mouse::X();
+ int my = Mouse::Y();
+
+ if (projector) {
+ double focal_dist = width / tan(projector->XAngle());
+
+ delta = projector->vpn() * focal_dist +
+ projector->vup() * -1 * (my-height/2) +
+ projector->vrt() * (mx-width/2);
+
+ delta.Normalize();
+ }
+
+ else {
+ return;
+ }
+ }
+
+ double az = atan2(fabs(delta.x), delta.z);
+ double el = asin(delta.y);
+
+ if (delta.x < 0)
+ az *= -1;
+
+ az += PI;
+
+ if (az >= 2*PI)
+ az -= 2*PI;
+
+ ship->SetHelmHeading(az);
+ ship->SetHelmPitch(el);
+}
+
+// +====================================================================+
+//
+// TACTICAL COMMUNICATIONS MENU:
+//
+
+static Menu* main_menu = 0;
+static Menu* view_menu = 0;
+static Menu* emcon_menu = 0;
+
+static Menu* fighter_menu = 0;
+static Menu* starship_menu = 0;
+static Menu* action_menu = 0;
+static Menu* formation_menu = 0;
+static Menu* sensors_menu = 0;
+static Menu* quantum_menu = 0;
+static Menu* farcast_menu = 0;
+
+static Element* dst_elem = 0;
+
+enum VIEW_MENU {
+ VIEW_FORWARD = 1000,
+ VIEW_CHASE,
+ VIEW_PADLOCK,
+ VIEW_ORBIT,
+ VIEW_NAV,
+ VIEW_WEP,
+ VIEW_ENG,
+ VIEW_FLT,
+ VIEW_INS,
+ VIEW_CMD
+};
+
+const int QUANTUM = 2000;
+const int FARCAST = 2001;
+
+void
+TacticalView::Initialize()
+{
+ static int initialized = 0;
+ if (initialized) return;
+
+ view_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.view"));
+ view_menu->AddItem(Game::GetText("TacView.item.forward"), VIEW_FORWARD);
+ view_menu->AddItem(Game::GetText("TacView.item.chase"), VIEW_CHASE);
+ view_menu->AddItem(Game::GetText("TacView.item.orbit"), VIEW_ORBIT);
+ view_menu->AddItem(Game::GetText("TacView.item.padlock"), VIEW_PADLOCK);
+
+ emcon_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.emcon"));
+
+ quantum_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.quantum"));
+ farcast_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.farcast"));
+
+ main_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.main"));
+
+ action_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.action"));
+ action_menu->AddItem(Game::GetText("TacView.item.engage"), RadioMessage::ATTACK);
+ action_menu->AddItem(Game::GetText("TacView.item.bracket"), RadioMessage::BRACKET);
+ action_menu->AddItem(Game::GetText("TacView.item.escort"), RadioMessage::ESCORT);
+ action_menu->AddItem(Game::GetText("TacView.item.identify"), RadioMessage::IDENTIFY);
+ action_menu->AddItem(Game::GetText("TacView.item.hold"), RadioMessage::WEP_HOLD);
+
+ formation_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.formation"));
+ formation_menu->AddItem(Game::GetText("TacView.item.diamond"), RadioMessage::GO_DIAMOND);
+ formation_menu->AddItem(Game::GetText("TacView.item.spread"), RadioMessage::GO_SPREAD);
+ formation_menu->AddItem(Game::GetText("TacView.item.box"), RadioMessage::GO_BOX);
+ formation_menu->AddItem(Game::GetText("TacView.item.trail"), RadioMessage::GO_TRAIL);
+
+ sensors_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.emcon"));
+ sensors_menu->AddItem(Game::GetText("TacView.item.emcon-1"), RadioMessage::GO_EMCON1);
+ sensors_menu->AddItem(Game::GetText("TacView.item.emcon-2"), RadioMessage::GO_EMCON2);
+ sensors_menu->AddItem(Game::GetText("TacView.item.emcon-3"), RadioMessage::GO_EMCON3);
+ sensors_menu->AddItem(Game::GetText("TacView.item.probe"), RadioMessage::LAUNCH_PROBE);
+
+ fighter_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.context"));
+ fighter_menu->AddMenu(Game::GetText("TacView.item.action"), action_menu);
+ fighter_menu->AddMenu(Game::GetText("TacView.item.formation"), formation_menu);
+ fighter_menu->AddMenu(Game::GetText("TacView.item.sensors"), sensors_menu);
+ fighter_menu->AddItem(Game::GetText("TacView.item.patrol"), RadioMessage::MOVE_PATROL);
+ fighter_menu->AddItem(Game::GetText("TacView.item.cancel"), RadioMessage::RESUME_MISSION);
+ fighter_menu->AddItem("", 0);
+ fighter_menu->AddItem(Game::GetText("TacView.item.rtb"), RadioMessage::RTB);
+ fighter_menu->AddItem(Game::GetText("TacView.item.dock"), RadioMessage::DOCK_WITH);
+ fighter_menu->AddMenu(Game::GetText("TacView.item.farcast"), farcast_menu);
+
+ starship_menu = new(__FILE__,__LINE__) Menu(Game::GetText("TacView.menu.context"));
+ starship_menu->AddMenu(Game::GetText("TacView.item.action"), action_menu);
+ starship_menu->AddMenu(Game::GetText("TacView.item.sensors"), sensors_menu);
+ starship_menu->AddItem(Game::GetText("TacView.item.patrol"), RadioMessage::MOVE_PATROL);
+ starship_menu->AddItem(Game::GetText("TacView.item.cancel"), RadioMessage::RESUME_MISSION);
+ starship_menu->AddItem("", 0);
+ starship_menu->AddMenu(Game::GetText("TacView.item.quantum"), quantum_menu);
+ starship_menu->AddMenu(Game::GetText("TacView.item.farcast"), farcast_menu);
+
+ initialized = 1;
+}
+
+void
+TacticalView::Close()
+{
+ delete view_menu;
+ delete emcon_menu;
+ delete main_menu;
+ delete fighter_menu;
+ delete starship_menu;
+ delete action_menu;
+ delete formation_menu;
+ delete sensors_menu;
+ delete quantum_menu;
+ delete farcast_menu;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalView::ProcessMenuItem(int action)
+{
+ Starshatter* stars = Starshatter::GetInstance();
+
+ switch (action) {
+ case RadioMessage::MOVE_PATROL:
+ show_move = true;
+ base_alt = 0;
+ move_alt = 0;
+
+ if (msg_ship) base_alt = msg_ship->Location().y;
+ break;
+
+ case RadioMessage::ATTACK:
+ case RadioMessage::BRACKET:
+ case RadioMessage::ESCORT:
+ case RadioMessage::IDENTIFY:
+ case RadioMessage::DOCK_WITH:
+ show_action = action;
+ break;
+
+ case RadioMessage::WEP_HOLD:
+ case RadioMessage::RESUME_MISSION:
+ case RadioMessage::RTB:
+ case RadioMessage::GO_DIAMOND:
+ case RadioMessage::GO_SPREAD:
+ case RadioMessage::GO_BOX:
+ case RadioMessage::GO_TRAIL:
+ case RadioMessage::GO_EMCON1:
+ case RadioMessage::GO_EMCON2:
+ case RadioMessage::GO_EMCON3:
+ case RadioMessage::LAUNCH_PROBE:
+ if (msg_ship) {
+ Element* elem = msg_ship->GetElement();
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(elem, ship, action);
+ if (msg)
+ RadioTraffic::Transmit(msg);
+ }
+ else if (ship) {
+ if (action == RadioMessage::GO_EMCON1)
+ ship->SetEMCON(1);
+ else if (action == RadioMessage::GO_EMCON2)
+ ship->SetEMCON(2);
+ else if (action == RadioMessage::GO_EMCON3)
+ ship->SetEMCON(3);
+ else if (action == RadioMessage::LAUNCH_PROBE)
+ ship->LaunchProbe();
+ }
+ break;
+
+ case VIEW_FORWARD: stars->PlayerCam(CameraDirector::MODE_COCKPIT); break;
+ case VIEW_CHASE: stars->PlayerCam(CameraDirector::MODE_CHASE); break;
+ case VIEW_PADLOCK: stars->PlayerCam(CameraDirector::MODE_TARGET); break;
+ case VIEW_ORBIT: stars->PlayerCam(CameraDirector::MODE_ORBIT); break;
+
+ case VIEW_NAV: gamescreen->ShowNavDlg(); break;
+ case VIEW_WEP: gamescreen->ShowWeaponsOverlay(); break;
+ case VIEW_ENG: gamescreen->ShowEngDlg(); break;
+ case VIEW_INS: HUDView::GetInstance()->CycleHUDInst(); break;
+ case VIEW_FLT: gamescreen->ShowFltDlg(); break;
+
+ case VIEW_CMD: if (ship && ship->IsStarship()) {
+ ship->CommandMode();
+ }
+ break;
+
+ case QUANTUM: if (sim) {
+ Ship* s = msg_ship;
+
+ if (!s)
+ s = ship;
+
+ if (s && s->GetQuantumDrive()) {
+ QuantumDrive* quantum = s->GetQuantumDrive();
+ if (quantum) {
+ MenuItem* menu_item = menu_view->GetMenuItem();
+ Text rgn_name = menu_item->GetText();
+ SimRegion* rgn = sim->FindRegion(rgn_name);
+
+ if (rgn) {
+ if (s == ship) {
+ quantum->SetDestination(rgn, Point(0,0,0));
+ quantum->Engage();
+ }
+
+ else {
+ Element* elem = msg_ship->GetElement();
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(elem, ship, RadioMessage::QUANTUM_TO);
+ if (msg) {
+ msg->SetInfo(rgn_name);
+ RadioTraffic::Transmit(msg);
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case FARCAST: if (sim && msg_ship) {
+ MenuItem* menu_item = menu_view->GetMenuItem();
+ Text rgn_name = menu_item->GetText();
+ SimRegion* rgn = sim->FindRegion(rgn_name);
+
+ if (rgn) {
+ Element* elem = msg_ship->GetElement();
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(elem, ship, RadioMessage::FARCAST_TO);
+ if (msg) {
+ msg->SetInfo(rgn_name);
+ RadioTraffic::Transmit(msg);
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalView::BuildMenu()
+{
+ main_menu->ClearItems();
+ quantum_menu->ClearItems();
+ farcast_menu->ClearItems();
+ emcon_menu->ClearItems();
+
+ if (!ship)
+ return;
+
+ // prepare quantum and farcast menus:
+ ListIter<SimRegion> iter = sim->GetRegions();
+ while (++iter) {
+ SimRegion* rgn = iter.value();
+ if (rgn != ship->GetRegion() && rgn->Type() != SimRegion::AIR_SPACE)
+ quantum_menu->AddItem(rgn->Name(), QUANTUM);
+ }
+
+ if (ship->GetRegion()) {
+ ListIter<Ship> iter = ship->GetRegion()->Ships();
+ while (++iter) {
+ Ship* s = iter.value();
+
+ if (s && s->GetFarcaster()) {
+ Farcaster* farcaster = s->GetFarcaster();
+
+ // ensure that the farcaster is connected:
+ farcaster->ExecFrame(0);
+
+ // now find the destination
+ const Ship* dest = farcaster->GetDest();
+
+ if (dest && dest->GetRegion()) {
+ SimRegion* rgn = dest->GetRegion();
+ farcast_menu->AddItem(rgn->Name(), FARCAST);
+ }
+ }
+ }
+ }
+
+ // build the main menu:
+ main_menu->AddMenu(Game::GetText("TacView.item.camera"), view_menu);
+ main_menu->AddItem("", 0);
+ main_menu->AddItem(Game::GetText("TacView.item.instructions"), VIEW_INS);
+ main_menu->AddItem(Game::GetText("TacView.item.navigation"), VIEW_NAV);
+
+ if (ship->Design()->repair_screen)
+ main_menu->AddItem(Game::GetText("TacView.item.engineering"), VIEW_ENG);
+
+ if (ship->Design()->wep_screen)
+ main_menu->AddItem(Game::GetText("TacView.item.weapons"), VIEW_WEP);
+
+ if (ship->NumFlightDecks() > 0)
+ main_menu->AddItem(Game::GetText("TacView.item.flight"), VIEW_FLT);
+
+ emcon_menu->AddItem(Game::GetText("TacView.item.emcon-1"), RadioMessage::GO_EMCON1);
+ emcon_menu->AddItem(Game::GetText("TacView.item.emcon-2"), RadioMessage::GO_EMCON2);
+ emcon_menu->AddItem(Game::GetText("TacView.item.emcon-3"), RadioMessage::GO_EMCON3);
+
+ if (ship->GetProbeLauncher())
+ emcon_menu->AddItem(Game::GetText("TacView.item.probe"), RadioMessage::LAUNCH_PROBE);
+
+ main_menu->AddItem("", 0);
+ main_menu->AddMenu(Game::GetText("TacView.item.sensors"), emcon_menu);
+
+ if (sim && ship->GetQuantumDrive()) {
+ main_menu->AddItem("", 0);
+ main_menu->AddMenu(Game::GetText("TacView.item.quantum"), quantum_menu);
+ }
+
+ if (ship->IsStarship()) {
+ main_menu->AddItem("", 0);
+ main_menu->AddItem(Game::GetText("TacView.item.command"), VIEW_CMD);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TacticalView::DrawMenu()
+{
+ active_menu = 0;
+
+ if (ship)
+ active_menu = main_menu;
+
+ if (msg_ship) {
+ if (msg_ship->IsStarship())
+ active_menu = starship_menu;
+ else if (msg_ship->IsDropship())
+ active_menu = fighter_menu;
+ }
+
+ if (menu_view) {
+ menu_view->SetBackColor(hud_color);
+ menu_view->SetTextColor(txt_color);
+ menu_view->SetMenu(active_menu);
+ menu_view->Refresh();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+TacticalView::GetMouseLoc3D()
+{
+ int mx = Mouse::X();
+ int my = Mouse::Y();
+
+ if (projector) {
+ double focal_dist = width / tan(projector->XAngle());
+ Point focal_vect = projector->vpn() * focal_dist +
+ projector->vup() * -1 * (my-height/2) +
+ projector->vrt() * (mx-width/2);
+
+ focal_vect.Normalize();
+
+ if (Keyboard::KeyDown(VK_SHIFT)) {
+ if (Mouse::RButton())
+ return true;
+
+ if (fabs(focal_vect.x) > fabs(focal_vect.z)) {
+ double dx = move_loc.x - projector->Pos().x;
+ double t = -1 * ((projector->Pos().x - dx) / focal_vect.x);
+
+ if (t > 0) {
+ Point p = projector->Pos() + focal_vect * t;
+ move_alt = p.y - base_alt;
+ }
+ }
+ else {
+ double dz = move_loc.z - projector->Pos().z;
+ double t = -1 * ((projector->Pos().z - dz) / focal_vect.z);
+ Point p = projector->Pos() + focal_vect * t;
+
+ if (t > 0) {
+ Point p = projector->Pos() + focal_vect * t;
+ move_alt = p.y - base_alt;
+ }
+ }
+
+ if (move_alt > 25e3)
+ move_alt = 25e3;
+ else if (move_alt < -25e3)
+ move_alt = -25e3;
+
+ return true;
+ }
+ else {
+ if (fabs(focal_vect.y) > 1e-5) {
+ if (Mouse::RButton())
+ return true;
+
+ bool clamp = false;
+ double t = -1 * ((projector->Pos().y - base_alt) / focal_vect.y);
+
+ while (t <= 0 && my < height-1) {
+ my++;
+ clamp = true;
+
+ focal_vect = projector->vpn() * focal_dist +
+ projector->vup() * -1 * (my-height/2) +
+ projector->vrt() * (mx-width/2);
+
+ focal_vect.Normalize();
+ t = -1 * ((projector->Pos().y - base_alt) / focal_vect.y);
+ }
+
+ if (t > 0) {
+ if (clamp)
+ Mouse::SetCursorPos(mx, my);
+
+ move_loc = projector->Pos() + focal_vect * t;
+ }
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void
+TacticalView::DrawMove()
+{
+ if (!projector || !show_move || !msg_ship) return;
+
+ Point origin = msg_ship->Location();
+
+ if (GetMouseLoc3D()) {
+ Point dest = move_loc;
+
+ double distance = (dest - origin).length();
+
+ projector->Transform(origin);
+ projector->Project(origin);
+
+ int x0 = (int) origin.x;
+ int y0 = (int) origin.y;
+
+ projector->Transform(dest);
+ projector->Project(dest);
+
+ int x = (int) dest.x;
+ int y = (int) dest.y;
+
+ window->DrawEllipse(x-10, y-10, x+10, y+10, Color::White);
+ window->DrawLine(x0, y0, x, y, Color::White);
+
+ char range[32];
+ Rect range_rect(x+12, y-8, 120, 20);
+
+ if (fabs(move_alt) > 1) {
+ dest = move_loc;
+ dest.y += move_alt;
+ distance = (dest - msg_ship->Location()).length();
+
+ projector->Transform(dest);
+ projector->Project(dest);
+
+ int x1 = (int) dest.x;
+ int y1 = (int) dest.y;
+
+ window->DrawEllipse(x1-10, y1-10, x1+10, y1+10, Color::White);
+ window->DrawLine(x0, y0, x1, y1, Color::White);
+ window->DrawLine(x1, y1, x, y, Color::White);
+
+ range_rect.x = x1+12;
+ range_rect.y = y1-8;
+ }
+
+ FormatNumber(range, distance);
+ font->SetColor(Color::White);
+ font->DrawText(range, 0, range_rect, DT_LEFT | DT_SINGLELINE);
+ font->SetColor(txt_color);
+ }
+}
+
+void
+TacticalView::SendMove()
+{
+ if (!projector || !show_move || !msg_ship) return;
+
+ if (GetMouseLoc3D()) {
+ Element* elem = msg_ship->GetElement();
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(elem, ship, RadioMessage::MOVE_PATROL);
+ Point dest = move_loc;
+ dest.y += move_alt;
+ msg->SetLocation(dest);
+ RadioTraffic::Transmit(msg);
+ HUDSounds::PlaySound(HUDSounds::SND_TAC_ACCEPT);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static int invalid_action = false;
+
+void
+TacticalView::DrawAction()
+{
+ if (!projector || !show_action || !msg_ship) return;
+
+ Point origin = msg_ship->Location();
+ projector->Transform(origin);
+ projector->Project(origin);
+
+ int x0 = (int) origin.x;
+ int y0 = (int) origin.y;
+
+ int mx = mouse_action.x;
+ int my = mouse_action.y;
+ int r = 10;
+
+ int enemy = 2;
+ if (ship->GetIFF() > 1)
+ enemy = 1;
+
+ Ship* tgt = WillSelectAt(mx, my);
+ int tgt_iff = 0;
+
+ if (tgt)
+ tgt_iff = tgt->GetIFF();
+
+ Color c = Color::White;
+
+ switch (show_action) {
+ case RadioMessage::ATTACK:
+ case RadioMessage::BRACKET:
+ c = Ship::IFFColor(enemy);
+ if (tgt) {
+ if (tgt_iff == ship->GetIFF() || tgt_iff == 0)
+ r = 0;
+ }
+ break;
+
+ case RadioMessage::ESCORT:
+ case RadioMessage::DOCK_WITH:
+ c = ship->MarkerColor();
+ if (tgt) {
+ if (tgt_iff == enemy)
+ r = 0;
+
+ // must have a hangar to dock with...
+ if (show_action == RadioMessage::DOCK_WITH && tgt->GetHangar() == 0)
+ r = 0;
+ }
+ break;
+
+ default:
+ if (tgt) {
+ if (tgt_iff == ship->GetIFF())
+ r = 0;
+ }
+ break;
+ }
+
+ if (tgt && r) {
+ if ((Game::RealTime()/200) & 1)
+ r = 20;
+ else
+ r = 15;
+ }
+
+ if (r) {
+ invalid_action = false;
+ window->DrawEllipse(mx-r, my-r, mx+r, my+r, c);
+ }
+
+ else {
+ invalid_action = true;
+ window->DrawLine(mx-10, my-10, mx+10, my+10, c);
+ window->DrawLine(mx+10, my-10, mx-10, my+10, c);
+ }
+
+ window->DrawLine(x0, y0, mx, my, c);
+}
+
+void
+TacticalView::SendAction()
+{
+ if (!show_action || !msg_ship || invalid_action) {
+ HUDSounds::PlaySound(HUDSounds::SND_TAC_REJECT);
+ return;
+ }
+
+ int mx = mouse_action.x;
+ int my = mouse_action.y;
+
+ Ship* tgt = WillSelectAt(mx, my);
+
+ if (tgt) {
+ Element* elem = msg_ship->GetElement();
+ RadioMessage* msg = new(__FILE__,__LINE__) RadioMessage(elem, ship, show_action);
+
+ /***
+ Element* tgt_elem = tgt->GetElement();
+
+ if (tgt_elem) {
+ for (int i = 1; i <= tgt_elem->NumShips(); i++)
+ msg->AddTarget(tgt_elem->GetShip(i));
+ }
+ else {
+ msg->AddTarget(tgt);
+ }
+ ***/
+
+ msg->AddTarget(tgt);
+
+ RadioTraffic::Transmit(msg);
+ HUDSounds::PlaySound(HUDSounds::SND_TAC_ACCEPT);
+ }
+ else {
+ HUDSounds::PlaySound(HUDSounds::SND_TAC_REJECT);
+ }
+}
+
+
diff --git a/Stars45/TacticalView.h b/Stars45/TacticalView.h
new file mode 100644
index 0000000..7b09a32
--- /dev/null
+++ b/Stars45/TacticalView.h
@@ -0,0 +1,120 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TacticalView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ View class for Radio Communications HUD Overlay
+*/
+
+#ifndef TacticalView_h
+#define TacticalView_h
+
+#include "Types.h"
+#include "View.h"
+#include "Color.h"
+#include "SimObject.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Font;
+class Ship;
+class RadioMessage;
+class CameraView;
+class Projector;
+class HUDView;
+class Menu;
+class MenuItem;
+class MenuView;
+class GameScreen;
+
+// +--------------------------------------------------------------------+
+
+class TacticalView : public View,
+ public SimObserver
+{
+public:
+ TacticalView(Window* c, GameScreen* parent);
+ virtual ~TacticalView();
+
+ // Operations:
+ virtual void Refresh();
+ virtual void OnWindowMove();
+ virtual void ExecFrame();
+ virtual void UseProjector(Projector* p);
+
+ virtual void DoMouseFrame();
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+ static void SetColor(Color c);
+
+ static void Initialize();
+ static void Close();
+
+ static TacticalView* GetInstance() { return tac_view; }
+
+protected:
+ virtual bool SelectAt(int x, int y);
+ virtual bool SelectRect(const Rect& r);
+ virtual Ship* WillSelectAt(int x, int y);
+ virtual void SetHelm(bool approach);
+
+ virtual void DrawMouseRect();
+ virtual void DrawSelection(Ship* seln);
+ virtual void DrawSelectionInfo(Ship* seln);
+ virtual void DrawSelectionList(ListIter<Ship> seln);
+
+ virtual void BuildMenu();
+ virtual void DrawMenu();
+ virtual void ProcessMenuItem(int action);
+
+ virtual void DrawMove();
+ virtual void SendMove();
+ virtual bool GetMouseLoc3D();
+
+ virtual void DrawAction();
+ virtual void SendAction();
+
+ GameScreen* gamescreen;
+ CameraView* camview;
+ Projector* projector;
+
+ int width, height;
+ double xcenter, ycenter;
+
+ int shift_down;
+ int mouse_down;
+ int right_down;
+ int show_move;
+ int show_action;
+
+ Point move_loc;
+ double base_alt;
+ double move_alt;
+
+ POINT mouse_action;
+ POINT mouse_start;
+ Rect mouse_rect;
+
+ Font* font;
+ Sim* sim;
+ Ship* ship;
+ Ship* msg_ship;
+ Text current_sector;
+
+ Menu* active_menu;
+ MenuView* menu_view;
+
+ static TacticalView* tac_view;
+};
+
+#endif TacticalView_h
+
diff --git a/Stars45/Terrain.cpp b/Stars45/Terrain.cpp
new file mode 100644
index 0000000..7a0251c
--- /dev/null
+++ b/Stars45/Terrain.cpp
@@ -0,0 +1,561 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Terrain.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "Terrain.h"
+#include "TerrainApron.h"
+#include "TerrainClouds.h"
+#include "TerrainLayer.h"
+#include "TerrainPatch.h"
+#include "TerrainRegion.h"
+#include "Water.h"
+
+#include "CameraView.h"
+#include "Projector.h"
+#include "Scene.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Game.h"
+#include "MachineInfo.h"
+
+// +--------------------------------------------------------------------+
+
+int Terrain::detail_level = 3; // default = MEDIUM DETAIL
+
+const int PATCH_SIZE = 16;
+
+// +--------------------------------------------------------------------+
+
+Terrain::Terrain(TerrainRegion* trgn)
+ : region(trgn), patches(0), water_patches(0), water(0),
+ aprons(0), clouds(0), terrain_normals(0)
+{
+ detail_frame = 0;
+ datapath = "Galaxy/";
+ terrain_texture = 0;
+ apron_texture = 0;
+ water_texture = 0;
+
+ for (int i = 0; i < 2; i++) {
+ cloud_texture[i] = 0;
+ shade_texture[i] = 0;
+ noise_texture[i] = 0;
+ }
+
+ if (region) {
+ scale = region->LateralScale();
+ mtnscale = region->MountainScale();
+
+ ListIter<TerrainLayer> iter = region->GetLayers();
+ while (++iter) {
+ TerrainLayer* orig = iter.value();
+ TerrainLayer* copy = new(__FILE__,__LINE__) TerrainLayer;
+ *copy = *orig;
+ layers.append(copy);
+ }
+
+ layers.sort();
+ }
+
+ else {
+ scale = 1;
+ mtnscale = 1;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Terrain::~Terrain()
+{
+ int i, j;
+
+ if (patches)
+ for (i = 0; i < subdivisions; i++)
+ for (j = 0; j < subdivisions; j++)
+ GRAPHIC_DESTROY(patches[i*subdivisions+j]);
+
+ if (water_patches)
+ for (i = 0; i < subdivisions; i++)
+ for (j = 0; j < subdivisions; j++)
+ GRAPHIC_DESTROY(water_patches[i*subdivisions+j]);
+
+ if (aprons)
+ for (i = 0; i < 8; i++)
+ GRAPHIC_DESTROY(aprons[i]);
+
+ if (clouds)
+ for (i = 0; i < nclouds; i++)
+ GRAPHIC_DESTROY(clouds[i]);
+
+ if (water)
+ for (i = 0; i < 6; i++)
+ delete water[i];
+
+ delete [] aprons;
+ delete [] clouds;
+ delete [] patches;
+ delete [] water;
+ delete [] terrain_normals;
+
+ terrain_patch.ClearImage();
+ terrain_apron.ClearImage();
+
+ layers.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Terrain::BuildTerrain()
+{
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath(datapath);
+ loader->LoadBitmap( region->PatchName(), terrain_patch);
+ loader->LoadBitmap( region->ApronName(), terrain_apron);
+ loader->LoadTexture(region->PatchTexture(), terrain_texture);
+ loader->LoadTexture(region->ApronTexture(), apron_texture);
+ if (region->WaterTexture().length()) {
+ loader->LoadTexture(region->WaterTexture(), water_texture);
+
+ if (region->EnvironmentTexture(0).length() > 0) {
+ loader->LoadTexture(region->EnvironmentTexture(0), env_texture[0]);
+ loader->LoadTexture(region->EnvironmentTexture(1), env_texture[1]);
+ loader->LoadTexture(region->EnvironmentTexture(2), env_texture[2]);
+ loader->LoadTexture(region->EnvironmentTexture(3), env_texture[3]);
+ loader->LoadTexture(region->EnvironmentTexture(4), env_texture[4]);
+ loader->LoadTexture(region->EnvironmentTexture(5), env_texture[5]);
+ }
+ }
+
+ loader->LoadTexture(region->CloudsHigh(), cloud_texture[0], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture(region->CloudsLow(), cloud_texture[1], Bitmap::BMP_TRANSLUCENT);
+ loader->LoadTexture(region->ShadesLow(), shade_texture[1], Bitmap::BMP_TRANSLUCENT);
+
+ if (region->DetailTexture0().length())
+ loader->LoadTexture(region->DetailTexture0(),noise_texture[0], Bitmap::BMP_TRANSLUCENT, false, true);
+
+ if (region->DetailTexture1().length())
+ loader->LoadTexture(region->DetailTexture1(),noise_texture[1], Bitmap::BMP_TRANSLUCENT, false, true);
+
+ subdivisions = terrain_patch.Width() / PATCH_SIZE;
+ patch_size = terrain_patch.Width() / subdivisions;
+
+ BuildNormals();
+
+ int i, j;
+ int ntiles = terrain_patch.Width()/2 * terrain_patch.Height()/2;
+ double dx = scale * patch_size;
+ double dz = scale * patch_size;
+ double offset = -subdivisions/2;
+
+ if (water_texture) {
+ water = new(__FILE__,__LINE__) Water*[6];
+ for (i = 0; i < 6; i++) {
+ water[i] = new(__FILE__,__LINE__) Water();
+ int n = (1<<i) + 1;
+ water[i]->Init(n, (float) (scale*patch_size), 90.0f);
+ }
+ }
+
+ // load tile textures:
+ for (i = 0; i < layers.size(); i++) {
+ TerrainLayer* layer = layers.at(i);
+
+ if (i < layers.size()-1)
+ layer->max_height = layers.at(i+1)->min_height;
+ else
+ layer->max_height = 1e6;
+
+ if (layer->tile_name.length())
+ loader->LoadTexture(layer->tile_name, layer->tile_texture);
+
+ if (layer->detail_name.length())
+ loader->LoadTexture(layer->detail_name, layer->detail_texture);
+ }
+
+ patches = new(__FILE__,__LINE__) TerrainPatch*[subdivisions*subdivisions];
+
+ if (water_texture)
+ water_patches = new(__FILE__,__LINE__) TerrainPatch*[subdivisions*subdivisions];
+
+ for (i = 0; i < subdivisions; i++) {
+ for (j = 0; j < subdivisions; j++) {
+ int j1 = subdivisions - j;
+ Rect rect(j * patch_size, i * patch_size, patch_size, patch_size);
+ Point p1((j1 + offset )*dx, 0, (i + offset )*dz);
+ Point p2((j1 + offset+1)*dx, mtnscale, (i + offset+1)*dz);
+
+ int index = i*subdivisions+j;
+ patches[index] = new(__FILE__,__LINE__) TerrainPatch(this, &terrain_patch, rect, p1, p2);
+
+ if (water_texture && patches[index]->MinHeight() < 3)
+ water_patches[index] = new(__FILE__,__LINE__) TerrainPatch(this, rect, p1, p2, 30);
+
+ else if (water_patches != 0)
+ water_patches[index] = 0;
+ }
+ }
+
+ int a = 0;
+ dx = scale * terrain_patch.Width();
+ dz = scale * terrain_patch.Height();
+ offset = -3.0/2;
+ double xoffset = offset + 1.0/16.0;
+
+ aprons = new(__FILE__,__LINE__) TerrainApron*[8];
+
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 3; j++) {
+ int j1 = 2 - j;
+ if (i != 1 || j1 != 1) {
+ Rect rect(j * subdivisions, i * subdivisions, subdivisions, subdivisions);
+ Point p1((j1 + xoffset )*dx, 0, (i + offset )*dz);
+ Point p2((j1 + xoffset+1)*dx, mtnscale, (i + offset+1)*dz);
+
+ aprons[a++] = new(__FILE__,__LINE__) TerrainApron(this, &terrain_apron, rect, p1, p2);
+ }
+ }
+ }
+
+ Weather::STATE state = region->GetWeather().State();
+
+ if (state == Weather::HIGH_CLOUDS || state == Weather::MODERATE_CLOUDS) {
+ double altitude = region->CloudAltHigh();
+ nclouds = 9;
+
+ if (state == Weather::MODERATE_CLOUDS)
+ nclouds *= 2;
+
+ clouds = new(__FILE__,__LINE__) TerrainClouds*[nclouds];
+
+ a = 0;
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 3; j++) {
+ clouds[a] = new(__FILE__,__LINE__) TerrainClouds(this, 0);
+
+ double xloc = (j-1) * 75000 + (rand()/32768.0 - 0.5) * 50000;
+ double yloc = (i-1) * 75000 + (rand()/32768.0 - 0.5) * 50000;
+
+ clouds[a]->MoveTo(Point(xloc, altitude, yloc));
+ a++;
+ }
+ }
+
+ if (state == Weather::MODERATE_CLOUDS) {
+ altitude = region->CloudAltLow();
+
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 3; j++) {
+ clouds[a] = new(__FILE__,__LINE__) TerrainClouds(this, 1);
+
+ double xloc = (j-1) * 75000 + (rand()/32768.0 - 0.5) * 50000;
+ double yloc = (i-1) * 75000 + (rand()/32768.0 - 0.5) * 50000;
+
+ clouds[a]->MoveTo(Point(xloc, altitude, yloc));
+ a++;
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Terrain::BuildNormals()
+{
+ if (terrain_normals) {
+ delete [] terrain_normals;
+ terrain_normals = 0;
+ }
+
+ int i, x, y;
+
+ int w = terrain_patch.Width();
+ int h = terrain_patch.Height();
+ Color* pix = terrain_patch.HiPixels();
+ BYTE* alt = new(__FILE__,__LINE__) BYTE[h*w];
+ int nverts = w * h;
+ double scale = region->MountainScale() / (region->LateralScale() * 2.0);
+
+ terrain_normals = new(__FILE__,__LINE__) Vec3B[nverts];
+
+ ZeroMemory(terrain_normals, sizeof(Vec3B) * nverts);
+
+ for (i = 0; i < w; i++) {
+ alt [ i] = 0;
+ alt [(h-1)*w + i] = 0;
+ terrain_normals[ i] = Vec3B(128,128,255);
+ terrain_normals[(h-1)*w + i] = Vec3B(128,128,255);
+ }
+
+ for (i = 0; i < h; i++) {
+ alt [i*w ] = 0;
+ alt [i*w + (w-1)] = 0;
+ terrain_normals[i*w ] = Vec3B(128,128,255);
+ terrain_normals[i*w + (w-1)] = Vec3B(128,128,255);
+ }
+
+ for (y = 1; y < h-1; y++) {
+ for (x = 1; x < w-1; x++) {
+ alt[y*w+x] = (BYTE) pix[y*w+x].Red();
+ }
+ }
+
+ for (y = 1; y < h-1; y++) {
+ for (x = 1; x < w-1; x++) {
+ double dx = (alt[y*w + (x-1)] - alt[y*w + (x+1)]) * scale +
+ (alt[(y-1)*w + (x-1)] - alt[(y-1)*w + (x+1)]) * scale * 0.5 +
+ (alt[(y+1)*w + (x-1)] - alt[(y+1)*w + (x+1)]) * scale * 0.5;
+
+ double dy = (alt[(y-1)*w + x ] - alt[(y+1)*w + x ]) * scale +
+ (alt[(y-1)*w + (x-1)] - alt[(y+1)*w + (x-1)]) * scale * 0.5 +
+ (alt[(y-1)*w + (x+1)] - alt[(y+1)*w + (x+1)]) * scale * 0.5;
+
+ Point norm(dx,dy,1);
+ norm.Normalize();
+
+ Vec3B* tnorm = &terrain_normals[y*w + x];
+
+ tnorm->x = (BYTE) (norm.x * 127 + 128);
+ tnorm->y = (BYTE) (norm.y * 127 + 128);
+ tnorm->z = (BYTE) (norm.z * 127 + 128);
+
+ double foo = dx/dy;
+ }
+ }
+
+ delete [] alt;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Terrain::Activate(Scene& scene)
+{
+ int i, j;
+
+ StarSystem* system = region->System();
+ if (system)
+ datapath = system->GetDataPath();
+
+ region->GetWeather().Update();
+
+ if (!patches)
+ BuildTerrain();
+
+ if (patches) {
+ for (i = 0; i < subdivisions; i++)
+ for (j = 0; j < subdivisions; j++)
+ if (patches[i*subdivisions+j])
+ scene.AddGraphic(patches[i*subdivisions+j]);
+ }
+
+ if (water_patches) {
+ for (i = 0; i < subdivisions; i++)
+ for (j = 0; j < subdivisions; j++)
+ if (water_patches[i*subdivisions+j])
+ scene.AddGraphic(water_patches[i*subdivisions+j]);
+ }
+
+ if (aprons) {
+ for (i = 0; i < 8; i++)
+ if (aprons[i])
+ scene.AddGraphic(aprons[i]);
+ }
+
+ if (clouds) {
+ for (i = 0; i < nclouds; i++)
+ if (clouds[i])
+ scene.AddGraphic(clouds[i]);
+ }
+}
+
+void
+Terrain::Deactivate(Scene& scene)
+{
+ int i, j;
+
+ if (patches) {
+ for (i = 0; i < subdivisions; i++) {
+ for (j = 0; j < subdivisions; j++) {
+ TerrainPatch* p = patches[i*subdivisions+j];
+
+ if (p) {
+ p->DeletePrivateData();
+ scene.DelGraphic(p);
+ }
+ }
+ }
+ }
+
+ if (water_patches) {
+ for (i = 0; i < subdivisions; i++) {
+ for (j = 0; j < subdivisions; j++) {
+ TerrainPatch* p = water_patches[i*subdivisions+j];
+
+ if (p) {
+ p->DeletePrivateData();
+ scene.DelGraphic(p);
+ }
+ }
+ }
+ }
+
+ if (aprons) {
+ for (i = 0; i < 8; i++)
+ if (aprons[i])
+ scene.DelGraphic(aprons[i]);
+ }
+
+ if (clouds) {
+ for (i = 0; i < nclouds; i++)
+ if (clouds[i])
+ scene.DelGraphic(clouds[i]);
+ }
+
+ StarSystem* system = region->System();
+
+ // restore sunlight color and brightness on exit:
+ if (system) {
+ system->RestoreTrueSunColor();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Terrain::ExecFrame(double seconds)
+{
+ if (water) {
+ for (int i = 0; i < 6; i++) {
+ if (water[i])
+ water[i]->CalcWaves(seconds);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Terrain::SelectDetail(Projector* projector)
+{
+ if (!patches)
+ return;
+
+ if (detail_frame >= Game::Frame())
+ return;
+
+ // compute detail map:
+ int x, z;
+
+ for (z = 0; z < subdivisions; z++) {
+ for (x = 0; x < subdivisions; x++) {
+ TerrainPatch* patch = patches[z*subdivisions + x];
+ int ndetail = 0;
+ Point loc = patch->Location();
+ float radius = patch->Radius();
+
+ if (loc.length() < 2*radius) {
+ ndetail = detail_level;
+ }
+
+ else {
+ double threshold = 4; //16;
+
+ for (int level = 1; level <= detail_level; level++) {
+ double feature_size = radius / (1 << level);
+
+ if (projector->ApparentRadius(loc, (float) feature_size) > threshold)
+ ndetail = level;
+ }
+ }
+
+ patch->SetDetailLevel(ndetail);
+
+ if (water_patches) {
+ patch = water_patches[z*subdivisions + x];
+ if (patch)
+ patch->SetDetailLevel(ndetail);
+ }
+ }
+ }
+
+ // compute fog fade level:
+ double hour = region->DayPhase();
+
+ if (hour < 12)
+ fog_fade = (hour) / 12.0;
+ else
+ fog_fade = (24-hour) / 12.0;
+
+ fog_fade = fog_fade * (1-region->HazeFade()) + region->HazeFade();
+
+ detail_frame = Game::Frame();
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Terrain::Height(double x, double y) const
+{
+ double h = 0;
+
+ if (patches) {
+ int ix = (int) floor(x / (patch_size * scale));
+ int iy = (int) floor(y / (patch_size * scale));
+
+ double px = x - ix * patch_size * scale;
+ double py = y - iy * patch_size * scale;
+
+ ix = subdivisions/2 - ix;
+ iy = subdivisions/2 + iy;
+
+ TerrainPatch* patch = 0;
+
+ if (ix >= 0 && ix < subdivisions &&
+ iy >= 0 && iy < subdivisions)
+ patch = patches[iy*subdivisions+ix];
+
+ if (patch)
+ h = patch->Height(px, py);
+ }
+
+ if (water_patches && h < 30)
+ h = 30;
+
+ return h;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Terrain::SetDetailLevel(int detail)
+{
+ if (detail >= 1 && detail <= 4) {
+
+ // limit detail on low memory machines:
+ if (detail > 3 && MachineInfo::GetTotalRam() < 64)
+ detail = 3;
+
+ detail_level = detail;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Terrain::IsFirstPatch(TerrainPatch* p) const
+{
+ return (patches && *patches == p);
+} \ No newline at end of file
diff --git a/Stars45/Terrain.h b/Stars45/Terrain.h
new file mode 100644
index 0000000..036dab4
--- /dev/null
+++ b/Stars45/Terrain.h
@@ -0,0 +1,119 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Terrain.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Test pseudo-random terrain heightfield, based on Solid
+*/
+
+#ifndef Terrain_h
+#define Terrain_h
+
+#include "Types.h"
+#include "Graphic.h"
+#include "Geometry.h"
+#include "Bitmap.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Projector;
+class Scene;
+class TerrainApron;
+class TerrainClouds;
+class TerrainLayer;
+class TerrainPatch;
+class TerrainRegion;
+class Water;
+
+// +--------------------------------------------------------------------+
+
+struct Vec3B
+{
+ Vec3B() { }
+ Vec3B(BYTE a, BYTE b, BYTE c) : x(a), y(b), z(c) { }
+
+ BYTE x, y, z;
+};
+
+// +--------------------------------------------------------------------+
+
+class Terrain
+{
+public:
+ Terrain(TerrainRegion* region);
+ virtual ~Terrain();
+
+ virtual void Activate(Scene& scene);
+ virtual void Deactivate(Scene& scene);
+
+ virtual void SelectDetail(Projector* proj);
+ virtual void BuildTerrain();
+ virtual void BuildNormals();
+
+ virtual void ExecFrame(double seconds);
+
+ double Height(double x, double y) const;
+
+ const Vec3B* Normals() const { return terrain_normals; }
+ TerrainRegion* GetRegion() { return region; }
+ double FogFade() const { return fog_fade; }
+
+ Bitmap* Texture() { return terrain_texture; }
+ Bitmap* ApronTexture() { return apron_texture; }
+ Bitmap* WaterTexture() { return water_texture; }
+ Bitmap** EnvironmentTexture() { return env_texture; }
+ Bitmap* TileTexture(int n) { return tiles[n]; }
+ Bitmap* CloudTexture(int n) { return cloud_texture[n]; }
+ Bitmap* ShadeTexture(int n) { return shade_texture[n]; }
+ Bitmap* DetailTexture(int n) { return noise_texture[n]; }
+ Water* GetWater(int level) { return water[level]; }
+ List<TerrainLayer>& GetLayers() { return layers; }
+
+ bool IsFirstPatch(TerrainPatch* p) const;
+
+ static int DetailLevel() { return detail_level; }
+ static void SetDetailLevel(int detail);
+
+protected:
+ TerrainRegion* region;
+ TerrainPatch** patches;
+ TerrainPatch** water_patches;
+ Water** water;
+ TerrainApron** aprons;
+ TerrainClouds** clouds;
+ int nclouds;
+
+ Bitmap terrain_patch;
+ Bitmap terrain_apron;
+ Bitmap* terrain_texture;
+ Bitmap* apron_texture;
+ Bitmap* water_texture;
+ Bitmap* env_texture[6];
+ Bitmap* tiles[256];
+ Bitmap* cloud_texture[2];
+ Bitmap* shade_texture[2];
+ Bitmap* noise_texture[2];
+
+ Vec3B* terrain_normals;
+ List<TerrainLayer> layers;
+
+ Text datapath;
+ double scale;
+ double mtnscale;
+ int subdivisions;
+ int patch_size;
+ DWORD detail_frame;
+ double fog_fade;
+
+ static int detail_level;
+};
+
+#endif Terrain_h
+
diff --git a/Stars45/TerrainApron.cpp b/Stars45/TerrainApron.cpp
new file mode 100644
index 0000000..24125ac
--- /dev/null
+++ b/Stars45/TerrainApron.cpp
@@ -0,0 +1,336 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TerrainApron.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "Terrain.h"
+#include "TerrainApron.h"
+#include "TerrainRegion.h"
+
+#include "CameraView.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Game.h"
+#include "Light.h"
+#include "Scene.h"
+
+// +====================================================================+
+
+const int PATCH_SIZE = 17;
+const int HALF_PATCH_SIZE = 8;
+const int MAX_VERTS = PATCH_SIZE * PATCH_SIZE;
+const int NUM_INDICES_TRI = 3;
+
+// +--------------------------------------------------------------------+
+
+TerrainApron::TerrainApron(Terrain* terr,const Bitmap* patch, const Rect& r,
+ const Point& p1, const Point& p2)
+ : terrain(terr), rect(r)
+{
+ size = fabs(p2.x - p1.x);
+ scale = size / (PATCH_SIZE-1);
+ mtnscale = 1.3 * (p2.y - p1.y);
+ base = p1.y;
+
+ terrain_width = patch->Width();
+
+ loc = (p1 + p2) * 0.5;
+ loc.y = base;
+
+ radius = (float) (size * 0.75);
+ heights = new(__FILE__,__LINE__) float[MAX_VERTS];
+
+ float* pHeight = heights;
+
+ int i, j;
+
+ for (i = 0; i < PATCH_SIZE; i++) {
+ int ty = rect.y + i;
+
+ if (ty < 0)
+ ty = 0;
+
+ if (ty > patch->Height()-1)
+ ty = patch->Height()-1;
+
+ for (j = 0; j < PATCH_SIZE; j++) {
+ int tx = rect.x + (PATCH_SIZE-1 - j);
+
+ if (tx < 0)
+ tx = 0;
+
+ if (tx > patch->Width()-1)
+ tx = patch->Width()-1;
+
+ *pHeight++ = (float) (patch->GetColor(tx,ty).Red() * mtnscale);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+TerrainApron::~TerrainApron()
+{
+ delete [] heights;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TerrainApron::SetScales(double s, double m, double b)
+{
+ scale = s;
+ mtnscale = m;
+ base = b;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+TerrainApron::BuildApron()
+{
+ int i, j;
+
+ int detail_size = PATCH_SIZE-1;
+ int ds1 = PATCH_SIZE;
+ int nverts = MAX_VERTS;
+ int npolys = detail_size * detail_size * 2;
+
+ model = new(__FILE__,__LINE__) Model;
+ model->SetLuminous(true);
+ model->SetDynamic(true);
+
+ Material* mtl = new(__FILE__,__LINE__) Material;
+ mtl->Ka = ColorValue(0.5f, 0.5f, 0.5f);
+ mtl->Kd = ColorValue(0.3f, 0.6f, 0.2f);
+ mtl->Ks = Color::Black;
+
+ mtl->tex_diffuse = terrain->ApronTexture();
+ strcpy(mtl->name, "Terrain Apron");
+
+ model->GetMaterials().append(mtl);
+
+ Surface* s = new(__FILE__,__LINE__) Surface;
+ VertexSet* vset = 0;
+
+ if (s) {
+ s->SetName("default");
+ s->CreateVerts(nverts);
+ s->CreatePolys(npolys);
+ s->AddIndices(npolys*NUM_INDICES_TRI);
+
+ vset = s->GetVertexSet();
+
+ ZeroMemory(vset->loc, nverts * sizeof(Vec3));
+ ZeroMemory(vset->diffuse, nverts * sizeof(DWORD));
+ ZeroMemory(vset->specular, nverts * sizeof(DWORD));
+ ZeroMemory(vset->tu, nverts * sizeof(float));
+ ZeroMemory(vset->tv, nverts * sizeof(float));
+ ZeroMemory(vset->rw, nverts * sizeof(float));
+
+
+ // initialize vertices
+ Vec3* pVert = vset->loc;
+ float* pTu = vset->tu;
+ float* pTv = vset->tv;
+ double dt = (1.0/3.0) / (double) detail_size;
+ double tu0 = (double) rect.x / rect.w / 3.0 + (1.0/3.0);
+ double tv0 = (double) rect.y / rect.h / 3.0;
+
+ for (i = 0; i < ds1; i++) {
+ for (j = 0; j < ds1; j++) {
+ *pVert++ = Vec3((float) (j* scale - (HALF_PATCH_SIZE*scale)),
+ (float) (heights[i*PATCH_SIZE + j]),
+ (float) (i* scale - (HALF_PATCH_SIZE*scale)));
+
+ *pTu++ = (float) (tu0 - j*dt);
+ *pTv++ = (float) (tv0 + i*dt);
+ }
+ }
+
+ // create the polys
+ for (i = 0; i < npolys; i++) {
+ Poly* p = s->GetPolys() + i;
+ p->nverts = 3;
+ p->vertex_set = vset;
+ p->material = mtl;
+ p->visible = 1;
+ p->sortval = 0;
+ }
+
+ int index = 0;
+
+ // build main patch polys:
+ for (i = 0; i < detail_size; i++) {
+ for (j = 0; j < detail_size; j++) {
+ // first triangle
+ Poly* p = s->GetPolys() + index++;
+ p->verts[0] = (ds1 * (i ) + (j ));
+ p->verts[1] = (ds1 * (i ) + (j+1));
+ p->verts[2] = (ds1 * (i+1) + (j+1));
+
+ // second triangle
+ p = s->GetPolys() + index++;
+ p->verts[0] = (ds1 * (i ) + (j ));
+ p->verts[1] = (ds1 * (i+1) + (j+1));
+ p->verts[2] = (ds1 * (i+1) + (j ));
+ }
+ }
+
+ // update the verts and colors of each poly:
+ for (i = 0; i < npolys; i++) {
+ Poly* p = s->GetPolys() + i;
+ Plane& plane = p->plane;
+ WORD* v = p->verts;
+
+ plane = Plane(vset->loc[v[0]] + loc,
+ vset->loc[v[1]] + loc,
+ vset->loc[v[2]] + loc);
+ }
+
+ // create continguous segments for each material:
+ s->Normalize();
+
+ Segment* segment = new(__FILE__,__LINE__) Segment(npolys, s->GetPolys(), mtl, model);
+ s->GetSegments().append(segment);
+
+ model->AddSurface(s);
+
+
+ // copy vertex normals:
+ Vec3 normal = Vec3(0, 1, 0);
+
+ for (i = 0; i < ds1; i++) {
+ for (j = 0; j < ds1; j++) {
+ vset->nrm[i*ds1+j] = normal;
+ }
+ }
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+TerrainApron::CollidesWith(Graphic& o)
+{
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TerrainApron::Update()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TerrainApron::Illuminate(Color ambient, List<Light>& lights)
+{
+ if (!model || model->NumVerts() < 1) return;
+ Surface* s = model->GetSurfaces().first();
+ if (!s) return;
+
+ // clear the solid lights to ambient:
+ VertexSet* vset = s->GetVertexSet();
+ int nverts = vset->nverts;
+ DWORD aval = ambient.Value();
+
+ for (int i = 0; i < nverts; i++) {
+ vset->diffuse[i] = aval;
+ }
+
+ TerrainRegion* trgn = terrain->GetRegion();
+ bool eclipsed = false;
+
+ // for each light:
+ ListIter<Light> iter = lights;
+ while (++iter) {
+ Light* light = iter.value();
+
+ if (light->CastsShadow())
+ eclipsed = light->Location().y < -100;
+
+ if (!light->CastsShadow() || !eclipsed) {
+ Vec3 vl = light->Location();
+ vl.Normalize();
+
+ for (i = 0; i < nverts; i++) {
+ Vec3& nrm = vset->nrm[i];
+ double val = 0;
+
+ if (light->IsDirectional()) {
+ double gain = vl * nrm;
+
+ if (gain > 0) {
+ val = light->Intensity() * (0.85 * gain);
+
+ if (val > 1)
+ val = 1;
+ }
+ }
+
+ if (val > 0.01)
+ vset->diffuse[i] = ((light->GetColor().dim(val)) + vset->diffuse[i]).Value();
+ }
+ }
+ }
+
+ InvalidateSurfaceData();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TerrainApron::Render(Video* video, DWORD flags)
+{
+ if (!video || (flags & RENDER_ADDITIVE) || (flags & RENDER_ADD_LIGHT)) return;
+
+ if (!model)
+ BuildApron();
+
+ if (scene) {
+ Illuminate(scene->Ambient(), scene->Lights());
+ }
+
+ double visibility = terrain->GetRegion()->GetWeather().Visibility();
+ FLOAT fog_density = (FLOAT) (terrain->GetRegion()->FogDensity() * 2.5e-5 * 1/visibility);
+
+ video->SetRenderState(Video::LIGHTING_ENABLE, false);
+ video->SetRenderState(Video::SPECULAR_ENABLE, false);
+ video->SetRenderState(Video::FOG_ENABLE, true);
+ video->SetRenderState(Video::FOG_COLOR, terrain->GetRegion()->FogColor().Value());
+ video->SetRenderState(Video::FOG_DENSITY, *((DWORD*) &fog_density));
+
+ Solid::Render(video, flags);
+
+ video->SetRenderState(Video::LIGHTING_ENABLE, true);
+ video->SetRenderState(Video::SPECULAR_ENABLE, true);
+ video->SetRenderState(Video::FOG_ENABLE, false);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+TerrainApron::CheckRayIntersection(Point Q, Point w, double len, Point& ipt, bool ttpas)
+{
+ // compute leading edge of ray:
+ Point sun = Q + w*len;
+
+ if (sun.y < loc.y)
+ return 1;
+
+ return 0;
+}
diff --git a/Stars45/TerrainApron.h b/Stars45/TerrainApron.h
new file mode 100644
index 0000000..dd2e4fd
--- /dev/null
+++ b/Stars45/TerrainApron.h
@@ -0,0 +1,71 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TerrainApron.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ A Single Edge Section of a Terrain Object
+*/
+
+#ifndef TerrainApron_h
+#define TerrainApron_h
+
+#include "Types.h"
+#include "Solid.h"
+
+// +--------------------------------------------------------------------+
+
+class Terrain;
+
+// +--------------------------------------------------------------------+
+
+class TerrainApron : public Solid
+{
+public:
+ TerrainApron(Terrain* terrain,
+ const Bitmap* patch, const Rect& rect,
+ const Point& p1, const Point& p2);
+ virtual ~TerrainApron();
+
+ virtual void Render(Video* video, DWORD flags);
+ virtual void Update();
+
+ virtual int CollidesWith(Graphic& o);
+ virtual bool Luminous() const { return false; }
+ virtual bool Translucent() const { return false; }
+
+ // accessors:
+ double Scale() const { return scale; }
+ double MountainScale() const { return mtnscale; }
+ double SeaLevel() const { return base; }
+ void SetScales(double scale, double mtnscale, double base);
+
+ void Illuminate(Color ambient, List<Light>& lights);
+ virtual int CheckRayIntersection(Point pt, Point vpn, double len, Point& ipt,
+ bool treat_translucent_polys_as_solid=true);
+
+protected:
+ virtual bool BuildApron();
+
+ Terrain* terrain;
+ int nverts;
+ int npolys;
+ int terrain_width;
+
+ Rect rect;
+ float* heights;
+
+ double scale;
+ double mtnscale;
+ double base;
+ double size;
+};
+
+
+#endif TerrainApron_h
+
diff --git a/Stars45/TerrainClouds.cpp b/Stars45/TerrainClouds.cpp
new file mode 100644
index 0000000..49f7113
--- /dev/null
+++ b/Stars45/TerrainClouds.cpp
@@ -0,0 +1,269 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TerrainClouds.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "TerrainClouds.h"
+#include "Terrain.h"
+#include "TerrainRegion.h"
+
+#include "Light.h"
+#include "CameraView.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Game.h"
+#include "Fix.h"
+#include "Scene.h"
+
+// +--------------------------------------------------------------------+
+
+TerrainClouds::TerrainClouds(Terrain* terr, int t)
+ : terrain(terr), type(t)
+{
+ nverts = 0;
+ npolys = 0;
+ mverts = 0;
+ verts = 0;
+ polys = 0;
+
+ loc = Point(0, 15000, 0);
+ radius = (float) (25000.0f);
+
+ BuildClouds();
+}
+
+// +--------------------------------------------------------------------+
+
+TerrainClouds::~TerrainClouds()
+{
+ delete [] mverts;
+ delete verts;
+ delete [] polys;
+}
+
+// +--------------------------------------------------------------------+
+
+static const double BANK_SIZE = 20000;
+static const int CIRRUS_BANKS = 4;
+static const int CUMULUS_BANKS = 4;
+
+void
+TerrainClouds::BuildClouds()
+{
+ if (type == 0) {
+ nbanks = CIRRUS_BANKS;
+ nverts = 4 * nbanks;
+ npolys = 2 * nbanks;
+ }
+ else {
+ nbanks = CUMULUS_BANKS;
+ nverts = 8 * nbanks;
+ npolys = 3 * nbanks;
+ }
+
+ Bitmap* cloud_texture = terrain->CloudTexture(type);
+ Bitmap* shade_texture = terrain->ShadeTexture(type);
+
+ strcpy(mtl_cloud.name, "cloud");
+ mtl_cloud.Ka = Color::White;
+ mtl_cloud.Kd = Color::White;
+ mtl_cloud.luminous = true;
+ mtl_cloud.blend = Material::MTL_TRANSLUCENT;
+ mtl_cloud.tex_diffuse = cloud_texture;
+
+ strcpy(mtl_shade.name, "shade");
+ mtl_shade.Ka = Color::White;
+ mtl_shade.Kd = Color::White;
+ mtl_shade.luminous = true;
+ mtl_shade.blend = Material::MTL_TRANSLUCENT;
+ mtl_shade.tex_diffuse = shade_texture;
+
+ verts = new(__FILE__,__LINE__) VertexSet(nverts);
+ mverts = new(__FILE__,__LINE__) Vec3[nverts];
+ polys = new(__FILE__,__LINE__) Poly[npolys];
+
+ verts->nverts = nverts;
+
+ // initialize vertices
+ Vec3* pVert = mverts;
+ float* pTu = verts->tu;
+ float* pTv = verts->tv;
+
+ int i, j, n;
+ double az = 0;
+ double r = 0;
+
+ for (n = 0; n < nbanks; n++) {
+ double xloc = r * cos(az);
+ double yloc = r * sin(az);
+ double alt = rand() / 32.768;
+
+ for (i = 0; i < 2; i++) {
+ for (j = 0; j < 2; j++) {
+ *pVert = Vec3((float) ((2*j-1) * BANK_SIZE + xloc),
+ (float) (alt),
+ (float) ((2*i-1) * BANK_SIZE + yloc));
+
+ *pTu++ = (float) (-j);
+ *pTv++ = (float) ( i);
+
+ float dist = pVert->length();
+ if (dist > radius)
+ radius = dist;
+
+ pVert++;
+ }
+ }
+
+ if (type > 0) {
+ for (i = 0; i < 2; i++) {
+ for (j = 0; j < 2; j++) {
+ *pVert = Vec3((float) ((2*j-1) * BANK_SIZE + xloc),
+ (float) (alt-100),
+ (float) ((2*i-1) * BANK_SIZE + yloc));
+
+ *pTu++ = (float) (-j);
+ *pTv++ = (float) ( i);
+
+ float dist = pVert->length();
+ if (dist > radius)
+ radius = dist;
+
+ pVert++;
+ }
+ }
+ }
+
+ az += (0.66 + rand()/32768.0) * 0.25 * PI;
+
+ if (r < BANK_SIZE)
+ r += BANK_SIZE;
+ else if (r < 1.75*BANK_SIZE)
+ r += BANK_SIZE/4;
+ else
+ r += BANK_SIZE/8;
+ }
+
+ // create the polys
+ for (i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+ p->nverts = 4;
+ p->vertex_set = verts;
+ p->material = (i<4*nbanks) ? &mtl_cloud : &mtl_shade;
+ p->visible = 1;
+ p->sortval = (i<4*nbanks) ? 1 : 2;
+ }
+
+ // build main patch polys: (facing down)
+ Poly* p = polys;
+
+ int stride = 4;
+ if (type > 0)
+ stride = 8;
+
+ // clouds:
+ for (n = 0; n < nbanks; n++) {
+ p->verts[0] = 0 + n*stride;
+ p->verts[1] = 1 + n*stride;
+ p->verts[2] = 3 + n*stride;
+ p->verts[3] = 2 + n*stride;
+ p++;
+
+ // reverse side: (facing up)
+ p->verts[0] = 0 + n*stride;
+ p->verts[3] = 1 + n*stride;
+ p->verts[2] = 3 + n*stride;
+ p->verts[1] = 2 + n*stride;
+ p++;
+ }
+
+ // shades:
+ if (type > 0) {
+ for (n = 0; n < nbanks; n++) {
+ p->verts[0] = 4 + n*stride;
+ p->verts[1] = 5 + n*stride;
+ p->verts[2] = 7 + n*stride;
+ p->verts[3] = 6 + n*stride;
+ p++;
+ }
+ }
+
+ // update the verts and colors of each poly:
+ for (i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+ WORD* v = p->verts;
+
+ p->plane = Plane(mverts[v[0]],
+ mverts[v[1]],
+ mverts[v[2]]);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TerrainClouds::Update()
+{
+ if (!nverts || !mverts || !verts)
+ return;
+
+ for (int i = 0; i < nverts; ++i)
+ verts->loc[i] = mverts[i] + loc;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TerrainClouds::Illuminate(Color ambient, List<Light>& lights)
+{
+ int i, n;
+
+ if (terrain) {
+ DWORD cloud_color = terrain->GetRegion()->CloudColor().Value() | Color(0,0,0,255).Value();
+ DWORD shade_color = terrain->GetRegion()->ShadeColor().Value() | Color(0,0,0,255).Value();
+
+ int stride = 4;
+ if (type > 0)
+ stride = 8;
+
+ for (i = 0; i < nbanks; i++) {
+ for (n = 0; n < 4; n++) {
+ verts->diffuse[stride*i + n] = cloud_color;
+ verts->specular[stride*i + n] = 0xff000000;
+ }
+
+ if (type > 0) {
+ for (; n < 8; n++) {
+ verts->diffuse[stride*i + n] = shade_color;
+ verts->specular[stride*i + n] = 0xff000000;
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TerrainClouds::Render(Video* video, DWORD flags)
+{
+ if ((flags & Graphic::RENDER_ALPHA) == 0)
+ return;
+
+ if (video && life && polys && npolys && verts) {
+ if (scene)
+ Illuminate(scene->Ambient(), scene->Lights());
+
+ video->SetRenderState(Video::FOG_ENABLE, false);
+ video->DrawPolys(npolys, polys);
+ }
+}
diff --git a/Stars45/TerrainClouds.h b/Stars45/TerrainClouds.h
new file mode 100644
index 0000000..5cf7466
--- /dev/null
+++ b/Stars45/TerrainClouds.h
@@ -0,0 +1,64 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TerrainClouds.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ A Single Edge Section of a Terrain Object
+*/
+
+#ifndef TerrainClouds_h
+#define TerrainClouds_h
+
+#include "Types.h"
+#include "Graphic.h"
+#include "Geometry.h"
+#include "Polygon.h"
+
+// +--------------------------------------------------------------------+
+
+class Terrain;
+class TerrainRegion;
+
+// +--------------------------------------------------------------------+
+
+class TerrainClouds : public Graphic
+{
+public:
+ TerrainClouds(Terrain* terr, int type);
+ virtual ~TerrainClouds();
+
+ virtual void Render(Video* video, DWORD flags);
+ virtual void Update();
+
+ // accessors:
+ virtual int CollidesWith(Graphic& o) { return 0; }
+ virtual bool Luminous() const { return true; }
+ virtual bool Translucent() const { return true; }
+
+ void Illuminate(Color ambient, List<Light>& lights);
+
+protected:
+ void BuildClouds();
+
+ Terrain* terrain;
+ Vec3* mverts;
+ VertexSet* verts;
+ Poly* polys;
+ Material mtl_cloud;
+ Material mtl_shade;
+
+ int type;
+ int nbanks;
+ int nverts;
+ int npolys;
+};
+
+
+#endif TerrainClouds_h
+
diff --git a/Stars45/TerrainHaze.cpp b/Stars45/TerrainHaze.cpp
new file mode 100644
index 0000000..9f32e68
--- /dev/null
+++ b/Stars45/TerrainHaze.cpp
@@ -0,0 +1,90 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TerrainHaze.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "Terrain.h"
+#include "TerrainHaze.h"
+#include "TerrainRegion.h"
+
+#include "Light.h"
+#include "CameraView.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Game.h"
+
+// +====================================================================+
+
+static Bitmap terrain_texture;
+
+// +--------------------------------------------------------------------+
+
+TerrainHaze::TerrainHaze()
+ : tregion(0)
+{
+}
+
+// +--------------------------------------------------------------------+
+
+TerrainHaze::~TerrainHaze()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TerrainHaze::Render(Video* video, DWORD flags)
+{
+ if (flags & RENDER_ADDITIVE)
+ return;
+
+ if (model) {
+ if (!Luminous()) {
+ SetLuminous(true);
+ model->SetDynamic(true);
+ }
+
+ Surface* surface = model->GetSurfaces().first();
+
+ if (!surface) return;
+
+ int i;
+ DWORD sky = 0;
+ DWORD fog = 0;
+
+ if (tregion) {
+ sky = tregion->SkyColor().Value();
+ fog = tregion->FogColor().Value();
+ }
+
+ // clear the solid lights to ambient:
+ VertexSet* vset = surface->GetVertexSet();
+
+ for (i = 0; i < vset->nverts; i++) {
+ if (vset->loc[i].y > 0)
+ vset->diffuse[i] = sky;
+ else
+ vset->diffuse[i] = fog;
+ }
+
+ InvalidateSurfaceData();
+ Solid::Render(video, flags);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+TerrainHaze::CheckRayIntersection(Point Q, Point w, double len, Point& ipt, bool ttpas)
+{
+ return 0;
+}
diff --git a/Stars45/TerrainHaze.h b/Stars45/TerrainHaze.h
new file mode 100644
index 0000000..3c8bcb6
--- /dev/null
+++ b/Stars45/TerrainHaze.h
@@ -0,0 +1,46 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TerrainHaze.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Atmospheric fog along the horizon
+*/
+
+#ifndef TerrainHaze_h
+#define TerrainHaze_h
+
+#include "Types.h"
+#include "Solid.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class TerrainRegion;
+
+// +--------------------------------------------------------------------+
+
+class TerrainHaze : public Solid
+{
+public:
+ TerrainHaze();
+ virtual ~TerrainHaze();
+
+ virtual void Render(Video* video, DWORD flags);
+
+ virtual int CheckRayIntersection(Point pt, Point vpn, double len, Point& ipt,
+ bool treat_translucent_polys_as_solid=true);
+
+ virtual void UseTerrainRegion(TerrainRegion* tr) { tregion = tr; }
+
+protected:
+ TerrainRegion* tregion;
+};
+
+#endif TerrainHaze_h
+
diff --git a/Stars45/TerrainLayer.h b/Stars45/TerrainLayer.h
new file mode 100644
index 0000000..aff6718
--- /dev/null
+++ b/Stars45/TerrainLayer.h
@@ -0,0 +1,63 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TerrainLayer.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ A blended detail texture applied to a terrain patch
+ through a specific range of altitudes
+*/
+
+#ifndef TerrainLayer_h
+#define TerrainLayer_h
+
+#include "Types.h"
+#include "Bitmap.h"
+
+// +--------------------------------------------------------------------+
+
+class Terrain;
+class TerrainRegion;
+
+// +--------------------------------------------------------------------+
+
+class TerrainLayer
+{
+ friend class Terrain;
+ friend class TerrainRegion;
+
+public:
+ static const char* TYPENAME() { return "TerrainLayer"; }
+
+ TerrainLayer() : tile_texture(0), detail_texture(0), min_height(0), max_height(-1) { }
+ ~TerrainLayer() { }
+
+ int operator < (const TerrainLayer& t) const { return min_height < t.min_height; }
+ int operator <= (const TerrainLayer& t) const { return min_height <= t.min_height; }
+ int operator == (const TerrainLayer& t) const { return min_height == t.min_height; }
+
+ // accessors:
+ const char* GetTileName() const { return tile_name; }
+ const char* GetDetailName() const { return detail_name; }
+ Bitmap* GetTileTexture() const { return tile_texture; }
+ Bitmap* GetDetailTexture() const { return detail_texture; }
+ double GetMinHeight() const { return min_height; }
+ double GetMaxHeight() const { return max_height; }
+
+private:
+ Text tile_name;
+ Text detail_name;
+ Bitmap* tile_texture;
+ Bitmap* detail_texture;
+ double min_height;
+ double max_height;
+};
+
+
+#endif TerrainLayer_h
+
diff --git a/Stars45/TerrainPatch.cpp b/Stars45/TerrainPatch.cpp
new file mode 100644
index 0000000..bb8718a
--- /dev/null
+++ b/Stars45/TerrainPatch.cpp
@@ -0,0 +1,1113 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TerrainPatch.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "Terrain.h"
+#include "TerrainLayer.h"
+#include "TerrainPatch.h"
+#include "TerrainRegion.h"
+#include "Sim.h"
+
+#include "Light.h"
+#include "CameraView.h"
+#include "Projector.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Game.h"
+#include "Scene.h"
+#include "Water.h"
+
+// +====================================================================+
+
+// #define DEBUG_DETAIL 1
+
+// +====================================================================+
+
+const int MAX_DETAIL = 4;
+const int PATCH_SIZE = 17;
+const int HALF_PATCH_SIZE = 8;
+const int MAX_VERTS = PATCH_SIZE * PATCH_SIZE;
+
+static bool illuminating = false;
+
+// +--------------------------------------------------------------------+
+
+TerrainPatch::TerrainPatch(Terrain* terr,const Bitmap* patch, const Rect& r,
+ const Point& p1, const Point& p2)
+ : ndetail(0), terrain(terr), rect(r), water(0), min_height(1e9), max_height(-1e9)
+{
+ luminous = true; // we will do our own lighting
+ own_model = false; // manage the model lifetimes in this derived class
+
+ max_detail = Terrain::DetailLevel();
+ scale = fabs(p1.x - p2.x) / (PATCH_SIZE-1);
+ mtnscale = p2.y - p1.y;
+ base = p1.y;
+ size = p2.x - p1.x;
+
+ ZeroMemory(detail_levels, sizeof(detail_levels));
+
+ terrain_width = patch->Width();
+
+ loc = (p1 + p2) * 0.5;
+ loc.y = base;
+
+ radius = (float) (size * 0.75);
+ heights = new(__FILE__,__LINE__) float[MAX_VERTS];
+
+ float* pHeight = heights;
+ int tscale = rect.w / (PATCH_SIZE-1);
+ int i, j;
+
+ for (i = 0; i < PATCH_SIZE; i++) {
+ int ty = rect.y + i * tscale;
+
+ if (ty < 0)
+ ty = 0;
+
+ if (ty > patch->Height()-1)
+ ty = patch->Height()-1;
+
+ for (j = 0; j < PATCH_SIZE; j++) {
+ int tx = rect.x + (PATCH_SIZE-1 - j) * tscale;
+
+ if (tx < 0)
+ tx = 0;
+
+ if (tx > patch->Width()-1)
+ tx = patch->Width()-1;
+
+ int red = patch->GetColor(tx,ty).Red();
+ float alt = (float) (red * mtnscale);
+
+ if (alt < min_height)
+ min_height = alt;
+ if (alt > max_height)
+ max_height = alt;
+
+ if (terrain->WaterTexture() && red < 2)
+ alt = -5000.0f;
+
+ *pHeight++ = alt;
+ }
+ }
+
+ Material* mtl = new(__FILE__,__LINE__) Material;
+ mtl->Ka = ColorValue(0.5f, 0.5f, 0.5f);
+ mtl->Kd = ColorValue(0.3f, 0.6f, 0.2f);
+ mtl->Ks = Color::Black;
+
+ mtl->tex_diffuse = terrain->Texture();
+
+ materials.append(mtl);
+
+ List<TerrainLayer>& layers = terrain->GetLayers();
+ for (i = 0; i < layers.size(); i++) {
+ Bitmap* tex0 = layers.at(i)->GetTileTexture();
+ Bitmap* tex1 = 0;
+ Bitmap* texd = layers.at(i)->GetDetailTexture();
+
+ if (i < layers.size()-1)
+ tex1 = layers.at(i+1)->GetTileTexture();
+
+ if (!texd)
+ texd = terrain->DetailTexture(0);
+
+ mtl = new(__FILE__,__LINE__) Material;
+ mtl->Ka = ColorValue(0.5f, 0.5f, 0.5f);
+ mtl->Kd = ColorValue(0.3f, 0.6f, 0.2f);
+ mtl->Ks = Color::Black;
+
+ if ((i & 1) != 0) {
+ mtl->tex_diffuse = tex1;
+ mtl->tex_alternate = tex0;
+ }
+ else {
+ mtl->tex_diffuse = tex0;
+ mtl->tex_alternate = tex1;
+ }
+
+ mtl->tex_detail = texd;
+
+ materials.append(mtl);
+ }
+
+ for (i = 0; i <= max_detail; i++)
+ BuildDetailLevel(i);
+
+ model = detail_levels[1];
+}
+
+// +--------------------------------------------------------------------+
+
+TerrainPatch::TerrainPatch(Terrain* terr,
+ const Rect& r,
+ const Point& p1,
+ const Point& p2,
+ double sea_level)
+ : ndetail(0), terrain(terr), rect(r), water(0)
+{
+ luminous = true; // water is lit by the graphics engine
+ own_model = false; // manage the model lifetimes in this derived class
+
+ max_detail = Terrain::DetailLevel();
+ scale = fabs(p1.x - p2.x) / (PATCH_SIZE-1);
+ mtnscale = 0;
+ base = sea_level;
+ size = p2.x - p1.x;
+
+ ZeroMemory(detail_levels, sizeof(detail_levels));
+
+ terrain_width = 0;
+
+ loc = (p1 + p2) * 0.5;
+ loc.y = base;
+
+ radius = (float) (size * 0.75);
+ heights = new(__FILE__,__LINE__) float[MAX_VERTS];
+
+ float* pHeight = heights;
+ int i, j;
+
+ for (i = 0; i < PATCH_SIZE; i++) {
+ for (j = 0; j < PATCH_SIZE; j++) {
+ *pHeight++ = (float) sea_level;
+ }
+ }
+
+ Material* mtl = new(__FILE__,__LINE__) Material;
+ mtl->Ka = Color::Gray;
+ mtl->Kd = Color::White;
+ mtl->Ks = Color::White;
+ mtl->power = 30.0f;
+ mtl->shadow = false;
+ mtl->tex_diffuse = terrain->WaterTexture();
+ //strcpy(mtl->shader, "WaterReflections");
+ materials.append(mtl);
+
+ water = terrain->GetWater(1);
+
+ for (i = 0; i <= max_detail; i++)
+ BuildDetailLevel(i);
+
+ model = detail_levels[1];
+}
+
+// +--------------------------------------------------------------------+
+
+TerrainPatch::~TerrainPatch()
+{
+ delete [] heights;
+
+ for (int i = 0; i < MAX_LOD; i++) {
+ Model* m = detail_levels[i];
+
+ if (m) {
+ m->GetMaterials().clear();
+ delete m;
+ }
+ }
+
+ materials.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TerrainPatch::SetScales(double s, double m, double b)
+{
+ scale = s;
+ mtnscale = m;
+ base = b;
+}
+
+// +--------------------------------------------------------------------+
+
+static int mcomp(const void* a, const void* b)
+{
+ Poly* pa = (Poly*) a;
+ Poly* pb = (Poly*) b;
+
+ if (pa->sortval == pb->sortval)
+ return 0;
+
+ if (pa->sortval < pb->sortval)
+ return 1;
+
+ return -1;
+}
+
+
+static void bisect(VertexSet* vset, int v[4])
+{
+ double d1 = fabs(vset->loc[ v[0] ].y -
+ vset->loc[ v[3] ].y);
+
+ double d2 = fabs(vset->loc[ v[1] ].y -
+ vset->loc[ v[2] ].y);
+
+ if (d2 < 0.7*d1) {
+ int odd[4] = { v[1], v[3], v[0], v[2] };
+ for (int i = 0; i < 4; i++)
+ v[i] = odd[i];
+ }
+}
+
+bool
+TerrainPatch::BuildDetailLevel(int level)
+{
+ int i, j;
+
+ int detail_size = 1 << level;
+ int ds1 = detail_size+1;
+
+ if (detail_size > PATCH_SIZE)
+ return false;
+
+ Model* model = new(__FILE__,__LINE__) Model;
+ detail_levels[level] = model;
+
+ model->SetLuminous(luminous);
+ model->SetDynamic(true);
+
+ const int NUM_STRIPS = 4;
+ const int NUM_INDICES_TRI = 3;
+ const int NUM_INDICES_QUAD = 6;
+
+ int nverts = ds1*ds1 + ds1*2*NUM_STRIPS;
+ int npolys = detail_size*detail_size*2;
+ int strip_len = detail_size;
+ int total = npolys + strip_len*NUM_STRIPS;
+
+ if (water) {
+ nverts = ds1*ds1;
+ strip_len = 0;
+ total = npolys;
+ }
+
+ Surface* s = new(__FILE__,__LINE__) Surface;
+ VertexSet* vset = 0;
+
+ if (s) {
+ s->SetName("default");
+ s->CreateVerts(nverts);
+ s->CreatePolys(total);
+ s->AddIndices(npolys*NUM_INDICES_TRI + strip_len*NUM_STRIPS*NUM_INDICES_QUAD);
+
+ vset = s->GetVertexSet();
+ if (!water)
+ vset->CreateAdditionalTexCoords();
+
+ ZeroMemory(vset->loc, nverts * sizeof(Vec3));
+ ZeroMemory(vset->diffuse, nverts * sizeof(DWORD));
+ ZeroMemory(vset->specular, nverts * sizeof(DWORD));
+ ZeroMemory(vset->tu, nverts * sizeof(float));
+ ZeroMemory(vset->tv, nverts * sizeof(float));
+ if (!water) {
+ ZeroMemory(vset->tu1, nverts * sizeof(float));
+ ZeroMemory(vset->tv1, nverts * sizeof(float));
+ }
+ ZeroMemory(vset->rw, nverts * sizeof(float));
+
+ // initialize vertices
+ Vec3* pVert = vset->loc;
+ float* pTu = vset->tu;
+ float* pTv = vset->tv;
+ float* pTu1 = vset->tu1;
+ float* pTv1 = vset->tv1;
+ DWORD* pSpec = vset->specular;
+
+ int dscale = (PATCH_SIZE-1)/detail_size;
+ double dt = 0.0625 / (ds1-1); // terrain texture scale
+ double dtt = 2.0000 / (ds1-1); // tile texture scale
+ double tu0 = (double) rect.x / rect.w / 16.0 + 1.0/16.0;
+ double tv0 = (double) rect.y / rect.h / 16.0;
+
+ // surface verts
+ for (i = 0; i < ds1; i++) {
+ for (j = 0; j < ds1; j++) {
+ *pVert = Vec3((float) (j* scale * dscale - (HALF_PATCH_SIZE*scale)),
+ (float) (heights[i*dscale*PATCH_SIZE + j*dscale]),
+ (float) (i* scale * dscale - (HALF_PATCH_SIZE*scale)));
+
+ if (level >= 2) {
+ *pTu++ = (float) (-j*dtt);
+ *pTv++ = (float) ( i*dtt);
+
+ if (level >= 4 && !water) {
+ *pTu1++ = (float) (-j*dtt*3);
+ *pTv1++ = (float) ( i*dtt*3);
+ }
+
+ *pSpec++ = BlendValue(pVert->y);
+ }
+
+ else {
+ *pTu++ = (float) (tu0 - j*dt);
+ *pTv++ = (float) (tv0 + i*dt);
+ }
+
+ pVert++;
+ }
+ }
+
+ if (!water) {
+ // strip 1 & 2 verts
+ for (i = 0; i < ds1; i += detail_size) {
+ for (j = 0; j < ds1; j++) {
+ Vec3 vl = Vec3((float) (j* scale * dscale - (HALF_PATCH_SIZE*scale)),
+ (float) (heights[i*dscale*PATCH_SIZE + j*dscale]),
+ (float) (i* scale * dscale - (HALF_PATCH_SIZE*scale)));
+
+ *pVert++ = vl;
+
+ DWORD blend = 0;
+
+ if (level >= 2) {
+ blend = BlendValue(vl.y);
+
+ *pSpec++ = blend;
+ *pTu++ = (float) (-j*dtt);
+ *pTv++ = (float) ( i*dtt);
+ }
+
+ else {
+ *pTu++ = (float) (tu0 - j*dt);
+ *pTv++ = (float) (tv0 + i*dt);
+ }
+
+ vl.y = -5000.0f;
+
+ *pVert++ = vl;
+
+ if (level >= 2) {
+ *pSpec++ = blend;
+ *pTu++ = (float) (-j*dtt);
+ *pTv++ = (float) ( i*dtt);
+ }
+
+ else {
+ *pTu++ = (float) (tu0 - j*dt);
+ *pTv++ = (float) (tv0 + i*dt);
+ }
+ }
+ }
+
+ // strip 3 & 4 verts
+ for (j = 0; j < ds1; j += detail_size) {
+ for (i = 0; i < ds1; i++) {
+ Vec3 vl = Vec3((float) (j* scale * dscale - (HALF_PATCH_SIZE*scale)),
+ (float) (heights[i*dscale*PATCH_SIZE + j*dscale]),
+ (float) (i* scale * dscale - (HALF_PATCH_SIZE*scale)));
+
+ *pVert++ = vl;
+
+ DWORD blend = 0;
+
+ if (level >= 2) {
+ blend = BlendValue(vl.y);
+
+ *pSpec++ = blend;
+ *pTu++ = (float) (-j*dtt);
+ *pTv++ = (float) ( i*dtt);
+ }
+
+ else {
+ *pTu++ = (float) (tu0 - j*dt);
+ *pTv++ = (float) (tv0 + i*dt);
+ }
+
+ vl.y = -5000.0f;
+
+ *pVert++ = vl;
+
+ if (level >= 2) {
+ *pSpec++ = blend;
+ *pTu++ = (float) (-j*dtt);
+ *pTv++ = (float) ( i*dtt);
+ }
+
+ else {
+ *pTu++ = (float) (tu0 - j*dt);
+ *pTv++ = (float) (tv0 + i*dt);
+ }
+ }
+ }
+ }
+
+ Material* m = materials.first();
+
+ // initialize the polys
+ for (i = 0; i < npolys; i++) {
+ Poly* p = s->GetPolys() + i;
+ p->nverts = 3;
+ p->vertex_set = vset;
+ p->visible = 1;
+ p->sortval = 0;
+ p->material = m;
+
+ if (level >= 2 && !water) {
+ p->material = materials.at(1);
+ p->sortval = 1;
+ }
+ }
+
+ for (i = npolys; i < total; i++) {
+ Poly* p = s->GetPolys() + i;
+ p->nverts = 4;
+ p->vertex_set = vset;
+ p->visible = 1;
+ p->sortval = 0;
+ p->material = m;
+ }
+
+ int index = 0;
+
+ // build main patch polys:
+ for (i = 0; i < detail_size; i++) {
+ for (j = 0; j < detail_size; j++) {
+ int v[4] = {
+ (ds1 * (i ) + (j )),
+ (ds1 * (i ) + (j+1)),
+ (ds1 * (i+1) + (j )),
+ (ds1 * (i+1) + (j+1)) };
+
+ bisect(vset, v);
+
+ // first triangle
+ Poly* p = s->GetPolys() + index++;
+ p->verts[0] = v[0];
+ p->verts[1] = v[1];
+ p->verts[2] = v[3];
+
+ if (level >= 2 && !water) {
+ int layer = CalcLayer(p) + 1;
+ p->material = materials.at(layer);
+ p->sortval = layer;
+ }
+
+ // second triangle
+ p = s->GetPolys() + index++;
+ p->verts[0] = v[0];
+ p->verts[1] = v[3];
+ p->verts[2] = v[2];
+
+ if (level >= 2 && !water) {
+ int layer = CalcLayer(p) + 1;
+ p->material = materials.at(layer);
+ p->sortval = layer;
+ }
+ }
+ }
+
+ // build vertical edge strip polys:
+
+ if (!water) {
+ for (i = 0; i < NUM_STRIPS; i++) {
+ Poly* p = s->GetPolys() + npolys + i*strip_len;
+ int base_index = ds1*ds1 + ds1*2*i;
+
+ for (j = 0; j < strip_len; j++) {
+ int v = base_index + j * 2;
+ p->nverts = 4;
+
+ if (i == 1 || i == 2) {
+ p->verts[0] = v;
+ p->verts[1] = v+2;
+ p->verts[2] = v+3;
+ p->verts[3] = v+1;
+ }
+
+ else {
+ p->verts[0] = v;
+ p->verts[1] = v+1;
+ p->verts[2] = v+3;
+ p->verts[3] = v+2;
+ }
+
+ if (level >= 2) {
+ int layer = CalcLayer(p) + 1;
+ p->material = materials.at(layer);
+ p->sortval = layer;
+ }
+
+ p++;
+ }
+ }
+ }
+
+ // update the poly planes:
+ for (i = 0; i < total; i++) {
+ Poly* p = s->GetPolys() + i;
+ Plane& plane = p->plane;
+ WORD* v = p->verts;
+
+ plane = Plane(vset->loc[v[0]] + loc,
+ vset->loc[v[1]] + loc,
+ vset->loc[v[2]] + loc);
+ }
+
+ s->Normalize();
+
+ // create continguous segments for each material:
+ // sort the polys by material index:
+ qsort((void*) s->GetPolys(), s->NumPolys(), sizeof(Poly), mcomp);
+
+ // then assign them to cohesive segments:
+ Segment* segment = 0;
+ Poly* spolys = s->GetPolys();
+
+ for (int n = 0; n < s->NumPolys(); n++) {
+ if (segment && segment->material == spolys[n].material) {
+ segment->npolys++;
+ }
+ else {
+ segment = 0;
+ }
+
+ if (!segment) {
+ segment = new(__FILE__,__LINE__) Segment;
+
+ segment->npolys = 1;
+ segment->polys = &spolys[n];
+ segment->material = segment->polys->material;
+ segment->model = model;
+
+ s->GetSegments().append(segment);
+ }
+ }
+
+ Solid::EnableCollision(false);
+ model->AddSurface(s);
+ Solid::EnableCollision(true);
+
+ // copy vertex normals:
+ const Vec3B* tnorms = terrain->Normals();
+
+ for (i = 0; i < ds1; i++) {
+ for (j = 0; j < ds1; j++) {
+
+ if (water) {
+ vset->nrm[i*ds1+j] = Point(0,1,0);
+ }
+
+ // blend adjacent normals:
+ else if (dscale > 1) {
+ Point normal;
+
+ // but don't blend more than 16 normals per vertex:
+ int step = 1;
+ if (dscale > 4)
+ step = dscale / 4;
+
+ for (int dy = -dscale/2; dy < dscale/2; dy += step) {
+ for (int dx = -dscale/2; dx < dscale/2; dx += step) {
+ int ix = rect.x + (ds1-1-j)*dscale + dx;
+ int iy = rect.y + i*dscale + dy;
+
+ if (ix < 0) ix = 0;
+ if (ix > terrain_width-1) ix = terrain_width-1;
+ if (iy < 0) iy = 0;
+ if (iy > terrain_width-1) iy = terrain_width-1;
+
+ Vec3B vbn = tnorms[iy*terrain_width + ix];
+ normal += Point((128-vbn.x)/127.0, (vbn.z-128)/127.0, (vbn.y-128)/127.0);
+ }
+ }
+
+ normal.Normalize();
+ vset->nrm[i*ds1+j] = normal;
+ }
+
+ // just copy the one normal:
+ else {
+ Vec3B vbn = tnorms[(rect.y + i*dscale)*terrain_width + (rect.x + (ds1-1-j) * dscale)];
+ Point normal = Point((128-vbn.x)/127.0, (vbn.z-128)/127.0, (vbn.y-128)/127.0);
+ vset->nrm[i*ds1+j] = normal;
+ }
+ }
+ }
+
+ if (!water) {
+ pVert = &vset->nrm[ds1*ds1];
+
+ // strip 1 & 2 verts
+ for (i = 0; i < ds1; i += detail_size) {
+ for (j = 0; j < ds1; j++) {
+ Vec3 vn = vset->nrm[i*ds1 + j];
+
+ *pVert++ = vn;
+ *pVert++ = vn;
+ }
+ }
+
+ // strip 3 & 4 verts
+ for (j = 0; j < ds1; j += detail_size) {
+ for (i = 0; i < ds1; i++) {
+ Vec3 vn = vset->nrm[i*ds1 + j];
+
+ *pVert++ = vn;
+ *pVert++ = vn;
+ }
+ }
+ }
+ }
+
+ if (level > max_detail)
+ max_detail = level;
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+TerrainPatch::BlendValue(double y)
+{
+ if (terrain && y >= 0 && !water) {
+ // find the proper layer:
+ for (int i = 0; i < terrain->GetLayers().size(); i++) {
+ TerrainLayer* layer = terrain->GetLayers().at(i);
+
+ if (y >= layer->GetMinHeight() && y < layer->GetMaxHeight()) {
+ double scale = (y-layer->GetMinHeight()) / (layer->GetMaxHeight()-layer->GetMinHeight());
+
+ if (scale < 0.2)
+ scale = 0;
+ else if (scale > 0.8)
+ scale = 1;
+ else
+ scale = (scale - 0.2) / 0.6;
+
+ if ((i & 1) == 0) {
+ scale = 1 - scale;
+ }
+
+ DWORD val = (DWORD) (scale*255);
+
+ return val << 24;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int
+TerrainPatch::CalcLayer(Poly* poly)
+{
+ if (terrain && poly) {
+ if (water)
+ return 0;
+
+ double y = 1e6;
+
+ for (int i = 0; i < poly->nverts; i++) {
+ double h = poly->vertex_set->loc[ poly->verts[i] ].y;
+
+ if (h >= 0 && h < y) {
+ y = h;
+ }
+ }
+
+ if (y <= terrain->GetLayers().first()->GetMinHeight())
+ return 0;
+
+ for (i = 0; i < terrain->GetLayers().size(); i++) {
+ TerrainLayer* layer = terrain->GetLayers().at(i);
+
+ if (y >= layer->GetMinHeight() && y < layer->GetMaxHeight()) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TerrainPatch::UpdateSurfaceWaves(Vec3& eyePos)
+{
+ if (water && model && model->NumVerts() > 1) {
+ Surface* s = model->GetSurfaces().first();
+ if (s) {
+ VertexSet* vset = s->GetVertexSet();
+ for (int i = 0; i < vset->nverts; i++)
+ vset->loc[i].y = 0.0f;
+
+ water->UpdateSurface(eyePos, vset);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+TerrainPatch::CollidesWith(Graphic& o)
+{
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TerrainPatch::SelectDetail(Projector* projector)
+{
+ // This is where all the work is done,
+ // Delegate to the overall terrain to
+ // compute a detail level for every patch:
+
+ if (terrain) {
+ terrain->SelectDetail(projector);
+ }
+
+ // Build detail levels on demand:
+
+ if (detail_levels[ndetail] == 0)
+ BuildDetailLevel(ndetail);
+}
+
+void
+TerrainPatch::SetDetailLevel(int nd)
+{
+ if (nd >= 0 && nd <= max_detail) {
+ if (ndetail != nd)
+ DeletePrivateData();
+
+ ndetail = nd;
+
+ // build detail levels on demand:
+ if (detail_levels[ndetail] == 0)
+ BuildDetailLevel(ndetail);
+
+ model = detail_levels[ndetail];
+
+ if (water)
+ water = terrain->GetWater(ndetail);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TerrainPatch::Illuminate(Color ambient, List<Light>& lights)
+{
+ if (!model || model->NumVerts() < 1) return;
+ Surface* s = model->GetSurfaces().first();
+ if (!s) return;
+
+ illuminating = true;
+
+ // clear the solid lights to ambient:
+ VertexSet* vset = s->GetVertexSet();
+ int nverts = vset->nverts;
+ DWORD aval = ambient.Value();
+
+ for (int i = 0; i < nverts; i++) {
+ vset->diffuse[i] = aval;
+ }
+
+ TerrainRegion* trgn = terrain->GetRegion();
+ bool eclipsed = false;
+ bool first = terrain->IsFirstPatch(this);
+
+ if (trgn && !first) {
+ eclipsed = trgn->IsEclipsed();
+ }
+
+ // for sun and back lights:
+ ListIter<Light> iter = lights;
+ while (++iter) {
+ Light* light = iter.value();
+
+ if (!light->IsDirectional()) // only do sun and
+ continue; // back lights
+
+ if (light->CastsShadow() && first) {
+ eclipsed = light->Location().y < -100 || // has sun set, or
+
+ scene->IsLightObscured(vset->loc[0], // is sun in eclipse
+ light->Location(), // by orbital body
+ radius); // such as a moon?
+ }
+
+ if (!light->CastsShadow() || !eclipsed) {
+ Vec3 vl = light->Location();
+ vl.Normalize();
+
+ for (i = 0; i < nverts; i++) {
+ Vec3& nrm = vset->nrm[i];
+ double val = 0;
+ double gain = vl * nrm;
+
+ if (gain > 0) {
+ val = light->Intensity() * (0.85 * gain);
+
+ if (val > 1)
+ val = 1;
+ }
+
+ if (val > 0.01)
+ vset->diffuse[i] = ((light->GetColor().dim(val)) + vset->diffuse[i]).Value();
+ }
+ }
+ }
+
+ // combine blend weights:
+ if (ndetail >= 2) {
+ for (i = 0; i < nverts; i++) {
+ vset->diffuse[i] = vset->specular[i] | (vset->diffuse[i] & 0x00ffffff);
+ }
+ }
+
+ if (trgn && first) {
+ trgn->SetEclipsed(eclipsed);
+ }
+
+ InvalidateSurfaceData();
+ illuminating = false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TerrainPatch::Render(Video* video, DWORD flags)
+{
+ if (max_height < 0)
+ return;
+
+ if (flags & RENDER_ADDITIVE)
+ return;
+
+ if (!model)
+ model = detail_levels[0];
+
+ if (scene) {
+ /***
+ *** TWO PASS LIGHTING FOR TERRAIN SHADOWS DOESN'T
+ *** WORK - IT MESSES UP THE HARDWARE FOG CALCS
+ ***
+ *** PLUS, IT'S INCREDIBLY SLOW!!!
+ ***/
+
+ if ((flags & RENDER_ADD_LIGHT) != 0)
+ return;
+
+ if (water) {
+ UpdateSurfaceWaves(Vec3(0,0,0));
+ Illuminate(scene->Ambient(), scene->Lights());
+ }
+ else {
+ Illuminate(scene->Ambient(), scene->Lights());
+ }
+ }
+ else {
+ if ((flags & RENDER_ADD_LIGHT) != 0)
+ return;
+ }
+
+ Bitmap* details[16];
+ ZeroMemory(details, sizeof(details));
+
+ if (ndetail < 3) {
+ for (int i = 0; i < 16 && i < materials.size(); i++) {
+ Material* mtl = materials[i];
+
+ if (mtl->tex_detail) {
+ details[i] = mtl->tex_detail;
+ mtl->tex_detail = 0;
+ }
+ }
+ }
+
+ double visibility = terrain->GetRegion()->GetWeather().Visibility();
+ FLOAT fog_density = (FLOAT) (terrain->GetRegion()->FogDensity() * 2.5e-5 * 1/visibility);
+
+ video->SetRenderState(Video::LIGHTING_ENABLE, false);
+ video->SetRenderState(Video::SPECULAR_ENABLE, false); //water != 0);
+ video->SetRenderState(Video::FOG_ENABLE, true);
+ video->SetRenderState(Video::FOG_COLOR, terrain->GetRegion()->FogColor().Value());
+ video->SetRenderState(Video::FOG_DENSITY, *((DWORD*) &fog_density));
+
+ // Too bad this doesn't work right. But it makes the
+ // ground mostly disappear. :-(
+ //
+ //video->SetRenderState(Video::Z_BIAS, -2);
+
+ Solid::Render(video, flags);
+
+ video->SetRenderState(Video::LIGHTING_ENABLE, true);
+ video->SetRenderState(Video::SPECULAR_ENABLE, true);
+ video->SetRenderState(Video::FOG_ENABLE, false);
+ //video->SetRenderState(Video::Z_BIAS, 0);
+
+ if (ndetail < 3) {
+ for (int i = 0; i < 16 && i < materials.size(); i++) {
+ Material* mtl = materials[i];
+
+ if (details[i] && !mtl->tex_detail) {
+ mtl->tex_detail = details[i];
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+TerrainPatch::CheckRayIntersection(Point obj_pos, Point dir, double len, Point& ipt, bool ttpas)
+{
+ Point light_pos = obj_pos + dir * len;
+ int impact = light_pos.y < -100;
+
+ // Special case for illumination -
+ // just check if sun is above or below the horizon:
+
+ if (illuminating || impact) {
+ return impact;
+ }
+
+ if (obj_pos.x != 0 || obj_pos.y != 0 || obj_pos.z != 0) {
+ return impact;
+ }
+
+ // the rest of this code is only used for eclipsing
+ // the solar lens flare:
+
+ // check right angle spherical distance:
+ Point d0 = loc;
+ Point d1 = d0.cross(dir);
+ double dlen = d1.length(); // distance of point from line
+
+ if (dlen > radius) // clean miss
+ return 0; // (no impact)
+
+ // make sure some part of this patch falls between
+ // the camera and the sun:
+
+ Point closest = loc + dir * radius;
+
+ if (closest * dir < 0)
+ return 0;
+
+ // probable hit at this point...
+ // test for polygon intersection:
+ if (!model)
+ return 0;
+
+ Surface* s = model->GetSurfaces().first();
+
+ if (!s)
+ return 0;
+
+
+ // transform ray into object space:
+ Matrix xform(Orientation());
+
+ Vec3 tmp = dir;
+
+ dir.x = tmp * Vec3(xform(0,0), xform(0,1), xform(0,2));
+ dir.y = tmp * Vec3(xform(1,0), xform(1,1), xform(1,2));
+ dir.z = tmp * Vec3(xform(2,0), xform(2,1), xform(2,2));
+
+ tmp = obj_pos-loc;
+
+ obj_pos.x = tmp * Vec3(xform(0,0), xform(0,1), xform(0,2));
+ obj_pos.y = tmp * Vec3(xform(1,0), xform(1,1), xform(1,2));
+ obj_pos.z = tmp * Vec3(xform(2,0), xform(2,1), xform(2,2));
+
+ double min = 2 * len;
+
+ // check each polygon:
+ Poly* p = s->GetPolys();
+
+ for (int i = 0; i < s->NumPolys(); i++) {
+ Point v = p->plane.normal;
+ double d = p->plane.distance;
+
+ double denom = dir*v;
+
+ if (denom < -1.0e-5) {
+ Point P = v * d;
+ double ilen = ((P-obj_pos)*v)/denom;
+
+ if (ilen > 0 && ilen < min) {
+ Point intersect = obj_pos + dir * ilen;
+
+ if (p->Contains(intersect)) {
+ ipt = intersect;
+ min = ilen;
+ impact = 1;
+ }
+ }
+ }
+
+ p++;
+ }
+
+ // xform impact point back into world coordinates:
+
+ if (impact) {
+ ipt = (ipt * Orientation()) + loc;
+ }
+
+ return impact;
+}
+
+// +--------------------------------------------------------------------+
+
+double
+TerrainPatch::Height(double x, double z) const
+{
+ if (water) return base;
+
+ double height = 0;
+
+ x /= scale;
+ z /= scale;
+
+ int x0 = (int) x;
+ int z0 = (int) z;
+
+ if (x0 >= 0 && x0 < PATCH_SIZE && z0 >= 0 && z0 < PATCH_SIZE) {
+ double dx = x-x0;
+ double dz = z-z0;
+
+ double h[4];
+
+ h[0] = heights[(z0*PATCH_SIZE + x0)];
+ h[1] = heights[((z0+1)*PATCH_SIZE + x0)];
+ h[2] = heights[(z0*PATCH_SIZE + x0+1)];
+ h[3] = heights[((z0+1)*PATCH_SIZE + x0+1)];
+
+ // if level, just return height of one vertex:
+ if (h[0] == h[1] && h[1] == h[2] && h[2] == h[3]) {
+ height = h[0];
+ }
+
+ // if sloped, interpolate between vertex heights:
+ else {
+ double hl = h[0]*(1-dz) + h[1]*dz;
+ double hr = h[2]*(1-dz) + h[3]*dz;
+
+ height = hl*(1-dx) + hr*dx + 5; // fudge
+ }
+ }
+
+ if (height < 0)
+ height = 0;
+
+ return height;
+}
diff --git a/Stars45/TerrainPatch.h b/Stars45/TerrainPatch.h
new file mode 100644
index 0000000..553cf78
--- /dev/null
+++ b/Stars45/TerrainPatch.h
@@ -0,0 +1,94 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TerrainPatch.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ A Single Multi-LOD Section of a Terrain Object
+*/
+
+#ifndef TerrainPatch_h
+#define TerrainPatch_h
+
+#include "Types.h"
+#include "Solid.h"
+#include "Geometry.h"
+#include "Polygon.h"
+
+// +--------------------------------------------------------------------+
+
+class Projector;
+class Terrain;
+class Water;
+
+// +--------------------------------------------------------------------+
+
+class TerrainPatch : public Solid
+{
+public:
+ TerrainPatch(Terrain* terrain,
+ const Bitmap* patch, const Rect& rect,
+ const Point& p1, const Point& p2);
+ TerrainPatch(Terrain* terrain, const Rect& rect,
+ const Point& p1, const Point& p2,
+ double sea_level);
+ virtual ~TerrainPatch();
+
+ virtual void SelectDetail(Projector* projector);
+ virtual void Render(Video* video, DWORD flags);
+
+ virtual int CollidesWith(Graphic& o);
+
+ // accessors:
+ double Scale() const { return scale; }
+ double MountainScale() const { return mtnscale; }
+ double SeaLevel() const { return base; }
+ double MinHeight() const { return min_height; }
+ double MaxHeight() const { return max_height; }
+ bool IsWater() const { return water != 0; }
+
+ void UpdateSurfaceWaves(Vec3& eyePos);
+
+ void SetScales(double scale, double mtnscale, double base);
+ void SetDetailLevel(int nd);
+
+ virtual int CheckRayIntersection(Point pt, Point vpn, double len, Point& ipt,
+ bool treat_translucent_polys_as_solid=true);
+
+ double Height(double x, double y) const;
+ DWORD BlendValue(double y);
+ int CalcLayer(Poly* p);
+ void Illuminate(Color ambient, List<Light>& lights);
+
+protected:
+ virtual bool BuildDetailLevel(int level);
+
+ enum { MAX_LOD=8 };
+
+ Terrain* terrain;
+ int patch_size;
+ int ndetail;
+ int max_detail;
+ int terrain_width;
+
+ Rect rect;
+ float* heights;
+ Model* detail_levels[MAX_LOD];
+ List<Material> materials;
+ Water* water;
+
+ double scale;
+ double mtnscale;
+ double base;
+ double size;
+ float min_height;
+ float max_height;
+};
+
+#endif TerrainPatch_h
+
diff --git a/Stars45/TerrainRegion.cpp b/Stars45/TerrainRegion.cpp
new file mode 100644
index 0000000..17be0bc
--- /dev/null
+++ b/Stars45/TerrainRegion.cpp
@@ -0,0 +1,266 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TerrainRegion.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Various Heavenly Bodies
+*/
+
+#include "MemDebug.h"
+#include "TerrainRegion.h"
+#include "Sky.h"
+#include "TerrainHaze.h"
+#include "TerrainLayer.h"
+#include "Sim.h"
+#include "Grid.h"
+#include "CameraDirector.h"
+#include "HUDView.h"
+
+#include "Game.h"
+#include "Sound.h"
+#include "Solid.h"
+#include "Sprite.h"
+#include "Light.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Scene.h"
+#include "ParseUtil.h"
+
+// +====================================================================+
+
+TerrainRegion::TerrainRegion(StarSystem* s, const char* n, double r, Orbital* p)
+ : OrbitalRegion(s, n, 0.0, r, 0.0, p),
+ scale(10e3),
+ mtnscale(1e3),
+ fog_density(0),
+ fog_scale(0),
+ day_phase(0),
+ eclipsed(false)
+{
+ type = TERRAIN;
+
+ if (primary) {
+ orbit = primary->Radius();
+ period = primary->Rotation();
+ }
+
+ clouds_alt_high = 12.0e3;
+ clouds_alt_low = 8.0e3;
+
+ Update();
+}
+
+// +--------------------------------------------------------------------+
+
+TerrainRegion::~TerrainRegion()
+{
+ layers.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TerrainRegion::Update()
+{
+ if (system && primary) {
+ phase = primary->RotationPhase();
+ loc = primary->Location() + Point((double) (orbit * cos(phase)),
+ (double) (orbit * sin(phase)),
+ 0);
+ }
+
+ if (rep)
+ rep->MoveTo(loc.OtherHand());
+
+ // calc day phase:
+ OrbitalBody* star = system->Bodies()[0];
+
+ Point tvpn = Location() - primary->Location();
+ Point tvst = star->Location() - primary->Location();
+
+ tvpn.Normalize();
+ tvst.Normalize();
+
+ day_phase = acos(tvpn * tvst) + PI;
+
+ Point meridian = tvpn.cross(tvst);
+
+ if (meridian.z < 0) {
+ day_phase = 2*PI - day_phase;
+ }
+
+ day_phase *= 24 / (2*PI);
+
+ if (!system || system->ActiveRegion() != this)
+ return;
+
+ // set sky color:
+ int base_hour = (int) day_phase;
+ double fraction = day_phase - base_hour;
+
+ sky_color[24] = Color::Scale(sky_color[base_hour], sky_color[base_hour+1], fraction);
+ sun_color[24] = Color::Scale(sun_color[base_hour], sun_color[base_hour+1], fraction);
+ fog_color[24] = Color::Scale(fog_color[base_hour], fog_color[base_hour+1], fraction);
+ ambient[24] = Color::Scale(ambient[base_hour], ambient[base_hour+1], fraction);
+ overcast[24] = Color::Scale(overcast[base_hour], overcast[base_hour+1], fraction);
+ cloud_color[24] = Color::Scale(cloud_color[base_hour], cloud_color[base_hour+1], fraction);
+ shade_color[24] = Color::Scale(shade_color[base_hour], shade_color[base_hour+1], fraction);
+
+ CameraDirector* cam_dir = CameraDirector::GetInstance();
+ Sim* sim = Sim::GetSim();
+ double alt = 0;
+ double dim = 1;
+
+ if (cam_dir && cam_dir->GetCamera())
+ alt = cam_dir->GetCamera()->Pos().y;
+
+ if (alt > 0) {
+ if (alt < TERRAIN_ALTITUDE_LIMIT) {
+ if (weather.Ceiling() > 0) {
+ fog_color[24] = overcast[24];
+ sky_color[24] = overcast[24];
+ dim = 0.125;
+
+ /***
+ if (alt < weather.Ceiling()) {
+ sky_color[24] = overcast[24];
+ dim = 0.125;
+ }
+ else if (alt < weather.Ceiling() + 500) {
+ double thick = (weather.Ceiling() + 500.0 - alt) / 500.0;
+ sky_color[24] = sky_color[24] * (1.0 - thick);
+ sky_color[24] += overcast[24] * thick;
+
+ dim = 1 - thick * 0.875;
+ }
+ else {
+ sky_color[24] = sky_color[24] * (1 - alt/TERRAIN_ALTITUDE_LIMIT);
+ }
+ ***/
+ }
+
+ else {
+ sky_color[24] = sky_color[24] * (1 - alt/TERRAIN_ALTITUDE_LIMIT);
+ }
+ }
+ else {
+ sky_color[24] = Color::Black;
+ }
+ }
+
+ if (system) {
+ system->SetSunlight(sun_color[24], dim);
+ system->SetBacklight(sky_color[24], dim);
+
+ HUDView* hud = HUDView::GetInstance();
+ if (hud) {
+ Color night_vision = hud->Ambient();
+ sky_color[24] += night_vision * 0.15;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TerrainRegion::LoadSkyColors(const char* bmp_name)
+{
+ Bitmap sky_colors_bmp;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->LoadBitmap(bmp_name, sky_colors_bmp);
+
+ int max_color = sky_colors_bmp.Width();
+
+ if (max_color > 24)
+ max_color = 24;
+
+ for (int i = 0; i < 25; i++) {
+ sun_color[i] = Color::White;
+ sky_color[i] = Color::Black;
+ fog_color[i] = Color::White;
+ ambient[i] = Color::DarkGray;
+ cloud_color[i] = Color::White;
+ shade_color[i] = Color::Gray;
+ }
+
+ for (i = 0; i < max_color; i++)
+ sky_color[i] = sky_colors_bmp.GetColor(i, 0);
+
+ if (sky_colors_bmp.Height() > 1)
+ for (i = 0; i < max_color; i++)
+ fog_color[i].Set(sky_colors_bmp.GetColor(i, 1).Value() | Color(0,0,0,255).Value());
+
+ if (sky_colors_bmp.Height() > 2)
+ for (i = 0; i < max_color; i++)
+ ambient[i] = sky_colors_bmp.GetColor(i, 2);
+
+ if (sky_colors_bmp.Height() > 3)
+ for (i = 0; i < max_color; i++)
+ sun_color[i] = sky_colors_bmp.GetColor(i, 3);
+
+ if (sky_colors_bmp.Height() > 4)
+ for (i = 0; i < max_color; i++)
+ overcast[i] = sky_colors_bmp.GetColor(i, 4);
+
+ if (sky_colors_bmp.Height() > 5)
+ for (i = 0; i < max_color; i++)
+ cloud_color[i] = sky_colors_bmp.GetColor(i, 5);
+
+ if (sky_colors_bmp.Height() > 6)
+ for (i = 0; i < max_color; i++)
+ shade_color[i] = sky_colors_bmp.GetColor(i, 6);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TerrainRegion::AddLayer(double h, const char* tile, const char* detail)
+{
+ TerrainLayer* layer = new(__FILE__,__LINE__) TerrainLayer;
+
+ layer->min_height = h;
+ layer->tile_name = tile;
+
+ if (detail && *detail)
+ layer->detail_name = detail;
+
+ layers.append(layer);
+}
+
+const Text&
+TerrainRegion::EnvironmentTexture(int face) const
+{
+ switch (face) {
+ case 0: return env_texture_positive_x;
+
+ case 1: if (env_texture_negative_x.length() > 0)
+ return env_texture_negative_x;
+ return env_texture_positive_x;
+
+ case 2: return env_texture_positive_y;
+
+ case 3: if (env_texture_negative_y.length() > 0)
+ return env_texture_negative_y;
+ return env_texture_positive_y;
+
+ case 4: if (env_texture_positive_z.length() > 0)
+ return env_texture_positive_z;
+ return env_texture_positive_x;
+
+ case 5: if (env_texture_negative_z.length() > 0)
+ return env_texture_negative_z;
+ if (env_texture_positive_z.length() > 0)
+ return env_texture_positive_z;
+ return env_texture_positive_x;
+ }
+
+ return env_texture_positive_x;
+}
+
diff --git a/Stars45/TerrainRegion.h b/Stars45/TerrainRegion.h
new file mode 100644
index 0000000..5a38547
--- /dev/null
+++ b/Stars45/TerrainRegion.h
@@ -0,0 +1,126 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TerrainRegion.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Various heavenly bodies
+*/
+
+#ifndef TerrainRegion_h
+#define TerrainRegion_h
+
+#include "Types.h"
+#include "StarSystem.h"
+#include "Weather.h"
+
+// +--------------------------------------------------------------------+
+
+const double TERRAIN_ALTITUDE_LIMIT = 35e3;
+
+class TerrainLayer;
+
+// +--------------------------------------------------------------------+
+
+class TerrainRegion : public OrbitalRegion
+{
+ friend class StarSystem;
+
+public:
+ TerrainRegion(StarSystem* sys, const char* n, double r, Orbital* prime=0);
+ virtual ~TerrainRegion();
+
+ // operations:
+ virtual void Update();
+
+ // accessors:
+ const Text& PatchName() const { return patch_name; }
+ const Text& PatchTexture() const { return patch_texture; }
+ const Text& ApronName() const { return apron_name; }
+ const Text& ApronTexture() const { return apron_texture; }
+ const Text& WaterTexture() const { return water_texture; }
+ const Text& DetailTexture0() const { return noise_tex0; }
+ const Text& DetailTexture1() const { return noise_tex1; }
+ const Text& HazeName() const { return haze_name; }
+ const Text& CloudsHigh() const { return clouds_high; }
+ const Text& ShadesHigh() const { return shades_high; }
+ const Text& CloudsLow() const { return clouds_low; }
+ const Text& ShadesLow() const { return shades_low; }
+ const Text& EnvironmentTexture(int n) const;
+
+ Color SunColor() const { return sun_color[24]; }
+ Color SkyColor() const { return sky_color[24]; }
+ Color FogColor() const { return fog_color[24]; }
+ Color Ambient() const { return ambient[24]; }
+ Color Overcast() const { return overcast[24]; }
+ Color CloudColor() const { return cloud_color[24];}
+ Color ShadeColor() const { return shade_color[24];}
+
+ double LateralScale() const { return scale; }
+ double MountainScale() const { return mtnscale; }
+ double FogDensity() const { return fog_density; }
+ double FogScale() const { return fog_scale; }
+ double DayPhase() const { return day_phase; }
+ double HazeFade() const { return haze_fade; }
+ double CloudAltHigh() const { return clouds_alt_high; }
+ double CloudAltLow() const { return clouds_alt_low; }
+ Weather& GetWeather() { return weather; }
+ List<TerrainLayer>& GetLayers() { return layers; }
+
+ bool IsEclipsed() const { return eclipsed; }
+ void SetEclipsed(bool e) { eclipsed = e; }
+
+ void LoadSkyColors(const char* bmp_name);
+ void AddLayer(double h, const char* tile, const char* detail=0);
+
+protected:
+ Text patch_name;
+ Text patch_texture;
+ Text apron_name;
+ Text apron_texture;
+ Text water_texture;
+ Text env_texture_positive_x;
+ Text env_texture_negative_x;
+ Text env_texture_positive_y;
+ Text env_texture_negative_y;
+ Text env_texture_positive_z;
+ Text env_texture_negative_z;
+ Text noise_tex0;
+ Text noise_tex1;
+ Text haze_name;
+ Text clouds_high;
+ Text clouds_low;
+ Text shades_high;
+ Text shades_low;
+
+ Color sun_color[25];
+ Color sky_color[25];
+ Color fog_color[25];
+ Color ambient[25];
+ Color overcast[25];
+ Color cloud_color[25];
+ Color shade_color[25];
+
+ double scale;
+ double mtnscale;
+
+ double fog_density;
+ double fog_scale;
+ double day_phase;
+ double haze_fade;
+ double clouds_alt_high;
+ double clouds_alt_low;
+
+ Weather weather;
+ bool eclipsed;
+
+ List<TerrainLayer> layers;
+};
+
+#endif TerrainRegion_h
+
diff --git a/Stars45/Thruster.cpp b/Stars45/Thruster.cpp
new file mode 100644
index 0000000..a189a26
--- /dev/null
+++ b/Stars45/Thruster.cpp
@@ -0,0 +1,595 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Thruster.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Weapon class
+*/
+
+#include "MemDebug.h"
+#include "Thruster.h"
+#include "Component.h"
+#include "Drive.h"
+#include "FlightComp.h"
+#include "SystemDesign.h"
+#include "Ship.h"
+#include "ShipDesign.h"
+#include "Sim.h"
+#include "CameraDirector.h"
+#include "AudioConfig.h"
+#include "Random.h"
+
+#include "Light.h"
+#include "Bitmap.h"
+#include "Sound.h"
+#include "DataLoader.h"
+#include "Bolt.h"
+#include "Sprite.h"
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+static Sound* thruster_resource = 0;
+static Sound* thruster_sound = 0;
+
+extern Bitmap* drive_flare_bitmap[8];
+extern Bitmap* drive_trail_bitmap[8];
+
+#define CLAMP(x, a, b) if (x < (a)) x = (a); else if (x > (b)) x = (b);
+
+// +----------------------------------------------------------------------+
+
+ThrusterPort::ThrusterPort(int t, const Point& l, DWORD f, float s)
+ : type(t), loc(l), flare(0), trail(0), fire(f), burn(0), scale(s)
+{ }
+
+ThrusterPort::~ThrusterPort()
+{
+ GRAPHIC_DESTROY(flare);
+ GRAPHIC_DESTROY(trail);
+}
+
+// +----------------------------------------------------------------------+
+
+static int sys_value = 2;
+
+// +----------------------------------------------------------------------+
+
+Thruster::Thruster(int dtype, double max_thrust, float flare_scale)
+ : System(DRIVE, dtype, "Thruster", sys_value,
+ max_thrust, max_thrust, max_thrust),
+ ship(0), thrust(1.0f), scale(flare_scale),
+ avail_x(1.0f), avail_y(1.0f), avail_z(1.0f)
+{
+ name = Game::GetText("sys.thruster");
+ abrv = Game::GetText("sys.thruster.abrv");
+
+ power_flags = POWER_WATTS;
+
+ ZeroMemory(burn, sizeof(burn));
+
+ emcon_power[0] = 50;
+ emcon_power[1] = 50;
+ emcon_power[2] = 100;
+}
+
+// +----------------------------------------------------------------------+
+
+Thruster::Thruster(const Thruster& t)
+ : System(t), ship(0),
+ thrust(1.0f), scale(t.scale),
+ avail_x(1.0f), avail_y(1.0f), avail_z(1.0f)
+{
+ power_flags = POWER_WATTS;
+ Mount(t);
+
+ ZeroMemory(burn, sizeof(burn));
+
+ if (subtype != Drive::STEALTH) {
+ for (int i = 0; i < t.ports.size(); i++) {
+ ThrusterPort* p = t.ports[i];
+ CreatePort(p->type, p->loc, p->fire, p->scale);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Thruster::~Thruster()
+{
+ ports.destroy();
+
+ if (thruster_sound && thruster_sound->IsPlaying()) {
+ thruster_sound->Stop();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Thruster::Initialize()
+{
+ static int initialized = 0;
+ if (initialized) return;
+
+ DataLoader* loader = DataLoader::GetLoader();
+
+ const int SOUND_FLAGS = Sound::LOCALIZED |
+ Sound::LOC_3D |
+ Sound::LOOP |
+ Sound::LOCKED;
+
+ loader->SetDataPath("Sounds/");
+ loader->LoadSound("thruster.wav", thruster_resource, SOUND_FLAGS);
+ loader->SetDataPath(0);
+
+ if (thruster_resource)
+ thruster_resource->SetMaxDistance(15.0e3f);
+
+ initialized = 1;
+}
+
+void
+Thruster::Close()
+{
+ delete thruster_resource;
+ thruster_resource = 0;
+
+ if (thruster_sound) {
+ thruster_sound->Stop();
+ thruster_sound->Release();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Thruster::Orient(const Physical* rep)
+{
+ System::Orient(rep);
+
+ bool hide_all = false;
+ if (!ship || (ship->IsAirborne() && ship->Class() != Ship::LCA)) {
+ hide_all = true;
+ }
+
+ if (ship->Rep() && ship->Rep()->Hidden()) {
+ hide_all = true;
+ }
+
+ if (thrust <= 0) {
+ hide_all = true;
+ }
+
+ const Matrix& orientation = rep->Cam().Orientation();
+ Point ship_loc = rep->Location();
+
+ for (int i = 0; i < ports.size(); i++) {
+ ThrusterPort* p = ports[i];
+
+ Point projector = (p->loc * orientation) + ship_loc;
+
+ if (p->flare)
+ p->flare->MoveTo(projector);
+
+ if (p->trail) {
+ double intensity = p->burn;
+
+ if (intensity > 0.5 && !hide_all) {
+ Bolt* t = (Bolt*) p->trail;
+ double len = -50 * p->scale * intensity;
+
+ t->Show();
+
+ switch (p->type) {
+ case LEFT: t->SetEndPoints(projector, projector + rep->Cam().vrt() * len); break;
+ case RIGHT: t->SetEndPoints(projector, projector - rep->Cam().vrt() * len); break;
+ case AFT: t->SetEndPoints(projector, projector + rep->Cam().vpn() * len); break;
+ case FORE: t->SetEndPoints(projector, projector - rep->Cam().vpn() * len); break;
+ case BOTTOM: t->SetEndPoints(projector, projector + rep->Cam().vup() * len); break;
+ case TOP: t->SetEndPoints(projector, projector - rep->Cam().vup() * len); break;
+ default: t->Hide(); break;
+ }
+ }
+ else {
+ p->trail->Hide();
+ if (p->flare)
+ p->flare->Hide();
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Thruster::ExecFrame(double seconds)
+{
+ System::ExecFrame(seconds);
+
+ if (ship) {
+ double rr = 0, pr = 0, yr = 0;
+ double rd = 0, pd = 0, yd = 0;
+
+ double agility = 1;
+ double stability = 1;
+
+ FlightComp* flcs = ship->GetFLCS();
+
+ if (flcs) {
+ if (!flcs->IsPowerOn() || flcs->Status() < DEGRADED) {
+ agility = 0.3;
+ stability = 0.0;
+ }
+ }
+
+ // check for thruster damage here:
+ if (components.size() >= 3) {
+ int stat = components[0]->Status();
+ if (stat == Component::NOMINAL)
+ avail_x = 1.0f;
+ else if (stat == Component::DEGRADED)
+ avail_x = 0.5f;
+ else
+ avail_x = 0.0f;
+
+ stat = components[1]->Status();
+ if (stat == Component::NOMINAL)
+ avail_z = 1.0f;
+ else if (stat == Component::DEGRADED)
+ avail_z = 0.5f;
+ else
+ avail_z = 0.0f;
+
+ stat = components[2]->Status();
+ if (stat == Component::NOMINAL)
+ avail_y = 1.0f;
+ else if (stat == Component::DEGRADED)
+ avail_y = 0.5f;
+ else
+ avail_y = 0.0f;
+ }
+
+ // thrust limited by power distribution:
+ thrust = energy/capacity;
+ energy = 0.0f;
+
+ if (thrust < 0)
+ thrust = 0.0f;
+
+ agility *= thrust;
+ stability *= thrust;
+
+ rr = roll_rate * agility * avail_y;
+ pr = pitch_rate * agility * avail_y;
+ yr = yaw_rate * agility * avail_x;
+
+ rd = roll_drag * stability * avail_y;
+ pd = pitch_drag * stability * avail_y;
+ yd = yaw_drag * stability * avail_x;
+
+ ship->SetAngularRates(rr,pr,yr);
+ ship->SetAngularDrag (rd,pd,yd);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Thruster::SetShip(Ship* s)
+{
+ const double ROLL_SPEED = PI * 0.0400;
+ const double PITCH_SPEED = PI * 0.0250;
+ const double YAW_SPEED = PI * 0.0250;
+
+ ship = s;
+
+ if (ship) {
+ ShipDesign* design = (ShipDesign*) ship->Design();
+
+ trans_x = design->trans_x;
+ trans_y = design->trans_y;
+ trans_z = design->trans_z;
+
+ roll_drag = design->roll_drag;
+ pitch_drag = design->pitch_drag;
+ yaw_drag = design->yaw_drag;
+
+ roll_rate = (float) (design->roll_rate * PI / 180);
+ pitch_rate = (float) (design->pitch_rate * PI / 180);
+ yaw_rate = (float) (design->yaw_rate * PI / 180);
+
+ double agility = design->agility;
+
+ if (roll_rate == 0) roll_rate = (float) (agility * ROLL_SPEED);
+ if (pitch_rate == 0) pitch_rate = (float) (agility * PITCH_SPEED);
+ if (yaw_rate == 0) yaw_rate = (float) (agility * YAW_SPEED);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Thruster::TransXLimit()
+{
+ return trans_x * avail_x;
+}
+
+double
+Thruster::TransYLimit()
+{
+ return trans_y * avail_y;
+}
+
+double
+Thruster::TransZLimit()
+{
+ return trans_z * avail_z;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Thruster::ExecTrans(double x, double y, double z)
+{
+ if (!ship || (ship->IsAirborne() && ship->Class() != Ship::LCA)) {
+ if (thruster_sound && thruster_sound->IsPlaying())
+ thruster_sound->Stop();
+
+ for (int i = 0; i < ports.size(); i++) {
+ ThrusterPort* p = ports[i];
+ if (p->flare) p->flare->Hide();
+ if (p->trail) p->trail->Hide();
+ }
+
+ return;
+ }
+
+ bool sound_on = false;
+ bool show_flare = true;
+
+ if (ship->Rep() && ship->Rep()->Hidden())
+ show_flare = false;
+
+ if (ship->Class() == Ship::LCA &&
+ ship->IsAirborne() &&
+ ship->Velocity().length() < 250 &&
+ ship->AltitudeAGL() > ship->Radius()/2) {
+
+ sound_on = true;
+ IncBurn(BOTTOM, TOP);
+ }
+
+ else if (!ship->IsAirborne()) {
+ double tx_limit = ship->Design()->trans_x;
+ double ty_limit = ship->Design()->trans_y;
+ double tz_limit = ship->Design()->trans_z;
+
+ if (x < -0.15 * tx_limit) IncBurn(RIGHT, LEFT);
+ else if (x > 0.15 * tx_limit) IncBurn(LEFT, RIGHT);
+ else DecBurn(LEFT, RIGHT);
+
+ if (y < -0.15 * ty_limit) IncBurn(FORE, AFT);
+ else if (y > 0.15 * ty_limit) IncBurn(AFT, FORE);
+ else DecBurn(FORE, AFT);
+
+ if (z < -0.15 * tz_limit) IncBurn(TOP, BOTTOM);
+ else if (z > 0.15 * tz_limit) IncBurn(BOTTOM, TOP);
+ else DecBurn(TOP, BOTTOM);
+
+ double r, p, y;
+ ship->GetAngularThrust(r, p, y);
+
+ // Roll seems to have the opposite sign from
+ // the pitch and yaw thrust factors. Not sure why.
+
+ if (r > 0) IncBurn(ROLL_L, ROLL_R);
+ else if (r < 0) IncBurn(ROLL_R, ROLL_L);
+ else DecBurn(ROLL_R, ROLL_L);
+
+ if (y < 0) IncBurn(YAW_L, YAW_R);
+ else if (y > 0) IncBurn(YAW_R, YAW_L);
+ else DecBurn(YAW_R, YAW_L);
+
+ if (p < 0) IncBurn(PITCH_D, PITCH_U);
+ else if (p > 0) IncBurn(PITCH_U, PITCH_D);
+ else DecBurn(PITCH_U, PITCH_D);
+ }
+
+ else {
+ for (int i = 0; i < 12; i++) {
+ burn[i] -= 0.1f;
+ if (burn[i] < 0)
+ burn[i] = 0.0f;
+ }
+ }
+
+ for (int i = 0; i < ports.size(); i++) {
+ ThrusterPort* p = ports[i];
+
+ if (p->fire) {
+ p->burn = 0;
+
+ int flag = 1;
+
+ for (int n = 0; n < 12; n++) {
+ if ((p->fire & flag) != 0 && burn[n] > p->burn)
+ p->burn = burn[n];
+
+ flag <<= 1;
+ }
+ }
+
+ else {
+ p->burn = burn[p->type];
+ }
+
+ if (p->burn > 0 && thrust > 0) {
+ sound_on = true;
+
+ if (show_flare) {
+ Sprite* flare_rep = (Sprite*) p->flare;
+ if (flare_rep) {
+ flare_rep->Show();
+ flare_rep->SetShade(1);
+ }
+
+ if (p->trail) {
+ Bolt* t = (Bolt*) p->trail;
+ t->Show();
+ t->SetShade(1);
+ }
+ }
+ }
+ else {
+ if (p->flare) p->flare->Hide();
+ if (p->trail) p->trail->Hide();
+ }
+ }
+
+ // thruster sound:
+ if (ship && ship == Sim::GetSim()->GetPlayerShip() && ports.size() > 0) {
+ CameraDirector* cam_dir = CameraDirector::GetInstance();
+
+ // no sound when paused!
+ if (!Game::Paused() && cam_dir && cam_dir->GetCamera()) {
+ if (!thruster_sound) {
+ if (thruster_resource)
+ thruster_sound = thruster_resource->Duplicate();
+ }
+
+ if (thruster_sound) {
+ if (sound_on) {
+ Point cam_loc = cam_dir->GetCamera()->Pos();
+ double dist = (ship->Location() - cam_loc).length();
+
+ long max_vol = AudioConfig::EfxVolume();
+ long volume = -2000;
+
+ if (volume > max_vol)
+ volume = max_vol;
+
+ if (dist < thruster_sound->GetMaxDistance()) {
+ thruster_sound->SetLocation(ship->Location());
+ thruster_sound->SetVolume(volume);
+ thruster_sound->Play();
+ }
+ else if (thruster_sound->IsPlaying()) {
+ thruster_sound->Stop();
+ }
+ }
+ else if (thruster_sound->IsPlaying()) {
+ thruster_sound->Stop();
+ }
+ }
+ }
+ }
+
+ ship->SetTransX(x * thrust);
+ ship->SetTransY(y * thrust);
+ ship->SetTransZ(z * thrust);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Thruster::AddPort(int type, const Point& loc, DWORD fire, float flare_scale)
+{
+ if (flare_scale == 0) flare_scale = scale;
+ ThrusterPort* port = new(__FILE__,__LINE__) ThrusterPort(type, loc, fire, flare_scale);
+ ports.append(port);
+}
+
+void
+Thruster::CreatePort(int type, const Point& loc, DWORD fire, float flare_scale)
+{
+ Bitmap* flare_bmp = drive_flare_bitmap[subtype];
+ Bitmap* trail_bmp = drive_trail_bitmap[subtype];
+
+ if (subtype != Drive::STEALTH) {
+ Sprite* flare_rep = new(__FILE__,__LINE__) Sprite(flare_bmp);
+ flare_rep->Scale(flare_scale * 0.667f);
+ flare_rep->SetShade(0);
+
+ Bolt* trail_rep = new(__FILE__,__LINE__) Bolt(flare_scale * 30, flare_scale * 8, trail_bmp, true);
+
+ ThrusterPort* port = new(__FILE__,__LINE__) ThrusterPort(type, loc, fire, flare_scale);
+ port->flare = flare_rep;
+ port->trail = trail_rep;
+ ports.append(port);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Thruster::NumThrusters() const
+{
+ return ports.size();
+}
+
+Graphic*
+Thruster::Flare(int engine) const
+{
+ if (engine >= 0 && engine < ports.size())
+ return ports[engine]->flare;
+
+ return 0;
+}
+
+Graphic*
+Thruster::Trail(int engine) const
+{
+ if (engine >= 0 && engine < ports.size())
+ return ports[engine]->trail;
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Thruster::IncBurn(int inc, int dec)
+{
+ burn[inc] += 0.1f;
+ if (burn[inc] > 1)
+ burn[inc] = 1.0f;
+
+ burn[dec] -= 0.1f;
+ if (burn[dec] < 0)
+ burn[dec] = 0.0f;
+}
+
+void
+Thruster::DecBurn(int a, int b)
+{
+ burn[a] -= 0.1f;
+ if (burn[a] < 0)
+ burn[a] = 0.0f;
+
+ burn[b] -= 0.1f;
+ if (burn[b] < 0)
+ burn[b] = 0.0f;
+}
+
+// +----------------------------------------------------------------------+
+
+double
+Thruster::GetRequest(double seconds) const
+{
+ if (!power_on)
+ return 0;
+
+ for (int i = 0; i < 12; i++)
+ if (burn[i] != 0)
+ return power_level * sink_rate * seconds;
+
+ return 0;
+}
+
diff --git a/Stars45/Thruster.h b/Stars45/Thruster.h
new file mode 100644
index 0000000..c7f9e00
--- /dev/null
+++ b/Stars45/Thruster.h
@@ -0,0 +1,97 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Thruster.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Conventional Thruster (system) class
+*/
+
+#ifndef Thruster_h
+#define Thruster_h
+
+#include "Types.h"
+#include "System.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class Ship;
+
+// +--------------------------------------------------------------------+
+
+struct ThrusterPort {
+ static const char* TYPENAME() { return "ThrusterPort"; }
+
+ ThrusterPort(int t, const Point& l, DWORD f, float s);
+ ~ThrusterPort();
+
+ int type;
+ DWORD fire;
+ float burn;
+ float scale;
+ Point loc;
+ Graphic* flare;
+ Graphic* trail;
+};
+
+// +--------------------------------------------------------------------+
+
+class Thruster : public System
+{
+public:
+ static const char* TYPENAME() { return "Thruster"; }
+
+ enum Constants {
+ LEFT, RIGHT, FORE, AFT, TOP, BOTTOM,
+ YAW_L, YAW_R, PITCH_D, PITCH_U, ROLL_L, ROLL_R
+ };
+
+ Thruster(int dtype, double thrust, float flare_scale=0);
+ Thruster(const Thruster& rhs);
+ virtual ~Thruster();
+
+ static void Initialize();
+ static void Close();
+
+ virtual void ExecFrame(double seconds);
+ virtual void ExecTrans(double x, double y, double z);
+ virtual void SetShip(Ship* s);
+
+ virtual double TransXLimit();
+ virtual double TransYLimit();
+ virtual double TransZLimit();
+
+ virtual void AddPort(int type, const Point& loc, DWORD fire, float flare_scale=0);
+ virtual void CreatePort(int type, const Point& loc, DWORD fire, float flare_scale);
+
+ int NumThrusters() const;
+ Graphic* Flare(int engine) const;
+ Graphic* Trail(int engine) const;
+ virtual void Orient(const Physical* rep);
+ virtual double GetRequest(double seconds) const;
+
+protected:
+ void IncBurn(int inc, int dec);
+ void DecBurn(int a, int b);
+
+ Ship* ship;
+ float thrust;
+ float scale;
+ float burn[12];
+
+ float avail_x, avail_y, avail_z;
+ float trans_x, trans_y, trans_z;
+ float roll_rate, pitch_rate, yaw_rate;
+ float roll_drag, pitch_drag, yaw_drag;
+
+ List<ThrusterPort> ports;
+};
+
+#endif Thruster_h
+
diff --git a/Stars45/TrackIR.cpp b/Stars45/TrackIR.cpp
new file mode 100644
index 0000000..8145f39
--- /dev/null
+++ b/Stars45/TrackIR.cpp
@@ -0,0 +1,247 @@
+/* Project Starshatter 4.6
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TrackIR.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ TrackIR head tracker interface class
+*/
+
+#include "MemDebug.h"
+#include "TrackIR.h"
+#include "NPClient.h"
+#include "NPClientWraps.h"
+
+#include "Game.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+const double TRACK_TOP = -8000;
+const double TRACK_BOTTOM = 8000;
+const double TRACK_LEFT = 16000;
+const double TRACK_RIGHT = -16000;
+const double TRACK_XYZ = 24000;
+
+// +--------------------------------------------------------------------+
+
+static Text GetDllFromRegistry()
+{
+ Text dllLoc;
+ BYTE dllBuf[1024];
+ DWORD dllLen = 0;
+ HKEY hkey = 0;
+
+ ZeroMemory(dllBuf, sizeof(dllBuf));
+
+ RegOpenKeyEx(HKEY_CURRENT_USER,
+ "Software\\NaturalPoint\\NATURALPOINT\\NPClient Location",
+ 0,
+ KEY_QUERY_VALUE,
+ &hkey);
+
+ if (hkey) {
+ dllLen = 1024;
+
+ LONG result =
+ RegQueryValueEx(hkey,
+ "Path",
+ NULL,
+ NULL,
+ dllBuf,
+ &dllLen);
+
+ if (result == ERROR_SUCCESS && dllLen > 0)
+ dllLoc = (const char*) dllBuf;
+
+ RegCloseKey(hkey);
+ }
+
+ return dllLoc;
+}
+
+static const char* NPErrString(NPRESULT r)
+{
+ switch (r) {
+ case NP_OK: return "OK";
+ case NP_ERR_DEVICE_NOT_PRESENT: return "Device not present";
+ case NP_ERR_UNSUPPORTED_OS: return "Unsupported O/S";
+ case NP_ERR_INVALID_ARG: return "Invalid argument";
+ case NP_ERR_DLL_NOT_FOUND: return "NaturalPoint DLL not found";
+ case NP_ERR_NO_DATA: return "No data available";
+ case NP_ERR_INTERNAL_DATA: return "Internal error";
+ }
+
+ return "Unknown error code";
+}
+
+// +--------------------------------------------------------------------+
+
+TrackIR::TrackIR() :
+ running(false), frame_signature(0),
+ az(0), el(0), x(0), y(0), z(0)
+{
+ Print("*** NaturalPoint Game Client Initialization ***\n");
+
+ // Hook up the NaturalPoint game client DLL using the wrapper module
+ NPRESULT result;
+ Text dllPath = GetDllFromRegistry();
+
+ // Initialize the NPClient interface
+ result = NPClient_Init(dllPath);
+ if (result == NP_OK) {
+ Print("NPClient - Initialize successful.\n");
+ }
+ else {
+ Print("NPClient - Unable to initialize interface: %s\n", NPErrString(result));
+ return;
+ }
+
+ // Register the app's window handle
+ result = NP_RegisterWindowHandle(Game::GetHWND());
+
+ if (result == NP_OK) {
+ Print("NPClient - Window handle registration successful.\n");
+ }
+ else {
+ Print("NPClient - Error registering window handle: %s\n", NPErrString(result));
+ return;
+ }
+
+ // Query the NaturalPoint software version
+ unsigned short wNPClientVer;
+ result = NP_QueryVersion( &wNPClientVer );
+
+ if (result == NP_OK) {
+ Print("NPClient - NaturalPoint software version: %d.%02d\n", (wNPClientVer >> 8), (wNPClientVer & 0x00FF));
+ }
+ else {
+ Print("NPClient - Error querying NaturalPoint software version: %s\n", NPErrString(result));
+ }
+
+
+ // It is *required* that your application registers the Developer ID
+ // assigned by NaturalPoint!
+
+ // Your assigned developer ID needs to be inserted below!
+ #define NP_DEVELOPER_ID 6401
+
+ // NOTE : The title of your project must show up
+ // in the list of supported titles shown in the Profiles
+ // tab of the TrackIR software, if it does not then the
+ // TrackIR software will *not* transmit data to your
+ // application. If your title is not present in the list,
+ // you may need to have the TrackIR software perform a
+ // game list update (to download support for new Developer IDs)
+ // using the menu item under the "Help" or "Update" menu.
+
+ NP_RegisterProgramProfileID(NP_DEVELOPER_ID);
+
+ unsigned int DataFields = 0;
+ DataFields |= NPPitch;
+ DataFields |= NPYaw;
+
+ NP_RequestData(DataFields);
+
+ result = NP_StopCursor();
+ if (result == NP_OK)
+ Print("NPClient - Cursor stopped.\n");
+ else
+ Print("NPClient - Error stopping cursor: %s\n", NPErrString(result));
+
+
+ result = NP_StartDataTransmission();
+ if (result == NP_OK) {
+ Print("NPClient - Data transmission started.\n");
+ running = true;
+ }
+ else {
+ Print("NPClient - Error starting data transmission: %s\n", NPErrString(result));
+ }
+
+}
+
+TrackIR::~TrackIR()
+{
+ if (running) {
+ Print("NaturalPoint Game Client Shutdown\n");
+
+ NP_StopDataTransmission();
+ NP_UnregisterWindowHandle();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+TrackIR::ExecFrame()
+{
+ TRACKIRDATA tid;
+
+ // Go get the latest data
+ NPRESULT result = NP_GetData( &tid );
+
+ if (result == NP_OK) {
+ // Got data to process ...
+ running = true;
+
+ // compare the last frame signature to the current one
+ // if they are not the same then new data has arrived since then
+
+ if (tid.wNPStatus == NPSTATUS_REMOTEACTIVE) {
+ if (frame_signature != tid.wPFrameSignature) {
+ double pitch = tid.fNPPitch;
+ double yaw = tid.fNPYaw;
+
+ if (pitch < 0) {
+ el = pitch / TRACK_TOP;
+ }
+ else {
+ el = -pitch / TRACK_BOTTOM;
+ }
+
+ if (yaw < 0) {
+ az = yaw / TRACK_RIGHT;
+ }
+ else {
+ az = -yaw / TRACK_LEFT;
+ }
+
+ x = tid.fNPX / TRACK_XYZ * -1;
+ y = tid.fNPY / TRACK_XYZ;
+ z = tid.fNPZ / TRACK_XYZ * -1;
+
+ if (z < -0.25) z = -0.25;
+
+ frame_signature = tid.wPFrameSignature;
+ }
+ else {
+ // Either there is no tracking data, the user has
+ // paused the trackIR, or the call happened before
+ // the TrackIR was able to update the interface
+ // with new data
+
+ az *= 0.75;
+ el *= 0.75;
+ x *= 0.75;
+ y *= 0.75;
+ z *= 0.75;
+
+ result = NP_ERR_NO_DATA;
+ }
+ }
+ else {
+ // The user has set the device out of trackIR Enhanced Mode
+ // and into Mouse Emulation mode with the hotkey
+ result = NP_ERR_NO_DATA;
+ running = false;
+ }
+ }
+
+ return result;
+}
diff --git a/Stars45/TrackIR.h b/Stars45/TrackIR.h
new file mode 100644
index 0000000..0545a14
--- /dev/null
+++ b/Stars45/TrackIR.h
@@ -0,0 +1,53 @@
+/* Project Starshatter 4.6
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: TrackIR.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ TrackIR head tracker interface class
+*/
+
+#ifndef TrackIR_h
+#define TrackIR_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class TrackIR
+{
+public:
+ TrackIR();
+ ~TrackIR();
+
+ DWORD ExecFrame();
+
+ bool IsRunning() const { return running; }
+ double GetAzimuth() const { return az; }
+ double GetElevation() const { return el; }
+
+ double GetX() const { return x; }
+ double GetY() const { return y; }
+ double GetZ() const { return z; }
+
+protected:
+
+ bool running;
+ DWORD stale_frames;
+ DWORD frame_signature;
+
+ double az;
+ double el;
+
+ double x; // vrt
+ double y; // vup
+ double z; // vpn (i think)
+};
+
+#endif TrackIR_h
+
diff --git a/Stars45/Trail.cpp b/Stars45/Trail.cpp
new file mode 100644
index 0000000..ab9c7a7
--- /dev/null
+++ b/Stars45/Trail.cpp
@@ -0,0 +1,200 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Trail.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Missile Trail representation class
+*/
+
+#include "MemDebug.h"
+#include "Trail.h"
+#include "Weapon.h"
+#include "Sim.h"
+
+#include "Game.h"
+#include "Light.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Sound.h"
+
+// +--------------------------------------------------------------------+
+
+Trail::Trail(Bitmap* tex, int n)
+ : ntrail(0), length0(0), length1(0), texture(tex), maxtrail(n), dim(1)
+{
+ trans = true;
+ luminous = true;
+
+ mtl.Kd = Color::White;
+ mtl.tex_diffuse = texture;
+ mtl.tex_emissive = texture;
+ mtl.blend = Video::BLEND_ADDITIVE;
+ mtl.luminous = true;
+
+ if (maxtrail < 4) maxtrail = 512;
+ trail = new(__FILE__,__LINE__) Point[maxtrail];
+ verts = new(__FILE__,__LINE__) VertexSet(maxtrail*2);
+ verts->Clear();
+ verts->nverts = 0;
+
+ for (int i = 0; i < maxtrail*2; i++) {
+ verts->diffuse[i] = D3DCOLOR_RGBA(255,255,255,255);
+ verts->specular[i] = D3DCOLOR_RGBA( 0, 0, 0,255);
+ verts->tu[i] = 0.0f;
+ verts->tv[i] = (i & 1) ? 1.0f : 0.0f;
+ }
+
+ polys = new(__FILE__,__LINE__) Poly[maxtrail];
+
+ for (i = 0; i < maxtrail; i++) {
+ polys[i].vertex_set = verts;
+ polys[i].nverts = 4;
+ polys[i].material = &mtl;
+ }
+
+ npolys = 0;
+ nverts = 0;
+ width = 6;
+ length = 0;
+ dim = 3;
+
+ last_point_time = 0;
+}
+
+Trail::~Trail()
+{
+ delete [] trail;
+ delete [] polys;
+ delete verts;
+}
+
+void
+Trail::UpdateVerts(const Point& cam_pos)
+{
+ if (ntrail < 2) return;
+
+ int bright = 255 - dim*ntrail;
+
+ Point head = trail[1] + loc;
+ Point tail = trail[0] + loc;
+ Point vcam = cam_pos - head;
+ Point vtmp = vcam.cross(head-tail);
+ vtmp.Normalize();
+ Point vlat = vtmp * (width + (0.1 * width * ntrail));
+
+ verts->loc[0] = tail - vlat;
+ verts->loc[1] = tail + vlat;
+ verts->diffuse[0] = 0;
+ verts->diffuse[1] = 0;
+
+ for (int i = 0; i < ntrail-1; i++) {
+ bright+=dim;
+ Point head = trail[i+1] + loc;
+ Point tail = trail[i] + loc;
+ Point vcam = cam_pos - head;
+ Point vtmp = vcam.cross(head-tail);
+ vtmp.Normalize();
+
+ float trail_width = (float) (width + (ntrail-i) * width * 0.1);
+ if (i == ntrail-2) trail_width = (float) (width * 0.7);
+ Point vlat = vtmp * trail_width;
+
+ verts->loc[2*i+2] = head - vlat;
+ verts->loc[2*i+3] = head + vlat;
+
+ if (bright <= 0) {
+ verts->diffuse[2*i+2] = 0;
+ verts->diffuse[2*i+3] = 0;
+ }
+ else {
+ verts->diffuse[2*i+2] = D3DCOLOR_RGBA(bright,bright,bright,bright);
+ verts->diffuse[2*i+3] = D3DCOLOR_RGBA(bright,bright,bright,bright);
+ }
+ }
+}
+
+void
+Trail::Render(Video* video, DWORD flags)
+{
+ if (!npolys) return;
+
+ if ((flags & RENDER_ADDITIVE) == 0)
+ return;
+
+ if (video && life) {
+ const Camera* cam = video->GetCamera();
+
+ if (cam)
+ UpdateVerts(cam->Pos());
+
+ video->DrawPolys(npolys, polys);
+ }
+}
+
+void
+Trail::AddPoint(const Point& v)
+{
+ if (ntrail >= maxtrail-1) return;
+
+ double real_time = Game::RealTime() / 1000.0;
+
+ if (ntrail == 0) {
+ radius = 1000;
+ }
+ else {
+ radius = (float) Point(v-loc).length();
+ }
+
+ // just adjust the last point:
+ if (ntrail > 1 && real_time - last_point_time < 0.05) {
+ trail[ntrail-1] = v;
+ }
+
+ // add a new point:
+ else {
+ last_point_time = real_time;
+ trail[ntrail++] = v;
+
+ nverts += 2;
+ verts->nverts = nverts;
+
+ if (ntrail > 1) {
+ length0 = length1;
+ length1 = length0 + (trail[ntrail-1]-trail[ntrail-2]).length();
+
+ polys[npolys].vertex_set = verts;
+ polys[npolys].nverts = 4;
+
+ polys[npolys].verts[0] = nverts-4;
+ polys[npolys].verts[1] = nverts-2;
+ polys[npolys].verts[2] = nverts-1;
+ polys[npolys].verts[3] = nverts-3;
+
+ float tu1 = (float) length1 / 250.0f;
+
+ verts->tu[2*ntrail-1] = tu1;
+ verts->tu[2*ntrail-2] = tu1;
+
+ npolys++;
+ }
+ }
+}
+
+double
+Trail::AverageLength()
+{
+ double avg = 0;
+
+ for (int i = 0; i < ntrail-1; i++)
+ avg += (trail[i+1]-trail[i]).length();
+ avg /= ntrail;
+
+ return avg;
+}
+
diff --git a/Stars45/Trail.h b/Stars45/Trail.h
new file mode 100644
index 0000000..59a3873
--- /dev/null
+++ b/Stars45/Trail.h
@@ -0,0 +1,60 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Trail.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Missile Trail (Graphic) class
+*/
+
+#ifndef Trail_h
+#define Trail_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Polygon.h"
+#include "SimObject.h"
+#include "Graphic.h"
+
+// +--------------------------------------------------------------------+
+
+class Trail : public Graphic
+{
+public:
+ Trail(Bitmap* tex, int n=512);
+ virtual ~Trail();
+
+ virtual void UpdateVerts(const Point& cam_pos);
+ virtual void Render(Video* video, DWORD flags);
+ virtual void AddPoint(const Point& v);
+ virtual double AverageLength();
+
+ virtual void SetWidth(double w) { width = w; }
+ virtual void SetDim(int d) { dim = d; }
+
+protected:
+ int ntrail;
+ int maxtrail;
+ Point* trail;
+
+ double length;
+ double width;
+ int dim;
+
+ int npolys, nverts;
+ Poly* polys;
+ VertexSet* verts;
+ Bitmap* texture;
+ Material mtl;
+
+ double length0, length1;
+ double last_point_time;
+};
+
+#endif Trail_h
+
diff --git a/Stars45/VidDlg.cpp b/Stars45/VidDlg.cpp
new file mode 100644
index 0000000..329a17b
--- /dev/null
+++ b/Stars45/VidDlg.cpp
@@ -0,0 +1,516 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: VidDlg.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#include "MemDebug.h"
+#include "VidDlg.h"
+#include "BaseScreen.h"
+#include "Starshatter.h"
+#include "Ship.h"
+#include "Terrain.h"
+#include "CameraDirector.h"
+
+#include "DataLoader.h"
+#include "Button.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Video.h"
+#include "VideoSettings.h"
+#include "Keyboard.h"
+#include "MachineInfo.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+
+DEF_MAP_CLIENT(VidDlg, OnTexSize);
+DEF_MAP_CLIENT(VidDlg, OnMode);
+DEF_MAP_CLIENT(VidDlg, OnDetail);
+DEF_MAP_CLIENT(VidDlg, OnTexture);
+DEF_MAP_CLIENT(VidDlg, OnGamma);
+DEF_MAP_CLIENT(VidDlg, OnApply);
+DEF_MAP_CLIENT(VidDlg, OnCancel);
+DEF_MAP_CLIENT(VidDlg, OnAudio);
+DEF_MAP_CLIENT(VidDlg, OnVideo);
+DEF_MAP_CLIENT(VidDlg, OnOptions);
+DEF_MAP_CLIENT(VidDlg, OnControls);
+DEF_MAP_CLIENT(VidDlg, OnMod);
+
+// +--------------------------------------------------------------------+
+
+VidDlg::VidDlg(Screen* s, FormDef& def, BaseScreen* mgr)
+ : FormWindow(s, 0, 0, s->Width(), s->Height()), manager(mgr),
+ selected_mode(0), selected_detail(0), orig_gamma(128),
+ selected_card(0), selected_tex_size(0), selected_render(0), selected_texture(0),
+ mode(0), tex_size(0), detail(0), texture(0), gamma(0), shadows(0), spec_maps(0),
+ bump_maps(0), lens_flare(0), corona(0), nebula(0), dust(0),
+ apply(0), cancel(0), vid_btn(0), aud_btn(0), ctl_btn(0), opt_btn(0), mod_btn(0),
+ closed(true)
+{
+ stars = Starshatter::GetInstance();
+
+ Init(def);
+ orig_gamma = Game::GammaLevel();
+}
+
+VidDlg::~VidDlg()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VidDlg::RegisterControls()
+{
+ if (apply)
+ return;
+
+ mode = (ComboBox*) FindControl(203);
+ REGISTER_CLIENT(EID_SELECT, mode, VidDlg, OnMode);
+
+ tex_size = (ComboBox*) FindControl(204);
+ REGISTER_CLIENT(EID_SELECT, tex_size, VidDlg, OnTexSize);
+
+ detail = (ComboBox*) FindControl(205);
+ REGISTER_CLIENT(EID_SELECT, detail, VidDlg, OnDetail);
+
+ texture = (ComboBox*) FindControl(206);
+ REGISTER_CLIENT(EID_SELECT, texture, VidDlg, OnTexture);
+
+ gamma = (Slider*) FindControl(215);
+
+ if (gamma) {
+ gamma->SetRangeMin(32);
+ gamma->SetRangeMax(224);
+ gamma->SetStepSize(16);
+
+ REGISTER_CLIENT(EID_CLICK, gamma, VidDlg, OnGamma);
+ }
+
+ lens_flare = (ComboBox*) FindControl(211);
+ corona = (ComboBox*) FindControl(212);
+ nebula = (ComboBox*) FindControl(213);
+ dust = (ComboBox*) FindControl(214);
+ shadows = (ComboBox*) FindControl(222);
+ spec_maps = (ComboBox*) FindControl(223);
+ bump_maps = (ComboBox*) FindControl(224);
+
+ apply = (Button*) FindControl(1);
+ REGISTER_CLIENT(EID_CLICK, apply, VidDlg, OnApply);
+
+ cancel = (Button*) FindControl(2);
+ REGISTER_CLIENT(EID_CLICK, cancel, VidDlg, OnCancel);
+
+ vid_btn = (Button*) FindControl(901);
+ REGISTER_CLIENT(EID_CLICK, vid_btn, VidDlg, OnVideo);
+
+ aud_btn = (Button*) FindControl(902);
+ REGISTER_CLIENT(EID_CLICK, aud_btn, VidDlg, OnAudio);
+
+ ctl_btn = (Button*) FindControl(903);
+ REGISTER_CLIENT(EID_CLICK, ctl_btn, VidDlg, OnControls);
+
+ opt_btn = (Button*) FindControl(904);
+ REGISTER_CLIENT(EID_CLICK, opt_btn, VidDlg, OnOptions);
+
+ mod_btn = (Button*) FindControl(905);
+ if (mod_btn)
+ REGISTER_CLIENT(EID_CLICK, mod_btn, VidDlg, OnMod);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VidDlg::Show()
+{
+ FormWindow::Show();
+
+ if (closed) {
+ bool fullscreen = true;
+
+ if (stars) {
+ selected_render = 9;
+ selected_card = 0;
+
+ int n = stars->MaxTexSize();
+
+ for (int i = 0; i < 7; i++) {
+ if (n <= pow(2, i+6)) {
+ selected_tex_size = i;
+ break;
+ }
+ }
+
+ Video* video = Game::GetVideo();
+
+ if (video) {
+ if (shadows)
+ shadows->SetSelection(video->IsShadowEnabled());
+
+ if (spec_maps)
+ spec_maps->SetSelection(video->IsSpecMapEnabled());
+
+ if (bump_maps)
+ bump_maps->SetSelection(video->IsBumpMapEnabled());
+
+ fullscreen = video->IsFullScreen();
+ }
+
+ if (lens_flare)
+ lens_flare->SetSelection(stars->LensFlare());
+
+ if (corona)
+ corona->SetSelection(stars->Corona());
+
+ if (nebula)
+ nebula->SetSelection(stars->Nebula());
+
+ if (dust)
+ dust->SetSelection(stars->Dust());
+ }
+
+ selected_detail = Terrain::DetailLevel() - 2;
+ selected_texture = true;
+
+ if (mode) {
+ BuildModeList();
+ mode->SetSelection(selected_mode);
+ mode->SetEnabled(fullscreen);
+ }
+
+ if (tex_size)
+ tex_size->SetSelection(selected_tex_size);
+
+ if (detail)
+ detail->SetSelection(selected_detail);
+
+ if (texture)
+ texture->SetSelection(selected_texture);
+
+
+ if (gamma) {
+ orig_gamma = Game::GammaLevel();
+ gamma->SetValue(orig_gamma);
+ }
+ }
+
+ if (vid_btn) vid_btn->SetButtonState(1);
+ if (aud_btn) aud_btn->SetButtonState(0);
+ if (ctl_btn) ctl_btn->SetButtonState(0);
+ if (opt_btn) opt_btn->SetButtonState(0);
+ if (mod_btn) mod_btn->SetButtonState(0);
+
+ closed = false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VidDlg::ExecFrame()
+{
+ if (Keyboard::KeyDown(VK_RETURN)) {
+ OnApply(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VidDlg::OnMode(AWEvent* event)
+{
+ selected_mode = mode->GetSelectedIndex();
+}
+
+void
+VidDlg::OnTexSize(AWEvent* event)
+{
+ selected_tex_size = tex_size->GetSelectedIndex();
+}
+
+void
+VidDlg::OnDetail(AWEvent* event)
+{
+ selected_detail = detail->GetSelectedIndex();
+}
+
+void
+VidDlg::OnTexture(AWEvent* event)
+{
+ selected_texture = texture->GetSelectedIndex();
+}
+
+void
+VidDlg::OnGamma(AWEvent* event)
+{
+ int g = gamma->GetValue();
+
+ if (g >= 0 && g <= 255) {
+ Game::SetGammaLevel(g);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void VidDlg::OnAudio(AWEvent* event) { manager->ShowAudDlg(); }
+void VidDlg::OnVideo(AWEvent* event) { manager->ShowVidDlg(); }
+void VidDlg::OnOptions(AWEvent* event) { manager->ShowOptDlg(); }
+void VidDlg::OnControls(AWEvent* event) { manager->ShowCtlDlg(); }
+void VidDlg::OnMod(AWEvent* event) { manager->ShowModDlg(); }
+
+// +--------------------------------------------------------------------+
+
+void
+VidDlg::OnApply(AWEvent* event)
+{
+ manager->ApplyOptions();
+}
+
+void
+VidDlg::OnCancel(AWEvent* event)
+{
+ manager->CancelOptions();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VidDlg::Apply()
+{
+ if (closed) return;
+
+ int w = 800;
+ int h = 600;
+ int d = 32;
+ int a = 1;
+ int g = 128;
+ int t = 2048;
+ float bias = 0;
+
+ const char* mode_desc = mode->GetSelectedItem();
+
+ if (strstr(mode_desc, "800 x 600")) {
+ w = 800;
+ h = 600;
+ }
+ else if (strstr(mode_desc, "1024 x 768")) {
+ w = 1024;
+ h = 768;
+ }
+ else if (strstr(mode_desc, "1152 x 864")) {
+ w = 1152;
+ h = 864;
+ }
+ else if (strstr(mode_desc, "1280 x 800")) {
+ w = 1280;
+ h = 800;
+ }
+ else if (strstr(mode_desc, "1280 x 960")) {
+ w = 1280;
+ h = 960;
+ }
+ else if (strstr(mode_desc, "1280 x 1024")) {
+ w = 1280;
+ h = 1024;
+ }
+ else if (strstr(mode_desc, "1440 x 900")) {
+ w = 1440;
+ h = 900;
+ }
+ else if (strstr(mode_desc, "1600 x 900")) {
+ w = 1600;
+ h = 900;
+ }
+ else if (strstr(mode_desc, "1600 x 1200")) {
+ w = 1600;
+ h = 1200;
+ }
+
+ if (strstr(mode_desc, "x 16"))
+ d = 16;
+ else if (strstr(mode_desc, "x 32"))
+ d = 32;
+
+ if (selected_tex_size)
+ t = (int) pow(2, selected_tex_size + 6);
+
+ bool video_change = false;
+
+ Video* video = Game::GetVideo();
+ if (video) {
+ const VideoSettings* vs = video->GetVideoSettings();
+
+ if (vs)
+ bias = vs->depth_bias;
+
+ if (video->IsFullScreen()) {
+ if (video->Width() != w)
+ video_change = true;
+
+ if (video->Height() != h)
+ video_change = true;
+
+ if (video->Depth() != d)
+ video_change = true;
+ }
+ else if (vs) {
+ w = vs->fullscreen_mode.width;
+ h = vs->fullscreen_mode.height;
+
+ if (vs->fullscreen_mode.format == VideoMode::FMT_R5G6B5 ||
+ vs->fullscreen_mode.format == VideoMode::FMT_R5G5B5)
+ d = 16;
+ else
+ d = 32;
+ }
+
+ if (Game::MaxTexSize() != t)
+ video_change = true;
+ }
+
+ FILE* f = 0;
+
+ if (video_change)
+ f = fopen("video2.cfg", "w");
+ else
+ f = fopen("video.cfg", "w");
+
+ if (gamma) {
+ g = gamma->GetValue();
+ }
+
+ if (f) {
+ fprintf(f, "VIDEO\n\n");
+ fprintf(f, "width: %4d\n", w);
+ fprintf(f, "height: %4d\n", h);
+ fprintf(f, "depth: %4d\n", d);
+ fprintf(f, "\n");
+ fprintf(f, "max_tex: %d\n", (int) pow(2, 6 + selected_tex_size));
+ fprintf(f, "primary3D: %s\n", (a>0)?"true":"false");
+ fprintf(f, "gamma: %4d\n", g);
+ fprintf(f, "\n");
+ fprintf(f, "terrain_detail_level: %d\n", selected_detail + 2);
+ fprintf(f, "terrain_texture_enable: %s\n", selected_texture ? "true" : "false");
+ fprintf(f, "\n");
+ fprintf(f, "shadows: %s\n", shadows->GetSelectedIndex() ? "true" : "false");
+ fprintf(f, "spec_maps: %s\n", spec_maps->GetSelectedIndex() ? "true" : "false");
+ fprintf(f, "bump_maps: %s\n", bump_maps->GetSelectedIndex() ? "true" : "false");
+ fprintf(f, "bias: %f\n", bias);
+ fprintf(f, "\n");
+ fprintf(f, "flare: %s\n", lens_flare->GetSelectedIndex() ? "true" : "false");
+ fprintf(f, "corona: %s\n", corona->GetSelectedIndex() ? "true" : "false");
+ fprintf(f, "nebula: %s\n", nebula->GetSelectedIndex() ? "true" : "false");
+ fprintf(f, "dust: %d\n", dust->GetSelectedIndex());
+
+ if (CameraDirector::GetRangeLimit() != 300e3)
+ fprintf(f, " cam_range_max: %f,\n", CameraDirector::GetRangeLimit());
+
+ fclose(f);
+ }
+
+ Starshatter* stars = Starshatter::GetInstance();
+
+ if (stars) {
+ if (video_change)
+ stars->RequestChangeVideo();
+ else
+ stars->LoadVideoConfig("video.cfg");
+ }
+
+ closed = true;
+}
+
+void
+VidDlg::Cancel()
+{
+ Game::SetGammaLevel(orig_gamma);
+ closed = true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VidDlg::BuildModeList()
+{
+ char mode_desc[32];
+ Starshatter* stars = Starshatter::GetInstance();
+
+ mode->ClearItems();
+ selected_mode = 0;
+
+ if (Game::DisplayModeSupported( 800, 600, 16)) mode->AddItem("800 x 600 x 16");
+ if (Game::DisplayModeSupported( 800, 600, 32)) mode->AddItem("800 x 600 x 32");
+
+ if (Game::DisplayModeSupported(1024, 768, 16)) mode->AddItem("1024 x 768 x 16");
+ if (Game::DisplayModeSupported(1024, 768, 32)) mode->AddItem("1024 x 768 x 32");
+
+ if (Game::DisplayModeSupported(1152, 864, 16)) mode->AddItem("1152 x 864 x 16");
+ if (Game::DisplayModeSupported(1152, 864, 32)) mode->AddItem("1152 x 864 x 32");
+
+ if (Game::DisplayModeSupported(1280, 800, 16)) mode->AddItem("1280 x 800 x 16");
+ if (Game::DisplayModeSupported(1280, 800, 32)) mode->AddItem("1280 x 800 x 32");
+
+ if (Game::DisplayModeSupported(1280, 960, 16)) mode->AddItem("1280 x 960 x 16");
+ if (Game::DisplayModeSupported(1280, 960, 32)) mode->AddItem("1280 x 960 x 32");
+
+ if (Game::DisplayModeSupported(1280,1024, 16)) mode->AddItem("1280 x 1024 x 16");
+ if (Game::DisplayModeSupported(1280,1024, 32)) mode->AddItem("1280 x 1024 x 32");
+
+ if (Game::DisplayModeSupported(1440, 900, 16)) mode->AddItem("1440 x 900 x 16");
+ if (Game::DisplayModeSupported(1440, 900, 32)) mode->AddItem("1440 x 900 x 32");
+
+ if (Game::DisplayModeSupported(1600, 900, 16)) mode->AddItem("1600 x 900 x 16");
+ if (Game::DisplayModeSupported(1600, 900, 32)) mode->AddItem("1600 x 900 x 32");
+
+ if (Game::DisplayModeSupported(1600,1200, 16)) mode->AddItem("1600 x 1200 x 16");
+ if (Game::DisplayModeSupported(1600,1200, 32)) mode->AddItem("1600 x 1200 x 32");
+
+ if (Game::DisplayModeSupported(1680,1050, 16)) mode->AddItem("1680 x 1050 x 16");
+ if (Game::DisplayModeSupported(1680,1050, 32)) mode->AddItem("1680 x 1050 x 32");
+
+ Video* video = Game::GetVideo();
+
+ if (stars && video) {
+ switch (video->Width()) {
+ case 800: strcpy(mode_desc, "800 x 600 x "); break;
+ default:
+ case 1024: strcpy(mode_desc, "1024 x 768 x "); break;
+ case 1152: strcpy(mode_desc, "1152 x 864 x "); break;
+ case 1280:
+ if (video->Height() < 900)
+ strcpy(mode_desc, "1280 x 800 x ");
+ if (video->Height() < 1000)
+ strcpy(mode_desc, "1280 x 960 x ");
+ else
+ strcpy(mode_desc, "1280 x 1024 x ");
+ break;
+ case 1440: strcpy(mode_desc, "1440 x 900 x "); break;
+ case 1600:
+ if (video->Height() < 1000)
+ strcpy(mode_desc, "1600 x 900 x ");
+ else
+ strcpy(mode_desc, "1600 x 1200 x ");
+ break;
+ }
+
+ switch (video->Depth()) {
+ default:
+ case 8: strcat(mode_desc, "8"); break;
+ case 16: strcat(mode_desc, "16"); break;
+ case 32: strcat(mode_desc, "32"); break;
+ }
+
+ for (int i = 0; i < mode->GetCount(); i++) {
+ if (!strcmp(mode->GetItem(i), mode_desc))
+ selected_mode = i;
+ }
+ }
+}
diff --git a/Stars45/VidDlg.h b/Stars45/VidDlg.h
new file mode 100644
index 0000000..c0f8ea1
--- /dev/null
+++ b/Stars45/VidDlg.h
@@ -0,0 +1,106 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: VidDlg.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Main Menu Dialog Active Window class
+*/
+
+#ifndef VidDlg_h
+#define VidDlg_h
+
+#include "Types.h"
+#include "FormWindow.h"
+#include "Bitmap.h"
+#include "Button.h"
+#include "ComboBox.h"
+#include "ListBox.h"
+#include "Slider.h"
+#include "Font.h"
+
+// +--------------------------------------------------------------------+
+
+class BaseScreen;
+class Starshatter;
+
+// +--------------------------------------------------------------------+
+
+class VidDlg : public FormWindow
+{
+public:
+ VidDlg(Screen* s, FormDef& def, BaseScreen* mgr);
+ virtual ~VidDlg();
+
+ virtual void RegisterControls();
+ virtual void Show();
+ virtual void ExecFrame();
+
+ // Operations:
+ virtual void OnTexSize(AWEvent* event);
+ virtual void OnMode(AWEvent* event);
+ virtual void OnDetail(AWEvent* event);
+ virtual void OnTexture(AWEvent* event);
+ virtual void OnGamma(AWEvent* event);
+
+ virtual void Apply();
+ virtual void Cancel();
+
+ virtual void OnApply(AWEvent* event);
+ virtual void OnCancel(AWEvent* event);
+
+ virtual void OnAudio(AWEvent* event);
+ virtual void OnVideo(AWEvent* event);
+ virtual void OnOptions(AWEvent* event);
+ virtual void OnControls(AWEvent* event);
+ virtual void OnMod(AWEvent* event);
+
+protected:
+ virtual void BuildModeList();
+
+ BaseScreen* manager;
+ Starshatter* stars;
+
+ ComboBox* mode;
+ ComboBox* tex_size;
+ ComboBox* detail;
+ ComboBox* texture;
+
+ ComboBox* shadows;
+ ComboBox* bump_maps;
+ ComboBox* spec_maps;
+
+ ComboBox* lens_flare;
+ ComboBox* corona;
+ ComboBox* nebula;
+ ComboBox* dust;
+
+ Slider* gamma;
+
+ Button* aud_btn;
+ Button* vid_btn;
+ Button* opt_btn;
+ Button* ctl_btn;
+ Button* mod_btn;
+
+ Button* apply;
+ Button* cancel;
+
+ int selected_render;
+ int selected_card;
+ int selected_tex_size;
+ int selected_mode;
+ int selected_detail;
+ int selected_texture;
+ int orig_gamma;
+
+ bool closed;
+};
+
+#endif VidDlg_h
+
diff --git a/Stars45/Weapon.cpp b/Stars45/Weapon.cpp
new file mode 100644
index 0000000..163a71a
--- /dev/null
+++ b/Stars45/Weapon.cpp
@@ -0,0 +1,1184 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Weapon.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Weapon class
+*/
+
+#include "MemDebug.h"
+#include "Weapon.h"
+#include "Shot.h"
+#include "Drone.h"
+#include "Contact.h"
+#include "Ship.h"
+#include "Sim.h"
+#include "SimEvent.h"
+#include "Random.h"
+
+#include "NetGame.h"
+#include "NetUtil.h"
+
+#include "Game.h"
+#include "Solid.h"
+
+// +----------------------------------------------------------------------+
+
+Weapon::Weapon(WeaponDesign* d, int nmuz, Vec3* muzzles, double az, double el)
+ : System(WEAPON, d->type, d->name, d->value,
+ d->capacity, d->capacity, d->recharge_rate),
+ design(d), group(d->group), ammo(-1), ripple_count(0),
+ aim_azimuth((float) az), aim_elevation((float) el),
+ old_azimuth(0.0f), old_elevation(0.0f), aim_time(0),
+ enabled(true), refire(0.0f),
+ mass(d->carry_mass), resist(d->carry_resist),
+ guided(d->guided), shot_speed(d->speed),
+ active_barrel(0), locked(false), centered(false), firing(false), blocked(false),
+ index(0), target(0), subtarget(0), beams(0), orders(MANUAL),
+ control(SINGLE_FIRE), sweep(SWEEP_TIGHT), turret(0), turret_base(0)
+{
+ ZeroMemory(visible_stores, sizeof(visible_stores));
+
+ if (design->primary)
+ abrv = Game::GetText("sys.weapon.primary.abrv");
+ else
+ abrv = Game::GetText("sys.weapon.secondary.abrv");
+
+ nbarrels = nmuz;
+
+ if (nbarrels > MAX_BARRELS)
+ nbarrels = MAX_BARRELS;
+
+ if (nbarrels == 0 && design->nbarrels > 0) {
+ nbarrels = design->nbarrels;
+
+ for (int i = 0; i < nbarrels; i++)
+ muzzle_pts[i] = rel_pts[i] = design->muzzle_pts[i] * design->scale;
+
+ ammo = design->ammo * nbarrels;
+ }
+ else if (nbarrels == 1 && design->nstores > 0) {
+ nbarrels = design->nstores;
+
+ for (int i = 0; i < nbarrels; i++)
+ muzzle_pts[i] = rel_pts[i] = (muzzles[0] + design->attachments[i]);
+
+ ammo = nbarrels;
+ }
+ else {
+ for (int i = 0; i < nbarrels; i++)
+ muzzle_pts[i] = rel_pts[i] = muzzles[i];
+
+ ammo = design->ammo * nbarrels;
+ }
+
+ if (design->syncro)
+ active_barrel = -1;
+
+ emcon_power[0] = 0;
+ emcon_power[1] = 0;
+ emcon_power[2] = 100;
+
+ aim_az_max = design->aim_az_max;
+ aim_az_min = design->aim_az_min;
+ aim_az_rest = design->aim_az_rest;
+
+ aim_el_max = design->aim_el_max;
+ aim_el_min = design->aim_el_min;
+ aim_el_rest = design->aim_el_rest;
+}
+
+// +----------------------------------------------------------------------+
+
+Weapon::Weapon(const Weapon& w)
+ : System(w), design(w.design), ammo(-1), ripple_count(0),
+ enabled(true), refire(0.0f),
+ mass(w.mass), resist(w.resist),
+ aim_azimuth(w.aim_azimuth), aim_elevation(w.aim_elevation),
+ old_azimuth(0.0f), old_elevation(0.0f), aim_time(0),
+ guided(w.guided), shot_speed(w.shot_speed),
+ active_barrel(0), locked(false), centered(false), firing(false), blocked(false),
+ target(0), subtarget(0), beams(0), orders(MANUAL),
+ control(SINGLE_FIRE), sweep(SWEEP_TIGHT), group(w.group),
+ aim_az_max(w.aim_az_max), aim_az_min(w.aim_az_min), aim_az_rest(w.aim_az_rest),
+ aim_el_max(w.aim_el_max), aim_el_min(w.aim_el_min), aim_el_rest(w.aim_el_rest),
+ turret(0), turret_base(0)
+{
+ Mount(w);
+ ZeroMemory(visible_stores, sizeof(visible_stores));
+
+ nbarrels = w.nbarrels;
+
+ for (int i = 0; i < nbarrels; i++) {
+ muzzle_pts[i] = rel_pts[i] = w.muzzle_pts[i];
+ }
+
+ ammo = design->ammo * nbarrels;
+
+ if (design->syncro)
+ active_barrel = -1;
+
+ if (design->beam) {
+ beams = new(__FILE__,__LINE__) Shot* [nbarrels];
+ ZeroMemory(beams, sizeof(Shot*) * nbarrels);
+ }
+
+ if (aim_az_rest >= 2*PI)
+ aim_az_rest = design->aim_az_rest;
+
+ if (aim_el_rest >= 2*PI)
+ aim_el_rest = design->aim_el_rest;
+}
+
+// +--------------------------------------------------------------------+
+
+Weapon::~Weapon()
+{
+ if (beams) {
+ for (int i = 0; i < nbarrels; i++) {
+ if (beams[i]) {
+ Ignore(beams[i]);
+ delete beams[i];
+ beams[i] = 0;
+ }
+ }
+
+ delete [] beams;
+ }
+
+ GRAPHIC_DESTROY(turret);
+ GRAPHIC_DESTROY(turret_base);
+
+ for (int i = 0; i < MAX_BARRELS; i++)
+ GRAPHIC_DESTROY(visible_stores[i]);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Weapon::IsPrimary() const
+{
+ return design->primary;
+}
+
+bool
+Weapon::IsDrone() const
+{
+ return design->drone;
+}
+
+bool
+Weapon::IsDecoy() const
+{
+ return design->decoy_type != 0;
+}
+
+bool
+Weapon::IsProbe() const
+{
+ return design->probe != 0;
+}
+
+bool
+Weapon::IsMissile() const
+{
+ return !design->primary;
+}
+
+bool
+Weapon::IsBeam() const
+{
+ return design->beam;
+}
+
+// +--------------------------------------------------------------------+
+
+Shot*
+Weapon::GetBeam(int i)
+{
+ if (beams && i >= 0 && i < nbarrels)
+ return beams[i];
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Weapon::SetOwner(Ship* s)
+{
+ ship = s;
+
+ if (design->turret_model) {
+ Solid* t = new(__FILE__,__LINE__) Solid;
+ t->UseModel(design->turret_model);
+ turret = t;
+ }
+
+ if (design->turret_base_model) {
+ Solid* t = new(__FILE__,__LINE__) Solid;
+ t->UseModel(design->turret_base_model);
+ turret_base = t;
+ }
+
+ if (!design->primary &&
+ design->visible_stores &&
+ ammo == nbarrels &&
+ design->shot_model != 0)
+ {
+ for (int i = 0; i < nbarrels; i++) {
+ Solid* s = new(__FILE__,__LINE__) Solid;
+ s->UseModel(design->shot_model);
+
+ visible_stores[i] = s;
+ }
+ }
+}
+
+Solid*
+Weapon::GetTurret()
+{
+ return turret;
+}
+
+Solid*
+Weapon::GetTurretBase()
+{
+ return turret_base;
+}
+
+Solid*
+Weapon::GetVisibleStore(int i)
+{
+ if (i >= 0 && i < MAX_BARRELS)
+ return visible_stores[i];
+
+ return 0;
+}
+
+void
+Weapon::SetAmmo(int a)
+{
+ if (a >= 0) {
+ if (active_barrel >= 0 && design->visible_stores) {
+ while (a < ammo) {
+ if (active_barrel >= nbarrels)
+ active_barrel = 0;
+
+ if (visible_stores[active_barrel]) {
+ GRAPHIC_DESTROY(visible_stores[active_barrel]);
+ active_barrel++;
+ ammo--;
+ }
+ }
+ }
+
+ ammo = a;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Weapon::ExecFrame(double seconds)
+{
+ System::ExecFrame(seconds);
+
+ if (refire > 0)
+ refire -= (float) seconds;
+
+ locked = false;
+ centered = false;
+
+ if (!ship)
+ return;
+
+ if (orders == POINT_DEFENSE && enabled)
+ SelectTarget();
+
+ if (beams && !target) {
+ for (int i = 0; i < nbarrels; i++) {
+ if (beams[i]) {
+ // aim beam straight:
+ Aim();
+ SetBeamPoints(false);
+ return;
+ }
+ }
+ }
+
+ if (design->self_aiming) {
+ Track(target, subtarget);
+ }
+ else if (turret) {
+ ZeroAim();
+ }
+
+ if (ship->CheckFire())
+ return;
+
+ // aim beam at target:
+ bool aim_beams = false;
+
+ if (beams) {
+ for (int i = 0; i < nbarrels; i++) {
+ if (beams[i]) {
+ aim_beams = true;
+ SetBeamPoints(true);
+ break;
+ }
+ }
+ }
+
+ if (!aim_beams) {
+ if (ripple_count > 0) {
+ if (Fire())
+ ripple_count--;
+ }
+
+ else if (locked && !blocked) {
+ if (!ship->IsHostileTo(target))
+ return;
+
+ if (orders == AUTO && centered) {
+ if (energy >= design->charge &&
+ (ammo < 0 || target && target->Integrity() >= 1) &&
+ objective.length() < design->max_range)
+ Fire();
+ }
+
+ else if (orders == POINT_DEFENSE) {
+ if (energy >= design->min_charge &&
+ (ammo < 0 || target && target->Integrity() >= 1) &&
+ objective.length() < design->max_range)
+ Fire();
+ }
+ }
+ }
+}
+
+// +----------------------------------------------------------------------+
+
+void
+Weapon::Distribute(double delivered_energy, double seconds)
+{
+ if (UsesWatts()) {
+ if (seconds < 0.01)
+ seconds = 0.01;
+
+ // convert Joules to Watts:
+ energy = (float) (delivered_energy/seconds);
+ }
+
+ else if (!Game::Paused()) {
+ energy += (float) (delivered_energy * 1.25);
+
+ if (energy > capacity)
+ energy = capacity;
+
+ else if (energy < 0)
+ energy = 0.0f;
+ }
+}
+
+
+// +--------------------------------------------------------------------+
+
+bool
+Weapon::Update(SimObject* obj)
+{
+ if (obj == target) {
+ target = 0;
+ }
+
+ else if (beams) {
+ for (int i = 0; i < nbarrels; i++)
+ if (obj == beams[i])
+ beams[i] = 0;
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+Weapon::GetObserverName() const
+{
+ static char name[256];
+ sprintf(name, "Weapon %s", design->name);
+ return name;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Weapon::SetFiringOrders(int o)
+{
+ if (o >= MANUAL && o <= POINT_DEFENSE)
+ orders = o;
+}
+
+void
+Weapon::SetControlMode(int m)
+{
+ if (m >= SINGLE_FIRE && m <= SALVO_FIRE)
+ control = m;
+}
+
+void
+Weapon::SetSweep(int s)
+{
+ if (s >= SWEEP_NONE && s <= SWEEP_WIDE)
+ sweep = s;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Weapon::CanTarget(DWORD classification) const
+{
+ return (design->target_type & classification) ? true : false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Weapon::SetTarget(SimObject* targ, System* sub)
+{
+ // check self targeting:
+ if (targ == (SimObject*) ship)
+ return;
+
+ // check target class filter:
+ if (targ) {
+ switch (targ->Type()) {
+ case SimObject::SIM_SHIP: {
+ Ship* tgt_ship = (Ship*) targ;
+
+ if ((tgt_ship->Class() & design->target_type) == 0)
+ return;
+ }
+ break;
+
+ case SimObject::SIM_SHOT:
+ return;
+
+ case SimObject::SIM_DRONE: {
+ if ((design->target_type & Ship::DRONE) == 0)
+ return;
+ }
+ break;
+
+ default:
+ return;
+ }
+ }
+
+ // if ok, target this object:
+ if (target != targ) {
+ target = targ;
+
+ if (target)
+ Observe(target);
+ }
+
+ subtarget = sub;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Weapon::SelectTarget()
+{
+ bool select_locked = false;
+ SimObject* targ = 0;
+ double dist = 1e9;
+ double az = 0;
+ double el = 0;
+
+ if (ammo && enabled && (availability > crit_level)) {
+ ZeroAim();
+
+ ListIter<Contact> contact = ship->ContactList();
+
+ // lock onto any threatening shots first (if we can):
+ if (design->target_type & Ship::DRONE) {
+ while (++contact) {
+ Shot* c_shot = contact->GetShot();
+
+ if (c_shot && contact->Threat(ship)) {
+
+ // distance from self to target:
+ double distance = Point(c_shot->Location() - muzzle_pts[0]).length();
+
+ if (distance > design->min_range &&
+ distance < design->max_range &&
+ distance < dist) {
+ // check aim basket:
+ select_locked = CanLockPoint(c_shot->Location(), az, el);
+
+ if (select_locked) {
+ targ = c_shot;
+ dist = distance;
+ }
+ }
+ }
+ }
+ }
+
+ // lock onto a threatening ship only if it is (much) closer:
+ dist *= 0.2;
+ contact.reset();
+ while (++contact) {
+ Ship* c_ship = contact->GetShip();
+
+ if (!c_ship) continue;
+
+ // can we lock onto this target?
+ if ((c_ship->IsRogue() || c_ship->GetIFF() > 0 && c_ship->GetIFF() != ship->GetIFF()) &&
+ (c_ship->Class() & design->target_type) &&
+ c_ship->Weapons().size() > 0) {
+ // distance from self to target:
+ double distance = Point(c_ship->Location() - muzzle_pts[0]).length();
+
+ if (distance < design->max_range && distance < dist) {
+ // check aim basket:
+ select_locked = CanLockPoint(c_ship->Location(), az, el);
+
+ if (select_locked) {
+ targ = c_ship;
+ dist = distance;
+ }
+ }
+ }
+ }
+ }
+
+ if (!ammo || !enabled) {
+ SetTarget(0,0);
+ locked = false;
+ }
+
+ else {
+ SetTarget(targ, 0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Weapon::Track(SimObject* targ, System* sub)
+{
+ if (ammo && enabled && (availability > crit_level)) {
+ firing = 0;
+ Aim();
+ }
+ else if (turret) {
+ ZeroAim();
+ }
+
+ return locked;
+}
+
+// +--------------------------------------------------------------------+
+
+Shot*
+Weapon::Fire()
+{
+ if (Game::Paused())
+ return 0;
+
+ if (ship && ship->CheckFire())
+ return 0;
+
+ if (ship->IsStarship() && target && !centered)
+ return 0;
+
+ if (beams && active_barrel >= 0 && active_barrel < nbarrels && beams[active_barrel])
+ return 0;
+
+ Shot* shot = 0;
+
+ if (ammo && enabled &&
+ (refire <= 0) && (energy > design->min_charge) &&
+ (availability > crit_level)) {
+
+ refire = design->refire_delay;
+
+ NetGame* net_game = NetGame::GetInstance();
+ bool net_client = net_game ? net_game->IsClient() : false;
+
+ // all barrels
+ if (active_barrel < 0) {
+ if (net_client || IsPrimary())
+ NetUtil::SendWepTrigger(this, nbarrels);
+
+ if (!net_game || IsPrimary()) {
+ for (int i = 0; i < nbarrels; i++)
+ shot = FireBarrel(i);
+ }
+
+ else if (net_game && net_game->IsServer() && IsMissile()) {
+ for (int i = 0; i < nbarrels; i++) {
+ shot = FireBarrel(i);
+ NetUtil::SendWepRelease(this, shot);
+ }
+ }
+ }
+
+ // single barrel
+ else {
+ if (net_client || IsPrimary())
+ NetUtil::SendWepTrigger(this, nbarrels);
+
+ if (!net_game || IsPrimary()) {
+ shot = FireBarrel(active_barrel++);
+ }
+
+ else if (net_game && net_game->IsServer() && IsMissile()) {
+ shot = FireBarrel(active_barrel++);
+ NetUtil::SendWepRelease(this, shot);
+ }
+
+ if (active_barrel >= nbarrels) {
+ active_barrel = 0;
+ refire += design->salvo_delay;
+ }
+ }
+
+ if (design->ripple_count > 0 && ripple_count <= 0)
+ ripple_count = design->ripple_count-1;
+
+ if (status != NOMINAL)
+ refire *= 2;
+ }
+
+ return shot;
+}
+
+// +--------------------------------------------------------------------+
+
+Shot*
+Weapon::NetFirePrimary(SimObject* tgt, System* sub, int count)
+{
+ Shot* shot = 0;
+
+ if (!IsPrimary() || Game::Paused())
+ return shot;
+
+ if (active_barrel < 0 || active_barrel >= nbarrels)
+ active_barrel = 0;
+
+ target = tgt;
+ subtarget = sub;
+ aim_time = 0;
+
+ for (int i = 0; i < count; i++) {
+ shot = FireBarrel(active_barrel++);
+
+ if (active_barrel >= nbarrels) {
+ active_barrel = 0;
+ refire += design->salvo_delay;
+ }
+ }
+
+ if (target)
+ Observe(target);
+
+ return shot;
+}
+
+Shot*
+Weapon::NetFireSecondary(SimObject* tgt, System* sub, DWORD objid)
+{
+ Shot* shot = 0;
+
+ if (IsPrimary() || Game::Paused())
+ return shot;
+
+ if (active_barrel < 0 || active_barrel >= nbarrels)
+ active_barrel = 0;
+
+ target = tgt;
+ subtarget = sub;
+ aim_time = 0;
+
+ shot = FireBarrel(active_barrel++);
+
+ if (active_barrel >= nbarrels) {
+ active_barrel = 0;
+ refire += design->salvo_delay;
+ }
+
+ if (shot)
+ shot->SetObjID(objid);
+
+ if (target)
+ Observe(target);
+
+ return shot;
+}
+
+// +--------------------------------------------------------------------+
+
+Shot*
+Weapon::FireBarrel(int n)
+{
+ const Point& base_vel = ship->Velocity();
+ Shot* shot = 0;
+ SimRegion* region = ship->GetRegion();
+
+ if (!region || n < 0 || n >= nbarrels || Game::Paused())
+ return 0;
+
+ firing = 1;
+ Aim();
+
+ Camera rail_cam;
+ rail_cam.Clone(aim_cam);
+
+ Point shotpos = muzzle_pts[n];
+ if (design->length > 0)
+ shotpos = shotpos + aim_cam.vpn() * design->length;
+
+ // guns may be slewed towards target:
+ if (design->primary) {
+ shot = CreateShot(shotpos, aim_cam, design, ship);
+
+ if (shot) {
+ shot->SetVelocity(shot->Velocity() + base_vel);
+ }
+ }
+
+ // missiles always launch in rail direction:
+ else {
+ // unless they are on a mobile launcher
+ if (turret && design->self_aiming) {
+ shot = CreateShot(shotpos, aim_cam, design, ship);
+ shot->SetVelocity(base_vel);
+ }
+ else {
+ shot = CreateShot(shotpos, rail_cam, design, ship);
+ if (shot /* && !turret */) {
+ Matrix orient = ship->Cam().Orientation();
+ if (aim_azimuth != 0) orient.Yaw(aim_azimuth);
+ if (aim_elevation != 0) orient.Pitch(aim_elevation);
+
+ Point eject = design->eject * orient;
+ shot->SetVelocity(base_vel + eject);
+ }
+ }
+
+ if (shot && visible_stores[n]) {
+ GRAPHIC_DESTROY(visible_stores[n]);
+ }
+ }
+
+ if (shot) {
+ if (ammo > 0)
+ ammo--;
+
+ if (guided && target)
+ shot->SeekTarget(target, subtarget);
+
+ float shot_load;
+ if (energy > design->charge)
+ shot_load = design->charge;
+ else
+ shot_load = energy;
+
+ energy -= shot_load;
+ shot->SetCharge(shot_load * availability);
+
+ if (target && design->flak && !design->guided) {
+ double speed = shot->Velocity().length();
+ double range = (target->Location() - shot->Location()).length();
+
+ if (range > design->min_range && range < design->max_range) {
+ shot->SetFuse(range / speed);
+ }
+ }
+
+ region->InsertObject(shot);
+
+ if (beams) {
+ beams[n] = shot;
+ Observe(beams[n]);
+
+ // aim beam at target:
+ SetBeamPoints(true);
+ }
+
+ if (ship) {
+ ShipStats* stats = ShipStats::Find(ship->Name());
+
+ if (design->primary)
+ stats->AddGunShot();
+ else if (design->decoy_type == 0 && design->damage > 0)
+ stats->AddMissileShot();
+ }
+ }
+
+ return shot;
+}
+
+Shot*
+Weapon::CreateShot(const Point& loc, const Camera& cam, WeaponDesign* dsn, const Ship* own)
+{
+ if (dsn->drone)
+ return new(__FILE__,__LINE__) Drone(loc, cam, dsn, own);
+
+ else
+ return new(__FILE__,__LINE__) Shot(loc, cam, dsn, own);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Weapon::Orient(const Physical* rep)
+{
+ System::Orient(rep);
+
+ const Matrix& orientation = rep->Cam().Orientation();
+ Point loc = rep->Location();
+
+ // align graphics with camera:
+ if (turret) {
+ if (!design->self_aiming)
+ ZeroAim();
+
+ const Matrix& aim = aim_cam.Orientation();
+
+ turret->MoveTo(mount_loc);
+ turret->SetOrientation(aim);
+
+ if (turret_base) {
+ Matrix base = orientation;
+
+ if (design->turret_axis == 1) {
+ base.Pitch(aim_elevation);
+ base.Pitch(old_elevation);
+ }
+ else {
+ base.Yaw(aim_azimuth);
+ base.Yaw(old_azimuth);
+ }
+
+ turret_base->MoveTo(mount_loc);
+ turret_base->SetOrientation(base);
+ }
+
+ for (int i = 0; i < nbarrels; i++) {
+ muzzle_pts[i] = (rel_pts[i] * aim) + mount_loc;
+
+ if (visible_stores[i]) {
+ visible_stores[i]->SetOrientation(aim);
+ visible_stores[i]->MoveTo(muzzle_pts[i]);
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < nbarrels; i++) {
+ muzzle_pts[i] = (rel_pts[i] * orientation) + loc;
+
+ if (visible_stores[i]) {
+ visible_stores[i]->SetOrientation(orientation);
+ visible_stores[i]->MoveTo(muzzle_pts[i]);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Weapon::SetBeamPoints(bool aim)
+{
+ for (int i = 0; i < nbarrels; i++) {
+ if (beams && beams[i]) {
+ Point from = muzzle_pts[i];
+ Point to;
+ double len = design->length;
+
+ if (len < 1 || len > 1e7)
+ len = design->max_range;
+
+ if (len < 1)
+ len = 3e5;
+
+ else if (len > 1e7)
+ len = 1e7;
+
+ // always use the aim cam, to enforce slew_rate
+ to = from + aim_cam.vpn() * len;
+
+ beams[i]->SetBeamPoints(from, to);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Weapon::Aim()
+{
+ locked = false;
+ centered = false;
+
+ FindObjective();
+
+ if (target) {
+ double az = 0;
+ double el = 0;
+
+ locked = CanLockPoint(obj_w, az, el, &objective);
+
+ double spread_az = design->spread_az;
+ double spread_el = design->spread_el;
+
+ // beam sweep target:
+ if (design->beam) {
+ double factor = 0;
+ double az_phase = 0;
+ double el_phase = 0;
+
+ if (target->Type() == SimObject::SIM_SHIP) {
+ Ship* s = (Ship*) target;
+
+ if (s->IsStarship()) {
+ switch (sweep) {
+ default:
+ case SWEEP_NONE: factor = 0; break;
+ case SWEEP_TIGHT: factor = 1; break;
+ case SWEEP_WIDE: factor = 2; break;
+ }
+ }
+ }
+
+ if (factor > 0) {
+ factor *= atan2(target->Radius(), (double) objective.z);
+
+ for (int i = 0; i < nbarrels; i++) {
+ if (beams && beams[i]) {
+ az_phase = sin(beams[i]->Life() * 0.4 * PI);
+ el_phase = sin(beams[i]->Life() * 1.0 * PI);
+ break;
+ }
+ }
+
+ az += factor * spread_az * az_phase;
+ el += factor * spread_el * el_phase * 0.25;
+ }
+ }
+
+ else if (!design->beam) {
+ if (spread_az > 0)
+ az += Random(-spread_az, spread_az);
+
+ if (spread_el > 0)
+ el += Random(-spread_el, spread_el);
+ }
+
+ AimTurret(az, -el);
+
+ // check range for guided weapons:
+ if (locked && guided) {
+ double range = objective.length();
+
+ if (range > design->max_track)
+ locked = false;
+
+ else if (range > design->max_range) {
+ if (firing) {
+ if (RandomChance(1,4)) // 1 in 4 chance of locking anyway
+ locked = false;
+ }
+ else {
+ locked = false;
+ }
+ }
+ }
+
+ if (locked) {
+ Point tloc = target->Location();
+ tloc = Transform(tloc);
+
+ if (tloc.z > 1) {
+ az = atan2(fabs(tloc.x), tloc.z);
+ el = atan2(fabs(tloc.y), tloc.z);
+
+ double firing_cone = 10*DEGREES;
+
+ if (orders == MANUAL)
+ firing_cone = 30*DEGREES;
+
+ if (az < firing_cone && el < firing_cone)
+ centered = true;
+ }
+ }
+ }
+ else {
+ AimTurret(aim_az_rest, -aim_el_rest);
+ }
+}
+
+void
+Weapon::FindObjective()
+{
+ ZeroAim();
+
+ if (!target || !design->self_aiming) {
+ objective = Point();
+ return;
+ }
+
+ obj_w = target->Location();
+
+ if (subtarget) {
+ obj_w = subtarget->MountLocation();
+ }
+ else if (target->Type() == SimObject::SIM_SHIP) {
+ Ship* tgt_ship = (Ship*) target;
+
+ if (tgt_ship->IsGroundUnit())
+ obj_w += Point(0,150,0);
+ }
+
+ if (!design->beam && shot_speed > 0) {
+ // distance from self to target:
+ double distance = Point(obj_w - muzzle_pts[0]).length();
+
+ // TRUE shot speed is relative to ship speed:
+ Point eff_shot_vel = ship->Velocity() + aim_cam.vpn() * shot_speed - target->Velocity();
+ double eff_shot_speed = eff_shot_vel.length();
+
+ // time to reach target:
+ double time = distance / eff_shot_speed;
+
+ // where the target will be when the shot reaches it:
+ obj_w += (target->Velocity() - ship->Velocity()) * time +
+ target->Acceleration() * 0.25 * time * time;
+ }
+
+ // transform into camera coords:
+ objective = Transform(obj_w);
+}
+
+Point
+Weapon::Transform(const Point& pt)
+{
+ Point result;
+
+ Point obj_t = pt - aim_cam.Pos();
+ result.x = obj_t * aim_cam.vrt();
+ result.y = obj_t * aim_cam.vup();
+ result.z = obj_t * aim_cam.vpn();
+
+ return result;
+}
+
+bool
+Weapon::CanLockPoint(const Point& test, double& az, double& el, Point* obj)
+{
+ Point pt = Transform(test);
+ bool locked = true;
+
+ // first compute az:
+ if (fabs(pt.z) < 0.1) pt.z = 0.1;
+ az = atan(pt.x / pt.z);
+ if (pt.z < 0) az -= PI;
+ if (az < -PI) az += 2*PI;
+
+ // then, rotate target into az-coords to compute el:
+ Camera tmp;
+ tmp.Clone(aim_cam);
+ aim_cam.Yaw(az);
+ pt = Transform(test);
+ aim_cam.Clone(tmp);
+
+ if (fabs(pt.z) < 0.1) pt.z = 0.1;
+ el = atan(pt.y / pt.z);
+
+ if (obj) *obj = pt;
+
+ // is the target in the basket?
+ // clamp if necessary:
+
+ if (az > aim_az_max) {
+ az = aim_az_max;
+ locked = false;
+ }
+ else if (az < aim_az_min) {
+ az = aim_az_min;
+ locked = false;
+ }
+
+ if (el > aim_el_max) {
+ el = aim_el_max;
+ locked = false;
+ }
+ else if (el < aim_el_min) {
+ el = aim_el_min;
+ locked = false;
+ }
+
+ if (IsDrone() && guided) {
+ double firing_cone = 10*DEGREES;
+
+ if (orders == MANUAL)
+ firing_cone = 20*DEGREES;
+
+ if (az < firing_cone && el < firing_cone)
+ locked = true;
+ }
+
+ return locked;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Weapon::AimTurret(double az, double el)
+{
+ double seconds = (Game::GameTime() - aim_time) / 1000.0;
+
+ // don't let the weapon turn faster than turret slew rate:
+ double max_turn = design->slew_rate * seconds;
+
+ if (fabs(az-old_azimuth) > max_turn) {
+ if (az > old_azimuth)
+ az = old_azimuth + max_turn;
+ else
+ az = old_azimuth - max_turn;
+ }
+
+ if (fabs(el-old_elevation) > PI/2 * seconds) {
+ if (el > old_elevation)
+ el = old_elevation + max_turn;
+ else
+ el = old_elevation - max_turn;
+ }
+
+ aim_cam.Yaw(az);
+ aim_cam.Pitch(el);
+
+ old_azimuth = (float) az;
+ old_elevation = (float) el;
+
+ aim_time = Game::GameTime();
+}
+
+void
+Weapon::ZeroAim()
+{
+ aim_cam.Clone(ship->Cam());
+ if (aim_azimuth != 0) aim_cam.Yaw(aim_azimuth);
+ if (aim_elevation != 0) aim_cam.Pitch(aim_elevation);
+
+ aim_cam.MoveTo(muzzle_pts[0]);
+}
diff --git a/Stars45/Weapon.h b/Stars45/Weapon.h
new file mode 100644
index 0000000..3e4335c
--- /dev/null
+++ b/Stars45/Weapon.h
@@ -0,0 +1,204 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Weapon.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Weapon (gun or missile launcher) class
+*/
+
+#ifndef Weapon_h
+#define Weapon_h
+
+#include "Types.h"
+#include "SimObject.h"
+#include "System.h"
+#include "WeaponDesign.h"
+#include "Geometry.h"
+#include "text.h"
+
+// +--------------------------------------------------------------------+
+
+class Weapon;
+class Ship;
+class Shot;
+
+class Solid;
+
+// +--------------------------------------------------------------------+
+
+class Weapon : public System, public SimObserver
+{
+public:
+ static const char* TYPENAME() { return "Weapon"; }
+
+ enum Constants { MAX_BARRELS=8 };
+ enum Orders { MANUAL, AUTO, POINT_DEFENSE };
+ enum Control { SINGLE_FIRE, RIPPLE_FIRE, SALVO_FIRE };
+ enum Sweep { SWEEP_NONE, SWEEP_TIGHT, SWEEP_WIDE };
+
+ Weapon(WeaponDesign* d, int nmuz, Vec3* muzzles, double az=0, double el=0);
+ Weapon(const Weapon& rhs);
+ virtual ~Weapon();
+
+ int operator==(const Weapon& w) const { return this == &w; }
+
+ int Track(SimObject* targ, System* sub);
+ Shot* Fire();
+ Shot* NetFirePrimary(SimObject* targ, System* sub, int count);
+ Shot* NetFireSecondary(SimObject* targ, System* sub, DWORD objid);
+ void SetTarget(SimObject* t, System* sub);
+ void SelectTarget();
+ bool CanTarget(DWORD classification) const;
+ SimObject* GetTarget() const { return target; }
+ System* GetSubTarget() const { return subtarget; }
+ void SetFiringOrders(int o);
+ int GetFiringOrders() const { return orders; }
+ void SetControlMode(int m);
+ int GetControlMode() const { return control; }
+ void SetSweep(int s);
+ int GetSweep() const { return sweep; }
+
+ void Enable() { enabled = true; }
+ void Disable() { enabled = false; }
+
+ const WeaponDesign* Design() const { return design; }
+ const char* Group() const { return group; }
+ int Enabled() const { return enabled; }
+ int Ammo() const { return ammo; }
+ int Guided() const { return guided; }
+ int Locked() const { return locked; }
+ float Velocity() const { return shot_speed; }
+ float Mass() const { return mass*ammo; }
+ float Resistance()const { return resist*ammo; }
+ Shot* GetBeam(int i);
+
+ // needed to set proper ammo level when joining multiplayer in progress:
+ void SetAmmo(int a);
+
+ bool IsPrimary() const;
+ bool IsDrone() const;
+ bool IsDecoy() const;
+ bool IsProbe() const;
+ bool IsMissile() const;
+ bool IsBeam() const;
+
+ virtual void ExecFrame(double factor);
+ virtual void Orient(const Physical* rep);
+ virtual void Distribute(double delivered_energy, double seconds);
+
+ const Ship* Owner() const { return ship; }
+ void SetOwner(Ship* ship);
+ int GetIndex() const { return index; }
+ void SetIndex(int n) { index = n; }
+
+ Point GetAimVector() const { return aim_cam.vpn(); }
+ void SetAzimuth(double a) { aim_azimuth = (float) a; }
+ double GetAzimuth() const { return aim_azimuth; }
+ void SetElevation(double e) { aim_elevation = (float) e; }
+ double GetElevation() const { return aim_elevation; }
+
+ void SetRestAzimuth(double a) { aim_az_rest = (float) a; }
+ double GetRestAzimuth() const { return aim_az_rest; }
+ void SetRestElevation(double e) { aim_el_rest = (float) e; }
+ double GetRestElevation() const { return aim_el_rest; }
+
+ void SetAzimuthMax(double a) { aim_az_max = (float) a; }
+ double GetAzimuthMax() const { return aim_az_max; }
+ void SetAzimuthMin(double a) { aim_az_min = (float) a; }
+ double GetAzimuthMin() const { return aim_az_min; }
+
+ void SetElevationMax(double e) { aim_el_max = (float) e; }
+ double GetElevationMax() const { return aim_el_max; }
+ void SetElevationMin(double e) { aim_el_min = (float) e; }
+ double GetElevationMin() const { return aim_el_min; }
+
+ void SetGroup(const char* n) { group = n; }
+
+ bool IsBlockedFriendly() const { return blocked; }
+ void SetBlockedFriendly(bool b) { blocked = b; }
+
+ Solid* GetTurret();
+ Solid* GetTurretBase();
+ Solid* GetVisibleStore(int i);
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+protected:
+ Shot* FireBarrel(int n);
+ Shot* CreateShot(const Point& loc, const Camera& cam, WeaponDesign* dsn, const Ship* owner);
+
+ void SetBeamPoints(bool aim=false);
+ void Aim();
+ void AimTurret(double az, double el);
+ void ZeroAim();
+ void FindObjective();
+ Point Transform(const Point& pt);
+ bool CanLockPoint(const Point& test, double& az, double& el, Point* obj=0);
+
+ // data members:
+ WeaponDesign* design;
+ Text group;
+ Point muzzle_pts[MAX_BARRELS];
+ Point rel_pts[MAX_BARRELS];
+ Solid* turret;
+ Solid* turret_base;
+ Solid* visible_stores[MAX_BARRELS];
+ int nbarrels;
+ int active_barrel;
+
+ float refire;
+ int ammo;
+ int ripple_count;
+
+ // carrying costs per shot:
+ float mass;
+ float resist;
+
+ // for targeting computer:
+ int guided;
+ bool enabled;
+ bool locked;
+ bool centered;
+ bool firing;
+ bool blocked;
+ float shot_speed;
+
+ int index;
+
+ int orders;
+ int control;
+ int sweep;
+
+ SimObject* target;
+ System* subtarget;
+
+ Point objective;
+ Point obj_w;
+ Camera aim_cam;
+ float aim_azimuth;
+ float aim_elevation;
+ float old_azimuth;
+ float old_elevation;
+
+ // auto-aiming arc
+ float aim_az_max; // maximum deflection in azimuth
+ float aim_az_min; // minimum deflection in azimuth
+ float aim_az_rest; // azimuth of turret at rest
+ float aim_el_max; // maximum deflection in elevation
+ float aim_el_min; // minimum deflection in elevation
+ float aim_el_rest; // elevation of turret at rest
+
+ DWORD aim_time;
+
+ Shot** beams;
+};
+
+#endif Weapon_h
+
diff --git a/Stars45/WeaponDesign.cpp b/Stars45/WeaponDesign.cpp
new file mode 100644
index 0000000..fecd505
--- /dev/null
+++ b/Stars45/WeaponDesign.cpp
@@ -0,0 +1,726 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: WeaponDesign.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Weapon Design parameters class
+*/
+
+#include "MemDebug.h"
+#include "WeaponDesign.h"
+#include "ShipDesign.h"
+#include "Weapon.h"
+
+#include "Game.h"
+#include "Sprite.h"
+#include "Light.h"
+#include "Bitmap.h"
+#include "Solid.h"
+#include "Sound.h"
+#include "DataLoader.h"
+
+#include "ParseUtil.h"
+
+// +--------------------------------------------------------------------+
+
+static List<WeaponDesign> catalog;
+static List<WeaponDesign> mod_catalog;
+static bool degrees;
+
+#define GET_DEF_BOOL(x) if(defname==(#x))GetDefBool(design->x,pdef,filename)
+#define GET_DEF_TEXT(x) if(defname==(#x))GetDefText(design->x,pdef,filename)
+#define GET_DEF_NUM(x) if(defname==(#x))GetDefNumber(design->x,pdef,filename)
+
+// +--------------------------------------------------------------------+
+
+WeaponDesign::WeaponDesign()
+{
+ type = 0;
+ secret = 0;
+ drone = 0;
+ primary = 0;
+ beam = 0;
+ flak = 0;
+ guided = 0;
+ self_aiming = 0;
+ syncro = 0;
+ value = 0;
+ decoy_type = 0;
+ probe = 0;
+ target_type = 0;
+
+ visible_stores = 0;
+ nstores = 0;
+ nbarrels = 0;
+
+ recharge_rate = 0.0f;
+ refire_delay = 0.0f;
+ salvo_delay = 0.0f;
+ charge = 0.0f;
+ min_charge = 0.0f;
+ carry_mass = 0.0f;
+ carry_resist = 0.0f;
+
+ speed = 0.0f;
+ life = 0.0f;
+ mass = 0.0f;
+ drag = 0.0f;
+ thrust = 0.0f;
+ roll_rate = 0.0f;
+ pitch_rate = 0.0f;
+ yaw_rate = 0.0f;
+ roll_drag = 0.0f;
+ pitch_drag = 0.0f;
+ yaw_drag = 0.0f;
+
+ min_range = 0.0f;
+ max_range = 0.0f;
+ max_track = 0.0f;
+
+ graphic_type = 0;
+ width = 0;
+ length = 0;
+
+ scale = 1.0f;
+ explosion_scale = 0.0f;
+ light = 0.0f;
+ light_color = Color::White;
+ flash_scale = 0.0f;
+ flare_scale = 0.0f;
+
+ spread_az = 0.0f;
+ spread_el = 0.0f;
+
+ beauty_img = 0;
+ turret_model = 0;
+ turret_base_model = 0;
+ animation = 0;
+ anim_length = 0;
+ shot_img = 0;
+ shot_model = 0;
+ trail_img = 0;
+ flash_img = 0;
+ flare_img = 0;
+ sound_resource = 0;
+
+ ammo = -1;
+ ripple_count = 0;
+ capacity = 100.0f;
+ damage = 1.0f;
+ damage_type = 0;
+ penetration = 1.0f;
+ firing_cone = 0.0f;
+ aim_az_max = 1.5f;
+ aim_az_min = -1.5f;
+ aim_az_rest = 0.0f;
+ aim_el_max = 1.5f;
+ aim_el_min = -1.5f;
+ aim_el_rest = 0.0f;
+ slew_rate = (float) (60*DEGREES);
+ turret_axis = 0;
+ lethal_radius = 500.0f;
+ integrity = 100.0f;
+
+ eject = Vec3(0.0f, -100.0f, 0.0f);
+
+ det_range = 0.0f;
+ det_count = 0;
+ det_spread = (float) (PI/8);
+
+ trail_length = 0;
+ trail_width = 0;
+ trail_dim = 0;
+
+ for (int i = 0; i < MAX_STORES; i++) {
+ muzzle_pts[i] = Vec3(0,0,0);
+ attachments[i] = Vec3(0,0,0);
+ }
+}
+
+WeaponDesign::~WeaponDesign()
+{
+ delete turret_model;
+ delete turret_base_model;
+ delete shot_model;
+ delete sound_resource;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WeaponDesign::Initialize(const char* filename)
+{
+ Print("Loading Weapon Designs '%s'\n", filename);
+ LoadDesign("Weapons/", filename);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WeaponDesign::Close()
+{
+ catalog.destroy();
+ mod_catalog.destroy();
+}
+
+void
+WeaponDesign::ClearModCatalog()
+{
+ mod_catalog.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WeaponDesign::LoadDesign(const char* path, const char* filename, bool mod)
+{
+ // Load Design File:
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath(path);
+
+ BYTE* block;
+ int blocklen = loader->LoadBuffer(filename, block, true);
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'\n", filename);
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "WEAPON") {
+ Print("ERROR: invalid weapon design file '%s'\n", filename);
+ return;
+ }
+ }
+
+ int type = 1;
+ degrees = false;
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ Text defname = def->name()->value();
+ defname.setSensitive(false);
+
+ if (defname == "primary" ||
+ defname == "missile" ||
+ defname == "drone" ||
+ defname == "beam") {
+
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: weapon structure missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = def->term()->isStruct();
+ WeaponDesign* design = new(__FILE__,__LINE__) WeaponDesign;
+
+ design->type = type++;
+ if (defname == "primary") {
+ design->primary = true;
+ }
+
+ else if (defname == "beam") {
+ design->primary = true;
+ design->beam = true;
+ design->guided = true;
+
+ design->spread_az = 0.15f;
+ design->spread_el = 0.15f;
+
+ }
+
+ else if (defname == "drone") {
+ design->drone = true;
+ design->penetration = 5.0f;
+ }
+
+ else { // missile
+ design->penetration = 5.0f;
+ }
+
+ float sound_min_dist = 1.0f;
+ float sound_max_dist = 100.0e3f;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ defname = pdef->name()->value();
+ defname.setSensitive(false);
+
+ GET_DEF_TEXT(name);
+ else GET_DEF_TEXT(group);
+ else GET_DEF_TEXT(description);
+ else GET_DEF_NUM (guided);
+ else GET_DEF_BOOL(self_aiming);
+ else GET_DEF_BOOL(flak);
+ else GET_DEF_BOOL(syncro);
+ else GET_DEF_BOOL(visible_stores);
+ else GET_DEF_NUM (value);
+
+ else if (defname==("degrees")) {
+ GetDefBool(degrees,pdef,filename);
+ }
+
+ else if (defname==("secret")) {
+ GetDefBool(design->secret,pdef,filename);
+ }
+
+ else if (defname==("aim_az_max")) {
+ GetDefNumber(design->aim_az_max,pdef,filename);
+ if (degrees) design->aim_az_max *= (float) DEGREES;
+ design->aim_az_min = 0.0f - design->aim_az_max;
+ }
+
+ else if (defname==("aim_el_max")) {
+ GetDefNumber(design->aim_el_max,pdef,filename);
+ if (degrees) design->aim_el_max *= (float) DEGREES;
+ design->aim_el_min = 0.0f - design->aim_el_max;
+ }
+
+ else if (defname==("aim_az_min")) {
+ GetDefNumber(design->aim_az_min,pdef,filename);
+ if (degrees) design->aim_az_min *= (float) DEGREES;
+ }
+
+ else if (defname==("aim_el_min")) {
+ GetDefNumber(design->aim_el_min,pdef,filename);
+ if (degrees) design->aim_el_min *= (float) DEGREES;
+ }
+
+ else if (defname==("aim_az_rest")) {
+ GetDefNumber(design->aim_az_rest,pdef,filename);
+ if (degrees) design->aim_az_rest *= (float) DEGREES;
+ }
+
+ else if (defname==("aim_el_rest")) {
+ GetDefNumber(design->aim_el_rest,pdef,filename);
+ if (degrees) design->aim_el_rest *= (float) DEGREES;
+ }
+
+ else if (defname==("spread_az")) {
+ GetDefNumber(design->spread_az,pdef,filename);
+ if (degrees) design->spread_az *= (float) DEGREES;
+ }
+
+ else if (defname==("spread_el")) {
+ GetDefNumber(design->spread_el,pdef,filename);
+ if (degrees) design->spread_el *= (float) DEGREES;
+ }
+
+ else GET_DEF_NUM (capacity);
+ else GET_DEF_NUM (recharge_rate);
+ else GET_DEF_NUM (refire_delay);
+ else GET_DEF_NUM (salvo_delay);
+ else GET_DEF_NUM (ammo);
+ else GET_DEF_NUM (ripple_count);
+ else GET_DEF_NUM (charge);
+ else GET_DEF_NUM (min_charge);
+ else GET_DEF_NUM (carry_mass);
+ else GET_DEF_NUM (carry_resist);
+ else GET_DEF_NUM (damage);
+ else GET_DEF_NUM (penetration);
+ else GET_DEF_NUM (speed);
+ else GET_DEF_NUM (life);
+ else GET_DEF_NUM (mass);
+ else GET_DEF_NUM (drag);
+ else GET_DEF_NUM (thrust);
+ else GET_DEF_NUM (roll_rate);
+ else GET_DEF_NUM (pitch_rate);
+ else GET_DEF_NUM (yaw_rate);
+ else GET_DEF_NUM (roll_drag);
+ else GET_DEF_NUM (pitch_drag);
+ else GET_DEF_NUM (yaw_drag);
+ else GET_DEF_NUM (lethal_radius);
+ else GET_DEF_NUM (integrity);
+
+ else GET_DEF_NUM (det_range);
+ else GET_DEF_NUM (det_count);
+ else GET_DEF_NUM (det_spread);
+ else GET_DEF_TEXT(det_child);
+
+ else GET_DEF_NUM (slew_rate);
+
+ else GET_DEF_NUM (min_range);
+ else GET_DEF_NUM (max_range);
+ else GET_DEF_NUM (max_track);
+
+ else GET_DEF_NUM (graphic_type);
+ else GET_DEF_NUM (width);
+ else GET_DEF_NUM (length);
+ else GET_DEF_NUM (scale);
+ else GET_DEF_NUM (explosion_scale);
+ else GET_DEF_NUM (light);
+ else GET_DEF_NUM (flash_scale);
+ else GET_DEF_NUM (flare_scale);
+
+ else GET_DEF_NUM (trail_length);
+ else GET_DEF_NUM (trail_width);
+ else GET_DEF_NUM (trail_dim);
+
+ else GET_DEF_TEXT(beauty);
+ else GET_DEF_TEXT(bitmap);
+ else GET_DEF_TEXT(turret);
+ else GET_DEF_TEXT(turret_base);
+ else GET_DEF_TEXT(model);
+ else GET_DEF_TEXT(trail);
+ else GET_DEF_TEXT(flash);
+ else GET_DEF_TEXT(flare);
+ else GET_DEF_TEXT(sound);
+ else GET_DEF_BOOL(probe);
+ else GET_DEF_NUM (turret_axis);
+ else GET_DEF_NUM (target_type);
+
+ else if (defname == "animation") {
+ if (design->anim_length < 16) {
+ GetDefText(design->anim_frames[design->anim_length++], pdef, filename);
+ }
+ else {
+ Print("WARNING: too many animation frames for weapon '%s' in '%s'\n",
+ (const char*) design->name, filename);
+ }
+ }
+
+ else if (defname == "light_color")
+ GetDefColor(design->light_color, pdef, filename);
+
+ else if (defname == "sound_min_dist")
+ GetDefNumber(sound_min_dist,pdef,filename);
+
+ else if (defname == "sound_max_dist")
+ GetDefNumber(sound_max_dist,pdef,filename);
+
+ else if (defname == "muzzle") {
+ if (design->nbarrels < MAX_STORES) {
+ Vec3 a;
+ GetDefVec(a, pdef, filename);
+ design->muzzle_pts[design->nbarrels++] = a;
+ }
+ else {
+ Print("WARNING: too many muzzles for weapon '%s' in '%s'\n",
+ (const char*) design->name, filename);
+ }
+ }
+
+ else if (defname == "attachment") {
+ if (design->nstores < MAX_STORES) {
+ Vec3 a;
+ GetDefVec(a, pdef, filename);
+ design->attachments[design->nstores++] = a;
+ }
+ else {
+ Print("WARNING: too many attachments for weapon '%s' in '%s'\n",
+ (const char*) design->name, filename);
+ }
+ }
+
+ else if (defname == "eject")
+ GetDefVec(design->eject,pdef,filename);
+
+ else if (defname == "decoy") {
+ char typestr[32];
+ GetDefText(typestr, pdef, filename);
+ design->decoy_type = ShipDesign::ClassForName(typestr);
+ }
+
+ else if (defname == "damage_type") {
+ char typestr[32];
+ GetDefText(typestr, pdef, filename);
+
+ if (!stricmp(typestr, "normal"))
+ design->damage_type = DMG_NORMAL;
+
+ else if (!stricmp(typestr, "emp"))
+ design->damage_type = DMG_EMP;
+
+ else if (!stricmp(typestr, "power"))
+ design->damage_type = DMG_POWER;
+
+ else
+ Print("WARNING: unknown weapon damage type '%s' in '%s'\n",
+ typestr, filename);
+ }
+
+
+ else {
+ Print("WARNING: parameter '%s' ignored in '%s'\n",
+ defname.data(), filename);
+ }
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ val->elements()->at(i)->print();
+ }
+ }
+
+ if (design->description.length()) {
+ design->description = Game::GetText(design->description);
+ }
+
+ if (design->anim_length > 0) {
+ design->animation = new(__FILE__,__LINE__) Bitmap[design->anim_length];
+ for (int i = 0; i < design->anim_length; i++) {
+ Bitmap* p = design->animation + i;
+ loader->LoadBitmap(design->anim_frames[i], *p, Bitmap::BMP_TRANSLUCENT);
+ p->MakeTexture();
+ }
+ }
+
+ else if (design->bitmap.length()) {
+ loader->LoadTexture(design->bitmap, design->shot_img, Bitmap::BMP_TRANSLUCENT);
+ }
+
+ if (design->beauty.length()) {
+ loader->LoadTexture(design->beauty, design->beauty_img, Bitmap::BMP_TRANSLUCENT);
+ }
+
+ if (design->turret.length()) {
+ Text p;
+ Text t = design->turret;
+ const char* s = strrchr(t.data(), '/');
+
+ if (s) {
+ p = Text(path) + t.substring(0, s-t.data()+1);
+ t = t.substring(s-t.data()+1, t.length());
+ }
+ else {
+ s = strrchr(t.data(), '\\');
+
+ if (s) {
+ p = Text(path) + t.substring(0, s-t.data()+1);
+ t = t.substring(s-t.data()+1, t.length());
+ }
+ }
+
+ if (p.length())
+ loader->SetDataPath(p);
+
+ design->turret_model = new(__FILE__,__LINE__) Model;
+ design->turret_model->Load(t, design->scale);
+
+ if (design->turret_base.length()) {
+ t = design->turret_base;
+ s = strrchr(t.data(), '/');
+
+ if (s) {
+ p = Text(path) + t.substring(0, s-t.data()+1);
+ t = t.substring(s-t.data()+1, t.length());
+ }
+ else {
+ s = strrchr(t.data(), '\\');
+
+ if (s) {
+ p = Text(path) + t.substring(0, s-t.data()+1);
+ t = t.substring(s-t.data()+1, t.length());
+ }
+ }
+
+ if (p.length())
+ loader->SetDataPath(p);
+
+ design->turret_base_model = new(__FILE__,__LINE__) Model;
+ design->turret_base_model->Load(t, design->scale);
+ }
+
+ loader->SetDataPath(path);
+ }
+
+ if (design->model.length()) {
+ Text p;
+ Text t = design->model;
+ const char* s = strrchr(t.data(), '/');
+
+ if (s) {
+ p = Text(path) + t.substring(0, s-t.data()+1);
+ t = t.substring(s-t.data()+1, t.length());
+ }
+ else {
+ s = strrchr(t.data(), '\\');
+
+ if (s) {
+ p = Text(path) + t.substring(0, s-t.data()+1);
+ t = t.substring(s-t.data()+1, t.length());
+ }
+ }
+
+ if (p.length())
+ loader->SetDataPath(p);
+
+ design->shot_model = new(__FILE__,__LINE__) Model;
+ design->shot_model->Load(t, design->scale);
+
+ loader->SetDataPath(path);
+ }
+
+ if (design->trail.length()) {
+ loader->LoadTexture(design->trail, design->trail_img, Bitmap::BMP_TRANSLUCENT);
+ }
+
+ if (design->flash.length()) {
+ loader->LoadTexture(design->flash, design->flash_img, Bitmap::BMP_TRANSLUCENT);
+ }
+
+ if (design->flare.length()) {
+ loader->LoadTexture(design->flare, design->flare_img, Bitmap::BMP_TRANSLUCENT);
+ }
+
+ if (design->sound.length()) {
+ int SOUND_FLAGS = Sound::LOCALIZED | Sound::LOC_3D;
+
+ if (design->beam)
+ SOUND_FLAGS = Sound::LOCALIZED | Sound::LOC_3D | Sound::LOCKED;
+
+ if (strstr(path, "Mods") == 0)
+ loader->SetDataPath("Sounds/");
+ loader->LoadSound(design->sound, design->sound_resource, SOUND_FLAGS);
+ loader->SetDataPath(path);
+
+ if (design->sound_resource) {
+ design->sound_resource->SetMinDistance(sound_min_dist);
+ design->sound_resource->SetMaxDistance(sound_max_dist);
+ }
+ }
+
+ if (design->max_range == 0.0f)
+ design->max_range = design->speed * design->life;
+
+ if (design->max_track == 0.0f)
+ design->max_track = 3.0f * design->max_range;
+
+ if (design->probe && design->lethal_radius < 1e3)
+ design->lethal_radius = 50e3f;
+
+ if (design->beam)
+ design->flak = false;
+
+ if (design->self_aiming) {
+ if (fabs(design->aim_az_max) > design->firing_cone)
+ design->firing_cone = (float) fabs(design->aim_az_max);
+
+ if (fabs(design->aim_az_min) > design->firing_cone)
+ design->firing_cone = (float) fabs(design->aim_az_min);
+
+ if (fabs(design->aim_el_max) > design->firing_cone)
+ design->firing_cone = (float) fabs(design->aim_el_max);
+
+ if (fabs(design->aim_el_min) > design->firing_cone)
+ design->firing_cone = (float) fabs(design->aim_el_min);
+ }
+
+ if (mod)
+ mod_catalog.append(design);
+ else
+ catalog.append(design);
+ }
+ }
+
+ else
+ Print("WARNING: unknown definition '%s' in '%s'\n",
+ def->name()->value().data(), filename);
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ term->print();
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+ loader->SetDataPath(0);
+}
+
+// +--------------------------------------------------------------------+
+
+WeaponDesign*
+WeaponDesign::Get(int type)
+{
+ WeaponDesign test;
+ test.type = type;
+
+ WeaponDesign* result = catalog.find(&test);
+
+ if (!result)
+ result = mod_catalog.find(&test);
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+WeaponDesign*
+WeaponDesign::Find(const char* name)
+{
+ for (int i = 0; i < catalog.size(); i++) {
+ WeaponDesign* d = catalog.at(i);
+
+ if (d->name == name) {
+ return d;
+ }
+ }
+
+ for (i = 0; i < mod_catalog.size(); i++) {
+ WeaponDesign* d = mod_catalog.at(i);
+
+ if (d->name == name) {
+ return d;
+ }
+ }
+
+ Print("WeaponDesign: no catalog entry for design '%s', checking mods...\n", name);
+ return WeaponDesign::FindModDesign(name);
+}
+
+WeaponDesign*
+WeaponDesign::FindModDesign(const char* name)
+{
+ Text fname = name;
+ fname += ".def";
+
+ LoadDesign("Mods/Weapons/", fname, true);
+
+ for (int i = 0; i < mod_catalog.size(); i++) {
+ WeaponDesign* d = mod_catalog.at(i);
+
+ if (d->name == name) {
+ Print("WeaponDesign: found mod weapon '%s'\n", d->name);
+ return d;
+ }
+ }
+
+ Print("WeaponDesign: no mod found for design '%s'\n", name);
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+WeaponDesign::GetDesignList(List<Text>& designs)
+{
+ designs.clear();
+
+ for (int i = 0; i < catalog.size(); i++) {
+ WeaponDesign* design = catalog[i];
+ designs.append(&design->name);
+ }
+
+ for (i = 0; i < mod_catalog.size(); i++) {
+ WeaponDesign* design = mod_catalog[i];
+ designs.append(&design->name);
+ }
+
+ return designs.size();
+}
diff --git a/Stars45/WeaponDesign.h b/Stars45/WeaponDesign.h
new file mode 100644
index 0000000..c31472e
--- /dev/null
+++ b/Stars45/WeaponDesign.h
@@ -0,0 +1,187 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: WeaponDesign.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Weapon (gun or missile launcher) Design parameters class
+*/
+
+#ifndef WeaponDesign_h
+#define WeaponDesign_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Color.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+class Model;
+class Sound;
+
+// +--------------------------------------------------------------------+
+
+class WeaponDesign
+{
+public:
+ static const char* TYPENAME() { return "WeaponDesign"; }
+
+ enum CONSTANTS {
+ DMG_NORMAL=0,
+ DMG_EMP =1,
+ DMG_POWER =2,
+ MAX_STORES=8
+ };
+
+ WeaponDesign();
+ ~WeaponDesign();
+ int operator == (const WeaponDesign& rhs) const { return (type == rhs.type) ||
+ (name == rhs.name); }
+
+ static void Initialize(const char* filename);
+ static void Close();
+
+ static WeaponDesign* Get(int type);
+ static WeaponDesign* Find(const char* name);
+ static WeaponDesign* FindModDesign(const char* name);
+ static void ClearModCatalog();
+ static int GetDesignList(List<Text>& designs);
+
+ // identification:
+ int type; // unique id
+ Text name;
+ Text group;
+ Text description; // background info for tactical reference
+ bool secret; // don't display in the tactical reference
+
+ bool drone; // visible to sensors?
+ bool primary; // laser or missile?
+ bool beam; // if laser, beam or bolt?
+ bool self_aiming; // turret or fixed?
+ bool syncro; // fire all barrels?
+ bool flak; // splash damage
+ int guided; // straight, pure pursuit, lead pursuit
+ int value; // AI importance of system
+ int decoy_type; // Ship Classifcation of decoy signature
+ bool probe; // is sensor probe?
+ DWORD target_type; // bitmask of acceptable target classes
+
+ // for turrets:
+ Vec3 muzzle_pts[MAX_STORES]; // default turret muzzle points
+ int nbarrels; // number of barrels on the turret
+
+ // for missile hard points:
+ bool visible_stores; // are external stores visible?
+ Vec3 attachments[MAX_STORES]; // attachment points on the rail
+ int nstores; // number of stores on this hard point
+ Vec3 eject; // eject velocity from rail in 3D
+
+ // auto-aiming arc
+ float firing_cone; // maximum deflection in any orientation
+ float aim_az_max; // maximum deflection in azimuth
+ float aim_az_min; // minimum deflection in azimuth
+ float aim_az_rest; // azimuth of turret at rest
+ float aim_el_max; // maximum deflection in elevation
+ float aim_el_min; // minimum deflection in elevation
+ float aim_el_rest; // elevation of turret at rest
+ float slew_rate; // max rate of turret slew in rad/sec
+ int turret_axis; // 0=az 1=el 2=not supported
+
+ // functional parameters:
+ float capacity; // full charge (joules)
+ float recharge_rate; // watts
+ float refire_delay; // seconds - mechanical limit
+ float salvo_delay; // seconds - ai refire time
+ int ammo;
+ int ripple_count; // number of rounds per salvo
+
+ // carrying costs per shot:
+ float charge; // energy cost of full charge
+ float min_charge; // minimum energy needed to fire
+ float carry_mass;
+ float carry_resist;
+
+ // shot parameters:
+ int damage_type; // 0: normal, 1: EMP, 2: power drain
+ float damage; // if beam, damage per second;
+ // else, damage per shot.
+ float penetration; // ability to pierce shields, 1 is default
+ float speed;
+ float life;
+ float mass;
+ float drag;
+ float thrust;
+ float roll_rate;
+ float pitch_rate;
+ float yaw_rate;
+ float roll_drag;
+ float pitch_drag;
+ float yaw_drag;
+ float integrity; // hit points for drones = 100
+ float lethal_radius; // detonation range for missiles
+
+ float det_range; // detonation range for cluster weapons
+ Text det_child; // type of submunition
+ int det_count; // number of submunitions
+ float det_spread; // spread of submunition deployment
+
+ // HUD parameters:
+ float min_range;
+ float max_range;
+ float max_track;
+
+ // shot representation:
+ int graphic_type; // sprite or blob?
+ float width; // blob width
+ float length; // blob length
+ float scale; // sprite scale
+ float explosion_scale; // scale factor for damage to this drone
+ float light; // light emitted by shot
+ Color light_color; // color of light emitted by shot
+ float flash_scale; // size of muzzle flash sprite
+ float flare_scale; // size of drive flare sprite
+
+ float spread_az; // spread range in radians
+ float spread_el; // spread range in radians
+
+ Text anim_frames[16];
+ int anim_length;
+ Text beauty;
+ Text bitmap;
+ Text model;
+ Text turret;
+ Text turret_base;
+ Text trail;
+ Text flash;
+ Text flare;
+ Text sound;
+
+ Bitmap* beauty_img;
+ Bitmap* animation;
+ Bitmap* shot_img;
+ Bitmap* trail_img;
+ Bitmap* flash_img;
+ Bitmap* flare_img;
+ Model* shot_model;
+ Model* turret_model;
+ Model* turret_base_model;
+ Sound* sound_resource;
+
+ int trail_length;
+ float trail_width;
+ int trail_dim;
+
+private:
+ static void LoadDesign(const char* path, const char* filename, bool mod=false);
+};
+
+#endif WeaponDesign_h
+
diff --git a/Stars45/WeaponGroup.cpp b/Stars45/WeaponGroup.cpp
new file mode 100644
index 0000000..3998ff5
--- /dev/null
+++ b/Stars45/WeaponGroup.cpp
@@ -0,0 +1,348 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2005. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: WeaponGroup.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Weapon Control Category (Group) class
+*/
+
+#include "MemDebug.h"
+#include "WeaponGroup.h"
+#include "Ship.h"
+
+// +----------------------------------------------------------------------+
+
+WeaponGroup::WeaponGroup(const char* n)
+ : selected(0), trigger(false), orders(Weapon::MANUAL),
+ control(Weapon::SINGLE_FIRE), sweep(Weapon::SWEEP_TIGHT),
+ mass(0.0f), resist(0.0f), name(n), ammo(0)
+{ }
+
+// +--------------------------------------------------------------------+
+
+WeaponGroup::~WeaponGroup()
+{
+ weapons.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WeaponGroup::SetName(const char* n)
+{
+ name = n;
+}
+
+void
+WeaponGroup::SetAbbreviation(const char* a)
+{
+ abrv = a;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+WeaponGroup::IsPrimary() const
+{
+ if (weapons.size() > 0)
+ return weapons[0]->IsPrimary();
+
+ return false;
+}
+
+bool
+WeaponGroup::IsDrone() const
+{
+ if (weapons.size() > 0)
+ return weapons[0]->IsDrone();
+
+ return false;
+}
+
+bool
+WeaponGroup::IsDecoy() const
+{
+ if (weapons.size() > 0)
+ return weapons[0]->IsDecoy();
+
+ return false;
+}
+
+bool
+WeaponGroup::IsProbe() const
+{
+ if (weapons.size() > 0)
+ return weapons[0]->IsProbe();
+
+ return false;
+}
+
+bool
+WeaponGroup::IsMissile() const
+{
+ if (weapons.size() > 0)
+ return weapons[0]->IsMissile();
+
+ return false;
+}
+
+bool
+WeaponGroup::IsBeam() const
+{
+ if (weapons.size() > 0)
+ return weapons[0]->IsBeam();
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WeaponGroup::AddWeapon(Weapon* w)
+{
+ weapons.append(w);
+}
+
+int
+WeaponGroup::NumWeapons() const
+{
+ return weapons.size();
+}
+
+List<Weapon>&
+WeaponGroup::GetWeapons()
+{
+ return weapons;
+}
+
+bool
+WeaponGroup::Contains(const Weapon* w) const
+{
+ return weapons.contains(w)?true:false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WeaponGroup::SelectWeapon(int n)
+{
+ if (n >= 0 && n < weapons.size())
+ selected = n;
+}
+
+void
+WeaponGroup::CycleWeapon()
+{
+ selected++;
+
+ if (selected >= weapons.size())
+ selected = 0;
+}
+
+Weapon*
+WeaponGroup::GetWeapon(int n) const
+{
+ if (n >= 0 && n < weapons.size())
+ return weapons[n];
+
+ return 0;
+}
+
+Weapon*
+WeaponGroup::GetSelected() const
+{
+ return weapons[selected];
+}
+
+bool
+WeaponGroup::CanTarget(DWORD tgt_class) const
+{
+ if (selected >= 0 && selected < weapons.size())
+ return weapons[selected]->CanTarget(tgt_class);
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WeaponGroup::ExecFrame(double seconds)
+{
+ ammo = 0;
+ mass = 0.0f;
+ resist = 0.0f;
+
+ ListIter<Weapon> iter = weapons;
+ while (++iter) {
+ Weapon* w = iter.value();
+ w->ExecFrame(seconds);
+
+ ammo += w->Ammo();
+ mass += w->Mass();
+ resist += w->Resistance();
+ }
+}
+
+void
+WeaponGroup::CheckAmmo()
+{
+ ammo = 0;
+ mass = 0.0f;
+ resist = 0.0f;
+
+ ListIter<Weapon> iter = weapons;
+ while (++iter) {
+ Weapon* w = iter.value();
+
+ ammo += w->Ammo();
+ mass += w->Mass();
+ resist += w->Resistance();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WeaponGroup::SetTarget(SimObject* target, System* subtarget)
+{
+ ListIter<Weapon> w = weapons;
+ while (++w)
+ w->SetTarget(target, subtarget);
+}
+
+SimObject*
+WeaponGroup::GetTarget() const
+{
+ SimObject* target = 0;
+
+ if (weapons.size())
+ target = weapons[0]->GetTarget();
+
+ return target;
+}
+
+System*
+WeaponGroup::GetSubTarget() const
+{
+ System* subtarget = 0;
+
+ if (weapons.size())
+ subtarget = weapons[0]->GetSubTarget();
+
+ return subtarget;
+}
+
+void
+WeaponGroup::DropTarget()
+{
+ ListIter<Weapon> w = weapons;
+ while (++w)
+ w->SetTarget(0, 0);
+}
+
+// +--------------------------------------------------------------------+
+
+WeaponDesign*
+WeaponGroup::GetDesign() const
+{
+ if (selected >= 0 && selected < weapons.size())
+ return (WeaponDesign*) weapons[selected]->Design();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+WeaponGroup::Status() const
+{
+ int status = System::NOMINAL;
+ int critical = true;
+
+ ListIter<Weapon> iter = (List<Weapon>&) weapons; // cast-away const
+ while (++iter) {
+ Weapon* w = iter.value();
+
+ if (w->Status() < System::NOMINAL)
+ status = System::DEGRADED;
+
+ if (w->Status() > System::CRITICAL)
+ critical = false;
+ }
+
+ if (critical)
+ return System::CRITICAL;
+
+ return status;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WeaponGroup::SetFiringOrders(int o)
+{
+ orders = o;
+
+ ListIter<Weapon> w = weapons;
+ while (++w)
+ w->SetFiringOrders(orders);
+}
+
+void
+WeaponGroup::SetControlMode(int m)
+{
+ control = m;
+
+ ListIter<Weapon> w = weapons;
+ while (++w)
+ w->SetControlMode(control);
+}
+
+void
+WeaponGroup::SetSweep(int s)
+{
+ sweep = s;
+
+ ListIter<Weapon> w = weapons;
+ while (++w)
+ w->SetSweep(sweep);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WeaponGroup::PowerOff()
+{
+ ListIter<Weapon> w = weapons;
+ while (++w)
+ w->PowerOff();
+}
+
+void
+WeaponGroup::PowerOn()
+{
+ ListIter<Weapon> w = weapons;
+ while (++w)
+ w->PowerOn();
+}
+
+// +--------------------------------------------------------------------+
+
+int
+WeaponGroup::Value() const
+{
+ int result = 0;
+
+ for (int i = 0; i < weapons.size(); i++) {
+ const Weapon* w = weapons[i];
+ result += w->Value();
+ }
+
+ return result;
+}
diff --git a/Stars45/WeaponGroup.h b/Stars45/WeaponGroup.h
new file mode 100644
index 0000000..2547ec2
--- /dev/null
+++ b/Stars45/WeaponGroup.h
@@ -0,0 +1,106 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: WeaponGroup.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Weapon Control Category (Group) class
+*/
+
+#ifndef WeaponGroup_h
+#define WeaponGroup_h
+
+#include "Types.h"
+#include "Weapon.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class WeaponGroup
+{
+public:
+ static const char* TYPENAME() { return "WeaponGroup"; }
+
+ WeaponGroup(const char* name);
+ ~WeaponGroup();
+
+ void ExecFrame(double factor);
+
+ // identification:
+ const char* Name() const { return name; }
+ const char* Abbreviation() const { return abrv; }
+ void SetName(const char* n);
+ void SetAbbreviation(const char* a);
+
+ bool IsPrimary() const;
+ bool IsDrone() const;
+ bool IsDecoy() const;
+ bool IsProbe() const;
+ bool IsMissile() const;
+ bool IsBeam() const;
+ int Value() const;
+
+ // weapon list:
+ void AddWeapon(Weapon* w);
+ int NumWeapons() const;
+ List<Weapon>& GetWeapons();
+ bool Contains(const Weapon* w) const;
+
+ // weapon selection:
+ void SelectWeapon(int n);
+ void CycleWeapon();
+ Weapon* GetWeapon(int n) const;
+ Weapon* GetSelected() const;
+
+ // operations:
+ bool GetTrigger() const { return trigger; }
+ void SetTrigger(bool t=true) { trigger = t; }
+ int Ammo() const { return ammo; }
+ float Mass() const { return mass; }
+ float Resistance() const { return resist; }
+ void CheckAmmo();
+
+ void SetTarget(SimObject* t, System* sub=0);
+ SimObject* GetTarget() const;
+ System* GetSubTarget() const;
+ void DropTarget();
+ void SetFiringOrders(int o);
+ int GetFiringOrders() const { return orders; }
+ void SetControlMode(int m);
+ int GetControlMode() const { return control; }
+ void SetSweep(int s);
+ int GetSweep() const { return sweep; }
+ int Status() const;
+
+ WeaponDesign* GetDesign() const;
+ bool CanTarget(DWORD tgt_class) const;
+
+ void PowerOn();
+ void PowerOff();
+
+protected:
+ // Displayable name:
+ Text name;
+ Text abrv;
+
+ List<Weapon> weapons;
+
+ int selected;
+ bool trigger;
+ int ammo;
+
+ int orders;
+ int control;
+ int sweep;
+
+ float mass;
+ float resist;
+};
+
+#endif WeaponGroup_h
+
diff --git a/Stars45/Weather.cpp b/Stars45/Weather.cpp
new file mode 100644
index 0000000..49a6259
--- /dev/null
+++ b/Stars45/Weather.cpp
@@ -0,0 +1,178 @@
+/* Project Starshatter 5.0
+ Destroyer Studios LLC
+ Copyright © 1997-2007. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Weather.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Manages local weather conditions according to the system stardate
+*/
+
+#include "MemDebug.h"
+#include "Weather.h"
+#include "StarSystem.h"
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+
+Weather::Weather()
+{
+ state = CLEAR;
+ period = 7 * 20 * 3600;
+ ceiling = 0;
+ visibility = 1;
+
+ for (int i = 0; i < NUM_STATES; i++)
+ chances[i] = 0;
+
+ chances[0] = 1;
+}
+
+// +--------------------------------------------------------------------+
+
+Weather::~Weather()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+Weather::SetChance(int n, double c)
+{
+ if (n >= 0 && n < NUM_STATES) {
+ if (c > 1 && c <= 100)
+ chances[n] = c / 100;
+
+ else if (c < 1)
+ chances[n] = c;
+ }
+}
+
+void
+Weather::NormalizeChances()
+{
+ double total = 0;
+
+ for (int i = 1; i < NUM_STATES; i++)
+ total += chances[i];
+
+ if (total <= 1) {
+ chances[0] = 1 - total;
+ }
+
+ else {
+ chances[0] = 0;
+
+ for (i = 1; i < NUM_STATES; i++)
+ chances[i] /= total;
+ }
+
+ int index = 0;
+ double level = 0;
+
+ for (i = 0; i < NUM_STATES; i++) {
+ if (chances[i] > 0) {
+ level += chances[i];
+
+ active_states[index] = (STATE) i;
+ thresholds[index] = level;
+
+ index++;
+ }
+ }
+
+ while (index < NUM_STATES)
+ thresholds[index++] = 10;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Weather::Update()
+{
+ NormalizeChances();
+
+ double weather = (sin(StarSystem::Stardate() * 2 * PI / period)+1)/2;
+
+ state = active_states[0];
+
+ for (int i = 1; i < NUM_STATES; i++) {
+ if (weather > thresholds[i-1] && weather <= thresholds[i]) {
+ state = active_states[i];
+ break;
+ }
+ }
+
+ switch (state) {
+ default:
+ case CLEAR:
+ ceiling = 0;
+ visibility = 1.0;
+ break;
+
+ case HIGH_CLOUDS:
+ ceiling = 0;
+ visibility = 0.9;
+ break;
+
+ case MODERATE_CLOUDS:
+ ceiling = 0;
+ visibility = 0.8;
+ break;
+
+ case OVERCAST:
+ ceiling = 6000;
+ visibility = 0.7;
+ break;
+
+ case FOG:
+ ceiling = 3500;
+ visibility = 0.6;
+ break;
+
+ case STORM:
+ ceiling = 7500;
+ visibility = 0.5;
+ break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+Weather::Description() const
+{
+ Text description;
+
+ switch (state) {
+ default:
+ case CLEAR:
+ description = Game::GetText("weather.clear");
+ break;
+
+ case HIGH_CLOUDS:
+ description = Game::GetText("weather.high-clouds");
+ break;
+
+ case MODERATE_CLOUDS:
+ description = Game::GetText("weather.partly-cloudy");
+ break;
+
+ case OVERCAST:
+ description = Game::GetText("weather.overcast");
+ break;
+
+ case FOG:
+ description = Game::GetText("weather.fog");
+ break;
+
+ case STORM:
+ description = Game::GetText("weather.storm");
+ break;
+ }
+
+ return description;
+} \ No newline at end of file
diff --git a/Stars45/Weather.h b/Stars45/Weather.h
new file mode 100644
index 0000000..6196e92
--- /dev/null
+++ b/Stars45/Weather.h
@@ -0,0 +1,67 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: Weather.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Manages local weather conditions according to the system stardate
+*/
+
+#ifndef Weather_h
+#define Weather_h
+
+#include "Types.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Weather
+{
+public:
+ Weather();
+ virtual ~Weather();
+
+ enum STATE { CLEAR,
+ HIGH_CLOUDS,
+ MODERATE_CLOUDS,
+ OVERCAST,
+ FOG,
+ STORM,
+
+ NUM_STATES
+ };
+
+ virtual void Update();
+
+ // accessors:
+ STATE State() const { return state; }
+ Text Description() const;
+ double Period() const { return period; }
+ double Chance(STATE s) const { return chances[(int)s]; }
+ double Ceiling() const { return ceiling; }
+ double Visibility() const { return visibility; }
+
+ void SetPeriod(double p) { period = p; }
+ void SetChance(int n, double c);
+
+protected:
+ void NormalizeChances();
+
+ STATE state;
+ double period;
+ double chances[NUM_STATES];
+ double ceiling;
+ double visibility;
+
+ STATE active_states[NUM_STATES];
+ double thresholds[NUM_STATES];
+};
+
+
+#endif Weather_h
+
diff --git a/Stars45/WepView.cpp b/Stars45/WepView.cpp
new file mode 100644
index 0000000..bcbec1f
--- /dev/null
+++ b/Stars45/WepView.cpp
@@ -0,0 +1,531 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: WepView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ View class for Tactical HUD Overlay
+*/
+
+#include "MemDebug.h"
+#include "WepView.h"
+#include "HUDView.h"
+#include "HUDSounds.h"
+#include "Ship.h"
+#include "Computer.h"
+#include "NavSystem.h"
+#include "Drive.h"
+#include "Power.h"
+#include "Shield.h"
+#include "Contact.h"
+#include "ShipDesign.h"
+#include "Shot.h"
+#include "Drone.h"
+#include "Weapon.h"
+#include "Sim.h"
+#include "StarSystem.h"
+#include "WeaponGroup.h"
+
+#include "CameraView.h"
+#include "Color.h"
+#include "Window.h"
+#include "Video.h"
+#include "Screen.h"
+#include "DataLoader.h"
+#include "Scene.h"
+#include "FontMgr.h"
+#include "Graphic.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "Game.h"
+#include "FormatUtil.h"
+
+static Bitmap tac_left;
+static Bitmap tac_right;
+static Bitmap tac_button;
+static Bitmap tac_man;
+static Bitmap tac_aut;
+static Bitmap tac_def;
+
+static BYTE* tac_left_shade;
+static BYTE* tac_right_shade;
+static BYTE* tac_button_shade;
+static BYTE* tac_man_shade;
+static BYTE* tac_aut_shade;
+static BYTE* tac_def_shade;
+
+static Color hud_color = Color::Black;
+static Color txt_color = Color::Black;
+
+static bool mouse_in = false;
+
+static Font* hud_font = 0;
+static Font* big_font = 0;
+
+// +--------------------------------------------------------------------+
+
+WepView* WepView::wep_view = 0;
+
+// +--------------------------------------------------------------------+
+
+WepView::WepView(Window* c)
+ : View(c), sim(0), ship(0), target(0), active_region(0),
+ transition(false), mode(0), mouse_down(0)
+{
+ wep_view = this;
+
+ sim = Sim::GetSim();
+
+ HUDView::PrepareBitmap("TAC_left.pcx", tac_left, tac_left_shade);
+ HUDView::PrepareBitmap("TAC_right.pcx", tac_right, tac_right_shade);
+ HUDView::PrepareBitmap("TAC_button.pcx", tac_button, tac_button_shade);
+ HUDView::PrepareBitmap("MAN.pcx", tac_man, tac_man_shade);
+ HUDView::PrepareBitmap("AUTO.pcx", tac_aut, tac_aut_shade);
+ HUDView::PrepareBitmap("DEF.pcx", tac_def, tac_def_shade);
+
+ tac_left.SetType(Bitmap::BMP_TRANSLUCENT);
+ tac_right.SetType(Bitmap::BMP_TRANSLUCENT);
+ tac_man.SetType(Bitmap::BMP_TRANSLUCENT);
+ tac_aut.SetType(Bitmap::BMP_TRANSLUCENT);
+ tac_def.SetType(Bitmap::BMP_TRANSLUCENT);
+
+ OnWindowMove();
+
+ hud_font = FontMgr::Find("HUD");
+ big_font = FontMgr::Find("GUI");
+
+ hud = HUDView::GetInstance();
+ if (hud)
+ SetColor(hud->GetHUDColor());
+}
+
+WepView::~WepView()
+{
+ tac_left.ClearImage();
+ tac_right.ClearImage();
+ tac_button.ClearImage();
+ tac_man.ClearImage();
+ tac_aut.ClearImage();
+ tac_def.ClearImage();
+
+ delete [] tac_left_shade;
+ delete [] tac_right_shade;
+ delete [] tac_button_shade;
+ delete [] tac_man_shade;
+ delete [] tac_aut_shade;
+ delete [] tac_def_shade;
+
+ tac_left_shade = 0;
+ tac_right_shade = 0;
+ tac_button_shade = 0;
+ tac_man_shade = 0;
+ tac_aut_shade = 0;
+ tac_def_shade = 0;
+
+ wep_view = 0;
+}
+
+void
+WepView::OnWindowMove()
+{
+ width = window->Width();
+ height = window->Height();
+ xcenter = (width / 2.0) - 0.5;
+ ycenter = (height / 2.0) + 0.5;
+
+ int btn_loc = width/2 - 147 - 45;
+ int man_loc = width/2 - 177 - 16;
+ int aut_loc = width/2 - 145 - 16;
+ int def_loc = width/2 - 115 - 16;
+
+ int index = 0;
+
+ for (int i = 0; i < MAX_WEP; i++) {
+ btn_rect[index++] = Rect(btn_loc, 30, 90, 20);
+ btn_rect[index++] = Rect(man_loc, 56, 32, 8);
+ btn_rect[index++] = Rect(aut_loc, 56, 32, 8);
+ btn_rect[index++] = Rect(def_loc, 56, 32, 8);
+
+ btn_loc += 98;
+ man_loc += 98;
+ aut_loc += 98;
+ def_loc += 98;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+WepView::Update(SimObject* obj)
+{
+ if (obj == ship) {
+ ship = 0;
+ target = 0;
+ }
+ else if (obj == target) {
+ target = 0;
+ }
+
+ return SimObserver::Update(obj);
+}
+
+const char*
+WepView::GetObserverName() const
+{
+ return "WepView";
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WepView::Refresh()
+{
+ sim = Sim::GetSim();
+ if (!sim || !hud || hud->GetHUDMode() == HUDView::HUD_MODE_OFF)
+ return;
+
+ if (ship != sim->GetPlayerShip()) {
+ ship = sim->GetPlayerShip();
+
+ if (ship) {
+ if (ship->Life() == 0 || ship->IsDying() || ship->IsDead()) {
+ ship = 0;
+ }
+ else {
+ Observe(ship);
+ }
+ }
+ }
+
+ if (mode < 1)
+ return;
+
+ if (ship) {
+ // no tactical overlay for fighters:
+ if (ship->Design() && !ship->Design()->wep_screen) {
+ mode = 0;
+ return;
+ }
+
+ // no hud in transition:
+ if (ship->InTransition()) {
+ transition = true;
+ return;
+ }
+
+ else if (transition) {
+ transition = false;
+ RestoreOverlay();
+ }
+
+ if (target != ship->GetTarget()) {
+ target = ship->GetTarget();
+ if (target) Observe(target);
+ }
+
+ DrawOverlay();
+ }
+ else {
+ if (target) {
+ target = 0;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WepView::ExecFrame()
+{
+ int hud_mode = 1;
+
+ // update the position of HUD elements that are
+ // part of the 3D scene (like fpm and lcos sprites)
+
+ if (hud) {
+ if (hud_color != hud->GetHUDColor()) {
+ hud_color = hud->GetHUDColor();
+ SetColor(hud_color);
+ }
+
+ if (hud->GetHUDMode() == HUDView::HUD_MODE_OFF)
+ hud_mode = 0;
+ }
+
+ if (ship && !transition && mode > 0 && hud_mode > 0) {
+ if (mode > 0) {
+ DoMouseFrame();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WepView::SetOverlayMode(int m)
+{
+ if (mode != m) {
+ mode = m;
+
+ if (hud)
+ hud->SetOverlayMode(mode);
+
+ RestoreOverlay();
+ }
+}
+
+void
+WepView::CycleOverlayMode()
+{
+ SetOverlayMode(!mode);
+}
+
+void
+WepView::RestoreOverlay()
+{
+ if (mode > 0) {
+ HUDSounds::PlaySound(HUDSounds::SND_WEP_DISP);
+ }
+
+ else {
+ HUDSounds::PlaySound(HUDSounds::SND_WEP_MODE);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WepView::SetColor(Color c)
+{
+ HUDView* hud = HUDView::GetInstance();
+
+ if (hud) {
+ hud_color = hud->GetHUDColor();
+ txt_color = hud->GetTextColor();
+ }
+ else {
+ hud_color = c;
+ txt_color = c;
+ }
+
+ HUDView::ColorizeBitmap(tac_left, tac_left_shade, hud_color);
+ HUDView::ColorizeBitmap(tac_right, tac_right_shade, hud_color);
+ HUDView::ColorizeBitmap(tac_button, tac_button_shade, hud_color);
+ HUDView::ColorizeBitmap(tac_man, tac_man_shade, hud_color);
+ HUDView::ColorizeBitmap(tac_aut, tac_aut_shade, hud_color);
+ HUDView::ColorizeBitmap(tac_def, tac_def_shade, hud_color);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WepView::DrawOverlay()
+{
+ int cx = width/2;
+ int cy = 0;
+ int w = tac_left.Width();
+ int h = tac_left.Height();
+
+ window->DrawBitmap(cx-w, cy, cx, cy+h, &tac_left, Video::BLEND_ALPHA);
+ window->DrawBitmap(cx, cy, cx+w, cy+h, &tac_right, Video::BLEND_ALPHA);
+
+ if (ship) {
+ List<WeaponGroup>& weapons = ship->Weapons();
+ for (int i = 0; i < MAX_WEP; i++) {
+ if (weapons.size() > i) {
+ // draw main fire button:
+ Rect r = btn_rect[i*4];
+
+ w = tac_button.Width();
+ h = tac_button.Height();
+ cx = r.x + r.w/2 - w/2;
+ cy = r.y + r.h/2 - h/2;
+
+ r.Deflate(5,5);
+
+ big_font->SetColor(txt_color);
+ window->SetFont(big_font);
+ window->DrawBitmap(cx, cy, cx+w, cy+h, &tac_button, Video::BLEND_ALPHA);
+ window->DrawText(weapons[i]->Name(), 0, r, DT_SINGLELINE | DT_CENTER);
+
+ r.Inflate(5,5);
+
+ // draw firing orders:
+ int o = weapons[i]->GetFiringOrders();
+ w = tac_man.Width();
+ h = tac_man.Height();
+
+ Color c0 = Color::Gray;
+ Color c1 = Color::White;
+
+ r = btn_rect[i*4 + 1];
+ cx = r.x + r.w/2 - w/2;
+ cy = r.y + r.h/2 - h/2;
+ window->FadeBitmap(cx, cy, cx+w, cy+h, &tac_man, (o==0 ? c1 : c0), Video::BLEND_ALPHA);
+
+ r = btn_rect[i*4 + 2];
+ cx = r.x + r.w/2 - w/2;
+ cy = r.y + r.h/2 - h/2;
+ window->FadeBitmap(cx, cy, cx+w, cy+h, &tac_aut, (o==1 ? c1 : c0), Video::BLEND_ALPHA);
+
+ r = btn_rect[i*4 + 3];
+ cx = r.x + r.w/2 - w/2;
+ cy = r.y + r.h/2 - h/2;
+ window->FadeBitmap(cx, cy, cx+w, cy+h, &tac_def, (o==2 ? c1 : c0), Video::BLEND_ALPHA);
+ }
+ }
+
+ Rect tgt_rect;
+ tgt_rect.x = width/2 + 73;
+ tgt_rect.y = 74;
+ tgt_rect.w = 100;
+ tgt_rect.h = 15;
+
+ Text subtxt;
+ Color stat = hud_color;
+ static DWORD blink = Game::RealTime();
+
+ if (ship->GetTarget()) {
+ if (ship->GetSubTarget()) {
+ int blink_delta = Game::RealTime() - blink;
+
+ System* sys = ship->GetSubTarget();
+ subtxt = sys->Abbreviation();
+ switch (sys->Status()) {
+ case System::DEGRADED: stat = Color(255,255, 0); break;
+ case System::CRITICAL:
+ case System::DESTROYED: stat = Color(255, 0, 0); break;
+ case System::MAINT:
+ if (blink_delta < 250)
+ stat = Color(8,8,8);
+ break;
+ }
+
+ if (blink_delta > 500)
+ blink = Game::RealTime();
+ }
+
+ else
+ subtxt = ship->GetTarget()->Name();
+ }
+ else {
+ subtxt = "NO TGT";
+ }
+
+ subtxt.toUpper();
+
+ hud_font->SetColor(stat);
+ window->SetFont(hud_font);
+ window->DrawText(subtxt.data(), subtxt.length(), tgt_rect, DT_SINGLELINE | DT_CENTER);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WepView::DoMouseFrame()
+{
+ static int mouse_down = false;
+ static int mouse_down_x = 0;
+ static int mouse_down_y = 0;
+
+ int x = Mouse::X();
+ int y = Mouse::Y();
+
+ // coarse-grained test: is mouse in overlay at all?
+ if (x < width/2-256 || x > width/2+256 || y > 90) {
+ mouse_in = false;
+ return;
+ }
+
+ mouse_in = true;
+
+ if (Mouse::LButton()) {
+ if (!mouse_down) {
+ mouse_down = true;
+ mouse_down_x = x;
+ mouse_down_y = y;
+ }
+
+ // check weapons buttons:
+ int max_wep = ship->Weapons().size();
+
+ if (max_wep > MAX_WEP)
+ max_wep = MAX_WEP;
+
+ for (int i = 0; i < max_wep; i++) {
+ int index = i * 4;
+
+ if (CheckButton(index, mouse_down_x, mouse_down_y)) {
+ ship->FireWeapon(i);
+ return;
+ }
+
+ else if (CheckButton(index + 1, mouse_down_x, mouse_down_y)) {
+ ship->Weapons()[i]->SetFiringOrders(Weapon::MANUAL);
+ return;
+ }
+
+ else if (CheckButton(index + 2, mouse_down_x, mouse_down_y)) {
+ ship->Weapons()[i]->SetFiringOrders(Weapon::AUTO);
+ return;
+ }
+
+ else if (CheckButton(index + 3, mouse_down_x, mouse_down_y)) {
+ ship->Weapons()[i]->SetFiringOrders(Weapon::POINT_DEFENSE);
+ return;
+ }
+ }
+ }
+
+ else if (mouse_down) {
+ mouse_down = false;
+ mouse_down_x = 0;
+ mouse_down_y = 0;
+
+ // check subtarget buttons:
+ if (ship->GetTarget()) {
+ Rect r(width/2+50, 70, 20, 20);
+ if (r.Contains(x,y)) {
+ CycleSubTarget(-1);
+ return;
+ }
+
+ r.x = width/2 + 180;
+ if (r.Contains(x,y)) {
+ CycleSubTarget(1);
+ return;
+ }
+ }
+ }
+}
+
+bool
+WepView::CheckButton(int index, int x, int y)
+{
+ if (index >= 0 && index < MAX_BTN) {
+ return btn_rect[index].Contains(x,y)?true:false;
+ }
+
+ return false;
+}
+
+void
+WepView::CycleSubTarget(int direction)
+{
+ if (ship->GetTarget() == 0 || ship->GetTarget()->Type() != SimObject::SIM_SHIP)
+ return;
+
+ ship->CycleSubTarget(direction);
+}
+
+bool
+WepView::IsMouseLatched()
+{
+ return mouse_in;
+}
diff --git a/Stars45/WepView.h b/Stars45/WepView.h
new file mode 100644
index 0000000..1f5bcfb
--- /dev/null
+++ b/Stars45/WepView.h
@@ -0,0 +1,88 @@
+/* Project Starshatter 4.5
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: Stars.exe
+ FILE: WepView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ View class for Tactical HUD Overlay
+*/
+
+#ifndef WepView_h
+#define WepView_h
+
+#include "Types.h"
+#include "View.h"
+#include "Projector.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "System.h"
+#include "SimObject.h"
+
+// +--------------------------------------------------------------------+
+
+class Graphic;
+class Sprite;
+class Ship;
+class Contact;
+class HUDView;
+
+// +--------------------------------------------------------------------+
+
+class WepView : public View,
+ public SimObserver
+{
+public:
+ WepView(Window* c);
+ virtual ~WepView();
+
+ // Operations:
+ virtual void Refresh();
+ virtual void OnWindowMove();
+ virtual void ExecFrame();
+ virtual void SetOverlayMode(int mode);
+ virtual int GetOverlayMode() const { return mode; }
+ virtual void CycleOverlayMode();
+
+ virtual void RestoreOverlay();
+
+ virtual bool Update(SimObject* obj);
+ virtual const char* GetObserverName() const;
+
+ static WepView* GetInstance() { return wep_view; }
+ static void SetColor(Color c);
+
+ static bool IsMouseLatched();
+
+protected:
+ void DrawOverlay();
+
+ void DoMouseFrame();
+ bool CheckButton(int index, int x, int y);
+ void CycleSubTarget(int direction);
+
+ int mode;
+ int transition;
+ int mouse_down;
+ int width, height, aw, ah;
+ double xcenter, ycenter;
+
+ Sim* sim;
+ Ship* ship;
+ SimObject* target;
+ HUDView* hud;
+
+ enum { MAX_WEP = 4, MAX_BTN = 16 };
+ Rect btn_rect[MAX_BTN];
+
+ SimRegion* active_region;
+
+ static WepView* wep_view;
+};
+
+#endif WepView_h
+
diff --git a/Stars45/resource.h b/Stars45/resource.h
new file mode 100644
index 0000000..3c14a64
--- /dev/null
+++ b/Stars45/resource.h
@@ -0,0 +1,18 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by Stars.rc
+//
+#define Stars 100
+#define IDI_ICON1 101
+#define IDI_ICON2 102
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 104
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Starshatter/Starshatter.sln b/Starshatter/Starshatter.sln
new file mode 100644
index 0000000..1f601b3
--- /dev/null
+++ b/Starshatter/Starshatter.sln
@@ -0,0 +1,235 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Magic", "..\Magic2\Magic.vcxproj", "{97D8BF03-E17C-1464-C00F-4C2E9330A2BA}"
+ ProjectSection(ProjectDependencies) = postProject
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93} = {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}
+ {235F1283-ADF3-2610-6172-F63C1B91614C} = {235F1283-ADF3-2610-6172-F63C1B91614C}
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6} = {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}
+ {86C01EAC-2406-40A7-B744-AA858B7607A9} = {86C01EAC-2406-40A7-B744-AA858B7607A9}
+ {75459DCB-61B5-811E-9994-D3A41FF13467} = {75459DCB-61B5-811E-9994-D3A41FF13467}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetEx", "..\NetEx\NetEx.vcxproj", "{D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nGenEx", "..\nGenEx\nGenEx.vcxproj", "{235F1283-ADF3-2610-6172-F63C1B91614C}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Opcode", "..\Opcode\Opcode.vcxproj", "{6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Stars", "..\Stars45\Stars.vcxproj", "{3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}"
+ ProjectSection(ProjectDependencies) = postProject
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93} = {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}
+ {235F1283-ADF3-2610-6172-F63C1B91614C} = {235F1283-ADF3-2610-6172-F63C1B91614C}
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6} = {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}
+ {86C01EAC-2406-40A7-B744-AA858B7607A9} = {86C01EAC-2406-40A7-B744-AA858B7607A9}
+ {75459DCB-61B5-811E-9994-D3A41FF13467} = {75459DCB-61B5-811E-9994-D3A41FF13467}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Zlib", "..\zlib\Zlib.vcxproj", "{75459DCB-61B5-811E-9994-D3A41FF13467}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpng", "..\libpng\libpng.vcxproj", "{86C01EAC-2406-40A7-B744-AA858B7607A9}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug Inferno SSE|Win32 = Debug Inferno SSE|Win32
+ Debug Inferno SSE2|Win32 = Debug Inferno SSE2|Win32
+ Debug Inferno|Win32 = Debug Inferno|Win32
+ Debug SSE|Win32 = Debug SSE|Win32
+ Debug SSE2|Win32 = Debug SSE2|Win32
+ Debug|Win32 = Debug|Win32
+ Release Inferno SSE|Win32 = Release Inferno SSE|Win32
+ Release Inferno SSE2|Win32 = Release Inferno SSE2|Win32
+ Release Inferno|Win32 = Release Inferno|Win32
+ Release SSE|Win32 = Release SSE|Win32
+ Release SSE2|Win32 = Release SSE2|Win32
+ Release|Win32 = Release|Win32
+ Template|Win32 = Template|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Debug Inferno SSE|Win32.ActiveCfg = Debug|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Debug Inferno SSE|Win32.Build.0 = Debug|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Debug Inferno SSE2|Win32.ActiveCfg = Debug|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Debug Inferno SSE2|Win32.Build.0 = Debug|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Debug Inferno|Win32.ActiveCfg = Debug|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Debug Inferno|Win32.Build.0 = Debug|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Debug SSE|Win32.ActiveCfg = Debug|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Debug SSE|Win32.Build.0 = Debug|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Debug SSE2|Win32.ActiveCfg = Debug|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Debug SSE2|Win32.Build.0 = Debug|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Debug|Win32.ActiveCfg = Debug|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Debug|Win32.Build.0 = Debug|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Release Inferno SSE|Win32.ActiveCfg = Release|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Release Inferno SSE|Win32.Build.0 = Release|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Release Inferno SSE2|Win32.ActiveCfg = Release|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Release Inferno SSE2|Win32.Build.0 = Release|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Release Inferno|Win32.ActiveCfg = Release|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Release Inferno|Win32.Build.0 = Release|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Release SSE|Win32.ActiveCfg = Release|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Release SSE|Win32.Build.0 = Release|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Release SSE2|Win32.ActiveCfg = Release|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Release SSE2|Win32.Build.0 = Release|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Release|Win32.ActiveCfg = Release|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Release|Win32.Build.0 = Release|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Template|Win32.ActiveCfg = Release|Win32
+ {97D8BF03-E17C-1464-C00F-4C2E9330A2BA}.Template|Win32.Build.0 = Release|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Debug Inferno SSE|Win32.ActiveCfg = Debug|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Debug Inferno SSE|Win32.Build.0 = Debug|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Debug Inferno SSE2|Win32.ActiveCfg = Debug|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Debug Inferno SSE2|Win32.Build.0 = Debug|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Debug Inferno|Win32.ActiveCfg = Debug|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Debug Inferno|Win32.Build.0 = Debug|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Debug SSE|Win32.ActiveCfg = Debug|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Debug SSE|Win32.Build.0 = Debug|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Debug SSE2|Win32.ActiveCfg = Debug|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Debug SSE2|Win32.Build.0 = Debug|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Debug|Win32.ActiveCfg = Debug|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Debug|Win32.Build.0 = Debug|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Release Inferno SSE|Win32.ActiveCfg = Release|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Release Inferno SSE|Win32.Build.0 = Release|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Release Inferno SSE2|Win32.ActiveCfg = Release|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Release Inferno SSE2|Win32.Build.0 = Release|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Release Inferno|Win32.ActiveCfg = Release|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Release Inferno|Win32.Build.0 = Release|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Release SSE|Win32.ActiveCfg = Release|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Release SSE|Win32.Build.0 = Release|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Release SSE2|Win32.ActiveCfg = Release|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Release SSE2|Win32.Build.0 = Release|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Release|Win32.ActiveCfg = Release|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Release|Win32.Build.0 = Release|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Template|Win32.ActiveCfg = Template|Win32
+ {D05CBFA1-0F55-B7EF-0FCA-6E5284AF0BA6}.Template|Win32.Build.0 = Template|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Debug Inferno SSE|Win32.ActiveCfg = Debug|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Debug Inferno SSE|Win32.Build.0 = Debug|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Debug Inferno SSE2|Win32.ActiveCfg = Debug|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Debug Inferno SSE2|Win32.Build.0 = Debug|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Debug Inferno|Win32.ActiveCfg = Debug|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Debug Inferno|Win32.Build.0 = Debug|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Debug SSE|Win32.ActiveCfg = Debug|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Debug SSE|Win32.Build.0 = Debug|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Debug SSE2|Win32.ActiveCfg = Debug|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Debug SSE2|Win32.Build.0 = Debug|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Debug|Win32.ActiveCfg = Debug|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Debug|Win32.Build.0 = Debug|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Release Inferno SSE|Win32.ActiveCfg = Release|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Release Inferno SSE|Win32.Build.0 = Release|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Release Inferno SSE2|Win32.ActiveCfg = Release|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Release Inferno SSE2|Win32.Build.0 = Release|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Release Inferno|Win32.ActiveCfg = Release|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Release Inferno|Win32.Build.0 = Release|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Release SSE|Win32.ActiveCfg = Release|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Release SSE|Win32.Build.0 = Release|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Release SSE2|Win32.ActiveCfg = Release|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Release SSE2|Win32.Build.0 = Release|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Release|Win32.ActiveCfg = Release|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Release|Win32.Build.0 = Release|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Template|Win32.ActiveCfg = Template|Win32
+ {235F1283-ADF3-2610-6172-F63C1B91614C}.Template|Win32.Build.0 = Template|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Debug Inferno SSE|Win32.ActiveCfg = Debug|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Debug Inferno SSE|Win32.Build.0 = Debug|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Debug Inferno SSE2|Win32.ActiveCfg = Debug|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Debug Inferno SSE2|Win32.Build.0 = Debug|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Debug Inferno|Win32.ActiveCfg = Debug|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Debug Inferno|Win32.Build.0 = Debug|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Debug SSE|Win32.ActiveCfg = Debug|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Debug SSE|Win32.Build.0 = Debug|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Debug SSE2|Win32.ActiveCfg = Debug|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Debug SSE2|Win32.Build.0 = Debug|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Debug|Win32.ActiveCfg = Debug|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Debug|Win32.Build.0 = Debug|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Release Inferno SSE|Win32.ActiveCfg = Release|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Release Inferno SSE|Win32.Build.0 = Release|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Release Inferno SSE2|Win32.ActiveCfg = Release|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Release Inferno SSE2|Win32.Build.0 = Release|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Release Inferno|Win32.ActiveCfg = Release|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Release Inferno|Win32.Build.0 = Release|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Release SSE|Win32.ActiveCfg = Release|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Release SSE|Win32.Build.0 = Release|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Release SSE2|Win32.ActiveCfg = Release|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Release SSE2|Win32.Build.0 = Release|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Release|Win32.ActiveCfg = Release|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Release|Win32.Build.0 = Release|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Template|Win32.ActiveCfg = Template|Win32
+ {6622FF0A-96E6-A43D-71E9-1B17FA1CCD93}.Template|Win32.Build.0 = Template|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Debug Inferno SSE|Win32.ActiveCfg = Debug|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Debug Inferno SSE|Win32.Build.0 = Debug|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Debug Inferno SSE2|Win32.ActiveCfg = Debug|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Debug Inferno SSE2|Win32.Build.0 = Debug|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Debug Inferno|Win32.ActiveCfg = Debug|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Debug Inferno|Win32.Build.0 = Debug|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Debug SSE|Win32.ActiveCfg = Debug|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Debug SSE|Win32.Build.0 = Debug|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Debug SSE2|Win32.ActiveCfg = Debug|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Debug SSE2|Win32.Build.0 = Debug|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Debug|Win32.ActiveCfg = Debug|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Debug|Win32.Build.0 = Debug|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Release Inferno SSE|Win32.ActiveCfg = Release|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Release Inferno SSE|Win32.Build.0 = Release|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Release Inferno SSE2|Win32.ActiveCfg = Release|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Release Inferno SSE2|Win32.Build.0 = Release|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Release Inferno|Win32.ActiveCfg = Release|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Release Inferno|Win32.Build.0 = Release|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Release SSE|Win32.ActiveCfg = Release|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Release SSE|Win32.Build.0 = Release|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Release SSE2|Win32.ActiveCfg = Release|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Release SSE2|Win32.Build.0 = Release|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Release|Win32.ActiveCfg = Release|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Release|Win32.Build.0 = Release|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Template|Win32.ActiveCfg = Template|Win32
+ {3CCA1A34-FCCF-D7DB-E6D8-A46D622C401B}.Template|Win32.Build.0 = Template|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Debug Inferno SSE|Win32.ActiveCfg = Debug|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Debug Inferno SSE|Win32.Build.0 = Debug|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Debug Inferno SSE2|Win32.ActiveCfg = Debug|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Debug Inferno SSE2|Win32.Build.0 = Debug|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Debug Inferno|Win32.ActiveCfg = Debug|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Debug Inferno|Win32.Build.0 = Debug|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Debug SSE|Win32.ActiveCfg = Debug|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Debug SSE|Win32.Build.0 = Debug|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Debug SSE2|Win32.ActiveCfg = Debug|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Debug SSE2|Win32.Build.0 = Debug|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Debug|Win32.ActiveCfg = Debug|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Debug|Win32.Build.0 = Debug|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Release Inferno SSE|Win32.ActiveCfg = Release|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Release Inferno SSE|Win32.Build.0 = Release|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Release Inferno SSE2|Win32.ActiveCfg = Release|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Release Inferno SSE2|Win32.Build.0 = Release|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Release Inferno|Win32.ActiveCfg = Release|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Release Inferno|Win32.Build.0 = Release|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Release SSE|Win32.ActiveCfg = Release|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Release SSE|Win32.Build.0 = Release|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Release SSE2|Win32.ActiveCfg = Release|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Release SSE2|Win32.Build.0 = Release|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Release|Win32.ActiveCfg = Release|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Release|Win32.Build.0 = Release|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Template|Win32.ActiveCfg = Template|Win32
+ {75459DCB-61B5-811E-9994-D3A41FF13467}.Template|Win32.Build.0 = Template|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Debug Inferno SSE|Win32.ActiveCfg = Debug Inferno SSE|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Debug Inferno SSE|Win32.Build.0 = Debug Inferno SSE|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Debug Inferno SSE2|Win32.ActiveCfg = Debug Inferno SSE2|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Debug Inferno SSE2|Win32.Build.0 = Debug Inferno SSE2|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Debug Inferno|Win32.ActiveCfg = Debug Inferno|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Debug Inferno|Win32.Build.0 = Debug Inferno|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Debug SSE|Win32.ActiveCfg = Debug SSE|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Debug SSE|Win32.Build.0 = Debug SSE|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Debug SSE2|Win32.ActiveCfg = Debug SSE2|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Debug SSE2|Win32.Build.0 = Debug SSE2|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Debug|Win32.ActiveCfg = Debug|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Debug|Win32.Build.0 = Debug|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Release Inferno SSE|Win32.ActiveCfg = Release Inferno SSE|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Release Inferno SSE|Win32.Build.0 = Release Inferno SSE|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Release Inferno SSE2|Win32.ActiveCfg = Release Inferno SSE2|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Release Inferno SSE2|Win32.Build.0 = Release Inferno SSE2|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Release Inferno|Win32.ActiveCfg = Release Inferno|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Release Inferno|Win32.Build.0 = Release Inferno|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Release SSE|Win32.ActiveCfg = Release SSE|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Release SSE|Win32.Build.0 = Release SSE|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Release SSE2|Win32.ActiveCfg = Release SSE2|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Release SSE2|Win32.Build.0 = Release SSE2|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Release|Win32.ActiveCfg = Release|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Release|Win32.Build.0 = Release|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Template|Win32.ActiveCfg = Release|Win32
+ {86C01EAC-2406-40A7-B744-AA858B7607A9}.Template|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/libpng/libpng.vcxproj b/libpng/libpng.vcxproj
new file mode 100644
index 0000000..5746b46
--- /dev/null
+++ b/libpng/libpng.vcxproj
@@ -0,0 +1,679 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug Inferno SSE2|Win32">
+ <Configuration>Debug Inferno SSE2</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug Inferno SSE|Win32">
+ <Configuration>Debug Inferno SSE</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug Inferno|Win32">
+ <Configuration>Debug Inferno</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug SSE2|Win32">
+ <Configuration>Debug SSE2</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug SSE|Win32">
+ <Configuration>Debug SSE</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release Inferno SSE2|Win32">
+ <Configuration>Release Inferno SSE2</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release Inferno SSE|Win32">
+ <Configuration>Release Inferno SSE</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release Inferno|Win32">
+ <Configuration>Release Inferno</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release SSE2|Win32">
+ <Configuration>Release SSE2</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release SSE|Win32">
+ <Configuration>Release SSE</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{86C01EAC-2406-40A7-B744-AA858B7607A9}</ProjectGuid>
+ <RootNamespace>libpng</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release SSE2|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release SSE|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Inferno SSE2|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Inferno SSE|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Inferno|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug SSE2|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug SSE|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Inferno SSE2|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Inferno SSE|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Inferno|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release SSE2|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release SSE|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Inferno SSE2|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Inferno SSE|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Inferno|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug SSE2|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug SSE|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Inferno SSE2|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Inferno SSE|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Inferno|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\$(ProjectName)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug Inferno|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug Inferno|Win32'">$(Configuration)\$(ProjectName)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug Inferno SSE|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug Inferno SSE|Win32'">$(Configuration)\$(ProjectName)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug Inferno SSE2|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug Inferno SSE2|Win32'">$(Configuration)\$(ProjectName)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug SSE|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug SSE|Win32'">$(Configuration)\$(ProjectName)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug SSE2|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug SSE2|Win32'">$(Configuration)\$(ProjectName)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\$(ProjectName)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release Inferno|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release Inferno|Win32'">$(Configuration)\$(ProjectName)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release Inferno SSE|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release Inferno SSE|Win32'">$(Configuration)\$(ProjectName)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release Inferno SSE2|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release Inferno SSE2|Win32'">$(Configuration)\$(ProjectName)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release SSE|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release SSE|Win32'">$(Configuration)\$(ProjectName)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release SSE2|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release SSE2|Win32'">$(Configuration)\$(ProjectName)\</IntDir>
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug Inferno SSE2|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug Inferno SSE2|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug Inferno SSE2|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug Inferno SSE|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug Inferno SSE|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug Inferno SSE|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug Inferno|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug Inferno|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug Inferno|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug SSE2|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug SSE2|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug SSE2|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug SSE|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug SSE|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug SSE|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release Inferno SSE2|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release Inferno SSE2|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release Inferno SSE2|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release Inferno SSE|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release Inferno SSE|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release Inferno SSE|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release Inferno|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release Inferno|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release Inferno|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release SSE2|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release SSE2|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release SSE2|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release SSE|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release SSE|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release SSE|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>PNG_DEBUG=1;WIN32;_DEBUG;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0;_HAS_ITERATOR_DEBUGGING=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Lib>
+ <OutputFile>$(OutDir)libpng.lib</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Lib>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(IntDir)$(ProjectName).bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Inferno|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>PNG_DEBUG=1;WIN32;_DEBUG;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0;_HAS_ITERATOR_DEBUGGING=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Lib>
+ <OutputFile>$(OutDir)libpng.lib</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Lib>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(IntDir)$(ProjectName).bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Inferno SSE|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>PNG_DEBUG=1;WIN32;_DEBUG;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0;_HAS_ITERATOR_DEBUGGING=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Lib>
+ <OutputFile>$(OutDir)libpng.lib</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Lib>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(IntDir)$(ProjectName).bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Inferno SSE2|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>PNG_DEBUG=1;WIN32;_DEBUG;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0;_HAS_ITERATOR_DEBUGGING=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Lib>
+ <OutputFile>$(OutDir)libpng.lib</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Lib>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(IntDir)$(ProjectName).bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug SSE|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>PNG_DEBUG=1;WIN32;_DEBUG;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0;_HAS_ITERATOR_DEBUGGING=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Lib>
+ <OutputFile>$(OutDir)libpng.lib</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Lib>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(IntDir)$(ProjectName).bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug SSE2|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>PNG_DEBUG=1;WIN32;_DEBUG;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0;_HAS_ITERATOR_DEBUGGING=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Lib>
+ <OutputFile>$(OutDir)libpng.lib</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Lib>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(IntDir)$(ProjectName).bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <AdditionalIncludeDirectories>..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0;_HAS_ITERATOR_DEBUGGING=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MinimalRebuild>false</MinimalRebuild>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>false</FunctionLevelLinking>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>
+ </DebugInformationFormat>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ <AdditionalIncludeDirectories>..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Lib>
+ <OutputFile>$(OutDir)libpng.lib</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Lib>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(IntDir)$(ProjectName).bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Inferno|Win32'">
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <AdditionalIncludeDirectories>..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0;_HAS_ITERATOR_DEBUGGING=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>false</FunctionLevelLinking>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ <AdditionalIncludeDirectories>..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Lib>
+ <OutputFile>$(OutDir)libpng.lib</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Lib>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(IntDir)$(ProjectName).bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Inferno SSE|Win32'">
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>Full</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <AdditionalIncludeDirectories>..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0;_HAS_ITERATOR_DEBUGGING=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>false</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ <AdditionalIncludeDirectories>..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Lib>
+ <OutputFile>$(OutDir)libpng.lib</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Lib>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(IntDir)$(ProjectName).bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Inferno SSE2|Win32'">
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>Full</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <AdditionalIncludeDirectories>..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0;_HAS_ITERATOR_DEBUGGING=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>false</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ <AdditionalIncludeDirectories>..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Lib>
+ <OutputFile>$(OutDir)libpng.lib</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Lib>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(IntDir)$(ProjectName).bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release SSE|Win32'">
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>Full</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <AdditionalIncludeDirectories>..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0;_HAS_ITERATOR_DEBUGGING=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MinimalRebuild>false</MinimalRebuild>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>false</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>
+ </DebugInformationFormat>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ <AdditionalIncludeDirectories>..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Lib>
+ <OutputFile>$(OutDir)libpng.lib</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Lib>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(IntDir)$(ProjectName).bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release SSE2|Win32'">
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>Full</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <AdditionalIncludeDirectories>..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0;_HAS_ITERATOR_DEBUGGING=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MinimalRebuild>false</MinimalRebuild>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>false</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <PrecompiledHeaderOutputFile>$(OutDir)$(ProjectName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>
+ </DebugInformationFormat>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ <AdditionalIncludeDirectories>..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Lib>
+ <OutputFile>$(OutDir)libpng.lib</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Lib>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(IntDir)$(ProjectName).bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\libpng\png.c" />
+ <ClCompile Include="..\..\libpng\pngerror.c" />
+ <ClCompile Include="..\..\libpng\pngget.c" />
+ <ClCompile Include="..\..\libpng\pngmem.c" />
+ <ClCompile Include="..\..\libpng\pngpread.c" />
+ <ClCompile Include="..\..\libpng\pngread.c" />
+ <ClCompile Include="..\..\libpng\pngrio.c" />
+ <ClCompile Include="..\..\libpng\pngrtran.c" />
+ <ClCompile Include="..\..\libpng\pngrutil.c" />
+ <ClCompile Include="..\..\libpng\pngset.c" />
+ <ClCompile Include="..\..\libpng\pngtrans.c" />
+ <ClCompile Include="..\..\libpng\pngwio.c" />
+ <ClCompile Include="..\..\libpng\pngwrite.c" />
+ <ClCompile Include="..\..\libpng\pngwtran.c" />
+ <ClCompile Include="..\..\libpng\pngwutil.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\libpng\png.h" />
+ <ClInclude Include="..\..\libpng\pngconf.h" />
+ <ClInclude Include="..\..\libpng\pngdebug.h" />
+ <ClInclude Include="..\..\libpng\pnginfo.h" />
+ <ClInclude Include="..\..\libpng\pnglibconf.h" />
+ <ClInclude Include="..\..\libpng\pngpriv.h" />
+ <ClInclude Include="..\..\libpng\pngstruct.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/libpng/libpng.vcxproj.filters b/libpng/libpng.vcxproj.filters
new file mode 100644
index 0000000..3157dda
--- /dev/null
+++ b/libpng/libpng.vcxproj.filters
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{8981e5df-9c92-47b2-a390-fe7196f3d2f2}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{3c6acdc9-079d-4763-8283-b313335fced5}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\libpng\png.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\libpng\pngerror.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\libpng\pngget.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\libpng\pngmem.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\libpng\pngpread.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\libpng\pngread.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\libpng\pngrio.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\libpng\pngrtran.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\libpng\pngrutil.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\libpng\pngset.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\libpng\pngtrans.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\libpng\pngwio.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\libpng\pngwrite.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\libpng\pngwtran.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\libpng\pngwutil.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\libpng\png.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\libpng\pngconf.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\libpng\pngdebug.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\libpng\pnginfo.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\libpng\pnglibconf.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\libpng\pngpriv.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\libpng\pngstruct.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/libpng/libpng.vcxproj.user b/libpng/libpng.vcxproj.user
new file mode 100644
index 0000000..695b5c7
--- /dev/null
+++ b/libpng/libpng.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project> \ No newline at end of file
diff --git a/libpng/png.c b/libpng/png.c
new file mode 100644
index 0000000..7d09b0d
--- /dev/null
+++ b/libpng/png.c
@@ -0,0 +1,2362 @@
+
+/* png.c - location for general purpose libpng functions
+ *
+ * Last changed in libpng 1.5.1 [February 3, 2011]
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include "pngpriv.h"
+
+/* Generate a compiler error if there is an old png.h in the search path. */
+typedef png_libpng_version_1_5_2 Your_png_h_is_not_version_1_5_2;
+
+/* Tells libpng that we have already handled the first "num_bytes" bytes
+ * of the PNG file signature. If the PNG data is embedded into another
+ * stream we can set num_bytes = 8 so that libpng will not attempt to read
+ * or write any of the magic bytes before it starts on the IHDR.
+ */
+
+#ifdef PNG_READ_SUPPORTED
+void PNGAPI
+png_set_sig_bytes(png_structp png_ptr, int num_bytes)
+{
+ png_debug(1, "in png_set_sig_bytes");
+
+ if (png_ptr == NULL)
+ return;
+
+ if (num_bytes > 8)
+ png_error(png_ptr, "Too many bytes for PNG signature");
+
+ png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes);
+}
+
+/* Checks whether the supplied bytes match the PNG signature. We allow
+ * checking less than the full 8-byte signature so that those apps that
+ * already read the first few bytes of a file to determine the file type
+ * can simply check the remaining bytes for extra assurance. Returns
+ * an integer less than, equal to, or greater than zero if sig is found,
+ * respectively, to be less than, to match, or be greater than the correct
+ * PNG signature (this is the same behaviour as strcmp, memcmp, etc).
+ */
+int PNGAPI
+png_sig_cmp(png_const_bytep sig, png_size_t start, png_size_t num_to_check)
+{
+ png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
+
+ if (num_to_check > 8)
+ num_to_check = 8;
+
+ else if (num_to_check < 1)
+ return (-1);
+
+ if (start > 7)
+ return (-1);
+
+ if (start + num_to_check > 8)
+ num_to_check = 8 - start;
+
+ return ((int)(png_memcmp(&sig[start], &png_signature[start], num_to_check)));
+}
+
+#endif /* PNG_READ_SUPPORTED */
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+/* Function to allocate memory for zlib */
+PNG_FUNCTION(voidpf /* PRIVATE */,
+png_zalloc,(voidpf png_ptr, uInt items, uInt size),PNG_ALLOCATED)
+{
+ png_voidp ptr;
+ png_structp p=(png_structp)png_ptr;
+ png_uint_32 save_flags=p->flags;
+ png_alloc_size_t num_bytes;
+
+ if (png_ptr == NULL)
+ return (NULL);
+
+ if (items > PNG_UINT_32_MAX/size)
+ {
+ png_warning (p, "Potential overflow in png_zalloc()");
+ return (NULL);
+ }
+ num_bytes = (png_alloc_size_t)items * size;
+
+ p->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK;
+ ptr = (png_voidp)png_malloc((png_structp)png_ptr, num_bytes);
+ p->flags=save_flags;
+
+ return ((voidpf)ptr);
+}
+
+/* Function to free memory for zlib */
+void /* PRIVATE */
+png_zfree(voidpf png_ptr, voidpf ptr)
+{
+ png_free((png_structp)png_ptr, (png_voidp)ptr);
+}
+
+/* Reset the CRC variable to 32 bits of 1's. Care must be taken
+ * in case CRC is > 32 bits to leave the top bits 0.
+ */
+void /* PRIVATE */
+png_reset_crc(png_structp png_ptr)
+{
+ png_ptr->crc = crc32(0, Z_NULL, 0);
+}
+
+/* Calculate the CRC over a section of data. We can only pass as
+ * much data to this routine as the largest single buffer size. We
+ * also check that this data will actually be used before going to the
+ * trouble of calculating it.
+ */
+void /* PRIVATE */
+png_calculate_crc(png_structp png_ptr, png_const_bytep ptr, png_size_t length)
+{
+ int need_crc = 1;
+
+ if (png_ptr->chunk_name[0] & 0x20) /* ancillary */
+ {
+ if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
+ (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
+ need_crc = 0;
+ }
+
+ else /* critical */
+ {
+ if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE)
+ need_crc = 0;
+ }
+
+ if (need_crc)
+ png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length);
+}
+
+/* Allocate the memory for an info_struct for the application. We don't
+ * really need the png_ptr, but it could potentially be useful in the
+ * future. This should be used in favour of malloc(png_sizeof(png_info))
+ * and png_info_init() so that applications that want to use a shared
+ * libpng don't have to be recompiled if png_info changes size.
+ */
+PNG_FUNCTION(png_infop,PNGAPI
+png_create_info_struct,(png_structp png_ptr),PNG_ALLOCATED)
+{
+ png_infop info_ptr;
+
+ png_debug(1, "in png_create_info_struct");
+
+ if (png_ptr == NULL)
+ return (NULL);
+
+#ifdef PNG_USER_MEM_SUPPORTED
+ info_ptr = (png_infop)png_create_struct_2(PNG_STRUCT_INFO,
+ png_ptr->malloc_fn, png_ptr->mem_ptr);
+#else
+ info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO);
+#endif
+ if (info_ptr != NULL)
+ png_info_init_3(&info_ptr, png_sizeof(png_info));
+
+ return (info_ptr);
+}
+
+/* This function frees the memory associated with a single info struct.
+ * Normally, one would use either png_destroy_read_struct() or
+ * png_destroy_write_struct() to free an info struct, but this may be
+ * useful for some applications.
+ */
+void PNGAPI
+png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr)
+{
+ png_infop info_ptr = NULL;
+
+ png_debug(1, "in png_destroy_info_struct");
+
+ if (png_ptr == NULL)
+ return;
+
+ if (info_ptr_ptr != NULL)
+ info_ptr = *info_ptr_ptr;
+
+ if (info_ptr != NULL)
+ {
+ png_info_destroy(png_ptr, info_ptr);
+
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_destroy_struct_2((png_voidp)info_ptr, png_ptr->free_fn,
+ png_ptr->mem_ptr);
+#else
+ png_destroy_struct((png_voidp)info_ptr);
+#endif
+ *info_ptr_ptr = NULL;
+ }
+}
+
+/* Initialize the info structure. This is now an internal function (0.89)
+ * and applications using it are urged to use png_create_info_struct()
+ * instead.
+ */
+
+void PNGAPI
+png_info_init_3(png_infopp ptr_ptr, png_size_t png_info_struct_size)
+{
+ png_infop info_ptr = *ptr_ptr;
+
+ png_debug(1, "in png_info_init_3");
+
+ if (info_ptr == NULL)
+ return;
+
+ if (png_sizeof(png_info) > png_info_struct_size)
+ {
+ png_destroy_struct(info_ptr);
+ info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO);
+ *ptr_ptr = info_ptr;
+ }
+
+ /* Set everything to 0 */
+ png_memset(info_ptr, 0, png_sizeof(png_info));
+}
+
+void PNGAPI
+png_data_freer(png_structp png_ptr, png_infop info_ptr,
+ int freer, png_uint_32 mask)
+{
+ png_debug(1, "in png_data_freer");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ if (freer == PNG_DESTROY_WILL_FREE_DATA)
+ info_ptr->free_me |= mask;
+
+ else if (freer == PNG_USER_WILL_FREE_DATA)
+ info_ptr->free_me &= ~mask;
+
+ else
+ png_warning(png_ptr,
+ "Unknown freer parameter in png_data_freer");
+}
+
+void PNGAPI
+png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask,
+ int num)
+{
+ png_debug(1, "in png_free_data");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+#ifdef PNG_TEXT_SUPPORTED
+ /* Free text item num or (if num == -1) all text items */
+ if ((mask & PNG_FREE_TEXT) & info_ptr->free_me)
+ {
+ if (num != -1)
+ {
+ if (info_ptr->text && info_ptr->text[num].key)
+ {
+ png_free(png_ptr, info_ptr->text[num].key);
+ info_ptr->text[num].key = NULL;
+ }
+ }
+
+ else
+ {
+ int i;
+ for (i = 0; i < info_ptr->num_text; i++)
+ png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i);
+ png_free(png_ptr, info_ptr->text);
+ info_ptr->text = NULL;
+ info_ptr->num_text=0;
+ }
+ }
+#endif
+
+#ifdef PNG_tRNS_SUPPORTED
+ /* Free any tRNS entry */
+ if ((mask & PNG_FREE_TRNS) & info_ptr->free_me)
+ {
+ png_free(png_ptr, info_ptr->trans_alpha);
+ info_ptr->trans_alpha = NULL;
+ info_ptr->valid &= ~PNG_INFO_tRNS;
+ }
+#endif
+
+#ifdef PNG_sCAL_SUPPORTED
+ /* Free any sCAL entry */
+ if ((mask & PNG_FREE_SCAL) & info_ptr->free_me)
+ {
+#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED)
+ png_free(png_ptr, info_ptr->scal_s_width);
+ png_free(png_ptr, info_ptr->scal_s_height);
+ info_ptr->scal_s_width = NULL;
+ info_ptr->scal_s_height = NULL;
+#endif
+ info_ptr->valid &= ~PNG_INFO_sCAL;
+ }
+#endif
+
+#ifdef PNG_pCAL_SUPPORTED
+ /* Free any pCAL entry */
+ if ((mask & PNG_FREE_PCAL) & info_ptr->free_me)
+ {
+ png_free(png_ptr, info_ptr->pcal_purpose);
+ png_free(png_ptr, info_ptr->pcal_units);
+ info_ptr->pcal_purpose = NULL;
+ info_ptr->pcal_units = NULL;
+ if (info_ptr->pcal_params != NULL)
+ {
+ int i;
+ for (i = 0; i < (int)info_ptr->pcal_nparams; i++)
+ {
+ png_free(png_ptr, info_ptr->pcal_params[i]);
+ info_ptr->pcal_params[i] = NULL;
+ }
+ png_free(png_ptr, info_ptr->pcal_params);
+ info_ptr->pcal_params = NULL;
+ }
+ info_ptr->valid &= ~PNG_INFO_pCAL;
+ }
+#endif
+
+#ifdef PNG_iCCP_SUPPORTED
+ /* Free any iCCP entry */
+ if ((mask & PNG_FREE_ICCP) & info_ptr->free_me)
+ {
+ png_free(png_ptr, info_ptr->iccp_name);
+ png_free(png_ptr, info_ptr->iccp_profile);
+ info_ptr->iccp_name = NULL;
+ info_ptr->iccp_profile = NULL;
+ info_ptr->valid &= ~PNG_INFO_iCCP;
+ }
+#endif
+
+#ifdef PNG_sPLT_SUPPORTED
+ /* Free a given sPLT entry, or (if num == -1) all sPLT entries */
+ if ((mask & PNG_FREE_SPLT) & info_ptr->free_me)
+ {
+ if (num != -1)
+ {
+ if (info_ptr->splt_palettes)
+ {
+ png_free(png_ptr, info_ptr->splt_palettes[num].name);
+ png_free(png_ptr, info_ptr->splt_palettes[num].entries);
+ info_ptr->splt_palettes[num].name = NULL;
+ info_ptr->splt_palettes[num].entries = NULL;
+ }
+ }
+
+ else
+ {
+ if (info_ptr->splt_palettes_num)
+ {
+ int i;
+ for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)
+ png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, i);
+
+ png_free(png_ptr, info_ptr->splt_palettes);
+ info_ptr->splt_palettes = NULL;
+ info_ptr->splt_palettes_num = 0;
+ }
+ info_ptr->valid &= ~PNG_INFO_sPLT;
+ }
+ }
+#endif
+
+#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
+ if (png_ptr->unknown_chunk.data)
+ {
+ png_free(png_ptr, png_ptr->unknown_chunk.data);
+ png_ptr->unknown_chunk.data = NULL;
+ }
+
+ if ((mask & PNG_FREE_UNKN) & info_ptr->free_me)
+ {
+ if (num != -1)
+ {
+ if (info_ptr->unknown_chunks)
+ {
+ png_free(png_ptr, info_ptr->unknown_chunks[num].data);
+ info_ptr->unknown_chunks[num].data = NULL;
+ }
+ }
+
+ else
+ {
+ int i;
+
+ if (info_ptr->unknown_chunks_num)
+ {
+ for (i = 0; i < info_ptr->unknown_chunks_num; i++)
+ png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, i);
+
+ png_free(png_ptr, info_ptr->unknown_chunks);
+ info_ptr->unknown_chunks = NULL;
+ info_ptr->unknown_chunks_num = 0;
+ }
+ }
+ }
+#endif
+
+#ifdef PNG_hIST_SUPPORTED
+ /* Free any hIST entry */
+ if ((mask & PNG_FREE_HIST) & info_ptr->free_me)
+ {
+ png_free(png_ptr, info_ptr->hist);
+ info_ptr->hist = NULL;
+ info_ptr->valid &= ~PNG_INFO_hIST;
+ }
+#endif
+
+ /* Free any PLTE entry that was internally allocated */
+ if ((mask & PNG_FREE_PLTE) & info_ptr->free_me)
+ {
+ png_zfree(png_ptr, info_ptr->palette);
+ info_ptr->palette = NULL;
+ info_ptr->valid &= ~PNG_INFO_PLTE;
+ info_ptr->num_palette = 0;
+ }
+
+#ifdef PNG_INFO_IMAGE_SUPPORTED
+ /* Free any image bits attached to the info structure */
+ if ((mask & PNG_FREE_ROWS) & info_ptr->free_me)
+ {
+ if (info_ptr->row_pointers)
+ {
+ int row;
+ for (row = 0; row < (int)info_ptr->height; row++)
+ {
+ png_free(png_ptr, info_ptr->row_pointers[row]);
+ info_ptr->row_pointers[row] = NULL;
+ }
+ png_free(png_ptr, info_ptr->row_pointers);
+ info_ptr->row_pointers = NULL;
+ }
+ info_ptr->valid &= ~PNG_INFO_IDAT;
+ }
+#endif
+
+ if (num != -1)
+ mask &= ~PNG_FREE_MUL;
+
+ info_ptr->free_me &= ~mask;
+}
+
+/* This is an internal routine to free any memory that the info struct is
+ * pointing to before re-using it or freeing the struct itself. Recall
+ * that png_free() checks for NULL pointers for us.
+ */
+void /* PRIVATE */
+png_info_destroy(png_structp png_ptr, png_infop info_ptr)
+{
+ png_debug(1, "in png_info_destroy");
+
+ png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ if (png_ptr->num_chunk_list)
+ {
+ png_free(png_ptr, png_ptr->chunk_list);
+ png_ptr->chunk_list = NULL;
+ png_ptr->num_chunk_list = 0;
+ }
+#endif
+
+ png_info_init_3(&info_ptr, png_sizeof(png_info));
+}
+#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */
+
+/* This function returns a pointer to the io_ptr associated with the user
+ * functions. The application should free any memory associated with this
+ * pointer before png_write_destroy() or png_read_destroy() are called.
+ */
+png_voidp PNGAPI
+png_get_io_ptr(png_structp png_ptr)
+{
+ if (png_ptr == NULL)
+ return (NULL);
+
+ return (png_ptr->io_ptr);
+}
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+# ifdef PNG_STDIO_SUPPORTED
+/* Initialize the default input/output functions for the PNG file. If you
+ * use your own read or write routines, you can call either png_set_read_fn()
+ * or png_set_write_fn() instead of png_init_io(). If you have defined
+ * PNG_NO_STDIO, you must use a function of your own because "FILE *" isn't
+ * necessarily available.
+ */
+void PNGAPI
+png_init_io(png_structp png_ptr, png_FILE_p fp)
+{
+ png_debug(1, "in png_init_io");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->io_ptr = (png_voidp)fp;
+}
+# endif
+
+# ifdef PNG_TIME_RFC1123_SUPPORTED
+/* Convert the supplied time into an RFC 1123 string suitable for use in
+ * a "Creation Time" or other text-based time string.
+ */
+png_const_charp PNGAPI
+png_convert_to_rfc1123(png_structp png_ptr, png_const_timep ptime)
+{
+ static PNG_CONST char short_months[12][4] =
+ {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+ if (png_ptr == NULL)
+ return (NULL);
+
+ if (png_ptr->time_buffer == NULL)
+ {
+ png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, (png_uint_32)(29*
+ png_sizeof(char)));
+ }
+
+# ifdef USE_FAR_KEYWORD
+ {
+ char near_time_buf[29];
+ png_snprintf6(near_time_buf, 29, "%d %s %d %02d:%02d:%02d +0000",
+ ptime->day % 32, short_months[(ptime->month - 1) % 12],
+ ptime->year, ptime->hour % 24, ptime->minute % 60,
+ ptime->second % 61);
+ png_memcpy(png_ptr->time_buffer, near_time_buf,
+ 29*png_sizeof(char));
+ }
+# else
+ png_snprintf6(png_ptr->time_buffer, 29, "%d %s %d %02d:%02d:%02d +0000",
+ ptime->day % 32, short_months[(ptime->month - 1) % 12],
+ ptime->year, ptime->hour % 24, ptime->minute % 60,
+ ptime->second % 61);
+# endif
+ return png_ptr->time_buffer;
+}
+# endif /* PNG_TIME_RFC1123_SUPPORTED */
+
+#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */
+
+png_const_charp PNGAPI
+png_get_copyright(png_const_structp png_ptr)
+{
+ PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */
+#ifdef PNG_STRING_COPYRIGHT
+ return PNG_STRING_COPYRIGHT
+#else
+# ifdef __STDC__
+ return PNG_STRING_NEWLINE \
+ "libpng version 1.5.2 - March 31, 2011" PNG_STRING_NEWLINE \
+ "Copyright (c) 1998-2011 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \
+ "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \
+ "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \
+ PNG_STRING_NEWLINE;
+# else
+ return "libpng version 1.5.2 - March 31, 2011\
+ Copyright (c) 1998-2011 Glenn Randers-Pehrson\
+ Copyright (c) 1996-1997 Andreas Dilger\
+ Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.";
+# endif
+#endif
+}
+
+/* The following return the library version as a short string in the
+ * format 1.0.0 through 99.99.99zz. To get the version of *.h files
+ * used with your application, print out PNG_LIBPNG_VER_STRING, which
+ * is defined in png.h.
+ * Note: now there is no difference between png_get_libpng_ver() and
+ * png_get_header_ver(). Due to the version_nn_nn_nn typedef guard,
+ * it is guaranteed that png.c uses the correct version of png.h.
+ */
+png_const_charp PNGAPI
+png_get_libpng_ver(png_const_structp png_ptr)
+{
+ /* Version of *.c files used when building libpng */
+ return png_get_header_ver(png_ptr);
+}
+
+png_const_charp PNGAPI
+png_get_header_ver(png_const_structp png_ptr)
+{
+ /* Version of *.h files used when building libpng */
+ PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */
+ return PNG_LIBPNG_VER_STRING;
+}
+
+png_const_charp PNGAPI
+png_get_header_version(png_const_structp png_ptr)
+{
+ /* Returns longer string containing both version and date */
+ PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */
+#ifdef __STDC__
+ return PNG_HEADER_VERSION_STRING
+# ifndef PNG_READ_SUPPORTED
+ " (NO READ SUPPORT)"
+# endif
+ PNG_STRING_NEWLINE;
+#else
+ return PNG_HEADER_VERSION_STRING;
+#endif
+}
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+int PNGAPI
+png_handle_as_unknown(png_structp png_ptr, png_const_bytep chunk_name)
+{
+ /* Check chunk_name and return "keep" value if it's on the list, else 0 */
+ int i;
+ png_bytep p;
+ if (png_ptr == NULL || chunk_name == NULL || png_ptr->num_chunk_list<=0)
+ return 0;
+
+ p = png_ptr->chunk_list + png_ptr->num_chunk_list*5 - 5;
+ for (i = png_ptr->num_chunk_list; i; i--, p -= 5)
+ if (!png_memcmp(chunk_name, p, 4))
+ return ((int)*(p + 4));
+ return 0;
+}
+# endif
+#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */
+
+#ifdef PNG_READ_SUPPORTED
+/* This function, added to libpng-1.0.6g, is untested. */
+int PNGAPI
+png_reset_zstream(png_structp png_ptr)
+{
+ if (png_ptr == NULL)
+ return Z_STREAM_ERROR;
+
+ return (inflateReset(&png_ptr->zstream));
+}
+#endif /* PNG_READ_SUPPORTED */
+
+/* This function was added to libpng-1.0.7 */
+png_uint_32 PNGAPI
+png_access_version_number(void)
+{
+ /* Version of *.c files used when building libpng */
+ return((png_uint_32)PNG_LIBPNG_VER);
+}
+
+
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+# ifdef PNG_SIZE_T
+/* Added at libpng version 1.2.6 */
+ PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size));
+png_size_t PNGAPI
+png_convert_size(size_t size)
+{
+ if (size > (png_size_t)-1)
+ PNG_ABORT(); /* We haven't got access to png_ptr, so no png_error() */
+
+ return ((png_size_t)size);
+}
+# endif /* PNG_SIZE_T */
+
+/* Added at libpng version 1.2.34 and 1.4.0 (moved from pngset.c) */
+# ifdef PNG_CHECK_cHRM_SUPPORTED
+
+int /* PRIVATE */
+png_check_cHRM_fixed(png_structp png_ptr,
+ png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x,
+ png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y,
+ png_fixed_point blue_x, png_fixed_point blue_y)
+{
+ int ret = 1;
+ unsigned long xy_hi,xy_lo,yx_hi,yx_lo;
+
+ png_debug(1, "in function png_check_cHRM_fixed");
+
+ if (png_ptr == NULL)
+ return 0;
+
+ if (white_x < 0 || white_y <= 0 ||
+ red_x < 0 || red_y < 0 ||
+ green_x < 0 || green_y < 0 ||
+ blue_x < 0 || blue_y < 0)
+ {
+ png_warning(png_ptr,
+ "Ignoring attempt to set negative chromaticity value");
+ ret = 0;
+ }
+ if (white_x > (png_fixed_point)PNG_UINT_31_MAX ||
+ white_y > (png_fixed_point)PNG_UINT_31_MAX ||
+ red_x > (png_fixed_point)PNG_UINT_31_MAX ||
+ red_y > (png_fixed_point)PNG_UINT_31_MAX ||
+ green_x > (png_fixed_point)PNG_UINT_31_MAX ||
+ green_y > (png_fixed_point)PNG_UINT_31_MAX ||
+ blue_x > (png_fixed_point)PNG_UINT_31_MAX ||
+ blue_y > (png_fixed_point)PNG_UINT_31_MAX )
+ {
+ png_warning(png_ptr,
+ "Ignoring attempt to set chromaticity value exceeding 21474.83");
+ ret = 0;
+ }
+ if (white_x > 100000L - white_y)
+ {
+ png_warning(png_ptr, "Invalid cHRM white point");
+ ret = 0;
+ }
+
+ if (red_x > 100000L - red_y)
+ {
+ png_warning(png_ptr, "Invalid cHRM red point");
+ ret = 0;
+ }
+
+ if (green_x > 100000L - green_y)
+ {
+ png_warning(png_ptr, "Invalid cHRM green point");
+ ret = 0;
+ }
+
+ if (blue_x > 100000L - blue_y)
+ {
+ png_warning(png_ptr, "Invalid cHRM blue point");
+ ret = 0;
+ }
+
+ png_64bit_product(green_x - red_x, blue_y - red_y, &xy_hi, &xy_lo);
+ png_64bit_product(green_y - red_y, blue_x - red_x, &yx_hi, &yx_lo);
+
+ if (xy_hi == yx_hi && xy_lo == yx_lo)
+ {
+ png_warning(png_ptr,
+ "Ignoring attempt to set cHRM RGB triangle with zero area");
+ ret = 0;
+ }
+
+ return ret;
+}
+# endif /* PNG_CHECK_cHRM_SUPPORTED */
+
+void /* PRIVATE */
+png_check_IHDR(png_structp png_ptr,
+ png_uint_32 width, png_uint_32 height, int bit_depth,
+ int color_type, int interlace_type, int compression_type,
+ int filter_type)
+{
+ int error = 0;
+
+ /* Check for width and height valid values */
+ if (width == 0)
+ {
+ png_warning(png_ptr, "Image width is zero in IHDR");
+ error = 1;
+ }
+
+ if (height == 0)
+ {
+ png_warning(png_ptr, "Image height is zero in IHDR");
+ error = 1;
+ }
+
+# ifdef PNG_SET_USER_LIMITS_SUPPORTED
+ if (width > png_ptr->user_width_max || width > PNG_USER_WIDTH_MAX)
+
+# else
+ if (width > PNG_USER_WIDTH_MAX)
+# endif
+ {
+ png_warning(png_ptr, "Image width exceeds user limit in IHDR");
+ error = 1;
+ }
+
+# ifdef PNG_SET_USER_LIMITS_SUPPORTED
+ if (height > png_ptr->user_height_max || height > PNG_USER_HEIGHT_MAX)
+# else
+ if (height > PNG_USER_HEIGHT_MAX)
+# endif
+ {
+ png_warning(png_ptr, "Image height exceeds user limit in IHDR");
+ error = 1;
+ }
+
+ if (width > PNG_UINT_31_MAX)
+ {
+ png_warning(png_ptr, "Invalid image width in IHDR");
+ error = 1;
+ }
+
+ if (height > PNG_UINT_31_MAX)
+ {
+ png_warning(png_ptr, "Invalid image height in IHDR");
+ error = 1;
+ }
+
+ if (width > (PNG_UINT_32_MAX
+ >> 3) /* 8-byte RGBA pixels */
+ - 48 /* bigrowbuf hack */
+ - 1 /* filter byte */
+ - 7*8 /* rounding of width to multiple of 8 pixels */
+ - 8) /* extra max_pixel_depth pad */
+ png_warning(png_ptr, "Width is too large for libpng to process pixels");
+
+ /* Check other values */
+ if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 &&
+ bit_depth != 8 && bit_depth != 16)
+ {
+ png_warning(png_ptr, "Invalid bit depth in IHDR");
+ error = 1;
+ }
+
+ if (color_type < 0 || color_type == 1 ||
+ color_type == 5 || color_type > 6)
+ {
+ png_warning(png_ptr, "Invalid color type in IHDR");
+ error = 1;
+ }
+
+ if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) ||
+ ((color_type == PNG_COLOR_TYPE_RGB ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
+ color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8))
+ {
+ png_warning(png_ptr, "Invalid color type/bit depth combination in IHDR");
+ error = 1;
+ }
+
+ if (interlace_type >= PNG_INTERLACE_LAST)
+ {
+ png_warning(png_ptr, "Unknown interlace method in IHDR");
+ error = 1;
+ }
+
+ if (compression_type != PNG_COMPRESSION_TYPE_BASE)
+ {
+ png_warning(png_ptr, "Unknown compression method in IHDR");
+ error = 1;
+ }
+
+# ifdef PNG_MNG_FEATURES_SUPPORTED
+ /* Accept filter_method 64 (intrapixel differencing) only if
+ * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
+ * 2. Libpng did not read a PNG signature (this filter_method is only
+ * used in PNG datastreams that are embedded in MNG datastreams) and
+ * 3. The application called png_permit_mng_features with a mask that
+ * included PNG_FLAG_MNG_FILTER_64 and
+ * 4. The filter_method is 64 and
+ * 5. The color_type is RGB or RGBA
+ */
+ if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) &&
+ png_ptr->mng_features_permitted)
+ png_warning(png_ptr, "MNG features are not allowed in a PNG datastream");
+
+ if (filter_type != PNG_FILTER_TYPE_BASE)
+ {
+ if (!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
+ (filter_type == PNG_INTRAPIXEL_DIFFERENCING) &&
+ ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) &&
+ (color_type == PNG_COLOR_TYPE_RGB ||
+ color_type == PNG_COLOR_TYPE_RGB_ALPHA)))
+ {
+ png_warning(png_ptr, "Unknown filter method in IHDR");
+ error = 1;
+ }
+
+ if (png_ptr->mode & PNG_HAVE_PNG_SIGNATURE)
+ {
+ png_warning(png_ptr, "Invalid filter method in IHDR");
+ error = 1;
+ }
+ }
+
+# else
+ if (filter_type != PNG_FILTER_TYPE_BASE)
+ {
+ png_warning(png_ptr, "Unknown filter method in IHDR");
+ error = 1;
+ }
+# endif
+
+ if (error == 1)
+ png_error(png_ptr, "Invalid IHDR data");
+}
+
+#if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED)
+/* ASCII to fp functions */
+/* Check an ASCII formated floating point value, see the more detailed
+ * comments in pngpriv.h
+ */
+/* The following is used internally to preserve the 'valid' flag */
+#define png_fp_add(state, flags) ((state) |= (flags))
+#define png_fp_set(state, value)\
+ ((state) = (value) | ((state) & PNG_FP_WAS_VALID))
+
+/* Internal type codes: bits above the base state! */
+#define PNG_FP_SIGN 0 /* [+-] */
+#define PNG_FP_DOT 4 /* . */
+#define PNG_FP_DIGIT 8 /* [0123456789] */
+#define PNG_FP_E 12 /* [Ee] */
+
+int /* PRIVATE */
+png_check_fp_number(png_const_charp string, png_size_t size, int *statep,
+ png_size_tp whereami)
+{
+ int state = *statep;
+ png_size_t i = *whereami;
+
+ while (i < size)
+ {
+ int type;
+ /* First find the type of the next character */
+ {
+ char ch = string[i];
+
+ if (ch >= 48 && ch <= 57)
+ type = PNG_FP_DIGIT;
+
+ else switch (ch)
+ {
+ case 43: case 45: type = PNG_FP_SIGN; break;
+ case 46: type = PNG_FP_DOT; break;
+ case 69: case 101: type = PNG_FP_E; break;
+ default: goto PNG_FP_End;
+ }
+ }
+
+ /* Now deal with this type according to the current
+ * state, the type is arranged to not overlap the
+ * bits of the PNG_FP_STATE.
+ */
+ switch ((state & PNG_FP_STATE) + type)
+ {
+ case PNG_FP_INTEGER + PNG_FP_SIGN:
+ if (state & PNG_FP_SAW_ANY)
+ goto PNG_FP_End; /* not a part of the number */
+
+ png_fp_add(state, PNG_FP_SAW_SIGN);
+ break;
+
+ case PNG_FP_INTEGER + PNG_FP_DOT:
+ /* Ok as trailer, ok as lead of fraction. */
+ if (state & PNG_FP_SAW_DOT) /* two dots */
+ goto PNG_FP_End;
+
+ else if (state & PNG_FP_SAW_DIGIT) /* trailing dot? */
+ png_fp_add(state, PNG_FP_SAW_DOT);
+
+ else
+ png_fp_set(state, PNG_FP_FRACTION | PNG_FP_SAW_DOT);
+
+ break;
+
+ case PNG_FP_INTEGER + PNG_FP_DIGIT:
+ if (state & PNG_FP_SAW_DOT) /* delayed fraction */
+ png_fp_set(state, PNG_FP_FRACTION | PNG_FP_SAW_DOT);
+
+ png_fp_add(state, PNG_FP_SAW_DIGIT + PNG_FP_WAS_VALID);
+
+ break;
+ case PNG_FP_INTEGER + PNG_FP_E:
+ if ((state & PNG_FP_SAW_DIGIT) == 0)
+ goto PNG_FP_End;
+
+ png_fp_set(state, PNG_FP_EXPONENT);
+
+ break;
+
+ /* case PNG_FP_FRACTION + PNG_FP_SIGN:
+ goto PNG_FP_End; ** no sign in exponent */
+
+ /* case PNG_FP_FRACTION + PNG_FP_DOT:
+ goto PNG_FP_End; ** Because SAW_DOT is always set */
+
+ case PNG_FP_FRACTION + PNG_FP_DIGIT:
+ png_fp_add(state, PNG_FP_SAW_DIGIT + PNG_FP_WAS_VALID);
+ break;
+
+ case PNG_FP_FRACTION + PNG_FP_E:
+ /* This is correct because the trailing '.' on an
+ * integer is handled above - so we can only get here
+ * with the sequence ".E" (with no preceding digits).
+ */
+ if ((state & PNG_FP_SAW_DIGIT) == 0)
+ goto PNG_FP_End;
+
+ png_fp_set(state, PNG_FP_EXPONENT);
+
+ break;
+
+ case PNG_FP_EXPONENT + PNG_FP_SIGN:
+ if (state & PNG_FP_SAW_ANY)
+ goto PNG_FP_End; /* not a part of the number */
+
+ png_fp_add(state, PNG_FP_SAW_SIGN);
+
+ break;
+
+ /* case PNG_FP_EXPONENT + PNG_FP_DOT:
+ goto PNG_FP_End; */
+
+ case PNG_FP_EXPONENT + PNG_FP_DIGIT:
+ png_fp_add(state, PNG_FP_SAW_DIGIT + PNG_FP_WAS_VALID);
+
+ break;
+
+ /* case PNG_FP_EXPONEXT + PNG_FP_E:
+ goto PNG_FP_End; */
+
+ default: goto PNG_FP_End; /* I.e. break 2 */
+ }
+
+ /* The character seems ok, continue. */
+ ++i;
+ }
+
+PNG_FP_End:
+ /* Here at the end, update the state and return the correct
+ * return code.
+ */
+ *statep = state;
+ *whereami = i;
+
+ return (state & PNG_FP_SAW_DIGIT) != 0;
+}
+
+
+/* The same but for a complete string. */
+int
+png_check_fp_string(png_const_charp string, png_size_t size)
+{
+ int state=0;
+ png_size_t char_index=0;
+
+ return png_check_fp_number(string, size, &state, &char_index) &&
+ (char_index == size || string[char_index] == 0);
+}
+#endif /* pCAL or sCAL */
+
+#ifdef PNG_READ_sCAL_SUPPORTED
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+/* Utility used below - a simple accurate power of ten from an integral
+ * exponent.
+ */
+static double
+png_pow10(int power)
+{
+ int recip = 0;
+ double d = 1;
+
+ /* Handle negative exponent with a reciprocal at the end because
+ * 10 is exact whereas .1 is inexact in base 2
+ */
+ if (power < 0)
+ {
+ if (power < DBL_MIN_10_EXP) return 0;
+ recip = 1, power = -power;
+ }
+
+ if (power > 0)
+ {
+ /* Decompose power bitwise. */
+ double mult = 10;
+ do
+ {
+ if (power & 1) d *= mult;
+ mult *= mult;
+ power >>= 1;
+ }
+ while (power > 0);
+
+ if (recip) d = 1/d;
+ }
+ /* else power is 0 and d is 1 */
+
+ return d;
+}
+
+/* Function to format a floating point value in ASCII with a given
+ * precision.
+ */
+void /* PRIVATE */
+png_ascii_from_fp(png_structp png_ptr, png_charp ascii, png_size_t size,
+ double fp, unsigned int precision)
+{
+ /* We use standard functions from math.h, but not printf because
+ * that would require stdio. The caller must supply a buffer of
+ * sufficient size or we will png_error. The tests on size and
+ * the space in ascii[] consumed are indicated below.
+ */
+ if (precision < 1)
+ precision = DBL_DIG;
+
+ /* Enforce the limit of the implementation precision too. */
+ if (precision > DBL_DIG+1)
+ precision = DBL_DIG+1;
+
+ /* Basic sanity checks */
+ if (size >= precision+5) /* See the requirements below. */
+ {
+ if (fp < 0)
+ {
+ fp = -fp;
+ *ascii++ = 45; /* '-' PLUS 1 TOTAL 1*/
+ --size;
+ }
+
+ if (fp >= DBL_MIN && fp <= DBL_MAX)
+ {
+ int exp_b10; /* A base 10 exponent */
+ double base; /* 10^exp_b10 */
+
+ /* First extract a base 10 exponent of the number,
+ * the calculation below rounds down when converting
+ * from base 2 to base 10 (multiply by log10(2) -
+ * 0.3010, but 77/256 is 0.3008, so exp_b10 needs to
+ * be increased. Note that the arithmetic shift
+ * performs a floor() unlike C arithmetic - using a
+ * C multiply would break the following for negative
+ * exponents.
+ */
+ (void)frexp(fp, &exp_b10); /* exponent to base 2 */
+
+ exp_b10 = (exp_b10 * 77) >> 8; /* <= exponent to base 10 */
+
+ /* Avoid underflow here. */
+ base = png_pow10(exp_b10); /* May underflow */
+
+ while (base < DBL_MIN || base < fp)
+ {
+ /* And this may overflow. */
+ double test = png_pow10(exp_b10+1);
+
+ if (test <= DBL_MAX)
+ ++exp_b10, base = test;
+
+ else
+ break;
+ }
+
+ /* Normalize fp and correct exp_b10, after this fp is in the
+ * range [.1,1) and exp_b10 is both the exponent and the digit
+ * *before* which the decimal point should be inserted
+ * (starting with 0 for the first digit). Note that this
+ * works even if 10^exp_b10 is out of range because of the
+ * test on DBL_MAX above.
+ */
+ fp /= base;
+ while (fp >= 1) fp /= 10, ++exp_b10;
+
+ /* Because of the code above fp may, at this point, be
+ * less than .1, this is ok because the code below can
+ * handle the leading zeros this generates, so no attempt
+ * is made to correct that here.
+ */
+
+ {
+ int czero, clead, cdigits;
+ char exponent[10];
+
+ /* Allow up to two leading zeros - this will not lengthen
+ * the number compared to using E-n.
+ */
+ if (exp_b10 < 0 && exp_b10 > -3) /* PLUS 3 TOTAL 4 */
+ {
+ czero = -exp_b10; /* PLUS 2 digits: TOTAL 3 */
+ exp_b10 = 0; /* Dot added below before first output. */
+ }
+ else
+ czero = 0; /* No zeros to add */
+
+ /* Generate the digit list, stripping trailing zeros and
+ * inserting a '.' before a digit if the exponent is 0.
+ */
+ clead = czero; /* Count of leading zeros */
+ cdigits = 0; /* Count of digits in list. */
+
+ do
+ {
+ double d;
+
+ fp *= 10;
+ /* Use modf here, not floor and subtract, so that
+ * the separation is done in one step. At the end
+ * of the loop don't break the number into parts so
+ * that the final digit is rounded.
+ */
+ if (cdigits+czero-clead+1 < (int)precision)
+ fp = modf(fp, &d);
+
+ else
+ {
+ d = floor(fp + .5);
+
+ if (d > 9)
+ {
+ /* Rounding up to 10, handle that here. */
+ if (czero > 0)
+ {
+ --czero, d = 1;
+ if (cdigits == 0) --clead;
+ }
+ else
+ {
+ while (cdigits > 0 && d > 9)
+ {
+ int ch = *--ascii;
+
+ if (exp_b10 != (-1))
+ ++exp_b10;
+
+ else if (ch == 46)
+ {
+ ch = *--ascii, ++size;
+ /* Advance exp_b10 to '1', so that the
+ * decimal point happens after the
+ * previous digit.
+ */
+ exp_b10 = 1;
+ }
+
+ --cdigits;
+ d = ch - 47; /* I.e. 1+(ch-48) */
+ }
+
+ /* Did we reach the beginning? If so adjust the
+ * exponent but take into account the leading
+ * decimal point.
+ */
+ if (d > 9) /* cdigits == 0 */
+ {
+ if (exp_b10 == (-1))
+ {
+ /* Leading decimal point (plus zeros?), if
+ * we lose the decimal point here it must
+ * be reentered below.
+ */
+ int ch = *--ascii;
+
+ if (ch == 46)
+ ++size, exp_b10 = 1;
+
+ /* Else lost a leading zero, so 'exp_b10' is
+ * still ok at (-1)
+ */
+ }
+ else
+ ++exp_b10;
+
+ /* In all cases we output a '1' */
+ d = 1;
+ }
+ }
+ }
+ fp = 0; /* Guarantees termination below. */
+ }
+
+ if (d == 0)
+ {
+ ++czero;
+ if (cdigits == 0) ++clead;
+ }
+ else
+ {
+ /* Included embedded zeros in the digit count. */
+ cdigits += czero - clead;
+ clead = 0;
+
+ while (czero > 0)
+ {
+ /* exp_b10 == (-1) means we just output the decimal
+ * place - after the DP don't adjust 'exp_b10' any
+ * more!
+ */
+ if (exp_b10 != (-1))
+ {
+ if (exp_b10 == 0) *ascii++ = 46, --size;
+ /* PLUS 1: TOTAL 4 */
+ --exp_b10;
+ }
+ *ascii++ = 48, --czero;
+ }
+
+ if (exp_b10 != (-1))
+ {
+ if (exp_b10 == 0) *ascii++ = 46, --size; /* counted
+ above */
+ --exp_b10;
+ }
+ *ascii++ = (char)(48 + (int)d), ++cdigits;
+ }
+ }
+ while (cdigits+czero-clead < (int)precision && fp > DBL_MIN);
+
+ /* The total output count (max) is now 4+precision */
+
+ /* Check for an exponent, if we don't need one we are
+ * done and just need to terminate the string. At
+ * this point exp_b10==(-1) is effectively if flag - it got
+ * to '-1' because of the decrement after outputing
+ * the decimal point above (the exponent required is
+ * *not* -1!)
+ */
+ if (exp_b10 >= (-1) && exp_b10 <= 2)
+ {
+ /* The following only happens if we didn't output the
+ * leading zeros above for negative exponent, so this
+ * doest add to the digit requirement. Note that the
+ * two zeros here can only be output if the two leading
+ * zeros were *not* output, so this doesn't increase
+ * the output count.
+ */
+ while (--exp_b10 >= 0) *ascii++ = 48;
+
+ *ascii = 0;
+
+ /* Total buffer requirement (including the '\0') is
+ * 5+precision - see check at the start.
+ */
+ return;
+ }
+
+ /* Here if an exponent is required, adjust size for
+ * the digits we output but did not count. The total
+ * digit output here so far is at most 1+precision - no
+ * decimal point and no leading or trailing zeros have
+ * been output.
+ */
+ size -= cdigits;
+
+ *ascii++ = 69, --size; /* 'E': PLUS 1 TOTAL 2+precision*/
+ if (exp_b10 < 0)
+ {
+ *ascii++ = 45, --size; /* '-': PLUS 1 TOTAL 3+precision */
+ exp_b10 = -exp_b10;
+ }
+
+ cdigits = 0;
+
+ while (exp_b10 > 0)
+ {
+ exponent[cdigits++] = (char)(48 + exp_b10 % 10);
+ exp_b10 /= 10;
+ }
+
+ /* Need another size check here for the exponent digits, so
+ * this need not be considered above.
+ */
+ if ((int)size > cdigits)
+ {
+ while (cdigits > 0) *ascii++ = exponent[--cdigits];
+
+ *ascii = 0;
+
+ return;
+ }
+ }
+ }
+ else if (!(fp >= DBL_MIN))
+ {
+ *ascii++ = 48; /* '0' */
+ *ascii = 0;
+ return;
+ }
+ else
+ {
+ *ascii++ = 105; /* 'i' */
+ *ascii++ = 110; /* 'n' */
+ *ascii++ = 102; /* 'f' */
+ *ascii = 0;
+ return;
+ }
+ }
+
+ /* Here on buffer too small. */
+ png_error(png_ptr, "ASCII conversion buffer too small");
+}
+
+# endif /* FLOATING_POINT */
+
+# ifdef PNG_FIXED_POINT_SUPPORTED
+/* Function to format a fixed point value in ASCII.
+ */
+void /* PRIVATE */
+png_ascii_from_fixed(png_structp png_ptr, png_charp ascii, png_size_t size,
+ png_fixed_point fp)
+{
+ /* Require space for 10 decimal digits, a decimal point, a minus sign and a
+ * trailing \0, 13 characters:
+ */
+ if (size > 12)
+ {
+ png_uint_32 num;
+
+ /* Avoid overflow here on the minimum integer. */
+ if (fp < 0)
+ *ascii++ = 45, --size, num = -fp;
+ else
+ num = fp;
+
+ if (num <= 0x80000000U) /* else overflowed */
+ {
+ unsigned int ndigits = 0, first = 16/*flag value*/;
+ char digits[10];
+
+ while (num)
+ {
+ /* Split the low digit off num: */
+ unsigned int tmp = num/10;
+ num -= tmp*10;
+ digits[ndigits++] = (char)(48 + num);
+ /* Record the first non-zero digit, note that this is a number
+ * starting at 1, it's not actually the array index.
+ */
+ if (first == 16 && num > 0)
+ first = ndigits;
+ num = tmp;
+ }
+
+ if (ndigits > 0)
+ {
+ while (ndigits > 5) *ascii++ = digits[--ndigits];
+ /* The remaining digits are fractional digits, ndigits is '5' or
+ * smaller at this point. It is certainly not zero. Check for a
+ * non-zero fractional digit:
+ */
+ if (first <= 5)
+ {
+ unsigned int i;
+ *ascii++ = 46; /* decimal point */
+ /* ndigits may be <5 for small numbers, output leading zeros
+ * then ndigits digits to first:
+ */
+ i = 5;
+ while (ndigits < i) *ascii++ = 48, --i;
+ while (ndigits >= first) *ascii++ = digits[--ndigits];
+ /* Don't output the trailing zeros! */
+ }
+ }
+ else
+ *ascii++ = 48;
+
+ /* And null terminate the string: */
+ *ascii = 0;
+ return;
+ }
+ }
+
+ /* Here on buffer too small. */
+ png_error(png_ptr, "ASCII conversion buffer too small");
+}
+# endif /* FIXED_POINT */
+#endif /* READ_SCAL */
+
+#if defined(PNG_FLOATING_POINT_SUPPORTED) && \
+ !defined(PNG_FIXED_POINT_MACRO_SUPPORTED)
+png_fixed_point
+png_fixed(png_structp png_ptr, double fp, png_const_charp text)
+{
+ double r = floor(100000 * fp + .5);
+
+ if (r > 2147483647. || r < -2147483648.)
+ png_fixed_error(png_ptr, text);
+
+ return (png_fixed_point)r;
+}
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || \
+ defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG__READ_pHYs_SUPPORTED)
+/* muldiv functions */
+/* This API takes signed arguments and rounds the result to the nearest
+ * integer (or, for a fixed point number - the standard argument - to
+ * the nearest .00001). Overflow and divide by zero are signalled in
+ * the result, a boolean - true on success, false on overflow.
+ */
+int
+png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times,
+ png_int_32 divisor)
+{
+ /* Return a * times / divisor, rounded. */
+ if (divisor != 0)
+ {
+ if (a == 0 || times == 0)
+ {
+ *res = 0;
+ return 1;
+ }
+ else
+ {
+#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+ double r = a;
+ r *= times;
+ r /= divisor;
+ r = floor(r+.5);
+
+ /* A png_fixed_point is a 32 bit integer. */
+ if (r <= 2147483647. && r >= -2147483648.)
+ {
+ *res = (png_fixed_point)r;
+ return 1;
+ }
+#else
+ int negative = 0;
+ png_uint_32 A, T, D;
+ png_uint_32 s16, s32, s00;
+
+ if (a < 0)
+ negative = 1, A = -a;
+ else
+ A = a;
+
+ if (times < 0)
+ negative = !negative, T = -times;
+ else
+ T = times;
+
+ if (divisor < 0)
+ negative = !negative, D = -divisor;
+ else
+ D = divisor;
+
+ /* Following can't overflow because the arguments only
+ * have 31 bits each, however the result may be 32 bits.
+ */
+ s16 = (A >> 16) * (T & 0xffff) +
+ (A & 0xffff) * (T >> 16);
+ /* Can't overflow because the a*times bit is only 30
+ * bits at most.
+ */
+ s32 = (A >> 16) * (T >> 16) + (s16 >> 16);
+ s00 = (A & 0xffff) * (T & 0xffff);
+
+ s16 = (s16 & 0xffff) << 16;
+ s00 += s16;
+
+ if (s00 < s16)
+ ++s32; /* carry */
+
+ if (s32 < D) /* else overflow */
+ {
+ /* s32.s00 is now the 64 bit product, do a standard
+ * division, we know that s32 < D, so the maximum
+ * required shift is 31.
+ */
+ int bitshift = 32;
+ png_fixed_point result = 0; /* NOTE: signed */
+
+ while (--bitshift >= 0)
+ {
+ png_uint_32 d32, d00;
+
+ if (bitshift > 0)
+ d32 = D >> (32-bitshift), d00 = D << bitshift;
+
+ else
+ d32 = 0, d00 = D;
+
+ if (s32 > d32)
+ {
+ if (s00 < d00) --s32; /* carry */
+ s32 -= d32, s00 -= d00, result += 1<<bitshift;
+ }
+
+ else
+ if (s32 == d32 && s00 >= d00)
+ s32 = 0, s00 -= d00, result += 1<<bitshift;
+ }
+
+ /* Handle the rounding. */
+ if (s00 >= (D >> 1))
+ ++result;
+
+ if (negative)
+ result = -result;
+
+ /* Check for overflow. */
+ if ((negative && result <= 0) || (!negative && result >= 0))
+ {
+ *res = result;
+ return 1;
+ }
+ }
+#endif
+ }
+ }
+
+ return 0;
+}
+#endif /* READ_GAMMA || INCH_CONVERSIONS */
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED)
+/* The following is for when the caller doesn't much care about the
+ * result.
+ */
+png_fixed_point
+png_muldiv_warn(png_structp png_ptr, png_fixed_point a, png_int_32 times,
+ png_int_32 divisor)
+{
+ png_fixed_point result;
+
+ if (png_muldiv(&result, a, times, divisor))
+ return result;
+
+ png_warning(png_ptr, "fixed point overflow ignored");
+ return 0;
+}
+#endif
+
+#ifdef PNG_READ_GAMMA_SUPPORTED /* more fixed point functions for gammma */
+/* Calculate a reciprocal, return 0 on div-by-zero or overflow. */
+png_fixed_point
+png_reciprocal(png_fixed_point a)
+{
+#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+ double r = floor(1E10/a+.5);
+
+ if (r <= 2147483647. && r >= -2147483648.)
+ return (png_fixed_point)r;
+#else
+ png_fixed_point res;
+
+ if (png_muldiv(&res, 100000, 100000, a))
+ return res;
+#endif
+
+ return 0; /* error/overflow */
+}
+
+/* A local convenience routine. */
+static png_fixed_point
+png_product2(png_fixed_point a, png_fixed_point b)
+{
+ /* The required result is 1/a * 1/b; the following preserves accuracy. */
+#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+ double r = a * 1E-5;
+ r *= b;
+ r = floor(r+.5);
+
+ if (r <= 2147483647. && r >= -2147483648.)
+ return (png_fixed_point)r;
+#else
+ png_fixed_point res;
+
+ if (png_muldiv(&res, a, b, 100000))
+ return res;
+#endif
+
+ return 0; /* overflow */
+}
+
+/* The inverse of the above. */
+png_fixed_point
+png_reciprocal2(png_fixed_point a, png_fixed_point b)
+{
+ /* The required result is 1/a * 1/b; the following preserves accuracy. */
+#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+ double r = 1E15/a;
+ r /= b;
+ r = floor(r+.5);
+
+ if (r <= 2147483647. && r >= -2147483648.)
+ return (png_fixed_point)r;
+#else
+ /* This may overflow because the range of png_fixed_point isn't symmetric,
+ * but this API is only used for the product of file and screen gamma so it
+ * doesn't matter that the smallest number it can produce is 1/21474, not
+ * 1/100000
+ */
+ png_fixed_point res = png_product2(a, b);
+
+ if (res != 0)
+ return png_reciprocal(res);
+#endif
+
+ return 0; /* overflow */
+}
+#endif /* READ_GAMMA */
+
+#ifdef PNG_CHECK_cHRM_SUPPORTED
+/* Added at libpng version 1.2.34 (Dec 8, 2008) and 1.4.0 (Jan 2,
+ * 2010: moved from pngset.c) */
+/*
+ * Multiply two 32-bit numbers, V1 and V2, using 32-bit
+ * arithmetic, to produce a 64 bit result in the HI/LO words.
+ *
+ * A B
+ * x C D
+ * ------
+ * AD || BD
+ * AC || CB || 0
+ *
+ * where A and B are the high and low 16-bit words of V1,
+ * C and D are the 16-bit words of V2, AD is the product of
+ * A and D, and X || Y is (X << 16) + Y.
+*/
+
+void /* PRIVATE */
+png_64bit_product (long v1, long v2, unsigned long *hi_product,
+ unsigned long *lo_product)
+{
+ int a, b, c, d;
+ long lo, hi, x, y;
+
+ a = (v1 >> 16) & 0xffff;
+ b = v1 & 0xffff;
+ c = (v2 >> 16) & 0xffff;
+ d = v2 & 0xffff;
+
+ lo = b * d; /* BD */
+ x = a * d + c * b; /* AD + CB */
+ y = ((lo >> 16) & 0xffff) + x;
+
+ lo = (lo & 0xffff) | ((y & 0xffff) << 16);
+ hi = (y >> 16) & 0xffff;
+
+ hi += a * c; /* AC */
+
+ *hi_product = (unsigned long)hi;
+ *lo_product = (unsigned long)lo;
+}
+#endif /* CHECK_cHRM */
+
+#ifdef PNG_READ_GAMMA_SUPPORTED /* gamma table code */
+#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED
+/* Fixed point gamma.
+ *
+ * To calculate gamma this code implements fast log() and exp() calls using only
+ * fixed point arithmetic. This code has sufficient precision for either 8 or
+ * 16 bit sample values.
+ *
+ * The tables used here were calculated using simple 'bc' programs, but C double
+ * precision floating point arithmetic would work fine. The programs are given
+ * at the head of each table.
+ *
+ * 8 bit log table
+ * This is a table of -log(value/255)/log(2) for 'value' in the range 128 to
+ * 255, so it's the base 2 logarithm of a normalized 8 bit floating point
+ * mantissa. The numbers are 32 bit fractions.
+ */
+static png_uint_32
+png_8bit_l2[128] =
+{
+# if PNG_DO_BC
+ for (i=128;i<256;++i) { .5 - l(i/255)/l(2)*65536*65536; }
+# endif
+ 4270715492U, 4222494797U, 4174646467U, 4127164793U, 4080044201U, 4033279239U,
+ 3986864580U, 3940795015U, 3895065449U, 3849670902U, 3804606499U, 3759867474U,
+ 3715449162U, 3671346997U, 3627556511U, 3584073329U, 3540893168U, 3498011834U,
+ 3455425220U, 3413129301U, 3371120137U, 3329393864U, 3287946700U, 3246774933U,
+ 3205874930U, 3165243125U, 3124876025U, 3084770202U, 3044922296U, 3005329011U,
+ 2965987113U, 2926893432U, 2888044853U, 2849438323U, 2811070844U, 2772939474U,
+ 2735041326U, 2697373562U, 2659933400U, 2622718104U, 2585724991U, 2548951424U,
+ 2512394810U, 2476052606U, 2439922311U, 2404001468U, 2368287663U, 2332778523U,
+ 2297471715U, 2262364947U, 2227455964U, 2192742551U, 2158222529U, 2123893754U,
+ 2089754119U, 2055801552U, 2022034013U, 1988449497U, 1955046031U, 1921821672U,
+ 1888774511U, 1855902668U, 1823204291U, 1790677560U, 1758320682U, 1726131893U,
+ 1694109454U, 1662251657U, 1630556815U, 1599023271U, 1567649391U, 1536433567U,
+ 1505374214U, 1474469770U, 1443718700U, 1413119487U, 1382670639U, 1352370686U,
+ 1322218179U, 1292211689U, 1262349810U, 1232631153U, 1203054352U, 1173618059U,
+ 1144320946U, 1115161701U, 1086139034U, 1057251672U, 1028498358U, 999877854U,
+ 971388940U, 943030410U, 914801076U, 886699767U, 858725327U, 830876614U,
+ 803152505U, 775551890U, 748073672U, 720716771U, 693480120U, 666362667U,
+ 639363374U, 612481215U, 585715177U, 559064263U, 532527486U, 506103872U,
+ 479792461U, 453592303U, 427502463U, 401522014U, 375650043U, 349885648U,
+ 324227938U, 298676034U, 273229066U, 247886176U, 222646516U, 197509248U,
+ 172473545U, 147538590U, 122703574U, 97967701U, 73330182U, 48790236U,
+ 24347096U, 0U
+#if 0
+ /* The following are the values for 16 bit tables - these work fine for the 8
+ * bit conversions but produce very slightly larger errors in the 16 bit log
+ * (about 1.2 as opposed to 0.7 absolute error in the final value). To use
+ * these all the shifts below must be adjusted appropriately.
+ */
+ 65166, 64430, 63700, 62976, 62257, 61543, 60835, 60132, 59434, 58741, 58054,
+ 57371, 56693, 56020, 55352, 54689, 54030, 53375, 52726, 52080, 51439, 50803,
+ 50170, 49542, 48918, 48298, 47682, 47070, 46462, 45858, 45257, 44661, 44068,
+ 43479, 42894, 42312, 41733, 41159, 40587, 40020, 39455, 38894, 38336, 37782,
+ 37230, 36682, 36137, 35595, 35057, 34521, 33988, 33459, 32932, 32408, 31887,
+ 31369, 30854, 30341, 29832, 29325, 28820, 28319, 27820, 27324, 26830, 26339,
+ 25850, 25364, 24880, 24399, 23920, 23444, 22970, 22499, 22029, 21562, 21098,
+ 20636, 20175, 19718, 19262, 18808, 18357, 17908, 17461, 17016, 16573, 16132,
+ 15694, 15257, 14822, 14390, 13959, 13530, 13103, 12678, 12255, 11834, 11415,
+ 10997, 10582, 10168, 9756, 9346, 8937, 8531, 8126, 7723, 7321, 6921, 6523,
+ 6127, 5732, 5339, 4947, 4557, 4169, 3782, 3397, 3014, 2632, 2251, 1872, 1495,
+ 1119, 744, 372
+#endif
+};
+
+static png_int_32
+png_log8bit(unsigned int x)
+{
+ unsigned int lg2 = 0;
+ /* Each time 'x' is multiplied by 2, 1 must be subtracted off the final log,
+ * because the log is actually negate that means adding 1. The final
+ * returned value thus has the range 0 (for 255 input) to 7.994 (for 1
+ * input), return 7.99998 for the overflow (log 0) case - so the result is
+ * always at most 19 bits.
+ */
+ if ((x &= 0xff) == 0)
+ return 0xffffffff;
+
+ if ((x & 0xf0) == 0)
+ lg2 = 4, x <<= 4;
+
+ if ((x & 0xc0) == 0)
+ lg2 += 2, x <<= 2;
+
+ if ((x & 0x80) == 0)
+ lg2 += 1, x <<= 1;
+
+ /* result is at most 19 bits, so this cast is safe: */
+ return (png_int_32)((lg2 << 16) + ((png_8bit_l2[x-128]+32768)>>16));
+}
+
+/* The above gives exact (to 16 binary places) log2 values for 8 bit images,
+ * for 16 bit images we use the most significant 8 bits of the 16 bit value to
+ * get an approximation then multiply the approximation by a correction factor
+ * determined by the remaining up to 8 bits. This requires an additional step
+ * in the 16 bit case.
+ *
+ * We want log2(value/65535), we have log2(v'/255), where:
+ *
+ * value = v' * 256 + v''
+ * = v' * f
+ *
+ * So f is value/v', which is equal to (256+v''/v') since v' is in the range 128
+ * to 255 and v'' is in the range 0 to 255 f will be in the range 256 to less
+ * than 258. The final factor also needs to correct for the fact that our 8 bit
+ * value is scaled by 255, whereas the 16 bit values must be scaled by 65535.
+ *
+ * This gives a final formula using a calculated value 'x' which is value/v' and
+ * scaling by 65536 to match the above table:
+ *
+ * log2(x/257) * 65536
+ *
+ * Since these numbers are so close to '1' we can use simple linear
+ * interpolation between the two end values 256/257 (result -368.61) and 258/257
+ * (result 367.179). The values used below are scaled by a further 64 to give
+ * 16 bit precision in the interpolation:
+ *
+ * Start (256): -23591
+ * Zero (257): 0
+ * End (258): 23499
+ */
+static png_int_32
+png_log16bit(png_uint_32 x)
+{
+ unsigned int lg2 = 0;
+
+ /* As above, but now the input has 16 bits. */
+ if ((x &= 0xffff) == 0)
+ return 0xffffffff;
+
+ if ((x & 0xff00) == 0)
+ lg2 = 8, x <<= 8;
+
+ if ((x & 0xf000) == 0)
+ lg2 += 4, x <<= 4;
+
+ if ((x & 0xc000) == 0)
+ lg2 += 2, x <<= 2;
+
+ if ((x & 0x8000) == 0)
+ lg2 += 1, x <<= 1;
+
+ /* Calculate the base logarithm from the top 8 bits as a 28 bit fractional
+ * value.
+ */
+ lg2 <<= 28;
+ lg2 += (png_8bit_l2[(x>>8)-128]+8) >> 4;
+
+ /* Now we need to interpolate the factor, this requires a division by the top
+ * 8 bits. Do this with maximum precision.
+ */
+ x = ((x << 16) + (x >> 9)) / (x >> 8);
+
+ /* Since we divided by the top 8 bits of 'x' there will be a '1' at 1<<24,
+ * the value at 1<<16 (ignoring this) will be 0 or 1; this gives us exactly
+ * 16 bits to interpolate to get the low bits of the result. Round the
+ * answer. Note that the end point values are scaled by 64 to retain overall
+ * precision and that 'lg2' is current scaled by an extra 12 bits, so adjust
+ * the overall scaling by 6-12. Round at every step.
+ */
+ x -= 1U << 24;
+
+ if (x <= 65536U) /* <= '257' */
+ lg2 += ((23591U * (65536U-x)) + (1U << (16+6-12-1))) >> (16+6-12);
+
+ else
+ lg2 -= ((23499U * (x-65536U)) + (1U << (16+6-12-1))) >> (16+6-12);
+
+ /* Safe, because the result can't have more than 20 bits: */
+ return (png_int_32)((lg2 + 2048) >> 12);
+}
+
+/* The 'exp()' case must invert the above, taking a 20 bit fixed point
+ * logarithmic value and returning a 16 or 8 bit number as appropriate. In
+ * each case only the low 16 bits are relevant - the fraction - since the
+ * integer bits (the top 4) simply determine a shift.
+ *
+ * The worst case is the 16 bit distinction between 65535 and 65534, this
+ * requires perhaps spurious accuracty in the decoding of the logarithm to
+ * distinguish log2(65535/65534.5) - 10^-5 or 17 bits. There is little chance
+ * of getting this accuracy in practice.
+ *
+ * To deal with this the following exp() function works out the exponent of the
+ * frational part of the logarithm by using an accurate 32 bit value from the
+ * top four fractional bits then multiplying in the remaining bits.
+ */
+static png_uint_32
+png_32bit_exp[16] =
+{
+# if PNG_DO_BC
+ for (i=0;i<16;++i) { .5 + e(-i/16*l(2))*2^32; }
+# endif
+ /* NOTE: the first entry is deliberately set to the maximum 32 bit value. */
+ 4294967295U, 4112874773U, 3938502376U, 3771522796U, 3611622603U, 3458501653U,
+ 3311872529U, 3171459999U, 3037000500U, 2908241642U, 2784941738U, 2666869345U,
+ 2553802834U, 2445529972U, 2341847524U, 2242560872U
+};
+
+/* Adjustment table; provided to explain the numbers in the code below. */
+#if PNG_DO_BC
+for (i=11;i>=0;--i){ print i, " ", (1 - e(-(2^i)/65536*l(2))) * 2^(32-i), "\n"}
+ 11 44937.64284865548751208448
+ 10 45180.98734845585101160448
+ 9 45303.31936980687359311872
+ 8 45364.65110595323018870784
+ 7 45395.35850361789624614912
+ 6 45410.72259715102037508096
+ 5 45418.40724413220722311168
+ 4 45422.25021786898173001728
+ 3 45424.17186732298419044352
+ 2 45425.13273269940811464704
+ 1 45425.61317555035558641664
+ 0 45425.85339951654943850496
+#endif
+
+static png_uint_32
+png_exp(png_fixed_point x)
+{
+ if (x > 0 && x <= 0xfffff) /* Else overflow or zero (underflow) */
+ {
+ /* Obtain a 4 bit approximation */
+ png_uint_32 e = png_32bit_exp[(x >> 12) & 0xf];
+
+ /* Incorporate the low 12 bits - these decrease the returned value by
+ * multiplying by a number less than 1 if the bit is set. The multiplier
+ * is determined by the above table and the shift. Notice that the values
+ * converge on 45426 and this is used to allow linear interpolation of the
+ * low bits.
+ */
+ if (x & 0x800)
+ e -= (((e >> 16) * 44938U) + 16U) >> 5;
+
+ if (x & 0x400)
+ e -= (((e >> 16) * 45181U) + 32U) >> 6;
+
+ if (x & 0x200)
+ e -= (((e >> 16) * 45303U) + 64U) >> 7;
+
+ if (x & 0x100)
+ e -= (((e >> 16) * 45365U) + 128U) >> 8;
+
+ if (x & 0x080)
+ e -= (((e >> 16) * 45395U) + 256U) >> 9;
+
+ if (x & 0x040)
+ e -= (((e >> 16) * 45410U) + 512U) >> 10;
+
+ /* And handle the low 6 bits in a single block. */
+ e -= (((e >> 16) * 355U * (x & 0x3fU)) + 256U) >> 9;
+
+ /* Handle the upper bits of x. */
+ e >>= x >> 16;
+ return e;
+ }
+
+ /* Check for overflow */
+ if (x <= 0)
+ return png_32bit_exp[0];
+
+ /* Else underflow */
+ return 0;
+}
+
+static png_byte
+png_exp8bit(png_fixed_point lg2)
+{
+ /* Get a 32 bit value: */
+ png_uint_32 x = png_exp(lg2);
+
+ /* Convert the 32 bit value to 0..255 by multiplying by 256-1, note that the
+ * second, rounding, step can't overflow because of the first, subtraction,
+ * step.
+ */
+ x -= x >> 8;
+ return (png_byte)((x + 0x7fffffU) >> 24);
+}
+
+static png_uint_16
+png_exp16bit(png_fixed_point lg2)
+{
+ /* Get a 32 bit value: */
+ png_uint_32 x = png_exp(lg2);
+
+ /* Convert the 32 bit value to 0..65535 by multiplying by 65536-1: */
+ x -= x >> 16;
+ return (png_uint_16)((x + 32767U) >> 16);
+}
+#endif /* FLOATING_ARITHMETIC */
+
+png_byte
+png_gamma_8bit_correct(unsigned int value, png_fixed_point gamma_val)
+{
+ if (value > 0 && value < 255)
+ {
+# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+ double r = floor(255*pow(value/255.,gamma_val*.00001)+.5);
+ return (png_byte)r;
+# else
+ png_int_32 lg2 = png_log8bit(value);
+ png_fixed_point res;
+
+ if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1))
+ return png_exp8bit(res);
+
+ /* Overflow. */
+ value = 0;
+# endif
+ }
+
+ return (png_byte)value;
+}
+
+png_uint_16
+png_gamma_16bit_correct(unsigned int value, png_fixed_point gamma_val)
+{
+ if (value > 0 && value < 65535)
+ {
+# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+ double r = floor(65535*pow(value/65535.,gamma_val*.00001)+.5);
+ return (png_uint_16)r;
+# else
+ png_int_32 lg2 = png_log16bit(value);
+ png_fixed_point res;
+
+ if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1))
+ return png_exp16bit(res);
+
+ /* Overflow. */
+ value = 0;
+# endif
+ }
+
+ return (png_uint_16)value;
+}
+
+/* This does the right thing based on the bit_depth field of the
+ * png_struct, interpreting values as 8 or 16 bit. While the result
+ * is nominally a 16 bit value if bit depth is 8 then the result is
+ * 8 bit (as are the arguments.)
+ */
+png_uint_16 /* PRIVATE */
+png_gamma_correct(png_structp png_ptr, unsigned int value,
+ png_fixed_point gamma_val)
+{
+ if (png_ptr->bit_depth == 8)
+ return png_gamma_8bit_correct(value, gamma_val);
+
+ else
+ return png_gamma_16bit_correct(value, gamma_val);
+}
+
+/* This is the shared test on whether a gamma value is 'significant' - whether
+ * it is worth doing gamma correction.
+ */
+int /* PRIVATE */
+png_gamma_significant(png_fixed_point gamma_val)
+{
+ return gamma_val < PNG_FP_1 - PNG_GAMMA_THRESHOLD_FIXED ||
+ gamma_val > PNG_FP_1 + PNG_GAMMA_THRESHOLD_FIXED;
+}
+
+/* Internal function to build a single 16 bit table - the table consists of
+ * 'num' 256 entry subtables, where 'num' is determined by 'shift' - the amount
+ * to shift the input values right (or 16-number_of_signifiant_bits).
+ *
+ * The caller is responsible for ensuring that the table gets cleaned up on
+ * png_error (i.e. if one of the mallocs below fails) - i.e. the *table argument
+ * should be somewhere that will be cleaned.
+ */
+static void
+png_build_16bit_table(png_structp png_ptr, png_uint_16pp *ptable,
+ PNG_CONST unsigned int shift, PNG_CONST png_fixed_point gamma_val)
+{
+ /* Various values derived from 'shift': */
+ PNG_CONST unsigned int num = 1U << (8U - shift);
+ PNG_CONST unsigned int max = (1U << (16U - shift))-1U;
+ PNG_CONST unsigned int max_by_2 = 1U << (15U-shift);
+ unsigned int i;
+
+ png_uint_16pp table = *ptable =
+ (png_uint_16pp)png_calloc(png_ptr, num * png_sizeof(png_uint_16p));
+
+ for (i = 0; i < num; i++)
+ {
+ png_uint_16p sub_table = table[i] =
+ (png_uint_16p)png_malloc(png_ptr, 256 * png_sizeof(png_uint_16));
+
+ /* The 'threshold' test is repeated here because it can arise for one of
+ * the 16 bit tables even if the others don't hit it.
+ */
+ if (png_gamma_significant(gamma_val))
+ {
+ /* The old code would overflow at the end and this would cause the
+ * 'pow' function to return a result >1, resulting in an
+ * arithmetic error. This code follows the spec exactly; ig is
+ * the recovered input sample, it always has 8-16 bits.
+ *
+ * We want input * 65535/max, rounded, the arithmetic fits in 32
+ * bits (unsigned) so long as max <= 32767.
+ */
+ unsigned int j;
+ for (j = 0; j < 256; j++)
+ {
+ png_uint_32 ig = (j << (8-shift)) + i;
+# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+ /* Inline the 'max' scaling operation: */
+ double d = floor(65535*pow(ig/(double)max, gamma_val*.00001)+.5);
+ sub_table[j] = (png_uint_16)d;
+# else
+ if (shift)
+ ig = (ig * 65535U + max_by_2)/max;
+
+ sub_table[j] = png_gamma_16bit_correct(ig, gamma_val);
+# endif
+ }
+ }
+ else
+ {
+ /* We must still build a table, but do it the fast way. */
+ unsigned int j;
+
+ for (j = 0; j < 256; j++)
+ {
+ png_uint_32 ig = (j << (8-shift)) + i;
+
+ if (shift)
+ ig = (ig * 65535U + max_by_2)/max;
+
+ sub_table[j] = (png_uint_16)ig;
+ }
+ }
+ }
+}
+
+/* NOTE: this function expects the *inverse* of the overall gamma transformation
+ * required.
+ */
+static void
+png_build_16to8_table(png_structp png_ptr, png_uint_16pp *ptable,
+ PNG_CONST unsigned int shift, PNG_CONST png_fixed_point gamma_val)
+{
+ PNG_CONST unsigned int num = 1U << (8U - shift);
+ PNG_CONST unsigned int max = (1U << (16U - shift))-1U;
+ unsigned int i;
+ png_uint_32 last;
+
+ png_uint_16pp table = *ptable =
+ (png_uint_16pp)png_calloc(png_ptr, num * png_sizeof(png_uint_16p));
+
+ /* 'num' is the number of tables and also the number of low bits of low
+ * bits of the input 16 bit value used to select a table. Each table is
+ * itself index by the high 8 bits of the value.
+ */
+ for (i = 0; i < num; i++)
+ table[i] = (png_uint_16p)png_malloc(png_ptr,
+ 256 * png_sizeof(png_uint_16));
+
+ /* 'gamma_val' is set to the reciprocal of the value calculated above, so
+ * pow(out,g) is an *input* value. 'last' is the last input value set.
+ *
+ * In the loop 'i' is used to find output values. Since the output is 8
+ * bit there are only 256 possible values. The tables are set up to
+ * select the closest possible output value for each input by finding
+ * the input value at the boundary between each pair of output values
+ * and filling the table up to that boundary with the lower output
+ * value.
+ *
+ * The boundary values are 0.5,1.5..253.5,254.5. Since these are 9 bit
+ * values the code below uses a 16 bit value in i; the values start at
+ * 128.5 (for 0.5) and step by 257, for a total of 254 values (the last
+ * entries are filled with 255). Start i at 128 and fill all 'last'
+ * table entries <= 'max'
+ */
+ last = 0;
+ for (i = 0; i < 255; ++i) /* 8 bit output value */
+ {
+ /* Find the corresponding maximum input value */
+ png_uint_16 out = (png_uint_16)(i * 257U); /* 16 bit output value */
+
+ /* Find the boundary value in 16 bits: */
+ png_uint_32 bound = png_gamma_16bit_correct(out+128U, gamma_val);
+
+ /* Adjust (round) to (16-shift) bits: */
+ bound = (bound * max + 32768U)/65535U + 1U;
+
+ while (last < bound)
+ {
+ table[last & (0xffU >> shift)][last >> (8U - shift)] = out;
+ last++;
+ }
+ }
+
+ /* And fill in the final entries. */
+ while (last < (num << 8))
+ {
+ table[last & (0xff >> shift)][last >> (8U - shift)] = 65535U;
+ last++;
+ }
+}
+
+/* Build a single 8 bit table: same as the 16 bit case but much simpler (and
+ * typically much faster). Note that libpng currently does no sBIT processing
+ * (apparently contrary to the spec) so a 256 entry table is always generated.
+ */
+static void
+png_build_8bit_table(png_structp png_ptr, png_bytepp ptable,
+ PNG_CONST png_fixed_point gamma_val)
+{
+ unsigned int i;
+ png_bytep table = *ptable = (png_bytep)png_malloc(png_ptr, 256);
+
+ if (png_gamma_significant(gamma_val)) for (i=0; i<256; i++)
+ table[i] = png_gamma_8bit_correct(i, gamma_val);
+
+ else for (i=0; i<256; ++i)
+ table[i] = (png_byte)i;
+}
+
+/* We build the 8- or 16-bit gamma tables here. Note that for 16-bit
+ * tables, we don't make a full table if we are reducing to 8-bit in
+ * the future. Note also how the gamma_16 tables are segmented so that
+ * we don't need to allocate > 64K chunks for a full 16-bit table.
+ */
+void /* PRIVATE */
+png_build_gamma_table(png_structp png_ptr, int bit_depth)
+{
+ png_debug(1, "in png_build_gamma_table");
+
+ if (bit_depth <= 8)
+ {
+ png_build_8bit_table(png_ptr, &png_ptr->gamma_table,
+ png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->gamma,
+ png_ptr->screen_gamma) : PNG_FP_1);
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+ defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+ if (png_ptr->transformations & ((PNG_BACKGROUND) | PNG_RGB_TO_GRAY))
+ {
+ png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1,
+ png_reciprocal(png_ptr->gamma));
+
+ png_build_8bit_table(png_ptr, &png_ptr->gamma_from_1,
+ png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) :
+ png_ptr->gamma/* Probably doing rgb_to_gray */);
+ }
+#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */
+ }
+ else
+ {
+ png_byte shift, sig_bit;
+
+ if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
+ {
+ sig_bit = png_ptr->sig_bit.red;
+
+ if (png_ptr->sig_bit.green > sig_bit)
+ sig_bit = png_ptr->sig_bit.green;
+
+ if (png_ptr->sig_bit.blue > sig_bit)
+ sig_bit = png_ptr->sig_bit.blue;
+ }
+ else
+ sig_bit = png_ptr->sig_bit.gray;
+
+ /* 16 bit gamma code uses this equation:
+ *
+ * ov = table[(iv & 0xff) >> gamma_shift][iv >> 8]
+ *
+ * Where 'iv' is the input color value and 'ov' is the output value -
+ * pow(iv, gamma).
+ *
+ * Thus the gamma table consists of up to 256 256 entry tables. The table
+ * is selected by the (8-gamma_shift) most significant of the low 8 bits of
+ * the color value then indexed by the upper 8 bits:
+ *
+ * table[low bits][high 8 bits]
+ *
+ * So the table 'n' corresponds to all those 'iv' of:
+ *
+ * <all high 8 bit values><n << gamma_shift>..<(n+1 << gamma_shift)-1>
+ *
+ */
+ if (sig_bit > 0 && sig_bit < 16U)
+ shift = (png_byte)(16U - sig_bit); /* shift == insignificant bits */
+
+ else
+ shift = 0; /* keep all 16 bits */
+
+ if (png_ptr->transformations & PNG_16_TO_8)
+ {
+ /* PNG_MAX_GAMMA_8 is the number of bits to keep - effectively
+ * the significant bits in the *input* when the output will
+ * eventually be 8 bits. By default it is 11.
+ */
+ if (shift < (16U - PNG_MAX_GAMMA_8))
+ shift = (16U - PNG_MAX_GAMMA_8);
+ }
+
+ if (shift > 8U)
+ shift = 8U; /* Guarantees at least one table! */
+
+ png_ptr->gamma_shift = shift;
+
+#ifdef PNG_16BIT_SUPPORTED
+ if (png_ptr->transformations & (PNG_16_TO_8 | PNG_BACKGROUND))
+#endif
+ png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift,
+ png_ptr->screen_gamma > 0 ? png_product2(png_ptr->gamma,
+ png_ptr->screen_gamma) : PNG_FP_1);
+
+#ifdef PNG_16BIT_SUPPORTED
+ else
+ png_build_16bit_table(png_ptr, &png_ptr->gamma_16_table, shift,
+ png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->gamma,
+ png_ptr->screen_gamma) : PNG_FP_1);
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+ defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+ if (png_ptr->transformations & (PNG_BACKGROUND | PNG_RGB_TO_GRAY))
+ {
+ png_build_16bit_table(png_ptr, &png_ptr->gamma_16_to_1, shift,
+ png_reciprocal(png_ptr->gamma));
+
+ /* Notice that the '16 from 1' table should be full precision, however
+ * the lookup on this table still uses gamma_shift, so it can't be.
+ * TODO: fix this.
+ */
+ png_build_16bit_table(png_ptr, &png_ptr->gamma_16_from_1, shift,
+ png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) :
+ png_ptr->gamma/* Probably doing rgb_to_gray */);
+ }
+#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */
+ }
+}
+#endif /* READ_GAMMA */
+#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */
diff --git a/libpng/png.h b/libpng/png.h
new file mode 100644
index 0000000..0af42b1
--- /dev/null
+++ b/libpng/png.h
@@ -0,0 +1,2309 @@
+
+/* png.h - header file for PNG reference library
+ *
+ * libpng version 1.5.2 - March 31, 2011
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license (See LICENSE, below)
+ *
+ * Authors and maintainers:
+ * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat
+ * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger
+ * libpng versions 0.97, January 1998, through 1.5.2 - March 31, 2011: Glenn
+ * See also "Contributing Authors", below.
+ *
+ * Note about libpng version numbers:
+ *
+ * Due to various miscommunications, unforeseen code incompatibilities
+ * and occasional factors outside the authors' control, version numbering
+ * on the library has not always been consistent and straightforward.
+ * The following table summarizes matters since version 0.89c, which was
+ * the first widely used release:
+ *
+ * source png.h png.h shared-lib
+ * version string int version
+ * ------- ------ ----- ----------
+ * 0.89c "1.0 beta 3" 0.89 89 1.0.89
+ * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90]
+ * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95]
+ * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96]
+ * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97]
+ * 0.97c 0.97 97 2.0.97
+ * 0.98 0.98 98 2.0.98
+ * 0.99 0.99 98 2.0.99
+ * 0.99a-m 0.99 99 2.0.99
+ * 1.00 1.00 100 2.1.0 [100 should be 10000]
+ * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000]
+ * 1.0.1 png.h string is 10001 2.1.0
+ * 1.0.1a-e identical to the 10002 from here on, the shared library
+ * 1.0.2 source version) 10002 is 2.V where V is the source code
+ * 1.0.2a-b 10003 version, except as noted.
+ * 1.0.3 10003
+ * 1.0.3a-d 10004
+ * 1.0.4 10004
+ * 1.0.4a-f 10005
+ * 1.0.5 (+ 2 patches) 10005
+ * 1.0.5a-d 10006
+ * 1.0.5e-r 10100 (not source compatible)
+ * 1.0.5s-v 10006 (not binary compatible)
+ * 1.0.6 (+ 3 patches) 10006 (still binary incompatible)
+ * 1.0.6d-f 10007 (still binary incompatible)
+ * 1.0.6g 10007
+ * 1.0.6h 10007 10.6h (testing xy.z so-numbering)
+ * 1.0.6i 10007 10.6i
+ * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0)
+ * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible)
+ * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible)
+ * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible)
+ * 1.0.7 1 10007 (still compatible)
+ * 1.0.8beta1-4 1 10008 2.1.0.8beta1-4
+ * 1.0.8rc1 1 10008 2.1.0.8rc1
+ * 1.0.8 1 10008 2.1.0.8
+ * 1.0.9beta1-6 1 10009 2.1.0.9beta1-6
+ * 1.0.9rc1 1 10009 2.1.0.9rc1
+ * 1.0.9beta7-10 1 10009 2.1.0.9beta7-10
+ * 1.0.9rc2 1 10009 2.1.0.9rc2
+ * 1.0.9 1 10009 2.1.0.9
+ * 1.0.10beta1 1 10010 2.1.0.10beta1
+ * 1.0.10rc1 1 10010 2.1.0.10rc1
+ * 1.0.10 1 10010 2.1.0.10
+ * 1.0.11beta1-3 1 10011 2.1.0.11beta1-3
+ * 1.0.11rc1 1 10011 2.1.0.11rc1
+ * 1.0.11 1 10011 2.1.0.11
+ * 1.0.12beta1-2 2 10012 2.1.0.12beta1-2
+ * 1.0.12rc1 2 10012 2.1.0.12rc1
+ * 1.0.12 2 10012 2.1.0.12
+ * 1.1.0a-f - 10100 2.1.1.0a-f (branch abandoned)
+ * 1.2.0beta1-2 2 10200 2.1.2.0beta1-2
+ * 1.2.0beta3-5 3 10200 3.1.2.0beta3-5
+ * 1.2.0rc1 3 10200 3.1.2.0rc1
+ * 1.2.0 3 10200 3.1.2.0
+ * 1.2.1beta1-4 3 10201 3.1.2.1beta1-4
+ * 1.2.1rc1-2 3 10201 3.1.2.1rc1-2
+ * 1.2.1 3 10201 3.1.2.1
+ * 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6
+ * 1.0.13beta1 10 10013 10.so.0.1.0.13beta1
+ * 1.0.13rc1 10 10013 10.so.0.1.0.13rc1
+ * 1.2.2rc1 12 10202 12.so.0.1.2.2rc1
+ * 1.0.13 10 10013 10.so.0.1.0.13
+ * 1.2.2 12 10202 12.so.0.1.2.2
+ * 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6
+ * 1.2.3 12 10203 12.so.0.1.2.3
+ * 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3
+ * 1.0.14rc1 13 10014 10.so.0.1.0.14rc1
+ * 1.2.4rc1 13 10204 12.so.0.1.2.4rc1
+ * 1.0.14 10 10014 10.so.0.1.0.14
+ * 1.2.4 13 10204 12.so.0.1.2.4
+ * 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2
+ * 1.0.15rc1-3 10 10015 10.so.0.1.0.15rc1-3
+ * 1.2.5rc1-3 13 10205 12.so.0.1.2.5rc1-3
+ * 1.0.15 10 10015 10.so.0.1.0.15
+ * 1.2.5 13 10205 12.so.0.1.2.5
+ * 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4
+ * 1.0.16 10 10016 10.so.0.1.0.16
+ * 1.2.6 13 10206 12.so.0.1.2.6
+ * 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2
+ * 1.0.17rc1 10 10017 12.so.0.1.0.17rc1
+ * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1
+ * 1.0.17 10 10017 12.so.0.1.0.17
+ * 1.2.7 13 10207 12.so.0.1.2.7
+ * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5
+ * 1.0.18rc1-5 10 10018 12.so.0.1.0.18rc1-5
+ * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5
+ * 1.0.18 10 10018 12.so.0.1.0.18
+ * 1.2.8 13 10208 12.so.0.1.2.8
+ * 1.2.9beta1-3 13 10209 12.so.0.1.2.9beta1-3
+ * 1.2.9beta4-11 13 10209 12.so.0.9[.0]
+ * 1.2.9rc1 13 10209 12.so.0.9[.0]
+ * 1.2.9 13 10209 12.so.0.9[.0]
+ * 1.2.10beta1-7 13 10210 12.so.0.10[.0]
+ * 1.2.10rc1-2 13 10210 12.so.0.10[.0]
+ * 1.2.10 13 10210 12.so.0.10[.0]
+ * 1.4.0beta1-5 14 10400 14.so.0.0[.0]
+ * 1.2.11beta1-4 13 10211 12.so.0.11[.0]
+ * 1.4.0beta7-8 14 10400 14.so.0.0[.0]
+ * 1.2.11 13 10211 12.so.0.11[.0]
+ * 1.2.12 13 10212 12.so.0.12[.0]
+ * 1.4.0beta9-14 14 10400 14.so.0.0[.0]
+ * 1.2.13 13 10213 12.so.0.13[.0]
+ * 1.4.0beta15-36 14 10400 14.so.0.0[.0]
+ * 1.4.0beta37-87 14 10400 14.so.14.0[.0]
+ * 1.4.0rc01 14 10400 14.so.14.0[.0]
+ * 1.4.0beta88-109 14 10400 14.so.14.0[.0]
+ * 1.4.0rc02-08 14 10400 14.so.14.0[.0]
+ * 1.4.0 14 10400 14.so.14.0[.0]
+ * 1.4.1beta01-03 14 10401 14.so.14.1[.0]
+ * 1.4.1rc01 14 10401 14.so.14.1[.0]
+ * 1.4.1beta04-12 14 10401 14.so.14.1[.0]
+ * 1.4.1 14 10401 14.so.14.1[.0]
+ * 1.4.2 14 10402 14.so.14.2[.0]
+ * 1.4.3 14 10403 14.so.14.3[.0]
+ * 1.4.4 14 10404 14.so.14.4[.0]
+ * 1.5.0beta01-58 15 10500 15.so.15.0[.0]
+ * 1.5.0rc01-07 15 10500 15.so.15.0[.0]
+ * 1.5.0 15 10500 15.so.15.0[.0]
+ * 1.5.1beta01-11 15 10501 15.so.15.1[.0]
+ * 1.5.1rc01-02 15 10501 15.so.15.1[.0]
+ * 1.5.1 15 10501 15.so.15.1[.0]
+ * 1.5.2beta01-03 15 10502 15.so.15.2[.0]
+ * 1.5.2rc01-03 15 10502 15.so.15.2[.0]
+ * 1.5.2 15 10502 15.so.15.2[.0]
+ *
+ * Henceforth the source version will match the shared-library major
+ * and minor numbers; the shared-library major version number will be
+ * used for changes in backward compatibility, as it is intended. The
+ * PNG_LIBPNG_VER macro, which is not used within libpng but is available
+ * for applications, is an unsigned integer of the form xyyzz corresponding
+ * to the source version x.y.z (leading zeros in y and z). Beta versions
+ * were given the previous public release number plus a letter, until
+ * version 1.0.6j; from then on they were given the upcoming public
+ * release number plus "betaNN" or "rcN".
+ *
+ * Binary incompatibility exists only when applications make direct access
+ * to the info_ptr or png_ptr members through png.h, and the compiled
+ * application is loaded with a different version of the library.
+ *
+ * DLLNUM will change each time there are forward or backward changes
+ * in binary compatibility (e.g., when a new feature is added).
+ *
+ * See libpng-manual.txt or libpng.3 for more information. The PNG
+ * specification is available as a W3C Recommendation and as an ISO
+ * Specification, <http://www.w3.org/TR/2003/REC-PNG-20031110/
+ */
+
+/*
+ * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE:
+ *
+ * If you modify libpng you may insert additional notices immediately following
+ * this sentence.
+ *
+ * This code is released under the libpng license.
+ *
+ * libpng versions 1.2.6, August 15, 2004, through 1.5.2, March 31, 2011, are
+ * Copyright (c) 2004, 2006-2011 Glenn Randers-Pehrson, and are
+ * distributed according to the same disclaimer and license as libpng-1.2.5
+ * with the following individual added to the list of Contributing Authors:
+ *
+ * Cosmin Truta
+ *
+ * libpng versions 1.0.7, July 1, 2000, through 1.2.5, October 3, 2002, are
+ * Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are
+ * distributed according to the same disclaimer and license as libpng-1.0.6
+ * with the following individuals added to the list of Contributing Authors:
+ *
+ * Simon-Pierre Cadieux
+ * Eric S. Raymond
+ * Gilles Vollant
+ *
+ * and with the following additions to the disclaimer:
+ *
+ * There is no warranty against interference with your enjoyment of the
+ * library or against infringement. There is no warranty that our
+ * efforts or the library will fulfill any of your particular purposes
+ * or needs. This library is provided with all faults, and the entire
+ * risk of satisfactory quality, performance, accuracy, and effort is with
+ * the user.
+ *
+ * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
+ * Copyright (c) 1998, 1999, 2000 Glenn Randers-Pehrson, and are
+ * distributed according to the same disclaimer and license as libpng-0.96,
+ * with the following individuals added to the list of Contributing Authors:
+ *
+ * Tom Lane
+ * Glenn Randers-Pehrson
+ * Willem van Schaik
+ *
+ * libpng versions 0.89, June 1996, through 0.96, May 1997, are
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Distributed according to the same disclaimer and license as libpng-0.88,
+ * with the following individuals added to the list of Contributing Authors:
+ *
+ * John Bowler
+ * Kevin Bracey
+ * Sam Bushell
+ * Magnus Holmgren
+ * Greg Roelofs
+ * Tom Tanner
+ *
+ * libpng versions 0.5, May 1995, through 0.88, January 1996, are
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ *
+ * For the purposes of this copyright and license, "Contributing Authors"
+ * is defined as the following set of individuals:
+ *
+ * Andreas Dilger
+ * Dave Martindale
+ * Guy Eric Schalnat
+ * Paul Schmidt
+ * Tim Wegner
+ *
+ * The PNG Reference Library is supplied "AS IS". The Contributing Authors
+ * and Group 42, Inc. disclaim all warranties, expressed or implied,
+ * including, without limitation, the warranties of merchantability and of
+ * fitness for any purpose. The Contributing Authors and Group 42, Inc.
+ * assume no liability for direct, indirect, incidental, special, exemplary,
+ * or consequential damages, which may result from the use of the PNG
+ * Reference Library, even if advised of the possibility of such damage.
+ *
+ * Permission is hereby granted to use, copy, modify, and distribute this
+ * source code, or portions hereof, for any purpose, without fee, subject
+ * to the following restrictions:
+ *
+ * 1. The origin of this source code must not be misrepresented.
+ *
+ * 2. Altered versions must be plainly marked as such and must not
+ * be misrepresented as being the original source.
+ *
+ * 3. This Copyright notice may not be removed or altered from
+ * any source or altered source distribution.
+ *
+ * The Contributing Authors and Group 42, Inc. specifically permit, without
+ * fee, and encourage the use of this source code as a component to
+ * supporting the PNG file format in commercial products. If you use this
+ * source code in a product, acknowledgment is not required but would be
+ * appreciated.
+ */
+
+/*
+ * A "png_get_copyright" function is available, for convenient use in "about"
+ * boxes and the like:
+ *
+ * printf("%s", png_get_copyright(NULL));
+ *
+ * Also, the PNG logo (in PNG format, of course) is supplied in the
+ * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31).
+ */
+
+/*
+ * Libpng is OSI Certified Open Source Software. OSI Certified is a
+ * certification mark of the Open Source Initiative.
+ */
+
+/*
+ * The contributing authors would like to thank all those who helped
+ * with testing, bug fixes, and patience. This wouldn't have been
+ * possible without all of you.
+ *
+ * Thanks to Frank J. T. Wojcik for helping with the documentation.
+ */
+
+/*
+ * Y2K compliance in libpng:
+ * =========================
+ *
+ * March 31, 2011
+ *
+ * Since the PNG Development group is an ad-hoc body, we can't make
+ * an official declaration.
+ *
+ * This is your unofficial assurance that libpng from version 0.71 and
+ * upward through 1.5.2 are Y2K compliant. It is my belief that
+ * earlier versions were also Y2K compliant.
+ *
+ * Libpng only has three year fields. One is a 2-byte unsigned integer
+ * that will hold years up to 65535. The other two hold the date in text
+ * format, and will hold years up to 9999.
+ *
+ * The integer is
+ * "png_uint_16 year" in png_time_struct.
+ *
+ * The strings are
+ * "png_charp time_buffer" in png_struct and
+ * "near_time_buffer", which is a local character string in png.c.
+ *
+ * There are seven time-related functions:
+ * png.c: png_convert_to_rfc_1123() in png.c
+ * (formerly png_convert_to_rfc_1152() in error)
+ * png_convert_from_struct_tm() in pngwrite.c, called in pngwrite.c
+ * png_convert_from_time_t() in pngwrite.c
+ * png_get_tIME() in pngget.c
+ * png_handle_tIME() in pngrutil.c, called in pngread.c
+ * png_set_tIME() in pngset.c
+ * png_write_tIME() in pngwutil.c, called in pngwrite.c
+ *
+ * All handle dates properly in a Y2K environment. The
+ * png_convert_from_time_t() function calls gmtime() to convert from system
+ * clock time, which returns (year - 1900), which we properly convert to
+ * the full 4-digit year. There is a possibility that applications using
+ * libpng are not passing 4-digit years into the png_convert_to_rfc_1123()
+ * function, or that they are incorrectly passing only a 2-digit year
+ * instead of "year - 1900" into the png_convert_from_struct_tm() function,
+ * but this is not under our control. The libpng documentation has always
+ * stated that it works with 4-digit years, and the APIs have been
+ * documented as such.
+ *
+ * The tIME chunk itself is also Y2K compliant. It uses a 2-byte unsigned
+ * integer to hold the year, and can hold years as large as 65535.
+ *
+ * zlib, upon which libpng depends, is also Y2K compliant. It contains
+ * no date-related code.
+ *
+ * Glenn Randers-Pehrson
+ * libpng maintainer
+ * PNG Development Group
+ */
+
+#ifndef PNG_H
+#define PNG_H
+
+/* This is not the place to learn how to use libpng. The file libpng-manual.txt
+ * describes how to use libpng, and the file example.c summarizes it
+ * with some code on which to build. This file is useful for looking
+ * at the actual function definitions and structure components.
+ */
+
+/* Version information for png.h - this should match the version in png.c */
+#define PNG_LIBPNG_VER_STRING "1.5.2"
+#define PNG_HEADER_VERSION_STRING \
+ " libpng version 1.5.2 - March 31, 2011\n"
+
+#define PNG_LIBPNG_VER_SONUM 15
+#define PNG_LIBPNG_VER_DLLNUM 15
+
+/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
+#define PNG_LIBPNG_VER_MAJOR 1
+#define PNG_LIBPNG_VER_MINOR 5
+#define PNG_LIBPNG_VER_RELEASE 2
+/* This should match the numeric part of the final component of
+ * PNG_LIBPNG_VER_STRING, omitting any leading zero:
+ */
+
+#define PNG_LIBPNG_VER_BUILD 0
+
+/* Release Status */
+#define PNG_LIBPNG_BUILD_ALPHA 1
+#define PNG_LIBPNG_BUILD_BETA 2
+#define PNG_LIBPNG_BUILD_RC 3
+#define PNG_LIBPNG_BUILD_STABLE 4
+#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7
+
+/* Release-Specific Flags */
+#define PNG_LIBPNG_BUILD_PATCH 8 /* Can be OR'ed with
+ PNG_LIBPNG_BUILD_STABLE only */
+#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with
+ PNG_LIBPNG_BUILD_SPECIAL */
+#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with
+ PNG_LIBPNG_BUILD_PRIVATE */
+
+#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_BETA
+
+/* Careful here. At one time, Guy wanted to use 082, but that would be octal.
+ * We must not include leading zeros.
+ * Versions 0.7 through 1.0.0 were in the range 0 to 100 here (only
+ * version 1.0.0 was mis-numbered 100 instead of 10000). From
+ * version 1.0.1 it's xxyyzz, where x=major, y=minor, z=release
+ */
+#define PNG_LIBPNG_VER 10502 /* 1.5.2 */
+
+/* Library configuration: these options cannot be changed after
+ * the library has been built.
+ */
+#ifndef PNGLCONF_H
+ /* If pnglibconf.h is missing, you can
+ * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h
+ */
+# include "pnglibconf.h"
+#endif
+
+#ifndef PNG_VERSION_INFO_ONLY
+# ifndef PNG_BUILDING_SYMBOL_TABLE
+ /*
+ * Standard header files (not needed for the version info or while
+ * building symbol table -- see scripts/pnglibconf.dfa)
+ */
+# ifdef PNG_SETJMP_SUPPORTED
+# include <setjmp.h>
+# endif
+
+ /* Need the time information for converting tIME chunks, it
+ * defines struct tm:
+ */
+# ifdef PNG_CONVERT_tIME_SUPPORTED
+ /* "time.h" functions are not supported on all operating systems */
+# include <time.h>
+# endif
+# endif
+
+/* Machine specific configuration. */
+# include "pngconf.h"
+#endif
+
+/*
+ * Added at libpng-1.2.8
+ *
+ * Ref MSDN: Private as priority over Special
+ * VS_FF_PRIVATEBUILD File *was not* built using standard release
+ * procedures. If this value is given, the StringFileInfo block must
+ * contain a PrivateBuild string.
+ *
+ * VS_FF_SPECIALBUILD File *was* built by the original company using
+ * standard release procedures but is a variation of the standard
+ * file of the same version number. If this value is given, the
+ * StringFileInfo block must contain a SpecialBuild string.
+ */
+
+#ifdef PNG_USER_PRIVATEBUILD /* From pnglibconf.h */
+# define PNG_LIBPNG_BUILD_TYPE \
+ (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE)
+#else
+# ifdef PNG_LIBPNG_SPECIALBUILD
+# define PNG_LIBPNG_BUILD_TYPE \
+ (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL)
+# else
+# define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE)
+# endif
+#endif
+
+#ifndef PNG_VERSION_INFO_ONLY
+
+/* Inhibit C++ name-mangling for libpng functions but not for system calls. */
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Version information for C files, stored in png.c. This had better match
+ * the version above.
+ */
+#define png_libpng_ver png_get_header_ver(NULL)
+
+/* This file is arranged in several sections:
+ *
+ * 1. Any configuration options that can be specified by for the application
+ * code when it is built. (Build time configuration is in pnglibconf.h)
+ * 2. Type definitions (base types are defined in pngconf.h), structure
+ * definitions.
+ * 3. Exported library functions.
+ *
+ * The library source code has additional files (principally pngpriv.h) that
+ * allow configuration of the library.
+ */
+/* Section 1: run time configuration
+ * See pnglibconf.h for build time configuration
+ *
+ * Run time configuration allows the application to choose between
+ * implementations of certain arithmetic APIs. The default is set
+ * at build time and recorded in pnglibconf.h, but it is safe to
+ * override these (and only these) settings. Note that this won't
+ * change what the library does, only application code, and the
+ * settings can (and probably should) be made on a per-file basis
+ * by setting the #defines before including png.h
+ *
+ * Use macros to read integers from PNG data or use the exported
+ * functions?
+ * PNG_USE_READ_MACROS: use the macros (see below) Note that
+ * the macros evaluate their argument multiple times.
+ * PNG_NO_USE_READ_MACROS: call the relevant library function.
+ *
+ * Use the alternative algorithm for compositing alpha samples that
+ * does not use division?
+ * PNG_READ_COMPOSITE_NODIV_SUPPORTED: use the 'no division'
+ * algorithm.
+ * PNG_NO_READ_COMPOSITE_NODIV: use the 'division' algorithm.
+ *
+ * How to handle benign errors if PNG_ALLOW_BENIGN_ERRORS is
+ * false?
+ * PNG_ALLOW_BENIGN_ERRORS: map calls to the benign error
+ * APIs to png_warning.
+ * Otherwise the calls are mapped to png_error.
+ */
+
+/* Section 2: type definitions, including structures and compile time
+ * constants.
+ * See pngconf.h for base types that vary by machine/system
+ */
+
+/* This triggers a compiler error in png.c, if png.c and png.h
+ * do not agree upon the version number.
+ */
+typedef char* png_libpng_version_1_5_2;
+
+/* Three color definitions. The order of the red, green, and blue, (and the
+ * exact size) is not important, although the size of the fields need to
+ * be png_byte or png_uint_16 (as defined below).
+ */
+typedef struct png_color_struct
+{
+ png_byte red;
+ png_byte green;
+ png_byte blue;
+} png_color;
+typedef png_color FAR * png_colorp;
+typedef PNG_CONST png_color FAR * png_const_colorp;
+typedef png_color FAR * FAR * png_colorpp;
+
+typedef struct png_color_16_struct
+{
+ png_byte index; /* used for palette files */
+ png_uint_16 red; /* for use in red green blue files */
+ png_uint_16 green;
+ png_uint_16 blue;
+ png_uint_16 gray; /* for use in grayscale files */
+} png_color_16;
+typedef png_color_16 FAR * png_color_16p;
+typedef PNG_CONST png_color_16 FAR * png_const_color_16p;
+typedef png_color_16 FAR * FAR * png_color_16pp;
+
+typedef struct png_color_8_struct
+{
+ png_byte red; /* for use in red green blue files */
+ png_byte green;
+ png_byte blue;
+ png_byte gray; /* for use in grayscale files */
+ png_byte alpha; /* for alpha channel files */
+} png_color_8;
+typedef png_color_8 FAR * png_color_8p;
+typedef PNG_CONST png_color_8 FAR * png_const_color_8p;
+typedef png_color_8 FAR * FAR * png_color_8pp;
+
+/*
+ * The following two structures are used for the in-core representation
+ * of sPLT chunks.
+ */
+typedef struct png_sPLT_entry_struct
+{
+ png_uint_16 red;
+ png_uint_16 green;
+ png_uint_16 blue;
+ png_uint_16 alpha;
+ png_uint_16 frequency;
+} png_sPLT_entry;
+typedef png_sPLT_entry FAR * png_sPLT_entryp;
+typedef PNG_CONST png_sPLT_entry FAR * png_const_sPLT_entryp;
+typedef png_sPLT_entry FAR * FAR * png_sPLT_entrypp;
+
+/* When the depth of the sPLT palette is 8 bits, the color and alpha samples
+ * occupy the LSB of their respective members, and the MSB of each member
+ * is zero-filled. The frequency member always occupies the full 16 bits.
+ */
+
+typedef struct png_sPLT_struct
+{
+ png_charp name; /* palette name */
+ png_byte depth; /* depth of palette samples */
+ png_sPLT_entryp entries; /* palette entries */
+ png_int_32 nentries; /* number of palette entries */
+} png_sPLT_t;
+typedef png_sPLT_t FAR * png_sPLT_tp;
+typedef PNG_CONST png_sPLT_t FAR * png_const_sPLT_tp;
+typedef png_sPLT_t FAR * FAR * png_sPLT_tpp;
+
+#ifdef PNG_TEXT_SUPPORTED
+/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file,
+ * and whether that contents is compressed or not. The "key" field
+ * points to a regular zero-terminated C string. The "text", "lang", and
+ * "lang_key" fields can be regular C strings, empty strings, or NULL pointers.
+ * However, the * structure returned by png_get_text() will always contain
+ * regular zero-terminated C strings (possibly empty), never NULL pointers,
+ * so they can be safely used in printf() and other string-handling functions.
+ */
+typedef struct png_text_struct
+{
+ int compression; /* compression value:
+ -1: tEXt, none
+ 0: zTXt, deflate
+ 1: iTXt, none
+ 2: iTXt, deflate */
+ png_charp key; /* keyword, 1-79 character description of "text" */
+ png_charp text; /* comment, may be an empty string (ie "")
+ or a NULL pointer */
+ png_size_t text_length; /* length of the text string */
+ png_size_t itxt_length; /* length of the itxt string */
+ png_charp lang; /* language code, 0-79 characters
+ or a NULL pointer */
+ png_charp lang_key; /* keyword translated UTF-8 string, 0 or more
+ chars or a NULL pointer */
+} png_text;
+typedef png_text FAR * png_textp;
+typedef PNG_CONST png_text FAR * png_const_textp;
+typedef png_text FAR * FAR * png_textpp;
+#endif
+
+/* Supported compression types for text in PNG files (tEXt, and zTXt).
+ * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */
+#define PNG_TEXT_COMPRESSION_NONE_WR -3
+#define PNG_TEXT_COMPRESSION_zTXt_WR -2
+#define PNG_TEXT_COMPRESSION_NONE -1
+#define PNG_TEXT_COMPRESSION_zTXt 0
+#define PNG_ITXT_COMPRESSION_NONE 1
+#define PNG_ITXT_COMPRESSION_zTXt 2
+#define PNG_TEXT_COMPRESSION_LAST 3 /* Not a valid value */
+
+/* png_time is a way to hold the time in an machine independent way.
+ * Two conversions are provided, both from time_t and struct tm. There
+ * is no portable way to convert to either of these structures, as far
+ * as I know. If you know of a portable way, send it to me. As a side
+ * note - PNG has always been Year 2000 compliant!
+ */
+typedef struct png_time_struct
+{
+ png_uint_16 year; /* full year, as in, 1995 */
+ png_byte month; /* month of year, 1 - 12 */
+ png_byte day; /* day of month, 1 - 31 */
+ png_byte hour; /* hour of day, 0 - 23 */
+ png_byte minute; /* minute of hour, 0 - 59 */
+ png_byte second; /* second of minute, 0 - 60 (for leap seconds) */
+} png_time;
+typedef png_time FAR * png_timep;
+typedef PNG_CONST png_time FAR * png_const_timep;
+typedef png_time FAR * FAR * png_timepp;
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) || \
+ defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED)
+/* png_unknown_chunk is a structure to hold queued chunks for which there is
+ * no specific support. The idea is that we can use this to queue
+ * up private chunks for output even though the library doesn't actually
+ * know about their semantics.
+ */
+typedef struct png_unknown_chunk_t
+{
+ png_byte name[5];
+ png_byte *data;
+ png_size_t size;
+
+ /* libpng-using applications should NOT directly modify this byte. */
+ png_byte location; /* mode of operation at read time */
+}
+png_unknown_chunk;
+typedef png_unknown_chunk FAR * png_unknown_chunkp;
+typedef PNG_CONST png_unknown_chunk FAR * png_const_unknown_chunkp;
+typedef png_unknown_chunk FAR * FAR * png_unknown_chunkpp;
+#endif
+
+typedef struct png_info_def png_info;
+typedef png_info FAR * png_infop;
+typedef PNG_CONST png_info FAR * png_const_infop;
+typedef png_info FAR * FAR * png_infopp;
+
+/* Maximum positive integer used in PNG is (2^31)-1 */
+#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL)
+#define PNG_UINT_32_MAX ((png_uint_32)(-1))
+#define PNG_SIZE_MAX ((png_size_t)(-1))
+
+/* These are constants for fixed point values encoded in the
+ * PNG specification manner (x100000)
+ */
+#define PNG_FP_1 100000
+#define PNG_FP_HALF 50000
+
+/* These describe the color_type field in png_info. */
+/* color type masks */
+#define PNG_COLOR_MASK_PALETTE 1
+#define PNG_COLOR_MASK_COLOR 2
+#define PNG_COLOR_MASK_ALPHA 4
+
+/* color types. Note that not all combinations are legal */
+#define PNG_COLOR_TYPE_GRAY 0
+#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE)
+#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR)
+#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA)
+#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA)
+/* aliases */
+#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA
+#define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA
+
+/* This is for compression type. PNG 1.0-1.2 only define the single type. */
+#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */
+#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE
+
+/* This is for filter type. PNG 1.0-1.2 only define the single type. */
+#define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */
+#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */
+#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE
+
+/* These are for the interlacing type. These values should NOT be changed. */
+#define PNG_INTERLACE_NONE 0 /* Non-interlaced image */
+#define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */
+#define PNG_INTERLACE_LAST 2 /* Not a valid value */
+
+/* These are for the oFFs chunk. These values should NOT be changed. */
+#define PNG_OFFSET_PIXEL 0 /* Offset in pixels */
+#define PNG_OFFSET_MICROMETER 1 /* Offset in micrometers (1/10^6 meter) */
+#define PNG_OFFSET_LAST 2 /* Not a valid value */
+
+/* These are for the pCAL chunk. These values should NOT be changed. */
+#define PNG_EQUATION_LINEAR 0 /* Linear transformation */
+#define PNG_EQUATION_BASE_E 1 /* Exponential base e transform */
+#define PNG_EQUATION_ARBITRARY 2 /* Arbitrary base exponential transform */
+#define PNG_EQUATION_HYPERBOLIC 3 /* Hyperbolic sine transformation */
+#define PNG_EQUATION_LAST 4 /* Not a valid value */
+
+/* These are for the sCAL chunk. These values should NOT be changed. */
+#define PNG_SCALE_UNKNOWN 0 /* unknown unit (image scale) */
+#define PNG_SCALE_METER 1 /* meters per pixel */
+#define PNG_SCALE_RADIAN 2 /* radians per pixel */
+#define PNG_SCALE_LAST 3 /* Not a valid value */
+
+/* These are for the pHYs chunk. These values should NOT be changed. */
+#define PNG_RESOLUTION_UNKNOWN 0 /* pixels/unknown unit (aspect ratio) */
+#define PNG_RESOLUTION_METER 1 /* pixels/meter */
+#define PNG_RESOLUTION_LAST 2 /* Not a valid value */
+
+/* These are for the sRGB chunk. These values should NOT be changed. */
+#define PNG_sRGB_INTENT_PERCEPTUAL 0
+#define PNG_sRGB_INTENT_RELATIVE 1
+#define PNG_sRGB_INTENT_SATURATION 2
+#define PNG_sRGB_INTENT_ABSOLUTE 3
+#define PNG_sRGB_INTENT_LAST 4 /* Not a valid value */
+
+/* This is for text chunks */
+#define PNG_KEYWORD_MAX_LENGTH 79
+
+/* Maximum number of entries in PLTE/sPLT/tRNS arrays */
+#define PNG_MAX_PALETTE_LENGTH 256
+
+/* These determine if an ancillary chunk's data has been successfully read
+ * from the PNG header, or if the application has filled in the corresponding
+ * data in the info_struct to be written into the output file. The values
+ * of the PNG_INFO_<chunk> defines should NOT be changed.
+ */
+#define PNG_INFO_gAMA 0x0001
+#define PNG_INFO_sBIT 0x0002
+#define PNG_INFO_cHRM 0x0004
+#define PNG_INFO_PLTE 0x0008
+#define PNG_INFO_tRNS 0x0010
+#define PNG_INFO_bKGD 0x0020
+#define PNG_INFO_hIST 0x0040
+#define PNG_INFO_pHYs 0x0080
+#define PNG_INFO_oFFs 0x0100
+#define PNG_INFO_tIME 0x0200
+#define PNG_INFO_pCAL 0x0400
+#define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */
+#define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */
+#define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */
+#define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */
+#define PNG_INFO_IDAT 0x8000L /* ESR, 1.0.6 */
+
+/* This is used for the transformation routines, as some of them
+ * change these values for the row. It also should enable using
+ * the routines for other purposes.
+ */
+typedef struct png_row_info_struct
+{
+ png_uint_32 width; /* width of row */
+ png_size_t rowbytes; /* number of bytes in row */
+ png_byte color_type; /* color type of row */
+ png_byte bit_depth; /* bit depth of row */
+ png_byte channels; /* number of channels (1, 2, 3, or 4) */
+ png_byte pixel_depth; /* bits per pixel (depth * channels) */
+} png_row_info;
+
+typedef png_row_info FAR * png_row_infop;
+typedef png_row_info FAR * FAR * png_row_infopp;
+
+/* These are the function types for the I/O functions and for the functions
+ * that allow the user to override the default I/O functions with his or her
+ * own. The png_error_ptr type should match that of user-supplied warning
+ * and error functions, while the png_rw_ptr type should match that of the
+ * user read/write data functions. Note that the 'write' function must not
+ * modify the buffer it is passed. The 'read' function, on the other hand, is
+ * expected to return the read data in the buffer.
+ */
+typedef struct png_struct_def png_struct;
+typedef PNG_CONST png_struct FAR * png_const_structp;
+typedef png_struct FAR * png_structp;
+
+typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp));
+typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, png_size_t));
+typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp));
+typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32,
+ int));
+typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32,
+ int));
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop));
+typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop));
+
+/* The following callback receives png_uint_32 row_number, int pass for the
+ * png_bytep data of the row. When transforming an interlaced image the
+ * row number is the row number within the sub-image of the interlace pass, so
+ * the value will increase to the height of the sub-image (not the full image)
+ * then reset to 0 for the next pass.
+ *
+ * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to
+ * find the output pixel (x,y) given an interlaced sub-image pixel
+ * (row,col,pass). (See below for these macros.)
+ */
+typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep,
+ png_uint_32, int));
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+ defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop,
+ png_bytep));
+#endif
+
+#ifdef PNG_USER_CHUNKS_SUPPORTED
+typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp,
+ png_unknown_chunkp));
+#endif
+#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
+typedef PNG_CALLBACK(void, *png_unknown_chunk_ptr, (png_structp));
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+/* This must match the function definition in <setjmp.h>, and the
+ * application must include this before png.h to obtain the definition
+ * of jmp_buf. The function is required to be PNG_NORETURN. (Note that
+ * PNG_PTR_NORETURN is used here because current versions of the Microsoft
+ * C compiler do not support the PNG_NORETURN attribute on a pointer.)
+ *
+ * If you get a type warning from the compiler when linking against this line
+ * then your compiler has 'longjmp' that does not match the requirements of the
+ * compiler that built libpng. You will have to write a wrapper function for
+ * your compiler's longjmp and call png_set_longjmp_fn directly (not via the
+ * png_jmpbuf macro.)
+ *
+ * If you get a warning here while building the library you will need to make
+ * changes to ensure that pnglibconf.h records the calling convention used by
+ * your compiler. This may be very difficult - try using a different compiler
+ * to build the library!
+ */
+typedef PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)),
+ PNG_PTR_NORETURN);
+#endif
+
+/* Transform masks for the high-level interface */
+#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */
+#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */
+#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */
+#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */
+#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */
+#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */
+#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */
+#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */
+#define PNG_TRANSFORM_BGR 0x0080 /* read and write */
+#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */
+#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */
+#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */
+#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */
+/* Added to libpng-1.2.34 */
+#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER
+#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */
+/* Added to libpng-1.4.0 */
+#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */
+
+/* Flags for MNG supported features */
+#define PNG_FLAG_MNG_EMPTY_PLTE 0x01
+#define PNG_FLAG_MNG_FILTER_64 0x04
+#define PNG_ALL_MNG_FEATURES 0x05
+
+/* NOTE: prior to 1.5 these functions had no 'API' style declaration,
+ * this allowed the zlib default functions to be used on Windows
+ * platforms. In 1.5 the zlib default malloc (which just calls malloc and
+ * ignores the first argument) should be completely compatible with the
+ * following.
+ */
+typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp,
+ png_alloc_size_t));
+typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp));
+
+typedef png_struct FAR * FAR * png_structpp;
+
+/* Section 3: exported functions
+ * Here are the function definitions most commonly used. This is not
+ * the place to find out how to use libpng. See libpng-manual.txt for the
+ * full explanation, see example.c for the summary. This just provides
+ * a simple one line description of the use of each function.
+ *
+ * The PNG_EXPORT() and PNG_EXPORTA() macros used below are defined in
+ * pngconf.h and in the *.dfn files in the scripts directory.
+ *
+ * PNG_EXPORT(ordinal, type, name, (args));
+ *
+ * ordinal: ordinal that is used while building
+ * *.def files. The ordinal value is only
+ * relevant when preprocessing png.h with
+ * the *.dfn files for building symbol table
+ * entries, and are removed by pngconf.h.
+ * type: return type of the function
+ * name: function name
+ * args: function arguments, with types
+ *
+ * When we wish to append attributes to a function prototype we use
+ * the PNG_EXPORTA() macro instead.
+ *
+ * PNG_EXPORTA(ordinal, type, name, (args), attributes);
+ *
+ * ordinal, type, name, and args: same as in PNG_EXPORT().
+ * attributes: function attributes
+ */
+
+/* Returns the version number of the library */
+PNG_EXPORT(1, png_uint_32, png_access_version_number, (void));
+
+/* Tell lib we have already handled the first <num_bytes> magic bytes.
+ * Handling more than 8 bytes from the beginning of the file is an error.
+ */
+PNG_EXPORT(2, void, png_set_sig_bytes, (png_structp png_ptr, int num_bytes));
+
+/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a
+ * PNG file. Returns zero if the supplied bytes match the 8-byte PNG
+ * signature, and non-zero otherwise. Having num_to_check == 0 or
+ * start > 7 will always fail (ie return non-zero).
+ */
+PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, png_size_t start,
+ png_size_t num_to_check));
+
+/* Simple signature checking function. This is the same as calling
+ * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n).
+ */
+#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n))
+
+/* Allocate and initialize png_ptr struct for reading, and any other memory. */
+PNG_EXPORTA(4, png_structp, png_create_read_struct,
+ (png_const_charp user_png_ver, png_voidp error_ptr,
+ png_error_ptr error_fn, png_error_ptr warn_fn),
+ PNG_ALLOCATED);
+
+/* Allocate and initialize png_ptr struct for writing, and any other memory */
+PNG_EXPORTA(5, png_structp, png_create_write_struct,
+ (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
+ png_error_ptr warn_fn),
+ PNG_ALLOCATED);
+
+PNG_EXPORT(6, png_size_t, png_get_compression_buffer_size,
+ (png_const_structp png_ptr));
+
+PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structp png_ptr,
+ png_size_t size));
+
+/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp
+ * match up.
+ */
+#ifdef PNG_SETJMP_SUPPORTED
+/* This function returns the jmp_buf built in to *png_ptr. It must be
+ * supplied with an appropriate 'longjmp' function to use on that jmp_buf
+ * unless the default error function is overridden in which case NULL is
+ * acceptable. The size of the jmp_buf is checked against the actual size
+ * allocated by the library - the call will return NULL on a mismatch
+ * indicating an ABI mismatch.
+ */
+PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structp png_ptr,
+ png_longjmp_ptr longjmp_fn, size_t jmp_buf_size));
+# define png_jmpbuf(png_ptr) \
+ (*png_set_longjmp_fn((png_ptr), longjmp, sizeof (jmp_buf)))
+#else
+# define png_jmpbuf(png_ptr) \
+ (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP)
+#endif
+/* This function should be used by libpng applications in place of
+ * longjmp(png_ptr->jmpbuf, val). If longjmp_fn() has been set, it
+ * will use it; otherwise it will call PNG_ABORT(). This function was
+ * added in libpng-1.5.0.
+ */
+PNG_EXPORTA(9, void, png_longjmp, (png_structp png_ptr, int val),
+ PNG_NORETURN);
+
+#ifdef PNG_READ_SUPPORTED
+/* Reset the compression stream */
+PNG_EXPORT(10, int, png_reset_zstream, (png_structp png_ptr));
+#endif
+
+/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */
+#ifdef PNG_USER_MEM_SUPPORTED
+PNG_EXPORTA(11, png_structp, png_create_read_struct_2,
+ (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
+ png_error_ptr warn_fn,
+ png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn),
+ PNG_ALLOCATED);
+PNG_EXPORTA(12, png_structp, png_create_write_struct_2,
+ (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
+ png_error_ptr warn_fn,
+ png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn),
+ PNG_ALLOCATED);
+#endif
+
+/* Write the PNG file signature. */
+PNG_EXPORT(13, void, png_write_sig, (png_structp png_ptr));
+
+/* Write a PNG chunk - size, type, (optional) data, CRC. */
+PNG_EXPORT(14, void, png_write_chunk, (png_structp png_ptr, png_const_bytep
+ chunk_name, png_const_bytep data, png_size_t length));
+
+/* Write the start of a PNG chunk - length and chunk name. */
+PNG_EXPORT(15, void, png_write_chunk_start, (png_structp png_ptr,
+ png_const_bytep chunk_name, png_uint_32 length));
+
+/* Write the data of a PNG chunk started with png_write_chunk_start(). */
+PNG_EXPORT(16, void, png_write_chunk_data, (png_structp png_ptr,
+ png_const_bytep data, png_size_t length));
+
+/* Finish a chunk started with png_write_chunk_start() (includes CRC). */
+PNG_EXPORT(17, void, png_write_chunk_end, (png_structp png_ptr));
+
+/* Allocate and initialize the info structure */
+PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_structp png_ptr),
+ PNG_ALLOCATED);
+
+PNG_EXPORT(19, void, png_info_init_3, (png_infopp info_ptr,
+ png_size_t png_info_struct_size));
+
+/* Writes all the PNG information before the image. */
+PNG_EXPORT(20, void, png_write_info_before_PLTE,
+ (png_structp png_ptr, png_infop info_ptr));
+PNG_EXPORT(21, void, png_write_info,
+ (png_structp png_ptr, png_infop info_ptr));
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read the information before the actual image data. */
+PNG_EXPORT(22, void, png_read_info,
+ (png_structp png_ptr, png_infop info_ptr));
+#endif
+
+#ifdef PNG_TIME_RFC1123_SUPPORTED
+PNG_EXPORT(23, png_const_charp, png_convert_to_rfc1123,
+ (png_structp png_ptr,
+ png_const_timep ptime));
+#endif
+
+#ifdef PNG_CONVERT_tIME_SUPPORTED
+/* Convert from a struct tm to png_time */
+PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime,
+ PNG_CONST struct tm FAR * ttime));
+
+/* Convert from time_t to png_time. Uses gmtime() */
+PNG_EXPORT(25, void, png_convert_from_time_t,
+ (png_timep ptime, time_t ttime));
+#endif /* PNG_CONVERT_tIME_SUPPORTED */
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */
+PNG_EXPORT(26, void, png_set_expand, (png_structp png_ptr));
+PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structp png_ptr));
+PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structp png_ptr));
+PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structp png_ptr));
+#endif
+
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+/* Expand to 16 bit channels, forces conversion of palette to RGB and expansion
+ * of a tRNS chunk if present.
+ */
+PNG_EXPORT(221, void, png_set_expand_16, (png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+/* Use blue, green, red order for pixels. */
+PNG_EXPORT(30, void, png_set_bgr, (png_structp png_ptr));
+#endif
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+/* Expand the grayscale to 24-bit RGB if necessary. */
+PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structp png_ptr));
+#endif
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+/* Reduce RGB to grayscale. */
+PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structp png_ptr,
+ int error_action, double red, double green));
+PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structp png_ptr,
+ int error_action, png_fixed_point red, png_fixed_point green));
+
+PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structp
+ png_ptr));
+#endif
+
+PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth,
+ png_colorp palette));
+
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+PNG_EXPORT(36, void, png_set_strip_alpha, (png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
+ defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+PNG_EXPORT(37, void, png_set_swap_alpha, (png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \
+ defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+PNG_EXPORT(38, void, png_set_invert_alpha, (png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
+/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */
+PNG_EXPORT(39, void, png_set_filler, (png_structp png_ptr, png_uint_32 filler,
+ int flags));
+/* The values of the PNG_FILLER_ defines should NOT be changed */
+# define PNG_FILLER_BEFORE 0
+# define PNG_FILLER_AFTER 1
+/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */
+PNG_EXPORT(40, void, png_set_add_alpha,
+ (png_structp png_ptr, png_uint_32 filler,
+ int flags));
+#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */
+
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+/* Swap bytes in 16-bit depth files. */
+PNG_EXPORT(41, void, png_set_swap, (png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */
+PNG_EXPORT(42, void, png_set_packing, (png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \
+ defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+/* Swap packing order of pixels in bytes. */
+PNG_EXPORT(43, void, png_set_packswap, (png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
+/* Converts files to legal bit depths. */
+PNG_EXPORT(44, void, png_set_shift, (png_structp png_ptr, png_const_color_8p
+ true_bits));
+#endif
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
+ defined(PNG_WRITE_INTERLACING_SUPPORTED)
+/* Have the code handle the interlacing. Returns the number of passes.
+ * MUST be called before png_read_update_info or png_start_read_image,
+ * otherwise it will not have the desired effect. Note that it is still
+ * necessary to call png_read_row or png_read_rows png_get_image_height
+ * times for each pass.
+*/
+PNG_EXPORT(45, int, png_set_interlace_handling, (png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
+/* Invert monochrome files */
+PNG_EXPORT(46, void, png_set_invert_mono, (png_structp png_ptr));
+#endif
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+/* Handle alpha and tRNS by replacing with a background color. */
+PNG_FP_EXPORT(47, void, png_set_background, (png_structp png_ptr,
+ png_const_color_16p background_color, int background_gamma_code,
+ int need_expand, double background_gamma));
+PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structp png_ptr,
+ png_const_color_16p background_color, int background_gamma_code,
+ int need_expand, png_fixed_point background_gamma));
+#endif
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+# define PNG_BACKGROUND_GAMMA_UNKNOWN 0
+# define PNG_BACKGROUND_GAMMA_SCREEN 1
+# define PNG_BACKGROUND_GAMMA_FILE 2
+# define PNG_BACKGROUND_GAMMA_UNIQUE 3
+#endif
+
+#ifdef PNG_READ_16_TO_8_SUPPORTED
+/* Strip the second byte of information from a 16-bit depth file. */
+PNG_EXPORT(48, void, png_set_strip_16, (png_structp png_ptr));
+#endif
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+/* Turn on quantizing, and reduce the palette to the number of colors
+ * available.
+ */
+PNG_EXPORT(49, void, png_set_quantize,
+ (png_structp png_ptr, png_colorp palette,
+ int num_palette, int maximum_colors, png_const_uint_16p histogram,
+ int full_quantize));
+#endif
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+/* The threshold on gamma processing is configurable but hard-wired into the
+ * library. The following is the floating point variant.
+ */
+#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001)
+
+/* Handle gamma correction. Screen_gamma=(display_exponent) */
+PNG_FP_EXPORT(50, void, png_set_gamma,
+ (png_structp png_ptr, double screen_gamma,
+ double default_file_gamma));
+PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structp png_ptr,
+ png_fixed_point screen_gamma, png_fixed_point default_file_gamma));
+#endif
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+/* Set how many lines between output flushes - 0 for no flushing */
+PNG_EXPORT(51, void, png_set_flush, (png_structp png_ptr, int nrows));
+/* Flush the current PNG output buffer */
+PNG_EXPORT(52, void, png_write_flush, (png_structp png_ptr));
+#endif
+
+/* Optional update palette with requested transformations */
+PNG_EXPORT(53, void, png_start_read_image, (png_structp png_ptr));
+
+/* Optional call to update the users info structure */
+PNG_EXPORT(54, void, png_read_update_info,
+ (png_structp png_ptr, png_infop info_ptr));
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read one or more rows of image data. */
+PNG_EXPORT(55, void, png_read_rows, (png_structp png_ptr, png_bytepp row,
+ png_bytepp display_row, png_uint_32 num_rows));
+#endif
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read a row of data. */
+PNG_EXPORT(56, void, png_read_row, (png_structp png_ptr, png_bytep row,
+ png_bytep display_row));
+#endif
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read the whole image into memory at once. */
+PNG_EXPORT(57, void, png_read_image, (png_structp png_ptr, png_bytepp image));
+#endif
+
+/* Write a row of image data */
+PNG_EXPORT(58, void, png_write_row,
+ (png_structp png_ptr, png_const_bytep row));
+
+/* Write a few rows of image data: (*row) is not written; however, the type
+ * is declared as writeable to maintain compatibility with previous versions
+ * of libpng and to allow the 'display_row' array from read_rows to be passed
+ * unchanged to write_rows.
+ */
+PNG_EXPORT(59, void, png_write_rows, (png_structp png_ptr, png_bytepp row,
+ png_uint_32 num_rows));
+
+/* Write the image data */
+PNG_EXPORT(60, void, png_write_image,
+ (png_structp png_ptr, png_bytepp image));
+
+/* Write the end of the PNG file. */
+PNG_EXPORT(61, void, png_write_end,
+ (png_structp png_ptr, png_infop info_ptr));
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read the end of the PNG file. */
+PNG_EXPORT(62, void, png_read_end, (png_structp png_ptr, png_infop info_ptr));
+#endif
+
+/* Free any memory associated with the png_info_struct */
+PNG_EXPORT(63, void, png_destroy_info_struct, (png_structp png_ptr,
+ png_infopp info_ptr_ptr));
+
+/* Free any memory associated with the png_struct and the png_info_structs */
+PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr,
+ png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr));
+
+/* Free any memory associated with the png_struct and the png_info_structs */
+PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr,
+ png_infopp info_ptr_ptr));
+
+/* Set the libpng method of handling chunk CRC errors */
+PNG_EXPORT(66, void, png_set_crc_action,
+ (png_structp png_ptr, int crit_action, int ancil_action));
+
+/* Values for png_set_crc_action() say how to handle CRC errors in
+ * ancillary and critical chunks, and whether to use the data contained
+ * therein. Note that it is impossible to "discard" data in a critical
+ * chunk. For versions prior to 0.90, the action was always error/quit,
+ * whereas in version 0.90 and later, the action for CRC errors in ancillary
+ * chunks is warn/discard. These values should NOT be changed.
+ *
+ * value action:critical action:ancillary
+ */
+#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */
+#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */
+#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */
+#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */
+#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */
+#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */
+
+/* These functions give the user control over the scan-line filtering in
+ * libpng and the compression methods used by zlib. These functions are
+ * mainly useful for testing, as the defaults should work with most users.
+ * Those users who are tight on memory or want faster performance at the
+ * expense of compression can modify them. See the compression library
+ * header file (zlib.h) for an explination of the compression functions.
+ */
+
+/* Set the filtering method(s) used by libpng. Currently, the only valid
+ * value for "method" is 0.
+ */
+PNG_EXPORT(67, void, png_set_filter,
+ (png_structp png_ptr, int method, int filters));
+
+/* Flags for png_set_filter() to say which filters to use. The flags
+ * are chosen so that they don't conflict with real filter types
+ * below, in case they are supplied instead of the #defined constants.
+ * These values should NOT be changed.
+ */
+#define PNG_NO_FILTERS 0x00
+#define PNG_FILTER_NONE 0x08
+#define PNG_FILTER_SUB 0x10
+#define PNG_FILTER_UP 0x20
+#define PNG_FILTER_AVG 0x40
+#define PNG_FILTER_PAETH 0x80
+#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \
+ PNG_FILTER_AVG | PNG_FILTER_PAETH)
+
+/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now.
+ * These defines should NOT be changed.
+ */
+#define PNG_FILTER_VALUE_NONE 0
+#define PNG_FILTER_VALUE_SUB 1
+#define PNG_FILTER_VALUE_UP 2
+#define PNG_FILTER_VALUE_AVG 3
+#define PNG_FILTER_VALUE_PAETH 4
+#define PNG_FILTER_VALUE_LAST 5
+
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* EXPERIMENTAL */
+/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_
+ * defines, either the default (minimum-sum-of-absolute-differences), or
+ * the experimental method (weighted-minimum-sum-of-absolute-differences).
+ *
+ * Weights are factors >= 1.0, indicating how important it is to keep the
+ * filter type consistent between rows. Larger numbers mean the current
+ * filter is that many times as likely to be the same as the "num_weights"
+ * previous filters. This is cumulative for each previous row with a weight.
+ * There needs to be "num_weights" values in "filter_weights", or it can be
+ * NULL if the weights aren't being specified. Weights have no influence on
+ * the selection of the first row filter. Well chosen weights can (in theory)
+ * improve the compression for a given image.
+ *
+ * Costs are factors >= 1.0 indicating the relative decoding costs of a
+ * filter type. Higher costs indicate more decoding expense, and are
+ * therefore less likely to be selected over a filter with lower computational
+ * costs. There needs to be a value in "filter_costs" for each valid filter
+ * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't
+ * setting the costs. Costs try to improve the speed of decompression without
+ * unduly increasing the compressed image size.
+ *
+ * A negative weight or cost indicates the default value is to be used, and
+ * values in the range [0.0, 1.0) indicate the value is to remain unchanged.
+ * The default values for both weights and costs are currently 1.0, but may
+ * change if good general weighting/cost heuristics can be found. If both
+ * the weights and costs are set to 1.0, this degenerates the WEIGHTED method
+ * to the UNWEIGHTED method, but with added encoding time/computation.
+ */
+PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structp png_ptr,
+ int heuristic_method, int num_weights, png_const_doublep filter_weights,
+ png_const_doublep filter_costs));
+PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed,
+ (png_structp png_ptr,
+ int heuristic_method, int num_weights, png_const_fixed_point_p
+ filter_weights, png_const_fixed_point_p filter_costs));
+#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
+
+/* Heuristic used for row filter selection. These defines should NOT be
+ * changed.
+ */
+#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */
+#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */
+#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */
+#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */
+
+/* Set the library compression level. Currently, valid values range from
+ * 0 - 9, corresponding directly to the zlib compression levels 0 - 9
+ * (0 - no compression, 9 - "maximal" compression). Note that tests have
+ * shown that zlib compression levels 3-6 usually perform as well as level 9
+ * for PNG images, and do considerably fewer caclulations. In the future,
+ * these values may not correspond directly to the zlib compression levels.
+ */
+PNG_EXPORT(69, void, png_set_compression_level,
+ (png_structp png_ptr, int level));
+
+PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structp png_ptr,
+ int mem_level));
+
+PNG_EXPORT(71, void, png_set_compression_strategy, (png_structp png_ptr,
+ int strategy));
+
+PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structp png_ptr,
+ int window_bits));
+
+PNG_EXPORT(73, void, png_set_compression_method, (png_structp png_ptr,
+ int method));
+
+/* These next functions are called for input/output, memory, and error
+ * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c,
+ * and call standard C I/O routines such as fread(), fwrite(), and
+ * fprintf(). These functions can be made to use other I/O routines
+ * at run time for those applications that need to handle I/O in a
+ * different manner by calling png_set_???_fn(). See libpng-manual.txt for
+ * more information.
+ */
+
+#ifdef PNG_STDIO_SUPPORTED
+/* Initialize the input/output for the PNG file to the default functions. */
+PNG_EXPORT(74, void, png_init_io, (png_structp png_ptr, png_FILE_p fp));
+#endif
+
+/* Replace the (error and abort), and warning functions with user
+ * supplied functions. If no messages are to be printed you must still
+ * write and use replacement functions. The replacement error_fn should
+ * still do a longjmp to the last setjmp location if you are using this
+ * method of error handling. If error_fn or warning_fn is NULL, the
+ * default function will be used.
+ */
+
+PNG_EXPORT(75, void, png_set_error_fn,
+ (png_structp png_ptr, png_voidp error_ptr,
+ png_error_ptr error_fn, png_error_ptr warning_fn));
+
+/* Return the user pointer associated with the error functions */
+PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structp png_ptr));
+
+/* Replace the default data output functions with a user supplied one(s).
+ * If buffered output is not used, then output_flush_fn can be set to NULL.
+ * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time
+ * output_flush_fn will be ignored (and thus can be NULL).
+ * It is probably a mistake to use NULL for output_flush_fn if
+ * write_data_fn is not also NULL unless you have built libpng with
+ * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's
+ * default flush function, which uses the standard *FILE structure, will
+ * be used.
+ */
+PNG_EXPORT(77, void, png_set_write_fn, (png_structp png_ptr, png_voidp io_ptr,
+ png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn));
+
+/* Replace the default data input function with a user supplied one. */
+PNG_EXPORT(78, void, png_set_read_fn, (png_structp png_ptr, png_voidp io_ptr,
+ png_rw_ptr read_data_fn));
+
+/* Return the user pointer associated with the I/O functions */
+PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_structp png_ptr));
+
+PNG_EXPORT(80, void, png_set_read_status_fn, (png_structp png_ptr,
+ png_read_status_ptr read_row_fn));
+
+PNG_EXPORT(81, void, png_set_write_status_fn, (png_structp png_ptr,
+ png_write_status_ptr write_row_fn));
+
+#ifdef PNG_USER_MEM_SUPPORTED
+/* Replace the default memory allocation functions with user supplied one(s). */
+PNG_EXPORT(82, void, png_set_mem_fn, (png_structp png_ptr, png_voidp mem_ptr,
+ png_malloc_ptr malloc_fn, png_free_ptr free_fn));
+/* Return the user pointer associated with the memory functions */
+PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structp png_ptr));
+#endif
+
+#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
+PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structp png_ptr,
+ png_user_transform_ptr read_user_transform_fn));
+#endif
+
+#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
+PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structp png_ptr,
+ png_user_transform_ptr write_user_transform_fn));
+#endif
+
+#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
+PNG_EXPORT(86, void, png_set_user_transform_info, (png_structp png_ptr,
+ png_voidp user_transform_ptr, int user_transform_depth,
+ int user_transform_channels));
+/* Return the user pointer associated with the user transform functions */
+PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr,
+ (png_const_structp png_ptr));
+#endif
+
+#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
+/* Return information about the row currently being processed. Note that these
+ * APIs do not fail but will return unexpected results if called outside a user
+ * transform callback. Also note that when transforming an interlaced image the
+ * row number is the row number within the sub-image of the interlace pass, so
+ * the value will increase to the height of the sub-image (not the full image)
+ * then reset to 0 for the next pass.
+ *
+ * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to
+ * find the output pixel (x,y) given an interlaced sub-image pixel
+ * (row,col,pass). (See below for these macros.)
+ */
+PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structp));
+PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structp));
+#endif
+
+#ifdef PNG_USER_CHUNKS_SUPPORTED
+PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structp png_ptr,
+ png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn));
+PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structp png_ptr));
+#endif
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+/* Sets the function callbacks for the push reader, and a pointer to a
+ * user-defined structure available to the callback functions.
+ */
+PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structp png_ptr,
+ png_voidp progressive_ptr, png_progressive_info_ptr info_fn,
+ png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn));
+
+/* Returns the user pointer associated with the push read functions */
+PNG_EXPORT(91, png_voidp, png_get_progressive_ptr, (png_const_structp png_ptr));
+
+/* Function to be called when data becomes available */
+PNG_EXPORT(92, void, png_process_data,
+ (png_structp png_ptr, png_infop info_ptr,
+ png_bytep buffer, png_size_t buffer_size));
+
+/* A function which may be called *only* within png_process_data to stop the
+ * processing of any more data. The function returns the number of bytes
+ * remaining, excluding any that libpng has cached internally. A subsequent
+ * call to png_process_data must supply these bytes again. If the argument
+ * 'save' is set to true the routine will first save all the pending data and
+ * will always return 0.
+ */
+PNG_EXPORT(219, png_size_t, png_process_data_pause, (png_structp, int save));
+
+/* A function which may be called *only* outside (after) a call to
+ * png_process_data. It returns the number of bytes of data to skip in the
+ * input. Normally it will return 0, but if it returns a non-zero value the
+ * application must skip than number of bytes of input data and pass the
+ * following data to the next call to png_process_data.
+ */
+PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structp));
+
+/* Function that combines rows. 'new_row' is a flag that should come from
+ * the callback and be non-NULL if anything needs to be done; the library
+ * stores its own version of the new data internally and ignores the passed
+ * in value.
+ */
+PNG_EXPORT(93, void, png_progressive_combine_row, (png_structp png_ptr,
+ png_bytep old_row, png_const_bytep new_row));
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
+PNG_EXPORTA(94, png_voidp, png_malloc,
+ (png_structp png_ptr, png_alloc_size_t size),
+ PNG_ALLOCATED);
+/* Added at libpng version 1.4.0 */
+PNG_EXPORTA(95, png_voidp, png_calloc,
+ (png_structp png_ptr, png_alloc_size_t size),
+ PNG_ALLOCATED);
+
+/* Added at libpng version 1.2.4 */
+PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_structp png_ptr,
+ png_alloc_size_t size), PNG_ALLOCATED);
+
+/* Frees a pointer allocated by png_malloc() */
+PNG_EXPORT(97, void, png_free, (png_structp png_ptr, png_voidp ptr));
+
+/* Free data that was allocated internally */
+PNG_EXPORT(98, void, png_free_data,
+ (png_structp png_ptr, png_infop info_ptr, png_uint_32 free_me, int num));
+
+/* Reassign responsibility for freeing existing data, whether allocated
+ * by libpng or by the application */
+PNG_EXPORT(99, void, png_data_freer,
+ (png_structp png_ptr, png_infop info_ptr, int freer, png_uint_32 mask));
+
+/* Assignments for png_data_freer */
+#define PNG_DESTROY_WILL_FREE_DATA 1
+#define PNG_SET_WILL_FREE_DATA 1
+#define PNG_USER_WILL_FREE_DATA 2
+/* Flags for png_ptr->free_me and info_ptr->free_me */
+#define PNG_FREE_HIST 0x0008
+#define PNG_FREE_ICCP 0x0010
+#define PNG_FREE_SPLT 0x0020
+#define PNG_FREE_ROWS 0x0040
+#define PNG_FREE_PCAL 0x0080
+#define PNG_FREE_SCAL 0x0100
+#define PNG_FREE_UNKN 0x0200
+#define PNG_FREE_LIST 0x0400
+#define PNG_FREE_PLTE 0x1000
+#define PNG_FREE_TRNS 0x2000
+#define PNG_FREE_TEXT 0x4000
+#define PNG_FREE_ALL 0x7fff
+#define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */
+
+#ifdef PNG_USER_MEM_SUPPORTED
+PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_structp png_ptr,
+ png_alloc_size_t size), PNG_ALLOCATED);
+PNG_EXPORT(101, void, png_free_default, (png_structp png_ptr, png_voidp ptr));
+#endif
+
+#ifdef PNG_ERROR_TEXT_SUPPORTED
+/* Fatal error in PNG image of libpng - can't continue */
+PNG_EXPORTA(102, void, png_error,
+ (png_structp png_ptr, png_const_charp error_message),
+ PNG_NORETURN);
+
+/* The same, but the chunk name is prepended to the error string. */
+PNG_EXPORTA(103, void, png_chunk_error, (png_structp png_ptr,
+ png_const_charp error_message), PNG_NORETURN);
+
+#else
+/* Fatal error in PNG image of libpng - can't continue */
+PNG_EXPORTA(104, void, png_err, (png_structp png_ptr), PNG_NORETURN);
+#endif
+
+/* Non-fatal error in libpng. Can continue, but may have a problem. */
+PNG_EXPORT(105, void, png_warning, (png_structp png_ptr,
+ png_const_charp warning_message));
+
+/* Non-fatal error in libpng, chunk name is prepended to message. */
+PNG_EXPORT(106, void, png_chunk_warning, (png_structp png_ptr,
+ png_const_charp warning_message));
+
+#ifdef PNG_BENIGN_ERRORS_SUPPORTED
+/* Benign error in libpng. Can continue, but may have a problem.
+ * User can choose whether to handle as a fatal error or as a warning. */
+# undef png_benign_error
+PNG_EXPORT(107, void, png_benign_error, (png_structp png_ptr,
+ png_const_charp warning_message));
+
+/* Same, chunk name is prepended to message. */
+# undef png_chunk_benign_error
+PNG_EXPORT(108, void, png_chunk_benign_error, (png_structp png_ptr,
+ png_const_charp warning_message));
+
+PNG_EXPORT(109, void, png_set_benign_errors,
+ (png_structp png_ptr, int allowed));
+#else
+# ifdef PNG_ALLOW_BENIGN_ERRORS
+# define png_benign_error png_warning
+# define png_chunk_benign_error png_chunk_warning
+# else
+# define png_benign_error png_error
+# define png_chunk_benign_error png_chunk_error
+# endif
+#endif
+
+/* The png_set_<chunk> functions are for storing values in the png_info_struct.
+ * Similarly, the png_get_<chunk> calls are used to read values from the
+ * png_info_struct, either storing the parameters in the passed variables, or
+ * setting pointers into the png_info_struct where the data is stored. The
+ * png_get_<chunk> functions return a non-zero value if the data was available
+ * in info_ptr, or return zero and do not change any of the parameters if the
+ * data was not available.
+ *
+ * These functions should be used instead of directly accessing png_info
+ * to avoid problems with future changes in the size and internal layout of
+ * png_info_struct.
+ */
+/* Returns "flag" if chunk data is valid in info_ptr. */
+PNG_EXPORT(110, png_uint_32, png_get_valid,
+ (png_const_structp png_ptr, png_const_infop info_ptr,
+ png_uint_32 flag));
+
+/* Returns number of bytes needed to hold a transformed row. */
+PNG_EXPORT(111, png_size_t, png_get_rowbytes, (png_const_structp png_ptr,
+ png_const_infop info_ptr));
+
+#ifdef PNG_INFO_IMAGE_SUPPORTED
+/* Returns row_pointers, which is an array of pointers to scanlines that was
+ * returned from png_read_png().
+ */
+PNG_EXPORT(112, png_bytepp, png_get_rows,
+ (png_const_structp png_ptr, png_const_infop info_ptr));
+/* Set row_pointers, which is an array of pointers to scanlines for use
+ * by png_write_png().
+ */
+PNG_EXPORT(113, void, png_set_rows, (png_structp png_ptr,
+ png_infop info_ptr, png_bytepp row_pointers));
+#endif
+
+/* Returns number of color channels in image. */
+PNG_EXPORT(114, png_byte, png_get_channels,
+ (png_const_structp png_ptr, png_const_infop info_ptr));
+
+#ifdef PNG_EASY_ACCESS_SUPPORTED
+/* Returns image width in pixels. */
+PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structp png_ptr,
+ png_const_infop info_ptr));
+
+/* Returns image height in pixels. */
+PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structp png_ptr,
+ png_const_infop info_ptr));
+
+/* Returns image bit_depth. */
+PNG_EXPORT(117, png_byte, png_get_bit_depth,
+ (png_const_structp png_ptr, png_const_infop info_ptr));
+
+/* Returns image color_type. */
+PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structp png_ptr,
+ png_const_infop info_ptr));
+
+/* Returns image filter_type. */
+PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structp png_ptr,
+ png_const_infop info_ptr));
+
+/* Returns image interlace_type. */
+PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structp png_ptr,
+ png_const_infop info_ptr));
+
+/* Returns image compression_type. */
+PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structp png_ptr,
+ png_const_infop info_ptr));
+
+/* Returns image resolution in pixels per meter, from pHYs chunk data. */
+PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter,
+ (png_const_structp png_ptr, png_const_infop info_ptr));
+PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter,
+ (png_const_structp png_ptr, png_const_infop info_ptr));
+PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter,
+ (png_const_structp png_ptr, png_const_infop info_ptr));
+
+/* Returns pixel aspect ratio, computed from pHYs chunk data. */
+PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio,
+ (png_const_structp png_ptr, png_const_infop info_ptr));
+PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed,
+ (png_const_structp png_ptr, png_const_infop info_ptr));
+
+/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */
+PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels,
+ (png_const_structp png_ptr, png_const_infop info_ptr));
+PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels,
+ (png_const_structp png_ptr, png_const_infop info_ptr));
+PNG_EXPORT(128, png_int_32, png_get_x_offset_microns,
+ (png_const_structp png_ptr, png_const_infop info_ptr));
+PNG_EXPORT(129, png_int_32, png_get_y_offset_microns,
+ (png_const_structp png_ptr, png_const_infop info_ptr));
+
+#endif /* PNG_EASY_ACCESS_SUPPORTED */
+
+/* Returns pointer to signature string read from PNG header */
+PNG_EXPORT(130, png_const_bytep, png_get_signature,
+ (png_const_structp png_ptr, png_infop info_ptr));
+
+#ifdef PNG_bKGD_SUPPORTED
+PNG_EXPORT(131, png_uint_32, png_get_bKGD,
+ (png_const_structp png_ptr, png_infop info_ptr,
+ png_color_16p *background));
+#endif
+
+#ifdef PNG_bKGD_SUPPORTED
+PNG_EXPORT(132, void, png_set_bKGD, (png_structp png_ptr, png_infop info_ptr,
+ png_const_color_16p background));
+#endif
+
+#ifdef PNG_cHRM_SUPPORTED
+PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structp png_ptr,
+ png_const_infop info_ptr, double *white_x, double *white_y, double *red_x,
+ double *red_y, double *green_x, double *green_y, double *blue_x,
+ double *blue_y));
+#ifdef PNG_FIXED_POINT_SUPPORTED /* Otherwise not implemented */
+PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed,
+ (png_const_structp png_ptr,
+ png_const_infop info_ptr, png_fixed_point *int_white_x,
+ png_fixed_point *int_white_y, png_fixed_point *int_red_x,
+ png_fixed_point *int_red_y, png_fixed_point *int_green_x,
+ png_fixed_point *int_green_y, png_fixed_point *int_blue_x,
+ png_fixed_point *int_blue_y));
+#endif
+#endif
+
+#ifdef PNG_cHRM_SUPPORTED
+PNG_FP_EXPORT(135, void, png_set_cHRM,
+ (png_structp png_ptr, png_infop info_ptr,
+ double white_x, double white_y, double red_x, double red_y, double green_x,
+ double green_y, double blue_x, double blue_y));
+PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_structp png_ptr,
+ png_infop info_ptr, png_fixed_point int_white_x,
+ png_fixed_point int_white_y, png_fixed_point int_red_x,
+ png_fixed_point int_red_y, png_fixed_point int_green_x,
+ png_fixed_point int_green_y, png_fixed_point int_blue_x,
+ png_fixed_point int_blue_y));
+#endif
+
+#ifdef PNG_gAMA_SUPPORTED
+PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA,
+ (png_const_structp png_ptr, png_const_infop info_ptr,
+ double *file_gamma));
+PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed,
+ (png_const_structp png_ptr, png_const_infop info_ptr,
+ png_fixed_point *int_file_gamma));
+#endif
+
+#ifdef PNG_gAMA_SUPPORTED
+PNG_FP_EXPORT(139, void, png_set_gAMA, (png_structp png_ptr,
+ png_infop info_ptr, double file_gamma));
+PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_structp png_ptr,
+ png_infop info_ptr, png_fixed_point int_file_gamma));
+#endif
+
+#ifdef PNG_hIST_SUPPORTED
+PNG_EXPORT(141, png_uint_32, png_get_hIST,
+ (png_const_structp png_ptr, png_const_infop info_ptr,
+ png_uint_16p *hist));
+#endif
+
+#ifdef PNG_hIST_SUPPORTED
+PNG_EXPORT(142, void, png_set_hIST, (png_structp png_ptr,
+ png_infop info_ptr, png_const_uint_16p hist));
+#endif
+
+PNG_EXPORT(143, png_uint_32, png_get_IHDR,
+ (png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 *width, png_uint_32 *height, int *bit_depth, int *color_type,
+ int *interlace_method, int *compression_method, int *filter_method));
+
+PNG_EXPORT(144, void, png_set_IHDR,
+ (png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 width, png_uint_32 height, int bit_depth, int color_type,
+ int interlace_method, int compression_method, int filter_method));
+
+#ifdef PNG_oFFs_SUPPORTED
+PNG_EXPORT(145, png_uint_32, png_get_oFFs,
+ (png_const_structp png_ptr, png_const_infop info_ptr,
+ png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type));
+#endif
+
+#ifdef PNG_oFFs_SUPPORTED
+PNG_EXPORT(146, void, png_set_oFFs,
+ (png_structp png_ptr, png_infop info_ptr,
+ png_int_32 offset_x, png_int_32 offset_y, int unit_type));
+#endif
+
+#ifdef PNG_pCAL_SUPPORTED
+PNG_EXPORT(147, png_uint_32, png_get_pCAL,
+ (png_const_structp png_ptr, png_const_infop info_ptr,
+ png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type,
+ int *nparams,
+ png_charp *units, png_charpp *params));
+#endif
+
+#ifdef PNG_pCAL_SUPPORTED
+PNG_EXPORT(148, void, png_set_pCAL, (png_structp png_ptr,
+ png_infop info_ptr,
+ png_const_charp purpose, png_int_32 X0, png_int_32 X1, int type,
+ int nparams, png_const_charp units, png_charpp params));
+#endif
+
+#ifdef PNG_pHYs_SUPPORTED
+PNG_EXPORT(149, png_uint_32, png_get_pHYs,
+ (png_const_structp png_ptr, png_const_infop info_ptr,
+ png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type));
+#endif
+
+#ifdef PNG_pHYs_SUPPORTED
+PNG_EXPORT(150, void, png_set_pHYs,
+ (png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 res_x, png_uint_32 res_y, int unit_type));
+#endif
+
+PNG_EXPORT(151, png_uint_32, png_get_PLTE,
+ (png_const_structp png_ptr, png_const_infop info_ptr,
+ png_colorp *palette, int *num_palette));
+
+PNG_EXPORT(152, void, png_set_PLTE,
+ (png_structp png_ptr, png_infop info_ptr,
+ png_const_colorp palette, int num_palette));
+
+#ifdef PNG_sBIT_SUPPORTED
+PNG_EXPORT(153, png_uint_32, png_get_sBIT,
+ (png_const_structp png_ptr, png_infop info_ptr,
+ png_color_8p *sig_bit));
+#endif
+
+#ifdef PNG_sBIT_SUPPORTED
+PNG_EXPORT(154, void, png_set_sBIT,
+ (png_structp png_ptr, png_infop info_ptr, png_const_color_8p sig_bit));
+#endif
+
+#ifdef PNG_sRGB_SUPPORTED
+PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structp png_ptr,
+ png_const_infop info_ptr, int *file_srgb_intent));
+#endif
+
+#ifdef PNG_sRGB_SUPPORTED
+PNG_EXPORT(156, void, png_set_sRGB,
+ (png_structp png_ptr, png_infop info_ptr, int srgb_intent));
+PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_structp png_ptr,
+ png_infop info_ptr, int srgb_intent));
+#endif
+
+#ifdef PNG_iCCP_SUPPORTED
+PNG_EXPORT(158, png_uint_32, png_get_iCCP,
+ (png_const_structp png_ptr, png_const_infop info_ptr,
+ png_charpp name, int *compression_type, png_bytepp profile,
+ png_uint_32 *proflen));
+#endif
+
+#ifdef PNG_iCCP_SUPPORTED
+PNG_EXPORT(159, void, png_set_iCCP,
+ (png_structp png_ptr, png_infop info_ptr,
+ png_const_charp name, int compression_type, png_const_bytep profile,
+ png_uint_32 proflen));
+#endif
+
+#ifdef PNG_sPLT_SUPPORTED
+PNG_EXPORT(160, png_uint_32, png_get_sPLT,
+ (png_const_structp png_ptr, png_const_infop info_ptr,
+ png_sPLT_tpp entries));
+#endif
+
+#ifdef PNG_sPLT_SUPPORTED
+PNG_EXPORT(161, void, png_set_sPLT,
+ (png_structp png_ptr, png_infop info_ptr,
+ png_const_sPLT_tp entries, int nentries));
+#endif
+
+#ifdef PNG_TEXT_SUPPORTED
+/* png_get_text also returns the number of text chunks in *num_text */
+PNG_EXPORT(162, png_uint_32, png_get_text,
+ (png_const_structp png_ptr, png_const_infop info_ptr,
+ png_textp *text_ptr, int *num_text));
+#endif
+
+/* Note while png_set_text() will accept a structure whose text,
+ * language, and translated keywords are NULL pointers, the structure
+ * returned by png_get_text will always contain regular
+ * zero-terminated C strings. They might be empty strings but
+ * they will never be NULL pointers.
+ */
+
+#ifdef PNG_TEXT_SUPPORTED
+PNG_EXPORT(163, void, png_set_text,
+ (png_structp png_ptr, png_infop info_ptr,
+ png_const_textp text_ptr, int num_text));
+#endif
+
+#ifdef PNG_tIME_SUPPORTED
+PNG_EXPORT(164, png_uint_32, png_get_tIME,
+ (png_const_structp png_ptr, png_infop info_ptr, png_timep *mod_time));
+#endif
+
+#ifdef PNG_tIME_SUPPORTED
+PNG_EXPORT(165, void, png_set_tIME,
+ (png_structp png_ptr, png_infop info_ptr, png_const_timep mod_time));
+#endif
+
+#ifdef PNG_tRNS_SUPPORTED
+PNG_EXPORT(166, png_uint_32, png_get_tRNS,
+ (png_const_structp png_ptr, png_infop info_ptr,
+ png_bytep *trans_alpha, int *num_trans, png_color_16p *trans_color));
+#endif
+
+#ifdef PNG_tRNS_SUPPORTED
+PNG_EXPORT(167, void, png_set_tRNS,
+ (png_structp png_ptr, png_infop info_ptr,
+ png_const_bytep trans_alpha, int num_trans,
+ png_const_color_16p trans_color));
+#endif
+
+#ifdef PNG_sCAL_SUPPORTED
+PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL,
+ (png_const_structp png_ptr, png_const_infop info_ptr,
+ int *unit, double *width, double *height));
+#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+/* NOTE: this API is currently implemented using floating point arithmetic,
+ * consequently it can only be used on systems with floating point support.
+ * In any case the range of values supported by png_fixed_point is small and it
+ * is highly recommended that png_get_sCAL_s be used instead.
+ */
+PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed,
+ (png_structp png_ptr, png_const_infop info_ptr, int *unit,
+ png_fixed_point *width,
+ png_fixed_point *height));
+#endif
+PNG_EXPORT(169, png_uint_32, png_get_sCAL_s,
+ (png_const_structp png_ptr, png_const_infop info_ptr,
+ int *unit, png_charpp swidth, png_charpp sheight));
+
+PNG_FP_EXPORT(170, void, png_set_sCAL,
+ (png_structp png_ptr, png_infop info_ptr,
+ int unit, double width, double height));
+PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_structp png_ptr,
+ png_infop info_ptr, int unit, png_fixed_point width,
+ png_fixed_point height));
+PNG_EXPORT(171, void, png_set_sCAL_s,
+ (png_structp png_ptr, png_infop info_ptr,
+ int unit, png_const_charp swidth, png_const_charp sheight));
+#endif /* PNG_sCAL_SUPPORTED */
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+/* Provide a list of chunks and how they are to be handled, if the built-in
+ handling or default unknown chunk handling is not desired. Any chunks not
+ listed will be handled in the default manner. The IHDR and IEND chunks
+ must not be listed.
+ keep = 0: follow default behaviour
+ = 1: do not keep
+ = 2: keep only if safe-to-copy
+ = 3: keep even if unsafe-to-copy
+*/
+PNG_EXPORT(172, void, png_set_keep_unknown_chunks,
+ (png_structp png_ptr, int keep,
+ png_const_bytep chunk_list, int num_chunks));
+PNG_EXPORT(173, int, png_handle_as_unknown, (png_structp png_ptr,
+ png_const_bytep chunk_name));
+#endif
+#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
+PNG_EXPORT(174, void, png_set_unknown_chunks, (png_structp png_ptr,
+ png_infop info_ptr, png_const_unknown_chunkp unknowns,
+ int num_unknowns));
+PNG_EXPORT(175, void, png_set_unknown_chunk_location,
+ (png_structp png_ptr, png_infop info_ptr, int chunk, int location));
+PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structp png_ptr,
+ png_const_infop info_ptr, png_unknown_chunkpp entries));
+#endif
+
+/* Png_free_data() will turn off the "valid" flag for anything it frees.
+ * If you need to turn it off for a chunk that your application has freed,
+ * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK);
+ */
+PNG_EXPORT(177, void, png_set_invalid,
+ (png_structp png_ptr, png_infop info_ptr, int mask));
+
+#ifdef PNG_INFO_IMAGE_SUPPORTED
+/* The "params" pointer is currently not used and is for future expansion. */
+PNG_EXPORT(178, void, png_read_png, (png_structp png_ptr, png_infop info_ptr,
+ int transforms, png_voidp params));
+PNG_EXPORT(179, void, png_write_png, (png_structp png_ptr, png_infop info_ptr,
+ int transforms, png_voidp params));
+#endif
+
+PNG_EXPORT(180, png_const_charp, png_get_copyright,
+ (png_const_structp png_ptr));
+PNG_EXPORT(181, png_const_charp, png_get_header_ver,
+ (png_const_structp png_ptr));
+PNG_EXPORT(182, png_const_charp, png_get_header_version,
+ (png_const_structp png_ptr));
+PNG_EXPORT(183, png_const_charp, png_get_libpng_ver,
+ (png_const_structp png_ptr));
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structp png_ptr,
+ png_uint_32 mng_features_permitted));
+#endif
+
+/* For use in png_set_keep_unknown, added to version 1.2.6 */
+#define PNG_HANDLE_CHUNK_AS_DEFAULT 0
+#define PNG_HANDLE_CHUNK_NEVER 1
+#define PNG_HANDLE_CHUNK_IF_SAFE 2
+#define PNG_HANDLE_CHUNK_ALWAYS 3
+
+/* Strip the prepended error numbers ("#nnn ") from error and warning
+ * messages before passing them to the error or warning handler.
+ */
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+PNG_EXPORT(185, void, png_set_strip_error_numbers,
+ (png_structp png_ptr,
+ png_uint_32 strip_mode));
+#endif
+
+/* Added in libpng-1.2.6 */
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+PNG_EXPORT(186, void, png_set_user_limits, (png_structp png_ptr,
+ png_uint_32 user_width_max, png_uint_32 user_height_max));
+PNG_EXPORT(187, png_uint_32, png_get_user_width_max,
+ (png_const_structp png_ptr));
+PNG_EXPORT(188, png_uint_32, png_get_user_height_max,
+ (png_const_structp png_ptr));
+/* Added in libpng-1.4.0 */
+PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structp png_ptr,
+ png_uint_32 user_chunk_cache_max));
+PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max,
+ (png_const_structp png_ptr));
+/* Added in libpng-1.4.1 */
+PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structp png_ptr,
+ png_alloc_size_t user_chunk_cache_max));
+PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max,
+ (png_const_structp png_ptr));
+#endif
+
+#if defined(PNG_INCH_CONVERSIONS_SUPPORTED)
+PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch,
+ (png_const_structp png_ptr, png_const_infop info_ptr));
+
+PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch,
+ (png_const_structp png_ptr, png_const_infop info_ptr));
+
+PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch,
+ (png_const_structp png_ptr, png_const_infop info_ptr));
+
+PNG_FP_EXPORT(196, float, png_get_x_offset_inches,
+ (png_const_structp png_ptr, png_const_infop info_ptr));
+#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */
+PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed,
+ (png_structp png_ptr, png_const_infop info_ptr));
+#endif
+
+PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structp png_ptr,
+ png_const_infop info_ptr));
+#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */
+PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed,
+ (png_structp png_ptr, png_const_infop info_ptr));
+#endif
+
+# ifdef PNG_pHYs_SUPPORTED
+PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structp png_ptr,
+ png_const_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y,
+ int *unit_type));
+# endif /* PNG_pHYs_SUPPORTED */
+#endif /* PNG_INCH_CONVERSIONS_SUPPORTED */
+
+/* Added in libpng-1.4.0 */
+#ifdef PNG_IO_STATE_SUPPORTED
+PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_structp png_ptr));
+
+PNG_EXPORTA(200, png_const_bytep, png_get_io_chunk_name,
+ (png_structp png_ptr), PNG_DEPRECATED);
+PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type,
+ (png_const_structp png_ptr));
+
+/* The flags returned by png_get_io_state() are the following: */
+# define PNG_IO_NONE 0x0000 /* no I/O at this moment */
+# define PNG_IO_READING 0x0001 /* currently reading */
+# define PNG_IO_WRITING 0x0002 /* currently writing */
+# define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */
+# define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */
+# define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */
+# define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */
+# define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */
+# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */
+#endif /* ?PNG_IO_STATE_SUPPORTED */
+
+/* Interlace support. The following macros are always defined so that if
+ * libpng interlace handling is turned off the macros may be used to handle
+ * interlaced images within the application.
+ */
+#define PNG_INTERLACE_ADAM7_PASSES 7
+
+/* Two macros to return the first row and first column of the original,
+ * full, image which appears in a given pass. 'pass' is in the range 0
+ * to 6 and the result is in the range 0 to 7.
+ */
+#define PNG_PASS_START_ROW(pass) (((1U&~(pass))<<(3-((pass)>>1)))&7)
+#define PNG_PASS_START_COL(pass) (((1U& (pass))<<(3-(((pass)+1)>>1)))&7)
+
+/* Two macros to help evaluate the number of rows or columns in each
+ * pass. This is expressed as a shift - effectively log2 of the number or
+ * rows or columns in each 8x8 tile of the original image.
+ */
+#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3)
+#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3)
+
+/* Hence two macros to determine the number of rows or columns in a given
+ * pass of an image given its height or width. In fact these macros may
+ * return non-zero even though the sub-image is empty, because the other
+ * dimension may be empty for a small image.
+ */
+#define PNG_PASS_ROWS(height, pass) (((height)+(((1<<PNG_PASS_ROW_SHIFT(pass))\
+ -1)-PNG_PASS_START_ROW(pass)))>>PNG_PASS_ROW_SHIFT(pass))
+#define PNG_PASS_COLS(width, pass) (((width)+(((1<<PNG_PASS_COL_SHIFT(pass))\
+ -1)-PNG_PASS_START_COL(pass)))>>PNG_PASS_COL_SHIFT(pass))
+
+/* For the reader row callbacks (both progressive and sequential) it is
+ * necessary to find the row in the output image given a row in an interlaced
+ * image, so two more macros:
+ */
+#define PNG_ROW_FROM_PASS_ROW(yIn, pass) \
+ (((yIn)<<PNG_PASS_ROW_SHIFT(pass))+PNG_PASS_START_ROW(pass))
+#define PNG_COL_FROM_PASS_COL(xIn, pass) \
+ (((xIn)<<PNG_PASS_COL_SHIFT(pass))+PNG_PASS_START_COL(pass))
+
+/* Two macros which return a boolean (0 or 1) saying whether the given row
+ * or column is in a particular pass. These use a common utility macro that
+ * returns a mask for a given pass - the offset 'off' selects the row or
+ * column version. The mask has the appropriate bit set for each column in
+ * the tile.
+ */
+#define PNG_PASS_MASK(pass,off) ( \
+ ((0x110145AFU>>(((7-(off))-(pass))<<2)) & 0xFU) | \
+ ((0x01145AF0U>>(((7-(off))-(pass))<<2)) & 0xF0U))
+
+#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \
+ ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1)
+#define PNG_COL_IN_INTERLACE_PASS(x, pass) \
+ ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1)
+
+#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED
+/* With these routines we avoid an integer divide, which will be slower on
+ * most machines. However, it does take more operations than the corresponding
+ * divide method, so it may be slower on a few RISC systems. There are two
+ * shifts (by 8 or 16 bits) and an addition, versus a single integer divide.
+ *
+ * Note that the rounding factors are NOT supposed to be the same! 128 and
+ * 32768 are correct for the NODIV code; 127 and 32767 are correct for the
+ * standard method.
+ *
+ * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ]
+ */
+
+ /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */
+
+# define png_composite(composite, fg, alpha, bg) \
+ { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \
+ * (png_uint_16)(alpha) \
+ + (png_uint_16)(bg)*(png_uint_16)(255 \
+ - (png_uint_16)(alpha)) + (png_uint_16)128); \
+ (composite) = (png_byte)((temp + (temp >> 8)) >> 8); }
+
+# define png_composite_16(composite, fg, alpha, bg) \
+ { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \
+ * (png_uint_32)(alpha) \
+ + (png_uint_32)(bg)*(png_uint_32)(65535L \
+ - (png_uint_32)(alpha)) + (png_uint_32)32768L); \
+ (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); }
+
+#else /* Standard method using integer division */
+
+# define png_composite(composite, fg, alpha, bg) \
+ (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \
+ (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \
+ (png_uint_16)127) / 255)
+
+# define png_composite_16(composite, fg, alpha, bg) \
+ (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \
+ (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) + \
+ (png_uint_32)32767) / (png_uint_32)65535L)
+#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */
+
+#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED
+PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf));
+PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf));
+PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf));
+#endif
+
+PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_structp png_ptr,
+ png_const_bytep buf));
+/* No png_get_int_16 -- may be added if there's a real need for it. */
+
+/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */
+#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED
+PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i));
+#endif
+#ifdef PNG_SAVE_INT_32_SUPPORTED
+PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i));
+#endif
+
+/* Place a 16-bit number into a buffer in PNG byte order.
+ * The parameter is declared unsigned int, not png_uint_16,
+ * just to avoid potential problems on pre-ANSI C compilers.
+ */
+#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED
+PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i));
+/* No png_save_int_16 -- may be added if there's a real need for it. */
+#endif
+
+#ifdef PNG_USE_READ_MACROS
+/* Inline macros to do direct reads of bytes from the input buffer.
+ * The png_get_int_32() routine assumes we are using two's complement
+ * format for negative values, which is almost certainly true.
+ */
+# define png_get_uint_32(buf) \
+ (((png_uint_32)(*(buf)) << 24) + \
+ ((png_uint_32)(*((buf) + 1)) << 16) + \
+ ((png_uint_32)(*((buf) + 2)) << 8) + \
+ ((png_uint_32)(*((buf) + 3))))
+
+ /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the
+ * function) incorrectly returned a value of type png_uint_32.
+ */
+# define png_get_uint_16(buf) \
+ ((png_uint_16) \
+ (((unsigned int)(*(buf)) << 8) + \
+ ((unsigned int)(*((buf) + 1)))))
+
+# define png_get_int_32(buf) \
+ ((png_int_32)((*(buf) & 0x80) \
+ ? -((png_int_32)((png_get_uint_32(buf) ^ 0xffffffffL) + 1)) \
+ : (png_int_32)png_get_uint_32(buf)))
+#endif
+
+/* Maintainer: Put new public prototypes here ^, in libpng.3, and project
+ * defs
+ */
+
+/* The last ordinal number (this is the *last* one already used; the next
+ * one to use is one more than this.) Maintainer, remember to add an entry to
+ * scripts/symbols.def as well.
+ */
+#ifdef PNG_EXPORT_LAST_ORDINAL
+ PNG_EXPORT_LAST_ORDINAL(221);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PNG_VERSION_INFO_ONLY */
+/* Do not put anything past this line */
+#endif /* PNG_H */
diff --git a/libpng/pngconf.h b/libpng/pngconf.h
new file mode 100644
index 0000000..c82fa58
--- /dev/null
+++ b/libpng/pngconf.h
@@ -0,0 +1,649 @@
+
+/* pngconf.h - machine configurable file for libpng
+ *
+ * libpng version 1.5.2 - March 31, 2011
+ *
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ */
+
+/* Any machine specific code is near the front of this file, so if you
+ * are configuring libpng for a machine, you may want to read the section
+ * starting here down to where it starts to typedef png_color, png_text,
+ * and png_info.
+ */
+
+#ifndef PNGCONF_H
+#define PNGCONF_H
+
+#ifndef PNG_BUILDING_SYMBOL_TABLE
+/* PNG_NO_LIMITS_H may be used to turn off the use of the standard C
+ * definition file for machine specific limits, this may impact the
+ * correctness of the definitons below (see uses of INT_MAX).
+ */
+# ifndef PNG_NO_LIMITS_H
+# include <limits.h>
+# endif
+
+/* For the memory copy APIs (i.e. the standard definitions of these),
+ * because this file defines png_memcpy and so on the base APIs must
+ * be defined here.
+ */
+# ifdef BSD
+# include <strings.h>
+# else
+# include <string.h>
+# endif
+
+/* For png_FILE_p - this provides the standard definition of a
+ * FILE
+ */
+# ifdef PNG_STDIO_SUPPORTED
+# include <stdio.h>
+# endif
+#endif
+
+/* This controls optimization of the reading of 16 and 32 bit values
+ * from PNG files. It can be set on a per-app-file basis - it
+ * just changes whether a macro is used to the function is called.
+ * The library builder sets the default, if read functions are not
+ * built into the library the macro implementation is forced on.
+ */
+#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED
+# define PNG_USE_READ_MACROS
+#endif
+#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS)
+# if PNG_DEFAULT_READ_MACROS
+# define PNG_USE_READ_MACROS
+# endif
+#endif
+
+/* COMPILER SPECIFIC OPTIONS.
+ *
+ * These options are provided so that a variety of difficult compilers
+ * can be used. Some are fixed at build time (e.g. PNG_API_RULE
+ * below) but still have compiler specific implementations, others
+ * may be changed on a per-file basis when compiling against libpng.
+ */
+
+/* The PNGARG macro protects us against machines that don't have function
+ * prototypes (ie K&R style headers). If your compiler does not handle
+ * function prototypes, define this macro and use the included ansi2knr.
+ * I've always been able to use _NO_PROTO as the indicator, but you may
+ * need to drag the empty declaration out in front of here, or change the
+ * ifdef to suit your own needs.
+ */
+#ifndef PNGARG
+
+# ifdef OF /* zlib prototype munger */
+# define PNGARG(arglist) OF(arglist)
+# else
+
+# ifdef _NO_PROTO
+# define PNGARG(arglist) ()
+# else
+# define PNGARG(arglist) arglist
+# endif /* _NO_PROTO */
+
+# endif /* OF */
+
+#endif /* PNGARG */
+
+/* Function calling conventions.
+ * =============================
+ * Normally it is not necessary to specify to the compiler how to call
+ * a function - it just does it - however on x86 systems derived from
+ * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems
+ * and some others) there are multiple ways to call a function and the
+ * default can be changed on the compiler command line. For this reason
+ * libpng specifies the calling convention of every exported function and
+ * every function called via a user supplied function pointer. This is
+ * done in this file by defining the following macros:
+ *
+ * PNGAPI Calling convention for exported functions.
+ * PNGCBAPI Calling convention for user provided (callback) functions.
+ * PNGCAPI Calling convention used by the ANSI-C library (required
+ * for longjmp callbacks and sometimes used internally to
+ * specify the calling convention for zlib).
+ *
+ * These macros should never be overridden. If it is necessary to
+ * change calling convention in a private build this can be done
+ * by setting PNG_API_RULE (which defaults to 0) to one of the values
+ * below to select the correct 'API' variants.
+ *
+ * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout.
+ * This is correct in every known environment.
+ * PNG_API_RULE=1 Use the operating system convention for PNGAPI and
+ * the 'C' calling convention (from PNGCAPI) for
+ * callbacks (PNGCBAPI). This is no longer required
+ * in any known environment - if it has to be used
+ * please post an explanation of the problem to the
+ * libpng mailing list.
+ *
+ * These cases only differ if the operating system does not use the C
+ * calling convention, at present this just means the above cases
+ * (x86 DOS/Windows sytems) and, even then, this does not apply to
+ * Cygwin running on those systems.
+ *
+ * Note that the value must be defined in pnglibconf.h so that what
+ * the application uses to call the library matches the conventions
+ * set when building the library.
+ */
+
+/* Symbol export
+ * =============
+ * When building a shared library it is almost always necessary to tell
+ * the compiler which symbols to export. The png.h macro 'PNG_EXPORT'
+ * is used to mark the symbols. On some systems these symbols can be
+ * extracted at link time and need no special processing by the compiler,
+ * on other systems the symbols are flagged by the compiler and just
+ * the declaration requires a special tag applied (unfortunately) in a
+ * compiler dependent way. Some systems can do either.
+ *
+ * A small number of older systems also require a symbol from a DLL to
+ * be flagged to the program that calls it. This is a problem because
+ * we do not know in the header file included by application code that
+ * the symbol will come from a shared library, as opposed to a statically
+ * linked one. For this reason the application must tell us by setting
+ * the magic flag PNG_USE_DLL to turn on the special processing before
+ * it includes png.h.
+ *
+ * Four additional macros are used to make this happen:
+ *
+ * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from
+ * the build or imported if PNG_USE_DLL is set - compiler
+ * and system specific.
+ *
+ * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to
+ * 'type', compiler specific.
+ *
+ * PNG_DLL_EXPORT Set to the magic to use during a libpng build to
+ * make a symbol exported from the DLL.
+ *
+ * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come
+ * from a DLL - used to define PNG_IMPEXP when
+ * PNG_USE_DLL is set.
+ */
+
+/* System specific discovery.
+ * ==========================
+ * This code is used at build time to find PNG_IMPEXP, the API settings
+ * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL
+ * import processing is possible. On Windows/x86 systems it also sets
+ * compiler-specific macros to the values required to change the calling
+ * conventions of the various functions.
+ */
+#if ( defined(_Windows) || defined(_WINDOWS) || defined(WIN32) ||\
+ defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) ) &&\
+ ( defined(_X86_) || defined(_X64_) || defined(_M_IX86) ||\
+ defined(_M_X64) || defined(_M_IA64) )
+ /* Windows system (DOS doesn't support DLLs) running on x86/x64. Includes
+ * builds under Cygwin or MinGW. Also includes Watcom builds but these need
+ * special treatment because they are not compatible with GCC or Visual C
+ * because of different calling conventions.
+ */
+# if PNG_API_RULE == 2
+ /* If this line results in an error, either because __watcall is not
+ * understood or because of a redefine just below you cannot use *this*
+ * build of the library with the compiler you are using. *This* build was
+ * build using Watcom and applications must also be built using Watcom!
+ */
+# define PNGCAPI __watcall
+# endif
+
+# if defined(__GNUC__) || (defined (_MSC_VER) && (_MSC_VER >= 800))
+# define PNGCAPI __cdecl
+# if PNG_API_RULE == 1
+# define PNGAPI __stdcall
+# endif
+# else
+ /* An older compiler, or one not detected (erroneously) above,
+ * if necessary override on the command line to get the correct
+ * variants for the compiler.
+ */
+# ifndef PNGCAPI
+# define PNGCAPI _cdecl
+# endif
+# if PNG_API_RULE == 1 && !defined(PNGAPI)
+# define PNGAPI _stdcall
+# endif
+# endif /* compiler/api */
+ /* NOTE: PNGCBAPI always defaults to PNGCAPI. */
+
+# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD)
+ ERROR: PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed
+# endif
+
+# if (defined(_MSC_VER) && _MSC_VER < 800) ||\
+ (defined(__BORLANDC__) && __BORLANDC__ < 0x500)
+ /* older Borland and MSC
+ * compilers used '__export' and required this to be after
+ * the type.
+ */
+# ifndef PNG_EXPORT_TYPE
+# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP
+# endif
+# define PNG_DLL_EXPORT __export
+# else /* newer compiler */
+# define PNG_DLL_EXPORT __declspec(dllexport)
+# ifndef PNG_DLL_IMPORT
+# define PNG_DLL_IMPORT __declspec(dllimport)
+# endif
+# endif /* compiler */
+
+#else /* !Windows/x86 */
+# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__)
+# define PNGAPI _System
+# else /* !Windows/x86 && !OS/2 */
+ /* Use the defaults, or define PNG*API on the command line (but
+ * this will have to be done for every compile!)
+ */
+# endif /* other system, !OS/2 */
+#endif /* !Windows/x86 */
+
+/* Now do all the defaulting . */
+#ifndef PNGCAPI
+# define PNGCAPI
+#endif
+#ifndef PNGCBAPI
+# define PNGCBAPI PNGCAPI
+#endif
+#ifndef PNGAPI
+# define PNGAPI PNGCAPI
+#endif
+
+/* The default for PNG_IMPEXP depends on whether the library is
+ * being built or used.
+ */
+#ifndef PNG_IMPEXP
+# ifdef PNGLIB_BUILD
+ /* Building the library */
+# if (defined(DLL_EXPORT)/*from libtool*/ ||\
+ defined(_WINDLL) || defined(_DLL) || defined(__DLL__) ||\
+ defined(_USRDLL) ||\
+ defined(PNG_BUILD_DLL)) && defined(PNG_DLL_EXPORT)
+ /* Building a DLL. */
+# define PNG_IMPEXP PNG_DLL_EXPORT
+# endif /* DLL */
+# else
+ /* Using the library */
+# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT)
+ /* This forces use of a DLL, disallowing static linking */
+# define PNG_IMPEXP PNG_DLL_IMPORT
+# endif
+# endif
+
+# ifndef PNG_IMPEXP
+# define PNG_IMPEXP
+# endif
+#endif
+
+/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat
+ * 'attributes' as a storage class - the attributes go at the start of the
+ * function definition, and attributes are always appended regardless of the
+ * compiler. This considerably simplifies these macros but may cause problems
+ * if any compilers both need function attributes and fail to handle them as
+ * a storage class (this is unlikely.)
+ */
+#ifndef PNG_FUNCTION
+# define PNG_FUNCTION(type, name, args, attributes) attributes type name args
+#endif
+
+#ifndef PNG_EXPORT_TYPE
+# define PNG_EXPORT_TYPE(type) PNG_IMPEXP type
+#endif
+
+ /* The ordinal value is only relevant when preprocessing png.h for symbol
+ * table entries, so we discard it here. See the .dfn files in the
+ * scripts directory.
+ */
+#ifndef PNG_EXPORTA
+
+# define PNG_EXPORTA(ordinal, type, name, args, attributes)\
+ PNG_FUNCTION(PNG_EXPORT_TYPE(type),(PNGAPI name),PNGARG(args), \
+ extern attributes)
+#endif
+
+/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument,
+ * so make something non-empty to satisfy the requirement:
+ */
+#define PNG_EMPTY /*empty list*/
+
+#define PNG_EXPORT(ordinal, type, name, args)\
+ PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY)
+
+/* Use PNG_REMOVED to comment out a removed interface. */
+#ifndef PNG_REMOVED
+# define PNG_REMOVED(ordinal, type, name, args, attributes)
+#endif
+
+#ifndef PNG_CALLBACK
+# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args)
+#endif
+
+/* Support for compiler specific function attributes. These are used
+ * so that where compiler support is available incorrect use of API
+ * functions in png.h will generate compiler warnings.
+ *
+ * Added at libpng-1.2.41.
+ */
+
+#ifndef PNG_NO_PEDANTIC_WARNINGS
+# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED
+# define PNG_PEDANTIC_WARNINGS_SUPPORTED
+# endif
+#endif
+
+#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED
+ /* Support for compiler specific function attributes. These are used
+ * so that where compiler support is available incorrect use of API
+ * functions in png.h will generate compiler warnings. Added at libpng
+ * version 1.2.41.
+ */
+# if defined(__GNUC__)
+# ifndef PNG_USE_RESULT
+# define PNG_USE_RESULT __attribute__((__warn_unused_result__))
+# endif
+# ifndef PNG_NORETURN
+# define PNG_NORETURN __attribute__((__noreturn__))
+# endif
+# ifndef PNG_PTR_NORETURN
+ /* It's not enough to have the compiler be the correct compiler at
+ * this point - it's necessary for the library (which defines
+ * the type of the library longjmp) to also be the GNU library.
+ * This is because many systems use the GNU compiler with a
+ * non-GNU libc implementation. Min/GW headers are also compatible
+ * with GCC as well as uclibc, so it seems best to exclude known
+ * problem libcs here rather than just including known libcs.
+ *
+ * NOTE: this relies on the only use of PNG_PTR_NORETURN being with
+ * the system longjmp. If the same type is used elsewhere then this
+ * will need to be changed.
+ */
+# if !defined(__CYGWIN__)
+# define PNG_PTR_NORETURN __attribute__((__noreturn__))
+# endif
+# endif
+# ifndef PNG_ALLOCATED
+# define PNG_ALLOCATED __attribute__((__malloc__))
+# endif
+
+ /* This specifically protects structure members that should only be
+ * accessed from within the library, therefore should be empty during
+ * a library build.
+ */
+# ifndef PNGLIB_BUILD
+# ifndef PNG_DEPRECATED
+# define PNG_DEPRECATED __attribute__((__deprecated__))
+# endif
+# ifndef PNG_DEPSTRUCT
+# define PNG_DEPSTRUCT __attribute__((__deprecated__))
+# endif
+# ifndef PNG_PRIVATE
+# if 0 /* Doesn't work so we use deprecated instead*/
+# define PNG_PRIVATE \
+ __attribute__((warning("This function is not exported by libpng.")))
+# else
+# define PNG_PRIVATE \
+ __attribute__((__deprecated__))
+# endif
+# endif
+# endif /* PNGLIB_BUILD */
+# endif /* __GNUC__ */
+
+# if defined(_MSC_VER) && (_MSC_VER >= 1300)
+# ifndef PNG_USE_RESULT
+# define PNG_USE_RESULT /* not supported */
+# endif
+# ifndef PNG_NORETURN
+# define PNG_NORETURN __declspec(noreturn)
+# endif
+# ifndef PNG_PTR_NORETURN
+# define PNG_PTR_NORETURN /* not supported */
+# endif
+# ifndef PNG_ALLOCATED
+# define PNG_ALLOCATED __declspec(restrict)
+# endif
+
+ /* This specifically protects structure members that should only be
+ * accessed from within the library, therefore should be empty during
+ * a library build.
+ */
+# ifndef PNGLIB_BUILD
+# ifndef PNG_DEPRECATED
+# define PNG_DEPRECATED __declspec(deprecated)
+# endif
+# ifndef PNG_DEPSTRUCT
+# define PNG_DEPSTRUCT __declspec(deprecated)
+# endif
+# ifndef PNG_PRIVATE
+# define PNG_PRIVATE __declspec(deprecated)
+# endif
+# endif /* PNGLIB_BUILD */
+# endif /* _MSC_VER */
+#endif /* PNG_PEDANTIC_WARNINGS */
+
+#ifndef PNG_DEPRECATED
+# define PNG_DEPRECATED /* Use of this function is deprecated */
+#endif
+#ifndef PNG_USE_RESULT
+# define PNG_USE_RESULT /* The result of this function must be checked */
+#endif
+#ifndef PNG_NORETURN
+# define PNG_NORETURN /* This function does not return */
+#endif
+#ifndef PNG_PTR_NORETURN
+# define PNG_PTR_NORETURN /* This function does not return */
+#endif
+#ifndef PNG_ALLOCATED
+# define PNG_ALLOCATED /* The result of the function is new memory */
+#endif
+#ifndef PNG_DEPSTRUCT
+# define PNG_DEPSTRUCT /* Access to this struct member is deprecated */
+#endif
+#ifndef PNG_PRIVATE
+# define PNG_PRIVATE /* This is a private libpng function */
+#endif
+#ifndef PNG_FP_EXPORT /* A floating point API. */
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+# define PNG_FP_EXPORT(ordinal, type, name, args)\
+ PNG_EXPORT(ordinal, type, name, args)
+# else /* No floating point APIs */
+# define PNG_FP_EXPORT(ordinal, type, name, args)
+# endif
+#endif
+#ifndef PNG_FIXED_EXPORT /* A fixed point API. */
+# ifdef PNG_FIXED_POINT_SUPPORTED
+# define PNG_FIXED_EXPORT(ordinal, type, name, args)\
+ PNG_EXPORT(ordinal, type, name, args)
+# else /* No fixed point APIs */
+# define PNG_FIXED_EXPORT(ordinal, type, name, args)
+# endif
+#endif
+
+/* The following uses const char * instead of char * for error
+ * and warning message functions, so some compilers won't complain.
+ * If you do not want to use const, define PNG_NO_CONST here.
+ *
+ * This should not change how the APIs are called, so it can be done
+ * on a per-file basis in the application.
+ */
+#ifndef PNG_CONST
+# ifndef PNG_NO_CONST
+# define PNG_CONST const
+# else
+# define PNG_CONST
+# endif
+#endif
+
+/* Some typedefs to get us started. These should be safe on most of the
+ * common platforms. The typedefs should be at least as large as the
+ * numbers suggest (a png_uint_32 must be at least 32 bits long), but they
+ * don't have to be exactly that size. Some compilers dislike passing
+ * unsigned shorts as function parameters, so you may be better off using
+ * unsigned int for png_uint_16.
+ */
+
+#if defined(INT_MAX) && (INT_MAX > 0x7ffffffeL)
+typedef unsigned int png_uint_32;
+typedef int png_int_32;
+#else
+typedef unsigned long png_uint_32;
+typedef long png_int_32;
+#endif
+typedef unsigned short png_uint_16;
+typedef short png_int_16;
+typedef unsigned char png_byte;
+
+#ifdef PNG_NO_SIZE_T
+typedef unsigned int png_size_t;
+#else
+typedef size_t png_size_t;
+#endif
+#define png_sizeof(x) (sizeof (x))
+
+/* The following is needed for medium model support. It cannot be in the
+ * pngpriv.h header. Needs modification for other compilers besides
+ * MSC. Model independent support declares all arrays and pointers to be
+ * large using the far keyword. The zlib version used must also support
+ * model independent data. As of version zlib 1.0.4, the necessary changes
+ * have been made in zlib. The USE_FAR_KEYWORD define triggers other
+ * changes that are needed. (Tim Wegner)
+ */
+
+/* Separate compiler dependencies (problem here is that zlib.h always
+ * defines FAR. (SJT)
+ */
+#ifdef __BORLANDC__
+# if defined(__LARGE__) || defined(__HUGE__) || defined(__COMPACT__)
+# define LDATA 1
+# else
+# define LDATA 0
+# endif
+ /* GRR: why is Cygwin in here? Cygwin is not Borland C... */
+# if !defined(__WIN32__) && !defined(__FLAT__) && !defined(__CYGWIN__)
+# define PNG_MAX_MALLOC_64K /* only used in build */
+# if (LDATA != 1)
+# ifndef FAR
+# define FAR __far
+# endif
+# define USE_FAR_KEYWORD
+# endif /* LDATA != 1 */
+ /* Possibly useful for moving data out of default segment.
+ * Uncomment it if you want. Could also define FARDATA as
+ * const if your compiler supports it. (SJT)
+# define FARDATA FAR
+ */
+# endif /* __WIN32__, __FLAT__, __CYGWIN__ */
+#endif /* __BORLANDC__ */
+
+
+/* Suggest testing for specific compiler first before testing for
+ * FAR. The Watcom compiler defines both __MEDIUM__ and M_I86MM,
+ * making reliance oncertain keywords suspect. (SJT)
+ */
+
+/* MSC Medium model */
+#ifdef FAR
+# ifdef M_I86MM
+# define USE_FAR_KEYWORD
+# define FARDATA FAR
+# include <dos.h>
+# endif
+#endif
+
+/* SJT: default case */
+#ifndef FAR
+# define FAR
+#endif
+
+/* At this point FAR is always defined */
+#ifndef FARDATA
+# define FARDATA
+#endif
+
+/* Typedef for floating-point numbers that are converted
+ * to fixed-point with a multiple of 100,000, e.g., gamma
+ */
+typedef png_int_32 png_fixed_point;
+
+/* Add typedefs for pointers */
+typedef void FAR * png_voidp;
+typedef PNG_CONST void FAR * png_const_voidp;
+typedef png_byte FAR * png_bytep;
+typedef PNG_CONST png_byte FAR * png_const_bytep;
+typedef png_uint_32 FAR * png_uint_32p;
+typedef PNG_CONST png_uint_32 FAR * png_const_uint_32p;
+typedef png_int_32 FAR * png_int_32p;
+typedef PNG_CONST png_int_32 FAR * png_const_int_32p;
+typedef png_uint_16 FAR * png_uint_16p;
+typedef PNG_CONST png_uint_16 FAR * png_const_uint_16p;
+typedef png_int_16 FAR * png_int_16p;
+typedef PNG_CONST png_int_16 FAR * png_const_int_16p;
+typedef char FAR * png_charp;
+typedef PNG_CONST char FAR * png_const_charp;
+typedef png_fixed_point FAR * png_fixed_point_p;
+typedef PNG_CONST png_fixed_point FAR * png_const_fixed_point_p;
+typedef png_size_t FAR * png_size_tp;
+typedef PNG_CONST png_size_t FAR * png_const_size_tp;
+
+#ifdef PNG_STDIO_SUPPORTED
+typedef FILE * png_FILE_p;
+#endif
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+typedef double FAR * png_doublep;
+typedef PNG_CONST double FAR * png_const_doublep;
+#endif
+
+/* Pointers to pointers; i.e. arrays */
+typedef png_byte FAR * FAR * png_bytepp;
+typedef png_uint_32 FAR * FAR * png_uint_32pp;
+typedef png_int_32 FAR * FAR * png_int_32pp;
+typedef png_uint_16 FAR * FAR * png_uint_16pp;
+typedef png_int_16 FAR * FAR * png_int_16pp;
+typedef PNG_CONST char FAR * FAR * png_const_charpp;
+typedef char FAR * FAR * png_charpp;
+typedef png_fixed_point FAR * FAR * png_fixed_point_pp;
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+typedef double FAR * FAR * png_doublepp;
+#endif
+
+/* Pointers to pointers to pointers; i.e., pointer to array */
+typedef char FAR * FAR * FAR * png_charppp;
+
+/* png_alloc_size_t is guaranteed to be no smaller than png_size_t,
+ * and no smaller than png_uint_32. Casts from png_size_t or png_uint_32
+ * to png_alloc_size_t are not necessary; in fact, it is recommended
+ * not to use them at all so that the compiler can complain when something
+ * turns out to be problematic.
+ * Casts in the other direction (from png_alloc_size_t to png_size_t or
+ * png_uint_32) should be explicitly applied; however, we do not expect
+ * to encounter practical situations that require such conversions.
+ */
+#if defined(__TURBOC__) && !defined(__FLAT__)
+ typedef unsigned long png_alloc_size_t;
+#else
+# if defined(_MSC_VER) && defined(MAXSEG_64K)
+ typedef unsigned long png_alloc_size_t;
+# else
+ /* This is an attempt to detect an old Windows system where (int) is
+ * actually 16 bits, in that case png_malloc must have an argument with a
+ * bigger size to accomodate the requirements of the library.
+ */
+# if (defined(_Windows) || defined(_WINDOWS) || defined(_WINDOWS_)) && \
+ (!defined(INT_MAX) || INT_MAX <= 0x7ffffffeL)
+ typedef DWORD png_alloc_size_t;
+# else
+ typedef png_size_t png_alloc_size_t;
+# endif
+# endif
+#endif
+
+#endif /* PNGCONF_H */
diff --git a/libpng/pngdebug.h b/libpng/pngdebug.h
new file mode 100644
index 0000000..16f81fd
--- /dev/null
+++ b/libpng/pngdebug.h
@@ -0,0 +1,157 @@
+
+/* pngdebug.h - Debugging macros for libpng, also used in pngtest.c
+ *
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * Last changed in libpng 1.5.0 [January 6, 2011]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+/* Define PNG_DEBUG at compile time for debugging information. Higher
+ * numbers for PNG_DEBUG mean more debugging information. This has
+ * only been added since version 0.95 so it is not implemented throughout
+ * libpng yet, but more support will be added as needed.
+ *
+ * png_debug[1-2]?(level, message ,arg{0-2})
+ * Expands to a statement (either a simple expression or a compound
+ * do..while(0) statement) that outputs a message with parameter
+ * substitution if PNG_DEBUG is defined to 2 or more. If PNG_DEBUG
+ * is undefined, 0 or 1 every png_debug expands to a simple expression
+ * (actually ((void)0)).
+ *
+ * level: level of detail of message, starting at 0. A level 'n'
+ * message is preceded by 'n' tab characters (not implemented
+ * on Microsoft compilers unless PNG_DEBUG_FILE is also
+ * defined, to allow debug DLL compilation with no standard IO).
+ * message: a printf(3) style text string. A trailing '\n' is added
+ * to the message.
+ * arg: 0 to 2 arguments for printf(3) style substitution in message.
+ */
+#ifndef PNGDEBUG_H
+#define PNGDEBUG_H
+/* These settings control the formatting of messages in png.c and pngerror.c */
+/* Moved to pngdebug.h at 1.5.0 */
+# ifndef PNG_LITERAL_SHARP
+# define PNG_LITERAL_SHARP 0x23
+# endif
+# ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET
+# define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b
+# endif
+# ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET
+# define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d
+# endif
+# ifndef PNG_STRING_NEWLINE
+# define PNG_STRING_NEWLINE "\n"
+# endif
+
+#ifdef PNG_DEBUG
+# if (PNG_DEBUG > 0)
+# if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER)
+# include <crtdbg.h>
+# if (PNG_DEBUG > 1)
+# ifndef _DEBUG
+# define _DEBUG
+# endif
+# ifndef png_debug
+# define png_debug(l,m) _RPT0(_CRT_WARN,m PNG_STRING_NEWLINE)
+# endif
+# ifndef png_debug1
+# define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m PNG_STRING_NEWLINE,p1)
+# endif
+# ifndef png_debug2
+# define png_debug2(l,m,p1,p2) \
+ _RPT2(_CRT_WARN,m PNG_STRING_NEWLINE,p1,p2)
+# endif
+# endif
+# else /* PNG_DEBUG_FILE || !_MSC_VER */
+# ifndef PNG_STDIO_SUPPORTED
+# include <stdio.h> /* not included yet */
+# endif
+# ifndef PNG_DEBUG_FILE
+# define PNG_DEBUG_FILE stderr
+# endif /* PNG_DEBUG_FILE */
+
+# if (PNG_DEBUG > 1)
+/* Note: ["%s"m PNG_STRING_NEWLINE] probably does not work on
+ * non-ISO compilers
+ */
+# ifdef __STDC__
+# ifndef png_debug
+# define png_debug(l,m) \
+ do { \
+ int num_tabs=l; \
+ fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \
+ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \
+ } while (0)
+# endif
+# ifndef png_debug1
+# define png_debug1(l,m,p1) \
+ do { \
+ int num_tabs=l; \
+ fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \
+ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \
+ } while (0)
+# endif
+# ifndef png_debug2
+# define png_debug2(l,m,p1,p2) \
+ do { \
+ int num_tabs=l; \
+ fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \
+ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \
+ } while (0)
+# endif
+# else /* __STDC __ */
+# ifndef png_debug
+# define png_debug(l,m) \
+ do { \
+ int num_tabs=l; \
+ char format[256]; \
+ snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
+ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
+ m,PNG_STRING_NEWLINE); \
+ fprintf(PNG_DEBUG_FILE,format); \
+ } while (0)
+# endif
+# ifndef png_debug1
+# define png_debug1(l,m,p1) \
+ do { \
+ int num_tabs=l; \
+ char format[256]; \
+ snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
+ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
+ m,PNG_STRING_NEWLINE); \
+ fprintf(PNG_DEBUG_FILE,format,p1); \
+ } while (0)
+# endif
+# ifndef png_debug2
+# define png_debug2(l,m,p1,p2) \
+ do { \
+ int num_tabs=l; \
+ char format[256]; \
+ snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \
+ (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \
+ m,PNG_STRING_NEWLINE); \
+ fprintf(PNG_DEBUG_FILE,format,p1,p2); \
+ } while (0)
+# endif
+# endif /* __STDC __ */
+# endif /* (PNG_DEBUG > 1) */
+
+# endif /* _MSC_VER */
+# endif /* (PNG_DEBUG > 0) */
+#endif /* PNG_DEBUG */
+#ifndef png_debug
+# define png_debug(l, m) ((void)0)
+#endif
+#ifndef png_debug1
+# define png_debug1(l, m, p1) ((void)0)
+#endif
+#ifndef png_debug2
+# define png_debug2(l, m, p1, p2) ((void)0)
+#endif
+#endif /* PNGDEBUG_H */
diff --git a/libpng/pngerror.c b/libpng/pngerror.c
new file mode 100644
index 0000000..8290bb4
--- /dev/null
+++ b/libpng/pngerror.c
@@ -0,0 +1,447 @@
+
+/* pngerror.c - stub functions for i/o and memory allocation
+ *
+ * Last changed in libpng 1.5.1 [February 3, 2011]
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * This file provides a location for all error handling. Users who
+ * need special error handling are expected to write replacement functions
+ * and use png_set_error_fn() to use those functions. See the instructions
+ * at each function.
+ */
+
+#include "pngpriv.h"
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+
+static PNG_FUNCTION(void, png_default_error,PNGARG((png_structp png_ptr,
+ png_const_charp error_message)),PNG_NORETURN);
+
+#ifdef PNG_WARNINGS_SUPPORTED
+static void /* PRIVATE */
+png_default_warning PNGARG((png_structp png_ptr,
+ png_const_charp warning_message));
+#endif /* PNG_WARNINGS_SUPPORTED */
+
+/* This function is called whenever there is a fatal error. This function
+ * should not be changed. If there is a need to handle errors differently,
+ * you should supply a replacement error function and use png_set_error_fn()
+ * to replace the error function at run-time.
+ */
+#ifdef PNG_ERROR_TEXT_SUPPORTED
+PNG_FUNCTION(void,PNGAPI
+png_error,(png_structp png_ptr, png_const_charp error_message),PNG_NORETURN)
+{
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+ char msg[16];
+ if (png_ptr != NULL)
+ {
+ if (png_ptr->flags&
+ (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))
+ {
+ if (*error_message == PNG_LITERAL_SHARP)
+ {
+ /* Strip "#nnnn " from beginning of error message. */
+ int offset;
+ for (offset = 1; offset<15; offset++)
+ if (error_message[offset] == ' ')
+ break;
+
+ if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT)
+ {
+ int i;
+ for (i = 0; i < offset - 1; i++)
+ msg[i] = error_message[i + 1];
+ msg[i - 1] = '\0';
+ error_message = msg;
+ }
+
+ else
+ error_message += offset;
+ }
+
+ else
+ {
+ if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT)
+ {
+ msg[0] = '0';
+ msg[1] = '\0';
+ error_message = msg;
+ }
+ }
+ }
+ }
+#endif
+ if (png_ptr != NULL && png_ptr->error_fn != NULL)
+ (*(png_ptr->error_fn))(png_ptr, error_message);
+
+ /* If the custom handler doesn't exist, or if it returns,
+ use the default handler, which will not return. */
+ png_default_error(png_ptr, error_message);
+}
+#else
+PNG_FUNCTION(void,PNGAPI
+png_err,(png_structp png_ptr),PNG_NORETURN)
+{
+ if (png_ptr != NULL && png_ptr->error_fn != NULL)
+ (*(png_ptr->error_fn))(png_ptr, '\0');
+
+ /* If the custom handler doesn't exist, or if it returns,
+ use the default handler, which will not return. */
+ png_default_error(png_ptr, '\0');
+}
+#endif /* PNG_ERROR_TEXT_SUPPORTED */
+
+#ifdef PNG_WARNINGS_SUPPORTED
+/* This function is called whenever there is a non-fatal error. This function
+ * should not be changed. If there is a need to handle warnings differently,
+ * you should supply a replacement warning function and use
+ * png_set_error_fn() to replace the warning function at run-time.
+ */
+void PNGAPI
+png_warning(png_structp png_ptr, png_const_charp warning_message)
+{
+ int offset = 0;
+ if (png_ptr != NULL)
+ {
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+ if (png_ptr->flags&
+ (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))
+#endif
+ {
+ if (*warning_message == PNG_LITERAL_SHARP)
+ {
+ for (offset = 1; offset < 15; offset++)
+ if (warning_message[offset] == ' ')
+ break;
+ }
+ }
+ }
+ if (png_ptr != NULL && png_ptr->warning_fn != NULL)
+ (*(png_ptr->warning_fn))(png_ptr, warning_message + offset);
+ else
+ png_default_warning(png_ptr, warning_message + offset);
+}
+#endif /* PNG_WARNINGS_SUPPORTED */
+
+#ifdef PNG_BENIGN_ERRORS_SUPPORTED
+void PNGAPI
+png_benign_error(png_structp png_ptr, png_const_charp error_message)
+{
+ if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN)
+ png_warning(png_ptr, error_message);
+ else
+ png_error(png_ptr, error_message);
+}
+#endif
+
+/* These utilities are used internally to build an error message that relates
+ * to the current chunk. The chunk name comes from png_ptr->chunk_name,
+ * this is used to prefix the message. The message is limited in length
+ * to 63 bytes, the name characters are output as hex digits wrapped in []
+ * if the character is invalid.
+ */
+#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
+static PNG_CONST char png_digit[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F'
+};
+
+#define PNG_MAX_ERROR_TEXT 64
+#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_ERROR_TEXT_SUPPORTED)
+static void /* PRIVATE */
+png_format_buffer(png_structp png_ptr, png_charp buffer, png_const_charp
+ error_message)
+{
+ int iout = 0, iin = 0;
+
+ while (iin < 4)
+ {
+ int c = png_ptr->chunk_name[iin++];
+ if (isnonalpha(c))
+ {
+ buffer[iout++] = PNG_LITERAL_LEFT_SQUARE_BRACKET;
+ buffer[iout++] = png_digit[(c & 0xf0) >> 4];
+ buffer[iout++] = png_digit[c & 0x0f];
+ buffer[iout++] = PNG_LITERAL_RIGHT_SQUARE_BRACKET;
+ }
+
+ else
+ {
+ buffer[iout++] = (png_byte)c;
+ }
+ }
+
+ if (error_message == NULL)
+ buffer[iout] = '\0';
+
+ else
+ {
+ buffer[iout++] = ':';
+ buffer[iout++] = ' ';
+ png_memcpy(buffer + iout, error_message, PNG_MAX_ERROR_TEXT);
+ buffer[iout + PNG_MAX_ERROR_TEXT - 1] = '\0';
+ }
+}
+#endif /* PNG_WARNINGS_SUPPORTED || PNG_ERROR_TEXT_SUPPORTED */
+
+#if defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)
+PNG_FUNCTION(void,PNGAPI
+png_chunk_error,(png_structp png_ptr, png_const_charp error_message),
+ PNG_NORETURN)
+{
+ char msg[18+PNG_MAX_ERROR_TEXT];
+ if (png_ptr == NULL)
+ png_error(png_ptr, error_message);
+
+ else
+ {
+ png_format_buffer(png_ptr, msg, error_message);
+ png_error(png_ptr, msg);
+ }
+}
+#endif /* PNG_READ_SUPPORTED && PNG_ERROR_TEXT_SUPPORTED */
+
+#ifdef PNG_WARNINGS_SUPPORTED
+void PNGAPI
+png_chunk_warning(png_structp png_ptr, png_const_charp warning_message)
+{
+ char msg[18+PNG_MAX_ERROR_TEXT];
+ if (png_ptr == NULL)
+ png_warning(png_ptr, warning_message);
+
+ else
+ {
+ png_format_buffer(png_ptr, msg, warning_message);
+ png_warning(png_ptr, msg);
+ }
+}
+#endif /* PNG_WARNINGS_SUPPORTED */
+
+#ifdef PNG_READ_SUPPORTED
+#ifdef PNG_BENIGN_ERRORS_SUPPORTED
+void PNGAPI
+png_chunk_benign_error(png_structp png_ptr, png_const_charp error_message)
+{
+ if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN)
+ png_chunk_warning(png_ptr, error_message);
+
+ else
+ png_chunk_error(png_ptr, error_message);
+}
+#endif
+#endif /* PNG_READ_SUPPORTED */
+
+#ifdef PNG_ERROR_TEXT_SUPPORTED
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+PNG_FUNCTION(void,
+png_fixed_error,(png_structp png_ptr, png_const_charp name),PNG_NORETURN)
+{
+# define fixed_message "fixed point overflow in "
+# define fixed_message_ln ((sizeof fixed_message)-1)
+ int iin;
+ char msg[fixed_message_ln+PNG_MAX_ERROR_TEXT];
+ png_memcpy(msg, fixed_message, fixed_message_ln);
+ iin = 0;
+ if (name != NULL) while (iin < (PNG_MAX_ERROR_TEXT-1) && name[iin] != 0)
+ {
+ msg[fixed_message_ln + iin] = name[iin];
+ ++iin;
+ }
+ msg[fixed_message_ln + iin] = 0;
+ png_error(png_ptr, msg);
+}
+#endif
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+/* This API only exists if ANSI-C style error handling is used,
+ * otherwise it is necessary for png_default_error to be overridden.
+ */
+jmp_buf* PNGAPI
+png_set_longjmp_fn(png_structp png_ptr, png_longjmp_ptr longjmp_fn,
+ size_t jmp_buf_size)
+{
+ if (png_ptr == NULL || jmp_buf_size != png_sizeof(jmp_buf))
+ return NULL;
+
+ png_ptr->longjmp_fn = longjmp_fn;
+ return &png_ptr->png_jmpbuf;
+}
+#endif
+
+/* This is the default error handling function. Note that replacements for
+ * this function MUST NOT RETURN, or the program will likely crash. This
+ * function is used by default, or if the program supplies NULL for the
+ * error function pointer in png_set_error_fn().
+ */
+static PNG_FUNCTION(void /* PRIVATE */,
+png_default_error,(png_structp png_ptr, png_const_charp error_message),
+ PNG_NORETURN)
+{
+#ifdef PNG_CONSOLE_IO_SUPPORTED
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+ if (*error_message == PNG_LITERAL_SHARP)
+ {
+ /* Strip "#nnnn " from beginning of error message. */
+ int offset;
+ char error_number[16];
+ for (offset = 0; offset<15; offset++)
+ {
+ error_number[offset] = error_message[offset + 1];
+ if (error_message[offset] == ' ')
+ break;
+ }
+
+ if ((offset > 1) && (offset < 15))
+ {
+ error_number[offset - 1] = '\0';
+ fprintf(stderr, "libpng error no. %s: %s",
+ error_number, error_message + offset + 1);
+ fprintf(stderr, PNG_STRING_NEWLINE);
+ }
+
+ else
+ {
+ fprintf(stderr, "libpng error: %s, offset=%d",
+ error_message, offset);
+ fprintf(stderr, PNG_STRING_NEWLINE);
+ }
+ }
+ else
+#endif
+ {
+ fprintf(stderr, "libpng error: %s", error_message);
+ fprintf(stderr, PNG_STRING_NEWLINE);
+ }
+#endif
+#ifndef PNG_CONSOLE_IO_SUPPORTED
+ PNG_UNUSED(error_message) /* Make compiler happy */
+#endif
+ png_longjmp(png_ptr, 1);
+}
+
+PNG_FUNCTION(void,PNGAPI
+png_longjmp,(png_structp png_ptr, int val),PNG_NORETURN)
+{
+#ifdef PNG_SETJMP_SUPPORTED
+ if (png_ptr && png_ptr->longjmp_fn)
+ {
+# ifdef USE_FAR_KEYWORD
+ {
+ jmp_buf png_jmpbuf;
+ png_memcpy(png_jmpbuf, png_ptr->png_jmpbuf, png_sizeof(jmp_buf));
+ png_ptr->longjmp_fn(png_jmpbuf, val);
+ }
+
+# else
+ png_ptr->longjmp_fn(png_ptr->png_jmpbuf, val);
+# endif
+ }
+#endif
+ /* Here if not setjmp support or if png_ptr is null. */
+ PNG_ABORT();
+}
+
+#ifdef PNG_WARNINGS_SUPPORTED
+/* This function is called when there is a warning, but the library thinks
+ * it can continue anyway. Replacement functions don't have to do anything
+ * here if you don't want them to. In the default configuration, png_ptr is
+ * not used, but it is passed in case it may be useful.
+ */
+static void /* PRIVATE */
+png_default_warning(png_structp png_ptr, png_const_charp warning_message)
+{
+#ifdef PNG_CONSOLE_IO_SUPPORTED
+# ifdef PNG_ERROR_NUMBERS_SUPPORTED
+ if (*warning_message == PNG_LITERAL_SHARP)
+ {
+ int offset;
+ char warning_number[16];
+ for (offset = 0; offset < 15; offset++)
+ {
+ warning_number[offset] = warning_message[offset + 1];
+ if (warning_message[offset] == ' ')
+ break;
+ }
+
+ if ((offset > 1) && (offset < 15))
+ {
+ warning_number[offset + 1] = '\0';
+ fprintf(stderr, "libpng warning no. %s: %s",
+ warning_number, warning_message + offset);
+ fprintf(stderr, PNG_STRING_NEWLINE);
+ }
+
+ else
+ {
+ fprintf(stderr, "libpng warning: %s",
+ warning_message);
+ fprintf(stderr, PNG_STRING_NEWLINE);
+ }
+ }
+ else
+# endif
+
+ {
+ fprintf(stderr, "libpng warning: %s", warning_message);
+ fprintf(stderr, PNG_STRING_NEWLINE);
+ }
+#else
+ PNG_UNUSED(warning_message) /* Make compiler happy */
+#endif
+ PNG_UNUSED(png_ptr) /* Make compiler happy */
+}
+#endif /* PNG_WARNINGS_SUPPORTED */
+
+/* This function is called when the application wants to use another method
+ * of handling errors and warnings. Note that the error function MUST NOT
+ * return to the calling routine or serious problems will occur. The return
+ * method used in the default routine calls longjmp(png_ptr->png_jmpbuf, 1)
+ */
+void PNGAPI
+png_set_error_fn(png_structp png_ptr, png_voidp error_ptr,
+ png_error_ptr error_fn, png_error_ptr warning_fn)
+{
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->error_ptr = error_ptr;
+ png_ptr->error_fn = error_fn;
+ png_ptr->warning_fn = warning_fn;
+}
+
+
+/* This function returns a pointer to the error_ptr associated with the user
+ * functions. The application should free any memory associated with this
+ * pointer before png_write_destroy and png_read_destroy are called.
+ */
+png_voidp PNGAPI
+png_get_error_ptr(png_const_structp png_ptr)
+{
+ if (png_ptr == NULL)
+ return NULL;
+
+ return ((png_voidp)png_ptr->error_ptr);
+}
+
+
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+void PNGAPI
+png_set_strip_error_numbers(png_structp png_ptr, png_uint_32 strip_mode)
+{
+ if (png_ptr != NULL)
+ {
+ png_ptr->flags &=
+ ((~(PNG_FLAG_STRIP_ERROR_NUMBERS |
+ PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode);
+ }
+}
+#endif
+#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */
diff --git a/libpng/pngget.c b/libpng/pngget.c
new file mode 100644
index 0000000..b5e5798
--- /dev/null
+++ b/libpng/pngget.c
@@ -0,0 +1,1032 @@
+
+/* pngget.c - retrieval of values from info struct
+ *
+ * Last changed in libpng 1.5.1 [February 3, 2011]
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ */
+
+#include "pngpriv.h"
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+
+png_uint_32 PNGAPI
+png_get_valid(png_const_structp png_ptr, png_const_infop info_ptr,
+ png_uint_32 flag)
+{
+ if (png_ptr != NULL && info_ptr != NULL)
+ return(info_ptr->valid & flag);
+
+ return(0);
+}
+
+png_size_t PNGAPI
+png_get_rowbytes(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+ if (png_ptr != NULL && info_ptr != NULL)
+ return(info_ptr->rowbytes);
+
+ return(0);
+}
+
+#ifdef PNG_INFO_IMAGE_SUPPORTED
+png_bytepp PNGAPI
+png_get_rows(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+ if (png_ptr != NULL && info_ptr != NULL)
+ return(info_ptr->row_pointers);
+
+ return(0);
+}
+#endif
+
+#ifdef PNG_EASY_ACCESS_SUPPORTED
+/* Easy access to info, added in libpng-0.99 */
+png_uint_32 PNGAPI
+png_get_image_width(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+ if (png_ptr != NULL && info_ptr != NULL)
+ return info_ptr->width;
+
+ return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_image_height(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+ if (png_ptr != NULL && info_ptr != NULL)
+ return info_ptr->height;
+
+ return (0);
+}
+
+png_byte PNGAPI
+png_get_bit_depth(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+ if (png_ptr != NULL && info_ptr != NULL)
+ return info_ptr->bit_depth;
+
+ return (0);
+}
+
+png_byte PNGAPI
+png_get_color_type(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+ if (png_ptr != NULL && info_ptr != NULL)
+ return info_ptr->color_type;
+
+ return (0);
+}
+
+png_byte PNGAPI
+png_get_filter_type(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+ if (png_ptr != NULL && info_ptr != NULL)
+ return info_ptr->filter_type;
+
+ return (0);
+}
+
+png_byte PNGAPI
+png_get_interlace_type(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+ if (png_ptr != NULL && info_ptr != NULL)
+ return info_ptr->interlace_type;
+
+ return (0);
+}
+
+png_byte PNGAPI
+png_get_compression_type(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+ if (png_ptr != NULL && info_ptr != NULL)
+ return info_ptr->compression_type;
+
+ return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_x_pixels_per_meter(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+#ifdef PNG_pHYs_SUPPORTED
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+ {
+ png_debug1(1, "in %s retrieval function",
+ "png_get_x_pixels_per_meter");
+
+ if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER)
+ return (info_ptr->x_pixels_per_unit);
+ }
+#endif
+
+ return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_y_pixels_per_meter(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+#ifdef PNG_pHYs_SUPPORTED
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+ {
+ png_debug1(1, "in %s retrieval function",
+ "png_get_y_pixels_per_meter");
+
+ if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER)
+ return (info_ptr->y_pixels_per_unit);
+ }
+#endif
+
+ return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_pixels_per_meter(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+#ifdef PNG_pHYs_SUPPORTED
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+ {
+ png_debug1(1, "in %s retrieval function", "png_get_pixels_per_meter");
+
+ if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER &&
+ info_ptr->x_pixels_per_unit == info_ptr->y_pixels_per_unit)
+ return (info_ptr->x_pixels_per_unit);
+ }
+#endif
+
+ return (0);
+}
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+float PNGAPI
+png_get_pixel_aspect_ratio(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+#ifdef PNG_READ_pHYs_SUPPORTED
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+ {
+ png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio");
+
+ if (info_ptr->x_pixels_per_unit != 0)
+ return ((float)((float)info_ptr->y_pixels_per_unit
+ /(float)info_ptr->x_pixels_per_unit));
+ }
+#endif
+
+ return ((float)0.0);
+}
+#endif
+
+#ifdef PNG_FIXED_POINT_SUPPORTED
+png_fixed_point PNGAPI
+png_get_pixel_aspect_ratio_fixed(png_const_structp png_ptr,
+ png_const_infop info_ptr)
+{
+#ifdef PNG_READ_pHYs_SUPPORTED
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)
+ && info_ptr->x_pixels_per_unit > 0 && info_ptr->y_pixels_per_unit > 0
+ && info_ptr->x_pixels_per_unit <= PNG_UINT_31_MAX
+ && info_ptr->y_pixels_per_unit <= PNG_UINT_31_MAX)
+ {
+ png_fixed_point res;
+
+ png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio_fixed");
+
+ /* The following casts work because a PNG 4 byte integer only has a valid
+ * range of 0..2^31-1; otherwise the cast might overflow.
+ */
+ if (png_muldiv(&res, (png_int_32)info_ptr->y_pixels_per_unit, PNG_FP_1,
+ (png_int_32)info_ptr->x_pixels_per_unit))
+ return res;
+ }
+#endif
+
+ return 0;
+}
+#endif
+
+png_int_32 PNGAPI
+png_get_x_offset_microns(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+#ifdef PNG_oFFs_SUPPORTED
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
+ {
+ png_debug1(1, "in %s retrieval function", "png_get_x_offset_microns");
+
+ if (info_ptr->offset_unit_type == PNG_OFFSET_MICROMETER)
+ return (info_ptr->x_offset);
+ }
+#endif
+
+ return (0);
+}
+
+png_int_32 PNGAPI
+png_get_y_offset_microns(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+#ifdef PNG_oFFs_SUPPORTED
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
+ {
+ png_debug1(1, "in %s retrieval function", "png_get_y_offset_microns");
+
+ if (info_ptr->offset_unit_type == PNG_OFFSET_MICROMETER)
+ return (info_ptr->y_offset);
+ }
+#endif
+
+ return (0);
+}
+
+png_int_32 PNGAPI
+png_get_x_offset_pixels(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+#ifdef PNG_oFFs_SUPPORTED
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
+ {
+ png_debug1(1, "in %s retrieval function", "png_get_x_offset_pixels");
+
+ if (info_ptr->offset_unit_type == PNG_OFFSET_PIXEL)
+ return (info_ptr->x_offset);
+ }
+#endif
+
+ return (0);
+}
+
+png_int_32 PNGAPI
+png_get_y_offset_pixels(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+#ifdef PNG_oFFs_SUPPORTED
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
+ {
+ png_debug1(1, "in %s retrieval function", "png_get_y_offset_pixels");
+
+ if (info_ptr->offset_unit_type == PNG_OFFSET_PIXEL)
+ return (info_ptr->y_offset);
+ }
+#endif
+
+ return (0);
+}
+
+#ifdef PNG_INCH_CONVERSIONS_SUPPORTED
+static png_uint_32
+ppi_from_ppm(png_uint_32 ppm)
+{
+#if 0
+ /* The conversion is *(2.54/100), in binary (32 digits):
+ * .00000110100000001001110101001001
+ */
+ png_uint_32 t1001, t1101;
+ ppm >>= 1; /* .1 */
+ t1001 = ppm + (ppm >> 3); /* .1001 */
+ t1101 = t1001 + (ppm >> 1); /* .1101 */
+ ppm >>= 20; /* .000000000000000000001 */
+ t1101 += t1101 >> 15; /* .1101000000000001101 */
+ t1001 >>= 11; /* .000000000001001 */
+ t1001 += t1001 >> 12; /* .000000000001001000000001001 */
+ ppm += t1001; /* .000000000001001000001001001 */
+ ppm += t1101; /* .110100000001001110101001001 */
+ return (ppm + 16) >> 5;/* .00000110100000001001110101001001 */
+#else
+ /* The argument is a PNG unsigned integer, so it is not permitted
+ * to be bigger than 2^31.
+ */
+ png_fixed_point result;
+ if (ppm <= PNG_UINT_31_MAX && png_muldiv(&result, (png_int_32)ppm, 127,
+ 5000))
+ return result;
+
+ /* Overflow. */
+ return 0;
+#endif
+}
+
+png_uint_32 PNGAPI
+png_get_pixels_per_inch(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+ return ppi_from_ppm(png_get_pixels_per_meter(png_ptr, info_ptr));
+}
+
+png_uint_32 PNGAPI
+png_get_x_pixels_per_inch(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+ return ppi_from_ppm(png_get_x_pixels_per_meter(png_ptr, info_ptr));
+}
+
+png_uint_32 PNGAPI
+png_get_y_pixels_per_inch(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+ return ppi_from_ppm(png_get_y_pixels_per_meter(png_ptr, info_ptr));
+}
+
+#ifdef PNG_FIXED_POINT_SUPPORTED
+static png_fixed_point
+png_fixed_inches_from_microns(png_structp png_ptr, png_int_32 microns)
+{
+ /* Convert from metres * 1,000,000 to inches * 100,000, meters to
+ * inches is simply *(100/2.54), so we want *(10/2.54) == 500/127.
+ * Notice that this can overflow - a warning is output and 0 is
+ * returned.
+ */
+ return png_muldiv_warn(png_ptr, microns, 500, 127);
+}
+
+png_fixed_point PNGAPI
+png_get_x_offset_inches_fixed(png_structp png_ptr,
+ png_const_infop info_ptr)
+{
+ return png_fixed_inches_from_microns(png_ptr,
+ png_get_x_offset_microns(png_ptr, info_ptr));
+}
+#endif
+
+#ifdef PNG_FIXED_POINT_SUPPORTED
+png_fixed_point PNGAPI
+png_get_y_offset_inches_fixed(png_structp png_ptr,
+ png_const_infop info_ptr)
+{
+ return png_fixed_inches_from_microns(png_ptr,
+ png_get_y_offset_microns(png_ptr, info_ptr));
+}
+#endif
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+float PNGAPI
+png_get_x_offset_inches(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+ /* To avoid the overflow do the conversion directly in floating
+ * point.
+ */
+ return (float)(png_get_x_offset_microns(png_ptr, info_ptr) * .00003937);
+}
+#endif
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+float PNGAPI
+png_get_y_offset_inches(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+ /* To avoid the overflow do the conversion directly in floating
+ * point.
+ */
+ return (float)(png_get_y_offset_microns(png_ptr, info_ptr) * .00003937);
+}
+#endif
+
+#ifdef PNG_pHYs_SUPPORTED
+png_uint_32 PNGAPI
+png_get_pHYs_dpi(png_const_structp png_ptr, png_const_infop info_ptr,
+ png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)
+{
+ png_uint_32 retval = 0;
+
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+ {
+ png_debug1(1, "in %s retrieval function", "pHYs");
+
+ if (res_x != NULL)
+ {
+ *res_x = info_ptr->x_pixels_per_unit;
+ retval |= PNG_INFO_pHYs;
+ }
+
+ if (res_y != NULL)
+ {
+ *res_y = info_ptr->y_pixels_per_unit;
+ retval |= PNG_INFO_pHYs;
+ }
+
+ if (unit_type != NULL)
+ {
+ *unit_type = (int)info_ptr->phys_unit_type;
+ retval |= PNG_INFO_pHYs;
+
+ if (*unit_type == 1)
+ {
+ if (res_x != NULL) *res_x = (png_uint_32)(*res_x * .0254 + .50);
+ if (res_y != NULL) *res_y = (png_uint_32)(*res_y * .0254 + .50);
+ }
+ }
+ }
+
+ return (retval);
+}
+#endif /* PNG_pHYs_SUPPORTED */
+#endif /* PNG_INCH_CONVERSIONS_SUPPORTED */
+
+/* png_get_channels really belongs in here, too, but it's been around longer */
+
+#endif /* PNG_EASY_ACCESS_SUPPORTED */
+
+png_byte PNGAPI
+png_get_channels(png_const_structp png_ptr, png_const_infop info_ptr)
+{
+ if (png_ptr != NULL && info_ptr != NULL)
+ return(info_ptr->channels);
+
+ return (0);
+}
+
+png_const_bytep PNGAPI
+png_get_signature(png_const_structp png_ptr, png_infop info_ptr)
+{
+ if (png_ptr != NULL && info_ptr != NULL)
+ return(info_ptr->signature);
+
+ return (NULL);
+}
+
+#ifdef PNG_bKGD_SUPPORTED
+png_uint_32 PNGAPI
+png_get_bKGD(png_const_structp png_ptr, png_infop info_ptr,
+ png_color_16p *background)
+{
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)
+ && background != NULL)
+ {
+ png_debug1(1, "in %s retrieval function", "bKGD");
+
+ *background = &(info_ptr->background);
+ return (PNG_INFO_bKGD);
+ }
+
+ return (0);
+}
+#endif
+
+#ifdef PNG_cHRM_SUPPORTED
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_cHRM(png_const_structp png_ptr, png_const_infop info_ptr,
+ double *white_x, double *white_y, double *red_x, double *red_y,
+ double *green_x, double *green_y, double *blue_x, double *blue_y)
+{
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM))
+ {
+ png_debug1(1, "in %s retrieval function", "cHRM");
+
+ if (white_x != NULL)
+ *white_x = png_float(png_ptr, info_ptr->x_white, "cHRM white X");
+ if (white_y != NULL)
+ *white_y = png_float(png_ptr, info_ptr->y_white, "cHRM white Y");
+ if (red_x != NULL)
+ *red_x = png_float(png_ptr, info_ptr->x_red, "cHRM red X");
+ if (red_y != NULL)
+ *red_y = png_float(png_ptr, info_ptr->y_red, "cHRM red Y");
+ if (green_x != NULL)
+ *green_x = png_float(png_ptr, info_ptr->x_green, "cHRM green X");
+ if (green_y != NULL)
+ *green_y = png_float(png_ptr, info_ptr->y_green, "cHRM green Y");
+ if (blue_x != NULL)
+ *blue_x = png_float(png_ptr, info_ptr->x_blue, "cHRM blue X");
+ if (blue_y != NULL)
+ *blue_y = png_float(png_ptr, info_ptr->y_blue, "cHRM blue Y");
+ return (PNG_INFO_cHRM);
+ }
+
+ return (0);
+}
+# endif
+
+# ifdef PNG_FIXED_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_cHRM_fixed(png_const_structp png_ptr, png_const_infop info_ptr,
+ png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x,
+ png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y,
+ png_fixed_point *blue_x, png_fixed_point *blue_y)
+{
+ png_debug1(1, "in %s retrieval function", "cHRM");
+
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM))
+ {
+ if (white_x != NULL)
+ *white_x = info_ptr->x_white;
+ if (white_y != NULL)
+ *white_y = info_ptr->y_white;
+ if (red_x != NULL)
+ *red_x = info_ptr->x_red;
+ if (red_y != NULL)
+ *red_y = info_ptr->y_red;
+ if (green_x != NULL)
+ *green_x = info_ptr->x_green;
+ if (green_y != NULL)
+ *green_y = info_ptr->y_green;
+ if (blue_x != NULL)
+ *blue_x = info_ptr->x_blue;
+ if (blue_y != NULL)
+ *blue_y = info_ptr->y_blue;
+ return (PNG_INFO_cHRM);
+ }
+
+ return (0);
+}
+# endif
+#endif
+
+#ifdef PNG_gAMA_SUPPORTED
+png_uint_32 PNGFAPI
+png_get_gAMA_fixed(png_const_structp png_ptr, png_const_infop info_ptr,
+ png_fixed_point *file_gamma)
+{
+ png_debug1(1, "in %s retrieval function", "gAMA");
+
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)
+ && file_gamma != NULL)
+ {
+ *file_gamma = info_ptr->gamma;
+ return (PNG_INFO_gAMA);
+ }
+
+ return (0);
+}
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_gAMA(png_const_structp png_ptr, png_const_infop info_ptr,
+ double *file_gamma)
+{
+ png_fixed_point igamma;
+ png_uint_32 ok = png_get_gAMA_fixed(png_ptr, info_ptr, &igamma);
+
+ if (ok)
+ *file_gamma = png_float(png_ptr, igamma, "png_get_gAMA");
+
+ return ok;
+}
+
+# endif
+#endif
+
+#ifdef PNG_sRGB_SUPPORTED
+png_uint_32 PNGAPI
+png_get_sRGB(png_const_structp png_ptr, png_const_infop info_ptr,
+ int *file_srgb_intent)
+{
+ png_debug1(1, "in %s retrieval function", "sRGB");
+
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)
+ && file_srgb_intent != NULL)
+ {
+ *file_srgb_intent = (int)info_ptr->srgb_intent;
+ return (PNG_INFO_sRGB);
+ }
+
+ return (0);
+}
+#endif
+
+#ifdef PNG_iCCP_SUPPORTED
+png_uint_32 PNGAPI
+png_get_iCCP(png_const_structp png_ptr, png_const_infop info_ptr,
+ png_charpp name, int *compression_type,
+ png_bytepp profile, png_uint_32 *proflen)
+{
+ png_debug1(1, "in %s retrieval function", "iCCP");
+
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP)
+ && name != NULL && profile != NULL && proflen != NULL)
+ {
+ *name = info_ptr->iccp_name;
+ *profile = info_ptr->iccp_profile;
+ /* Compression_type is a dummy so the API won't have to change
+ * if we introduce multiple compression types later.
+ */
+ *proflen = (int)info_ptr->iccp_proflen;
+ *compression_type = (int)info_ptr->iccp_compression;
+ return (PNG_INFO_iCCP);
+ }
+
+ return (0);
+}
+#endif
+
+#ifdef PNG_sPLT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_sPLT(png_const_structp png_ptr, png_const_infop info_ptr,
+ png_sPLT_tpp spalettes)
+{
+ if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL)
+ {
+ *spalettes = info_ptr->splt_palettes;
+ return ((png_uint_32)info_ptr->splt_palettes_num);
+ }
+
+ return (0);
+}
+#endif
+
+#ifdef PNG_hIST_SUPPORTED
+png_uint_32 PNGAPI
+png_get_hIST(png_const_structp png_ptr, png_const_infop info_ptr,
+ png_uint_16p *hist)
+{
+ png_debug1(1, "in %s retrieval function", "hIST");
+
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)
+ && hist != NULL)
+ {
+ *hist = info_ptr->hist;
+ return (PNG_INFO_hIST);
+ }
+
+ return (0);
+}
+#endif
+
+png_uint_32 PNGAPI
+png_get_IHDR(png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 *width, png_uint_32 *height, int *bit_depth,
+ int *color_type, int *interlace_type, int *compression_type,
+ int *filter_type)
+
+{
+ png_debug1(1, "in %s retrieval function", "IHDR");
+
+ if (png_ptr == NULL || info_ptr == NULL || width == NULL ||
+ height == NULL || bit_depth == NULL || color_type == NULL)
+ return (0);
+
+ *width = info_ptr->width;
+ *height = info_ptr->height;
+ *bit_depth = info_ptr->bit_depth;
+ *color_type = info_ptr->color_type;
+
+ if (compression_type != NULL)
+ *compression_type = info_ptr->compression_type;
+
+ if (filter_type != NULL)
+ *filter_type = info_ptr->filter_type;
+
+ if (interlace_type != NULL)
+ *interlace_type = info_ptr->interlace_type;
+
+ /* This is redundant if we can be sure that the info_ptr values were all
+ * assigned in png_set_IHDR(). We do the check anyhow in case an
+ * application has ignored our advice not to mess with the members
+ * of info_ptr directly.
+ */
+ png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height,
+ info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type,
+ info_ptr->compression_type, info_ptr->filter_type);
+
+ return (1);
+}
+
+#ifdef PNG_oFFs_SUPPORTED
+png_uint_32 PNGAPI
+png_get_oFFs(png_const_structp png_ptr, png_const_infop info_ptr,
+ png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type)
+{
+ png_debug1(1, "in %s retrieval function", "oFFs");
+
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)
+ && offset_x != NULL && offset_y != NULL && unit_type != NULL)
+ {
+ *offset_x = info_ptr->x_offset;
+ *offset_y = info_ptr->y_offset;
+ *unit_type = (int)info_ptr->offset_unit_type;
+ return (PNG_INFO_oFFs);
+ }
+
+ return (0);
+}
+#endif
+
+#ifdef PNG_pCAL_SUPPORTED
+png_uint_32 PNGAPI
+png_get_pCAL(png_const_structp png_ptr, png_const_infop info_ptr,
+ png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams,
+ png_charp *units, png_charpp *params)
+{
+ png_debug1(1, "in %s retrieval function", "pCAL");
+
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)
+ && purpose != NULL && X0 != NULL && X1 != NULL && type != NULL &&
+ nparams != NULL && units != NULL && params != NULL)
+ {
+ *purpose = info_ptr->pcal_purpose;
+ *X0 = info_ptr->pcal_X0;
+ *X1 = info_ptr->pcal_X1;
+ *type = (int)info_ptr->pcal_type;
+ *nparams = (int)info_ptr->pcal_nparams;
+ *units = info_ptr->pcal_units;
+ *params = info_ptr->pcal_params;
+ return (PNG_INFO_pCAL);
+ }
+
+ return (0);
+}
+#endif
+
+#ifdef PNG_sCAL_SUPPORTED
+# ifdef PNG_FIXED_POINT_SUPPORTED
+# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED
+png_uint_32 PNGAPI
+png_get_sCAL_fixed(png_structp png_ptr, png_const_infop info_ptr,
+ int *unit, png_fixed_point *width, png_fixed_point *height)
+{
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_sCAL))
+ {
+ *unit = info_ptr->scal_unit;
+ /*TODO: make this work without FP support */
+ *width = png_fixed(png_ptr, atof(info_ptr->scal_s_width), "sCAL width");
+ *height = png_fixed(png_ptr, atof(info_ptr->scal_s_height),
+ "sCAL height");
+ return (PNG_INFO_sCAL);
+ }
+
+ return(0);
+}
+# endif /* FLOATING_ARITHMETIC */
+# endif /* FIXED_POINT */
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_sCAL(png_const_structp png_ptr, png_const_infop info_ptr,
+ int *unit, double *width, double *height)
+{
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_sCAL))
+ {
+ *unit = info_ptr->scal_unit;
+ *width = atof(info_ptr->scal_s_width);
+ *height = atof(info_ptr->scal_s_height);
+ return (PNG_INFO_sCAL);
+ }
+
+ return(0);
+}
+# endif /* FLOATING POINT */
+png_uint_32 PNGAPI
+png_get_sCAL_s(png_const_structp png_ptr, png_const_infop info_ptr,
+ int *unit, png_charpp width, png_charpp height)
+{
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_sCAL))
+ {
+ *unit = info_ptr->scal_unit;
+ *width = info_ptr->scal_s_width;
+ *height = info_ptr->scal_s_height;
+ return (PNG_INFO_sCAL);
+ }
+
+ return(0);
+}
+#endif /* sCAL */
+
+#ifdef PNG_pHYs_SUPPORTED
+png_uint_32 PNGAPI
+png_get_pHYs(png_const_structp png_ptr, png_const_infop info_ptr,
+ png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)
+{
+ png_uint_32 retval = 0;
+
+ png_debug1(1, "in %s retrieval function", "pHYs");
+
+ if (png_ptr != NULL && info_ptr != NULL &&
+ (info_ptr->valid & PNG_INFO_pHYs))
+ {
+ if (res_x != NULL)
+ {
+ *res_x = info_ptr->x_pixels_per_unit;
+ retval |= PNG_INFO_pHYs;
+ }
+
+ if (res_y != NULL)
+ {
+ *res_y = info_ptr->y_pixels_per_unit;
+ retval |= PNG_INFO_pHYs;
+ }
+
+ if (unit_type != NULL)
+ {
+ *unit_type = (int)info_ptr->phys_unit_type;
+ retval |= PNG_INFO_pHYs;
+ }
+ }
+
+ return (retval);
+}
+#endif /* pHYs */
+
+png_uint_32 PNGAPI
+png_get_PLTE(png_const_structp png_ptr, png_const_infop info_ptr,
+ png_colorp *palette, int *num_palette)
+{
+ png_debug1(1, "in %s retrieval function", "PLTE");
+
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_PLTE)
+ && palette != NULL)
+ {
+ *palette = info_ptr->palette;
+ *num_palette = info_ptr->num_palette;
+ png_debug1(3, "num_palette = %d", *num_palette);
+ return (PNG_INFO_PLTE);
+ }
+
+ return (0);
+}
+
+#ifdef PNG_sBIT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_sBIT(png_const_structp png_ptr, png_infop info_ptr,
+ png_color_8p *sig_bit)
+{
+ png_debug1(1, "in %s retrieval function", "sBIT");
+
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)
+ && sig_bit != NULL)
+ {
+ *sig_bit = &(info_ptr->sig_bit);
+ return (PNG_INFO_sBIT);
+ }
+
+ return (0);
+}
+#endif
+
+#ifdef PNG_TEXT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_text(png_const_structp png_ptr, png_const_infop info_ptr,
+ png_textp *text_ptr, int *num_text)
+{
+ if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0)
+ {
+ png_debug1(1, "in %s retrieval function",
+ (png_ptr->chunk_name[0] == '\0' ? "text" :
+ (png_const_charp)png_ptr->chunk_name));
+
+ if (text_ptr != NULL)
+ *text_ptr = info_ptr->text;
+
+ if (num_text != NULL)
+ *num_text = info_ptr->num_text;
+
+ return ((png_uint_32)info_ptr->num_text);
+ }
+
+ if (num_text != NULL)
+ *num_text = 0;
+
+ return(0);
+}
+#endif
+
+#ifdef PNG_tIME_SUPPORTED
+png_uint_32 PNGAPI
+png_get_tIME(png_const_structp png_ptr, png_infop info_ptr, png_timep *mod_time)
+{
+ png_debug1(1, "in %s retrieval function", "tIME");
+
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)
+ && mod_time != NULL)
+ {
+ *mod_time = &(info_ptr->mod_time);
+ return (PNG_INFO_tIME);
+ }
+
+ return (0);
+}
+#endif
+
+#ifdef PNG_tRNS_SUPPORTED
+png_uint_32 PNGAPI
+png_get_tRNS(png_const_structp png_ptr, png_infop info_ptr,
+ png_bytep *trans_alpha, int *num_trans, png_color_16p *trans_color)
+{
+ png_uint_32 retval = 0;
+ if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS))
+ {
+ png_debug1(1, "in %s retrieval function", "tRNS");
+
+ if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ if (trans_alpha != NULL)
+ {
+ *trans_alpha = info_ptr->trans_alpha;
+ retval |= PNG_INFO_tRNS;
+ }
+
+ if (trans_color != NULL)
+ *trans_color = &(info_ptr->trans_color);
+ }
+
+ else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */
+ {
+ if (trans_color != NULL)
+ {
+ *trans_color = &(info_ptr->trans_color);
+ retval |= PNG_INFO_tRNS;
+ }
+
+ if (trans_alpha != NULL)
+ *trans_alpha = NULL;
+ }
+
+ if (num_trans != NULL)
+ {
+ *num_trans = info_ptr->num_trans;
+ retval |= PNG_INFO_tRNS;
+ }
+ }
+
+ return (retval);
+}
+#endif
+
+#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
+int PNGAPI
+png_get_unknown_chunks(png_const_structp png_ptr, png_const_infop info_ptr,
+ png_unknown_chunkpp unknowns)
+{
+ if (png_ptr != NULL && info_ptr != NULL && unknowns != NULL)
+ {
+ *unknowns = info_ptr->unknown_chunks;
+ return info_ptr->unknown_chunks_num;
+ }
+
+ return (0);
+}
+#endif
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+png_byte PNGAPI
+png_get_rgb_to_gray_status (png_const_structp png_ptr)
+{
+ return (png_byte)(png_ptr ? png_ptr->rgb_to_gray_status : 0);
+}
+#endif
+
+#ifdef PNG_USER_CHUNKS_SUPPORTED
+png_voidp PNGAPI
+png_get_user_chunk_ptr(png_const_structp png_ptr)
+{
+ return (png_ptr ? png_ptr->user_chunk_ptr : NULL);
+}
+#endif
+
+png_size_t PNGAPI
+png_get_compression_buffer_size(png_const_structp png_ptr)
+{
+ return (png_ptr ? png_ptr->zbuf_size : 0L);
+}
+
+
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+/* These functions were added to libpng 1.2.6 and were enabled
+ * by default in libpng-1.4.0 */
+png_uint_32 PNGAPI
+png_get_user_width_max (png_const_structp png_ptr)
+{
+ return (png_ptr ? png_ptr->user_width_max : 0);
+}
+
+png_uint_32 PNGAPI
+png_get_user_height_max (png_const_structp png_ptr)
+{
+ return (png_ptr ? png_ptr->user_height_max : 0);
+}
+
+/* This function was added to libpng 1.4.0 */
+png_uint_32 PNGAPI
+png_get_chunk_cache_max (png_const_structp png_ptr)
+{
+ return (png_ptr ? png_ptr->user_chunk_cache_max : 0);
+}
+
+/* This function was added to libpng 1.4.1 */
+png_alloc_size_t PNGAPI
+png_get_chunk_malloc_max (png_const_structp png_ptr)
+{
+ return (png_ptr ? png_ptr->user_chunk_malloc_max : 0);
+}
+#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */
+
+/* These functions were added to libpng 1.4.0 */
+#ifdef PNG_IO_STATE_SUPPORTED
+png_uint_32 PNGAPI
+png_get_io_state (png_structp png_ptr)
+{
+ return png_ptr->io_state;
+}
+
+png_uint_32 PNGAPI
+png_get_io_chunk_type (png_const_structp png_ptr)
+{
+ return ((png_ptr->chunk_name[0] << 24) +
+ (png_ptr->chunk_name[1] << 16) +
+ (png_ptr->chunk_name[2] << 8) +
+ (png_ptr->chunk_name[3]));
+}
+
+png_const_bytep PNGAPI
+png_get_io_chunk_name (png_structp png_ptr)
+{
+ return png_ptr->chunk_name;
+}
+#endif /* ?PNG_IO_STATE_SUPPORTED */
+
+#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */
diff --git a/libpng/pnginfo.h b/libpng/pnginfo.h
new file mode 100644
index 0000000..fa19f85
--- /dev/null
+++ b/libpng/pnginfo.h
@@ -0,0 +1,270 @@
+
+/* pnginfo.h - header file for PNG reference library
+ *
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * Last changed in libpng 1.5.0 [January 6, 2011]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+ /* png_info is a structure that holds the information in a PNG file so
+ * that the application can find out the characteristics of the image.
+ * If you are reading the file, this structure will tell you what is
+ * in the PNG file. If you are writing the file, fill in the information
+ * you want to put into the PNG file, using png_set_*() functions, then
+ * call png_write_info().
+ *
+ * The names chosen should be very close to the PNG specification, so
+ * consult that document for information about the meaning of each field.
+ *
+ * With libpng < 0.95, it was only possible to directly set and read the
+ * the values in the png_info_struct, which meant that the contents and
+ * order of the values had to remain fixed. With libpng 0.95 and later,
+ * however, there are now functions that abstract the contents of
+ * png_info_struct from the application, so this makes it easier to use
+ * libpng with dynamic libraries, and even makes it possible to use
+ * libraries that don't have all of the libpng ancillary chunk-handing
+ * functionality. In libpng-1.5.0 this was moved into a separate private
+ * file that is not visible to applications.
+ *
+ * The following members may have allocated storage attached that should be
+ * cleaned up before the structure is discarded: palette, trans, text,
+ * pcal_purpose, pcal_units, pcal_params, hist, iccp_name, iccp_profile,
+ * splt_palettes, scal_unit, row_pointers, and unknowns. By default, these
+ * are automatically freed when the info structure is deallocated, if they were
+ * allocated internally by libpng. This behavior can be changed by means
+ * of the png_data_freer() function.
+ *
+ * More allocation details: all the chunk-reading functions that
+ * change these members go through the corresponding png_set_*
+ * functions. A function to clear these members is available: see
+ * png_free_data(). The png_set_* functions do not depend on being
+ * able to point info structure members to any of the storage they are
+ * passed (they make their own copies), EXCEPT that the png_set_text
+ * functions use the same storage passed to them in the text_ptr or
+ * itxt_ptr structure argument, and the png_set_rows and png_set_unknowns
+ * functions do not make their own copies.
+ */
+#ifndef PNGINFO_H
+#define PNGINFO_H
+
+struct png_info_def
+{
+ /* the following are necessary for every PNG file */
+ png_uint_32 width; /* width of image in pixels (from IHDR) */
+ png_uint_32 height; /* height of image in pixels (from IHDR) */
+ png_uint_32 valid; /* valid chunk data (see PNG_INFO_ below) */
+ png_size_t rowbytes; /* bytes needed to hold an untransformed row */
+ png_colorp palette; /* array of color values (valid & PNG_INFO_PLTE) */
+ png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */
+ png_uint_16 num_trans; /* number of transparent palette color (tRNS) */
+ png_byte bit_depth; /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */
+ png_byte color_type; /* see PNG_COLOR_TYPE_ below (from IHDR) */
+ /* The following three should have been named *_method not *_type */
+ png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */
+ png_byte filter_type; /* must be PNG_FILTER_TYPE_BASE (from IHDR) */
+ png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
+
+ /* The following is informational only on read, and not used on writes. */
+ png_byte channels; /* number of data channels per pixel (1, 2, 3, 4) */
+ png_byte pixel_depth; /* number of bits per pixel */
+ png_byte spare_byte; /* to align the data, and for future use */
+ png_byte signature[8]; /* magic bytes read by libpng from start of file */
+
+ /* The rest of the data is optional. If you are reading, check the
+ * valid field to see if the information in these are valid. If you
+ * are writing, set the valid field to those chunks you want written,
+ * and initialize the appropriate fields below.
+ */
+
+#if defined(PNG_gAMA_SUPPORTED)
+ /* The gAMA chunk describes the gamma characteristics of the system
+ * on which the image was created, normally in the range [1.0, 2.5].
+ * Data is valid if (valid & PNG_INFO_gAMA) is non-zero.
+ */
+ png_fixed_point gamma;
+#endif
+
+#ifdef PNG_sRGB_SUPPORTED
+ /* GR-P, 0.96a */
+ /* Data valid if (valid & PNG_INFO_sRGB) non-zero. */
+ png_byte srgb_intent; /* sRGB rendering intent [0, 1, 2, or 3] */
+#endif
+
+#ifdef PNG_TEXT_SUPPORTED
+ /* The tEXt, and zTXt chunks contain human-readable textual data in
+ * uncompressed, compressed, and optionally compressed forms, respectively.
+ * The data in "text" is an array of pointers to uncompressed,
+ * null-terminated C strings. Each chunk has a keyword that describes the
+ * textual data contained in that chunk. Keywords are not required to be
+ * unique, and the text string may be empty. Any number of text chunks may
+ * be in an image.
+ */
+ int num_text; /* number of comments read or comments to write */
+ int max_text; /* current size of text array */
+ png_textp text; /* array of comments read or comments to write */
+#endif /* PNG_TEXT_SUPPORTED */
+
+#ifdef PNG_tIME_SUPPORTED
+ /* The tIME chunk holds the last time the displayed image data was
+ * modified. See the png_time struct for the contents of this struct.
+ */
+ png_time mod_time;
+#endif
+
+#ifdef PNG_sBIT_SUPPORTED
+ /* The sBIT chunk specifies the number of significant high-order bits
+ * in the pixel data. Values are in the range [1, bit_depth], and are
+ * only specified for the channels in the pixel data. The contents of
+ * the low-order bits is not specified. Data is valid if
+ * (valid & PNG_INFO_sBIT) is non-zero.
+ */
+ png_color_8 sig_bit; /* significant bits in color channels */
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_EXPAND_SUPPORTED) || \
+defined(PNG_READ_BACKGROUND_SUPPORTED)
+ /* The tRNS chunk supplies transparency data for paletted images and
+ * other image types that don't need a full alpha channel. There are
+ * "num_trans" transparency values for a paletted image, stored in the
+ * same order as the palette colors, starting from index 0. Values
+ * for the data are in the range [0, 255], ranging from fully transparent
+ * to fully opaque, respectively. For non-paletted images, there is a
+ * single color specified that should be treated as fully transparent.
+ * Data is valid if (valid & PNG_INFO_tRNS) is non-zero.
+ */
+ png_bytep trans; /* alpha values for paletted image */
+ png_bytep trans_alpha; /* alpha values for paletted image */
+ png_color_16 trans_color; /* transparent color for non-palette image */
+#endif
+
+#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+ /* The bKGD chunk gives the suggested image background color if the
+ * display program does not have its own background color and the image
+ * is needs to composited onto a background before display. The colors
+ * in "background" are normally in the same color space/depth as the
+ * pixel data. Data is valid if (valid & PNG_INFO_bKGD) is non-zero.
+ */
+ png_color_16 background;
+#endif
+
+#ifdef PNG_oFFs_SUPPORTED
+ /* The oFFs chunk gives the offset in "offset_unit_type" units rightwards
+ * and downwards from the top-left corner of the display, page, or other
+ * application-specific co-ordinate space. See the PNG_OFFSET_ defines
+ * below for the unit types. Valid if (valid & PNG_INFO_oFFs) non-zero.
+ */
+ png_int_32 x_offset; /* x offset on page */
+ png_int_32 y_offset; /* y offset on page */
+ png_byte offset_unit_type; /* offset units type */
+#endif
+
+#ifdef PNG_pHYs_SUPPORTED
+ /* The pHYs chunk gives the physical pixel density of the image for
+ * display or printing in "phys_unit_type" units (see PNG_RESOLUTION_
+ * defines below). Data is valid if (valid & PNG_INFO_pHYs) is non-zero.
+ */
+ png_uint_32 x_pixels_per_unit; /* horizontal pixel density */
+ png_uint_32 y_pixels_per_unit; /* vertical pixel density */
+ png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */
+#endif
+
+#ifdef PNG_hIST_SUPPORTED
+ /* The hIST chunk contains the relative frequency or importance of the
+ * various palette entries, so that a viewer can intelligently select a
+ * reduced-color palette, if required. Data is an array of "num_palette"
+ * values in the range [0,65535]. Data valid if (valid & PNG_INFO_hIST)
+ * is non-zero.
+ */
+ png_uint_16p hist;
+#endif
+
+#ifdef PNG_cHRM_SUPPORTED
+ /* The cHRM chunk describes the CIE color characteristics of the monitor
+ * on which the PNG was created. This data allows the viewer to do gamut
+ * mapping of the input image to ensure that the viewer sees the same
+ * colors in the image as the creator. Values are in the range
+ * [0.0, 0.8]. Data valid if (valid & PNG_INFO_cHRM) non-zero.
+ */
+ png_fixed_point x_white;
+ png_fixed_point y_white;
+ png_fixed_point x_red;
+ png_fixed_point y_red;
+ png_fixed_point x_green;
+ png_fixed_point y_green;
+ png_fixed_point x_blue;
+ png_fixed_point y_blue;
+#endif
+
+#ifdef PNG_pCAL_SUPPORTED
+ /* The pCAL chunk describes a transformation between the stored pixel
+ * values and original physical data values used to create the image.
+ * The integer range [0, 2^bit_depth - 1] maps to the floating-point
+ * range given by [pcal_X0, pcal_X1], and are further transformed by a
+ * (possibly non-linear) transformation function given by "pcal_type"
+ * and "pcal_params" into "pcal_units". Please see the PNG_EQUATION_
+ * defines below, and the PNG-Group's PNG extensions document for a
+ * complete description of the transformations and how they should be
+ * implemented, and for a description of the ASCII parameter strings.
+ * Data values are valid if (valid & PNG_INFO_pCAL) non-zero.
+ */
+ png_charp pcal_purpose; /* pCAL chunk description string */
+ png_int_32 pcal_X0; /* minimum value */
+ png_int_32 pcal_X1; /* maximum value */
+ png_charp pcal_units; /* Latin-1 string giving physical units */
+ png_charpp pcal_params; /* ASCII strings containing parameter values */
+ png_byte pcal_type; /* equation type (see PNG_EQUATION_ below) */
+ png_byte pcal_nparams; /* number of parameters given in pcal_params */
+#endif
+
+/* New members added in libpng-1.0.6 */
+ png_uint_32 free_me; /* flags items libpng is responsible for freeing */
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) || \
+ defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED)
+ /* Storage for unknown chunks that the library doesn't recognize. */
+ png_unknown_chunkp unknown_chunks;
+ int unknown_chunks_num;
+#endif
+
+#ifdef PNG_iCCP_SUPPORTED
+ /* iCCP chunk data. */
+ png_charp iccp_name; /* profile name */
+ png_bytep iccp_profile; /* International Color Consortium profile data */
+ png_uint_32 iccp_proflen; /* ICC profile data length */
+ png_byte iccp_compression; /* Always zero */
+#endif
+
+#ifdef PNG_sPLT_SUPPORTED
+ /* Data on sPLT chunks (there may be more than one). */
+ png_sPLT_tp splt_palettes;
+ png_uint_32 splt_palettes_num;
+#endif
+
+#ifdef PNG_sCAL_SUPPORTED
+ /* The sCAL chunk describes the actual physical dimensions of the
+ * subject matter of the graphic. The chunk contains a unit specification
+ * a byte value, and two ASCII strings representing floating-point
+ * values. The values are width and height corresponsing to one pixel
+ * in the image. Data values are valid if (valid & PNG_INFO_sCAL) is
+ * non-zero.
+ */
+ png_byte scal_unit; /* unit of physical scale */
+ png_charp scal_s_width; /* string containing height */
+ png_charp scal_s_height; /* string containing width */
+#endif
+
+#ifdef PNG_INFO_IMAGE_SUPPORTED
+ /* Memory has been allocated if (valid & PNG_ALLOCATED_INFO_ROWS)
+ non-zero */
+ /* Data valid if (valid & PNG_INFO_IDAT) non-zero */
+ png_bytepp row_pointers; /* the image bits */
+#endif
+
+};
+#endif /* PNGINFO_H */
diff --git a/libpng/pnglibconf.h b/libpng/pnglibconf.h
new file mode 100644
index 0000000..5f0c852
--- /dev/null
+++ b/libpng/pnglibconf.h
@@ -0,0 +1,181 @@
+
+/* libpng STANDARD API DEFINITION */
+
+/* pnglibconf.h - library build configuration */
+
+/* libpng version 1.5.0 - last changed on February 11, 2011 */
+
+/* Copyright (c) 1998-2011 Glenn Randers-Pehrson */
+
+/* This code is released under the libpng license. */
+/* For conditions of distribution and use, see the disclaimer */
+/* and license in png.h */
+
+/* pnglibconf.h */
+/* Derived from: scripts/pnglibconf.dfa */
+/* If you edit this file by hand you must obey the rules expressed in */
+/* pnglibconf.dfa with respect to the dependencies between the following */
+/* symbols. It is much better to generate a new file using */
+/* scripts/libpngconf.mak */
+
+#ifndef PNGLCONF_H
+#define PNGLCONF_H
+/* settings */
+#define PNG_API_RULE 0
+#define PNG_CALLOC_SUPPORTED
+#define PNG_COST_SHIFT 3
+#define PNG_DEFAULT_READ_MACROS 1
+#define PNG_GAMMA_THRESHOLD_FIXED 5000
+#define PNG_MAX_GAMMA_8 11
+#define PNG_QUANTIZE_BLUE_BITS 5
+#define PNG_QUANTIZE_GREEN_BITS 5
+#define PNG_QUANTIZE_RED_BITS 5
+#define PNG_sCAL_PRECISION 5
+#define PNG_USER_CHUNK_CACHE_MAX 0
+#define PNG_USER_CHUNK_MALLOC_MAX 0
+#define PNG_USER_HEIGHT_MAX 1000000L
+#define PNG_USER_WIDTH_MAX 1000000L
+#define PNG_WEIGHT_SHIFT 8
+#define PNG_ZBUF_SIZE 8192
+/* end of settings */
+/* options */
+#define PNG_16BIT_SUPPORTED
+#define PNG_ALIGN_MEMORY_SUPPORTED
+#define PNG_BENIGN_ERRORS_SUPPORTED
+#define PNG_bKGD_SUPPORTED
+#define PNG_CHECK_cHRM_SUPPORTED
+#define PNG_cHRM_SUPPORTED
+#define PNG_CONSOLE_IO_SUPPORTED
+#define PNG_CONVERT_tIME_SUPPORTED
+#define PNG_EASY_ACCESS_SUPPORTED
+#define PNG_ERROR_TEXT_SUPPORTED
+#define PNG_FIXED_POINT_SUPPORTED
+#define PNG_FLOATING_ARITHMETIC_SUPPORTED
+#define PNG_FLOATING_POINT_SUPPORTED
+#define PNG_gAMA_SUPPORTED
+#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+#define PNG_hIST_SUPPORTED
+#define PNG_iCCP_SUPPORTED
+#define PNG_INCH_CONVERSIONS_SUPPORTED
+#define PNG_INFO_IMAGE_SUPPORTED
+#define PNG_IO_STATE_SUPPORTED
+#define PNG_iTXt_SUPPORTED
+#define PNG_MNG_FEATURES_SUPPORTED
+#define PNG_oFFs_SUPPORTED
+#define PNG_pCAL_SUPPORTED
+#define PNG_pHYs_SUPPORTED
+#define PNG_POINTER_INDEXING_SUPPORTED
+#define PNG_PROGRESSIVE_READ_SUPPORTED
+#define PNG_READ_16BIT_SUPPORTED
+#define PNG_READ_16_TO_8_SUPPORTED
+#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED
+#define PNG_READ_BACKGROUND_SUPPORTED
+#define PNG_READ_BGR_SUPPORTED
+#define PNG_READ_bKGD_SUPPORTED
+#define PNG_READ_cHRM_SUPPORTED
+#define PNG_READ_COMPOSITE_NODIV_SUPPORTED
+#define PNG_READ_EXPAND_16_SUPPORTED
+#define PNG_READ_EXPAND_SUPPORTED
+#define PNG_READ_FILLER_SUPPORTED
+#define PNG_READ_gAMA_SUPPORTED
+#define PNG_READ_GAMMA_SUPPORTED
+#define PNG_READ_GRAY_TO_RGB_SUPPORTED
+#define PNG_READ_hIST_SUPPORTED
+#define PNG_READ_iCCP_SUPPORTED
+#define PNG_READ_INTERLACING_SUPPORTED
+#define PNG_READ_INT_FUNCTIONS_SUPPORTED
+#define PNG_READ_INVERT_ALPHA_SUPPORTED
+#define PNG_READ_INVERT_SUPPORTED
+#define PNG_READ_iTXt_SUPPORTED
+#define PNG_READ_oFFs_SUPPORTED
+#define PNG_READ_OPT_PLTE_SUPPORTED
+#define PNG_READ_PACK_SUPPORTED
+#define PNG_READ_PACKSWAP_SUPPORTED
+#define PNG_READ_pCAL_SUPPORTED
+#define PNG_READ_pHYs_SUPPORTED
+#define PNG_READ_QUANTIZE_SUPPORTED
+#define PNG_READ_RGB_TO_GRAY_SUPPORTED
+#define PNG_READ_sBIT_SUPPORTED
+#define PNG_READ_sCAL_SUPPORTED
+#define PNG_READ_SHIFT_SUPPORTED
+#define PNG_READ_sPLT_SUPPORTED
+#define PNG_READ_sRGB_SUPPORTED
+#define PNG_READ_STRIP_ALPHA_SUPPORTED
+#define PNG_READ_SUPPORTED
+#define PNG_READ_SWAP_ALPHA_SUPPORTED
+#define PNG_READ_SWAP_SUPPORTED
+#define PNG_READ_tEXt_SUPPORTED
+#define PNG_READ_TEXT_SUPPORTED
+#define PNG_READ_tIME_SUPPORTED
+#define PNG_READ_TRANSFORMS_SUPPORTED
+#define PNG_READ_tRNS_SUPPORTED
+#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_READ_USER_CHUNKS_SUPPORTED
+#define PNG_READ_USER_TRANSFORM_SUPPORTED
+#define PNG_READ_zTXt_SUPPORTED
+#define PNG_SAVE_INT_32_SUPPORTED
+#define PNG_sBIT_SUPPORTED
+#define PNG_sCAL_SUPPORTED
+#define PNG_SEQUENTIAL_READ_SUPPORTED
+#define PNG_SET_CHUNK_CACHE_LIMIT_SUPPORTED
+#define PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED
+#define PNG_SETJMP_SUPPORTED
+#define PNG_SET_USER_LIMITS_SUPPORTED
+#define PNG_sPLT_SUPPORTED
+#define PNG_sRGB_SUPPORTED
+#define PNG_STDIO_SUPPORTED
+#define PNG_tEXt_SUPPORTED
+#define PNG_TEXT_SUPPORTED
+#define PNG_TIME_RFC1123_SUPPORTED
+#define PNG_tIME_SUPPORTED
+#define PNG_tRNS_SUPPORTED
+#define PNG_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_USER_CHUNKS_SUPPORTED
+#define PNG_USER_LIMITS_SUPPORTED
+#define PNG_USER_MEM_SUPPORTED
+#define PNG_USER_TRANSFORM_INFO_SUPPORTED
+#define PNG_USER_TRANSFORM_PTR_SUPPORTED
+#define PNG_WARNINGS_SUPPORTED
+#define PNG_WRITE_16BIT_SUPPORTED
+#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED
+#define PNG_WRITE_BGR_SUPPORTED
+#define PNG_WRITE_bKGD_SUPPORTED
+#define PNG_WRITE_cHRM_SUPPORTED
+#define PNG_WRITE_FILLER_SUPPORTED
+#define PNG_WRITE_FILTER_SUPPORTED
+#define PNG_WRITE_FLUSH_SUPPORTED
+#define PNG_WRITE_gAMA_SUPPORTED
+#define PNG_WRITE_hIST_SUPPORTED
+#define PNG_WRITE_iCCP_SUPPORTED
+#define PNG_WRITE_INTERLACING_SUPPORTED
+#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED
+#define PNG_WRITE_INVERT_ALPHA_SUPPORTED
+#define PNG_WRITE_INVERT_SUPPORTED
+#define PNG_WRITE_iTXt_SUPPORTED
+#define PNG_WRITE_oFFs_SUPPORTED
+#define PNG_WRITE_PACK_SUPPORTED
+#define PNG_WRITE_PACKSWAP_SUPPORTED
+#define PNG_WRITE_pCAL_SUPPORTED
+#define PNG_WRITE_pHYs_SUPPORTED
+#define PNG_WRITE_sBIT_SUPPORTED
+#define PNG_WRITE_sCAL_SUPPORTED
+#define PNG_WRITE_SHIFT_SUPPORTED
+#define PNG_WRITE_sPLT_SUPPORTED
+#define PNG_WRITE_sRGB_SUPPORTED
+#define PNG_WRITE_SUPPORTED
+#define PNG_WRITE_SWAP_ALPHA_SUPPORTED
+#define PNG_WRITE_SWAP_SUPPORTED
+#define PNG_WRITE_tEXt_SUPPORTED
+#define PNG_WRITE_TEXT_SUPPORTED
+#define PNG_WRITE_tIME_SUPPORTED
+#define PNG_WRITE_TRANSFORMS_SUPPORTED
+#define PNG_WRITE_tRNS_SUPPORTED
+#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_WRITE_USER_TRANSFORM_SUPPORTED
+#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+#define PNG_WRITE_zTXt_SUPPORTED
+#define PNG_zTXt_SUPPORTED
+/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/
+/*#undef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED*/
+/* end of options */
+#endif /* PNGLCONF_H */
diff --git a/libpng/pngmem.c b/libpng/pngmem.c
new file mode 100644
index 0000000..a15d8b0
--- /dev/null
+++ b/libpng/pngmem.c
@@ -0,0 +1,658 @@
+
+/* pngmem.c - stub functions for memory allocation
+ *
+ * Last changed in libpng 1.5.1 [February 3, 2011]
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * This file provides a location for all memory allocation. Users who
+ * need special memory handling are expected to supply replacement
+ * functions for png_malloc() and png_free(), and to use
+ * png_create_read_struct_2() and png_create_write_struct_2() to
+ * identify the replacement functions.
+ */
+
+#include "pngpriv.h"
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+
+/* Borland DOS special memory handler */
+#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__)
+/* If you change this, be sure to change the one in png.h also */
+
+/* Allocate memory for a png_struct. The malloc and memset can be replaced
+ by a single call to calloc() if this is thought to improve performance. */
+PNG_FUNCTION(png_voidp /* PRIVATE */,
+png_create_struct,(int type),PNG_ALLOCATED)
+{
+# ifdef PNG_USER_MEM_SUPPORTED
+ return (png_create_struct_2(type, NULL, NULL));
+}
+
+/* Alternate version of png_create_struct, for use with user-defined malloc. */
+PNG_FUNCTION(png_voidp /* PRIVATE */,
+png_create_struct_2,(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr),
+ PNG_ALLOCATED)
+{
+# endif /* PNG_USER_MEM_SUPPORTED */
+ png_size_t size;
+ png_voidp struct_ptr;
+
+ if (type == PNG_STRUCT_INFO)
+ size = png_sizeof(png_info);
+
+ else if (type == PNG_STRUCT_PNG)
+ size = png_sizeof(png_struct);
+
+ else
+ return (png_get_copyright(NULL));
+
+# ifdef PNG_USER_MEM_SUPPORTED
+ if (malloc_fn != NULL)
+ {
+ png_struct dummy_struct;
+ png_structp png_ptr = &dummy_struct;
+ png_ptr->mem_ptr=mem_ptr;
+ struct_ptr = (*(malloc_fn))(png_ptr, (png_uint_32)size);
+ }
+
+ else
+# endif /* PNG_USER_MEM_SUPPORTED */
+ struct_ptr = (png_voidp)farmalloc(size);
+ if (struct_ptr != NULL)
+ png_memset(struct_ptr, 0, size);
+
+ return (struct_ptr);
+}
+
+/* Free memory allocated by a png_create_struct() call */
+void /* PRIVATE */
+png_destroy_struct(png_voidp struct_ptr)
+{
+# ifdef PNG_USER_MEM_SUPPORTED
+ png_destroy_struct_2(struct_ptr, NULL, NULL);
+}
+
+/* Free memory allocated by a png_create_struct() call */
+void /* PRIVATE */
+png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn,
+ png_voidp mem_ptr)
+{
+# endif
+ if (struct_ptr != NULL)
+ {
+# ifdef PNG_USER_MEM_SUPPORTED
+ if (free_fn != NULL)
+ {
+ png_struct dummy_struct;
+ png_structp png_ptr = &dummy_struct;
+ png_ptr->mem_ptr=mem_ptr;
+ (*(free_fn))(png_ptr, struct_ptr);
+ return;
+ }
+
+# endif /* PNG_USER_MEM_SUPPORTED */
+ farfree (struct_ptr);
+ }
+}
+
+/* Allocate memory. For reasonable files, size should never exceed
+ * 64K. However, zlib may allocate more then 64K if you don't tell
+ * it not to. See zconf.h and png.h for more information. zlib does
+ * need to allocate exactly 64K, so whatever you call here must
+ * have the ability to do that.
+ *
+ * Borland seems to have a problem in DOS mode for exactly 64K.
+ * It gives you a segment with an offset of 8 (perhaps to store its
+ * memory stuff). zlib doesn't like this at all, so we have to
+ * detect and deal with it. This code should not be needed in
+ * Windows or OS/2 modes, and only in 16 bit mode. This code has
+ * been updated by Alexander Lehmann for version 0.89 to waste less
+ * memory.
+ *
+ * Note that we can't use png_size_t for the "size" declaration,
+ * since on some systems a png_size_t is a 16-bit quantity, and as a
+ * result, we would be truncating potentially larger memory requests
+ * (which should cause a fatal error) and introducing major problems.
+ */
+PNG_FUNCTION(png_voidp,PNGAPI
+png_calloc,(png_structp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
+{
+ png_voidp ret;
+
+ ret = (png_malloc(png_ptr, size));
+
+ if (ret != NULL)
+ png_memset(ret,0,(png_size_t)size);
+
+ return (ret);
+}
+
+PNG_FUNCTION(png_voidp,PNGAPI
+png_malloc,(png_structp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
+{
+ png_voidp ret;
+
+ if (png_ptr == NULL || size == 0)
+ return (NULL);
+
+# ifdef PNG_USER_MEM_SUPPORTED
+ if (png_ptr->malloc_fn != NULL)
+ ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size));
+
+ else
+ ret = (png_malloc_default(png_ptr, size));
+
+ if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+ png_error(png_ptr, "Out of memory");
+
+ return (ret);
+}
+
+PNG_FUNCTION(png_voidp,PNGAPI
+png_malloc_default,(png_structp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
+{
+ png_voidp ret;
+# endif /* PNG_USER_MEM_SUPPORTED */
+
+ if (png_ptr == NULL || size == 0)
+ return (NULL);
+
+# ifdef PNG_MAX_MALLOC_64K
+ if (size > (png_uint_32)65536L)
+ {
+ png_warning(png_ptr, "Cannot Allocate > 64K");
+ ret = NULL;
+ }
+
+ else
+# endif
+
+ if (size != (size_t)size)
+ ret = NULL;
+
+ else if (size == (png_uint_32)65536L)
+ {
+ if (png_ptr->offset_table == NULL)
+ {
+ /* Try to see if we need to do any of this fancy stuff */
+ ret = farmalloc(size);
+ if (ret == NULL || ((png_size_t)ret & 0xffff))
+ {
+ int num_blocks;
+ png_uint_32 total_size;
+ png_bytep table;
+ int i;
+ png_byte huge * hptr;
+
+ if (ret != NULL)
+ {
+ farfree(ret);
+ ret = NULL;
+ }
+
+ if (png_ptr->zlib_window_bits > 14)
+ num_blocks = (int)(1 << (png_ptr->zlib_window_bits - 14));
+
+ else
+ num_blocks = 1;
+
+ if (png_ptr->zlib_mem_level >= 7)
+ num_blocks += (int)(1 << (png_ptr->zlib_mem_level - 7));
+
+ else
+ num_blocks++;
+
+ total_size = ((png_uint_32)65536L) * (png_uint_32)num_blocks+16;
+
+ table = farmalloc(total_size);
+
+ if (table == NULL)
+ {
+# ifndef PNG_USER_MEM_SUPPORTED
+ if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+ png_error(png_ptr, "Out Of Memory"); /* Note "O", "M" */
+
+ else
+ png_warning(png_ptr, "Out Of Memory");
+# endif
+ return (NULL);
+ }
+
+ if ((png_size_t)table & 0xfff0)
+ {
+# ifndef PNG_USER_MEM_SUPPORTED
+ if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+ png_error(png_ptr,
+ "Farmalloc didn't return normalized pointer");
+
+ else
+ png_warning(png_ptr,
+ "Farmalloc didn't return normalized pointer");
+# endif
+ return (NULL);
+ }
+
+ png_ptr->offset_table = table;
+ png_ptr->offset_table_ptr = farmalloc(num_blocks *
+ png_sizeof(png_bytep));
+
+ if (png_ptr->offset_table_ptr == NULL)
+ {
+# ifndef PNG_USER_MEM_SUPPORTED
+ if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+ png_error(png_ptr, "Out Of memory"); /* Note "O", "m" */
+
+ else
+ png_warning(png_ptr, "Out Of memory");
+# endif
+ return (NULL);
+ }
+
+ hptr = (png_byte huge *)table;
+ if ((png_size_t)hptr & 0xf)
+ {
+ hptr = (png_byte huge *)((long)(hptr) & 0xfffffff0L);
+ hptr = hptr + 16L; /* "hptr += 16L" fails on Turbo C++ 3.0 */
+ }
+
+ for (i = 0; i < num_blocks; i++)
+ {
+ png_ptr->offset_table_ptr[i] = (png_bytep)hptr;
+ hptr = hptr + (png_uint_32)65536L; /* "+=" fails on TC++3.0 */
+ }
+
+ png_ptr->offset_table_number = num_blocks;
+ png_ptr->offset_table_count = 0;
+ png_ptr->offset_table_count_free = 0;
+ }
+ }
+
+ if (png_ptr->offset_table_count >= png_ptr->offset_table_number)
+ {
+# ifndef PNG_USER_MEM_SUPPORTED
+ if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+ png_error(png_ptr, "Out of Memory"); /* Note "o" and "M" */
+
+ else
+ png_warning(png_ptr, "Out of Memory");
+# endif
+ return (NULL);
+ }
+
+ ret = png_ptr->offset_table_ptr[png_ptr->offset_table_count++];
+ }
+
+ else
+ ret = farmalloc(size);
+
+# ifndef PNG_USER_MEM_SUPPORTED
+ if (ret == NULL)
+ {
+ if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+ png_error(png_ptr, "Out of memory"); /* Note "o" and "m" */
+
+ else
+ png_warning(png_ptr, "Out of memory"); /* Note "o" and "m" */
+ }
+# endif
+
+ return (ret);
+}
+
+/* Free a pointer allocated by png_malloc(). In the default
+ * configuration, png_ptr is not used, but is passed in case it
+ * is needed. If ptr is NULL, return without taking any action.
+ */
+void PNGAPI
+png_free(png_structp png_ptr, png_voidp ptr)
+{
+ if (png_ptr == NULL || ptr == NULL)
+ return;
+
+# ifdef PNG_USER_MEM_SUPPORTED
+ if (png_ptr->free_fn != NULL)
+ {
+ (*(png_ptr->free_fn))(png_ptr, ptr);
+ return;
+ }
+
+ else
+ png_free_default(png_ptr, ptr);
+}
+
+void PNGAPI
+png_free_default(png_structp png_ptr, png_voidp ptr)
+{
+# endif /* PNG_USER_MEM_SUPPORTED */
+
+ if (png_ptr == NULL || ptr == NULL)
+ return;
+
+ if (png_ptr->offset_table != NULL)
+ {
+ int i;
+
+ for (i = 0; i < png_ptr->offset_table_count; i++)
+ {
+ if (ptr == png_ptr->offset_table_ptr[i])
+ {
+ ptr = NULL;
+ png_ptr->offset_table_count_free++;
+ break;
+ }
+ }
+ if (png_ptr->offset_table_count_free == png_ptr->offset_table_count)
+ {
+ farfree(png_ptr->offset_table);
+ farfree(png_ptr->offset_table_ptr);
+ png_ptr->offset_table = NULL;
+ png_ptr->offset_table_ptr = NULL;
+ }
+ }
+
+ if (ptr != NULL)
+ farfree(ptr);
+}
+
+#else /* Not the Borland DOS special memory handler */
+
+/* Allocate memory for a png_struct or a png_info. The malloc and
+ memset can be replaced by a single call to calloc() if this is thought
+ to improve performance noticably. */
+PNG_FUNCTION(png_voidp /* PRIVATE */,
+png_create_struct,(int type),PNG_ALLOCATED)
+{
+# ifdef PNG_USER_MEM_SUPPORTED
+ return (png_create_struct_2(type, NULL, NULL));
+}
+
+/* Allocate memory for a png_struct or a png_info. The malloc and
+ memset can be replaced by a single call to calloc() if this is thought
+ to improve performance noticably. */
+PNG_FUNCTION(png_voidp /* PRIVATE */,
+png_create_struct_2,(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr),
+ PNG_ALLOCATED)
+{
+# endif /* PNG_USER_MEM_SUPPORTED */
+ png_size_t size;
+ png_voidp struct_ptr;
+
+ if (type == PNG_STRUCT_INFO)
+ size = png_sizeof(png_info);
+
+ else if (type == PNG_STRUCT_PNG)
+ size = png_sizeof(png_struct);
+
+ else
+ return (NULL);
+
+# ifdef PNG_USER_MEM_SUPPORTED
+ if (malloc_fn != NULL)
+ {
+ png_struct dummy_struct;
+ png_structp png_ptr = &dummy_struct;
+ png_ptr->mem_ptr=mem_ptr;
+ struct_ptr = (*(malloc_fn))(png_ptr, size);
+
+ if (struct_ptr != NULL)
+ png_memset(struct_ptr, 0, size);
+
+ return (struct_ptr);
+ }
+# endif /* PNG_USER_MEM_SUPPORTED */
+
+# if defined(__TURBOC__) && !defined(__FLAT__)
+ struct_ptr = (png_voidp)farmalloc(size);
+# else
+# if defined(_MSC_VER) && defined(MAXSEG_64K)
+ struct_ptr = (png_voidp)halloc(size, 1);
+# else
+ struct_ptr = (png_voidp)malloc(size);
+# endif
+# endif
+
+ if (struct_ptr != NULL)
+ png_memset(struct_ptr, 0, size);
+
+ return (struct_ptr);
+}
+
+
+/* Free memory allocated by a png_create_struct() call */
+void /* PRIVATE */
+png_destroy_struct(png_voidp struct_ptr)
+{
+# ifdef PNG_USER_MEM_SUPPORTED
+ png_destroy_struct_2(struct_ptr, NULL, NULL);
+}
+
+/* Free memory allocated by a png_create_struct() call */
+void /* PRIVATE */
+png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn,
+ png_voidp mem_ptr)
+{
+# endif /* PNG_USER_MEM_SUPPORTED */
+ if (struct_ptr != NULL)
+ {
+# ifdef PNG_USER_MEM_SUPPORTED
+ if (free_fn != NULL)
+ {
+ png_struct dummy_struct;
+ png_structp png_ptr = &dummy_struct;
+ png_ptr->mem_ptr=mem_ptr;
+ (*(free_fn))(png_ptr, struct_ptr);
+ return;
+ }
+# endif /* PNG_USER_MEM_SUPPORTED */
+# if defined(__TURBOC__) && !defined(__FLAT__)
+ farfree(struct_ptr);
+
+# else
+# if defined(_MSC_VER) && defined(MAXSEG_64K)
+ hfree(struct_ptr);
+
+# else
+ free(struct_ptr);
+
+# endif
+# endif
+ }
+}
+
+/* Allocate memory. For reasonable files, size should never exceed
+ * 64K. However, zlib may allocate more then 64K if you don't tell
+ * it not to. See zconf.h and png.h for more information. zlib does
+ * need to allocate exactly 64K, so whatever you call here must
+ * have the ability to do that.
+ */
+
+PNG_FUNCTION(png_voidp,PNGAPI
+png_calloc,(png_structp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
+{
+ png_voidp ret;
+
+ ret = (png_malloc(png_ptr, size));
+
+ if (ret != NULL)
+ png_memset(ret,0,(png_size_t)size);
+
+ return (ret);
+}
+
+PNG_FUNCTION(png_voidp,PNGAPI
+png_malloc,(png_structp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
+{
+ png_voidp ret;
+
+# ifdef PNG_USER_MEM_SUPPORTED
+ if (png_ptr == NULL || size == 0)
+ return (NULL);
+
+ if (png_ptr->malloc_fn != NULL)
+ ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size));
+
+ else
+ ret = (png_malloc_default(png_ptr, size));
+
+ if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+ png_error(png_ptr, "Out of Memory");
+
+ return (ret);
+}
+
+PNG_FUNCTION(png_voidp,PNGAPI
+png_malloc_default,(png_structp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
+{
+ png_voidp ret;
+# endif /* PNG_USER_MEM_SUPPORTED */
+
+ if (png_ptr == NULL || size == 0)
+ return (NULL);
+
+# ifdef PNG_MAX_MALLOC_64K
+ if (size > (png_uint_32)65536L)
+ {
+# ifndef PNG_USER_MEM_SUPPORTED
+ if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+ png_error(png_ptr, "Cannot Allocate > 64K");
+
+ else
+# endif
+ return NULL;
+ }
+# endif
+
+ /* Check for overflow */
+# if defined(__TURBOC__) && !defined(__FLAT__)
+
+ if (size != (unsigned long)size)
+ ret = NULL;
+
+ else
+ ret = farmalloc(size);
+
+# else
+# if defined(_MSC_VER) && defined(MAXSEG_64K)
+ if (size != (unsigned long)size)
+ ret = NULL;
+
+ else
+ ret = halloc(size, 1);
+
+# else
+ if (size != (size_t)size)
+ ret = NULL;
+
+ else
+ ret = malloc((size_t)size);
+# endif
+# endif
+
+# ifndef PNG_USER_MEM_SUPPORTED
+ if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+ png_error(png_ptr, "Out of Memory");
+# endif
+
+ return (ret);
+}
+
+/* Free a pointer allocated by png_malloc(). If ptr is NULL, return
+ * without taking any action.
+ */
+void PNGAPI
+png_free(png_structp png_ptr, png_voidp ptr)
+{
+ if (png_ptr == NULL || ptr == NULL)
+ return;
+
+# ifdef PNG_USER_MEM_SUPPORTED
+ if (png_ptr->free_fn != NULL)
+ {
+ (*(png_ptr->free_fn))(png_ptr, ptr);
+ return;
+ }
+
+ else
+ png_free_default(png_ptr, ptr);
+}
+
+void PNGAPI
+png_free_default(png_structp png_ptr, png_voidp ptr)
+{
+ if (png_ptr == NULL || ptr == NULL)
+ return;
+
+# endif /* PNG_USER_MEM_SUPPORTED */
+
+# if defined(__TURBOC__) && !defined(__FLAT__)
+ farfree(ptr);
+
+# else
+# if defined(_MSC_VER) && defined(MAXSEG_64K)
+ hfree(ptr);
+
+# else
+ free(ptr);
+
+# endif
+# endif
+}
+#endif /* Not Borland DOS special memory handler */
+
+/* This function was added at libpng version 1.2.3. The png_malloc_warn()
+ * function will set up png_malloc() to issue a png_warning and return NULL
+ * instead of issuing a png_error, if it fails to allocate the requested
+ * memory.
+ */
+PNG_FUNCTION(png_voidp,PNGAPI
+png_malloc_warn,(png_structp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
+{
+ png_voidp ptr;
+ png_uint_32 save_flags;
+ if (png_ptr == NULL)
+ return (NULL);
+
+ save_flags = png_ptr->flags;
+ png_ptr->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK;
+ ptr = (png_voidp)png_malloc((png_structp)png_ptr, size);
+ png_ptr->flags=save_flags;
+ return(ptr);
+}
+
+
+#ifdef PNG_USER_MEM_SUPPORTED
+/* This function is called when the application wants to use another method
+ * of allocating and freeing memory.
+ */
+void PNGAPI
+png_set_mem_fn(png_structp png_ptr, png_voidp mem_ptr, png_malloc_ptr
+ malloc_fn, png_free_ptr free_fn)
+{
+ if (png_ptr != NULL)
+ {
+ png_ptr->mem_ptr = mem_ptr;
+ png_ptr->malloc_fn = malloc_fn;
+ png_ptr->free_fn = free_fn;
+ }
+}
+
+/* This function returns a pointer to the mem_ptr associated with the user
+ * functions. The application should free any memory associated with this
+ * pointer before png_write_destroy and png_read_destroy are called.
+ */
+png_voidp PNGAPI
+png_get_mem_ptr(png_const_structp png_ptr)
+{
+ if (png_ptr == NULL)
+ return (NULL);
+
+ return ((png_voidp)png_ptr->mem_ptr);
+}
+#endif /* PNG_USER_MEM_SUPPORTED */
+#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */
diff --git a/libpng/pngpread.c b/libpng/pngpread.c
new file mode 100644
index 0000000..5ab9007
--- /dev/null
+++ b/libpng/pngpread.c
@@ -0,0 +1,1854 @@
+
+/* pngpread.c - read a png file in push mode
+ *
+ * Last changed in libpng 1.5.2 [March 31, 2011]
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include "pngpriv.h"
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+
+/* Push model modes */
+#define PNG_READ_SIG_MODE 0
+#define PNG_READ_CHUNK_MODE 1
+#define PNG_READ_IDAT_MODE 2
+#define PNG_SKIP_MODE 3
+#define PNG_READ_tEXt_MODE 4
+#define PNG_READ_zTXt_MODE 5
+#define PNG_READ_DONE_MODE 6
+#define PNG_READ_iTXt_MODE 7
+#define PNG_ERROR_MODE 8
+
+void PNGAPI
+png_process_data(png_structp png_ptr, png_infop info_ptr,
+ png_bytep buffer, png_size_t buffer_size)
+{
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ png_push_restore_buffer(png_ptr, buffer, buffer_size);
+
+ while (png_ptr->buffer_size)
+ {
+ png_process_some_data(png_ptr, info_ptr);
+ }
+}
+
+png_size_t PNGAPI
+png_process_data_pause(png_structp png_ptr, int save)
+{
+ if (png_ptr != NULL)
+ {
+ /* It's easiest for the caller if we do the save, then the caller doesn't
+ * have to supply the same data again:
+ */
+ if (save)
+ png_push_save_buffer(png_ptr);
+ else
+ {
+ /* This includes any pending saved bytes: */
+ png_size_t remaining = png_ptr->buffer_size;
+ png_ptr->buffer_size = 0;
+
+ /* So subtract the saved buffer size, unless all the data
+ * is actually 'saved', in which case we just return 0
+ */
+ if (png_ptr->save_buffer_size < remaining)
+ return remaining - png_ptr->save_buffer_size;
+ }
+ }
+
+ return 0;
+}
+
+png_uint_32 PNGAPI
+png_process_data_skip(png_structp png_ptr)
+{
+ png_uint_32 remaining = 0;
+
+ if (png_ptr != NULL && png_ptr->process_mode == PNG_SKIP_MODE &&
+ png_ptr->skip_length > 0)
+ {
+ /* At the end of png_process_data the buffer size must be 0 (see the loop
+ * above) so we can detect a broken call here:
+ */
+ if (png_ptr->buffer_size != 0)
+ png_error(png_ptr,
+ "png_process_data_skip called inside png_process_data");
+
+ /* If is impossible for there to be a saved buffer at this point -
+ * otherwise we could not be in SKIP mode. This will also happen if
+ * png_process_skip is called inside png_process_data (but only very
+ * rarely.)
+ */
+ if (png_ptr->save_buffer_size != 0)
+ png_error(png_ptr, "png_process_data_skip called with saved data");
+
+ remaining = png_ptr->skip_length;
+ png_ptr->skip_length = 0;
+ png_ptr->process_mode = PNG_READ_CHUNK_MODE;
+ }
+
+ return remaining;
+}
+
+/* What we do with the incoming data depends on what we were previously
+ * doing before we ran out of data...
+ */
+void /* PRIVATE */
+png_process_some_data(png_structp png_ptr, png_infop info_ptr)
+{
+ if (png_ptr == NULL)
+ return;
+
+ switch (png_ptr->process_mode)
+ {
+ case PNG_READ_SIG_MODE:
+ {
+ png_push_read_sig(png_ptr, info_ptr);
+ break;
+ }
+
+ case PNG_READ_CHUNK_MODE:
+ {
+ png_push_read_chunk(png_ptr, info_ptr);
+ break;
+ }
+
+ case PNG_READ_IDAT_MODE:
+ {
+ png_push_read_IDAT(png_ptr);
+ break;
+ }
+
+#ifdef PNG_READ_tEXt_SUPPORTED
+ case PNG_READ_tEXt_MODE:
+ {
+ png_push_read_tEXt(png_ptr, info_ptr);
+ break;
+ }
+
+#endif
+#ifdef PNG_READ_zTXt_SUPPORTED
+ case PNG_READ_zTXt_MODE:
+ {
+ png_push_read_zTXt(png_ptr, info_ptr);
+ break;
+ }
+
+#endif
+#ifdef PNG_READ_iTXt_SUPPORTED
+ case PNG_READ_iTXt_MODE:
+ {
+ png_push_read_iTXt(png_ptr, info_ptr);
+ break;
+ }
+
+#endif
+ case PNG_SKIP_MODE:
+ {
+ png_push_crc_finish(png_ptr);
+ break;
+ }
+
+ default:
+ {
+ png_ptr->buffer_size = 0;
+ break;
+ }
+ }
+}
+
+/* Read any remaining signature bytes from the stream and compare them with
+ * the correct PNG signature. It is possible that this routine is called
+ * with bytes already read from the signature, either because they have been
+ * checked by the calling application, or because of multiple calls to this
+ * routine.
+ */
+void /* PRIVATE */
+png_push_read_sig(png_structp png_ptr, png_infop info_ptr)
+{
+ png_size_t num_checked = png_ptr->sig_bytes,
+ num_to_check = 8 - num_checked;
+
+ if (png_ptr->buffer_size < num_to_check)
+ {
+ num_to_check = png_ptr->buffer_size;
+ }
+
+ png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]),
+ num_to_check);
+ png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes + num_to_check);
+
+ if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check))
+ {
+ if (num_checked < 4 &&
+ png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4))
+ png_error(png_ptr, "Not a PNG file");
+
+ else
+ png_error(png_ptr, "PNG file corrupted by ASCII conversion");
+ }
+ else
+ {
+ if (png_ptr->sig_bytes >= 8)
+ {
+ png_ptr->process_mode = PNG_READ_CHUNK_MODE;
+ }
+ }
+}
+
+void /* PRIVATE */
+png_push_read_chunk(png_structp png_ptr, png_infop info_ptr)
+{
+ PNG_IHDR;
+ PNG_IDAT;
+ PNG_IEND;
+ PNG_PLTE;
+#ifdef PNG_READ_bKGD_SUPPORTED
+ PNG_bKGD;
+#endif
+#ifdef PNG_READ_cHRM_SUPPORTED
+ PNG_cHRM;
+#endif
+#ifdef PNG_READ_gAMA_SUPPORTED
+ PNG_gAMA;
+#endif
+#ifdef PNG_READ_hIST_SUPPORTED
+ PNG_hIST;
+#endif
+#ifdef PNG_READ_iCCP_SUPPORTED
+ PNG_iCCP;
+#endif
+#ifdef PNG_READ_iTXt_SUPPORTED
+ PNG_iTXt;
+#endif
+#ifdef PNG_READ_oFFs_SUPPORTED
+ PNG_oFFs;
+#endif
+#ifdef PNG_READ_pCAL_SUPPORTED
+ PNG_pCAL;
+#endif
+#ifdef PNG_READ_pHYs_SUPPORTED
+ PNG_pHYs;
+#endif
+#ifdef PNG_READ_sBIT_SUPPORTED
+ PNG_sBIT;
+#endif
+#ifdef PNG_READ_sCAL_SUPPORTED
+ PNG_sCAL;
+#endif
+#ifdef PNG_READ_sRGB_SUPPORTED
+ PNG_sRGB;
+#endif
+#ifdef PNG_READ_sPLT_SUPPORTED
+ PNG_sPLT;
+#endif
+#ifdef PNG_READ_tEXt_SUPPORTED
+ PNG_tEXt;
+#endif
+#ifdef PNG_READ_tIME_SUPPORTED
+ PNG_tIME;
+#endif
+#ifdef PNG_READ_tRNS_SUPPORTED
+ PNG_tRNS;
+#endif
+#ifdef PNG_READ_zTXt_SUPPORTED
+ PNG_zTXt;
+#endif
+
+ /* First we make sure we have enough data for the 4 byte chunk name
+ * and the 4 byte chunk length before proceeding with decoding the
+ * chunk data. To fully decode each of these chunks, we also make
+ * sure we have enough data in the buffer for the 4 byte CRC at the
+ * end of every chunk (except IDAT, which is handled separately).
+ */
+ if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER))
+ {
+ png_byte chunk_length[4];
+
+ if (png_ptr->buffer_size < 8)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_push_fill_buffer(png_ptr, chunk_length, 4);
+ png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length);
+ png_reset_crc(png_ptr);
+ png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+ png_check_chunk_name(png_ptr, png_ptr->chunk_name);
+ png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
+ }
+
+ if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+ if (png_ptr->mode & PNG_AFTER_IDAT)
+ png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT;
+
+ if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4))
+ {
+ if (png_ptr->push_length != 13)
+ png_error(png_ptr, "Invalid IHDR length");
+
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+ else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length);
+
+ png_ptr->process_mode = PNG_READ_DONE_MODE;
+ png_push_have_end(png_ptr, info_ptr);
+ }
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+ png_ptr->mode |= PNG_HAVE_IDAT;
+
+ png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length);
+
+ if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4))
+ png_ptr->mode |= PNG_HAVE_PLTE;
+
+ else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+ {
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before IDAT");
+
+ else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+ !(png_ptr->mode & PNG_HAVE_PLTE))
+ png_error(png_ptr, "Missing PLTE before IDAT");
+ }
+ }
+
+#endif
+ else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+ png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+ else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+ {
+ /* If we reach an IDAT chunk, this means we have read all of the
+ * header chunks, and we can start reading the image (or if this
+ * is called after the image has been read - we have an error).
+ */
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before IDAT");
+
+ else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+ !(png_ptr->mode & PNG_HAVE_PLTE))
+ png_error(png_ptr, "Missing PLTE before IDAT");
+
+ if (png_ptr->mode & PNG_HAVE_IDAT)
+ {
+ if (!(png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT))
+ if (png_ptr->push_length == 0)
+ return;
+
+ if (png_ptr->mode & PNG_AFTER_IDAT)
+ png_benign_error(png_ptr, "Too many IDATs found");
+ }
+
+ png_ptr->idat_size = png_ptr->push_length;
+ png_ptr->mode |= PNG_HAVE_IDAT;
+ png_ptr->process_mode = PNG_READ_IDAT_MODE;
+ png_push_have_info(png_ptr, info_ptr);
+ png_ptr->zstream.avail_out =
+ (uInt) PNG_ROWBYTES(png_ptr->pixel_depth,
+ png_ptr->iwidth) + 1;
+ png_ptr->zstream.next_out = png_ptr->row_buf;
+ return;
+ }
+
+#ifdef PNG_READ_gAMA_SUPPORTED
+ else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+#endif
+#ifdef PNG_READ_sBIT_SUPPORTED
+ else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+#endif
+#ifdef PNG_READ_cHRM_SUPPORTED
+ else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+#endif
+#ifdef PNG_READ_sRGB_SUPPORTED
+ else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+#endif
+#ifdef PNG_READ_iCCP_SUPPORTED
+ else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+#endif
+#ifdef PNG_READ_sPLT_SUPPORTED
+ else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+#endif
+#ifdef PNG_READ_tRNS_SUPPORTED
+ else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+#endif
+#ifdef PNG_READ_bKGD_SUPPORTED
+ else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+#endif
+#ifdef PNG_READ_hIST_SUPPORTED
+ else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+#endif
+#ifdef PNG_READ_pHYs_SUPPORTED
+ else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+#endif
+#ifdef PNG_READ_oFFs_SUPPORTED
+ else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length);
+ }
+#endif
+
+#ifdef PNG_READ_pCAL_SUPPORTED
+ else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+#endif
+#ifdef PNG_READ_sCAL_SUPPORTED
+ else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+#endif
+#ifdef PNG_READ_tIME_SUPPORTED
+ else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+#endif
+#ifdef PNG_READ_tEXt_SUPPORTED
+ else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_push_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+#endif
+#ifdef PNG_READ_zTXt_SUPPORTED
+ else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_push_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+#endif
+#ifdef PNG_READ_iTXt_SUPPORTED
+ else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4))
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_push_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+#endif
+ else
+ {
+ if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+ png_push_handle_unknown(png_ptr, info_ptr, png_ptr->push_length);
+ }
+
+ png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
+}
+
+void /* PRIVATE */
+png_push_crc_skip(png_structp png_ptr, png_uint_32 skip)
+{
+ png_ptr->process_mode = PNG_SKIP_MODE;
+ png_ptr->skip_length = skip;
+}
+
+void /* PRIVATE */
+png_push_crc_finish(png_structp png_ptr)
+{
+ if (png_ptr->skip_length && png_ptr->save_buffer_size)
+ {
+ png_size_t save_size = png_ptr->save_buffer_size;
+ png_uint_32 skip_length = png_ptr->skip_length;
+
+ /* We want the smaller of 'skip_length' and 'save_buffer_size', but
+ * they are of different types and we don't know which variable has the
+ * fewest bits. Carefully select the smaller and cast it to the type of
+ * the larger - this cannot overflow. Do not cast in the following test
+ * - it will break on either 16 or 64 bit platforms.
+ */
+ if (skip_length < save_size)
+ save_size = (png_size_t)skip_length;
+
+ else
+ skip_length = (png_uint_32)save_size;
+
+ png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size);
+
+ png_ptr->skip_length -= skip_length;
+ png_ptr->buffer_size -= save_size;
+ png_ptr->save_buffer_size -= save_size;
+ png_ptr->save_buffer_ptr += save_size;
+ }
+ if (png_ptr->skip_length && png_ptr->current_buffer_size)
+ {
+ png_size_t save_size = png_ptr->current_buffer_size;
+ png_uint_32 skip_length = png_ptr->skip_length;
+
+ /* We want the smaller of 'skip_length' and 'current_buffer_size', here,
+ * the same problem exists as above and the same solution.
+ */
+ if (skip_length < save_size)
+ save_size = (png_size_t)skip_length;
+
+ else
+ skip_length = (png_uint_32)save_size;
+
+ png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size);
+
+ png_ptr->skip_length -= skip_length;
+ png_ptr->buffer_size -= save_size;
+ png_ptr->current_buffer_size -= save_size;
+ png_ptr->current_buffer_ptr += save_size;
+ }
+ if (!png_ptr->skip_length)
+ {
+ if (png_ptr->buffer_size < 4)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_crc_finish(png_ptr, 0);
+ png_ptr->process_mode = PNG_READ_CHUNK_MODE;
+ }
+}
+
+void PNGCBAPI
+png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length)
+{
+ png_bytep ptr;
+
+ if (png_ptr == NULL)
+ return;
+
+ ptr = buffer;
+ if (png_ptr->save_buffer_size)
+ {
+ png_size_t save_size;
+
+ if (length < png_ptr->save_buffer_size)
+ save_size = length;
+
+ else
+ save_size = png_ptr->save_buffer_size;
+
+ png_memcpy(ptr, png_ptr->save_buffer_ptr, save_size);
+ length -= save_size;
+ ptr += save_size;
+ png_ptr->buffer_size -= save_size;
+ png_ptr->save_buffer_size -= save_size;
+ png_ptr->save_buffer_ptr += save_size;
+ }
+ if (length && png_ptr->current_buffer_size)
+ {
+ png_size_t save_size;
+
+ if (length < png_ptr->current_buffer_size)
+ save_size = length;
+
+ else
+ save_size = png_ptr->current_buffer_size;
+
+ png_memcpy(ptr, png_ptr->current_buffer_ptr, save_size);
+ png_ptr->buffer_size -= save_size;
+ png_ptr->current_buffer_size -= save_size;
+ png_ptr->current_buffer_ptr += save_size;
+ }
+}
+
+void /* PRIVATE */
+png_push_save_buffer(png_structp png_ptr)
+{
+ if (png_ptr->save_buffer_size)
+ {
+ if (png_ptr->save_buffer_ptr != png_ptr->save_buffer)
+ {
+ png_size_t i, istop;
+ png_bytep sp;
+ png_bytep dp;
+
+ istop = png_ptr->save_buffer_size;
+ for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer;
+ i < istop; i++, sp++, dp++)
+ {
+ *dp = *sp;
+ }
+ }
+ }
+ if (png_ptr->save_buffer_size + png_ptr->current_buffer_size >
+ png_ptr->save_buffer_max)
+ {
+ png_size_t new_max;
+ png_bytep old_buffer;
+
+ if (png_ptr->save_buffer_size > PNG_SIZE_MAX -
+ (png_ptr->current_buffer_size + 256))
+ {
+ png_error(png_ptr, "Potential overflow of save_buffer");
+ }
+
+ new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256;
+ old_buffer = png_ptr->save_buffer;
+ png_ptr->save_buffer = (png_bytep)png_malloc_warn(png_ptr,
+ (png_size_t)new_max);
+
+ if (png_ptr->save_buffer == NULL)
+ {
+ png_free(png_ptr, old_buffer);
+ png_error(png_ptr, "Insufficient memory for save_buffer");
+ }
+
+ png_memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size);
+ png_free(png_ptr, old_buffer);
+ png_ptr->save_buffer_max = new_max;
+ }
+ if (png_ptr->current_buffer_size)
+ {
+ png_memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size,
+ png_ptr->current_buffer_ptr, png_ptr->current_buffer_size);
+ png_ptr->save_buffer_size += png_ptr->current_buffer_size;
+ png_ptr->current_buffer_size = 0;
+ }
+ png_ptr->save_buffer_ptr = png_ptr->save_buffer;
+ png_ptr->buffer_size = 0;
+}
+
+void /* PRIVATE */
+png_push_restore_buffer(png_structp png_ptr, png_bytep buffer,
+ png_size_t buffer_length)
+{
+ png_ptr->current_buffer = buffer;
+ png_ptr->current_buffer_size = buffer_length;
+ png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size;
+ png_ptr->current_buffer_ptr = png_ptr->current_buffer;
+}
+
+void /* PRIVATE */
+png_push_read_IDAT(png_structp png_ptr)
+{
+ PNG_IDAT;
+ if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER))
+ {
+ png_byte chunk_length[4];
+
+ if (png_ptr->buffer_size < 8)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_push_fill_buffer(png_ptr, chunk_length, 4);
+ png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length);
+ png_reset_crc(png_ptr);
+ png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+ png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
+
+ if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+ {
+ png_ptr->process_mode = PNG_READ_CHUNK_MODE;
+
+ if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED))
+ png_error(png_ptr, "Not enough compressed data");
+
+ return;
+ }
+
+ png_ptr->idat_size = png_ptr->push_length;
+ }
+ if (png_ptr->idat_size && png_ptr->save_buffer_size)
+ {
+ png_size_t save_size = png_ptr->save_buffer_size;
+ png_uint_32 idat_size = png_ptr->idat_size;
+
+ /* We want the smaller of 'idat_size' and 'current_buffer_size', but they
+ * are of different types and we don't know which variable has the fewest
+ * bits. Carefully select the smaller and cast it to the type of the
+ * larger - this cannot overflow. Do not cast in the following test - it
+ * will break on either 16 or 64 bit platforms.
+ */
+ if (idat_size < save_size)
+ save_size = (png_size_t)idat_size;
+
+ else
+ idat_size = (png_uint_32)save_size;
+
+ png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size);
+
+ png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size);
+
+ png_ptr->idat_size -= idat_size;
+ png_ptr->buffer_size -= save_size;
+ png_ptr->save_buffer_size -= save_size;
+ png_ptr->save_buffer_ptr += save_size;
+ }
+
+ if (png_ptr->idat_size && png_ptr->current_buffer_size)
+ {
+ png_size_t save_size = png_ptr->current_buffer_size;
+ png_uint_32 idat_size = png_ptr->idat_size;
+
+ /* We want the smaller of 'idat_size' and 'current_buffer_size', but they
+ * are of different types and we don't know which variable has the fewest
+ * bits. Carefully select the smaller and cast it to the type of the
+ * larger - this cannot overflow.
+ */
+ if (idat_size < save_size)
+ save_size = (png_size_t)idat_size;
+
+ else
+ idat_size = (png_uint_32)save_size;
+
+ png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size);
+
+ png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size);
+
+ png_ptr->idat_size -= idat_size;
+ png_ptr->buffer_size -= save_size;
+ png_ptr->current_buffer_size -= save_size;
+ png_ptr->current_buffer_ptr += save_size;
+ }
+ if (!png_ptr->idat_size)
+ {
+ if (png_ptr->buffer_size < 4)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_crc_finish(png_ptr, 0);
+ png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
+ png_ptr->mode |= PNG_AFTER_IDAT;
+ }
+}
+
+void /* PRIVATE */
+png_process_IDAT_data(png_structp png_ptr, png_bytep buffer,
+ png_size_t buffer_length)
+{
+ /* The caller checks for a non-zero buffer length. */
+ if (!(buffer_length > 0) || buffer == NULL)
+ png_error(png_ptr, "No IDAT data (internal error)");
+
+ /* This routine must process all the data it has been given
+ * before returning, calling the row callback as required to
+ * handle the uncompressed results.
+ */
+ png_ptr->zstream.next_in = buffer;
+ png_ptr->zstream.avail_in = (uInt)buffer_length;
+
+ /* Keep going until the decompressed data is all processed
+ * or the stream marked as finished.
+ */
+ while (png_ptr->zstream.avail_in > 0 &&
+ !(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED))
+ {
+ int ret;
+
+ /* We have data for zlib, but we must check that zlib
+ * has someplace to put the results. It doesn't matter
+ * if we don't expect any results -- it may be the input
+ * data is just the LZ end code.
+ */
+ if (!(png_ptr->zstream.avail_out > 0))
+ {
+ png_ptr->zstream.avail_out =
+ (uInt) PNG_ROWBYTES(png_ptr->pixel_depth,
+ png_ptr->iwidth) + 1;
+
+ png_ptr->zstream.next_out = png_ptr->row_buf;
+ }
+
+ /* Using Z_SYNC_FLUSH here means that an unterminated
+ * LZ stream (a stream with a missing end code) can still
+ * be handled, otherwise (Z_NO_FLUSH) a future zlib
+ * implementation might defer output and therefore
+ * change the current behavior (see comments in inflate.c
+ * for why this doesn't happen at present with zlib 1.2.5).
+ */
+ ret = inflate(&png_ptr->zstream, Z_SYNC_FLUSH);
+
+ /* Check for any failure before proceeding. */
+ if (ret != Z_OK && ret != Z_STREAM_END)
+ {
+ /* Terminate the decompression. */
+ png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+
+ /* This may be a truncated stream (missing or
+ * damaged end code). Treat that as a warning.
+ */
+ if (png_ptr->row_number >= png_ptr->num_rows ||
+ png_ptr->pass > 6)
+ png_warning(png_ptr, "Truncated compressed data in IDAT");
+
+ else
+ png_error(png_ptr, "Decompression error in IDAT");
+
+ /* Skip the check on unprocessed input */
+ return;
+ }
+
+ /* Did inflate output any data? */
+ if (png_ptr->zstream.next_out != png_ptr->row_buf)
+ {
+ /* Is this unexpected data after the last row?
+ * If it is, artificially terminate the LZ output
+ * here.
+ */
+ if (png_ptr->row_number >= png_ptr->num_rows ||
+ png_ptr->pass > 6)
+ {
+ /* Extra data. */
+ png_warning(png_ptr, "Extra compressed data in IDAT");
+ png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+
+ /* Do no more processing; skip the unprocessed
+ * input check below.
+ */
+ return;
+ }
+
+ /* Do we have a complete row? */
+ if (png_ptr->zstream.avail_out == 0)
+ png_push_process_row(png_ptr);
+ }
+
+ /* And check for the end of the stream. */
+ if (ret == Z_STREAM_END)
+ png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+ }
+
+ /* All the data should have been processed, if anything
+ * is left at this point we have bytes of IDAT data
+ * after the zlib end code.
+ */
+ if (png_ptr->zstream.avail_in > 0)
+ png_warning(png_ptr, "Extra compression data in IDAT");
+}
+
+void /* PRIVATE */
+png_push_process_row(png_structp png_ptr)
+{
+ png_ptr->row_info.color_type = png_ptr->color_type;
+ png_ptr->row_info.width = png_ptr->iwidth;
+ png_ptr->row_info.channels = png_ptr->channels;
+ png_ptr->row_info.bit_depth = png_ptr->bit_depth;
+ png_ptr->row_info.pixel_depth = png_ptr->pixel_depth;
+
+ png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth,
+ png_ptr->row_info.width);
+
+ png_read_filter_row(png_ptr, &(png_ptr->row_info),
+ png_ptr->row_buf + 1, png_ptr->prev_row + 1,
+ (int)(png_ptr->row_buf[0]));
+
+ png_memcpy(png_ptr->prev_row, png_ptr->row_buf, png_ptr->rowbytes + 1);
+
+ if (png_ptr->transformations)
+ png_do_read_transformations(png_ptr);
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+ /* Blow up interlaced rows to full size */
+ if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
+ {
+ if (png_ptr->pass < 6)
+/* old interface (pre-1.0.9):
+ png_do_read_interlace(&(png_ptr->row_info),
+ png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations);
+ */
+ png_do_read_interlace(png_ptr);
+
+ switch (png_ptr->pass)
+ {
+ case 0:
+ {
+ int i;
+ for (i = 0; i < 8 && png_ptr->pass == 0; i++)
+ {
+ png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+ png_read_push_finish_row(png_ptr); /* Updates png_ptr->pass */
+ }
+
+ if (png_ptr->pass == 2) /* Pass 1 might be empty */
+ {
+ for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+ {
+ png_push_have_row(png_ptr, NULL);
+ png_read_push_finish_row(png_ptr);
+ }
+ }
+
+ if (png_ptr->pass == 4 && png_ptr->height <= 4)
+ {
+ for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+ {
+ png_push_have_row(png_ptr, NULL);
+ png_read_push_finish_row(png_ptr);
+ }
+ }
+
+ if (png_ptr->pass == 6 && png_ptr->height <= 4)
+ {
+ png_push_have_row(png_ptr, NULL);
+ png_read_push_finish_row(png_ptr);
+ }
+
+ break;
+ }
+
+ case 1:
+ {
+ int i;
+ for (i = 0; i < 8 && png_ptr->pass == 1; i++)
+ {
+ png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+ png_read_push_finish_row(png_ptr);
+ }
+
+ if (png_ptr->pass == 2) /* Skip top 4 generated rows */
+ {
+ for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+ {
+ png_push_have_row(png_ptr, NULL);
+ png_read_push_finish_row(png_ptr);
+ }
+ }
+
+ break;
+ }
+
+ case 2:
+ {
+ int i;
+
+ for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+ {
+ png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+ png_read_push_finish_row(png_ptr);
+ }
+
+ for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+ {
+ png_push_have_row(png_ptr, NULL);
+ png_read_push_finish_row(png_ptr);
+ }
+
+ if (png_ptr->pass == 4) /* Pass 3 might be empty */
+ {
+ for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+ {
+ png_push_have_row(png_ptr, NULL);
+ png_read_push_finish_row(png_ptr);
+ }
+ }
+
+ break;
+ }
+
+ case 3:
+ {
+ int i;
+
+ for (i = 0; i < 4 && png_ptr->pass == 3; i++)
+ {
+ png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+ png_read_push_finish_row(png_ptr);
+ }
+
+ if (png_ptr->pass == 4) /* Skip top two generated rows */
+ {
+ for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+ {
+ png_push_have_row(png_ptr, NULL);
+ png_read_push_finish_row(png_ptr);
+ }
+ }
+
+ break;
+ }
+
+ case 4:
+ {
+ int i;
+
+ for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+ {
+ png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+ png_read_push_finish_row(png_ptr);
+ }
+
+ for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+ {
+ png_push_have_row(png_ptr, NULL);
+ png_read_push_finish_row(png_ptr);
+ }
+
+ if (png_ptr->pass == 6) /* Pass 5 might be empty */
+ {
+ png_push_have_row(png_ptr, NULL);
+ png_read_push_finish_row(png_ptr);
+ }
+
+ break;
+ }
+
+ case 5:
+ {
+ int i;
+
+ for (i = 0; i < 2 && png_ptr->pass == 5; i++)
+ {
+ png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+ png_read_push_finish_row(png_ptr);
+ }
+
+ if (png_ptr->pass == 6) /* Skip top generated row */
+ {
+ png_push_have_row(png_ptr, NULL);
+ png_read_push_finish_row(png_ptr);
+ }
+
+ break;
+ }
+
+ default:
+ case 6:
+ {
+ png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+ png_read_push_finish_row(png_ptr);
+
+ if (png_ptr->pass != 6)
+ break;
+
+ png_push_have_row(png_ptr, NULL);
+ png_read_push_finish_row(png_ptr);
+ }
+ }
+ }
+ else
+#endif
+ {
+ png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+ png_read_push_finish_row(png_ptr);
+ }
+}
+
+void /* PRIVATE */
+png_read_push_finish_row(png_structp png_ptr)
+{
+ /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+ /* Start of interlace block */
+ PNG_CONST int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0};
+
+ /* Offset to next interlace block */
+ PNG_CONST int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};
+
+ /* Start of interlace block in the y direction */
+ PNG_CONST int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1};
+
+ /* Offset to next interlace block in the y direction */
+ PNG_CONST int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2};
+
+ /* Height of interlace block. This is not currently used - if you need
+ * it, uncomment it here and in png.h
+ PNG_CONST int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1};
+ */
+
+ png_ptr->row_number++;
+ if (png_ptr->row_number < png_ptr->num_rows)
+ return;
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+ if (png_ptr->interlaced)
+ {
+ png_ptr->row_number = 0;
+ png_memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
+
+ do
+ {
+ png_ptr->pass++;
+ if ((png_ptr->pass == 1 && png_ptr->width < 5) ||
+ (png_ptr->pass == 3 && png_ptr->width < 3) ||
+ (png_ptr->pass == 5 && png_ptr->width < 2))
+ png_ptr->pass++;
+
+ if (png_ptr->pass > 7)
+ png_ptr->pass--;
+
+ if (png_ptr->pass >= 7)
+ break;
+
+ png_ptr->iwidth = (png_ptr->width +
+ png_pass_inc[png_ptr->pass] - 1 -
+ png_pass_start[png_ptr->pass]) /
+ png_pass_inc[png_ptr->pass];
+
+ if (png_ptr->transformations & PNG_INTERLACE)
+ break;
+
+ png_ptr->num_rows = (png_ptr->height +
+ png_pass_yinc[png_ptr->pass] - 1 -
+ png_pass_ystart[png_ptr->pass]) /
+ png_pass_yinc[png_ptr->pass];
+
+ } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0);
+ }
+#endif /* PNG_READ_INTERLACING_SUPPORTED */
+}
+
+#ifdef PNG_READ_tEXt_SUPPORTED
+void /* PRIVATE */
+png_push_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32
+ length)
+{
+ if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND))
+ {
+ PNG_UNUSED(info_ptr) /* To quiet some compiler warnings */
+ png_error(png_ptr, "Out of place tEXt");
+ /*NOT REACHED*/
+ }
+
+#ifdef PNG_MAX_MALLOC_64K
+ png_ptr->skip_length = 0; /* This may not be necessary */
+
+ if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */
+ {
+ png_warning(png_ptr, "tEXt chunk too large to fit in memory");
+ png_ptr->skip_length = length - (png_uint_32)65535L;
+ length = (png_uint_32)65535L;
+ }
+#endif
+
+ png_ptr->current_text = (png_charp)png_malloc(png_ptr,
+ (png_size_t)(length + 1));
+ png_ptr->current_text[length] = '\0';
+ png_ptr->current_text_ptr = png_ptr->current_text;
+ png_ptr->current_text_size = (png_size_t)length;
+ png_ptr->current_text_left = (png_size_t)length;
+ png_ptr->process_mode = PNG_READ_tEXt_MODE;
+}
+
+void /* PRIVATE */
+png_push_read_tEXt(png_structp png_ptr, png_infop info_ptr)
+{
+ if (png_ptr->buffer_size && png_ptr->current_text_left)
+ {
+ png_size_t text_size;
+
+ if (png_ptr->buffer_size < png_ptr->current_text_left)
+ text_size = png_ptr->buffer_size;
+
+ else
+ text_size = png_ptr->current_text_left;
+
+ png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size);
+ png_ptr->current_text_left -= text_size;
+ png_ptr->current_text_ptr += text_size;
+ }
+ if (!(png_ptr->current_text_left))
+ {
+ png_textp text_ptr;
+ png_charp text;
+ png_charp key;
+ int ret;
+
+ if (png_ptr->buffer_size < 4)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_push_crc_finish(png_ptr);
+
+#ifdef PNG_MAX_MALLOC_64K
+ if (png_ptr->skip_length)
+ return;
+#endif
+
+ key = png_ptr->current_text;
+
+ for (text = key; *text; text++)
+ /* Empty loop */ ;
+
+ if (text < key + png_ptr->current_text_size)
+ text++;
+
+ text_ptr = (png_textp)png_malloc(png_ptr, png_sizeof(png_text));
+ text_ptr->compression = PNG_TEXT_COMPRESSION_NONE;
+ text_ptr->key = key;
+ text_ptr->itxt_length = 0;
+ text_ptr->lang = NULL;
+ text_ptr->lang_key = NULL;
+ text_ptr->text = text;
+
+ ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+ png_free(png_ptr, key);
+ png_free(png_ptr, text_ptr);
+ png_ptr->current_text = NULL;
+
+ if (ret)
+ png_warning(png_ptr, "Insufficient memory to store text chunk");
+ }
+}
+#endif
+
+#ifdef PNG_READ_zTXt_SUPPORTED
+void /* PRIVATE */
+png_push_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32
+ length)
+{
+ if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND))
+ {
+ PNG_UNUSED(info_ptr) /* To quiet some compiler warnings */
+ png_error(png_ptr, "Out of place zTXt");
+ /*NOT REACHED*/
+ }
+
+#ifdef PNG_MAX_MALLOC_64K
+ /* We can't handle zTXt chunks > 64K, since we don't have enough space
+ * to be able to store the uncompressed data. Actually, the threshold
+ * is probably around 32K, but it isn't as definite as 64K is.
+ */
+ if (length > (png_uint_32)65535L)
+ {
+ png_warning(png_ptr, "zTXt chunk too large to fit in memory");
+ png_push_crc_skip(png_ptr, length);
+ return;
+ }
+#endif
+
+ png_ptr->current_text = (png_charp)png_malloc(png_ptr,
+ (png_size_t)(length + 1));
+ png_ptr->current_text[length] = '\0';
+ png_ptr->current_text_ptr = png_ptr->current_text;
+ png_ptr->current_text_size = (png_size_t)length;
+ png_ptr->current_text_left = (png_size_t)length;
+ png_ptr->process_mode = PNG_READ_zTXt_MODE;
+}
+
+void /* PRIVATE */
+png_push_read_zTXt(png_structp png_ptr, png_infop info_ptr)
+{
+ if (png_ptr->buffer_size && png_ptr->current_text_left)
+ {
+ png_size_t text_size;
+
+ if (png_ptr->buffer_size < (png_uint_32)png_ptr->current_text_left)
+ text_size = png_ptr->buffer_size;
+
+ else
+ text_size = png_ptr->current_text_left;
+
+ png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size);
+ png_ptr->current_text_left -= text_size;
+ png_ptr->current_text_ptr += text_size;
+ }
+ if (!(png_ptr->current_text_left))
+ {
+ png_textp text_ptr;
+ png_charp text;
+ png_charp key;
+ int ret;
+ png_size_t text_size, key_size;
+
+ if (png_ptr->buffer_size < 4)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_push_crc_finish(png_ptr);
+
+ key = png_ptr->current_text;
+
+ for (text = key; *text; text++)
+ /* Empty loop */ ;
+
+ /* zTXt can't have zero text */
+ if (text >= key + png_ptr->current_text_size)
+ {
+ png_ptr->current_text = NULL;
+ png_free(png_ptr, key);
+ return;
+ }
+
+ text++;
+
+ if (*text != PNG_TEXT_COMPRESSION_zTXt) /* Check compression byte */
+ {
+ png_ptr->current_text = NULL;
+ png_free(png_ptr, key);
+ return;
+ }
+
+ text++;
+
+ png_ptr->zstream.next_in = (png_bytep)text;
+ png_ptr->zstream.avail_in = (uInt)(png_ptr->current_text_size -
+ (text - key));
+ png_ptr->zstream.next_out = png_ptr->zbuf;
+ png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+
+ key_size = text - key;
+ text_size = 0;
+ text = NULL;
+ ret = Z_STREAM_END;
+
+ while (png_ptr->zstream.avail_in)
+ {
+ ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+ if (ret != Z_OK && ret != Z_STREAM_END)
+ {
+ inflateReset(&png_ptr->zstream);
+ png_ptr->zstream.avail_in = 0;
+ png_ptr->current_text = NULL;
+ png_free(png_ptr, key);
+ png_free(png_ptr, text);
+ return;
+ }
+
+ if (!(png_ptr->zstream.avail_out) || ret == Z_STREAM_END)
+ {
+ if (text == NULL)
+ {
+ text = (png_charp)png_malloc(png_ptr,
+ (png_ptr->zbuf_size
+ - png_ptr->zstream.avail_out + key_size + 1));
+
+ png_memcpy(text + key_size, png_ptr->zbuf,
+ png_ptr->zbuf_size - png_ptr->zstream.avail_out);
+
+ png_memcpy(text, key, key_size);
+
+ text_size = key_size + png_ptr->zbuf_size -
+ png_ptr->zstream.avail_out;
+
+ *(text + text_size) = '\0';
+ }
+
+ else
+ {
+ png_charp tmp;
+
+ tmp = text;
+ text = (png_charp)png_malloc(png_ptr, text_size +
+ (png_ptr->zbuf_size
+ - png_ptr->zstream.avail_out + 1));
+
+ png_memcpy(text, tmp, text_size);
+ png_free(png_ptr, tmp);
+
+ png_memcpy(text + text_size, png_ptr->zbuf,
+ png_ptr->zbuf_size - png_ptr->zstream.avail_out);
+
+ text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out;
+ *(text + text_size) = '\0';
+ }
+
+ if (ret != Z_STREAM_END)
+ {
+ png_ptr->zstream.next_out = png_ptr->zbuf;
+ png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+ }
+ }
+ else
+ {
+ break;
+ }
+
+ if (ret == Z_STREAM_END)
+ break;
+ }
+
+ inflateReset(&png_ptr->zstream);
+ png_ptr->zstream.avail_in = 0;
+
+ if (ret != Z_STREAM_END)
+ {
+ png_ptr->current_text = NULL;
+ png_free(png_ptr, key);
+ png_free(png_ptr, text);
+ return;
+ }
+
+ png_ptr->current_text = NULL;
+ png_free(png_ptr, key);
+ key = text;
+ text += key_size;
+
+ text_ptr = (png_textp)png_malloc(png_ptr,
+ png_sizeof(png_text));
+ text_ptr->compression = PNG_TEXT_COMPRESSION_zTXt;
+ text_ptr->key = key;
+ text_ptr->itxt_length = 0;
+ text_ptr->lang = NULL;
+ text_ptr->lang_key = NULL;
+ text_ptr->text = text;
+
+ ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+ png_free(png_ptr, key);
+ png_free(png_ptr, text_ptr);
+
+ if (ret)
+ png_warning(png_ptr, "Insufficient memory to store text chunk");
+ }
+}
+#endif
+
+#ifdef PNG_READ_iTXt_SUPPORTED
+void /* PRIVATE */
+png_push_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32
+ length)
+{
+ if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND))
+ {
+ PNG_UNUSED(info_ptr) /* To quiet some compiler warnings */
+ png_error(png_ptr, "Out of place iTXt");
+ /*NOT REACHED*/
+ }
+
+#ifdef PNG_MAX_MALLOC_64K
+ png_ptr->skip_length = 0; /* This may not be necessary */
+
+ if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */
+ {
+ png_warning(png_ptr, "iTXt chunk too large to fit in memory");
+ png_ptr->skip_length = length - (png_uint_32)65535L;
+ length = (png_uint_32)65535L;
+ }
+#endif
+
+ png_ptr->current_text = (png_charp)png_malloc(png_ptr,
+ (png_size_t)(length + 1));
+ png_ptr->current_text[length] = '\0';
+ png_ptr->current_text_ptr = png_ptr->current_text;
+ png_ptr->current_text_size = (png_size_t)length;
+ png_ptr->current_text_left = (png_size_t)length;
+ png_ptr->process_mode = PNG_READ_iTXt_MODE;
+}
+
+void /* PRIVATE */
+png_push_read_iTXt(png_structp png_ptr, png_infop info_ptr)
+{
+
+ if (png_ptr->buffer_size && png_ptr->current_text_left)
+ {
+ png_size_t text_size;
+
+ if (png_ptr->buffer_size < png_ptr->current_text_left)
+ text_size = png_ptr->buffer_size;
+
+ else
+ text_size = png_ptr->current_text_left;
+
+ png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size);
+ png_ptr->current_text_left -= text_size;
+ png_ptr->current_text_ptr += text_size;
+ }
+
+ if (!(png_ptr->current_text_left))
+ {
+ png_textp text_ptr;
+ png_charp key;
+ int comp_flag;
+ png_charp lang;
+ png_charp lang_key;
+ png_charp text;
+ int ret;
+
+ if (png_ptr->buffer_size < 4)
+ {
+ png_push_save_buffer(png_ptr);
+ return;
+ }
+
+ png_push_crc_finish(png_ptr);
+
+#ifdef PNG_MAX_MALLOC_64K
+ if (png_ptr->skip_length)
+ return;
+#endif
+
+ key = png_ptr->current_text;
+
+ for (lang = key; *lang; lang++)
+ /* Empty loop */ ;
+
+ if (lang < key + png_ptr->current_text_size - 3)
+ lang++;
+
+ comp_flag = *lang++;
+ lang++; /* Skip comp_type, always zero */
+
+ for (lang_key = lang; *lang_key; lang_key++)
+ /* Empty loop */ ;
+
+ lang_key++; /* Skip NUL separator */
+
+ text=lang_key;
+
+ if (lang_key < key + png_ptr->current_text_size - 1)
+ {
+ for (; *text; text++)
+ /* Empty loop */ ;
+ }
+
+ if (text < key + png_ptr->current_text_size)
+ text++;
+
+ text_ptr = (png_textp)png_malloc(png_ptr,
+ png_sizeof(png_text));
+
+ text_ptr->compression = comp_flag + 2;
+ text_ptr->key = key;
+ text_ptr->lang = lang;
+ text_ptr->lang_key = lang_key;
+ text_ptr->text = text;
+ text_ptr->text_length = 0;
+ text_ptr->itxt_length = png_strlen(text);
+
+ ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+ png_ptr->current_text = NULL;
+
+ png_free(png_ptr, text_ptr);
+ if (ret)
+ png_warning(png_ptr, "Insufficient memory to store iTXt chunk");
+ }
+}
+#endif
+
+/* This function is called when we haven't found a handler for this
+ * chunk. If there isn't a problem with the chunk itself (ie a bad chunk
+ * name or a critical chunk), the chunk is (currently) silently ignored.
+ */
+void /* PRIVATE */
+png_push_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32
+ length)
+{
+ png_uint_32 skip = 0;
+
+ if (!(png_ptr->chunk_name[0] & 0x20))
+ {
+#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+ if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name) !=
+ PNG_HANDLE_CHUNK_ALWAYS
+#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+ && png_ptr->read_user_chunk_fn == NULL
+#endif
+ )
+#endif
+ png_chunk_error(png_ptr, "unknown critical chunk");
+
+ PNG_UNUSED(info_ptr) /* To quiet some compiler warnings */
+ }
+
+#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+ if (png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS)
+ {
+#ifdef PNG_MAX_MALLOC_64K
+ if (length > (png_uint_32)65535L)
+ {
+ png_warning(png_ptr, "unknown chunk too large to fit in memory");
+ skip = length - (png_uint_32)65535L;
+ length = (png_uint_32)65535L;
+ }
+#endif
+ png_memcpy((png_charp)png_ptr->unknown_chunk.name,
+ (png_charp)png_ptr->chunk_name,
+ png_sizeof(png_ptr->unknown_chunk.name));
+ png_ptr->unknown_chunk.name[png_sizeof(png_ptr->unknown_chunk.name) - 1]
+ = '\0';
+
+ png_ptr->unknown_chunk.size = (png_size_t)length;
+
+ if (length == 0)
+ png_ptr->unknown_chunk.data = NULL;
+
+ else
+ {
+ png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr,
+ (png_size_t)length);
+ png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length);
+ }
+
+#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+ if (png_ptr->read_user_chunk_fn != NULL)
+ {
+ /* Callback to user unknown chunk handler */
+ int ret;
+ ret = (*(png_ptr->read_user_chunk_fn))
+ (png_ptr, &png_ptr->unknown_chunk);
+
+ if (ret < 0)
+ png_chunk_error(png_ptr, "error in user chunk");
+
+ if (ret == 0)
+ {
+ if (!(png_ptr->chunk_name[0] & 0x20))
+ if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name) !=
+ PNG_HANDLE_CHUNK_ALWAYS)
+ png_chunk_error(png_ptr, "unknown critical chunk");
+ png_set_unknown_chunks(png_ptr, info_ptr,
+ &png_ptr->unknown_chunk, 1);
+ }
+ }
+
+ else
+#endif
+ png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1);
+ png_free(png_ptr, png_ptr->unknown_chunk.data);
+ png_ptr->unknown_chunk.data = NULL;
+ }
+
+ else
+#endif
+ skip=length;
+ png_push_crc_skip(png_ptr, skip);
+}
+
+void /* PRIVATE */
+png_push_have_info(png_structp png_ptr, png_infop info_ptr)
+{
+ if (png_ptr->info_fn != NULL)
+ (*(png_ptr->info_fn))(png_ptr, info_ptr);
+}
+
+void /* PRIVATE */
+png_push_have_end(png_structp png_ptr, png_infop info_ptr)
+{
+ if (png_ptr->end_fn != NULL)
+ (*(png_ptr->end_fn))(png_ptr, info_ptr);
+}
+
+void /* PRIVATE */
+png_push_have_row(png_structp png_ptr, png_bytep row)
+{
+ if (png_ptr->row_fn != NULL)
+ (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number,
+ (int)png_ptr->pass);
+}
+
+void PNGAPI
+png_progressive_combine_row (png_structp png_ptr, png_bytep old_row,
+ png_const_bytep new_row)
+{
+ PNG_CONST int FARDATA png_pass_dsp_mask[7] =
+ {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff};
+
+ if (png_ptr == NULL)
+ return;
+
+ if (new_row != NULL) /* new_row must == png_ptr->row_buf here. */
+ png_combine_row(png_ptr, old_row, png_pass_dsp_mask[png_ptr->pass]);
+}
+
+void PNGAPI
+png_set_progressive_read_fn(png_structp png_ptr, png_voidp progressive_ptr,
+ png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn,
+ png_progressive_end_ptr end_fn)
+{
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->info_fn = info_fn;
+ png_ptr->row_fn = row_fn;
+ png_ptr->end_fn = end_fn;
+
+ png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer);
+}
+
+png_voidp PNGAPI
+png_get_progressive_ptr(png_const_structp png_ptr)
+{
+ if (png_ptr == NULL)
+ return (NULL);
+
+ return png_ptr->io_ptr;
+}
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
diff --git a/libpng/pngpriv.h b/libpng/pngpriv.h
new file mode 100644
index 0000000..a7e3b52
--- /dev/null
+++ b/libpng/pngpriv.h
@@ -0,0 +1,1246 @@
+
+/* pngpriv.h - private declarations for use inside libpng
+ *
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * Last changed in libpng 1.5.2 [March 31, 2011]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+/* The symbols declared in this file (including the functions declared
+ * as PNG_EXTERN) are PRIVATE. They are not part of the libpng public
+ * interface, and are not recommended for use by regular applications.
+ * Some of them may become public in the future; others may stay private,
+ * change in an incompatible way, or even disappear.
+ * Although the libpng users are not forbidden to include this header,
+ * they should be well aware of the issues that may arise from doing so.
+ */
+
+#ifndef PNGPRIV_H
+#define PNGPRIV_H
+
+/* This is required for the definition of abort(), used as a last ditch
+ * error handler when all else fails.
+ */
+#include <stdlib.h>
+
+#define PNGLIB_BUILD
+#ifdef PNG_USER_CONFIG
+# include "pngusr.h"
+ /* These should have been defined in pngusr.h */
+# ifndef PNG_USER_PRIVATEBUILD
+# define PNG_USER_PRIVATEBUILD "Custom libpng build"
+# endif
+# ifndef PNG_USER_DLLFNAME_POSTFIX
+# define PNG_USER_DLLFNAME_POSTFIX "Cb"
+# endif
+#endif
+#include "png.h"
+#include "pnginfo.h"
+#include "pngstruct.h"
+
+/* This is used for 16 bit gamma tables - only the top level pointers are const,
+ * this could be changed:
+ */
+typedef PNG_CONST png_uint_16p FAR * png_const_uint_16pp;
+
+/* Added at libpng-1.2.9 */
+/* Moved to pngpriv.h at libpng-1.5.0 */
+
+/* config.h is created by and PNG_CONFIGURE_LIBPNG is set by the "configure"
+ * script. We may need it here to get the correct configuration on things
+ * like limits.
+ */
+#ifdef PNG_CONFIGURE_LIBPNG
+# ifdef HAVE_CONFIG_H
+# include "config.h"
+# endif
+#endif
+
+/* Moved to pngpriv.h at libpng-1.5.0 */
+/* NOTE: some of these may have been used in external applications as
+ * these definitions were exposed in pngconf.h prior to 1.5.
+ */
+
+/* If you are running on a machine where you cannot allocate more
+ * than 64K of memory at once, uncomment this. While libpng will not
+ * normally need that much memory in a chunk (unless you load up a very
+ * large file), zlib needs to know how big of a chunk it can use, and
+ * libpng thus makes sure to check any memory allocation to verify it
+ * will fit into memory.
+ *
+ * zlib provides 'MAXSEG_64K' which, if defined, indicates the
+ * same limit and pngconf.h (already included) sets the limit
+ * if certain operating systems are detected.
+ */
+#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K)
+# define PNG_MAX_MALLOC_64K
+#endif
+
+#ifndef PNG_UNUSED
+/* Unused formal parameter warnings are silenced using the following macro
+ * which is expected to have no bad effects on performance (optimizing
+ * compilers will probably remove it entirely). Note that if you replace
+ * it with something other than whitespace, you must include the terminating
+ * semicolon.
+ */
+# define PNG_UNUSED(param) (void)param;
+#endif
+
+/* Just a little check that someone hasn't tried to define something
+ * contradictory.
+ */
+#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K)
+# undef PNG_ZBUF_SIZE
+# define PNG_ZBUF_SIZE 65536L
+#endif
+
+/* If warnings or errors are turned off the code is disabled
+ * or redirected here.
+ */
+#ifndef PNG_WARNINGS_SUPPORTED
+# define png_warning(s1,s2) ((void)0)
+# define png_chunk_warning(s1,s2) ((void)0)
+#endif
+#ifndef PNG_ERROR_TEXT_SUPPORTED
+# define png_error(s1,s2) png_err(s1)
+# define png_chunk_error(s1,s2) png_err(s1)
+# define png_fixed_error(s1,s2) png_err(s1)
+#endif
+
+#ifndef PNG_EXTERN
+/* The functions exported by PNG_EXTERN are internal functions, which
+ * aren't usually used outside the library (as far as I know), so it is
+ * debatable if they should be exported at all. In the future, when it
+ * is possible to have run-time registry of chunk-handling functions,
+ * some of these might be made available again.
+# define PNG_EXTERN extern
+ */
+# define PNG_EXTERN
+#endif
+
+/* Some fixed point APIs are still required even if not exported because
+ * they get used by the corresponding floating point APIs. This magic
+ * deals with this:
+ */
+#ifdef PNG_FIXED_POINT_SUPPORTED
+# define PNGFAPI PNGAPI
+#else
+# define PNGFAPI /* PRIVATE */
+#endif
+
+/* Other defines specific to compilers can go here. Try to keep
+ * them inside an appropriate ifdef/endif pair for portability.
+ */
+#if defined(PNG_FLOATING_POINT_SUPPORTED) ||\
+ defined(PNG_FLOATING_ARITHMETIC_SUPPORTED)
+ /* png.c requires the following ANSI-C constants if the conversion of
+ * floating point to ASCII is implemented therein:
+ *
+ * DBL_DIG Maximum number of decimal digits (can be set to any constant)
+ * DBL_MIN Smallest normalized fp number (can be set to an arbitrary value)
+ * DBL_MAX Maximum floating point number (can be set to an arbitrary value)
+ */
+# include <float.h>
+
+# if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \
+ defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC)
+ /* We need to check that <math.h> hasn't already been included earlier
+ * as it seems it doesn't agree with <fp.h>, yet we should really use
+ * <fp.h> if possible.
+ */
+# if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__)
+# include <fp.h>
+# endif
+# else
+# include <math.h>
+# endif
+# if defined(_AMIGA) && defined(__SASC) && defined(_M68881)
+ /* Amiga SAS/C: We must include builtin FPU functions when compiling using
+ * MATH=68881
+ */
+# include <m68881.h>
+# endif
+#endif
+
+/* This provides the non-ANSI (far) memory allocation routines. */
+#if defined(__TURBOC__) && defined(__MSDOS__)
+# include <mem.h>
+# include <alloc.h>
+#endif
+
+#if defined(WIN32) || defined(_Windows) || defined(_WINDOWS) || \
+ defined(_WIN32) || defined(__WIN32__)
+# include <windows.h> /* defines _WINDOWS_ macro */
+#endif
+
+/* Moved here around 1.5.0beta36 from pngconf.h */
+/* Users may want to use these so they are not private. Any library
+ * functions that are passed far data must be model-independent.
+ */
+
+/* Memory model/platform independent fns */
+#ifndef PNG_ABORT
+# ifdef _WINDOWS_
+# define PNG_ABORT() ExitProcess(0)
+# else
+# define PNG_ABORT() abort()
+# endif
+#endif
+
+#ifdef USE_FAR_KEYWORD
+/* Use this to make far-to-near assignments */
+# define CHECK 1
+# define NOCHECK 0
+# define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK))
+# define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK))
+# define png_strcpy _fstrcpy
+# define png_strncpy _fstrncpy /* Added to v 1.2.6 */
+# define png_strlen _fstrlen
+# define png_memcmp _fmemcmp /* SJT: added */
+# define png_memcpy _fmemcpy
+# define png_memset _fmemset
+# define png_sprintf sprintf
+#else
+# ifdef _WINDOWS_ /* Favor Windows over C runtime fns */
+# define CVT_PTR(ptr) (ptr)
+# define CVT_PTR_NOCHECK(ptr) (ptr)
+# define png_strcpy lstrcpyA
+# define png_strncpy lstrcpynA
+# define png_strlen lstrlenA
+# define png_memcmp memcmp
+# define png_memcpy CopyMemory
+# define png_memset memset
+# define png_sprintf wsprintfA
+# else
+# define CVT_PTR(ptr) (ptr)
+# define CVT_PTR_NOCHECK(ptr) (ptr)
+# define png_strcpy strcpy
+# define png_strncpy strncpy /* Added to v 1.2.6 */
+# define png_strlen strlen
+# define png_memcmp memcmp /* SJT: added */
+# define png_memcpy memcpy
+# define png_memset memset
+# define png_sprintf sprintf
+# endif
+#endif
+/* End of memory model/platform independent support */
+
+#ifndef PNG_NO_SNPRINTF
+# ifdef _MSC_VER
+# define png_snprintf _snprintf /* Added to v 1.2.19 */
+# define png_snprintf2 _snprintf
+# define png_snprintf6 _snprintf
+# else
+# define png_snprintf snprintf /* Added to v 1.2.19 */
+# define png_snprintf2 snprintf
+# define png_snprintf6 snprintf
+# endif
+#else
+ /* You don't have or don't want to use snprintf(). Caution: Using
+ * sprintf instead of snprintf exposes your application to accidental
+ * or malevolent buffer overflows. If you don't have snprintf()
+ * as a general rule you should provide one (you can get one from
+ * Portable OpenSSH).
+ */
+# define png_snprintf(s1,n,fmt,x1) png_sprintf(s1,fmt,x1)
+# define png_snprintf2(s1,n,fmt,x1,x2) png_sprintf(s1,fmt,x1,x2)
+# define png_snprintf6(s1,n,fmt,x1,x2,x3,x4,x5,x6) \
+ png_sprintf(s1,fmt,x1,x2,x3,x4,x5,x6)
+#endif
+/* End of 1.5.0beta36 move from pngconf.h */
+
+/* CONSTANTS and UTILITY MACROS
+ * These are used internally by libpng and not exposed in the API
+ */
+
+/* Various modes of operation. Note that after an init, mode is set to
+ * zero automatically when the structure is created.
+ */
+#define PNG_HAVE_IHDR 0x01
+#define PNG_HAVE_PLTE 0x02
+#define PNG_HAVE_IDAT 0x04
+#define PNG_AFTER_IDAT 0x08 /* Have complete zlib datastream */
+#define PNG_HAVE_IEND 0x10
+#define PNG_HAVE_gAMA 0x20
+#define PNG_HAVE_cHRM 0x40
+#define PNG_HAVE_sRGB 0x80
+#define PNG_HAVE_CHUNK_HEADER 0x100
+#define PNG_WROTE_tIME 0x200
+#define PNG_WROTE_INFO_BEFORE_PLTE 0x400
+#define PNG_BACKGROUND_IS_GRAY 0x800
+#define PNG_HAVE_PNG_SIGNATURE 0x1000
+#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */
+
+/* Flags for the transformations the PNG library does on the image data */
+#define PNG_BGR 0x0001
+#define PNG_INTERLACE 0x0002
+#define PNG_PACK 0x0004
+#define PNG_SHIFT 0x0008
+#define PNG_SWAP_BYTES 0x0010
+#define PNG_INVERT_MONO 0x0020
+#define PNG_QUANTIZE 0x0040
+#define PNG_BACKGROUND 0x0080
+#define PNG_BACKGROUND_EXPAND 0x0100
+#define PNG_EXPAND_16 0x0200 /* Added to libpng 1.5.2 */
+#define PNG_16_TO_8 0x0400
+#define PNG_RGBA 0x0800
+#define PNG_EXPAND 0x1000
+#define PNG_GAMMA 0x2000
+#define PNG_GRAY_TO_RGB 0x4000
+#define PNG_FILLER 0x8000L
+#define PNG_PACKSWAP 0x10000L
+#define PNG_SWAP_ALPHA 0x20000L
+#define PNG_STRIP_ALPHA 0x40000L
+#define PNG_INVERT_ALPHA 0x80000L
+#define PNG_USER_TRANSFORM 0x100000L
+#define PNG_RGB_TO_GRAY_ERR 0x200000L
+#define PNG_RGB_TO_GRAY_WARN 0x400000L
+#define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */
+ /* 0x800000L Unused */
+#define PNG_ADD_ALPHA 0x1000000L /* Added to libpng-1.2.7 */
+#define PNG_EXPAND_tRNS 0x2000000L /* Added to libpng-1.2.9 */
+ /* 0x4000000L unused */
+ /* 0x8000000L unused */
+ /* 0x10000000L unused */
+ /* 0x20000000L unused */
+ /* 0x40000000L unused */
+
+/* Flags for png_create_struct */
+#define PNG_STRUCT_PNG 0x0001
+#define PNG_STRUCT_INFO 0x0002
+
+/* Scaling factor for filter heuristic weighting calculations */
+#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT))
+#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT))
+
+/* Flags for the png_ptr->flags rather than declaring a byte for each one */
+#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001
+#define PNG_FLAG_ZLIB_CUSTOM_LEVEL 0x0002
+#define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL 0x0004
+#define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS 0x0008
+#define PNG_FLAG_ZLIB_CUSTOM_METHOD 0x0010
+#define PNG_FLAG_ZLIB_FINISHED 0x0020
+#define PNG_FLAG_ROW_INIT 0x0040
+#define PNG_FLAG_FILLER_AFTER 0x0080
+#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100
+#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200
+#define PNG_FLAG_CRC_CRITICAL_USE 0x0400
+#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800
+ /* 0x1000 unused */
+ /* 0x2000 unused */
+ /* 0x4000 unused */
+#define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000L
+#define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000L
+#define PNG_FLAG_LIBRARY_MISMATCH 0x20000L
+#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000L
+#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000L
+#define PNG_FLAG_MALLOC_NULL_MEM_OK 0x100000L
+ /* 0x200000L unused */
+ /* 0x400000L unused */
+#define PNG_FLAG_BENIGN_ERRORS_WARN 0x800000L /* Added to libpng-1.4.0 */
+ /* 0x1000000L unused */
+ /* 0x2000000L unused */
+ /* 0x4000000L unused */
+ /* 0x8000000L unused */
+ /* 0x10000000L unused */
+ /* 0x20000000L unused */
+ /* 0x40000000L unused */
+
+#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \
+ PNG_FLAG_CRC_ANCILLARY_NOWARN)
+
+#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \
+ PNG_FLAG_CRC_CRITICAL_IGNORE)
+
+#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \
+ PNG_FLAG_CRC_CRITICAL_MASK)
+
+/* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib
+ * can handle at once. This type need be no larger than 16 bits (so maximum of
+ * 65535), this define allows us to discover how big it is, but limited by the
+ * maximuum for png_size_t. The value can be overriden in a library build
+ * (pngusr.h, or set it in CPPFLAGS) and it works to set it to a considerably
+ * lower value (e.g. 255 works). A lower value may help memory usage (slightly)
+ * and may even improve performance on some systems (and degrade it on others.)
+ */
+#ifndef ZLIB_IO_MAX
+# define ZLIB_IO_MAX ((uInt)-1)
+#endif
+
+/* Save typing and make code easier to understand */
+
+#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \
+ abs((int)((c1).green) - (int)((c2).green)) + \
+ abs((int)((c1).blue) - (int)((c2).blue)))
+
+/* Added to libpng-1.2.6 JB */
+#define PNG_ROWBYTES(pixel_bits, width) \
+ ((pixel_bits) >= 8 ? \
+ ((png_size_t)(width) * (((png_size_t)(pixel_bits)) >> 3)) : \
+ (( ((png_size_t)(width) * ((png_size_t)(pixel_bits))) + 7) >> 3) )
+
+/* PNG_OUT_OF_RANGE returns true if value is outside the range
+ * ideal-delta..ideal+delta. Each argument is evaluated twice.
+ * "ideal" and "delta" should be constants, normally simple
+ * integers, "value" a variable. Added to libpng-1.2.6 JB
+ */
+#define PNG_OUT_OF_RANGE(value, ideal, delta) \
+ ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) )
+
+/* Conversions between fixed and floating point, only defined if
+ * required (to make sure the code doesn't accidentally use float
+ * when it is supposedly disabled.)
+ */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+/* The floating point conversion can't overflow, though it can and
+ * does lose accuracy relative to the original fixed point value.
+ * In practice this doesn't matter because png_fixed_point only
+ * stores numbers with very low precision. The png_ptr and s
+ * arguments are unused by default but are there in case error
+ * checking becomes a requirement.
+ */
+#define png_float(png_ptr, fixed, s) (.00001 * (fixed))
+
+/* The fixed point conversion performs range checking and evaluates
+ * its argument multiple times, so must be used with care. The
+ * range checking uses the PNG specification values for a signed
+ * 32 bit fixed point value except that the values are deliberately
+ * rounded-to-zero to an integral value - 21474 (21474.83 is roughly
+ * (2^31-1) * 100000). 's' is a string that describes the value being
+ * converted.
+ *
+ * NOTE: this macro will raise a png_error if the range check fails,
+ * therefore it is normally only appropriate to use this on values
+ * that come from API calls or other sources where an out of range
+ * error indicates a programming error, not a data error!
+ *
+ * NOTE: by default this is off - the macro is not used - because the
+ * function call saves a lot of code.
+ */
+#ifdef PNG_FIXED_POINT_MACRO_SUPPORTED
+#define png_fixed(png_ptr, fp, s) ((fp) <= 21474 && (fp) >= -21474 ?\
+ ((png_fixed_point)(100000 * (fp))) : (png_fixed_error(png_ptr, s),0))
+#else
+PNG_EXTERN png_fixed_point png_fixed PNGARG((png_structp png_ptr, double fp,
+ png_const_charp text));
+#endif
+#endif
+
+/* Constant strings for known chunk types. If you need to add a chunk,
+ * define the name here, and add an invocation of the macro wherever it's
+ * needed.
+ */
+#define PNG_IHDR PNG_CONST png_byte png_IHDR[5] = { 73, 72, 68, 82, '\0'}
+#define PNG_IDAT PNG_CONST png_byte png_IDAT[5] = { 73, 68, 65, 84, '\0'}
+#define PNG_IEND PNG_CONST png_byte png_IEND[5] = { 73, 69, 78, 68, '\0'}
+#define PNG_PLTE PNG_CONST png_byte png_PLTE[5] = { 80, 76, 84, 69, '\0'}
+#define PNG_bKGD PNG_CONST png_byte png_bKGD[5] = { 98, 75, 71, 68, '\0'}
+#define PNG_cHRM PNG_CONST png_byte png_cHRM[5] = { 99, 72, 82, 77, '\0'}
+#define PNG_gAMA PNG_CONST png_byte png_gAMA[5] = {103, 65, 77, 65, '\0'}
+#define PNG_hIST PNG_CONST png_byte png_hIST[5] = {104, 73, 83, 84, '\0'}
+#define PNG_iCCP PNG_CONST png_byte png_iCCP[5] = {105, 67, 67, 80, '\0'}
+#define PNG_iTXt PNG_CONST png_byte png_iTXt[5] = {105, 84, 88, 116, '\0'}
+#define PNG_oFFs PNG_CONST png_byte png_oFFs[5] = {111, 70, 70, 115, '\0'}
+#define PNG_pCAL PNG_CONST png_byte png_pCAL[5] = {112, 67, 65, 76, '\0'}
+#define PNG_sCAL PNG_CONST png_byte png_sCAL[5] = {115, 67, 65, 76, '\0'}
+#define PNG_pHYs PNG_CONST png_byte png_pHYs[5] = {112, 72, 89, 115, '\0'}
+#define PNG_sBIT PNG_CONST png_byte png_sBIT[5] = {115, 66, 73, 84, '\0'}
+#define PNG_sPLT PNG_CONST png_byte png_sPLT[5] = {115, 80, 76, 84, '\0'}
+#define PNG_sRGB PNG_CONST png_byte png_sRGB[5] = {115, 82, 71, 66, '\0'}
+#define PNG_sTER PNG_CONST png_byte png_sTER[5] = {115, 84, 69, 82, '\0'}
+#define PNG_tEXt PNG_CONST png_byte png_tEXt[5] = {116, 69, 88, 116, '\0'}
+#define PNG_tIME PNG_CONST png_byte png_tIME[5] = {116, 73, 77, 69, '\0'}
+#define PNG_tRNS PNG_CONST png_byte png_tRNS[5] = {116, 82, 78, 83, '\0'}
+#define PNG_zTXt PNG_CONST png_byte png_zTXt[5] = {122, 84, 88, 116, '\0'}
+
+
+/* Inhibit C++ name-mangling for libpng functions but not for system calls. */
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* These functions are used internally in the code. They generally
+ * shouldn't be used unless you are writing code to add or replace some
+ * functionality in libpng. More information about most functions can
+ * be found in the files where the functions are located.
+ */
+
+/* Allocate memory for an internal libpng struct */
+PNG_EXTERN PNG_FUNCTION(png_voidp,png_create_struct,PNGARG((int type)),
+ PNG_ALLOCATED);
+
+/* Free memory from internal libpng struct */
+PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr));
+
+PNG_EXTERN PNG_FUNCTION(png_voidp,png_create_struct_2,
+ PNGARG((int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr)),
+ PNG_ALLOCATED);
+PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr,
+ png_free_ptr free_fn, png_voidp mem_ptr));
+
+/* Free any memory that info_ptr points to and reset struct. */
+PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr,
+ png_infop info_ptr));
+
+/* Function to allocate memory for zlib. PNGAPI is disallowed. */
+PNG_EXTERN PNG_FUNCTION(voidpf,png_zalloc,PNGARG((voidpf png_ptr, uInt items,
+ uInt size)),PNG_ALLOCATED);
+
+/* Function to free memory for zlib. PNGAPI is disallowed. */
+PNG_EXTERN void png_zfree PNGARG((voidpf png_ptr, voidpf ptr));
+
+/* Next four functions are used internally as callbacks. PNGCBAPI is required
+ * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3, changed to
+ * PNGCBAPI at 1.5.0
+ */
+
+PNG_EXTERN void PNGCBAPI png_default_read_data PNGARG((png_structp png_ptr,
+ png_bytep data, png_size_t length));
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+PNG_EXTERN void PNGCBAPI png_push_fill_buffer PNGARG((png_structp png_ptr,
+ png_bytep buffer, png_size_t length));
+#endif
+
+PNG_EXTERN void PNGCBAPI png_default_write_data PNGARG((png_structp png_ptr,
+ png_bytep data, png_size_t length));
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+# ifdef PNG_STDIO_SUPPORTED
+PNG_EXTERN void PNGCBAPI png_default_flush PNGARG((png_structp png_ptr));
+# endif
+#endif
+
+/* Reset the CRC variable */
+PNG_EXTERN void png_reset_crc PNGARG((png_structp png_ptr));
+
+/* Write the "data" buffer to whatever output you are using */
+PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr,
+ png_const_bytep data, png_size_t length));
+
+/* Read and check the PNG file signature */
+PNG_EXTERN void png_read_sig PNGARG((png_structp png_ptr, png_infop info_ptr));
+
+/* Read the chunk header (length + type name) */
+PNG_EXTERN png_uint_32 png_read_chunk_header PNGARG((png_structp png_ptr));
+
+/* Read data from whatever input you are using into the "data" buffer */
+PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data,
+ png_size_t length));
+
+/* Read bytes into buf, and update png_ptr->crc */
+PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf,
+ png_size_t length));
+
+/* Decompress data in a chunk that uses compression */
+#if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \
+ defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED)
+PNG_EXTERN void png_decompress_chunk PNGARG((png_structp png_ptr,
+ int comp_type, png_size_t chunklength, png_size_t prefix_length,
+ png_size_t *data_length));
+#endif
+
+/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */
+PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip));
+
+/* Read the CRC from the file and compare it to the libpng calculated CRC */
+PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr));
+
+/* Calculate the CRC over a section of data. Note that we are only
+ * passing a maximum of 64K on systems that have this as a memory limit,
+ * since this is the maximum buffer size we can specify.
+ */
+PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr,
+ png_const_bytep ptr, png_size_t length));
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+PNG_EXTERN void png_flush PNGARG((png_structp png_ptr));
+#endif
+
+/* Write various chunks */
+
+/* Write the IHDR chunk, and update the png_struct with the necessary
+ * information.
+ */
+PNG_EXTERN void png_write_IHDR PNGARG((png_structp png_ptr, png_uint_32 width,
+ png_uint_32 height,
+ int bit_depth, int color_type, int compression_method, int filter_method,
+ int interlace_method));
+
+PNG_EXTERN void png_write_PLTE PNGARG((png_structp png_ptr,
+ png_const_colorp palette, png_uint_32 num_pal));
+
+PNG_EXTERN void png_write_IDAT PNGARG((png_structp png_ptr, png_bytep data,
+ png_size_t length));
+
+PNG_EXTERN void png_write_IEND PNGARG((png_structp png_ptr));
+
+#ifdef PNG_WRITE_gAMA_SUPPORTED
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+PNG_EXTERN void png_write_gAMA PNGARG((png_structp png_ptr, double file_gamma));
+# endif
+# ifdef PNG_FIXED_POINT_SUPPORTED
+PNG_EXTERN void png_write_gAMA_fixed PNGARG((png_structp png_ptr,
+ png_fixed_point file_gamma));
+# endif
+#endif
+
+#ifdef PNG_WRITE_sBIT_SUPPORTED
+PNG_EXTERN void png_write_sBIT PNGARG((png_structp png_ptr,
+ png_const_color_8p sbit, int color_type));
+#endif
+
+#ifdef PNG_WRITE_cHRM_SUPPORTED
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+PNG_EXTERN void png_write_cHRM PNGARG((png_structp png_ptr,
+ double white_x, double white_y,
+ double red_x, double red_y, double green_x, double green_y,
+ double blue_x, double blue_y));
+# endif
+PNG_EXTERN void png_write_cHRM_fixed PNGARG((png_structp png_ptr,
+ png_fixed_point int_white_x, png_fixed_point int_white_y,
+ png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point
+ int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x,
+ png_fixed_point int_blue_y));
+#endif
+
+#ifdef PNG_WRITE_sRGB_SUPPORTED
+PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr,
+ int intent));
+#endif
+
+#ifdef PNG_WRITE_iCCP_SUPPORTED
+PNG_EXTERN void png_write_iCCP PNGARG((png_structp png_ptr,
+ png_const_charp name, int compression_type,
+ png_const_charp profile, int proflen));
+ /* Note to maintainer: profile should be png_bytep */
+#endif
+
+#ifdef PNG_WRITE_sPLT_SUPPORTED
+PNG_EXTERN void png_write_sPLT PNGARG((png_structp png_ptr,
+ png_const_sPLT_tp palette));
+#endif
+
+#ifdef PNG_WRITE_tRNS_SUPPORTED
+PNG_EXTERN void png_write_tRNS PNGARG((png_structp png_ptr,
+ png_const_bytep trans, png_const_color_16p values, int number,
+ int color_type));
+#endif
+
+#ifdef PNG_WRITE_bKGD_SUPPORTED
+PNG_EXTERN void png_write_bKGD PNGARG((png_structp png_ptr,
+ png_const_color_16p values, int color_type));
+#endif
+
+#ifdef PNG_WRITE_hIST_SUPPORTED
+PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr,
+ png_const_uint_16p hist, int num_hist));
+#endif
+
+#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \
+ defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
+PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr,
+ png_const_charp key, png_charpp new_key));
+#endif
+
+#ifdef PNG_WRITE_tEXt_SUPPORTED
+PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_const_charp key,
+ png_const_charp text, png_size_t text_len));
+#endif
+
+#ifdef PNG_WRITE_zTXt_SUPPORTED
+PNG_EXTERN void png_write_zTXt PNGARG((png_structp png_ptr, png_const_charp key,
+ png_const_charp text, png_size_t text_len, int compression));
+#endif
+
+#ifdef PNG_WRITE_iTXt_SUPPORTED
+PNG_EXTERN void png_write_iTXt PNGARG((png_structp png_ptr,
+ int compression, png_const_charp key, png_const_charp lang,
+ png_const_charp lang_key, png_const_charp text));
+#endif
+
+#ifdef PNG_TEXT_SUPPORTED /* Added at version 1.0.14 and 1.2.4 */
+PNG_EXTERN int png_set_text_2 PNGARG((png_structp png_ptr,
+ png_infop info_ptr, png_const_textp text_ptr, int num_text));
+#endif
+
+#ifdef PNG_WRITE_oFFs_SUPPORTED
+PNG_EXTERN void png_write_oFFs PNGARG((png_structp png_ptr,
+ png_int_32 x_offset, png_int_32 y_offset, int unit_type));
+#endif
+
+#ifdef PNG_WRITE_pCAL_SUPPORTED
+PNG_EXTERN void png_write_pCAL PNGARG((png_structp png_ptr, png_charp purpose,
+ png_int_32 X0, png_int_32 X1, int type, int nparams,
+ png_const_charp units, png_charpp params));
+#endif
+
+#ifdef PNG_WRITE_pHYs_SUPPORTED
+PNG_EXTERN void png_write_pHYs PNGARG((png_structp png_ptr,
+ png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit,
+ int unit_type));
+#endif
+
+#ifdef PNG_WRITE_tIME_SUPPORTED
+PNG_EXTERN void png_write_tIME PNGARG((png_structp png_ptr,
+ png_const_timep mod_time));
+#endif
+
+#ifdef PNG_WRITE_sCAL_SUPPORTED
+PNG_EXTERN void png_write_sCAL_s PNGARG((png_structp png_ptr,
+ int unit, png_const_charp width, png_const_charp height));
+#endif
+
+/* Called when finished processing a row of data */
+PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr));
+
+/* Internal use only. Called before first row of data */
+PNG_EXTERN void png_write_start_row PNGARG((png_structp png_ptr));
+
+/* Combine a row of data, dealing with alpha, etc. if requested */
+PNG_EXTERN void png_combine_row PNGARG((png_structp png_ptr, png_bytep row,
+ int mask));
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+/* Expand an interlaced row */
+/* OLD pre-1.0.9 interface:
+PNG_EXTERN void png_do_read_interlace PNGARG((png_row_infop row_info,
+ png_bytep row, int pass, png_uint_32 transformations));
+ */
+PNG_EXTERN void png_do_read_interlace PNGARG((png_structp png_ptr));
+#endif
+
+/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+/* Grab pixels out of a row for an interlaced pass */
+PNG_EXTERN void png_do_write_interlace PNGARG((png_row_infop row_info,
+ png_bytep row, int pass));
+#endif
+
+/* Unfilter a row */
+PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr,
+ png_row_infop row_info, png_bytep row, png_const_bytep prev_row,
+ int filter));
+
+/* Choose the best filter to use and filter the row data */
+PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr,
+ png_row_infop row_info));
+
+/* Write out the filtered row. */
+PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr,
+ png_bytep filtered_row));
+/* Finish a row while reading, dealing with interlacing passes, etc. */
+PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr));
+
+/* Initialize the row buffers, etc. */
+PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr));
+/* Optional call to update the users info structure */
+PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr,
+ png_infop info_ptr));
+
+/* These are the functions that do the transformations */
+#ifdef PNG_READ_FILLER_SUPPORTED
+PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info,
+ png_bytep row, png_uint_32 filler, png_uint_32 flags));
+#endif
+
+#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
+PNG_EXTERN void png_do_read_swap_alpha PNGARG((png_row_infop row_info,
+ png_bytep row));
+#endif
+
+#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
+PNG_EXTERN void png_do_write_swap_alpha PNGARG((png_row_infop row_info,
+ png_bytep row));
+#endif
+
+#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
+PNG_EXTERN void png_do_read_invert_alpha PNGARG((png_row_infop row_info,
+ png_bytep row));
+#endif
+
+#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
+PNG_EXTERN void png_do_write_invert_alpha PNGARG((png_row_infop row_info,
+ png_bytep row));
+#endif
+
+#if defined(PNG_WRITE_FILLER_SUPPORTED) || \
+ defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+PNG_EXTERN void png_do_strip_channel PNGARG((png_row_infop row_info,
+ png_bytep row, int at_start));
+#endif
+
+#ifdef PNG_16BIT_SUPPORTED
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+PNG_EXTERN void png_do_swap PNGARG((png_row_infop row_info,
+ png_bytep row));
+#endif
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \
+ defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+PNG_EXTERN void png_do_packswap PNGARG((png_row_infop row_info,
+ png_bytep row));
+#endif
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+PNG_EXTERN int png_do_rgb_to_gray PNGARG((png_structp png_ptr,
+ png_row_infop row_info, png_bytep row));
+#endif
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+PNG_EXTERN void png_do_gray_to_rgb PNGARG((png_row_infop row_info,
+ png_bytep row));
+#endif
+
+#ifdef PNG_READ_PACK_SUPPORTED
+PNG_EXTERN void png_do_unpack PNGARG((png_row_infop row_info,
+ png_bytep row));
+#endif
+
+#ifdef PNG_READ_SHIFT_SUPPORTED
+PNG_EXTERN void png_do_unshift PNGARG((png_row_infop row_info,
+ png_bytep row, png_const_color_8p sig_bits));
+#endif
+
+#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
+PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info,
+ png_bytep row));
+#endif
+
+#ifdef PNG_READ_16_TO_8_SUPPORTED
+PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info,
+ png_bytep row));
+#endif
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+PNG_EXTERN void png_do_quantize PNGARG((png_row_infop row_info,
+ png_bytep row, png_const_bytep palette_lookup,
+ png_const_bytep quantize_lookup));
+
+# ifdef PNG_CORRECT_PALETTE_SUPPORTED
+PNG_EXTERN void png_correct_palette PNGARG((png_structp png_ptr,
+ png_colorp palette, int num_palette));
+# endif
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+PNG_EXTERN void png_do_bgr PNGARG((png_row_infop row_info,
+ png_bytep row));
+#endif
+
+#ifdef PNG_WRITE_PACK_SUPPORTED
+PNG_EXTERN void png_do_pack PNGARG((png_row_infop row_info,
+ png_bytep row, png_uint_32 bit_depth));
+#endif
+
+#ifdef PNG_WRITE_SHIFT_SUPPORTED
+PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info,
+ png_bytep row, png_const_color_8p bit_depth));
+#endif
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+# ifdef PNG_READ_GAMMA_SUPPORTED
+PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info,
+ png_bytep row, png_const_color_16p trans_color,
+ png_const_color_16p background, png_const_color_16p background_1,
+ png_const_bytep gamma_table, png_const_bytep gamma_from_1,
+ png_const_bytep gamma_to_1, png_const_uint_16pp gamma_16,
+ png_const_uint_16pp gamma_16_from_1, png_const_uint_16pp gamma_16_to_1,
+ int gamma_shift));
+# else
+PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info,
+ png_bytep row, png_const_color_16p trans_color,
+ png_const_color_16p background));
+# endif
+#endif
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info,
+ png_bytep row, png_const_bytep gamma_table,
+ png_const_uint_16pp gamma_16_table, int gamma_shift));
+#endif
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+PNG_EXTERN void png_do_expand_palette PNGARG((png_row_infop row_info,
+ png_bytep row, png_const_colorp palette, png_const_bytep trans,
+ int num_trans));
+PNG_EXTERN void png_do_expand PNGARG((png_row_infop row_info,
+ png_bytep row, png_const_color_16p trans_color));
+#endif
+
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+PNG_EXTERN void png_do_expand_16 PNGARG((png_row_infop row_info,
+ png_bytep row));
+#endif
+
+/* The following decodes the appropriate chunks, and does error correction,
+ * then calls the appropriate callback for the chunk if it is valid.
+ */
+
+/* Decode the IHDR chunk */
+PNG_EXTERN void png_handle_IHDR PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+PNG_EXTERN void png_handle_PLTE PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+PNG_EXTERN void png_handle_IEND PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+
+#ifdef PNG_READ_bKGD_SUPPORTED
+PNG_EXTERN void png_handle_bKGD PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+#endif
+
+#ifdef PNG_READ_cHRM_SUPPORTED
+PNG_EXTERN void png_handle_cHRM PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+#endif
+
+#ifdef PNG_READ_gAMA_SUPPORTED
+PNG_EXTERN void png_handle_gAMA PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+#endif
+
+#ifdef PNG_READ_hIST_SUPPORTED
+PNG_EXTERN void png_handle_hIST PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+#endif
+
+#ifdef PNG_READ_iCCP_SUPPORTED
+PNG_EXTERN void png_handle_iCCP PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+#endif /* PNG_READ_iCCP_SUPPORTED */
+
+#ifdef PNG_READ_iTXt_SUPPORTED
+PNG_EXTERN void png_handle_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+#endif
+
+#ifdef PNG_READ_oFFs_SUPPORTED
+PNG_EXTERN void png_handle_oFFs PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+#endif
+
+#ifdef PNG_READ_pCAL_SUPPORTED
+PNG_EXTERN void png_handle_pCAL PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+#endif
+
+#ifdef PNG_READ_pHYs_SUPPORTED
+PNG_EXTERN void png_handle_pHYs PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+#endif
+
+#ifdef PNG_READ_sBIT_SUPPORTED
+PNG_EXTERN void png_handle_sBIT PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+#endif
+
+#ifdef PNG_READ_sCAL_SUPPORTED
+PNG_EXTERN void png_handle_sCAL PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+#endif
+
+#ifdef PNG_READ_sPLT_SUPPORTED
+PNG_EXTERN void png_handle_sPLT PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+#endif /* PNG_READ_sPLT_SUPPORTED */
+
+#ifdef PNG_READ_sRGB_SUPPORTED
+PNG_EXTERN void png_handle_sRGB PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+#endif
+
+#ifdef PNG_READ_tEXt_SUPPORTED
+PNG_EXTERN void png_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+#endif
+
+#ifdef PNG_READ_tIME_SUPPORTED
+PNG_EXTERN void png_handle_tIME PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+#endif
+
+#ifdef PNG_READ_tRNS_SUPPORTED
+PNG_EXTERN void png_handle_tRNS PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+#endif
+
+#ifdef PNG_READ_zTXt_SUPPORTED
+PNG_EXTERN void png_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 length));
+#endif
+
+PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr,
+ png_infop info_ptr, png_uint_32 length));
+
+PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr,
+ png_const_bytep chunk_name));
+
+/* Handle the transformations for reading and writing */
+PNG_EXTERN void png_do_read_transformations PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_do_write_transformations PNGARG((png_structp png_ptr));
+
+PNG_EXTERN void png_init_read_transformations PNGARG((png_structp png_ptr));
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr,
+ png_infop info_ptr));
+PNG_EXTERN void png_push_read_sig PNGARG((png_structp png_ptr,
+ png_infop info_ptr));
+PNG_EXTERN void png_push_check_crc PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_push_crc_skip PNGARG((png_structp png_ptr,
+ png_uint_32 length));
+PNG_EXTERN void png_push_crc_finish PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_push_save_buffer PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_push_restore_buffer PNGARG((png_structp png_ptr,
+ png_bytep buffer, png_size_t buffer_length));
+PNG_EXTERN void png_push_read_IDAT PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_process_IDAT_data PNGARG((png_structp png_ptr,
+ png_bytep buffer, png_size_t buffer_length));
+PNG_EXTERN void png_push_process_row PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_push_handle_unknown PNGARG((png_structp png_ptr,
+ png_infop info_ptr, png_uint_32 length));
+PNG_EXTERN void png_push_have_info PNGARG((png_structp png_ptr,
+ png_infop info_ptr));
+PNG_EXTERN void png_push_have_end PNGARG((png_structp png_ptr,
+ png_infop info_ptr));
+PNG_EXTERN void png_push_have_row PNGARG((png_structp png_ptr, png_bytep row));
+PNG_EXTERN void png_push_read_end PNGARG((png_structp png_ptr,
+ png_infop info_ptr));
+PNG_EXTERN void png_process_some_data PNGARG((png_structp png_ptr,
+ png_infop info_ptr));
+PNG_EXTERN void png_read_push_finish_row PNGARG((png_structp png_ptr));
+# ifdef PNG_READ_tEXt_SUPPORTED
+PNG_EXTERN void png_push_handle_tEXt PNGARG((png_structp png_ptr,
+ png_infop info_ptr, png_uint_32 length));
+PNG_EXTERN void png_push_read_tEXt PNGARG((png_structp png_ptr,
+ png_infop info_ptr));
+# endif
+# ifdef PNG_READ_zTXt_SUPPORTED
+PNG_EXTERN void png_push_handle_zTXt PNGARG((png_structp png_ptr,
+ png_infop info_ptr, png_uint_32 length));
+PNG_EXTERN void png_push_read_zTXt PNGARG((png_structp png_ptr,
+ png_infop info_ptr));
+# endif
+# ifdef PNG_READ_iTXt_SUPPORTED
+PNG_EXTERN void png_push_handle_iTXt PNGARG((png_structp png_ptr,
+ png_infop info_ptr, png_uint_32 length));
+PNG_EXTERN void png_push_read_iTXt PNGARG((png_structp png_ptr,
+ png_infop info_ptr));
+# endif
+
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+PNG_EXTERN void png_do_read_intrapixel PNGARG((png_row_infop row_info,
+ png_bytep row));
+PNG_EXTERN void png_do_write_intrapixel PNGARG((png_row_infop row_info,
+ png_bytep row));
+#endif
+
+/* Added at libpng version 1.4.0 */
+#ifdef PNG_CHECK_cHRM_SUPPORTED
+PNG_EXTERN int png_check_cHRM_fixed PNGARG((png_structp png_ptr,
+ png_fixed_point int_white_x, png_fixed_point int_white_y,
+ png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point
+ int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x,
+ png_fixed_point int_blue_y));
+#endif
+
+#ifdef PNG_CHECK_cHRM_SUPPORTED
+/* Added at libpng version 1.2.34 and 1.4.0 */
+/* Currently only used by png_check_cHRM_fixed */
+PNG_EXTERN void png_64bit_product PNGARG((long v1, long v2,
+ unsigned long *hi_product, unsigned long *lo_product));
+#endif
+
+/* Added at libpng version 1.4.0 */
+PNG_EXTERN void png_check_IHDR PNGARG((png_structp png_ptr,
+ png_uint_32 width, png_uint_32 height, int bit_depth,
+ int color_type, int interlace_type, int compression_type,
+ int filter_type));
+
+/* Free all memory used by the read (old method - NOT DLL EXPORTED) */
+PNG_EXTERN void png_read_destroy PNGARG((png_structp png_ptr,
+ png_infop info_ptr, png_infop end_info_ptr));
+
+/* Free any memory used in png_ptr struct (old method - NOT DLL EXPORTED) */
+PNG_EXTERN void png_write_destroy PNGARG((png_structp png_ptr));
+
+#ifdef USE_FAR_KEYWORD /* memory model conversion function */
+PNG_EXTERN void *png_far_to_near PNGARG((png_structp png_ptr, png_voidp ptr,
+ int check));
+#endif /* USE_FAR_KEYWORD */
+
+#if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)
+PNG_EXTERN PNG_FUNCTION(void, png_fixed_error, (png_structp png_ptr,
+ png_const_charp name),PNG_NORETURN);
+#endif
+
+/* ASCII to FP interfaces, currently only implemented if sCAL
+ * support is required.
+ */
+#if defined(PNG_READ_sCAL_SUPPORTED)
+/* MAX_DIGITS is actually the maximum number of characters in an sCAL
+ * width or height, derived from the precision (number of significant
+ * digits - a build time settable option) and assumpitions about the
+ * maximum ridiculous exponent.
+ */
+#define PNG_sCAL_MAX_DIGITS (PNG_sCAL_PRECISION+1/*.*/+1/*E*/+10/*exponent*/)
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+PNG_EXTERN void png_ascii_from_fp PNGARG((png_structp png_ptr, png_charp ascii,
+ png_size_t size, double fp, unsigned int precision));
+#endif /* FLOATING_POINT */
+
+#ifdef PNG_FIXED_POINT_SUPPORTED
+PNG_EXTERN void png_ascii_from_fixed PNGARG((png_structp png_ptr,
+ png_charp ascii, png_size_t size, png_fixed_point fp));
+#endif /* FIXED_POINT */
+#endif /* READ_sCAL */
+
+#if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED)
+/* An internal API to validate the format of a floating point number.
+ * The result is the index of the next character. If the number is
+ * not valid it will be the index of a character in the supposed number.
+ *
+ * The format of a number is defined in the PNG extensions specification
+ * and this API is strictly conformant to that spec, not anyone elses!
+ *
+ * The format as a regular expression is:
+ *
+ * [+-]?[0-9]+.?([Ee][+-]?[0-9]+)?
+ *
+ * or:
+ *
+ * [+-]?.[0-9]+(.[0-9]+)?([Ee][+-]?[0-9]+)?
+ *
+ * The complexity is that either integer or fraction must be present and the
+ * fraction is permitted to have no digits only if the integer is present.
+ *
+ * NOTE: The dangling E problem.
+ * There is a PNG valid floating point number in the following:
+ *
+ * PNG floating point numb1.ers are not greedy.
+ *
+ * Working this out requires *TWO* character lookahead (because of the
+ * sign), the parser does not do this - it will fail at the 'r' - this
+ * doesn't matter for PNG sCAL chunk values, but it requires more care
+ * if the value were ever to be embedded in something more complex. Use
+ * ANSI-C strtod if you need the lookahead.
+ */
+/* State table for the parser. */
+#define PNG_FP_INTEGER 0 /* before or in integer */
+#define PNG_FP_FRACTION 1 /* before or in fraction */
+#define PNG_FP_EXPONENT 2 /* before or in exponent */
+#define PNG_FP_STATE 3 /* mask for the above */
+#define PNG_FP_SAW_SIGN 4 /* Saw +/- in current state */
+#define PNG_FP_SAW_DIGIT 8 /* Saw a digit in current state */
+#define PNG_FP_SAW_DOT 16 /* Saw a dot in current state */
+#define PNG_FP_SAW_E 32 /* Saw an E (or e) in current state */
+#define PNG_FP_SAW_ANY 60 /* Saw any of the above 4 */
+#define PNG_FP_WAS_VALID 64 /* Preceding substring is a valid fp number */
+#define PNG_FP_INVALID 128 /* Available for callers as a distinct value */
+
+/* Result codes for the parser (boolean - true meants ok, false means
+ * not ok yet.)
+ */
+#define PNG_FP_MAYBE 0 /* The number may be valid in the future */
+#define PNG_FP_OK 1 /* The number is valid */
+
+/* The actual parser. This can be called repeatedly, it updates
+ * the index into the string and the state variable (which must
+ * be initialzed to 0). It returns a result code, as above. There
+ * is no point calling the parser any more if it fails to advance to
+ * the end of the string - it is stuck on an invalid character (or
+ * terminated by '\0').
+ *
+ * Note that the pointer will consume an E or even an E+ then leave
+ * a 'maybe' state even though a preceding integer.fraction is valid.
+ * The PNG_FP_WAS_VALID flag indicates that a preceding substring was
+ * a valid number. It's possible to recover from this by calling
+ * the parser again (from the start, with state 0) but with a string
+ * that omits the last character (i.e. set the size to the index of
+ * the problem character.) This has not been tested within libpng.
+ */
+PNG_EXTERN int png_check_fp_number PNGARG((png_const_charp string,
+ png_size_t size, int *statep, png_size_tp whereami));
+
+/* This is the same but it checks a complete string and returns true
+ * only if it just contains a floating point number.
+ */
+PNG_EXTERN int png_check_fp_string PNGARG((png_const_charp string,
+ png_size_t size));
+#endif /* pCAL || sCAL */
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) ||\
+ defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED)
+/* Added at libpng version 1.5.0 */
+/* This is a utility to provide a*times/div (rounded) and indicate
+ * if there is an overflow. The result is a boolean - false (0)
+ * for overflow, true (1) if no overflow, in which case *res
+ * holds the result.
+ */
+PNG_EXTERN int png_muldiv PNGARG((png_fixed_point_p res, png_fixed_point a,
+ png_int_32 multiplied_by, png_int_32 divided_by));
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED)
+/* Same deal, but issue a warning on overflow and return 0. */
+PNG_EXTERN png_fixed_point png_muldiv_warn PNGARG((png_structp png_ptr,
+ png_fixed_point a, png_int_32 multiplied_by, png_int_32 divided_by));
+#endif
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+/* Calculate a reciprocal - used for gamma values. This returns
+ * 0 if the argument is 0 in order to maintain an undefined value,
+ * there are no warnings.
+ */
+PNG_EXTERN png_fixed_point png_reciprocal PNGARG((png_fixed_point a));
+
+/* The same but gives a reciprocal of the product of two fixed point
+ * values. Accuracy is suitable for gamma calculations but this is
+ * not exact - use png_muldiv for that.
+ */
+PNG_EXTERN png_fixed_point png_reciprocal2 PNGARG((png_fixed_point a,
+ png_fixed_point b));
+#endif
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+/* Internal fixed point gamma correction. These APIs are called as
+ * required to convert single values - they don't need to be fast,
+ * they are not used when processing image pixel values.
+ *
+ * While the input is an 'unsigned' value it must actually be the
+ * correct bit value - 0..255 or 0..65535 as required.
+ */
+PNG_EXTERN png_uint_16 png_gamma_correct PNGARG((png_structp png_ptr,
+ unsigned int value, png_fixed_point gamma_value));
+PNG_EXTERN int png_gamma_significant PNGARG((png_fixed_point gamma_value));
+PNG_EXTERN png_uint_16 png_gamma_16bit_correct PNGARG((unsigned int value,
+ png_fixed_point gamma_value));
+PNG_EXTERN png_byte png_gamma_8bit_correct PNGARG((unsigned int value,
+ png_fixed_point gamma_value));
+PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr,
+ int bit_depth));
+#endif
+
+/* Maintainer: Put new private prototypes here ^ and in libpngpf.3 */
+
+
+#include "pngdebug.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PNGPRIV_H */
diff --git a/libpng/pngread.c b/libpng/pngread.c
new file mode 100644
index 0000000..aa84001
--- /dev/null
+++ b/libpng/pngread.c
@@ -0,0 +1,1473 @@
+
+/* pngread.c - read a PNG file
+ *
+ * Last changed in libpng 1.5.2 [March 31, 2011]
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * This file contains routines that an application calls directly to
+ * read a PNG file or stream.
+ */
+
+#include "pngpriv.h"
+
+#ifdef PNG_READ_SUPPORTED
+
+/* Create a PNG structure for reading, and allocate any memory needed. */
+PNG_FUNCTION(png_structp,PNGAPI
+png_create_read_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
+ png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED)
+{
+
+#ifdef PNG_USER_MEM_SUPPORTED
+ return (png_create_read_struct_2(user_png_ver, error_ptr, error_fn,
+ warn_fn, NULL, NULL, NULL));
+}
+
+/* Alternate create PNG structure for reading, and allocate any memory
+ * needed.
+ */
+PNG_FUNCTION(png_structp,PNGAPI
+png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
+ png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
+ png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+#ifdef PNG_SETJMP_SUPPORTED
+ volatile
+#endif
+ png_structp png_ptr;
+ volatile int png_cleanup_needed = 0;
+
+#ifdef PNG_SETJMP_SUPPORTED
+#ifdef USE_FAR_KEYWORD
+ jmp_buf png_jmpbuf;
+#endif
+#endif
+
+ int i;
+
+ png_debug(1, "in png_create_read_struct");
+
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG,
+ malloc_fn, mem_ptr);
+#else
+ png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG);
+#endif
+ if (png_ptr == NULL)
+ return (NULL);
+
+ /* Added at libpng-1.2.6 */
+#ifdef PNG_USER_LIMITS_SUPPORTED
+ png_ptr->user_width_max = PNG_USER_WIDTH_MAX;
+ png_ptr->user_height_max = PNG_USER_HEIGHT_MAX;
+
+# ifdef PNG_USER_CHUNK_CACHE_MAX
+ /* Added at libpng-1.2.43 and 1.4.0 */
+ png_ptr->user_chunk_cache_max = PNG_USER_CHUNK_CACHE_MAX;
+# endif
+
+# ifdef PNG_SET_USER_CHUNK_MALLOC_MAX
+ /* Added at libpng-1.2.43 and 1.4.1 */
+ png_ptr->user_chunk_malloc_max = PNG_USER_CHUNK_MALLOC_MAX;
+# endif
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+/* Applications that neglect to set up their own setjmp() and then
+ encounter a png_error() will longjmp here. Since the jmpbuf is
+ then meaningless we abort instead of returning. */
+#ifdef USE_FAR_KEYWORD
+ if (setjmp(png_jmpbuf))
+#else
+ if (setjmp(png_jmpbuf(png_ptr))) /* Sets longjmp to match setjmp */
+#endif
+ PNG_ABORT();
+#ifdef USE_FAR_KEYWORD
+ png_memcpy(png_jmpbuf(png_ptr), png_jmpbuf, png_sizeof(jmp_buf));
+#endif
+#endif /* PNG_SETJMP_SUPPORTED */
+
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn);
+#endif
+
+ png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
+
+ if (user_png_ver)
+ {
+ i = 0;
+
+ do
+ {
+ if (user_png_ver[i] != png_libpng_ver[i])
+ png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
+ } while (png_libpng_ver[i++]);
+ }
+
+ else
+ png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
+
+
+ if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH)
+ {
+ /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
+ * we must recompile any applications that use any older library version.
+ * For versions after libpng 1.0, we will be compatible, so we need
+ * only check the first digit.
+ */
+ if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
+ (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) ||
+ (user_png_ver[0] == '0' && user_png_ver[2] < '9'))
+ {
+#ifdef PNG_CONSOLE_IO_SUPPORTED
+ char msg[80];
+ if (user_png_ver)
+ {
+ png_snprintf2(msg, 80,
+ "Application built with libpng-%.20s"
+ " but running with %.20s",
+ user_png_ver,
+ png_libpng_ver);
+ png_warning(png_ptr, msg);
+ }
+#else
+ png_warning(png_ptr,
+ "Incompatible libpng version in application and library");
+#endif
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+ png_ptr->flags = 0;
+#endif
+
+ png_cleanup_needed = 1;
+ }
+ }
+
+ if (!png_cleanup_needed)
+ {
+ /* Initialize zbuf - compression buffer */
+ png_ptr->zbuf_size = PNG_ZBUF_SIZE;
+ png_ptr->zbuf = (png_bytep)png_malloc_warn(png_ptr, png_ptr->zbuf_size);
+
+ if (png_ptr->zbuf == NULL)
+ png_cleanup_needed = 1;
+ }
+
+ png_ptr->zstream.zalloc = png_zalloc;
+ png_ptr->zstream.zfree = png_zfree;
+ png_ptr->zstream.opaque = (voidpf)png_ptr;
+
+ if (!png_cleanup_needed)
+ {
+ switch (inflateInit(&png_ptr->zstream))
+ {
+ case Z_OK:
+ break; /* Do nothing */
+
+ case Z_MEM_ERROR:
+ png_warning(png_ptr, "zlib memory error");
+ png_cleanup_needed = 1;
+ break;
+
+ case Z_STREAM_ERROR:
+ png_warning(png_ptr, "zlib stream error");
+ png_cleanup_needed = 1;
+ break;
+
+ case Z_VERSION_ERROR:
+ png_warning(png_ptr, "zlib version error");
+ png_cleanup_needed = 1;
+ break;
+
+ default: png_warning(png_ptr, "Unknown zlib error");
+ png_cleanup_needed = 1;
+ }
+ }
+
+ if (png_cleanup_needed)
+ {
+ /* Clean up PNG structure and deallocate any memory. */
+ png_free(png_ptr, png_ptr->zbuf);
+ png_ptr->zbuf = NULL;
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_destroy_struct_2((png_voidp)png_ptr,
+ (png_free_ptr)free_fn, (png_voidp)mem_ptr);
+#else
+ png_destroy_struct((png_voidp)png_ptr);
+#endif
+ return (NULL);
+ }
+
+ png_ptr->zstream.next_out = png_ptr->zbuf;
+ png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+
+ png_set_read_fn(png_ptr, NULL, NULL);
+
+
+ return (png_ptr);
+}
+
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read the information before the actual image data. This has been
+ * changed in v0.90 to allow reading a file that already has the magic
+ * bytes read from the stream. You can tell libpng how many bytes have
+ * been read from the beginning of the stream (up to the maximum of 8)
+ * via png_set_sig_bytes(), and we will only check the remaining bytes
+ * here. The application can then have access to the signature bytes we
+ * read if it is determined that this isn't a valid PNG file.
+ */
+void PNGAPI
+png_read_info(png_structp png_ptr, png_infop info_ptr)
+{
+ png_debug(1, "in png_read_info");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ /* Read and check the PNG file signature. */
+ png_read_sig(png_ptr, info_ptr);
+
+ for (;;)
+ {
+ PNG_IHDR;
+ PNG_IDAT;
+ PNG_IEND;
+ PNG_PLTE;
+#ifdef PNG_READ_bKGD_SUPPORTED
+ PNG_bKGD;
+#endif
+#ifdef PNG_READ_cHRM_SUPPORTED
+ PNG_cHRM;
+#endif
+#ifdef PNG_READ_gAMA_SUPPORTED
+ PNG_gAMA;
+#endif
+#ifdef PNG_READ_hIST_SUPPORTED
+ PNG_hIST;
+#endif
+#ifdef PNG_READ_iCCP_SUPPORTED
+ PNG_iCCP;
+#endif
+#ifdef PNG_READ_iTXt_SUPPORTED
+ PNG_iTXt;
+#endif
+#ifdef PNG_READ_oFFs_SUPPORTED
+ PNG_oFFs;
+#endif
+#ifdef PNG_READ_pCAL_SUPPORTED
+ PNG_pCAL;
+#endif
+#ifdef PNG_READ_pHYs_SUPPORTED
+ PNG_pHYs;
+#endif
+#ifdef PNG_READ_sBIT_SUPPORTED
+ PNG_sBIT;
+#endif
+#ifdef PNG_READ_sCAL_SUPPORTED
+ PNG_sCAL;
+#endif
+#ifdef PNG_READ_sPLT_SUPPORTED
+ PNG_sPLT;
+#endif
+#ifdef PNG_READ_sRGB_SUPPORTED
+ PNG_sRGB;
+#endif
+#ifdef PNG_READ_tEXt_SUPPORTED
+ PNG_tEXt;
+#endif
+#ifdef PNG_READ_tIME_SUPPORTED
+ PNG_tIME;
+#endif
+#ifdef PNG_READ_tRNS_SUPPORTED
+ PNG_tRNS;
+#endif
+#ifdef PNG_READ_zTXt_SUPPORTED
+ PNG_zTXt;
+#endif
+ png_uint_32 length = png_read_chunk_header(png_ptr);
+ PNG_CONST png_bytep chunk_name = png_ptr->chunk_name;
+
+ /* This should be a binary subdivision search or a hash for
+ * matching the chunk name rather than a linear search.
+ */
+ if (!png_memcmp(chunk_name, png_IDAT, 4))
+ if (png_ptr->mode & PNG_AFTER_IDAT)
+ png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT;
+
+ if (!png_memcmp(chunk_name, png_IHDR, 4))
+ png_handle_IHDR(png_ptr, info_ptr, length);
+
+ else if (!png_memcmp(chunk_name, png_IEND, 4))
+ png_handle_IEND(png_ptr, info_ptr, length);
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ else if (png_handle_as_unknown(png_ptr, chunk_name))
+ {
+ if (!png_memcmp(chunk_name, png_IDAT, 4))
+ png_ptr->mode |= PNG_HAVE_IDAT;
+
+ png_handle_unknown(png_ptr, info_ptr, length);
+
+ if (!png_memcmp(chunk_name, png_PLTE, 4))
+ png_ptr->mode |= PNG_HAVE_PLTE;
+
+ else if (!png_memcmp(chunk_name, png_IDAT, 4))
+ {
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before IDAT");
+
+ else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+ !(png_ptr->mode & PNG_HAVE_PLTE))
+ png_error(png_ptr, "Missing PLTE before IDAT");
+
+ break;
+ }
+ }
+#endif
+ else if (!png_memcmp(chunk_name, png_PLTE, 4))
+ png_handle_PLTE(png_ptr, info_ptr, length);
+
+ else if (!png_memcmp(chunk_name, png_IDAT, 4))
+ {
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before IDAT");
+
+ else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+ !(png_ptr->mode & PNG_HAVE_PLTE))
+ png_error(png_ptr, "Missing PLTE before IDAT");
+
+ png_ptr->idat_size = length;
+ png_ptr->mode |= PNG_HAVE_IDAT;
+ break;
+ }
+
+#ifdef PNG_READ_bKGD_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_bKGD, 4))
+ png_handle_bKGD(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_cHRM_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_cHRM, 4))
+ png_handle_cHRM(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_gAMA_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_gAMA, 4))
+ png_handle_gAMA(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_hIST_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_hIST, 4))
+ png_handle_hIST(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_oFFs_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_oFFs, 4))
+ png_handle_oFFs(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_pCAL_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_pCAL, 4))
+ png_handle_pCAL(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_sCAL_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_sCAL, 4))
+ png_handle_sCAL(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_pHYs_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_pHYs, 4))
+ png_handle_pHYs(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_sBIT_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_sBIT, 4))
+ png_handle_sBIT(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_sRGB_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_sRGB, 4))
+ png_handle_sRGB(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_iCCP_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_iCCP, 4))
+ png_handle_iCCP(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_sPLT_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_sPLT, 4))
+ png_handle_sPLT(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_tEXt_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_tEXt, 4))
+ png_handle_tEXt(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_tIME_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_tIME, 4))
+ png_handle_tIME(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_tRNS_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_tRNS, 4))
+ png_handle_tRNS(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_zTXt_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_zTXt, 4))
+ png_handle_zTXt(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_iTXt_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_iTXt, 4))
+ png_handle_iTXt(png_ptr, info_ptr, length);
+#endif
+
+ else
+ png_handle_unknown(png_ptr, info_ptr, length);
+ }
+}
+#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
+
+/* Optional call to update the users info_ptr structure */
+void PNGAPI
+png_read_update_info(png_structp png_ptr, png_infop info_ptr)
+{
+ png_debug(1, "in png_read_update_info");
+
+ if (png_ptr == NULL)
+ return;
+
+ if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
+ png_read_start_row(png_ptr);
+
+ else
+ png_warning(png_ptr,
+ "Ignoring extra png_read_update_info() call;"
+ " row buffer not reallocated");
+
+ png_read_transform_info(png_ptr, info_ptr);
+}
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Initialize palette, background, etc, after transformations
+ * are set, but before any reading takes place. This allows
+ * the user to obtain a gamma-corrected palette, for example.
+ * If the user doesn't call this, we will do it ourselves.
+ */
+void PNGAPI
+png_start_read_image(png_structp png_ptr)
+{
+ png_debug(1, "in png_start_read_image");
+
+ if (png_ptr == NULL)
+ return;
+
+ if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
+ png_read_start_row(png_ptr);
+ else
+ png_warning(png_ptr,
+ "Ignoring extra png_start_read_image() call;"
+ " row buffer not reallocated");
+}
+#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+void PNGAPI
+png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row)
+{
+ PNG_IDAT;
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+ PNG_CONST int png_pass_dsp_mask[7] = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55,
+ 0xff};
+ PNG_CONST int png_pass_mask[7] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff};
+#endif
+ int ret;
+
+ if (png_ptr == NULL)
+ return;
+
+ png_debug2(1, "in png_read_row (row %lu, pass %d)",
+ (unsigned long)png_ptr->row_number, png_ptr->pass);
+
+ if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
+ png_read_start_row(png_ptr);
+
+ if (png_ptr->row_number == 0 && png_ptr->pass == 0)
+ {
+ /* Check for transforms that have been set but were defined out */
+#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED)
+ if (png_ptr->transformations & PNG_INVERT_MONO)
+ png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined");
+#endif
+
+#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED)
+ if (png_ptr->transformations & PNG_FILLER)
+ png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined");
+#endif
+
+#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \
+ !defined(PNG_READ_PACKSWAP_SUPPORTED)
+ if (png_ptr->transformations & PNG_PACKSWAP)
+ png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined");
+#endif
+
+#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED)
+ if (png_ptr->transformations & PNG_PACK)
+ png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined");
+#endif
+
+#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED)
+ if (png_ptr->transformations & PNG_SHIFT)
+ png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined");
+#endif
+
+#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED)
+ if (png_ptr->transformations & PNG_BGR)
+ png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined");
+#endif
+
+#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED)
+ if (png_ptr->transformations & PNG_SWAP_BYTES)
+ png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined");
+#endif
+ }
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+ /* If interlaced and we do not need a new row, combine row and return */
+ if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
+ {
+ switch (png_ptr->pass)
+ {
+ case 0:
+ if (png_ptr->row_number & 0x07)
+ {
+ if (dsp_row != NULL)
+ png_combine_row(png_ptr, dsp_row,
+ png_pass_dsp_mask[png_ptr->pass]);
+ png_read_finish_row(png_ptr);
+ return;
+ }
+ break;
+
+ case 1:
+ if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)
+ {
+ if (dsp_row != NULL)
+ png_combine_row(png_ptr, dsp_row,
+ png_pass_dsp_mask[png_ptr->pass]);
+
+ png_read_finish_row(png_ptr);
+ return;
+ }
+ break;
+
+ case 2:
+ if ((png_ptr->row_number & 0x07) != 4)
+ {
+ if (dsp_row != NULL && (png_ptr->row_number & 4))
+ png_combine_row(png_ptr, dsp_row,
+ png_pass_dsp_mask[png_ptr->pass]);
+
+ png_read_finish_row(png_ptr);
+ return;
+ }
+ break;
+
+ case 3:
+ if ((png_ptr->row_number & 3) || png_ptr->width < 3)
+ {
+ if (dsp_row != NULL)
+ png_combine_row(png_ptr, dsp_row,
+ png_pass_dsp_mask[png_ptr->pass]);
+
+ png_read_finish_row(png_ptr);
+ return;
+ }
+ break;
+
+ case 4:
+ if ((png_ptr->row_number & 3) != 2)
+ {
+ if (dsp_row != NULL && (png_ptr->row_number & 2))
+ png_combine_row(png_ptr, dsp_row,
+ png_pass_dsp_mask[png_ptr->pass]);
+
+ png_read_finish_row(png_ptr);
+ return;
+ }
+ break;
+ case 5:
+ if ((png_ptr->row_number & 1) || png_ptr->width < 2)
+ {
+ if (dsp_row != NULL)
+ png_combine_row(png_ptr, dsp_row,
+ png_pass_dsp_mask[png_ptr->pass]);
+
+ png_read_finish_row(png_ptr);
+ return;
+ }
+ break;
+
+ default:
+ case 6:
+ if (!(png_ptr->row_number & 1))
+ {
+ png_read_finish_row(png_ptr);
+ return;
+ }
+ break;
+ }
+ }
+#endif
+
+ if (!(png_ptr->mode & PNG_HAVE_IDAT))
+ png_error(png_ptr, "Invalid attempt to read row data");
+
+ png_ptr->zstream.next_out = png_ptr->row_buf;
+ png_ptr->zstream.avail_out =
+ (uInt)(PNG_ROWBYTES(png_ptr->pixel_depth,
+ png_ptr->iwidth) + 1);
+
+ do
+ {
+ if (!(png_ptr->zstream.avail_in))
+ {
+ while (!png_ptr->idat_size)
+ {
+ png_crc_finish(png_ptr, 0);
+
+ png_ptr->idat_size = png_read_chunk_header(png_ptr);
+ if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+ png_error(png_ptr, "Not enough image data");
+ }
+ png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size;
+ png_ptr->zstream.next_in = png_ptr->zbuf;
+ if (png_ptr->zbuf_size > png_ptr->idat_size)
+ png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size;
+ png_crc_read(png_ptr, png_ptr->zbuf,
+ (png_size_t)png_ptr->zstream.avail_in);
+ png_ptr->idat_size -= png_ptr->zstream.avail_in;
+ }
+
+ ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+
+ if (ret == Z_STREAM_END)
+ {
+ if (png_ptr->zstream.avail_out || png_ptr->zstream.avail_in ||
+ png_ptr->idat_size)
+ png_benign_error(png_ptr, "Extra compressed data");
+ png_ptr->mode |= PNG_AFTER_IDAT;
+ png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+ break;
+ }
+
+ if (ret != Z_OK)
+ png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg :
+ "Decompression error");
+
+ } while (png_ptr->zstream.avail_out);
+
+ png_ptr->row_info.color_type = png_ptr->color_type;
+ png_ptr->row_info.width = png_ptr->iwidth;
+ png_ptr->row_info.channels = png_ptr->channels;
+ png_ptr->row_info.bit_depth = png_ptr->bit_depth;
+ png_ptr->row_info.pixel_depth = png_ptr->pixel_depth;
+ png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth,
+ png_ptr->row_info.width);
+
+ if (png_ptr->row_buf[0])
+ png_read_filter_row(png_ptr, &(png_ptr->row_info),
+ png_ptr->row_buf + 1, png_ptr->prev_row + 1,
+ (int)(png_ptr->row_buf[0]));
+
+ png_memcpy(png_ptr->prev_row, png_ptr->row_buf, png_ptr->rowbytes + 1);
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+ if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
+ (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
+ {
+ /* Intrapixel differencing */
+ png_do_read_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1);
+ }
+#endif
+
+
+ if (png_ptr->transformations)
+ png_do_read_transformations(png_ptr);
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+ /* Blow up interlaced rows to full size */
+ if (png_ptr->interlaced &&
+ (png_ptr->transformations & PNG_INTERLACE))
+ {
+ if (png_ptr->pass < 6)
+ /* Old interface (pre-1.0.9):
+ * png_do_read_interlace(&(png_ptr->row_info),
+ * png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations);
+ */
+ png_do_read_interlace(png_ptr);
+
+ if (dsp_row != NULL)
+ png_combine_row(png_ptr, dsp_row, png_pass_dsp_mask[png_ptr->pass]);
+
+ if (row != NULL)
+ png_combine_row(png_ptr, row, png_pass_mask[png_ptr->pass]);
+ }
+
+ else
+#endif
+ {
+ if (row != NULL)
+ png_combine_row(png_ptr, row, 0xff);
+
+ if (dsp_row != NULL)
+ png_combine_row(png_ptr, dsp_row, 0xff);
+ }
+ png_read_finish_row(png_ptr);
+
+ if (png_ptr->read_row_fn != NULL)
+ (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
+}
+#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read one or more rows of image data. If the image is interlaced,
+ * and png_set_interlace_handling() has been called, the rows need to
+ * contain the contents of the rows from the previous pass. If the
+ * image has alpha or transparency, and png_handle_alpha()[*] has been
+ * called, the rows contents must be initialized to the contents of the
+ * screen.
+ *
+ * "row" holds the actual image, and pixels are placed in it
+ * as they arrive. If the image is displayed after each pass, it will
+ * appear to "sparkle" in. "display_row" can be used to display a
+ * "chunky" progressive image, with finer detail added as it becomes
+ * available. If you do not want this "chunky" display, you may pass
+ * NULL for display_row. If you do not want the sparkle display, and
+ * you have not called png_handle_alpha(), you may pass NULL for rows.
+ * If you have called png_handle_alpha(), and the image has either an
+ * alpha channel or a transparency chunk, you must provide a buffer for
+ * rows. In this case, you do not have to provide a display_row buffer
+ * also, but you may. If the image is not interlaced, or if you have
+ * not called png_set_interlace_handling(), the display_row buffer will
+ * be ignored, so pass NULL to it.
+ *
+ * [*] png_handle_alpha() does not exist yet, as of this version of libpng
+ */
+
+void PNGAPI
+png_read_rows(png_structp png_ptr, png_bytepp row,
+ png_bytepp display_row, png_uint_32 num_rows)
+{
+ png_uint_32 i;
+ png_bytepp rp;
+ png_bytepp dp;
+
+ png_debug(1, "in png_read_rows");
+
+ if (png_ptr == NULL)
+ return;
+
+ rp = row;
+ dp = display_row;
+ if (rp != NULL && dp != NULL)
+ for (i = 0; i < num_rows; i++)
+ {
+ png_bytep rptr = *rp++;
+ png_bytep dptr = *dp++;
+
+ png_read_row(png_ptr, rptr, dptr);
+ }
+
+ else if (rp != NULL)
+ for (i = 0; i < num_rows; i++)
+ {
+ png_bytep rptr = *rp;
+ png_read_row(png_ptr, rptr, NULL);
+ rp++;
+ }
+
+ else if (dp != NULL)
+ for (i = 0; i < num_rows; i++)
+ {
+ png_bytep dptr = *dp;
+ png_read_row(png_ptr, NULL, dptr);
+ dp++;
+ }
+}
+#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read the entire image. If the image has an alpha channel or a tRNS
+ * chunk, and you have called png_handle_alpha()[*], you will need to
+ * initialize the image to the current image that PNG will be overlaying.
+ * We set the num_rows again here, in case it was incorrectly set in
+ * png_read_start_row() by a call to png_read_update_info() or
+ * png_start_read_image() if png_set_interlace_handling() wasn't called
+ * prior to either of these functions like it should have been. You can
+ * only call this function once. If you desire to have an image for
+ * each pass of a interlaced image, use png_read_rows() instead.
+ *
+ * [*] png_handle_alpha() does not exist yet, as of this version of libpng
+ */
+void PNGAPI
+png_read_image(png_structp png_ptr, png_bytepp image)
+{
+ png_uint_32 i, image_height;
+ int pass, j;
+ png_bytepp rp;
+
+ png_debug(1, "in png_read_image");
+
+ if (png_ptr == NULL)
+ return;
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+ if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
+ {
+ pass = png_set_interlace_handling(png_ptr);
+ /* And make sure transforms are initialized. */
+ png_start_read_image(png_ptr);
+ }
+ else
+ {
+ if (png_ptr->interlaced && !(png_ptr->transformations & PNG_INTERLACE))
+ {
+ /* Caller called png_start_read_image or png_read_update_info without
+ * first turning on the PNG_INTERLACE transform. We can fix this here,
+ * but the caller should do it!
+ */
+ png_warning(png_ptr, "Interlace handling should be turned on when "
+ "using png_read_image");
+ /* Make sure this is set correctly */
+ png_ptr->num_rows = png_ptr->height;
+ }
+
+ /* Obtain the pass number, which also turns on the PNG_INTERLACE flag in
+ * the above error case.
+ */
+ pass = png_set_interlace_handling(png_ptr);
+ }
+#else
+ if (png_ptr->interlaced)
+ png_error(png_ptr,
+ "Cannot read interlaced image -- interlace handler disabled");
+
+ pass = 1;
+#endif
+
+ image_height=png_ptr->height;
+
+ for (j = 0; j < pass; j++)
+ {
+ rp = image;
+ for (i = 0; i < image_height; i++)
+ {
+ png_read_row(png_ptr, *rp, NULL);
+ rp++;
+ }
+ }
+}
+#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read the end of the PNG file. Will not read past the end of the
+ * file, will verify the end is accurate, and will read any comments
+ * or time information at the end of the file, if info is not NULL.
+ */
+void PNGAPI
+png_read_end(png_structp png_ptr, png_infop info_ptr)
+{
+ png_debug(1, "in png_read_end");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_crc_finish(png_ptr, 0); /* Finish off CRC from last IDAT chunk */
+
+ do
+ {
+ PNG_IHDR;
+ PNG_IDAT;
+ PNG_IEND;
+ PNG_PLTE;
+#ifdef PNG_READ_bKGD_SUPPORTED
+ PNG_bKGD;
+#endif
+#ifdef PNG_READ_cHRM_SUPPORTED
+ PNG_cHRM;
+#endif
+#ifdef PNG_READ_gAMA_SUPPORTED
+ PNG_gAMA;
+#endif
+#ifdef PNG_READ_hIST_SUPPORTED
+ PNG_hIST;
+#endif
+#ifdef PNG_READ_iCCP_SUPPORTED
+ PNG_iCCP;
+#endif
+#ifdef PNG_READ_iTXt_SUPPORTED
+ PNG_iTXt;
+#endif
+#ifdef PNG_READ_oFFs_SUPPORTED
+ PNG_oFFs;
+#endif
+#ifdef PNG_READ_pCAL_SUPPORTED
+ PNG_pCAL;
+#endif
+#ifdef PNG_READ_pHYs_SUPPORTED
+ PNG_pHYs;
+#endif
+#ifdef PNG_READ_sBIT_SUPPORTED
+ PNG_sBIT;
+#endif
+#ifdef PNG_READ_sCAL_SUPPORTED
+ PNG_sCAL;
+#endif
+#ifdef PNG_READ_sPLT_SUPPORTED
+ PNG_sPLT;
+#endif
+#ifdef PNG_READ_sRGB_SUPPORTED
+ PNG_sRGB;
+#endif
+#ifdef PNG_READ_tEXt_SUPPORTED
+ PNG_tEXt;
+#endif
+#ifdef PNG_READ_tIME_SUPPORTED
+ PNG_tIME;
+#endif
+#ifdef PNG_READ_tRNS_SUPPORTED
+ PNG_tRNS;
+#endif
+#ifdef PNG_READ_zTXt_SUPPORTED
+ PNG_zTXt;
+#endif
+ png_uint_32 length = png_read_chunk_header(png_ptr);
+ PNG_CONST png_bytep chunk_name = png_ptr->chunk_name;
+
+ if (!png_memcmp(chunk_name, png_IHDR, 4))
+ png_handle_IHDR(png_ptr, info_ptr, length);
+
+ else if (!png_memcmp(chunk_name, png_IEND, 4))
+ png_handle_IEND(png_ptr, info_ptr, length);
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ else if (png_handle_as_unknown(png_ptr, chunk_name))
+ {
+ if (!png_memcmp(chunk_name, png_IDAT, 4))
+ {
+ if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT))
+ png_benign_error(png_ptr, "Too many IDATs found");
+ }
+ png_handle_unknown(png_ptr, info_ptr, length);
+ if (!png_memcmp(chunk_name, png_PLTE, 4))
+ png_ptr->mode |= PNG_HAVE_PLTE;
+ }
+#endif
+
+ else if (!png_memcmp(chunk_name, png_IDAT, 4))
+ {
+ /* Zero length IDATs are legal after the last IDAT has been
+ * read, but not after other chunks have been read.
+ */
+ if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT))
+ png_benign_error(png_ptr, "Too many IDATs found");
+
+ png_crc_finish(png_ptr, length);
+ }
+ else if (!png_memcmp(chunk_name, png_PLTE, 4))
+ png_handle_PLTE(png_ptr, info_ptr, length);
+
+#ifdef PNG_READ_bKGD_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_bKGD, 4))
+ png_handle_bKGD(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_cHRM_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_cHRM, 4))
+ png_handle_cHRM(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_gAMA_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_gAMA, 4))
+ png_handle_gAMA(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_hIST_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_hIST, 4))
+ png_handle_hIST(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_oFFs_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_oFFs, 4))
+ png_handle_oFFs(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_pCAL_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_pCAL, 4))
+ png_handle_pCAL(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_sCAL_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_sCAL, 4))
+ png_handle_sCAL(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_pHYs_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_pHYs, 4))
+ png_handle_pHYs(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_sBIT_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_sBIT, 4))
+ png_handle_sBIT(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_sRGB_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_sRGB, 4))
+ png_handle_sRGB(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_iCCP_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_iCCP, 4))
+ png_handle_iCCP(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_sPLT_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_sPLT, 4))
+ png_handle_sPLT(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_tEXt_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_tEXt, 4))
+ png_handle_tEXt(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_tIME_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_tIME, 4))
+ png_handle_tIME(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_tRNS_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_tRNS, 4))
+ png_handle_tRNS(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_zTXt_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_zTXt, 4))
+ png_handle_zTXt(png_ptr, info_ptr, length);
+#endif
+
+#ifdef PNG_READ_iTXt_SUPPORTED
+ else if (!png_memcmp(chunk_name, png_iTXt, 4))
+ png_handle_iTXt(png_ptr, info_ptr, length);
+#endif
+
+ else
+ png_handle_unknown(png_ptr, info_ptr, length);
+ } while (!(png_ptr->mode & PNG_HAVE_IEND));
+}
+#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
+
+/* Free all memory used by the read */
+void PNGAPI
+png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr,
+ png_infopp end_info_ptr_ptr)
+{
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL, end_info_ptr = NULL;
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_free_ptr free_fn = NULL;
+ png_voidp mem_ptr = NULL;
+#endif
+
+ png_debug(1, "in png_destroy_read_struct");
+
+ if (png_ptr_ptr != NULL)
+ png_ptr = *png_ptr_ptr;
+ if (png_ptr == NULL)
+ return;
+
+#ifdef PNG_USER_MEM_SUPPORTED
+ free_fn = png_ptr->free_fn;
+ mem_ptr = png_ptr->mem_ptr;
+#endif
+
+ if (info_ptr_ptr != NULL)
+ info_ptr = *info_ptr_ptr;
+
+ if (end_info_ptr_ptr != NULL)
+ end_info_ptr = *end_info_ptr_ptr;
+
+ png_read_destroy(png_ptr, info_ptr, end_info_ptr);
+
+ if (info_ptr != NULL)
+ {
+#ifdef PNG_TEXT_SUPPORTED
+ png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, -1);
+#endif
+
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn,
+ (png_voidp)mem_ptr);
+#else
+ png_destroy_struct((png_voidp)info_ptr);
+#endif
+ *info_ptr_ptr = NULL;
+ }
+
+ if (end_info_ptr != NULL)
+ {
+#ifdef PNG_READ_TEXT_SUPPORTED
+ png_free_data(png_ptr, end_info_ptr, PNG_FREE_TEXT, -1);
+#endif
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_destroy_struct_2((png_voidp)end_info_ptr, (png_free_ptr)free_fn,
+ (png_voidp)mem_ptr);
+#else
+ png_destroy_struct((png_voidp)end_info_ptr);
+#endif
+ *end_info_ptr_ptr = NULL;
+ }
+
+ if (png_ptr != NULL)
+ {
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn,
+ (png_voidp)mem_ptr);
+#else
+ png_destroy_struct((png_voidp)png_ptr);
+#endif
+ *png_ptr_ptr = NULL;
+ }
+}
+
+/* Free all memory used by the read (old method) */
+void /* PRIVATE */
+png_read_destroy(png_structp png_ptr, png_infop info_ptr,
+ png_infop end_info_ptr)
+{
+#ifdef PNG_SETJMP_SUPPORTED
+ jmp_buf tmp_jmp;
+#endif
+ png_error_ptr error_fn;
+ png_error_ptr warning_fn;
+ png_voidp error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_free_ptr free_fn;
+#endif
+
+ png_debug(1, "in png_read_destroy");
+
+ if (info_ptr != NULL)
+ png_info_destroy(png_ptr, info_ptr);
+
+ if (end_info_ptr != NULL)
+ png_info_destroy(png_ptr, end_info_ptr);
+
+ png_free(png_ptr, png_ptr->zbuf);
+ png_free(png_ptr, png_ptr->big_row_buf);
+ png_free(png_ptr, png_ptr->prev_row);
+ png_free(png_ptr, png_ptr->chunkdata);
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+ png_free(png_ptr, png_ptr->palette_lookup);
+ png_free(png_ptr, png_ptr->quantize_index);
+#endif
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ png_free(png_ptr, png_ptr->gamma_table);
+#endif
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+ png_free(png_ptr, png_ptr->gamma_from_1);
+ png_free(png_ptr, png_ptr->gamma_to_1);
+#endif
+
+ if (png_ptr->free_me & PNG_FREE_PLTE)
+ png_zfree(png_ptr, png_ptr->palette);
+ png_ptr->free_me &= ~PNG_FREE_PLTE;
+
+#if defined(PNG_tRNS_SUPPORTED) || \
+ defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+ if (png_ptr->free_me & PNG_FREE_TRNS)
+ png_free(png_ptr, png_ptr->trans_alpha);
+ png_ptr->free_me &= ~PNG_FREE_TRNS;
+#endif
+
+#ifdef PNG_READ_hIST_SUPPORTED
+ if (png_ptr->free_me & PNG_FREE_HIST)
+ png_free(png_ptr, png_ptr->hist);
+ png_ptr->free_me &= ~PNG_FREE_HIST;
+#endif
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ if (png_ptr->gamma_16_table != NULL)
+ {
+ int i;
+ int istop = (1 << (8 - png_ptr->gamma_shift));
+ for (i = 0; i < istop; i++)
+ {
+ png_free(png_ptr, png_ptr->gamma_16_table[i]);
+ }
+ png_free(png_ptr, png_ptr->gamma_16_table);
+ }
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+ if (png_ptr->gamma_16_from_1 != NULL)
+ {
+ int i;
+ int istop = (1 << (8 - png_ptr->gamma_shift));
+ for (i = 0; i < istop; i++)
+ {
+ png_free(png_ptr, png_ptr->gamma_16_from_1[i]);
+ }
+ png_free(png_ptr, png_ptr->gamma_16_from_1);
+ }
+ if (png_ptr->gamma_16_to_1 != NULL)
+ {
+ int i;
+ int istop = (1 << (8 - png_ptr->gamma_shift));
+ for (i = 0; i < istop; i++)
+ {
+ png_free(png_ptr, png_ptr->gamma_16_to_1[i]);
+ }
+ png_free(png_ptr, png_ptr->gamma_16_to_1);
+ }
+#endif
+#endif
+
+#ifdef PNG_TIME_RFC1123_SUPPORTED
+ png_free(png_ptr, png_ptr->time_buffer);
+#endif
+
+ inflateEnd(&png_ptr->zstream);
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+ png_free(png_ptr, png_ptr->save_buffer);
+#endif
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+#ifdef PNG_TEXT_SUPPORTED
+ png_free(png_ptr, png_ptr->current_text);
+#endif /* PNG_TEXT_SUPPORTED */
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
+ /* Save the important info out of the png_struct, in case it is
+ * being used again.
+ */
+#ifdef PNG_SETJMP_SUPPORTED
+ png_memcpy(tmp_jmp, png_ptr->png_jmpbuf, png_sizeof(jmp_buf));
+#endif
+
+ error_fn = png_ptr->error_fn;
+ warning_fn = png_ptr->warning_fn;
+ error_ptr = png_ptr->error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+ free_fn = png_ptr->free_fn;
+#endif
+
+ png_memset(png_ptr, 0, png_sizeof(png_struct));
+
+ png_ptr->error_fn = error_fn;
+ png_ptr->warning_fn = warning_fn;
+ png_ptr->error_ptr = error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_ptr->free_fn = free_fn;
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+ png_memcpy(png_ptr->png_jmpbuf, tmp_jmp, png_sizeof(jmp_buf));
+#endif
+
+}
+
+void PNGAPI
+png_set_read_status_fn(png_structp png_ptr, png_read_status_ptr read_row_fn)
+{
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->read_row_fn = read_row_fn;
+}
+
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+#ifdef PNG_INFO_IMAGE_SUPPORTED
+void PNGAPI
+png_read_png(png_structp png_ptr, png_infop info_ptr,
+ int transforms,
+ voidp params)
+{
+ int row;
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ /* png_read_info() gives us all of the information from the
+ * PNG file before the first IDAT (image data chunk).
+ */
+ png_read_info(png_ptr, info_ptr);
+ if (info_ptr->height > PNG_UINT_32_MAX/png_sizeof(png_bytep))
+ png_error(png_ptr, "Image is too high to process with png_read_png()");
+
+ /* -------------- image transformations start here ------------------- */
+
+#ifdef PNG_READ_16_TO_8_SUPPORTED
+ /* Tell libpng to strip 16 bit/color files down to 8 bits per color.
+ */
+ if (transforms & PNG_TRANSFORM_STRIP_16)
+ png_set_strip_16(png_ptr);
+#endif
+
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+ /* Strip alpha bytes from the input data without combining with
+ * the background (not recommended).
+ */
+ if (transforms & PNG_TRANSFORM_STRIP_ALPHA)
+ png_set_strip_alpha(png_ptr);
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED) && !defined(PNG_READ_EXPAND_SUPPORTED)
+ /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single
+ * byte into separate bytes (useful for paletted and grayscale images).
+ */
+ if (transforms & PNG_TRANSFORM_PACKING)
+ png_set_packing(png_ptr);
+#endif
+
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+ /* Change the order of packed pixels to least significant bit first
+ * (not useful if you are using png_set_packing).
+ */
+ if (transforms & PNG_TRANSFORM_PACKSWAP)
+ png_set_packswap(png_ptr);
+#endif
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+ /* Expand paletted colors into true RGB triplets
+ * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel
+ * Expand paletted or RGB images with transparency to full alpha
+ * channels so the data will be available as RGBA quartets.
+ */
+ if (transforms & PNG_TRANSFORM_EXPAND)
+ if ((png_ptr->bit_depth < 8) ||
+ (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ||
+ (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)))
+ png_set_expand(png_ptr);
+#endif
+
+ /* We don't handle background color or gamma transformation or quantizing.
+ */
+
+#ifdef PNG_READ_INVERT_SUPPORTED
+ /* Invert monochrome files to have 0 as white and 1 as black
+ */
+ if (transforms & PNG_TRANSFORM_INVERT_MONO)
+ png_set_invert_mono(png_ptr);
+#endif
+
+#ifdef PNG_READ_SHIFT_SUPPORTED
+ /* If you want to shift the pixel values from the range [0,255] or
+ * [0,65535] to the original [0,7] or [0,31], or whatever range the
+ * colors were originally in:
+ */
+ if ((transforms & PNG_TRANSFORM_SHIFT)
+ && png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT))
+ {
+ png_color_8p sig_bit;
+
+ png_get_sBIT(png_ptr, info_ptr, &sig_bit);
+ png_set_shift(png_ptr, sig_bit);
+ }
+#endif
+
+#ifdef PNG_READ_BGR_SUPPORTED
+ /* Flip the RGB pixels to BGR (or RGBA to BGRA) */
+ if (transforms & PNG_TRANSFORM_BGR)
+ png_set_bgr(png_ptr);
+#endif
+
+#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
+ /* Swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */
+ if (transforms & PNG_TRANSFORM_SWAP_ALPHA)
+ png_set_swap_alpha(png_ptr);
+#endif
+
+#ifdef PNG_READ_SWAP_SUPPORTED
+ /* Swap bytes of 16 bit files to least significant byte first */
+ if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)
+ png_set_swap(png_ptr);
+#endif
+
+/* Added at libpng-1.2.41 */
+#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
+ /* Invert the alpha channel from opacity to transparency */
+ if (transforms & PNG_TRANSFORM_INVERT_ALPHA)
+ png_set_invert_alpha(png_ptr);
+#endif
+
+/* Added at libpng-1.2.41 */
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+ /* Expand grayscale image to RGB */
+ if (transforms & PNG_TRANSFORM_GRAY_TO_RGB)
+ png_set_gray_to_rgb(png_ptr);
+#endif
+
+ /* We don't handle adding filler bytes */
+
+ /* We use png_read_image and rely on that for interlace handling, but we also
+ * call png_read_update_info therefore must turn on interlace handling now:
+ */
+ (void)png_set_interlace_handling(png_ptr);
+
+ /* Optional call to gamma correct and add the background to the palette
+ * and update info structure. REQUIRED if you are expecting libpng to
+ * update the palette for you (i.e., you selected such a transform above).
+ */
+ png_read_update_info(png_ptr, info_ptr);
+
+ /* -------------- image transformations end here ------------------- */
+
+ png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0);
+ if (info_ptr->row_pointers == NULL)
+ {
+ png_uint_32 iptr;
+
+ info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr,
+ info_ptr->height * png_sizeof(png_bytep));
+ for (iptr=0; iptr<info_ptr->height; iptr++)
+ info_ptr->row_pointers[iptr] = NULL;
+
+ info_ptr->free_me |= PNG_FREE_ROWS;
+
+ for (row = 0; row < (int)info_ptr->height; row++)
+ info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr,
+ png_get_rowbytes(png_ptr, info_ptr));
+ }
+
+ png_read_image(png_ptr, info_ptr->row_pointers);
+ info_ptr->valid |= PNG_INFO_IDAT;
+
+ /* Read rest of file, and get additional chunks in info_ptr - REQUIRED */
+ png_read_end(png_ptr, info_ptr);
+
+ PNG_UNUSED(transforms) /* Quiet compiler warnings */
+ PNG_UNUSED(params)
+
+}
+#endif /* PNG_INFO_IMAGE_SUPPORTED */
+#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
+#endif /* PNG_READ_SUPPORTED */
diff --git a/libpng/pngrio.c b/libpng/pngrio.c
new file mode 100644
index 0000000..e9c381c
--- /dev/null
+++ b/libpng/pngrio.c
@@ -0,0 +1,176 @@
+
+/* pngrio.c - functions for data input
+ *
+ * Last changed in libpng 1.5.0 [January 6, 2011]
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * This file provides a location for all input. Users who need
+ * special handling are expected to write a function that has the same
+ * arguments as this and performs a similar function, but that possibly
+ * has a different input method. Note that you shouldn't change this
+ * function, but rather write a replacement function and then make
+ * libpng use it at run time with png_set_read_fn(...).
+ */
+
+#include "pngpriv.h"
+
+#ifdef PNG_READ_SUPPORTED
+
+/* Read the data from whatever input you are using. The default routine
+ * reads from a file pointer. Note that this routine sometimes gets called
+ * with very small lengths, so you should implement some kind of simple
+ * buffering if you are using unbuffered reads. This should never be asked
+ * to read more then 64K on a 16 bit machine.
+ */
+void /* PRIVATE */
+png_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ png_debug1(4, "reading %d bytes", (int)length);
+
+ if (png_ptr->read_data_fn != NULL)
+ (*(png_ptr->read_data_fn))(png_ptr, data, length);
+
+ else
+ png_error(png_ptr, "Call to NULL read function");
+}
+
+#ifdef PNG_STDIO_SUPPORTED
+/* This is the function that does the actual reading of data. If you are
+ * not reading from a standard C stream, you should create a replacement
+ * read_data function and use it at run time with png_set_read_fn(), rather
+ * than changing the library.
+ */
+# ifndef USE_FAR_KEYWORD
+void PNGCBAPI
+png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ png_size_t check;
+
+ if (png_ptr == NULL)
+ return;
+
+ /* fread() returns 0 on error, so it is OK to store this in a png_size_t
+ * instead of an int, which is what fread() actually returns.
+ */
+ check = fread(data, 1, length, (png_FILE_p)png_ptr->io_ptr);
+
+ if (check != length)
+ png_error(png_ptr, "Read Error");
+}
+# else
+/* This is the model-independent version. Since the standard I/O library
+ can't handle far buffers in the medium and small models, we have to copy
+ the data.
+*/
+
+#define NEAR_BUF_SIZE 1024
+#define MIN(a,b) (a <= b ? a : b)
+
+static void PNGCBAPI
+png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ png_size_t check;
+ png_byte *n_data;
+ png_FILE_p io_ptr;
+
+ if (png_ptr == NULL)
+ return;
+
+ /* Check if data really is near. If so, use usual code. */
+ n_data = (png_byte *)CVT_PTR_NOCHECK(data);
+ io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr);
+
+ if ((png_bytep)n_data == data)
+ {
+ check = fread(n_data, 1, length, io_ptr);
+ }
+
+ else
+ {
+ png_byte buf[NEAR_BUF_SIZE];
+ png_size_t read, remaining, err;
+ check = 0;
+ remaining = length;
+
+ do
+ {
+ read = MIN(NEAR_BUF_SIZE, remaining);
+ err = fread(buf, 1, read, io_ptr);
+ png_memcpy(data, buf, read); /* copy far buffer to near buffer */
+
+ if (err != read)
+ break;
+
+ else
+ check += err;
+
+ data += read;
+ remaining -= read;
+ }
+ while (remaining != 0);
+ }
+
+ if ((png_uint_32)check != (png_uint_32)length)
+ png_error(png_ptr, "read Error");
+}
+# endif
+#endif
+
+/* This function allows the application to supply a new input function
+ * for libpng if standard C streams aren't being used.
+ *
+ * This function takes as its arguments:
+ *
+ * png_ptr - pointer to a png input data structure
+ *
+ * io_ptr - pointer to user supplied structure containing info about
+ * the input functions. May be NULL.
+ *
+ * read_data_fn - pointer to a new input function that takes as its
+ * arguments a pointer to a png_struct, a pointer to
+ * a location where input data can be stored, and a 32-bit
+ * unsigned int that is the number of bytes to be read.
+ * To exit and output any fatal error messages the new write
+ * function should call png_error(png_ptr, "Error msg").
+ * May be NULL, in which case libpng's default function will
+ * be used.
+ */
+void PNGAPI
+png_set_read_fn(png_structp png_ptr, png_voidp io_ptr,
+ png_rw_ptr read_data_fn)
+{
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->io_ptr = io_ptr;
+
+#ifdef PNG_STDIO_SUPPORTED
+ if (read_data_fn != NULL)
+ png_ptr->read_data_fn = read_data_fn;
+
+ else
+ png_ptr->read_data_fn = png_default_read_data;
+#else
+ png_ptr->read_data_fn = read_data_fn;
+#endif
+
+ /* It is an error to write to a read device */
+ if (png_ptr->write_data_fn != NULL)
+ {
+ png_ptr->write_data_fn = NULL;
+ png_warning(png_ptr,
+ "Can't set both read_data_fn and write_data_fn in the"
+ " same structure");
+ }
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+ png_ptr->output_flush_fn = NULL;
+#endif
+}
+#endif /* PNG_READ_SUPPORTED */
diff --git a/libpng/pngrtran.c b/libpng/pngrtran.c
new file mode 100644
index 0000000..de5871a
--- /dev/null
+++ b/libpng/pngrtran.c
@@ -0,0 +1,4303 @@
+
+/* pngrtran.c - transforms the data in a row for PNG readers
+ *
+ * Last changed in libpng 1.5.2 [March 31, 2011]
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * This file contains functions optionally called by an application
+ * in order to tell libpng how to handle data when reading a PNG.
+ * Transformations that are used in both reading and writing are
+ * in pngtrans.c.
+ */
+
+#include "pngpriv.h"
+
+#ifdef PNG_READ_SUPPORTED
+
+/* Set the action on getting a CRC error for an ancillary or critical chunk. */
+void PNGAPI
+png_set_crc_action(png_structp png_ptr, int crit_action, int ancil_action)
+{
+ png_debug(1, "in png_set_crc_action");
+
+ if (png_ptr == NULL)
+ return;
+
+ /* Tell libpng how we react to CRC errors in critical chunks */
+ switch (crit_action)
+ {
+ case PNG_CRC_NO_CHANGE: /* Leave setting as is */
+ break;
+
+ case PNG_CRC_WARN_USE: /* Warn/use data */
+ png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
+ png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE;
+ break;
+
+ case PNG_CRC_QUIET_USE: /* Quiet/use data */
+ png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
+ png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE |
+ PNG_FLAG_CRC_CRITICAL_IGNORE;
+ break;
+
+ case PNG_CRC_WARN_DISCARD: /* Not a valid action for critical data */
+ png_warning(png_ptr,
+ "Can't discard critical data on CRC error");
+ case PNG_CRC_ERROR_QUIT: /* Error/quit */
+
+ case PNG_CRC_DEFAULT:
+ default:
+ png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
+ break;
+ }
+
+ /* Tell libpng how we react to CRC errors in ancillary chunks */
+ switch (ancil_action)
+ {
+ case PNG_CRC_NO_CHANGE: /* Leave setting as is */
+ break;
+
+ case PNG_CRC_WARN_USE: /* Warn/use data */
+ png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+ png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE;
+ break;
+
+ case PNG_CRC_QUIET_USE: /* Quiet/use data */
+ png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+ png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE |
+ PNG_FLAG_CRC_ANCILLARY_NOWARN;
+ break;
+
+ case PNG_CRC_ERROR_QUIT: /* Error/quit */
+ png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+ png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN;
+ break;
+
+ case PNG_CRC_WARN_DISCARD: /* Warn/discard data */
+
+ case PNG_CRC_DEFAULT:
+ default:
+ png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+ break;
+ }
+}
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+/* Handle alpha and tRNS via a background color */
+void PNGFAPI
+png_set_background_fixed(png_structp png_ptr,
+ png_const_color_16p background_color, int background_gamma_code,
+ int need_expand, png_fixed_point background_gamma)
+{
+ png_debug(1, "in png_set_background_fixed");
+
+ if (png_ptr == NULL)
+ return;
+
+ if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN)
+ {
+ png_warning(png_ptr, "Application must supply a known background gamma");
+ return;
+ }
+
+ png_ptr->transformations |= PNG_BACKGROUND;
+ png_memcpy(&(png_ptr->background), background_color,
+ png_sizeof(png_color_16));
+ png_ptr->background_gamma = background_gamma;
+ png_ptr->background_gamma_type = (png_byte)(background_gamma_code);
+ png_ptr->transformations |= (need_expand ? PNG_BACKGROUND_EXPAND : 0);
+}
+
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_background(png_structp png_ptr,
+ png_const_color_16p background_color, int background_gamma_code,
+ int need_expand, double background_gamma)
+{
+ png_set_background_fixed(png_ptr, background_color, background_gamma_code,
+ need_expand, png_fixed(png_ptr, background_gamma, "png_set_background"));
+}
+# endif /* FLOATING_POINT */
+#endif /* READ_BACKGROUND */
+
+#ifdef PNG_READ_16_TO_8_SUPPORTED
+/* Strip 16 bit depth files to 8 bit depth */
+void PNGAPI
+png_set_strip_16(png_structp png_ptr)
+{
+ png_debug(1, "in png_set_strip_16");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->transformations |= PNG_16_TO_8;
+ png_ptr->transformations &= ~PNG_EXPAND_16;
+}
+#endif
+
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+void PNGAPI
+png_set_strip_alpha(png_structp png_ptr)
+{
+ png_debug(1, "in png_set_strip_alpha");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->transformations |= PNG_STRIP_ALPHA;
+}
+#endif
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+/* Dither file to 8 bit. Supply a palette, the current number
+ * of elements in the palette, the maximum number of elements
+ * allowed, and a histogram if possible. If the current number
+ * of colors is greater then the maximum number, the palette will be
+ * modified to fit in the maximum number. "full_quantize" indicates
+ * whether we need a quantizing cube set up for RGB images, or if we
+ * simply are reducing the number of colors in a paletted image.
+ */
+
+typedef struct png_dsort_struct
+{
+ struct png_dsort_struct FAR * next;
+ png_byte left;
+ png_byte right;
+} png_dsort;
+typedef png_dsort FAR * png_dsortp;
+typedef png_dsort FAR * FAR * png_dsortpp;
+
+void PNGAPI
+png_set_quantize(png_structp png_ptr, png_colorp palette,
+ int num_palette, int maximum_colors, png_const_uint_16p histogram,
+ int full_quantize)
+{
+ png_debug(1, "in png_set_quantize");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->transformations |= PNG_QUANTIZE;
+
+ if (!full_quantize)
+ {
+ int i;
+
+ png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr,
+ (png_uint_32)(num_palette * png_sizeof(png_byte)));
+ for (i = 0; i < num_palette; i++)
+ png_ptr->quantize_index[i] = (png_byte)i;
+ }
+
+ if (num_palette > maximum_colors)
+ {
+ if (histogram != NULL)
+ {
+ /* This is easy enough, just throw out the least used colors.
+ * Perhaps not the best solution, but good enough.
+ */
+
+ int i;
+
+ /* Initialize an array to sort colors */
+ png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr,
+ (png_uint_32)(num_palette * png_sizeof(png_byte)));
+
+ /* Initialize the quantize_sort array */
+ for (i = 0; i < num_palette; i++)
+ png_ptr->quantize_sort[i] = (png_byte)i;
+
+ /* Find the least used palette entries by starting a
+ * bubble sort, and running it until we have sorted
+ * out enough colors. Note that we don't care about
+ * sorting all the colors, just finding which are
+ * least used.
+ */
+
+ for (i = num_palette - 1; i >= maximum_colors; i--)
+ {
+ int done; /* To stop early if the list is pre-sorted */
+ int j;
+
+ done = 1;
+ for (j = 0; j < i; j++)
+ {
+ if (histogram[png_ptr->quantize_sort[j]]
+ < histogram[png_ptr->quantize_sort[j + 1]])
+ {
+ png_byte t;
+
+ t = png_ptr->quantize_sort[j];
+ png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1];
+ png_ptr->quantize_sort[j + 1] = t;
+ done = 0;
+ }
+ }
+
+ if (done)
+ break;
+ }
+
+ /* Swap the palette around, and set up a table, if necessary */
+ if (full_quantize)
+ {
+ int j = num_palette;
+
+ /* Put all the useful colors within the max, but don't
+ * move the others.
+ */
+ for (i = 0; i < maximum_colors; i++)
+ {
+ if ((int)png_ptr->quantize_sort[i] >= maximum_colors)
+ {
+ do
+ j--;
+ while ((int)png_ptr->quantize_sort[j] >= maximum_colors);
+
+ palette[i] = palette[j];
+ }
+ }
+ }
+ else
+ {
+ int j = num_palette;
+
+ /* Move all the used colors inside the max limit, and
+ * develop a translation table.
+ */
+ for (i = 0; i < maximum_colors; i++)
+ {
+ /* Only move the colors we need to */
+ if ((int)png_ptr->quantize_sort[i] >= maximum_colors)
+ {
+ png_color tmp_color;
+
+ do
+ j--;
+ while ((int)png_ptr->quantize_sort[j] >= maximum_colors);
+
+ tmp_color = palette[j];
+ palette[j] = palette[i];
+ palette[i] = tmp_color;
+ /* Indicate where the color went */
+ png_ptr->quantize_index[j] = (png_byte)i;
+ png_ptr->quantize_index[i] = (png_byte)j;
+ }
+ }
+
+ /* Find closest color for those colors we are not using */
+ for (i = 0; i < num_palette; i++)
+ {
+ if ((int)png_ptr->quantize_index[i] >= maximum_colors)
+ {
+ int min_d, k, min_k, d_index;
+
+ /* Find the closest color to one we threw out */
+ d_index = png_ptr->quantize_index[i];
+ min_d = PNG_COLOR_DIST(palette[d_index], palette[0]);
+ for (k = 1, min_k = 0; k < maximum_colors; k++)
+ {
+ int d;
+
+ d = PNG_COLOR_DIST(palette[d_index], palette[k]);
+
+ if (d < min_d)
+ {
+ min_d = d;
+ min_k = k;
+ }
+ }
+ /* Point to closest color */
+ png_ptr->quantize_index[i] = (png_byte)min_k;
+ }
+ }
+ }
+ png_free(png_ptr, png_ptr->quantize_sort);
+ png_ptr->quantize_sort = NULL;
+ }
+ else
+ {
+ /* This is much harder to do simply (and quickly). Perhaps
+ * we need to go through a median cut routine, but those
+ * don't always behave themselves with only a few colors
+ * as input. So we will just find the closest two colors,
+ * and throw out one of them (chosen somewhat randomly).
+ * [We don't understand this at all, so if someone wants to
+ * work on improving it, be our guest - AED, GRP]
+ */
+ int i;
+ int max_d;
+ int num_new_palette;
+ png_dsortp t;
+ png_dsortpp hash;
+
+ t = NULL;
+
+ /* Initialize palette index arrays */
+ png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr,
+ (png_uint_32)(num_palette * png_sizeof(png_byte)));
+ png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr,
+ (png_uint_32)(num_palette * png_sizeof(png_byte)));
+
+ /* Initialize the sort array */
+ for (i = 0; i < num_palette; i++)
+ {
+ png_ptr->index_to_palette[i] = (png_byte)i;
+ png_ptr->palette_to_index[i] = (png_byte)i;
+ }
+
+ hash = (png_dsortpp)png_calloc(png_ptr, (png_uint_32)(769 *
+ png_sizeof(png_dsortp)));
+
+ num_new_palette = num_palette;
+
+ /* Initial wild guess at how far apart the farthest pixel
+ * pair we will be eliminating will be. Larger
+ * numbers mean more areas will be allocated, Smaller
+ * numbers run the risk of not saving enough data, and
+ * having to do this all over again.
+ *
+ * I have not done extensive checking on this number.
+ */
+ max_d = 96;
+
+ while (num_new_palette > maximum_colors)
+ {
+ for (i = 0; i < num_new_palette - 1; i++)
+ {
+ int j;
+
+ for (j = i + 1; j < num_new_palette; j++)
+ {
+ int d;
+
+ d = PNG_COLOR_DIST(palette[i], palette[j]);
+
+ if (d <= max_d)
+ {
+
+ t = (png_dsortp)png_malloc_warn(png_ptr,
+ (png_uint_32)(png_sizeof(png_dsort)));
+
+ if (t == NULL)
+ break;
+
+ t->next = hash[d];
+ t->left = (png_byte)i;
+ t->right = (png_byte)j;
+ hash[d] = t;
+ }
+ }
+ if (t == NULL)
+ break;
+ }
+
+ if (t != NULL)
+ for (i = 0; i <= max_d; i++)
+ {
+ if (hash[i] != NULL)
+ {
+ png_dsortp p;
+
+ for (p = hash[i]; p; p = p->next)
+ {
+ if ((int)png_ptr->index_to_palette[p->left]
+ < num_new_palette &&
+ (int)png_ptr->index_to_palette[p->right]
+ < num_new_palette)
+ {
+ int j, next_j;
+
+ if (num_new_palette & 0x01)
+ {
+ j = p->left;
+ next_j = p->right;
+ }
+ else
+ {
+ j = p->right;
+ next_j = p->left;
+ }
+
+ num_new_palette--;
+ palette[png_ptr->index_to_palette[j]]
+ = palette[num_new_palette];
+ if (!full_quantize)
+ {
+ int k;
+
+ for (k = 0; k < num_palette; k++)
+ {
+ if (png_ptr->quantize_index[k] ==
+ png_ptr->index_to_palette[j])
+ png_ptr->quantize_index[k] =
+ png_ptr->index_to_palette[next_j];
+
+ if ((int)png_ptr->quantize_index[k] ==
+ num_new_palette)
+ png_ptr->quantize_index[k] =
+ png_ptr->index_to_palette[j];
+ }
+ }
+
+ png_ptr->index_to_palette[png_ptr->palette_to_index
+ [num_new_palette]] = png_ptr->index_to_palette[j];
+
+ png_ptr->palette_to_index[png_ptr->index_to_palette[j]]
+ = png_ptr->palette_to_index[num_new_palette];
+
+ png_ptr->index_to_palette[j] =
+ (png_byte)num_new_palette;
+
+ png_ptr->palette_to_index[num_new_palette] =
+ (png_byte)j;
+ }
+ if (num_new_palette <= maximum_colors)
+ break;
+ }
+ if (num_new_palette <= maximum_colors)
+ break;
+ }
+ }
+
+ for (i = 0; i < 769; i++)
+ {
+ if (hash[i] != NULL)
+ {
+ png_dsortp p = hash[i];
+ while (p)
+ {
+ t = p->next;
+ png_free(png_ptr, p);
+ p = t;
+ }
+ }
+ hash[i] = 0;
+ }
+ max_d += 96;
+ }
+ png_free(png_ptr, hash);
+ png_free(png_ptr, png_ptr->palette_to_index);
+ png_free(png_ptr, png_ptr->index_to_palette);
+ png_ptr->palette_to_index = NULL;
+ png_ptr->index_to_palette = NULL;
+ }
+ num_palette = maximum_colors;
+ }
+ if (png_ptr->palette == NULL)
+ {
+ png_ptr->palette = palette;
+ }
+ png_ptr->num_palette = (png_uint_16)num_palette;
+
+ if (full_quantize)
+ {
+ int i;
+ png_bytep distance;
+ int total_bits = PNG_QUANTIZE_RED_BITS + PNG_QUANTIZE_GREEN_BITS +
+ PNG_QUANTIZE_BLUE_BITS;
+ int num_red = (1 << PNG_QUANTIZE_RED_BITS);
+ int num_green = (1 << PNG_QUANTIZE_GREEN_BITS);
+ int num_blue = (1 << PNG_QUANTIZE_BLUE_BITS);
+ png_size_t num_entries = ((png_size_t)1 << total_bits);
+
+ png_ptr->palette_lookup = (png_bytep)png_calloc(png_ptr,
+ (png_uint_32)(num_entries * png_sizeof(png_byte)));
+
+ distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries *
+ png_sizeof(png_byte)));
+
+ png_memset(distance, 0xff, num_entries * png_sizeof(png_byte));
+
+ for (i = 0; i < num_palette; i++)
+ {
+ int ir, ig, ib;
+ int r = (palette[i].red >> (8 - PNG_QUANTIZE_RED_BITS));
+ int g = (palette[i].green >> (8 - PNG_QUANTIZE_GREEN_BITS));
+ int b = (palette[i].blue >> (8 - PNG_QUANTIZE_BLUE_BITS));
+
+ for (ir = 0; ir < num_red; ir++)
+ {
+ /* int dr = abs(ir - r); */
+ int dr = ((ir > r) ? ir - r : r - ir);
+ int index_r = (ir << (PNG_QUANTIZE_BLUE_BITS +
+ PNG_QUANTIZE_GREEN_BITS));
+
+ for (ig = 0; ig < num_green; ig++)
+ {
+ /* int dg = abs(ig - g); */
+ int dg = ((ig > g) ? ig - g : g - ig);
+ int dt = dr + dg;
+ int dm = ((dr > dg) ? dr : dg);
+ int index_g = index_r | (ig << PNG_QUANTIZE_BLUE_BITS);
+
+ for (ib = 0; ib < num_blue; ib++)
+ {
+ int d_index = index_g | ib;
+ /* int db = abs(ib - b); */
+ int db = ((ib > b) ? ib - b : b - ib);
+ int dmax = ((dm > db) ? dm : db);
+ int d = dmax + dt + db;
+
+ if (d < (int)distance[d_index])
+ {
+ distance[d_index] = (png_byte)d;
+ png_ptr->palette_lookup[d_index] = (png_byte)i;
+ }
+ }
+ }
+ }
+ }
+
+ png_free(png_ptr, distance);
+ }
+}
+#endif /* PNG_READ_QUANTIZE_SUPPORTED */
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+/* Transform the image from the file_gamma to the screen_gamma. We
+ * only do transformations on images where the file_gamma and screen_gamma
+ * are not close reciprocals, otherwise it slows things down slightly, and
+ * also needlessly introduces small errors.
+ *
+ * We will turn off gamma transformation later if no semitransparent entries
+ * are present in the tRNS array for palette images. We can't do it here
+ * because we don't necessarily have the tRNS chunk yet.
+ */
+static int /* PRIVATE */
+png_gamma_threshold(png_fixed_point scrn_gamma, png_fixed_point file_gamma)
+{
+ /* PNG_GAMMA_THRESHOLD is the threshold for performing gamma
+ * correction as a difference of the overall transform from 1.0
+ *
+ * We want to compare the threshold with s*f - 1, if we get
+ * overflow here it is because of wacky gamma values so we
+ * turn on processing anyway.
+ */
+ png_fixed_point gtest;
+ return !png_muldiv(&gtest, scrn_gamma, file_gamma, PNG_FP_1) ||
+ png_gamma_significant(gtest);
+}
+
+void PNGFAPI
+png_set_gamma_fixed(png_structp png_ptr, png_fixed_point scrn_gamma,
+ png_fixed_point file_gamma)
+{
+ png_debug(1, "in png_set_gamma_fixed");
+
+ if (png_ptr == NULL)
+ return;
+
+ if ((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) ||
+ (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ||
+ png_gamma_threshold(scrn_gamma, file_gamma))
+ png_ptr->transformations |= PNG_GAMMA;
+ png_ptr->gamma = file_gamma;
+ png_ptr->screen_gamma = scrn_gamma;
+}
+
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_gamma(png_structp png_ptr, double scrn_gamma, double file_gamma)
+{
+ png_set_gamma_fixed(png_ptr,
+ png_fixed(png_ptr, scrn_gamma, "png_set_gamma screen gamma"),
+ png_fixed(png_ptr, file_gamma, "png_set_gamma file gamma"));
+}
+# endif /* FLOATING_POINT_SUPPORTED */
+#endif /* READ_GAMMA */
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+/* Expand paletted images to RGB, expand grayscale images of
+ * less than 8-bit depth to 8-bit depth, and expand tRNS chunks
+ * to alpha channels.
+ */
+void PNGAPI
+png_set_expand(png_structp png_ptr)
+{
+ png_debug(1, "in png_set_expand");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
+ png_ptr->flags &= ~PNG_FLAG_ROW_INIT;
+}
+
+/* GRR 19990627: the following three functions currently are identical
+ * to png_set_expand(). However, it is entirely reasonable that someone
+ * might wish to expand an indexed image to RGB but *not* expand a single,
+ * fully transparent palette entry to a full alpha channel--perhaps instead
+ * convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace
+ * the transparent color with a particular RGB value, or drop tRNS entirely.
+ * IOW, a future version of the library may make the transformations flag
+ * a bit more fine-grained, with separate bits for each of these three
+ * functions.
+ *
+ * More to the point, these functions make it obvious what libpng will be
+ * doing, whereas "expand" can (and does) mean any number of things.
+ *
+ * GRP 20060307: In libpng-1.2.9, png_set_gray_1_2_4_to_8() was modified
+ * to expand only the sample depth but not to expand the tRNS to alpha
+ * and its name was changed to png_set_expand_gray_1_2_4_to_8().
+ */
+
+/* Expand paletted images to RGB. */
+void PNGAPI
+png_set_palette_to_rgb(png_structp png_ptr)
+{
+ png_debug(1, "in png_set_palette_to_rgb");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
+ png_ptr->flags &= ~PNG_FLAG_ROW_INIT;
+}
+
+/* Expand grayscale images of less than 8-bit depth to 8 bits. */
+void PNGAPI
+png_set_expand_gray_1_2_4_to_8(png_structp png_ptr)
+{
+ png_debug(1, "in png_set_expand_gray_1_2_4_to_8");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->transformations |= PNG_EXPAND;
+ png_ptr->flags &= ~PNG_FLAG_ROW_INIT;
+}
+
+
+
+/* Expand tRNS chunks to alpha channels. */
+void PNGAPI
+png_set_tRNS_to_alpha(png_structp png_ptr)
+{
+ png_debug(1, "in png_set_tRNS_to_alpha");
+
+ png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
+ png_ptr->flags &= ~PNG_FLAG_ROW_INIT;
+}
+#endif /* defined(PNG_READ_EXPAND_SUPPORTED) */
+
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+/* Expand to 16 bit channels, expand the tRNS chunk too (because otherwise
+ * it may not work correctly.)
+ */
+void PNGAPI
+png_set_expand_16(png_structp png_ptr)
+{
+ png_debug(1, "in png_set_expand_16");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->transformations |= (PNG_EXPAND_16 | PNG_EXPAND | PNG_EXPAND_tRNS);
+ png_ptr->transformations &= ~PNG_16_TO_8;
+
+ png_ptr->flags &= ~PNG_FLAG_ROW_INIT;
+}
+#endif
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+void PNGAPI
+png_set_gray_to_rgb(png_structp png_ptr)
+{
+ png_debug(1, "in png_set_gray_to_rgb");
+
+ if (png_ptr != NULL)
+ {
+ /* Because rgb must be 8 bits or more: */
+ png_set_expand_gray_1_2_4_to_8(png_ptr);
+ png_ptr->transformations |= PNG_GRAY_TO_RGB;
+ png_ptr->flags &= ~PNG_FLAG_ROW_INIT;
+ }
+}
+#endif
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+void PNGFAPI
+png_set_rgb_to_gray_fixed(png_structp png_ptr, int error_action,
+ png_fixed_point red, png_fixed_point green)
+{
+ png_debug(1, "in png_set_rgb_to_gray");
+
+ if (png_ptr == NULL)
+ return;
+
+ switch(error_action)
+ {
+ case 1:
+ png_ptr->transformations |= PNG_RGB_TO_GRAY;
+ break;
+
+ case 2:
+ png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN;
+ break;
+
+ case 3:
+ png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR;
+ break;
+
+ default:
+ png_error(png_ptr, "invalid error action to rgb_to_gray");
+ break;
+ }
+ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+#ifdef PNG_READ_EXPAND_SUPPORTED
+ png_ptr->transformations |= PNG_EXPAND;
+#else
+ {
+ png_warning(png_ptr,
+ "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED");
+
+ png_ptr->transformations &= ~PNG_RGB_TO_GRAY;
+ }
+#endif
+ {
+ png_uint_16 red_int, green_int;
+ if (red < 0 || green < 0)
+ {
+ red_int = 6968; /* .212671 * 32768 + .5 */
+ green_int = 23434; /* .715160 * 32768 + .5 */
+ }
+
+ else if (red + green < 100000L)
+ {
+ red_int = (png_uint_16)(((png_uint_32)red*32768L)/100000L);
+ green_int = (png_uint_16)(((png_uint_32)green*32768L)/100000L);
+ }
+
+ else
+ {
+ png_warning(png_ptr, "ignoring out of range rgb_to_gray coefficients");
+ red_int = 6968;
+ green_int = 23434;
+ }
+
+ png_ptr->rgb_to_gray_red_coeff = red_int;
+ png_ptr->rgb_to_gray_green_coeff = green_int;
+ png_ptr->rgb_to_gray_blue_coeff =
+ (png_uint_16)(32768 - red_int - green_int);
+ }
+}
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+/* Convert a RGB image to a grayscale of the same width. This allows us,
+ * for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image.
+ */
+
+void PNGAPI
+png_set_rgb_to_gray(png_structp png_ptr, int error_action, double red,
+ double green)
+{
+ if (png_ptr == NULL)
+ return;
+
+ png_set_rgb_to_gray_fixed(png_ptr, error_action,
+ png_fixed(png_ptr, red, "rgb to gray red coefficient"),
+ png_fixed(png_ptr, green, "rgb to gray green coefficient"));
+}
+#endif /* FLOATING POINT */
+
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+ defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+void PNGAPI
+png_set_read_user_transform_fn(png_structp png_ptr, png_user_transform_ptr
+ read_user_transform_fn)
+{
+ png_debug(1, "in png_set_read_user_transform_fn");
+
+ if (png_ptr == NULL)
+ return;
+
+#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
+ png_ptr->transformations |= PNG_USER_TRANSFORM;
+ png_ptr->read_user_transform_fn = read_user_transform_fn;
+#endif
+}
+#endif
+
+/* Initialize everything needed for the read. This includes modifying
+ * the palette.
+ */
+void /* PRIVATE */
+png_init_read_transformations(png_structp png_ptr)
+{
+ png_debug(1, "in png_init_read_transformations");
+
+ {
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+ defined(PNG_READ_SHIFT_SUPPORTED) || \
+ defined(PNG_READ_GAMMA_SUPPORTED)
+ int color_type = png_ptr->color_type;
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED)
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+ /* Detect gray background and attempt to enable optimization
+ * for gray --> RGB case
+ *
+ * Note: if PNG_BACKGROUND_EXPAND is set and color_type is either RGB or
+ * RGB_ALPHA (in which case need_expand is superfluous anyway), the
+ * background color might actually be gray yet not be flagged as such.
+ * This is not a problem for the current code, which uses
+ * PNG_BACKGROUND_IS_GRAY only to decide when to do the
+ * png_do_gray_to_rgb() transformation.
+ */
+ if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) &&
+ !(color_type & PNG_COLOR_MASK_COLOR))
+ {
+ png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;
+ }
+
+ else if ((png_ptr->transformations & PNG_BACKGROUND) &&
+ !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) &&
+ (png_ptr->transformations & PNG_GRAY_TO_RGB) &&
+ png_ptr->background.red == png_ptr->background.green &&
+ png_ptr->background.red == png_ptr->background.blue)
+ {
+ png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;
+ png_ptr->background.gray = png_ptr->background.red;
+ }
+#endif
+
+ if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) &&
+ (png_ptr->transformations & PNG_EXPAND))
+ {
+ if (!(color_type & PNG_COLOR_MASK_COLOR)) /* i.e., GRAY or GRAY_ALPHA */
+ {
+ /* Expand background and tRNS chunks */
+ switch (png_ptr->bit_depth)
+ {
+ case 1:
+ png_ptr->background.gray *= (png_uint_16)0xff;
+ png_ptr->background.red = png_ptr->background.green
+ = png_ptr->background.blue = png_ptr->background.gray;
+ if (!(png_ptr->transformations & PNG_EXPAND_tRNS))
+ {
+ png_ptr->trans_color.gray *= (png_uint_16)0xff;
+ png_ptr->trans_color.red = png_ptr->trans_color.green
+ = png_ptr->trans_color.blue = png_ptr->trans_color.gray;
+ }
+ break;
+
+ case 2:
+ png_ptr->background.gray *= (png_uint_16)0x55;
+ png_ptr->background.red = png_ptr->background.green
+ = png_ptr->background.blue = png_ptr->background.gray;
+ if (!(png_ptr->transformations & PNG_EXPAND_tRNS))
+ {
+ png_ptr->trans_color.gray *= (png_uint_16)0x55;
+ png_ptr->trans_color.red = png_ptr->trans_color.green
+ = png_ptr->trans_color.blue = png_ptr->trans_color.gray;
+ }
+ break;
+
+ case 4:
+ png_ptr->background.gray *= (png_uint_16)0x11;
+ png_ptr->background.red = png_ptr->background.green
+ = png_ptr->background.blue = png_ptr->background.gray;
+ if (!(png_ptr->transformations & PNG_EXPAND_tRNS))
+ {
+ png_ptr->trans_color.gray *= (png_uint_16)0x11;
+ png_ptr->trans_color.red = png_ptr->trans_color.green
+ = png_ptr->trans_color.blue = png_ptr->trans_color.gray;
+ }
+ break;
+
+ default:
+
+ case 8:
+
+ case 16:
+ png_ptr->background.red = png_ptr->background.green
+ = png_ptr->background.blue = png_ptr->background.gray;
+ break;
+ }
+ }
+ else if (color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ png_ptr->background.red =
+ png_ptr->palette[png_ptr->background.index].red;
+ png_ptr->background.green =
+ png_ptr->palette[png_ptr->background.index].green;
+ png_ptr->background.blue =
+ png_ptr->palette[png_ptr->background.index].blue;
+
+#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
+ if (png_ptr->transformations & PNG_INVERT_ALPHA)
+ {
+#ifdef PNG_READ_EXPAND_SUPPORTED
+ if (!(png_ptr->transformations & PNG_EXPAND_tRNS))
+#endif
+ {
+ /* Invert the alpha channel (in tRNS) unless the pixels are
+ * going to be expanded, in which case leave it for later
+ */
+ int i, istop;
+ istop=(int)png_ptr->num_trans;
+ for (i=0; i<istop; i++)
+ png_ptr->trans_alpha[i] = (png_byte)(255 -
+ png_ptr->trans_alpha[i]);
+ }
+ }
+#endif
+
+ }
+ }
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED)
+ png_ptr->background_1 = png_ptr->background;
+#endif
+#ifdef PNG_READ_GAMMA_SUPPORTED
+
+ if ((color_type == PNG_COLOR_TYPE_PALETTE && png_ptr->num_trans != 0)
+ && png_gamma_threshold(png_ptr->screen_gamma, png_ptr->gamma))
+ {
+ int i, k;
+ k=0;
+ for (i=0; i<png_ptr->num_trans; i++)
+ {
+ if (png_ptr->trans_alpha[i] != 0 && png_ptr->trans_alpha[i] != 0xff)
+ k=1; /* Partial transparency is present */
+ }
+ if (k == 0)
+ png_ptr->transformations &= ~PNG_GAMMA;
+ }
+
+ if ((png_ptr->transformations & (PNG_GAMMA | PNG_RGB_TO_GRAY)) &&
+ png_ptr->gamma != 0)
+ {
+ png_build_gamma_table(png_ptr, png_ptr->bit_depth);
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+ if (png_ptr->transformations & PNG_BACKGROUND)
+ {
+ if (color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ /* Could skip if no transparency */
+ png_color back, back_1;
+ png_colorp palette = png_ptr->palette;
+ int num_palette = png_ptr->num_palette;
+ int i;
+ if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE)
+ {
+
+ back.red = png_ptr->gamma_table[png_ptr->background.red];
+ back.green = png_ptr->gamma_table[png_ptr->background.green];
+ back.blue = png_ptr->gamma_table[png_ptr->background.blue];
+
+ back_1.red = png_ptr->gamma_to_1[png_ptr->background.red];
+ back_1.green = png_ptr->gamma_to_1[png_ptr->background.green];
+ back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue];
+ }
+ else
+ {
+ png_fixed_point g, gs;
+
+ switch (png_ptr->background_gamma_type)
+ {
+ case PNG_BACKGROUND_GAMMA_SCREEN:
+ g = (png_ptr->screen_gamma);
+ gs = PNG_FP_1;
+ break;
+
+ case PNG_BACKGROUND_GAMMA_FILE:
+ g = png_reciprocal(png_ptr->gamma);
+ gs = png_reciprocal2(png_ptr->gamma,
+ png_ptr->screen_gamma);
+ break;
+
+ case PNG_BACKGROUND_GAMMA_UNIQUE:
+ g = png_reciprocal(png_ptr->background_gamma);
+ gs = png_reciprocal2(png_ptr->background_gamma,
+ png_ptr->screen_gamma);
+ break;
+ default:
+ g = PNG_FP_1; /* back_1 */
+ gs = PNG_FP_1; /* back */
+ break;
+ }
+
+ if (png_gamma_significant(gs))
+ {
+ back.red = (png_byte)png_ptr->background.red;
+ back.green = (png_byte)png_ptr->background.green;
+ back.blue = (png_byte)png_ptr->background.blue;
+ }
+
+ else
+ {
+ back.red = png_gamma_8bit_correct(png_ptr->background.red,
+ gs);
+ back.green = png_gamma_8bit_correct(png_ptr->background.green,
+ gs);
+ back.blue = png_gamma_8bit_correct(png_ptr->background.blue,
+ gs);
+ }
+ back_1.red = png_gamma_8bit_correct(png_ptr->background.red, g);
+ back_1.green = png_gamma_8bit_correct(png_ptr->background.green,
+ g);
+ back_1.blue = png_gamma_8bit_correct(png_ptr->background.blue,
+ g);
+ }
+ for (i = 0; i < num_palette; i++)
+ {
+ if (i < (int)png_ptr->num_trans &&
+ png_ptr->trans_alpha[i] != 0xff)
+ {
+ if (png_ptr->trans_alpha[i] == 0)
+ {
+ palette[i] = back;
+ }
+ else /* if (png_ptr->trans_alpha[i] != 0xff) */
+ {
+ png_byte v, w;
+
+ v = png_ptr->gamma_to_1[palette[i].red];
+ png_composite(w, v, png_ptr->trans_alpha[i], back_1.red);
+ palette[i].red = png_ptr->gamma_from_1[w];
+
+ v = png_ptr->gamma_to_1[palette[i].green];
+ png_composite(w, v, png_ptr->trans_alpha[i], back_1.green);
+ palette[i].green = png_ptr->gamma_from_1[w];
+
+ v = png_ptr->gamma_to_1[palette[i].blue];
+ png_composite(w, v, png_ptr->trans_alpha[i], back_1.blue);
+ palette[i].blue = png_ptr->gamma_from_1[w];
+ }
+ }
+ else
+ {
+ palette[i].red = png_ptr->gamma_table[palette[i].red];
+ palette[i].green = png_ptr->gamma_table[palette[i].green];
+ palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+ }
+ }
+ /* Prevent the transformations being done again, and make sure
+ * that the now spurious alpha channel is stripped - the code
+ * has just reduced background composition and gamma correction
+ * to a simple alpha channel strip.
+ */
+ png_ptr->transformations &= ~PNG_BACKGROUND;
+ png_ptr->transformations &= ~PNG_GAMMA;
+ png_ptr->transformations |= PNG_STRIP_ALPHA;
+ }
+
+ /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */
+ else
+ /* color_type != PNG_COLOR_TYPE_PALETTE */
+ {
+ png_fixed_point g = PNG_FP_1;
+ png_fixed_point gs = PNG_FP_1;
+
+ switch (png_ptr->background_gamma_type)
+ {
+ case PNG_BACKGROUND_GAMMA_SCREEN:
+ g = png_ptr->screen_gamma;
+ /* gs = PNG_FP_1; */
+ break;
+
+ case PNG_BACKGROUND_GAMMA_FILE:
+ g = png_reciprocal(png_ptr->gamma);
+ gs = png_reciprocal2(png_ptr->gamma, png_ptr->screen_gamma);
+ break;
+
+ case PNG_BACKGROUND_GAMMA_UNIQUE:
+ g = png_reciprocal(png_ptr->background_gamma);
+ gs = png_reciprocal2(png_ptr->background_gamma,
+ png_ptr->screen_gamma);
+ break;
+
+ default:
+ png_error(png_ptr, "invalid background gamma type");
+ }
+
+ png_ptr->background_1.gray = png_gamma_correct(png_ptr,
+ png_ptr->background.gray, g);
+
+ png_ptr->background.gray = png_gamma_correct(png_ptr,
+ png_ptr->background.gray, gs);
+
+ if ((png_ptr->background.red != png_ptr->background.green) ||
+ (png_ptr->background.red != png_ptr->background.blue) ||
+ (png_ptr->background.red != png_ptr->background.gray))
+ {
+ /* RGB or RGBA with color background */
+ png_ptr->background_1.red = png_gamma_correct(png_ptr,
+ png_ptr->background.red, g);
+
+ png_ptr->background_1.green = png_gamma_correct(png_ptr,
+ png_ptr->background.green, g);
+
+ png_ptr->background_1.blue = png_gamma_correct(png_ptr,
+ png_ptr->background.blue, g);
+
+ png_ptr->background.red = png_gamma_correct(png_ptr,
+ png_ptr->background.red, gs);
+
+ png_ptr->background.green = png_gamma_correct(png_ptr,
+ png_ptr->background.green, gs);
+
+ png_ptr->background.blue = png_gamma_correct(png_ptr,
+ png_ptr->background.blue, gs);
+ }
+
+ else
+ {
+ /* GRAY, GRAY ALPHA, RGB, or RGBA with gray background */
+ png_ptr->background_1.red = png_ptr->background_1.green
+ = png_ptr->background_1.blue = png_ptr->background_1.gray;
+
+ png_ptr->background.red = png_ptr->background.green
+ = png_ptr->background.blue = png_ptr->background.gray;
+ }
+ }
+ }
+ else
+ /* Transformation does not include PNG_BACKGROUND */
+#endif /* PNG_READ_BACKGROUND_SUPPORTED */
+ if (color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ png_colorp palette = png_ptr->palette;
+ int num_palette = png_ptr->num_palette;
+ int i;
+
+ for (i = 0; i < num_palette; i++)
+ {
+ palette[i].red = png_ptr->gamma_table[palette[i].red];
+ palette[i].green = png_ptr->gamma_table[palette[i].green];
+ palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+ }
+
+ /* Done the gamma correction. */
+ png_ptr->transformations &= ~PNG_GAMMA;
+ }
+ }
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+ else
+#endif
+#endif /* PNG_READ_GAMMA_SUPPORTED */
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+ /* No GAMMA transformation */
+ if ((png_ptr->transformations & PNG_BACKGROUND) &&
+ (color_type == PNG_COLOR_TYPE_PALETTE))
+ {
+ int i;
+ int istop = (int)png_ptr->num_trans;
+ png_color back;
+ png_colorp palette = png_ptr->palette;
+
+ back.red = (png_byte)png_ptr->background.red;
+ back.green = (png_byte)png_ptr->background.green;
+ back.blue = (png_byte)png_ptr->background.blue;
+
+ for (i = 0; i < istop; i++)
+ {
+ if (png_ptr->trans_alpha[i] == 0)
+ {
+ palette[i] = back;
+ }
+
+ else if (png_ptr->trans_alpha[i] != 0xff)
+ {
+ /* The png_composite() macro is defined in png.h */
+ png_composite(palette[i].red, palette[i].red,
+ png_ptr->trans_alpha[i], back.red);
+
+ png_composite(palette[i].green, palette[i].green,
+ png_ptr->trans_alpha[i], back.green);
+
+ png_composite(palette[i].blue, palette[i].blue,
+ png_ptr->trans_alpha[i], back.blue);
+ }
+ }
+
+ /* Handled alpha, still need to strip the channel. */
+ png_ptr->transformations &= ~PNG_BACKGROUND;
+ png_ptr->transformations |= PNG_STRIP_ALPHA;
+ }
+#endif /* PNG_READ_BACKGROUND_SUPPORTED */
+
+#ifdef PNG_READ_SHIFT_SUPPORTED
+ if ((png_ptr->transformations & PNG_SHIFT) &&
+ (color_type == PNG_COLOR_TYPE_PALETTE))
+ {
+ png_uint_16 i;
+ png_uint_16 istop = png_ptr->num_palette;
+ int sr = 8 - png_ptr->sig_bit.red;
+ int sg = 8 - png_ptr->sig_bit.green;
+ int sb = 8 - png_ptr->sig_bit.blue;
+
+ if (sr < 0 || sr > 8)
+ sr = 0;
+
+ if (sg < 0 || sg > 8)
+ sg = 0;
+
+ if (sb < 0 || sb > 8)
+ sb = 0;
+
+ for (i = 0; i < istop; i++)
+ {
+ png_ptr->palette[i].red >>= sr;
+ png_ptr->palette[i].green >>= sg;
+ png_ptr->palette[i].blue >>= sb;
+ }
+ }
+#endif /* PNG_READ_SHIFT_SUPPORTED */
+ }
+#if !defined(PNG_READ_GAMMA_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) \
+ && !defined(PNG_READ_BACKGROUND_SUPPORTED)
+ if (png_ptr)
+ return;
+#endif
+}
+
+/* Modify the info structure to reflect the transformations. The
+ * info should be updated so a PNG file could be written with it,
+ * assuming the transformations result in valid PNG data.
+ */
+void /* PRIVATE */
+png_read_transform_info(png_structp png_ptr, png_infop info_ptr)
+{
+ png_debug(1, "in png_read_transform_info");
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+ if (png_ptr->transformations & PNG_EXPAND)
+ {
+ if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ if (png_ptr->num_trans &&
+ (png_ptr->transformations & PNG_EXPAND_tRNS))
+ info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+
+ else
+ info_ptr->color_type = PNG_COLOR_TYPE_RGB;
+
+ info_ptr->bit_depth = 8;
+ info_ptr->num_trans = 0;
+ }
+ else
+ {
+ if (png_ptr->num_trans)
+ {
+ if (png_ptr->transformations & PNG_EXPAND_tRNS)
+ info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
+ }
+ if (info_ptr->bit_depth < 8)
+ info_ptr->bit_depth = 8;
+
+ info_ptr->num_trans = 0;
+ }
+ }
+#endif
+
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+ if (png_ptr->transformations & PNG_EXPAND_16 && info_ptr->bit_depth == 8 &&
+ info_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
+ {
+ info_ptr->bit_depth = 16;
+ }
+#endif
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+ if (png_ptr->transformations & PNG_BACKGROUND)
+ {
+ info_ptr->color_type = (png_byte)(info_ptr->color_type &
+ ~PNG_COLOR_MASK_ALPHA);
+ info_ptr->num_trans = 0;
+ info_ptr->background = png_ptr->background;
+ }
+#endif
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ if (png_ptr->transformations & PNG_GAMMA)
+ {
+ info_ptr->gamma = png_ptr->gamma;
+ }
+#endif
+
+#ifdef PNG_READ_16_TO_8_SUPPORTED
+#ifdef PNG_READ_16BIT_SUPPORTED
+ if ((png_ptr->transformations & PNG_16_TO_8) && (info_ptr->bit_depth == 16))
+ info_ptr->bit_depth = 8;
+#else
+ /* Force chopping 16-bit input down to 8 */
+ if (info_ptr->bit_depth == 16)
+ {
+ png_ptr->transformations |=PNG_16_TO_8;
+ info_ptr->bit_depth = 8;
+ }
+#endif
+#endif
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+ if (png_ptr->transformations & PNG_GRAY_TO_RGB)
+ info_ptr->color_type |= PNG_COLOR_MASK_COLOR;
+#endif
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+ if (png_ptr->transformations & PNG_RGB_TO_GRAY)
+ info_ptr->color_type &= ~PNG_COLOR_MASK_COLOR;
+#endif
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+ if (png_ptr->transformations & PNG_QUANTIZE)
+ {
+ if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) ||
+ (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) &&
+ png_ptr->palette_lookup && info_ptr->bit_depth == 8)
+ {
+ info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
+ }
+ }
+#endif
+
+#ifdef PNG_READ_PACK_SUPPORTED
+ if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8))
+ info_ptr->bit_depth = 8;
+#endif
+
+ if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ info_ptr->channels = 1;
+
+ else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
+ info_ptr->channels = 3;
+
+ else
+ info_ptr->channels = 1;
+
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+ if (png_ptr->transformations & PNG_STRIP_ALPHA)
+ info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA;
+#endif
+
+ if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
+ info_ptr->channels++;
+
+#ifdef PNG_READ_FILLER_SUPPORTED
+ /* STRIP_ALPHA and FILLER allowed: MASK_ALPHA bit stripped above */
+ if ((png_ptr->transformations & PNG_FILLER) &&
+ ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) ||
+ (info_ptr->color_type == PNG_COLOR_TYPE_GRAY)))
+ {
+ info_ptr->channels++;
+ /* If adding a true alpha channel not just filler */
+ if (png_ptr->transformations & PNG_ADD_ALPHA)
+ info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
+ }
+#endif
+
+#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \
+defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
+ if (png_ptr->transformations & PNG_USER_TRANSFORM)
+ {
+ if (info_ptr->bit_depth < png_ptr->user_transform_depth)
+ info_ptr->bit_depth = png_ptr->user_transform_depth;
+
+ if (info_ptr->channels < png_ptr->user_transform_channels)
+ info_ptr->channels = png_ptr->user_transform_channels;
+ }
+#endif
+
+ info_ptr->pixel_depth = (png_byte)(info_ptr->channels *
+ info_ptr->bit_depth);
+
+ info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, info_ptr->width);
+
+#ifndef PNG_READ_EXPAND_SUPPORTED
+ if (png_ptr)
+ return;
+#endif
+}
+
+/* Transform the row. The order of transformations is significant,
+ * and is very touchy. If you add a transformation, take care to
+ * decide how it fits in with the other transformations here.
+ */
+void /* PRIVATE */
+png_do_read_transformations(png_structp png_ptr)
+{
+ png_debug(1, "in png_do_read_transformations");
+
+ if (png_ptr->row_buf == NULL)
+ {
+#ifdef PNG_CONSOLE_IO_SUPPORTED
+ char msg[50];
+
+ png_snprintf2(msg, 50,
+ "NULL row buffer for row %ld, pass %d", (long)png_ptr->row_number,
+ png_ptr->pass);
+ png_error(png_ptr, msg);
+#else
+ png_error(png_ptr, "NULL row buffer");
+#endif
+ }
+#ifdef PNG_WARN_UNINITIALIZED_ROW
+ if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
+ /* Application has failed to call either png_read_start_image()
+ * or png_read_update_info() after setting transforms that expand
+ * pixels. This check added to libpng-1.2.19
+ */
+#if (PNG_WARN_UNINITIALIZED_ROW==1)
+ png_error(png_ptr, "Uninitialized row");
+#else
+ png_warning(png_ptr, "Uninitialized row");
+#endif
+#endif
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+ if (png_ptr->transformations & PNG_EXPAND)
+ {
+ if (png_ptr->row_info.color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ png_do_expand_palette(&(png_ptr->row_info), png_ptr->row_buf + 1,
+ png_ptr->palette, png_ptr->trans_alpha, png_ptr->num_trans);
+ }
+
+ else
+ {
+ if (png_ptr->num_trans &&
+ (png_ptr->transformations & PNG_EXPAND_tRNS))
+ png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1,
+ &(png_ptr->trans_color));
+
+ else
+ png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1,
+ NULL);
+ }
+ }
+#endif
+
+ /* Delay the 'expand 16' step until later for efficiency, so that the
+ * intermediate steps work with 8 bit data.
+ */
+
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+ if ((png_ptr->transformations & PNG_STRIP_ALPHA) &&
+ (png_ptr->row_info.color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
+ png_ptr->row_info.color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
+ png_do_strip_channel(&(png_ptr->row_info), png_ptr->row_buf + 1,
+ 0/*!at_start, because SWAP_ALPHA happens later*/);
+#endif
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+ if (png_ptr->transformations & PNG_RGB_TO_GRAY)
+ {
+ int rgb_error =
+ png_do_rgb_to_gray(png_ptr, &(png_ptr->row_info),
+ png_ptr->row_buf + 1);
+
+ if (rgb_error)
+ {
+ png_ptr->rgb_to_gray_status=1;
+ if ((png_ptr->transformations & PNG_RGB_TO_GRAY) ==
+ PNG_RGB_TO_GRAY_WARN)
+ png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel");
+
+ if ((png_ptr->transformations & PNG_RGB_TO_GRAY) ==
+ PNG_RGB_TO_GRAY_ERR)
+ png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel");
+ }
+ }
+#endif
+
+/* From Andreas Dilger e-mail to png-implement, 26 March 1998:
+ *
+ * In most cases, the "simple transparency" should be done prior to doing
+ * gray-to-RGB, or you will have to test 3x as many bytes to check if a
+ * pixel is transparent. You would also need to make sure that the
+ * transparency information is upgraded to RGB.
+ *
+ * To summarize, the current flow is:
+ * - Gray + simple transparency -> compare 1 or 2 gray bytes and composite
+ * with background "in place" if transparent,
+ * convert to RGB if necessary
+ * - Gray + alpha -> composite with gray background and remove alpha bytes,
+ * convert to RGB if necessary
+ *
+ * To support RGB backgrounds for gray images we need:
+ * - Gray + simple transparency -> convert to RGB + simple transparency,
+ * compare 3 or 6 bytes and composite with
+ * background "in place" if transparent
+ * (3x compare/pixel compared to doing
+ * composite with gray bkgrnd)
+ * - Gray + alpha -> convert to RGB + alpha, composite with background and
+ * remove alpha bytes (3x float
+ * operations/pixel compared with composite
+ * on gray background)
+ *
+ * Greg's change will do this. The reason it wasn't done before is for
+ * performance, as this increases the per-pixel operations. If we would check
+ * in advance if the background was gray or RGB, and position the gray-to-RGB
+ * transform appropriately, then it would save a lot of work/time.
+ */
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+ /* If gray -> RGB, do so now only if background is non-gray; else do later
+ * for performance reasons
+ */
+ if ((png_ptr->transformations & PNG_GRAY_TO_RGB) &&
+ !(png_ptr->mode & PNG_BACKGROUND_IS_GRAY))
+ png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+ if ((png_ptr->transformations & PNG_BACKGROUND) &&
+ ((png_ptr->num_trans != 0) ||
+ (png_ptr->color_type & PNG_COLOR_MASK_ALPHA)))
+ png_do_background(&(png_ptr->row_info), png_ptr->row_buf + 1,
+ &(png_ptr->trans_color), &(png_ptr->background)
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ , &(png_ptr->background_1),
+ png_ptr->gamma_table, png_ptr->gamma_from_1,
+ png_ptr->gamma_to_1, png_ptr->gamma_16_table,
+ png_ptr->gamma_16_from_1, png_ptr->gamma_16_to_1,
+ png_ptr->gamma_shift
+#endif
+ );
+#endif
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ if ((png_ptr->transformations & PNG_GAMMA) &&
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+ !((png_ptr->transformations & PNG_BACKGROUND) &&
+ ((png_ptr->num_trans != 0) ||
+ (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) &&
+#endif
+ (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE))
+ png_do_gamma(&(png_ptr->row_info), png_ptr->row_buf + 1,
+ png_ptr->gamma_table, png_ptr->gamma_16_table,
+ png_ptr->gamma_shift);
+#endif
+
+#ifdef PNG_READ_16_TO_8_SUPPORTED
+ if (png_ptr->transformations & PNG_16_TO_8)
+ png_do_chop(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+ if (png_ptr->transformations & PNG_QUANTIZE)
+ {
+ png_do_quantize(&(png_ptr->row_info), png_ptr->row_buf + 1,
+ png_ptr->palette_lookup, png_ptr->quantize_index);
+
+ if (png_ptr->row_info.rowbytes == 0)
+ png_error(png_ptr, "png_do_quantize returned rowbytes=0");
+ }
+#endif /* PNG_READ_QUANTIZE_SUPPORTED */
+
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+ /* Do the expansion now, after all the arithmetic has been done. Notice
+ * that previous transformations can handle the PNG_EXPAND_16 flag if this
+ * is efficient (particularly true in the case of gamma correction, where
+ * better accuracy results faster!)
+ */
+ if (png_ptr->transformations & PNG_EXPAND_16)
+ png_do_expand_16(&png_ptr->row_info, png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_INVERT_SUPPORTED
+ if (png_ptr->transformations & PNG_INVERT_MONO)
+ png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_SHIFT_SUPPORTED
+ if (png_ptr->transformations & PNG_SHIFT)
+ png_do_unshift(&(png_ptr->row_info), png_ptr->row_buf + 1,
+ &(png_ptr->shift));
+#endif
+
+#ifdef PNG_READ_PACK_SUPPORTED
+ if (png_ptr->transformations & PNG_PACK)
+ png_do_unpack(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_BGR_SUPPORTED
+ if (png_ptr->transformations & PNG_BGR)
+ png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+ if (png_ptr->transformations & PNG_PACKSWAP)
+ png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+ /*NOTE: this must be in the wrong place - what happens if BGR is set too?
+ * Need pngvalid to test this combo.
+ */
+ /* If gray -> RGB, do so now only if we did not do so above */
+ if ((png_ptr->transformations & PNG_GRAY_TO_RGB) &&
+ (png_ptr->mode & PNG_BACKGROUND_IS_GRAY))
+ png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_FILLER_SUPPORTED
+ if (png_ptr->transformations & PNG_FILLER)
+ png_do_read_filler(&(png_ptr->row_info), png_ptr->row_buf + 1,
+ (png_uint_32)png_ptr->filler, png_ptr->flags);
+#endif
+
+#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
+ if (png_ptr->transformations & PNG_INVERT_ALPHA)
+ png_do_read_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
+ if (png_ptr->transformations & PNG_SWAP_ALPHA)
+ png_do_read_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+#ifdef PNG_READ_SWAP_SUPPORTED
+ if (png_ptr->transformations & PNG_SWAP_BYTES)
+ png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+#endif
+
+#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
+ if (png_ptr->transformations & PNG_USER_TRANSFORM)
+ {
+ if (png_ptr->read_user_transform_fn != NULL)
+ (*(png_ptr->read_user_transform_fn)) /* User read transform function */
+ (png_ptr, /* png_ptr */
+ &(png_ptr->row_info), /* row_info: */
+ /* png_uint_32 width; width of row */
+ /* png_size_t rowbytes; number of bytes in row */
+ /* png_byte color_type; color type of pixels */
+ /* png_byte bit_depth; bit depth of samples */
+ /* png_byte channels; number of channels (1-4) */
+ /* png_byte pixel_depth; bits per pixel (depth*channels) */
+ png_ptr->row_buf + 1); /* start of pixel data for row */
+#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
+ if (png_ptr->user_transform_depth)
+ png_ptr->row_info.bit_depth = png_ptr->user_transform_depth;
+
+ if (png_ptr->user_transform_channels)
+ png_ptr->row_info.channels = png_ptr->user_transform_channels;
+#endif
+ png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth *
+ png_ptr->row_info.channels);
+
+ png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth,
+ png_ptr->row_info.width);
+ }
+#endif
+
+}
+
+#ifdef PNG_READ_PACK_SUPPORTED
+/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel,
+ * without changing the actual values. Thus, if you had a row with
+ * a bit depth of 1, you would end up with bytes that only contained
+ * the numbers 0 or 1. If you would rather they contain 0 and 255, use
+ * png_do_shift() after this.
+ */
+void /* PRIVATE */
+png_do_unpack(png_row_infop row_info, png_bytep row)
+{
+ png_debug(1, "in png_do_unpack");
+
+ if (row_info->bit_depth < 8)
+ {
+ png_uint_32 i;
+ png_uint_32 row_width=row_info->width;
+
+ switch (row_info->bit_depth)
+ {
+ case 1:
+ {
+ png_bytep sp = row + (png_size_t)((row_width - 1) >> 3);
+ png_bytep dp = row + (png_size_t)row_width - 1;
+ png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07);
+ for (i = 0; i < row_width; i++)
+ {
+ *dp = (png_byte)((*sp >> shift) & 0x01);
+
+ if (shift == 7)
+ {
+ shift = 0;
+ sp--;
+ }
+
+ else
+ shift++;
+
+ dp--;
+ }
+ break;
+ }
+
+ case 2:
+ {
+
+ png_bytep sp = row + (png_size_t)((row_width - 1) >> 2);
+ png_bytep dp = row + (png_size_t)row_width - 1;
+ png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
+ for (i = 0; i < row_width; i++)
+ {
+ *dp = (png_byte)((*sp >> shift) & 0x03);
+
+ if (shift == 6)
+ {
+ shift = 0;
+ sp--;
+ }
+
+ else
+ shift += 2;
+
+ dp--;
+ }
+ break;
+ }
+
+ case 4:
+ {
+ png_bytep sp = row + (png_size_t)((row_width - 1) >> 1);
+ png_bytep dp = row + (png_size_t)row_width - 1;
+ png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2);
+ for (i = 0; i < row_width; i++)
+ {
+ *dp = (png_byte)((*sp >> shift) & 0x0f);
+
+ if (shift == 4)
+ {
+ shift = 0;
+ sp--;
+ }
+
+ else
+ shift = 4;
+
+ dp--;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ row_info->bit_depth = 8;
+ row_info->pixel_depth = (png_byte)(8 * row_info->channels);
+ row_info->rowbytes = row_width * row_info->channels;
+ }
+}
+#endif
+
+#ifdef PNG_READ_SHIFT_SUPPORTED
+/* Reverse the effects of png_do_shift. This routine merely shifts the
+ * pixels back to their significant bits values. Thus, if you have
+ * a row of bit depth 8, but only 5 are significant, this will shift
+ * the values back to 0 through 31.
+ */
+void /* PRIVATE */
+png_do_unshift(png_row_infop row_info, png_bytep row,
+ png_const_color_8p sig_bits)
+{
+ png_debug(1, "in png_do_unshift");
+
+ if (
+ row_info->color_type != PNG_COLOR_TYPE_PALETTE)
+ {
+ int shift[4];
+ int channels = 0;
+ int c;
+ png_uint_16 value = 0;
+ png_uint_32 row_width = row_info->width;
+
+ if (row_info->color_type & PNG_COLOR_MASK_COLOR)
+ {
+ shift[channels++] = row_info->bit_depth - sig_bits->red;
+ shift[channels++] = row_info->bit_depth - sig_bits->green;
+ shift[channels++] = row_info->bit_depth - sig_bits->blue;
+ }
+
+ else
+ {
+ shift[channels++] = row_info->bit_depth - sig_bits->gray;
+ }
+
+ if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
+ {
+ shift[channels++] = row_info->bit_depth - sig_bits->alpha;
+ }
+
+ for (c = 0; c < channels; c++)
+ {
+ if (shift[c] <= 0)
+ shift[c] = 0;
+
+ else
+ value = 1;
+ }
+
+ if (!value)
+ return;
+
+ switch (row_info->bit_depth)
+ {
+ default:
+ break;
+
+ case 2:
+ {
+ png_bytep bp;
+ png_size_t i;
+ png_size_t istop = row_info->rowbytes;
+
+ for (bp = row, i = 0; i < istop; i++)
+ {
+ *bp >>= 1;
+ *bp++ &= 0x55;
+ }
+ break;
+ }
+
+ case 4:
+ {
+ png_bytep bp = row;
+ png_size_t i;
+ png_size_t istop = row_info->rowbytes;
+ png_byte mask = (png_byte)((((int)0xf0 >> shift[0]) & (int)0xf0) |
+ (png_byte)((int)0xf >> shift[0]));
+
+ for (i = 0; i < istop; i++)
+ {
+ *bp >>= shift[0];
+ *bp++ &= mask;
+ }
+ break;
+ }
+
+ case 8:
+ {
+ png_bytep bp = row;
+ png_uint_32 i;
+ png_uint_32 istop = row_width * channels;
+
+ for (i = 0; i < istop; i++)
+ {
+ *bp++ >>= shift[i%channels];
+ }
+ break;
+ }
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+ case 16:
+ {
+ png_bytep bp = row;
+ png_uint_32 i;
+ png_uint_32 istop = channels * row_width;
+
+ for (i = 0; i < istop; i++)
+ {
+ value = (png_uint_16)((*bp << 8) + *(bp + 1));
+ value >>= shift[i%channels];
+ *bp++ = (png_byte)(value >> 8);
+ *bp++ = (png_byte)(value & 0xff);
+ }
+ break;
+ }
+#endif
+ }
+ }
+}
+#endif
+
+#ifdef PNG_READ_16_TO_8_SUPPORTED
+/* Chop rows of bit depth 16 down to 8 */
+void /* PRIVATE */
+png_do_chop(png_row_infop row_info, png_bytep row)
+{
+ png_debug(1, "in png_do_chop");
+
+ if (row_info->bit_depth == 16)
+ {
+ png_bytep sp = row;
+ png_bytep dp = row;
+ png_uint_32 i;
+ png_uint_32 istop = row_info->width * row_info->channels;
+
+ for (i = 0; i<istop; i++, sp += 2, dp++)
+ {
+#ifdef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED
+ /* This does a more accurate scaling of the 16-bit color
+ * value, rather than a simple low-byte truncation.
+ *
+ * What the ideal calculation should be:
+ * *dp = (((((png_uint_32)(*sp) << 8) |
+ * (png_uint_32)(*(sp + 1))) * 255 + 127)
+ * / (png_uint_32)65535L;
+ *
+ * GRR: no, I think this is what it really should be:
+ * *dp = (((((png_uint_32)(*sp) << 8) |
+ * (png_uint_32)(*(sp + 1))) + 128L)
+ * / (png_uint_32)257L;
+ *
+ * GRR: here's the exact calculation with shifts:
+ * temp = (((png_uint_32)(*sp) << 8) |
+ * (png_uint_32)(*(sp + 1))) + 128L;
+ * *dp = (temp - (temp >> 8)) >> 8;
+ *
+ * Approximate calculation with shift/add instead of multiply/divide:
+ * *dp = ((((png_uint_32)(*sp) << 8) |
+ * (png_uint_32)((int)(*(sp + 1)) - *sp)) + 128) >> 8;
+ *
+ * What we actually do to avoid extra shifting and conversion:
+ */
+
+ *dp = *sp + ((((int)(*(sp + 1)) - *sp) > 128) ? 1 : 0);
+#else
+ /* Simply discard the low order byte */
+ *dp = *sp;
+#endif
+ }
+ row_info->bit_depth = 8;
+ row_info->pixel_depth = (png_byte)(8 * row_info->channels);
+ row_info->rowbytes = row_info->width * row_info->channels;
+ }
+}
+#endif
+
+#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED
+void /* PRIVATE */
+png_do_read_swap_alpha(png_row_infop row_info, png_bytep row)
+{
+ png_debug(1, "in png_do_read_swap_alpha");
+
+ {
+ png_uint_32 row_width = row_info->width;
+ if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ {
+ /* This converts from RGBA to ARGB */
+ if (row_info->bit_depth == 8)
+ {
+ png_bytep sp = row + row_info->rowbytes;
+ png_bytep dp = sp;
+ png_byte save;
+ png_uint_32 i;
+
+ for (i = 0; i < row_width; i++)
+ {
+ save = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = save;
+ }
+ }
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+ /* This converts from RRGGBBAA to AARRGGBB */
+ else
+ {
+ png_bytep sp = row + row_info->rowbytes;
+ png_bytep dp = sp;
+ png_byte save[2];
+ png_uint_32 i;
+
+ for (i = 0; i < row_width; i++)
+ {
+ save[0] = *(--sp);
+ save[1] = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = save[0];
+ *(--dp) = save[1];
+ }
+ }
+#endif
+ }
+
+ else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ {
+ /* This converts from GA to AG */
+ if (row_info->bit_depth == 8)
+ {
+ png_bytep sp = row + row_info->rowbytes;
+ png_bytep dp = sp;
+ png_byte save;
+ png_uint_32 i;
+
+ for (i = 0; i < row_width; i++)
+ {
+ save = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = save;
+ }
+ }
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+ /* This converts from GGAA to AAGG */
+ else
+ {
+ png_bytep sp = row + row_info->rowbytes;
+ png_bytep dp = sp;
+ png_byte save[2];
+ png_uint_32 i;
+
+ for (i = 0; i < row_width; i++)
+ {
+ save[0] = *(--sp);
+ save[1] = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = save[0];
+ *(--dp) = save[1];
+ }
+ }
+#endif
+ }
+ }
+}
+#endif
+
+#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED
+void /* PRIVATE */
+png_do_read_invert_alpha(png_row_infop row_info, png_bytep row)
+{
+ png_uint_32 row_width;
+ png_debug(1, "in png_do_read_invert_alpha");
+
+ row_width = row_info->width;
+ if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ {
+ if (row_info->bit_depth == 8)
+ {
+ /* This inverts the alpha channel in RGBA */
+ png_bytep sp = row + row_info->rowbytes;
+ png_bytep dp = sp;
+ png_uint_32 i;
+
+ for (i = 0; i < row_width; i++)
+ {
+ *(--dp) = (png_byte)(255 - *(--sp));
+
+/* This does nothing:
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ We can replace it with:
+*/
+ sp-=3;
+ dp=sp;
+ }
+ }
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+ /* This inverts the alpha channel in RRGGBBAA */
+ else
+ {
+ png_bytep sp = row + row_info->rowbytes;
+ png_bytep dp = sp;
+ png_uint_32 i;
+
+ for (i = 0; i < row_width; i++)
+ {
+ *(--dp) = (png_byte)(255 - *(--sp));
+ *(--dp) = (png_byte)(255 - *(--sp));
+
+/* This does nothing:
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ We can replace it with:
+*/
+ sp-=6;
+ dp=sp;
+ }
+ }
+#endif
+ }
+ else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ {
+ if (row_info->bit_depth == 8)
+ {
+ /* This inverts the alpha channel in GA */
+ png_bytep sp = row + row_info->rowbytes;
+ png_bytep dp = sp;
+ png_uint_32 i;
+
+ for (i = 0; i < row_width; i++)
+ {
+ *(--dp) = (png_byte)(255 - *(--sp));
+ *(--dp) = *(--sp);
+ }
+ }
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+ else
+ {
+ /* This inverts the alpha channel in GGAA */
+ png_bytep sp = row + row_info->rowbytes;
+ png_bytep dp = sp;
+ png_uint_32 i;
+
+ for (i = 0; i < row_width; i++)
+ {
+ *(--dp) = (png_byte)(255 - *(--sp));
+ *(--dp) = (png_byte)(255 - *(--sp));
+/*
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+*/
+ sp-=2;
+ dp=sp;
+ }
+ }
+#endif
+ }
+}
+#endif
+
+#ifdef PNG_READ_FILLER_SUPPORTED
+/* Add filler channel if we have RGB color */
+void /* PRIVATE */
+png_do_read_filler(png_row_infop row_info, png_bytep row,
+ png_uint_32 filler, png_uint_32 flags)
+{
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+ png_byte hi_filler = (png_byte)((filler>>8) & 0xff);
+#endif
+ png_byte lo_filler = (png_byte)(filler & 0xff);
+
+ png_debug(1, "in png_do_read_filler");
+
+ if (
+ row_info->color_type == PNG_COLOR_TYPE_GRAY)
+ {
+ if (row_info->bit_depth == 8)
+ {
+ if (flags & PNG_FLAG_FILLER_AFTER)
+ {
+ /* This changes the data from G to GX */
+ png_bytep sp = row + (png_size_t)row_width;
+ png_bytep dp = sp + (png_size_t)row_width;
+ for (i = 1; i < row_width; i++)
+ {
+ *(--dp) = lo_filler;
+ *(--dp) = *(--sp);
+ }
+ *(--dp) = lo_filler;
+ row_info->channels = 2;
+ row_info->pixel_depth = 16;
+ row_info->rowbytes = row_width * 2;
+ }
+
+ else
+ {
+ /* This changes the data from G to XG */
+ png_bytep sp = row + (png_size_t)row_width;
+ png_bytep dp = sp + (png_size_t)row_width;
+ for (i = 0; i < row_width; i++)
+ {
+ *(--dp) = *(--sp);
+ *(--dp) = lo_filler;
+ }
+ row_info->channels = 2;
+ row_info->pixel_depth = 16;
+ row_info->rowbytes = row_width * 2;
+ }
+ }
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+ else if (row_info->bit_depth == 16)
+ {
+ if (flags & PNG_FLAG_FILLER_AFTER)
+ {
+ /* This changes the data from GG to GGXX */
+ png_bytep sp = row + (png_size_t)row_width * 2;
+ png_bytep dp = sp + (png_size_t)row_width * 2;
+ for (i = 1; i < row_width; i++)
+ {
+ *(--dp) = hi_filler;
+ *(--dp) = lo_filler;
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ }
+ *(--dp) = hi_filler;
+ *(--dp) = lo_filler;
+ row_info->channels = 2;
+ row_info->pixel_depth = 32;
+ row_info->rowbytes = row_width * 4;
+ }
+
+ else
+ {
+ /* This changes the data from GG to XXGG */
+ png_bytep sp = row + (png_size_t)row_width * 2;
+ png_bytep dp = sp + (png_size_t)row_width * 2;
+ for (i = 0; i < row_width; i++)
+ {
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = hi_filler;
+ *(--dp) = lo_filler;
+ }
+ row_info->channels = 2;
+ row_info->pixel_depth = 32;
+ row_info->rowbytes = row_width * 4;
+ }
+ }
+#endif
+ } /* COLOR_TYPE == GRAY */
+ else if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+ {
+ if (row_info->bit_depth == 8)
+ {
+ if (flags & PNG_FLAG_FILLER_AFTER)
+ {
+ /* This changes the data from RGB to RGBX */
+ png_bytep sp = row + (png_size_t)row_width * 3;
+ png_bytep dp = sp + (png_size_t)row_width;
+ for (i = 1; i < row_width; i++)
+ {
+ *(--dp) = lo_filler;
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ }
+ *(--dp) = lo_filler;
+ row_info->channels = 4;
+ row_info->pixel_depth = 32;
+ row_info->rowbytes = row_width * 4;
+ }
+
+ else
+ {
+ /* This changes the data from RGB to XRGB */
+ png_bytep sp = row + (png_size_t)row_width * 3;
+ png_bytep dp = sp + (png_size_t)row_width;
+ for (i = 0; i < row_width; i++)
+ {
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = lo_filler;
+ }
+ row_info->channels = 4;
+ row_info->pixel_depth = 32;
+ row_info->rowbytes = row_width * 4;
+ }
+ }
+
+#ifdef PNG_READ_16BIT_SUPPORTED
+ else if (row_info->bit_depth == 16)
+ {
+ if (flags & PNG_FLAG_FILLER_AFTER)
+ {
+ /* This changes the data from RRGGBB to RRGGBBXX */
+ png_bytep sp = row + (png_size_t)row_width * 6;
+ png_bytep dp = sp + (png_size_t)row_width * 2;
+ for (i = 1; i < row_width; i++)
+ {
+ *(--dp) = hi_filler;
+ *(--dp) = lo_filler;
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ }
+ *(--dp) = hi_filler;
+ *(--dp) = lo_filler;
+ row_info->channels = 4;
+ row_info->pixel_depth = 64;
+ row_info->rowbytes = row_width * 8;
+ }
+
+ else
+ {
+ /* This changes the data from RRGGBB to XXRRGGBB */
+ png_bytep sp = row + (png_size_t)row_width * 6;
+ png_bytep dp = sp + (png_size_t)row_width * 2;
+ for (i = 0; i < row_width; i++)
+ {
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = *(--sp);
+ *(--dp) = hi_filler;
+ *(--dp) = lo_filler;
+ }
+
+ row_info->channels = 4;
+ row_info->pixel_depth = 64;
+ row_info->rowbytes = row_width * 8;
+ }
+ }
+#endif
+ } /* COLOR_TYPE == RGB */
+}
+#endif
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+/* Expand grayscale files to RGB, with or without alpha */
+void /* PRIVATE */
+png_do_gray_to_rgb(png_row_infop row_info, png_bytep row)
+{
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+
+ png_debug(1, "in png_do_gray_to_rgb");
+
+ if (row_info->bit_depth >= 8 &&
+ !(row_info->color_type & PNG_COLOR_MASK_COLOR))
+ {
+ if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
+ {
+ if (row_info->bit_depth == 8)
+ {
+ /* This changes G to RGB */
+ png_bytep sp = row + (png_size_t)row_width - 1;
+ png_bytep dp = sp + (png_size_t)row_width * 2;
+ for (i = 0; i < row_width; i++)
+ {
+ *(dp--) = *sp;
+ *(dp--) = *sp;
+ *(dp--) = *(sp--);
+ }
+ }
+
+ else
+ {
+ /* This changes GG to RRGGBB */
+ png_bytep sp = row + (png_size_t)row_width * 2 - 1;
+ png_bytep dp = sp + (png_size_t)row_width * 4;
+ for (i = 0; i < row_width; i++)
+ {
+ *(dp--) = *sp;
+ *(dp--) = *(sp - 1);
+ *(dp--) = *sp;
+ *(dp--) = *(sp - 1);
+ *(dp--) = *(sp--);
+ *(dp--) = *(sp--);
+ }
+ }
+ }
+
+ else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ {
+ if (row_info->bit_depth == 8)
+ {
+ /* This changes GA to RGBA */
+ png_bytep sp = row + (png_size_t)row_width * 2 - 1;
+ png_bytep dp = sp + (png_size_t)row_width * 2;
+ for (i = 0; i < row_width; i++)
+ {
+ *(dp--) = *(sp--);
+ *(dp--) = *sp;
+ *(dp--) = *sp;
+ *(dp--) = *(sp--);
+ }
+ }
+
+ else
+ {
+ /* This changes GGAA to RRGGBBAA */
+ png_bytep sp = row + (png_size_t)row_width * 4 - 1;
+ png_bytep dp = sp + (png_size_t)row_width * 4;
+ for (i = 0; i < row_width; i++)
+ {
+ *(dp--) = *(sp--);
+ *(dp--) = *(sp--);
+ *(dp--) = *sp;
+ *(dp--) = *(sp - 1);
+ *(dp--) = *sp;
+ *(dp--) = *(sp - 1);
+ *(dp--) = *(sp--);
+ *(dp--) = *(sp--);
+ }
+ }
+ }
+ row_info->channels += (png_byte)2;
+ row_info->color_type |= PNG_COLOR_MASK_COLOR;
+ row_info->pixel_depth = (png_byte)(row_info->channels *
+ row_info->bit_depth);
+ row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
+ }
+}
+#endif
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+/* Reduce RGB files to grayscale, with or without alpha
+ * using the equation given in Poynton's ColorFAQ at
+ * <http://www.inforamp.net/~poynton/> (THIS LINK IS DEAD June 2008)
+ * New link:
+ * <http://www.poynton.com/notes/colour_and_gamma/>
+ * Charles Poynton poynton at poynton.com
+ *
+ * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B
+ *
+ * We approximate this with
+ *
+ * Y = 0.21268 * R + 0.7151 * G + 0.07217 * B
+ *
+ * which can be expressed with integers as
+ *
+ * Y = (6969 * R + 23434 * G + 2365 * B)/32768
+ *
+ * The calculation is to be done in a linear colorspace.
+ *
+ * Other integer coefficents can be used via png_set_rgb_to_gray().
+ */
+int /* PRIVATE */
+png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row)
+
+{
+ png_uint_32 i;
+
+ png_uint_32 row_width = row_info->width;
+ int rgb_error = 0;
+
+ png_debug(1, "in png_do_rgb_to_gray");
+
+ if (!(row_info->color_type & PNG_COLOR_MASK_PALETTE) &&
+ (row_info->color_type & PNG_COLOR_MASK_COLOR))
+ {
+ png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff;
+ png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff;
+ png_uint_32 bc = png_ptr->rgb_to_gray_blue_coeff;
+
+ if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+ {
+ if (row_info->bit_depth == 8)
+ {
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+ if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL)
+ {
+ png_bytep sp = row;
+ png_bytep dp = row;
+
+ for (i = 0; i < row_width; i++)
+ {
+ png_byte red = png_ptr->gamma_to_1[*(sp++)];
+ png_byte green = png_ptr->gamma_to_1[*(sp++)];
+ png_byte blue = png_ptr->gamma_to_1[*(sp++)];
+
+ if (red != green || red != blue)
+ {
+ rgb_error |= 1;
+ *(dp++) = png_ptr->gamma_from_1[
+ (rc*red + gc*green + bc*blue)>>15];
+ }
+
+ else
+ *(dp++) = *(sp - 1);
+ }
+ }
+ else
+#endif
+ {
+ png_bytep sp = row;
+ png_bytep dp = row;
+ for (i = 0; i < row_width; i++)
+ {
+ png_byte red = *(sp++);
+ png_byte green = *(sp++);
+ png_byte blue = *(sp++);
+
+ if (red != green || red != blue)
+ {
+ rgb_error |= 1;
+ *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15);
+ }
+
+ else
+ *(dp++) = *(sp - 1);
+ }
+ }
+ }
+
+ else /* RGB bit_depth == 16 */
+ {
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+ if (png_ptr->gamma_16_to_1 != NULL &&
+ png_ptr->gamma_16_from_1 != NULL)
+ {
+ png_bytep sp = row;
+ png_bytep dp = row;
+ for (i = 0; i < row_width; i++)
+ {
+ png_uint_16 red, green, blue, w;
+
+ red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
+ green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
+ blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
+
+ if (red == green && red == blue)
+ w = red;
+
+ else
+ {
+ png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff)
+ >> png_ptr->gamma_shift][red>>8];
+ png_uint_16 green_1 =
+ png_ptr->gamma_16_to_1[(green&0xff) >>
+ png_ptr->gamma_shift][green>>8];
+ png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff)
+ >> png_ptr->gamma_shift][blue>>8];
+ png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1
+ + bc*blue_1)>>15);
+ w = png_ptr->gamma_16_from_1[(gray16&0xff) >>
+ png_ptr->gamma_shift][gray16 >> 8];
+ rgb_error |= 1;
+ }
+
+ *(dp++) = (png_byte)((w>>8) & 0xff);
+ *(dp++) = (png_byte)(w & 0xff);
+ }
+ }
+ else
+#endif
+ {
+ png_bytep sp = row;
+ png_bytep dp = row;
+ for (i = 0; i < row_width; i++)
+ {
+ png_uint_16 red, green, blue, gray16;
+
+ red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
+ green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
+ blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
+
+ if (red != green || red != blue)
+ rgb_error |= 1;
+
+ gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15);
+ *(dp++) = (png_byte)((gray16>>8) & 0xff);
+ *(dp++) = (png_byte)(gray16 & 0xff);
+ }
+ }
+ }
+ }
+ if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ {
+ if (row_info->bit_depth == 8)
+ {
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+ if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL)
+ {
+ png_bytep sp = row;
+ png_bytep dp = row;
+ for (i = 0; i < row_width; i++)
+ {
+ png_byte red = png_ptr->gamma_to_1[*(sp++)];
+ png_byte green = png_ptr->gamma_to_1[*(sp++)];
+ png_byte blue = png_ptr->gamma_to_1[*(sp++)];
+
+ if (red != green || red != blue)
+ rgb_error |= 1;
+
+ *(dp++) = png_ptr->gamma_from_1
+ [(rc*red + gc*green + bc*blue)>>15];
+
+ *(dp++) = *(sp++); /* alpha */
+ }
+ }
+ else
+#endif
+ {
+ png_bytep sp = row;
+ png_bytep dp = row;
+ for (i = 0; i < row_width; i++)
+ {
+ png_byte red = *(sp++);
+ png_byte green = *(sp++);
+ png_byte blue = *(sp++);
+ if (red != green || red != blue)
+ rgb_error |= 1;
+
+ *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15);
+ *(dp++) = *(sp++); /* alpha */
+ }
+ }
+ }
+ else /* RGBA bit_depth == 16 */
+ {
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+ if (png_ptr->gamma_16_to_1 != NULL &&
+ png_ptr->gamma_16_from_1 != NULL)
+ {
+ png_bytep sp = row;
+ png_bytep dp = row;
+ for (i = 0; i < row_width; i++)
+ {
+ png_uint_16 red, green, blue, w;
+
+ red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
+ green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
+ blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2;
+
+ if (red == green && red == blue)
+ w = red;
+
+ else
+ {
+ png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >>
+ png_ptr->gamma_shift][red>>8];
+
+ png_uint_16 green_1 =
+ png_ptr->gamma_16_to_1[(green&0xff) >>
+ png_ptr->gamma_shift][green>>8];
+
+ png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >>
+ png_ptr->gamma_shift][blue>>8];
+
+ png_uint_16 gray16 = (png_uint_16)((rc * red_1
+ + gc * green_1 + bc * blue_1)>>15);
+
+ w = png_ptr->gamma_16_from_1[(gray16&0xff) >>
+ png_ptr->gamma_shift][gray16 >> 8];
+
+ rgb_error |= 1;
+ }
+
+ *(dp++) = (png_byte)((w>>8) & 0xff);
+ *(dp++) = (png_byte)(w & 0xff);
+ *(dp++) = *(sp++); /* alpha */
+ *(dp++) = *(sp++);
+ }
+ }
+ else
+#endif
+ {
+ png_bytep sp = row;
+ png_bytep dp = row;
+ for (i = 0; i < row_width; i++)
+ {
+ png_uint_16 red, green, blue, gray16;
+ red = (png_uint_16)((*(sp)<<8) | *(sp + 1)); sp += 2;
+ green = (png_uint_16)((*(sp)<<8) | *(sp + 1)); sp += 2;
+ blue = (png_uint_16)((*(sp)<<8) | *(sp + 1)); sp += 2;
+
+ if (red != green || red != blue)
+ rgb_error |= 1;
+
+ gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15);
+ *(dp++) = (png_byte)((gray16>>8) & 0xff);
+ *(dp++) = (png_byte)(gray16 & 0xff);
+ *(dp++) = *(sp++); /* alpha */
+ *(dp++) = *(sp++);
+ }
+ }
+ }
+ }
+ row_info->channels -= 2;
+ row_info->color_type = (png_byte)(row_info->color_type &
+ ~PNG_COLOR_MASK_COLOR);
+ row_info->pixel_depth = (png_byte)(row_info->channels *
+ row_info->bit_depth);
+ row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
+ }
+ return rgb_error;
+}
+#endif
+
+/* Build a grayscale palette. Palette is assumed to be 1 << bit_depth
+ * large of png_color. This lets grayscale images be treated as
+ * paletted. Most useful for gamma correction and simplification
+ * of code.
+ */
+void PNGAPI
+png_build_grayscale_palette(int bit_depth, png_colorp palette)
+{
+ int num_palette;
+ int color_inc;
+ int i;
+ int v;
+
+ png_debug(1, "in png_do_build_grayscale_palette");
+
+ if (palette == NULL)
+ return;
+
+ switch (bit_depth)
+ {
+ case 1:
+ num_palette = 2;
+ color_inc = 0xff;
+ break;
+
+ case 2:
+ num_palette = 4;
+ color_inc = 0x55;
+ break;
+
+ case 4:
+ num_palette = 16;
+ color_inc = 0x11;
+ break;
+
+ case 8:
+ num_palette = 256;
+ color_inc = 1;
+ break;
+
+ default:
+ num_palette = 0;
+ color_inc = 0;
+ break;
+ }
+
+ for (i = 0, v = 0; i < num_palette; i++, v += color_inc)
+ {
+ palette[i].red = (png_byte)v;
+ palette[i].green = (png_byte)v;
+ palette[i].blue = (png_byte)v;
+ }
+}
+
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+/* Replace any alpha or transparency with the supplied background color.
+ * "background" is already in the screen gamma, while "background_1" is
+ * at a gamma of 1.0. Paletted files have already been taken care of.
+ */
+void /* PRIVATE */
+png_do_background(png_row_infop row_info, png_bytep row,
+ png_const_color_16p trans_color, png_const_color_16p background
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ , png_const_color_16p background_1, png_const_bytep gamma_table,
+ png_const_bytep gamma_from_1, png_const_bytep gamma_to_1,
+ png_const_uint_16pp gamma_16, png_const_uint_16pp gamma_16_from_1,
+ png_const_uint_16pp gamma_16_to_1, int gamma_shift
+#endif
+ )
+{
+ png_bytep sp, dp;
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+ int shift;
+
+ png_debug(1, "in png_do_background");
+
+ if (background != NULL &&
+ (!(row_info->color_type & PNG_COLOR_MASK_ALPHA) ||
+ (row_info->color_type != PNG_COLOR_TYPE_PALETTE && trans_color)))
+ {
+ switch (row_info->color_type)
+ {
+ case PNG_COLOR_TYPE_GRAY:
+ {
+ switch (row_info->bit_depth)
+ {
+ case 1:
+ {
+ sp = row;
+ shift = 7;
+ for (i = 0; i < row_width; i++)
+ {
+ if ((png_uint_16)((*sp >> shift) & 0x01)
+ == trans_color->gray)
+ {
+ *sp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff);
+ *sp |= (png_byte)(background->gray << shift);
+ }
+
+ if (!shift)
+ {
+ shift = 7;
+ sp++;
+ }
+
+ else
+ shift--;
+ }
+ break;
+ }
+
+ case 2:
+ {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ if (gamma_table != NULL)
+ {
+ sp = row;
+ shift = 6;
+ for (i = 0; i < row_width; i++)
+ {
+ if ((png_uint_16)((*sp >> shift) & 0x03)
+ == trans_color->gray)
+ {
+ *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+ *sp |= (png_byte)(background->gray << shift);
+ }
+
+ else
+ {
+ png_byte p = (png_byte)((*sp >> shift) & 0x03);
+ png_byte g = (png_byte)((gamma_table [p | (p << 2) |
+ (p << 4) | (p << 6)] >> 6) & 0x03);
+ *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+ *sp |= (png_byte)(g << shift);
+ }
+
+ if (!shift)
+ {
+ shift = 6;
+ sp++;
+ }
+
+ else
+ shift -= 2;
+ }
+ }
+
+ else
+#endif
+ {
+ sp = row;
+ shift = 6;
+ for (i = 0; i < row_width; i++)
+ {
+ if ((png_uint_16)((*sp >> shift) & 0x03)
+ == trans_color->gray)
+ {
+ *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+ *sp |= (png_byte)(background->gray << shift);
+ }
+
+ if (!shift)
+ {
+ shift = 6;
+ sp++;
+ }
+
+ else
+ shift -= 2;
+ }
+ }
+ break;
+ }
+
+ case 4:
+ {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ if (gamma_table != NULL)
+ {
+ sp = row;
+ shift = 4;
+ for (i = 0; i < row_width; i++)
+ {
+ if ((png_uint_16)((*sp >> shift) & 0x0f)
+ == trans_color->gray)
+ {
+ *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+ *sp |= (png_byte)(background->gray << shift);
+ }
+
+ else
+ {
+ png_byte p = (png_byte)((*sp >> shift) & 0x0f);
+ png_byte g = (png_byte)((gamma_table[p |
+ (p << 4)] >> 4) & 0x0f);
+ *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+ *sp |= (png_byte)(g << shift);
+ }
+
+ if (!shift)
+ {
+ shift = 4;
+ sp++;
+ }
+
+ else
+ shift -= 4;
+ }
+ }
+
+ else
+#endif
+ {
+ sp = row;
+ shift = 4;
+ for (i = 0; i < row_width; i++)
+ {
+ if ((png_uint_16)((*sp >> shift) & 0x0f)
+ == trans_color->gray)
+ {
+ *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+ *sp |= (png_byte)(background->gray << shift);
+ }
+
+ if (!shift)
+ {
+ shift = 4;
+ sp++;
+ }
+
+ else
+ shift -= 4;
+ }
+ }
+ break;
+ }
+
+ case 8:
+ {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ if (gamma_table != NULL)
+ {
+ sp = row;
+ for (i = 0; i < row_width; i++, sp++)
+ {
+ if (*sp == trans_color->gray)
+ *sp = (png_byte)background->gray;
+
+ else
+ *sp = gamma_table[*sp];
+ }
+ }
+ else
+#endif
+ {
+ sp = row;
+ for (i = 0; i < row_width; i++, sp++)
+ {
+ if (*sp == trans_color->gray)
+ *sp = (png_byte)background->gray;
+ }
+ }
+ break;
+ }
+
+ case 16:
+ {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ if (gamma_16 != NULL)
+ {
+ sp = row;
+ for (i = 0; i < row_width; i++, sp += 2)
+ {
+ png_uint_16 v;
+
+ v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+
+ if (v == trans_color->gray)
+ {
+ /* Background is already in screen gamma */
+ *sp = (png_byte)((background->gray >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(background->gray & 0xff);
+ }
+
+ else
+ {
+ v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+ *sp = (png_byte)((v >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(v & 0xff);
+ }
+ }
+ }
+ else
+#endif
+ {
+ sp = row;
+ for (i = 0; i < row_width; i++, sp += 2)
+ {
+ png_uint_16 v;
+
+ v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+
+ if (v == trans_color->gray)
+ {
+ *sp = (png_byte)((background->gray >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(background->gray & 0xff);
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ case PNG_COLOR_TYPE_RGB:
+ {
+ if (row_info->bit_depth == 8)
+ {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ if (gamma_table != NULL)
+ {
+ sp = row;
+ for (i = 0; i < row_width; i++, sp += 3)
+ {
+ if (*sp == trans_color->red &&
+ *(sp + 1) == trans_color->green &&
+ *(sp + 2) == trans_color->blue)
+ {
+ *sp = (png_byte)background->red;
+ *(sp + 1) = (png_byte)background->green;
+ *(sp + 2) = (png_byte)background->blue;
+ }
+
+ else
+ {
+ *sp = gamma_table[*sp];
+ *(sp + 1) = gamma_table[*(sp + 1)];
+ *(sp + 2) = gamma_table[*(sp + 2)];
+ }
+ }
+ }
+ else
+#endif
+ {
+ sp = row;
+ for (i = 0; i < row_width; i++, sp += 3)
+ {
+ if (*sp == trans_color->red &&
+ *(sp + 1) == trans_color->green &&
+ *(sp + 2) == trans_color->blue)
+ {
+ *sp = (png_byte)background->red;
+ *(sp + 1) = (png_byte)background->green;
+ *(sp + 2) = (png_byte)background->blue;
+ }
+ }
+ }
+ }
+ else /* if (row_info->bit_depth == 16) */
+ {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ if (gamma_16 != NULL)
+ {
+ sp = row;
+ for (i = 0; i < row_width; i++, sp += 6)
+ {
+ png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+
+ png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
+ + *(sp + 3));
+
+ png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
+ + *(sp + 5));
+
+ if (r == trans_color->red && g == trans_color->green &&
+ b == trans_color->blue)
+ {
+ /* Background is already in screen gamma */
+ *sp = (png_byte)((background->red >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(background->red & 0xff);
+ *(sp + 2) = (png_byte)((background->green >> 8) & 0xff);
+ *(sp + 3) = (png_byte)(background->green & 0xff);
+ *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff);
+ *(sp + 5) = (png_byte)(background->blue & 0xff);
+ }
+
+ else
+ {
+ png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+ *sp = (png_byte)((v >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(v & 0xff);
+
+ v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];
+ *(sp + 2) = (png_byte)((v >> 8) & 0xff);
+ *(sp + 3) = (png_byte)(v & 0xff);
+
+ v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];
+ *(sp + 4) = (png_byte)((v >> 8) & 0xff);
+ *(sp + 5) = (png_byte)(v & 0xff);
+ }
+ }
+ }
+
+ else
+#endif
+ {
+ sp = row;
+ for (i = 0; i < row_width; i++, sp += 6)
+ {
+ png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+
+ png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
+ + *(sp + 3));
+
+ png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
+ + *(sp + 5));
+
+ if (r == trans_color->red && g == trans_color->green &&
+ b == trans_color->blue)
+ {
+ *sp = (png_byte)((background->red >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(background->red & 0xff);
+ *(sp + 2) = (png_byte)((background->green >> 8) & 0xff);
+ *(sp + 3) = (png_byte)(background->green & 0xff);
+ *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff);
+ *(sp + 5) = (png_byte)(background->blue & 0xff);
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ {
+ if (row_info->bit_depth == 8)
+ {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
+ gamma_table != NULL)
+ {
+ sp = row;
+ dp = row;
+ for (i = 0; i < row_width; i++, sp += 2, dp++)
+ {
+ png_uint_16 a = *(sp + 1);
+
+ if (a == 0xff)
+ *dp = gamma_table[*sp];
+
+ else if (a == 0)
+ {
+ /* Background is already in screen gamma */
+ *dp = (png_byte)background->gray;
+ }
+
+ else
+ {
+ png_byte v, w;
+
+ v = gamma_to_1[*sp];
+ png_composite(w, v, a, background_1->gray);
+ *dp = gamma_from_1[w];
+ }
+ }
+ }
+ else
+#endif
+ {
+ sp = row;
+ dp = row;
+ for (i = 0; i < row_width; i++, sp += 2, dp++)
+ {
+ png_byte a = *(sp + 1);
+
+ if (a == 0xff)
+ *dp = *sp;
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ else if (a == 0)
+ *dp = (png_byte)background->gray;
+
+ else
+ png_composite(*dp, *sp, a, background_1->gray);
+
+#else
+ *dp = (png_byte)background->gray;
+#endif
+ }
+ }
+ }
+ else /* if (png_ptr->bit_depth == 16) */
+ {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
+ gamma_16_to_1 != NULL)
+ {
+ sp = row;
+ dp = row;
+ for (i = 0; i < row_width; i++, sp += 4, dp += 2)
+ {
+ png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8)
+ + *(sp + 3));
+
+ if (a == (png_uint_16)0xffff)
+ {
+ png_uint_16 v;
+
+ v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+ *dp = (png_byte)((v >> 8) & 0xff);
+ *(dp + 1) = (png_byte)(v & 0xff);
+ }
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ else if (a == 0)
+#else
+ else
+#endif
+ {
+ /* Background is already in screen gamma */
+ *dp = (png_byte)((background->gray >> 8) & 0xff);
+ *(dp + 1) = (png_byte)(background->gray & 0xff);
+ }
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ else
+ {
+ png_uint_16 g, v, w;
+
+ g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
+ png_composite_16(v, g, a, background_1->gray);
+ w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8];
+ *dp = (png_byte)((w >> 8) & 0xff);
+ *(dp + 1) = (png_byte)(w & 0xff);
+ }
+#endif
+ }
+ }
+ else
+#endif
+ {
+ sp = row;
+ dp = row;
+ for (i = 0; i < row_width; i++, sp += 4, dp += 2)
+ {
+ png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8)
+ + *(sp + 3));
+
+ if (a == (png_uint_16)0xffff)
+ png_memcpy(dp, sp, 2);
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ else if (a == 0)
+#else
+ else
+#endif
+ {
+ *dp = (png_byte)((background->gray >> 8) & 0xff);
+ *(dp + 1) = (png_byte)(background->gray & 0xff);
+ }
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ else
+ {
+ png_uint_16 g, v;
+
+ g = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+ png_composite_16(v, g, a, background_1->gray);
+ *dp = (png_byte)((v >> 8) & 0xff);
+ *(dp + 1) = (png_byte)(v & 0xff);
+ }
+#endif
+ }
+ }
+ }
+ break;
+ }
+
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ {
+ if (row_info->bit_depth == 8)
+ {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
+ gamma_table != NULL)
+ {
+ sp = row;
+ dp = row;
+ for (i = 0; i < row_width; i++, sp += 4, dp += 3)
+ {
+ png_byte a = *(sp + 3);
+
+ if (a == 0xff)
+ {
+ *dp = gamma_table[*sp];
+ *(dp + 1) = gamma_table[*(sp + 1)];
+ *(dp + 2) = gamma_table[*(sp + 2)];
+ }
+
+ else if (a == 0)
+ {
+ /* Background is already in screen gamma */
+ *dp = (png_byte)background->red;
+ *(dp + 1) = (png_byte)background->green;
+ *(dp + 2) = (png_byte)background->blue;
+ }
+
+ else
+ {
+ png_byte v, w;
+
+ v = gamma_to_1[*sp];
+ png_composite(w, v, a, background_1->red);
+ *dp = gamma_from_1[w];
+
+ v = gamma_to_1[*(sp + 1)];
+ png_composite(w, v, a, background_1->green);
+ *(dp + 1) = gamma_from_1[w];
+
+ v = gamma_to_1[*(sp + 2)];
+ png_composite(w, v, a, background_1->blue);
+ *(dp + 2) = gamma_from_1[w];
+ }
+ }
+ }
+ else
+#endif
+ {
+ sp = row;
+ dp = row;
+ for (i = 0; i < row_width; i++, sp += 4, dp += 3)
+ {
+ png_byte a = *(sp + 3);
+
+ if (a == 0xff)
+ {
+ *dp = *sp;
+ *(dp + 1) = *(sp + 1);
+ *(dp + 2) = *(sp + 2);
+ }
+
+ else if (a == 0)
+ {
+ *dp = (png_byte)background->red;
+ *(dp + 1) = (png_byte)background->green;
+ *(dp + 2) = (png_byte)background->blue;
+ }
+
+ else
+ {
+ png_composite(*dp, *sp, a, background->red);
+
+ png_composite(*(dp + 1), *(sp + 1), a,
+ background->green);
+
+ png_composite(*(dp + 2), *(sp + 2), a,
+ background->blue);
+ }
+ }
+ }
+ }
+ else /* if (row_info->bit_depth == 16) */
+ {
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
+ gamma_16_to_1 != NULL)
+ {
+ sp = row;
+ dp = row;
+ for (i = 0; i < row_width; i++, sp += 8, dp += 6)
+ {
+ png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
+ << 8) + (png_uint_16)(*(sp + 7)));
+
+ if (a == (png_uint_16)0xffff)
+ {
+ png_uint_16 v;
+
+ v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+ *dp = (png_byte)((v >> 8) & 0xff);
+ *(dp + 1) = (png_byte)(v & 0xff);
+
+ v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];
+ *(dp + 2) = (png_byte)((v >> 8) & 0xff);
+ *(dp + 3) = (png_byte)(v & 0xff);
+
+ v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];
+ *(dp + 4) = (png_byte)((v >> 8) & 0xff);
+ *(dp + 5) = (png_byte)(v & 0xff);
+ }
+
+ else if (a == 0)
+ {
+ /* Background is already in screen gamma */
+ *dp = (png_byte)((background->red >> 8) & 0xff);
+ *(dp + 1) = (png_byte)(background->red & 0xff);
+ *(dp + 2) = (png_byte)((background->green >> 8) & 0xff);
+ *(dp + 3) = (png_byte)(background->green & 0xff);
+ *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff);
+ *(dp + 5) = (png_byte)(background->blue & 0xff);
+ }
+
+ else
+ {
+ png_uint_16 v, w, x;
+
+ v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
+ png_composite_16(w, v, a, background_1->red);
+
+ x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8];
+ *dp = (png_byte)((x >> 8) & 0xff);
+ *(dp + 1) = (png_byte)(x & 0xff);
+
+ v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)];
+ png_composite_16(w, v, a, background_1->green);
+
+ x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8];
+ *(dp + 2) = (png_byte)((x >> 8) & 0xff);
+ *(dp + 3) = (png_byte)(x & 0xff);
+
+ v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)];
+ png_composite_16(w, v, a, background_1->blue);
+
+ x = gamma_16_from_1[(w & 0xff) >> gamma_shift][w >> 8];
+ *(dp + 4) = (png_byte)((x >> 8) & 0xff);
+ *(dp + 5) = (png_byte)(x & 0xff);
+ }
+ }
+ }
+
+ else
+#endif
+ {
+ sp = row;
+ dp = row;
+ for (i = 0; i < row_width; i++, sp += 8, dp += 6)
+ {
+ png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
+ << 8) + (png_uint_16)(*(sp + 7)));
+
+ if (a == (png_uint_16)0xffff)
+ {
+ png_memcpy(dp, sp, 6);
+ }
+
+ else if (a == 0)
+ {
+ *dp = (png_byte)((background->red >> 8) & 0xff);
+ *(dp + 1) = (png_byte)(background->red & 0xff);
+ *(dp + 2) = (png_byte)((background->green >> 8) & 0xff);
+ *(dp + 3) = (png_byte)(background->green & 0xff);
+ *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff);
+ *(dp + 5) = (png_byte)(background->blue & 0xff);
+ }
+
+ else
+ {
+ png_uint_16 v;
+
+ png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+ png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
+ + *(sp + 3));
+ png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
+ + *(sp + 5));
+
+ png_composite_16(v, r, a, background->red);
+ *dp = (png_byte)((v >> 8) & 0xff);
+ *(dp + 1) = (png_byte)(v & 0xff);
+
+ png_composite_16(v, g, a, background->green);
+ *(dp + 2) = (png_byte)((v >> 8) & 0xff);
+ *(dp + 3) = (png_byte)(v & 0xff);
+
+ png_composite_16(v, b, a, background->blue);
+ *(dp + 4) = (png_byte)((v >> 8) & 0xff);
+ *(dp + 5) = (png_byte)(v & 0xff);
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
+ {
+ row_info->color_type = (png_byte)(row_info->color_type &
+ ~PNG_COLOR_MASK_ALPHA);
+ row_info->channels--;
+ row_info->pixel_depth = (png_byte)(row_info->channels *
+ row_info->bit_depth);
+ row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
+ }
+ }
+}
+#endif
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+/* Gamma correct the image, avoiding the alpha channel. Make sure
+ * you do this after you deal with the transparency issue on grayscale
+ * or RGB images. If your bit depth is 8, use gamma_table, if it
+ * is 16, use gamma_16_table and gamma_shift. Build these with
+ * build_gamma_table().
+ */
+void /* PRIVATE */
+png_do_gamma(png_row_infop row_info, png_bytep row,
+ png_const_bytep gamma_table, png_const_uint_16pp gamma_16_table,
+ int gamma_shift)
+{
+ png_bytep sp;
+ png_uint_32 i;
+ png_uint_32 row_width=row_info->width;
+
+ png_debug(1, "in png_do_gamma");
+
+ if (((row_info->bit_depth <= 8 && gamma_table != NULL) ||
+ (row_info->bit_depth == 16 && gamma_16_table != NULL)))
+ {
+ switch (row_info->color_type)
+ {
+ case PNG_COLOR_TYPE_RGB:
+ {
+ if (row_info->bit_depth == 8)
+ {
+ sp = row;
+ for (i = 0; i < row_width; i++)
+ {
+ *sp = gamma_table[*sp];
+ sp++;
+ *sp = gamma_table[*sp];
+ sp++;
+ *sp = gamma_table[*sp];
+ sp++;
+ }
+ }
+
+ else /* if (row_info->bit_depth == 16) */
+ {
+ sp = row;
+ for (i = 0; i < row_width; i++)
+ {
+ png_uint_16 v;
+
+ v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+ *sp = (png_byte)((v >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(v & 0xff);
+ sp += 2;
+
+ v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+ *sp = (png_byte)((v >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(v & 0xff);
+ sp += 2;
+
+ v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+ *sp = (png_byte)((v >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(v & 0xff);
+ sp += 2;
+ }
+ }
+ break;
+ }
+
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ {
+ if (row_info->bit_depth == 8)
+ {
+ sp = row;
+ for (i = 0; i < row_width; i++)
+ {
+ *sp = gamma_table[*sp];
+ sp++;
+
+ *sp = gamma_table[*sp];
+ sp++;
+
+ *sp = gamma_table[*sp];
+ sp++;
+
+ sp++;
+ }
+ }
+
+ else /* if (row_info->bit_depth == 16) */
+ {
+ sp = row;
+ for (i = 0; i < row_width; i++)
+ {
+ png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+ *sp = (png_byte)((v >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(v & 0xff);
+ sp += 2;
+
+ v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+ *sp = (png_byte)((v >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(v & 0xff);
+ sp += 2;
+
+ v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+ *sp = (png_byte)((v >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(v & 0xff);
+ sp += 4;
+ }
+ }
+ break;
+ }
+
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ {
+ if (row_info->bit_depth == 8)
+ {
+ sp = row;
+ for (i = 0; i < row_width; i++)
+ {
+ *sp = gamma_table[*sp];
+ sp += 2;
+ }
+ }
+
+ else /* if (row_info->bit_depth == 16) */
+ {
+ sp = row;
+ for (i = 0; i < row_width; i++)
+ {
+ png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+ *sp = (png_byte)((v >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(v & 0xff);
+ sp += 4;
+ }
+ }
+ break;
+ }
+
+ case PNG_COLOR_TYPE_GRAY:
+ {
+ if (row_info->bit_depth == 2)
+ {
+ sp = row;
+ for (i = 0; i < row_width; i += 4)
+ {
+ int a = *sp & 0xc0;
+ int b = *sp & 0x30;
+ int c = *sp & 0x0c;
+ int d = *sp & 0x03;
+
+ *sp = (png_byte)(
+ ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)]) ) & 0xc0)|
+ ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)|
+ ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)|
+ ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) ));
+ sp++;
+ }
+ }
+
+ if (row_info->bit_depth == 4)
+ {
+ sp = row;
+ for (i = 0; i < row_width; i += 2)
+ {
+ int msb = *sp & 0xf0;
+ int lsb = *sp & 0x0f;
+
+ *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0)
+ | (((int)gamma_table[(lsb << 4) | lsb]) >> 4));
+ sp++;
+ }
+ }
+
+ else if (row_info->bit_depth == 8)
+ {
+ sp = row;
+ for (i = 0; i < row_width; i++)
+ {
+ *sp = gamma_table[*sp];
+ sp++;
+ }
+ }
+
+ else if (row_info->bit_depth == 16)
+ {
+ sp = row;
+ for (i = 0; i < row_width; i++)
+ {
+ png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+ *sp = (png_byte)((v >> 8) & 0xff);
+ *(sp + 1) = (png_byte)(v & 0xff);
+ sp += 2;
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+}
+#endif
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+/* Expands a palette row to an RGB or RGBA row depending
+ * upon whether you supply trans and num_trans.
+ */
+void /* PRIVATE */
+png_do_expand_palette(png_row_infop row_info, png_bytep row,
+ png_const_colorp palette, png_const_bytep trans_alpha, int num_trans)
+{
+ int shift, value;
+ png_bytep sp, dp;
+ png_uint_32 i;
+ png_uint_32 row_width=row_info->width;
+
+ png_debug(1, "in png_do_expand_palette");
+
+ if (row_info->color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ if (row_info->bit_depth < 8)
+ {
+ switch (row_info->bit_depth)
+ {
+ case 1:
+ {
+ sp = row + (png_size_t)((row_width - 1) >> 3);
+ dp = row + (png_size_t)row_width - 1;
+ shift = 7 - (int)((row_width + 7) & 0x07);
+ for (i = 0; i < row_width; i++)
+ {
+ if ((*sp >> shift) & 0x01)
+ *dp = 1;
+
+ else
+ *dp = 0;
+
+ if (shift == 7)
+ {
+ shift = 0;
+ sp--;
+ }
+
+ else
+ shift++;
+
+ dp--;
+ }
+ break;
+ }
+
+ case 2:
+ {
+ sp = row + (png_size_t)((row_width - 1) >> 2);
+ dp = row + (png_size_t)row_width - 1;
+ shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
+ for (i = 0; i < row_width; i++)
+ {
+ value = (*sp >> shift) & 0x03;
+ *dp = (png_byte)value;
+ if (shift == 6)
+ {
+ shift = 0;
+ sp--;
+ }
+
+ else
+ shift += 2;
+
+ dp--;
+ }
+ break;
+ }
+
+ case 4:
+ {
+ sp = row + (png_size_t)((row_width - 1) >> 1);
+ dp = row + (png_size_t)row_width - 1;
+ shift = (int)((row_width & 0x01) << 2);
+ for (i = 0; i < row_width; i++)
+ {
+ value = (*sp >> shift) & 0x0f;
+ *dp = (png_byte)value;
+ if (shift == 4)
+ {
+ shift = 0;
+ sp--;
+ }
+
+ else
+ shift += 4;
+
+ dp--;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ row_info->bit_depth = 8;
+ row_info->pixel_depth = 8;
+ row_info->rowbytes = row_width;
+ }
+
+ if (row_info->bit_depth == 8)
+ {
+ {
+ if (trans_alpha != NULL)
+ {
+ sp = row + (png_size_t)row_width - 1;
+ dp = row + (png_size_t)(row_width << 2) - 1;
+
+ for (i = 0; i < row_width; i++)
+ {
+ if ((int)(*sp) >= num_trans)
+ *dp-- = 0xff;
+
+ else
+ *dp-- = trans_alpha[*sp];
+
+ *dp-- = palette[*sp].blue;
+ *dp-- = palette[*sp].green;
+ *dp-- = palette[*sp].red;
+ sp--;
+ }
+ row_info->bit_depth = 8;
+ row_info->pixel_depth = 32;
+ row_info->rowbytes = row_width * 4;
+ row_info->color_type = 6;
+ row_info->channels = 4;
+ }
+
+ else
+ {
+ sp = row + (png_size_t)row_width - 1;
+ dp = row + (png_size_t)(row_width * 3) - 1;
+
+ for (i = 0; i < row_width; i++)
+ {
+ *dp-- = palette[*sp].blue;
+ *dp-- = palette[*sp].green;
+ *dp-- = palette[*sp].red;
+ sp--;
+ }
+
+ row_info->bit_depth = 8;
+ row_info->pixel_depth = 24;
+ row_info->rowbytes = row_width * 3;
+ row_info->color_type = 2;
+ row_info->channels = 3;
+ }
+ }
+ }
+ }
+}
+
+/* If the bit depth < 8, it is expanded to 8. Also, if the already
+ * expanded transparency value is supplied, an alpha channel is built.
+ */
+void /* PRIVATE */
+png_do_expand(png_row_infop row_info, png_bytep row,
+ png_const_color_16p trans_value)
+{
+ int shift, value;
+ png_bytep sp, dp;
+ png_uint_32 i;
+ png_uint_32 row_width=row_info->width;
+
+ png_debug(1, "in png_do_expand");
+
+ {
+ if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
+ {
+ png_uint_16 gray = (png_uint_16)(trans_value ? trans_value->gray : 0);
+
+ if (row_info->bit_depth < 8)
+ {
+ switch (row_info->bit_depth)
+ {
+ case 1:
+ {
+ gray = (png_uint_16)((gray & 0x01) * 0xff);
+ sp = row + (png_size_t)((row_width - 1) >> 3);
+ dp = row + (png_size_t)row_width - 1;
+ shift = 7 - (int)((row_width + 7) & 0x07);
+ for (i = 0; i < row_width; i++)
+ {
+ if ((*sp >> shift) & 0x01)
+ *dp = 0xff;
+
+ else
+ *dp = 0;
+
+ if (shift == 7)
+ {
+ shift = 0;
+ sp--;
+ }
+
+ else
+ shift++;
+
+ dp--;
+ }
+ break;
+ }
+
+ case 2:
+ {
+ gray = (png_uint_16)((gray & 0x03) * 0x55);
+ sp = row + (png_size_t)((row_width - 1) >> 2);
+ dp = row + (png_size_t)row_width - 1;
+ shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
+ for (i = 0; i < row_width; i++)
+ {
+ value = (*sp >> shift) & 0x03;
+ *dp = (png_byte)(value | (value << 2) | (value << 4) |
+ (value << 6));
+ if (shift == 6)
+ {
+ shift = 0;
+ sp--;
+ }
+
+ else
+ shift += 2;
+
+ dp--;
+ }
+ break;
+ }
+
+ case 4:
+ {
+ gray = (png_uint_16)((gray & 0x0f) * 0x11);
+ sp = row + (png_size_t)((row_width - 1) >> 1);
+ dp = row + (png_size_t)row_width - 1;
+ shift = (int)((1 - ((row_width + 1) & 0x01)) << 2);
+ for (i = 0; i < row_width; i++)
+ {
+ value = (*sp >> shift) & 0x0f;
+ *dp = (png_byte)(value | (value << 4));
+ if (shift == 4)
+ {
+ shift = 0;
+ sp--;
+ }
+
+ else
+ shift = 4;
+
+ dp--;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ row_info->bit_depth = 8;
+ row_info->pixel_depth = 8;
+ row_info->rowbytes = row_width;
+ }
+
+ if (trans_value != NULL)
+ {
+ if (row_info->bit_depth == 8)
+ {
+ gray = gray & 0xff;
+ sp = row + (png_size_t)row_width - 1;
+ dp = row + (png_size_t)(row_width << 1) - 1;
+
+ for (i = 0; i < row_width; i++)
+ {
+ if (*sp == gray)
+ *dp-- = 0;
+
+ else
+ *dp-- = 0xff;
+
+ *dp-- = *sp--;
+ }
+ }
+
+ else if (row_info->bit_depth == 16)
+ {
+ png_byte gray_high = (png_byte)((gray >> 8) & 0xff);
+ png_byte gray_low = (png_byte)(gray & 0xff);
+ sp = row + row_info->rowbytes - 1;
+ dp = row + (row_info->rowbytes << 1) - 1;
+ for (i = 0; i < row_width; i++)
+ {
+ if (*(sp - 1) == gray_high && *(sp) == gray_low)
+ {
+ *dp-- = 0;
+ *dp-- = 0;
+ }
+
+ else
+ {
+ *dp-- = 0xff;
+ *dp-- = 0xff;
+ }
+
+ *dp-- = *sp--;
+ *dp-- = *sp--;
+ }
+ }
+
+ row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+ row_info->channels = 2;
+ row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1);
+ row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
+ row_width);
+ }
+ }
+ else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_value)
+ {
+ if (row_info->bit_depth == 8)
+ {
+ png_byte red = (png_byte)(trans_value->red & 0xff);
+ png_byte green = (png_byte)(trans_value->green & 0xff);
+ png_byte blue = (png_byte)(trans_value->blue & 0xff);
+ sp = row + (png_size_t)row_info->rowbytes - 1;
+ dp = row + (png_size_t)(row_width << 2) - 1;
+ for (i = 0; i < row_width; i++)
+ {
+ if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue)
+ *dp-- = 0;
+
+ else
+ *dp-- = 0xff;
+
+ *dp-- = *sp--;
+ *dp-- = *sp--;
+ *dp-- = *sp--;
+ }
+ }
+ else if (row_info->bit_depth == 16)
+ {
+ png_byte red_high = (png_byte)((trans_value->red >> 8) & 0xff);
+ png_byte green_high = (png_byte)((trans_value->green >> 8) & 0xff);
+ png_byte blue_high = (png_byte)((trans_value->blue >> 8) & 0xff);
+ png_byte red_low = (png_byte)(trans_value->red & 0xff);
+ png_byte green_low = (png_byte)(trans_value->green & 0xff);
+ png_byte blue_low = (png_byte)(trans_value->blue & 0xff);
+ sp = row + row_info->rowbytes - 1;
+ dp = row + (png_size_t)(row_width << 3) - 1;
+ for (i = 0; i < row_width; i++)
+ {
+ if (*(sp - 5) == red_high &&
+ *(sp - 4) == red_low &&
+ *(sp - 3) == green_high &&
+ *(sp - 2) == green_low &&
+ *(sp - 1) == blue_high &&
+ *(sp ) == blue_low)
+ {
+ *dp-- = 0;
+ *dp-- = 0;
+ }
+
+ else
+ {
+ *dp-- = 0xff;
+ *dp-- = 0xff;
+ }
+
+ *dp-- = *sp--;
+ *dp-- = *sp--;
+ *dp-- = *sp--;
+ *dp-- = *sp--;
+ *dp-- = *sp--;
+ *dp-- = *sp--;
+ }
+ }
+ row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ row_info->channels = 4;
+ row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2);
+ row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
+ }
+ }
+}
+#endif
+
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+/* If the bit depth is 8 and the colour type is not a palette type expand the
+ * whole row to 16 bits. Has no effect otherwise.
+ */
+void /* PRIVATE */
+png_do_expand_16(png_row_infop row_info, png_bytep row)
+{
+ if (row_info->bit_depth == 8 &&
+ row_info->color_type != PNG_COLOR_TYPE_PALETTE)
+ {
+ /* The row have a sequence of bytes containing [0..255] and we need
+ * to turn it into another row containing [0..65535], to do this we
+ * calculate:
+ *
+ * (input / 255) * 65535
+ *
+ * Which happens to be exactly input * 257 and this can be achieved
+ * simply by byte replication in place (copying backwards).
+ */
+ png_byte *sp = row + row_info->rowbytes; /* source, last byte + 1 */
+ png_byte *dp = sp + row_info->rowbytes; /* destination, end + 1 */
+ while (dp > sp)
+ dp[-2] = dp[-1] = *--sp, dp -= 2;
+
+ row_info->rowbytes *= 2;
+ row_info->bit_depth = 16;
+ row_info->pixel_depth = (png_byte)(row_info->channels * 16);
+ }
+}
+#endif
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+void /* PRIVATE */
+png_do_quantize(png_row_infop row_info, png_bytep row,
+ png_const_bytep palette_lookup, png_const_bytep quantize_lookup)
+{
+ png_bytep sp, dp;
+ png_uint_32 i;
+ png_uint_32 row_width=row_info->width;
+
+ png_debug(1, "in png_do_quantize");
+
+ if (row_info->bit_depth == 8)
+ {
+ if (row_info->color_type == PNG_COLOR_TYPE_RGB && palette_lookup)
+ {
+ int r, g, b, p;
+ sp = row;
+ dp = row;
+ for (i = 0; i < row_width; i++)
+ {
+ r = *sp++;
+ g = *sp++;
+ b = *sp++;
+
+ /* This looks real messy, but the compiler will reduce
+ * it down to a reasonable formula. For example, with
+ * 5 bits per color, we get:
+ * p = (((r >> 3) & 0x1f) << 10) |
+ * (((g >> 3) & 0x1f) << 5) |
+ * ((b >> 3) & 0x1f);
+ */
+ p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) &
+ ((1 << PNG_QUANTIZE_RED_BITS) - 1)) <<
+ (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) |
+ (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) &
+ ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) <<
+ (PNG_QUANTIZE_BLUE_BITS)) |
+ ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) &
+ ((1 << PNG_QUANTIZE_BLUE_BITS) - 1));
+
+ *dp++ = palette_lookup[p];
+ }
+
+ row_info->color_type = PNG_COLOR_TYPE_PALETTE;
+ row_info->channels = 1;
+ row_info->pixel_depth = row_info->bit_depth;
+ row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
+ }
+
+ else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA &&
+ palette_lookup != NULL)
+ {
+ int r, g, b, p;
+ sp = row;
+ dp = row;
+ for (i = 0; i < row_width; i++)
+ {
+ r = *sp++;
+ g = *sp++;
+ b = *sp++;
+ sp++;
+
+ p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) &
+ ((1 << PNG_QUANTIZE_RED_BITS) - 1)) <<
+ (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) |
+ (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) &
+ ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) <<
+ (PNG_QUANTIZE_BLUE_BITS)) |
+ ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) &
+ ((1 << PNG_QUANTIZE_BLUE_BITS) - 1));
+
+ *dp++ = palette_lookup[p];
+ }
+
+ row_info->color_type = PNG_COLOR_TYPE_PALETTE;
+ row_info->channels = 1;
+ row_info->pixel_depth = row_info->bit_depth;
+ row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width);
+ }
+
+ else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE &&
+ quantize_lookup)
+ {
+ sp = row;
+
+ for (i = 0; i < row_width; i++, sp++)
+ {
+ *sp = quantize_lookup[*sp];
+ }
+ }
+ }
+}
+#endif /* PNG_READ_QUANTIZE_SUPPORTED */
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+/* Undoes intrapixel differencing */
+void /* PRIVATE */
+png_do_read_intrapixel(png_row_infop row_info, png_bytep row)
+{
+ png_debug(1, "in png_do_read_intrapixel");
+
+ if (
+ (row_info->color_type & PNG_COLOR_MASK_COLOR))
+ {
+ int bytes_per_pixel;
+ png_uint_32 row_width = row_info->width;
+
+ if (row_info->bit_depth == 8)
+ {
+ png_bytep rp;
+ png_uint_32 i;
+
+ if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+ bytes_per_pixel = 3;
+
+ else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ bytes_per_pixel = 4;
+
+ else
+ return;
+
+ for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
+ {
+ *(rp) = (png_byte)((256 + *rp + *(rp + 1)) & 0xff);
+ *(rp+2) = (png_byte)((256 + *(rp + 2) + *(rp + 1)) & 0xff);
+ }
+ }
+ else if (row_info->bit_depth == 16)
+ {
+ png_bytep rp;
+ png_uint_32 i;
+
+ if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+ bytes_per_pixel = 6;
+
+ else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ bytes_per_pixel = 8;
+
+ else
+ return;
+
+ for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
+ {
+ png_uint_32 s0 = (*(rp ) << 8) | *(rp + 1);
+ png_uint_32 s1 = (*(rp + 2) << 8) | *(rp + 3);
+ png_uint_32 s2 = (*(rp + 4) << 8) | *(rp + 5);
+ png_uint_32 red = (png_uint_32)((s0 + s1 + 65536L) & 0xffffL);
+ png_uint_32 blue = (png_uint_32)((s2 + s1 + 65536L) & 0xffffL);
+ *(rp ) = (png_byte)((red >> 8) & 0xff);
+ *(rp + 1) = (png_byte)(red & 0xff);
+ *(rp + 4) = (png_byte)((blue >> 8) & 0xff);
+ *(rp + 5) = (png_byte)(blue & 0xff);
+ }
+ }
+ }
+}
+#endif /* PNG_MNG_FEATURES_SUPPORTED */
+#endif /* PNG_READ_SUPPORTED */
diff --git a/libpng/pngrutil.c b/libpng/pngrutil.c
new file mode 100644
index 0000000..d476431
--- /dev/null
+++ b/libpng/pngrutil.c
@@ -0,0 +1,3632 @@
+
+/* pngrutil.c - utilities to read a PNG file
+ *
+ * Last changed in libpng 1.5.2 [March 31, 2011]
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * This file contains routines that are only called from within
+ * libpng itself during the course of reading an image.
+ */
+
+#include "pngpriv.h"
+
+#ifdef PNG_READ_SUPPORTED
+
+#define png_strtod(p,a,b) strtod(a,b)
+
+png_uint_32 PNGAPI
+png_get_uint_31(png_structp png_ptr, png_const_bytep buf)
+{
+ png_uint_32 uval = png_get_uint_32(buf);
+
+ if (uval > PNG_UINT_31_MAX)
+ png_error(png_ptr, "PNG unsigned integer out of range");
+
+ return (uval);
+}
+
+#if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_READ_cHRM_SUPPORTED)
+/* The following is a variation on the above for use with the fixed
+ * point values used for gAMA and cHRM. Instead of png_error it
+ * issues a warning and returns (-1) - an invalid value because both
+ * gAMA and cHRM use *unsigned* integers for fixed point values.
+ */
+#define PNG_FIXED_ERROR (-1)
+
+static png_fixed_point /* PRIVATE */
+png_get_fixed_point(png_structp png_ptr, png_const_bytep buf)
+{
+ png_uint_32 uval = png_get_uint_32(buf);
+
+ if (uval <= PNG_UINT_31_MAX)
+ return (png_fixed_point)uval; /* known to be in range */
+
+ /* The caller can turn off the warning by passing NULL. */
+ if (png_ptr != NULL)
+ png_warning(png_ptr, "PNG fixed point integer out of range");
+
+ return PNG_FIXED_ERROR;
+}
+#endif
+
+#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED
+/* NOTE: the read macros will obscure these definitions, so that if
+ * PNG_USE_READ_MACROS is set the library will not use them internally,
+ * but the APIs will still be available externally.
+ *
+ * The parentheses around "PNGAPI function_name" in the following three
+ * functions are necessary because they allow the macros to co-exist with
+ * these (unused but exported) functions.
+ */
+
+/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */
+png_uint_32 (PNGAPI
+png_get_uint_32)(png_const_bytep buf)
+{
+ png_uint_32 uval =
+ ((png_uint_32)(*(buf )) << 24) +
+ ((png_uint_32)(*(buf + 1)) << 16) +
+ ((png_uint_32)(*(buf + 2)) << 8) +
+ ((png_uint_32)(*(buf + 3)) ) ;
+
+ return uval;
+}
+
+/* Grab a signed 32-bit integer from a buffer in big-endian format. The
+ * data is stored in the PNG file in two's complement format and there
+ * is no guarantee that a 'png_int_32' is exactly 32 bits, therefore
+ * the following code does a two's complement to native conversion.
+ */
+png_int_32 (PNGAPI
+png_get_int_32)(png_const_bytep buf)
+{
+ png_uint_32 uval = png_get_uint_32(buf);
+ if ((uval & 0x80000000L) == 0) /* non-negative */
+ return uval;
+
+ uval = (uval ^ 0xffffffffL) + 1; /* 2's complement: -x = ~x+1 */
+ return -(png_int_32)uval;
+}
+
+/* Grab an unsigned 16-bit integer from a buffer in big-endian format. */
+png_uint_16 (PNGAPI
+png_get_uint_16)(png_const_bytep buf)
+{
+ /* ANSI-C requires an int value to accomodate at least 16 bits so this
+ * works and allows the compiler not to worry about possible narrowing
+ * on 32 bit systems. (Pre-ANSI systems did not make integers smaller
+ * than 16 bits either.)
+ */
+ unsigned int val =
+ ((unsigned int)(*buf) << 8) +
+ ((unsigned int)(*(buf + 1)));
+
+ return (png_uint_16)val;
+}
+
+#endif /* PNG_READ_INT_FUNCTIONS_SUPPORTED */
+
+/* Read and check the PNG file signature */
+void /* PRIVATE */
+png_read_sig(png_structp png_ptr, png_infop info_ptr)
+{
+ png_size_t num_checked, num_to_check;
+
+ /* Exit if the user application does not expect a signature. */
+ if (png_ptr->sig_bytes >= 8)
+ return;
+
+ num_checked = png_ptr->sig_bytes;
+ num_to_check = 8 - num_checked;
+
+#ifdef PNG_IO_STATE_SUPPORTED
+ png_ptr->io_state = PNG_IO_READING | PNG_IO_SIGNATURE;
+#endif
+
+ /* The signature must be serialized in a single I/O call. */
+ png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check);
+ png_ptr->sig_bytes = 8;
+
+ if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check))
+ {
+ if (num_checked < 4 &&
+ png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4))
+ png_error(png_ptr, "Not a PNG file");
+ else
+ png_error(png_ptr, "PNG file corrupted by ASCII conversion");
+ }
+ if (num_checked < 3)
+ png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;
+}
+
+/* Read the chunk header (length + type name).
+ * Put the type name into png_ptr->chunk_name, and return the length.
+ */
+png_uint_32 /* PRIVATE */
+png_read_chunk_header(png_structp png_ptr)
+{
+ png_byte buf[8];
+ png_uint_32 length;
+
+#ifdef PNG_IO_STATE_SUPPORTED
+ png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_HDR;
+#endif
+
+ /* Read the length and the chunk name.
+ * This must be performed in a single I/O call.
+ */
+ png_read_data(png_ptr, buf, 8);
+ length = png_get_uint_31(png_ptr, buf);
+
+ /* Put the chunk name into png_ptr->chunk_name. */
+ png_memcpy(png_ptr->chunk_name, buf + 4, 4);
+
+ png_debug2(0, "Reading %s chunk, length = %u",
+ png_ptr->chunk_name, length);
+
+ /* Reset the crc and run it over the chunk name. */
+ png_reset_crc(png_ptr);
+ png_calculate_crc(png_ptr, png_ptr->chunk_name, 4);
+
+ /* Check to see if chunk name is valid. */
+ png_check_chunk_name(png_ptr, png_ptr->chunk_name);
+
+#ifdef PNG_IO_STATE_SUPPORTED
+ png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA;
+#endif
+
+ return length;
+}
+
+/* Read data, and (optionally) run it through the CRC. */
+void /* PRIVATE */
+png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length)
+{
+ if (png_ptr == NULL)
+ return;
+
+ png_read_data(png_ptr, buf, length);
+ png_calculate_crc(png_ptr, buf, length);
+}
+
+/* Optionally skip data and then check the CRC. Depending on whether we
+ * are reading a ancillary or critical chunk, and how the program has set
+ * things up, we may calculate the CRC on the data and print a message.
+ * Returns '1' if there was a CRC error, '0' otherwise.
+ */
+int /* PRIVATE */
+png_crc_finish(png_structp png_ptr, png_uint_32 skip)
+{
+ png_size_t i;
+ png_size_t istop = png_ptr->zbuf_size;
+
+ for (i = (png_size_t)skip; i > istop; i -= istop)
+ {
+ png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
+ }
+
+ if (i)
+ {
+ png_crc_read(png_ptr, png_ptr->zbuf, i);
+ }
+
+ if (png_crc_error(png_ptr))
+ {
+ if (((png_ptr->chunk_name[0] & 0x20) && /* Ancillary */
+ !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) ||
+ (!(png_ptr->chunk_name[0] & 0x20) && /* Critical */
+ (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE)))
+ {
+ png_chunk_warning(png_ptr, "CRC error");
+ }
+
+ else
+ {
+ png_chunk_benign_error(png_ptr, "CRC error");
+ return (0);
+ }
+
+ return (1);
+ }
+
+ return (0);
+}
+
+/* Compare the CRC stored in the PNG file with that calculated by libpng from
+ * the data it has read thus far.
+ */
+int /* PRIVATE */
+png_crc_error(png_structp png_ptr)
+{
+ png_byte crc_bytes[4];
+ png_uint_32 crc;
+ int need_crc = 1;
+
+ if (png_ptr->chunk_name[0] & 0x20) /* ancillary */
+ {
+ if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
+ (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
+ need_crc = 0;
+ }
+
+ else /* critical */
+ {
+ if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE)
+ need_crc = 0;
+ }
+
+#ifdef PNG_IO_STATE_SUPPORTED
+ png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC;
+#endif
+
+ /* The chunk CRC must be serialized in a single I/O call. */
+ png_read_data(png_ptr, crc_bytes, 4);
+
+ if (need_crc)
+ {
+ crc = png_get_uint_32(crc_bytes);
+ return ((int)(crc != png_ptr->crc));
+ }
+
+ else
+ return (0);
+}
+
+#if defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) || \
+ defined(PNG_READ_iCCP_SUPPORTED)
+static png_size_t
+png_inflate(png_structp png_ptr, png_bytep data, png_size_t size,
+ png_bytep output, png_size_t output_size)
+{
+ png_size_t count = 0;
+
+ /* zlib can't necessarily handle more than 65535 bytes at once (i.e. it can't
+ * even necessarily handle 65536 bytes) because the type uInt is "16 bits or
+ * more". Consequently it is necessary to chunk the input to zlib. This
+ * code uses ZLIB_IO_MAX, from pngpriv.h, as the maximum (the maximum value
+ * that can be stored in a uInt.) It is possible to set ZLIB_IO_MAX to a
+ * lower value in pngpriv.h and this may sometimes have a performance
+ * advantage, because it forces access of the input data to be separated from
+ * at least some of the use by some period of time.
+ */
+ png_ptr->zstream.next_in = data;
+ /* avail_in is set below from 'size' */
+ png_ptr->zstream.avail_in = 0;
+
+ while (1)
+ {
+ int ret, avail;
+
+ /* The setting of 'avail_in' used to be outside the loop, by setting it
+ * inside it is possible to chunk the input to zlib and simply rely on
+ * zlib to advance the 'next_in' pointer. This allows arbitrary amounts o
+ * data to be passed through zlib at the unavoidable cost of requiring a
+ * window save (memcpy of up to 32768 output bytes) every ZLIB_IO_MAX
+ * input bytes.
+ */
+ if (png_ptr->zstream.avail_in == 0 && size > 0)
+ {
+ if (size <= ZLIB_IO_MAX)
+ {
+ /* The value is less than ZLIB_IO_MAX so the cast is safe: */
+ png_ptr->zstream.avail_in = (uInt)size;
+ size = 0;
+ }
+
+ else
+ {
+ png_ptr->zstream.avail_in = ZLIB_IO_MAX;
+ size -= ZLIB_IO_MAX;
+ }
+ }
+
+ /* Reset the output buffer each time round - we empty it
+ * after every inflate call.
+ */
+ png_ptr->zstream.next_out = png_ptr->zbuf;
+ png_ptr->zstream.avail_out = png_ptr->zbuf_size;
+
+ ret = inflate(&png_ptr->zstream, Z_NO_FLUSH);
+ avail = png_ptr->zbuf_size - png_ptr->zstream.avail_out;
+
+ /* First copy/count any new output - but only if we didn't
+ * get an error code.
+ */
+ if ((ret == Z_OK || ret == Z_STREAM_END) && avail > 0)
+ {
+ png_size_t space = avail; /* > 0, see above */
+
+ if (output != 0 && output_size > count)
+ {
+ png_size_t copy = output_size - count;
+
+ if (space < copy)
+ copy = space;
+
+ png_memcpy(output + count, png_ptr->zbuf, copy);
+ }
+ count += space;
+ }
+
+ if (ret == Z_OK)
+ continue;
+
+ /* Termination conditions - always reset the zstream, it
+ * must be left in inflateInit state.
+ */
+ png_ptr->zstream.avail_in = 0;
+ inflateReset(&png_ptr->zstream);
+
+ if (ret == Z_STREAM_END)
+ return count; /* NOTE: may be zero. */
+
+ /* Now handle the error codes - the API always returns 0
+ * and the error message is dumped into the uncompressed
+ * buffer if available.
+ */
+ {
+ PNG_CONST char *msg;
+#ifdef PNG_CONSOLE_IO_SUPPORTED
+ char umsg[52];
+#endif
+ if (png_ptr->zstream.msg != 0)
+ msg = png_ptr->zstream.msg;
+
+ else
+ {
+#ifdef PNG_CONSOLE_IO_SUPPORTED
+ switch (ret)
+ {
+ case Z_BUF_ERROR:
+ msg = "Buffer error in compressed datastream in %s chunk";
+ break;
+
+ case Z_DATA_ERROR:
+ msg = "Data error in compressed datastream in %s chunk";
+ break;
+
+ default:
+ msg = "Incomplete compressed datastream in %s chunk";
+ break;
+ }
+
+ png_snprintf(umsg, sizeof umsg, msg, png_ptr->chunk_name);
+ msg = umsg;
+#else
+ msg = "Damaged compressed datastream in chunk other than IDAT";
+#endif
+ }
+
+ png_warning(png_ptr, msg);
+ }
+
+ /* 0 means an error - notice that this code simply ignores
+ * zero length compressed chunks as a result.
+ */
+ return 0;
+ }
+}
+
+/*
+ * Decompress trailing data in a chunk. The assumption is that chunkdata
+ * points at an allocated area holding the contents of a chunk with a
+ * trailing compressed part. What we get back is an allocated area
+ * holding the original prefix part and an uncompressed version of the
+ * trailing part (the malloc area passed in is freed).
+ */
+void /* PRIVATE */
+png_decompress_chunk(png_structp png_ptr, int comp_type,
+ png_size_t chunklength,
+ png_size_t prefix_size, png_size_t *newlength)
+{
+ /* The caller should guarantee this */
+ if (prefix_size > chunklength)
+ {
+ /* The recovery is to delete the chunk. */
+ png_warning(png_ptr, "invalid chunklength");
+ prefix_size = 0; /* To delete everything */
+ }
+
+ else if (comp_type == PNG_COMPRESSION_TYPE_BASE)
+ {
+ png_size_t expanded_size = png_inflate(png_ptr,
+ (png_bytep)(png_ptr->chunkdata + prefix_size),
+ chunklength - prefix_size,
+ 0, /*output*/
+ 0); /*output size*/
+
+ /* Now check the limits on this chunk - if the limit fails the
+ * compressed data will be removed, the prefix will remain.
+ */
+#ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED
+ if (png_ptr->user_chunk_malloc_max &&
+ (prefix_size + expanded_size >= png_ptr->user_chunk_malloc_max - 1))
+#else
+# ifdef PNG_USER_CHUNK_MALLOC_MAX
+ if ((PNG_USER_CHUNK_MALLOC_MAX > 0) &&
+ prefix_size + expanded_size >= PNG_USER_CHUNK_MALLOC_MAX - 1)
+# endif
+#endif
+ png_warning(png_ptr, "Exceeded size limit while expanding chunk");
+
+ /* If the size is zero either there was an error and a message
+ * has already been output (warning) or the size really is zero
+ * and we have nothing to do - the code will exit through the
+ * error case below.
+ */
+#if defined(PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED) || \
+ defined(PNG_USER_CHUNK_MALLOC_MAX)
+ else if (expanded_size > 0)
+#else
+ if (expanded_size > 0)
+#endif
+ {
+ /* Success (maybe) - really uncompress the chunk. */
+ png_size_t new_size = 0;
+ png_charp text = png_malloc_warn(png_ptr,
+ prefix_size + expanded_size + 1);
+
+ if (text != NULL)
+ {
+ png_memcpy(text, png_ptr->chunkdata, prefix_size);
+ new_size = png_inflate(png_ptr,
+ (png_bytep)(png_ptr->chunkdata + prefix_size),
+ chunklength - prefix_size,
+ (png_bytep)(text + prefix_size), expanded_size);
+ text[prefix_size + expanded_size] = 0; /* just in case */
+
+ if (new_size == expanded_size)
+ {
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = text;
+ *newlength = prefix_size + expanded_size;
+ return; /* The success return! */
+ }
+
+ png_warning(png_ptr, "png_inflate logic error");
+ png_free(png_ptr, text);
+ }
+
+ else
+ png_warning(png_ptr, "Not enough memory to decompress chunk");
+ }
+ }
+
+ else /* if (comp_type != PNG_COMPRESSION_TYPE_BASE) */
+ {
+#ifdef PNG_STDIO_SUPPORTED
+ char umsg[50];
+
+ png_snprintf(umsg, sizeof umsg,
+ "Unknown zTXt compression type %d", comp_type);
+ png_warning(png_ptr, umsg);
+#else
+ png_warning(png_ptr, "Unknown zTXt compression type");
+#endif
+
+ /* The recovery is to simply drop the data. */
+ }
+
+ /* Generic error return - leave the prefix, delete the compressed
+ * data, reallocate the chunkdata to remove the potentially large
+ * amount of compressed data.
+ */
+ {
+ png_charp text = png_malloc_warn(png_ptr, prefix_size + 1);
+
+ if (text != NULL)
+ {
+ if (prefix_size > 0)
+ png_memcpy(text, png_ptr->chunkdata, prefix_size);
+
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = text;
+
+ /* This is an extra zero in the 'uncompressed' part. */
+ *(png_ptr->chunkdata + prefix_size) = 0x00;
+ }
+ /* Ignore a malloc error here - it is safe. */
+ }
+
+ *newlength = prefix_size;
+}
+#endif
+
+/* Read and check the IDHR chunk */
+void /* PRIVATE */
+png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ png_byte buf[13];
+ png_uint_32 width, height;
+ int bit_depth, color_type, compression_type, filter_type;
+ int interlace_type;
+
+ png_debug(1, "in png_handle_IHDR");
+
+ if (png_ptr->mode & PNG_HAVE_IHDR)
+ png_error(png_ptr, "Out of place IHDR");
+
+ /* Check the length */
+ if (length != 13)
+ png_error(png_ptr, "Invalid IHDR chunk");
+
+ png_ptr->mode |= PNG_HAVE_IHDR;
+
+ png_crc_read(png_ptr, buf, 13);
+ png_crc_finish(png_ptr, 0);
+
+ width = png_get_uint_31(png_ptr, buf);
+ height = png_get_uint_31(png_ptr, buf + 4);
+ bit_depth = buf[8];
+ color_type = buf[9];
+ compression_type = buf[10];
+ filter_type = buf[11];
+ interlace_type = buf[12];
+
+ /* Set internal variables */
+ png_ptr->width = width;
+ png_ptr->height = height;
+ png_ptr->bit_depth = (png_byte)bit_depth;
+ png_ptr->interlaced = (png_byte)interlace_type;
+ png_ptr->color_type = (png_byte)color_type;
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+ png_ptr->filter_type = (png_byte)filter_type;
+#endif
+ png_ptr->compression_type = (png_byte)compression_type;
+
+ /* Find number of channels */
+ switch (png_ptr->color_type)
+ {
+ default: /* invalid, png_set_IHDR calls png_error */
+ case PNG_COLOR_TYPE_GRAY:
+ case PNG_COLOR_TYPE_PALETTE:
+ png_ptr->channels = 1;
+ break;
+
+ case PNG_COLOR_TYPE_RGB:
+ png_ptr->channels = 3;
+ break;
+
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ png_ptr->channels = 2;
+ break;
+
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ png_ptr->channels = 4;
+ break;
+ }
+
+ /* Set up other useful info */
+ png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth *
+ png_ptr->channels);
+ png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->width);
+ png_debug1(3, "bit_depth = %d", png_ptr->bit_depth);
+ png_debug1(3, "channels = %d", png_ptr->channels);
+ png_debug1(3, "rowbytes = %lu", (unsigned long)png_ptr->rowbytes);
+ png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
+ color_type, interlace_type, compression_type, filter_type);
+}
+
+/* Read and check the palette */
+void /* PRIVATE */
+png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ png_color palette[PNG_MAX_PALETTE_LENGTH];
+ int num, i;
+#ifdef PNG_POINTER_INDEXING_SUPPORTED
+ png_colorp pal_ptr;
+#endif
+
+ png_debug(1, "in png_handle_PLTE");
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before PLTE");
+
+ else if (png_ptr->mode & PNG_HAVE_IDAT)
+ {
+ png_warning(png_ptr, "Invalid PLTE after IDAT");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ else if (png_ptr->mode & PNG_HAVE_PLTE)
+ png_error(png_ptr, "Duplicate PLTE chunk");
+
+ png_ptr->mode |= PNG_HAVE_PLTE;
+
+ if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR))
+ {
+ png_warning(png_ptr,
+ "Ignoring PLTE chunk in grayscale PNG");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+#ifndef PNG_READ_OPT_PLTE_SUPPORTED
+ if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
+ {
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+#endif
+
+ if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3)
+ {
+ if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
+ {
+ png_warning(png_ptr, "Invalid palette chunk");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ else
+ {
+ png_error(png_ptr, "Invalid palette chunk");
+ }
+ }
+
+ num = (int)length / 3;
+
+#ifdef PNG_POINTER_INDEXING_SUPPORTED
+ for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++)
+ {
+ png_byte buf[3];
+
+ png_crc_read(png_ptr, buf, 3);
+ pal_ptr->red = buf[0];
+ pal_ptr->green = buf[1];
+ pal_ptr->blue = buf[2];
+ }
+#else
+ for (i = 0; i < num; i++)
+ {
+ png_byte buf[3];
+
+ png_crc_read(png_ptr, buf, 3);
+ /* Don't depend upon png_color being any order */
+ palette[i].red = buf[0];
+ palette[i].green = buf[1];
+ palette[i].blue = buf[2];
+ }
+#endif
+
+ /* If we actually need the PLTE chunk (ie for a paletted image), we do
+ * whatever the normal CRC configuration tells us. However, if we
+ * have an RGB image, the PLTE can be considered ancillary, so
+ * we will act as though it is.
+ */
+#ifndef PNG_READ_OPT_PLTE_SUPPORTED
+ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+#endif
+ {
+ png_crc_finish(png_ptr, 0);
+ }
+
+#ifndef PNG_READ_OPT_PLTE_SUPPORTED
+ else if (png_crc_error(png_ptr)) /* Only if we have a CRC error */
+ {
+ /* If we don't want to use the data from an ancillary chunk,
+ * we have two options: an error abort, or a warning and we
+ * ignore the data in this chunk (which should be OK, since
+ * it's considered ancillary for a RGB or RGBA image).
+ */
+ if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE))
+ {
+ if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)
+ {
+ png_chunk_benign_error(png_ptr, "CRC error");
+ }
+
+ else
+ {
+ png_chunk_warning(png_ptr, "CRC error");
+ return;
+ }
+ }
+
+ /* Otherwise, we (optionally) emit a warning and use the chunk. */
+ else if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN))
+ {
+ png_chunk_warning(png_ptr, "CRC error");
+ }
+ }
+#endif
+
+ png_set_PLTE(png_ptr, info_ptr, palette, num);
+
+#ifdef PNG_READ_tRNS_SUPPORTED
+ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS))
+ {
+ if (png_ptr->num_trans > (png_uint_16)num)
+ {
+ png_warning(png_ptr, "Truncating incorrect tRNS chunk length");
+ png_ptr->num_trans = (png_uint_16)num;
+ }
+
+ if (info_ptr->num_trans > (png_uint_16)num)
+ {
+ png_warning(png_ptr, "Truncating incorrect info tRNS chunk length");
+ info_ptr->num_trans = (png_uint_16)num;
+ }
+ }
+ }
+#endif
+
+}
+
+void /* PRIVATE */
+png_handle_IEND(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ png_debug(1, "in png_handle_IEND");
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT))
+ {
+ png_error(png_ptr, "No image in file");
+ }
+
+ png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND);
+
+ if (length != 0)
+ {
+ png_warning(png_ptr, "Incorrect IEND chunk length");
+ }
+
+ png_crc_finish(png_ptr, length);
+
+ PNG_UNUSED(info_ptr) /* Quiet compiler warnings about unused info_ptr */
+}
+
+#ifdef PNG_READ_gAMA_SUPPORTED
+void /* PRIVATE */
+png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ png_fixed_point igamma;
+ png_byte buf[4];
+
+ png_debug(1, "in png_handle_gAMA");
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before gAMA");
+
+ else if (png_ptr->mode & PNG_HAVE_IDAT)
+ {
+ png_warning(png_ptr, "Invalid gAMA after IDAT");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ else if (png_ptr->mode & PNG_HAVE_PLTE)
+ /* Should be an error, but we can cope with it */
+ png_warning(png_ptr, "Out of place gAMA chunk");
+
+ if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)
+#ifdef PNG_READ_sRGB_SUPPORTED
+ && !(info_ptr->valid & PNG_INFO_sRGB)
+#endif
+ )
+ {
+ png_warning(png_ptr, "Duplicate gAMA chunk");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ if (length != 4)
+ {
+ png_warning(png_ptr, "Incorrect gAMA chunk length");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ png_crc_read(png_ptr, buf, 4);
+
+ if (png_crc_finish(png_ptr, 0))
+ return;
+
+ igamma = png_get_fixed_point(NULL, buf);
+
+ /* Check for zero gamma or an error. */
+ if (igamma <= 0)
+ {
+ png_warning(png_ptr,
+ "Ignoring gAMA chunk with out of range gamma");
+
+ return;
+ }
+
+# ifdef PNG_READ_sRGB_SUPPORTED
+ if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB))
+ {
+ if (PNG_OUT_OF_RANGE(igamma, 45500L, 500))
+ {
+ png_warning(png_ptr,
+ "Ignoring incorrect gAMA value when sRGB is also present");
+
+# ifdef PNG_CONSOLE_IO_SUPPORTED
+ fprintf(stderr, "gamma = (%d/100000)", (int)igamma);
+# endif
+ return;
+ }
+ }
+# endif /* PNG_READ_sRGB_SUPPORTED */
+
+# ifdef PNG_READ_GAMMA_SUPPORTED
+ /* Gamma correction on read is supported. */
+ png_ptr->gamma = igamma;
+# endif
+ /* And set the 'info' structure members. */
+ png_set_gAMA_fixed(png_ptr, info_ptr, igamma);
+}
+#endif
+
+#ifdef PNG_READ_sBIT_SUPPORTED
+void /* PRIVATE */
+png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ png_size_t truelen;
+ png_byte buf[4];
+
+ png_debug(1, "in png_handle_sBIT");
+
+ buf[0] = buf[1] = buf[2] = buf[3] = 0;
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before sBIT");
+
+ else if (png_ptr->mode & PNG_HAVE_IDAT)
+ {
+ png_warning(png_ptr, "Invalid sBIT after IDAT");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ else if (png_ptr->mode & PNG_HAVE_PLTE)
+ {
+ /* Should be an error, but we can cope with it */
+ png_warning(png_ptr, "Out of place sBIT chunk");
+ }
+
+ if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT))
+ {
+ png_warning(png_ptr, "Duplicate sBIT chunk");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ truelen = 3;
+
+ else
+ truelen = (png_size_t)png_ptr->channels;
+
+ if (length != truelen || length > 4)
+ {
+ png_warning(png_ptr, "Incorrect sBIT chunk length");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ png_crc_read(png_ptr, buf, truelen);
+
+ if (png_crc_finish(png_ptr, 0))
+ return;
+
+ if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
+ {
+ png_ptr->sig_bit.red = buf[0];
+ png_ptr->sig_bit.green = buf[1];
+ png_ptr->sig_bit.blue = buf[2];
+ png_ptr->sig_bit.alpha = buf[3];
+ }
+
+ else
+ {
+ png_ptr->sig_bit.gray = buf[0];
+ png_ptr->sig_bit.red = buf[0];
+ png_ptr->sig_bit.green = buf[0];
+ png_ptr->sig_bit.blue = buf[0];
+ png_ptr->sig_bit.alpha = buf[1];
+ }
+
+ png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit));
+}
+#endif
+
+#ifdef PNG_READ_cHRM_SUPPORTED
+void /* PRIVATE */
+png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ png_byte buf[32];
+ png_fixed_point x_white, y_white, x_red, y_red, x_green, y_green, x_blue,
+ y_blue;
+
+ png_debug(1, "in png_handle_cHRM");
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before cHRM");
+
+ else if (png_ptr->mode & PNG_HAVE_IDAT)
+ {
+ png_warning(png_ptr, "Invalid cHRM after IDAT");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ else if (png_ptr->mode & PNG_HAVE_PLTE)
+ /* Should be an error, but we can cope with it */
+ png_warning(png_ptr, "Missing PLTE before cHRM");
+
+ if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)
+# ifdef PNG_READ_sRGB_SUPPORTED
+ && !(info_ptr->valid & PNG_INFO_sRGB)
+# endif
+ )
+ {
+ png_warning(png_ptr, "Duplicate cHRM chunk");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ if (length != 32)
+ {
+ png_warning(png_ptr, "Incorrect cHRM chunk length");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ png_crc_read(png_ptr, buf, 32);
+
+ if (png_crc_finish(png_ptr, 0))
+ return;
+
+ x_white = png_get_fixed_point(NULL, buf);
+ y_white = png_get_fixed_point(NULL, buf + 4);
+ x_red = png_get_fixed_point(NULL, buf + 8);
+ y_red = png_get_fixed_point(NULL, buf + 12);
+ x_green = png_get_fixed_point(NULL, buf + 16);
+ y_green = png_get_fixed_point(NULL, buf + 20);
+ x_blue = png_get_fixed_point(NULL, buf + 24);
+ y_blue = png_get_fixed_point(NULL, buf + 28);
+
+ if (x_white == PNG_FIXED_ERROR ||
+ y_white == PNG_FIXED_ERROR ||
+ x_red == PNG_FIXED_ERROR ||
+ y_red == PNG_FIXED_ERROR ||
+ x_green == PNG_FIXED_ERROR ||
+ y_green == PNG_FIXED_ERROR ||
+ x_blue == PNG_FIXED_ERROR ||
+ y_blue == PNG_FIXED_ERROR)
+ {
+ png_warning(png_ptr, "Ignoring cHRM chunk with negative chromaticities");
+ return;
+ }
+
+#ifdef PNG_READ_sRGB_SUPPORTED
+ if ((info_ptr != NULL) && (info_ptr->valid & PNG_INFO_sRGB))
+ {
+ if (PNG_OUT_OF_RANGE(x_white, 31270, 1000) ||
+ PNG_OUT_OF_RANGE(y_white, 32900, 1000) ||
+ PNG_OUT_OF_RANGE(x_red, 64000L, 1000) ||
+ PNG_OUT_OF_RANGE(y_red, 33000, 1000) ||
+ PNG_OUT_OF_RANGE(x_green, 30000, 1000) ||
+ PNG_OUT_OF_RANGE(y_green, 60000L, 1000) ||
+ PNG_OUT_OF_RANGE(x_blue, 15000, 1000) ||
+ PNG_OUT_OF_RANGE(y_blue, 6000, 1000))
+ {
+ png_warning(png_ptr,
+ "Ignoring incorrect cHRM value when sRGB is also present");
+
+#ifdef PNG_CONSOLE_IO_SUPPORTED
+ fprintf(stderr, "wx=%d, wy=%d, rx=%d, ry=%d\n",
+ x_white, y_white, x_red, y_red);
+
+ fprintf(stderr, "gx=%d, gy=%d, bx=%d, by=%d\n",
+ x_green, y_green, x_blue, y_blue);
+#endif /* PNG_CONSOLE_IO_SUPPORTED */
+ }
+ return;
+ }
+#endif /* PNG_READ_sRGB_SUPPORTED */
+
+ png_set_cHRM_fixed(png_ptr, info_ptr, x_white, y_white, x_red, y_red,
+ x_green, y_green, x_blue, y_blue);
+}
+#endif
+
+#ifdef PNG_READ_sRGB_SUPPORTED
+void /* PRIVATE */
+png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ int intent;
+ png_byte buf[1];
+
+ png_debug(1, "in png_handle_sRGB");
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before sRGB");
+
+ else if (png_ptr->mode & PNG_HAVE_IDAT)
+ {
+ png_warning(png_ptr, "Invalid sRGB after IDAT");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ else if (png_ptr->mode & PNG_HAVE_PLTE)
+ /* Should be an error, but we can cope with it */
+ png_warning(png_ptr, "Out of place sRGB chunk");
+
+ if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB))
+ {
+ png_warning(png_ptr, "Duplicate sRGB chunk");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ if (length != 1)
+ {
+ png_warning(png_ptr, "Incorrect sRGB chunk length");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ png_crc_read(png_ptr, buf, 1);
+
+ if (png_crc_finish(png_ptr, 0))
+ return;
+
+ intent = buf[0];
+
+ /* Check for bad intent */
+ if (intent >= PNG_sRGB_INTENT_LAST)
+ {
+ png_warning(png_ptr, "Unknown sRGB intent");
+ return;
+ }
+
+#if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED)
+ if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA))
+ {
+ if (PNG_OUT_OF_RANGE(info_ptr->gamma, 45500L, 500))
+ {
+ png_warning(png_ptr,
+ "Ignoring incorrect gAMA value when sRGB is also present");
+#ifdef PNG_CONSOLE_IO_SUPPORTED
+ fprintf(stderr, "incorrect gamma=(%d/100000)\n", info_ptr->gamma);
+#endif
+ }
+ }
+#endif /* PNG_READ_gAMA_SUPPORTED */
+
+#ifdef PNG_READ_cHRM_SUPPORTED
+ if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM))
+ if (PNG_OUT_OF_RANGE(info_ptr->x_white, 31270, 1000) ||
+ PNG_OUT_OF_RANGE(info_ptr->y_white, 32900, 1000) ||
+ PNG_OUT_OF_RANGE(info_ptr->x_red, 64000L, 1000) ||
+ PNG_OUT_OF_RANGE(info_ptr->y_red, 33000, 1000) ||
+ PNG_OUT_OF_RANGE(info_ptr->x_green, 30000, 1000) ||
+ PNG_OUT_OF_RANGE(info_ptr->y_green, 60000L, 1000) ||
+ PNG_OUT_OF_RANGE(info_ptr->x_blue, 15000, 1000) ||
+ PNG_OUT_OF_RANGE(info_ptr->y_blue, 6000, 1000))
+ {
+ png_warning(png_ptr,
+ "Ignoring incorrect cHRM value when sRGB is also present");
+ }
+#endif /* PNG_READ_cHRM_SUPPORTED */
+
+ png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, intent);
+}
+#endif /* PNG_READ_sRGB_SUPPORTED */
+
+#ifdef PNG_READ_iCCP_SUPPORTED
+void /* PRIVATE */
+png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+/* Note: this does not properly handle chunks that are > 64K under DOS */
+{
+ png_byte compression_type;
+ png_bytep pC;
+ png_charp profile;
+ png_uint_32 skip = 0;
+ png_uint_32 profile_size;
+ png_alloc_size_t profile_length;
+ png_size_t slength, prefix_length, data_length;
+
+ png_debug(1, "in png_handle_iCCP");
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before iCCP");
+
+ else if (png_ptr->mode & PNG_HAVE_IDAT)
+ {
+ png_warning(png_ptr, "Invalid iCCP after IDAT");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ else if (png_ptr->mode & PNG_HAVE_PLTE)
+ /* Should be an error, but we can cope with it */
+ png_warning(png_ptr, "Out of place iCCP chunk");
+
+ if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP))
+ {
+ png_warning(png_ptr, "Duplicate iCCP chunk");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+#ifdef PNG_MAX_MALLOC_64K
+ if (length > (png_uint_32)65535L)
+ {
+ png_warning(png_ptr, "iCCP chunk too large to fit in memory");
+ skip = length - (png_uint_32)65535L;
+ length = (png_uint_32)65535L;
+ }
+#endif
+
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = (png_charp)png_malloc(png_ptr, length + 1);
+ slength = (png_size_t)length;
+ png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength);
+
+ if (png_crc_finish(png_ptr, skip))
+ {
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ return;
+ }
+
+ png_ptr->chunkdata[slength] = 0x00;
+
+ for (profile = png_ptr->chunkdata; *profile; profile++)
+ /* Empty loop to find end of name */ ;
+
+ ++profile;
+
+ /* There should be at least one zero (the compression type byte)
+ * following the separator, and we should be on it
+ */
+ if (profile >= png_ptr->chunkdata + slength - 1)
+ {
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ png_warning(png_ptr, "Malformed iCCP chunk");
+ return;
+ }
+
+ /* Compression_type should always be zero */
+ compression_type = *profile++;
+
+ if (compression_type)
+ {
+ png_warning(png_ptr, "Ignoring nonzero compression type in iCCP chunk");
+ compression_type = 0x00; /* Reset it to zero (libpng-1.0.6 through 1.0.8
+ wrote nonzero) */
+ }
+
+ prefix_length = profile - png_ptr->chunkdata;
+ png_decompress_chunk(png_ptr, compression_type,
+ slength, prefix_length, &data_length);
+
+ profile_length = data_length - prefix_length;
+
+ if (prefix_length > data_length || profile_length < 4)
+ {
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ png_warning(png_ptr, "Profile size field missing from iCCP chunk");
+ return;
+ }
+
+ /* Check the profile_size recorded in the first 32 bits of the ICC profile */
+ pC = (png_bytep)(png_ptr->chunkdata + prefix_length);
+ profile_size = ((*(pC )) << 24) |
+ ((*(pC + 1)) << 16) |
+ ((*(pC + 2)) << 8) |
+ ((*(pC + 3)) );
+
+ /* NOTE: the following guarantees that 'profile_length' fits into 32 bits,
+ * because profile_size is a 32 bit value.
+ */
+ if (profile_size < profile_length)
+ profile_length = profile_size;
+
+ /* And the following guarantees that profile_size == profile_length. */
+ if (profile_size > profile_length)
+ {
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+#ifdef PNG_STDIO_SUPPORTED
+ {
+ char umsg[80];
+
+ png_snprintf2(umsg, 80,
+ "Ignoring iCCP chunk with declared size = %u "
+ "and actual length = %u",
+ (unsigned int) profile_size,
+ (unsigned int) profile_length);
+ png_warning(png_ptr, umsg);
+ }
+#else
+ png_warning(png_ptr,
+ "Ignoring iCCP chunk with uncompressed size mismatch");
+#endif
+ return;
+ }
+
+ png_set_iCCP(png_ptr, info_ptr, png_ptr->chunkdata,
+ compression_type, (png_bytep)png_ptr->chunkdata + prefix_length,
+ profile_size);
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+}
+#endif /* PNG_READ_iCCP_SUPPORTED */
+
+#ifdef PNG_READ_sPLT_SUPPORTED
+void /* PRIVATE */
+png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+/* Note: this does not properly handle chunks that are > 64K under DOS */
+{
+ png_bytep entry_start;
+ png_sPLT_t new_palette;
+ png_sPLT_entryp pp;
+ png_uint_32 data_length;
+ int entry_size, i;
+ png_uint_32 skip = 0;
+ png_size_t slength;
+ png_uint_32 dl;
+ png_size_t max_dl;
+
+ png_debug(1, "in png_handle_sPLT");
+
+#ifdef PNG_USER_LIMITS_SUPPORTED
+
+ if (png_ptr->user_chunk_cache_max != 0)
+ {
+ if (png_ptr->user_chunk_cache_max == 1)
+ {
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ if (--png_ptr->user_chunk_cache_max == 1)
+ {
+ png_warning(png_ptr, "No space in chunk cache for sPLT");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+ }
+#endif
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before sPLT");
+
+ else if (png_ptr->mode & PNG_HAVE_IDAT)
+ {
+ png_warning(png_ptr, "Invalid sPLT after IDAT");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+#ifdef PNG_MAX_MALLOC_64K
+ if (length > (png_uint_32)65535L)
+ {
+ png_warning(png_ptr, "sPLT chunk too large to fit in memory");
+ skip = length - (png_uint_32)65535L;
+ length = (png_uint_32)65535L;
+ }
+#endif
+
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = (png_charp)png_malloc(png_ptr, length + 1);
+
+ /* WARNING: this may break if size_t is less than 32 bits; it is assumed
+ * that the PNG_MAX_MALLOC_64K test is enabled in this case, but this is a
+ * potential breakage point if the types in pngconf.h aren't exactly right.
+ */
+ slength = (png_size_t)length;
+ png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength);
+
+ if (png_crc_finish(png_ptr, skip))
+ {
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ return;
+ }
+
+ png_ptr->chunkdata[slength] = 0x00;
+
+ for (entry_start = (png_bytep)png_ptr->chunkdata; *entry_start;
+ entry_start++)
+ /* Empty loop to find end of name */ ;
+
+ ++entry_start;
+
+ /* A sample depth should follow the separator, and we should be on it */
+ if (entry_start > (png_bytep)png_ptr->chunkdata + slength - 2)
+ {
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ png_warning(png_ptr, "malformed sPLT chunk");
+ return;
+ }
+
+ new_palette.depth = *entry_start++;
+ entry_size = (new_palette.depth == 8 ? 6 : 10);
+ /* This must fit in a png_uint_32 because it is derived from the original
+ * chunk data length (and use 'length', not 'slength' here for clarity -
+ * they are guaranteed to be the same, see the tests above.)
+ */
+ data_length = length - (png_uint_32)(entry_start -
+ (png_bytep)png_ptr->chunkdata);
+
+ /* Integrity-check the data length */
+ if (data_length % entry_size)
+ {
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ png_warning(png_ptr, "sPLT chunk has bad length");
+ return;
+ }
+
+ dl = (png_int_32)(data_length / entry_size);
+ max_dl = PNG_SIZE_MAX / png_sizeof(png_sPLT_entry);
+
+ if (dl > max_dl)
+ {
+ png_warning(png_ptr, "sPLT chunk too long");
+ return;
+ }
+
+ new_palette.nentries = (png_int_32)(data_length / entry_size);
+
+ new_palette.entries = (png_sPLT_entryp)png_malloc_warn(
+ png_ptr, new_palette.nentries * png_sizeof(png_sPLT_entry));
+
+ if (new_palette.entries == NULL)
+ {
+ png_warning(png_ptr, "sPLT chunk requires too much memory");
+ return;
+ }
+
+#ifdef PNG_POINTER_INDEXING_SUPPORTED
+ for (i = 0; i < new_palette.nentries; i++)
+ {
+ pp = new_palette.entries + i;
+
+ if (new_palette.depth == 8)
+ {
+ pp->red = *entry_start++;
+ pp->green = *entry_start++;
+ pp->blue = *entry_start++;
+ pp->alpha = *entry_start++;
+ }
+
+ else
+ {
+ pp->red = png_get_uint_16(entry_start); entry_start += 2;
+ pp->green = png_get_uint_16(entry_start); entry_start += 2;
+ pp->blue = png_get_uint_16(entry_start); entry_start += 2;
+ pp->alpha = png_get_uint_16(entry_start); entry_start += 2;
+ }
+
+ pp->frequency = png_get_uint_16(entry_start); entry_start += 2;
+ }
+#else
+ pp = new_palette.entries;
+
+ for (i = 0; i < new_palette.nentries; i++)
+ {
+
+ if (new_palette.depth == 8)
+ {
+ pp[i].red = *entry_start++;
+ pp[i].green = *entry_start++;
+ pp[i].blue = *entry_start++;
+ pp[i].alpha = *entry_start++;
+ }
+
+ else
+ {
+ pp[i].red = png_get_uint_16(entry_start); entry_start += 2;
+ pp[i].green = png_get_uint_16(entry_start); entry_start += 2;
+ pp[i].blue = png_get_uint_16(entry_start); entry_start += 2;
+ pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2;
+ }
+
+ pp[i].frequency = png_get_uint_16(entry_start); entry_start += 2;
+ }
+#endif
+
+ /* Discard all chunk data except the name and stash that */
+ new_palette.name = png_ptr->chunkdata;
+
+ png_set_sPLT(png_ptr, info_ptr, &new_palette, 1);
+
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ png_free(png_ptr, new_palette.entries);
+}
+#endif /* PNG_READ_sPLT_SUPPORTED */
+
+#ifdef PNG_READ_tRNS_SUPPORTED
+void /* PRIVATE */
+png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ png_byte readbuf[PNG_MAX_PALETTE_LENGTH];
+
+ png_debug(1, "in png_handle_tRNS");
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before tRNS");
+
+ else if (png_ptr->mode & PNG_HAVE_IDAT)
+ {
+ png_warning(png_ptr, "Invalid tRNS after IDAT");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS))
+ {
+ png_warning(png_ptr, "Duplicate tRNS chunk");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
+ {
+ png_byte buf[2];
+
+ if (length != 2)
+ {
+ png_warning(png_ptr, "Incorrect tRNS chunk length");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ png_crc_read(png_ptr, buf, 2);
+ png_ptr->num_trans = 1;
+ png_ptr->trans_color.gray = png_get_uint_16(buf);
+ }
+
+ else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+ {
+ png_byte buf[6];
+
+ if (length != 6)
+ {
+ png_warning(png_ptr, "Incorrect tRNS chunk length");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ png_crc_read(png_ptr, buf, (png_size_t)length);
+ png_ptr->num_trans = 1;
+ png_ptr->trans_color.red = png_get_uint_16(buf);
+ png_ptr->trans_color.green = png_get_uint_16(buf + 2);
+ png_ptr->trans_color.blue = png_get_uint_16(buf + 4);
+ }
+
+ else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ if (!(png_ptr->mode & PNG_HAVE_PLTE))
+ {
+ /* Should be an error, but we can cope with it. */
+ png_warning(png_ptr, "Missing PLTE before tRNS");
+ }
+
+ if (length > (png_uint_32)png_ptr->num_palette ||
+ length > PNG_MAX_PALETTE_LENGTH)
+ {
+ png_warning(png_ptr, "Incorrect tRNS chunk length");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ if (length == 0)
+ {
+ png_warning(png_ptr, "Zero length tRNS chunk");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ png_crc_read(png_ptr, readbuf, (png_size_t)length);
+ png_ptr->num_trans = (png_uint_16)length;
+ }
+
+ else
+ {
+ png_warning(png_ptr, "tRNS chunk not allowed with alpha channel");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ if (png_crc_finish(png_ptr, 0))
+ {
+ png_ptr->num_trans = 0;
+ return;
+ }
+
+ png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans,
+ &(png_ptr->trans_color));
+}
+#endif
+
+#ifdef PNG_READ_bKGD_SUPPORTED
+void /* PRIVATE */
+png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ png_size_t truelen;
+ png_byte buf[6];
+
+ png_debug(1, "in png_handle_bKGD");
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before bKGD");
+
+ else if (png_ptr->mode & PNG_HAVE_IDAT)
+ {
+ png_warning(png_ptr, "Invalid bKGD after IDAT");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+ !(png_ptr->mode & PNG_HAVE_PLTE))
+ {
+ png_warning(png_ptr, "Missing PLTE before bKGD");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD))
+ {
+ png_warning(png_ptr, "Duplicate bKGD chunk");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ truelen = 1;
+
+ else if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
+ truelen = 6;
+
+ else
+ truelen = 2;
+
+ if (length != truelen)
+ {
+ png_warning(png_ptr, "Incorrect bKGD chunk length");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ png_crc_read(png_ptr, buf, truelen);
+
+ if (png_crc_finish(png_ptr, 0))
+ return;
+
+ /* We convert the index value into RGB components so that we can allow
+ * arbitrary RGB values for background when we have transparency, and
+ * so it is easy to determine the RGB values of the background color
+ * from the info_ptr struct.
+ */
+ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ png_ptr->background.index = buf[0];
+
+ if (info_ptr && info_ptr->num_palette)
+ {
+ if (buf[0] >= info_ptr->num_palette)
+ {
+ png_warning(png_ptr, "Incorrect bKGD chunk index value");
+ return;
+ }
+
+ png_ptr->background.red =
+ (png_uint_16)png_ptr->palette[buf[0]].red;
+
+ png_ptr->background.green =
+ (png_uint_16)png_ptr->palette[buf[0]].green;
+
+ png_ptr->background.blue =
+ (png_uint_16)png_ptr->palette[buf[0]].blue;
+ }
+ }
+
+ else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* GRAY */
+ {
+ png_ptr->background.red =
+ png_ptr->background.green =
+ png_ptr->background.blue =
+ png_ptr->background.gray = png_get_uint_16(buf);
+ }
+
+ else
+ {
+ png_ptr->background.red = png_get_uint_16(buf);
+ png_ptr->background.green = png_get_uint_16(buf + 2);
+ png_ptr->background.blue = png_get_uint_16(buf + 4);
+ }
+
+ png_set_bKGD(png_ptr, info_ptr, &(png_ptr->background));
+}
+#endif
+
+#ifdef PNG_READ_hIST_SUPPORTED
+void /* PRIVATE */
+png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ unsigned int num, i;
+ png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH];
+
+ png_debug(1, "in png_handle_hIST");
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before hIST");
+
+ else if (png_ptr->mode & PNG_HAVE_IDAT)
+ {
+ png_warning(png_ptr, "Invalid hIST after IDAT");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ else if (!(png_ptr->mode & PNG_HAVE_PLTE))
+ {
+ png_warning(png_ptr, "Missing PLTE before hIST");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST))
+ {
+ png_warning(png_ptr, "Duplicate hIST chunk");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ num = length / 2 ;
+
+ if (num != (unsigned int)png_ptr->num_palette || num >
+ (unsigned int)PNG_MAX_PALETTE_LENGTH)
+ {
+ png_warning(png_ptr, "Incorrect hIST chunk length");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ for (i = 0; i < num; i++)
+ {
+ png_byte buf[2];
+
+ png_crc_read(png_ptr, buf, 2);
+ readbuf[i] = png_get_uint_16(buf);
+ }
+
+ if (png_crc_finish(png_ptr, 0))
+ return;
+
+ png_set_hIST(png_ptr, info_ptr, readbuf);
+}
+#endif
+
+#ifdef PNG_READ_pHYs_SUPPORTED
+void /* PRIVATE */
+png_handle_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ png_byte buf[9];
+ png_uint_32 res_x, res_y;
+ int unit_type;
+
+ png_debug(1, "in png_handle_pHYs");
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before pHYs");
+
+ else if (png_ptr->mode & PNG_HAVE_IDAT)
+ {
+ png_warning(png_ptr, "Invalid pHYs after IDAT");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+ {
+ png_warning(png_ptr, "Duplicate pHYs chunk");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ if (length != 9)
+ {
+ png_warning(png_ptr, "Incorrect pHYs chunk length");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ png_crc_read(png_ptr, buf, 9);
+
+ if (png_crc_finish(png_ptr, 0))
+ return;
+
+ res_x = png_get_uint_32(buf);
+ res_y = png_get_uint_32(buf + 4);
+ unit_type = buf[8];
+ png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type);
+}
+#endif
+
+#ifdef PNG_READ_oFFs_SUPPORTED
+void /* PRIVATE */
+png_handle_oFFs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ png_byte buf[9];
+ png_int_32 offset_x, offset_y;
+ int unit_type;
+
+ png_debug(1, "in png_handle_oFFs");
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before oFFs");
+
+ else if (png_ptr->mode & PNG_HAVE_IDAT)
+ {
+ png_warning(png_ptr, "Invalid oFFs after IDAT");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
+ {
+ png_warning(png_ptr, "Duplicate oFFs chunk");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ if (length != 9)
+ {
+ png_warning(png_ptr, "Incorrect oFFs chunk length");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ png_crc_read(png_ptr, buf, 9);
+
+ if (png_crc_finish(png_ptr, 0))
+ return;
+
+ offset_x = png_get_int_32(buf);
+ offset_y = png_get_int_32(buf + 4);
+ unit_type = buf[8];
+ png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type);
+}
+#endif
+
+#ifdef PNG_READ_pCAL_SUPPORTED
+/* Read the pCAL chunk (described in the PNG Extensions document) */
+void /* PRIVATE */
+png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ png_int_32 X0, X1;
+ png_byte type, nparams;
+ png_charp buf, units, endptr;
+ png_charpp params;
+ png_size_t slength;
+ int i;
+
+ png_debug(1, "in png_handle_pCAL");
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before pCAL");
+
+ else if (png_ptr->mode & PNG_HAVE_IDAT)
+ {
+ png_warning(png_ptr, "Invalid pCAL after IDAT");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL))
+ {
+ png_warning(png_ptr, "Duplicate pCAL chunk");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ png_debug1(2, "Allocating and reading pCAL chunk data (%u bytes)",
+ length + 1);
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1);
+
+ if (png_ptr->chunkdata == NULL)
+ {
+ png_warning(png_ptr, "No memory for pCAL purpose");
+ return;
+ }
+
+ slength = (png_size_t)length;
+ png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength);
+
+ if (png_crc_finish(png_ptr, 0))
+ {
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ return;
+ }
+
+ png_ptr->chunkdata[slength] = 0x00; /* Null terminate the last string */
+
+ png_debug(3, "Finding end of pCAL purpose string");
+ for (buf = png_ptr->chunkdata; *buf; buf++)
+ /* Empty loop */ ;
+
+ endptr = png_ptr->chunkdata + slength;
+
+ /* We need to have at least 12 bytes after the purpose string
+ * in order to get the parameter information.
+ */
+ if (endptr <= buf + 12)
+ {
+ png_warning(png_ptr, "Invalid pCAL data");
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ return;
+ }
+
+ png_debug(3, "Reading pCAL X0, X1, type, nparams, and units");
+ X0 = png_get_int_32((png_bytep)buf+1);
+ X1 = png_get_int_32((png_bytep)buf+5);
+ type = buf[9];
+ nparams = buf[10];
+ units = buf + 11;
+
+ png_debug(3, "Checking pCAL equation type and number of parameters");
+ /* Check that we have the right number of parameters for known
+ * equation types.
+ */
+ if ((type == PNG_EQUATION_LINEAR && nparams != 2) ||
+ (type == PNG_EQUATION_BASE_E && nparams != 3) ||
+ (type == PNG_EQUATION_ARBITRARY && nparams != 3) ||
+ (type == PNG_EQUATION_HYPERBOLIC && nparams != 4))
+ {
+ png_warning(png_ptr, "Invalid pCAL parameters for equation type");
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ return;
+ }
+
+ else if (type >= PNG_EQUATION_LAST)
+ {
+ png_warning(png_ptr, "Unrecognized equation type for pCAL chunk");
+ }
+
+ for (buf = units; *buf; buf++)
+ /* Empty loop to move past the units string. */ ;
+
+ png_debug(3, "Allocating pCAL parameters array");
+
+ params = (png_charpp)png_malloc_warn(png_ptr,
+ (png_size_t)(nparams * png_sizeof(png_charp)));
+
+ if (params == NULL)
+ {
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ png_warning(png_ptr, "No memory for pCAL params");
+ return;
+ }
+
+ /* Get pointers to the start of each parameter string. */
+ for (i = 0; i < (int)nparams; i++)
+ {
+ buf++; /* Skip the null string terminator from previous parameter. */
+
+ png_debug1(3, "Reading pCAL parameter %d", i);
+
+ for (params[i] = buf; buf <= endptr && *buf != 0x00; buf++)
+ /* Empty loop to move past each parameter string */ ;
+
+ /* Make sure we haven't run out of data yet */
+ if (buf > endptr)
+ {
+ png_warning(png_ptr, "Invalid pCAL data");
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ png_free(png_ptr, params);
+ return;
+ }
+ }
+
+ png_set_pCAL(png_ptr, info_ptr, png_ptr->chunkdata, X0, X1, type, nparams,
+ units, params);
+
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ png_free(png_ptr, params);
+}
+#endif
+
+#ifdef PNG_READ_sCAL_SUPPORTED
+/* Read the sCAL chunk */
+void /* PRIVATE */
+png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ png_size_t slength, i;
+ int state;
+
+ png_debug(1, "in png_handle_sCAL");
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before sCAL");
+
+ else if (png_ptr->mode & PNG_HAVE_IDAT)
+ {
+ png_warning(png_ptr, "Invalid sCAL after IDAT");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL))
+ {
+ png_warning(png_ptr, "Duplicate sCAL chunk");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ png_debug1(2, "Allocating and reading sCAL chunk data (%u bytes)",
+ length + 1);
+
+ png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1);
+
+ if (png_ptr->chunkdata == NULL)
+ {
+ png_warning(png_ptr, "Out of memory while processing sCAL chunk");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ slength = (png_size_t)length;
+ png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength);
+ png_ptr->chunkdata[slength] = 0x00; /* Null terminate the last string */
+
+ if (png_crc_finish(png_ptr, 0))
+ {
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ return;
+ }
+
+ /* Validate the unit. */
+ if (png_ptr->chunkdata[0] != 1 && png_ptr->chunkdata[0] != 2)
+ {
+ png_warning(png_ptr, "Invalid sCAL ignored: invalid unit");
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ return;
+ }
+
+ /* Validate the ASCII numbers, need two ASCII numbers separated by
+ * a '\0' and they need to fit exactly in the chunk data.
+ */
+ i = 0;
+ state = 0;
+
+ if (png_ptr->chunkdata[1] == 45 /* negative width */ ||
+ !png_check_fp_number(png_ptr->chunkdata, slength, &state, &i) ||
+ i >= slength || png_ptr->chunkdata[i++] != 0)
+ png_warning(png_ptr, "Invalid sCAL chunk ignored: bad width format");
+
+ else
+ {
+ png_size_t heighti = i;
+
+ if (png_ptr->chunkdata[i] == 45 /* negative height */ ||
+ !png_check_fp_number(png_ptr->chunkdata, slength, &state, &i) ||
+ i != slength)
+ png_warning(png_ptr, "Invalid sCAL chunk ignored: bad height format");
+
+ else
+ /* This is the (only) success case. */
+ png_set_sCAL_s(png_ptr, info_ptr, png_ptr->chunkdata[0],
+ png_ptr->chunkdata+1, png_ptr->chunkdata+heighti);
+ }
+
+ /* Clean up - just free the temporarily allocated buffer. */
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+}
+#endif
+
+#ifdef PNG_READ_tIME_SUPPORTED
+void /* PRIVATE */
+png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ png_byte buf[7];
+ png_time mod_time;
+
+ png_debug(1, "in png_handle_tIME");
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Out of place tIME chunk");
+
+ else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME))
+ {
+ png_warning(png_ptr, "Duplicate tIME chunk");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ if (png_ptr->mode & PNG_HAVE_IDAT)
+ png_ptr->mode |= PNG_AFTER_IDAT;
+
+ if (length != 7)
+ {
+ png_warning(png_ptr, "Incorrect tIME chunk length");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ png_crc_read(png_ptr, buf, 7);
+
+ if (png_crc_finish(png_ptr, 0))
+ return;
+
+ mod_time.second = buf[6];
+ mod_time.minute = buf[5];
+ mod_time.hour = buf[4];
+ mod_time.day = buf[3];
+ mod_time.month = buf[2];
+ mod_time.year = png_get_uint_16(buf);
+
+ png_set_tIME(png_ptr, info_ptr, &mod_time);
+}
+#endif
+
+#ifdef PNG_READ_tEXt_SUPPORTED
+/* Note: this does not properly handle chunks that are > 64K under DOS */
+void /* PRIVATE */
+png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ png_textp text_ptr;
+ png_charp key;
+ png_charp text;
+ png_uint_32 skip = 0;
+ png_size_t slength;
+ int ret;
+
+ png_debug(1, "in png_handle_tEXt");
+
+#ifdef PNG_USER_LIMITS_SUPPORTED
+ if (png_ptr->user_chunk_cache_max != 0)
+ {
+ if (png_ptr->user_chunk_cache_max == 1)
+ {
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ if (--png_ptr->user_chunk_cache_max == 1)
+ {
+ png_warning(png_ptr, "No space in chunk cache for tEXt");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+ }
+#endif
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before tEXt");
+
+ if (png_ptr->mode & PNG_HAVE_IDAT)
+ png_ptr->mode |= PNG_AFTER_IDAT;
+
+#ifdef PNG_MAX_MALLOC_64K
+ if (length > (png_uint_32)65535L)
+ {
+ png_warning(png_ptr, "tEXt chunk too large to fit in memory");
+ skip = length - (png_uint_32)65535L;
+ length = (png_uint_32)65535L;
+ }
+#endif
+
+ png_free(png_ptr, png_ptr->chunkdata);
+
+ png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1);
+
+ if (png_ptr->chunkdata == NULL)
+ {
+ png_warning(png_ptr, "No memory to process text chunk");
+ return;
+ }
+
+ slength = (png_size_t)length;
+ png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength);
+
+ if (png_crc_finish(png_ptr, skip))
+ {
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ return;
+ }
+
+ key = png_ptr->chunkdata;
+
+ key[slength] = 0x00;
+
+ for (text = key; *text; text++)
+ /* Empty loop to find end of key */ ;
+
+ if (text != key + slength)
+ text++;
+
+ text_ptr = (png_textp)png_malloc_warn(png_ptr,
+ png_sizeof(png_text));
+
+ if (text_ptr == NULL)
+ {
+ png_warning(png_ptr, "Not enough memory to process text chunk");
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ return;
+ }
+
+ text_ptr->compression = PNG_TEXT_COMPRESSION_NONE;
+ text_ptr->key = key;
+ text_ptr->lang = NULL;
+ text_ptr->lang_key = NULL;
+ text_ptr->itxt_length = 0;
+ text_ptr->text = text;
+ text_ptr->text_length = png_strlen(text);
+
+ ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ png_free(png_ptr, text_ptr);
+
+ if (ret)
+ png_warning(png_ptr, "Insufficient memory to process text chunk");
+}
+#endif
+
+#ifdef PNG_READ_zTXt_SUPPORTED
+/* Note: this does not correctly handle chunks that are > 64K under DOS */
+void /* PRIVATE */
+png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ png_textp text_ptr;
+ png_charp text;
+ int comp_type;
+ int ret;
+ png_size_t slength, prefix_len, data_len;
+
+ png_debug(1, "in png_handle_zTXt");
+
+#ifdef PNG_USER_LIMITS_SUPPORTED
+ if (png_ptr->user_chunk_cache_max != 0)
+ {
+ if (png_ptr->user_chunk_cache_max == 1)
+ {
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ if (--png_ptr->user_chunk_cache_max == 1)
+ {
+ png_warning(png_ptr, "No space in chunk cache for zTXt");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+ }
+#endif
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before zTXt");
+
+ if (png_ptr->mode & PNG_HAVE_IDAT)
+ png_ptr->mode |= PNG_AFTER_IDAT;
+
+#ifdef PNG_MAX_MALLOC_64K
+ /* We will no doubt have problems with chunks even half this size, but
+ * there is no hard and fast rule to tell us where to stop.
+ */
+ if (length > (png_uint_32)65535L)
+ {
+ png_warning(png_ptr, "zTXt chunk too large to fit in memory");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+#endif
+
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1);
+
+ if (png_ptr->chunkdata == NULL)
+ {
+ png_warning(png_ptr, "Out of memory processing zTXt chunk");
+ return;
+ }
+
+ slength = (png_size_t)length;
+ png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength);
+
+ if (png_crc_finish(png_ptr, 0))
+ {
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ return;
+ }
+
+ png_ptr->chunkdata[slength] = 0x00;
+
+ for (text = png_ptr->chunkdata; *text; text++)
+ /* Empty loop */ ;
+
+ /* zTXt must have some text after the chunkdataword */
+ if (text >= png_ptr->chunkdata + slength - 2)
+ {
+ png_warning(png_ptr, "Truncated zTXt chunk");
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ return;
+ }
+
+ else
+ {
+ comp_type = *(++text);
+
+ if (comp_type != PNG_TEXT_COMPRESSION_zTXt)
+ {
+ png_warning(png_ptr, "Unknown compression type in zTXt chunk");
+ comp_type = PNG_TEXT_COMPRESSION_zTXt;
+ }
+
+ text++; /* Skip the compression_method byte */
+ }
+
+ prefix_len = text - png_ptr->chunkdata;
+
+ png_decompress_chunk(png_ptr, comp_type,
+ (png_size_t)length, prefix_len, &data_len);
+
+ text_ptr = (png_textp)png_malloc_warn(png_ptr,
+ png_sizeof(png_text));
+
+ if (text_ptr == NULL)
+ {
+ png_warning(png_ptr, "Not enough memory to process zTXt chunk");
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ return;
+ }
+
+ text_ptr->compression = comp_type;
+ text_ptr->key = png_ptr->chunkdata;
+ text_ptr->lang = NULL;
+ text_ptr->lang_key = NULL;
+ text_ptr->itxt_length = 0;
+ text_ptr->text = png_ptr->chunkdata + prefix_len;
+ text_ptr->text_length = data_len;
+
+ ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+ png_free(png_ptr, text_ptr);
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+
+ if (ret)
+ png_error(png_ptr, "Insufficient memory to store zTXt chunk");
+}
+#endif
+
+#ifdef PNG_READ_iTXt_SUPPORTED
+/* Note: this does not correctly handle chunks that are > 64K under DOS */
+void /* PRIVATE */
+png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ png_textp text_ptr;
+ png_charp key, lang, text, lang_key;
+ int comp_flag;
+ int comp_type = 0;
+ int ret;
+ png_size_t slength, prefix_len, data_len;
+
+ png_debug(1, "in png_handle_iTXt");
+
+#ifdef PNG_USER_LIMITS_SUPPORTED
+ if (png_ptr->user_chunk_cache_max != 0)
+ {
+ if (png_ptr->user_chunk_cache_max == 1)
+ {
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ if (--png_ptr->user_chunk_cache_max == 1)
+ {
+ png_warning(png_ptr, "No space in chunk cache for iTXt");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+ }
+#endif
+
+ if (!(png_ptr->mode & PNG_HAVE_IHDR))
+ png_error(png_ptr, "Missing IHDR before iTXt");
+
+ if (png_ptr->mode & PNG_HAVE_IDAT)
+ png_ptr->mode |= PNG_AFTER_IDAT;
+
+#ifdef PNG_MAX_MALLOC_64K
+ /* We will no doubt have problems with chunks even half this size, but
+ * there is no hard and fast rule to tell us where to stop.
+ */
+ if (length > (png_uint_32)65535L)
+ {
+ png_warning(png_ptr, "iTXt chunk too large to fit in memory");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+#endif
+
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1);
+
+ if (png_ptr->chunkdata == NULL)
+ {
+ png_warning(png_ptr, "No memory to process iTXt chunk");
+ return;
+ }
+
+ slength = (png_size_t)length;
+ png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength);
+
+ if (png_crc_finish(png_ptr, 0))
+ {
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ return;
+ }
+
+ png_ptr->chunkdata[slength] = 0x00;
+
+ for (lang = png_ptr->chunkdata; *lang; lang++)
+ /* Empty loop */ ;
+
+ lang++; /* Skip NUL separator */
+
+ /* iTXt must have a language tag (possibly empty), two compression bytes,
+ * translated keyword (possibly empty), and possibly some text after the
+ * keyword
+ */
+
+ if (lang >= png_ptr->chunkdata + slength - 3)
+ {
+ png_warning(png_ptr, "Truncated iTXt chunk");
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ return;
+ }
+
+ else
+ {
+ comp_flag = *lang++;
+ comp_type = *lang++;
+ }
+
+ for (lang_key = lang; *lang_key; lang_key++)
+ /* Empty loop */ ;
+
+ lang_key++; /* Skip NUL separator */
+
+ if (lang_key >= png_ptr->chunkdata + slength)
+ {
+ png_warning(png_ptr, "Truncated iTXt chunk");
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ return;
+ }
+
+ for (text = lang_key; *text; text++)
+ /* Empty loop */ ;
+
+ text++; /* Skip NUL separator */
+
+ if (text >= png_ptr->chunkdata + slength)
+ {
+ png_warning(png_ptr, "Malformed iTXt chunk");
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ return;
+ }
+
+ prefix_len = text - png_ptr->chunkdata;
+
+ key=png_ptr->chunkdata;
+
+ if (comp_flag)
+ png_decompress_chunk(png_ptr, comp_type,
+ (size_t)length, prefix_len, &data_len);
+
+ else
+ data_len = png_strlen(png_ptr->chunkdata + prefix_len);
+
+ text_ptr = (png_textp)png_malloc_warn(png_ptr,
+ png_sizeof(png_text));
+
+ if (text_ptr == NULL)
+ {
+ png_warning(png_ptr, "Not enough memory to process iTXt chunk");
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+ return;
+ }
+
+ text_ptr->compression = (int)comp_flag + 1;
+ text_ptr->lang_key = png_ptr->chunkdata + (lang_key - key);
+ text_ptr->lang = png_ptr->chunkdata + (lang - key);
+ text_ptr->itxt_length = data_len;
+ text_ptr->text_length = 0;
+ text_ptr->key = png_ptr->chunkdata;
+ text_ptr->text = png_ptr->chunkdata + prefix_len;
+
+ ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+ png_free(png_ptr, text_ptr);
+ png_free(png_ptr, png_ptr->chunkdata);
+ png_ptr->chunkdata = NULL;
+
+ if (ret)
+ png_error(png_ptr, "Insufficient memory to store iTXt chunk");
+}
+#endif
+
+/* This function is called when we haven't found a handler for a
+ * chunk. If there isn't a problem with the chunk itself (ie bad
+ * chunk name, CRC, or a critical chunk), the chunk is silently ignored
+ * -- unless the PNG_FLAG_UNKNOWN_CHUNKS_SUPPORTED flag is on in which
+ * case it will be saved away to be written out later.
+ */
+void /* PRIVATE */
+png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+ png_uint_32 skip = 0;
+
+ png_debug(1, "in png_handle_unknown");
+
+#ifdef PNG_USER_LIMITS_SUPPORTED
+ if (png_ptr->user_chunk_cache_max != 0)
+ {
+ if (png_ptr->user_chunk_cache_max == 1)
+ {
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+
+ if (--png_ptr->user_chunk_cache_max == 1)
+ {
+ png_warning(png_ptr, "No space in chunk cache for unknown chunk");
+ png_crc_finish(png_ptr, length);
+ return;
+ }
+ }
+#endif
+
+ if (png_ptr->mode & PNG_HAVE_IDAT)
+ {
+ PNG_IDAT;
+
+ if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) /* Not an IDAT */
+ png_ptr->mode |= PNG_AFTER_IDAT;
+ }
+
+ if (!(png_ptr->chunk_name[0] & 0x20))
+ {
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name) !=
+ PNG_HANDLE_CHUNK_ALWAYS
+#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+ && png_ptr->read_user_chunk_fn == NULL
+#endif
+ )
+#endif
+ png_chunk_error(png_ptr, "unknown critical chunk");
+ }
+
+#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+ if ((png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS)
+#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+ || (png_ptr->read_user_chunk_fn != NULL)
+#endif
+ )
+ {
+#ifdef PNG_MAX_MALLOC_64K
+ if (length > (png_uint_32)65535L)
+ {
+ png_warning(png_ptr, "unknown chunk too large to fit in memory");
+ skip = length - (png_uint_32)65535L;
+ length = (png_uint_32)65535L;
+ }
+#endif
+
+ png_memcpy((png_charp)png_ptr->unknown_chunk.name,
+ (png_charp)png_ptr->chunk_name,
+ png_sizeof(png_ptr->unknown_chunk.name));
+
+ png_ptr->unknown_chunk.name[png_sizeof(png_ptr->unknown_chunk.name)-1]
+ = '\0';
+
+ png_ptr->unknown_chunk.size = (png_size_t)length;
+
+ if (length == 0)
+ png_ptr->unknown_chunk.data = NULL;
+
+ else
+ {
+ png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, length);
+ png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length);
+ }
+
+#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+ if (png_ptr->read_user_chunk_fn != NULL)
+ {
+ /* Callback to user unknown chunk handler */
+ int ret;
+
+ ret = (*(png_ptr->read_user_chunk_fn))
+ (png_ptr, &png_ptr->unknown_chunk);
+
+ if (ret < 0)
+ png_chunk_error(png_ptr, "error in user chunk");
+
+ if (ret == 0)
+ {
+ if (!(png_ptr->chunk_name[0] & 0x20))
+ {
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name) !=
+ PNG_HANDLE_CHUNK_ALWAYS)
+#endif
+ png_chunk_error(png_ptr, "unknown critical chunk");
+ }
+
+ png_set_unknown_chunks(png_ptr, info_ptr,
+ &png_ptr->unknown_chunk, 1);
+ }
+ }
+
+ else
+#endif
+ png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1);
+
+ png_free(png_ptr, png_ptr->unknown_chunk.data);
+ png_ptr->unknown_chunk.data = NULL;
+ }
+
+ else
+#endif
+ skip = length;
+
+ png_crc_finish(png_ptr, skip);
+
+#ifndef PNG_READ_USER_CHUNKS_SUPPORTED
+ PNG_UNUSED(info_ptr) /* Quiet compiler warnings about unused info_ptr */
+#endif
+}
+
+/* This function is called to verify that a chunk name is valid.
+ * This function can't have the "critical chunk check" incorporated
+ * into it, since in the future we will need to be able to call user
+ * functions to handle unknown critical chunks after we check that
+ * the chunk name itself is valid.
+ */
+
+#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
+
+void /* PRIVATE */
+png_check_chunk_name(png_structp png_ptr, png_const_bytep chunk_name)
+{
+ png_debug(1, "in png_check_chunk_name");
+ if (isnonalpha(chunk_name[0]) || isnonalpha(chunk_name[1]) ||
+ isnonalpha(chunk_name[2]) || isnonalpha(chunk_name[3]))
+ {
+ png_chunk_error(png_ptr, "invalid chunk type");
+ }
+}
+
+/* Combines the row recently read in with the existing pixels in the
+ * row. This routine takes care of alpha and transparency if requested.
+ * This routine also handles the two methods of progressive display
+ * of interlaced images, depending on the mask value.
+ * The mask value describes which pixels are to be combined with
+ * the row. The pattern always repeats every 8 pixels, so just 8
+ * bits are needed. A one indicates the pixel is to be combined,
+ * a zero indicates the pixel is to be skipped. This is in addition
+ * to any alpha or transparency value associated with the pixel. If
+ * you want all pixels to be combined, pass 0xff (255) in mask.
+ */
+
+void /* PRIVATE */
+png_combine_row(png_structp png_ptr, png_bytep row, int mask)
+{
+ png_debug(1, "in png_combine_row");
+
+ if (mask == 0xff)
+ {
+ png_memcpy(row, png_ptr->row_buf + 1,
+ PNG_ROWBYTES(png_ptr->row_info.pixel_depth, png_ptr->width));
+ }
+
+ else
+ {
+ switch (png_ptr->row_info.pixel_depth)
+ {
+ case 1:
+ {
+ png_bytep sp = png_ptr->row_buf + 1;
+ png_bytep dp = row;
+ int s_inc, s_start, s_end;
+ int m = 0x80;
+ int shift;
+ png_uint_32 i;
+ png_uint_32 row_width = png_ptr->width;
+
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+ if (png_ptr->transformations & PNG_PACKSWAP)
+ {
+ s_start = 0;
+ s_end = 7;
+ s_inc = 1;
+ }
+
+ else
+#endif
+ {
+ s_start = 7;
+ s_end = 0;
+ s_inc = -1;
+ }
+
+ shift = s_start;
+
+ for (i = 0; i < row_width; i++)
+ {
+ if (m & mask)
+ {
+ int value;
+
+ value = (*sp >> shift) & 0x01;
+ *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff);
+ *dp |= (png_byte)(value << shift);
+ }
+
+ if (shift == s_end)
+ {
+ shift = s_start;
+ sp++;
+ dp++;
+ }
+
+ else
+ shift += s_inc;
+
+ if (m == 1)
+ m = 0x80;
+
+ else
+ m >>= 1;
+ }
+ break;
+ }
+
+ case 2:
+ {
+ png_bytep sp = png_ptr->row_buf + 1;
+ png_bytep dp = row;
+ int s_start, s_end, s_inc;
+ int m = 0x80;
+ int shift;
+ png_uint_32 i;
+ png_uint_32 row_width = png_ptr->width;
+ int value;
+
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+ if (png_ptr->transformations & PNG_PACKSWAP)
+ {
+ s_start = 0;
+ s_end = 6;
+ s_inc = 2;
+ }
+
+ else
+#endif
+ {
+ s_start = 6;
+ s_end = 0;
+ s_inc = -2;
+ }
+
+ shift = s_start;
+
+ for (i = 0; i < row_width; i++)
+ {
+ if (m & mask)
+ {
+ value = (*sp >> shift) & 0x03;
+ *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+ *dp |= (png_byte)(value << shift);
+ }
+
+ if (shift == s_end)
+ {
+ shift = s_start;
+ sp++;
+ dp++;
+ }
+
+ else
+ shift += s_inc;
+
+ if (m == 1)
+ m = 0x80;
+
+ else
+ m >>= 1;
+ }
+ break;
+ }
+
+ case 4:
+ {
+ png_bytep sp = png_ptr->row_buf + 1;
+ png_bytep dp = row;
+ int s_start, s_end, s_inc;
+ int m = 0x80;
+ int shift;
+ png_uint_32 i;
+ png_uint_32 row_width = png_ptr->width;
+ int value;
+
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+ if (png_ptr->transformations & PNG_PACKSWAP)
+ {
+ s_start = 0;
+ s_end = 4;
+ s_inc = 4;
+ }
+
+ else
+#endif
+ {
+ s_start = 4;
+ s_end = 0;
+ s_inc = -4;
+ }
+ shift = s_start;
+
+ for (i = 0; i < row_width; i++)
+ {
+ if (m & mask)
+ {
+ value = (*sp >> shift) & 0xf;
+ *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+ *dp |= (png_byte)(value << shift);
+ }
+
+ if (shift == s_end)
+ {
+ shift = s_start;
+ sp++;
+ dp++;
+ }
+
+ else
+ shift += s_inc;
+
+ if (m == 1)
+ m = 0x80;
+
+ else
+ m >>= 1;
+ }
+ break;
+ }
+
+ default:
+ {
+ png_bytep sp = png_ptr->row_buf + 1;
+ png_bytep dp = row;
+ png_size_t pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
+ png_uint_32 i;
+ png_uint_32 row_width = png_ptr->width;
+ png_byte m = 0x80;
+
+ for (i = 0; i < row_width; i++)
+ {
+ if (m & mask)
+ {
+ png_memcpy(dp, sp, pixel_bytes);
+ }
+
+ sp += pixel_bytes;
+ dp += pixel_bytes;
+
+ if (m == 1)
+ m = 0x80;
+
+ else
+ m >>= 1;
+ }
+ break;
+ }
+ }
+ }
+}
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+void /* PRIVATE */
+png_do_read_interlace(png_structp png_ptr)
+{
+ png_row_infop row_info = &(png_ptr->row_info);
+ png_bytep row = png_ptr->row_buf + 1;
+ int pass = png_ptr->pass;
+ png_uint_32 transformations = png_ptr->transformations;
+ /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+ /* Offset to next interlace block */
+ PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+ png_debug(1, "in png_do_read_interlace");
+ if (row != NULL && row_info != NULL)
+ {
+ png_uint_32 final_width;
+
+ final_width = row_info->width * png_pass_inc[pass];
+
+ switch (row_info->pixel_depth)
+ {
+ case 1:
+ {
+ png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3);
+ png_bytep dp = row + (png_size_t)((final_width - 1) >> 3);
+ int sshift, dshift;
+ int s_start, s_end, s_inc;
+ int jstop = png_pass_inc[pass];
+ png_byte v;
+ png_uint_32 i;
+ int j;
+
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+ if (transformations & PNG_PACKSWAP)
+ {
+ sshift = (int)((row_info->width + 7) & 0x07);
+ dshift = (int)((final_width + 7) & 0x07);
+ s_start = 7;
+ s_end = 0;
+ s_inc = -1;
+ }
+
+ else
+#endif
+ {
+ sshift = 7 - (int)((row_info->width + 7) & 0x07);
+ dshift = 7 - (int)((final_width + 7) & 0x07);
+ s_start = 0;
+ s_end = 7;
+ s_inc = 1;
+ }
+
+ for (i = 0; i < row_info->width; i++)
+ {
+ v = (png_byte)((*sp >> sshift) & 0x01);
+ for (j = 0; j < jstop; j++)
+ {
+ *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff);
+ *dp |= (png_byte)(v << dshift);
+
+ if (dshift == s_end)
+ {
+ dshift = s_start;
+ dp--;
+ }
+
+ else
+ dshift += s_inc;
+ }
+
+ if (sshift == s_end)
+ {
+ sshift = s_start;
+ sp--;
+ }
+
+ else
+ sshift += s_inc;
+ }
+ break;
+ }
+
+ case 2:
+ {
+ png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2);
+ png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2);
+ int sshift, dshift;
+ int s_start, s_end, s_inc;
+ int jstop = png_pass_inc[pass];
+ png_uint_32 i;
+
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+ if (transformations & PNG_PACKSWAP)
+ {
+ sshift = (int)(((row_info->width + 3) & 0x03) << 1);
+ dshift = (int)(((final_width + 3) & 0x03) << 1);
+ s_start = 6;
+ s_end = 0;
+ s_inc = -2;
+ }
+
+ else
+#endif
+ {
+ sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1);
+ dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1);
+ s_start = 0;
+ s_end = 6;
+ s_inc = 2;
+ }
+
+ for (i = 0; i < row_info->width; i++)
+ {
+ png_byte v;
+ int j;
+
+ v = (png_byte)((*sp >> sshift) & 0x03);
+ for (j = 0; j < jstop; j++)
+ {
+ *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff);
+ *dp |= (png_byte)(v << dshift);
+
+ if (dshift == s_end)
+ {
+ dshift = s_start;
+ dp--;
+ }
+
+ else
+ dshift += s_inc;
+ }
+
+ if (sshift == s_end)
+ {
+ sshift = s_start;
+ sp--;
+ }
+
+ else
+ sshift += s_inc;
+ }
+ break;
+ }
+
+ case 4:
+ {
+ png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1);
+ png_bytep dp = row + (png_size_t)((final_width - 1) >> 1);
+ int sshift, dshift;
+ int s_start, s_end, s_inc;
+ png_uint_32 i;
+ int jstop = png_pass_inc[pass];
+
+#ifdef PNG_READ_PACKSWAP_SUPPORTED
+ if (transformations & PNG_PACKSWAP)
+ {
+ sshift = (int)(((row_info->width + 1) & 0x01) << 2);
+ dshift = (int)(((final_width + 1) & 0x01) << 2);
+ s_start = 4;
+ s_end = 0;
+ s_inc = -4;
+ }
+
+ else
+#endif
+ {
+ sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2);
+ dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2);
+ s_start = 0;
+ s_end = 4;
+ s_inc = 4;
+ }
+
+ for (i = 0; i < row_info->width; i++)
+ {
+ png_byte v = (png_byte)((*sp >> sshift) & 0xf);
+ int j;
+
+ for (j = 0; j < jstop; j++)
+ {
+ *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff);
+ *dp |= (png_byte)(v << dshift);
+
+ if (dshift == s_end)
+ {
+ dshift = s_start;
+ dp--;
+ }
+
+ else
+ dshift += s_inc;
+ }
+
+ if (sshift == s_end)
+ {
+ sshift = s_start;
+ sp--;
+ }
+
+ else
+ sshift += s_inc;
+ }
+ break;
+ }
+ default:
+ {
+ png_size_t pixel_bytes = (row_info->pixel_depth >> 3);
+
+ png_bytep sp = row + (png_size_t)(row_info->width - 1)
+ * pixel_bytes;
+
+ png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes;
+
+ int jstop = png_pass_inc[pass];
+ png_uint_32 i;
+
+ for (i = 0; i < row_info->width; i++)
+ {
+ png_byte v[8];
+ int j;
+
+ png_memcpy(v, sp, pixel_bytes);
+
+ for (j = 0; j < jstop; j++)
+ {
+ png_memcpy(dp, v, pixel_bytes);
+ dp -= pixel_bytes;
+ }
+
+ sp -= pixel_bytes;
+ }
+ break;
+ }
+ }
+ row_info->width = final_width;
+ row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, final_width);
+ }
+#ifndef PNG_READ_PACKSWAP_SUPPORTED
+ PNG_UNUSED(transformations) /* Silence compiler warning */
+#endif
+}
+#endif /* PNG_READ_INTERLACING_SUPPORTED */
+
+void /* PRIVATE */
+png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep row,
+ png_const_bytep prev_row, int filter)
+{
+ png_debug(1, "in png_read_filter_row");
+ png_debug2(2, "row = %u, filter = %d", png_ptr->row_number, filter);
+ switch (filter)
+ {
+ case PNG_FILTER_VALUE_NONE:
+ break;
+
+ case PNG_FILTER_VALUE_SUB:
+ {
+ png_size_t i;
+ png_size_t istop = row_info->rowbytes;
+ unsigned int bpp = (row_info->pixel_depth + 7) >> 3;
+ png_bytep rp = row + bpp;
+ png_bytep lp = row;
+
+ for (i = bpp; i < istop; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff);
+ rp++;
+ }
+ break;
+ }
+ case PNG_FILTER_VALUE_UP:
+ {
+ png_size_t i;
+ png_size_t istop = row_info->rowbytes;
+ png_bytep rp = row;
+ png_const_bytep pp = prev_row;
+
+ for (i = 0; i < istop; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+ rp++;
+ }
+ break;
+ }
+ case PNG_FILTER_VALUE_AVG:
+ {
+ png_size_t i;
+ png_bytep rp = row;
+ png_const_bytep pp = prev_row;
+ png_bytep lp = row;
+ unsigned int bpp = (row_info->pixel_depth + 7) >> 3;
+ png_size_t istop = row_info->rowbytes - bpp;
+
+ for (i = 0; i < bpp; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) +
+ ((int)(*pp++) / 2 )) & 0xff);
+
+ rp++;
+ }
+
+ for (i = 0; i < istop; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) +
+ (int)(*pp++ + *lp++) / 2 ) & 0xff);
+
+ rp++;
+ }
+ break;
+ }
+ case PNG_FILTER_VALUE_PAETH:
+ {
+ png_size_t i;
+ png_bytep rp = row;
+ png_const_bytep pp = prev_row;
+ png_bytep lp = row;
+ png_const_bytep cp = prev_row;
+ unsigned int bpp = (row_info->pixel_depth + 7) >> 3;
+ png_size_t istop=row_info->rowbytes - bpp;
+
+ for (i = 0; i < bpp; i++)
+ {
+ *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+ rp++;
+ }
+
+ for (i = 0; i < istop; i++) /* Use leftover rp,pp */
+ {
+ int a, b, c, pa, pb, pc, p;
+
+ a = *lp++;
+ b = *pp++;
+ c = *cp++;
+
+ p = b - c;
+ pc = a - c;
+
+#ifdef PNG_USE_ABS
+ pa = abs(p);
+ pb = abs(pc);
+ pc = abs(p + pc);
+#else
+ pa = p < 0 ? -p : p;
+ pb = pc < 0 ? -pc : pc;
+ pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+
+ /*
+ if (pa <= pb && pa <= pc)
+ p = a;
+
+ else if (pb <= pc)
+ p = b;
+
+ else
+ p = c;
+ */
+
+ p = (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c;
+
+ *rp = (png_byte)(((int)(*rp) + p) & 0xff);
+ rp++;
+ }
+ break;
+ }
+ default:
+ png_error(png_ptr, "Ignoring bad adaptive filter type");
+ /*NOT REACHED */
+ break;
+ }
+}
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+void /* PRIVATE */
+png_read_finish_row(png_structp png_ptr)
+{
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+ /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+ /* Start of interlace block */
+ PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+ /* Offset to next interlace block */
+ PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+ /* Start of interlace block in the y direction */
+ PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+
+ /* Offset to next interlace block in the y direction */
+ PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+#endif /* PNG_READ_INTERLACING_SUPPORTED */
+
+ png_debug(1, "in png_read_finish_row");
+ png_ptr->row_number++;
+ if (png_ptr->row_number < png_ptr->num_rows)
+ return;
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+ if (png_ptr->interlaced)
+ {
+ png_ptr->row_number = 0;
+
+ png_memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
+
+ do
+ {
+ png_ptr->pass++;
+
+ if (png_ptr->pass >= 7)
+ break;
+
+ png_ptr->iwidth = (png_ptr->width +
+ png_pass_inc[png_ptr->pass] - 1 -
+ png_pass_start[png_ptr->pass]) /
+ png_pass_inc[png_ptr->pass];
+
+ if (!(png_ptr->transformations & PNG_INTERLACE))
+ {
+ png_ptr->num_rows = (png_ptr->height +
+ png_pass_yinc[png_ptr->pass] - 1 -
+ png_pass_ystart[png_ptr->pass]) /
+ png_pass_yinc[png_ptr->pass];
+ }
+
+ else /* if (png_ptr->transformations & PNG_INTERLACE) */
+ break; /* libpng deinterlacing sees every row */
+
+ } while (png_ptr->num_rows == 0 || png_ptr->iwidth == 0);
+
+ if (png_ptr->pass < 7)
+ return;
+ }
+#endif /* PNG_READ_INTERLACING_SUPPORTED */
+
+ if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED))
+ {
+ PNG_IDAT;
+ char extra;
+ int ret;
+
+ png_ptr->zstream.next_out = (Byte *)&extra;
+ png_ptr->zstream.avail_out = (uInt)1;
+
+ for (;;)
+ {
+ if (!(png_ptr->zstream.avail_in))
+ {
+ while (!png_ptr->idat_size)
+ {
+ png_crc_finish(png_ptr, 0);
+ png_ptr->idat_size = png_read_chunk_header(png_ptr);
+ if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+ png_error(png_ptr, "Not enough image data");
+ }
+
+ png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size;
+ png_ptr->zstream.next_in = png_ptr->zbuf;
+
+ if (png_ptr->zbuf_size > png_ptr->idat_size)
+ png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size;
+
+ png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream.avail_in);
+ png_ptr->idat_size -= png_ptr->zstream.avail_in;
+ }
+
+ ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+
+ if (ret == Z_STREAM_END)
+ {
+ if (!(png_ptr->zstream.avail_out) || png_ptr->zstream.avail_in ||
+ png_ptr->idat_size)
+ png_warning(png_ptr, "Extra compressed data");
+
+ png_ptr->mode |= PNG_AFTER_IDAT;
+ png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+ break;
+ }
+
+ if (ret != Z_OK)
+ png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg :
+ "Decompression Error");
+
+ if (!(png_ptr->zstream.avail_out))
+ {
+ png_warning(png_ptr, "Extra compressed data");
+ png_ptr->mode |= PNG_AFTER_IDAT;
+ png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+ break;
+ }
+
+ }
+ png_ptr->zstream.avail_out = 0;
+ }
+
+ if (png_ptr->idat_size || png_ptr->zstream.avail_in)
+ png_warning(png_ptr, "Extra compression data");
+
+ inflateReset(&png_ptr->zstream);
+
+ png_ptr->mode |= PNG_AFTER_IDAT;
+}
+#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */
+
+void /* PRIVATE */
+png_read_start_row(png_structp png_ptr)
+{
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+ /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+ /* Start of interlace block */
+ PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+ /* Offset to next interlace block */
+ PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+ /* Start of interlace block in the y direction */
+ PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+
+ /* Offset to next interlace block in the y direction */
+ PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+#endif
+
+ int max_pixel_depth;
+ png_size_t row_bytes;
+
+ png_debug(1, "in png_read_start_row");
+ png_ptr->zstream.avail_in = 0;
+ png_init_read_transformations(png_ptr);
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+ if (png_ptr->interlaced)
+ {
+ if (!(png_ptr->transformations & PNG_INTERLACE))
+ png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
+ png_pass_ystart[0]) / png_pass_yinc[0];
+
+ else
+ png_ptr->num_rows = png_ptr->height;
+
+ png_ptr->iwidth = (png_ptr->width +
+ png_pass_inc[png_ptr->pass] - 1 -
+ png_pass_start[png_ptr->pass]) /
+ png_pass_inc[png_ptr->pass];
+ }
+
+ else
+#endif /* PNG_READ_INTERLACING_SUPPORTED */
+ {
+ png_ptr->num_rows = png_ptr->height;
+ png_ptr->iwidth = png_ptr->width;
+ }
+
+ max_pixel_depth = png_ptr->pixel_depth;
+
+#ifdef PNG_READ_PACK_SUPPORTED
+ if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8)
+ max_pixel_depth = 8;
+#endif
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+ if (png_ptr->transformations & PNG_EXPAND)
+ {
+ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ if (png_ptr->num_trans)
+ max_pixel_depth = 32;
+
+ else
+ max_pixel_depth = 24;
+ }
+
+ else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
+ {
+ if (max_pixel_depth < 8)
+ max_pixel_depth = 8;
+
+ if (png_ptr->num_trans)
+ max_pixel_depth *= 2;
+ }
+
+ else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+ {
+ if (png_ptr->num_trans)
+ {
+ max_pixel_depth *= 4;
+ max_pixel_depth /= 3;
+ }
+ }
+ }
+#endif
+
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+ if (png_ptr->transformations & PNG_EXPAND_16)
+ {
+# ifdef PNG_READ_EXPAND_SUPPORTED
+ /* In fact it is an error if it isn't supported, but checking is
+ * the safe way.
+ */
+ if (png_ptr->transformations & PNG_EXPAND)
+ {
+ if (png_ptr->bit_depth < 16)
+ max_pixel_depth *= 2;
+ }
+ else
+# endif
+ png_ptr->transformations &= ~PNG_EXPAND_16;
+ }
+#endif
+
+#ifdef PNG_READ_FILLER_SUPPORTED
+ if (png_ptr->transformations & (PNG_FILLER))
+ {
+ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ max_pixel_depth = 32;
+
+ else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
+ {
+ if (max_pixel_depth <= 8)
+ max_pixel_depth = 16;
+
+ else
+ max_pixel_depth = 32;
+ }
+
+ else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+ {
+ if (max_pixel_depth <= 32)
+ max_pixel_depth = 32;
+
+ else
+ max_pixel_depth = 64;
+ }
+ }
+#endif
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+ if (png_ptr->transformations & PNG_GRAY_TO_RGB)
+ {
+ if (
+#ifdef PNG_READ_EXPAND_SUPPORTED
+ (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) ||
+#endif
+#ifdef PNG_READ_FILLER_SUPPORTED
+ (png_ptr->transformations & (PNG_FILLER)) ||
+#endif
+ png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ {
+ if (max_pixel_depth <= 16)
+ max_pixel_depth = 32;
+
+ else
+ max_pixel_depth = 64;
+ }
+
+ else
+ {
+ if (max_pixel_depth <= 8)
+ {
+ if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ max_pixel_depth = 32;
+
+ else
+ max_pixel_depth = 24;
+ }
+
+ else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ max_pixel_depth = 64;
+
+ else
+ max_pixel_depth = 48;
+ }
+ }
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \
+defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
+ if (png_ptr->transformations & PNG_USER_TRANSFORM)
+ {
+ int user_pixel_depth = png_ptr->user_transform_depth*
+ png_ptr->user_transform_channels;
+
+ if (user_pixel_depth > max_pixel_depth)
+ max_pixel_depth=user_pixel_depth;
+ }
+#endif
+
+ /* Align the width on the next larger 8 pixels. Mainly used
+ * for interlacing
+ */
+ row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7));
+ /* Calculate the maximum bytes needed, adding a byte and a pixel
+ * for safety's sake
+ */
+ row_bytes = PNG_ROWBYTES(max_pixel_depth, row_bytes) +
+ 1 + ((max_pixel_depth + 7) >> 3);
+
+#ifdef PNG_MAX_MALLOC_64K
+ if (row_bytes > (png_uint_32)65536L)
+ png_error(png_ptr, "This image requires a row greater than 64KB");
+#endif
+
+ if (row_bytes + 48 > png_ptr->old_big_row_buf_size)
+ {
+ png_free(png_ptr, png_ptr->big_row_buf);
+
+ if (png_ptr->interlaced)
+ png_ptr->big_row_buf = (png_bytep)png_calloc(png_ptr,
+ row_bytes + 48);
+
+ else
+ png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr,
+ row_bytes + 48);
+
+ png_ptr->old_big_row_buf_size = row_bytes + 48;
+
+#ifdef PNG_ALIGNED_MEMORY_SUPPORTED
+ /* Use 16-byte aligned memory for row_buf with at least 16 bytes
+ * of padding before and after row_buf.
+ */
+ png_ptr->row_buf = png_ptr->big_row_buf + 32 -
+ (((png_alloc_size_t)png_ptr->big_row_buf + 15) & 0x0F);
+
+ png_ptr->old_big_row_buf_size = row_bytes + 48;
+#else
+ /* Use 32 bytes of padding before and 16 bytes after row_buf. */
+ png_ptr->row_buf = png_ptr->big_row_buf + 32;
+#endif
+ png_ptr->old_big_row_buf_size = row_bytes + 48;
+ }
+
+#ifdef PNG_MAX_MALLOC_64K
+ if (png_ptr->rowbytes > 65535)
+ png_error(png_ptr, "This image requires a row greater than 64KB");
+
+#endif
+ if (png_ptr->rowbytes > (PNG_SIZE_MAX - 1))
+ png_error(png_ptr, "Row has too many bytes to allocate in memory");
+
+ if (png_ptr->rowbytes + 1 > png_ptr->old_prev_row_size)
+ {
+ png_free(png_ptr, png_ptr->prev_row);
+
+ png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, png_ptr->rowbytes + 1);
+
+ png_ptr->old_prev_row_size = png_ptr->rowbytes + 1;
+ }
+
+ png_memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
+
+ png_debug1(3, "width = %u,", png_ptr->width);
+ png_debug1(3, "height = %u,", png_ptr->height);
+ png_debug1(3, "iwidth = %u,", png_ptr->iwidth);
+ png_debug1(3, "num_rows = %u,", png_ptr->num_rows);
+ png_debug1(3, "rowbytes = %lu,", (unsigned long)png_ptr->rowbytes);
+ png_debug1(3, "irowbytes = %lu",
+ (unsigned long)PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1);
+
+ png_ptr->flags |= PNG_FLAG_ROW_INIT;
+}
+#endif /* PNG_READ_SUPPORTED */
diff --git a/libpng/pngset.c b/libpng/pngset.c
new file mode 100644
index 0000000..2cfcf33
--- /dev/null
+++ b/libpng/pngset.c
@@ -0,0 +1,1225 @@
+
+/* pngset.c - storage of image information into info struct
+ *
+ * Last changed in libpng 1.5.1 [February 3, 2011]
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * The functions here are used during reads to store data from the file
+ * into the info struct, and during writes to store application data
+ * into the info struct for writing into the file. This abstracts the
+ * info struct and allows us to change the structure in the future.
+ */
+
+#include "pngpriv.h"
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+
+#ifdef PNG_bKGD_SUPPORTED
+void PNGAPI
+png_set_bKGD(png_structp png_ptr, png_infop info_ptr,
+ png_const_color_16p background)
+{
+ png_debug1(1, "in %s storage function", "bKGD");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ png_memcpy(&(info_ptr->background), background, png_sizeof(png_color_16));
+ info_ptr->valid |= PNG_INFO_bKGD;
+}
+#endif
+
+#ifdef PNG_cHRM_SUPPORTED
+void PNGFAPI
+png_set_cHRM_fixed(png_structp png_ptr, png_infop info_ptr,
+ png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x,
+ png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y,
+ png_fixed_point blue_x, png_fixed_point blue_y)
+{
+ png_debug1(1, "in %s storage function", "cHRM fixed");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+# ifdef PNG_CHECK_cHRM_SUPPORTED
+ if (png_check_cHRM_fixed(png_ptr,
+ white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y))
+# endif
+ {
+ info_ptr->x_white = white_x;
+ info_ptr->y_white = white_y;
+ info_ptr->x_red = red_x;
+ info_ptr->y_red = red_y;
+ info_ptr->x_green = green_x;
+ info_ptr->y_green = green_y;
+ info_ptr->x_blue = blue_x;
+ info_ptr->y_blue = blue_y;
+ info_ptr->valid |= PNG_INFO_cHRM;
+ }
+}
+
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_cHRM(png_structp png_ptr, png_infop info_ptr,
+ double white_x, double white_y, double red_x, double red_y,
+ double green_x, double green_y, double blue_x, double blue_y)
+{
+ png_set_cHRM_fixed(png_ptr, info_ptr,
+ png_fixed(png_ptr, white_x, "cHRM White X"),
+ png_fixed(png_ptr, white_y, "cHRM White Y"),
+ png_fixed(png_ptr, red_x, "cHRM Red X"),
+ png_fixed(png_ptr, red_y, "cHRM Red Y"),
+ png_fixed(png_ptr, green_x, "cHRM Green X"),
+ png_fixed(png_ptr, green_y, "cHRM Green Y"),
+ png_fixed(png_ptr, blue_x, "cHRM Blue X"),
+ png_fixed(png_ptr, blue_y, "cHRM Blue Y"));
+}
+# endif /* PNG_FLOATING_POINT_SUPPORTED */
+
+#endif /* PNG_cHRM_SUPPORTED */
+
+#ifdef PNG_gAMA_SUPPORTED
+void PNGFAPI
+png_set_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point
+ file_gamma)
+{
+ png_debug1(1, "in %s storage function", "gAMA");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ /* Previously these values were limited, however they must be
+ * wrong, therefore storing them (and setting PNG_INFO_gAMA)
+ * must be wrong too.
+ */
+ if (file_gamma > (png_fixed_point)PNG_UINT_31_MAX)
+ png_warning(png_ptr, "Gamma too large, ignored");
+
+ else if (file_gamma <= 0)
+ png_warning(png_ptr, "Negative or zero gamma ignored");
+
+ else
+ {
+ info_ptr->gamma = file_gamma;
+ info_ptr->valid |= PNG_INFO_gAMA;
+ }
+}
+
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_gAMA(png_structp png_ptr, png_infop info_ptr, double file_gamma)
+{
+ png_set_gAMA_fixed(png_ptr, info_ptr, png_fixed(png_ptr, file_gamma,
+ "png_set_gAMA"));
+}
+# endif
+#endif
+
+#ifdef PNG_hIST_SUPPORTED
+void PNGAPI
+png_set_hIST(png_structp png_ptr, png_infop info_ptr, png_const_uint_16p hist)
+{
+ int i;
+
+ png_debug1(1, "in %s storage function", "hIST");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ if (info_ptr->num_palette == 0 || info_ptr->num_palette
+ > PNG_MAX_PALETTE_LENGTH)
+ {
+ png_warning(png_ptr,
+ "Invalid palette size, hIST allocation skipped");
+
+ return;
+ }
+
+ png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0);
+
+ /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in
+ * version 1.2.1
+ */
+ png_ptr->hist = (png_uint_16p)png_malloc_warn(png_ptr,
+ PNG_MAX_PALETTE_LENGTH * png_sizeof(png_uint_16));
+
+ if (png_ptr->hist == NULL)
+ {
+ png_warning(png_ptr, "Insufficient memory for hIST chunk data");
+ return;
+ }
+
+ for (i = 0; i < info_ptr->num_palette; i++)
+ png_ptr->hist[i] = hist[i];
+
+ info_ptr->hist = png_ptr->hist;
+ info_ptr->valid |= PNG_INFO_hIST;
+ info_ptr->free_me |= PNG_FREE_HIST;
+}
+#endif
+
+void PNGAPI
+png_set_IHDR(png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 width, png_uint_32 height, int bit_depth,
+ int color_type, int interlace_type, int compression_type,
+ int filter_type)
+{
+ png_debug1(1, "in %s storage function", "IHDR");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ info_ptr->width = width;
+ info_ptr->height = height;
+ info_ptr->bit_depth = (png_byte)bit_depth;
+ info_ptr->color_type = (png_byte)color_type;
+ info_ptr->compression_type = (png_byte)compression_type;
+ info_ptr->filter_type = (png_byte)filter_type;
+ info_ptr->interlace_type = (png_byte)interlace_type;
+
+ png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height,
+ info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type,
+ info_ptr->compression_type, info_ptr->filter_type);
+
+ if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ info_ptr->channels = 1;
+
+ else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
+ info_ptr->channels = 3;
+
+ else
+ info_ptr->channels = 1;
+
+ if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
+ info_ptr->channels++;
+
+ info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth);
+
+ /* Check for potential overflow */
+ if (width >
+ (PNG_UINT_32_MAX >> 3) /* 8-byte RRGGBBAA pixels */
+ - 48 /* bigrowbuf hack */
+ - 1 /* filter byte */
+ - 7*8 /* rounding of width to multiple of 8 pixels */
+ - 8) /* extra max_pixel_depth pad */
+ info_ptr->rowbytes = 0;
+ else
+ info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width);
+}
+
+#ifdef PNG_oFFs_SUPPORTED
+void PNGAPI
+png_set_oFFs(png_structp png_ptr, png_infop info_ptr,
+ png_int_32 offset_x, png_int_32 offset_y, int unit_type)
+{
+ png_debug1(1, "in %s storage function", "oFFs");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ info_ptr->x_offset = offset_x;
+ info_ptr->y_offset = offset_y;
+ info_ptr->offset_unit_type = (png_byte)unit_type;
+ info_ptr->valid |= PNG_INFO_oFFs;
+}
+#endif
+
+#ifdef PNG_pCAL_SUPPORTED
+void PNGAPI
+png_set_pCAL(png_structp png_ptr, png_infop info_ptr,
+ png_const_charp purpose, png_int_32 X0, png_int_32 X1, int type,
+ int nparams, png_const_charp units, png_charpp params)
+{
+ png_size_t length;
+ int i;
+
+ png_debug1(1, "in %s storage function", "pCAL");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ length = png_strlen(purpose) + 1;
+ png_debug1(3, "allocating purpose for info (%lu bytes)",
+ (unsigned long)length);
+
+ /* TODO: validate format of calibration name and unit name */
+
+ /* Check that the type matches the specification. */
+ if (type < 0 || type > 3)
+ png_error(png_ptr, "Invalid pCAL equation type");
+
+ /* Validate params[nparams] */
+ for (i=0; i<nparams; ++i)
+ if (!png_check_fp_string(params[i], png_strlen(params[i])))
+ png_error(png_ptr, "Invalid format for pCAL parameter");
+
+ info_ptr->pcal_purpose = (png_charp)png_malloc_warn(png_ptr, length);
+
+ if (info_ptr->pcal_purpose == NULL)
+ {
+ png_warning(png_ptr, "Insufficient memory for pCAL purpose");
+ return;
+ }
+
+ png_memcpy(info_ptr->pcal_purpose, purpose, length);
+
+ png_debug(3, "storing X0, X1, type, and nparams in info");
+ info_ptr->pcal_X0 = X0;
+ info_ptr->pcal_X1 = X1;
+ info_ptr->pcal_type = (png_byte)type;
+ info_ptr->pcal_nparams = (png_byte)nparams;
+
+ length = png_strlen(units) + 1;
+ png_debug1(3, "allocating units for info (%lu bytes)",
+ (unsigned long)length);
+
+ info_ptr->pcal_units = (png_charp)png_malloc_warn(png_ptr, length);
+
+ if (info_ptr->pcal_units == NULL)
+ {
+ png_warning(png_ptr, "Insufficient memory for pCAL units");
+ return;
+ }
+
+ png_memcpy(info_ptr->pcal_units, units, length);
+
+ info_ptr->pcal_params = (png_charpp)png_malloc_warn(png_ptr,
+ (png_size_t)((nparams + 1) * png_sizeof(png_charp)));
+
+ if (info_ptr->pcal_params == NULL)
+ {
+ png_warning(png_ptr, "Insufficient memory for pCAL params");
+ return;
+ }
+
+ png_memset(info_ptr->pcal_params, 0, (nparams + 1) * png_sizeof(png_charp));
+
+ for (i = 0; i < nparams; i++)
+ {
+ length = png_strlen(params[i]) + 1;
+ png_debug2(3, "allocating parameter %d for info (%lu bytes)", i,
+ (unsigned long)length);
+
+ info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length);
+
+ if (info_ptr->pcal_params[i] == NULL)
+ {
+ png_warning(png_ptr, "Insufficient memory for pCAL parameter");
+ return;
+ }
+
+ png_memcpy(info_ptr->pcal_params[i], params[i], length);
+ }
+
+ info_ptr->valid |= PNG_INFO_pCAL;
+ info_ptr->free_me |= PNG_FREE_PCAL;
+}
+#endif
+
+#ifdef PNG_sCAL_SUPPORTED
+void PNGAPI
+png_set_sCAL_s(png_structp png_ptr, png_infop info_ptr,
+ int unit, png_const_charp swidth, png_const_charp sheight)
+{
+ png_size_t lengthw = 0, lengthh = 0;
+
+ png_debug1(1, "in %s storage function", "sCAL");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ /* Double check the unit (should never get here with an invalid
+ * unit unless this is an API call.)
+ */
+ if (unit != 1 && unit != 2)
+ png_error(png_ptr, "Invalid sCAL unit");
+
+ if (swidth == NULL || (lengthw = png_strlen(swidth)) <= 0 ||
+ swidth[0] == 45 /*'-'*/ || !png_check_fp_string(swidth, lengthw))
+ png_error(png_ptr, "Invalid sCAL width");
+
+ if (sheight == NULL || (lengthh = png_strlen(sheight)) <= 0 ||
+ sheight[0] == 45 /*'-'*/ || !png_check_fp_string(sheight, lengthh))
+ png_error(png_ptr, "Invalid sCAL height");
+
+ info_ptr->scal_unit = (png_byte)unit;
+
+ ++lengthw;
+
+ png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthw);
+
+ info_ptr->scal_s_width = (png_charp)png_malloc_warn(png_ptr, lengthw);
+
+ if (info_ptr->scal_s_width == NULL)
+ {
+ png_warning(png_ptr, "Memory allocation failed while processing sCAL");
+ return;
+ }
+
+ png_memcpy(info_ptr->scal_s_width, swidth, lengthw);
+
+ ++lengthh;
+
+ png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthh);
+
+ info_ptr->scal_s_height = (png_charp)png_malloc_warn(png_ptr, lengthh);
+
+ if (info_ptr->scal_s_height == NULL)
+ {
+ png_free (png_ptr, info_ptr->scal_s_width);
+ info_ptr->scal_s_width = NULL;
+
+ png_warning(png_ptr, "Memory allocation failed while processing sCAL");
+ return;
+ }
+
+ png_memcpy(info_ptr->scal_s_height, sheight, lengthh);
+
+ info_ptr->valid |= PNG_INFO_sCAL;
+ info_ptr->free_me |= PNG_FREE_SCAL;
+}
+
+# ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_sCAL(png_structp png_ptr, png_infop info_ptr, int unit, double width,
+ double height)
+{
+ png_debug1(1, "in %s storage function", "sCAL");
+
+ /* Check the arguments. */
+ if (width <= 0)
+ png_warning(png_ptr, "Invalid sCAL width ignored");
+
+ else if (height <= 0)
+ png_warning(png_ptr, "Invalid sCAL height ignored");
+
+ else
+ {
+ /* Convert 'width' and 'height' to ASCII. */
+ char swidth[PNG_sCAL_MAX_DIGITS+1];
+ char sheight[PNG_sCAL_MAX_DIGITS+1];
+
+ png_ascii_from_fp(png_ptr, swidth, sizeof swidth, width,
+ PNG_sCAL_PRECISION);
+ png_ascii_from_fp(png_ptr, sheight, sizeof sheight, height,
+ PNG_sCAL_PRECISION);
+
+ png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight);
+ }
+}
+# endif
+
+# ifdef PNG_FIXED_POINT_SUPPORTED
+void PNGAPI
+png_set_sCAL_fixed(png_structp png_ptr, png_infop info_ptr, int unit,
+ png_fixed_point width, png_fixed_point height)
+{
+ png_debug1(1, "in %s storage function", "sCAL");
+
+ /* Check the arguments. */
+ if (width <= 0)
+ png_warning(png_ptr, "Invalid sCAL width ignored");
+
+ else if (height <= 0)
+ png_warning(png_ptr, "Invalid sCAL height ignored");
+
+ else
+ {
+ /* Convert 'width' and 'height' to ASCII. */
+ char swidth[PNG_sCAL_MAX_DIGITS+1];
+ char sheight[PNG_sCAL_MAX_DIGITS+1];
+
+ png_ascii_from_fixed(png_ptr, swidth, sizeof swidth, width);
+ png_ascii_from_fixed(png_ptr, sheight, sizeof sheight, height);
+
+ png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight);
+ }
+}
+# endif
+#endif
+
+#ifdef PNG_pHYs_SUPPORTED
+void PNGAPI
+png_set_pHYs(png_structp png_ptr, png_infop info_ptr,
+ png_uint_32 res_x, png_uint_32 res_y, int unit_type)
+{
+ png_debug1(1, "in %s storage function", "pHYs");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ info_ptr->x_pixels_per_unit = res_x;
+ info_ptr->y_pixels_per_unit = res_y;
+ info_ptr->phys_unit_type = (png_byte)unit_type;
+ info_ptr->valid |= PNG_INFO_pHYs;
+}
+#endif
+
+void PNGAPI
+png_set_PLTE(png_structp png_ptr, png_infop info_ptr,
+ png_const_colorp palette, int num_palette)
+{
+
+ png_debug1(1, "in %s storage function", "PLTE");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH)
+ {
+ if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ png_error(png_ptr, "Invalid palette length");
+
+ else
+ {
+ png_warning(png_ptr, "Invalid palette length");
+ return;
+ }
+ }
+
+ /* It may not actually be necessary to set png_ptr->palette here;
+ * we do it for backward compatibility with the way the png_handle_tRNS
+ * function used to do the allocation.
+ */
+ png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0);
+
+ /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead
+ * of num_palette entries, in case of an invalid PNG file that has
+ * too-large sample values.
+ */
+ png_ptr->palette = (png_colorp)png_calloc(png_ptr,
+ PNG_MAX_PALETTE_LENGTH * png_sizeof(png_color));
+
+ png_memcpy(png_ptr->palette, palette, num_palette * png_sizeof(png_color));
+ info_ptr->palette = png_ptr->palette;
+ info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette;
+
+ info_ptr->free_me |= PNG_FREE_PLTE;
+
+ info_ptr->valid |= PNG_INFO_PLTE;
+}
+
+#ifdef PNG_sBIT_SUPPORTED
+void PNGAPI
+png_set_sBIT(png_structp png_ptr, png_infop info_ptr,
+ png_const_color_8p sig_bit)
+{
+ png_debug1(1, "in %s storage function", "sBIT");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ png_memcpy(&(info_ptr->sig_bit), sig_bit, png_sizeof(png_color_8));
+ info_ptr->valid |= PNG_INFO_sBIT;
+}
+#endif
+
+#ifdef PNG_sRGB_SUPPORTED
+void PNGAPI
+png_set_sRGB(png_structp png_ptr, png_infop info_ptr, int srgb_intent)
+{
+ png_debug1(1, "in %s storage function", "sRGB");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ info_ptr->srgb_intent = (png_byte)srgb_intent;
+ info_ptr->valid |= PNG_INFO_sRGB;
+}
+
+void PNGAPI
+png_set_sRGB_gAMA_and_cHRM(png_structp png_ptr, png_infop info_ptr,
+ int srgb_intent)
+{
+ png_debug1(1, "in %s storage function", "sRGB_gAMA_and_cHRM");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ png_set_sRGB(png_ptr, info_ptr, srgb_intent);
+
+# ifdef PNG_gAMA_SUPPORTED
+ png_set_gAMA_fixed(png_ptr, info_ptr, 45455L);
+# endif
+
+# ifdef PNG_cHRM_SUPPORTED
+ png_set_cHRM_fixed(png_ptr, info_ptr,
+ /* color x y */
+ /* white */ 31270L, 32900L,
+ /* red */ 64000L, 33000L,
+ /* green */ 30000L, 60000L,
+ /* blue */ 15000L, 6000L
+ );
+# endif /* cHRM */
+}
+#endif /* sRGB */
+
+
+#ifdef PNG_iCCP_SUPPORTED
+void PNGAPI
+png_set_iCCP(png_structp png_ptr, png_infop info_ptr,
+ png_const_charp name, int compression_type,
+ png_const_bytep profile, png_uint_32 proflen)
+{
+ png_charp new_iccp_name;
+ png_bytep new_iccp_profile;
+ png_uint_32 length;
+
+ png_debug1(1, "in %s storage function", "iCCP");
+
+ if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL)
+ return;
+
+ length = png_strlen(name)+1;
+ new_iccp_name = (png_charp)png_malloc_warn(png_ptr, length);
+
+ if (new_iccp_name == NULL)
+ {
+ png_warning(png_ptr, "Insufficient memory to process iCCP chunk");
+ return;
+ }
+
+ png_memcpy(new_iccp_name, name, length);
+ new_iccp_profile = (png_bytep)png_malloc_warn(png_ptr, proflen);
+
+ if (new_iccp_profile == NULL)
+ {
+ png_free (png_ptr, new_iccp_name);
+ png_warning(png_ptr,
+ "Insufficient memory to process iCCP profile");
+ return;
+ }
+
+ png_memcpy(new_iccp_profile, profile, (png_size_t)proflen);
+
+ png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0);
+
+ info_ptr->iccp_proflen = proflen;
+ info_ptr->iccp_name = new_iccp_name;
+ info_ptr->iccp_profile = new_iccp_profile;
+ /* Compression is always zero but is here so the API and info structure
+ * does not have to change if we introduce multiple compression types
+ */
+ info_ptr->iccp_compression = (png_byte)compression_type;
+ info_ptr->free_me |= PNG_FREE_ICCP;
+ info_ptr->valid |= PNG_INFO_iCCP;
+}
+#endif
+
+#ifdef PNG_TEXT_SUPPORTED
+void PNGAPI
+png_set_text(png_structp png_ptr, png_infop info_ptr, png_const_textp text_ptr,
+ int num_text)
+{
+ int ret;
+ ret = png_set_text_2(png_ptr, info_ptr, text_ptr, num_text);
+
+ if (ret)
+ png_error(png_ptr, "Insufficient memory to store text");
+}
+
+int /* PRIVATE */
+png_set_text_2(png_structp png_ptr, png_infop info_ptr,
+ png_const_textp text_ptr, int num_text)
+{
+ int i;
+
+ png_debug1(1, "in %s storage function", ((png_ptr == NULL ||
+ png_ptr->chunk_name[0] == '\0') ?
+ "text" : (png_const_charp)png_ptr->chunk_name));
+
+ if (png_ptr == NULL || info_ptr == NULL || num_text == 0)
+ return(0);
+
+ /* Make sure we have enough space in the "text" array in info_struct
+ * to hold all of the incoming text_ptr objects.
+ */
+ if (info_ptr->num_text + num_text > info_ptr->max_text)
+ {
+ if (info_ptr->text != NULL)
+ {
+ png_textp old_text;
+ int old_max;
+
+ old_max = info_ptr->max_text;
+ info_ptr->max_text = info_ptr->num_text + num_text + 8;
+ old_text = info_ptr->text;
+ info_ptr->text = (png_textp)png_malloc_warn(png_ptr,
+ (png_size_t)(info_ptr->max_text * png_sizeof(png_text)));
+
+ if (info_ptr->text == NULL)
+ {
+ png_free(png_ptr, old_text);
+ return(1);
+ }
+
+ png_memcpy(info_ptr->text, old_text, (png_size_t)(old_max *
+ png_sizeof(png_text)));
+ png_free(png_ptr, old_text);
+ }
+
+ else
+ {
+ info_ptr->max_text = num_text + 8;
+ info_ptr->num_text = 0;
+ info_ptr->text = (png_textp)png_malloc_warn(png_ptr,
+ (png_size_t)(info_ptr->max_text * png_sizeof(png_text)));
+ if (info_ptr->text == NULL)
+ return(1);
+ info_ptr->free_me |= PNG_FREE_TEXT;
+ }
+
+ png_debug1(3, "allocated %d entries for info_ptr->text",
+ info_ptr->max_text);
+ }
+ for (i = 0; i < num_text; i++)
+ {
+ png_size_t text_length, key_len;
+ png_size_t lang_len, lang_key_len;
+ png_textp textp = &(info_ptr->text[info_ptr->num_text]);
+
+ if (text_ptr[i].key == NULL)
+ continue;
+
+ if (text_ptr[i].compression < PNG_TEXT_COMPRESSION_NONE ||
+ text_ptr[i].compression >= PNG_TEXT_COMPRESSION_LAST)
+ {
+ png_warning(png_ptr, "text compression mode is out of range");
+ continue;
+ }
+
+ key_len = png_strlen(text_ptr[i].key);
+
+ if (text_ptr[i].compression <= 0)
+ {
+ lang_len = 0;
+ lang_key_len = 0;
+ }
+
+ else
+# ifdef PNG_iTXt_SUPPORTED
+ {
+ /* Set iTXt data */
+
+ if (text_ptr[i].lang != NULL)
+ lang_len = png_strlen(text_ptr[i].lang);
+
+ else
+ lang_len = 0;
+
+ if (text_ptr[i].lang_key != NULL)
+ lang_key_len = png_strlen(text_ptr[i].lang_key);
+
+ else
+ lang_key_len = 0;
+ }
+# else /* PNG_iTXt_SUPPORTED */
+ {
+ png_warning(png_ptr, "iTXt chunk not supported");
+ continue;
+ }
+# endif
+
+ if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0')
+ {
+ text_length = 0;
+# ifdef PNG_iTXt_SUPPORTED
+ if (text_ptr[i].compression > 0)
+ textp->compression = PNG_ITXT_COMPRESSION_NONE;
+
+ else
+# endif
+ textp->compression = PNG_TEXT_COMPRESSION_NONE;
+ }
+
+ else
+ {
+ text_length = png_strlen(text_ptr[i].text);
+ textp->compression = text_ptr[i].compression;
+ }
+
+ textp->key = (png_charp)png_malloc_warn(png_ptr,
+ (png_size_t)
+ (key_len + text_length + lang_len + lang_key_len + 4));
+
+ if (textp->key == NULL)
+ return(1);
+
+ png_debug2(2, "Allocated %lu bytes at %p in png_set_text",
+ (unsigned long)(png_uint_32)
+ (key_len + lang_len + lang_key_len + text_length + 4),
+ textp->key);
+
+ png_memcpy(textp->key, text_ptr[i].key,(png_size_t)(key_len));
+ *(textp->key + key_len) = '\0';
+
+ if (text_ptr[i].compression > 0)
+ {
+ textp->lang = textp->key + key_len + 1;
+ png_memcpy(textp->lang, text_ptr[i].lang, lang_len);
+ *(textp->lang + lang_len) = '\0';
+ textp->lang_key = textp->lang + lang_len + 1;
+ png_memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len);
+ *(textp->lang_key + lang_key_len) = '\0';
+ textp->text = textp->lang_key + lang_key_len + 1;
+ }
+
+ else
+ {
+ textp->lang=NULL;
+ textp->lang_key=NULL;
+ textp->text = textp->key + key_len + 1;
+ }
+
+ if (text_length)
+ png_memcpy(textp->text, text_ptr[i].text,
+ (png_size_t)(text_length));
+
+ *(textp->text + text_length) = '\0';
+
+# ifdef PNG_iTXt_SUPPORTED
+ if (textp->compression > 0)
+ {
+ textp->text_length = 0;
+ textp->itxt_length = text_length;
+ }
+
+ else
+# endif
+ {
+ textp->text_length = text_length;
+ textp->itxt_length = 0;
+ }
+
+ info_ptr->num_text++;
+ png_debug1(3, "transferred text chunk %d", info_ptr->num_text);
+ }
+ return(0);
+}
+#endif
+
+#ifdef PNG_tIME_SUPPORTED
+void PNGAPI
+png_set_tIME(png_structp png_ptr, png_infop info_ptr, png_const_timep mod_time)
+{
+ png_debug1(1, "in %s storage function", "tIME");
+
+ if (png_ptr == NULL || info_ptr == NULL ||
+ (png_ptr->mode & PNG_WROTE_tIME))
+ return;
+
+ png_memcpy(&(info_ptr->mod_time), mod_time, png_sizeof(png_time));
+ info_ptr->valid |= PNG_INFO_tIME;
+}
+#endif
+
+#ifdef PNG_tRNS_SUPPORTED
+void PNGAPI
+png_set_tRNS(png_structp png_ptr, png_infop info_ptr,
+ png_const_bytep trans_alpha, int num_trans, png_const_color_16p trans_color)
+{
+ png_debug1(1, "in %s storage function", "tRNS");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ if (trans_alpha != NULL)
+ {
+ /* It may not actually be necessary to set png_ptr->trans_alpha here;
+ * we do it for backward compatibility with the way the png_handle_tRNS
+ * function used to do the allocation.
+ */
+
+ png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0);
+
+ /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */
+ png_ptr->trans_alpha = info_ptr->trans_alpha =
+ (png_bytep)png_malloc(png_ptr, (png_size_t)PNG_MAX_PALETTE_LENGTH);
+
+ if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH)
+ png_memcpy(info_ptr->trans_alpha, trans_alpha, (png_size_t)num_trans);
+ }
+
+ if (trans_color != NULL)
+ {
+ int sample_max = (1 << info_ptr->bit_depth);
+
+ if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY &&
+ (int)trans_color->gray > sample_max) ||
+ (info_ptr->color_type == PNG_COLOR_TYPE_RGB &&
+ ((int)trans_color->red > sample_max ||
+ (int)trans_color->green > sample_max ||
+ (int)trans_color->blue > sample_max)))
+ png_warning(png_ptr,
+ "tRNS chunk has out-of-range samples for bit_depth");
+
+ png_memcpy(&(info_ptr->trans_color), trans_color,
+ png_sizeof(png_color_16));
+
+ if (num_trans == 0)
+ num_trans = 1;
+ }
+
+ info_ptr->num_trans = (png_uint_16)num_trans;
+
+ if (num_trans != 0)
+ {
+ info_ptr->valid |= PNG_INFO_tRNS;
+ info_ptr->free_me |= PNG_FREE_TRNS;
+ }
+}
+#endif
+
+#ifdef PNG_sPLT_SUPPORTED
+void PNGAPI
+png_set_sPLT(png_structp png_ptr,
+ png_infop info_ptr, png_const_sPLT_tp entries, int nentries)
+/*
+ * entries - array of png_sPLT_t structures
+ * to be added to the list of palettes
+ * in the info structure.
+ *
+ * nentries - number of palette structures to be
+ * added.
+ */
+{
+ png_sPLT_tp np;
+ int i;
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ np = (png_sPLT_tp)png_malloc_warn(png_ptr,
+ (info_ptr->splt_palettes_num + nentries) *
+ (png_size_t)png_sizeof(png_sPLT_t));
+
+ if (np == NULL)
+ {
+ png_warning(png_ptr, "No memory for sPLT palettes");
+ return;
+ }
+
+ png_memcpy(np, info_ptr->splt_palettes,
+ info_ptr->splt_palettes_num * png_sizeof(png_sPLT_t));
+
+ png_free(png_ptr, info_ptr->splt_palettes);
+ info_ptr->splt_palettes=NULL;
+
+ for (i = 0; i < nentries; i++)
+ {
+ png_sPLT_tp to = np + info_ptr->splt_palettes_num + i;
+ png_const_sPLT_tp from = entries + i;
+ png_uint_32 length;
+
+ length = png_strlen(from->name) + 1;
+ to->name = (png_charp)png_malloc_warn(png_ptr, (png_size_t)length);
+
+ if (to->name == NULL)
+ {
+ png_warning(png_ptr,
+ "Out of memory while processing sPLT chunk");
+ continue;
+ }
+
+ png_memcpy(to->name, from->name, length);
+ to->entries = (png_sPLT_entryp)png_malloc_warn(png_ptr,
+ (png_size_t)(from->nentries * png_sizeof(png_sPLT_entry)));
+
+ if (to->entries == NULL)
+ {
+ png_warning(png_ptr,
+ "Out of memory while processing sPLT chunk");
+ png_free(png_ptr, to->name);
+ to->name = NULL;
+ continue;
+ }
+
+ png_memcpy(to->entries, from->entries,
+ from->nentries * png_sizeof(png_sPLT_entry));
+
+ to->nentries = from->nentries;
+ to->depth = from->depth;
+ }
+
+ info_ptr->splt_palettes = np;
+ info_ptr->splt_palettes_num += nentries;
+ info_ptr->valid |= PNG_INFO_sPLT;
+ info_ptr->free_me |= PNG_FREE_SPLT;
+}
+#endif /* PNG_sPLT_SUPPORTED */
+
+#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
+void PNGAPI
+png_set_unknown_chunks(png_structp png_ptr,
+ png_infop info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns)
+{
+ png_unknown_chunkp np;
+ int i;
+
+ if (png_ptr == NULL || info_ptr == NULL || num_unknowns == 0)
+ return;
+
+ np = (png_unknown_chunkp)png_malloc_warn(png_ptr,
+ (png_size_t)(info_ptr->unknown_chunks_num + num_unknowns) *
+ png_sizeof(png_unknown_chunk));
+
+ if (np == NULL)
+ {
+ png_warning(png_ptr,
+ "Out of memory while processing unknown chunk");
+ return;
+ }
+
+ png_memcpy(np, info_ptr->unknown_chunks,
+ (png_size_t)info_ptr->unknown_chunks_num *
+ png_sizeof(png_unknown_chunk));
+
+ png_free(png_ptr, info_ptr->unknown_chunks);
+ info_ptr->unknown_chunks = NULL;
+
+ for (i = 0; i < num_unknowns; i++)
+ {
+ png_unknown_chunkp to = np + info_ptr->unknown_chunks_num + i;
+ png_const_unknown_chunkp from = unknowns + i;
+
+ png_memcpy(to->name, from->name, png_sizeof(from->name));
+ to->name[png_sizeof(to->name)-1] = '\0';
+ to->size = from->size;
+
+ /* Note our location in the read or write sequence */
+ to->location = (png_byte)(png_ptr->mode & 0xff);
+
+ if (from->size == 0)
+ to->data=NULL;
+
+ else
+ {
+ to->data = (png_bytep)png_malloc_warn(png_ptr,
+ (png_size_t)from->size);
+
+ if (to->data == NULL)
+ {
+ png_warning(png_ptr,
+ "Out of memory while processing unknown chunk");
+ to->size = 0;
+ }
+
+ else
+ png_memcpy(to->data, from->data, from->size);
+ }
+ }
+
+ info_ptr->unknown_chunks = np;
+ info_ptr->unknown_chunks_num += num_unknowns;
+ info_ptr->free_me |= PNG_FREE_UNKN;
+}
+
+void PNGAPI
+png_set_unknown_chunk_location(png_structp png_ptr, png_infop info_ptr,
+ int chunk, int location)
+{
+ if (png_ptr != NULL && info_ptr != NULL && chunk >= 0 && chunk <
+ info_ptr->unknown_chunks_num)
+ info_ptr->unknown_chunks[chunk].location = (png_byte)location;
+}
+#endif
+
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+png_uint_32 PNGAPI
+png_permit_mng_features (png_structp png_ptr, png_uint_32 mng_features)
+{
+ png_debug(1, "in png_permit_mng_features");
+
+ if (png_ptr == NULL)
+ return (png_uint_32)0;
+
+ png_ptr->mng_features_permitted =
+ (png_byte)(mng_features & PNG_ALL_MNG_FEATURES);
+
+ return (png_uint_32)png_ptr->mng_features_permitted;
+}
+#endif
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+void PNGAPI
+png_set_keep_unknown_chunks(png_structp png_ptr, int keep, png_const_bytep
+ chunk_list, int num_chunks)
+{
+ png_bytep new_list, p;
+ int i, old_num_chunks;
+ if (png_ptr == NULL)
+ return;
+
+ if (num_chunks == 0)
+ {
+ if (keep == PNG_HANDLE_CHUNK_ALWAYS || keep == PNG_HANDLE_CHUNK_IF_SAFE)
+ png_ptr->flags |= PNG_FLAG_KEEP_UNKNOWN_CHUNKS;
+
+ else
+ png_ptr->flags &= ~PNG_FLAG_KEEP_UNKNOWN_CHUNKS;
+
+ if (keep == PNG_HANDLE_CHUNK_ALWAYS)
+ png_ptr->flags |= PNG_FLAG_KEEP_UNSAFE_CHUNKS;
+
+ else
+ png_ptr->flags &= ~PNG_FLAG_KEEP_UNSAFE_CHUNKS;
+
+ return;
+ }
+
+ if (chunk_list == NULL)
+ return;
+
+ old_num_chunks = png_ptr->num_chunk_list;
+ new_list=(png_bytep)png_malloc(png_ptr,
+ (png_size_t)(5*(num_chunks + old_num_chunks)));
+
+ if (png_ptr->chunk_list != NULL)
+ {
+ png_memcpy(new_list, png_ptr->chunk_list,
+ (png_size_t)(5*old_num_chunks));
+ png_free(png_ptr, png_ptr->chunk_list);
+ png_ptr->chunk_list=NULL;
+ }
+
+ png_memcpy(new_list + 5*old_num_chunks, chunk_list,
+ (png_size_t)(5*num_chunks));
+
+ for (p = new_list + 5*old_num_chunks + 4, i = 0; i<num_chunks; i++, p += 5)
+ *p=(png_byte)keep;
+
+ png_ptr->num_chunk_list = old_num_chunks + num_chunks;
+ png_ptr->chunk_list = new_list;
+ png_ptr->free_me |= PNG_FREE_LIST;
+}
+#endif
+
+#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+void PNGAPI
+png_set_read_user_chunk_fn(png_structp png_ptr, png_voidp user_chunk_ptr,
+ png_user_chunk_ptr read_user_chunk_fn)
+{
+ png_debug(1, "in png_set_read_user_chunk_fn");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->read_user_chunk_fn = read_user_chunk_fn;
+ png_ptr->user_chunk_ptr = user_chunk_ptr;
+}
+#endif
+
+#ifdef PNG_INFO_IMAGE_SUPPORTED
+void PNGAPI
+png_set_rows(png_structp png_ptr, png_infop info_ptr, png_bytepp row_pointers)
+{
+ png_debug1(1, "in %s storage function", "rows");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ if (info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers))
+ png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0);
+
+ info_ptr->row_pointers = row_pointers;
+
+ if (row_pointers)
+ info_ptr->valid |= PNG_INFO_IDAT;
+}
+#endif
+
+void PNGAPI
+png_set_compression_buffer_size(png_structp png_ptr, png_size_t size)
+{
+ if (png_ptr == NULL)
+ return;
+
+ png_free(png_ptr, png_ptr->zbuf);
+
+ if (size > ZLIB_IO_MAX)
+ {
+ png_warning(png_ptr, "Attempt to set buffer size beyond max ignored");
+ png_ptr->zbuf_size = ZLIB_IO_MAX;
+ size = ZLIB_IO_MAX; /* must fit */
+ }
+
+ else
+ png_ptr->zbuf_size = (uInt)size;
+
+ png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, size);
+
+ /* The following ensures a relatively safe failure if this gets called while
+ * the buffer is actually in use.
+ */
+ png_ptr->zstream.next_out = png_ptr->zbuf;
+ png_ptr->zstream.avail_out = 0;
+ png_ptr->zstream.avail_in = 0;
+}
+
+void PNGAPI
+png_set_invalid(png_structp png_ptr, png_infop info_ptr, int mask)
+{
+ if (png_ptr && info_ptr)
+ info_ptr->valid &= ~mask;
+}
+
+
+
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+/* This function was added to libpng 1.2.6 */
+void PNGAPI
+png_set_user_limits (png_structp png_ptr, png_uint_32 user_width_max,
+ png_uint_32 user_height_max)
+{
+ /* Images with dimensions larger than these limits will be
+ * rejected by png_set_IHDR(). To accept any PNG datastream
+ * regardless of dimensions, set both limits to 0x7ffffffL.
+ */
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->user_width_max = user_width_max;
+ png_ptr->user_height_max = user_height_max;
+}
+
+/* This function was added to libpng 1.4.0 */
+void PNGAPI
+png_set_chunk_cache_max (png_structp png_ptr,
+ png_uint_32 user_chunk_cache_max)
+{
+ if (png_ptr)
+ png_ptr->user_chunk_cache_max = user_chunk_cache_max;
+}
+
+/* This function was added to libpng 1.4.1 */
+void PNGAPI
+png_set_chunk_malloc_max (png_structp png_ptr,
+ png_alloc_size_t user_chunk_malloc_max)
+{
+ if (png_ptr)
+ png_ptr->user_chunk_malloc_max = user_chunk_malloc_max;
+}
+#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */
+
+
+#ifdef PNG_BENIGN_ERRORS_SUPPORTED
+void PNGAPI
+png_set_benign_errors(png_structp png_ptr, int allowed)
+{
+ png_debug(1, "in png_set_benign_errors");
+
+ if (allowed)
+ png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN;
+
+ else
+ png_ptr->flags &= ~PNG_FLAG_BENIGN_ERRORS_WARN;
+}
+#endif /* PNG_BENIGN_ERRORS_SUPPORTED */
+#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */
diff --git a/libpng/pngstruct.h b/libpng/pngstruct.h
new file mode 100644
index 0000000..8d781fa
--- /dev/null
+++ b/libpng/pngstruct.h
@@ -0,0 +1,308 @@
+
+/* pngstruct.h - header file for PNG reference library
+ *
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * Last changed in libpng 1.5.0 [January 6, 2011]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+/* The structure that holds the information to read and write PNG files.
+ * The only people who need to care about what is inside of this are the
+ * people who will be modifying the library for their own special needs.
+ * It should NOT be accessed directly by an application.
+ */
+
+#ifndef PNGSTRUCT_H
+#define PNGSTRUCT_H
+/* zlib.h defines the structure z_stream, an instance of which is included
+ * in this structure and is required for decompressing the LZ compressed
+ * data in PNG files.
+ */
+#include "zlib.h"
+
+struct png_struct_def
+{
+#ifdef PNG_SETJMP_SUPPORTED
+ jmp_buf png_jmpbuf; /* used in png_error */
+ png_longjmp_ptr longjmp_fn;/* setjmp non-local goto function. */
+#endif
+ png_error_ptr error_fn; /* function for printing errors and aborting */
+ png_error_ptr warning_fn; /* function for printing warnings */
+ png_voidp error_ptr; /* user supplied struct for error functions */
+ png_rw_ptr write_data_fn; /* function for writing output data */
+ png_rw_ptr read_data_fn; /* function for reading input data */
+ png_voidp io_ptr; /* ptr to application struct for I/O functions */
+
+#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
+ png_user_transform_ptr read_user_transform_fn; /* user read transform */
+#endif
+
+#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
+ png_user_transform_ptr write_user_transform_fn; /* user write transform */
+#endif
+
+/* These were added in libpng-1.0.2 */
+#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+ defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+ png_voidp user_transform_ptr; /* user supplied struct for user transform */
+ png_byte user_transform_depth; /* bit depth of user transformed pixels */
+ png_byte user_transform_channels; /* channels in user transformed pixels */
+#endif
+#endif
+
+ png_uint_32 mode; /* tells us where we are in the PNG file */
+ png_uint_32 flags; /* flags indicating various things to libpng */
+ png_uint_32 transformations; /* which transformations to perform */
+
+ z_stream zstream; /* pointer to decompression structure (below) */
+ png_bytep zbuf; /* buffer for zlib */
+ uInt zbuf_size; /* size of zbuf (typically 65536) */
+ int zlib_level; /* holds zlib compression level */
+ int zlib_method; /* holds zlib compression method */
+ int zlib_window_bits; /* holds zlib compression window bits */
+ int zlib_mem_level; /* holds zlib compression memory level */
+ int zlib_strategy; /* holds zlib compression strategy */
+
+ png_uint_32 width; /* width of image in pixels */
+ png_uint_32 height; /* height of image in pixels */
+ png_uint_32 num_rows; /* number of rows in current pass */
+ png_uint_32 usr_width; /* width of row at start of write */
+ png_size_t rowbytes; /* size of row in bytes */
+ png_uint_32 iwidth; /* width of current interlaced row in pixels */
+ png_uint_32 row_number; /* current row in interlace pass */
+ png_bytep prev_row; /* buffer to save previous (unfiltered) row */
+ png_bytep row_buf; /* buffer to save current (unfiltered) row */
+ png_bytep sub_row; /* buffer to save "sub" row when filtering */
+ png_bytep up_row; /* buffer to save "up" row when filtering */
+ png_bytep avg_row; /* buffer to save "avg" row when filtering */
+ png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */
+ png_row_info row_info; /* used for transformation routines */
+
+ png_uint_32 idat_size; /* current IDAT size for read */
+ png_uint_32 crc; /* current chunk CRC value */
+ png_colorp palette; /* palette from the input file */
+ png_uint_16 num_palette; /* number of color entries in palette */
+ png_uint_16 num_trans; /* number of transparency values */
+ png_byte chunk_name[5]; /* null-terminated name of current chunk */
+ png_byte compression; /* file compression type (always 0) */
+ png_byte filter; /* file filter type (always 0) */
+ png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
+ png_byte pass; /* current interlace pass (0 - 6) */
+ png_byte do_filter; /* row filter flags (see PNG_FILTER_ below ) */
+ png_byte color_type; /* color type of file */
+ png_byte bit_depth; /* bit depth of file */
+ png_byte usr_bit_depth; /* bit depth of users row */
+ png_byte pixel_depth; /* number of bits per pixel */
+ png_byte channels; /* number of channels in file */
+ png_byte usr_channels; /* channels at start of write */
+ png_byte sig_bytes; /* magic bytes read/written from start of file */
+
+#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
+ png_uint_16 filler; /* filler bytes for pixel expansion */
+#endif
+
+#ifdef PNG_bKGD_SUPPORTED
+ png_byte background_gamma_type;
+ png_fixed_point background_gamma;
+ png_color_16 background; /* background color in screen gamma space */
+#ifdef PNG_READ_GAMMA_SUPPORTED
+ png_color_16 background_1; /* background normalized to gamma 1.0 */
+#endif
+#endif /* PNG_bKGD_SUPPORTED */
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+ png_flush_ptr output_flush_fn; /* Function for flushing output */
+ png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */
+ png_uint_32 flush_rows; /* number of rows written since last flush */
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+ int gamma_shift; /* number of "insignificant" bits in 16-bit gamma */
+ png_fixed_point gamma; /* file gamma value */
+ png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+ png_bytep gamma_table; /* gamma table for 8-bit depth files */
+ png_bytep gamma_from_1; /* converts from 1.0 to screen */
+ png_bytep gamma_to_1; /* converts from file to 1.0 */
+ png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */
+ png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */
+ png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED)
+ png_color_8 sig_bit; /* significant bits in each available channel */
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
+ png_color_8 shift; /* shift for significant bit tranformation */
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \
+ || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+ png_bytep trans_alpha; /* alpha values for paletted files */
+ png_color_16 trans_color; /* transparent color for non-paletted files */
+#endif
+
+ png_read_status_ptr read_row_fn; /* called after each row is decoded */
+ png_write_status_ptr write_row_fn; /* called after each row is encoded */
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+ png_progressive_info_ptr info_fn; /* called after header data fully read */
+ png_progressive_row_ptr row_fn; /* called after a prog. row is decoded */
+ png_progressive_end_ptr end_fn; /* called after image is complete */
+ png_bytep save_buffer_ptr; /* current location in save_buffer */
+ png_bytep save_buffer; /* buffer for previously read data */
+ png_bytep current_buffer_ptr; /* current location in current_buffer */
+ png_bytep current_buffer; /* buffer for recently used data */
+ png_uint_32 push_length; /* size of current input chunk */
+ png_uint_32 skip_length; /* bytes to skip in input data */
+ png_size_t save_buffer_size; /* amount of data now in save_buffer */
+ png_size_t save_buffer_max; /* total size of save_buffer */
+ png_size_t buffer_size; /* total amount of available input data */
+ png_size_t current_buffer_size; /* amount of data now in current_buffer */
+ int process_mode; /* what push library is currently doing */
+ int cur_palette; /* current push library palette index */
+
+# ifdef PNG_TEXT_SUPPORTED
+ png_size_t current_text_size; /* current size of text input data */
+ png_size_t current_text_left; /* how much text left to read in input */
+ png_charp current_text; /* current text chunk buffer */
+ png_charp current_text_ptr; /* current location in current_text */
+# endif /* PNG_PROGRESSIVE_READ_SUPPORTED && PNG_TEXT_SUPPORTED */
+
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
+#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__)
+/* For the Borland special 64K segment handler */
+ png_bytepp offset_table_ptr;
+ png_bytep offset_table;
+ png_uint_16 offset_table_number;
+ png_uint_16 offset_table_count;
+ png_uint_16 offset_table_count_free;
+#endif
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+ png_bytep palette_lookup; /* lookup table for quantizing */
+ png_bytep quantize_index; /* index translation for palette files */
+#endif
+
+#if defined(PNG_READ_QUANTIZE_SUPPORTED) || defined(PNG_hIST_SUPPORTED)
+ png_uint_16p hist; /* histogram */
+#endif
+
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+ png_byte heuristic_method; /* heuristic for row filter selection */
+ png_byte num_prev_filters; /* number of weights for previous rows */
+ png_bytep prev_filters; /* filter type(s) of previous row(s) */
+ png_uint_16p filter_weights; /* weight(s) for previous line(s) */
+ png_uint_16p inv_filter_weights; /* 1/weight(s) for previous line(s) */
+ png_uint_16p filter_costs; /* relative filter calculation cost */
+ png_uint_16p inv_filter_costs; /* 1/relative filter calculation cost */
+#endif
+
+#ifdef PNG_TIME_RFC1123_SUPPORTED
+ png_charp time_buffer; /* String to hold RFC 1123 time text */
+#endif
+
+/* New members added in libpng-1.0.6 */
+
+ png_uint_32 free_me; /* flags items libpng is responsible for freeing */
+
+#ifdef PNG_USER_CHUNKS_SUPPORTED
+ png_voidp user_chunk_ptr;
+ png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */
+#endif
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ int num_chunk_list;
+ png_bytep chunk_list;
+#endif
+
+/* New members added in libpng-1.0.3 */
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+ png_byte rgb_to_gray_status;
+ /* These were changed from png_byte in libpng-1.0.6 */
+ png_uint_16 rgb_to_gray_red_coeff;
+ png_uint_16 rgb_to_gray_green_coeff;
+ png_uint_16 rgb_to_gray_blue_coeff;
+#endif
+
+/* New member added in libpng-1.0.4 (renamed in 1.0.9) */
+#if defined(PNG_MNG_FEATURES_SUPPORTED) || \
+ defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \
+ defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED)
+/* Changed from png_byte to png_uint_32 at version 1.2.0 */
+ png_uint_32 mng_features_permitted;
+#endif
+
+/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+ png_byte filter_type;
+#endif
+
+/* New members added in libpng-1.2.0 */
+
+/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_voidp mem_ptr; /* user supplied struct for mem functions */
+ png_malloc_ptr malloc_fn; /* function for allocating memory */
+ png_free_ptr free_fn; /* function for freeing memory */
+#endif
+
+/* New member added in libpng-1.0.13 and 1.2.0 */
+ png_bytep big_row_buf; /* buffer to save current (unfiltered) row */
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+/* The following three members were added at version 1.0.14 and 1.2.4 */
+ png_bytep quantize_sort; /* working sort array */
+ png_bytep index_to_palette; /* where the original index currently is
+ in the palette */
+ png_bytep palette_to_index; /* which original index points to this
+ palette color */
+#endif
+
+/* New members added in libpng-1.0.16 and 1.2.6 */
+ png_byte compression_type;
+
+#ifdef PNG_USER_LIMITS_SUPPORTED
+ png_uint_32 user_width_max;
+ png_uint_32 user_height_max;
+
+ /* Added in libpng-1.4.0: Total number of sPLT, text, and unknown
+ * chunks that can be stored (0 means unlimited).
+ */
+ png_uint_32 user_chunk_cache_max;
+
+ /* Total memory that a zTXt, sPLT, iTXt, iCCP, or unknown chunk
+ * can occupy when decompressed. 0 means unlimited.
+ */
+ png_alloc_size_t user_chunk_malloc_max;
+#endif
+
+/* New member added in libpng-1.0.25 and 1.2.17 */
+#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
+ /* Storage for unknown chunk that the library doesn't recognize. */
+ png_unknown_chunk unknown_chunk;
+#endif
+
+/* New members added in libpng-1.2.26 */
+ png_size_t old_big_row_buf_size;
+ png_size_t old_prev_row_size;
+
+/* New member added in libpng-1.2.30 */
+ png_charp chunkdata; /* buffer for reading chunk data */
+
+#ifdef PNG_IO_STATE_SUPPORTED
+/* New member added in libpng-1.4.0 */
+ png_uint_32 io_state;
+#endif
+};
+#endif /* PNGSTRUCT_H */
diff --git a/libpng/pngtrans.c b/libpng/pngtrans.c
new file mode 100644
index 0000000..cf7622c
--- /dev/null
+++ b/libpng/pngtrans.c
@@ -0,0 +1,674 @@
+
+/* pngtrans.c - transforms the data in a row (used by both readers and writers)
+ *
+ * Last changed in libpng 1.5.2 [March 31, 2011]
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include "pngpriv.h"
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+/* Turn on BGR-to-RGB mapping */
+void PNGAPI
+png_set_bgr(png_structp png_ptr)
+{
+ png_debug(1, "in png_set_bgr");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->transformations |= PNG_BGR;
+}
+#endif
+
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+/* Turn on 16 bit byte swapping */
+void PNGAPI
+png_set_swap(png_structp png_ptr)
+{
+ png_debug(1, "in png_set_swap");
+
+ if (png_ptr == NULL)
+ return;
+
+ if (png_ptr->bit_depth == 16)
+ png_ptr->transformations |= PNG_SWAP_BYTES;
+}
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+/* Turn on pixel packing */
+void PNGAPI
+png_set_packing(png_structp png_ptr)
+{
+ png_debug(1, "in png_set_packing");
+
+ if (png_ptr == NULL)
+ return;
+
+ if (png_ptr->bit_depth < 8)
+ {
+ png_ptr->transformations |= PNG_PACK;
+ png_ptr->usr_bit_depth = 8;
+ }
+}
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+/* Turn on packed pixel swapping */
+void PNGAPI
+png_set_packswap(png_structp png_ptr)
+{
+ png_debug(1, "in png_set_packswap");
+
+ if (png_ptr == NULL)
+ return;
+
+ if (png_ptr->bit_depth < 8)
+ png_ptr->transformations |= PNG_PACKSWAP;
+}
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
+void PNGAPI
+png_set_shift(png_structp png_ptr, png_const_color_8p true_bits)
+{
+ png_debug(1, "in png_set_shift");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->transformations |= PNG_SHIFT;
+ png_ptr->shift = *true_bits;
+}
+#endif
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
+ defined(PNG_WRITE_INTERLACING_SUPPORTED)
+int PNGAPI
+png_set_interlace_handling(png_structp png_ptr)
+{
+ png_debug(1, "in png_set_interlace handling");
+
+ if (png_ptr && png_ptr->interlaced)
+ {
+ png_ptr->transformations |= PNG_INTERLACE;
+ return (7);
+ }
+
+ return (1);
+}
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
+/* Add a filler byte on read, or remove a filler or alpha byte on write.
+ * The filler type has changed in v0.95 to allow future 2-byte fillers
+ * for 48-bit input data, as well as to avoid problems with some compilers
+ * that don't like bytes as parameters.
+ */
+void PNGAPI
+png_set_filler(png_structp png_ptr, png_uint_32 filler, int filler_loc)
+{
+ png_debug(1, "in png_set_filler");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->transformations |= PNG_FILLER;
+ png_ptr->filler = (png_uint_16)filler;
+
+ if (filler_loc == PNG_FILLER_AFTER)
+ png_ptr->flags |= PNG_FLAG_FILLER_AFTER;
+
+ else
+ png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER;
+
+ /* This should probably go in the "do_read_filler" routine.
+ * I attempted to do that in libpng-1.0.1a but that caused problems
+ * so I restored it in libpng-1.0.2a
+ */
+
+ if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+ {
+ png_ptr->usr_channels = 4;
+ }
+
+ /* Also I added this in libpng-1.0.2a (what happens when we expand
+ * a less-than-8-bit grayscale to GA?) */
+
+ if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY && png_ptr->bit_depth >= 8)
+ {
+ png_ptr->usr_channels = 2;
+ }
+}
+
+/* Added to libpng-1.2.7 */
+void PNGAPI
+png_set_add_alpha(png_structp png_ptr, png_uint_32 filler, int filler_loc)
+{
+ png_debug(1, "in png_set_add_alpha");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_set_filler(png_ptr, filler, filler_loc);
+ png_ptr->transformations |= PNG_ADD_ALPHA;
+}
+
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
+ defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+void PNGAPI
+png_set_swap_alpha(png_structp png_ptr)
+{
+ png_debug(1, "in png_set_swap_alpha");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->transformations |= PNG_SWAP_ALPHA;
+}
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \
+ defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+void PNGAPI
+png_set_invert_alpha(png_structp png_ptr)
+{
+ png_debug(1, "in png_set_invert_alpha");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->transformations |= PNG_INVERT_ALPHA;
+}
+#endif
+
+#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
+void PNGAPI
+png_set_invert_mono(png_structp png_ptr)
+{
+ png_debug(1, "in png_set_invert_mono");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->transformations |= PNG_INVERT_MONO;
+}
+
+/* Invert monochrome grayscale data */
+void /* PRIVATE */
+png_do_invert(png_row_infop row_info, png_bytep row)
+{
+ png_debug(1, "in png_do_invert");
+
+ /* This test removed from libpng version 1.0.13 and 1.2.0:
+ * if (row_info->bit_depth == 1 &&
+ */
+ if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
+ {
+ png_bytep rp = row;
+ png_size_t i;
+ png_size_t istop = row_info->rowbytes;
+
+ for (i = 0; i < istop; i++)
+ {
+ *rp = (png_byte)(~(*rp));
+ rp++;
+ }
+ }
+
+ else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
+ row_info->bit_depth == 8)
+ {
+ png_bytep rp = row;
+ png_size_t i;
+ png_size_t istop = row_info->rowbytes;
+
+ for (i = 0; i < istop; i += 2)
+ {
+ *rp = (png_byte)(~(*rp));
+ rp += 2;
+ }
+ }
+
+#ifdef PNG_16BIT_SUPPORTED
+ else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
+ row_info->bit_depth == 16)
+ {
+ png_bytep rp = row;
+ png_size_t i;
+ png_size_t istop = row_info->rowbytes;
+
+ for (i = 0; i < istop; i += 4)
+ {
+ *rp = (png_byte)(~(*rp));
+ *(rp + 1) = (png_byte)(~(*(rp + 1)));
+ rp += 4;
+ }
+ }
+#endif
+}
+#endif
+
+#ifdef PNG_16BIT_SUPPORTED
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+/* Swaps byte order on 16 bit depth images */
+void /* PRIVATE */
+png_do_swap(png_row_infop row_info, png_bytep row)
+{
+ png_debug(1, "in png_do_swap");
+
+ if (row_info->bit_depth == 16)
+ {
+ png_bytep rp = row;
+ png_uint_32 i;
+ png_uint_32 istop= row_info->width * row_info->channels;
+
+ for (i = 0; i < istop; i++, rp += 2)
+ {
+ png_byte t = *rp;
+ *rp = *(rp + 1);
+ *(rp + 1) = t;
+ }
+ }
+}
+#endif
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+static PNG_CONST png_byte onebppswaptable[256] = {
+ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
+ 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
+ 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
+ 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
+ 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
+ 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
+ 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
+ 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
+ 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
+ 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
+ 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
+ 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
+ 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
+ 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
+ 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
+ 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+ 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
+ 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
+ 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
+ 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
+ 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
+ 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+ 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
+ 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
+ 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
+ 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
+ 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
+ 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
+ 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
+ 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
+};
+
+static PNG_CONST png_byte twobppswaptable[256] = {
+ 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0,
+ 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0,
+ 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4,
+ 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4,
+ 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8,
+ 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8,
+ 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC,
+ 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC,
+ 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1,
+ 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1,
+ 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5,
+ 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5,
+ 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9,
+ 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9,
+ 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD,
+ 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD,
+ 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2,
+ 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2,
+ 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6,
+ 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6,
+ 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA,
+ 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA,
+ 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE,
+ 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE,
+ 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3,
+ 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3,
+ 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7,
+ 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7,
+ 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB,
+ 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB,
+ 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF,
+ 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF
+};
+
+static PNG_CONST png_byte fourbppswaptable[256] = {
+ 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
+ 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0,
+ 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71,
+ 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1,
+ 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72,
+ 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2,
+ 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73,
+ 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3,
+ 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74,
+ 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4,
+ 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75,
+ 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5,
+ 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76,
+ 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6,
+ 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77,
+ 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7,
+ 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78,
+ 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8,
+ 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79,
+ 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9,
+ 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A,
+ 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA,
+ 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B,
+ 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB,
+ 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C,
+ 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC,
+ 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D,
+ 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD,
+ 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E,
+ 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE,
+ 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F,
+ 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF
+};
+
+/* Swaps pixel packing order within bytes */
+void /* PRIVATE */
+png_do_packswap(png_row_infop row_info, png_bytep row)
+{
+ png_debug(1, "in png_do_packswap");
+
+ if (row_info->bit_depth < 8)
+ {
+ png_bytep rp;
+ png_const_bytep end, table;
+
+ end = row + row_info->rowbytes;
+
+ if (row_info->bit_depth == 1)
+ table = onebppswaptable;
+
+ else if (row_info->bit_depth == 2)
+ table = twobppswaptable;
+
+ else if (row_info->bit_depth == 4)
+ table = fourbppswaptable;
+
+ else
+ return;
+
+ for (rp = row; rp < end; rp++)
+ *rp = table[*rp];
+ }
+}
+#endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */
+
+#if defined(PNG_WRITE_FILLER_SUPPORTED) || \
+ defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+/* Remove a channel - this used to be 'png_do_strip_filler' but it used a
+ * somewhat weird combination of flags to determine what to do. All the calls
+ * to png_do_strip_filler are changed in 1.5.2 to call this instead with the
+ * correct arguments.
+ *
+ * The routine isn't general - the channel must be the channel at the start or
+ * end (not in the middle) of each pixel.
+ */
+void /* PRIVATE */
+png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start)
+{
+ png_bytep sp = row; /* source pointer */
+ png_bytep dp = row; /* destination pointer */
+ png_bytep ep = row + row_info->rowbytes; /* One beyond end of row */
+
+ /* At the start sp will point to the first byte to copy and dp to where
+ * it is copied to. ep always points just beyond the end of the row, so
+ * the loop simply copies (channels-1) channels until sp reaches ep.
+ */
+ /* GA, GX, XG cases */
+ if (row_info->channels == 2)
+ {
+ if (row_info->bit_depth == 8)
+ {
+ if (at_start) /* Skip initial filler */
+ ++sp;
+ else /* Skip initial channels and, for sp, the filler */
+ sp += 2, ++dp;
+
+ /* For a 1 pixel wide image there is nothing to do */
+ while (sp < ep)
+ *dp++ = *sp, sp += 2;
+
+ row_info->pixel_depth = 8;
+ }
+
+ else if (row_info->bit_depth == 16)
+ {
+ if (at_start)
+ sp += 2;
+ else
+ sp += 4, dp += 2;
+
+ while (sp < ep)
+ *dp++ = *sp++, *dp++ = *sp, sp += 3;
+
+ row_info->pixel_depth = 16;
+ }
+
+ else
+ return; /* bad bit depth */
+
+ row_info->channels = 1;
+
+ /* Finally fix the color type if it records an alpha channel */
+ if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ row_info->color_type = PNG_COLOR_TYPE_GRAY;
+ }
+
+ /* RGBA, RGBX, XRGB cases */
+ else if (row_info->channels == 4)
+ {
+ if (row_info->bit_depth == 8)
+ {
+ if (at_start) /* Skip initial filler */
+ ++sp;
+ else /* Skip initial channels and, for sp, the filler */
+ sp += 4, dp += 3;
+
+ /* Note that the loop adds 3 to dp and 4 to sp each time. */
+ while (sp < ep)
+ *dp++ = *sp++, *dp++ = *sp++, *dp++ = *sp, sp += 2;
+
+ row_info->pixel_depth = 24;
+ }
+
+ else if (row_info->bit_depth == 16)
+ {
+ if (at_start)
+ sp += 2;
+ else
+ sp += 8, dp += 6;
+
+ while (sp < ep)
+ {
+ /* Copy 6 bytes, skip 2 */
+ *dp++ = *sp++, *dp++ = *sp++;
+ *dp++ = *sp++, *dp++ = *sp++;
+ *dp++ = *sp++, *dp++ = *sp, sp += 3;
+ }
+
+ row_info->pixel_depth = 48;
+ }
+
+ else
+ return; /* bad bit depth */
+
+ row_info->channels = 3;
+
+ /* Finally fix the color type if it records an alpha channel */
+ if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ row_info->color_type = PNG_COLOR_TYPE_RGB;
+ }
+
+ else
+ return; /* The filler channel has gone already */
+
+ /* Fix the rowbytes value. */
+ row_info->rowbytes = dp-row;
+}
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+/* Swaps red and blue bytes within a pixel */
+void /* PRIVATE */
+png_do_bgr(png_row_infop row_info, png_bytep row)
+{
+ png_debug(1, "in png_do_bgr");
+
+ if ((row_info->color_type & PNG_COLOR_MASK_COLOR))
+ {
+ png_uint_32 row_width = row_info->width;
+ if (row_info->bit_depth == 8)
+ {
+ if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+ {
+ png_bytep rp;
+ png_uint_32 i;
+
+ for (i = 0, rp = row; i < row_width; i++, rp += 3)
+ {
+ png_byte save = *rp;
+ *rp = *(rp + 2);
+ *(rp + 2) = save;
+ }
+ }
+
+ else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ {
+ png_bytep rp;
+ png_uint_32 i;
+
+ for (i = 0, rp = row; i < row_width; i++, rp += 4)
+ {
+ png_byte save = *rp;
+ *rp = *(rp + 2);
+ *(rp + 2) = save;
+ }
+ }
+ }
+
+#ifdef PNG_16BIT_SUPPORTED
+ else if (row_info->bit_depth == 16)
+ {
+ if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+ {
+ png_bytep rp;
+ png_uint_32 i;
+
+ for (i = 0, rp = row; i < row_width; i++, rp += 6)
+ {
+ png_byte save = *rp;
+ *rp = *(rp + 4);
+ *(rp + 4) = save;
+ save = *(rp + 1);
+ *(rp + 1) = *(rp + 5);
+ *(rp + 5) = save;
+ }
+ }
+
+ else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ {
+ png_bytep rp;
+ png_uint_32 i;
+
+ for (i = 0, rp = row; i < row_width; i++, rp += 8)
+ {
+ png_byte save = *rp;
+ *rp = *(rp + 4);
+ *(rp + 4) = save;
+ save = *(rp + 1);
+ *(rp + 1) = *(rp + 5);
+ *(rp + 5) = save;
+ }
+ }
+ }
+#endif
+ }
+}
+#endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+ defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
+void PNGAPI
+png_set_user_transform_info(png_structp png_ptr, png_voidp
+ user_transform_ptr, int user_transform_depth, int user_transform_channels)
+{
+ png_debug(1, "in png_set_user_transform_info");
+
+ if (png_ptr == NULL)
+ return;
+ png_ptr->user_transform_ptr = user_transform_ptr;
+ png_ptr->user_transform_depth = (png_byte)user_transform_depth;
+ png_ptr->user_transform_channels = (png_byte)user_transform_channels;
+}
+#endif
+
+/* This function returns a pointer to the user_transform_ptr associated with
+ * the user transform functions. The application should free any memory
+ * associated with this pointer before png_write_destroy and png_read_destroy
+ * are called.
+ */
+#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
+png_voidp PNGAPI
+png_get_user_transform_ptr(png_const_structp png_ptr)
+{
+ if (png_ptr == NULL)
+ return (NULL);
+
+ return ((png_voidp)png_ptr->user_transform_ptr);
+}
+#endif
+
+#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
+png_uint_32 PNGAPI
+png_get_current_row_number(png_const_structp png_ptr)
+{
+ /* See the comments in png.h - this is the sub-image row when reading and
+ * interlaced image.
+ */
+ if (png_ptr != NULL)
+ return png_ptr->row_number;
+
+ return PNG_UINT_32_MAX; /* help the app not to fail silently */
+}
+
+png_byte PNGAPI
+png_get_current_pass_number(png_const_structp png_ptr)
+{
+ if (png_ptr != NULL)
+ return png_ptr->pass;
+ return 8; /* invalid */
+}
+#endif /* PNG_USER_TRANSFORM_INFO_SUPPORTED */
+#endif /* PNG_READ_USER_TRANSFORM_SUPPORTED ||
+ PNG_WRITE_USER_TRANSFORM_SUPPORTED */
+#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */
diff --git a/libpng/pngwio.c b/libpng/pngwio.c
new file mode 100644
index 0000000..95ffb34
--- /dev/null
+++ b/libpng/pngwio.c
@@ -0,0 +1,254 @@
+
+/* pngwio.c - functions for data output
+ *
+ * Last changed in libpng 1.5.0 [January 6, 2011]
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * This file provides a location for all output. Users who need
+ * special handling are expected to write functions that have the same
+ * arguments as these and perform similar functions, but that possibly
+ * use different output methods. Note that you shouldn't change these
+ * functions, but rather write replacement functions and then change
+ * them at run time with png_set_write_fn(...).
+ */
+
+#include "pngpriv.h"
+
+#ifdef PNG_WRITE_SUPPORTED
+
+/* Write the data to whatever output you are using. The default routine
+ * writes to a file pointer. Note that this routine sometimes gets called
+ * with very small lengths, so you should implement some kind of simple
+ * buffering if you are using unbuffered writes. This should never be asked
+ * to write more than 64K on a 16 bit machine.
+ */
+
+void /* PRIVATE */
+png_write_data(png_structp png_ptr, png_const_bytep data, png_size_t length)
+{
+ /* NOTE: write_data_fn must not change the buffer! */
+ if (png_ptr->write_data_fn != NULL )
+ (*(png_ptr->write_data_fn))(png_ptr, (png_bytep)data, length);
+
+ else
+ png_error(png_ptr, "Call to NULL write function");
+}
+
+#ifdef PNG_STDIO_SUPPORTED
+/* This is the function that does the actual writing of data. If you are
+ * not writing to a standard C stream, you should create a replacement
+ * write_data function and use it at run time with png_set_write_fn(), rather
+ * than changing the library.
+ */
+#ifndef USE_FAR_KEYWORD
+void PNGCBAPI
+png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ png_size_t check;
+
+ if (png_ptr == NULL)
+ return;
+
+ check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr));
+
+ if (check != length)
+ png_error(png_ptr, "Write Error");
+}
+#else
+/* This is the model-independent version. Since the standard I/O library
+ * can't handle far buffers in the medium and small models, we have to copy
+ * the data.
+ */
+
+#define NEAR_BUF_SIZE 1024
+#define MIN(a,b) (a <= b ? a : b)
+
+void PNGCBAPI
+png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ png_uint_32 check;
+ png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */
+ png_FILE_p io_ptr;
+
+ if (png_ptr == NULL)
+ return;
+
+ /* Check if data really is near. If so, use usual code. */
+ near_data = (png_byte *)CVT_PTR_NOCHECK(data);
+ io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr);
+
+ if ((png_bytep)near_data == data)
+ {
+ check = fwrite(near_data, 1, length, io_ptr);
+ }
+
+ else
+ {
+ png_byte buf[NEAR_BUF_SIZE];
+ png_size_t written, remaining, err;
+ check = 0;
+ remaining = length;
+
+ do
+ {
+ written = MIN(NEAR_BUF_SIZE, remaining);
+ png_memcpy(buf, data, written); /* Copy far buffer to near buffer */
+ err = fwrite(buf, 1, written, io_ptr);
+
+ if (err != written)
+ break;
+
+ else
+ check += err;
+
+ data += written;
+ remaining -= written;
+ }
+ while (remaining != 0);
+ }
+
+ if (check != length)
+ png_error(png_ptr, "Write Error");
+}
+
+#endif
+#endif
+
+/* This function is called to output any data pending writing (normally
+ * to disk). After png_flush is called, there should be no data pending
+ * writing in any buffers.
+ */
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+void /* PRIVATE */
+png_flush(png_structp png_ptr)
+{
+ if (png_ptr->output_flush_fn != NULL)
+ (*(png_ptr->output_flush_fn))(png_ptr);
+}
+
+# ifdef PNG_STDIO_SUPPORTED
+void PNGCBAPI
+png_default_flush(png_structp png_ptr)
+{
+ png_FILE_p io_ptr;
+
+ if (png_ptr == NULL)
+ return;
+
+ io_ptr = (png_FILE_p)CVT_PTR((png_ptr->io_ptr));
+ fflush(io_ptr);
+}
+# endif
+#endif
+
+/* This function allows the application to supply new output functions for
+ * libpng if standard C streams aren't being used.
+ *
+ * This function takes as its arguments:
+ * png_ptr - pointer to a png output data structure
+ * io_ptr - pointer to user supplied structure containing info about
+ * the output functions. May be NULL.
+ * write_data_fn - pointer to a new output function that takes as its
+ * arguments a pointer to a png_struct, a pointer to
+ * data to be written, and a 32-bit unsigned int that is
+ * the number of bytes to be written. The new write
+ * function should call png_error(png_ptr, "Error msg")
+ * to exit and output any fatal error messages. May be
+ * NULL, in which case libpng's default function will
+ * be used.
+ * flush_data_fn - pointer to a new flush function that takes as its
+ * arguments a pointer to a png_struct. After a call to
+ * the flush function, there should be no data in any buffers
+ * or pending transmission. If the output method doesn't do
+ * any buffering of output, a function prototype must still be
+ * supplied although it doesn't have to do anything. If
+ * PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile
+ * time, output_flush_fn will be ignored, although it must be
+ * supplied for compatibility. May be NULL, in which case
+ * libpng's default function will be used, if
+ * PNG_WRITE_FLUSH_SUPPORTED is defined. This is not
+ * a good idea if io_ptr does not point to a standard
+ * *FILE structure.
+ */
+void PNGAPI
+png_set_write_fn(png_structp png_ptr, png_voidp io_ptr,
+ png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)
+{
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->io_ptr = io_ptr;
+
+#ifdef PNG_STDIO_SUPPORTED
+ if (write_data_fn != NULL)
+ png_ptr->write_data_fn = write_data_fn;
+
+ else
+ png_ptr->write_data_fn = png_default_write_data;
+#else
+ png_ptr->write_data_fn = write_data_fn;
+#endif
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+# ifdef PNG_STDIO_SUPPORTED
+
+ if (output_flush_fn != NULL)
+ png_ptr->output_flush_fn = output_flush_fn;
+
+ else
+ png_ptr->output_flush_fn = png_default_flush;
+
+# else
+ png_ptr->output_flush_fn = output_flush_fn;
+# endif
+#endif /* PNG_WRITE_FLUSH_SUPPORTED */
+
+ /* It is an error to read while writing a png file */
+ if (png_ptr->read_data_fn != NULL)
+ {
+ png_ptr->read_data_fn = NULL;
+
+ png_warning(png_ptr,
+ "Can't set both read_data_fn and write_data_fn in the"
+ " same structure");
+ }
+}
+
+#ifdef USE_FAR_KEYWORD
+# ifdef _MSC_VER
+void *png_far_to_near(png_structp png_ptr, png_voidp ptr, int check)
+{
+ void *near_ptr;
+ void FAR *far_ptr;
+ FP_OFF(near_ptr) = FP_OFF(ptr);
+ far_ptr = (void FAR *)near_ptr;
+
+ if (check != 0)
+ if (FP_SEG(ptr) != FP_SEG(far_ptr))
+ png_error(png_ptr, "segment lost in conversion");
+
+ return(near_ptr);
+}
+# else
+void *png_far_to_near(png_structp png_ptr, png_voidp ptr, int check)
+{
+ void *near_ptr;
+ void FAR *far_ptr;
+ near_ptr = (void FAR *)ptr;
+ far_ptr = (void FAR *)near_ptr;
+
+ if (check != 0)
+ if (far_ptr != ptr)
+ png_error(png_ptr, "segment lost in conversion");
+
+ return(near_ptr);
+}
+# endif
+#endif
+#endif /* PNG_WRITE_SUPPORTED */
diff --git a/libpng/pngwrite.c b/libpng/pngwrite.c
new file mode 100644
index 0000000..9277a95
--- /dev/null
+++ b/libpng/pngwrite.c
@@ -0,0 +1,1605 @@
+
+/* pngwrite.c - general routines to write a PNG file
+ *
+ * Last changed in libpng 1.5.1 [February 3, 2011]
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include "pngpriv.h"
+
+#ifdef PNG_WRITE_SUPPORTED
+
+/* Writes all the PNG information. This is the suggested way to use the
+ * library. If you have a new chunk to add, make a function to write it,
+ * and put it in the correct location here. If you want the chunk written
+ * after the image data, put it in png_write_end(). I strongly encourage
+ * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing
+ * the chunk, as that will keep the code from breaking if you want to just
+ * write a plain PNG file. If you have long comments, I suggest writing
+ * them in png_write_end(), and compressing them.
+ */
+void PNGAPI
+png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr)
+{
+ png_debug(1, "in png_write_info_before_PLTE");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
+ {
+ /* Write PNG signature */
+ png_write_sig(png_ptr);
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+ if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) && \
+ (png_ptr->mng_features_permitted))
+ {
+ png_warning(png_ptr, "MNG features are not allowed in a PNG datastream");
+ png_ptr->mng_features_permitted = 0;
+ }
+#endif
+
+ /* Write IHDR information. */
+ png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,
+ info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type,
+ info_ptr->filter_type,
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ info_ptr->interlace_type);
+#else
+ 0);
+#endif
+ /* The rest of these check to see if the valid field has the appropriate
+ * flag set, and if it does, writes the chunk.
+ */
+#ifdef PNG_WRITE_gAMA_SUPPORTED
+ if (info_ptr->valid & PNG_INFO_gAMA)
+ png_write_gAMA_fixed(png_ptr, info_ptr->gamma);
+#endif
+#ifdef PNG_WRITE_sRGB_SUPPORTED
+ if (info_ptr->valid & PNG_INFO_sRGB)
+ png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent);
+#endif
+
+#ifdef PNG_WRITE_iCCP_SUPPORTED
+ if (info_ptr->valid & PNG_INFO_iCCP)
+ png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE,
+ (png_charp)info_ptr->iccp_profile, (int)info_ptr->iccp_proflen);
+#endif
+#ifdef PNG_WRITE_sBIT_SUPPORTED
+ if (info_ptr->valid & PNG_INFO_sBIT)
+ png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);
+#endif
+#ifdef PNG_WRITE_cHRM_SUPPORTED
+ if (info_ptr->valid & PNG_INFO_cHRM)
+ png_write_cHRM_fixed(png_ptr,
+ info_ptr->x_white, info_ptr->y_white,
+ info_ptr->x_red, info_ptr->y_red,
+ info_ptr->x_green, info_ptr->y_green,
+ info_ptr->x_blue, info_ptr->y_blue);
+#endif
+
+#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+ if (info_ptr->unknown_chunks_num)
+ {
+ png_unknown_chunk *up;
+
+ png_debug(5, "writing extra chunks");
+
+ for (up = info_ptr->unknown_chunks;
+ up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
+ up++)
+ {
+ int keep = png_handle_as_unknown(png_ptr, up->name);
+
+ if (keep != PNG_HANDLE_CHUNK_NEVER &&
+ up->location && !(up->location & PNG_HAVE_PLTE) &&
+ !(up->location & PNG_HAVE_IDAT) &&
+ ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
+ (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
+ {
+ if (up->size == 0)
+ png_warning(png_ptr, "Writing zero-length unknown chunk");
+
+ png_write_chunk(png_ptr, up->name, up->data, up->size);
+ }
+ }
+ }
+#endif
+ png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE;
+ }
+}
+
+void PNGAPI
+png_write_info(png_structp png_ptr, png_infop info_ptr)
+{
+#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
+ int i;
+#endif
+
+ png_debug(1, "in png_write_info");
+
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ png_write_info_before_PLTE(png_ptr, info_ptr);
+
+ if (info_ptr->valid & PNG_INFO_PLTE)
+ png_write_PLTE(png_ptr, info_ptr->palette,
+ (png_uint_32)info_ptr->num_palette);
+
+ else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ png_error(png_ptr, "Valid palette required for paletted images");
+
+#ifdef PNG_WRITE_tRNS_SUPPORTED
+ if (info_ptr->valid & PNG_INFO_tRNS)
+ {
+#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
+ /* Invert the alpha channel (in tRNS) */
+ if ((png_ptr->transformations & PNG_INVERT_ALPHA) &&
+ info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ int j;
+ for (j = 0; j<(int)info_ptr->num_trans; j++)
+ info_ptr->trans_alpha[j] =
+ (png_byte)(255 - info_ptr->trans_alpha[j]);
+ }
+#endif
+ png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color),
+ info_ptr->num_trans, info_ptr->color_type);
+ }
+#endif
+#ifdef PNG_WRITE_bKGD_SUPPORTED
+ if (info_ptr->valid & PNG_INFO_bKGD)
+ png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);
+#endif
+
+#ifdef PNG_WRITE_hIST_SUPPORTED
+ if (info_ptr->valid & PNG_INFO_hIST)
+ png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
+#endif
+
+#ifdef PNG_WRITE_oFFs_SUPPORTED
+ if (info_ptr->valid & PNG_INFO_oFFs)
+ png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,
+ info_ptr->offset_unit_type);
+#endif
+
+#ifdef PNG_WRITE_pCAL_SUPPORTED
+ if (info_ptr->valid & PNG_INFO_pCAL)
+ png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,
+ info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,
+ info_ptr->pcal_units, info_ptr->pcal_params);
+#endif
+
+#ifdef PNG_WRITE_sCAL_SUPPORTED
+ if (info_ptr->valid & PNG_INFO_sCAL)
+ png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit,
+ info_ptr->scal_s_width, info_ptr->scal_s_height);
+#endif /* sCAL */
+
+#ifdef PNG_WRITE_pHYs_SUPPORTED
+ if (info_ptr->valid & PNG_INFO_pHYs)
+ png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,
+ info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);
+#endif /* pHYs */
+
+#ifdef PNG_WRITE_tIME_SUPPORTED
+ if (info_ptr->valid & PNG_INFO_tIME)
+ {
+ png_write_tIME(png_ptr, &(info_ptr->mod_time));
+ png_ptr->mode |= PNG_WROTE_tIME;
+ }
+#endif /* tIME */
+
+#ifdef PNG_WRITE_sPLT_SUPPORTED
+ if (info_ptr->valid & PNG_INFO_sPLT)
+ for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)
+ png_write_sPLT(png_ptr, info_ptr->splt_palettes + i);
+#endif /* sPLT */
+
+#ifdef PNG_WRITE_TEXT_SUPPORTED
+ /* Check to see if we need to write text chunks */
+ for (i = 0; i < info_ptr->num_text; i++)
+ {
+ png_debug2(2, "Writing header text chunk %d, type %d", i,
+ info_ptr->text[i].compression);
+ /* An internationalized chunk? */
+ if (info_ptr->text[i].compression > 0)
+ {
+#ifdef PNG_WRITE_iTXt_SUPPORTED
+ /* Write international chunk */
+ png_write_iTXt(png_ptr,
+ info_ptr->text[i].compression,
+ info_ptr->text[i].key,
+ info_ptr->text[i].lang,
+ info_ptr->text[i].lang_key,
+ info_ptr->text[i].text);
+#else
+ png_warning(png_ptr, "Unable to write international text");
+#endif
+ /* Mark this chunk as written */
+ info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+ }
+
+ /* If we want a compressed text chunk */
+ else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt)
+ {
+#ifdef PNG_WRITE_zTXt_SUPPORTED
+ /* Write compressed chunk */
+ png_write_zTXt(png_ptr, info_ptr->text[i].key,
+ info_ptr->text[i].text, 0,
+ info_ptr->text[i].compression);
+#else
+ png_warning(png_ptr, "Unable to write compressed text");
+#endif
+ /* Mark this chunk as written */
+ info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
+ }
+
+ else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
+ {
+#ifdef PNG_WRITE_tEXt_SUPPORTED
+ /* Write uncompressed chunk */
+ png_write_tEXt(png_ptr, info_ptr->text[i].key,
+ info_ptr->text[i].text,
+ 0);
+ /* Mark this chunk as written */
+ info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+#else
+ /* Can't get here */
+ png_warning(png_ptr, "Unable to write uncompressed text");
+#endif
+ }
+ }
+#endif /* tEXt */
+
+#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+ if (info_ptr->unknown_chunks_num)
+ {
+ png_unknown_chunk *up;
+
+ png_debug(5, "writing extra chunks");
+
+ for (up = info_ptr->unknown_chunks;
+ up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
+ up++)
+ {
+ int keep = png_handle_as_unknown(png_ptr, up->name);
+ if (keep != PNG_HANDLE_CHUNK_NEVER &&
+ up->location && (up->location & PNG_HAVE_PLTE) &&
+ !(up->location & PNG_HAVE_IDAT) &&
+ ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
+ (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
+ {
+ png_write_chunk(png_ptr, up->name, up->data, up->size);
+ }
+ }
+ }
+#endif
+}
+
+/* Writes the end of the PNG file. If you don't want to write comments or
+ * time information, you can pass NULL for info. If you already wrote these
+ * in png_write_info(), do not write them again here. If you have long
+ * comments, I suggest writing them here, and compressing them.
+ */
+void PNGAPI
+png_write_end(png_structp png_ptr, png_infop info_ptr)
+{
+ png_debug(1, "in png_write_end");
+
+ if (png_ptr == NULL)
+ return;
+
+ if (!(png_ptr->mode & PNG_HAVE_IDAT))
+ png_error(png_ptr, "No IDATs written into file");
+
+ /* See if user wants us to write information chunks */
+ if (info_ptr != NULL)
+ {
+#ifdef PNG_WRITE_TEXT_SUPPORTED
+ int i; /* local index variable */
+#endif
+#ifdef PNG_WRITE_tIME_SUPPORTED
+ /* Check to see if user has supplied a time chunk */
+ if ((info_ptr->valid & PNG_INFO_tIME) &&
+ !(png_ptr->mode & PNG_WROTE_tIME))
+ png_write_tIME(png_ptr, &(info_ptr->mod_time));
+
+#endif
+#ifdef PNG_WRITE_TEXT_SUPPORTED
+ /* Loop through comment chunks */
+ for (i = 0; i < info_ptr->num_text; i++)
+ {
+ png_debug2(2, "Writing trailer text chunk %d, type %d", i,
+ info_ptr->text[i].compression);
+ /* An internationalized chunk? */
+ if (info_ptr->text[i].compression > 0)
+ {
+#ifdef PNG_WRITE_iTXt_SUPPORTED
+ /* Write international chunk */
+ png_write_iTXt(png_ptr,
+ info_ptr->text[i].compression,
+ info_ptr->text[i].key,
+ info_ptr->text[i].lang,
+ info_ptr->text[i].lang_key,
+ info_ptr->text[i].text);
+#else
+ png_warning(png_ptr, "Unable to write international text");
+#endif
+ /* Mark this chunk as written */
+ info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+ }
+
+ else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
+ {
+#ifdef PNG_WRITE_zTXt_SUPPORTED
+ /* Write compressed chunk */
+ png_write_zTXt(png_ptr, info_ptr->text[i].key,
+ info_ptr->text[i].text, 0,
+ info_ptr->text[i].compression);
+#else
+ png_warning(png_ptr, "Unable to write compressed text");
+#endif
+ /* Mark this chunk as written */
+ info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
+ }
+
+ else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
+ {
+#ifdef PNG_WRITE_tEXt_SUPPORTED
+ /* Write uncompressed chunk */
+ png_write_tEXt(png_ptr, info_ptr->text[i].key,
+ info_ptr->text[i].text, 0);
+#else
+ png_warning(png_ptr, "Unable to write uncompressed text");
+#endif
+
+ /* Mark this chunk as written */
+ info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+ }
+ }
+#endif
+#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+ if (info_ptr->unknown_chunks_num)
+ {
+ png_unknown_chunk *up;
+
+ png_debug(5, "writing extra chunks");
+
+ for (up = info_ptr->unknown_chunks;
+ up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
+ up++)
+ {
+ int keep = png_handle_as_unknown(png_ptr, up->name);
+ if (keep != PNG_HANDLE_CHUNK_NEVER &&
+ up->location && (up->location & PNG_AFTER_IDAT) &&
+ ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
+ (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
+ {
+ png_write_chunk(png_ptr, up->name, up->data, up->size);
+ }
+ }
+ }
+#endif
+ }
+
+ png_ptr->mode |= PNG_AFTER_IDAT;
+
+ /* Write end of PNG file */
+ png_write_IEND(png_ptr);
+ /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03,
+ * and restored again in libpng-1.2.30, may cause some applications that
+ * do not set png_ptr->output_flush_fn to crash. If your application
+ * experiences a problem, please try building libpng with
+ * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to
+ * png-mng-implement at lists.sf.net .
+ */
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+# ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED
+ png_flush(png_ptr);
+# endif
+#endif
+}
+
+#ifdef PNG_CONVERT_tIME_SUPPORTED
+/* "tm" structure is not supported on WindowsCE */
+void PNGAPI
+png_convert_from_struct_tm(png_timep ptime, PNG_CONST struct tm FAR * ttime)
+{
+ png_debug(1, "in png_convert_from_struct_tm");
+
+ ptime->year = (png_uint_16)(1900 + ttime->tm_year);
+ ptime->month = (png_byte)(ttime->tm_mon + 1);
+ ptime->day = (png_byte)ttime->tm_mday;
+ ptime->hour = (png_byte)ttime->tm_hour;
+ ptime->minute = (png_byte)ttime->tm_min;
+ ptime->second = (png_byte)ttime->tm_sec;
+}
+
+void PNGAPI
+png_convert_from_time_t(png_timep ptime, time_t ttime)
+{
+ struct tm *tbuf;
+
+ png_debug(1, "in png_convert_from_time_t");
+
+ tbuf = gmtime(&ttime);
+ png_convert_from_struct_tm(ptime, tbuf);
+}
+#endif
+
+/* Initialize png_ptr structure, and allocate any memory needed */
+PNG_FUNCTION(png_structp,PNGAPI
+png_create_write_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
+ png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED)
+{
+#ifdef PNG_USER_MEM_SUPPORTED
+ return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn,
+ warn_fn, NULL, NULL, NULL));
+}
+
+/* Alternate initialize png_ptr structure, and allocate any memory needed */
+static void png_reset_filter_heuristics(png_structp png_ptr); /* forward decl */
+
+PNG_FUNCTION(png_structp,PNGAPI
+png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
+ png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
+ png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+ volatile int png_cleanup_needed = 0;
+#ifdef PNG_SETJMP_SUPPORTED
+ volatile
+#endif
+ png_structp png_ptr;
+#ifdef PNG_SETJMP_SUPPORTED
+#ifdef USE_FAR_KEYWORD
+ jmp_buf png_jmpbuf;
+#endif
+#endif
+ int i;
+
+ png_debug(1, "in png_create_write_struct");
+
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG,
+ (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr);
+#else
+ png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG);
+#endif /* PNG_USER_MEM_SUPPORTED */
+ if (png_ptr == NULL)
+ return (NULL);
+
+ /* Added at libpng-1.2.6 */
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+ png_ptr->user_width_max = PNG_USER_WIDTH_MAX;
+ png_ptr->user_height_max = PNG_USER_HEIGHT_MAX;
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+/* Applications that neglect to set up their own setjmp() and then
+ encounter a png_error() will longjmp here. Since the jmpbuf is
+ then meaningless we abort instead of returning. */
+#ifdef USE_FAR_KEYWORD
+ if (setjmp(png_jmpbuf))
+#else
+ if (setjmp(png_jmpbuf(png_ptr))) /* sets longjmp to match setjmp */
+#endif
+#ifdef USE_FAR_KEYWORD
+ png_memcpy(png_jmpbuf(png_ptr), png_jmpbuf, png_sizeof(jmp_buf));
+#endif
+ PNG_ABORT();
+#endif
+
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn);
+#endif /* PNG_USER_MEM_SUPPORTED */
+ png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
+
+ if (user_png_ver)
+ {
+ i = 0;
+ do
+ {
+ if (user_png_ver[i] != png_libpng_ver[i])
+ png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
+ } while (png_libpng_ver[i++]);
+ }
+
+ if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH)
+ {
+ /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
+ * we must recompile any applications that use any older library version.
+ * For versions after libpng 1.0, we will be compatible, so we need
+ * only check the first digit.
+ */
+ if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
+ (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) ||
+ (user_png_ver[0] == '0' && user_png_ver[2] < '9'))
+ {
+#ifdef PNG_CONSOLE_IO_SUPPORTED
+ char msg[80];
+
+ if (user_png_ver)
+ {
+ png_snprintf2(msg, 80,
+ "Application built with libpng-%.20s"
+ " but running with %.20s",
+ user_png_ver,
+ png_libpng_ver);
+ png_warning(png_ptr, msg);
+ }
+#else
+ png_warning(png_ptr,
+ "Incompatible libpng version in application and library");
+#endif
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+ png_ptr->flags = 0;
+#endif
+ png_cleanup_needed = 1;
+ }
+ }
+
+ /* Initialize zbuf - compression buffer */
+ png_ptr->zbuf_size = PNG_ZBUF_SIZE;
+
+ if (!png_cleanup_needed)
+ {
+ png_ptr->zbuf = (png_bytep)png_malloc_warn(png_ptr,
+ png_ptr->zbuf_size);
+ if (png_ptr->zbuf == NULL)
+ png_cleanup_needed = 1;
+ }
+
+ if (png_cleanup_needed)
+ {
+ /* Clean up PNG structure and deallocate any memory. */
+ png_free(png_ptr, png_ptr->zbuf);
+ png_ptr->zbuf = NULL;
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_destroy_struct_2((png_voidp)png_ptr,
+ (png_free_ptr)free_fn, (png_voidp)mem_ptr);
+#else
+ png_destroy_struct((png_voidp)png_ptr);
+#endif
+ return (NULL);
+ }
+
+ png_set_write_fn(png_ptr, NULL, NULL, NULL);
+
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+ png_reset_filter_heuristics(png_ptr);
+#endif
+
+ return (png_ptr);
+}
+
+
+/* Write a few rows of image data. If the image is interlaced,
+ * either you will have to write the 7 sub images, or, if you
+ * have called png_set_interlace_handling(), you will have to
+ * "write" the image seven times.
+ */
+void PNGAPI
+png_write_rows(png_structp png_ptr, png_bytepp row,
+ png_uint_32 num_rows)
+{
+ png_uint_32 i; /* row counter */
+ png_bytepp rp; /* row pointer */
+
+ png_debug(1, "in png_write_rows");
+
+ if (png_ptr == NULL)
+ return;
+
+ /* Loop through the rows */
+ for (i = 0, rp = row; i < num_rows; i++, rp++)
+ {
+ png_write_row(png_ptr, *rp);
+ }
+}
+
+/* Write the image. You only need to call this function once, even
+ * if you are writing an interlaced image.
+ */
+void PNGAPI
+png_write_image(png_structp png_ptr, png_bytepp image)
+{
+ png_uint_32 i; /* row index */
+ int pass, num_pass; /* pass variables */
+ png_bytepp rp; /* points to current row */
+
+ if (png_ptr == NULL)
+ return;
+
+ png_debug(1, "in png_write_image");
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ /* Initialize interlace handling. If image is not interlaced,
+ * this will set pass to 1
+ */
+ num_pass = png_set_interlace_handling(png_ptr);
+#else
+ num_pass = 1;
+#endif
+ /* Loop through passes */
+ for (pass = 0; pass < num_pass; pass++)
+ {
+ /* Loop through image */
+ for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
+ {
+ png_write_row(png_ptr, *rp);
+ }
+ }
+}
+
+/* Called by user to write a row of image data */
+void PNGAPI
+png_write_row(png_structp png_ptr, png_const_bytep row)
+{
+ if (png_ptr == NULL)
+ return;
+
+ png_debug2(1, "in png_write_row (row %u, pass %d)",
+ png_ptr->row_number, png_ptr->pass);
+
+ /* Initialize transformations and other stuff if first time */
+ if (png_ptr->row_number == 0 && png_ptr->pass == 0)
+ {
+ /* Make sure we wrote the header info */
+ if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
+ png_error(png_ptr,
+ "png_write_info was never called before png_write_row");
+
+ /* Check for transforms that have been set but were defined out */
+#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED)
+ if (png_ptr->transformations & PNG_INVERT_MONO)
+ png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined");
+#endif
+
+#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED)
+ if (png_ptr->transformations & PNG_FILLER)
+ png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined");
+#endif
+#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \
+ defined(PNG_READ_PACKSWAP_SUPPORTED)
+ if (png_ptr->transformations & PNG_PACKSWAP)
+ png_warning(png_ptr,
+ "PNG_WRITE_PACKSWAP_SUPPORTED is not defined");
+#endif
+
+#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)
+ if (png_ptr->transformations & PNG_PACK)
+ png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined");
+#endif
+
+#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)
+ if (png_ptr->transformations & PNG_SHIFT)
+ png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined");
+#endif
+
+#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)
+ if (png_ptr->transformations & PNG_BGR)
+ png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined");
+#endif
+
+#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)
+ if (png_ptr->transformations & PNG_SWAP_BYTES)
+ png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined");
+#endif
+
+ png_write_start_row(png_ptr);
+ }
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ /* If interlaced and not interested in row, return */
+ if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
+ {
+ switch (png_ptr->pass)
+ {
+ case 0:
+ if (png_ptr->row_number & 0x07)
+ {
+ png_write_finish_row(png_ptr);
+ return;
+ }
+ break;
+
+ case 1:
+ if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)
+ {
+ png_write_finish_row(png_ptr);
+ return;
+ }
+ break;
+
+ case 2:
+ if ((png_ptr->row_number & 0x07) != 4)
+ {
+ png_write_finish_row(png_ptr);
+ return;
+ }
+ break;
+
+ case 3:
+ if ((png_ptr->row_number & 0x03) || png_ptr->width < 3)
+ {
+ png_write_finish_row(png_ptr);
+ return;
+ }
+ break;
+
+ case 4:
+ if ((png_ptr->row_number & 0x03) != 2)
+ {
+ png_write_finish_row(png_ptr);
+ return;
+ }
+ break;
+
+ case 5:
+ if ((png_ptr->row_number & 0x01) || png_ptr->width < 2)
+ {
+ png_write_finish_row(png_ptr);
+ return;
+ }
+ break;
+
+ case 6:
+ if (!(png_ptr->row_number & 0x01))
+ {
+ png_write_finish_row(png_ptr);
+ return;
+ }
+ break;
+
+ default: /* error: ignore it */
+ break;
+ }
+ }
+#endif
+
+ /* Set up row info for transformations */
+ png_ptr->row_info.color_type = png_ptr->color_type;
+ png_ptr->row_info.width = png_ptr->usr_width;
+ png_ptr->row_info.channels = png_ptr->usr_channels;
+ png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth;
+ png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth *
+ png_ptr->row_info.channels);
+
+ png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth,
+ png_ptr->row_info.width);
+
+ png_debug1(3, "row_info->color_type = %d", png_ptr->row_info.color_type);
+ png_debug1(3, "row_info->width = %u", png_ptr->row_info.width);
+ png_debug1(3, "row_info->channels = %d", png_ptr->row_info.channels);
+ png_debug1(3, "row_info->bit_depth = %d", png_ptr->row_info.bit_depth);
+ png_debug1(3, "row_info->pixel_depth = %d", png_ptr->row_info.pixel_depth);
+ png_debug1(3, "row_info->rowbytes = %lu",
+ (unsigned long)png_ptr->row_info.rowbytes);
+
+ /* Copy user's row into buffer, leaving room for filter byte. */
+ png_memcpy(png_ptr->row_buf + 1, row, png_ptr->row_info.rowbytes);
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ /* Handle interlacing */
+ if (png_ptr->interlaced && png_ptr->pass < 6 &&
+ (png_ptr->transformations & PNG_INTERLACE))
+ {
+ png_do_write_interlace(&(png_ptr->row_info),
+ png_ptr->row_buf + 1, png_ptr->pass);
+ /* This should always get caught above, but still ... */
+ if (!(png_ptr->row_info.width))
+ {
+ png_write_finish_row(png_ptr);
+ return;
+ }
+ }
+#endif
+
+ /* Handle other transformations */
+ if (png_ptr->transformations)
+ png_do_write_transformations(png_ptr);
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+ /* Write filter_method 64 (intrapixel differencing) only if
+ * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
+ * 2. Libpng did not write a PNG signature (this filter_method is only
+ * used in PNG datastreams that are embedded in MNG datastreams) and
+ * 3. The application called png_permit_mng_features with a mask that
+ * included PNG_FLAG_MNG_FILTER_64 and
+ * 4. The filter_method is 64 and
+ * 5. The color_type is RGB or RGBA
+ */
+ if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
+ (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
+ {
+ /* Intrapixel differencing */
+ png_do_write_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1);
+ }
+#endif
+
+ /* Find a filter if necessary, filter the row and write it out. */
+ png_write_find_filter(png_ptr, &(png_ptr->row_info));
+
+ if (png_ptr->write_row_fn != NULL)
+ (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
+}
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+/* Set the automatic flush interval or 0 to turn flushing off */
+void PNGAPI
+png_set_flush(png_structp png_ptr, int nrows)
+{
+ png_debug(1, "in png_set_flush");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
+}
+
+/* Flush the current output buffers now */
+void PNGAPI
+png_write_flush(png_structp png_ptr)
+{
+ int wrote_IDAT;
+
+ png_debug(1, "in png_write_flush");
+
+ if (png_ptr == NULL)
+ return;
+
+ /* We have already written out all of the data */
+ if (png_ptr->row_number >= png_ptr->num_rows)
+ return;
+
+ do
+ {
+ int ret;
+
+ /* Compress the data */
+ ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH);
+ wrote_IDAT = 0;
+
+ /* Check for compression errors */
+ if (ret != Z_OK)
+ {
+ if (png_ptr->zstream.msg != NULL)
+ png_error(png_ptr, png_ptr->zstream.msg);
+
+ else
+ png_error(png_ptr, "zlib error");
+ }
+
+ if (!(png_ptr->zstream.avail_out))
+ {
+ /* Write the IDAT and reset the zlib output buffer */
+ png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
+ png_ptr->zstream.next_out = png_ptr->zbuf;
+ png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+ wrote_IDAT = 1;
+ }
+ } while (wrote_IDAT == 1);
+
+ /* If there is any data left to be output, write it into a new IDAT */
+ if (png_ptr->zbuf_size != png_ptr->zstream.avail_out)
+ {
+ /* Write the IDAT and reset the zlib output buffer */
+ png_write_IDAT(png_ptr, png_ptr->zbuf,
+ png_ptr->zbuf_size - png_ptr->zstream.avail_out);
+ png_ptr->zstream.next_out = png_ptr->zbuf;
+ png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+ }
+ png_ptr->flush_rows = 0;
+ png_flush(png_ptr);
+}
+#endif /* PNG_WRITE_FLUSH_SUPPORTED */
+
+/* Free all memory used by the write */
+void PNGAPI
+png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
+{
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL;
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_free_ptr free_fn = NULL;
+ png_voidp mem_ptr = NULL;
+#endif
+
+ png_debug(1, "in png_destroy_write_struct");
+
+ if (png_ptr_ptr != NULL)
+ {
+ png_ptr = *png_ptr_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+ free_fn = png_ptr->free_fn;
+ mem_ptr = png_ptr->mem_ptr;
+#endif
+ }
+
+#ifdef PNG_USER_MEM_SUPPORTED
+ if (png_ptr != NULL)
+ {
+ free_fn = png_ptr->free_fn;
+ mem_ptr = png_ptr->mem_ptr;
+ }
+#endif
+
+ if (info_ptr_ptr != NULL)
+ info_ptr = *info_ptr_ptr;
+
+ if (info_ptr != NULL)
+ {
+ if (png_ptr != NULL)
+ {
+ png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
+
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+ if (png_ptr->num_chunk_list)
+ {
+ png_free(png_ptr, png_ptr->chunk_list);
+ png_ptr->num_chunk_list = 0;
+ }
+#endif
+ }
+
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn,
+ (png_voidp)mem_ptr);
+#else
+ png_destroy_struct((png_voidp)info_ptr);
+#endif
+ *info_ptr_ptr = NULL;
+ }
+
+ if (png_ptr != NULL)
+ {
+ png_write_destroy(png_ptr);
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn,
+ (png_voidp)mem_ptr);
+#else
+ png_destroy_struct((png_voidp)png_ptr);
+#endif
+ *png_ptr_ptr = NULL;
+ }
+}
+
+
+/* Free any memory used in png_ptr struct (old method) */
+void /* PRIVATE */
+png_write_destroy(png_structp png_ptr)
+{
+#ifdef PNG_SETJMP_SUPPORTED
+ jmp_buf tmp_jmp; /* Save jump buffer */
+#endif
+ png_error_ptr error_fn;
+ png_error_ptr warning_fn;
+ png_voidp error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_free_ptr free_fn;
+#endif
+
+ png_debug(1, "in png_write_destroy");
+
+ /* Free any memory zlib uses */
+ deflateEnd(&png_ptr->zstream);
+
+ /* Free our memory. png_free checks NULL for us. */
+ png_free(png_ptr, png_ptr->zbuf);
+ png_free(png_ptr, png_ptr->row_buf);
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+ png_free(png_ptr, png_ptr->prev_row);
+ png_free(png_ptr, png_ptr->sub_row);
+ png_free(png_ptr, png_ptr->up_row);
+ png_free(png_ptr, png_ptr->avg_row);
+ png_free(png_ptr, png_ptr->paeth_row);
+#endif
+
+#ifdef PNG_TIME_RFC1123_SUPPORTED
+ png_free(png_ptr, png_ptr->time_buffer);
+#endif
+
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+ /* Use this to save a little code space, it doesn't free the filter_costs */
+ png_reset_filter_heuristics(png_ptr);
+ png_free(png_ptr, png_ptr->filter_costs);
+ png_free(png_ptr, png_ptr->inv_filter_costs);
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+ /* Reset structure */
+ png_memcpy(tmp_jmp, png_ptr->png_jmpbuf, png_sizeof(jmp_buf));
+#endif
+
+ error_fn = png_ptr->error_fn;
+ warning_fn = png_ptr->warning_fn;
+ error_ptr = png_ptr->error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+ free_fn = png_ptr->free_fn;
+#endif
+
+ png_memset(png_ptr, 0, png_sizeof(png_struct));
+
+ png_ptr->error_fn = error_fn;
+ png_ptr->warning_fn = warning_fn;
+ png_ptr->error_ptr = error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_ptr->free_fn = free_fn;
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+ png_memcpy(png_ptr->png_jmpbuf, tmp_jmp, png_sizeof(jmp_buf));
+#endif
+}
+
+/* Allow the application to select one or more row filters to use. */
+void PNGAPI
+png_set_filter(png_structp png_ptr, int method, int filters)
+{
+ png_debug(1, "in png_set_filter");
+
+ if (png_ptr == NULL)
+ return;
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+ if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
+ (method == PNG_INTRAPIXEL_DIFFERENCING))
+ method = PNG_FILTER_TYPE_BASE;
+
+#endif
+ if (method == PNG_FILTER_TYPE_BASE)
+ {
+ switch (filters & (PNG_ALL_FILTERS | 0x07))
+ {
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+ case 5:
+ case 6:
+ case 7: png_warning(png_ptr, "Unknown row filter for method 0");
+#endif /* PNG_WRITE_FILTER_SUPPORTED */
+ case PNG_FILTER_VALUE_NONE:
+ png_ptr->do_filter = PNG_FILTER_NONE; break;
+
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+ case PNG_FILTER_VALUE_SUB:
+ png_ptr->do_filter = PNG_FILTER_SUB; break;
+
+ case PNG_FILTER_VALUE_UP:
+ png_ptr->do_filter = PNG_FILTER_UP; break;
+
+ case PNG_FILTER_VALUE_AVG:
+ png_ptr->do_filter = PNG_FILTER_AVG; break;
+
+ case PNG_FILTER_VALUE_PAETH:
+ png_ptr->do_filter = PNG_FILTER_PAETH; break;
+
+ default:
+ png_ptr->do_filter = (png_byte)filters; break;
+#else
+ default:
+ png_warning(png_ptr, "Unknown row filter for method 0");
+#endif /* PNG_WRITE_FILTER_SUPPORTED */
+ }
+
+ /* If we have allocated the row_buf, this means we have already started
+ * with the image and we should have allocated all of the filter buffers
+ * that have been selected. If prev_row isn't already allocated, then
+ * it is too late to start using the filters that need it, since we
+ * will be missing the data in the previous row. If an application
+ * wants to start and stop using particular filters during compression,
+ * it should start out with all of the filters, and then add and
+ * remove them after the start of compression.
+ */
+ if (png_ptr->row_buf != NULL)
+ {
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+ if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL)
+ {
+ png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
+ (png_ptr->rowbytes + 1));
+ png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
+ }
+
+ if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL)
+ {
+ if (png_ptr->prev_row == NULL)
+ {
+ png_warning(png_ptr, "Can't add Up filter after starting");
+ png_ptr->do_filter = (png_byte)(png_ptr->do_filter &
+ ~PNG_FILTER_UP);
+ }
+
+ else
+ {
+ png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
+ (png_ptr->rowbytes + 1));
+ png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
+ }
+ }
+
+ if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL)
+ {
+ if (png_ptr->prev_row == NULL)
+ {
+ png_warning(png_ptr, "Can't add Average filter after starting");
+ png_ptr->do_filter = (png_byte)(png_ptr->do_filter &
+ ~PNG_FILTER_AVG);
+ }
+
+ else
+ {
+ png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
+ (png_ptr->rowbytes + 1));
+ png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
+ }
+ }
+
+ if ((png_ptr->do_filter & PNG_FILTER_PAETH) &&
+ png_ptr->paeth_row == NULL)
+ {
+ if (png_ptr->prev_row == NULL)
+ {
+ png_warning(png_ptr, "Can't add Paeth filter after starting");
+ png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH);
+ }
+
+ else
+ {
+ png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,
+ (png_ptr->rowbytes + 1));
+ png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
+ }
+ }
+
+ if (png_ptr->do_filter == PNG_NO_FILTERS)
+#endif /* PNG_WRITE_FILTER_SUPPORTED */
+ png_ptr->do_filter = PNG_FILTER_NONE;
+ }
+ }
+ else
+ png_error(png_ptr, "Unknown custom filter method");
+}
+
+/* This allows us to influence the way in which libpng chooses the "best"
+ * filter for the current scanline. While the "minimum-sum-of-absolute-
+ * differences metric is relatively fast and effective, there is some
+ * question as to whether it can be improved upon by trying to keep the
+ * filtered data going to zlib more consistent, hopefully resulting in
+ * better compression.
+ */
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */
+/* Convenience reset API. */
+static void
+png_reset_filter_heuristics(png_structp png_ptr)
+{
+ /* Clear out any old values in the 'weights' - this must be done because if
+ * the app calls set_filter_heuristics multiple times with different
+ * 'num_weights' values we would otherwise potentially have wrong sized
+ * arrays.
+ */
+ png_ptr->num_prev_filters = 0;
+ png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED;
+ if (png_ptr->prev_filters != NULL)
+ {
+ png_bytep old = png_ptr->prev_filters;
+ png_ptr->prev_filters = NULL;
+ png_free(png_ptr, old);
+ }
+ if (png_ptr->filter_weights != NULL)
+ {
+ png_uint_16p old = png_ptr->filter_weights;
+ png_ptr->filter_weights = NULL;
+ png_free(png_ptr, old);
+ }
+
+ if (png_ptr->inv_filter_weights != NULL)
+ {
+ png_uint_16p old = png_ptr->inv_filter_weights;
+ png_ptr->inv_filter_weights = NULL;
+ png_free(png_ptr, old);
+ }
+
+ /* Leave the filter_costs - this array is fixed size. */
+}
+
+static int
+png_init_filter_heuristics(png_structp png_ptr, int heuristic_method,
+ int num_weights)
+{
+ if (png_ptr == NULL)
+ return 0;
+
+ /* Clear out the arrays */
+ png_reset_filter_heuristics(png_ptr);
+
+ /* Check arguments; the 'reset' function makes the correct settings for the
+ * unweighted case, but we must handle the weight case by initializing the
+ * arrays for the caller.
+ */
+ if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+ {
+ int i;
+
+ if (num_weights > 0)
+ {
+ png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr,
+ (png_uint_32)(png_sizeof(png_byte) * num_weights));
+
+ /* To make sure that the weighting starts out fairly */
+ for (i = 0; i < num_weights; i++)
+ {
+ png_ptr->prev_filters[i] = 255;
+ }
+
+ png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr,
+ (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
+
+ png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr,
+ (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
+
+ for (i = 0; i < num_weights; i++)
+ {
+ png_ptr->inv_filter_weights[i] =
+ png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
+ }
+
+ /* Safe to set this now */
+ png_ptr->num_prev_filters = (png_byte)num_weights;
+ }
+
+ /* If, in the future, there are other filter methods, this would
+ * need to be based on png_ptr->filter.
+ */
+ if (png_ptr->filter_costs == NULL)
+ {
+ png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr,
+ (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
+
+ png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr,
+ (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
+ }
+
+ for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
+ {
+ png_ptr->inv_filter_costs[i] =
+ png_ptr->filter_costs[i] = PNG_COST_FACTOR;
+ }
+
+ /* All the arrays are inited, safe to set this: */
+ png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_WEIGHTED;
+
+ /* Return the 'ok' code. */
+ return 1;
+ }
+ else if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT ||
+ heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED)
+ {
+ return 1;
+ }
+ else
+ {
+ png_warning(png_ptr, "Unknown filter heuristic method");
+ return 0;
+ }
+}
+
+/* Provide floating and fixed point APIs */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_filter_heuristics(png_structp png_ptr, int heuristic_method,
+ int num_weights, png_const_doublep filter_weights,
+ png_const_doublep filter_costs)
+{
+ png_debug(1, "in png_set_filter_heuristics");
+
+ /* The internal API allocates all the arrays and ensures that the elements of
+ * those arrays are set to the default value.
+ */
+ if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights))
+ return;
+
+ /* If using the weighted method copy in the weights. */
+ if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+ {
+ int i;
+ for (i = 0; i < num_weights; i++)
+ {
+ if (filter_weights[i] <= 0.0)
+ {
+ png_ptr->inv_filter_weights[i] =
+ png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
+ }
+
+ else
+ {
+ png_ptr->inv_filter_weights[i] =
+ (png_uint_16)(PNG_WEIGHT_FACTOR*filter_weights[i]+.5);
+
+ png_ptr->filter_weights[i] =
+ (png_uint_16)(PNG_WEIGHT_FACTOR/filter_weights[i]+.5);
+ }
+ }
+
+ /* Here is where we set the relative costs of the different filters. We
+ * should take the desired compression level into account when setting
+ * the costs, so that Paeth, for instance, has a high relative cost at low
+ * compression levels, while it has a lower relative cost at higher
+ * compression settings. The filter types are in order of increasing
+ * relative cost, so it would be possible to do this with an algorithm.
+ */
+ for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) if (filter_costs[i] >= 1.0)
+ {
+ png_ptr->inv_filter_costs[i] =
+ (png_uint_16)(PNG_COST_FACTOR / filter_costs[i] + .5);
+
+ png_ptr->filter_costs[i] =
+ (png_uint_16)(PNG_COST_FACTOR * filter_costs[i] + .5);
+ }
+ }
+}
+#endif /* FLOATING_POINT */
+
+#ifdef PNG_FIXED_POINT_SUPPORTED
+void PNGAPI
+png_set_filter_heuristics_fixed(png_structp png_ptr, int heuristic_method,
+ int num_weights, png_const_fixed_point_p filter_weights,
+ png_const_fixed_point_p filter_costs)
+{
+ png_debug(1, "in png_set_filter_heuristics_fixed");
+
+ /* The internal API allocates all the arrays and ensures that the elements of
+ * those arrays are set to the default value.
+ */
+ if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights))
+ return;
+
+ /* If using the weighted method copy in the weights. */
+ if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+ {
+ int i;
+ for (i = 0; i < num_weights; i++)
+ {
+ if (filter_weights[i] <= 0)
+ {
+ png_ptr->inv_filter_weights[i] =
+ png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
+ }
+
+ else
+ {
+ png_ptr->inv_filter_weights[i] = (png_uint_16)
+ ((PNG_WEIGHT_FACTOR*filter_weights[i]+PNG_FP_HALF)/PNG_FP_1);
+
+ png_ptr->filter_weights[i] = (png_uint_16)((PNG_WEIGHT_FACTOR*
+ PNG_FP_1+(filter_weights[i]/2))/filter_weights[i]);
+ }
+ }
+
+ /* Here is where we set the relative costs of the different filters. We
+ * should take the desired compression level into account when setting
+ * the costs, so that Paeth, for instance, has a high relative cost at low
+ * compression levels, while it has a lower relative cost at higher
+ * compression settings. The filter types are in order of increasing
+ * relative cost, so it would be possible to do this with an algorithm.
+ */
+ for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
+ if (filter_costs[i] >= PNG_FP_1)
+ {
+ png_uint_32 tmp;
+
+ /* Use a 32 bit unsigned temporary here because otherwise the
+ * intermediate value will be a 32 bit *signed* integer (ANSI rules)
+ * and this will get the wrong answer on division.
+ */
+ tmp = PNG_COST_FACTOR*PNG_FP_1 + (filter_costs[i]/2);
+ tmp /= filter_costs[i];
+
+ png_ptr->inv_filter_costs[i] = (png_uint_16)tmp;
+
+ tmp = PNG_COST_FACTOR * filter_costs[i] + PNG_FP_HALF;
+ tmp /= PNG_FP_1;
+
+ png_ptr->filter_costs[i] = (png_uint_16)tmp;
+ }
+ }
+}
+#endif /* FIXED_POINT */
+#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
+
+void PNGAPI
+png_set_compression_level(png_structp png_ptr, int level)
+{
+ png_debug(1, "in png_set_compression_level");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL;
+ png_ptr->zlib_level = level;
+}
+
+void PNGAPI
+png_set_compression_mem_level(png_structp png_ptr, int mem_level)
+{
+ png_debug(1, "in png_set_compression_mem_level");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL;
+ png_ptr->zlib_mem_level = mem_level;
+}
+
+void PNGAPI
+png_set_compression_strategy(png_structp png_ptr, int strategy)
+{
+ png_debug(1, "in png_set_compression_strategy");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
+ png_ptr->zlib_strategy = strategy;
+}
+
+void PNGAPI
+png_set_compression_window_bits(png_structp png_ptr, int window_bits)
+{
+ if (png_ptr == NULL)
+ return;
+
+ if (window_bits > 15)
+ png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
+
+ else if (window_bits < 8)
+ png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
+
+#ifndef WBITS_8_OK
+ /* Avoid libpng bug with 256-byte windows */
+ if (window_bits == 8)
+ {
+ png_warning(png_ptr, "Compression window is being reset to 512");
+ window_bits = 9;
+ }
+
+#endif
+ png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS;
+ png_ptr->zlib_window_bits = window_bits;
+}
+
+void PNGAPI
+png_set_compression_method(png_structp png_ptr, int method)
+{
+ png_debug(1, "in png_set_compression_method");
+
+ if (png_ptr == NULL)
+ return;
+
+ if (method != 8)
+ png_warning(png_ptr, "Only compression method 8 is supported by PNG");
+
+ png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD;
+ png_ptr->zlib_method = method;
+}
+
+void PNGAPI
+png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn)
+{
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->write_row_fn = write_row_fn;
+}
+
+#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
+void PNGAPI
+png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr
+ write_user_transform_fn)
+{
+ png_debug(1, "in png_set_write_user_transform_fn");
+
+ if (png_ptr == NULL)
+ return;
+
+ png_ptr->transformations |= PNG_USER_TRANSFORM;
+ png_ptr->write_user_transform_fn = write_user_transform_fn;
+}
+#endif
+
+
+#ifdef PNG_INFO_IMAGE_SUPPORTED
+void PNGAPI
+png_write_png(png_structp png_ptr, png_infop info_ptr,
+ int transforms, voidp params)
+{
+ if (png_ptr == NULL || info_ptr == NULL)
+ return;
+
+ /* Write the file header information. */
+ png_write_info(png_ptr, info_ptr);
+
+ /* ------ these transformations don't touch the info structure ------- */
+
+#ifdef PNG_WRITE_INVERT_SUPPORTED
+ /* Invert monochrome pixels */
+ if (transforms & PNG_TRANSFORM_INVERT_MONO)
+ png_set_invert_mono(png_ptr);
+#endif
+
+#ifdef PNG_WRITE_SHIFT_SUPPORTED
+ /* Shift the pixels up to a legal bit depth and fill in
+ * as appropriate to correctly scale the image.
+ */
+ if ((transforms & PNG_TRANSFORM_SHIFT)
+ && (info_ptr->valid & PNG_INFO_sBIT))
+ png_set_shift(png_ptr, &info_ptr->sig_bit);
+#endif
+
+#ifdef PNG_WRITE_PACK_SUPPORTED
+ /* Pack pixels into bytes */
+ if (transforms & PNG_TRANSFORM_PACKING)
+ png_set_packing(png_ptr);
+#endif
+
+#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
+ /* Swap location of alpha bytes from ARGB to RGBA */
+ if (transforms & PNG_TRANSFORM_SWAP_ALPHA)
+ png_set_swap_alpha(png_ptr);
+#endif
+
+#ifdef PNG_WRITE_FILLER_SUPPORTED
+ /* Pack XRGB/RGBX/ARGB/RGBA into * RGB (4 channels -> 3 channels) */
+ if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER)
+ png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
+
+ else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE)
+ png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
+#endif
+
+#ifdef PNG_WRITE_BGR_SUPPORTED
+ /* Flip BGR pixels to RGB */
+ if (transforms & PNG_TRANSFORM_BGR)
+ png_set_bgr(png_ptr);
+#endif
+
+#ifdef PNG_WRITE_SWAP_SUPPORTED
+ /* Swap bytes of 16-bit files to most significant byte first */
+ if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)
+ png_set_swap(png_ptr);
+#endif
+
+#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
+ /* Swap bits of 1, 2, 4 bit packed pixel formats */
+ if (transforms & PNG_TRANSFORM_PACKSWAP)
+ png_set_packswap(png_ptr);
+#endif
+
+#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
+ /* Invert the alpha channel from opacity to transparency */
+ if (transforms & PNG_TRANSFORM_INVERT_ALPHA)
+ png_set_invert_alpha(png_ptr);
+#endif
+
+ /* ----------------------- end of transformations ------------------- */
+
+ /* Write the bits */
+ if (info_ptr->valid & PNG_INFO_IDAT)
+ png_write_image(png_ptr, info_ptr->row_pointers);
+
+ /* It is REQUIRED to call this to finish writing the rest of the file */
+ png_write_end(png_ptr, info_ptr);
+
+ PNG_UNUSED(transforms) /* Quiet compiler warnings */
+ PNG_UNUSED(params)
+}
+#endif
+#endif /* PNG_WRITE_SUPPORTED */
diff --git a/libpng/pngwtran.c b/libpng/pngwtran.c
new file mode 100644
index 0000000..bbf4a25
--- /dev/null
+++ b/libpng/pngwtran.c
@@ -0,0 +1,631 @@
+
+/* pngwtran.c - transforms the data in a row for PNG writers
+ *
+ * Last changed in libpng 1.5.2 [March 31, 2011]
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include "pngpriv.h"
+
+#ifdef PNG_WRITE_SUPPORTED
+
+/* Transform the data according to the user's wishes. The order of
+ * transformations is significant.
+ */
+void /* PRIVATE */
+png_do_write_transformations(png_structp png_ptr)
+{
+ png_debug(1, "in png_do_write_transformations");
+
+ if (png_ptr == NULL)
+ return;
+
+#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
+ if (png_ptr->transformations & PNG_USER_TRANSFORM)
+ if (png_ptr->write_user_transform_fn != NULL)
+ (*(png_ptr->write_user_transform_fn)) /* User write transform
+ function */
+ (png_ptr, /* png_ptr */
+ &(png_ptr->row_info), /* row_info: */
+ /* png_uint_32 width; width of row */
+ /* png_size_t rowbytes; number of bytes in row */
+ /* png_byte color_type; color type of pixels */
+ /* png_byte bit_depth; bit depth of samples */
+ /* png_byte channels; number of channels (1-4) */
+ /* png_byte pixel_depth; bits per pixel (depth*channels) */
+ png_ptr->row_buf + 1); /* start of pixel data for row */
+#endif
+
+#ifdef PNG_WRITE_FILLER_SUPPORTED
+ if (png_ptr->transformations & PNG_FILLER)
+ png_do_strip_channel(&(png_ptr->row_info), png_ptr->row_buf + 1,
+ !(png_ptr->flags & PNG_FILLER_AFTER));
+#endif
+
+#ifdef PNG_WRITE_PACKSWAP_SUPPORTED
+ if (png_ptr->transformations & PNG_PACKSWAP)
+ png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_WRITE_PACK_SUPPORTED
+ if (png_ptr->transformations & PNG_PACK)
+ png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1,
+ (png_uint_32)png_ptr->bit_depth);
+#endif
+
+#ifdef PNG_WRITE_SWAP_SUPPORTED
+ if (png_ptr->transformations & PNG_SWAP_BYTES)
+ png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_WRITE_SHIFT_SUPPORTED
+ if (png_ptr->transformations & PNG_SHIFT)
+ png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1,
+ &(png_ptr->shift));
+#endif
+
+#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
+ if (png_ptr->transformations & PNG_SWAP_ALPHA)
+ png_do_write_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
+ if (png_ptr->transformations & PNG_INVERT_ALPHA)
+ png_do_write_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_WRITE_BGR_SUPPORTED
+ if (png_ptr->transformations & PNG_BGR)
+ png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#ifdef PNG_WRITE_INVERT_SUPPORTED
+ if (png_ptr->transformations & PNG_INVERT_MONO)
+ png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+}
+
+#ifdef PNG_WRITE_PACK_SUPPORTED
+/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The
+ * row_info bit depth should be 8 (one pixel per byte). The channels
+ * should be 1 (this only happens on grayscale and paletted images).
+ */
+void /* PRIVATE */
+png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth)
+{
+ png_debug(1, "in png_do_pack");
+
+ if (row_info->bit_depth == 8 &&
+ row_info->channels == 1)
+ {
+ switch ((int)bit_depth)
+ {
+ case 1:
+ {
+ png_bytep sp, dp;
+ int mask, v;
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+
+ sp = row;
+ dp = row;
+ mask = 0x80;
+ v = 0;
+
+ for (i = 0; i < row_width; i++)
+ {
+ if (*sp != 0)
+ v |= mask;
+
+ sp++;
+
+ if (mask > 1)
+ mask >>= 1;
+
+ else
+ {
+ mask = 0x80;
+ *dp = (png_byte)v;
+ dp++;
+ v = 0;
+ }
+ }
+
+ if (mask != 0x80)
+ *dp = (png_byte)v;
+
+ break;
+ }
+
+ case 2:
+ {
+ png_bytep sp, dp;
+ int shift, v;
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+
+ sp = row;
+ dp = row;
+ shift = 6;
+ v = 0;
+
+ for (i = 0; i < row_width; i++)
+ {
+ png_byte value;
+
+ value = (png_byte)(*sp & 0x03);
+ v |= (value << shift);
+
+ if (shift == 0)
+ {
+ shift = 6;
+ *dp = (png_byte)v;
+ dp++;
+ v = 0;
+ }
+
+ else
+ shift -= 2;
+
+ sp++;
+ }
+
+ if (shift != 6)
+ *dp = (png_byte)v;
+
+ break;
+ }
+
+ case 4:
+ {
+ png_bytep sp, dp;
+ int shift, v;
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+
+ sp = row;
+ dp = row;
+ shift = 4;
+ v = 0;
+
+ for (i = 0; i < row_width; i++)
+ {
+ png_byte value;
+
+ value = (png_byte)(*sp & 0x0f);
+ v |= (value << shift);
+
+ if (shift == 0)
+ {
+ shift = 4;
+ *dp = (png_byte)v;
+ dp++;
+ v = 0;
+ }
+
+ else
+ shift -= 4;
+
+ sp++;
+ }
+
+ if (shift != 4)
+ *dp = (png_byte)v;
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ row_info->bit_depth = (png_byte)bit_depth;
+ row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels);
+ row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
+ row_info->width);
+ }
+}
+#endif
+
+#ifdef PNG_WRITE_SHIFT_SUPPORTED
+/* Shift pixel values to take advantage of whole range. Pass the
+ * true number of bits in bit_depth. The row should be packed
+ * according to row_info->bit_depth. Thus, if you had a row of
+ * bit depth 4, but the pixels only had values from 0 to 7, you
+ * would pass 3 as bit_depth, and this routine would translate the
+ * data to 0 to 15.
+ */
+void /* PRIVATE */
+png_do_shift(png_row_infop row_info, png_bytep row,
+ png_const_color_8p bit_depth)
+{
+ png_debug(1, "in png_do_shift");
+
+ if (row_info->color_type != PNG_COLOR_TYPE_PALETTE)
+ {
+ int shift_start[4], shift_dec[4];
+ int channels = 0;
+
+ if (row_info->color_type & PNG_COLOR_MASK_COLOR)
+ {
+ shift_start[channels] = row_info->bit_depth - bit_depth->red;
+ shift_dec[channels] = bit_depth->red;
+ channels++;
+
+ shift_start[channels] = row_info->bit_depth - bit_depth->green;
+ shift_dec[channels] = bit_depth->green;
+ channels++;
+
+ shift_start[channels] = row_info->bit_depth - bit_depth->blue;
+ shift_dec[channels] = bit_depth->blue;
+ channels++;
+ }
+
+ else
+ {
+ shift_start[channels] = row_info->bit_depth - bit_depth->gray;
+ shift_dec[channels] = bit_depth->gray;
+ channels++;
+ }
+
+ if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
+ {
+ shift_start[channels] = row_info->bit_depth - bit_depth->alpha;
+ shift_dec[channels] = bit_depth->alpha;
+ channels++;
+ }
+
+ /* With low row depths, could only be grayscale, so one channel */
+ if (row_info->bit_depth < 8)
+ {
+ png_bytep bp = row;
+ png_size_t i;
+ png_byte mask;
+ png_size_t row_bytes = row_info->rowbytes;
+
+ if (bit_depth->gray == 1 && row_info->bit_depth == 2)
+ mask = 0x55;
+
+ else if (row_info->bit_depth == 4 && bit_depth->gray == 3)
+ mask = 0x11;
+
+ else
+ mask = 0xff;
+
+ for (i = 0; i < row_bytes; i++, bp++)
+ {
+ png_uint_16 v;
+ int j;
+
+ v = *bp;
+ *bp = 0;
+
+ for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0])
+ {
+ if (j > 0)
+ *bp |= (png_byte)((v << j) & 0xff);
+
+ else
+ *bp |= (png_byte)((v >> (-j)) & mask);
+ }
+ }
+ }
+
+ else if (row_info->bit_depth == 8)
+ {
+ png_bytep bp = row;
+ png_uint_32 i;
+ png_uint_32 istop = channels * row_info->width;
+
+ for (i = 0; i < istop; i++, bp++)
+ {
+
+ png_uint_16 v;
+ int j;
+ int c = (int)(i%channels);
+
+ v = *bp;
+ *bp = 0;
+
+ for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
+ {
+ if (j > 0)
+ *bp |= (png_byte)((v << j) & 0xff);
+
+ else
+ *bp |= (png_byte)((v >> (-j)) & 0xff);
+ }
+ }
+ }
+
+ else
+ {
+ png_bytep bp;
+ png_uint_32 i;
+ png_uint_32 istop = channels * row_info->width;
+
+ for (bp = row, i = 0; i < istop; i++)
+ {
+ int c = (int)(i%channels);
+ png_uint_16 value, v;
+ int j;
+
+ v = (png_uint_16)(((png_uint_16)(*bp) << 8) + *(bp + 1));
+ value = 0;
+
+ for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
+ {
+ if (j > 0)
+ value |= (png_uint_16)((v << j) & (png_uint_16)0xffff);
+
+ else
+ value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff);
+ }
+ *bp++ = (png_byte)(value >> 8);
+ *bp++ = (png_byte)(value & 0xff);
+ }
+ }
+ }
+}
+#endif
+
+#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED
+void /* PRIVATE */
+png_do_write_swap_alpha(png_row_infop row_info, png_bytep row)
+{
+ png_debug(1, "in png_do_write_swap_alpha");
+
+ {
+ if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ {
+ if (row_info->bit_depth == 8)
+ {
+ /* This converts from ARGB to RGBA */
+ png_bytep sp, dp;
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+
+ for (i = 0, sp = dp = row; i < row_width; i++)
+ {
+ png_byte save = *(sp++);
+ *(dp++) = *(sp++);
+ *(dp++) = *(sp++);
+ *(dp++) = *(sp++);
+ *(dp++) = save;
+ }
+ }
+
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+ else
+ {
+ /* This converts from AARRGGBB to RRGGBBAA */
+ png_bytep sp, dp;
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+
+ for (i = 0, sp = dp = row; i < row_width; i++)
+ {
+ png_byte save[2];
+ save[0] = *(sp++);
+ save[1] = *(sp++);
+ *(dp++) = *(sp++);
+ *(dp++) = *(sp++);
+ *(dp++) = *(sp++);
+ *(dp++) = *(sp++);
+ *(dp++) = *(sp++);
+ *(dp++) = *(sp++);
+ *(dp++) = save[0];
+ *(dp++) = save[1];
+ }
+ }
+#endif /* PNG_WRITE_16BIT_SUPPORTED */
+ }
+
+ else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ {
+ if (row_info->bit_depth == 8)
+ {
+ /* This converts from AG to GA */
+ png_bytep sp, dp;
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+
+ for (i = 0, sp = dp = row; i < row_width; i++)
+ {
+ png_byte save = *(sp++);
+ *(dp++) = *(sp++);
+ *(dp++) = save;
+ }
+ }
+
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+ else
+ {
+ /* This converts from AAGG to GGAA */
+ png_bytep sp, dp;
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+
+ for (i = 0, sp = dp = row; i < row_width; i++)
+ {
+ png_byte save[2];
+ save[0] = *(sp++);
+ save[1] = *(sp++);
+ *(dp++) = *(sp++);
+ *(dp++) = *(sp++);
+ *(dp++) = save[0];
+ *(dp++) = save[1];
+ }
+ }
+#endif /* PNG_WRITE_16BIT_SUPPORTED */
+ }
+ }
+}
+#endif
+
+#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
+void /* PRIVATE */
+png_do_write_invert_alpha(png_row_infop row_info, png_bytep row)
+{
+ png_debug(1, "in png_do_write_invert_alpha");
+
+ {
+ if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ {
+ if (row_info->bit_depth == 8)
+ {
+ /* This inverts the alpha channel in RGBA */
+ png_bytep sp, dp;
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+
+ for (i = 0, sp = dp = row; i < row_width; i++)
+ {
+ /* Does nothing
+ *(dp++) = *(sp++);
+ *(dp++) = *(sp++);
+ *(dp++) = *(sp++);
+ */
+ sp+=3; dp = sp;
+ *(dp++) = (png_byte)(255 - *(sp++));
+ }
+ }
+
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+ else
+ {
+ /* This inverts the alpha channel in RRGGBBAA */
+ png_bytep sp, dp;
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+
+ for (i = 0, sp = dp = row; i < row_width; i++)
+ {
+ /* Does nothing
+ *(dp++) = *(sp++);
+ *(dp++) = *(sp++);
+ *(dp++) = *(sp++);
+ *(dp++) = *(sp++);
+ *(dp++) = *(sp++);
+ *(dp++) = *(sp++);
+ */
+ sp+=6; dp = sp;
+ *(dp++) = (png_byte)(255 - *(sp++));
+ *(dp++) = (png_byte)(255 - *(sp++));
+ }
+ }
+#endif /* PNG_WRITE_16BIT_SUPPORTED */
+ }
+
+ else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ {
+ if (row_info->bit_depth == 8)
+ {
+ /* This inverts the alpha channel in GA */
+ png_bytep sp, dp;
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+
+ for (i = 0, sp = dp = row; i < row_width; i++)
+ {
+ *(dp++) = *(sp++);
+ *(dp++) = (png_byte)(255 - *(sp++));
+ }
+ }
+
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+ else
+ {
+ /* This inverts the alpha channel in GGAA */
+ png_bytep sp, dp;
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+
+ for (i = 0, sp = dp = row; i < row_width; i++)
+ {
+ /* Does nothing
+ *(dp++) = *(sp++);
+ *(dp++) = *(sp++);
+ */
+ sp+=2; dp = sp;
+ *(dp++) = (png_byte)(255 - *(sp++));
+ *(dp++) = (png_byte)(255 - *(sp++));
+ }
+ }
+#endif /* PNG_WRITE_16BIT_SUPPORTED */
+ }
+ }
+}
+#endif
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+/* Undoes intrapixel differencing */
+void /* PRIVATE */
+png_do_write_intrapixel(png_row_infop row_info, png_bytep row)
+{
+ png_debug(1, "in png_do_write_intrapixel");
+
+ if ((row_info->color_type & PNG_COLOR_MASK_COLOR))
+ {
+ int bytes_per_pixel;
+ png_uint_32 row_width = row_info->width;
+ if (row_info->bit_depth == 8)
+ {
+ png_bytep rp;
+ png_uint_32 i;
+
+ if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+ bytes_per_pixel = 3;
+
+ else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ bytes_per_pixel = 4;
+
+ else
+ return;
+
+ for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
+ {
+ *(rp) = (png_byte)((*rp - *(rp + 1)) & 0xff);
+ *(rp + 2) = (png_byte)((*(rp + 2) - *(rp + 1)) & 0xff);
+ }
+ }
+
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+ else if (row_info->bit_depth == 16)
+ {
+ png_bytep rp;
+ png_uint_32 i;
+
+ if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+ bytes_per_pixel = 6;
+
+ else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ bytes_per_pixel = 8;
+
+ else
+ return;
+
+ for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
+ {
+ png_uint_32 s0 = (*(rp ) << 8) | *(rp + 1);
+ png_uint_32 s1 = (*(rp + 2) << 8) | *(rp + 3);
+ png_uint_32 s2 = (*(rp + 4) << 8) | *(rp + 5);
+ png_uint_32 red = (png_uint_32)((s0 - s1) & 0xffffL);
+ png_uint_32 blue = (png_uint_32)((s2 - s1) & 0xffffL);
+ *(rp ) = (png_byte)((red >> 8) & 0xff);
+ *(rp + 1) = (png_byte)(red & 0xff);
+ *(rp + 4) = (png_byte)((blue >> 8) & 0xff);
+ *(rp + 5) = (png_byte)(blue & 0xff);
+ }
+ }
+#endif /* PNG_WRITE_16BIT_SUPPORTED */
+ }
+}
+#endif /* PNG_MNG_FEATURES_SUPPORTED */
+#endif /* PNG_WRITE_SUPPORTED */
diff --git a/libpng/pngwutil.c b/libpng/pngwutil.c
new file mode 100644
index 0000000..2c35785
--- /dev/null
+++ b/libpng/pngwutil.c
@@ -0,0 +1,2948 @@
+
+/* pngwutil.c - utilities to write a PNG file
+ *
+ * Last changed in libpng 1.5.0 [January 6, 2011]
+ * Copyright (c) 1998-2011 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ */
+
+#include "pngpriv.h"
+
+#ifdef PNG_WRITE_SUPPORTED
+
+#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED
+/* Place a 32-bit number into a buffer in PNG byte order. We work
+ * with unsigned numbers for convenience, although one supported
+ * ancillary chunk uses signed (two's complement) numbers.
+ */
+void PNGAPI
+png_save_uint_32(png_bytep buf, png_uint_32 i)
+{
+ buf[0] = (png_byte)((i >> 24) & 0xff);
+ buf[1] = (png_byte)((i >> 16) & 0xff);
+ buf[2] = (png_byte)((i >> 8) & 0xff);
+ buf[3] = (png_byte)(i & 0xff);
+}
+
+#ifdef PNG_SAVE_INT_32_SUPPORTED
+/* The png_save_int_32 function assumes integers are stored in two's
+ * complement format. If this isn't the case, then this routine needs to
+ * be modified to write data in two's complement format. Note that,
+ * the following works correctly even if png_int_32 has more than 32 bits
+ * (compare the more complex code required on read for sign extention.)
+ */
+void PNGAPI
+png_save_int_32(png_bytep buf, png_int_32 i)
+{
+ buf[0] = (png_byte)((i >> 24) & 0xff);
+ buf[1] = (png_byte)((i >> 16) & 0xff);
+ buf[2] = (png_byte)((i >> 8) & 0xff);
+ buf[3] = (png_byte)(i & 0xff);
+}
+#endif
+
+/* Place a 16-bit number into a buffer in PNG byte order.
+ * The parameter is declared unsigned int, not png_uint_16,
+ * just to avoid potential problems on pre-ANSI C compilers.
+ */
+void PNGAPI
+png_save_uint_16(png_bytep buf, unsigned int i)
+{
+ buf[0] = (png_byte)((i >> 8) & 0xff);
+ buf[1] = (png_byte)(i & 0xff);
+}
+#endif
+
+/* Simple function to write the signature. If we have already written
+ * the magic bytes of the signature, or more likely, the PNG stream is
+ * being embedded into another stream and doesn't need its own signature,
+ * we should call png_set_sig_bytes() to tell libpng how many of the
+ * bytes have already been written.
+ */
+void PNGAPI
+png_write_sig(png_structp png_ptr)
+{
+ png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
+
+#ifdef PNG_IO_STATE_SUPPORTED
+ /* Inform the I/O callback that the signature is being written */
+ png_ptr->io_state = PNG_IO_WRITING | PNG_IO_SIGNATURE;
+#endif
+
+ /* Write the rest of the 8 byte signature */
+ png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes],
+ (png_size_t)(8 - png_ptr->sig_bytes));
+
+ if (png_ptr->sig_bytes < 3)
+ png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;
+}
+
+/* Write a PNG chunk all at once. The type is an array of ASCII characters
+ * representing the chunk name. The array must be at least 4 bytes in
+ * length, and does not need to be null terminated. To be safe, pass the
+ * pre-defined chunk names here, and if you need a new one, define it
+ * where the others are defined. The length is the length of the data.
+ * All the data must be present. If that is not possible, use the
+ * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end()
+ * functions instead.
+ */
+void PNGAPI
+png_write_chunk(png_structp png_ptr, png_const_bytep chunk_name,
+ png_const_bytep data, png_size_t length)
+{
+ if (png_ptr == NULL)
+ return;
+
+ png_write_chunk_start(png_ptr, chunk_name, (png_uint_32)length);
+ png_write_chunk_data(png_ptr, data, (png_size_t)length);
+ png_write_chunk_end(png_ptr);
+}
+
+/* Write the start of a PNG chunk. The type is the chunk type.
+ * The total_length is the sum of the lengths of all the data you will be
+ * passing in png_write_chunk_data().
+ */
+void PNGAPI
+png_write_chunk_start(png_structp png_ptr, png_const_bytep chunk_name,
+ png_uint_32 length)
+{
+ png_byte buf[8];
+
+ png_debug2(0, "Writing %s chunk, length = %lu", chunk_name,
+ (unsigned long)length);
+
+ if (png_ptr == NULL)
+ return;
+
+#ifdef PNG_IO_STATE_SUPPORTED
+ /* Inform the I/O callback that the chunk header is being written.
+ * PNG_IO_CHUNK_HDR requires a single I/O call.
+ */
+ png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_HDR;
+#endif
+
+ /* Write the length and the chunk name */
+ png_save_uint_32(buf, length);
+ png_memcpy(buf + 4, chunk_name, 4);
+ png_write_data(png_ptr, buf, (png_size_t)8);
+
+ /* Put the chunk name into png_ptr->chunk_name */
+ png_memcpy(png_ptr->chunk_name, chunk_name, 4);
+
+ /* Reset the crc and run it over the chunk name */
+ png_reset_crc(png_ptr);
+
+ png_calculate_crc(png_ptr, chunk_name, 4);
+
+#ifdef PNG_IO_STATE_SUPPORTED
+ /* Inform the I/O callback that chunk data will (possibly) be written.
+ * PNG_IO_CHUNK_DATA does NOT require a specific number of I/O calls.
+ */
+ png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_DATA;
+#endif
+}
+
+/* Write the data of a PNG chunk started with png_write_chunk_start().
+ * Note that multiple calls to this function are allowed, and that the
+ * sum of the lengths from these calls *must* add up to the total_length
+ * given to png_write_chunk_start().
+ */
+void PNGAPI
+png_write_chunk_data(png_structp png_ptr, png_const_bytep data,
+ png_size_t length)
+{
+ /* Write the data, and run the CRC over it */
+ if (png_ptr == NULL)
+ return;
+
+ if (data != NULL && length > 0)
+ {
+ png_write_data(png_ptr, data, length);
+
+ /* Update the CRC after writing the data,
+ * in case that the user I/O routine alters it.
+ */
+ png_calculate_crc(png_ptr, data, length);
+ }
+}
+
+/* Finish a chunk started with png_write_chunk_start(). */
+void PNGAPI
+png_write_chunk_end(png_structp png_ptr)
+{
+ png_byte buf[4];
+
+ if (png_ptr == NULL) return;
+
+#ifdef PNG_IO_STATE_SUPPORTED
+ /* Inform the I/O callback that the chunk CRC is being written.
+ * PNG_IO_CHUNK_CRC requires a single I/O function call.
+ */
+ png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_CRC;
+#endif
+
+ /* Write the crc in a single operation */
+ png_save_uint_32(buf, png_ptr->crc);
+
+ png_write_data(png_ptr, buf, (png_size_t)4);
+}
+
+#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_iCCP_SUPPORTED)
+/* This pair of functions encapsulates the operation of (a) compressing a
+ * text string, and (b) issuing it later as a series of chunk data writes.
+ * The compression_state structure is shared context for these functions
+ * set up by the caller in order to make the whole mess thread-safe.
+ */
+
+typedef struct
+{
+ png_const_bytep input; /* The uncompressed input data */
+ png_size_t input_len; /* Its length */
+ int num_output_ptr; /* Number of output pointers used */
+ int max_output_ptr; /* Size of output_ptr */
+ png_bytep *output_ptr; /* Array of pointers to output */
+} compression_state;
+
+/* Compress given text into storage in the png_ptr structure */
+static int /* PRIVATE */
+png_text_compress(png_structp png_ptr,
+ png_const_charp text, png_size_t text_len, int compression,
+ compression_state *comp)
+{
+ int ret;
+
+ comp->num_output_ptr = 0;
+ comp->max_output_ptr = 0;
+ comp->output_ptr = NULL;
+ comp->input = NULL;
+ comp->input_len = 0;
+
+ /* We may just want to pass the text right through */
+ if (compression == PNG_TEXT_COMPRESSION_NONE)
+ {
+ comp->input = (png_const_bytep)text;
+ comp->input_len = text_len;
+ return((int)text_len);
+ }
+
+ if (compression >= PNG_TEXT_COMPRESSION_LAST)
+ {
+#ifdef PNG_CONSOLE_IO_SUPPORTED
+ char msg[50];
+ png_snprintf(msg, 50, "Unknown compression type %d", compression);
+ png_warning(png_ptr, msg);
+#else
+ png_warning(png_ptr, "Unknown compression type");
+#endif
+ }
+
+ /* We can't write the chunk until we find out how much data we have,
+ * which means we need to run the compressor first and save the
+ * output. This shouldn't be a problem, as the vast majority of
+ * comments should be reasonable, but we will set up an array of
+ * malloc'd pointers to be sure.
+ *
+ * If we knew the application was well behaved, we could simplify this
+ * greatly by assuming we can always malloc an output buffer large
+ * enough to hold the compressed text ((1001 * text_len / 1000) + 12)
+ * and malloc this directly. The only time this would be a bad idea is
+ * if we can't malloc more than 64K and we have 64K of random input
+ * data, or if the input string is incredibly large (although this
+ * wouldn't cause a failure, just a slowdown due to swapping).
+ */
+
+ /* Set up the compression buffers */
+ /* TODO: the following cast hides a potential overflow problem. */
+ png_ptr->zstream.avail_in = (uInt)text_len;
+ /* NOTE: assume zlib doesn't overwrite the input */
+ png_ptr->zstream.next_in = (Bytef *)text;
+ png_ptr->zstream.avail_out = png_ptr->zbuf_size;
+ png_ptr->zstream.next_out = png_ptr->zbuf;
+
+ /* This is the same compression loop as in png_write_row() */
+ do
+ {
+ /* Compress the data */
+ ret = deflate(&png_ptr->zstream, Z_NO_FLUSH);
+
+ if (ret != Z_OK)
+ {
+ /* Error */
+ if (png_ptr->zstream.msg != NULL)
+ png_error(png_ptr, png_ptr->zstream.msg);
+
+ else
+ png_error(png_ptr, "zlib error");
+ }
+
+ /* Check to see if we need more room */
+ if (!(png_ptr->zstream.avail_out))
+ {
+ /* Make sure the output array has room */
+ if (comp->num_output_ptr >= comp->max_output_ptr)
+ {
+ int old_max;
+
+ old_max = comp->max_output_ptr;
+ comp->max_output_ptr = comp->num_output_ptr + 4;
+ if (comp->output_ptr != NULL)
+ {
+ png_bytepp old_ptr;
+
+ old_ptr = comp->output_ptr;
+
+ comp->output_ptr = (png_bytepp)png_malloc(png_ptr,
+ (png_alloc_size_t)
+ (comp->max_output_ptr * png_sizeof(png_charpp)));
+
+ png_memcpy(comp->output_ptr, old_ptr, old_max
+ * png_sizeof(png_charp));
+
+ png_free(png_ptr, old_ptr);
+ }
+ else
+ comp->output_ptr = (png_bytepp)png_malloc(png_ptr,
+ (png_alloc_size_t)
+ (comp->max_output_ptr * png_sizeof(png_charp)));
+ }
+
+ /* Save the data */
+ comp->output_ptr[comp->num_output_ptr] =
+ (png_bytep)png_malloc(png_ptr,
+ (png_alloc_size_t)png_ptr->zbuf_size);
+
+ png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf,
+ png_ptr->zbuf_size);
+
+ comp->num_output_ptr++;
+
+ /* and reset the buffer */
+ png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+ png_ptr->zstream.next_out = png_ptr->zbuf;
+ }
+ /* Continue until we don't have any more to compress */
+ } while (png_ptr->zstream.avail_in);
+
+ /* Finish the compression */
+ do
+ {
+ /* Tell zlib we are finished */
+ ret = deflate(&png_ptr->zstream, Z_FINISH);
+
+ if (ret == Z_OK)
+ {
+ /* Check to see if we need more room */
+ if (!(png_ptr->zstream.avail_out))
+ {
+ /* Check to make sure our output array has room */
+ if (comp->num_output_ptr >= comp->max_output_ptr)
+ {
+ int old_max;
+
+ old_max = comp->max_output_ptr;
+ comp->max_output_ptr = comp->num_output_ptr + 4;
+ if (comp->output_ptr != NULL)
+ {
+ png_bytepp old_ptr;
+
+ old_ptr = comp->output_ptr;
+
+ /* This could be optimized to realloc() */
+ comp->output_ptr = (png_bytepp)png_malloc(png_ptr,
+ (png_alloc_size_t)(comp->max_output_ptr *
+ png_sizeof(png_charp)));
+
+ png_memcpy(comp->output_ptr, old_ptr,
+ old_max * png_sizeof(png_charp));
+
+ png_free(png_ptr, old_ptr);
+ }
+
+ else
+ comp->output_ptr = (png_bytepp)png_malloc(png_ptr,
+ (png_alloc_size_t)(comp->max_output_ptr *
+ png_sizeof(png_charp)));
+ }
+
+ /* Save the data */
+ comp->output_ptr[comp->num_output_ptr] =
+ (png_bytep)png_malloc(png_ptr,
+ (png_alloc_size_t)png_ptr->zbuf_size);
+
+ png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf,
+ png_ptr->zbuf_size);
+
+ comp->num_output_ptr++;
+
+ /* and reset the buffer pointers */
+ png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+ png_ptr->zstream.next_out = png_ptr->zbuf;
+ }
+ }
+ else if (ret != Z_STREAM_END)
+ {
+ /* We got an error */
+ if (png_ptr->zstream.msg != NULL)
+ png_error(png_ptr, png_ptr->zstream.msg);
+
+ else
+ png_error(png_ptr, "zlib error");
+ }
+ } while (ret != Z_STREAM_END);
+
+ /* Text length is number of buffers plus last buffer */
+ text_len = png_ptr->zbuf_size * comp->num_output_ptr;
+
+ if (png_ptr->zstream.avail_out < png_ptr->zbuf_size)
+ text_len += png_ptr->zbuf_size - (png_size_t)png_ptr->zstream.avail_out;
+
+ return((int)text_len);
+}
+
+/* Ship the compressed text out via chunk writes */
+static void /* PRIVATE */
+png_write_compressed_data_out(png_structp png_ptr, compression_state *comp)
+{
+ int i;
+
+ /* Handle the no-compression case */
+ if (comp->input)
+ {
+ png_write_chunk_data(png_ptr, comp->input, comp->input_len);
+
+ return;
+ }
+
+ /* Write saved output buffers, if any */
+ for (i = 0; i < comp->num_output_ptr; i++)
+ {
+ png_write_chunk_data(png_ptr, comp->output_ptr[i],
+ (png_size_t)png_ptr->zbuf_size);
+
+ png_free(png_ptr, comp->output_ptr[i]);
+ }
+
+ if (comp->max_output_ptr != 0)
+ png_free(png_ptr, comp->output_ptr);
+
+ /* Write anything left in zbuf */
+ if (png_ptr->zstream.avail_out < (png_uint_32)png_ptr->zbuf_size)
+ png_write_chunk_data(png_ptr, png_ptr->zbuf,
+ (png_size_t)(png_ptr->zbuf_size - png_ptr->zstream.avail_out));
+
+ /* Reset zlib for another zTXt/iTXt or image data */
+ deflateReset(&png_ptr->zstream);
+ png_ptr->zstream.data_type = Z_BINARY;
+}
+#endif
+
+/* Write the IHDR chunk, and update the png_struct with the necessary
+ * information. Note that the rest of this code depends upon this
+ * information being correct.
+ */
+void /* PRIVATE */
+png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height,
+ int bit_depth, int color_type, int compression_type, int filter_type,
+ int interlace_type)
+{
+ PNG_IHDR;
+ int ret;
+
+ png_byte buf[13]; /* Buffer to store the IHDR info */
+
+ png_debug(1, "in png_write_IHDR");
+
+ /* Check that we have valid input data from the application info */
+ switch (color_type)
+ {
+ case PNG_COLOR_TYPE_GRAY:
+ switch (bit_depth)
+ {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+ case 16:
+#endif
+ png_ptr->channels = 1; break;
+
+ default:
+ png_error(png_ptr,
+ "Invalid bit depth for grayscale image");
+ }
+ break;
+
+ case PNG_COLOR_TYPE_RGB:
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+ if (bit_depth != 8 && bit_depth != 16)
+#else
+ if (bit_depth != 8)
+#endif
+ png_error(png_ptr, "Invalid bit depth for RGB image");
+
+ png_ptr->channels = 3;
+ break;
+
+ case PNG_COLOR_TYPE_PALETTE:
+ switch (bit_depth)
+ {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ png_ptr->channels = 1;
+ break;
+
+ default:
+ png_error(png_ptr, "Invalid bit depth for paletted image");
+ }
+ break;
+
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ if (bit_depth != 8 && bit_depth != 16)
+ png_error(png_ptr, "Invalid bit depth for grayscale+alpha image");
+
+ png_ptr->channels = 2;
+ break;
+
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+ if (bit_depth != 8 && bit_depth != 16)
+#else
+ if (bit_depth != 8)
+#endif
+ png_error(png_ptr, "Invalid bit depth for RGBA image");
+
+ png_ptr->channels = 4;
+ break;
+
+ default:
+ png_error(png_ptr, "Invalid image color type specified");
+ }
+
+ if (compression_type != PNG_COMPRESSION_TYPE_BASE)
+ {
+ png_warning(png_ptr, "Invalid compression type specified");
+ compression_type = PNG_COMPRESSION_TYPE_BASE;
+ }
+
+ /* Write filter_method 64 (intrapixel differencing) only if
+ * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
+ * 2. Libpng did not write a PNG signature (this filter_method is only
+ * used in PNG datastreams that are embedded in MNG datastreams) and
+ * 3. The application called png_permit_mng_features with a mask that
+ * included PNG_FLAG_MNG_FILTER_64 and
+ * 4. The filter_method is 64 and
+ * 5. The color_type is RGB or RGBA
+ */
+ if (
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+ !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
+ ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) &&
+ (color_type == PNG_COLOR_TYPE_RGB ||
+ color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
+ (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) &&
+#endif
+ filter_type != PNG_FILTER_TYPE_BASE)
+ {
+ png_warning(png_ptr, "Invalid filter type specified");
+ filter_type = PNG_FILTER_TYPE_BASE;
+ }
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ if (interlace_type != PNG_INTERLACE_NONE &&
+ interlace_type != PNG_INTERLACE_ADAM7)
+ {
+ png_warning(png_ptr, "Invalid interlace type specified");
+ interlace_type = PNG_INTERLACE_ADAM7;
+ }
+#else
+ interlace_type=PNG_INTERLACE_NONE;
+#endif
+
+ /* Save the relevent information */
+ png_ptr->bit_depth = (png_byte)bit_depth;
+ png_ptr->color_type = (png_byte)color_type;
+ png_ptr->interlaced = (png_byte)interlace_type;
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+ png_ptr->filter_type = (png_byte)filter_type;
+#endif
+ png_ptr->compression_type = (png_byte)compression_type;
+ png_ptr->width = width;
+ png_ptr->height = height;
+
+ png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels);
+ png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width);
+ /* Set the usr info, so any transformations can modify it */
+ png_ptr->usr_width = png_ptr->width;
+ png_ptr->usr_bit_depth = png_ptr->bit_depth;
+ png_ptr->usr_channels = png_ptr->channels;
+
+ /* Pack the header information into the buffer */
+ png_save_uint_32(buf, width);
+ png_save_uint_32(buf + 4, height);
+ buf[8] = (png_byte)bit_depth;
+ buf[9] = (png_byte)color_type;
+ buf[10] = (png_byte)compression_type;
+ buf[11] = (png_byte)filter_type;
+ buf[12] = (png_byte)interlace_type;
+
+ /* Write the chunk */
+ png_write_chunk(png_ptr, png_IHDR, buf, (png_size_t)13);
+
+ /* Initialize zlib with PNG info */
+ png_ptr->zstream.zalloc = png_zalloc;
+ png_ptr->zstream.zfree = png_zfree;
+ png_ptr->zstream.opaque = (voidpf)png_ptr;
+
+ if (!(png_ptr->do_filter))
+ {
+ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
+ png_ptr->bit_depth < 8)
+ png_ptr->do_filter = PNG_FILTER_NONE;
+
+ else
+ png_ptr->do_filter = PNG_ALL_FILTERS;
+ }
+
+ if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY))
+ {
+ if (png_ptr->do_filter != PNG_FILTER_NONE)
+ png_ptr->zlib_strategy = Z_FILTERED;
+
+ else
+ png_ptr->zlib_strategy = Z_DEFAULT_STRATEGY;
+ }
+
+ if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_LEVEL))
+ png_ptr->zlib_level = Z_DEFAULT_COMPRESSION;
+
+ if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL))
+ png_ptr->zlib_mem_level = 8;
+
+ if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS))
+ png_ptr->zlib_window_bits = 15;
+
+ if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_METHOD))
+ png_ptr->zlib_method = 8;
+
+ ret = deflateInit2(&png_ptr->zstream, png_ptr->zlib_level,
+ png_ptr->zlib_method, png_ptr->zlib_window_bits,
+ png_ptr->zlib_mem_level, png_ptr->zlib_strategy);
+
+ if (ret != Z_OK)
+ {
+ if (ret == Z_VERSION_ERROR)
+ png_error(png_ptr,
+ "zlib failed to initialize compressor -- version error");
+
+ if (ret == Z_STREAM_ERROR)
+ png_error(png_ptr,
+ "zlib failed to initialize compressor -- stream error");
+
+ if (ret == Z_MEM_ERROR)
+ png_error(png_ptr,
+ "zlib failed to initialize compressor -- mem error");
+
+ png_error(png_ptr, "zlib failed to initialize compressor");
+ }
+
+ png_ptr->zstream.next_out = png_ptr->zbuf;
+ png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+ /* libpng is not interested in zstream.data_type, so set it
+ * to a predefined value, to avoid its evaluation inside zlib
+ */
+ png_ptr->zstream.data_type = Z_BINARY;
+
+ png_ptr->mode = PNG_HAVE_IHDR;
+}
+
+/* Write the palette. We are careful not to trust png_color to be in the
+ * correct order for PNG, so people can redefine it to any convenient
+ * structure.
+ */
+void /* PRIVATE */
+png_write_PLTE(png_structp png_ptr, png_const_colorp palette,
+ png_uint_32 num_pal)
+{
+ PNG_PLTE;
+ png_uint_32 i;
+ png_const_colorp pal_ptr;
+ png_byte buf[3];
+
+ png_debug(1, "in png_write_PLTE");
+
+ if ((
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+ !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) &&
+#endif
+ num_pal == 0) || num_pal > 256)
+ {
+ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ png_error(png_ptr, "Invalid number of colors in palette");
+ }
+
+ else
+ {
+ png_warning(png_ptr, "Invalid number of colors in palette");
+ return;
+ }
+ }
+
+ if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR))
+ {
+ png_warning(png_ptr,
+ "Ignoring request to write a PLTE chunk in grayscale PNG");
+
+ return;
+ }
+
+ png_ptr->num_palette = (png_uint_16)num_pal;
+ png_debug1(3, "num_palette = %d", png_ptr->num_palette);
+
+ png_write_chunk_start(png_ptr, png_PLTE, (png_uint_32)(num_pal * 3));
+#ifdef PNG_POINTER_INDEXING_SUPPORTED
+
+ for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++)
+ {
+ buf[0] = pal_ptr->red;
+ buf[1] = pal_ptr->green;
+ buf[2] = pal_ptr->blue;
+ png_write_chunk_data(png_ptr, buf, (png_size_t)3);
+ }
+
+#else
+ /* This is a little slower but some buggy compilers need to do this
+ * instead
+ */
+ pal_ptr=palette;
+
+ for (i = 0; i < num_pal; i++)
+ {
+ buf[0] = pal_ptr[i].red;
+ buf[1] = pal_ptr[i].green;
+ buf[2] = pal_ptr[i].blue;
+ png_write_chunk_data(png_ptr, buf, (png_size_t)3);
+ }
+
+#endif
+ png_write_chunk_end(png_ptr);
+ png_ptr->mode |= PNG_HAVE_PLTE;
+}
+
+/* Write an IDAT chunk */
+void /* PRIVATE */
+png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ PNG_IDAT;
+
+ png_debug(1, "in png_write_IDAT");
+
+ /* Optimize the CMF field in the zlib stream. */
+ /* This hack of the zlib stream is compliant to the stream specification. */
+ if (!(png_ptr->mode & PNG_HAVE_IDAT) &&
+ png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
+ {
+ unsigned int z_cmf = data[0]; /* zlib compression method and flags */
+ if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70)
+ {
+ /* Avoid memory underflows and multiplication overflows.
+ *
+ * The conditions below are practically always satisfied;
+ * however, they still must be checked.
+ */
+ if (length >= 2 &&
+ png_ptr->height < 16384 && png_ptr->width < 16384)
+ {
+ png_uint_32 uncompressed_idat_size = png_ptr->height *
+ ((png_ptr->width *
+ png_ptr->channels * png_ptr->bit_depth + 15) >> 3);
+ unsigned int z_cinfo = z_cmf >> 4;
+ unsigned int half_z_window_size = 1 << (z_cinfo + 7);
+ while (uncompressed_idat_size <= half_z_window_size &&
+ half_z_window_size >= 256)
+ {
+ z_cinfo--;
+ half_z_window_size >>= 1;
+ }
+
+ z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4);
+
+ if (data[0] != z_cmf)
+ {
+ int tmp;
+ data[0] = (png_byte)z_cmf;
+ tmp = data[1] & 0xe0;
+ tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f;
+ data[1] = (png_byte)tmp;
+ }
+ }
+ }
+
+ else
+ png_error(png_ptr,
+ "Invalid zlib compression method or flags in IDAT");
+ }
+
+ png_write_chunk(png_ptr, png_IDAT, data, length);
+ png_ptr->mode |= PNG_HAVE_IDAT;
+}
+
+/* Write an IEND chunk */
+void /* PRIVATE */
+png_write_IEND(png_structp png_ptr)
+{
+ PNG_IEND;
+
+ png_debug(1, "in png_write_IEND");
+
+ png_write_chunk(png_ptr, png_IEND, NULL, (png_size_t)0);
+ png_ptr->mode |= PNG_HAVE_IEND;
+}
+
+#ifdef PNG_WRITE_gAMA_SUPPORTED
+/* Write a gAMA chunk */
+void /* PRIVATE */
+png_write_gAMA_fixed(png_structp png_ptr, png_fixed_point file_gamma)
+{
+ PNG_gAMA;
+ png_byte buf[4];
+
+ png_debug(1, "in png_write_gAMA");
+
+ /* file_gamma is saved in 1/100,000ths */
+ png_save_uint_32(buf, (png_uint_32)file_gamma);
+ png_write_chunk(png_ptr, png_gAMA, buf, (png_size_t)4);
+}
+#endif
+
+#ifdef PNG_WRITE_sRGB_SUPPORTED
+/* Write a sRGB chunk */
+void /* PRIVATE */
+png_write_sRGB(png_structp png_ptr, int srgb_intent)
+{
+ PNG_sRGB;
+ png_byte buf[1];
+
+ png_debug(1, "in png_write_sRGB");
+
+ if (srgb_intent >= PNG_sRGB_INTENT_LAST)
+ png_warning(png_ptr,
+ "Invalid sRGB rendering intent specified");
+
+ buf[0]=(png_byte)srgb_intent;
+ png_write_chunk(png_ptr, png_sRGB, buf, (png_size_t)1);
+}
+#endif
+
+#ifdef PNG_WRITE_iCCP_SUPPORTED
+/* Write an iCCP chunk */
+void /* PRIVATE */
+png_write_iCCP(png_structp png_ptr, png_const_charp name, int compression_type,
+ png_const_charp profile, int profile_len)
+{
+ PNG_iCCP;
+ png_size_t name_len;
+ png_charp new_name;
+ compression_state comp;
+ int embedded_profile_len = 0;
+
+ png_debug(1, "in png_write_iCCP");
+
+ comp.num_output_ptr = 0;
+ comp.max_output_ptr = 0;
+ comp.output_ptr = NULL;
+ comp.input = NULL;
+ comp.input_len = 0;
+
+ if ((name_len = png_check_keyword(png_ptr, name, &new_name)) == 0)
+ return;
+
+ if (compression_type != PNG_COMPRESSION_TYPE_BASE)
+ png_warning(png_ptr, "Unknown compression type in iCCP chunk");
+
+ if (profile == NULL)
+ profile_len = 0;
+
+ if (profile_len > 3)
+ embedded_profile_len =
+ ((*( (png_const_bytep)profile ))<<24) |
+ ((*( (png_const_bytep)profile + 1))<<16) |
+ ((*( (png_const_bytep)profile + 2))<< 8) |
+ ((*( (png_const_bytep)profile + 3)) );
+
+ if (embedded_profile_len < 0)
+ {
+ png_warning(png_ptr,
+ "Embedded profile length in iCCP chunk is negative");
+
+ png_free(png_ptr, new_name);
+ return;
+ }
+
+ if (profile_len < embedded_profile_len)
+ {
+ png_warning(png_ptr,
+ "Embedded profile length too large in iCCP chunk");
+
+ png_free(png_ptr, new_name);
+ return;
+ }
+
+ if (profile_len > embedded_profile_len)
+ {
+ png_warning(png_ptr,
+ "Truncating profile to actual length in iCCP chunk");
+
+ profile_len = embedded_profile_len;
+ }
+
+ if (profile_len)
+ profile_len = png_text_compress(png_ptr, profile,
+ (png_size_t)profile_len, PNG_COMPRESSION_TYPE_BASE, &comp);
+
+ /* Make sure we include the NULL after the name and the compression type */
+ png_write_chunk_start(png_ptr, png_iCCP,
+ (png_uint_32)(name_len + profile_len + 2));
+
+ new_name[name_len + 1] = 0x00;
+
+ png_write_chunk_data(png_ptr, (png_bytep)new_name,
+ (png_size_t)(name_len + 2));
+
+ if (profile_len)
+ png_write_compressed_data_out(png_ptr, &comp);
+
+ png_write_chunk_end(png_ptr);
+ png_free(png_ptr, new_name);
+}
+#endif
+
+#ifdef PNG_WRITE_sPLT_SUPPORTED
+/* Write a sPLT chunk */
+void /* PRIVATE */
+png_write_sPLT(png_structp png_ptr, png_const_sPLT_tp spalette)
+{
+ PNG_sPLT;
+ png_size_t name_len;
+ png_charp new_name;
+ png_byte entrybuf[10];
+ png_size_t entry_size = (spalette->depth == 8 ? 6 : 10);
+ png_size_t palette_size = entry_size * spalette->nentries;
+ png_sPLT_entryp ep;
+#ifndef PNG_POINTER_INDEXING_SUPPORTED
+ int i;
+#endif
+
+ png_debug(1, "in png_write_sPLT");
+
+ if ((name_len = png_check_keyword(png_ptr,spalette->name, &new_name))==0)
+ return;
+
+ /* Make sure we include the NULL after the name */
+ png_write_chunk_start(png_ptr, png_sPLT,
+ (png_uint_32)(name_len + 2 + palette_size));
+
+ png_write_chunk_data(png_ptr, (png_bytep)new_name,
+ (png_size_t)(name_len + 1));
+
+ png_write_chunk_data(png_ptr, &spalette->depth, (png_size_t)1);
+
+ /* Loop through each palette entry, writing appropriately */
+#ifdef PNG_POINTER_INDEXING_SUPPORTED
+ for (ep = spalette->entries; ep<spalette->entries + spalette->nentries; ep++)
+ {
+ if (spalette->depth == 8)
+ {
+ entrybuf[0] = (png_byte)ep->red;
+ entrybuf[1] = (png_byte)ep->green;
+ entrybuf[2] = (png_byte)ep->blue;
+ entrybuf[3] = (png_byte)ep->alpha;
+ png_save_uint_16(entrybuf + 4, ep->frequency);
+ }
+
+ else
+ {
+ png_save_uint_16(entrybuf + 0, ep->red);
+ png_save_uint_16(entrybuf + 2, ep->green);
+ png_save_uint_16(entrybuf + 4, ep->blue);
+ png_save_uint_16(entrybuf + 6, ep->alpha);
+ png_save_uint_16(entrybuf + 8, ep->frequency);
+ }
+
+ png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size);
+ }
+#else
+ ep=spalette->entries;
+ for (i = 0; i>spalette->nentries; i++)
+ {
+ if (spalette->depth == 8)
+ {
+ entrybuf[0] = (png_byte)ep[i].red;
+ entrybuf[1] = (png_byte)ep[i].green;
+ entrybuf[2] = (png_byte)ep[i].blue;
+ entrybuf[3] = (png_byte)ep[i].alpha;
+ png_save_uint_16(entrybuf + 4, ep[i].frequency);
+ }
+
+ else
+ {
+ png_save_uint_16(entrybuf + 0, ep[i].red);
+ png_save_uint_16(entrybuf + 2, ep[i].green);
+ png_save_uint_16(entrybuf + 4, ep[i].blue);
+ png_save_uint_16(entrybuf + 6, ep[i].alpha);
+ png_save_uint_16(entrybuf + 8, ep[i].frequency);
+ }
+
+ png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size);
+ }
+#endif
+
+ png_write_chunk_end(png_ptr);
+ png_free(png_ptr, new_name);
+}
+#endif
+
+#ifdef PNG_WRITE_sBIT_SUPPORTED
+/* Write the sBIT chunk */
+void /* PRIVATE */
+png_write_sBIT(png_structp png_ptr, png_const_color_8p sbit, int color_type)
+{
+ PNG_sBIT;
+ png_byte buf[4];
+ png_size_t size;
+
+ png_debug(1, "in png_write_sBIT");
+
+ /* Make sure we don't depend upon the order of PNG_COLOR_8 */
+ if (color_type & PNG_COLOR_MASK_COLOR)
+ {
+ png_byte maxbits;
+
+ maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 :
+ png_ptr->usr_bit_depth);
+
+ if (sbit->red == 0 || sbit->red > maxbits ||
+ sbit->green == 0 || sbit->green > maxbits ||
+ sbit->blue == 0 || sbit->blue > maxbits)
+ {
+ png_warning(png_ptr, "Invalid sBIT depth specified");
+ return;
+ }
+
+ buf[0] = sbit->red;
+ buf[1] = sbit->green;
+ buf[2] = sbit->blue;
+ size = 3;
+ }
+
+ else
+ {
+ if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth)
+ {
+ png_warning(png_ptr, "Invalid sBIT depth specified");
+ return;
+ }
+
+ buf[0] = sbit->gray;
+ size = 1;
+ }
+
+ if (color_type & PNG_COLOR_MASK_ALPHA)
+ {
+ if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth)
+ {
+ png_warning(png_ptr, "Invalid sBIT depth specified");
+ return;
+ }
+
+ buf[size++] = sbit->alpha;
+ }
+
+ png_write_chunk(png_ptr, png_sBIT, buf, size);
+}
+#endif
+
+#ifdef PNG_WRITE_cHRM_SUPPORTED
+/* Write the cHRM chunk */
+void /* PRIVATE */
+png_write_cHRM_fixed(png_structp png_ptr, png_fixed_point white_x,
+ png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y,
+ png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x,
+ png_fixed_point blue_y)
+{
+ PNG_cHRM;
+ png_byte buf[32];
+
+ png_debug(1, "in png_write_cHRM");
+
+ /* Each value is saved in 1/100,000ths */
+#ifdef PNG_CHECK_cHRM_SUPPORTED
+ if (png_check_cHRM_fixed(png_ptr, white_x, white_y, red_x, red_y,
+ green_x, green_y, blue_x, blue_y))
+#endif
+ {
+ png_save_uint_32(buf, (png_uint_32)white_x);
+ png_save_uint_32(buf + 4, (png_uint_32)white_y);
+
+ png_save_uint_32(buf + 8, (png_uint_32)red_x);
+ png_save_uint_32(buf + 12, (png_uint_32)red_y);
+
+ png_save_uint_32(buf + 16, (png_uint_32)green_x);
+ png_save_uint_32(buf + 20, (png_uint_32)green_y);
+
+ png_save_uint_32(buf + 24, (png_uint_32)blue_x);
+ png_save_uint_32(buf + 28, (png_uint_32)blue_y);
+
+ png_write_chunk(png_ptr, png_cHRM, buf, (png_size_t)32);
+ }
+}
+#endif
+
+#ifdef PNG_WRITE_tRNS_SUPPORTED
+/* Write the tRNS chunk */
+void /* PRIVATE */
+png_write_tRNS(png_structp png_ptr, png_const_bytep trans_alpha,
+ png_const_color_16p tran, int num_trans, int color_type)
+{
+ PNG_tRNS;
+ png_byte buf[6];
+
+ png_debug(1, "in png_write_tRNS");
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette)
+ {
+ png_warning(png_ptr, "Invalid number of transparent colors specified");
+ return;
+ }
+
+ /* Write the chunk out as it is */
+ png_write_chunk(png_ptr, png_tRNS, trans_alpha, (png_size_t)num_trans);
+ }
+
+ else if (color_type == PNG_COLOR_TYPE_GRAY)
+ {
+ /* One 16 bit value */
+ if (tran->gray >= (1 << png_ptr->bit_depth))
+ {
+ png_warning(png_ptr,
+ "Ignoring attempt to write tRNS chunk out-of-range for bit_depth");
+
+ return;
+ }
+
+ png_save_uint_16(buf, tran->gray);
+ png_write_chunk(png_ptr, png_tRNS, buf, (png_size_t)2);
+ }
+
+ else if (color_type == PNG_COLOR_TYPE_RGB)
+ {
+ /* Three 16 bit values */
+ png_save_uint_16(buf, tran->red);
+ png_save_uint_16(buf + 2, tran->green);
+ png_save_uint_16(buf + 4, tran->blue);
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+ if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]))
+#else
+ if (buf[0] | buf[2] | buf[4])
+#endif
+ {
+ png_warning(png_ptr,
+ "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8");
+ return;
+ }
+
+ png_write_chunk(png_ptr, png_tRNS, buf, (png_size_t)6);
+ }
+
+ else
+ {
+ png_warning(png_ptr, "Can't write tRNS with an alpha channel");
+ }
+}
+#endif
+
+#ifdef PNG_WRITE_bKGD_SUPPORTED
+/* Write the background chunk */
+void /* PRIVATE */
+png_write_bKGD(png_structp png_ptr, png_const_color_16p back, int color_type)
+{
+ PNG_bKGD;
+ png_byte buf[6];
+
+ png_debug(1, "in png_write_bKGD");
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ if (
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+ (png_ptr->num_palette ||
+ (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) &&
+#endif
+ back->index >= png_ptr->num_palette)
+ {
+ png_warning(png_ptr, "Invalid background palette index");
+ return;
+ }
+
+ buf[0] = back->index;
+ png_write_chunk(png_ptr, png_bKGD, buf, (png_size_t)1);
+ }
+
+ else if (color_type & PNG_COLOR_MASK_COLOR)
+ {
+ png_save_uint_16(buf, back->red);
+ png_save_uint_16(buf + 2, back->green);
+ png_save_uint_16(buf + 4, back->blue);
+#ifdef PNG_WRITE_16BIT_SUPPORTED
+ if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]))
+#else
+ if (buf[0] | buf[2] | buf[4])
+#endif
+ {
+ png_warning(png_ptr,
+ "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8");
+
+ return;
+ }
+
+ png_write_chunk(png_ptr, png_bKGD, buf, (png_size_t)6);
+ }
+
+ else
+ {
+ if (back->gray >= (1 << png_ptr->bit_depth))
+ {
+ png_warning(png_ptr,
+ "Ignoring attempt to write bKGD chunk out-of-range for bit_depth");
+
+ return;
+ }
+
+ png_save_uint_16(buf, back->gray);
+ png_write_chunk(png_ptr, png_bKGD, buf, (png_size_t)2);
+ }
+}
+#endif
+
+#ifdef PNG_WRITE_hIST_SUPPORTED
+/* Write the histogram */
+void /* PRIVATE */
+png_write_hIST(png_structp png_ptr, png_const_uint_16p hist, int num_hist)
+{
+ PNG_hIST;
+ int i;
+ png_byte buf[3];
+
+ png_debug(1, "in png_write_hIST");
+
+ if (num_hist > (int)png_ptr->num_palette)
+ {
+ png_debug2(3, "num_hist = %d, num_palette = %d", num_hist,
+ png_ptr->num_palette);
+
+ png_warning(png_ptr, "Invalid number of histogram entries specified");
+ return;
+ }
+
+ png_write_chunk_start(png_ptr, png_hIST, (png_uint_32)(num_hist * 2));
+
+ for (i = 0; i < num_hist; i++)
+ {
+ png_save_uint_16(buf, hist[i]);
+ png_write_chunk_data(png_ptr, buf, (png_size_t)2);
+ }
+
+ png_write_chunk_end(png_ptr);
+}
+#endif
+
+#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \
+ defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
+/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification,
+ * and if invalid, correct the keyword rather than discarding the entire
+ * chunk. The PNG 1.0 specification requires keywords 1-79 characters in
+ * length, forbids leading or trailing whitespace, multiple internal spaces,
+ * and the non-break space (0x80) from ISO 8859-1. Returns keyword length.
+ *
+ * The new_key is allocated to hold the corrected keyword and must be freed
+ * by the calling routine. This avoids problems with trying to write to
+ * static keywords without having to have duplicate copies of the strings.
+ */
+png_size_t /* PRIVATE */
+png_check_keyword(png_structp png_ptr, png_const_charp key, png_charpp new_key)
+{
+ png_size_t key_len;
+ png_const_charp ikp;
+ png_charp kp, dp;
+ int kflag;
+ int kwarn=0;
+
+ png_debug(1, "in png_check_keyword");
+
+ *new_key = NULL;
+
+ if (key == NULL || (key_len = png_strlen(key)) == 0)
+ {
+ png_warning(png_ptr, "zero length keyword");
+ return ((png_size_t)0);
+ }
+
+ png_debug1(2, "Keyword to be checked is '%s'", key);
+
+ *new_key = (png_charp)png_malloc_warn(png_ptr, (png_uint_32)(key_len + 2));
+
+ if (*new_key == NULL)
+ {
+ png_warning(png_ptr, "Out of memory while procesing keyword");
+ return ((png_size_t)0);
+ }
+
+ /* Replace non-printing characters with a blank and print a warning */
+ for (ikp = key, dp = *new_key; *ikp != '\0'; ikp++, dp++)
+ {
+ if ((png_byte)*ikp < 0x20 ||
+ ((png_byte)*ikp > 0x7E && (png_byte)*ikp < 0xA1))
+ {
+#ifdef PNG_CONSOLE_IO_SUPPORTED
+ char msg[40];
+
+ png_snprintf(msg, 40,
+ "invalid keyword character 0x%02X", (png_byte)*ikp);
+ png_warning(png_ptr, msg);
+#else
+ png_warning(png_ptr, "invalid character in keyword");
+#endif
+ *dp = ' ';
+ }
+
+ else
+ {
+ *dp = *ikp;
+ }
+ }
+ *dp = '\0';
+
+ /* Remove any trailing white space. */
+ kp = *new_key + key_len - 1;
+ if (*kp == ' ')
+ {
+ png_warning(png_ptr, "trailing spaces removed from keyword");
+
+ while (*kp == ' ')
+ {
+ *(kp--) = '\0';
+ key_len--;
+ }
+ }
+
+ /* Remove any leading white space. */
+ kp = *new_key;
+ if (*kp == ' ')
+ {
+ png_warning(png_ptr, "leading spaces removed from keyword");
+
+ while (*kp == ' ')
+ {
+ kp++;
+ key_len--;
+ }
+ }
+
+ png_debug1(2, "Checking for multiple internal spaces in '%s'", kp);
+
+ /* Remove multiple internal spaces. */
+ for (kflag = 0, dp = *new_key; *kp != '\0'; kp++)
+ {
+ if (*kp == ' ' && kflag == 0)
+ {
+ *(dp++) = *kp;
+ kflag = 1;
+ }
+
+ else if (*kp == ' ')
+ {
+ key_len--;
+ kwarn = 1;
+ }
+
+ else
+ {
+ *(dp++) = *kp;
+ kflag = 0;
+ }
+ }
+ *dp = '\0';
+ if (kwarn)
+ png_warning(png_ptr, "extra interior spaces removed from keyword");
+
+ if (key_len == 0)
+ {
+ png_free(png_ptr, *new_key);
+ png_warning(png_ptr, "Zero length keyword");
+ }
+
+ if (key_len > 79)
+ {
+ png_warning(png_ptr, "keyword length must be 1 - 79 characters");
+ (*new_key)[79] = '\0';
+ key_len = 79;
+ }
+
+ return (key_len);
+}
+#endif
+
+#ifdef PNG_WRITE_tEXt_SUPPORTED
+/* Write a tEXt chunk */
+void /* PRIVATE */
+png_write_tEXt(png_structp png_ptr, png_const_charp key, png_const_charp text,
+ png_size_t text_len)
+{
+ PNG_tEXt;
+ png_size_t key_len;
+ png_charp new_key;
+
+ png_debug(1, "in png_write_tEXt");
+
+ if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0)
+ return;
+
+ if (text == NULL || *text == '\0')
+ text_len = 0;
+
+ else
+ text_len = png_strlen(text);
+
+ /* Make sure we include the 0 after the key */
+ png_write_chunk_start(png_ptr, png_tEXt,
+ (png_uint_32)(key_len + text_len + 1));
+ /*
+ * We leave it to the application to meet PNG-1.0 requirements on the
+ * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of
+ * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them.
+ * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
+ */
+ png_write_chunk_data(png_ptr, (png_bytep)new_key,
+ (png_size_t)(key_len + 1));
+
+ if (text_len)
+ png_write_chunk_data(png_ptr, (png_const_bytep)text,
+ (png_size_t)text_len);
+
+ png_write_chunk_end(png_ptr);
+ png_free(png_ptr, new_key);
+}
+#endif
+
+#ifdef PNG_WRITE_zTXt_SUPPORTED
+/* Write a compressed text chunk */
+void /* PRIVATE */
+png_write_zTXt(png_structp png_ptr, png_const_charp key, png_const_charp text,
+ png_size_t text_len, int compression)
+{
+ PNG_zTXt;
+ png_size_t key_len;
+ png_byte buf;
+ png_charp new_key;
+ compression_state comp;
+
+ png_debug(1, "in png_write_zTXt");
+
+ comp.num_output_ptr = 0;
+ comp.max_output_ptr = 0;
+ comp.output_ptr = NULL;
+ comp.input = NULL;
+ comp.input_len = 0;
+
+ if ((key_len = png_check_keyword(png_ptr, key, &new_key)) == 0)
+ {
+ png_free(png_ptr, new_key);
+ return;
+ }
+
+ if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE)
+ {
+ png_write_tEXt(png_ptr, new_key, text, (png_size_t)0);
+ png_free(png_ptr, new_key);
+ return;
+ }
+
+ text_len = png_strlen(text);
+
+ /* Compute the compressed data; do it now for the length */
+ text_len = png_text_compress(png_ptr, text, text_len, compression,
+ &comp);
+
+ /* Write start of chunk */
+ png_write_chunk_start(png_ptr, png_zTXt,
+ (png_uint_32)(key_len+text_len + 2));
+
+ /* Write key */
+ png_write_chunk_data(png_ptr, (png_bytep)new_key,
+ (png_size_t)(key_len + 1));
+
+ png_free(png_ptr, new_key);
+
+ buf = (png_byte)compression;
+
+ /* Write compression */
+ png_write_chunk_data(png_ptr, &buf, (png_size_t)1);
+
+ /* Write the compressed data */
+ png_write_compressed_data_out(png_ptr, &comp);
+
+ /* Close the chunk */
+ png_write_chunk_end(png_ptr);
+}
+#endif
+
+#ifdef PNG_WRITE_iTXt_SUPPORTED
+/* Write an iTXt chunk */
+void /* PRIVATE */
+png_write_iTXt(png_structp png_ptr, int compression, png_const_charp key,
+ png_const_charp lang, png_const_charp lang_key, png_const_charp text)
+{
+ PNG_iTXt;
+ png_size_t lang_len, key_len, lang_key_len, text_len;
+ png_charp new_lang;
+ png_charp new_key = NULL;
+ png_byte cbuf[2];
+ compression_state comp;
+
+ png_debug(1, "in png_write_iTXt");
+
+ comp.num_output_ptr = 0;
+ comp.max_output_ptr = 0;
+ comp.output_ptr = NULL;
+ comp.input = NULL;
+
+ if ((key_len = png_check_keyword(png_ptr, key, &new_key)) == 0)
+ return;
+
+ if ((lang_len = png_check_keyword(png_ptr, lang, &new_lang)) == 0)
+ {
+ png_warning(png_ptr, "Empty language field in iTXt chunk");
+ new_lang = NULL;
+ lang_len = 0;
+ }
+
+ if (lang_key == NULL)
+ lang_key_len = 0;
+
+ else
+ lang_key_len = png_strlen(lang_key);
+
+ if (text == NULL)
+ text_len = 0;
+
+ else
+ text_len = png_strlen(text);
+
+ /* Compute the compressed data; do it now for the length */
+ text_len = png_text_compress(png_ptr, text, text_len, compression - 2,
+ &comp);
+
+
+ /* Make sure we include the compression flag, the compression byte,
+ * and the NULs after the key, lang, and lang_key parts
+ */
+
+ png_write_chunk_start(png_ptr, png_iTXt, (png_uint_32)(
+ 5 /* comp byte, comp flag, terminators for key, lang and lang_key */
+ + key_len
+ + lang_len
+ + lang_key_len
+ + text_len));
+
+ /* We leave it to the application to meet PNG-1.0 requirements on the
+ * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of
+ * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them.
+ * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
+ */
+ png_write_chunk_data(png_ptr, (png_bytep)new_key, (png_size_t)(key_len + 1));
+
+ /* Set the compression flag */
+ if (compression == PNG_ITXT_COMPRESSION_NONE ||
+ compression == PNG_TEXT_COMPRESSION_NONE)
+ cbuf[0] = 0;
+
+ else /* compression == PNG_ITXT_COMPRESSION_zTXt */
+ cbuf[0] = 1;
+
+ /* Set the compression method */
+ cbuf[1] = 0;
+
+ png_write_chunk_data(png_ptr, cbuf, (png_size_t)2);
+
+ cbuf[0] = 0;
+ png_write_chunk_data(png_ptr, (new_lang ? (png_const_bytep)new_lang : cbuf),
+ (png_size_t)(lang_len + 1));
+
+ png_write_chunk_data(png_ptr, (lang_key ? (png_const_bytep)lang_key : cbuf),
+ (png_size_t)(lang_key_len + 1));
+
+ png_write_compressed_data_out(png_ptr, &comp);
+
+ png_write_chunk_end(png_ptr);
+
+ png_free(png_ptr, new_key);
+ png_free(png_ptr, new_lang);
+}
+#endif
+
+#ifdef PNG_WRITE_oFFs_SUPPORTED
+/* Write the oFFs chunk */
+void /* PRIVATE */
+png_write_oFFs(png_structp png_ptr, png_int_32 x_offset, png_int_32 y_offset,
+ int unit_type)
+{
+ PNG_oFFs;
+ png_byte buf[9];
+
+ png_debug(1, "in png_write_oFFs");
+
+ if (unit_type >= PNG_OFFSET_LAST)
+ png_warning(png_ptr, "Unrecognized unit type for oFFs chunk");
+
+ png_save_int_32(buf, x_offset);
+ png_save_int_32(buf + 4, y_offset);
+ buf[8] = (png_byte)unit_type;
+
+ png_write_chunk(png_ptr, png_oFFs, buf, (png_size_t)9);
+}
+#endif
+#ifdef PNG_WRITE_pCAL_SUPPORTED
+/* Write the pCAL chunk (described in the PNG extensions document) */
+void /* PRIVATE */
+png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0,
+ png_int_32 X1, int type, int nparams, png_const_charp units,
+ png_charpp params)
+{
+ PNG_pCAL;
+ png_size_t purpose_len, units_len, total_len;
+ png_uint_32p params_len;
+ png_byte buf[10];
+ png_charp new_purpose;
+ int i;
+
+ png_debug1(1, "in png_write_pCAL (%d parameters)", nparams);
+
+ if (type >= PNG_EQUATION_LAST)
+ png_warning(png_ptr, "Unrecognized equation type for pCAL chunk");
+
+ purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1;
+ png_debug1(3, "pCAL purpose length = %d", (int)purpose_len);
+ units_len = png_strlen(units) + (nparams == 0 ? 0 : 1);
+ png_debug1(3, "pCAL units length = %d", (int)units_len);
+ total_len = purpose_len + units_len + 10;
+
+ params_len = (png_uint_32p)png_malloc(png_ptr,
+ (png_alloc_size_t)(nparams * png_sizeof(png_uint_32)));
+
+ /* Find the length of each parameter, making sure we don't count the
+ * null terminator for the last parameter.
+ */
+ for (i = 0; i < nparams; i++)
+ {
+ params_len[i] = png_strlen(params[i]) + (i == nparams - 1 ? 0 : 1);
+ png_debug2(3, "pCAL parameter %d length = %lu", i,
+ (unsigned long)params_len[i]);
+ total_len += (png_size_t)params_len[i];
+ }
+
+ png_debug1(3, "pCAL total length = %d", (int)total_len);
+ png_write_chunk_start(png_ptr, png_pCAL, (png_uint_32)total_len);
+ png_write_chunk_data(png_ptr, (png_const_bytep)new_purpose,
+ (png_size_t)purpose_len);
+ png_save_int_32(buf, X0);
+ png_save_int_32(buf + 4, X1);
+ buf[8] = (png_byte)type;
+ buf[9] = (png_byte)nparams;
+ png_write_chunk_data(png_ptr, buf, (png_size_t)10);
+ png_write_chunk_data(png_ptr, (png_const_bytep)units, (png_size_t)units_len);
+
+ png_free(png_ptr, new_purpose);
+
+ for (i = 0; i < nparams; i++)
+ {
+ png_write_chunk_data(png_ptr, (png_const_bytep)params[i],
+ (png_size_t)params_len[i]);
+ }
+
+ png_free(png_ptr, params_len);
+ png_write_chunk_end(png_ptr);
+}
+#endif
+
+#ifdef PNG_WRITE_sCAL_SUPPORTED
+/* Write the sCAL chunk */
+void /* PRIVATE */
+png_write_sCAL_s(png_structp png_ptr, int unit, png_const_charp width,
+ png_const_charp height)
+{
+ PNG_sCAL;
+ png_byte buf[64];
+ png_size_t wlen, hlen, total_len;
+
+ png_debug(1, "in png_write_sCAL_s");
+
+ wlen = png_strlen(width);
+ hlen = png_strlen(height);
+ total_len = wlen + hlen + 2;
+
+ if (total_len > 64)
+ {
+ png_warning(png_ptr, "Can't write sCAL (buffer too small)");
+ return;
+ }
+
+ buf[0] = (png_byte)unit;
+ png_memcpy(buf + 1, width, wlen + 1); /* Append the '\0' here */
+ png_memcpy(buf + wlen + 2, height, hlen); /* Do NOT append the '\0' here */
+
+ png_debug1(3, "sCAL total length = %u", (unsigned int)total_len);
+ png_write_chunk(png_ptr, png_sCAL, buf, total_len);
+}
+#endif
+
+#ifdef PNG_WRITE_pHYs_SUPPORTED
+/* Write the pHYs chunk */
+void /* PRIVATE */
+png_write_pHYs(png_structp png_ptr, png_uint_32 x_pixels_per_unit,
+ png_uint_32 y_pixels_per_unit,
+ int unit_type)
+{
+ PNG_pHYs;
+ png_byte buf[9];
+
+ png_debug(1, "in png_write_pHYs");
+
+ if (unit_type >= PNG_RESOLUTION_LAST)
+ png_warning(png_ptr, "Unrecognized unit type for pHYs chunk");
+
+ png_save_uint_32(buf, x_pixels_per_unit);
+ png_save_uint_32(buf + 4, y_pixels_per_unit);
+ buf[8] = (png_byte)unit_type;
+
+ png_write_chunk(png_ptr, png_pHYs, buf, (png_size_t)9);
+}
+#endif
+
+#ifdef PNG_WRITE_tIME_SUPPORTED
+/* Write the tIME chunk. Use either png_convert_from_struct_tm()
+ * or png_convert_from_time_t(), or fill in the structure yourself.
+ */
+void /* PRIVATE */
+png_write_tIME(png_structp png_ptr, png_const_timep mod_time)
+{
+ PNG_tIME;
+ png_byte buf[7];
+
+ png_debug(1, "in png_write_tIME");
+
+ if (mod_time->month > 12 || mod_time->month < 1 ||
+ mod_time->day > 31 || mod_time->day < 1 ||
+ mod_time->hour > 23 || mod_time->second > 60)
+ {
+ png_warning(png_ptr, "Invalid time specified for tIME chunk");
+ return;
+ }
+
+ png_save_uint_16(buf, mod_time->year);
+ buf[2] = mod_time->month;
+ buf[3] = mod_time->day;
+ buf[4] = mod_time->hour;
+ buf[5] = mod_time->minute;
+ buf[6] = mod_time->second;
+
+ png_write_chunk(png_ptr, png_tIME, buf, (png_size_t)7);
+}
+#endif
+
+/* Initializes the row writing capability of libpng */
+void /* PRIVATE */
+png_write_start_row(png_structp png_ptr)
+{
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+ /* Start of interlace block */
+ int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+ /* Offset to next interlace block */
+ int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+ /* Start of interlace block in the y direction */
+ int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+
+ /* Offset to next interlace block in the y direction */
+ int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+#endif
+
+ png_size_t buf_size;
+
+ png_debug(1, "in png_write_start_row");
+
+ buf_size = (png_size_t)(PNG_ROWBYTES(
+ png_ptr->usr_channels*png_ptr->usr_bit_depth, png_ptr->width) + 1);
+
+ /* Set up row buffer */
+ png_ptr->row_buf = (png_bytep)png_malloc(png_ptr,
+ (png_alloc_size_t)buf_size);
+
+ png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE;
+
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+ /* Set up filtering buffer, if using this filter */
+ if (png_ptr->do_filter & PNG_FILTER_SUB)
+ {
+ png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, png_ptr->rowbytes + 1);
+
+ png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
+ }
+
+ /* We only need to keep the previous row if we are using one of these. */
+ if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH))
+ {
+ /* Set up previous row buffer */
+ png_ptr->prev_row = (png_bytep)png_calloc(png_ptr,
+ (png_alloc_size_t)buf_size);
+
+ if (png_ptr->do_filter & PNG_FILTER_UP)
+ {
+ png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
+ png_ptr->rowbytes + 1);
+
+ png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
+ }
+
+ if (png_ptr->do_filter & PNG_FILTER_AVG)
+ {
+ png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
+ png_ptr->rowbytes + 1);
+
+ png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
+ }
+
+ if (png_ptr->do_filter & PNG_FILTER_PAETH)
+ {
+ png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,
+ png_ptr->rowbytes + 1);
+
+ png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
+ }
+ }
+#endif /* PNG_WRITE_FILTER_SUPPORTED */
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ /* If interlaced, we need to set up width and height of pass */
+ if (png_ptr->interlaced)
+ {
+ if (!(png_ptr->transformations & PNG_INTERLACE))
+ {
+ png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
+ png_pass_ystart[0]) / png_pass_yinc[0];
+
+ png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 -
+ png_pass_start[0]) / png_pass_inc[0];
+ }
+
+ else
+ {
+ png_ptr->num_rows = png_ptr->height;
+ png_ptr->usr_width = png_ptr->width;
+ }
+ }
+
+ else
+#endif
+ {
+ png_ptr->num_rows = png_ptr->height;
+ png_ptr->usr_width = png_ptr->width;
+ }
+
+ png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+ png_ptr->zstream.next_out = png_ptr->zbuf;
+}
+
+/* Internal use only. Called when finished processing a row of data. */
+void /* PRIVATE */
+png_write_finish_row(png_structp png_ptr)
+{
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+ /* Start of interlace block */
+ int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+ /* Offset to next interlace block */
+ int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+ /* Start of interlace block in the y direction */
+ int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+
+ /* Offset to next interlace block in the y direction */
+ int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+#endif
+
+ int ret;
+
+ png_debug(1, "in png_write_finish_row");
+
+ /* Next row */
+ png_ptr->row_number++;
+
+ /* See if we are done */
+ if (png_ptr->row_number < png_ptr->num_rows)
+ return;
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+ /* If interlaced, go to next pass */
+ if (png_ptr->interlaced)
+ {
+ png_ptr->row_number = 0;
+ if (png_ptr->transformations & PNG_INTERLACE)
+ {
+ png_ptr->pass++;
+ }
+
+ else
+ {
+ /* Loop until we find a non-zero width or height pass */
+ do
+ {
+ png_ptr->pass++;
+
+ if (png_ptr->pass >= 7)
+ break;
+
+ png_ptr->usr_width = (png_ptr->width +
+ png_pass_inc[png_ptr->pass] - 1 -
+ png_pass_start[png_ptr->pass]) /
+ png_pass_inc[png_ptr->pass];
+
+ png_ptr->num_rows = (png_ptr->height +
+ png_pass_yinc[png_ptr->pass] - 1 -
+ png_pass_ystart[png_ptr->pass]) /
+ png_pass_yinc[png_ptr->pass];
+
+ if (png_ptr->transformations & PNG_INTERLACE)
+ break;
+
+ } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0);
+
+ }
+
+ /* Reset the row above the image for the next pass */
+ if (png_ptr->pass < 7)
+ {
+ if (png_ptr->prev_row != NULL)
+ png_memset(png_ptr->prev_row, 0,
+ (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels*
+ png_ptr->usr_bit_depth, png_ptr->width)) + 1);
+
+ return;
+ }
+ }
+#endif
+
+ /* If we get here, we've just written the last row, so we need
+ to flush the compressor */
+ do
+ {
+ /* Tell the compressor we are done */
+ ret = deflate(&png_ptr->zstream, Z_FINISH);
+
+ /* Check for an error */
+ if (ret == Z_OK)
+ {
+ /* Check to see if we need more room */
+ if (!(png_ptr->zstream.avail_out))
+ {
+ png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
+ png_ptr->zstream.next_out = png_ptr->zbuf;
+ png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+ }
+ }
+
+ else if (ret != Z_STREAM_END)
+ {
+ if (png_ptr->zstream.msg != NULL)
+ png_error(png_ptr, png_ptr->zstream.msg);
+
+ else
+ png_error(png_ptr, "zlib error");
+ }
+ } while (ret != Z_STREAM_END);
+
+ /* Write any extra space */
+ if (png_ptr->zstream.avail_out < png_ptr->zbuf_size)
+ {
+ png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size -
+ png_ptr->zstream.avail_out);
+ }
+
+ deflateReset(&png_ptr->zstream);
+ png_ptr->zstream.data_type = Z_BINARY;
+}
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+/* Pick out the correct pixels for the interlace pass.
+ * The basic idea here is to go through the row with a source
+ * pointer and a destination pointer (sp and dp), and copy the
+ * correct pixels for the pass. As the row gets compacted,
+ * sp will always be >= dp, so we should never overwrite anything.
+ * See the default: case for the easiest code to understand.
+ */
+void /* PRIVATE */
+png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass)
+{
+ /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+ /* Start of interlace block */
+ int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+ /* Offset to next interlace block */
+ int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+ png_debug(1, "in png_do_write_interlace");
+
+ /* We don't have to do anything on the last pass (6) */
+ if (pass < 6)
+ {
+ /* Each pixel depth is handled separately */
+ switch (row_info->pixel_depth)
+ {
+ case 1:
+ {
+ png_bytep sp;
+ png_bytep dp;
+ int shift;
+ int d;
+ int value;
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+
+ dp = row;
+ d = 0;
+ shift = 7;
+
+ for (i = png_pass_start[pass]; i < row_width;
+ i += png_pass_inc[pass])
+ {
+ sp = row + (png_size_t)(i >> 3);
+ value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01;
+ d |= (value << shift);
+
+ if (shift == 0)
+ {
+ shift = 7;
+ *dp++ = (png_byte)d;
+ d = 0;
+ }
+
+ else
+ shift--;
+
+ }
+ if (shift != 7)
+ *dp = (png_byte)d;
+
+ break;
+ }
+
+ case 2:
+ {
+ png_bytep sp;
+ png_bytep dp;
+ int shift;
+ int d;
+ int value;
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+
+ dp = row;
+ shift = 6;
+ d = 0;
+
+ for (i = png_pass_start[pass]; i < row_width;
+ i += png_pass_inc[pass])
+ {
+ sp = row + (png_size_t)(i >> 2);
+ value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03;
+ d |= (value << shift);
+
+ if (shift == 0)
+ {
+ shift = 6;
+ *dp++ = (png_byte)d;
+ d = 0;
+ }
+
+ else
+ shift -= 2;
+ }
+ if (shift != 6)
+ *dp = (png_byte)d;
+
+ break;
+ }
+
+ case 4:
+ {
+ png_bytep sp;
+ png_bytep dp;
+ int shift;
+ int d;
+ int value;
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+
+ dp = row;
+ shift = 4;
+ d = 0;
+ for (i = png_pass_start[pass]; i < row_width;
+ i += png_pass_inc[pass])
+ {
+ sp = row + (png_size_t)(i >> 1);
+ value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f;
+ d |= (value << shift);
+
+ if (shift == 0)
+ {
+ shift = 4;
+ *dp++ = (png_byte)d;
+ d = 0;
+ }
+
+ else
+ shift -= 4;
+ }
+ if (shift != 4)
+ *dp = (png_byte)d;
+
+ break;
+ }
+
+ default:
+ {
+ png_bytep sp;
+ png_bytep dp;
+ png_uint_32 i;
+ png_uint_32 row_width = row_info->width;
+ png_size_t pixel_bytes;
+
+ /* Start at the beginning */
+ dp = row;
+
+ /* Find out how many bytes each pixel takes up */
+ pixel_bytes = (row_info->pixel_depth >> 3);
+
+ /* Loop through the row, only looking at the pixels that matter */
+ for (i = png_pass_start[pass]; i < row_width;
+ i += png_pass_inc[pass])
+ {
+ /* Find out where the original pixel is */
+ sp = row + (png_size_t)i * pixel_bytes;
+
+ /* Move the pixel */
+ if (dp != sp)
+ png_memcpy(dp, sp, pixel_bytes);
+
+ /* Next pixel */
+ dp += pixel_bytes;
+ }
+ break;
+ }
+ }
+ /* Set new row width */
+ row_info->width = (row_info->width +
+ png_pass_inc[pass] - 1 -
+ png_pass_start[pass]) /
+ png_pass_inc[pass];
+
+ row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
+ row_info->width);
+ }
+}
+#endif
+
+/* This filters the row, chooses which filter to use, if it has not already
+ * been specified by the application, and then writes the row out with the
+ * chosen filter.
+ */
+#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1)
+#define PNG_HISHIFT 10
+#define PNG_LOMASK ((png_uint_32)0xffffL)
+#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT))
+void /* PRIVATE */
+png_write_find_filter(png_structp png_ptr, png_row_infop row_info)
+{
+ png_bytep best_row;
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+ png_bytep prev_row, row_buf;
+ png_uint_32 mins, bpp;
+ png_byte filter_to_do = png_ptr->do_filter;
+ png_size_t row_bytes = row_info->rowbytes;
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+ int num_p_filters = (int)png_ptr->num_prev_filters;
+#endif
+
+ png_debug(1, "in png_write_find_filter");
+
+#ifndef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+ if (png_ptr->row_number == 0 && filter_to_do == PNG_ALL_FILTERS)
+ {
+ /* These will never be selected so we need not test them. */
+ filter_to_do &= ~(PNG_FILTER_UP | PNG_FILTER_PAETH);
+ }
+#endif
+
+ /* Find out how many bytes offset each pixel is */
+ bpp = (row_info->pixel_depth + 7) >> 3;
+
+ prev_row = png_ptr->prev_row;
+#endif
+ best_row = png_ptr->row_buf;
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+ row_buf = best_row;
+ mins = PNG_MAXSUM;
+
+ /* The prediction method we use is to find which method provides the
+ * smallest value when summing the absolute values of the distances
+ * from zero, using anything >= 128 as negative numbers. This is known
+ * as the "minimum sum of absolute differences" heuristic. Other
+ * heuristics are the "weighted minimum sum of absolute differences"
+ * (experimental and can in theory improve compression), and the "zlib
+ * predictive" method (not implemented yet), which does test compressions
+ * of lines using different filter methods, and then chooses the
+ * (series of) filter(s) that give minimum compressed data size (VERY
+ * computationally expensive).
+ *
+ * GRR 980525: consider also
+ *
+ * (1) minimum sum of absolute differences from running average (i.e.,
+ * keep running sum of non-absolute differences & count of bytes)
+ * [track dispersion, too? restart average if dispersion too large?]
+ *
+ * (1b) minimum sum of absolute differences from sliding average, probably
+ * with window size <= deflate window (usually 32K)
+ *
+ * (2) minimum sum of squared differences from zero or running average
+ * (i.e., ~ root-mean-square approach)
+ */
+
+
+ /* We don't need to test the 'no filter' case if this is the only filter
+ * that has been chosen, as it doesn't actually do anything to the data.
+ */
+ if ((filter_to_do & PNG_FILTER_NONE) && filter_to_do != PNG_FILTER_NONE)
+ {
+ png_bytep rp;
+ png_uint_32 sum = 0;
+ png_size_t i;
+ int v;
+
+ for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++)
+ {
+ v = *rp;
+ sum += (v < 128) ? v : 256 - v;
+ }
+
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+ if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+ {
+ png_uint_32 sumhi, sumlo;
+ int j;
+ sumlo = sum & PNG_LOMASK;
+ sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */
+
+ /* Reduce the sum if we match any of the previous rows */
+ for (j = 0; j < num_p_filters; j++)
+ {
+ if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE)
+ {
+ sumlo = (sumlo * png_ptr->filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+
+ sumhi = (sumhi * png_ptr->filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+ }
+ }
+
+ /* Factor in the cost of this filter (this is here for completeness,
+ * but it makes no sense to have a "cost" for the NONE filter, as
+ * it has the minimum possible computational cost - none).
+ */
+ sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >>
+ PNG_COST_SHIFT;
+
+ sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >>
+ PNG_COST_SHIFT;
+
+ if (sumhi > PNG_HIMASK)
+ sum = PNG_MAXSUM;
+
+ else
+ sum = (sumhi << PNG_HISHIFT) + sumlo;
+ }
+#endif
+ mins = sum;
+ }
+
+ /* Sub filter */
+ if (filter_to_do == PNG_FILTER_SUB)
+ /* It's the only filter so no testing is needed */
+ {
+ png_bytep rp, lp, dp;
+ png_size_t i;
+
+ for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp;
+ i++, rp++, dp++)
+ {
+ *dp = *rp;
+ }
+
+ for (lp = row_buf + 1; i < row_bytes;
+ i++, rp++, lp++, dp++)
+ {
+ *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
+ }
+
+ best_row = png_ptr->sub_row;
+ }
+
+ else if (filter_to_do & PNG_FILTER_SUB)
+ {
+ png_bytep rp, dp, lp;
+ png_uint_32 sum = 0, lmins = mins;
+ png_size_t i;
+ int v;
+
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+ /* We temporarily increase the "minimum sum" by the factor we
+ * would reduce the sum of this filter, so that we can do the
+ * early exit comparison without scaling the sum each time.
+ */
+ if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+ {
+ int j;
+ png_uint_32 lmhi, lmlo;
+ lmlo = lmins & PNG_LOMASK;
+ lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
+
+ for (j = 0; j < num_p_filters; j++)
+ {
+ if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB)
+ {
+ lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+
+ lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+ }
+ }
+
+ lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
+ PNG_COST_SHIFT;
+
+ lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
+ PNG_COST_SHIFT;
+
+ if (lmhi > PNG_HIMASK)
+ lmins = PNG_MAXSUM;
+
+ else
+ lmins = (lmhi << PNG_HISHIFT) + lmlo;
+ }
+#endif
+
+ for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp;
+ i++, rp++, dp++)
+ {
+ v = *dp = *rp;
+
+ sum += (v < 128) ? v : 256 - v;
+ }
+
+ for (lp = row_buf + 1; i < row_bytes;
+ i++, rp++, lp++, dp++)
+ {
+ v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
+
+ sum += (v < 128) ? v : 256 - v;
+
+ if (sum > lmins) /* We are already worse, don't continue. */
+ break;
+ }
+
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+ if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+ {
+ int j;
+ png_uint_32 sumhi, sumlo;
+ sumlo = sum & PNG_LOMASK;
+ sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
+
+ for (j = 0; j < num_p_filters; j++)
+ {
+ if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB)
+ {
+ sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+
+ sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+ }
+ }
+
+ sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
+ PNG_COST_SHIFT;
+
+ sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
+ PNG_COST_SHIFT;
+
+ if (sumhi > PNG_HIMASK)
+ sum = PNG_MAXSUM;
+
+ else
+ sum = (sumhi << PNG_HISHIFT) + sumlo;
+ }
+#endif
+
+ if (sum < mins)
+ {
+ mins = sum;
+ best_row = png_ptr->sub_row;
+ }
+ }
+
+ /* Up filter */
+ if (filter_to_do == PNG_FILTER_UP)
+ {
+ png_bytep rp, dp, pp;
+ png_size_t i;
+
+ for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1,
+ pp = prev_row + 1; i < row_bytes;
+ i++, rp++, pp++, dp++)
+ {
+ *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff);
+ }
+
+ best_row = png_ptr->up_row;
+ }
+
+ else if (filter_to_do & PNG_FILTER_UP)
+ {
+ png_bytep rp, dp, pp;
+ png_uint_32 sum = 0, lmins = mins;
+ png_size_t i;
+ int v;
+
+
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+ if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+ {
+ int j;
+ png_uint_32 lmhi, lmlo;
+ lmlo = lmins & PNG_LOMASK;
+ lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
+
+ for (j = 0; j < num_p_filters; j++)
+ {
+ if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP)
+ {
+ lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+
+ lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+ }
+ }
+
+ lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >>
+ PNG_COST_SHIFT;
+
+ lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >>
+ PNG_COST_SHIFT;
+
+ if (lmhi > PNG_HIMASK)
+ lmins = PNG_MAXSUM;
+
+ else
+ lmins = (lmhi << PNG_HISHIFT) + lmlo;
+ }
+#endif
+
+ for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1,
+ pp = prev_row + 1; i < row_bytes; i++)
+ {
+ v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
+
+ sum += (v < 128) ? v : 256 - v;
+
+ if (sum > lmins) /* We are already worse, don't continue. */
+ break;
+ }
+
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+ if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+ {
+ int j;
+ png_uint_32 sumhi, sumlo;
+ sumlo = sum & PNG_LOMASK;
+ sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
+
+ for (j = 0; j < num_p_filters; j++)
+ {
+ if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP)
+ {
+ sumlo = (sumlo * png_ptr->filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+
+ sumhi = (sumhi * png_ptr->filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+ }
+ }
+
+ sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >>
+ PNG_COST_SHIFT;
+
+ sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >>
+ PNG_COST_SHIFT;
+
+ if (sumhi > PNG_HIMASK)
+ sum = PNG_MAXSUM;
+
+ else
+ sum = (sumhi << PNG_HISHIFT) + sumlo;
+ }
+#endif
+
+ if (sum < mins)
+ {
+ mins = sum;
+ best_row = png_ptr->up_row;
+ }
+ }
+
+ /* Avg filter */
+ if (filter_to_do == PNG_FILTER_AVG)
+ {
+ png_bytep rp, dp, pp, lp;
+ png_uint_32 i;
+
+ for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1,
+ pp = prev_row + 1; i < bpp; i++)
+ {
+ *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
+ }
+
+ for (lp = row_buf + 1; i < row_bytes; i++)
+ {
+ *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2))
+ & 0xff);
+ }
+ best_row = png_ptr->avg_row;
+ }
+
+ else if (filter_to_do & PNG_FILTER_AVG)
+ {
+ png_bytep rp, dp, pp, lp;
+ png_uint_32 sum = 0, lmins = mins;
+ png_size_t i;
+ int v;
+
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+ if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+ {
+ int j;
+ png_uint_32 lmhi, lmlo;
+ lmlo = lmins & PNG_LOMASK;
+ lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
+
+ for (j = 0; j < num_p_filters; j++)
+ {
+ if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG)
+ {
+ lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+
+ lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+ }
+ }
+
+ lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >>
+ PNG_COST_SHIFT;
+
+ lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >>
+ PNG_COST_SHIFT;
+
+ if (lmhi > PNG_HIMASK)
+ lmins = PNG_MAXSUM;
+
+ else
+ lmins = (lmhi << PNG_HISHIFT) + lmlo;
+ }
+#endif
+
+ for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1,
+ pp = prev_row + 1; i < bpp; i++)
+ {
+ v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
+
+ sum += (v < 128) ? v : 256 - v;
+ }
+
+ for (lp = row_buf + 1; i < row_bytes; i++)
+ {
+ v = *dp++ =
+ (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff);
+
+ sum += (v < 128) ? v : 256 - v;
+
+ if (sum > lmins) /* We are already worse, don't continue. */
+ break;
+ }
+
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+ if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+ {
+ int j;
+ png_uint_32 sumhi, sumlo;
+ sumlo = sum & PNG_LOMASK;
+ sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
+
+ for (j = 0; j < num_p_filters; j++)
+ {
+ if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE)
+ {
+ sumlo = (sumlo * png_ptr->filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+
+ sumhi = (sumhi * png_ptr->filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+ }
+ }
+
+ sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >>
+ PNG_COST_SHIFT;
+
+ sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >>
+ PNG_COST_SHIFT;
+
+ if (sumhi > PNG_HIMASK)
+ sum = PNG_MAXSUM;
+
+ else
+ sum = (sumhi << PNG_HISHIFT) + sumlo;
+ }
+#endif
+
+ if (sum < mins)
+ {
+ mins = sum;
+ best_row = png_ptr->avg_row;
+ }
+ }
+
+ /* Paeth filter */
+ if (filter_to_do == PNG_FILTER_PAETH)
+ {
+ png_bytep rp, dp, pp, cp, lp;
+ png_size_t i;
+
+ for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1,
+ pp = prev_row + 1; i < bpp; i++)
+ {
+ *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
+ }
+
+ for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)
+ {
+ int a, b, c, pa, pb, pc, p;
+
+ b = *pp++;
+ c = *cp++;
+ a = *lp++;
+
+ p = b - c;
+ pc = a - c;
+
+#ifdef PNG_USE_ABS
+ pa = abs(p);
+ pb = abs(pc);
+ pc = abs(p + pc);
+#else
+ pa = p < 0 ? -p : p;
+ pb = pc < 0 ? -pc : pc;
+ pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+
+ p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
+
+ *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
+ }
+ best_row = png_ptr->paeth_row;
+ }
+
+ else if (filter_to_do & PNG_FILTER_PAETH)
+ {
+ png_bytep rp, dp, pp, cp, lp;
+ png_uint_32 sum = 0, lmins = mins;
+ png_size_t i;
+ int v;
+
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+ if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+ {
+ int j;
+ png_uint_32 lmhi, lmlo;
+ lmlo = lmins & PNG_LOMASK;
+ lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
+
+ for (j = 0; j < num_p_filters; j++)
+ {
+ if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH)
+ {
+ lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+
+ lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+ }
+ }
+
+ lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >>
+ PNG_COST_SHIFT;
+
+ lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >>
+ PNG_COST_SHIFT;
+
+ if (lmhi > PNG_HIMASK)
+ lmins = PNG_MAXSUM;
+
+ else
+ lmins = (lmhi << PNG_HISHIFT) + lmlo;
+ }
+#endif
+
+ for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1,
+ pp = prev_row + 1; i < bpp; i++)
+ {
+ v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
+
+ sum += (v < 128) ? v : 256 - v;
+ }
+
+ for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)
+ {
+ int a, b, c, pa, pb, pc, p;
+
+ b = *pp++;
+ c = *cp++;
+ a = *lp++;
+
+#ifndef PNG_SLOW_PAETH
+ p = b - c;
+ pc = a - c;
+#ifdef PNG_USE_ABS
+ pa = abs(p);
+ pb = abs(pc);
+ pc = abs(p + pc);
+#else
+ pa = p < 0 ? -p : p;
+ pb = pc < 0 ? -pc : pc;
+ pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+ p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
+#else /* PNG_SLOW_PAETH */
+ p = a + b - c;
+ pa = abs(p - a);
+ pb = abs(p - b);
+ pc = abs(p - c);
+
+ if (pa <= pb && pa <= pc)
+ p = a;
+
+ else if (pb <= pc)
+ p = b;
+
+ else
+ p = c;
+#endif /* PNG_SLOW_PAETH */
+
+ v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
+
+ sum += (v < 128) ? v : 256 - v;
+
+ if (sum > lmins) /* We are already worse, don't continue. */
+ break;
+ }
+
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+ if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+ {
+ int j;
+ png_uint_32 sumhi, sumlo;
+ sumlo = sum & PNG_LOMASK;
+ sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
+
+ for (j = 0; j < num_p_filters; j++)
+ {
+ if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH)
+ {
+ sumlo = (sumlo * png_ptr->filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+
+ sumhi = (sumhi * png_ptr->filter_weights[j]) >>
+ PNG_WEIGHT_SHIFT;
+ }
+ }
+
+ sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >>
+ PNG_COST_SHIFT;
+
+ sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >>
+ PNG_COST_SHIFT;
+
+ if (sumhi > PNG_HIMASK)
+ sum = PNG_MAXSUM;
+
+ else
+ sum = (sumhi << PNG_HISHIFT) + sumlo;
+ }
+#endif
+
+ if (sum < mins)
+ {
+ best_row = png_ptr->paeth_row;
+ }
+ }
+#endif /* PNG_WRITE_FILTER_SUPPORTED */
+ /* Do the actual writing of the filtered row data from the chosen filter. */
+
+ png_write_filtered_row(png_ptr, best_row);
+
+#ifdef PNG_WRITE_FILTER_SUPPORTED
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+ /* Save the type of filter we picked this time for future calculations */
+ if (png_ptr->num_prev_filters > 0)
+ {
+ int j;
+
+ for (j = 1; j < num_p_filters; j++)
+ {
+ png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1];
+ }
+
+ png_ptr->prev_filters[j] = best_row[0];
+ }
+#endif
+#endif /* PNG_WRITE_FILTER_SUPPORTED */
+}
+
+
+/* Do the actual writing of a previously filtered row. */
+void /* PRIVATE */
+png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row)
+{
+ png_size_t avail;
+
+ png_debug(1, "in png_write_filtered_row");
+
+ png_debug1(2, "filter = %d", filtered_row[0]);
+ /* Set up the zlib input buffer */
+
+ png_ptr->zstream.next_in = filtered_row;
+ png_ptr->zstream.avail_in = 0;
+ avail = png_ptr->row_info.rowbytes + 1;
+ /* Repeat until we have compressed all the data */
+ do
+ {
+ int ret; /* Return of zlib */
+
+ /* Record the number of bytes available - zlib supports at least 65535
+ * bytes at one step, depending on the size of the zlib type 'uInt', the
+ * maximum size zlib can write at once is ZLIB_IO_MAX (from pngpriv.h).
+ * Use this because on 16 bit systems 'rowbytes' can be up to 65536 (i.e.
+ * one more than 16 bits) and, in this case 'rowbytes+1' can overflow a
+ * uInt. ZLIB_IO_MAX can be safely reduced to cause zlib to be called
+ * with smaller chunks of data.
+ */
+ if (png_ptr->zstream.avail_in == 0)
+ {
+ if (avail > ZLIB_IO_MAX)
+ {
+ png_ptr->zstream.avail_in = ZLIB_IO_MAX;
+ avail -= ZLIB_IO_MAX;
+ }
+
+ else
+ {
+ /* So this will fit in the available uInt space: */
+ png_ptr->zstream.avail_in = (uInt)avail;
+ avail = 0;
+ }
+ }
+
+ /* Compress the data */
+ ret = deflate(&png_ptr->zstream, Z_NO_FLUSH);
+
+ /* Check for compression errors */
+ if (ret != Z_OK)
+ {
+ if (png_ptr->zstream.msg != NULL)
+ png_error(png_ptr, png_ptr->zstream.msg);
+
+ else
+ png_error(png_ptr, "zlib error");
+ }
+
+ /* See if it is time to write another IDAT */
+ if (!(png_ptr->zstream.avail_out))
+ {
+ /* Write the IDAT and reset the zlib output buffer */
+ png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
+ png_ptr->zstream.next_out = png_ptr->zbuf;
+ png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+ }
+ /* Repeat until all data has been compressed */
+ } while (avail > 0 || png_ptr->zstream.avail_in > 0);
+
+ /* Swap the current and previous rows */
+ if (png_ptr->prev_row != NULL)
+ {
+ png_bytep tptr;
+
+ tptr = png_ptr->prev_row;
+ png_ptr->prev_row = png_ptr->row_buf;
+ png_ptr->row_buf = tptr;
+ }
+
+ /* Finish row - updates counters and flushes zlib if last row */
+ png_write_finish_row(png_ptr);
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+ png_ptr->flush_rows++;
+
+ if (png_ptr->flush_dist > 0 &&
+ png_ptr->flush_rows >= png_ptr->flush_dist)
+ {
+ png_write_flush(png_ptr);
+ }
+#endif
+}
+#endif /* PNG_WRITE_SUPPORTED */
diff --git a/nGenEx/ActiveWindow.cpp b/nGenEx/ActiveWindow.cpp
new file mode 100644
index 0000000..4eebfa6
--- /dev/null
+++ b/nGenEx/ActiveWindow.cpp
@@ -0,0 +1,1001 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ActiveWindow.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Window class
+*/
+
+#include "MemDebug.h"
+#include "ActiveWindow.h"
+#include "EventDispatch.h"
+#include "Color.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "FontMgr.h"
+#include "Layout.h"
+#include "Polygon.h"
+#include "Screen.h"
+#include "View.h"
+#include "Video.h"
+
+// +--------------------------------------------------------------------+
+
+Font* ActiveWindow::sys_font = 0;
+Color ActiveWindow::sys_back_color = Color(128,128,128);
+Color ActiveWindow::sys_fore_color = Color( 0, 0, 0);
+
+void ActiveWindow::SetSystemFont(Font* f) { sys_font = f; }
+void ActiveWindow::SetSystemBackColor(Color c) { sys_back_color = c; }
+void ActiveWindow::SetSystemForeColor(Color c) { sys_fore_color = c; }
+
+// +--------------------------------------------------------------------+
+
+ActiveWindow::ActiveWindow(Screen* screen, int ax, int ay, int aw, int ah,
+ DWORD aid, DWORD s, ActiveWindow* pParent)
+ : Window(screen, ax, ay, aw, ah), id(aid), style(s), focus(false), enabled(true),
+ text_align(DT_CENTER), single_line(false), alpha(1),
+ texture(0), back_color(sys_back_color), fore_color(sys_fore_color),
+ parent(pParent), form(0), transparent(false), topmost(true),
+ layout(0), rows(1), cols(1), polys(0), vset(0), mtl(0),
+ fixed_width(0), fixed_height(0), hide_partial(true)
+{
+ ZeroMemory(tab, sizeof(tab));
+
+ font = sys_font;
+
+ if (parent) {
+ parent->AddChild(this);
+ }
+ else {
+ screen->AddWindow(this);
+ }
+
+ shown = false;
+ Show();
+
+ char buf[32];
+ sprintf(buf, "ActiveWindow %d", id);
+ desc = buf;
+}
+
+// +--------------------------------------------------------------------+
+
+ActiveWindow::~ActiveWindow()
+{
+ if (layout) delete layout;
+
+ screen->DelWindow(this);
+ Hide();
+ clients.destroy();
+ children.destroy();
+
+ if (polys) delete [] polys;
+ if (vset) delete vset;
+ if (mtl) delete mtl;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::Show()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch)
+ dispatch->Register(this);
+
+ ListIter<View> v_iter = view_list;
+ while (++v_iter) {
+ View* view = v_iter.value();
+ view->OnShow();
+ }
+
+ ListIter<ActiveWindow> c_iter = children;
+ while (++c_iter) {
+ ActiveWindow* child = c_iter.value();
+ child->Show();
+ }
+
+ shown = true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::Hide()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch) {
+ dispatch->Unregister(this);
+ focus = false;
+ }
+
+ ListIter<View> v_iter = view_list;
+ while (++v_iter) {
+ View* view = v_iter.value();
+ view->OnHide();
+ }
+
+ ListIter<ActiveWindow> c_iter = children;
+ while (++c_iter) {
+ ActiveWindow* child = c_iter.value();
+ child->Hide();
+ }
+
+ shown = false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::MoveTo(const Rect& r)
+{
+ if (rect.x == r.x &&
+ rect.y == r.y &&
+ rect.w == r.w &&
+ rect.h == r.h)
+ return;
+
+ rect = r;
+ CalcGrid();
+
+ ListIter<View> v = view_list;
+ while (++v)
+ v->OnWindowMove();
+
+ if (layout)
+ layout->DoLayout(this);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::AddChild(ActiveWindow* child)
+{
+ if (child)
+ children.append(child);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::DoLayout()
+{
+ if (layout)
+ layout->DoLayout(this);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::UseLayout(const ArrayList& min_x,
+ const ArrayList& min_y,
+ const FloatList& weight_x,
+ const FloatList& weight_y)
+{
+ if (!layout)
+ layout = new(__FILE__,__LINE__) Layout;
+
+ if (layout)
+ layout->SetConstraints(min_x, min_y, weight_x, weight_y);
+}
+
+void
+ActiveWindow::UseLayout(const FloatList& min_x,
+ const FloatList& min_y,
+ const FloatList& weight_x,
+ const FloatList& weight_y)
+{
+ if (!layout)
+ layout = new(__FILE__,__LINE__) Layout;
+
+ if (layout)
+ layout->SetConstraints(min_x, min_y, weight_x, weight_y);
+}
+
+void
+ActiveWindow::UseLayout(int nrows,
+ int ncols,
+ int* min_x,
+ int* min_y,
+ float* weight_x,
+ float* weight_y)
+{
+ if (!layout)
+ layout = new(__FILE__,__LINE__) Layout;
+
+ if (layout)
+ layout->SetConstraints(nrows, ncols, min_x, min_y, weight_x, weight_y);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::Paint()
+{
+ Draw();
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+ActiveWindow::ShadeColor(Color c, double shade)
+{
+ int ishade = (int) (shade * Color::SHADE_LEVELS);
+ return c.ShadeColor(ishade);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::Draw()
+{
+ int w = rect.w;
+ int h = rect.h;
+
+ if (w < 1 || h < 1 || !shown)
+ return;
+
+ float old_alpha = alpha;
+
+ if (!enabled)
+ SetAlpha(0.5);
+
+ if (!transparent) {
+ if (texture && texture->Width()) {
+ DrawTextureGrid();
+ }
+ else {
+ FillRect(0, 0, w, h, ShadeColor(back_color, 1.0));
+ }
+ }
+
+ if (enabled && view_list.size()) {
+ ListIter<View> v = view_list;
+ while (++v)
+ v->Refresh();
+ }
+
+ if (!transparent) {
+ DrawStyleRect(0, 0, w, h, style);
+ }
+
+ // draw text here:
+ DrawTabbedText();
+
+ if (!enabled)
+ SetAlpha(old_alpha);
+
+ // update children windows:
+ ListIter<ActiveWindow> iter = children;
+ while (++iter) {
+ ActiveWindow* child = iter.value();
+ child->Draw();
+ }
+}
+
+void
+ActiveWindow::CalcGrid()
+{
+ if (polys) delete [] polys;
+ if (vset) delete vset;
+ if (mtl) delete mtl;
+
+ rows = 1;
+ cols = 1;
+
+ if (!texture || texture->Width() < 1)
+ return;
+
+ if (margins.left > 0) cols++;
+ if (margins.right > 0) cols++;
+ if (margins.top > 0) rows++;
+ if (margins.bottom > 0) rows++;
+
+ int npolys = rows*cols;
+ int nverts = (rows+1) * (cols+1);
+
+ if (style & WIN_FRAME_ONLY && npolys == 9)
+ npolys = 8; // skip the center poly
+
+ if (npolys > 0) {
+ int i, j;
+ int x_offsets[4];
+ int y_offsets[4];
+ float u_offsets[4];
+ float v_offsets[4];
+
+ x_offsets[0] = 0;
+ x_offsets[1] = margins.left ? margins.left : rect.w - margins.right;
+ x_offsets[2] = cols==2 ? rect.w : rect.w - margins.right;
+ x_offsets[3] = rect.w;
+
+ y_offsets[0] = 0;
+ y_offsets[1] = margins.top ? margins.top : rect.h - margins.bottom;
+ y_offsets[2] = rows==2 ? rect.h : rect.h - margins.bottom;
+ y_offsets[3] = rect.h;
+
+ float tex_w = (float) texture->Width();
+ float tex_h = (float) texture->Height();
+
+ if (tex_w > rect.w) tex_w = (float) rect.w;
+ if (tex_h > rect.h) tex_h = (float) rect.h;
+
+ u_offsets[0] = 0.0f;
+ u_offsets[1] = margins.left ? (float) margins.left : tex_w - (float) margins.right;
+ u_offsets[2] = cols==2 ? tex_w : tex_w - (float) margins.right;
+ u_offsets[3] = tex_w;
+
+ v_offsets[0] = 0.0f;
+ v_offsets[1] = margins.top ? (float) margins.top : tex_h - (float) margins.bottom;
+ v_offsets[2] = rows==2 ? tex_h : tex_h - (float) margins.bottom;
+ v_offsets[3] = tex_h;
+
+ tex_w = (float) texture->Width();
+ tex_h = (float) texture->Height();
+
+ vset = new(__FILE__,__LINE__) VertexSet(nverts);
+
+ int v = 0;
+
+ Color c = Color::White;
+ c.SetAlpha((BYTE) (alpha*255));
+
+ vset->space = VertexSet::SCREEN_SPACE;
+
+ for (i = 0; i <= rows; i++) {
+ for (j = 0; j <= cols; j++) {
+ vset->diffuse[v] = c.Value();
+
+ vset->s_loc[v].x = (float) (rect.x + x_offsets[j]) - 0.5f;
+ vset->s_loc[v].y = (float) (rect.y + y_offsets[i]) - 0.5f;
+ vset->s_loc[v].z = 0.0f;
+ vset->rw[v] = 1.0f;
+
+ vset->tu[v] = u_offsets[j] / tex_w;
+ vset->tv[v] = v_offsets[i] / tex_h;
+
+ v++;
+ }
+ }
+
+ mtl = new(__FILE__,__LINE__) Material;
+ mtl->tex_diffuse = texture;
+
+ polys = new(__FILE__,__LINE__) Poly[npolys];
+
+ Poly* p = polys;
+
+ ZeroMemory(polys, npolys*sizeof(Poly));
+
+ for (i = 0; i < rows; i++) {
+ for (j = 0; j < cols; j++) {
+ if (style & WIN_FRAME_ONLY) {
+ if (i == 1 && j == 1)
+ continue;
+ }
+
+ p->nverts = 4;
+ p->vertex_set = vset;
+ p->material = mtl;
+
+ p->verts[0] = (i+0)*(cols+1) + j;
+ p->verts[1] = (i+0)*(cols+1) + j + 1;
+ p->verts[2] = (i+1)*(cols+1) + j + 1;
+ p->verts[3] = (i+1)*(cols+1) + j;
+
+ p++;
+ }
+ }
+ }
+}
+
+void
+ActiveWindow::DrawTextureGrid()
+{
+ int npolys = rows*cols;
+
+ if (style & WIN_FRAME_ONLY && npolys == 9)
+ npolys = 8; // skip the center poly
+
+ if (mtl) {
+ mtl->tex_diffuse = texture;
+ }
+
+ int blend = Video::BLEND_SOLID;
+
+ if (alpha < 1)
+ blend = Video::BLEND_ALPHA;
+
+ Video* video = screen->GetVideo();
+ video->SetRenderState(Video::TEXTURE_WRAP, 0);
+ video->DrawScreenPolys(npolys, polys, blend);
+ video->SetRenderState(Video::TEXTURE_WRAP, 1);
+}
+
+void
+ActiveWindow::DrawStyleRect(const Rect& r, int style)
+{
+ DrawStyleRect(r.x, r.y, r.x+r.w, r.y+r.h, style);
+}
+
+void
+ActiveWindow::DrawStyleRect(int x1, int y1, int x2, int y2, int style)
+{
+ if (style & WIN_THIN_FRAME) {
+ DrawRect(x1,y1,x2-1,y2-1, ShadeColor(fore_color, 1.0));
+ }
+ else if (style & WIN_THICK_FRAME) {
+ DrawRect(x1+0,y1+0,x2-1,y2-1, ShadeColor(fore_color, 1.0));
+ DrawRect(x1+1,y1+1,x2-2,y2-2, ShadeColor(fore_color, 1.0));
+ DrawRect(x1+2,y1+2,x2-3,y2-3, ShadeColor(fore_color, 1.0));
+ }
+ else {
+ // draw bevel:
+ if ((style & WIN_RAISED_FRAME) && (style & WIN_SUNK_FRAME)) {
+ Color c = ShadeColor(back_color, 1.6); // full highlight
+ DrawLine(x1, y1, x2-1, y1, c);
+ DrawLine(x1, y1, x1, y2-1, c);
+
+ c = ShadeColor(back_color, 1.3); // soft highlight
+ DrawLine(x1+1,y1+1, x2-2, y1+1, c);
+ DrawLine(x1+1,y1+1, x1+1, y2-2, c);
+
+ c = ShadeColor(back_color, 0.6); // soft shadow
+ DrawLine(x2-2,y1+1, x2-2,y2-1, c);
+ DrawLine(x1+1,y2-2, x2-1,y2-2, c);
+
+ c = ShadeColor(back_color, 0.3); // full shadow
+ DrawLine(x2-1,y1, x2-1,y2, c);
+ DrawLine(x1 ,y2-1, x2,y2-1, c);
+
+ DrawRect(x1+4,y1+4, x2-5,y2-5, ShadeColor(back_color, 0.6)); // soft shadow
+ DrawRect(x1+5,y1+5, x2-6,y2-6, ShadeColor(back_color, 0.3)); // full shadow
+ DrawLine(x1+5,y2-6, x2-5,y2-6, ShadeColor(back_color, 1.3)); // soft highlight (bottom)
+ DrawLine(x2-6,y1+5, x2-6,y2-6, ShadeColor(back_color, 1.3)); // soft highlight (side)
+ DrawLine(x1+4,y2-5, x2-4,y2-5, ShadeColor(back_color, 1.6)); // soft highlight (bottom)
+ DrawLine(x2-5,y1+4, x2-5,y2-5, ShadeColor(back_color, 1.6)); // soft highlight (side)
+ }
+
+ else if (style & WIN_RAISED_FRAME) {
+ Color c = ShadeColor(back_color, 1.6); // full highlight
+ DrawLine(x1, y1, x2-1, y1, c);
+ DrawLine(x1, y1, x1, y2-1, c);
+
+ c = ShadeColor(back_color, 1.3); // soft highlight
+ DrawLine(x1+1,y1+1, x2-2, y1+1, c);
+ DrawLine(x1+1,y1+1, x1+1, y2-2, c);
+
+ c = ShadeColor(back_color, 0.6); // soft shadow
+ DrawLine(x2-2,y1+1, x2-2,y2-1, c);
+ DrawLine(x1+1,y2-2, x2-1,y2-2, c);
+
+ c = ShadeColor(back_color, 0.3); // full shadow
+ DrawLine(x2-1,y1, x2-1,y2, c);
+ DrawLine(x1 ,y2-1, x2,y2-1, c);
+ }
+
+ else if (style & WIN_SUNK_FRAME) {
+ Color c = ShadeColor(back_color, 0.3); // full shadow
+ DrawLine(x1+1,y1+1, x1+1, y2, c);
+ DrawLine(x1+1,y1+1, x2, y1+1, c);
+
+ c = ShadeColor(back_color, 0.6); // soft shadow
+ DrawLine(x1, y1, x1, y2, c);
+ DrawLine(x1, y1, x2, y1, c);
+
+ c = ShadeColor(back_color, 1.3); // soft highlight
+ DrawLine(x2-2,y1+1, x2-2,y2-1, c);
+ DrawLine(x1+1,y2-2, x2-1,y2-2, c);
+
+ c = ShadeColor(back_color, 1.6); // full highlight
+ DrawLine(x2-1,y1+1, x2-1,y2, c);
+ DrawLine(x1 ,y2-1, x2,y2-1, c);
+ }
+
+ // draw frame:
+ if (style & WIN_BLACK_FRAME)
+ DrawRect(x1,y1,x2-1,y2-1, ShadeColor(Color::Black, 1.0));
+ else if (style & WIN_WHITE_FRAME)
+ DrawRect(x1,y1,x2-1,y2-1, ShadeColor(Color::White, 1.0));
+ }
+}
+
+void
+ActiveWindow::DrawTabbedText()
+{
+ if (shown && font && text.length()) {
+ Rect label_rect;
+
+ if (text_insets.left) {
+ label_rect.w = rect.w;
+ label_rect.h = rect.h;
+
+ label_rect.Inset(text_insets.left,
+ text_insets.right,
+ text_insets.top,
+ text_insets.bottom);
+ }
+ else {
+ int border_size = 4;
+
+ if (style & WIN_RAISED_FRAME && style & WIN_SUNK_FRAME)
+ border_size = 8;
+
+ label_rect.x = border_size;
+ label_rect.y = border_size;
+ label_rect.w = rect.w - border_size * 2;
+ label_rect.h = rect.h - border_size * 2;
+ }
+
+ font->SetAlpha(alpha);
+
+ // no tabs set:
+ if (tab[0] == 0) {
+ DWORD text_flags = DT_WORDBREAK | text_align;
+
+ if (single_line)
+ text_flags = text_flags | DT_SINGLELINE;
+
+ if (style & WIN_TEXT_SHADOW) {
+ label_rect.x++;
+ label_rect.y++;
+
+ if (transparent) {
+ font->SetColor(back_color);
+ DrawText(text.data(), 0, label_rect, text_flags);
+ }
+
+ else {
+ Color shadow = ShadeColor(back_color, 1.6);
+ font->SetColor(shadow);
+ DrawText(text.data(), 0, label_rect, text_flags);
+ }
+
+ label_rect.x--;
+ label_rect.y--;
+ }
+
+ Color fore = ShadeColor(fore_color, 1);
+ font->SetColor(fore);
+ DrawText(text.data(), 0, label_rect, text_flags);
+ }
+
+ // use tabs:
+ else {
+ }
+
+ font->SetAlpha(1);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::SetTabStop(int n, int x)
+{
+ if (n >= 0 && n < 10)
+ tab[n] = x;
+}
+
+int
+ActiveWindow::GetTabStop(int n) const
+{
+ if (n >= 0 && n < 10)
+ return tab[n];
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::SetText(const char* t)
+{
+ if (t && text != t) {
+ int len = strlen(t);
+
+ if (len > 0) {
+ char* buf = new(__FILE__,__LINE__) char[2*len];
+
+ if (buf != 0) {
+ const char* src = t;
+ char* dst = buf;
+
+ while (*src) {
+ if (*src != '\\') {
+ *dst++ = *src++;
+ }
+ else {
+ src++;
+
+ switch (*src) {
+ case 'n': *dst++ = '\n'; break;
+ case 't': *dst++ = '\t'; break;
+ default: *dst++ = *src; break;
+ }
+
+ src++;
+ }
+ }
+
+ *dst = 0;
+
+ if (text != buf) {
+ text = buf;
+ }
+
+ delete [] buf;
+ }
+ }
+ else {
+ text = t;
+ }
+ }
+}
+
+void
+ActiveWindow::SetText(const Text& t)
+{
+ if (t && text != t) {
+ int len = t.length();
+
+ if (len > 0 && t.contains('\\')) {
+ char* buf = new(__FILE__,__LINE__) char[2*len];
+
+ if (buf != 0) {
+ const char* src = t;
+ char* dst = buf;
+
+ while (*src) {
+ if (*src != '\\') {
+ *dst++ = *src++;
+ }
+ else {
+ src++;
+
+ switch (*src) {
+ case 'n': *dst++ = '\n'; break;
+ case 't': *dst++ = '\t'; break;
+ default: *dst++ = *src; break;
+ }
+
+ src++;
+ }
+ }
+
+ *dst = 0;
+
+ if (text != buf) {
+ text = buf;
+ }
+
+ delete [] buf;
+ }
+ }
+ else {
+ text = t;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::AddText(const char* t)
+{
+ if (t) {
+ text += t;
+ }
+}
+
+void
+ActiveWindow::AddText(const Text& t)
+{
+ if (t) {
+ text += t;
+ }
+}
+
+void
+ActiveWindow::SetTextAlign(DWORD a)
+{
+ if (a == DT_LEFT || a == DT_RIGHT || a == DT_CENTER)
+ text_align = a;
+}
+
+void
+ActiveWindow::SetMargins(const Insets& m)
+{
+ margins = m;
+ CalcGrid();
+}
+
+void
+ActiveWindow::SetTextInsets(const Insets& t)
+{
+ text_insets = t;
+}
+
+void
+ActiveWindow::SetCellInsets(const Insets& c)
+{
+ cell_insets = c;
+}
+
+void
+ActiveWindow::SetCells(int cx, int cy, int cw, int ch)
+{
+ cells.x = cx;
+ cells.y = cy;
+ cells.w = cw;
+ cells.h = ch;
+
+ if (cells.w < 1)
+ cells.w = 1;
+
+ if (cells.h < 1)
+ cells.h = 1;
+}
+
+void
+ActiveWindow::SetAlpha(double a)
+{
+ if (alpha != a) {
+ alpha = (float) a;
+
+ Color c = Color::White;
+ c.SetAlpha((BYTE) (alpha*255));
+
+ if (vset && vset->nverts) {
+ for (int i = 0; i < vset->nverts; i++) {
+ vset->diffuse[i] = c.Value();
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::DrawText(const char* txt, int count, Rect& txt_rect, DWORD flags)
+{
+ if (font) {
+ font->SetAlpha(alpha);
+ Window::DrawText(txt, count, txt_rect, flags);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::RegisterClient(int eid, ActiveWindow* client, PFVAWE callback)
+{
+ AWMap* map = new(__FILE__,__LINE__) AWMap(eid, client, callback);
+
+ if (map != 0)
+ clients.append(map);
+}
+
+void
+ActiveWindow::UnregisterClient(int eid, ActiveWindow* client)
+{
+ AWMap test(eid, client, 0);
+ int index = clients.index(&test);
+
+ if (index >= 0)
+ delete clients.removeIndex(index);
+}
+
+void
+ActiveWindow::ClientEvent(int eid, int x, int y)
+{
+ event.window = this;
+ event.eid = eid;
+ event.x = x;
+ event.y = y;
+
+ ListIter<AWMap> map = clients;
+ while (++map) {
+ if (map->eid == eid)
+ map->func(map->client, &event);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int ActiveWindow::OnMouseEnter(int x, int y)
+{
+ ClientEvent(EID_MOUSE_ENTER, x, y);
+ return 0;
+}
+
+int ActiveWindow::OnMouseExit(int x, int y)
+{
+ ClientEvent(EID_MOUSE_EXIT, x, y);
+ return 0;
+}
+
+int ActiveWindow::OnMouseMove(int x, int y)
+{
+ ClientEvent(EID_MOUSE_MOVE, x, y);
+ return 0;
+}
+
+int ActiveWindow::OnMouseWheel(int wheel)
+{
+ ClientEvent(EID_MOUSE_WHEEL, wheel, 0);
+ return 0;
+}
+
+int ActiveWindow::OnLButtonDown(int x, int y)
+{
+ ClientEvent(EID_LBUTTON_DOWN, x, y);
+ return 0;
+}
+
+int ActiveWindow::OnLButtonUp(int x, int y)
+{
+ ClientEvent(EID_LBUTTON_UP, x, y);
+ return 0;
+}
+
+int ActiveWindow::OnClick()
+{
+ ClientEvent(EID_CLICK);
+ return 0;
+}
+
+int ActiveWindow::OnSelect()
+{
+ ClientEvent(EID_SELECT);
+ return 0;
+}
+
+int ActiveWindow::OnRButtonDown(int x, int y)
+{
+ ClientEvent(EID_RBUTTON_DOWN, x, y);
+ return 0;
+}
+
+int ActiveWindow::OnRButtonUp(int x, int y)
+{
+ ClientEvent(EID_RBUTTON_UP, x, y);
+ return 0;
+}
+
+int ActiveWindow::OnKeyDown(int vk, int flags)
+{
+ ClientEvent(EID_KEY_DOWN, vk, flags);
+ return 0;
+}
+
+int ActiveWindow::OnDragStart(int x, int y)
+{
+ ClientEvent(EID_DRAG_START, x, y);
+ return 0;
+}
+
+int ActiveWindow::OnDragDrop(int x, int y, ActiveWindow* source)
+{
+ ClientEvent(EID_DRAG_DROP, x, y);
+ return 0;
+}
+
+Rect ActiveWindow::TargetRect() const
+{
+ return rect;
+}
+
+// +--------------------------------------------------------------------+
+
+void ActiveWindow::SetFocus()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch)
+ dispatch->SetFocus(this);
+
+ focus = true;
+ ClientEvent(EID_SET_FOCUS);
+}
+
+void ActiveWindow::KillFocus()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch)
+ dispatch->KillFocus(this);
+
+ focus = false;
+ ClientEvent(EID_KILL_FOCUS);
+}
+
+// +--------------------------------------------------------------------+
+
+ActiveWindow*
+ActiveWindow::GetCapture()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch)
+ return (ActiveWindow*) dispatch->GetCapture();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+ActiveWindow::SetCapture()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch)
+ return dispatch->CaptureMouse(this);
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+ActiveWindow::ReleaseCapture()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch)
+ return dispatch->ReleaseMouse(this);
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+ActiveWindow::IsFormActive() const
+{
+ if (form)
+ return form->IsTopMost();
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+ActiveWindow*
+ActiveWindow::FindChild(DWORD id)
+{
+ ListIter<ActiveWindow> iter = children;
+ while (++iter) {
+ ActiveWindow* w = iter.value();
+ if (w->GetID() == id)
+ return w;
+
+ ActiveWindow* w2 = w->FindChild(id);
+
+ if (w2)
+ return w2;
+ }
+
+ return 0;
+}
+
+
+// +--------------------------------------------------------------------+
+
+ActiveWindow*
+ActiveWindow::FindChild(int x, int y)
+{
+ ActiveWindow* mouse_tgt = 0;
+
+ ListIter<ActiveWindow> iter = children;
+ while (++iter) {
+ ActiveWindow* test = iter.value();
+ if (test->TargetRect().Contains(x,y))
+ mouse_tgt = test;
+ }
+
+ return mouse_tgt;
+}
diff --git a/nGenEx/ActiveWindow.h b/nGenEx/ActiveWindow.h
new file mode 100644
index 0000000..50707fb
--- /dev/null
+++ b/nGenEx/ActiveWindow.h
@@ -0,0 +1,325 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ActiveWindow.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Active Window class (a window that knows how to draw itself)
+*/
+
+#ifndef ActiveWindow_h
+#define ActiveWindow_h
+
+#include "Types.h"
+#include "Color.h"
+#include "Geometry.h"
+#include "Bitmap.h"
+#include "Window.h"
+#include "EventTarget.h"
+#include "ArrayList.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+struct Poly;
+struct Material;
+struct VertexSet;
+class Layout;
+
+// +--------------------------------------------------------------------+
+
+enum {
+ WIN_NO_FRAME = 0x0000,
+ WIN_BLACK_FRAME = 0x0001,
+ WIN_WHITE_FRAME = 0x0002,
+ WIN_THIN_FRAME = 0x0004,
+ WIN_THICK_FRAME = 0x0008,
+ WIN_RAISED_FRAME = 0x0010,
+ WIN_SUNK_FRAME = 0x0020,
+ WIN_TEXT_SHADOW = 0x0040,
+ WIN_FRAME_ONLY = 0x0080
+};
+
+enum {
+ EID_CREATE,
+ EID_DESTROY,
+ EID_MOUSE_MOVE,
+ EID_CLICK,
+ EID_SELECT,
+ EID_LBUTTON_DOWN,
+ EID_LBUTTON_UP,
+ EID_RBUTTON_DOWN,
+ EID_RBUTTON_UP,
+ EID_KEY_DOWN,
+ EID_SET_FOCUS,
+ EID_KILL_FOCUS,
+ EID_MOUSE_ENTER,
+ EID_MOUSE_EXIT,
+ EID_MOUSE_WHEEL,
+ EID_DRAG_START,
+ EID_DRAG_DROP,
+
+ EID_USER_1,
+ EID_USER_2,
+ EID_USER_3,
+ EID_USER_4,
+
+ EID_NUM_EVENTS
+};
+
+// +--------------------------------------------------------------------+
+
+class ActiveWindow;
+
+struct AWEvent
+{
+ static const char* TYPENAME() { return "AWEvent"; }
+
+ AWEvent() : window(0), eid(0), x(0), y(0) { }
+ AWEvent(ActiveWindow* w, int e, int ax=0, int ay=0) : window(w), eid(e), x(ax), y(ay) { }
+
+ int operator == (const AWEvent& e) const { return (window == e.window) &&
+ (eid == e.eid) &&
+ (x == e.x) &&
+ (y == e.y); }
+
+ ActiveWindow* window;
+ int eid;
+ int x;
+ int y;
+};
+
+typedef void (*PFVAWE)(ActiveWindow*, AWEvent*);
+
+struct AWMap
+{
+ static const char* TYPENAME() { return "AWMap"; }
+
+ AWMap() : eid(0), client(0), func(0) { }
+ AWMap(int e, ActiveWindow* w, PFVAWE f) : eid(e), client(w), func(f) { }
+
+ int operator == (const AWMap& m) const { return (eid == m.eid) &&
+ (client == m.client); }
+
+ int eid;
+ ActiveWindow* client;
+ PFVAWE func;
+};
+
+// +--------------------------------------------------------------------+
+
+class ActiveWindow : public Window,
+ public EventTarget
+{
+public:
+ static const char* TYPENAME() { return "ActiveWindow"; }
+
+ ActiveWindow(Screen* s, int ax, int ay, int aw, int ah,
+ DWORD id=0, DWORD style=0, ActiveWindow* parent=0);
+ virtual ~ActiveWindow();
+
+ int operator == (const ActiveWindow& w) const { return id == w.id; }
+
+ // Operations:
+ virtual void Paint(); // blt to screen
+ virtual void Draw(); // refresh backing store
+ virtual void Show();
+ virtual void Hide();
+ virtual void MoveTo(const Rect& r);
+ virtual void UseLayout(const ArrayList& min_x,
+ const ArrayList& min_y,
+ const FloatList& weight_x,
+ const FloatList& weight_y);
+ virtual void UseLayout(const FloatList& min_x,
+ const FloatList& min_y,
+ const FloatList& weight_x,
+ const FloatList& weight_y);
+ virtual void UseLayout(int ncols,
+ int nrows,
+ int* min_x,
+ int* min_y,
+ float* weight_x,
+ float* weight_y);
+ virtual void DoLayout();
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnClick();
+ virtual int OnSelect();
+ virtual int OnRButtonDown(int x, int y);
+ virtual int OnRButtonUp(int x, int y);
+ virtual int OnMouseEnter(int x, int y);
+ virtual int OnMouseExit(int x, int y);
+ virtual int OnMouseWheel(int wheel);
+
+ virtual int OnKeyDown(int vk, int flags);
+
+ virtual const char* GetDescription() const { return desc; }
+
+ // pseudo-events:
+ virtual int OnDragStart(int x, int y);
+ virtual int OnDragDrop(int x, int y, ActiveWindow* source);
+
+ virtual ActiveWindow* FindControl(int x, int y) { return 0; }
+ virtual Rect TargetRect() const;
+
+ virtual ActiveWindow* GetCapture();
+ virtual int SetCapture();
+ virtual int ReleaseCapture();
+
+ // Property accessors:
+ virtual void SetFocus();
+ virtual void KillFocus();
+ virtual bool HasFocus() const { return focus; }
+
+ void SetEnabled(bool e=true) { enabled = e; }
+ bool IsEnabled() const { return enabled; }
+ bool IsVisible() const { return shown; }
+
+ DWORD GetID() const { return id; }
+ void SetStyle(DWORD s) { style = s; }
+ DWORD GetStyle() const { return style; }
+
+ void SetText(const char* t);
+ void SetText(const Text& t);
+ void AddText(const char* t);
+ void AddText(const Text& t);
+ const Text& GetText() const { return text; }
+
+ void SetAltText(const char* t) { alt_text = t; }
+ void SetAltText(const Text& t) { alt_text = t; }
+ const Text& GetAltText() const { return alt_text; }
+
+ void SetTexture(Bitmap* bmp) { texture = bmp; }
+ Bitmap* GetTexture() { return texture; }
+ void SetMargins(const Insets& m);
+ Insets& GetMargins() { return margins; }
+ void SetTextInsets(const Insets& t);
+ Insets& GetTextInsets() { return text_insets; }
+
+ List<ActiveWindow>& GetChildren() { return children; }
+ void SetCellInsets(const Insets& c);
+ Insets& GetCellInsets() { return cell_insets; }
+ void SetCells(int cx, int cy, int cw=1, int ch=1);
+ void SetCells(const Rect& r) { cells = r; }
+ Rect& GetCells() { return cells; }
+ void SetFixedWidth(int w) { fixed_width = w; }
+ int GetFixedWidth() const { return fixed_width; }
+ void SetFixedHeight(int h) { fixed_height = h; }
+ int GetFixedHeight() const { return fixed_height;}
+
+ void SetAlpha(double a);
+ double GetAlpha() const { return alpha; }
+ void SetBackColor(Color c) { back_color = c; }
+ Color GetBackColor() const { return back_color; }
+ void SetBaseColor(Color c) { base_color = c; }
+ Color GetBaseColor() const { return base_color; }
+ void SetForeColor(Color c) { fore_color = c; }
+ Color GetForeColor() const { return fore_color; }
+ void SetSingleLine(bool a) { single_line = a; }
+ bool GetSingleLine() const { return single_line; }
+ void SetTextAlign(DWORD a);
+ DWORD GetTextAlign() const { return text_align; }
+ void SetTransparent(bool t) { transparent = t; }
+ bool GetTransparent() const { return transparent; }
+ void SetHidePartial(bool a) { hide_partial = a; }
+ bool GetHidePartial() const { return hide_partial;}
+
+ void SetTabStop(int n, int x);
+ int GetTabStop(int n) const;
+
+ void DrawText(const char* txt, int count, Rect& txt_rect, DWORD flags);
+
+ // class properties:
+ static void SetSystemFont(Font* f);
+ static void SetSystemBackColor(Color c);
+ static void SetSystemForeColor(Color c);
+
+ // callback function registration:
+ virtual void RegisterClient(int EID, ActiveWindow* client, PFVAWE callback);
+ virtual void UnregisterClient(int EID, ActiveWindow* client);
+ virtual void ClientEvent(int EID, int x=0, int y=0);
+
+ // form context:
+ virtual ActiveWindow* GetForm() { return form; }
+ virtual void SetForm(ActiveWindow* f) { form = f; }
+ virtual bool IsFormActive() const;
+ virtual bool IsTopMost() const { return topmost; }
+ virtual void SetTopMost(bool t) { topmost = t; }
+
+ virtual ActiveWindow* FindChild(DWORD id);
+ virtual ActiveWindow* FindChild(int x, int y);
+
+protected:
+ virtual Color ShadeColor(Color c, double shade);
+ virtual void AddChild(ActiveWindow* child);
+ virtual void DrawStyleRect(const Rect& r, int style);
+ virtual void DrawStyleRect(int x1, int y1, int x2, int y2, int style);
+ virtual void DrawTabbedText();
+ virtual void DrawTextureGrid();
+ virtual void CalcGrid();
+
+ DWORD id;
+ DWORD style;
+ DWORD text_align;
+ bool single_line;
+ bool focus;
+ bool enabled;
+ bool hide_partial;
+ float alpha;
+ Color back_color;
+ Color base_color;
+ Color fore_color;
+ Text text;
+ Text alt_text;
+ Text desc;
+ Bitmap* texture;
+ Insets margins;
+ Insets text_insets;
+ Insets cell_insets;
+ Rect cells;
+ int fixed_width;
+ int fixed_height;
+ int tab[10];
+
+ ActiveWindow* parent;
+ ActiveWindow* form;
+ bool transparent;
+ bool topmost;
+
+ Layout* layout;
+ List<ActiveWindow> children;
+ List<AWMap> clients;
+ AWEvent event;
+
+ int rows;
+ int cols;
+ Poly* polys;
+ VertexSet* vset;
+ Material* mtl;
+
+ static Font* sys_font;
+ static Color sys_back_color;
+ static Color sys_fore_color;
+};
+
+#define DEF_MAP_CLIENT(cname, fname)\
+ void Map##cname##fname(ActiveWindow* client, AWEvent* event) \
+ { cname* c = (cname*) client; c->fname(event); }
+
+#define REGISTER_CLIENT(eid, ctrl, cname, fname)\
+ if (ctrl) ctrl->RegisterClient(eid, this, Map##cname##fname);
+
+#define UNREGISTER_CLIENT(eid, ctrl, cname)\
+ if (ctrl) ctrl->UnregisterClient(eid, this);
+
+#endif ActiveWindow_h
+
diff --git a/nGenEx/Archive.cpp b/nGenEx/Archive.cpp
new file mode 100644
index 0000000..dfec332
--- /dev/null
+++ b/nGenEx/Archive.cpp
@@ -0,0 +1,587 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Archive.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include "MemDebug.h"
+#include "Types.h"
+#include "Archive.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <io.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "zlib.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+int verbose = 1;
+int err;
+
+#define CHECK_ERR(err, msg) { \
+ if (err != Z_OK) { \
+ fprintf(stderr, "%s error: %d\n", msg, err); \
+ exit(1); \
+ } \
+}
+
+// +--------------------------------------------------------------------+
+
+DataArchive::DataArchive(const char* name)
+{
+ ZeroMemory(this, sizeof(DataArchive));
+
+ if (name)
+ LoadDatafile(name);
+}
+
+DataArchive::~DataArchive()
+{
+ delete [] block_map;
+ delete [] directory;
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::WriteEntry(int index, BYTE* buf)
+{
+ int f = _open(datafile, _O_RDWR|_O_CREAT|_O_BINARY, _S_IREAD|_S_IWRITE);
+
+ if (f != -1) {
+ header.dir_size_comp = DirBlocks() * BLOCK_SIZE;
+ dirbuf = new(__FILE__,__LINE__) BYTE[header.dir_size_comp];
+
+ if (!dirbuf) {
+ err = Z_MEM_ERROR;
+ }
+
+ else {
+ err = compress(dirbuf, &header.dir_size_comp,
+ (BYTE*) directory, header.nfiles * sizeof(DataEntry));
+ CHECK_ERR(err, "compress");
+
+ header.dir_blocks = Blocks(header.dir_size_comp) * BLOCK_SIZE;
+
+ _lseek(f, 0, SEEK_SET);
+ _write(f, &header, sizeof(DataHeader));
+ _lseek(f, sizeof(DataHeader) + header.dir_offset, SEEK_SET);
+ _write(f, dirbuf, header.dir_blocks);
+
+ delete [] dirbuf;
+ dirbuf = 0;
+ }
+
+ if (buf && directory && directory[index].size_comp) {
+ _lseek(f, sizeof(DataHeader) + directory[index].offset, SEEK_SET);
+ _write(f, buf, directory[index].size_comp);
+ }
+ _close(f);
+ }
+ else
+ perror("WriteEntry");
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD DataArchive::Blocks(DWORD raw_size)
+{
+ int full_blocks = raw_size / BLOCK_SIZE;
+ int part_blocks = (raw_size % BLOCK_SIZE) > 0;
+
+ return full_blocks + part_blocks;
+}
+
+DWORD DataArchive::DirBlocks()
+{
+ DWORD result = Blocks(header.nfiles * sizeof(DataEntry));
+ if (result == 0) result = 1;
+ return result;
+}
+
+DWORD DataArchive::FileBlocks(int index)
+{
+ if (index >= 0 && index < (int) header.nfiles && directory)
+ return Blocks(directory[index].size_comp);
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::CreateBlockMap()
+{
+ delete [] block_map;
+ block_map = 0;
+
+ if (header.nfiles == 0) return;
+
+ DWORD i,j;
+ DWORD dir_usage = header.dir_offset + DirBlocks() * BLOCK_SIZE;
+ DWORD max_usage = dir_usage;
+
+ for (i = 0; i < header.nfiles; i++) {
+ DWORD last_block = directory[i].offset + FileBlocks(i) * BLOCK_SIZE;
+ if (last_block > max_usage)
+ max_usage = last_block;
+ }
+
+ nblocks = max_usage/BLOCK_SIZE;
+ block_map = new(__FILE__,__LINE__) DWORD[nblocks];
+
+ if (!block_map) {
+ nblocks = 0;
+ }
+
+ else {
+ ZeroMemory(block_map, nblocks*sizeof(DWORD));
+
+ DWORD first_block = header.dir_offset/BLOCK_SIZE +
+ (header.dir_offset%BLOCK_SIZE > 0);
+
+ for (j = 0; j < DirBlocks(); j++)
+ block_map[first_block+j] = 1;
+
+ for (i = 0; i < header.nfiles; i++) {
+ DWORD first_block = directory[i].offset/BLOCK_SIZE +
+ (directory[i].offset%BLOCK_SIZE > 0);
+
+ for (j = 0; j < FileBlocks(i); j++)
+ block_map[first_block+j] = i+2;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int DataArchive::FindDataBlocks(int need)
+{
+ if ((int) (nblocks)-need > 0) {
+ DWORD start;
+ int i;
+
+ for (start = 0; start < nblocks-need; start++) {
+ for (i = 0; block_map[start+i] == 0 && i < need; i++);
+
+ if (i == need) return start*BLOCK_SIZE;
+
+ start += i;
+ }
+ }
+
+ return nblocks*BLOCK_SIZE;
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::LoadDatafile(const char* name)
+{
+ if (!name) return;
+
+ delete [] directory;
+ delete [] block_map;
+
+ ZeroMemory(this, sizeof(DataArchive));
+ strncpy(datafile, name, NAMELEN-1);
+
+ FILE* f = fopen(datafile, "rb");
+ if (f) {
+ fread(&header, sizeof(DataHeader), 1, f);
+
+ if (header.version != VERSION) {
+ Print("ERROR: datafile '%s' invalid version '%d'\n",
+ datafile, header.version);
+ fclose(f);
+ ZeroMemory(&header, sizeof(header));
+ return;
+ }
+
+ DWORD len = DirBlocks() * BLOCK_SIZE;
+ DWORD dirsize = header.nfiles + 64;
+
+ dirbuf = new(__FILE__,__LINE__) BYTE[len];
+ directory = new(__FILE__,__LINE__) DataEntry[dirsize];
+
+ if (!dirbuf || !directory) {
+ err = Z_MEM_ERROR;
+ }
+
+ else {
+ ZeroMemory(directory, sizeof(DataEntry) * dirsize);
+
+ fseek(f, sizeof(DataHeader) + header.dir_offset, SEEK_SET);
+ fread(dirbuf, header.dir_size_comp, 1, f);
+
+ int err = uncompress((BYTE*) directory, &len,
+ dirbuf, header.dir_size_comp);
+ if (err != Z_OK)
+ ZeroMemory(directory, sizeof(DataEntry) * dirsize);
+
+ delete [] dirbuf;
+ dirbuf = 0;
+
+ CreateBlockMap();
+ }
+ }
+ else {
+ Print("Creating Archive '%s'...\n", datafile);
+
+ header.version = VERSION;
+ header.nfiles = 0;
+ header.dir_blocks = 0;
+ header.dir_size_comp = 0;
+ header.dir_offset = 0;
+
+ nblocks = DirBlocks();
+
+ delete [] block_map;
+ block_map = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int DataArchive::FindEntry(const char* req_name)
+{
+ int entry = -1;
+
+ if (req_name && *req_name && directory) {
+ char path[256];
+ int len = strlen(req_name);
+
+ ZeroMemory(path, sizeof(path));
+
+ for (int c = 0; c < len; c++) {
+ if (req_name[c] == '\\')
+ path[c] = '/';
+ else
+ path[c] = req_name[c];
+ }
+
+ for (DWORD i = 0; i < header.nfiles; i++) {
+ if (!stricmp(directory[i].name, path))
+ return i;
+ }
+ }
+
+ return entry;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE* DataArchive::CompressEntry(int i)
+{
+ if (directory && i >= 0 && i < (int) header.nfiles) {
+ char* name = directory[i].name;
+
+ FILE* f = fopen(name, "rb");
+
+ if (f) {
+ fseek(f, 0, SEEK_END);
+ DWORD len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ BYTE* buf = new(__FILE__,__LINE__) BYTE[len];
+
+ if (!buf) {
+ err = Z_MEM_ERROR;
+ }
+
+ else {
+ fread(buf, len, 1, f);
+ fclose(f);
+
+ directory[i].size_orig = len;
+
+ directory[i].size_comp = (int) (len * 1.1);
+ BYTE* cbuf = new(__FILE__,__LINE__) BYTE[directory[i].size_comp];
+
+ if (!cbuf) {
+ err = Z_MEM_ERROR;
+ }
+ else {
+ err = compress(cbuf, &directory[i].size_comp, buf, len);
+ CHECK_ERR(err, "compress");
+ }
+
+ delete [] buf;
+ return cbuf;
+ }
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int DataArchive::ExpandEntry(int i, BYTE*& buf, bool null_terminate)
+{
+ DWORD len = 0;
+
+ if (directory && i >= 0 && i < (int) header.nfiles) {
+ FILE* f = fopen(datafile, "rb");
+
+ if (f) {
+ DWORD clen = directory[i].size_comp;
+ BYTE* cbuf = new(__FILE__,__LINE__) BYTE[clen];
+
+ if (!cbuf) {
+ err = Z_MEM_ERROR;
+ }
+
+ else {
+ fseek(f, sizeof(DataHeader) + directory[i].offset, SEEK_SET);
+ fread(cbuf, clen, 1, f);
+
+ len = directory[i].size_orig;
+
+ if (null_terminate) {
+ buf = new(__FILE__,__LINE__) BYTE[len+1];
+ if (buf) buf[len] = 0;
+ }
+
+ else {
+ buf = new(__FILE__,__LINE__) BYTE[len];
+ }
+
+ if (!buf) {
+ err = Z_MEM_ERROR;
+ }
+
+ else {
+ err = uncompress(buf, &len, cbuf, clen);
+ if (err != Z_OK) {
+ delete [] buf;
+ buf = 0;
+ }
+ }
+
+ delete [] cbuf;
+ fclose(f);
+ }
+ }
+ }
+
+ return len;
+}
+
+// +--------------------------------------------------------------------+
+
+int DataArchive::InsertEntry(const char* name)
+{
+ if (name && *name) {
+ char path[256];
+ DWORD len = strlen(name);
+
+ ZeroMemory(path, sizeof(path));
+
+ for (DWORD c = 0; c < len; c++) {
+ if (name[c] == '\\')
+ path[c] = '/';
+ else
+ path[c] = name[c];
+ }
+
+ int dirsize = header.nfiles + 64;
+
+ if (directory && dirsize) {
+ for (int i = 0; i < dirsize; i++) {
+ if (directory[i].size_orig == 0) {
+ ZeroMemory(directory[i].name, NAMELEN);
+ strncpy(directory[i].name, path, NAMELEN);
+ directory[i].name[NAMELEN-1] = '\0';
+ directory[i].size_orig = 1;
+
+ return i;
+ }
+ }
+ }
+
+ DataEntry* dir = new(__FILE__,__LINE__) DataEntry[dirsize + 64];
+
+ if (directory && dirsize) {
+ ZeroMemory(dir, (dirsize + 64) * sizeof(DataEntry));
+ CopyMemory(dir, directory, dirsize * sizeof(DataEntry));
+ }
+
+ delete [] directory;
+
+ header.nfiles = dirsize + 64;
+ directory = dir;
+
+ ZeroMemory(directory[dirsize].name, NAMELEN);
+ strncpy(directory[dirsize].name, path, NAMELEN);
+ directory[dirsize].name[NAMELEN-1] = '\0';
+ directory[dirsize].size_orig = 1;
+
+ return dirsize;
+ }
+
+ return -1;
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::RemoveEntry(int index)
+{
+ if (directory && index >= 0 && index < (int) header.nfiles)
+ ZeroMemory(&directory[index], sizeof(DataEntry));
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::Insert(const char* name)
+{
+ DWORD old_blocks = 0, old_offset = 0, new_blocks = 0;
+ DWORD old_dir_blocks = 0, old_dir_offset = 0, new_dir_blocks = 0;
+ int added = 0;
+
+ int index = FindEntry(name);
+
+ if (index < 0) {
+ old_dir_blocks = DirBlocks();
+ old_dir_offset = header.dir_offset;
+
+ index = InsertEntry(name);
+
+ if (index >= (int) header.nfiles) {
+ header.nfiles = index+1;
+ added = 1;
+ }
+
+ new_dir_blocks = DirBlocks();
+
+ if (new_dir_blocks > old_dir_blocks) {
+ header.dir_offset = FindDataBlocks(new_dir_blocks);
+ CreateBlockMap();
+ }
+ }
+ else {
+ old_blocks = FileBlocks(index);
+ old_offset = directory[index].offset;
+ }
+
+ if (index >= 0) {
+ DataEntry& e = directory[index];
+
+ if (verbose) Print(" Inserting: %-16s ", e.name);
+
+ BYTE* buf = CompressEntry(index);
+
+ if (!buf) {
+ // this is (almost) unrecoverable,
+ // so we quit before screwing things up:
+ Print("ERROR: Could not compress %d:%s\n", index, directory[index].name);
+ exit(1);
+ }
+
+ new_blocks = FileBlocks(index);
+
+ // the file is new, or got bigger,
+ // need to find room for the data:
+ if (new_blocks > old_blocks) {
+ directory[index].offset = FindDataBlocks(new_blocks);
+ CreateBlockMap();
+ }
+
+ WriteEntry(index, buf);
+ delete [] buf;
+
+ if (verbose) {
+ int ratio = (int) (100.0 * (double) e.size_comp / (double) e.size_orig);
+ Print("%9d => %9d (%2d%%)\n", e.size_orig, e.size_comp, ratio);
+ }
+ }
+ else if (added)
+ header.nfiles--;
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::Extract(const char* name)
+{
+ int index = FindEntry(name);
+
+ if (!directory || index < 0 || index >= (int) header.nfiles) {
+ Print("Could not extract '%s', not found\n", name);
+ return;
+ }
+
+ BYTE* buf;
+ ExpandEntry(index, buf);
+
+ FILE* f = fopen(directory[index].name, "wb");
+ if (f) {
+ fwrite(buf, directory[index].size_orig, 1, f);
+ fclose(f);
+ }
+ else
+ Print("Could not extract '%s', could not open file for writing\n", name);
+
+ delete [] buf;
+
+ if (verbose) Print(" Extracted: %s\n", name);
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::Remove(const char* name)
+{
+ int index = FindEntry(name);
+
+ if (!directory || index < 0 || index >= (int) header.nfiles) {
+ Print("Could not remove '%s', not found\n", name);
+ return;
+ }
+
+ RemoveEntry(index);
+ WriteEntry(index, 0);
+
+ if (verbose) Print(" Removed: %s\n", name);
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::List()
+{
+ int total_orig = 0;
+ int total_comp = 0;
+
+ printf("DATAFILE: %s\n", datafile);
+ printf("Files: %d\n", header.nfiles);
+ printf("\n");
+
+ if (directory && header.nfiles) {
+ printf("Index Orig Size Comp Size Ratio Name\n");
+ printf("----- --------- --------- ----- ----------------\n");
+
+ for (DWORD i = 0; i < header.nfiles; i++) {
+ DataEntry& e = directory[i];
+ int ratio = (int) (100.0 * (double) e.size_comp / (double) e.size_orig);
+
+ printf("%5d %9d %9d %2d%% %s\n", i+1, e.size_orig, e.size_comp, ratio, e.name);
+
+ total_orig += e.size_orig;
+ total_comp += e.size_comp;
+ }
+
+ int total_ratio = (int) (100.0 * (double) total_comp / (double) total_orig);
+
+ printf("----- --------- --------- -----\n");
+ printf("TOTAL %9d %9d %2d%%\n\n", total_orig, total_comp, total_ratio);
+ }
+}
+
+
+// +--------------------------------------------------------------------+
+
diff --git a/nGenEx/Archive.h b/nGenEx/Archive.h
new file mode 100644
index 0000000..ad4bd5e
--- /dev/null
+++ b/nGenEx/Archive.h
@@ -0,0 +1,90 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Archive.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef Archive_h
+#define Archive_h
+
+// +------------------------------------------------------------------+
+
+#define VERSION 0x0010
+#define BLOCK_SIZE 1024
+#define FILE_BLOCK 1024
+#define NAMELEN 64
+
+// +------------------------------------------------------------------+
+
+struct DataHeader
+{
+ static const char* TYPENAME() { return "DataHeader"; }
+
+ DWORD version;
+ DWORD nfiles;
+ DWORD dir_blocks;
+ DWORD dir_size_comp;
+ DWORD dir_offset;
+};
+
+struct DataEntry
+{
+ static const char* TYPENAME() { return "DataEntry"; }
+
+ char name[NAMELEN];
+ DWORD size_orig;
+ DWORD size_comp;
+ DWORD offset;
+};
+
+class DataArchive
+{
+public:
+ static const char* TYPENAME() { return "DataArchive"; }
+
+ // ctor:
+ DataArchive(const char* name = 0);
+ ~DataArchive();
+
+ // operations:
+ void LoadDatafile(const char* name);
+ void Insert(const char* name);
+ void Extract(const char* name);
+ void Remove(const char* name);
+ void List();
+
+ void WriteEntry(int index, BYTE* buf);
+ int FindEntry(const char* req_name);
+ int ExpandEntry(int index, BYTE*& buf, bool null_terminate=false);
+ BYTE* CompressEntry(int index);
+ int InsertEntry(const char* name);
+ void RemoveEntry(int index);
+ DWORD Blocks(DWORD raw_size);
+ DWORD DirBlocks();
+ DWORD FileBlocks(int index);
+ int FindDataBlocks(int blocks_needed);
+ void CreateBlockMap();
+
+ DWORD NumFiles() { return header.nfiles; }
+ DataEntry* GetFile(int i) { if (i>=0 && i<(int)header.nfiles) return &directory[i]; return 0; }
+
+ const char* Name() const { return datafile; }
+
+private:
+ // persistent data members:
+ DataHeader header;
+ DataEntry* directory;
+ BYTE* dirbuf;
+
+ // transient members:
+ char datafile[NAMELEN];
+
+ DWORD* block_map;
+ DWORD nblocks;
+};
+
+#endif Archive_h
diff --git a/nGenEx/AviFile.cpp b/nGenEx/AviFile.cpp
new file mode 100644
index 0000000..983de0c
--- /dev/null
+++ b/nGenEx/AviFile.cpp
@@ -0,0 +1,178 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: AviFile.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ AviFile reader / writer
+*/
+
+
+#include "MemDebug.h"
+#include "AviFile.h"
+
+#include <vfw.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+AviFile::AviFile(const char* fname)
+ : filename(fname), fps(0), play(true), pfile(0), ps(0), ps_comp(0),
+ nframe(0), nsamp(0), iserr(false), frame_size(0)
+{
+ AVIFileInit();
+}
+
+AviFile::AviFile(const char* fname, const Rect& r, int frame_rate)
+ : filename(fname), rect(r), fps(frame_rate), play(false), pfile(0),
+ ps(0), ps_comp(0), nframe(0), nsamp(0), iserr(false)
+{
+ Print("\n\nAviFile(%s, w=%d, h=%d, f=%d FPS)\n", fname, r.w, r.h, fps);
+ frame_size = r.w * r.h * 3;
+
+ AVIFileInit();
+ HRESULT hr = AVIFileOpen(&pfile, fname, OF_WRITE|OF_CREATE, 0);
+
+ if (hr != AVIERR_OK) {
+ Print("AviFile - open failed. %08x\n", hr);
+ iserr = true;
+ return;
+ }
+
+ Print("AviFile - open successful\n");
+
+ AVISTREAMINFO strhdr;
+ ZeroMemory(&strhdr,sizeof(strhdr));
+
+ strhdr.fccType = streamtypeVIDEO;
+ strhdr.fccHandler = 0;
+ strhdr.dwScale = 1000 / fps;
+ strhdr.dwRate = 1000;
+ strhdr.dwSuggestedBufferSize = frame_size;
+
+ SetRect(&strhdr.rcFrame, 0, 0, r.w, r.h);
+
+ hr = AVIFileCreateStream(pfile, &ps, &strhdr);
+
+ if (hr != AVIERR_OK) {
+ Print("AviFile - create stream failed. %08x\n", hr);
+ iserr = true;
+ return;
+ }
+
+ Print("AviFile - create stream successful\n");
+
+ AVICOMPRESSOPTIONS opts;
+ ZeroMemory(&opts,sizeof(opts));
+ opts.fccType = streamtypeVIDEO;
+ //opts.fccHandler = mmioFOURCC('W','M','V','3');
+ opts.fccHandler = mmioFOURCC('D','I','B',' '); // (full frames)
+ opts.dwFlags = 8;
+
+ hr = AVIMakeCompressedStream(&ps_comp, ps, &opts, 0);
+ if (hr != AVIERR_OK) {
+ Print("AviFile - compressed stream failed. %08x\n", hr);
+ iserr=true;
+ return;
+ }
+
+ Print("AviFile - make compressed stream successful\n");
+
+ BITMAPINFOHEADER bmih;
+ ZeroMemory(&bmih, sizeof(BITMAPINFOHEADER));
+
+ bmih.biSize = sizeof(BITMAPINFOHEADER);
+ bmih.biBitCount = 24;
+ bmih.biCompression = BI_RGB;
+ bmih.biWidth = rect.w;
+ bmih.biHeight = rect.h;
+ bmih.biPlanes = 1;
+ bmih.biSizeImage = frame_size;
+
+ hr = AVIStreamSetFormat(ps_comp, 0, &bmih, sizeof(BITMAPINFOHEADER));
+
+ if (hr != AVIERR_OK) {
+ Print("AviFile - stream format failed. %08x\n", hr);
+ iserr=true;
+ return;
+ }
+
+ Print("AviFile - stream format successful\n");
+}
+
+AviFile::~AviFile()
+{
+ if (!play) {
+ Print("*** Closing AVI file '%s' with %d frames\n", (const char*) filename, nframe);
+ }
+
+ if (ps_comp) AVIStreamRelease(ps_comp);
+ if (ps) AVIStreamRelease(ps);
+ if (pfile) AVIFileRelease(pfile);
+
+ AVIFileExit();
+}
+
+// +--------------------------------------------------------------------+
+//
+// Note that AVI frames use DIB format - Y is inverted.
+// So we need to flip the native bmp before sending to the
+// file.
+
+HRESULT
+AviFile::AddFrame(const Bitmap& bmp)
+{
+ HRESULT hr = E_FAIL;
+
+ if (!iserr && !play && bmp.IsHighColor() && bmp.Width() == rect.w && bmp.Height() == rect.h) {
+ int w = bmp.Width();
+ int h = bmp.Height();
+ BYTE* buffer = new(__FILE__,__LINE__) BYTE[frame_size];
+ BYTE* dst = buffer;
+
+ for (int y = 0; y < bmp.Height(); y++) {
+ Color* src = bmp.HiPixels() + (h - 1 - y) * w;
+
+ for (int x = 0; x < bmp.Width(); x++) {
+ *dst++ = (BYTE) src->Blue();
+ *dst++ = (BYTE) src->Green();
+ *dst++ = (BYTE) src->Red();
+ src++;
+ }
+ }
+
+ hr = AVIStreamWrite(ps_comp, nframe, 1, buffer, frame_size, AVIIF_KEYFRAME, 0, 0);
+
+ if (SUCCEEDED(hr)) {
+ nframe++;
+ }
+ else {
+ Print("AVIStreamWriteFile failed. %08x\n", hr);
+ iserr = true;
+ }
+
+ delete [] buffer;
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+AviFile::GetFrame(double seconds, Bitmap& bmp)
+{
+ HRESULT hr = E_FAIL;
+ return hr;
+}
+
diff --git a/nGenEx/AviFile.h b/nGenEx/AviFile.h
new file mode 100644
index 0000000..495693a
--- /dev/null
+++ b/nGenEx/AviFile.h
@@ -0,0 +1,63 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: AviFile.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ PCX image file loader
+*/
+
+#ifndef AviFile_h
+#define AviFile_h
+
+#include "Text.h"
+#include "Color.h"
+#include "Bitmap.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+struct IAVIFile;
+struct IAVIStream;
+
+// +--------------------------------------------------------------------+
+
+class AviFile
+{
+public:
+ static const char* TYPENAME() { return "AviFile"; }
+
+ // open for reading:
+ AviFile(const char* fname);
+
+ // create for writing
+ AviFile(const char* fname, const Rect& rect, int frame_rate=30);
+ ~AviFile();
+
+ HRESULT AddFrame(const Bitmap& bmp);
+ HRESULT GetFrame(double seconds, Bitmap& bmp);
+
+private:
+ Text filename;
+ Rect rect;
+ int fps;
+ bool play;
+
+ IAVIFile* pfile; // created by CreateAvi
+ IAVIStream* ps;
+ IAVIStream* ps_comp; // video stream, when first created
+ DWORD frame_size; // total bytes per frame of video
+ DWORD nframe; // which frame will be added next
+ DWORD nsamp; // which sample will be added next
+ bool iserr; // if true, then no function will do anything
+};
+
+// +--------------------------------------------------------------------+
+
+
+#endif AviFile_h
diff --git a/nGenEx/Bitmap.cpp b/nGenEx/Bitmap.cpp
new file mode 100644
index 0000000..54cd458
--- /dev/null
+++ b/nGenEx/Bitmap.cpp
@@ -0,0 +1,1618 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Bitmap.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Bitmap Resource class
+*/
+
+#include "MemDebug.h"
+#include "Bitmap.h"
+#include "Video.h"
+#include "Color.h"
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+
+DWORD GetRealTime();
+
+static inline void swap(int& a, int& b) { int tmp=a; a=b; b=tmp; }
+static inline void sort(int& a, int& b) { if (a>b) swap(a,b); }
+static inline void swap(double& a, double& b) { double tmp=a; a=b; b=tmp; }
+static inline void sort(double& a, double& b) { if (a>b) swap(a,b); }
+static void draw_strip(BYTE* dst, int pitch, int pixsize, int x, int y, int len, Color color);
+static void draw_vline(BYTE* dst, int pitch, int pixsize, int x, int y, int len, Color color);
+
+class WinPlot
+{
+public:
+ WinPlot(Bitmap* bmp);
+ void plot(int x, int y, DWORD val, int exch=0);
+
+private:
+ BYTE* s;
+ int pitch, pixsize;
+};
+
+WinPlot::WinPlot(Bitmap* bmp)
+{
+ s = bmp->GetSurface();
+ pitch = bmp->Pitch();
+ pixsize = bmp->PixSize();
+}
+
+void WinPlot::plot(int x, int y, DWORD val, int exch)
+{
+ if (exch) swap(x,y);
+ BYTE* dst = s + y*pitch + x*pixsize;
+
+ switch (pixsize) {
+ case 1: *dst = (BYTE) val; break;
+ case 2: { LPWORD dst2 = (LPWORD) dst; *dst2 = (WORD) val; } break;
+ case 4: { LPDWORD dst4 = (LPDWORD) dst; *dst4 = (DWORD) val; } break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Bitmap::Bitmap()
+ : type(BMP_SOLID), width(0), height(0),
+ ownpix(false), alpha_loaded(false), texture(false),
+ pix(0), hipix(0), mapsize(0),
+ last_modified(0)
+{
+ strcpy(filename, "Bitmap()");
+}
+
+Bitmap::Bitmap(int w, int h, ColorIndex* p, int t)
+ : type(t), width(w), height(h),
+ ownpix(false), alpha_loaded(false), texture(false),
+ pix(p), hipix(0), mapsize(w*h),
+ last_modified(GetRealTime())
+{
+ sprintf(filename, "Bitmap(%d, %d, index, type=%d)", w, h, (int) t);
+}
+
+Bitmap::Bitmap(int w, int h, Color* p, int t)
+ : type(t), width(w), height(h),
+ ownpix(false), alpha_loaded(false), texture(false),
+ pix(0), hipix(p), mapsize(w*h),
+ last_modified(GetRealTime())
+{
+ sprintf(filename, "Bitmap(%d, %d, hicolor, type=%d)", w, h, (int) t);
+}
+
+// +--------------------------------------------------------------------+
+
+Bitmap::~Bitmap()
+{
+ if (ownpix) {
+ delete [] pix;
+ delete [] hipix;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Bitmap::BmpSize() const
+{
+ return mapsize * PixSize();
+}
+
+int
+Bitmap::RowSize() const
+{
+ return width;
+}
+
+int
+Bitmap::Pitch() const
+{
+ return width * PixSize();
+}
+
+int
+Bitmap::PixSize() const
+{
+ if (hipix)
+ return sizeof(Color);
+
+ else if (pix)
+ return sizeof(ColorIndex);
+
+ return 0;
+}
+
+BYTE*
+Bitmap::GetSurface()
+{
+ if (ownpix) {
+ if (hipix)
+ return (BYTE*) hipix;
+
+ return (BYTE*) pix;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::BitBlt(int x, int y, const Bitmap& srcBmp, int sx, int sy, int w, int h, bool blend)
+{
+ if (!ownpix || x < 0 || y < 0 || x >= width || y >= height)
+ return;
+
+ if (sx < 0 || sy < 0 || sx >= srcBmp.Width() || sy >= srcBmp.Height())
+ return;
+
+ if (hipix) {
+ if (srcBmp.HiPixels()) {
+ int dpitch = width;
+ int spitch = srcBmp.Width();
+ int rowlen = w * sizeof(Color);
+ Color* dst = hipix + (y*dpitch) + x;
+ Color* src = srcBmp.HiPixels() + (sy*spitch) + sx;
+
+ if (!blend) {
+ for (int i = 0; i < h; i++) {
+ memcpy(dst, src, rowlen);
+ dst += dpitch;
+ src += spitch;
+ }
+ }
+ else {
+ for (int i = 0; i < h; i++) {
+ Color* ps = src;
+ Color* pd = dst;
+
+ for (int n = 0; n < w; n++) {
+ if (ps->Value())
+ pd->Set(Color::FormattedBlend(ps->Value(), pd->Value()));
+ ps++;
+ pd++;
+ }
+
+ dst += dpitch;
+ src += spitch;
+ }
+ }
+ }
+
+ else {
+ int dpitch = width;
+ int spitch = srcBmp.Width();
+ Color* dst = hipix + (y*dpitch) + x;
+ ColorIndex* src = srcBmp.Pixels() + (sy*spitch) + sx;
+
+ if (!blend) {
+ for (int j = 0; j < h; j++) {
+ for (int i = 0; i < w; i++) {
+ dst[i].Set(src[i].Formatted());
+ }
+
+ dst += dpitch;
+ src += spitch;
+ }
+ }
+ else {
+ for (int i = 0; i < h; i++) {
+ ColorIndex* ps = src;
+ Color* pd = dst;
+
+ for (int n = 0; n < w; n++) {
+ if (ps->Index())
+ pd->Set(Color::FormattedBlend(ps->Formatted(), pd->Value()));
+ ps++;
+ pd++;
+ }
+ }
+
+ dst += dpitch;
+ src += spitch;
+ }
+ }
+ }
+
+ else if (pix) {
+ if (srcBmp.Pixels()) {
+ int dpitch = width;
+ int spitch = srcBmp.Width();
+ int rowlen = w;
+ Color* dst = hipix + (y*dpitch) + x;
+ Color* src = srcBmp.HiPixels() + (sy*spitch) + sx;
+
+ for (int i = 0; i < h; i++) {
+ memcpy(dst, src, rowlen);
+ dst += dpitch;
+ src += spitch;
+ }
+ }
+ }
+
+ alpha_loaded = srcBmp.alpha_loaded;
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::CopyBitmap(const Bitmap& rhs)
+{
+ if (ownpix) {
+ delete [] pix;
+ delete [] hipix;
+ pix = 0;
+ hipix = 0;
+ }
+
+ type = rhs.type;
+ width = rhs.width;
+ height = rhs.height;
+ alpha_loaded = rhs.alpha_loaded;
+ texture = rhs.texture;
+ ownpix = true;
+
+ mapsize = width * height;
+
+ if (rhs.pix) {
+ pix = new(__FILE__,__LINE__) ColorIndex[mapsize];
+
+ if (!pix) {
+ width = 0;
+ height = 0;
+ mapsize = 0;
+ }
+
+ else {
+ memcpy(pix, rhs.pix, mapsize*sizeof(ColorIndex));
+ }
+ }
+
+ if (rhs.hipix) {
+ hipix = new(__FILE__,__LINE__) Color[mapsize];
+
+ if (!hipix && !pix) {
+ width = 0;
+ height = 0;
+ mapsize = 0;
+ }
+
+ else {
+ memcpy(hipix, rhs.hipix, mapsize*sizeof(Color));
+ }
+ }
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::ClearImage()
+{
+ if (ownpix) {
+ delete [] pix;
+ delete [] hipix;
+ pix = 0;
+ hipix = 0;
+ }
+
+ type = BMP_SOLID;
+ width = 0;
+ height = 0;
+ mapsize = 0;
+ ownpix = false;
+ texture = false;
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::CopyImage(int w, int h, BYTE* p, int t)
+{
+ if (ownpix) {
+ delete [] pix;
+ pix = 0;
+ }
+ else {
+ hipix = 0;
+ }
+
+ type = t;
+ width = w;
+ height = h;
+ ownpix = true;
+ texture = false;
+ mapsize = w * h;
+
+ pix = new(__FILE__,__LINE__) ColorIndex[mapsize];
+
+ if (!pix) {
+ width = 0;
+ height = 0;
+ mapsize = 0;
+ }
+
+ else {
+ memcpy(pix, p, mapsize);
+ }
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::CopyHighColorImage(int w, int h, DWORD* p, int t)
+{
+ if (ownpix) {
+ delete [] hipix;
+ hipix = 0;
+ }
+ else {
+ pix = 0;
+ }
+
+ type = t;
+ width = w;
+ height = h;
+ ownpix = true;
+ texture = false;
+ mapsize = w * h;
+
+ hipix = new(__FILE__,__LINE__) Color[mapsize];
+
+ if (!hipix) {
+ width = 0;
+ height = 0;
+ mapsize = 0;
+ }
+
+ else {
+ memcpy(hipix, p, mapsize*sizeof(DWORD));
+ }
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::CopyAlphaImage(int w, int h, BYTE* a)
+{
+ if (!hipix || width != w || height != h)
+ return;
+
+ type = BMP_TRANSLUCENT;
+ alpha_loaded = true;
+
+ Color* p = hipix;
+
+ for (int i = 0; i < mapsize; i++) {
+ p->SetAlpha(*a);
+ p++;
+ a++;
+ }
+
+ last_modified = GetRealTime();
+}
+
+void
+Bitmap::CopyAlphaRedChannel(int w, int h, DWORD* a)
+{
+ if (!hipix || width != w || height != h)
+ return;
+
+ type = BMP_TRANSLUCENT;
+ alpha_loaded = true;
+
+ Color* p = hipix;
+
+ for (int i = 0; i < mapsize; i++) {
+ p->SetAlpha((BYTE) ((*a & Color::RMask) >> Color::RShift));
+ p++;
+ a++;
+ }
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::AutoMask(DWORD mask)
+{
+ if (!hipix || !mapsize || alpha_loaded)
+ return;
+
+ type = BMP_TRANSLUCENT;
+ alpha_loaded = true;
+
+ Color* p = hipix;
+ DWORD m = mask & Color::RGBMask;
+
+ for (int i = 0; i < mapsize; i++) {
+ if ((p->Value() & Color::RGBMask) == m)
+ p->SetAlpha(0);
+ else
+ p->SetAlpha(255);
+
+ p++;
+ }
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::FillColor(Color c)
+{
+ if (!width || !height)
+ return;
+
+ if (pix) {
+ ColorIndex* p = pix;
+ BYTE index = c.Index();
+
+ for (int i = 0; i < mapsize; i++)
+ *p++ = index;
+ }
+
+ if (hipix) {
+ Color* p = hipix;
+ DWORD value = c.Value();
+
+ for (int i = 0; i < mapsize; i++) {
+ p->Set(value);
+ p++;
+ }
+ }
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::ScaleTo(int w, int h)
+{
+ if (w < 1 || h < 1)
+ return;
+
+ double dx = (double) width / (double) w;
+ double dy = (double) height / (double) h;
+
+ bool mem_ok = true;
+
+ if (hipix) {
+ Color* src = hipix;
+ Color* buf = new(__FILE__,__LINE__) Color[w*h];
+ Color* dst = buf;
+
+ if (!buf) {
+ mem_ok = false;
+ }
+
+ else {
+ for (int y = 0; y < h; y++) {
+ int y_offset = (int) (y * dy);
+ for (int x = 0; x < w; x++) {
+ int x_offset = (int) (x * dx);
+ src = hipix + (y_offset * width) + x_offset;
+ *dst++ = *src;
+ }
+ }
+
+ if (ownpix)
+ delete [] hipix;
+
+ hipix = buf;
+ ownpix = true;
+ }
+ }
+
+ if (pix) {
+ ColorIndex* src = pix;
+ ColorIndex* buf = new(__FILE__,__LINE__) ColorIndex[w*h];
+ ColorIndex* dst = buf;
+
+ if (!buf) {
+ mem_ok = false;
+ }
+
+ else {
+ for (int y = 0; y < h; y++) {
+ int y_offset = (int) (y * dy);
+ for (int x = 0; x < w; x++) {
+ int x_offset = (int) (x * dx);
+ src = pix + (y_offset * width) + x_offset;
+ *dst++ = *src;
+ }
+ }
+
+ if (ownpix)
+ delete [] pix;
+
+ pix = buf;
+ ownpix = true;
+ }
+ }
+
+ if (mem_ok) {
+ width = w;
+ height = h;
+ mapsize = width * height;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::MakeIndexed()
+{
+ if (hipix) {
+ if (pix && ownpix)
+ delete [] pix;
+ pix = new(__FILE__,__LINE__) ColorIndex[mapsize];
+
+ if (pix) {
+ Color* src = hipix;
+ ColorIndex* dst = pix;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ *dst++ = src->Index();
+ src++;
+ }
+ }
+
+ if (!ownpix)
+ hipix = 0;
+
+ ownpix = true;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::MakeHighColor()
+{
+ if (pix) {
+ if (hipix && ownpix)
+ delete [] hipix;
+
+ hipix = new(__FILE__,__LINE__) Color[mapsize];
+
+ if (hipix) {
+ ColorIndex* src = pix;
+ Color* dst = hipix;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ *dst++ = src->Index();
+ src++;
+ }
+ }
+
+ if (!ownpix)
+ pix = 0;
+
+ ownpix = true;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static int FindBestTexSize(int n, int max_size)
+{
+ int delta = 100000;
+ int best = 1;
+
+ for (int i = 0; i < 12; i++) {
+ int size = 1 << i;
+
+ if (size > max_size)
+ break;
+
+ int dx = abs(n-size);
+
+ if (size < n)
+ dx *= 4;
+
+ if (dx < delta) {
+ delta = dx;
+ best = size;
+ }
+ }
+
+ return best;
+}
+
+void
+Bitmap::MakeTexture()
+{
+ if (width < 1 || height < 1 || (!pix && !hipix)) {
+ if (ownpix) {
+ delete [] pix;
+ delete [] hipix;
+ }
+
+ width = 0;
+ height = 0;
+ pix = 0;
+ hipix = 0;
+ texture = false;
+ return;
+ }
+
+ // texture surface format is 32-bit RGBA:
+ if (pix && !hipix) {
+ MakeHighColor();
+ }
+
+ // check size and aspect ratio:
+ int max_tex_size = Game::MaxTexSize();
+ int max_tex_aspect = Game::MaxTexAspect();
+
+ int best_width = FindBestTexSize(width, max_tex_size);
+ int best_height = FindBestTexSize(height, max_tex_size);
+ int aspect = 1;
+
+ // correct sizes for aspect if necessary:
+ if (best_width > best_height) {
+ aspect = best_width / best_height;
+
+ if (aspect > max_tex_aspect)
+ best_height = best_width / max_tex_aspect;
+ }
+
+ else {
+ aspect = best_height / best_width;
+
+ if (aspect > max_tex_aspect)
+ best_width = best_height / max_tex_aspect;
+ }
+
+ // rescale if necessary:
+ if (width != best_width || height != best_height)
+ ScaleTo(best_width, best_width);
+
+ texture = true;
+}
+
+// +--------------------------------------------------------------------+
+
+ColorIndex
+Bitmap::GetIndex(int x, int y) const
+{
+ ColorIndex result(0);
+
+ if (x < 0 || y < 0 || x > width-1 || y > height-1)
+ return result;
+
+ if (pix) {
+ result = *(pix + y*width + x);
+ }
+ else if (hipix) {
+ result = (hipix + y*width + x)->Index();
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Bitmap::GetColor(int x, int y) const
+{
+ Color result = Color::Black;
+
+ if (x < 0 || y < 0 || x > width-1 || y > height-1)
+ return result;
+
+ if (pix) {
+ result = (pix + y*width + x)->Index();
+ }
+ else if (hipix) {
+ result = *(hipix + y*width + x);
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::SetIndex(int x, int y, ColorIndex c)
+{
+ if (x < 0 || y < 0 || x > width || y > height)
+ return;
+
+ if (pix) {
+ *(pix + y*width + x) = c;
+ }
+ else if (hipix) {
+ *(hipix + y*width + x) = c.Index();
+ }
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::SetColor(int x, int y, Color c)
+{
+ if (x < 0 || y < 0 || x > width || y > height)
+ return;
+
+ if (pix) {
+ *(pix + y*width + x) = c.Index();
+ }
+ else if (hipix) {
+ *(hipix + y*width + x) = c;
+ }
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::SetFilename(const char* s)
+{
+ if (s) {
+ int n = strlen(s);
+
+ if (n >= 60) {
+ ZeroMemory(filename, sizeof(filename));
+ strcpy(filename, "...");
+ strcat(filename, s + n - 59);
+ filename[63] = 0;
+ }
+
+ else {
+ strcpy(filename, s);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Bitmap*
+Bitmap::Default()
+{
+ static Bitmap def;
+
+ if (!def.width) {
+ def.width = def.height = 64;
+ def.mapsize = 64*64;
+ def.ownpix = true;
+ def.pix = new(__FILE__,__LINE__) ColorIndex[def.mapsize];
+
+ // 8 bit palette mode
+ if (def.pix) {
+ ColorIndex* p = def.pix;
+
+ for (int y = 0; y < 64; y++) {
+ for (int x = 0; x < 64; x++) {
+ double distance = sqrt((x-32.0)*(x-32.0) + (y-32.0)*(y-32.0));
+ if (distance > 31.0) distance = 31.0;
+ BYTE color = 24 + (BYTE) distance;
+
+ if (x == 0 || y == 0) color = 255;
+ else if (x == 32 || y == 32) color = 251;
+
+ *p++ = color;
+ }
+ }
+ }
+ }
+
+ return &def;
+}
+
+// +--------------------------------------------------------------------+
+
+static List<Bitmap> bitmap_cache;
+
+Bitmap*
+Bitmap::GetBitmapByID(HANDLE bmp_id)
+{
+ for (int i = 0; i < bitmap_cache.size(); i++) {
+ if (bitmap_cache[i]->Handle() == bmp_id) {
+ return bitmap_cache[i];
+ }
+ }
+
+ return 0;
+}
+
+Bitmap*
+Bitmap::CheckCache(const char* filename)
+{
+ for (int i = 0; i < bitmap_cache.size(); i++) {
+ if (!stricmp(bitmap_cache[i]->GetFilename(), filename)) {
+ return bitmap_cache[i];
+ }
+ }
+
+ return 0;
+}
+
+void
+Bitmap::AddToCache(Bitmap* bmp)
+{
+ bitmap_cache.append(bmp);
+}
+
+void
+Bitmap::CacheUpdate()
+{
+ for (int i = 0; i < bitmap_cache.size(); i++) {
+ Bitmap* bmp = bitmap_cache[i];
+
+ if (bmp->IsTexture())
+ bmp->MakeTexture();
+ }
+}
+
+void
+Bitmap::ClearCache()
+{
+ bitmap_cache.destroy();
+}
+
+DWORD
+Bitmap::CacheMemoryFootprint()
+{
+ DWORD result = sizeof(bitmap_cache);
+ result += bitmap_cache.size() * sizeof(Bitmap*);
+
+ for (int i = 0; i < bitmap_cache.size(); i++) {
+ Bitmap* bmp = bitmap_cache[i];
+
+ if (bmp->pix)
+ result += bmp->mapsize * sizeof(ColorIndex);
+
+ if (bmp->hipix)
+ result += bmp->mapsize * sizeof(Color);
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Bitmap::ClipLine(int& x1, int& y1, int& x2, int& y2)
+{
+ // vertical lines:
+ if (x1==x2) {
+clip_vertical:
+ sort(y1,y2);
+ if (x1 < 0 || x1 >= width) return false;
+ if (y1 < 0) y1 = 0;
+ if (y2 >= height) y2 = height;
+ return true;
+ }
+
+ // horizontal lines:
+ if (y1==y2) {
+clip_horizontal:
+ sort(x1,x2);
+ if (y1 < 0 || y1 >= height) return false;
+ if (x1 < 0) x1 = 0;
+ if (x2 > width) x2 = width;
+ return true;
+ }
+
+ // general lines:
+
+ // sort left to right:
+ if (x1 > x2) {
+ swap(x1,x2);
+ swap(y1,y2);
+ }
+
+ double m = (double)(y2-y1) / (double)(x2-x1);
+ double b = (double) y1 - (m * x1);
+
+ // clip:
+ if (x1 < 0) { x1 = 0; y1 = (int) b; }
+ if (x1 >= width) return false;
+ if (x2 < 0) return false;
+ if (x2 > width-1) { x2 = width-1; y2 = (int) (m * x2 + b); }
+
+ if (y1 < 0 && y2 < 0) return false;
+ if (y1 >= height && y2 >= height) return false;
+
+ if (y1 < 0) { y1 = 0; x1 = (int) (-b/m); }
+ if (y1 >= height) { y1 = height-1; x1 = (int) ((y1-b)/m); }
+ if (y2 < 0) { y2 = 0; x2 = (int) (-b/m); }
+ if (y2 >= height) { y2 = height-1; x2 = (int) ((y2-b)/m); }
+
+ if (x1 == x2)
+ goto clip_vertical;
+
+ if (y1 == y2)
+ goto clip_horizontal;
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Bitmap::ClipLine(double& x1, double& y1, double& x2, double& y2)
+{
+ // vertical lines:
+ if (x1==x2) {
+clip_vertical:
+ sort(y1,y2);
+ if (x1 < 0 || x1 >= width) return false;
+ if (y1 < 0) y1 = 0;
+ if (y2 >= height) y2 = height;
+ return true;
+ }
+
+ // horizontal lines:
+ if (y1==y2) {
+clip_horizontal:
+ sort(x1,x2);
+ if (y1 < 0 || y1 >= height) return false;
+ if (x1 < 0) x1 = 0;
+ if (x2 > width) x2 = width;
+ return true;
+ }
+
+ // general lines:
+
+ // sort left to right:
+ if (x1 > x2) {
+ swap(x1,x2);
+ swap(y1,y2);
+ }
+
+ double m = (double)(y2-y1) / (double)(x2-x1);
+ double b = (double) y1 - (m * x1);
+
+ // clip:
+ if (x1 < 0) { x1 = 0; y1 = b; }
+ if (x1 >= width) return false;
+ if (x2 < 0) return false;
+ if (x2 > width-1) { x2 = width-1; y2 = (m * x2 + b); }
+
+ if (y1 < 0 && y2 < 0) return false;
+ if (y1 >= height && y2 >= height) return false;
+
+ if (y1 < 0) { y1 = 0; x1 = (-b/m); }
+ if (y1 >= height) { y1 = height-1; x1 = ((y1-b)/m); }
+ if (y2 < 0) { y2 = 0; x2 = (-b/m); }
+ if (y2 >= height) { y2 = height-1; x2 = ((y2-b)/m); }
+
+ if (x1 == x2)
+ goto clip_vertical;
+
+ if (y1 == y2)
+ goto clip_horizontal;
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::DrawLine(int x1, int y1, int x2, int y2, Color color)
+{
+ BYTE* s = GetSurface();
+
+ if (!s) return;
+
+ last_modified = GetRealTime();
+
+ // vertical lines:
+ if (x1==x2) {
+draw_vertical:
+ sort(y1,y2);
+ int fh = y2-y1;
+ if (x1 < 0 || x1 >= width) return;
+ if (y1 < 0) y1 = 0;
+ if (y2 >= height) y2 = height;
+ fh = y2-y1;
+ draw_vline(s, Pitch(), PixSize(), x1, y1, fh, color);
+ return;
+ }
+
+ // horizontal lines:
+ if (y1==y2) {
+draw_horizontal:
+ sort(x1,x2);
+ int fw = x2-x1;
+ if (y1 < 0 || y1 >= height) return;
+ if (x1 < 0) x1 = 0;
+ if (x2 > width) x2 = width;
+ fw = x2-x1;
+ draw_strip(s, Pitch(), PixSize(), x1, y1, fw, color);
+ return;
+ }
+
+ // general lines:
+
+ // sort left to right:
+ if (x1 > x2) {
+ swap(x1,x2);
+ swap(y1,y2);
+ }
+
+ double m = (double)(y2-y1) / (double)(x2-x1);
+ double b = (double) y1 - (m * x1);
+
+ // clip:
+ if (x1 < 0) { x1 = 0; y1 = (int) b; }
+ if (x1 >= width) return;
+ if (x2 < 0) return;
+ if (x2 > width-1) { x2 = width-1; y2 = (int) (m * x2 + b); }
+
+ if (y1 < 0 && y2 < 0) return;
+ if (y1 >= height && y2 >= height) return;
+
+ if (y1 < 0) { y1 = 0; x1 = (int) (-b/m); }
+ if (y1 >= height) { y1 = height-1; x1 = (int) ((y1-b)/m); }
+ if (y2 < 0) { y2 = 0; x2 = (int) (-b/m); }
+ if (y2 >= height) { y2 = height-1; x2 = (int) ((y2-b)/m); }
+
+ if (x1 > x2)
+ return;
+
+ if (x1 == x2)
+ goto draw_vertical;
+
+ if (y1 == y2)
+ goto draw_horizontal;
+
+ // plot the line using
+ /*
+ Symmetric Double Step Line Algorithm
+ by Brian Wyvill
+ from "Graphics Gems", Academic Press, 1990
+ */
+
+ WinPlot plotter(this);
+
+ DWORD pix = color.Value();
+ int sign_x=1, sign_y=1, step, reflect;
+ int i, inc1, inc2, c, D, x_end, pixleft;
+ int dx = x2 - x1;
+ int dy = y2 - y1;
+
+ if (dx < 0) {
+ sign_x = -1;
+ dx *= -1;
+ }
+ if (dy < 0) {
+ sign_y = -1;
+ dy *= -1;
+ }
+
+ // decide increment sign by the slope sign
+ if (sign_x == sign_y)
+ step = 1;
+ else
+ step = -1;
+
+ if (dy > dx) { // chooses axis of greatest movement (make * dx)
+ swap(x1, y1);
+ swap(x2, y2);
+ swap(dx, dy);
+ reflect = 1;
+ } else
+ reflect = 0;
+
+ if (x1 > x2) { // start from the smaller coordinate
+ swap(x1,x2);
+ swap(y1,y2);
+ }
+
+ /* Note dx=n implies 0 - n or (dx+1) pixels to be set */
+ /* Go round loop dx/4 times then plot last 0,1,2 or 3 pixels */
+ /* In fact (dx-1)/4 as 2 pixels are already plottted */
+ x_end = (dx - 1) / 4;
+ pixleft = (dx - 1) % 4; /* number of pixels left over at the end */
+
+ plotter.plot(x1, y1, pix, reflect);
+ plotter.plot(x2, y2, pix, reflect); /* plot first two points */
+
+ inc2 = 4 * dy - 2 * dx;
+ if (inc2 < 0) { /* slope less than 1/2 */
+ c = 2 * dy;
+ inc1 = 2 * c;
+ D = inc1 - dx;
+
+ for (i = 0; i < x_end; i++) { /* plotting loop */
+ ++x1;
+ --x2;
+ if (D < 0) {
+ /* pattern 1 forwards */
+ plotter.plot(x1, y1, pix, reflect);
+ plotter.plot(++x1, y1, pix, reflect);
+ /* pattern 1 backwards */
+ plotter.plot(x2, y2, pix, reflect);
+ plotter.plot(--x2, y2, pix, reflect);
+ D += inc1;
+ }
+ else {
+ if (D < c) {
+ /* pattern 2 forwards */
+ plotter.plot(x1, y1, pix, reflect);
+ plotter.plot(++x1, y1 += step, pix, reflect);
+ /* pattern 2 backwards */
+ plotter.plot(x2, y2, pix, reflect);
+ plotter.plot(--x2, y2 -= step, pix, reflect);
+ }
+ else {
+ /* pattern 3 forwards */
+ plotter.plot(x1, y1 += step, pix, reflect);
+ plotter.plot(++x1, y1, pix, reflect);
+ /* pattern 3 backwards */
+ plotter.plot(x2, y2 -= step, pix, reflect);
+ plotter.plot(--x2, y2, pix, reflect);
+ }
+ D += inc2;
+ }
+ } /* end for */
+
+ /* plot last pattern */
+ if (pixleft) {
+ if (D < 0) {
+ plotter.plot(++x1, y1, pix, reflect); /* pattern 1 */
+ if (pixleft > 1)
+ plotter.plot(++x1, y1, pix, reflect);
+ if (pixleft > 2)
+ plotter.plot(--x2, y2, pix, reflect);
+ }
+ else {
+ if (D < c) {
+ plotter.plot(++x1, y1, pix, reflect); /* pattern 2 */
+ if (pixleft > 1)
+ plotter.plot(++x1, y1 += step, pix, reflect);
+ if (pixleft > 2)
+ plotter.plot(--x2, y2, pix, reflect);
+ }
+ else {
+ /* pattern 3 */
+ plotter.plot(++x1, y1 += step, pix, reflect);
+ if (pixleft > 1)
+ plotter.plot(++x1, y1, pix, reflect);
+ if (pixleft > 2)
+ plotter.plot(--x2, y2 -= step, pix, reflect);
+ }
+ }
+ } /* end if pixleft */
+ }
+ /* end slope < 1/2 */
+ else { /* slope greater than 1/2 */
+ c = 2 * (dy - dx);
+ inc1 = 2 * c;
+ D = inc1 + dx;
+ for (i = 0; i < x_end; i++) {
+ ++x1;
+ --x2;
+ if (D > 0) {
+ /* pattern 4 forwards */
+ plotter.plot(x1, y1 += step, pix, reflect);
+ plotter.plot(++x1, y1 += step, pix, reflect);
+ /* pattern 4 backwards */
+ plotter.plot(x2, y2 -= step, pix, reflect);
+ plotter.plot(--x2, y2 -= step, pix, reflect);
+ D += inc1;
+ } else {
+ if (D < c) {
+ /* pattern 2 forwards */
+ plotter.plot(x1, y1, pix, reflect);
+ plotter.plot(++x1, y1 += step, pix, reflect);
+
+ /* pattern 2 backwards */
+ plotter.plot(x2, y2, pix, reflect);
+ plotter.plot(--x2, y2 -= step, pix, reflect);
+ } else {
+ /* pattern 3 forwards */
+ plotter.plot(x1, y1 += step, pix, reflect);
+ plotter.plot(++x1, y1, pix, reflect);
+ /* pattern 3 backwards */
+ plotter.plot(x2, y2 -= step, pix, reflect);
+ plotter.plot(--x2, y2, pix, reflect);
+ }
+ D += inc2;
+ }
+ } /* end for */
+ /* plot last pattern */
+ if (pixleft) {
+ if (D > 0) {
+ plotter.plot(++x1, y1 += step, pix, reflect); /* pattern 4 */
+ if (pixleft > 1)
+ plotter.plot(++x1, y1 += step, pix, reflect);
+ if (pixleft > 2)
+ plotter.plot(--x2, y2 -= step, pix, reflect);
+ } else {
+ if (D < c) {
+ plotter.plot(++x1, y1, pix, reflect); /* pattern 2 */
+ if (pixleft > 1)
+ plotter.plot(++x1, y1 += step, pix, reflect);
+ if (pixleft > 2)
+ plotter.plot(--x2, y2, pix, reflect);
+ } else {
+ /* pattern 3 */
+ plotter.plot(++x1, y1 += step, pix, reflect);
+ if (pixleft > 1)
+ plotter.plot(++x1, y1, pix, reflect);
+ if (pixleft > 2) {
+ if (D > c) /* step 3 */
+ plotter.plot(--x2, y2 -= step, pix, reflect);
+ else /* step 2 */
+ plotter.plot(--x2, y2, pix, reflect);
+ }
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::DrawRect(int x1, int y1, int x2, int y2, Color color)
+{
+ sort(x1,x2);
+ sort(y1,y2);
+
+ int fw = x2-x1;
+ int fh = y2-y1;
+
+ if (fw == 0 || fh == 0) return;
+
+ // perform clip
+ int left = (x1 >= 0);
+ int right = (x2 <= width);
+ int top = (y1 >= 0);
+ int bottom = (y2 <= height);
+
+ BYTE* s = GetSurface();
+ if (!s) return;
+ int pitch = Pitch();
+ int pixsize = PixSize();
+
+ if (left) draw_vline(s, pitch, pixsize, x1, y1, fh, color);
+ if (right) draw_vline(s, pitch, pixsize, x2, y1, fh, color);
+ if (top) draw_strip(s, pitch, pixsize, x1, y1, fw, color);
+ if (bottom) draw_strip(s, pitch, pixsize, x1, y2, fw, color);
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::DrawRect(const Rect& r, Color color)
+{
+ if (r.w == 0 || r.h == 0) return;
+
+ int x1 = r.x;
+ int y1 = r.y;
+ int x2 = r.x + r.w;
+ int y2 = r.y + r.h;
+
+ // perform clip
+ int left = (x1 >= 0);
+ int right = (x2 <= width);
+ int top = (y1 >= 0);
+ int bottom = (y2 <= height);
+
+ BYTE* s = GetSurface();
+ if (!s) return;
+ int pitch = Pitch();
+ int pixsize = PixSize();
+
+ if (left) draw_vline(s, pitch, pixsize, x1, y1, r.h, color);
+ if (right) draw_vline(s, pitch, pixsize, x2, y1, r.h, color);
+ if (top) draw_strip(s, pitch, pixsize, x1, y1, r.w, color);
+ if (bottom) draw_strip(s, pitch, pixsize, x1, y2, r.w, color);
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::FillRect(int x1, int y1, int x2, int y2, Color color)
+{
+ // perform clip
+ if (x1 < 0) x1 = 0;
+ if (x2 > width-1) x2 = width-1;
+ if (y1 < 0) y1 = 0;
+ if (y2 > height) y2 = height;
+
+ int fw = x2-x1;
+ int fh = y2-y1;
+
+ if (fw == 0 || fh == 0) return;
+
+ BYTE* s = GetSurface();
+ if (!s) return;
+ int pitch = Pitch();
+ int pixsize = PixSize();
+
+ for (int i = 0; i < fh; i++)
+ draw_strip(s, pitch, pixsize, x1, y1+i, fw, color);
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::FillRect(const Rect& r, Color color)
+{
+ int x1 = r.x;
+ int y1 = r.y;
+ int x2 = r.x + r.w;
+ int y2 = r.y + r.h;
+
+ // perform clip
+ if (x1 < 0) x1 = 0;
+ if (x2 > width-1) x2 = width-1;
+ if (y1 < 0) y1 = 0;
+ if (y2 > height) y2 = height;
+
+ int fw = x2-x1;
+ int fh = y2-y1;
+
+ if (fw == 0 || fh == 0) return;
+
+ BYTE* s = GetSurface();
+ if (!s) return;
+ int pitch = Pitch();
+ int pixsize = PixSize();
+
+ for (int i = 0; i < fh; i++)
+ draw_strip(s, pitch, pixsize, x1, y1+i, fw, color);
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::DrawEllipse(int x1, int y1, int x2, int y2, Color color, BYTE quad)
+{
+ BYTE* orig = GetSurface();
+ BYTE* s = orig;
+
+ if (!s) return;
+
+ sort(x1,x2);
+ sort(y1,y2);
+
+ int fw = x2-x1;
+ int fh = y2-y1;
+
+ if (fw < 1 || fh < 1) return;
+
+ // clip:
+ if (x1 >= width || x2 < 0) return;
+ if (y1 >= height || y2 < 0) return;
+
+ double a = fw / 2.0;
+ double b = fh / 2.0;
+ double a2 = a*a;
+ double b2 = b*b;
+
+ int x = 0;
+ int y = (int) b;
+ int x0 = (x1+x2)/2;
+ int y0 = (y1+y2)/2;
+
+ // clip super-giant ellipses:
+ if (x1 < 0 && y1 < 0 && x2 > width && y2 > height) {
+ double r2 = (a2<b2) ? a2 : b2;
+
+ if (r2 > 32 * height)
+ return;
+
+ double ul = (x1-x0)*(x1-x0) + (y1-y0)*(y1-y0);
+ double ur = (x2-x0)*(x2-x0) + (y1-y0)*(y1-y0);
+ double ll = (x1-x0)*(x1-x0) + (y2-y0)*(y2-y0);
+ double lr = (x2-x0)*(x2-x0) + (y2-y0)*(y2-y0);
+
+ if (ul > r2 && ur > r2 && ll > r2 && lr > r2)
+ return;
+ }
+
+ DrawEllipsePoints(x0,y0,x,y,color,quad);
+
+ // region 1
+ double d1 = (b2)-(a2*b)+(0.25*a2);
+ while ((a2)*(y-0.5) > (b2)*(x+1)) {
+ if (d1 < 0)
+ d1 += b2*(2*x+3);
+ else {
+ d1 += b2*(2*x+3) + a2*(-2*y+2);
+ y--;
+ }
+ x++;
+
+ DrawEllipsePoints(x0,y0,x,y,color,quad);
+ }
+
+ // region 2
+ double d2 = b2*(x+0.5)*(x+0.5) + a2*(y-1)*(y-1) - a2*b2;
+ while (y > 0) {
+ if (d2 < 0) {
+ d2 += b2*(2*x+2) + a2*(-2*y+3);
+ x++;
+ }
+ else
+ d2 += a2*(-2*y+3);
+ y--;
+
+ DrawEllipsePoints(x0,y0,x,y,color,quad);
+ }
+
+ last_modified = GetRealTime();
+}
+
+void
+Bitmap::DrawEllipsePoints(int x0, int y0, int x, int y, Color c, BYTE quad)
+{
+ BYTE* s = GetSurface();
+
+ if (!s) return;
+
+ int pitch = Pitch();
+ int pixsize = PixSize();
+
+ int left = x0-x;
+ int right = x0+x+1;
+ int top = y0-y;
+ int bottom = y0+y+1;
+
+ // clip:
+ if (left >= width || right < 0) return;
+ if (top >= height || bottom < 0) return;
+
+ BYTE* dst = 0;
+ DWORD cf = c.Value();
+
+ if (left >= 0 && top >= 0 && quad&1) {
+ dst = s + top*pitch + left*pixsize;
+
+ switch (pixsize) {
+ case 1: *dst = (BYTE) cf; break;
+ case 2: { LPWORD sw = (LPWORD) dst; *sw = (WORD) cf; } break;
+ case 4: { LPDWORD sd = (LPDWORD) dst; *sd = (DWORD) cf; } break;
+ }
+ }
+
+ if (right < width && top >= 0 && quad&2) {
+ dst = s + top*pitch + right*pixsize;
+
+ switch (pixsize) {
+ case 1: *dst = (BYTE) cf; break;
+ case 2: { LPWORD sw = (LPWORD) dst; *sw = (WORD) cf; } break;
+ case 4: { LPDWORD sd = (LPDWORD) dst; *sd = (DWORD) cf; } break;
+ }
+ }
+
+ if (left >= 0 && bottom < height && quad&4) {
+ dst = s + bottom*pitch + left*pixsize;
+
+ switch (pixsize) {
+ case 1: *dst = (BYTE) cf; break;
+ case 2: { LPWORD sw = (LPWORD) dst; *sw = (WORD) cf; } break;
+ case 4: { LPDWORD sd = (LPDWORD) dst; *sd = (DWORD) cf; } break;
+ }
+ }
+
+ if (right < width && bottom < height && quad&4) {
+ dst = s + bottom*pitch + right*pixsize;
+
+ switch (pixsize) {
+ case 1: *dst = (BYTE) cf; break;
+ case 2: { LPWORD sw = (LPWORD) dst; *sw = (WORD) cf; } break;
+ case 4: { LPDWORD sd = (LPDWORD) dst; *sd = (DWORD) cf; } break;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static void draw_strip(BYTE* s, int pitch, int pixsize, int x, int y, int len, Color color)
+{
+ if (!s) return;
+ s += y*pitch + x*pixsize;
+
+ DWORD value = color.Formatted();
+
+ switch (pixsize) {
+ case 1: {
+ if (len > 1)
+ memset(s, (BYTE) value, len);
+ }
+ break;
+
+ case 2: {
+ LPWORD sw = (LPWORD) s;
+ for (int x = 0; x < len; x++) {
+ *sw++ = (WORD) value;
+ }
+ }
+ break;
+
+ case 4: {
+ Color* sd = (Color*) s;
+ for (int x = 0; x < len; x++) {
+ *sd++ = color;
+ }
+ }
+ break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static void draw_vline(BYTE* s, int pitch, int pixsize, int x, int y, int len, Color color)
+{
+ if (!s) return;
+ s += (y)*pitch + (x)*pixsize;
+
+ DWORD value = color.Formatted();
+
+ switch (pixsize) {
+ case 1: {
+ for (int y = 0; y < len; y++) {
+ *s = (BYTE) value;
+ s += pitch;
+ }
+ }
+ break;
+
+ case 2: {
+ LPWORD sw = (LPWORD) s;
+ pitch /= 2;
+
+ for (int y = 0; y < len; y++) {
+ *sw = (WORD) value;
+ sw += pitch;
+ }
+ }
+ break;
+
+ case 4: {
+ Color* sd = (Color*) s;
+ pitch /= 4;
+
+ for (int y = 0; y < len; y++) {
+ *sd = color;
+ sd += pitch;
+ }
+ }
+ break;
+ }
+}
+
+
diff --git a/nGenEx/Bitmap.h b/nGenEx/Bitmap.h
new file mode 100644
index 0000000..519f589
--- /dev/null
+++ b/nGenEx/Bitmap.h
@@ -0,0 +1,124 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Bitmap.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Bitmap Resource class
+*/
+
+#ifndef Bitmap_h
+#define Bitmap_h
+
+#include "Res.h"
+#include "Types.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class Color;
+class ColorIndex;
+
+class Bitmap : public Resource
+{
+public:
+ static const char* TYPENAME() { return "Bitmap"; }
+
+ enum BMP_TYPES { BMP_SOLID, BMP_TRANSPARENT, BMP_TRANSLUCENT };
+
+ Bitmap();
+ Bitmap(int w, int h, ColorIndex* p=0, int t=BMP_SOLID);
+ Bitmap(int w, int h, Color* p, int t=BMP_SOLID);
+ virtual ~Bitmap();
+
+ int IsIndexed() const { return pix != 0; }
+ int IsHighColor() const { return hipix != 0; }
+ int IsDual() const { return IsIndexed() &&
+ IsHighColor(); }
+
+ void SetType(int t) { type = t; }
+ int Type() const { return type; }
+ bool IsSolid() const { return type==BMP_SOLID; }
+ bool IsTransparent() const { return type==BMP_TRANSPARENT; }
+ bool IsTranslucent() const { return type==BMP_TRANSLUCENT; }
+
+ int Width() const { return width; }
+ int Height() const { return height; }
+ ColorIndex* Pixels() const { return pix; }
+ Color* HiPixels() const { return hipix; }
+ int BmpSize() const;
+ int RowSize() const;
+
+ ColorIndex GetIndex(int x, int y) const;
+ Color GetColor(int x, int y) const;
+ void SetIndex(int x, int y, ColorIndex c);
+ void SetColor(int x, int y, Color c);
+
+ void FillColor(Color c);
+
+ void ClearImage();
+ void BitBlt(int x, int y, const Bitmap& srcImage, int sx, int sy, int w, int h, bool blend=false);
+ void CopyBitmap(const Bitmap& rhs);
+ void CopyImage(int w, int h, BYTE* p, int t=BMP_SOLID);
+ void CopyHighColorImage(int w, int h, DWORD* p, int t=BMP_SOLID);
+ void CopyAlphaImage(int w, int h, BYTE* p);
+ void CopyAlphaRedChannel(int w, int h, DWORD* p);
+ void AutoMask(DWORD mask=0);
+
+ virtual BYTE* GetSurface();
+ virtual int Pitch() const;
+ virtual int PixSize() const;
+ bool ClipLine(int& x1, int& y1, int& x2, int& y2);
+ bool ClipLine(double& x1, double& y1, double& x2, double& y2);
+ void DrawLine(int x1, int y1, int x2, int y2, Color color);
+ void DrawRect(int x1, int y1, int x2, int y2, Color color);
+ void DrawRect(const Rect& r, Color color);
+ void FillRect(int x1, int y1, int x2, int y2, Color color);
+ void FillRect(const Rect& r, Color color);
+ void DrawEllipse(int x1, int y1, int x2, int y2, Color color, BYTE quad=0x0f);
+ void DrawEllipsePoints(int x0, int y0, int x, int y, Color c, BYTE quad);
+
+ void ScaleTo(int w, int h);
+ void MakeIndexed();
+ void MakeHighColor();
+ void MakeTexture();
+ bool IsTexture() const { return texture; }
+ void TakeOwnership() { ownpix = true; }
+
+ const char* GetFilename() const { return filename; }
+ void SetFilename(const char* s);
+
+ DWORD LastModified() const { return last_modified; }
+
+ static Bitmap* Default();
+
+ static Bitmap* GetBitmapByID(HANDLE bmp_id);
+ static Bitmap* CheckCache(const char* filename);
+ static void AddToCache(Bitmap* bmp);
+ static void CacheUpdate();
+ static void ClearCache();
+ static DWORD CacheMemoryFootprint();
+
+protected:
+ int type;
+ int width;
+ int height;
+ int mapsize;
+
+ bool ownpix;
+ bool alpha_loaded;
+ bool texture;
+
+ ColorIndex* pix;
+ Color* hipix;
+ DWORD last_modified;
+ char filename[64];
+};
+
+#endif Bitmap_h
+
diff --git a/nGenEx/Bmp.cpp b/nGenEx/Bmp.cpp
new file mode 100644
index 0000000..18c5870
--- /dev/null
+++ b/nGenEx/Bmp.cpp
@@ -0,0 +1,251 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Bmp.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ BMP image file loader
+*/
+
+
+#include "MemDebug.h"
+#include "BMP.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// +--------------------------------------------------------------------+
+
+BmpImage::BmpImage()
+ : width(0), height(0), image(0)
+{ }
+
+BmpImage::BmpImage(short w, short h, unsigned long* hibits)
+{
+ ZeroMemory(this, sizeof(BmpImage));
+
+ width = w;
+ height = h;
+
+ file_hdr.type = 19778; // 'BM'
+ file_hdr.size = sizeof(BmpFileHeader) +
+ sizeof(BmpInfoHeader) +
+ w * h * 3;
+
+ file_hdr.offset = sizeof(BmpFileHeader) +
+ sizeof(BmpInfoHeader);
+
+ info_hdr.hdr_size = sizeof(BmpInfoHeader);
+ info_hdr.width = width;
+ info_hdr.height = height;
+ info_hdr.planes = 1;
+ info_hdr.bit_count = 24;
+
+ int pixels = width * height;
+
+ image = new(__FILE__,__LINE__) DWORD [pixels];
+
+ if (image && pixels) {
+ for (int i = 0; i < pixels; i++)
+ image[i] = hibits[i];
+ }
+}
+
+BmpImage::~BmpImage()
+{
+ delete [] image;
+}
+
+// +--------------------------------------------------------------------+
+
+int BmpImage::Load(char *filename)
+{
+ int status = BMP_INVALID;
+ FILE* f;
+
+ f = fopen(filename,"rb");
+ if (f == NULL)
+ return BMP_NOFILE;
+
+ fread(&file_hdr.type, sizeof(WORD), 1, f);
+ fread(&file_hdr.size, sizeof(DWORD), 1, f);
+ fread(&file_hdr.rsvd1, sizeof(WORD), 1, f);
+ fread(&file_hdr.rsvd2, sizeof(WORD), 1, f);
+ fread(&file_hdr.offset, sizeof(DWORD), 1, f);
+ fread(&info_hdr, BMP_INFO_HDR_SIZE, 1, f);
+
+ if (info_hdr.width > 32768 || info_hdr.height > 32768 ||
+ (info_hdr.width&3) || (info_hdr.height&3) ||
+ info_hdr.compression != 0) {
+ fclose(f);
+ return BMP_INVALID;
+ }
+
+ width = (WORD) info_hdr.width;
+ height = (WORD) info_hdr.height;
+
+ // read 256 color BMP file
+ if (info_hdr.bit_count == 8) {
+ fread(palette, sizeof(palette), 1, f);
+
+ int pixels = width*height;
+
+ delete [] image;
+ image = new(__FILE__,__LINE__) DWORD[pixels];
+ if (image == NULL)
+ return BMP_NOMEM;
+
+ for (int row = height-1; row >= 0; row--) {
+ for (int col = 0; col < width; col++) {
+ BYTE index = fgetc(f);
+ image[row*width+col] = palette[index];
+ }
+ }
+
+ status = BMP_OK;
+ }
+
+ // read 24-bit (true COLOR) BMP file
+ else if (info_hdr.bit_count == 24) {
+ int pixels = width*height;
+
+ delete [] image;
+ image = new(__FILE__,__LINE__) DWORD[pixels];
+ if (image == NULL)
+ return BMP_NOMEM;
+
+ for (int row = height-1; row >= 0; row--) {
+ for (int col = 0; col < width; col++) {
+ DWORD blue = fgetc(f);
+ DWORD green = fgetc(f);
+ DWORD red = fgetc(f);
+
+ image[row*width+col] = 0xff000000 | (red << 16) | (green << 8) | blue;
+ }
+ }
+
+ status = BMP_OK;
+ }
+
+ fclose(f);
+ return status;
+}
+
+// +--------------------------------------------------------------------+
+
+int BmpImage::LoadBuffer(unsigned char* buf, int len)
+{
+ int status = BMP_INVALID;
+ BYTE* fp;
+
+ if (buf == NULL)
+ return BMP_NOFILE;
+
+ fp = buf;
+ memcpy(&info_hdr, buf + BMP_FILE_HDR_SIZE, BMP_INFO_HDR_SIZE);
+ fp += BMP_FILE_HDR_SIZE + BMP_INFO_HDR_SIZE;
+
+ if (info_hdr.width > 32768 || info_hdr.height > 32768 ||
+ (info_hdr.width&3) || (info_hdr.height&3) ||
+ info_hdr.compression != 0) {
+ return BMP_INVALID;
+ }
+
+ width = (WORD) info_hdr.width;
+ height = (WORD) info_hdr.height;
+
+ // read 256 color BMP file
+ if (info_hdr.bit_count == 8) {
+ memcpy(palette, fp, sizeof(palette));
+ fp += sizeof(palette);
+
+ int pixels = width*height;
+
+ delete [] image;
+ image = new(__FILE__,__LINE__) DWORD[pixels];
+ if (image == NULL)
+ return BMP_NOMEM;
+
+ for (int row = height-1; row >= 0; row--) {
+ for (int col = 0; col < width; col++) {
+ BYTE index = *fp++;
+ image[row*width+col] = palette[index];
+ }
+ }
+
+ status = BMP_OK;
+ }
+
+ // read 24-bit (true COLOR) BMP file
+ else if (info_hdr.bit_count == 24) {
+ int pixels = width*height;
+
+ delete [] image;
+ image = new(__FILE__,__LINE__) DWORD[pixels];
+ if (image == NULL)
+ return BMP_NOMEM;
+
+ for (int row = height-1; row >= 0; row--) {
+ for (int col = 0; col < width; col++) {
+ DWORD blue = *fp++;
+ DWORD green = *fp++;
+ DWORD red = *fp++;
+
+ image[row*width+col] = 0xff000000 | (red << 16) | (green << 8) | blue;
+ }
+ }
+
+ status = BMP_OK;
+ }
+
+ return status;
+}
+
+// +--------------------------------------------------------------------+
+
+int BmpImage::Save(char *filename)
+{
+ int status = BMP_INVALID;
+ FILE* f;
+
+ f = fopen(filename,"wb");
+ if (f == NULL)
+ return BMP_NOFILE;
+
+ info_hdr.bit_count = 24;
+ info_hdr.compression = 0;
+
+ fwrite(&file_hdr.type, sizeof(WORD), 1, f);
+ fwrite(&file_hdr.size, sizeof(DWORD), 1, f);
+ fwrite(&file_hdr.rsvd1, sizeof(WORD), 1, f);
+ fwrite(&file_hdr.rsvd2, sizeof(WORD), 1, f);
+ fwrite(&file_hdr.offset, sizeof(DWORD), 1, f);
+ fwrite(&info_hdr, BMP_INFO_HDR_SIZE, 1, f);
+
+ // write 24-bit (TRUE COLOR) BMP file
+ for (int row = height-1; row >= 0; row--) {
+ for (int col = 0; col < width; col++) {
+ DWORD pixel = image[row*width+col];
+
+ BYTE blue = (BYTE) ((pixel & 0x000000ff) >> 0);
+ BYTE green = (BYTE) ((pixel & 0x0000ff00) >> 8);
+ BYTE red = (BYTE) ((pixel & 0x00ff0000) >> 16);
+
+ fwrite(&blue, 1, 1, f);
+ fwrite(&green, 1, 1, f);
+ fwrite(&red, 1, 1, f);
+ }
+ }
+
+ status = BMP_OK;
+
+ fclose(f);
+ return status;
+}
+
diff --git a/nGenEx/Bmp.h b/nGenEx/Bmp.h
new file mode 100644
index 0000000..b4b0762
--- /dev/null
+++ b/nGenEx/Bmp.h
@@ -0,0 +1,76 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Bmp.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ BMP image file loader
+*/
+
+#ifndef BMP_H
+#define BMP_H
+
+// +--------------------------------------------------------------------+
+
+enum { BMP_OK, BMP_NOMEM, BMP_INVALID, BMP_NOFILE };
+
+struct BmpFileHeader
+{
+ WORD type;
+ DWORD size;
+ WORD rsvd1;
+ WORD rsvd2;
+ DWORD offset;
+};
+
+struct BmpInfoHeader
+{
+ DWORD hdr_size;
+ DWORD width;
+ DWORD height;
+ WORD planes;
+ WORD bit_count;
+ DWORD compression;
+ DWORD img_size;
+ DWORD x_pixels_per_meter;
+ DWORD y_pixels_per_meter;
+ DWORD colors_used;
+ DWORD colors_important;
+};
+
+const int BMP_FILE_HDR_SIZE = 14;
+const int BMP_INFO_HDR_SIZE = 40;
+
+// +--------------------------------------------------------------------+
+
+struct BmpImage
+{
+ static const char* TYPENAME() { return "BmpImage"; }
+
+ BmpImage(short w, short h, unsigned long* hibits);
+
+ BmpImage();
+ ~BmpImage();
+
+ int Load(char *filename);
+ int Save(char *filename);
+
+ int LoadBuffer(unsigned char* buf, int len);
+
+ BmpFileHeader file_hdr;
+ BmpInfoHeader info_hdr;
+ DWORD palette[256];
+ DWORD* image;
+ WORD width;
+ WORD height;
+};
+
+// +--------------------------------------------------------------------+
+
+
+#endif BMP_H
diff --git a/nGenEx/Bolt.cpp b/nGenEx/Bolt.cpp
new file mode 100644
index 0000000..af758b8
--- /dev/null
+++ b/nGenEx/Bolt.cpp
@@ -0,0 +1,176 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Bolt.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ 3D Bolt (Polygon) Object
+*/
+
+#include "MemDebug.h"
+#include "Bolt.h"
+#include "Bitmap.h"
+#include "Camera.h"
+#include "Video.h"
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+Bolt::Bolt(double len, double wid, Bitmap* tex, int share)
+ : vset(4), poly(0), texture(tex),
+ length(len), width(wid), shade(1.0), vpn(0, 1, 0), shared(share)
+{
+ trans = true;
+
+ loc = Vec3(0.0f, 0.0f, 1000.0f);
+
+ vset.nverts = 4;
+
+ vset.loc[0] = Point( width, 0, 1000);
+ vset.loc[1] = Point( width, -length, 1000);
+ vset.loc[2] = Point(-width, -length, 1000);
+ vset.loc[3] = Point(-width, 0, 1000);
+
+ vset.tu[0] = 0.0f;
+ vset.tv[0] = 0.0f;
+ vset.tu[1] = 1.0f;
+ vset.tv[1] = 0.0f;
+ vset.tu[2] = 1.0f;
+ vset.tv[2] = 1.0f;
+ vset.tu[3] = 0.0f;
+ vset.tv[3] = 1.0f;
+
+ Plane plane(vset.loc[0], vset.loc[1], vset.loc[2]);
+
+ for (int i = 0; i < 4; i++) {
+ vset.nrm[i] = plane.normal;
+ }
+
+ mtl.Ka = Color::White;
+ mtl.Kd = Color::White;
+ mtl.Ks = Color::Black;
+ mtl.Ke = Color::White;
+ mtl.tex_diffuse = texture;
+ mtl.tex_emissive = texture;
+ mtl.blend = Video::BLEND_ADDITIVE;
+
+ poly.nverts = 4;
+ poly.vertex_set = &vset;
+ poly.material = &mtl;
+ poly.verts[0] = 0;
+ poly.verts[1] = 1;
+ poly.verts[2] = 2;
+ poly.verts[3] = 3;
+
+ radius = (float) ((length>width) ? (length) : (width*2));
+
+ if (texture) {
+ strncpy(name, texture->GetFilename(), 31);
+ name[31] = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Bolt::~Bolt()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bolt::Render(Video* video, DWORD flags)
+{
+ if ((flags & RENDER_ADDITIVE) == 0)
+ return;
+
+ if (visible && !hidden && video && life) {
+ const Camera* camera = video->GetCamera();
+
+ Point head = loc;
+ Point tail = origin;
+ Point vtail = tail - head;
+ Point vcam = camera->Pos() - loc;
+ Point vtmp = vcam.cross(vtail);
+ vtmp.Normalize();
+ Point vlat = vtmp * -width;
+ Vec3 vnrm = camera->vpn() * -1;
+
+ vset.loc[0] = head + vlat;
+ vset.loc[1] = tail + vlat;
+ vset.loc[2] = tail - vlat;
+ vset.loc[3] = head - vlat;
+
+ vset.nrm[0] = vnrm;
+ vset.nrm[1] = vnrm;
+ vset.nrm[2] = vnrm;
+ vset.nrm[3] = vnrm;
+
+ ColorValue white((float) shade, (float) shade, (float) shade);
+ mtl.Ka = white;
+ mtl.Kd = white;
+ mtl.Ks = Color::Black;
+ mtl.Ke = white;
+
+ video->DrawPolys(1, &poly);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bolt::Update()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bolt::TranslateBy(const Point& ref)
+{
+ loc = loc - ref;
+ origin = origin - ref;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bolt::SetOrientation(const Matrix& o)
+{
+ vpn = Point(o(2,0), o(2,1), o(2,2));
+ origin = loc + (vpn * -length);
+}
+
+void
+Bolt::SetDirection(const Point& v)
+{
+ vpn = v;
+ origin = loc + (vpn * -length);
+}
+
+void
+Bolt::SetEndPoints(const Point& from, const Point& to)
+{
+ loc = to;
+ origin = from;
+ vpn = to - from;
+ length = vpn.Normalize();
+ radius = (float) length;
+}
+
+void
+Bolt::SetTextureOffset(double from, double to)
+{
+ vset.tu[0] = (float) from;
+ vset.tu[1] = (float) to;
+ vset.tu[2] = (float) to;
+ vset.tu[3] = (float) from;
+}
+
+
diff --git a/nGenEx/Bolt.h b/nGenEx/Bolt.h
new file mode 100644
index 0000000..3cf3842
--- /dev/null
+++ b/nGenEx/Bolt.h
@@ -0,0 +1,65 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Bolt.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ 3D Bolt (Polygon) Object
+*/
+
+#ifndef Bolt_h
+#define Bolt_h
+
+#include "Graphic.h"
+#include "Polygon.h"
+
+// +--------------------------------------------------------------------+
+
+class Bolt : public Graphic
+{
+public:
+ static const char* TYPENAME() { return "Bolt"; }
+
+ Bolt(double len=16, double wid=1, Bitmap* tex=0, int share=0);
+ virtual ~Bolt();
+
+ // operations
+ virtual void Render(Video* video, DWORD flags);
+ virtual void Update();
+
+ // accessors / mutators
+ virtual void SetOrientation(const Matrix& o);
+ void SetDirection(const Point& v);
+ void SetEndPoints(const Point& from, const Point& to);
+ void SetTextureOffset(double from, double to);
+
+ virtual void TranslateBy(const Point& ref);
+
+ double Shade() const { return shade; }
+ void SetShade(double s) { shade = s; }
+ virtual bool IsBolt() const { return true; }
+
+protected:
+ double length;
+ double width;
+ double shade;
+
+ Poly poly;
+ Material mtl;
+ VertexSet vset;
+ Bitmap* texture;
+ int shared;
+
+ Point vpn;
+ Point origin;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Bolt_h
+
diff --git a/nGenEx/Button.cpp b/nGenEx/Button.cpp
new file mode 100644
index 0000000..2a52b57
--- /dev/null
+++ b/nGenEx/Button.cpp
@@ -0,0 +1,687 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Button.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Button class
+*/
+
+#include "MemDebug.h"
+#include "Button.h"
+#include "Video.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "Sound.h"
+#include "DataLoader.h"
+
+// +--------------------------------------------------------------------+
+
+static Sound* button_sound = 0;
+static Sound* click_sound = 0;
+static Sound* swish_sound = 0;
+static Sound* chirp_sound = 0;
+static Sound* accept_sound = 0;
+static Sound* reject_sound = 0;
+static Sound* confirm_sound = 0;
+static Sound* list_select_sound = 0;
+static Sound* list_scroll_sound = 0;
+static Sound* list_drop_sound = 0;
+static Sound* combo_open_sound = 0;
+static Sound* combo_close_sound = 0;
+static Sound* combo_hilite_sound = 0;
+static Sound* combo_select_sound = 0;
+static Sound* menu_open_sound = 0;
+static Sound* menu_close_sound = 0;
+static Sound* menu_select_sound = 0;
+static Sound* menu_hilite_sound = 0;
+
+static int gui_volume = 0;
+
+// +--------------------------------------------------------------------+
+
+Button::Button(Screen* s, int ax, int ay, int aw, int ah, DWORD aid)
+ : ActiveWindow(s, ax, ay, aw, ah, aid)
+{
+ animated = true;
+ bevel_width = 5;
+ border = false;
+ button_state = 0;
+ drop_shadow = false;
+ sticky = false;
+ picture_loc = 1;
+ captured = 0;
+ pre_state = 0;
+ text_align = DT_CENTER;
+
+ standard_image = 0;
+ activated_image = 0;
+ transition_image = 0;
+
+ char buf[32];
+ sprintf(buf, "Button %d", id);
+ desc = buf;
+}
+
+Button::Button(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid)
+ : ActiveWindow(p->GetScreen(), ax, ay, aw, ah, aid, 0, p)
+{
+ animated = true;
+ bevel_width = 5;
+ border = false;
+ button_state = 0;
+ drop_shadow = false;
+ sticky = false;
+ picture_loc = 1;
+ captured = 0;
+ pre_state = 0;
+ text_align = DT_CENTER;
+
+ standard_image = 0;
+ activated_image = 0;
+ transition_image = 0;
+
+ char buf[32];
+ sprintf(buf, "Button %d", id);
+ desc = buf;
+}
+
+// +--------------------------------------------------------------------+
+
+Button::~Button()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+static void LoadInterfaceSound(DataLoader* loader, const char* wave, Sound*& s)
+{
+ loader->LoadSound(wave, s, 0, true); // optional sound effect
+
+ if (s)
+ s->SetFlags(s->GetFlags() | Sound::INTERFACE);
+}
+
+void
+Button::Initialize()
+{
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Sounds/");
+
+ LoadInterfaceSound(loader, "button.wav", button_sound);
+ LoadInterfaceSound(loader, "click.wav", click_sound);
+ LoadInterfaceSound(loader, "swish.wav", swish_sound);
+ LoadInterfaceSound(loader, "chirp.wav", chirp_sound);
+ LoadInterfaceSound(loader, "accept.wav", accept_sound);
+ LoadInterfaceSound(loader, "reject.wav", reject_sound);
+ LoadInterfaceSound(loader, "confirm.wav", confirm_sound);
+ LoadInterfaceSound(loader, "list_select.wav", list_select_sound);
+ LoadInterfaceSound(loader, "list_scroll.wav", list_scroll_sound);
+ LoadInterfaceSound(loader, "list_drop.wav", list_drop_sound);
+ LoadInterfaceSound(loader, "combo_open.wav", combo_open_sound);
+ LoadInterfaceSound(loader, "combo_close.wav", combo_close_sound);
+ LoadInterfaceSound(loader, "combo_hilite.wav", combo_hilite_sound);
+ LoadInterfaceSound(loader, "combo_select.wav", combo_select_sound);
+ LoadInterfaceSound(loader, "menu_open.wav", menu_open_sound);
+ LoadInterfaceSound(loader, "menu_close.wav", menu_close_sound);
+ LoadInterfaceSound(loader, "menu_select.wav", menu_select_sound);
+ LoadInterfaceSound(loader, "menu_hilite.wav", menu_hilite_sound);
+
+ loader->SetDataPath(0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Button::Close()
+{
+ delete button_sound;
+ delete click_sound;
+ delete swish_sound;
+ delete chirp_sound;
+ delete accept_sound;
+ delete reject_sound;
+ delete confirm_sound;
+ delete list_select_sound;
+ delete list_scroll_sound;
+ delete list_drop_sound;
+ delete combo_open_sound;
+ delete combo_close_sound;
+ delete combo_hilite_sound;
+ delete combo_select_sound;
+ delete menu_open_sound;
+ delete menu_close_sound;
+ delete menu_select_sound;
+ delete menu_hilite_sound;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Button::Draw()
+{
+ if (!IsShown()) return;
+
+ int x = 0;
+ int y = 0;
+ int w = rect.w;
+ int h = rect.h;
+ int img_w = picture.Width();
+ int img_h = picture.Height();
+
+ float old_alpha = alpha;
+
+ if (!enabled)
+ SetAlpha(0.35);
+
+ Rect btn_rect(x,y,w,h);
+
+ if (!transparent) {
+ if (standard_image) {
+ if (!enabled) {
+ texture = standard_image;
+ }
+
+ else {
+ switch (button_state) {
+ case -1:
+ texture = activated_image;
+ break;
+
+ default:
+ case 0:
+ texture = standard_image;
+ break;
+
+ case 1:
+ if (sticky)
+ texture = activated_image;
+ else
+ texture = transition_image;
+ break;
+
+ case 2:
+ texture = transition_image;
+ break;
+ }
+ }
+
+ if (!texture)
+ texture = standard_image;
+
+ DrawTextureGrid();
+ }
+
+ else {
+ FillRect(0, 0, w, h, ShadeColor(back_color, 1.0));
+ DrawStyleRect(0, 0, w, h, style);
+ }
+ }
+
+ // draw the picture (if any)
+ if (picture.Width()) {
+ Rect irect = CalcPictureRect();
+ DrawImage(&picture, irect);
+ }
+
+ // draw text here:
+ if (font && text.length()) {
+ Rect label_rect = CalcLabelRect(img_w,img_h);
+ int vert_space = label_rect.h;
+ int horz_space = label_rect.w;
+ int align = DT_WORDBREAK | text_align;
+
+ DrawText(text.data(), 0, label_rect, DT_CALCRECT | align);
+ vert_space = (vert_space - label_rect.h)/2;
+
+ label_rect.w = horz_space;
+
+ if (vert_space > 0)
+ label_rect.y += vert_space;
+
+ if (animated && button_state > 0) {
+ label_rect.x += button_state;
+ label_rect.y += button_state;
+ }
+
+ if (drop_shadow) {
+ label_rect.x++;
+ label_rect.y++;
+
+ font->SetColor(back_color);
+ DrawText(text.data(), text.length(), label_rect, align);
+
+ label_rect.x--;
+ label_rect.y--;
+ }
+
+ font->SetColor(fore_color);
+ DrawText(text.data(), text.length(), label_rect, align);
+ }
+
+ if (!enabled)
+ SetAlpha(old_alpha);
+}
+
+Rect Button::CalcLabelRect(int img_w, int img_h)
+{
+ // fit the text in the bevel:
+ Rect label_rect;
+ label_rect.x = 0;
+ label_rect.y = 0;
+ label_rect.w = rect.w;
+ label_rect.h = rect.h;
+
+ if (text_align == DT_LEFT)
+ label_rect.Deflate(bevel_width + 8, bevel_width + 1);
+ else
+ label_rect.Deflate(bevel_width + 1, bevel_width + 1);
+
+ // and around the picture, if any:
+ if (img_h != 0)
+ {
+ switch (picture_loc)
+ {
+ default:
+ case 0: // the four corner positions
+ case 2: // and the center position
+ case 4: // don't affect the text position
+ case 6:
+ case 8:
+ break;
+
+ case 1: // north
+ label_rect.y += img_h;
+ label_rect.h -= img_h;
+ break;
+
+ case 3: // west
+ label_rect.x += img_w;
+ label_rect.w -= img_w;
+ break;
+
+ case 5: // east
+ label_rect.w -= img_w;
+ break;
+
+ case 7: // south
+ label_rect.h -= img_h;
+ break;
+ }
+ }
+
+ return label_rect;
+}
+
+// +--------------------------------------------------------------------+
+
+Rect
+Button::CalcPictureRect()
+{
+ int w = rect.w;
+ int h = rect.h;
+ int img_w = picture.Width();
+ int img_h = picture.Height();
+
+ if (img_h > h) img_h = h-2;
+ if (img_w > w) img_w = w-2;
+
+ int img_x_offset = bevel_width;
+ int img_y_offset = bevel_width;
+
+ switch (picture_loc)
+ {
+ default:
+ // TOP ROW:
+ case 0: break;
+
+ case 1: img_x_offset = (w/2-img_w/2);
+ break;
+
+ case 2: img_x_offset = w - img_w - bevel_width;
+ break;
+
+ // MIDDLE ROW:
+ case 3: img_y_offset = (h/2-img_h/2);
+ break;
+ case 4: img_x_offset = (w/2-img_w/2);
+ img_y_offset = (h/2-img_h/2);
+ break;
+ case 5: img_x_offset = w - img_w - bevel_width;
+ img_y_offset = (h/2-img_h/2);
+ break;
+
+ // BOTTOM ROW:
+ case 6:
+ img_y_offset = h - img_h - bevel_width;
+ break;
+ case 7: img_x_offset = (w/2-img_w/2);
+ img_y_offset = h - img_h - bevel_width;
+ break;
+ case 8: img_x_offset = w - img_w - bevel_width;
+ img_y_offset = h - img_h - bevel_width;
+ break;
+ }
+
+ Rect img_rect;
+ img_rect.x = img_x_offset;
+ img_rect.y = img_y_offset;
+
+ if (animated && button_state > 0) {
+ img_rect.x += button_state;
+ img_rect.y += button_state;
+ }
+
+ img_rect.w = img_w;
+ img_rect.h = img_h;
+
+ return img_rect;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Button::DrawImage(Bitmap* bmp, const Rect& irect)
+{
+ if (bmp) {
+ DrawBitmap(irect.x,
+ irect.y,
+ irect.x + irect.w,
+ irect.y + irect.h,
+ bmp,
+ Video::BLEND_ALPHA);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int Button::OnMouseMove(int x, int y)
+{
+ bool dirty = false;
+
+ if (captured)
+ {
+ ActiveWindow* test = GetCapture();
+
+ if (test != this)
+ {
+ captured = false;
+ button_state = pre_state;
+ dirty = true;
+ }
+
+ else if (sticky)
+ {
+ if (button_state == 2)
+ {
+ if (!rect.Contains(x,y))
+ {
+ button_state = pre_state;
+ dirty = true;
+ }
+ }
+ else
+ {
+ if (rect.Contains(x,y))
+ {
+ button_state = 2;
+ dirty = true;
+ }
+ }
+ }
+ else
+ {
+ if (button_state == 1)
+ {
+ if (!rect.Contains(x,y))
+ {
+ button_state = 0;
+ dirty = true;
+ }
+ }
+ else
+ {
+ if (rect.Contains(x,y))
+ {
+ button_state = 1;
+ dirty = true;
+ }
+ }
+ }
+ }
+
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+int Button::OnLButtonDown(int x, int y)
+{
+ if (!captured)
+ captured = SetCapture();
+
+ if (sticky)
+ button_state = 2;
+ else
+ button_state = 1;
+
+ return ActiveWindow::OnLButtonDown(x,y);
+}
+
+int Button::OnLButtonUp(int x, int y)
+{
+ if (captured) {
+ ReleaseCapture();
+ captured = 0;
+ }
+
+ button_state = pre_state;
+ return ActiveWindow::OnLButtonUp(x,y);
+}
+
+void Button::SetVolume(int vol)
+{
+ if (vol >= -10000 && vol <= 0)
+ gui_volume = vol;
+}
+
+void Button::PlaySound(int n)
+{
+ Sound* sound = 0;
+
+ switch (n) {
+ default:
+ case SND_BUTTON: if (button_sound) sound = button_sound->Duplicate(); break;
+ case SND_CLICK: if (click_sound) sound = click_sound->Duplicate(); break;
+ case SND_SWISH: if (swish_sound) sound = swish_sound->Duplicate(); break;
+ case SND_CHIRP: if (chirp_sound) sound = chirp_sound->Duplicate(); break;
+ case SND_ACCEPT: if (accept_sound) sound = accept_sound->Duplicate(); break;
+ case SND_REJECT: if (reject_sound) sound = reject_sound->Duplicate(); break;
+ case SND_CONFIRM: if (confirm_sound) sound = confirm_sound->Duplicate(); break;
+ case SND_LIST_SELECT: if (list_select_sound) sound = list_select_sound->Duplicate(); break;
+ case SND_LIST_SCROLL: if (list_scroll_sound) sound = list_scroll_sound->Duplicate(); break;
+ case SND_LIST_DROP: if (list_drop_sound) sound = list_drop_sound->Duplicate(); break;
+ case SND_COMBO_OPEN: if (combo_open_sound) sound = combo_open_sound->Duplicate(); break;
+ case SND_COMBO_CLOSE: if (combo_close_sound) sound = combo_close_sound->Duplicate(); break;
+ case SND_COMBO_HILITE: if (combo_hilite_sound) sound = combo_hilite_sound->Duplicate(); break;
+ case SND_COMBO_SELECT: if (combo_select_sound) sound = combo_select_sound->Duplicate(); break;
+ case SND_MENU_OPEN: if (menu_open_sound) sound = menu_open_sound->Duplicate(); break;
+ case SND_MENU_CLOSE: if (menu_close_sound) sound = menu_close_sound->Duplicate(); break;
+ case SND_MENU_SELECT: if (menu_select_sound) sound = menu_select_sound->Duplicate(); break;
+ case SND_MENU_HILITE: if (menu_hilite_sound) sound = menu_hilite_sound->Duplicate(); break;
+ }
+
+ if (sound) {
+ sound->SetVolume(gui_volume);
+ sound->Play();
+ }
+}
+
+int Button::OnClick()
+{
+ PlaySound(SND_BUTTON);
+
+ if (sticky)
+ button_state = !pre_state;
+
+ pre_state = button_state;
+
+ return ActiveWindow::OnClick();
+}
+
+int Button::OnMouseEnter(int mx, int my)
+{
+ if (button_state >= 0)
+ pre_state = button_state;
+
+ if (button_state == 0)
+ button_state = -1;
+
+ if (IsEnabled() && IsShown())
+ PlaySound(SND_SWISH);
+
+ return ActiveWindow::OnMouseEnter(mx, my);
+}
+
+int Button::OnMouseExit(int mx, int my)
+{
+ if (button_state == -1)
+ button_state = pre_state;
+
+ return ActiveWindow::OnMouseExit(mx, my);
+}
+
+// +--------------------------------------------------------------------+
+
+void Button::SetStandardImage(Bitmap* img)
+{
+ standard_image = img;
+ texture = standard_image;
+}
+
+void Button::SetActivatedImage(Bitmap* img)
+{
+ activated_image = img;
+}
+
+void Button::SetTransitionImage(Bitmap* img)
+{
+ transition_image = img;
+}
+
+// +--------------------------------------------------------------------+
+
+short Button::GetBevelWidth()
+{
+ return bevel_width;
+}
+
+void Button::SetBevelWidth(short nNewValue)
+{
+ if (nNewValue < 0) nNewValue = 0;
+ if (nNewValue > rect.w/2) nNewValue = rect.w/2;
+ bevel_width = nNewValue;
+}
+
+bool Button::GetBorder()
+{
+ return border;
+}
+
+void Button::SetBorder(bool bNewValue)
+{
+ border = bNewValue;
+}
+
+Color Button::GetBorderColor()
+{
+ return border_color;
+}
+
+void Button::SetBorderColor(Color newValue)
+{
+ border_color = newValue;
+}
+
+Color Button::GetActiveColor()
+{
+ return active_color;
+}
+
+void Button::SetActiveColor(Color newValue)
+{
+ active_color = newValue;
+}
+
+bool Button::GetAnimated()
+{
+ return animated;
+}
+
+void Button::SetAnimated(bool bNewValue)
+{
+ animated = bNewValue;
+}
+
+bool Button::GetDropShadow()
+{
+ return drop_shadow;
+}
+
+void Button::SetDropShadow(bool bNewValue)
+{
+ drop_shadow = bNewValue;
+}
+
+// +--------------------------------------------------------------------+
+
+short Button::GetButtonState()
+{
+ return button_state;
+}
+
+void Button::SetButtonState(short n)
+{
+ if (button_state != n && n >= -2 && n <= 2) {
+ button_state = n;
+ pre_state = n;
+ }
+}
+
+void Button::GetPicture(Bitmap& img)
+{
+ img.CopyBitmap(picture);
+}
+
+void Button::SetPicture(const Bitmap& img)
+{
+ picture.CopyBitmap(img);
+ picture.AutoMask();
+}
+
+short Button::GetPictureLocation()
+{
+ return picture_loc;
+}
+
+void Button::SetPictureLocation(short n)
+{
+ if (picture_loc != n && n >= 0 && n <= 8) {
+ picture_loc = n;
+ }
+}
+
+bool Button::GetSticky()
+{
+ return sticky;
+}
+
+void Button::SetSticky(bool n)
+{
+ if (sticky != n)
+ sticky = n;
+}
+
diff --git a/nGenEx/Button.h b/nGenEx/Button.h
new file mode 100644
index 0000000..ac16bfb
--- /dev/null
+++ b/nGenEx/Button.h
@@ -0,0 +1,121 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Button.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Button class
+*/
+
+#ifndef Button_h
+#define Button_h
+
+#include "Types.h"
+#include "ActiveWindow.h"
+#include "Bitmap.h"
+
+// +--------------------------------------------------------------------+
+
+class Button : public ActiveWindow
+{
+public:
+ enum SOUNDS {
+ SND_BUTTON,
+ SND_CLICK,
+ SND_SWISH,
+ SND_CHIRP,
+ SND_ACCEPT,
+ SND_REJECT,
+ SND_CONFIRM,
+ SND_LIST_SELECT,
+ SND_LIST_SCROLL,
+ SND_LIST_DROP,
+ SND_COMBO_OPEN,
+ SND_COMBO_CLOSE,
+ SND_COMBO_HILITE,
+ SND_COMBO_SELECT,
+ SND_MENU_OPEN,
+ SND_MENU_CLOSE,
+ SND_MENU_SELECT,
+ SND_MENU_HILITE
+ };
+
+ Button(Screen* s, int ax, int ay, int aw, int ah, DWORD id=0);
+ Button(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD id=0);
+ virtual ~Button();
+
+ static void Initialize();
+ static void Close();
+ static void PlaySound(int n=0);
+ static void SetVolume(int vol);
+
+ // Operations:
+ virtual void Draw(); // refresh backing store
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnClick();
+ virtual int OnMouseEnter(int x, int y);
+ virtual int OnMouseExit(int x, int y);
+
+ // Property accessors:
+ Color GetActiveColor();
+ void SetActiveColor(Color c);
+ bool GetAnimated();
+ void SetAnimated(bool bNewValue);
+ short GetBevelWidth();
+ void SetBevelWidth(short nNewValue);
+ bool GetBorder();
+ void SetBorder(bool bNewValue);
+ Color GetBorderColor();
+ void SetBorderColor(Color c);
+ short GetButtonState();
+ void SetButtonState(short nNewValue);
+ bool GetDropShadow();
+ void SetDropShadow(bool bNewValue);
+ void GetPicture(Bitmap& img);
+ void SetPicture(const Bitmap& img);
+ short GetPictureLocation();
+ void SetPictureLocation(short nNewValue);
+ bool GetSticky();
+ void SetSticky(bool bNewValue);
+
+ void SetStandardImage(Bitmap* img);
+ void SetActivatedImage(Bitmap* img);
+ void SetTransitionImage(Bitmap* img);
+
+protected:
+ Rect CalcLabelRect(int img_w, int img_h);
+ Rect CalcPictureRect();
+ void DrawImage(Bitmap* bmp, const Rect& irect);
+
+ bool animated;
+ bool drop_shadow;
+ bool sticky;
+ bool border;
+
+ Color active_color;
+ Color border_color;
+
+ int captured;
+ int pre_state;
+ short bevel_width;
+ short button_state;
+
+ short picture_loc;
+ Bitmap picture;
+
+ Bitmap* standard_image; // state = 0
+ Bitmap* activated_image; // state = 1 (if sticky)
+ Bitmap* transition_image; // state = 2 (if sticky)
+};
+
+#endif Button_h
+
diff --git a/nGenEx/Camera.cpp b/nGenEx/Camera.cpp
new file mode 100644
index 0000000..bbc72dd
--- /dev/null
+++ b/nGenEx/Camera.cpp
@@ -0,0 +1,212 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Camera.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Camera Class - Position and Point of View
+*/
+
+#include "MemDebug.h"
+#include "Camera.h"
+
+// +--------------------------------------------------------------------+
+
+Camera::Camera(double x, double y, double z)
+ : pos(x,y,z)
+{ }
+
+Camera::~Camera()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+Camera::MoveTo(double x, double y, double z)
+{
+ pos.x = x;
+ pos.y = y;
+ pos.z = z;
+}
+
+void
+Camera::MoveTo(const Point& p)
+{
+ pos.x = p.x;
+ pos.y = p.y;
+ pos.z = p.z;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Camera::MoveBy(double dx, double dy, double dz)
+{
+ pos.x += dx;
+ pos.y += dz;
+ pos.z += dy;
+}
+
+void
+Camera::MoveBy(const Point& p)
+{
+ pos.x += p.x;
+ pos.y += p.y;
+ pos.z += p.z;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Camera::Clone(const Camera& cam)
+{
+ pos = cam.pos;
+ orientation = cam.orientation;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Camera::LookAt(const Point& target, const Point& eye, const Point& up)
+{
+ Point zaxis = target - eye; zaxis.Normalize();
+ Point xaxis = up.cross(zaxis); xaxis.Normalize();
+ Point yaxis = zaxis.cross(xaxis); yaxis.Normalize();
+
+ orientation(0,0) = xaxis.x;
+ orientation(0,1) = xaxis.y;
+ orientation(0,2) = xaxis.z;
+
+ orientation(1,0) = yaxis.x;
+ orientation(1,1) = yaxis.y;
+ orientation(1,2) = yaxis.z;
+
+ orientation(2,0) = zaxis.x;
+ orientation(2,1) = zaxis.y;
+ orientation(2,2) = zaxis.z;
+
+ pos = eye;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Camera::LookAt(const Point& target)
+{
+ // No navel gazing:
+ if (target == Pos())
+ return;
+
+ Point tgt, tmp = target - Pos();
+
+ // Rotate into the view orientation:
+ tgt.x = (tmp * vrt());
+ tgt.y = (tmp * vup());
+ tgt.z = (tmp * vpn());
+
+ if (tgt.z == 0) {
+ Pitch(0.5);
+ Yaw(0.5);
+ LookAt(target);
+ return;
+ }
+
+ double az = atan(tgt.x/tgt.z);
+ double el = atan(tgt.y/tgt.z);
+
+ // if target is behind, offset by 180 degrees:
+ if (tgt.z < 0)
+ az -= PI;
+
+ Pitch(-el);
+ Yaw(az);
+
+ // roll to upright position:
+ double deflection = vrt().y;
+ while (fabs(deflection) > 0.001) {
+ double theta = asin(deflection/vrt().length());
+ Roll(-theta);
+
+ deflection = vrt().y;
+ }
+}
+
+
+// +--------------------------------------------------------------------+
+
+bool
+Camera::Padlock(const Point& target, double alimit, double e_lo, double e_hi)
+{
+ // No navel gazing:
+ if (target == Pos())
+ return false;
+
+ Point tgt, tmp = target - Pos();
+
+ // Rotate into the view orientation:
+ tgt.x = (tmp * vrt());
+ tgt.y = (tmp * vup());
+ tgt.z = (tmp * vpn());
+
+ if (tgt.z == 0) {
+ Yaw(0.1);
+
+ tgt.x = (tmp * vrt());
+ tgt.y = (tmp * vup());
+ tgt.z = (tmp * vpn());
+
+ if (tgt.z == 0)
+ return false;
+ }
+
+ bool locked = true;
+ double az = atan(tgt.x/tgt.z);
+ double orig = az;
+
+ // if target is behind, offset by 180 degrees:
+ if (tgt.z < 0)
+ az -= PI;
+
+ while (az > PI) az -= 2*PI;
+ while (az < -PI) az += 2*PI;
+
+ if (alimit > 0) {
+ if (az < -alimit) {
+ az = -alimit;
+ locked = false;
+ }
+ else if (az > alimit) {
+ az = alimit;
+ locked = false;
+ }
+ }
+
+ Yaw(az);
+
+ // Rotate into the new view orientation:
+ tgt.x = (tmp * vrt());
+ tgt.y = (tmp * vup());
+ tgt.z = (tmp * vpn());
+
+ double el = atan(tgt.y/tgt.z);
+
+ if (e_lo > 0 && el < -e_lo) {
+ el = -e_lo;
+ locked = false;
+ }
+
+ else if (e_hi > 0 && el > e_hi) {
+ el = e_hi;
+ locked = false;
+ }
+
+ Pitch(-el);
+
+ return locked;
+}
+
diff --git a/nGenEx/Camera.h b/nGenEx/Camera.h
new file mode 100644
index 0000000..498d112
--- /dev/null
+++ b/nGenEx/Camera.h
@@ -0,0 +1,61 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Camera.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Camera class - Position and Point of View
+*/
+
+#ifndef Camera_h
+#define Camera_h
+
+// +--------------------------------------------------------------------+
+
+#include "Types.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class Camera
+{
+public:
+ static const char* TYPENAME() { return "Camera"; }
+
+ Camera(double x=0.0, double y=0.0, double z=0.0);
+ virtual ~Camera();
+
+ void Aim(double roll, double pitch, double yaw) { orientation.Rotate(roll, pitch, yaw); }
+ void Roll(double roll) { orientation.Roll(roll); }
+ void Pitch(double pitch) { orientation.Pitch(pitch); }
+ void Yaw(double yaw) { orientation.Yaw(yaw); }
+
+ void MoveTo(double x, double y, double z);
+ void MoveTo(const Point& p);
+ void MoveBy(double dx, double dy, double dz);
+ void MoveBy(const Point& p);
+
+ void Clone(const Camera& cam);
+ void LookAt(const Point& target);
+ void LookAt(const Point& target, const Point& eye, const Point& up);
+ bool Padlock(const Point& target, double alimit=-1, double e_lo=-1, double e_hi=-1);
+
+ Point Pos() const { return pos; }
+ Point vrt() const { return Point(orientation(0,0), orientation(0,1), orientation(0,2)); }
+ Point vup() const { return Point(orientation(1,0), orientation(1,1), orientation(1,2)); }
+ Point vpn() const { return Point(orientation(2,0), orientation(2,1), orientation(2,2)); }
+
+ const Matrix& Orientation() const { return orientation; }
+
+protected:
+ Point pos;
+ Matrix orientation;
+};
+
+#endif Camera_h
+
diff --git a/nGenEx/CameraView.cpp b/nGenEx/CameraView.cpp
new file mode 100644
index 0000000..4b7d1d7
--- /dev/null
+++ b/nGenEx/CameraView.cpp
@@ -0,0 +1,805 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: CameraView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ 3D Projection Camera View class
+ uses abstract PolyRender class to draw the triangles
+*/
+
+#include "MemDebug.h"
+#include "CameraView.h"
+#include "Color.h"
+#include "Window.h"
+#include "Scene.h"
+#include "Light.h"
+#include "Solid.h"
+#include "Shadow.h"
+#include "Sprite.h"
+#include "Video.h"
+#include "Bitmap.h"
+#include "Screen.h"
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+static Camera emergency_cam;
+static Scene emergency_scene;
+
+// +--------------------------------------------------------------------+
+
+CameraView::CameraView(Window* c, Camera* cam, Scene* s)
+ : View(c), video(0), camera(cam), projector(c, cam), scene(s),
+ lens_flare_enable(0), halo_bitmap(0), infinite(0),
+ projection_type(Video::PROJECTION_PERSPECTIVE)
+{
+ elem_bitmap[0] = 0;
+ elem_bitmap[1] = 0;
+ elem_bitmap[2] = 0;
+
+ if (!camera)
+ camera = &emergency_cam;
+
+ if (!scene)
+ scene = &emergency_scene;
+
+ Rect r = window->GetRect();
+ width = r.w;
+ height = r.h;
+}
+
+CameraView::~CameraView()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraView::UseCamera(Camera* cam)
+{
+ if (cam)
+ camera = cam;
+ else
+ camera = &emergency_cam;
+
+ projector.UseCamera(camera);
+}
+
+void
+CameraView::UseScene(Scene* s)
+{
+ if (s)
+ scene = s;
+ else
+ scene = &emergency_scene;
+}
+
+void
+CameraView::SetFieldOfView(double fov)
+{
+ projector.SetFieldOfView(fov);
+}
+
+double
+CameraView::GetFieldOfView() const
+{
+ return projector.GetFieldOfView();
+}
+
+void
+CameraView::SetProjectionType(DWORD pt)
+{
+ projector.SetOrthogonal(pt == Video::PROJECTION_ORTHOGONAL);
+ projection_type = pt;
+}
+
+DWORD
+CameraView::GetProjectionType() const
+{
+ return projection_type;
+}
+
+void
+CameraView::OnWindowMove()
+{
+ Rect r = window->GetRect();
+ projector.UseWindow(window);
+
+ width = r.w;
+ height = r.h;
+}
+
+
+// +--------------------------------------------------------------------+
+// Enable or disable lens flare effect, and provide bitmaps for rendering
+// +--------------------------------------------------------------------+
+
+void
+CameraView::LensFlare(int on, double dim)
+{
+ lens_flare_enable = on;
+ lens_flare_dim = dim;
+}
+
+void
+CameraView::LensFlareElements(Bitmap* halo, Bitmap* e1, Bitmap* e2, Bitmap* e3)
+{
+ if (halo)
+ halo_bitmap = halo;
+
+ if (e1)
+ elem_bitmap[0] = e1;
+
+ if (e2)
+ elem_bitmap[1] = e2;
+
+ if (e3)
+ elem_bitmap[2] = e3;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+CameraView::SetInfinite(int i)
+{
+ int old = infinite;
+ infinite = i;
+ projector.SetInfinite(i);
+ return old;
+}
+
+// +--------------------------------------------------------------------+
+// Compute the Depth of a Graphic
+// +--------------------------------------------------------------------+
+
+void
+CameraView::FindDepth(Graphic* g)
+{
+ if (infinite) {
+ g->SetDepth(1.0e20f);
+ return;
+ }
+
+ // Translate into a viewpoint-relative coordinate
+ Vec3 loc = g->Location() - camera->Pos();
+
+ // Rotate into the view orientation
+ float z = (float) (loc * camera->vpn());
+ g->SetDepth(z);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraView::Refresh()
+{
+ // disabled:
+ if (camera == &emergency_cam)
+ return;
+
+ // prologue:
+ video = Video::GetInstance();
+ if (!video)
+ return;
+
+ int cw = window->Width();
+ int ch = window->Height();
+
+ cvrt = camera->vrt();
+ cvup = camera->vup();
+ cvpn = camera->vpn();
+
+ TranslateScene();
+ MarkVisibleObjects();
+
+ Rect old_rect;
+ video->GetWindowRect(old_rect);
+
+ video->SetCamera(camera);
+ video->SetWindowRect(window->GetRect());
+ video->SetProjection((float) GetFieldOfView(), 1.0f, 1.0e6f, projection_type);
+
+ // project and render:
+ RenderBackground();
+ RenderScene();
+ RenderForeground();
+ RenderSprites();
+ RenderLensFlare();
+
+ UnTranslateScene();
+
+ video->SetWindowRect(old_rect);
+}
+
+// +--------------------------------------------------------------------+
+// Translate all objects and lights to camera relative coordinates:
+// +--------------------------------------------------------------------+
+
+void
+CameraView::TranslateScene()
+{
+ camera_loc = camera->Pos();
+
+ ListIter<Graphic> g_iter = scene->Graphics();
+ while (++g_iter) {
+ Graphic* graphic = g_iter.value();
+
+ if (!graphic->IsInfinite())
+ graphic->TranslateBy(camera_loc);
+ }
+
+ g_iter.attach(scene->Foreground());
+ while (++g_iter) {
+ Graphic* graphic = g_iter.value();
+ graphic->TranslateBy(camera_loc);
+ }
+
+ g_iter.attach(scene->Sprites());
+ while (++g_iter) {
+ Graphic* graphic = g_iter.value();
+
+ if (!graphic->IsInfinite())
+ graphic->TranslateBy(camera_loc);
+ }
+
+ ListIter<Light> l_iter = scene->Lights();
+ while (++l_iter) {
+ Light* light = l_iter.value();
+ light->TranslateBy(camera_loc);
+ }
+
+ camera->MoveTo(0,0,0);
+}
+
+// +--------------------------------------------------------------------+
+// Translate all objects and lights back to original positions:
+// +--------------------------------------------------------------------+
+
+void
+CameraView::UnTranslateScene()
+{
+ Point reloc = -camera_loc;
+
+ ListIter<Graphic> g_iter = scene->Graphics();
+ while (++g_iter) {
+ Graphic* graphic = g_iter.value();
+
+ if (!graphic->IsInfinite())
+ graphic->TranslateBy(reloc);
+ }
+
+ g_iter.attach(scene->Foreground());
+ while (++g_iter) {
+ Graphic* graphic = g_iter.value();
+ graphic->TranslateBy(reloc);
+ }
+
+ g_iter.attach(scene->Sprites());
+ while (++g_iter) {
+ Graphic* graphic = g_iter.value();
+
+ if (!graphic->IsInfinite())
+ graphic->TranslateBy(reloc);
+ }
+
+ ListIter<Light> l_iter = scene->Lights();
+ while (++l_iter) {
+ Light* light = l_iter.value();
+ light->TranslateBy(reloc);
+ }
+
+ camera->MoveTo(camera_loc);
+}
+
+// +--------------------------------------------------------------------+
+// Mark visible objects
+// +--------------------------------------------------------------------+
+
+void
+CameraView::MarkVisibleObjects()
+{
+ projector.StartFrame();
+ graphics.clear();
+
+ ListIter<Graphic> graphic_iter = scene->Graphics();
+ while (++graphic_iter) {
+ Graphic* graphic = graphic_iter.value();
+
+ if (graphic->Hidden())
+ continue;
+
+ if (graphic->CheckVisibility(projector)) {
+ graphic->Update();
+ graphics.append(graphic);
+ }
+ else {
+ graphic->ProjectScreenRect(0);
+ }
+ }
+}
+
+void
+CameraView::MarkVisibleLights(Graphic* graphic, DWORD flags)
+{
+ if (flags < Graphic::RENDER_FIRST_LIGHT) {
+ flags = flags | Graphic::RENDER_FIRST_LIGHT | Graphic::RENDER_ADD_LIGHT;
+ }
+
+ if (graphic->IsVisible()) {
+ Vec3 eye = camera->Pos();
+
+ ListIter<Light> light_iter = scene->Lights();
+
+ while (++light_iter) {
+ Light* light = light_iter.value();
+ bool bright_enough = light->Type() == Light::LIGHT_DIRECTIONAL ||
+ light->Intensity() >= 1e9;
+
+ if (!bright_enough) {
+ Point test = graphic->Location() - light->Location();
+ if (test.length() < light->Intensity()*10)
+ bright_enough = true;
+ }
+
+ // turn off lights that won't be used this pass:
+ if (light->CastsShadow()) {
+ if ((flags & Graphic::RENDER_ADD_LIGHT) == 0)
+ bright_enough = false;
+ }
+ else {
+ if ((flags & Graphic::RENDER_FIRST_LIGHT) == 0)
+ bright_enough = false;
+ }
+
+ double obs_radius = graphic->Radius();
+ if (obs_radius < 100)
+ obs_radius = 100;
+
+ light->SetActive(bright_enough);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraView::RenderBackground()
+{
+ if (scene->Background().isEmpty()) return;
+
+ video->SetRenderState(Video::FILL_MODE, Video::FILL_SOLID);
+ video->SetRenderState(Video::Z_ENABLE, FALSE);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, FALSE);
+ video->SetRenderState(Video::STENCIL_ENABLE, FALSE);
+ video->SetRenderState(Video::LIGHTING_ENABLE, TRUE);
+
+ // solid items:
+ ListIter<Graphic> iter = scene->Background();
+ while (++iter) {
+ Graphic* g = iter.value();
+
+ if (!g->Hidden())
+ Render(g, Graphic::RENDER_SOLID);
+ }
+
+ // blended items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+
+ if (!g->Hidden())
+ Render(g, Graphic::RENDER_ALPHA);
+ }
+
+ // glowing items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+
+ if (!g->Hidden())
+ Render(g, Graphic::RENDER_ADDITIVE);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraView::RenderForeground()
+{
+ bool foregroundVisible = false;
+
+ ListIter<Graphic> iter = scene->Foreground();
+ while (++iter && !foregroundVisible) {
+ Graphic* g = iter.value();
+ if (g && !g->Hidden())
+ foregroundVisible = true;
+ }
+
+ if (!foregroundVisible)
+ return;
+
+ video->SetRenderState(Video::FILL_MODE, Video::FILL_SOLID);
+ video->SetRenderState(Video::Z_ENABLE, TRUE);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, TRUE);
+ video->SetRenderState(Video::STENCIL_ENABLE, FALSE);
+ video->SetRenderState(Video::LIGHTING_ENABLE, TRUE);
+ video->SetProjection((float) GetFieldOfView(), 1.0f, 1.0e6f, projection_type);
+
+ if (video->IsShadowEnabled() || video->IsBumpMapEnabled()) {
+ // solid items, ambient and non-shadow lights:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+ Render(g, Graphic::RENDER_SOLID | Graphic::RENDER_FIRST_LIGHT);
+ }
+
+ video->SetAmbient(Color::Black);
+ video->SetRenderState(Video::LIGHTING_PASS, 2);
+
+ // solid items, shadow lights:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+ Render(g, Graphic::RENDER_SOLID | Graphic::RENDER_ADD_LIGHT);
+ }
+ }
+
+ else {
+ // solid items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+ Render(g, Graphic::RENDER_SOLID);
+ }
+ }
+
+ video->SetAmbient(scene->Ambient());
+ video->SetRenderState(Video::LIGHTING_PASS, 0);
+ video->SetRenderState(Video::STENCIL_ENABLE, FALSE);
+ video->SetRenderState(Video::Z_ENABLE, TRUE);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, FALSE);
+
+ // blended items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+ Render(g, Graphic::RENDER_ALPHA);
+ g->ProjectScreenRect(&projector);
+ }
+
+ // glowing items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+ Render(g, Graphic::RENDER_ADDITIVE);
+ g->ProjectScreenRect(&projector);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraView::RenderSprites()
+{
+ if (scene->Sprites().isEmpty()) return;
+
+ video->SetRenderState(Video::FILL_MODE, Video::FILL_SOLID);
+ video->SetRenderState(Video::Z_ENABLE, TRUE);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, FALSE);
+ video->SetRenderState(Video::STENCIL_ENABLE, FALSE);
+ video->SetRenderState(Video::LIGHTING_ENABLE, TRUE);
+
+ // compute depth:
+ ListIter<Graphic> iter = scene->Sprites();
+ while (++iter) {
+ Graphic* g = iter.value();
+ if (g && g->IsVisible() && !g->Hidden()) {
+ FindDepth(g);
+ }
+ }
+
+ // sort the list:
+ scene->Sprites().sort();
+
+ // blended items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+ Render(g, Graphic::RENDER_ALPHA);
+ }
+
+ // glowing items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+ Render(g, Graphic::RENDER_ADDITIVE);
+ }
+}
+
+// +--------------------------------------------------------------------+
+// Render the whole scene, sorted back to front
+// +--------------------------------------------------------------------+
+
+void
+CameraView::RenderScene()
+{
+ if (graphics.isEmpty()) return;
+
+ int i = 0;
+ int ngraphics = graphics.size();
+
+ // compute depth:
+ ListIter<Graphic> iter = graphics;
+ while (++iter) {
+ Graphic* g = iter.value();
+ if (g && !g->Hidden()) {
+ FindDepth(g);
+
+ if (g->IsSolid()) {
+ Solid* solid = (Solid*) g;
+
+ solid->SelectDetail(&projector);
+
+ if (video->IsShadowEnabled()) {
+ MarkVisibleLights(solid, Graphic::RENDER_ADD_LIGHT);
+ solid->UpdateShadows(scene->Lights());
+ }
+ }
+ }
+ }
+
+ // sort the list:
+ graphics.sort();
+
+ Graphic* g = graphics.last();
+ if (g->Depth() > 5e6) {
+ RenderSceneObjects(true);
+ video->ClearDepthBuffer();
+ }
+
+ RenderSceneObjects(false);
+}
+
+void
+CameraView::RenderSceneObjects(bool distant)
+{
+ ListIter<Graphic> iter = graphics;
+
+ video->SetAmbient(scene->Ambient());
+ video->SetRenderState(Video::FILL_MODE, Video::FILL_SOLID);
+ video->SetRenderState(Video::Z_ENABLE, TRUE);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, TRUE);
+ video->SetRenderState(Video::LIGHTING_ENABLE, TRUE);
+
+ if (distant)
+ video->SetProjection((float) GetFieldOfView(), 5.0e6f, 1.0e12f, projection_type);
+ else
+ video->SetProjection((float) GetFieldOfView(), 1.0f, 1.0e6f, projection_type);
+
+ if (video->IsShadowEnabled() || video->IsBumpMapEnabled()) {
+ // solid items, ambient and non-shadow lights:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+
+ if (distant && g->Depth() > 5e6 || !distant && g->Depth() < 5e6) {
+ Render(g, Graphic::RENDER_SOLID | Graphic::RENDER_FIRST_LIGHT);
+ }
+ }
+
+ // send shadows to stencil buffer:
+ if (video->IsShadowEnabled()) {
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+ if (distant && g->Depth() > 5e6 || !distant && g->Depth() < 5e6) {
+ if (g->IsSolid()) {
+ Solid* solid = (Solid*) g;
+
+ ListIter<Shadow> shadow_iter = solid->GetShadows();
+ while (++shadow_iter) {
+ Shadow* shadow = shadow_iter.value();
+ shadow->Render(video);
+ }
+ }
+ }
+ }
+ }
+
+ video->SetAmbient(Color::Black);
+ video->SetRenderState(Video::LIGHTING_PASS, 2);
+ video->SetRenderState(Video::STENCIL_ENABLE, TRUE);
+
+ // solid items, shadow lights:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+
+ if (distant && g->Depth() > 5e6 || !distant && g->Depth() < 5e6) {
+ Render(g, Graphic::RENDER_SOLID | Graphic::RENDER_ADD_LIGHT);
+ }
+ }
+ }
+
+ else {
+ // solid items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+
+ if (distant && g->Depth() > 5e6 || !distant && g->Depth() < 5e6) {
+ Render(g, Graphic::RENDER_SOLID);
+ }
+ }
+ }
+
+ video->SetAmbient(scene->Ambient());
+ video->SetRenderState(Video::LIGHTING_PASS, 0);
+ video->SetRenderState(Video::STENCIL_ENABLE, FALSE);
+ video->SetRenderState(Video::Z_ENABLE, TRUE);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, FALSE);
+
+ // blended items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+
+ if (distant && g->Depth() > 5e6 || !distant && g->Depth() < 5e6) {
+ Render(g, Graphic::RENDER_ALPHA);
+ g->ProjectScreenRect(&projector);
+ }
+ }
+
+ // glowing items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+
+ if (distant && g->Depth() > 5e6 || !distant && g->Depth() < 5e6) {
+ Render(g, Graphic::RENDER_ADDITIVE);
+ g->ProjectScreenRect(&projector);
+ }
+ }
+}
+
+void
+CameraView::Render(Graphic* g, DWORD flags)
+{
+ if (g && g->IsVisible() && !g->Hidden()) {
+ if (g->IsSolid()) {
+ MarkVisibleLights(g, flags);
+ video->SetLights(scene->Lights());
+ }
+
+ g->Render(video, flags);
+ }
+}
+
+// +--------------------------------------------------------------------+
+// Draw the lens flare effect, if enabled and light source visible
+// +--------------------------------------------------------------------+
+
+void
+CameraView::RenderLensFlare()
+{
+ if (!lens_flare_enable || lens_flare_dim < 0.01)
+ return;
+
+ if (!halo_bitmap)
+ return;
+
+ video->SetRenderState(Video::STENCIL_ENABLE, FALSE);
+ video->SetRenderState(Video::Z_ENABLE, FALSE);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, FALSE);
+
+ Vec3 flare_pos;
+ Vec3 sun_pos;
+ Vec3 center((float)width/2.0f, (float)height/2.0f, 1.0f);
+ int flare_visible = 0;
+
+ ListIter<Light> light_iter = scene->Lights();
+ while (++light_iter) {
+ Light* light = light_iter.value();
+
+ if (!light->IsActive())
+ continue;
+
+ if (light->Type() == Light::LIGHT_DIRECTIONAL && light->Intensity() < 1)
+ continue;
+
+ double distance = (light->Location()-camera->Pos()).length();
+
+ // only do lens flare for the sun:
+ if (distance > 1e9) {
+ if (projector.IsVisible(light->Location(), 1.0f)) {
+ // FOUND IT: TRANSFORM/PROJECT FLARE LOCATION
+ Point sun_pos = light->Location();
+
+ if (light->CastsShadow() && scene->IsLightObscured(camera->Pos(), sun_pos, -1))
+ continue;
+
+ projector.Transform(sun_pos);
+
+ if (sun_pos.z < 100)
+ continue;
+
+ projector.Project(sun_pos, false);
+
+ int x = (int) (sun_pos.x);
+ int y = (int) (sun_pos.y);
+ int w = (int) (window->Width() / 4.0);
+ int h = w;
+
+ // halo:
+ window->DrawBitmap(x-w,y-h,x+w,y+h, halo_bitmap, Video::BLEND_ADDITIVE);
+
+ // lens elements:
+ if (elem_bitmap[0]) {
+ Point vector = center - sun_pos;
+ float vlen = (float) vector.length();
+ vector.Normalize();
+
+ static int nelem = 12;
+ static int elem_indx[] = { 0, 1, 1, 1, 0, 0, 0, 0, 2, 0, 0, 2 };
+ static float elem_dist[] = { -0.2f, 0.5f, 0.55f, 0.62f, 1.23f, 1.33f, 1.35f, 0.8f, 0.9f, 1.4f, 1.7f, 1.8f };
+ static float elem_size[] = { 0.3f, 0.2f, 0.4f, 0.3f, 0.4f, 0.2f, 0.6f, 0.1f, 0.1f, 1.6f, 1.0f, 0.2f };
+
+ for (int elem = 0; elem < nelem; elem++) {
+ Bitmap* img = elem_bitmap[elem_indx[elem]];
+
+ /***
+ if (elem == 10)
+ shade *= 0.5;
+ ***/
+
+ if (img == 0)
+ img = elem_bitmap[0];
+
+ flare_pos = sun_pos + (vector * elem_dist[elem] * vlen);
+ x = (int) (flare_pos.x);
+ y = (int) (flare_pos.y);
+ w = (int) (window->Width() / 8.0 * elem_size[elem]);
+ h = w;
+
+ window->DrawBitmap(x-w,y-h,x+w,y+h, img, Video::BLEND_ADDITIVE);
+ }
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+// Rotate and translate a plane in world space to view space.
+// +--------------------------------------------------------------------+
+
+void
+CameraView::WorldPlaneToView(Plane& plane)
+{
+ // Determine the distance from the viewpoint
+ Vec3 tnormal = plane.normal;
+
+ if (!infinite)
+ plane.distance -= (float) (camera->Pos() * tnormal);
+
+ // Rotate the normal into view orientation
+ plane.normal.x = tnormal * cvrt;
+ plane.normal.y = tnormal * cvup;
+ plane.normal.z = tnormal * cvpn;
+}
+
+void
+CameraView::SetDepthScale(float scale)
+{
+ projector.SetDepthScale(scale);
+}
diff --git a/nGenEx/CameraView.h b/nGenEx/CameraView.h
new file mode 100644
index 0000000..493e4f7
--- /dev/null
+++ b/nGenEx/CameraView.h
@@ -0,0 +1,114 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: CameraView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ 3D Projection Camera View class
+*/
+
+#ifndef CameraView_h
+#define CameraView_h
+
+#include "Types.h"
+#include "View.h"
+#include "Camera.h"
+#include "Projector.h"
+#include "Video.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Video;
+class Scene;
+class Bitmap;
+
+class Graphic;
+
+// +--------------------------------------------------------------------+
+
+class CameraView : public View
+{
+public:
+ static const char* TYPENAME() { return "CameraView"; }
+
+ CameraView(Window* c, Camera* cam, Scene* s);
+ virtual ~CameraView();
+
+ // Operations:
+ virtual void Refresh();
+ virtual void OnWindowMove();
+ virtual void UseCamera(Camera* cam);
+ virtual void UseScene(Scene* scene);
+ virtual void LensFlareElements(Bitmap* halo, Bitmap* e1=0, Bitmap* e2=0, Bitmap* e3=0);
+ virtual void LensFlare(int on, double dim = 1);
+ virtual void SetDepthScale(float scale);
+
+ // accessors:
+ Camera* GetCamera() const { return camera; }
+ Projector* GetProjector() { return &projector; }
+ Scene* GetScene() const { return scene; }
+ virtual void SetFieldOfView(double fov);
+ virtual double GetFieldOfView() const;
+ virtual void SetProjectionType(DWORD pt);
+ virtual DWORD GetProjectionType() const;
+
+ Point Pos() const { return camera->Pos(); }
+ Point vrt() { return camera->vrt(); }
+ Point vup() { return camera->vup(); }
+ Point vpn() { return camera->vpn(); }
+ const Matrix& Orientation() const { return camera->Orientation(); }
+
+ Point SceneOffset() const { return camera_loc; }
+
+ // projection and clipping geometry:
+ virtual void TranslateScene();
+ virtual void UnTranslateScene();
+ virtual void MarkVisibleObjects();
+ virtual void MarkVisibleLights(Graphic* g, DWORD flags);
+
+ virtual void RenderScene();
+ virtual void RenderSceneObjects(bool distant=false);
+ virtual void RenderForeground();
+ virtual void RenderBackground();
+ virtual void RenderSprites();
+ virtual void RenderLensFlare();
+ virtual void Render(Graphic* g, DWORD flags);
+
+ virtual void FindDepth(Graphic* g);
+ virtual int SetInfinite(int i);
+
+protected:
+ Camera* camera;
+ Scene* scene;
+ Video* video;
+
+ virtual void WorldPlaneToView(Plane& plane);
+
+ Point camera_loc;
+ Vec3 cvrt;
+ Vec3 cvup;
+ Vec3 cvpn;
+
+ Projector projector;
+ int infinite;
+ int width;
+ int height;
+ DWORD projection_type;
+
+ // lens flare:
+ int lens_flare_enable;
+ double lens_flare_dim;
+ Bitmap* halo_bitmap;
+ Bitmap* elem_bitmap[3];
+
+ List<Graphic> graphics;
+};
+
+#endif CameraView_h
+
diff --git a/nGenEx/Color.cpp b/nGenEx/Color.cpp
new file mode 100644
index 0000000..41657eb
--- /dev/null
+++ b/nGenEx/Color.cpp
@@ -0,0 +1,682 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Color.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Universal Color Format class
+*/
+
+#include "MemDebug.h"
+#include "Color.h"
+#include "Video.h"
+#include "PCX.h"
+#include "Fix.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+Color Color::White = Color(255,255,255);
+Color Color::Black = Color( 0, 0, 0);
+Color Color::Gray = Color(128,128,128);
+Color Color::LightGray = Color(192,192,192);
+Color Color::DarkGray = Color( 64, 64, 64);
+Color Color::BrightRed = Color(255, 0, 0);
+Color Color::BrightBlue = Color( 0, 0,255);
+Color Color::BrightGreen= Color( 0,255, 0);
+Color Color::DarkRed = Color(128, 0, 0);
+Color Color::DarkBlue = Color( 0, 0,128);
+Color Color::DarkGreen = Color( 0,128, 0);
+Color Color::Yellow = Color(255,255, 0);
+Color Color::Cyan = Color( 0,255,255);
+Color Color::Magenta = Color(255, 0,255);
+Color Color::Tan = Color(180,150,120);
+Color Color::Brown = Color(128,100, 80);
+Color Color::Violet = Color(128, 0,128);
+Color Color::Orange = Color(255,150, 20);
+
+bool Color::standard_format = false;
+int Color::texture_alpha_level = 0;
+ColorFormat Color::format = ColorFormat(256);
+ColorFormat Color::texture_format[4] = { ColorFormat(256), ColorFormat(256), ColorFormat(256), ColorFormat(256) };
+PALETTEENTRY Color::palette[256];
+BYTE Color::table[32768];
+double Color::fade = 1.0;
+Color Color::fade_color;
+Video* Color::video = 0;
+DWORD ColorIndex::texture_palettes[4][256];
+DWORD ColorIndex::unfaded_palette[256];
+DWORD ColorIndex::formatted_palette[256];
+DWORD ColorIndex::shade_table[256*256];
+BYTE ColorIndex::blend_table[256*256];
+DWORD* ColorIndex::texture_palette = ColorIndex::texture_palettes[0];
+
+// +--------------------------------------------------------------------+
+
+Color::Color(BYTE index)
+{
+ PALETTEENTRY* p = &palette[index];
+
+ Set(p->peRed, p->peGreen, p->peBlue);
+}
+
+// +--------------------------------------------------------------------+
+
+Color&
+Color::operator+=(const Color& c)
+{
+ int r = Red() + c.Red(); if (r > 255) r = 255;
+ int g = Green() + c.Green(); if (g > 255) g = 255;
+ int b = Blue() + c.Blue(); if (b > 255) b = 255;
+
+ Set((BYTE) r, (BYTE) g, (BYTE) b);
+ return *this;
+}
+
+Color
+Color::operator+(DWORD d) const
+{
+ int r = Red() + ((d & RMask) >> RShift); if (r > 255) r = 255;
+ int g = Green() + ((d & GMask) >> GShift); if (g > 255) g = 255;
+ int b = Blue() + ((d & BMask) >> BShift); if (b > 255) b = 255;
+
+ return Color((BYTE) r,(BYTE) g,(BYTE) b);
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Color::operator+(const Color& c) const
+{
+ float src_alpha = c.fAlpha();
+ float dst_alpha = 1.0f - src_alpha;
+
+ BYTE r = (BYTE)((fRed() *dst_alpha + c.fRed() *src_alpha)*255.0f);
+ BYTE g = (BYTE)((fGreen()*dst_alpha + c.fGreen()*src_alpha)*255.0f);
+ BYTE b = (BYTE)((fBlue() *dst_alpha + c.fBlue() *src_alpha)*255.0f);
+
+ return Color(r, g, b);
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Color::operator*(const Color& c) const
+{
+ BYTE r = (BYTE) ((fRed() * c.fRed()) *255.0f);
+ BYTE g = (BYTE) ((fGreen() * c.fGreen()) *255.0f);
+ BYTE b = (BYTE) ((fBlue() * c.fBlue()) *255.0f);
+
+ return Color(r, g, b);
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Color::operator*(double scale) const
+{
+ int r = fast_f2i(Red() * scale); if (r > 255) r = 255;
+ int g = fast_f2i(Green() * scale); if (g > 255) g = 255;
+ int b = fast_f2i(Blue() * scale); if (b > 255) b = 255;
+ int a = fast_f2i(Alpha() * scale); if (a > 255) a = 255;
+
+ return Color((BYTE) r, (BYTE) g, (BYTE) b, (BYTE) a);
+}
+
+Color
+Color::dim(double scale) const
+{
+ int r = fast_f2i(Red() * scale);
+ int g = fast_f2i(Green() * scale);
+ int b = fast_f2i(Blue() * scale);
+
+ return Color((BYTE) r, (BYTE) g, (BYTE) b, (BYTE) Alpha());
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+Color::Formatted() const
+{
+ if (format.pal) {
+ return Index();
+ }
+
+ else {
+ if (fade != 1.0) {
+ double step = (1.0 - fade);
+
+ DWORD r = ((int) ((fRed() - (fRed() - fade_color.fRed()) * step)*255.0)) >> format.rdown;
+ DWORD g = ((int) ((fGreen() - (fGreen() - fade_color.fGreen())* step)*255.0)) >> format.gdown;
+ DWORD b = ((int) ((fBlue() - (fBlue() - fade_color.fBlue()) * step)*255.0)) >> format.bdown;
+ DWORD a = Alpha()>>format.adown;
+
+ return (r<<format.rshift)|(g<<format.gshift)|(b<<format.bshift)|(a<<format.ashift);
+
+ }
+
+ else if (standard_format) {
+ return rgba;
+ }
+
+ else {
+ DWORD r = Red() >>format.rdown;
+ DWORD g = Green()>>format.gdown;
+ DWORD b = Blue() >>format.bdown;
+ DWORD a = Alpha()>>format.adown;
+
+ return (r<<format.rshift)|(g<<format.gshift)|(b<<format.bshift)|(a<<format.ashift);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Color::Faded() const
+{
+ if (fade != 1.0) {
+ double step = (1.0 - fade);
+
+ DWORD r = ((int) ((fRed() - (fRed() - fade_color.fRed()) * step)*255.0));
+ DWORD g = ((int) ((fGreen() - (fGreen() - fade_color.fGreen())* step)*255.0));
+ DWORD b = ((int) ((fBlue() - (fBlue() - fade_color.fBlue()) * step)*255.0));
+ DWORD a = Alpha();
+
+ return Color((BYTE) r, (BYTE) g, (BYTE) b, (BYTE) a);
+
+ }
+
+ else {
+ return *this;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+Color::Unfaded() const
+{
+ if (standard_format) {
+ return rgba;
+ }
+
+ if (format.pal) {
+ return Index();
+ }
+ else {
+ DWORD r = Red() >>format.rdown;
+ DWORD g = Green()>>format.gdown;
+ DWORD b = Blue() >>format.bdown;
+ DWORD a = Alpha()>>format.adown;
+
+ return (r<<format.rshift)|(g<<format.gshift)|(b<<format.bshift)|(a<<format.ashift);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+Color::TextureFormat(int keep_alpha) const
+{
+ if (texture_format[texture_alpha_level].pal) {
+ return Index();
+ }
+ else if (rgba == 0) {
+ return 0;
+ }
+ else {
+ ColorFormat& tf = texture_format[texture_alpha_level];
+
+ DWORD r = Red();
+ DWORD g = Green();
+ DWORD b = Blue();
+ DWORD a = 0;
+
+ if (keep_alpha) {
+ a = Alpha()>>tf.adown;
+ }
+
+ else if (texture_alpha_level == 1) {
+ // transparent:
+ a = 255>>tf.adown;
+ }
+
+ else if (texture_alpha_level == 2) {
+ // translucent:
+ if (r || g || b)
+ a = ((r+g+b+255)>>2)>>tf.adown;
+ }
+
+ r = r >>tf.rdown;
+ g = g >>tf.gdown;
+ b = b >>tf.bdown;
+
+ return (r<<tf.rshift)|(g<<tf.gshift)|(b<<tf.bshift)|(a<<tf.ashift);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Color::ShadeColor(int shade) const
+{
+ double fr = fRed(), sr = fr;
+ double fg = fGreen(), sg = fg;
+ double fb = fBlue(), sb = fb;
+ double range = SHADE_LEVELS;
+
+ // first shade:
+ if (shade < SHADE_LEVELS) { // shade towards black
+ sr = fr * (shade/range);
+ sg = fg * (shade/range);
+ sb = fb * (shade/range);
+ }
+ else if (shade > SHADE_LEVELS) { // shade towards white
+ double step = (shade - range)/range;
+
+ sr = fr - (fr - 1.0) * step;
+ sg = fg - (fg - 1.0) * step;
+ sb = fb - (fb - 1.0) * step;
+ }
+
+ return Color((BYTE) (sr*255.0), (BYTE) (sg*255.0), (BYTE) (sb*255.0), (BYTE) Alpha());
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+Color::Shaded(int shade) const
+{
+ return ShadeColor(shade).Formatted();
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Color::Unformat(DWORD formatted_color)
+{
+ if (format.pal) {
+ return Color((BYTE) formatted_color);
+ }
+ else if (standard_format) {
+ Color c;
+ c.Set(formatted_color);
+ return c;
+ }
+ else {
+ BYTE r = (BYTE) ((formatted_color & format.rmask)>>format.rshift) << format.rdown;
+ BYTE g = (BYTE) ((formatted_color & format.gmask)>>format.gshift) << format.gdown;
+ BYTE b = (BYTE) ((formatted_color & format.bmask)>>format.bshift) << format.bdown;
+ BYTE a = (BYTE) ((formatted_color & format.amask)>>format.ashift) << format.adown;
+
+ return Color(r,g,b,a);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Color::Scale(const Color& c1, const Color& c2, double scale)
+{
+ BYTE r = (BYTE) ((c1.fRed() + (c2.fRed() - c1.fRed() )*scale) * 255);
+ BYTE g = (BYTE) ((c1.fGreen() + (c2.fGreen() - c1.fGreen())*scale) * 255);
+ BYTE b = (BYTE) ((c1.fBlue() + (c2.fBlue() - c1.fBlue() )*scale) * 255);
+ BYTE a = (BYTE) ((c1.fAlpha() + (c2.fAlpha() - c1.fAlpha())*scale) * 255);
+
+ return Color(r,g,b,a);
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+Color::FormattedBlend(DWORD c1, DWORD c2)
+{
+ if (format.pal) {
+ return ColorIndex::blend_table[(BYTE) c1 * 256 + (BYTE) c2];
+ }
+ else {
+ ColorFormat& cf = format;
+
+ DWORD r = (c1 & cf.rmask) + (c2 & cf.rmask);
+ DWORD g = (c1 & cf.gmask) + (c2 & cf.gmask);
+ DWORD b = (c1 & cf.bmask) + (c2 & cf.bmask);
+
+ if (r & ~cf.rmask) r = cf.rmask;
+ if (g & ~cf.gmask) g = cf.gmask;
+ if (b & ~cf.bmask) b = cf.bmask;
+
+ return (DWORD) (r|g|b);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::UseVideo(Video* v)
+{
+ video = v;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::UseFormat(const ColorFormat& cf)
+{
+ format = cf;
+
+ if (format.rmask == RMask && format.gmask == GMask && format.bmask == BMask)
+ standard_format = true;
+ else
+ standard_format = false;
+
+ if (cf.pal) {
+ for (int i = 0; i < 256; i++) {
+ ColorIndex::formatted_palette[i] = i;
+ ColorIndex::unfaded_palette[i] = i;
+ }
+ }
+ else {
+ double old_fade = fade;
+
+ for (int i = 0; i < 256; i++) {
+ ColorIndex::formatted_palette[i] = Color(i).Formatted();
+
+ fade = 1.0;
+ ColorIndex::unfaded_palette[i] = Color(i).Formatted();
+ fade = old_fade;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::UseTextureFormat(const ColorFormat& cf, int alpha_level)
+{
+ texture_format[alpha_level] = cf;
+
+ if (cf.pal) {
+ for (int i = 0; i < 256; i++) {
+ ColorIndex::texture_palettes[alpha_level][i] = i;
+ }
+ }
+ else {
+ double old_fade = fade;
+
+ for (int i = 0; i < 256; i++) {
+ int old_texture_alpha_level = texture_alpha_level;
+ texture_alpha_level = alpha_level;
+ ColorIndex::texture_palettes[alpha_level][i] = Color(i).TextureFormat();
+ texture_alpha_level = old_texture_alpha_level;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::WithTextureFormat(int alpha_level)
+{
+ texture_alpha_level = alpha_level;
+ ColorIndex::texture_palette = ColorIndex::texture_palettes[alpha_level];
+}
+
+// +--------------------------------------------------------------------+
+
+static BYTE MatchRGB(PALETTEENTRY* pal, BYTE r, BYTE g, BYTE b)
+{
+ double mindist = 100000000.0;
+ BYTE match = 0;
+
+ for (int i = 0; i < 256; i++) {
+ PALETTEENTRY* p = pal++;
+
+ double dr = p->peRed - r;
+ double dg = p->peGreen - g;
+ double db = p->peBlue - b;
+
+ double d = (dr*dr) + (dg*dg) + (db*db);
+
+ if (d < mindist) {
+ mindist = d;
+ match = i;
+
+ if (d < 1.0)
+ return match;
+ }
+ }
+
+ return match;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::SetPalette(PALETTEENTRY* pal, int palsize, BYTE* invpal)
+{
+ for (int i = 0; i < palsize; i++)
+ palette[i] = pal[i];
+
+ if (invpal) {
+ for (i = 0; i < 32768; i++)
+ table[i] = invpal[i];
+ }
+ else {
+ for (i = 0; i < 32768; i++) {
+ BYTE r = (i >> 10) & 0x1f;
+ BYTE g = (i >> 5) & 0x1f;
+ BYTE b = (i ) & 0x1f;
+
+ Color c(r<<3, g<<3, b<<3);
+
+ table[i] = MatchRGB(palette, r<<3, g<<3, b<<3);
+ }
+ }
+
+ // set up formatted palette:
+ UseFormat(format);
+
+ for (i = 0; i < 4; i++)
+ UseTextureFormat(texture_format[i], i);
+
+ // set up shade table:
+ double old_fade = fade;
+ fade = 1.0;
+ BuildShadeTable();
+ fade = old_fade;
+
+ // and blend table:
+ BuildBlendTable();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::SavePalette(const char* basename)
+{
+ char filename[256];
+
+ sprintf(filename, "%s.ipl", basename);
+ FILE* f = fopen(filename, "wb");
+ if (f) {
+ fwrite(table, sizeof(table), 1, f);
+ fclose(f);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::SetFade(double f, Color c, int build_shade)
+{
+ static int shade_built = 0;
+
+ if (fade == f && fade_color == c && (build_shade ? shade_built : 1))
+ return;
+
+ fade = f;
+ fade_color = c;
+
+ // set up formatted palette:
+ UseFormat(format);
+
+ // if this is a paletted video mode,
+ // modify the video palette as well:
+ if (format.pal && video) {
+ PALETTEENTRY fade_palette[256];
+
+ double step = (1.0 - fade);
+ for (int i = 0; i < 256; i++) {
+ PALETTEENTRY& entry = fade_palette[i];
+ ColorIndex c = ColorIndex(i);
+
+ entry.peRed = ((int) ((c.fRed() - (c.fRed() - fade_color.fRed()) * step)*255.0));
+ entry.peGreen = ((int) ((c.fGreen() - (c.fGreen() - fade_color.fGreen())* step)*255.0));
+ entry.peBlue = ((int) ((c.fBlue() - (c.fBlue() - fade_color.fBlue()) * step)*255.0));
+ entry.peFlags = 0;
+ }
+ }
+
+ // otherwise, we need to re-compute
+ // the shade table:
+ else {
+ if (build_shade) {
+ BuildShadeTable();
+ shade_built = 1;
+ }
+ else {
+ shade_built = 0;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::BuildShadeTable()
+{
+ for (int shade = 0; shade < SHADE_LEVELS*2; shade++)
+ for (int index = 0; index < 256; index++)
+ ColorIndex::shade_table[shade*256+index] = Color(index).Shaded(shade);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::BuildBlendTable()
+{
+ for (int src = 0; src < 256; src++) {
+ for (int dst = 0; dst < 256; dst++) {
+ ColorIndex src_clr = ColorIndex(src);
+ ColorIndex dst_clr = ColorIndex(dst);
+
+ int r = src_clr.Red() + dst_clr.Red();
+ int g = src_clr.Green() + dst_clr.Green();
+ int b = src_clr.Blue() + dst_clr.Blue();
+
+ if (r>255) r=255;
+ if (g>255) g=255;
+ if (b>255) b=255;
+
+ ColorIndex::blend_table[src*256+dst] = Color((BYTE)r,(BYTE)g,(BYTE)b).Index();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::SaveShadeTable(const char* basename)
+{
+ if (!format.pal)
+ return;
+
+ char filename[256];
+ sprintf(filename, "%s_clut.pcx", basename);
+
+ BYTE clut[256*256];
+ BYTE* pc = clut;
+
+ for (int i = 0; i < SHADE_LEVELS*2; i++)
+ for (int j = 0; j < 256; j++)
+ *pc++ = (BYTE) ColorIndex::shade_table[i*256+j];
+
+ for (; i < 256; i++)
+ for (int j = 0; j < 256; j++)
+ *pc++ = (BYTE) 0;
+
+ PcxImage pcx(256, 256, clut, (BYTE*) palette);
+ pcx.Save(filename);
+}
+
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+
+ColorValue&
+ColorValue::operator+=(const ColorValue& c)
+{
+ r += c.r;
+ g += c.g;
+ b += c.b;
+
+ return *this;
+}
+
+ColorValue
+ColorValue::operator+(const ColorValue& c) const
+{
+ float src_alpha = c.a;
+ float dst_alpha = 1.0f - a;
+
+ float fr = (r * dst_alpha) + (c.r * src_alpha);
+ float fg = (g * dst_alpha) + (c.g * src_alpha);
+ float fb = (b * dst_alpha) + (c.b * src_alpha);
+
+ return ColorValue(fr, fg, fb);
+}
+
+ColorValue
+ColorValue::operator*(const ColorValue& c) const
+{
+ return ColorValue(r*c.r, g*c.g, b*c.b);
+}
+
+ColorValue
+ColorValue::operator*(double scale) const
+{
+ return ColorValue((float) (r*scale),
+ (float) (g*scale),
+ (float) (b*scale),
+ (float) (a*scale));
+}
+
+ColorValue
+ColorValue::dim(double scale) const
+{
+ return ColorValue((float) (r*scale),
+ (float) (g*scale),
+ (float) (b*scale));
+}
+
+// +--------------------------------------------------------------------+
+
+inline BYTE bclamp(float x) { return (BYTE) ((x<0) ? 0 : (x>255) ? 255 : x); }
+
+Color
+ColorValue::ToColor() const
+{
+ return Color(bclamp(r * 255.0f),
+ bclamp(g * 255.0f),
+ bclamp(b * 255.0f),
+ bclamp(a * 255.0f));
+}
diff --git a/nGenEx/Color.h b/nGenEx/Color.h
new file mode 100644
index 0000000..d719f34
--- /dev/null
+++ b/nGenEx/Color.h
@@ -0,0 +1,295 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Color.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Universal Color Format class
+*/
+
+#ifndef Color_h
+#define Color_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class Video;
+
+// +--------------------------------------------------------------------+
+
+struct ColorFormat
+{
+ ColorFormat(int palsize)
+ : pal(palsize), bpp(8),
+ rdown(0), rshift(0), rmask(0),
+ gdown(0), gshift(0), gmask(0),
+ bdown(0), bshift(0), bmask(0),
+ adown(0), ashift(0), amask(0) { }
+
+ ColorFormat(int size, BYTE r, BYTE rs, BYTE g, BYTE gs, BYTE b, BYTE bs, BYTE a=0, BYTE as=0)
+ : pal(0), bpp(size),
+ rdown(8-r), rshift(rs), rmask(((1<<r)-1)<<rs),
+ gdown(8-g), gshift(gs), gmask(((1<<g)-1)<<gs),
+ bdown(8-b), bshift(bs), bmask(((1<<b)-1)<<bs),
+ adown(8-a), ashift(as), amask(((1<<a)-1)<<as) { }
+
+ int pal;
+ int bpp;
+ BYTE rdown, rshift;
+ BYTE gdown, gshift;
+ BYTE bdown, bshift;
+ BYTE adown, ashift;
+
+ DWORD rmask;
+ DWORD gmask;
+ DWORD bmask;
+ DWORD amask;
+};
+
+// +--------------------------------------------------------------------+
+
+class Color
+{
+ friend class ColorIndex;
+ friend class ColorValue;
+
+public:
+ static const char* TYPENAME() { return "Color"; }
+
+ enum Misc { SHADE_LEVELS = 64 }; // max 128!
+ enum Mask { RMask = 0x00ff0000,
+ GMask = 0x0000ff00,
+ BMask = 0x000000ff,
+ AMask = 0xff000000,
+ RGBMask = 0x00ffffff };
+ enum Shift { RShift = 16,
+ GShift = 8,
+ BShift = 0,
+ AShift = 24 };
+
+ Color() : rgba(0) { }
+ Color(const Color& c) : rgba(c.rgba) { }
+ Color(BYTE r, BYTE g, BYTE b, BYTE a=255) {
+ rgba = (r<<RShift)|(g<<GShift)|(b<<BShift)|(a<<AShift); }
+
+ Color(BYTE index);
+
+ Color& operator= (const Color& c) { rgba = c.rgba; return *this; }
+ int operator==(const Color& c) const { return rgba == c.rgba; }
+ int operator!=(const Color& c) const { return rgba != c.rgba; }
+ Color& operator+=(const Color& c); // simple summation
+
+ Color operator+(DWORD d) const;
+
+ Color operator+(const Color& c) const; // color alpha blending
+ Color operator*(const Color& c) const; // color modulation
+ Color operator*(double scale) const;
+ Color dim(double scale) const;
+
+ void Set(DWORD value) { rgba = value; }
+ void Set(BYTE r, BYTE g, BYTE b, BYTE a=255) {
+ rgba = (r<<RShift)|(g<<GShift)|(b<<BShift)|(a<<AShift); }
+
+ DWORD Value() const { return rgba; }
+
+ void SetRed(BYTE r) { rgba = (rgba & ~RMask) | (r << RShift); }
+ void SetGreen(BYTE g) { rgba = (rgba & ~GMask) | (g << GShift); }
+ void SetBlue(BYTE b) { rgba = (rgba & ~BMask) | (b << BShift); }
+ void SetAlpha(BYTE a) { rgba = (rgba & ~AMask) | (a << AShift); }
+
+ DWORD Red() const { return (rgba & RMask) >> RShift; }
+ DWORD Green() const { return (rgba & GMask) >> GShift; }
+ DWORD Blue() const { return (rgba & BMask) >> BShift; }
+ DWORD Alpha() const { return (rgba & AMask) >> AShift; }
+
+ float fRed() const { return (float)(Red() /255.0); }
+ float fGreen() const { return (float)(Green()/255.0); }
+ float fBlue() const { return (float)(Blue() /255.0); }
+ float fAlpha() const { return (float)(Alpha()/255.0); }
+
+ BYTE Index() const { return table[((rgba&RMask)>>(RShift+3)<<10)|
+ ((rgba&GMask)>>(GShift+3)<< 5)|
+ ((rgba&BMask)>>(BShift+3))]; }
+
+ inline BYTE IndexedBlend(BYTE dst) const;
+ static DWORD FormattedBlend(DWORD c1, DWORD c2);
+
+ DWORD TextureFormat(int keep_alpha=0) const;
+ DWORD Formatted() const;
+ DWORD Unfaded() const;
+ Color ShadeColor(int shade) const;
+ DWORD Shaded(int shade) const;
+ Color Faded() const;
+
+ // some useful colors
+ static Color White;
+ static Color Black;
+ static Color Gray;
+ static Color LightGray;
+ static Color DarkGray;
+ static Color BrightRed;
+ static Color BrightBlue;
+ static Color BrightGreen;
+ static Color DarkRed;
+ static Color DarkBlue;
+ static Color DarkGreen;
+ static Color Yellow;
+ static Color Cyan;
+ static Color Magenta;
+ static Color Tan;
+ static Color Brown;
+ static Color Violet;
+ static Color Orange;
+
+ static void UseVideo(Video* v);
+ static void UseFormat(const ColorFormat& cf);
+ static void UseTextureFormat(const ColorFormat& cf, int alpha_level=0);
+ static void WithTextureFormat(int alpha_level=0);
+ static void SetPalette(PALETTEENTRY* pal, int palsize, BYTE* invpal=0);
+ static void SavePalette(const char* basename);
+ static void SetFade(double f, Color c=Black, int build_shade=false);
+
+ static const ColorFormat& GetTextureFormat(int alpha_level=0) { return texture_format[alpha_level]; }
+ static const ColorFormat& GetScreenFormat() { return format; }
+
+ // indexed color initialization:
+ static void BuildShadeTable();
+ static void SaveShadeTable(const char* basename);
+ static void BuildBlendTable();
+
+ static double GetFade() { return fade; }
+ static Color GetFadeColor() { return fade_color; }
+
+ static Color Scale(const Color& c1, const Color& c2, double scale);
+ static Color Unformat(DWORD formatted_color);
+
+private:
+ DWORD rgba;
+
+ static bool standard_format;
+ static PALETTEENTRY palette[256];
+ static BYTE table[32768];
+ static ColorFormat format;
+ static int texture_alpha_level;
+ static ColorFormat texture_format[4];
+ static double fade;
+ static Color fade_color;
+ static Video* video;
+};
+
+// +--------------------------------------------------------------------+
+
+class ColorValue
+{
+ friend class Color;
+
+public:
+ static const char* TYPENAME() { return "ColorValue"; }
+
+ ColorValue() : r(0), g(0), b(0), a(0) { }
+ ColorValue(const ColorValue& c) : r(c.r), g(c.g), b(c.b), a(c.a) { }
+ ColorValue(const Color& c) : r( c.fRed() ),
+ g( c.fGreen() ),
+ b( c.fBlue() ),
+ a( c.fAlpha() ) { }
+ ColorValue(float ar,
+ float ag,
+ float ab,
+ float aa=1.0f) : r(ar), g(ag), b(ab), a(aa) { }
+
+ int operator==(const ColorValue& c) const { return r==c.r && g==c.g && b==c.b && a==c.a; }
+ int operator!=(const ColorValue& c) const { return r!=c.r || g!=c.g || b!=c.b || a!=c.a; }
+ ColorValue& operator+=(const ColorValue& c); // simple summation
+
+ ColorValue operator+(const ColorValue& c) const; // color alpha blending
+ ColorValue operator*(const ColorValue& c) const; // color modulation
+ ColorValue operator*(double scale) const;
+ ColorValue dim(double scale) const;
+
+ void Set(float ar, float ag, float ab, float aa=1.0f) { r=ar; g=ag; b=ab; a=aa; }
+
+ void SetRed(float ar) { r=ar; }
+ void SetGreen(float ag) { g=ag; }
+ void SetBlue(float ab) { b=ab; }
+ void SetAlpha(float aa) { a=aa; }
+
+ float Red() const { return r; }
+ float Green() const { return g; }
+ float Blue() const { return b; }
+ float Alpha() const { return a; }
+
+ Color ToColor() const;
+ DWORD TextureFormat(int keep_alpha=0) const { return ToColor().TextureFormat(keep_alpha); }
+ DWORD Formatted() const { return ToColor().Formatted(); }
+ DWORD Unfaded() const { return ToColor().Unfaded(); }
+ Color ShadeColor(int shade) const { return ToColor().ShadeColor(shade); }
+ DWORD Shaded(int shade) const { return ToColor().Shaded(shade); }
+ Color Faded() const { return ToColor().Faded(); }
+
+private:
+ float r, g, b, a;
+};
+
+// +--------------------------------------------------------------------+
+
+class ColorIndex
+{
+ friend class Color;
+
+public:
+ static const char* TYPENAME() { return "ColorIndex"; }
+
+ ColorIndex() : index(0) { }
+ ColorIndex(const ColorIndex& c) : index(c.index) { }
+ ColorIndex(BYTE r, BYTE g, BYTE b) { index = Color(r,g,b).Index(); }
+ ColorIndex(BYTE i) : index(i) { }
+
+ ColorIndex& operator= (const ColorIndex& c) { index = c.index; return *this; }
+ int operator==(const ColorIndex& c) const { return index == c.index; }
+ int operator!=(const ColorIndex& c) const { return index != c.index; }
+
+ BYTE Index() const { return index; }
+
+ DWORD Red() const { return Color::palette[index].peRed; }
+ DWORD Green() const { return Color::palette[index].peGreen; }
+ DWORD Blue() const { return Color::palette[index].peBlue; }
+
+ float fRed() const { return (float)(Red() /255.0); }
+ float fGreen() const { return (float)(Green()/255.0); }
+ float fBlue() const { return (float)(Blue() /255.0); }
+
+ DWORD TextureFormat() const { return texture_palette[index]; }
+ DWORD Unfaded() const { return unfaded_palette[index]; }
+ DWORD Formatted() const { return formatted_palette[index]; }
+ DWORD Shaded(int shade) const { return shade_table[shade*256+index]; }
+ ColorIndex Faded() const { return ColorIndex(index); }
+
+ // note: this will only work in 8-bit color mode...
+ ColorIndex ShadeColor(int s) const { return ColorIndex((BYTE)(shade_table[s*256+index])); }
+
+ // for poly shading optimization
+ static DWORD* ShadeTable() { return shade_table; }
+
+ BYTE IndexedBlend(BYTE dst) const { return blend_table[dst*256+index]; }
+
+private:
+ BYTE index;
+
+ static DWORD* texture_palette;
+ static DWORD texture_palettes[4][256];
+ static DWORD unfaded_palette[256];
+ static DWORD formatted_palette[256];
+ static DWORD shade_table[256*256];
+ static BYTE blend_table[256*256];
+};
+
+inline BYTE Color::IndexedBlend(BYTE dst) const { return ColorIndex::blend_table[dst*256+Index()]; }
+
+#endif Color_h
+
diff --git a/nGenEx/ComboBox.cpp b/nGenEx/ComboBox.cpp
new file mode 100644
index 0000000..82880de
--- /dev/null
+++ b/nGenEx/ComboBox.cpp
@@ -0,0 +1,434 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ComboBox.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Drop-down list of Buttons class
+*/
+
+#include "MemDebug.h"
+#include "ComboBox.h"
+#include "ComboList.h"
+#include "Button.h"
+#include "Video.h"
+#include "Screen.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "EventDispatch.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+DEF_MAP_CLIENT(ComboBox, OnListSelect);
+DEF_MAP_CLIENT(ComboBox, OnListExit);
+
+// +--------------------------------------------------------------------+
+
+ComboBox::ComboBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid)
+ : ActiveWindow(p->GetScreen(), ax, ay, aw, ah, aid, 0, p)
+{
+ button_state = 0;
+ captured = 0;
+ pre_state = 0;
+ seln = 0;
+ list = 0;
+ list_showing = false;
+ border = true;
+ animated = true;
+ bevel_width = 5;
+
+ char buf[32];
+ sprintf(buf, "ComboBox %d", id);
+ desc = buf;
+}
+
+ComboBox::ComboBox(Screen* s, int ax, int ay, int aw, int ah, DWORD aid)
+ : ActiveWindow(s, ax, ay, aw, ah, aid)
+{
+ button_state = 0;
+ captured = 0;
+ pre_state = 0;
+ seln = 0;
+ list = 0;
+ list_showing = false;
+ border = true;
+ animated = true;
+ bevel_width = 5;
+
+ char buf[32];
+ sprintf(buf, "ComboBox %d", id);
+ desc = buf;
+}
+
+// +--------------------------------------------------------------------+
+
+ComboBox::~ComboBox()
+{
+ items.destroy();
+
+ if (list && !list_showing)
+ delete list;
+}
+
+// +--------------------------------------------------------------------+
+
+void ComboBox::SetBevelWidth(short nNewValue)
+{
+ if (nNewValue < 0) nNewValue = 0;
+ bevel_width = nNewValue;
+}
+
+void ComboBox::SetBorder(bool bNewValue)
+{
+ border = bNewValue;
+}
+
+void ComboBox::SetBorderColor(Color newValue)
+{
+ border_color = newValue;
+}
+
+void ComboBox::SetActiveColor(Color newValue)
+{
+ active_color = newValue;
+}
+
+void ComboBox::SetAnimated(bool bNewValue)
+{
+ animated = bNewValue;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ComboBox::Draw()
+{
+ int x = 0;
+ int y = 0;
+ int w = rect.w;
+ int h = rect.h;
+
+ if (w < 1 || h < 1 || !shown)
+ return;
+
+ Rect btn_rect(x,y,w,h);
+
+ // draw the bevel:
+ DrawRectSimple(btn_rect, button_state);
+
+ // draw the border:
+ if (border)
+ DrawRect(0,0,w-1,h-1,border_color);
+
+ // draw the arrow:
+ POINT arrow[3];
+ double h3 = (double)h/3.0;
+
+ arrow[0].x = (int) (w-2*h3);
+ arrow[0].y = (int) (h3);
+ arrow[1].x = (int) (w-h3);
+ arrow[1].y = (int) (h3);
+ arrow[2].x = (int) ((arrow[1].x + arrow[0].x)/2);
+ arrow[2].y = (int) (2*h3);
+
+ FillPoly(3, arrow, border_color);
+
+ // draw text here:
+ Text caption = text;
+ if (GetSelectedIndex() >= 0)
+ caption = GetSelectedItem();
+
+ if (font && caption.length()) {
+ int border_size = 4;
+
+ if (style & WIN_RAISED_FRAME && style & WIN_SUNK_FRAME)
+ border_size = 8;
+
+ Rect label_rect = CalcLabelRect();
+ int vert_space = label_rect.h;
+ int horz_space = label_rect.w;
+
+ DrawText(caption.data(), 0, label_rect, DT_CALCRECT | DT_WORDBREAK | text_align);
+ vert_space = (vert_space - label_rect.h)/2;
+
+ label_rect.w = horz_space;
+
+ if (vert_space > 0)
+ label_rect.y += vert_space;
+
+ if (animated && button_state > 0) {
+ label_rect.x += button_state;
+ label_rect.y += button_state;
+ }
+
+ font->SetColor(fore_color);
+ DrawText(caption.data(), 0, label_rect, DT_WORDBREAK | text_align);
+ }
+}
+
+Rect ComboBox::CalcLabelRect()
+{
+ // fit the text in the bevel:
+ Rect label_rect;
+ label_rect.x = 0;
+ label_rect.y = 0;
+ label_rect.w = rect.w;
+ label_rect.h = rect.h;
+
+ label_rect.Deflate(bevel_width, bevel_width);
+
+ return label_rect;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ComboBox::DrawRectSimple(Rect& rect, int state)
+{
+ if (state && active_color != Color::Black)
+ FillRect(rect, active_color);
+ else
+ FillRect(rect, back_color);
+}
+
+// +--------------------------------------------------------------------+
+
+int ComboBox::OnMouseMove(int x, int y)
+{
+ bool dirty = false;
+
+ if (captured)
+ {
+ ActiveWindow* test = GetCapture();
+
+ if (test != this)
+ {
+ captured = false;
+ button_state = 0;
+ dirty = true;
+ }
+
+ else
+ {
+ if (button_state == 1)
+ {
+ if (!rect.Contains(x,y))
+ {
+ button_state = 0;
+ dirty = true;
+ }
+ }
+ else
+ {
+ if (rect.Contains(x,y))
+ {
+ button_state = 1;
+ dirty = true;
+ }
+ }
+ }
+ }
+
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+int ComboBox::OnLButtonDown(int x, int y)
+{
+ if (!captured)
+ captured = SetCapture();
+
+ button_state = 1;
+
+ return ActiveWindow::OnLButtonDown(x,y);
+}
+
+int ComboBox::OnLButtonUp(int x, int y)
+{
+ if (captured) {
+ ReleaseCapture();
+ captured = 0;
+ }
+
+ button_state = -1;
+ ShowList();
+ Button::PlaySound(Button::SND_COMBO_OPEN);
+ return ActiveWindow::OnLButtonUp(x,y);
+}
+
+int ComboBox::OnClick()
+{
+ return ActiveWindow::OnClick();
+}
+
+int ComboBox::OnMouseEnter(int mx, int my)
+{
+ if (button_state == 0)
+ button_state = -1;
+
+ return ActiveWindow::OnMouseEnter(mx, my);
+}
+
+int ComboBox::OnMouseExit(int mx, int my)
+{
+ if (button_state == -1)
+ button_state = 0;
+
+ return ActiveWindow::OnMouseExit(mx, my);
+}
+
+// +--------------------------------------------------------------------+
+
+void ComboBox::MoveTo(const Rect& r)
+{
+ ActiveWindow::MoveTo(r);
+
+ if (list) {
+ delete list;
+ list = 0;
+ }
+}
+
+void ComboBox::ShowList()
+{
+ if (!list) {
+ list = new(__FILE__,__LINE__) ComboList(this, screen,
+ rect.x, rect.y,
+ rect.w, rect.h,
+ items.size());
+
+ }
+
+ if (list) {
+ list->SetTextAlign(text_align);
+ list->SetFont(font);
+ list->SetText(text);
+ list->SetSelection(seln);
+ list->SetItems(items);
+
+ list->Show();
+ list_showing = true;
+
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch) {
+ dispatch->MouseEnter(list);
+ dispatch->SetFocus(list);
+ }
+
+ REGISTER_CLIENT(EID_CLICK, list, ComboBox, OnListSelect);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, list, ComboBox, OnListExit);
+ }
+}
+
+void ComboBox::HideList()
+{
+ if (list) {
+ // These will be handled by the list window itself (i hope)
+ UNREGISTER_CLIENT(EID_CLICK, list, ComboBox);
+ UNREGISTER_CLIENT(EID_MOUSE_EXIT, list, ComboBox);
+
+ list->Hide();
+ list_showing = false;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void ComboBox::OnListSelect(AWEvent* event)
+{
+ if (list) {
+ int new_seln = list->GetSelectedIndex();
+
+ if (new_seln >= 0 && new_seln < items.size())
+ seln = new_seln;
+
+ HideList();
+ OnSelect();
+ Button::PlaySound(Button::SND_COMBO_SELECT);
+ }
+}
+
+void ComboBox::OnListExit(AWEvent* event)
+{
+ //HideList();
+ //Button::PlaySound(Button::SND_COMBO_CLOSE);
+}
+
+// +--------------------------------------------------------------------+
+
+int ComboBox::NumItems()
+{
+ return items.size();
+}
+
+void ComboBox::ClearItems()
+{
+ items.destroy();
+ seln = 0;
+}
+
+void ComboBox::AddItem(const char* item)
+{
+ Text* t = new(__FILE__,__LINE__) Text(item);
+
+ if (t) items.append(t);
+}
+
+const char* ComboBox::GetItem(int index)
+{
+ if (index >= 0 && index < items.size())
+ return items[index]->data();
+ else
+ return 0;
+}
+
+void ComboBox::SetItem(int index, const char* item)
+{
+ if (index >= 0 && index < items.size()) {
+ *items[index] = item;
+ }
+
+ else {
+ Text* t = new(__FILE__,__LINE__) Text(item);
+ if (t)
+ items.append(t);
+ }
+}
+
+void ComboBox::SetLabel(const char* label)
+{
+ SetText(label);
+}
+
+int ComboBox::GetCount()
+{
+ return items.size();
+}
+
+const char* ComboBox::GetSelectedItem()
+{
+ if (seln >= 0 && seln < items.size())
+ return items[seln]->data();
+ else
+ return 0;
+}
+
+int ComboBox::GetSelectedIndex()
+{
+ if (seln >= 0 && seln < items.size())
+ return seln;
+ else
+ return -1;
+}
+
+void ComboBox::SetSelection(int index)
+{
+ if (seln != index && index >= 0 && index < items.size()) {
+ seln = index;
+ }
+}
+
diff --git a/nGenEx/ComboBox.h b/nGenEx/ComboBox.h
new file mode 100644
index 0000000..586dbba
--- /dev/null
+++ b/nGenEx/ComboBox.h
@@ -0,0 +1,105 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ComboBox.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ComboBox class
+*/
+
+#ifndef ComboBox_h
+#define ComboBox_h
+
+#include "Types.h"
+#include "ActiveWindow.h"
+#include "Bitmap.h"
+
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class ComboList;
+
+// +--------------------------------------------------------------------+
+
+class ComboBox : public ActiveWindow
+{
+public:
+ static const char* TYPENAME() { return "ComboBox"; }
+
+ ComboBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid=0);
+ ComboBox(Screen* s, int ax, int ay, int aw, int ah, DWORD aid=0);
+ virtual ~ComboBox();
+
+ // Operations:
+ virtual void Draw();
+ virtual void ShowList();
+ virtual void HideList();
+ virtual void MoveTo(const Rect& r);
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnClick();
+ virtual int OnMouseEnter(int x, int y);
+ virtual int OnMouseExit(int x, int y);
+
+ virtual void OnListSelect(AWEvent* event);
+ virtual void OnListExit(AWEvent* event);
+
+ // Property accessors:
+ virtual int NumItems();
+ virtual void ClearItems();
+ virtual void AddItem(const char* item);
+ virtual const char* GetItem(int index);
+ virtual void SetItem(int index, const char* item);
+ virtual void SetLabel(const char* label);
+
+ virtual int GetCount();
+ virtual const char* GetSelectedItem();
+ virtual int GetSelectedIndex();
+ virtual void SetSelection(int index);
+
+ Color GetActiveColor() const { return active_color; }
+ void SetActiveColor(Color c);
+ bool GetAnimated() const { return animated; }
+ void SetAnimated(bool bNewValue);
+ short GetBevelWidth() const { return bevel_width; }
+ void SetBevelWidth(short nNewValue);
+ bool GetBorder() const { return border; }
+ void SetBorder(bool bNewValue);
+ Color GetBorderColor() const { return border_color; }
+ void SetBorderColor(Color c);
+ short GetButtonState() const { return button_state; }
+ void SetButtonState(short nNewValue);
+
+ bool IsListShowing() const { return list_showing; }
+
+protected:
+ Rect CalcLabelRect();
+ void DrawRectSimple(Rect& rect, int stat);
+
+ List<Text> items;
+ ComboList* list;
+ bool list_showing;
+ bool animated;
+ bool border;
+ int seln;
+ int captured;
+ int bevel_width;
+ int button_state;
+ int pre_state;
+
+ Color active_color;
+ Color border_color;
+};
+
+#endif ComboBox_h
+
diff --git a/nGenEx/ComboList.cpp b/nGenEx/ComboList.cpp
new file mode 100644
index 0000000..d6e1b2f
--- /dev/null
+++ b/nGenEx/ComboList.cpp
@@ -0,0 +1,434 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ComboList.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Drop-down list of Buttons class
+*/
+
+#include "MemDebug.h"
+#include "ComboList.h"
+#include "ComboBox.h"
+#include "Button.h"
+#include "Video.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "Screen.h"
+
+// +--------------------------------------------------------------------+
+
+ComboList::ComboList(ComboBox* ctrl, ActiveWindow* p, int ax, int ay, int aw, int ah, int maxentries)
+ : ScrollWindow(p, ax, ay, aw, ah + (ah-2)*maxentries + 2, 0, 0, p), combo_box(ctrl)
+{
+ button_state = 0;
+ button_height = ah;
+ captured = 0;
+ seln = -1;
+ max_entries = maxentries;
+ scroll = 0;
+ scrolling = false;
+ border = true;
+ animated = true;
+ bevel_width = 5;
+
+ if (ctrl)
+ CopyStyle(*ctrl);
+
+ rect.h = (ah-2)*maxentries + 2;
+
+ int screen_height = Video::GetInstance()->Height();
+
+ if (rect.h > screen_height) {
+ rect.y = 0;
+ rect.h = screen_height;
+ }
+
+ else if (rect.y + rect.h > screen_height) {
+ rect.y = screen_height - rect.h;
+ }
+}
+
+ComboList::ComboList(ComboBox* ctrl, Screen* s, int ax, int ay, int aw, int ah, int maxentries)
+ : ScrollWindow(s, ax, ay, aw, ah + (ah-2)*maxentries + 2, 0), combo_box(ctrl)
+{
+ button_state = 0;
+ button_height = ah;
+ captured = 0;
+ seln = -1;
+ max_entries = maxentries;
+ scroll = 0;
+ scrolling = false;
+ border = true;
+ animated = true;
+ bevel_width = 5;
+
+ if (ctrl)
+ CopyStyle(*ctrl);
+
+ rect.h = (ah-2)*maxentries + 2;
+
+ int screen_height = Video::GetInstance()->Height();
+
+ if (rect.h > screen_height) {
+ rect.y = 0;
+ rect.h = screen_height;
+ }
+
+ else if (rect.y + rect.h > screen_height) {
+ rect.y = screen_height - rect.h;
+ }
+
+ screen->AddWindow(this); // just in case the base ctor couldn't do this
+}
+
+// +--------------------------------------------------------------------+
+
+ComboList::~ComboList()
+{
+ items.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ComboList::CopyStyle(const ComboBox& ctrl)
+{
+ active_color = ctrl.GetActiveColor();
+ border_color = ctrl.GetBorderColor();
+ fore_color = ctrl.GetForeColor();
+ back_color = ctrl.GetBackColor();
+
+ animated = ctrl.GetAnimated();
+ bevel_width = ctrl.GetBevelWidth();
+ border = ctrl.GetBorder();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ComboList::Show()
+{
+ if (!scrolling) {
+ scrolling = 1;
+ scroll = 0;
+ }
+
+ ScrollWindow::Show();
+}
+
+void
+ComboList::Hide()
+{
+ scrolling = 0;
+ scroll = 0;
+ ScrollWindow::Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ComboList::Draw()
+{
+ int x = 0;
+ int y = 0;
+ int w = rect.w;
+ int h = button_height;
+
+ int index = 0;
+
+ // opening:
+ if (scrolling > 0) {
+ int limit = min(items.size(), max_entries);
+
+ if (scroll < limit) {
+ if (limit > 6)
+ scroll += 4;
+
+ else if (limit > 4)
+ scroll += 2;
+
+ else
+ scroll += 1;
+ }
+
+ if (scroll >= limit) {
+ scroll = limit;
+ scrolling = 0;
+ }
+ }
+
+ // closing:
+ else if (scrolling < 0) {
+ if (scroll > 0)
+ scroll--;
+ else
+ scrolling = 0;
+ }
+
+ if (items.size() < 1) {
+ Rect btn_rect(x,y,w,h);
+ DrawItem(" ", btn_rect, 0);
+ }
+ else {
+ ListIter<Text> item = items;
+ while (++item && index < max_entries) {
+ Rect btn_rect(x,y,w,h);
+ int sub_state = (index == seln) ? button_state : 0;
+
+ DrawItem(*item, btn_rect, sub_state);
+
+ y += button_height-2;
+ index++;
+ }
+ }
+
+ DrawRect(0,0,rect.w-1,rect.h-1,fore_color);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ComboList::DrawRectSimple(Rect& rect, int state)
+{
+ if (state && active_color != Color::Black)
+ FillRect(rect, active_color);
+ else
+ FillRect(rect, back_color);
+}
+
+void
+ComboList::DrawItem(Text label, Rect& btn_rect, int state)
+{
+ int x = btn_rect.x;
+ int y = btn_rect.y;
+ int w = btn_rect.w;
+ int h = btn_rect.h;
+
+ // draw the bevel:
+ DrawRectSimple(btn_rect, state);
+
+ // draw text here:
+ if (font && label.length()) {
+ int border_size = 4;
+
+ if (style & WIN_RAISED_FRAME && style & WIN_SUNK_FRAME)
+ border_size = 8;
+
+ Rect label_rect = CalcLabelRect(btn_rect);
+ int vert_space = label_rect.h;
+ int horz_space = label_rect.w;
+
+ DrawText(label.data(), 0, label_rect, DT_CALCRECT | DT_WORDBREAK | text_align);
+ vert_space = (vert_space - label_rect.h)/2;
+
+ label_rect.w = horz_space;
+
+ if (vert_space > 0)
+ label_rect.y += vert_space;
+
+ if (animated && state > 0) {
+ label_rect.x += state;
+ label_rect.y += state;
+ }
+
+ font->SetColor(fore_color);
+ DrawText(label.data(), 0, label_rect, DT_WORDBREAK | text_align);
+ }
+}
+
+Rect ComboList::CalcLabelRect(const Rect& btn_rect)
+{
+ // fit the text in the bevel:
+ Rect label_rect = btn_rect;
+ label_rect.Deflate(bevel_width, bevel_width);
+
+ return label_rect;
+}
+
+int ComboList::CalcSeln(int x, int y)
+{
+ y -= rect.y;
+
+ if (button_height < 1)
+ return 0;
+
+ return (int) (y / (button_height-2));
+}
+
+// +--------------------------------------------------------------------+
+
+int ComboList::OnMouseMove(int x, int y)
+{
+ if (captured)
+ {
+ ActiveWindow* test = GetCapture();
+
+ if (test != this)
+ {
+ captured = false;
+ button_state = -1;
+ }
+ }
+
+ int new_seln = CalcSeln(x,y);
+
+ if (new_seln != seln) {
+ seln = new_seln;
+ Button::PlaySound(Button::SND_COMBO_HILITE);
+ }
+
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+int ComboList::OnLButtonDown(int x, int y)
+{
+ if (!captured)
+ captured = SetCapture();
+
+ seln = CalcSeln(x,y);
+ button_state = 1;
+
+ return ActiveWindow::OnLButtonDown(x,y);
+}
+
+int ComboList::OnLButtonUp(int x, int y)
+{
+ if (captured) {
+ ReleaseCapture();
+ captured = 0;
+ }
+
+ seln = CalcSeln(x,y);
+ button_state = -1;
+ return ActiveWindow::OnLButtonUp(x,y);
+}
+
+int ComboList::OnClick()
+{
+ return ActiveWindow::OnClick();
+}
+
+int ComboList::OnMouseEnter(int mx, int my)
+{
+ if (button_state == 0)
+ button_state = -1;
+
+ return ActiveWindow::OnMouseEnter(mx, my);
+}
+
+int ComboList::OnMouseExit(int mx, int my)
+{
+ if (button_state == -1)
+ button_state = 0;
+
+ return ActiveWindow::OnMouseExit(mx, my);
+}
+
+void ComboList::KillFocus()
+{
+ if (combo_box)
+ combo_box->HideList();
+}
+
+// +--------------------------------------------------------------------+
+
+void ComboList::ClearItems()
+{
+ items.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void ComboList::AddItem(const char* item)
+{
+ items.append(new(__FILE__,__LINE__) Text(item));
+}
+
+void ComboList::AddItems(ListIter<Text> item)
+{
+ while (++item)
+ items.append(new(__FILE__,__LINE__) Text(*item));
+}
+
+void ComboList::SetItems(ListIter<Text> item)
+{
+ items.destroy();
+ while (++item)
+ items.append(new(__FILE__,__LINE__) Text(*item));
+
+ // Resize window:
+
+ int ah = button_height;
+ max_entries = items.size();
+ Rect r = rect;
+ r.y = combo_box->Y();
+ int length = max_entries;
+
+ if (length < 1)
+ length = 1;
+
+ r.h = (ah-2)*length + 2;
+
+ int screen_height = Video::GetInstance()->Height();
+
+ if (r.h > screen_height) {
+ r.y = 0;
+ r.h = screen_height;
+ }
+
+ else if (r.y + r.h > screen_height) {
+ r.y = screen_height - r.h;
+ }
+
+ MoveTo(r);
+}
+
+const char* ComboList::GetItem(int index)
+{
+ if (index >= 0 && index < items.size())
+ return items[index]->data();
+ else
+ return 0;
+}
+
+void ComboList::SetItem(int index, const char* item)
+{
+ if (index >= 0 && index < items.size())
+ *items[index] = item;
+ else
+ items.append(new(__FILE__,__LINE__) Text(item));
+}
+
+int ComboList::GetCount()
+{
+ return items.size();
+}
+
+const char* ComboList::GetSelectedItem()
+{
+ if (seln >= 0 && seln < items.size())
+ return items[seln]->data();
+ else
+ return 0;
+}
+
+int ComboList::GetSelectedIndex()
+{
+ if (seln >= 0 && seln < items.size())
+ return seln;
+ else
+ return -1;
+}
+
+void ComboList::SetSelection(int index)
+{
+ if (index >= 0 && index < items.size())
+ seln = index;
+}
+
diff --git a/nGenEx/ComboList.h b/nGenEx/ComboList.h
new file mode 100644
index 0000000..d4ea794
--- /dev/null
+++ b/nGenEx/ComboList.h
@@ -0,0 +1,92 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ComboList.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ComboList class
+*/
+
+#ifndef ComboList_h
+#define ComboList_h
+
+#include "Types.h"
+#include "ScrollWindow.h"
+#include "Bitmap.h"
+
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class ComboBox;
+
+// +--------------------------------------------------------------------+
+
+class ComboList : public ScrollWindow
+{
+public:
+ static const char* TYPENAME() { return "ComboList"; }
+
+ ComboList(ComboBox* ctrl, ActiveWindow* p, int ax, int ay, int aw, int ah, int maxentries);
+ ComboList(ComboBox* ctrl, Screen* s, int ax, int ay, int aw, int ah, int maxentries);
+ virtual ~ComboList();
+
+ // Operations:
+ virtual void Draw();
+ virtual void Show();
+ virtual void Hide();
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnClick();
+ virtual int OnMouseEnter(int x, int y);
+ virtual int OnMouseExit(int x, int y);
+ virtual void KillFocus();
+
+ // Property accessors:
+ virtual void ClearItems();
+ virtual void AddItem(const char* item);
+ virtual void AddItems(ListIter<Text> item_list);
+ virtual void SetItems(ListIter<Text> item_list);
+ virtual const char* GetItem(int index);
+ virtual void SetItem(int index, const char* item);
+
+ virtual int GetCount();
+ virtual const char* GetSelectedItem();
+ virtual int GetSelectedIndex();
+ virtual void SetSelection(int index);
+
+protected:
+ void DrawRectSimple(Rect& rect, int stat);
+ void DrawItem(Text label, Rect& btn_rect, int state);
+ Rect CalcLabelRect(const Rect& btn_rect);
+ int CalcSeln(int x, int y);
+ void CopyStyle(const ComboBox& ctrl);
+
+ ComboBox* combo_box;
+ List<Text> items;
+ bool animated;
+ bool border;
+ int seln;
+ int captured;
+ int bevel_width;
+ int button_state;
+ int button_height;
+ int max_entries;
+ int scroll;
+ int scrolling;
+
+ Color active_color;
+ Color border_color;
+};
+
+#endif ComboList_h
+
diff --git a/nGenEx/ContentBundle.cpp b/nGenEx/ContentBundle.cpp
new file mode 100644
index 0000000..ca6c8e5
--- /dev/null
+++ b/nGenEx/ContentBundle.cpp
@@ -0,0 +1,137 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ContentBundle.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Chained collection of localized strings
+*/
+
+#include "MemDebug.h"
+#include "ContentBundle.h"
+#include "DataLoader.h"
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+ContentBundle::ContentBundle(const char* bundle, Locale* locale)
+{
+ Text file = FindFile(bundle, locale);
+ if (file.length() > 0) {
+ LoadBundle(file);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+ContentBundle::~ContentBundle()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+ContentBundle::GetText(const char* key) const
+{
+ return values.find(key, Text(key));
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+ContentBundle::FindFile(const char* bundle, Locale* locale)
+{
+ Text result;
+ Text basename = Text(bundle);
+ DataLoader* loader = DataLoader::GetLoader();
+
+ if (loader && bundle) {
+ if (locale) {
+ result = basename + locale->GetFullCode() + ".txt";
+
+ if (loader->FindFile(result))
+ return result;
+
+ result = basename + "_" + locale->GetLanguage() + ".txt";
+
+ if (loader->FindFile(result))
+ return result;
+ }
+
+ result = basename + ".txt";
+
+ if (loader->FindFile(result))
+ return result;
+ }
+
+ return Text();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ContentBundle::LoadBundle(const char* filename)
+{
+ DataLoader* loader = DataLoader::GetLoader();
+ if (loader && filename && *filename) {
+ BYTE* buffer = 0;
+ loader->LoadBuffer(filename, buffer, true, true);
+ if (buffer && *buffer) {
+ char key[1024];
+ char val[2048];
+ char* p = (char*) buffer;
+ int s = 0, ik = 0, iv = 0;
+
+ key[0] = 0;
+ val[0] = 0;
+
+ while (*p) {
+ if (*p == '=') {
+ s = 1;
+ }
+ else if (*p == '\n' || *p == '\r') {
+ if (key[0] && val[0])
+ values.insert(Text(key).trim(), Text(val).trim());
+
+ ZeroMemory(key, 1024);
+ ZeroMemory(val, 2048);
+ s = 0;
+ ik = 0;
+ iv = 0;
+ }
+ else if (s == 0) {
+ if (!key[0]) {
+ if (*p == '#') {
+ s = -1; // comment
+ }
+ else if (!isspace(*p)) {
+ key[ik++] = *p;
+ }
+ }
+ else {
+ key[ik++] = *p;
+ }
+ }
+ else if (s == 1) {
+ if (!isspace(*p)) {
+ s = 2;
+ val[iv++] = *p;
+ }
+ }
+ else if (s == 2) {
+ val[iv++] = *p;
+ }
+
+ p++;
+ }
+
+ loader->ReleaseBuffer(buffer);
+ }
+ }
+}
diff --git a/nGenEx/ContentBundle.h b/nGenEx/ContentBundle.h
new file mode 100644
index 0000000..d322466
--- /dev/null
+++ b/nGenEx/ContentBundle.h
@@ -0,0 +1,48 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ContentBundle.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Chained collection of localized strings
+*/
+
+#ifndef ContentBundle_h
+#define ContentBundle_h
+
+#include "Types.h"
+#include "Dictionary.h"
+#include "Text.h"
+#include "Locale.h"
+
+// +--------------------------------------------------------------------+
+
+class ContentBundle
+{
+public:
+ static const char* TYPENAME() { return "ContentBundle"; }
+
+ ContentBundle(const char* bundle, Locale* locale);
+ virtual ~ContentBundle();
+
+ int operator == (const ContentBundle& that) const { return this == &that; }
+
+ const Text& GetName() const { return name; }
+ Text GetText(const char* key) const;
+ bool IsLoaded() const { return !values.isEmpty(); }
+
+protected:
+ void LoadBundle(const char* filename);
+ Text FindFile(const char* bundle, Locale* locale);
+
+ Text name;
+ Dictionary<Text> values;
+};
+
+#endif ContentBundle_h
+
diff --git a/nGenEx/D3DXImage.cpp b/nGenEx/D3DXImage.cpp
new file mode 100644
index 0000000..3300253
--- /dev/null
+++ b/nGenEx/D3DXImage.cpp
@@ -0,0 +1,222 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: D3DXImage.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ D3DX image file loader
+*/
+
+
+#include "MemDebug.h"
+#include "D3DXImage.h"
+#include "VideoDX9.h"
+
+// +--------------------------------------------------------------------+
+
+D3DXImage::D3DXImage()
+ : width(0), height(0), format(0), image(0)
+{ }
+
+D3DXImage::D3DXImage(WORD w, WORD h, DWORD* img)
+{
+ ZeroMemory(this, sizeof(D3DXImage));
+
+ width = w;
+ height = h;
+
+ int pixels = width * height;
+
+ image = new(__FILE__,__LINE__) DWORD [pixels];
+
+ if (image && pixels) {
+ for (int i = 0; i < pixels; i++)
+ image[i] = img[i];
+ }
+}
+
+D3DXImage::~D3DXImage()
+{
+ delete [] image;
+}
+
+// +--------------------------------------------------------------------+
+
+bool D3DXImage::Load(char *filename)
+{
+ bool success = false;
+ FILE* f;
+
+ f = fopen(filename,"rb");
+ if (f == NULL)
+ return success;
+
+ int len = 0;
+ BYTE* buf = 0;
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ buf = new(__FILE__,__LINE__) BYTE[len];
+
+ if (buf) {
+ fread(buf, len, 1, f);
+ fclose(f);
+
+ success = LoadBuffer(buf, len);
+ }
+
+ return success;
+}
+
+// +--------------------------------------------------------------------+
+
+bool D3DXImage::LoadBuffer(BYTE* buf, int len)
+{
+ bool success = false;
+ HRESULT hr = E_FAIL;
+ D3DXIMAGE_INFO info;
+
+ if (buf == NULL)
+ return success;
+
+ hr = D3DXGetImageInfoFromFileInMemory(buf, len, &info);
+
+ if (FAILED(hr))
+ return success;
+
+ width = info.Width;
+ height = info.Height;
+ format = info.Format;
+
+ if (image) {
+ delete [] image;
+ image = 0;
+ }
+
+ IDirect3DSurface9* surf = 0;
+ IDirect3DDevice9* dev = VideoDX9::GetD3DDevice9();
+
+
+ hr = dev->CreateOffscreenPlainSurface( width,
+ height,
+ D3DFMT_A8R8G8B8,
+ D3DPOOL_SYSTEMMEM,
+ &surf,
+ NULL);
+
+ if (FAILED(hr))
+ return success;
+
+ hr = D3DXLoadSurfaceFromFileInMemory( surf, // dest surface
+ NULL, // dest palette (none)
+ NULL, // dest rect (entire image)
+ buf, // memory file
+ len, // size of data
+ NULL, // source rect (entire image)
+ D3DX_DEFAULT, // filter operation
+ 0, // color key (none)
+ NULL); // image info
+
+ if (SUCCEEDED(hr)) {
+ D3DLOCKED_RECT locked_rect;
+ hr = surf->LockRect(&locked_rect, NULL, D3DLOCK_READONLY);
+
+ if (SUCCEEDED(hr)) {
+ // copy surface into image
+ image = new(__FILE__,__LINE__) DWORD[width*height];
+ if (image) {
+ for (DWORD i = 0; i < height; i++) {
+ BYTE* dst = (BYTE*) (image + i * width);
+ BYTE* src = (BYTE*) locked_rect.pBits + i * locked_rect.Pitch;
+
+ CopyMemory(dst, src, width * sizeof(DWORD));
+ }
+
+ success = true;
+ }
+
+ surf->UnlockRect();
+ }
+ }
+
+ surf->Release();
+ surf = 0;
+
+ return success;
+}
+
+// +--------------------------------------------------------------------+
+
+bool D3DXImage::Save(char *filename)
+{
+ bool success = false;
+ HRESULT hr = E_FAIL;
+
+ if (!image || !width || !height)
+ return success;
+
+ FILE* f = fopen(filename,"wb");
+ if (f == NULL)
+ return success;
+
+ IDirect3DSurface9* surf = 0;
+ IDirect3DDevice9* dev = VideoDX9::GetD3DDevice9();
+
+ hr = dev->CreateOffscreenPlainSurface( width,
+ height,
+ D3DFMT_A8R8G8B8,
+ D3DPOOL_SYSTEMMEM,
+ &surf,
+ NULL);
+
+
+ if (SUCCEEDED(hr)) {
+ D3DLOCKED_RECT locked_rect;
+ hr = surf->LockRect(&locked_rect, NULL, 0);
+
+ if (SUCCEEDED(hr)) {
+ // copy image into surface
+ for (DWORD i = 0; i < height; i++) {
+ BYTE* src = (BYTE*) (image + i * width);
+ BYTE* dst = (BYTE*) locked_rect.pBits + i * locked_rect.Pitch;
+
+ CopyMemory(dst, src, width * sizeof(DWORD));
+ }
+
+ surf->UnlockRect();
+
+ ID3DXBuffer* buffer = 0;
+ D3DXIMAGE_FILEFORMAT imgfmt = D3DXIFF_PNG;
+
+ if (strstr(filename, ".jpg") || strstr(filename, ".JPG"))
+ imgfmt = D3DXIFF_JPG;
+
+ else if (strstr(filename, ".bmp") || strstr(filename, ".BMP"))
+ imgfmt = D3DXIFF_BMP;
+
+ hr = D3DXSaveSurfaceToFileInMemory(&buffer, // destination
+ imgfmt, // type of file
+ surf, // image to save
+ NULL, // palette
+ NULL); // source rect (entire image)
+
+ if (SUCCEEDED(hr)) {
+ fwrite(buffer->GetBufferPointer(), buffer->GetBufferSize(), 1, f);
+ success = true;
+ }
+ }
+ }
+
+ surf->Release();
+ surf = 0;
+ fclose(f);
+ return success;
+}
+
diff --git a/nGenEx/D3DXImage.h b/nGenEx/D3DXImage.h
new file mode 100644
index 0000000..2150ab0
--- /dev/null
+++ b/nGenEx/D3DXImage.h
@@ -0,0 +1,43 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: D3DXImage.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ D3DX image file loader
+*/
+
+#ifndef D3DXImage_h
+#define D3DXImage_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+struct D3DXImage
+{
+ static const char* TYPENAME() { return "D3DXImage"; }
+
+ D3DXImage();
+ D3DXImage(WORD w, WORD h, DWORD* img);
+ ~D3DXImage();
+
+ bool Load(char *filename);
+ bool Save(char *filename);
+
+ bool LoadBuffer(BYTE* buf, int len);
+
+ DWORD* image;
+ DWORD width;
+ DWORD height;
+ DWORD format;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif D3DXImage_h
diff --git a/nGenEx/DataLoader.cpp b/nGenEx/DataLoader.cpp
new file mode 100644
index 0000000..354232b
--- /dev/null
+++ b/nGenEx/DataLoader.cpp
@@ -0,0 +1,1013 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: DataLoader.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include "MemDebug.h"
+#include "DataLoader.h"
+#include "Archive.h"
+#include "Color.h"
+#include "D3DXImage.h"
+#include "Bitmap.h"
+#include "Bmp.h"
+#include "PCX.h"
+#include "Sound.h"
+#include "Resource.h"
+#include "Video.h"
+#include "Wave.h"
+
+// +------------------------------------------------------------------+
+
+static DataLoader* def_loader = 0;
+ DataLoader* DataLoader::loader = 0;
+
+static List<DataArchive> archives;
+
+// +--------------------------------------------------------------------+
+
+DataLoader::DataLoader()
+ : datapath(""), video(0), use_file_system(true), enable_media(true)
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+DataLoader::UseVideo(Video* v)
+{
+ video = v;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DataLoader::Initialize()
+{
+ def_loader = new(__FILE__,__LINE__) DataLoader;
+ loader = def_loader;
+
+ archives.destroy();
+}
+
+void
+DataLoader::Close()
+{
+ archives.destroy();
+ Bitmap::ClearCache();
+
+ delete def_loader;
+ def_loader = 0;
+ loader = 0;
+}
+
+void
+DataLoader::Reset()
+{
+ Close();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DataLoader::UseFileSystem(bool use)
+{
+ use_file_system = use;
+}
+
+void
+DataLoader::EnableMedia(bool enable)
+{
+ enable_media = enable;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::EnableDatafile(const char* name)
+{
+ int status = DATAFILE_NOTEXIST;
+
+ FILE* f = fopen(name, "rb");
+ if (f) {
+ ::fclose(f);
+
+ DataArchive* a = new(__FILE__,__LINE__) DataArchive(name);
+
+ if (a && a->NumFiles() >= 1) {
+ status = DATAFILE_OK;
+
+ bool found = false;
+ ListIter<DataArchive> iter = archives;
+ while (++iter && !found) {
+ DataArchive* archive = iter.value();
+ if (!strcmp(archive->Name(), name)) {
+ found = true;
+ }
+ }
+
+ if (!found)
+ archives.append(a);
+ }
+ else {
+ Print(" WARNING: invalid data file '%s'\n", name);
+ status = DATAFILE_INVALID;
+
+ delete a;
+ }
+
+ loader = this;
+ }
+ else {
+ Print(" WARNING: could not open datafile '%s'\n", name);
+ status = DATAFILE_NOTEXIST;
+ }
+
+ return status;
+}
+
+int
+DataLoader::DisableDatafile(const char* name)
+{
+ ListIter<DataArchive> iter = archives;
+ while (++iter) {
+ DataArchive* a = iter.value();
+ if (!strcmp(a->Name(), name)) {
+ delete iter.removeItem();
+ return DATAFILE_OK;
+ }
+ }
+
+ return DATAFILE_NOTEXIST;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DataLoader::SetDataPath(const char* path)
+{
+ if (path)
+ datapath = path;
+ else
+ datapath = "";
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+DataLoader::FindFile(const char* name)
+{
+ // assemble file name:
+ char filename[1024];
+ strcpy(filename, datapath);
+ strcat(filename, name);
+
+ // first check current directory:
+ if (use_file_system) {
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ ::fclose(f);
+ return true;
+ }
+ }
+
+ // then check datafiles, from last to first:
+ int narchives = archives.size();
+ for (int i = 0; i < narchives; i++) {
+ DataArchive* a = archives[narchives-1-i];
+ if (a->FindEntry(filename) > -1) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::ListFiles(const char* filter, List<Text>& list, bool recurse)
+{
+ list.destroy();
+
+ ListFileSystem(filter, list, datapath, recurse);
+
+ // then check datafile(s):
+ int narchives = archives.size();
+ for (int i = 0; i < narchives; i++) {
+ DataArchive* a = archives[narchives-1-i];
+ ListArchiveFiles(a->Name(), filter, list);
+ }
+
+ return list.size();
+}
+
+int
+DataLoader::ListArchiveFiles(const char* archive_name, const char* filter, List<Text> &list)
+{
+ int pathlen = datapath.length();
+ DataArchive* a = 0;
+
+ if (archive_name) {
+ int narchives = archives.size();
+ for (int i = 0; i < narchives && !a; i++) {
+ a = archives[narchives-1-i];
+
+ if (stricmp(a->Name(), archive_name))
+ a = 0;
+ }
+ }
+
+ if (!a) {
+ ListFileSystem(filter, list, datapath, true);
+ return list.size();
+ }
+
+ if (!strcmp(filter, "*.*")) {
+ int count = a->NumFiles();
+ for (int n = 0; n < count; n++) {
+ DataEntry* entry = a->GetFile(n);
+ Text entry_name = entry->name;
+ entry_name.setSensitive(false);
+
+ if (entry_name.contains(datapath)) {
+ Text fname = entry_name(pathlen, 1000);
+
+ if (!list.contains(&fname))
+ list.append(new Text(fname));
+ }
+ }
+ }
+ else {
+ char data_filter[256];
+ ZeroMemory(data_filter, 256);
+
+ const char* pf = filter;
+ char* pdf = data_filter;
+
+ while (*pf) {
+ if (*pf != '*')
+ *pdf++ = *pf;
+ pf++;
+ }
+
+ int count = a->NumFiles();
+ for (int n = 0; n < count; n++) {
+ DataEntry* entry = a->GetFile(n);
+ Text entry_name = entry->name;
+ entry_name.setSensitive(false);
+
+ if (entry_name.contains(datapath) && entry_name.contains(data_filter)) {
+ Text fname = entry_name(pathlen, 1000);
+
+ if (!list.contains(&fname))
+ list.append(new Text(fname));
+ }
+ }
+ }
+
+ return list.size();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DataLoader::ListFileSystem(const char* filter, List<Text>& list, Text base_path, bool recurse)
+{
+ if (use_file_system) {
+ char data_filter[256];
+ ZeroMemory(data_filter, 256);
+
+ const char* pf = filter;
+ char* pdf = data_filter;
+
+ while (*pf) {
+ if (*pf != '*')
+ *pdf++ = *pf;
+ pf++;
+ }
+
+ int pathlen = base_path.length();
+
+ // assemble win32 find filter:
+ char win32_filter[1024];
+ strcpy(win32_filter, datapath);
+
+ if (recurse)
+ strcat(win32_filter, "*.*");
+ else
+ strcat(win32_filter, filter);
+
+ // first check current directory:
+ WIN32_FIND_DATA data;
+ HANDLE hFind = FindFirstFile(win32_filter, &data);
+
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+ if (recurse && data.cFileName[0] != '.') {
+ Text old_datapath = datapath;
+
+ Text newpath = datapath;
+ newpath += data.cFileName;
+ newpath += "/";
+ datapath = newpath;
+
+ ListFileSystem(filter, list, base_path, recurse);
+
+ datapath = old_datapath;
+ }
+ }
+ else {
+ if (!strcmp(filter, "*.*") || strstr(data.cFileName, data_filter)) {
+ Text full_name = datapath;
+ full_name += data.cFileName;
+
+ list.append(new Text(full_name(pathlen, 1000)));
+ }
+ }
+ }
+ while (FindNextFile(hFind, &data));
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::LoadBuffer(const char* name, BYTE*& buf, bool null_terminate, bool optional)
+{
+ buf = 0;
+
+ // assemble file name:
+ char filename[1024];
+ strcpy(filename, datapath);
+ strcat(filename, name);
+
+ if (use_file_system) {
+ // first check current directory:
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ ::fseek(f, 0, SEEK_END);
+ int len = ftell(f);
+ ::fseek(f, 0, SEEK_SET);
+
+ if (null_terminate) {
+ buf = new(__FILE__,__LINE__) BYTE[len+1];
+ if (buf)
+ buf[len] = 0;
+ }
+
+ else {
+ buf = new(__FILE__,__LINE__) BYTE[len];
+ }
+
+ if (buf)
+ ::fread(buf, len, 1, f);
+
+ ::fclose(f);
+
+ return len;
+ }
+ }
+
+ // then check datafile(s):
+ int narchives = archives.size();
+
+ // vox files are usually in their own archive,
+ // so check there first
+ if (narchives > 1 && strstr(filename, "Vox")) {
+ for (int i = 0; i < narchives; i++) {
+ DataArchive* a = archives[narchives-1-i];
+ if (strstr(a->Name(), "vox")) {
+ int index = a->FindEntry(filename);
+ if (index > -1)
+ return a->ExpandEntry(index, buf, null_terminate);
+ }
+ }
+ }
+
+ for (int i = 0; i < narchives; i++) {
+ DataArchive* a = archives[narchives-1-i];
+ int index = a->FindEntry(filename);
+ if (index > -1)
+ return a->ExpandEntry(index, buf, null_terminate);
+ }
+
+ if (!optional)
+ Print("WARNING - DataLoader could not load buffer '%s'\n", filename);
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::LoadPartialFile(const char* name, BYTE*& buf, int max_load, bool optional)
+{
+ buf = 0;
+
+ // assemble file name:
+ char filename[1024];
+ strcpy(filename, datapath);
+ strcat(filename, name);
+
+ // first check current directory:
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ ::fseek(f, 0, SEEK_END);
+ int len = ftell(f);
+ ::fseek(f, 0, SEEK_SET);
+
+ if (len > max_load) {
+ len = max_load;
+ }
+
+ buf = new(__FILE__,__LINE__) BYTE[len];
+
+ if (buf)
+ ::fread(buf, len, 1, f);
+
+ ::fclose(f);
+
+ return len;
+ }
+
+ if (!optional)
+ Print("WARNING - DataLoader could not load partial file '%s'\n", filename);
+ return 0;
+}
+
+int
+DataLoader::fread(void* buffer, size_t size, size_t count, BYTE*& stream)
+{
+ CopyMemory(buffer, stream, size*count);
+ stream += size*count;
+
+ return size*count;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DataLoader::ReleaseBuffer(BYTE*& buf)
+{
+ delete [] buf;
+ buf = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::CacheBitmap(const char* name, Bitmap*& bitmap, int type, bool optional)
+{
+ int result = 0;
+
+ // search cache:
+ bitmap = Bitmap::CheckCache(name);
+ if (bitmap) return 1;
+
+ // not in cache yet:
+ bitmap = new(__FILE__,__LINE__) Bitmap;
+
+ if (bitmap)
+ result = LoadBitmap(name, *bitmap, type, optional);
+
+ if (result && bitmap)
+ Bitmap::AddToCache(bitmap);
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::LoadBitmap(const char* name, Bitmap& bitmap, int type, bool optional)
+{
+ if (!enable_media)
+ return 0;
+
+ int result = LoadIndexed(name, bitmap, type);
+
+ // check for a matching high color bitmap:
+ if (result == 1) {
+ int hi_result = LoadHiColor(name, bitmap, type);
+
+ if (hi_result == 2)
+ result = 3;
+ }
+
+ bitmap.SetFilename(name);
+
+ if (!result && !optional)
+ Print("WARNING - DataLoader could not load bitmap '%s%s'\n", datapath.data(), name);
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::LoadTexture(const char* name, Bitmap*& bitmap, int type, bool preload_cache, bool optional)
+{
+ if (!enable_media)
+ return 0;
+
+ int result = 0;
+
+ // assemble file name:
+ char filename[256];
+ strcpy(filename, datapath);
+ strcat(filename, name);
+
+ // search cache:
+ bitmap = Bitmap::CheckCache(filename);
+ if (bitmap) return 1;
+
+ // not in cache yet:
+ bitmap = new(__FILE__,__LINE__) Bitmap;
+
+ if (bitmap) {
+ result = LoadHiColor(name, *bitmap, type);
+
+ if (!result) {
+ result = LoadIndexed(name, *bitmap, type);
+ }
+
+ bitmap->SetFilename(filename);
+
+ if (result) {
+ bitmap->MakeTexture();
+ Bitmap::AddToCache(bitmap);
+ }
+ else {
+ delete bitmap;
+ bitmap = 0;
+
+ if (!optional)
+ Print("WARNING - DataLoader could not load texture '%s%s'\n", datapath.data(), name);
+ }
+ }
+ else if (!optional) {
+ Print("WARNING - DataLoader could not allocate texture '%s%s'\n", datapath.data(), name);
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::LoadIndexed(const char* name, Bitmap& bitmap, int type)
+{
+ if (!enable_media)
+ return 0;
+
+ int result = 0;
+ PcxImage pcx;
+ D3DXImage d3dx;
+ bool pcx_file = strstr(name, ".pcx") || strstr(name, ".PCX");
+
+ // assemble file name:
+ char filename[256];
+ strcpy(filename, datapath);
+ strcat(filename, name);
+
+ // first try to load from current directory:
+ bool loaded = false;
+
+ if (use_file_system) {
+ if (pcx_file)
+ loaded = pcx.Load(filename) == PCX_OK;
+
+ else
+ loaded = d3dx.Load(filename);
+ }
+
+ if (!loaded) {
+ // then check datafile:
+ int len = 0;
+ BYTE* tmp_buf = 0;
+
+ int narchives = archives.size();
+ for (int i = 0; i < narchives; i++) {
+ DataArchive* a = archives[narchives-1-i];
+ int index = a->FindEntry(filename);
+ if (index > -1) {
+ len = a->ExpandEntry(index, tmp_buf);
+
+ if (pcx_file)
+ pcx.LoadBuffer(tmp_buf, len);
+
+ else
+ d3dx.LoadBuffer(tmp_buf, len);
+
+ ReleaseBuffer(tmp_buf);
+ break;
+ }
+ }
+ }
+
+ // now copy the image into the bitmap:
+ if (pcx_file) {
+ if (pcx.bitmap) {
+ bitmap.CopyImage(pcx.width, pcx.height, pcx.bitmap, type);
+ result = 1;
+ }
+
+ else if (pcx.himap) {
+ bitmap.CopyHighColorImage(pcx.width, pcx.height, pcx.himap, type);
+ result = 2;
+ }
+
+ if (result == 2)
+ LoadAlpha(name, bitmap, type);
+ }
+
+ else {
+ if (d3dx.image) {
+ bitmap.CopyHighColorImage(d3dx.width, d3dx.height, d3dx.image, type);
+ result = 2;
+ }
+
+ if (result == 2) {
+ LoadAlpha(name, bitmap, type);
+ }
+ }
+
+ return result;
+}
+
+int
+DataLoader::LoadHiColor(const char* name, Bitmap& bitmap, int type)
+{
+ if (!enable_media)
+ return 0;
+
+ int result = 0;
+ PcxImage pcx;
+ D3DXImage d3dx;
+ bool pcx_file = strstr(name, ".pcx") || strstr(name, ".PCX");
+ bool bmp_file = strstr(name, ".bmp") || strstr(name, ".BMP");
+ bool png_file = strstr(name, ".png") || strstr(name, ".PNG");
+
+ // check for a matching high color bitmap:
+ char filename[256];
+ char name2[256];
+ strcpy(name2, name);
+
+ char* dot = strrchr(name2, '.');
+ if (dot && pcx_file)
+ strcpy(dot, "+.pcx");
+ else if (dot && bmp_file)
+ strcpy(dot, "+.bmp");
+ else if (dot && png_file)
+ strcpy(dot, "+.png");
+ else
+ return result;
+
+ strcpy(filename, datapath);
+ strcat(filename, name2);
+
+ // first try to load from current directory:
+ bool loaded = false;
+
+ if (use_file_system) {
+ if (pcx_file)
+ loaded = pcx.Load(filename) == PCX_OK;
+
+ else
+ loaded = d3dx.Load(filename);
+ }
+
+ if (!loaded) {
+ // then check datafile:
+ int len = 0;
+ BYTE* tmp_buf = 0;
+
+ int narchives = archives.size();
+ for (int i = 0; i < narchives; i++) {
+ DataArchive* a = archives[narchives-1-i];
+ int index = a->FindEntry(filename);
+ if (index > -1) {
+ len = a->ExpandEntry(index, tmp_buf);
+
+ if (pcx_file)
+ pcx.LoadBuffer(tmp_buf, len);
+ else
+ d3dx.LoadBuffer(tmp_buf, len);
+
+ ReleaseBuffer(tmp_buf);
+ break;
+ }
+ }
+ }
+
+ // now copy the image into the bitmap:
+ if (pcx_file && pcx.himap) {
+ bitmap.CopyHighColorImage(pcx.width, pcx.height, pcx.himap, type);
+ result = 2;
+ }
+
+ else if (d3dx.image) {
+ bitmap.CopyHighColorImage(d3dx.width, d3dx.height, d3dx.image, type);
+ result = 2;
+ }
+
+ if (result == 2)
+ LoadAlpha(name, bitmap, type);
+
+ return result;
+}
+
+int
+DataLoader::LoadAlpha(const char* name, Bitmap& bitmap, int type)
+{
+ if (!enable_media)
+ return 0;
+
+ PcxImage pcx;
+ D3DXImage d3dx;
+ bool pcx_file = strstr(name, ".pcx") || strstr(name, ".PCX");
+ bool bmp_file = strstr(name, ".bmp") || strstr(name, ".BMP");
+ bool png_file = strstr(name, ".png") || strstr(name, ".PNG");
+ bool tga_file = strstr(name, ".tga") || strstr(name, ".TGA");
+
+ // check for an associated alpha-only (grayscale) bitmap:
+ char filename[256];
+ char name2[256];
+ strcpy(name2, name);
+ char* dot = strrchr(name2, '.');
+ if (dot && pcx_file)
+ strcpy(dot, "@.pcx");
+ else if (dot && bmp_file)
+ strcpy(dot, "@.bmp");
+ else if (dot && png_file)
+ strcpy(dot, "@.png");
+ else if (dot && tga_file)
+ strcpy(dot, "@.tga");
+ else
+ return 0;
+
+ strcpy(filename, datapath);
+ strcat(filename, name2);
+
+ // first try to load from current directory:
+ bool loaded = false;
+
+ if (use_file_system) {
+ if (pcx_file)
+ loaded = pcx.Load(filename) == PCX_OK;
+
+ else
+ loaded = d3dx.Load(filename);
+ }
+
+ if (!loaded) {
+ // then check datafile:
+ int len = 0;
+ BYTE* tmp_buf = 0;
+
+ int narchives = archives.size();
+ for (int i = 0; i < narchives; i++) {
+ DataArchive* a = archives[narchives-1-i];
+ int index = a->FindEntry(filename);
+ if (index > -1) {
+ len = a->ExpandEntry(index, tmp_buf);
+
+ if (pcx_file)
+ pcx.LoadBuffer(tmp_buf, len);
+ else
+ d3dx.LoadBuffer(tmp_buf, len);
+
+ ReleaseBuffer(tmp_buf);
+ break;
+ }
+ }
+ }
+
+ // now copy the alpha values into the bitmap:
+ if (pcx_file && pcx.bitmap) {
+ bitmap.CopyAlphaImage(pcx.width, pcx.height, pcx.bitmap);
+ }
+ else if (pcx_file && pcx.himap) {
+ bitmap.CopyAlphaRedChannel(pcx.width, pcx.height, pcx.himap);
+ }
+ else if (d3dx.image) {
+ bitmap.CopyAlphaRedChannel(d3dx.width, d3dx.height, d3dx.image);
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::LoadSound(const char* name, Sound*& snd, DWORD flags, bool optional)
+{
+ if (!enable_media)
+ return 0;
+
+ if (strstr(name, ".ogg"))
+ return LoadStream(name, snd, optional);
+
+ int result = 0;
+
+ WAVE_HEADER head;
+ WAVE_FMT fmt;
+ WAVE_FACT fact;
+ WAVE_DATA data;
+ WAVEFORMATEX wfex;
+ LPBYTE wave;
+
+ LPBYTE buf;
+ LPBYTE p;
+ int len;
+
+ ZeroMemory(&head, sizeof(head));
+ ZeroMemory(&fmt, sizeof(fmt));
+ ZeroMemory(&fact, sizeof(fact));
+ ZeroMemory(&data, sizeof(data));
+
+ len = LoadBuffer(name, buf, false, optional);
+
+ if (len > sizeof(head)) {
+ CopyMemory(&head, buf, sizeof(head));
+
+ if (head.RIFF == MAKEFOURCC('R', 'I', 'F', 'F') &&
+ head.WAVE == MAKEFOURCC('W', 'A', 'V', 'E')) {
+
+ p = buf + sizeof(WAVE_HEADER);
+
+ do {
+ DWORD chunk_id = *((LPDWORD) p);
+
+ switch (chunk_id) {
+ case MAKEFOURCC('f', 'm', 't', ' '):
+ CopyMemory(&fmt, p, sizeof(fmt));
+ p += fmt.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('f', 'a', 'c', 't'):
+ CopyMemory(&fact, p, sizeof(fact));
+ p += fact.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('s', 'm', 'p', 'l'):
+ CopyMemory(&fact, p, sizeof(fact));
+ p += fact.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('d', 'a', 't', 'a'):
+ CopyMemory(&data, p, sizeof(data));
+ p += 8;
+ break;
+
+ default:
+ ReleaseBuffer(buf);
+ return result;
+ }
+ }
+ while (data.chunk_size == 0);
+
+ wfex.wFormatTag = fmt.wFormatTag;
+ wfex.nChannels = fmt.nChannels;
+ wfex.nSamplesPerSec = fmt.nSamplesPerSec;
+ wfex.nAvgBytesPerSec = fmt.nAvgBytesPerSec;
+ wfex.nBlockAlign = fmt.nBlockAlign;
+ wfex.wBitsPerSample = fmt.wBitsPerSample;
+ wfex.cbSize = 0;
+ wave = p;
+
+ snd = Sound::Create(flags, &wfex, data.chunk_size, wave);
+
+ if (snd)
+ result = data.chunk_size;
+ }
+ }
+
+ ReleaseBuffer(buf);
+ return result;
+}
+
+int
+DataLoader::LoadStream(const char* name, Sound*& snd, bool optional)
+{
+ if (!enable_media)
+ return 0;
+
+ if (!name)
+ return 0;
+
+ int namelen = strlen(name);
+
+ if (namelen < 5)
+ return 0;
+
+ if ((name[namelen-3] == 'o' || name[namelen-3] == 'O') &&
+ (name[namelen-2] == 'g' || name[namelen-2] == 'G') &&
+ (name[namelen-1] == 'g' || name[namelen-1] == 'G')) {
+
+ return LoadOggStream(name, snd);
+ }
+
+ int result = 0;
+
+ WAVE_HEADER head;
+ WAVE_FMT fmt;
+ WAVE_FACT fact;
+ WAVE_DATA data;
+ WAVEFORMATEX wfex;
+
+ LPBYTE buf;
+ LPBYTE p;
+ int len;
+
+ ZeroMemory(&head, sizeof(head));
+ ZeroMemory(&fmt, sizeof(fmt));
+ ZeroMemory(&fact, sizeof(fact));
+ ZeroMemory(&data, sizeof(data));
+
+ len = LoadPartialFile(name, buf, 4096, optional);
+
+ if (len > sizeof(head)) {
+ CopyMemory(&head, buf, sizeof(head));
+
+ if (head.RIFF == MAKEFOURCC('R', 'I', 'F', 'F') &&
+ head.WAVE == MAKEFOURCC('W', 'A', 'V', 'E')) {
+
+ p = buf + sizeof(WAVE_HEADER);
+
+ do {
+ DWORD chunk_id = *((LPDWORD) p);
+
+ switch (chunk_id) {
+ case MAKEFOURCC('f', 'm', 't', ' '):
+ CopyMemory(&fmt, p, sizeof(fmt));
+ p += fmt.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('f', 'a', 'c', 't'):
+ CopyMemory(&fact, p, sizeof(fact));
+ p += fact.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('s', 'm', 'p', 'l'):
+ CopyMemory(&fact, p, sizeof(fact));
+ p += fact.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('d', 'a', 't', 'a'):
+ CopyMemory(&data, p, sizeof(data));
+ p += 8;
+ break;
+
+ default:
+ ReleaseBuffer(buf);
+ return result;
+ }
+ }
+ while (data.chunk_size == 0);
+
+ wfex.wFormatTag = fmt.wFormatTag;
+ wfex.nChannels = fmt.nChannels;
+ wfex.nSamplesPerSec = fmt.nSamplesPerSec;
+ wfex.nAvgBytesPerSec = fmt.nAvgBytesPerSec;
+ wfex.nBlockAlign = fmt.nBlockAlign;
+ wfex.wBitsPerSample = fmt.wBitsPerSample;
+ wfex.cbSize = 0;
+
+ snd = Sound::Create(Sound::STREAMED, &wfex);
+
+ if (snd) {
+ // assemble file name:
+ char filename[1024];
+ strcpy(filename, datapath);
+ strcat(filename, name);
+
+ snd->StreamFile(filename, p - buf);
+
+ result = data.chunk_size;
+ }
+ }
+ }
+
+ ReleaseBuffer(buf);
+ return result;
+}
+
+int
+DataLoader::LoadOggStream(const char* name, Sound*& snd)
+{
+ if (!enable_media)
+ return 0;
+
+ snd = Sound::CreateOggStream(name);
+
+ return snd != 0;
+}
diff --git a/nGenEx/DataLoader.h b/nGenEx/DataLoader.h
new file mode 100644
index 0000000..87dbdbb
--- /dev/null
+++ b/nGenEx/DataLoader.h
@@ -0,0 +1,86 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: DataLoader.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef DataLoader_h
+#define DataLoader_h
+
+#include "Types.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+class Sound;
+class Video;
+
+// +--------------------------------------------------------------------+
+
+class DataLoader
+{
+public:
+ static const char* TYPENAME() { return "DataLoader"; }
+
+ enum { DATAFILE_OK, DATAFILE_INVALID, DATAFILE_NOTEXIST };
+
+ DataLoader();
+
+ static DataLoader* GetLoader() { return loader; }
+ static void Initialize();
+ static void Close();
+
+ void Reset();
+ void UseFileSystem(bool use=true);
+ void UseVideo(Video* v);
+ void EnableMedia(bool enable=true);
+
+ int EnableDatafile(const char* name);
+ int DisableDatafile(const char* name);
+
+ void SetDataPath(const char* path);
+ const char* GetDataPath() const { return datapath; }
+
+ bool IsFileSystemEnabled() const { return use_file_system; }
+ bool IsMediaLoadEnabled() const { return enable_media; }
+
+ bool FindFile(const char* fname);
+ int ListFiles(const char* filter, List<Text>& list, bool recurse=false);
+ int ListArchiveFiles(const char* archive, const char* filter, List<Text>& list);
+ int LoadBuffer(const char* name, BYTE*& buf, bool null_terminate=false, bool optional=false);
+ int LoadBitmap(const char* name, Bitmap& bmp, int type=0, bool optional=false);
+ int CacheBitmap(const char* name, Bitmap*& bmp, int type=0, bool optional=false);
+ int LoadTexture(const char* name, Bitmap*& bmp, int type=0, bool preload_cache=false, bool optional=false);
+ int LoadSound(const char* fname, Sound*& snd, DWORD flags=0, bool optional=false);
+ int LoadStream(const char* fname, Sound*& snd, bool optional=false);
+
+ void ReleaseBuffer(BYTE*& buf);
+ int fread(void* buffer, size_t size, size_t count, BYTE*& stream);
+
+private:
+ int LoadIndexed(const char* name, Bitmap& bmp, int type);
+ int LoadHiColor(const char* name, Bitmap& bmp, int type);
+ int LoadAlpha( const char* name, Bitmap& bmp, int type);
+
+ void ListFileSystem(const char* filter, List<Text>& list, Text base_path, bool recurse);
+ int LoadPartialFile(const char* fname, BYTE*& buf, int max_load, bool optional=false);
+ int LoadOggStream(const char* fname, Sound*& snd);
+
+ Text datapath;
+ Video* video;
+ bool use_file_system;
+ bool enable_media;
+
+ static DataLoader* loader;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif DataLoader_h
+
diff --git a/nGenEx/Director.h b/nGenEx/Director.h
new file mode 100644
index 0000000..7eda1b2
--- /dev/null
+++ b/nGenEx/Director.h
@@ -0,0 +1,44 @@
+/* Project nGen
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: Director.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Director (AI or Human Input) for Physical Objects
+*/
+
+#ifndef Director_h
+#define Director_h
+
+#include "Types.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class Physical;
+
+// +--------------------------------------------------------------------+
+
+class Director
+{
+public:
+ Director() { }
+ virtual ~Director() { }
+
+ // accessors:
+ virtual int Type() const { return 0; }
+ virtual int Subframe() const { return 0; }
+
+ // operations
+ virtual void ExecFrame(double factor) { }
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Director_h
+
diff --git a/nGenEx/EditBox.cpp b/nGenEx/EditBox.cpp
new file mode 100644
index 0000000..0cc780d
--- /dev/null
+++ b/nGenEx/EditBox.cpp
@@ -0,0 +1,452 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: EditBox.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ EditBox ActiveWindow class
+*/
+
+#include "MemDebug.h"
+#include "EditBox.h"
+#include "FormWindow.h"
+#include "Video.h"
+#include "Font.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+
+DWORD GetRealTime();
+
+// +--------------------------------------------------------------------+
+
+static int old_cursor;
+
+// +--------------------------------------------------------------------+
+
+EditBox::EditBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid)
+ : ScrollWindow(p->GetScreen(), ax, ay, aw, ah, aid, 0, p), caret_x(0), caret_y(0)
+{
+ sel_start = 0;
+ sel_length = 0;
+ h_offset = 0;
+ pass_char = 0;
+
+ selected_color = Color::Yellow;
+
+ char buf[32];
+ sprintf(buf, "EditBox %d", id);
+ desc = buf;
+}
+
+EditBox::EditBox(Screen* s, int ax, int ay, int aw, int ah, DWORD aid)
+ : ScrollWindow(s, ax, ay, aw, ah, aid), caret_x(0), caret_y(0)
+{
+ sel_start = 0;
+ sel_length = 0;
+ h_offset = 0;
+ pass_char = 0;
+
+ selected_color = Color::Yellow;
+
+ char buf[32];
+ sprintf(buf, "EditBox %d", id);
+ desc = buf;
+}
+
+EditBox::~EditBox()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EditBox::DrawContent(const Rect& ctrl_rect)
+{
+ int h = rect.h;
+
+ if (line_height < 1)
+ line_height = GetFont()->Height();
+ page_size = h / (line_height + leading);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EditBox::DrawTabbedText()
+{
+ if (font && text.length()) {
+ int border_size = 4;
+
+ if (style & WIN_RAISED_FRAME && style & WIN_SUNK_FRAME)
+ border_size = 8;
+
+ Rect label_rect = rect;
+
+ label_rect.x = border_size;
+ label_rect.y = border_size;
+ label_rect.w -= border_size * 2;
+ label_rect.h -= border_size * 2;
+
+ if (focus)
+ font->SetCaretIndex(sel_start);
+
+ // if displaying in password mode, create a display string
+ // containing the proper number of password chars:
+ Text s = text;
+ if (pass_char)
+ s = Text(pass_char, text.length());
+
+ // no tabs set:
+ if (tab[0] == 0) {
+ DWORD text_flags = DT_WORDBREAK | text_align;
+
+ if (single_line)
+ text_flags = text_flags | DT_SINGLELINE;
+
+ if (style & WIN_TEXT_SHADOW) {
+ label_rect.x++;
+ label_rect.y++;
+
+ font->SetColor(back_color);
+ DrawText(s.data() + h_offset, 0, label_rect, text_flags);
+
+ label_rect.x--;
+ label_rect.y--;
+ }
+
+ font->SetColor(fore_color);
+ DrawText(s.data() + h_offset, 0, label_rect, text_flags);
+ }
+
+ // use tabs:
+ else {
+ }
+
+ font->SetCaretIndex(-1);
+ }
+ else {
+ caret_x = 2;
+ caret_y = 3;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Color EditBox::GetSelectedColor()
+{
+ return selected_color;
+}
+
+void EditBox::SetSelectedColor(Color c)
+{
+ if (selected_color != c) {
+ selected_color = c;
+ }
+}
+
+Text EditBox::GetSelText()
+{
+ if (sel_start == 0 && sel_length == text.length())
+ return text;
+
+ Text selection;
+ char* buf = new(__FILE__,__LINE__) char[sel_length+1];
+
+ if (buf) {
+ for (int i = 0; i < sel_length; i++)
+ buf[i] = text[(int) (sel_start + i)];
+
+ buf[sel_length] = 0;
+
+ selection = buf;
+ delete [] buf;
+ }
+
+ return selection;
+}
+
+void EditBox::SetSelStart(int n)
+{
+ if (n >= 0 && n <= text.length())
+ sel_start = n;
+}
+
+void EditBox::SetSelLength(int n)
+{
+ if (n <= text.length() - sel_start)
+ sel_length = n;
+}
+
+void EditBox::EnsureCaretVisible()
+{
+ if (!single_line) {
+ h_offset = 0;
+ return;
+ }
+
+ if (sel_start < 0) {
+ sel_start = 0;
+ h_offset = 0;
+ }
+
+ else if (sel_start > h_offset) {
+ int x_caret;
+ bool moved;
+
+ do {
+ x_caret = 0;
+ moved = false;
+
+ if (pass_char) {
+ Text pass = Text(pass_char, sel_start-h_offset);
+ x_caret += font->StringWidth(pass.data(), pass.length());
+ }
+ else {
+ Text sub = text.substring(h_offset, sel_start-h_offset);
+ x_caret += font->StringWidth(sub.data(), sub.length());
+ }
+
+ if (x_caret >= Width()-4) {
+ if (h_offset < text.length()) {
+ h_offset++;
+ moved = true;
+ }
+ }
+ }
+ while (moved);
+ }
+
+ else {
+ h_offset = sel_start;
+ }
+}
+
+bool EditBox::CanScroll(int dir, int nlines)
+{
+ return false;
+}
+
+void EditBox::Scroll(int direction, int nlines)
+{
+ scrolling = SCROLL_NONE;
+}
+
+void EditBox::ScrollTo(int index)
+{
+}
+
+int EditBox::GetPageCount()
+{
+ return 1;
+}
+
+int EditBox::GetPageSize()
+{
+ return page_size;
+}
+
+// +--------------------------------------------------------------------+
+
+void EditBox::SetFocus()
+{
+ ActiveWindow::SetFocus();
+
+ sel_start = text.length();
+ sel_length = 0;
+}
+
+void EditBox::KillFocus()
+{
+ ActiveWindow::KillFocus();
+}
+
+// +--------------------------------------------------------------------+
+
+int EditBox::CaretFromPoint(int x, int y) const
+{
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int EditBox::OnMouseMove(int x, int y)
+{
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int EditBox::OnLButtonDown(int x, int y)
+{
+ return ActiveWindow::OnLButtonDown(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int EditBox::OnLButtonUp(int x, int y)
+{
+ return ActiveWindow::OnLButtonUp(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int EditBox::OnClick()
+{
+ int fire_click = !scrolling;
+
+ if (scrolling == SCROLL_THUMB)
+ scrolling = SCROLL_NONE;
+
+ if (fire_click)
+ return ActiveWindow::OnClick();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void EditBox::Insert(char c)
+{
+ if (single_line && c == '\n')
+ return;
+
+ if (sel_start < text.length()) {
+ if (sel_start == 0) {
+ text = Text(c) + text;
+ sel_start = 1;
+ }
+ else {
+ Text t1 = text.substring(0, sel_start);
+ Text t2 = text.substring(sel_start, text.length()-sel_start);
+ text = t1 + Text(c) + t2;
+ sel_start++;
+ }
+ }
+ else {
+ text += c;
+ sel_start = text.length();
+ }
+
+ EnsureCaretVisible();
+}
+
+void EditBox::Insert(const char* s)
+{
+}
+
+void EditBox::Delete()
+{
+ if (sel_start < text.length()) {
+ if (sel_start == 0) {
+ text = text.substring(1, text.length()-1);
+ }
+ else {
+ Text t1 = text.substring(0, sel_start);
+ Text t2 = text.substring(sel_start+1, text.length()-sel_start-1);
+ text = t1 + t2;
+ }
+ }
+
+ EnsureCaretVisible();
+}
+
+void EditBox::Backspace()
+{
+ if (sel_start > 0) {
+ if (sel_start == text.length()) {
+ text = text.substring(0, sel_start-1);
+ }
+ else {
+ Text t1 = text.substring(0, sel_start-1);
+ Text t2 = text.substring(sel_start, text.length()-sel_start);
+ text = t1 + t2;
+ }
+
+ sel_start--;
+ EnsureCaretVisible();
+ }
+}
+
+int EditBox::OnKeyDown(int vk, int flags)
+{
+ if (vk >= 'A' && vk <= 'Z') {
+ if (flags & 1)
+ Insert((char) vk);
+ else
+ Insert((char) tolower(vk));
+ }
+ else {
+ switch (vk) {
+ case VK_LEFT:
+ if (sel_start > 0) sel_start--;
+ EnsureCaretVisible();
+ break;
+
+ case VK_RIGHT:
+ if (sel_start < text.length()) sel_start++;
+ EnsureCaretVisible();
+ break;
+
+ case VK_HOME:
+ sel_start = 0;
+ EnsureCaretVisible();
+ break;
+
+ case VK_END:
+ sel_start = text.length();
+ EnsureCaretVisible();
+ break;
+
+ case VK_DELETE: Delete(); break;
+ case VK_BACK: Backspace(); break;
+
+ case VK_SPACE: Insert(' '); break;
+ case VK_RETURN: Insert('\n'); break;
+
+ case VK_NUMPAD0: Insert('0'); break;
+ case VK_NUMPAD1: Insert('1'); break;
+ case VK_NUMPAD2: Insert('2'); break;
+ case VK_NUMPAD3: Insert('3'); break;
+ case VK_NUMPAD4: Insert('4'); break;
+ case VK_NUMPAD5: Insert('5'); break;
+ case VK_NUMPAD6: Insert('6'); break;
+ case VK_NUMPAD7: Insert('7'); break;
+ case VK_NUMPAD8: Insert('8'); break;
+ case VK_NUMPAD9: Insert('9'); break;
+ case VK_DECIMAL: Insert('.'); break;
+ case VK_ADD: Insert('+'); break;
+ case VK_SUBTRACT: Insert('-'); break;
+ case VK_MULTIPLY: Insert('*'); break;
+ case VK_DIVIDE: Insert('/'); break;
+
+ case '0': if (flags & 1) Insert(')'); else Insert('0'); break;
+ case '1': if (flags & 1) Insert('!'); else Insert('1'); break;
+ case '2': if (flags & 1) Insert('@'); else Insert('2'); break;
+ case '3': if (flags & 1) Insert('#'); else Insert('3'); break;
+ case '4': if (flags & 1) Insert('$'); else Insert('4'); break;
+ case '5': if (flags & 1) Insert('%'); else Insert('5'); break;
+ case '6': if (flags & 1) Insert('^'); else Insert('6'); break;
+ case '7': if (flags & 1) Insert('&'); else Insert('7'); break;
+ case '8': if (flags & 1) Insert('*'); else Insert('8'); break;
+ case '9': if (flags & 1) Insert('('); else Insert('9'); break;
+ case 186: if (flags & 1) Insert(':'); else Insert(';'); break;
+ case 187: if (flags & 1) Insert('+'); else Insert('='); break;
+ case 188: if (flags & 1) Insert('<'); else Insert(','); break;
+ case 189: if (flags & 1) Insert('_'); else Insert('-'); break;
+ case 190: if (flags & 1) Insert('>'); else Insert('.'); break;
+ case 191: if (flags & 1) Insert('?'); else Insert('/'); break;
+ case 192: if (flags & 1) Insert('~'); else Insert('`'); break;
+ case 219: if (flags & 1) Insert('{'); else Insert('['); break;
+ case 221: if (flags & 1) Insert('}'); else Insert(']'); break;
+ case 220: if (flags & 1) Insert('|'); else Insert('\\'); break;
+ case 222: if (flags & 1) Insert('"'); else Insert('\''); break;
+ }
+ }
+
+ return ActiveWindow::OnKeyDown(vk, flags);
+}
diff --git a/nGenEx/EditBox.h b/nGenEx/EditBox.h
new file mode 100644
index 0000000..fba4fbf
--- /dev/null
+++ b/nGenEx/EditBox.h
@@ -0,0 +1,92 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: EditBox.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ EditBox ActiveWindow class
+*/
+
+#ifndef EditBox_h
+#define EditBox_h
+
+#include "Types.h"
+#include "ScrollWindow.h"
+
+// +--------------------------------------------------------------------+
+
+class EditBox : public ScrollWindow
+{
+public:
+ static const char* TYPENAME() { return "EditBox"; }
+
+ enum ALIGN { EDIT_ALIGN_LEFT = DT_LEFT,
+ EDIT_ALIGN_CENTER = DT_CENTER,
+ EDIT_ALIGN_RIGHT = DT_RIGHT
+ };
+
+ EditBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid);
+ EditBox(Screen* s, int ax, int ay, int aw, int ah, DWORD aid);
+ virtual ~EditBox();
+
+ // Operations:
+ virtual void DrawContent(const Rect& ctrl_rect);
+ virtual void DrawTabbedText();
+
+ // Event Target Interface:
+ virtual void SetFocus();
+ virtual void KillFocus();
+
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnClick();
+
+ virtual int OnKeyDown(int vk, int flags);
+
+ // ScrollWindow Interface:
+ virtual bool CanScroll(int direction, int nlines=1);
+ virtual void Scroll(int direction, int nlines=1);
+ virtual void ScrollTo(int index);
+ virtual int GetPageCount();
+ virtual int GetPageSize();
+
+ Color GetSelectedColor();
+ void SetSelectedColor(Color c);
+
+ int GetSelStart() { return sel_start; }
+ int GetSelLength() { return sel_length; }
+ Text GetSelText();
+
+ void SetSelStart(int n);
+ void SetSelLength(int n);
+
+ char GetPasswordChar() { return pass_char; }
+ void SetPasswordChar(char c) { pass_char = c; }
+
+protected:
+ void Insert(char c);
+ void Insert(const char* s);
+ void Delete();
+ void Backspace();
+ int CaretFromPoint(int x, int y) const;
+ void EnsureCaretVisible();
+
+ int sel_start;
+ int sel_length;
+ int h_offset;
+ int caret_x;
+ int caret_y;
+
+ char pass_char;
+
+ Color selected_color;
+};
+
+#endif EditBox_h
+
diff --git a/nGenEx/Encrypt.cpp b/nGenEx/Encrypt.cpp
new file mode 100644
index 0000000..6fa79d2
--- /dev/null
+++ b/nGenEx/Encrypt.cpp
@@ -0,0 +1,171 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Encrypt.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Simple Encryption / Decryption class
+*/
+
+
+#include "MemDebug.h"
+#include "Encrypt.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+static long k[4] = {
+ 0x3B398E26,
+ 0x40C29501,
+ 0x614D7630,
+ 0x7F59409A
+};
+
+static void encypher(long* v)
+{
+ DWORD y=v[0];
+ DWORD z=v[1];
+ DWORD sum=0;
+ DWORD delta=0x9e3779b9; // a key schedule constant
+ DWORD n=32; // num iterations
+
+ while (n-->0) { // basic cycle start
+ sum += delta;
+ y += (z<<4)+k[0] ^ z+sum ^ (z>>5)+k[1];
+ z += (y<<4)+k[2] ^ y+sum ^ (y>>5)+k[3];
+ }
+
+ v[0]=y;
+ v[1]=z;
+}
+
+static void decypher(long* v)
+{
+ DWORD y=v[0];
+ DWORD z=v[1];
+ DWORD sum=0;
+ DWORD delta=0x9e3779b9; // a key schedule constant
+ DWORD n=32; // num iterations
+
+ sum=delta<<5;
+
+ while (n-->0) {
+ z-= (y<<4)+k[2] ^ y+sum ^ (y>>5)+k[3];
+ y-= (z<<4)+k[0] ^ z+sum ^ (z>>5)+k[1];
+ sum-=delta;
+ }
+
+ v[0]=y;
+ v[1]=z;
+}
+
+// +-------------------------------------------------------------------+
+
+Text
+Encryption::Encrypt(Text block)
+{
+ int len = block.length();
+
+ if (len < 1)
+ return Text();
+
+ // pad to eight byte chunks
+ if (len & 0x7) {
+ len /= 8;
+ len *= 8;
+ len += 8;
+ }
+
+ BYTE* work = new(__FILE__,__LINE__) BYTE[len];
+ ZeroMemory(work, len);
+ CopyMemory(work, block.data(), block.length());
+
+ long* v = (long*) work;
+ for (int i = 0; i < len/8; i++) {
+ encypher(v);
+ v += 2;
+ }
+
+ Text cypher((const char*) work, len);
+ delete [] work;
+ return cypher;
+}
+
+// +-------------------------------------------------------------------+
+
+Text
+Encryption::Decrypt(Text block)
+{
+ int len = block.length();
+
+ if (len & 0x7) {
+ Print("WARNING: attempt to decrypt odd length block (len=%d)\n", len);
+ return Text();
+ }
+
+ BYTE* work = new(__FILE__,__LINE__) BYTE[len];
+ CopyMemory(work, block.data(), len);
+
+ long* v = (long*) work;
+ for (int i = 0; i < len/8; i++) {
+ decypher(v);
+ v += 2;
+ }
+
+ Text clear((const char*) work, len);
+ delete [] work;
+ return clear;
+}
+
+// +-------------------------------------------------------------------+
+
+static const char* codes = "abcdefghijklmnop";
+
+Text
+Encryption::Encode(Text block)
+{
+ int len = block.length() * 2;
+ char* work = new(__FILE__,__LINE__) char[len + 1];
+
+ for (int i = 0; i < block.length(); i++) {
+ BYTE b = (BYTE) (block.data()[i]);
+ work[2*i] = codes[b>>4 & 0xf];
+ work[2*i+1] = codes[b & 0xf];
+ }
+
+ work[len] = 0;
+
+ Text code(work, len);
+ delete [] work;
+ return code;
+}
+
+// +-------------------------------------------------------------------+
+
+Text
+Encryption::Decode(Text block)
+{
+ int len = block.length() / 2;
+ char* work = new(__FILE__,__LINE__) char[len + 1];
+
+ for (int i = 0; i < len; i++) {
+ char u = block[2*i];
+ char l = block[2*i + 1];
+
+ work[i] = (u - codes[0]) << 4 |
+ (l - codes[0]);
+ }
+
+ work[len] = 0;
+
+ Text clear(work, len);
+ delete [] work;
+ return clear;
+}
+
diff --git a/nGenEx/Encrypt.h b/nGenEx/Encrypt.h
new file mode 100644
index 0000000..9024740
--- /dev/null
+++ b/nGenEx/Encrypt.h
@@ -0,0 +1,38 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Encrypt.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Simple Encryption / Decryption class
+*/
+
+
+#ifndef Encrypt_h
+#define Encrypt_h
+
+#include "Types.h"
+#include "Text.h"
+
+// +-------------------------------------------------------------------+
+
+class Encryption
+{
+public:
+ // private key encryption / decryption of
+ // arbitrary blocks of data
+ static Text Encrypt(Text block);
+ static Text Decrypt(Text block);
+
+ // encode / decode binary blocks into
+ // ascii strings for use in text files
+ static Text Encode(Text block);
+ static Text Decode(Text block);
+};
+
+#endif Encrypt_h \ No newline at end of file
diff --git a/nGenEx/EventDispatch.cpp b/nGenEx/EventDispatch.cpp
new file mode 100644
index 0000000..e17713b
--- /dev/null
+++ b/nGenEx/EventDispatch.cpp
@@ -0,0 +1,275 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: EventDispatch.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include "MemDebug.h"
+#include "EventDispatch.h"
+#include "Mouse.h"
+#include "Keyboard.h"
+
+// +--------------------------------------------------------------------+
+
+int GetKeyPlus(int& key, int& shift);
+
+// +--------------------------------------------------------------------+
+
+EventDispatch* EventDispatch::dispatcher = 0;
+
+// +--------------------------------------------------------------------+
+
+EventDispatch::EventDispatch()
+ : capture(0), current(0), focus(0), click_tgt(0)
+ , mouse_x(0), mouse_y(0), mouse_l(0), mouse_r(0)
+{ }
+
+EventDispatch::~EventDispatch()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+EventDispatch::Create()
+{
+ dispatcher = new(__FILE__,__LINE__) EventDispatch;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EventDispatch::Close()
+{
+ delete dispatcher;
+ dispatcher = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EventDispatch::Dispatch()
+{
+ int ml = Mouse::LButton();
+ int mr = Mouse::RButton();
+ int mx = Mouse::X();
+ int my = Mouse::Y();
+ int mw = Mouse::Wheel();
+
+ EventTarget* mouse_tgt = capture;
+ EventTarget* key_tgt = focus;
+ EventTarget* do_click = 0;
+
+ if (!mouse_tgt) {
+ ListIter<EventTarget> iter = clients;
+ while (++iter) {
+ EventTarget* test = iter.value();
+ if (test->IsFormActive()) {
+ if (test->TargetRect().Contains(mx,my))
+ mouse_tgt = test;
+
+ if (test->HasFocus())
+ key_tgt = test;
+ }
+ }
+ }
+
+ // Mouse Events:
+
+ if (mouse_tgt != current) {
+ if (current && current->IsEnabled() && current->IsVisible())
+ current->OnMouseExit(mx,my);
+
+ current = mouse_tgt;
+
+ if (current && current->IsEnabled() && current->IsVisible())
+ current->OnMouseEnter(mx,my);
+ }
+
+ if (mouse_tgt && mouse_tgt->IsEnabled()) {
+ if (mx != mouse_x || my != mouse_y)
+ mouse_tgt->OnMouseMove(mx,my);
+
+ if (mw != 0)
+ mouse_tgt->OnMouseWheel(mw);
+
+ if (ml != mouse_l) {
+ if (ml) {
+ mouse_tgt->OnLButtonDown(mx,my);
+ click_tgt = mouse_tgt;
+ }
+ else {
+ mouse_tgt->OnLButtonUp(mx,my);
+
+ if (click_tgt == mouse_tgt) {
+ if (click_tgt->TargetRect().Contains(mx,my))
+ do_click = click_tgt;
+ click_tgt = 0;
+ }
+ }
+ }
+
+ if (mr != mouse_r) {
+ if (mr)
+ mouse_tgt->OnRButtonDown(mx,my);
+ else
+ mouse_tgt->OnRButtonUp(mx,my);
+ }
+ }
+
+ mouse_l = ml;
+ mouse_r = mr;
+ mouse_x = mx;
+ mouse_y = my;
+
+ // Keyboard Events:
+
+ if (click_tgt && click_tgt != key_tgt) {
+ if (key_tgt) key_tgt->KillFocus();
+ key_tgt = click_tgt;
+
+ if (key_tgt != focus) {
+ if (focus) focus->KillFocus();
+
+ if (key_tgt && key_tgt->IsEnabled() && key_tgt->IsVisible())
+ focus = key_tgt;
+ else
+ key_tgt = 0;
+
+ if (focus) focus->SetFocus();
+ }
+ }
+
+ if (key_tgt && key_tgt->IsEnabled()) {
+ int key = 0;
+ int shift = 0;
+
+ while (GetKeyPlus(key, shift)) {
+ if (key == VK_ESCAPE) {
+ key_tgt->KillFocus();
+ focus = 0;
+ break;
+ }
+
+ else if (key == VK_TAB && focus) {
+ int key_index = clients.index(focus) + 1;
+
+ if (shift & 1) key_index -= 2;
+
+ if (key_index >= clients.size())
+ key_index = 0;
+ else if (key_index < 0)
+ key_index = clients.size()-1;
+
+ if (focus) focus->KillFocus();
+ focus = clients[key_index];
+ if (focus) focus->SetFocus();
+
+ break;
+ }
+
+ key_tgt->OnKeyDown(key, shift);
+ }
+ }
+
+ if (do_click)
+ do_click->OnClick();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EventDispatch::MouseEnter(EventTarget* mouse_tgt)
+{
+ if (mouse_tgt != current) {
+ if (current) current->OnMouseExit(0,0);
+ current = mouse_tgt;
+ if (current) current->OnMouseEnter(0,0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EventDispatch::Register(EventTarget* tgt)
+{
+ if (!clients.contains(tgt))
+ clients.append(tgt);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EventDispatch::Unregister(EventTarget* tgt)
+{
+ clients.remove(tgt);
+
+ if (capture == tgt) capture = 0;
+ if (current == tgt) current = 0;
+ if (focus == tgt) focus = 0;
+ if (click_tgt == tgt) click_tgt = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+EventTarget*
+EventDispatch::GetCapture()
+{
+ return capture;
+}
+
+int
+EventDispatch::CaptureMouse(EventTarget* tgt)
+{
+ if (tgt) {
+ capture = tgt;
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+EventDispatch::ReleaseMouse(EventTarget* tgt)
+{
+ if (capture == tgt) {
+ capture = 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+EventTarget*
+EventDispatch::GetFocus()
+{
+ return focus;
+}
+
+void
+EventDispatch::SetFocus(EventTarget* tgt)
+{
+ if (focus != tgt) {
+ if (focus)
+ focus->KillFocus();
+
+ focus = tgt;
+
+ if (focus && focus->IsEnabled() && focus->IsVisible())
+ focus->SetFocus();
+ }
+}
+
+void
+EventDispatch::KillFocus(EventTarget* tgt)
+{
+ if (focus && focus == tgt) {
+ focus = 0;
+ tgt->KillFocus();
+ }
+}
diff --git a/nGenEx/EventDispatch.h b/nGenEx/EventDispatch.h
new file mode 100644
index 0000000..d2e3150
--- /dev/null
+++ b/nGenEx/EventDispatch.h
@@ -0,0 +1,62 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: EventDispatch.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Event Dispatch class
+*/
+
+#ifndef EventDispatch_h
+#define EventDispatch_h
+
+#include "Types.h"
+#include "EventTarget.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class EventDispatch
+{
+public:
+ static const char* TYPENAME() { return "EventDispatch"; }
+
+ EventDispatch();
+ virtual ~EventDispatch();
+
+ static void Create();
+ static void Close();
+ static EventDispatch* GetInstance() { return dispatcher; }
+
+ virtual void Dispatch();
+ virtual void Register(EventTarget* tgt);
+ virtual void Unregister(EventTarget* tgt);
+
+ virtual EventTarget* GetCapture();
+ virtual int CaptureMouse(EventTarget* tgt);
+ virtual int ReleaseMouse(EventTarget* tgt);
+
+ virtual EventTarget* GetFocus();
+ virtual void SetFocus(EventTarget* tgt);
+ virtual void KillFocus(EventTarget* tgt);
+
+ virtual void MouseEnter(EventTarget* tgt);
+
+protected:
+ int mouse_x, mouse_y, mouse_l, mouse_r;
+ List<EventTarget> clients;
+ EventTarget* capture;
+ EventTarget* current;
+ EventTarget* focus;
+ EventTarget* click_tgt;
+
+ static EventDispatch* dispatcher;
+};
+
+#endif EventDispatch_h
+
diff --git a/nGenEx/EventTarget.h b/nGenEx/EventTarget.h
new file mode 100644
index 0000000..af24a58
--- /dev/null
+++ b/nGenEx/EventTarget.h
@@ -0,0 +1,59 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: EventTarget.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Event Target interface class
+*/
+
+#ifndef EventTarget_h
+#define EventTarget_h
+
+#include "Types.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class EventTarget
+{
+public:
+ static const char* TYPENAME() { return "EventTarget"; }
+
+ virtual ~EventTarget() { }
+
+ int operator == (const EventTarget& t) const { return this == &t; }
+
+ virtual int OnMouseMove(int x, int y) { return 0; }
+ virtual int OnLButtonDown(int x, int y) { return 0; }
+ virtual int OnLButtonUp(int x, int y) { return 0; }
+ virtual int OnClick() { return 0; }
+ virtual int OnSelect() { return 0; }
+ virtual int OnRButtonDown(int x, int y) { return 0; }
+ virtual int OnRButtonUp(int x, int y) { return 0; }
+ virtual int OnMouseEnter(int x, int y) { return 0; }
+ virtual int OnMouseExit(int x, int y) { return 0; }
+ virtual int OnMouseWheel(int wheel) { return 0; }
+
+ virtual int OnKeyDown(int vk, int flags) { return 0; }
+
+ virtual void SetFocus() { }
+ virtual void KillFocus() { }
+ virtual bool HasFocus() const { return false; }
+
+ virtual bool IsEnabled() const { return true; }
+ virtual bool IsVisible() const { return true; }
+ virtual bool IsFormActive() const { return true; }
+
+ virtual Rect TargetRect() const { return Rect(); }
+
+ virtual const char* GetDescription() const { return "EventTarget"; }
+};
+
+#endif EventTarget_h
+
diff --git a/nGenEx/FadeView.cpp b/nGenEx/FadeView.cpp
new file mode 100644
index 0000000..3ff62f7
--- /dev/null
+++ b/nGenEx/FadeView.cpp
@@ -0,0 +1,145 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FadeView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Fading Bitmap "billboard" Image View class
+*/
+
+#include "MemDebug.h"
+#include "FadeView.h"
+#include "Color.h"
+#include "Window.h"
+#include "Video.h"
+#include "Screen.h"
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+
+FadeView::FadeView(Window* c, double in, double out, double hold)
+ : View(c),
+ fade_in(in * 1000),
+ fade_out(out * 1000),
+ hold_time(hold * 1000),
+ step_time(0),
+ fast(1),
+ time(0)
+{
+ state = StateStart;
+}
+
+FadeView::~FadeView()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void FadeView::FadeIn(double in) { fade_in = in * 1000; }
+void FadeView::FadeOut(double out) { fade_out = out * 1000; }
+void FadeView::FastFade(int fade_fast) { fast = fade_fast; }
+void FadeView::StopHold()
+{
+ //Print(" FadeView::StopHold()\n");
+ hold_time = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FadeView::Refresh()
+{
+ double msec = 0;
+
+ if (state == StateStart) {
+ time = Game::RealTime();
+ }
+ else if (state != StateDone) {
+ double new_time = Game::RealTime();
+ msec = new_time - time;
+ time = new_time;
+ }
+
+ switch (state) {
+ case StateStart:
+ if (fade_in) {
+ //Print(" * FadeView: %f, %f, %f\n", fade_in, fade_out, hold_time);
+ Color::SetFade(0);
+ //Print(" 1. FadeView SetFade to 0 (%6.1f)\n", time);
+ }
+
+ step_time = 0;
+ state = State2;
+ break;
+
+ case State2:
+ if (fade_in) {
+ Color::SetFade(0);
+ //Print(" 1. FadeView SetFade to 0 (%6.1f)\n", time);
+ }
+
+ step_time = 0;
+ state = StateIn;
+ break;
+
+ case StateIn:
+ if (step_time < fade_in) {
+ double fade = step_time / fade_in;
+ Color::SetFade(fade);
+ //Print(" 2. FadeView SetFade to %3d (%6.1f) %6.1f\n", (int) (fade * 100), time, step_time);
+ step_time += msec;
+ }
+ else {
+ Color::SetFade(1);
+ //Print(" 2. FadeView SetFade to %3d (%6.1f) %6.1f => HOLDING\n", 100, time, step_time);
+ step_time = 0;
+ state = StateHold;
+ }
+ break;
+
+ case StateHold:
+ if (step_time < hold_time) {
+ step_time += msec;
+ //Print(" 3. FadeView holding at %3d (%6.1f) %6.1f\n", 100, time, step_time);
+ }
+ else {
+ //Print(" 3. FadeView HOLD COMPLETE (%6.1f) %6.1f\n", time, step_time);
+ step_time = 0;
+ state = StateOut;
+ }
+ break;
+
+ case StateOut:
+ if (fade_out > 0) {
+ if (step_time < fade_out) {
+ double fade = 1 - step_time / fade_out;
+ Color::SetFade(fade);
+ //Print(" 4. FadeView SetFade to %3d (%6.1f) %6.1f\n", (int) (fade*100), time, step_time);
+ step_time += msec;
+ }
+ else {
+ Color::SetFade(0);
+ //Print(" 4. FadeView SetFade to %3d (%6.1f) %6.1f\n", 0, time, step_time);
+ step_time = 0;
+ state = StateDone;
+ }
+ }
+ else {
+ Color::SetFade(1);
+ //Print(" 4. FadeView SetFade to %3d (%6.1f) %6.1f\n", 0, time, step_time);
+ step_time = 0;
+ state = StateDone;
+ }
+ break;
+
+ default:
+ case StateDone:
+ //Print(" 5. FadeView done (%6.1f) %6.1f\n", time, step_time);
+ break;
+ }
+}
+
diff --git a/nGenEx/FadeView.h b/nGenEx/FadeView.h
new file mode 100644
index 0000000..ed7f15a
--- /dev/null
+++ b/nGenEx/FadeView.h
@@ -0,0 +1,56 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FadeView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Non-rendering view class that controls the fade level (fade-in/fade-out)
+*/
+
+#ifndef FadeView_h
+#define FadeView_h
+
+#include "Types.h"
+#include "View.h"
+
+// +--------------------------------------------------------------------+
+
+class FadeView : public View
+{
+public:
+ static const char* TYPENAME() { return "FadeView"; }
+
+ enum FadeState { StateStart, State2, StateIn, StateHold, StateOut, StateDone };
+
+ FadeView(Window* c, double fade_in=1, double fade_out=1, double hold_time=4);
+ virtual ~FadeView();
+
+ // Operations:
+ virtual void Refresh();
+ virtual bool Done() const { return state == StateDone; }
+ virtual bool Holding() const { return state == StateHold; }
+
+ // Control:
+ virtual void FastFade(int fade_fast);
+ virtual void FadeIn(double fade_in);
+ virtual void FadeOut(double fade_out);
+ virtual void StopHold();
+
+protected:
+ double fade_in;
+ double fade_out;
+ double hold_time;
+ double time;
+ double step_time;
+
+ int fast;
+ FadeState state;
+};
+
+#endif FadeView_h
+
diff --git a/nGenEx/Fix.cpp b/nGenEx/Fix.cpp
new file mode 100644
index 0000000..f6dd213
--- /dev/null
+++ b/nGenEx/Fix.cpp
@@ -0,0 +1,24 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Fix.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Fixed point number class with 16 bits of fractional precision
+*/
+
+#include "MemDebug.h"
+#include "Fix.h"
+
+// +--------------------------------------------------------------------+
+
+const fix fix::one = fix(1);
+const fix fix::two = fix(2);
+const fix fix::three = fix(3);
+const fix fix::five = fix(5);
+const fix fix::ten = fix(10);
diff --git a/nGenEx/Fix.h b/nGenEx/Fix.h
new file mode 100644
index 0000000..b67185e
--- /dev/null
+++ b/nGenEx/Fix.h
@@ -0,0 +1,180 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Fix.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Fixed point number class with 16 bits of fractional precision
+*/
+
+#ifndef Fix_h
+#define Fix_h
+
+// +--------------------------------------------------------------------+
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+const double fix_sixty_five=65536.0;
+
+inline int fast_f2i(double d)
+{
+ int i;
+
+ _asm {
+ fld d
+ fistp i
+ }
+
+ return i;
+}
+
+// +--------------------------------------------------------------------+
+
+class fix
+{
+public:
+ static const char* TYPENAME() { return "fix"; }
+
+ enum FixDef { Precision=16, IntMask=0xffff0000, FractMask=0x0000ffff };
+ static const fix one;
+ static const fix two;
+ static const fix three;
+ static const fix five;
+ static const fix ten;
+
+ fix() { }
+ fix(int n) : val(n<<Precision) { }
+ fix(double d)
+ {
+ long ival;
+ _asm {
+ fld fix_sixty_five
+ fmul d
+ fistp ival
+ }
+ val=ival;
+ }
+ fix(const fix& f) : val(f.val) { }
+
+ // conversion operators:
+ operator int () const { return (val>>Precision); }
+ operator float () const { return ((float) val) / ((float) fix_sixty_five); }
+ operator double() const { return ((double) val) / fix_sixty_five; }
+
+ // assignment operators:
+ fix& operator=(const fix& f) { val=f.val; return *this; }
+ fix& operator=(int n) { val=(n<<Precision); return *this; }
+ fix& operator=(double d) { long ival;
+ _asm { fld fix_sixty_five
+ fmul d
+ fistp ival }
+ val = ival;
+ return *this; }
+
+ // comparison operators:
+ int operator==(const fix& f) const { return val==f.val; }
+ int operator!=(const fix& f) const { return val!=f.val; }
+ int operator<=(const fix& f) const { return val<=f.val; }
+ int operator>=(const fix& f) const { return val>=f.val; }
+ int operator< (const fix& f) const { return val< f.val; }
+ int operator> (const fix& f) const { return val> f.val; }
+
+ // arithmetic operators:
+ fix operator+(const fix& f) const { fix r; r.val = val+f.val; return r; }
+ fix operator-(const fix& f) const { fix r; r.val = val-f.val; return r; }
+ fix operator*(const fix& f) const { long a=val; long b=f.val;
+ _asm {
+ mov eax, a
+ mov edx, b
+ imul edx
+ shrd eax, edx, 16
+ mov a, eax
+ }
+ fix r; r.val = a; return r; }
+ fix operator/(const fix& f) const { long a=val; long b=f.val;
+ _asm {
+ mov eax, a
+ mov ebx, b
+ mov edx, eax
+ sar edx, 16
+ shl eax, 16
+ idiv ebx
+ mov a, eax
+ }
+ fix r; r.val = a; return r; }
+ fix& operator+=(const fix& f) { val+=f.val; return *this; }
+ fix& operator-=(const fix& f) { val-=f.val; return *this; }
+ fix& operator*=(const fix& f) { long a=val; long b=f.val;
+ _asm {
+ mov eax, a
+ mov edx, b
+ imul edx
+ shrd eax, edx, 16
+ mov a, eax
+ }
+ val=a; return *this; }
+ fix& operator/=(const fix& f) { long a=val; long b=f.val;
+ _asm {
+ mov eax, a
+ mov ebx, b
+ mov edx, eax
+ sar edx, 16
+ shl eax, 16
+ idiv ebx
+ mov a, eax
+ }
+ val=a; return *this; }
+
+ fix operator+(int n) const { fix r; r.val = val+(n<<Precision); return r; }
+ fix operator-(int n) const { fix r; r.val = val-(n<<Precision); return r; }
+ fix operator*(int n) const { fix r; r.val = val*n; return r; }
+ fix operator/(int n) const { fix r; r.val = val/n; return r; }
+ fix& operator+=(int n) { val+=(n<<Precision); return *this; }
+ fix& operator-=(int n) { val-=(n<<Precision); return *this; }
+ fix& operator*=(int n) { val*=n; return *this; }
+ fix& operator/=(int n) { val/=n; return *this; }
+
+ fix operator+(double d) const { fix f(d); return (*this)+f; }
+ fix operator-(double d) const { fix f(d); return (*this)-f; }
+ fix operator*(double d) const { fix f(d); return (*this)*f; }
+ fix operator/(double d) const { fix f(d); return (*this)/f; }
+ fix& operator+=(double d) { fix f(d); val+=f.val; return *this; }
+ fix& operator-=(double d) { fix f(d); val-=f.val; return *this; }
+ fix& operator*=(double d) { int n; _asm { fld d
+ fistp n } val*=n; return *this; }
+ fix& operator/=(double d) { int n; _asm { fld d
+ fistp n } val/=n; return *this; }
+
+ // misc. functions:
+ fix truncate() const { fix r; r.val = val&IntMask; return r; }
+ fix fraction() const { fix r; r.val = val-truncate().val; return r; }
+ fix floor() const { fix r; r.val = val&IntMask; return r; }
+ fix ceil() const { fix r; r.val = (val+FractMask)&IntMask; return r; }
+ fix adjust_up() const { fix r; r.val = val+FractMask; return r; }
+ fix adjust_down() const { fix r; r.val = val-FractMask; return r; }
+
+ fix muldiv(const fix& num, const fix& den) const
+ { long a=val, b=num.val, c=den.val;
+ _asm {
+ mov eax, a
+ mov edx, b
+ mov ebx, c
+ imul edx
+ idiv ebx
+ mov a, eax
+ }
+ fix r; r.val = a; return r; }
+
+ // data:
+ long val;
+};
+
+#endif Fix_h
+
diff --git a/nGenEx/Font.cpp b/nGenEx/Font.cpp
new file mode 100644
index 0000000..e1c90dc
--- /dev/null
+++ b/nGenEx/Font.cpp
@@ -0,0 +1,1232 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Font.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Font Resource class implementation
+*/
+
+#include "MemDebug.h"
+#include "Font.h"
+#include "Polygon.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "ParseUtil.h"
+#include "Video.h"
+
+DWORD GetRealTime();
+
+// +--------------------------------------------------------------------+
+
+Font::Font()
+ : flags(0), height(0), baseline(0), interspace(0), spacewidth(0),
+ imagewidth(0), image(0), expansion(0), alpha(1), blend(Video::BLEND_ALPHA),
+ scale(1), material(0), vset(0), polys(0), npolys(0),
+ caret_index(-1), caret_x(0), caret_y(0), tgt_bitmap(0)
+{
+ ZeroMemory(name, sizeof(name));
+ ZeroMemory(glyph, sizeof(glyph));
+ ZeroMemory(kern, sizeof(kern));
+}
+
+Font::Font(const char* n)
+ : flags(0), height(0), baseline(0), interspace(0), spacewidth(4),
+ imagewidth(0), image(0), expansion(0), alpha(1), blend(Video::BLEND_ALPHA),
+ scale(1), material(0), vset(0), polys(0), npolys(0),
+ caret_index(-1), caret_x(0), caret_y(0), tgt_bitmap(0)
+{
+ ZeroMemory(glyph, sizeof(glyph));
+ ZeroMemory(kern, sizeof(kern));
+ CopyMemory(name, n, sizeof(name));
+
+ if (!Load(name)) {
+ flags = 0;
+ height = 0;
+ baseline = 0;
+ interspace = 0;
+ spacewidth = 0;
+ imagewidth = 0;
+ image = 0;
+
+ ZeroMemory(glyph, sizeof(glyph));
+ ZeroMemory(kern, sizeof(kern));
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Font::~Font()
+{
+ if (image) delete [] image;
+ if (vset) delete vset;
+ if (polys) delete [] polys;
+ if (material) delete material;
+}
+
+// +--------------------------------------------------------------------+
+
+static char kern_tweak[256][256];
+
+bool
+Font::Load(const char* name)
+{
+ if (!name || !name[0])
+ return false;
+
+ char imgname[256];
+ char defname[256];
+ wsprintf(defname, "%s.def", name);
+ wsprintf(imgname, "%s.pcx", name);
+
+ DataLoader* loader = DataLoader::GetLoader();
+ if (!loader)
+ return false;
+
+ LoadDef(defname, imgname);
+
+ for (int i = 0; i < 256; i++) {
+ glyph[i].offset = GlyphOffset(i);
+ glyph[i].width = 0;
+ }
+
+ if (loader->LoadBitmap(imgname, bitmap)) {
+ if (!bitmap.Pixels() && !bitmap.HiPixels())
+ return false;
+
+ scale = bitmap.Width() / 256;
+ imagewidth = bitmap.Width();
+ if (height > bitmap.Height())
+ height = bitmap.Height();
+
+ int imgsize = bitmap.Width() * bitmap.Height();
+ image = new(__FILE__,__LINE__) BYTE[imgsize];
+
+ if (image) {
+ if (bitmap.Pixels()) {
+ CopyMemory(image, bitmap.Pixels(), imgsize);
+ }
+
+ else {
+ for (int i = 0; i < imgsize; i++)
+ image[i] = (BYTE) bitmap.HiPixels()[i].Alpha();
+ }
+ }
+
+ material = new(__FILE__,__LINE__) Material;
+ material->tex_diffuse = &bitmap;
+ }
+ else {
+ return false;
+ }
+
+ for (i = 0; i < 256; i++) {
+ glyph[i].width = CalcWidth(i);
+ }
+
+ color = Color::White;
+
+ if (!(flags & (FONT_FIXED_PITCH | FONT_NO_KERN)))
+ AutoKern();
+
+ for (i = 0; i < 256; i++) {
+ for (int j = 0; j < 256; j++) {
+ if (kern_tweak[i][j] < 100) {
+ kern[i][j] = kern_tweak[i][j];
+ }
+ }
+ }
+
+ return true;
+}
+
+void
+Font::LoadDef(char* defname, char* imgname)
+{
+ for (int i = 0; i < 256; i++)
+ for (int j = 0; j < 256; j++)
+ kern_tweak[i][j] = 111;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ if (!loader)
+ return;
+
+ BYTE* block;
+ int blocklen = loader->LoadBuffer(defname, block, true);
+
+ if (!block || blocklen < 4)
+ return;
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("WARNING: could not parse '%s'\n", defname);
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "FONT") {
+ Print("WARNING: invalid font def file '%s'\n", defname);
+ return;
+ }
+ }
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value().indexOf("image") == 0) {
+ GetDefText(imgname, def, defname);
+ }
+
+ else if (def->name()->value() == "height") {
+ int h=0;
+ GetDefNumber(h, def, defname);
+
+ if (h >= 0 && h <= 32)
+ height = (BYTE) h;
+ }
+
+ else if (def->name()->value() == "baseline") {
+ int b=0;
+ GetDefNumber(b, def, defname);
+
+ if (b >= 0 && b <= 32)
+ baseline = (BYTE) b;
+ }
+
+ else if (def->name()->value() == "flags") {
+ if (def->term()->isText()) {
+ Text buf;
+ GetDefText(buf, def, defname);
+ buf.setSensitive(false);
+
+ flags = 0;
+
+ if (buf.contains("caps"))
+ flags = flags | FONT_ALL_CAPS;
+
+ if (buf.contains("kern"))
+ flags = flags | FONT_NO_KERN;
+
+ if (buf.contains("fixed"))
+ flags = flags | FONT_FIXED_PITCH;
+ }
+
+ else {
+ int f=0;
+ GetDefNumber(f, def, defname);
+ flags = (WORD) f;
+ }
+ }
+
+ else if (def->name()->value() == "interspace") {
+ int n=0;
+ GetDefNumber(n, def, defname);
+
+ if (n >= 0 && n <= 100)
+ interspace = (BYTE) n;
+ }
+
+ else if (def->name()->value() == "spacewidth") {
+ int n=0;
+ GetDefNumber(n, def, defname);
+
+ if (n >= 0 && n <= 100)
+ spacewidth = (BYTE) n;
+ }
+
+ else if (def->name()->value() == "expansion") {
+ GetDefNumber(expansion, def, defname);
+ }
+
+ else if (def->name()->value() == "kern") {
+ TermStruct* val = def->term()->isStruct();
+
+ char a[8], b[8];
+ int k=111;
+
+ a[0] = 0;
+ b[0] = 0;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "left" || pdef->name()->value() == "a")
+ GetDefText(a, pdef, defname);
+
+ else if (pdef->name()->value() == "right" || pdef->name()->value() == "b")
+ GetDefText(b, pdef, defname);
+
+ else if (pdef->name()->value() == "kern" || pdef->name()->value() == "k")
+ GetDefNumber(k, pdef, defname);
+ }
+ }
+
+ if (k < 100)
+ kern_tweak[a[0]][b[0]] = k;
+ }
+
+ else {
+ Print("WARNING: unknown object '%s' in '%s'\n",
+ def->name()->value().data(), defname);
+ }
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", defname);
+ term->print();
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+
+}
+
+// +--------------------------------------------------------------------+
+
+static const int pipe_width = 16;
+static const int char_width = 16;
+static const int char_height = 16;
+static const int row_items = 16;
+static const int row_width = row_items * char_width;
+static const int row_size = char_height * row_width;
+
+int
+Font::GlyphOffset(BYTE c) const
+{
+ if (flags & FONT_ALL_CAPS)
+ if (islower(c))
+ c = toupper(c);
+
+ return (c/row_items * row_size * scale * scale +
+ c%row_items * char_width * scale);
+}
+
+int
+Font::GlyphLocationX(BYTE c) const
+{
+ if (flags & FONT_ALL_CAPS)
+ if (islower(c))
+ c = toupper(c);
+
+ return c%row_items * char_width;
+}
+
+int
+Font::GlyphLocationY(BYTE c) const
+{
+ if (flags & FONT_ALL_CAPS)
+ if (islower(c))
+ c = toupper(c);
+
+ return c/row_items * char_height;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Font::CalcWidth(BYTE c) const
+{
+ if (c >= PIPE_NBSP && c <= ARROW_RIGHT)
+ return pipe_width;
+
+ if (c >= 128 || !image)
+ return 0;
+
+ // all digits should be same size:
+ if (isdigit(c))
+ c = '0';
+
+ int result = 0;
+ int w = 16 * scale;
+ int h = 16 * scale;
+
+ BYTE* src = image + GlyphOffset(c);
+
+ for (int y = 0; y < h; y++) {
+ BYTE* pleft = src;
+
+ for (int x = 0; x < w; x++) {
+ if (*pleft++ > 0 && x > result)
+ result = x;
+ }
+
+ src += imagewidth;
+ }
+
+ return result + 2;
+}
+
+// +--------------------------------------------------------------------+
+
+struct FontKernData
+{
+ double l[32];
+ double r[32];
+};
+
+void
+Font::FindEdges(BYTE c, double* l, double* r)
+{
+ if (!image)
+ return;
+
+ int w = glyph[c].width;
+ int h = height;
+
+ if (h > 32)
+ h = 32;
+
+ BYTE* src = image + GlyphOffset(c);
+
+ for (int y = 0; y < h; y++) {
+ BYTE* pleft = src;
+ BYTE* pright = src+w-1;
+
+ *l = -1;
+ *r = -1;
+
+ for (int x = 0; x < w; x++) {
+ if (*l == -1 && *pleft != 0)
+ *l = x + 1 - (double) *pleft/255.0;
+ if (*r == -1 && *pright != 0)
+ *r = x + 1 - (double) *pright/255.0;
+
+ pleft++;
+ pright--;
+ }
+
+ src += imagewidth;
+ l++;
+ r++;
+ }
+}
+
+static bool nokern(char c)
+{
+ if (c <= Font::ARROW_RIGHT || c >= 128)
+ return true;
+
+ const char* nokernchars = "0123456789+=<>-.,:;?'\"";
+
+ if (strchr(nokernchars, c))
+ return true;
+
+ return false;
+}
+
+void
+Font::AutoKern()
+{
+ FontKernData* data = new(__FILE__,__LINE__) FontKernData[256];
+
+ if (!data)
+ return;
+
+ int h = height;
+ if (h > 32) h = 32;
+
+ int i, j;
+
+ // first, compute row edges for each glyph:
+
+ for (i = 0; i < 256; i++) {
+ ZeroMemory(&data[i], sizeof(FontKernData));
+
+ char c = i;
+
+ if ((flags & FONT_ALL_CAPS) && islower(c))
+ c = toupper(c);
+
+ if (glyph[(BYTE) c].width > 0) {
+ FindEdges((BYTE) c, data[i].l, data[i].r);
+ }
+ }
+
+ // then, compute the appropriate kern for each pair.
+ // use a desired average distance of one pixel,
+ // with a desired minimum distance of more than half a pixel:
+
+ double desired_avg = 2.5 + expansion;
+ double desired_min = 1;
+
+ for (i = 0; i < 256; i++) {
+ for (j = 0; j < 256; j++) {
+ // no kerning between digits or dashes:
+ if (nokern(i) || nokern(j)) {
+ kern[i][j] = (char) 0;
+ }
+
+ else {
+ double delta = 0;
+ double avg = 0;
+ double min = 2500;
+ int n = 0;
+
+ for (int y = 0; y < h; y++) {
+ if (data[i].r[y] >= 0 && data[j].l[y] >= 0) {
+ delta = data[i].r[y] + data[j].l[y];
+ avg += delta;
+ if (delta < min)
+ min = delta;
+
+ n++;
+ }
+ }
+
+ if (n > 0) {
+ avg /= n;
+
+ delta = desired_avg - avg;
+
+ if (delta < desired_min - min) {
+ delta = ceil(desired_min - min);
+
+ if (i == 'T' && islower(j) && !(flags & FONT_ALL_CAPS))
+ delta += 1;
+ }
+ }
+ else {
+ delta = 0;
+ }
+
+ kern[i][j] = (char) delta;
+ }
+ }
+ }
+
+ delete [] data;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Font::CharWidth(char c) const
+{
+ if (flags & FONT_ALL_CAPS)
+ if (islower(c))
+ c = toupper(c);
+
+ int result = 0;
+
+ if (c >= PIPE_NBSP && c <= ARROW_RIGHT)
+ result = pipe_width;
+
+ else if (c < 0 || isspace(c))
+ result = spacewidth;
+
+ else
+ result = glyph[c].width + interspace;
+
+ return result;
+}
+
+int
+Font::SpaceWidth() const
+{
+ return spacewidth;
+}
+
+int
+Font::KernWidth(char a, char b) const
+{
+ if (flags & FONT_ALL_CAPS) {
+ if (islower(a)) a = toupper(a);
+ if (islower(b)) b = toupper(b);
+ }
+
+ return kern[a][b];
+}
+
+void
+Font::SetKern(char a, char b, int k)
+{
+ if (k < -100 || k > 100)
+ return;
+
+ if (flags & FONT_ALL_CAPS) {
+ if (islower(a)) a = toupper(a);
+ if (islower(b)) b = toupper(b);
+ }
+
+ kern[a][b] = (char) k;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Font::StringWidth(const char* str, int len) const
+{
+ int result = 0;
+
+ if (!str)
+ return result;
+
+ if (!len)
+ len = strlen(str);
+
+ const char* c = str;
+ for (int i = 0; i < len; i++) {
+ if (isspace(*c) && (*c < PIPE_NBSP || *c > ARROW_RIGHT))
+ result += spacewidth;
+ else {
+ int cc = *c;
+ if (flags & FONT_ALL_CAPS)
+ if (islower(cc))
+ cc = toupper(cc);
+
+ int k = 0;
+ if (i < len-1)
+ k = kern[cc][str[i+1]];
+
+ result += glyph[cc].width + interspace + k;
+ }
+ c++;
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Font::DrawText(const char* text, int count, Rect& text_rect, DWORD flags, Bitmap* tgt)
+{
+ Rect clip_rect = text_rect;
+
+ if (clip_rect.w < 1 || clip_rect.h < 1)
+ return;
+
+ tgt_bitmap = tgt;
+
+ if (text && text[0]) {
+ if (count < 1)
+ count = strlen(text);
+
+ // single line:
+ if (flags & DT_SINGLELINE) {
+ DrawTextSingle(text, count, text_rect, clip_rect, flags);
+ }
+
+ // multi-line with word wrap:
+ else if (flags & DT_WORDBREAK) {
+ DrawTextWrap(text, count, text_rect, clip_rect, flags);
+ }
+
+ // multi-line with clip:
+ else {
+ DrawTextMulti(text, count, text_rect, clip_rect, flags);
+ }
+ }
+ else {
+ caret_x = text_rect.x + 2;
+ caret_y = text_rect.y + 2;
+ }
+
+ // if calc only, update the rectangle:
+ if (flags & DT_CALCRECT) {
+ text_rect.h = clip_rect.h;
+ text_rect.w = clip_rect.w;
+ }
+
+ // otherwise, draw caret if requested:
+ else if (caret_index >= 0 && caret_y >= text_rect.y && caret_y <= text_rect.y + text_rect.h) {//caret_y + height < text_rect.y + text_rect.h) {
+ Video* video = Video::GetInstance();
+
+ if (video && (GetRealTime() / 500) & 1) {
+ float v[4];
+ v[0] = (float) (caret_x + 1);
+ v[1] = (float) (caret_y);
+ v[2] = (float) (caret_x + 1);
+ v[3] = (float) (caret_y + height);
+
+ video->DrawScreenLines(1, v, color, blend);
+ }
+
+ caret_index = -1;
+ }
+
+ tgt_bitmap = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+static int find_next_word_start(const char* text, int index)
+{
+ // step through intra-word space:
+ while (text[index] && isspace(text[index]) && text[index] != '\n')
+ index++;
+
+ return index;
+}
+
+static int find_next_word_end(const char* text, int index)
+{
+ if (index < 0)
+ return index;
+
+ // check for leading newline:
+ if (text[index] == '\n')
+ return index;
+
+ // step through intra-word space:
+ while (text[index] && isspace(text[index]))
+ index++;
+
+ // step through word:
+ while (text[index] && !isspace(text[index]))
+ index++;
+
+ return index-1;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Font::DrawTextSingle(const char* text, int count, const Rect& text_rect, Rect& clip_rect, DWORD flags)
+{
+ // parse the format flags:
+ bool nodraw = (flags & DT_CALCRECT) ?true:false;
+
+ int align = DT_LEFT;
+ if (flags & DT_RIGHT)
+ align = DT_RIGHT;
+ else if (flags & DT_CENTER)
+ align = DT_CENTER;
+
+ int max_width = 0;
+
+ int valign = DT_TOP;
+ if (flags & DT_BOTTOM) valign = DT_BOTTOM;
+ else if (flags & DT_VCENTER) valign = DT_VCENTER;
+
+ int xoffset = 0;
+ int yoffset = 0;
+
+ int length = StringWidth(text, count);
+ if (length < text_rect.w) {
+ switch (align) {
+ default:
+ case DT_LEFT: break;
+ case DT_RIGHT: xoffset = text_rect.w - length; break;
+ case DT_CENTER: xoffset = (text_rect.w - length)/2; break;
+ }
+ }
+
+ if (Height() < text_rect.h) {
+ switch (valign) {
+ default:
+ case DT_TOP: break;
+ case DT_BOTTOM: yoffset = text_rect.h - Height(); break;
+ case DT_VCENTER: yoffset = (text_rect.h - Height())/2; break;
+ }
+ }
+
+ max_width = length;
+
+ // if calc only, update the rectangle:
+ if (nodraw) {
+ clip_rect.h = Height();
+ clip_rect.w = max_width;
+ }
+
+ // otherwise, draw the string now:
+ else {
+ int x1 = text_rect.x + xoffset;
+ int y1 = text_rect.y + yoffset;
+
+ DrawString(text, count, x1, y1, text_rect);
+ }
+
+ if (caret_index >= 0 && caret_index <= count) {
+ caret_x = text_rect.x + xoffset;
+ caret_y = text_rect.y + yoffset;
+
+ if (caret_index > 0)
+ caret_x += StringWidth(text, caret_index);
+ }
+
+ else {
+ caret_x = text_rect.x + 0;
+ caret_y = text_rect.y + 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Font::DrawTextWrap(const char* text, int count, const Rect& text_rect, Rect& clip_rect, DWORD flags)
+{
+ // parse the format flags:
+ bool nodraw = (flags & DT_CALCRECT) ?true:false;
+
+ int align = DT_LEFT;
+ if (flags & DT_RIGHT)
+ align = DT_RIGHT;
+ else if (flags & DT_CENTER)
+ align = DT_CENTER;
+
+ int nlines = 0;
+ int max_width = 0;
+
+ int line_start = 0;
+ int line_count = 0;
+ int count_remaining = count;
+ int curr_word_end = -1;
+ int next_word_end = 0;
+ int eol_index = 0;
+
+ int xoffset = 0;
+ int yoffset = 0;
+
+ caret_x = -1;
+ caret_y = -1;
+
+ // repeat for each line of text:
+ while (count_remaining > 0) {
+ int length = 0;
+
+ // find the end of the last whole word that fits on the line:
+ for (;;) {
+ next_word_end = find_next_word_end(text, curr_word_end+1);
+
+ if (next_word_end < 0 || next_word_end == curr_word_end)
+ break;
+
+ if (text[next_word_end] == '\n') {
+ eol_index = curr_word_end = next_word_end;
+ break;
+ }
+
+ int word_len = next_word_end - line_start + 1;
+
+ length = StringWidth(text+line_start, word_len);
+
+ if (length < text_rect.w) {
+ curr_word_end = next_word_end;
+
+ // check for a newline in the next block of white space:
+ eol_index = 0;
+ const char* eol = &text[curr_word_end+1];
+ while (*eol && isspace(*eol) && *eol != '\n')
+ eol++;
+
+ if (*eol == '\n') {
+ eol_index = eol - text;
+ break;
+ }
+ }
+ else
+ break;
+ }
+
+ line_count = curr_word_end - line_start + 1;
+
+ if (line_count > 0) {
+ length = StringWidth(text+line_start, line_count);
+ }
+
+ // there was a single word longer than the entire line:
+ else {
+ line_count = next_word_end - line_start + 1;
+ length = StringWidth(text+line_start, line_count);
+ curr_word_end = next_word_end;
+ }
+
+ xoffset = 0;
+ if (length < text_rect.w) {
+ switch (align) {
+ default:
+ case DT_LEFT: break;
+ case DT_RIGHT: xoffset = text_rect.w - length; break;
+ case DT_CENTER: xoffset = (text_rect.w - length)/2; break;
+ }
+ }
+
+ if (length > max_width) max_width = length;
+
+ if (eol_index > 0)
+ curr_word_end = eol_index;
+
+ int next_line_start = find_next_word_start(text, curr_word_end+1);
+
+ if (length > 0 && !nodraw) {
+ int x1 = text_rect.x + xoffset;
+ int y1 = text_rect.y + yoffset;
+
+ DrawString(text+line_start, line_count, x1, y1, text_rect);
+
+ if (caret_index == line_start) {
+ caret_x = x1 - 2;
+ caret_y = y1;
+ }
+ else if (caret_index > line_start && caret_index < next_line_start) {
+ caret_x = text_rect.x + xoffset + StringWidth(text+line_start, caret_index-line_start) - 2;
+ caret_y = text_rect.y + yoffset;
+ }
+ else if (caret_index == count) {
+ if (text[count-1] == '\n') {
+ caret_x = x1 - 2;
+ caret_y = y1 + height;
+ }
+ else {
+ caret_x = text_rect.x + xoffset + StringWidth(text+line_start, caret_index-line_start) - 2;
+ caret_y = text_rect.y + yoffset;
+ }
+ }
+ }
+
+ nlines++;
+ yoffset += Height();
+ if (eol_index > 0)
+ curr_word_end = eol_index;
+ line_start = find_next_word_start(text, curr_word_end+1);
+ count_remaining = count - line_start;
+ }
+
+ // if calc only, update the rectangle:
+ if (nodraw) {
+ clip_rect.h = nlines * Height();
+ clip_rect.w = max_width;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Font::DrawTextMulti(const char* text, int count, const Rect& text_rect, Rect& clip_rect, DWORD flags)
+{
+ // parse the format flags:
+ bool nodraw = (flags & DT_CALCRECT) ?true:false;
+
+ int align = DT_LEFT;
+ if (flags & DT_RIGHT)
+ align = DT_RIGHT;
+ else if (flags & DT_CENTER)
+ align = DT_CENTER;
+
+ int nlines = 1;
+ int max_width = 0;
+ int line_start = 0;
+ int count_remaining = count;
+
+ int xoffset = 0;
+ int yoffset = 0;
+ nlines = 0;
+
+ // repeat for each line of text:
+ while (count_remaining > 0) {
+ int length = 0;
+ int line_count = 0;
+
+ // find the end of line:
+ while (line_count < count_remaining) {
+ char c = text[line_start+line_count];
+ if (!c || c == '\n')
+ break;
+
+ line_count++;
+ }
+
+ if (line_count > 0) {
+ length = StringWidth(text+line_start, line_count);
+ }
+
+ xoffset = 0;
+ if (length < text_rect.w) {
+ switch (align) {
+ default:
+ case DT_LEFT: break;
+ case DT_RIGHT: xoffset = text_rect.w - length; break;
+ case DT_CENTER: xoffset = (text_rect.w - length)/2; break;
+ }
+ }
+
+ if (length > max_width) max_width = length;
+
+ if (length && !nodraw) {
+ int x1 = text_rect.x + xoffset;
+ int y1 = text_rect.y + yoffset;
+
+ DrawString(text+line_start, line_count, x1, y1, text_rect);
+ }
+
+ nlines++;
+ yoffset += Height();
+
+ if (line_start+line_count+1 < count) {
+ line_start = find_next_word_start(text, line_start+line_count+1);
+ count_remaining = count - line_start;
+ }
+ else {
+ count_remaining = 0;
+ }
+ }
+
+ // if calc only, update the rectangle:
+ if (nodraw) {
+ clip_rect.h = nlines * Height();
+ clip_rect.w = max_width;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Font::DrawString(const char* str, int len, int x1, int y1, const Rect& clip, Bitmap* tgt)
+{
+ Video* video = Video::GetInstance();
+ int count = 0;
+ int maxw = clip.w;
+ int maxh = clip.h;
+
+ if (len < 1 || !video)
+ return count;
+
+ // vertical clip
+ if ((y1 < clip.y) || (y1 > clip.y + clip.h))
+ return count;
+
+ // RENDER TO BITMAP
+
+ if (!tgt)
+ tgt = tgt_bitmap;
+
+ if (tgt) {
+ for (int i = 0; i < len; i++) {
+ char c = str[i];
+
+ if ((flags & FONT_ALL_CAPS) && islower(c))
+ c = toupper(c);
+
+ int cw = glyph[c].width + interspace;
+ int ch = height;
+ int k = 0;
+
+ if (i < len-1)
+ k = kern[c][str[i+1]];
+
+ // horizontal clip:
+ if (x1 < clip.x) {
+ if (isspace(c) && (c < PIPE_NBSP || c > ARROW_RIGHT)) {
+ x1 += spacewidth;
+ maxw -= spacewidth;
+ }
+ else {
+ x1 += cw+k;
+ maxw -= cw+k;
+ }
+ }
+ else if (x1+cw > clip.x+clip.w) {
+ return count;
+ }
+ else {
+ if (isspace(c) && (c < PIPE_NBSP || c > ARROW_RIGHT)) {
+ x1 += spacewidth;
+ maxw -= spacewidth;
+ }
+ else {
+ int sx = GlyphLocationX(c);
+ int sy = GlyphLocationY(c);
+
+ Color* srcpix = bitmap.HiPixels();
+ Color* dstpix = tgt->HiPixels();
+ if (srcpix && dstpix) {
+ int spitch = bitmap.Width();
+ int dpitch = tgt->Width();
+
+ Color* dst = dstpix + (y1*dpitch) + x1;
+ Color* src = srcpix + (sy*spitch) + sx;
+
+ for (int i = 0; i < ch; i++) {
+ Color* ps = src;
+ Color* pd = dst;
+
+ for (int n = 0; n < cw; n++) {
+ DWORD alpha = ps->Alpha();
+ if (alpha) {
+ *pd = color.dim(alpha / 240.0);
+ }
+ ps++;
+ pd++;
+ }
+
+ dst += dpitch;
+ src += spitch;
+ }
+ }
+ else {
+ // this probably won't work...
+ tgt->BitBlt(x1, y1, bitmap, sx, sy, cw, ch, true);
+ }
+
+ x1 += cw + k;
+ maxw -= cw + k;
+ }
+
+ count++;
+ }
+ }
+ return count;
+ }
+
+ // RENDER TO VIDEO
+
+ // allocate verts, if necessary
+ int nverts = 4*len;
+ if (!vset) {
+ vset = new(__FILE__,__LINE__) VertexSet(nverts);
+
+ if (!vset)
+ return false;
+
+ vset->space = VertexSet::SCREEN_SPACE;
+
+ for (int v = 0; v < vset->nverts; v++) {
+ vset->s_loc[v].z = 0.0f;
+ vset->rw[v] = 1.0f;
+ }
+ }
+ else if (vset->nverts < nverts) {
+ vset->Resize(nverts);
+
+ for (int v = 0; v < vset->nverts; v++) {
+ vset->s_loc[v].z = 0.0f;
+ vset->rw[v] = 1.0f;
+ }
+ }
+
+ if (vset->nverts < nverts)
+ return count;
+
+ if (alpha < 1)
+ color.SetAlpha((BYTE) (alpha * 255.0f));
+ else
+ color.SetAlpha(255);
+
+ for (int i = 0; i < len; i++) {
+ char c = str[i];
+
+ if ((flags & FONT_ALL_CAPS) && islower(c))
+ c = toupper(c);
+
+ int cw = glyph[c].width + interspace;
+ int k = 0;
+
+ if (i < len-1)
+ k = kern[c][str[i+1]];
+
+ // horizontal clip:
+ if (x1 < clip.x) {
+ if (isspace(c) && (c < PIPE_NBSP || c > ARROW_RIGHT)) {
+ x1 += spacewidth;
+ maxw -= spacewidth;
+ }
+ else {
+ x1 += cw+k;
+ maxw -= cw+k;
+ }
+ }
+ else if (x1+cw > clip.x+clip.w) {
+ break;
+ }
+ else {
+ if (isspace(c) && (c < PIPE_NBSP || c > ARROW_RIGHT)) {
+ x1 += spacewidth;
+ maxw -= spacewidth;
+ }
+ else {
+ // create four verts for this character:
+ int v = count*4;
+ double char_x = GlyphLocationX(c);
+ double char_y = GlyphLocationY(c);
+ double char_w = glyph[c].width;
+ double char_h = height;
+
+ if (y1 + char_h > clip.y + clip.h) {
+ char_h = clip.y + clip.h - y1;
+ }
+
+ vset->s_loc[v+0].x = (float) (x1 - 0.5);
+ vset->s_loc[v+0].y = (float) (y1 - 0.5);
+ vset->tu[v+0] = (float) (char_x / 256);
+ vset->tv[v+0] = (float) (char_y / 256);
+ vset->diffuse[v+0] = color.Value();
+
+ vset->s_loc[v+1].x = (float) (x1 + char_w - 0.5);
+ vset->s_loc[v+1].y = (float) (y1 - 0.5);
+ vset->tu[v+1] = (float) (char_x / 256 + char_w / 256);
+ vset->tv[v+1] = (float) (char_y / 256);
+ vset->diffuse[v+1] = color.Value();
+
+ vset->s_loc[v+2].x = (float) (x1 + char_w - 0.5);
+ vset->s_loc[v+2].y = (float) (y1 + char_h - 0.5);
+ vset->tu[v+2] = (float) (char_x / 256 + char_w / 256);
+ vset->tv[v+2] = (float) (char_y / 256 + char_h / 256);
+ vset->diffuse[v+2] = color.Value();
+
+ vset->s_loc[v+3].x = (float) (x1 - 0.5);
+ vset->s_loc[v+3].y = (float) (y1 + char_h - 0.5);
+ vset->tu[v+3] = (float) (char_x / 256);
+ vset->tv[v+3] = (float) (char_y / 256 + char_h / 256);
+ vset->diffuse[v+3] = color.Value();
+
+ x1 += cw + k;
+ maxw -= cw + k;
+
+ count++;
+ }
+ }
+ }
+
+ if (count) {
+ // this small hack is an optimization to reduce the
+ // size of vertex buffer needed for font rendering:
+
+ int old_nverts = vset->nverts;
+ vset->nverts = 4 * count;
+
+ // create a larger poly array, if necessary:
+ if (count > npolys) {
+ if (polys)
+ delete [] polys;
+
+ npolys = count;
+ polys = new(__FILE__,__LINE__) Poly[npolys];
+ Poly* p = polys;
+ int index = 0;
+
+ for (int i = 0; i < npolys; i++) {
+ p->nverts = 4;
+ p->vertex_set = vset;
+ p->material = material;
+ p->verts[0] = index++;
+ p->verts[1] = index++;
+ p->verts[2] = index++;
+ p->verts[3] = index++;
+
+ p++;
+ }
+ }
+
+ video->DrawScreenPolys(count, polys, blend);
+
+ // remember to restore the proper size of the vertex set:
+ vset->nverts = old_nverts;
+ }
+
+ return count;
+}
+
diff --git a/nGenEx/Font.h b/nGenEx/Font.h
new file mode 100644
index 0000000..bea3c33
--- /dev/null
+++ b/nGenEx/Font.h
@@ -0,0 +1,143 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Font.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Font Resource class
+*/
+
+#ifndef Font_h
+#define Font_h
+
+#include "Types.h"
+#include "Bitmap.h"
+#include "Color.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+struct Poly;
+struct Material;
+struct VertexSet;
+class Video;
+
+// +--------------------------------------------------------------------+
+
+struct FontChar
+{
+ short offset;
+ short width;
+};
+
+// +--------------------------------------------------------------------+
+
+class Font
+{
+public:
+ static const char* TYPENAME() { return "Font"; }
+
+ enum FLAGS { FONT_FIXED_PITCH = 1,
+ FONT_ALL_CAPS = 2,
+ FONT_NO_KERN = 4
+ };
+
+ enum CHARS { PIPE_NBSP = 16,
+ PIPE_VERT = 17,
+ PIPE_LT = 18,
+ PIPE_TEE = 19,
+ PIPE_UL = 20,
+ PIPE_LL = 21,
+ PIPE_HORZ = 22,
+ PIPE_PLUS = 23,
+ PIPE_MINUS = 24,
+ ARROW_UP = 25,
+ ARROW_DOWN = 26,
+ ARROW_LEFT = 27,
+ ARROW_RIGHT = 28
+ };
+
+ // default constructor:
+ Font();
+ Font(const char* name);
+ ~Font();
+
+ bool Load(const char* name);
+
+ int CharWidth(char c) const;
+ int SpaceWidth() const;
+ int KernWidth(char left, char right) const;
+ int StringWidth(const char* str, int len=0) const;
+
+ void DrawText(const char* txt, int count, Rect& txt_rect, DWORD flags, Bitmap* tgt_bitmap=0);
+ int DrawString( const char* txt, int len, int x1, int y1, const Rect& clip, Bitmap* tgt_bitmap=0);
+
+ int Height() const { return height; }
+ int Baseline() const { return baseline; }
+ WORD GetFlags() const { return flags; }
+ void SetFlags(WORD s) { flags = s; }
+ Color GetColor() const { return color; }
+ void SetColor(const Color& c) { color = c; }
+ double GetExpansion() const { return expansion; }
+ void SetExpansion(double e) { expansion = (float) e; }
+ double GetAlpha() const { return alpha; }
+ void SetAlpha(double a) { alpha = (float) a; }
+ int GetBlend() const { return blend; }
+ void SetBlend(int b) { blend = b; }
+
+ void SetKern(char left, char right, int k=0);
+
+ int GetCaretIndex() const { return caret_index; }
+ void SetCaretIndex(int n) { caret_index = n; }
+
+private:
+ void AutoKern();
+ void FindEdges(BYTE c, double* l, double* r);
+ int CalcWidth(BYTE c) const;
+ int GlyphOffset(BYTE c) const;
+ int GlyphLocationX(BYTE c) const;
+ int GlyphLocationY(BYTE c) const;
+
+ void DrawTextSingle(const char* txt, int count, const Rect& txt_rect, Rect& clip_rect, DWORD flags);
+ void DrawTextWrap(const char* txt, int count, const Rect& txt_rect, Rect& clip_rect, DWORD flags);
+ void DrawTextMulti(const char* txt, int count, const Rect& txt_rect, Rect& clip_rect, DWORD flags);
+
+ void LoadDef(char* defname, char* imgname);
+
+ char name[64];
+ WORD flags;
+ BYTE height;
+ BYTE baseline;
+ BYTE interspace;
+ BYTE spacewidth;
+ float expansion;
+ float alpha;
+ int blend;
+ int scale;
+
+ int caret_index;
+ int caret_x;
+ int caret_y;
+
+ int imagewidth;
+ BYTE* image;
+ Bitmap bitmap;
+ Bitmap* tgt_bitmap;
+ Material* material;
+ VertexSet* vset;
+ Poly* polys;
+ int npolys;
+
+ FontChar glyph[256];
+ Color color;
+
+ char kern[256][256];
+};
+
+#endif Font_h
+
diff --git a/nGenEx/FontMgr.cpp b/nGenEx/FontMgr.cpp
new file mode 100644
index 0000000..e37b2b9
--- /dev/null
+++ b/nGenEx/FontMgr.cpp
@@ -0,0 +1,58 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FontMgr.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Font Resource Manager class implementation
+*/
+
+#include "MemDebug.h"
+#include "FontMgr.h"
+
+// +--------------------------------------------------------------------+
+
+List<FontItem> FontMgr::fonts;
+
+// +--------------------------------------------------------------------+
+
+void
+FontMgr::Close()
+{
+ fonts.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FontMgr::Register(const char* name, Font* font)
+{
+ FontItem* item = new(__FILE__,__LINE__) FontItem;
+
+ if (item) {
+ item->name = name;
+ item->size = 0;
+ item->font = font;
+
+ fonts.append(item);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Font*
+FontMgr::Find(const char* name)
+{
+ ListIter<FontItem> item = fonts;
+ while (++item) {
+ if (item->name == name)
+ return item->font;
+ }
+
+ return 0;
+}
diff --git a/nGenEx/FontMgr.h b/nGenEx/FontMgr.h
new file mode 100644
index 0000000..5c99118
--- /dev/null
+++ b/nGenEx/FontMgr.h
@@ -0,0 +1,50 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FontMgr.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Font Resource Manager class
+*/
+
+#ifndef FontMgr_h
+#define FontMgr_h
+
+#include "Types.h"
+#include "Color.h"
+#include "List.h"
+#include "Text.h"
+
+class Font;
+
+// +--------------------------------------------------------------------+
+
+struct FontItem
+{
+ static const char* TYPENAME() { return "FontItem"; }
+
+ Text name;
+ int size;
+ Font* font;
+};
+
+class FontMgr
+{
+public:
+ static const char* TYPENAME() { return "FontMgr"; }
+
+ static void Close();
+ static void Register(const char* name, Font* font);
+ static Font* Find(const char* name);
+
+private:
+ static List<FontItem> fonts;
+};
+
+#endif FontMgr_h
+
diff --git a/nGenEx/FormDef.cpp b/nGenEx/FormDef.cpp
new file mode 100644
index 0000000..9cef31f
--- /dev/null
+++ b/nGenEx/FormDef.cpp
@@ -0,0 +1,1269 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FormDef.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Form and Control Definition Resources
+*/
+
+#include "MemDebug.h"
+#include "FormDef.h"
+#include "ParseUtil.h"
+#include "DataLoader.h"
+#include "Bitmap.h"
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+ColumnDef::ColumnDef()
+ : width(10), align(0), sort(0), color(Color::White), use_color(false)
+{ }
+
+ColumnDef::ColumnDef(const char* t, int w, int a, int s)
+ : title(t), width(w), align(a), sort(s),
+ color(Color::White), use_color(false)
+{ }
+
+// +----------------------------------------------------------------------+
+
+WinDef::WinDef(DWORD a_id, DWORD a_type, const char* a_text, DWORD a_style)
+ : id(a_id), pid(0), type(a_type), text(a_text), style(a_style)
+{
+ rect = Rect(0,0,0,0);
+ text_align = 0;
+ single_line = false;
+ enabled = true;
+ transparent = false;
+ hide_partial = true;
+ back_color = Color::Gray;
+ base_color = Color::Gray;
+ fore_color = Color::Black;
+ fixed_width = 0;
+ fixed_height = 0;
+}
+
+void WinDef::SetID(DWORD i) { id = i; }
+void WinDef::SetParentID(DWORD i) { pid = i; }
+void WinDef::SetType(DWORD t) { type = t; }
+void WinDef::SetRect(const Rect& r) { rect = r; }
+void WinDef::SetEnabled(bool e) { enabled = e; }
+void WinDef::SetStyle(DWORD s) { style = s; }
+void WinDef::SetFont(const char* t) { font = t; }
+void WinDef::SetText(const char* t) { text = t; }
+void WinDef::SetAltText(const char* t) { alt_text = t; }
+void WinDef::SetTexture(const char* t) { texture = t; }
+void WinDef::SetBackColor(Color c) { back_color = c; }
+void WinDef::SetBaseColor(Color c) { base_color = c; }
+void WinDef::SetForeColor(Color c) { fore_color = c; }
+void WinDef::SetTextAlign(DWORD a) { text_align = a; }
+void WinDef::SetSingleLine(bool a) { single_line = a; }
+void WinDef::SetTransparent(bool t) { transparent = t; }
+void WinDef::SetHidePartial(bool a) { hide_partial = a; }
+
+void WinDef::SetMargins(const Insets& m) { margins = m; }
+void WinDef::SetTextInsets(const Insets& t) { text_insets = t; }
+void WinDef::SetCellInsets(const Insets& c) { cell_insets = c; }
+void WinDef::SetCells(const Rect& r) { cells = r; }
+
+// +----------------------------------------------------------------------+
+
+#define CTRL_DEF_ANIMATED 0x0001
+#define CTRL_DEF_BORDER 0x0002
+#define CTRL_DEF_DROP_SHADOW 0x0004
+#define CTRL_DEF_INDENT 0x0008
+#define CTRL_DEF_INVERT_LABEL 0x0010
+#define CTRL_DEF_GLOW 0x0020
+#define CTRL_DEF_SIMPLE 0x0040
+#define CTRL_DEF_STICKY 0x0080
+
+CtrlDef::CtrlDef(DWORD a_id, DWORD a_type, const char* a_text, DWORD a_style)
+ : WinDef(a_id, a_type, a_text, a_style)
+{
+ ctrl_flags = CTRL_DEF_ANIMATED | CTRL_DEF_BORDER | CTRL_DEF_INDENT;
+ bevel_width = 5;
+ picture_loc = 1; // North
+ picture_type = Bitmap::BMP_SOLID;
+
+ active = false;
+ show_headings = false;
+
+ leading = 0;
+ line_height = 0;
+ multiselect = 0;
+ dragdrop = 0;
+ orientation = 0;
+ scroll_bar = 1;
+ num_leds = 1;
+
+ smooth_scroll = false;
+
+ item_style = 0;
+ selected_style = 0;
+ pass_char = 0;
+
+ items.destroy();
+
+ ZeroMemory(tabs, sizeof(tabs));
+ ntabs = 0;
+}
+
+CtrlDef::~CtrlDef()
+{
+ items.destroy();
+ columns.destroy();
+}
+
+CtrlDef& CtrlDef::operator=(const CtrlDef& ctrl)
+{
+ WinDef::operator=(ctrl);
+
+ ctrl_flags = ctrl.ctrl_flags;
+ bevel_width = ctrl.bevel_width;
+ picture_loc = ctrl.picture_loc;
+ picture_type = ctrl.picture_type;
+
+ active = ctrl.active;
+ show_headings = ctrl.show_headings;
+
+ leading = ctrl.leading;
+ line_height = ctrl.line_height;
+ multiselect = ctrl.multiselect;
+ dragdrop = ctrl.dragdrop;
+ orientation = ctrl.orientation;
+ scroll_bar = ctrl.scroll_bar;
+ pass_char = ctrl.pass_char;
+ active_color = ctrl.active_color;
+ border_color = ctrl.border_color;
+
+ smooth_scroll = ctrl.smooth_scroll;
+
+ item_style = ctrl.item_style;
+ selected_style = ctrl.selected_style;
+ pass_char = ctrl.pass_char;
+
+ standard_image = ctrl.standard_image;
+ activated_image = ctrl.activated_image;
+ transition_image = ctrl.transition_image;
+
+ return *this;
+}
+
+int CtrlDef::GetOrientation() const
+{
+ return orientation;
+}
+
+void CtrlDef::SetOrientation(int o)
+{
+ orientation = o;
+}
+
+bool CtrlDef::GetActive() const
+{
+ return active;
+}
+
+void CtrlDef::SetActive(bool c)
+{
+ active = c;
+}
+
+Color CtrlDef::GetActiveColor() const
+{
+ return active_color;
+}
+
+void CtrlDef::SetActiveColor(Color c)
+{
+ active_color = c;
+}
+
+bool CtrlDef::GetAnimated() const
+{
+ return ctrl_flags & CTRL_DEF_ANIMATED;
+}
+
+void CtrlDef::SetAnimated(bool bNewValue)
+{
+ if (bNewValue)
+ ctrl_flags |= CTRL_DEF_ANIMATED;
+ else
+ ctrl_flags &= ~CTRL_DEF_ANIMATED;
+}
+
+short CtrlDef::GetBevelWidth() const
+{
+ return bevel_width;
+}
+
+void CtrlDef::SetBevelWidth(short nNewValue)
+{
+ bevel_width = nNewValue;
+}
+
+bool CtrlDef::GetBorder() const
+{
+ return (ctrl_flags & CTRL_DEF_BORDER)?true:false;
+}
+
+void CtrlDef::SetBorder(bool bNewValue)
+{
+ if (bNewValue)
+ ctrl_flags |= CTRL_DEF_BORDER;
+ else
+ ctrl_flags &= ~CTRL_DEF_BORDER;
+}
+
+Color CtrlDef::GetBorderColor() const
+{
+ return border_color;
+}
+
+void CtrlDef::SetBorderColor(Color c)
+{
+ border_color = c;
+}
+
+bool CtrlDef::GetDropShadow() const
+{
+ return (ctrl_flags & CTRL_DEF_DROP_SHADOW)?true:false;
+}
+
+void CtrlDef::SetDropShadow(bool bNewValue)
+{
+ if (bNewValue)
+ ctrl_flags |= CTRL_DEF_DROP_SHADOW;
+ else
+ ctrl_flags &= ~CTRL_DEF_DROP_SHADOW;
+}
+
+bool CtrlDef::GetIndent() const
+{
+ return (ctrl_flags & CTRL_DEF_INDENT)?true:false;
+}
+
+void CtrlDef::SetIndent(bool bNewValue)
+{
+ if (bNewValue)
+ ctrl_flags |= CTRL_DEF_INDENT;
+ else
+ ctrl_flags &= ~CTRL_DEF_INDENT;
+}
+
+bool CtrlDef::GetInvertLabel() const
+{
+ return (ctrl_flags & CTRL_DEF_INVERT_LABEL)?true:false;
+}
+
+void CtrlDef::SetInvertLabel(bool bNewValue)
+{
+ if (bNewValue)
+ ctrl_flags |= CTRL_DEF_INVERT_LABEL;
+ else
+ ctrl_flags &= ~CTRL_DEF_INVERT_LABEL;
+}
+
+Text CtrlDef::GetPicture() const
+{
+ return picture;
+}
+
+void CtrlDef::SetPicture(const Text& img_name)
+{
+ picture = img_name;
+}
+
+short CtrlDef::GetPictureLocation() const
+{
+ return picture_loc;
+}
+
+void CtrlDef::SetPictureLocation(short nNewValue)
+{
+ picture_loc = nNewValue;
+}
+
+short CtrlDef::GetPictureType() const
+{
+ return picture_type;
+}
+
+void CtrlDef::SetPictureType(short nNewValue)
+{
+ picture_type = nNewValue;
+}
+
+bool CtrlDef::GetSticky() const
+{
+ return (ctrl_flags & CTRL_DEF_STICKY)?true:false;
+}
+
+void CtrlDef::SetSticky(bool bNewValue)
+{
+ if (bNewValue)
+ ctrl_flags |= CTRL_DEF_STICKY;
+ else
+ ctrl_flags &= ~CTRL_DEF_STICKY;
+}
+
+int CtrlDef::GetNumLeds() const
+{
+ return num_leds;
+}
+
+void CtrlDef::SetNumLeds(int n)
+{
+ if (n > 0)
+ num_leds = n;
+}
+
+int CtrlDef::NumItems() const
+{
+ return items.size();
+}
+
+Text CtrlDef::GetItem(int i) const
+{
+ Text result;
+
+ if (i >= 0 && i < items.size())
+ result = *(items[i]);
+
+ return result;
+}
+
+void CtrlDef::AddItem(const char* t)
+{
+ items.append(new(__FILE__,__LINE__) Text(t));
+}
+
+int CtrlDef::NumColumns() const
+{
+ return columns.size();
+}
+
+ColumnDef* CtrlDef::GetColumn(int i) const
+{
+ ColumnDef* result = 0;
+
+ if (i >= 0 && i < columns.size())
+ result = columns[i];
+
+ return result;
+}
+
+void CtrlDef::AddColumn(const char* t, int w, int a, int s)
+{
+ columns.append(new(__FILE__,__LINE__) ColumnDef(t,w,a,s));
+}
+
+int CtrlDef::NumTabs() const
+{
+ return ntabs;
+}
+
+int CtrlDef::GetTab(int i) const
+{
+ if (i >= 0 && i < ntabs)
+ return tabs[i];
+ return 0;
+}
+
+void CtrlDef::SetTab(int i, int t)
+{
+ if (i >= 0 && i < 10) {
+ tabs[i] = t;
+ if (i >= ntabs)
+ ntabs = i+1;
+ }
+}
+
+void CtrlDef::AddTab(int i)
+{
+ if (ntabs < 10)
+ tabs[ntabs++] = i;
+}
+
+bool CtrlDef::GetShowHeadings() const
+{
+ return show_headings;
+}
+
+void CtrlDef::SetShowHeadings(bool bNewValue)
+{
+ show_headings = bNewValue;
+}
+
+int CtrlDef::GetLeading() const
+{
+ return leading;
+}
+
+void CtrlDef::SetLeading(int nNewValue)
+{
+ leading = nNewValue;
+}
+
+int CtrlDef::GetLineHeight() const
+{
+ return line_height;
+}
+
+void CtrlDef::SetLineHeight(int nNewValue)
+{
+ line_height = nNewValue;
+}
+
+int CtrlDef::GetMultiSelect() const
+{
+ return multiselect;
+}
+
+void CtrlDef::SetMultiSelect(int nNewValue)
+{
+ multiselect = nNewValue;
+}
+
+int CtrlDef::GetDragDrop() const
+{
+ return dragdrop;
+}
+
+void CtrlDef::SetDragDrop(int nNewValue)
+{
+ dragdrop = nNewValue;
+}
+
+int CtrlDef::GetScrollBarVisible() const
+{
+ return scroll_bar;
+}
+
+void CtrlDef::SetScrollBarVisible(int nNewValue)
+{
+ scroll_bar = nNewValue;
+}
+
+bool CtrlDef::GetSmoothScroll() const
+{
+ return smooth_scroll;
+}
+
+void CtrlDef::SetSmoothScroll(bool bNewValue)
+{
+ smooth_scroll = bNewValue;
+}
+
+short CtrlDef::GetItemStyle() const
+{
+ return item_style;
+}
+
+void CtrlDef::SetItemStyle(short nNewValue)
+{
+ item_style = nNewValue;
+}
+
+short CtrlDef::GetSelectedStyle() const
+{
+ return selected_style;
+}
+
+void CtrlDef::SetSelectedStyle(short nNewValue)
+{
+ selected_style = nNewValue;
+}
+
+char CtrlDef::GetPasswordChar() const
+{
+ return pass_char;
+}
+
+void CtrlDef::SetPasswordChar(char nNewValue)
+{
+ pass_char = nNewValue;
+}
+
+Text CtrlDef::GetStandardImage() const
+{
+ return standard_image;
+}
+
+void CtrlDef::SetStandardImage(const Text& img_name)
+{
+ standard_image = img_name;
+}
+
+Text CtrlDef::GetActivatedImage() const
+{
+ return activated_image;
+}
+
+void CtrlDef::SetActivatedImage(const Text& img_name)
+{
+ activated_image = img_name;
+}
+
+Text CtrlDef::GetTransitionImage() const
+{
+ return transition_image;
+}
+
+void CtrlDef::SetTransitionImage(const Text& img_name)
+{
+ transition_image = img_name;
+}
+
+
+// +----------------------------------------------------------------------+
+
+FormDef::FormDef(const char* a_text, DWORD a_style)
+ : WinDef(0, WIN_DEF_FORM, a_text, a_style)
+{
+}
+
+FormDef::~FormDef()
+{
+ controls.destroy();
+}
+
+void FormDef::AddCtrl(CtrlDef* def)
+{
+ if (def)
+ controls.append(def);
+}
+
+CtrlDef* FormDef::FindCtrl(BYTE ctrl_id)
+{
+ if (ctrl_id > 0) {
+ CtrlDef test(ctrl_id, 0);
+ return controls.find(&test);
+ }
+
+ return 0;
+}
+
+ListIter<CtrlDef>
+FormDef::GetControls() const
+{
+ // cast away const
+ FormDef* f = (FormDef*) this;
+ return f->controls;
+}
+
+// +----------------------------------------------------------------------+
+
+static char filename[64];
+static char path_name[64];
+
+void
+FormDef::Load(const char* fname)
+{
+ sprintf(filename, "%s.frm", fname);
+
+ Print("Loading Form '%s'\n", fname);
+
+ sprintf(path_name, "Screens/");
+
+ // Load Design File:
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath(path_name);
+
+ BYTE* block;
+ int blocklen = loader->LoadBuffer(filename, block, true);
+
+ if (!block || blocklen < 4)
+ return;
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'\n", filename);
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "FORM") {
+ Print("ERROR: invalid form file '%s'\n", filename);
+ return;
+ }
+ }
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "form") {
+
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: form structure missing in '%s'\n", filename);
+ }
+ else {
+ FormDef* form = this;
+ TermStruct* val = def->term()->isStruct();
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ char buf[256];
+
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "text" ||
+ pdef->name()->value() == "caption") {
+
+ GetDefText(buf, pdef, filename);
+ form->SetText(Game::GetText(buf));
+ }
+
+ else if (pdef->name()->value() == "id") {
+ DWORD id;
+ GetDefNumber(id, pdef, filename);
+ form->SetID(id);
+ }
+
+ else if (pdef->name()->value() == "pid") {
+ DWORD id;
+ GetDefNumber(id, pdef, filename);
+ form->SetParentID(id);
+ }
+
+ else if (pdef->name()->value() == "rect") {
+ Rect r;
+ GetDefRect(r, pdef, filename);
+ form->SetRect(r);
+ }
+
+ else if (pdef->name()->value() == "font") {
+ GetDefText(buf, pdef, filename);
+ form->SetFont(buf);
+ }
+
+ else if (pdef->name()->value() == "back_color") {
+ Color c;
+ GetDefColor(c, pdef, filename);
+ form->SetBackColor(c);
+ }
+
+ else if (pdef->name()->value() == "base_color") {
+ Color c;
+ GetDefColor(c, pdef, filename);
+ form->SetBaseColor(c);
+ }
+
+ else if (pdef->name()->value() == "fore_color") {
+ Color c;
+ GetDefColor(c, pdef, filename);
+ form->SetForeColor(c);
+ }
+
+ else if (pdef->name()->value() == "margins") {
+ GetDefInsets(form->margins, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "text_insets") {
+ GetDefInsets(form->text_insets, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "cell_insets") {
+ GetDefInsets(form->cell_insets, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "cells") {
+ GetDefRect(form->cells, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "texture") {
+ GetDefText(buf, pdef, filename);
+
+ if (*buf && !strchr(buf, '.'))
+ strcat(buf, ".pcx");
+
+ form->SetTexture(buf);
+ }
+
+ else if (pdef->name()->value() == "transparent") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ form->SetTransparent(b);
+ }
+
+ else if (pdef->name()->value() == "style") {
+ DWORD s;
+ GetDefNumber(s, pdef, filename);
+ form->SetStyle(s);
+ }
+
+ else if (pdef->name()->value() == "align" ||
+ pdef->name()->value() == "text_align") {
+ DWORD a = DT_LEFT;
+
+ if (GetDefText(buf, pdef, filename)) {
+ if (!stricmp(buf, "left"))
+ a = DT_LEFT;
+ else if (!stricmp(buf, "right"))
+ a = DT_RIGHT;
+ else if (!stricmp(buf, "center"))
+ a = DT_CENTER;
+ }
+
+ else {
+ GetDefNumber(a, pdef, filename);
+ }
+
+ form->SetTextAlign(a);
+ }
+
+ // layout constraints:
+
+ else if (pdef->name()->value() == "layout") {
+
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: layout structure missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+ ParseLayoutDef(&form->layout, val);
+ }
+ }
+
+ // controls:
+
+ else if (pdef->name()->value() == "defctrl") {
+
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: defctrl structure missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+ ParseCtrlDef(&form->defctrl, val);
+ }
+ }
+
+ else if (pdef->name()->value() == "ctrl") {
+
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: ctrl structure missing in '%s'\n", filename);
+ }
+ else {
+ CtrlDef* ctrl = new(__FILE__,__LINE__) CtrlDef;
+ TermStruct* val = pdef->term()->isStruct();
+
+ form->AddCtrl(ctrl);
+ *ctrl = form->defctrl; // copy default params
+
+ ParseCtrlDef(ctrl, val);
+ }
+ }
+
+ // end of controls.
+ }
+ } // end form params
+ } // end form struct
+ } // end form
+
+ else
+ Print("WARNING: unknown object '%s' in '%s'\n",
+ def->name()->value().data(), filename);
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ term->print();
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+ loader->SetDataPath(0);
+}
+
+void FormDef::ParseCtrlDef(CtrlDef* ctrl, TermStruct* val)
+{
+ Text buf;
+
+ ctrl->SetText("");
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "text" ||
+ pdef->name()->value() == "caption") {
+ GetDefText(buf, pdef, filename);
+ ctrl->SetText(Game::GetText(buf));
+ }
+
+ else if (pdef->name()->value() == "id") {
+ DWORD id;
+ GetDefNumber(id, pdef, filename);
+ ctrl->SetID(id);
+ }
+
+ else if (pdef->name()->value() == "pid") {
+ DWORD id;
+ GetDefNumber(id, pdef, filename);
+ ctrl->SetParentID(id);
+ }
+
+ else if (pdef->name()->value() == "alt") {
+ GetDefText(buf, pdef, filename);
+ ctrl->SetAltText(Game::GetText(buf));
+ }
+
+ else if (pdef->name()->value() == "type") {
+ DWORD type = WIN_DEF_LABEL;
+
+ GetDefText(buf, pdef, filename);
+ Text type_name(buf);
+
+ if (type_name == "button")
+ type = WIN_DEF_BUTTON;
+
+ else if (type_name == "combo")
+ type = WIN_DEF_COMBO;
+
+ else if (type_name == "edit")
+ type = WIN_DEF_EDIT;
+
+ else if (type_name == "image")
+ type = WIN_DEF_IMAGE;
+
+ else if (type_name == "slider")
+ type = WIN_DEF_SLIDER;
+
+ else if (type_name == "list")
+ type = WIN_DEF_LIST;
+
+ else if (type_name == "rich" || type_name == "text" || type_name == "rich_text")
+ type = WIN_DEF_RICH;
+
+ ctrl->SetType(type);
+ }
+
+ else if (pdef->name()->value() == "rect") {
+ Rect r;
+ GetDefRect(r, pdef, filename);
+ ctrl->SetRect(r);
+ }
+
+ else if (pdef->name()->value() == "font") {
+ GetDefText(buf, pdef, filename);
+ ctrl->SetFont(buf);
+ }
+
+ else if (pdef->name()->value() == "active_color") {
+ Color c;
+ GetDefColor(c, pdef, filename);
+ ctrl->SetActiveColor(c);
+ }
+
+ else if (pdef->name()->value() == "back_color") {
+ Color c;
+ GetDefColor(c, pdef, filename);
+ ctrl->SetBackColor(c);
+ }
+
+ else if (pdef->name()->value() == "base_color") {
+ Color c;
+ GetDefColor(c, pdef, filename);
+ ctrl->SetBaseColor(c);
+ }
+
+ else if (pdef->name()->value() == "border_color") {
+ Color c;
+ GetDefColor(c, pdef, filename);
+ ctrl->SetBorderColor(c);
+ }
+
+ else if (pdef->name()->value() == "fore_color") {
+ Color c;
+ GetDefColor(c, pdef, filename);
+ ctrl->SetForeColor(c);
+ }
+
+ else if (pdef->name()->value() == "texture") {
+ GetDefText(buf, pdef, filename);
+
+ if (buf.length() > 0 && !buf.contains('.'))
+ buf.append(".pcx");
+
+ ctrl->SetTexture(buf);
+ }
+
+ else if (pdef->name()->value() == "margins") {
+ GetDefInsets(ctrl->margins, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "text_insets") {
+ GetDefInsets(ctrl->text_insets, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "cell_insets") {
+ GetDefInsets(ctrl->cell_insets, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "cells") {
+ GetDefRect(ctrl->cells, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "fixed_width") {
+ GetDefNumber(ctrl->fixed_width, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "fixed_height") {
+ GetDefNumber(ctrl->fixed_height, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "standard_image") {
+ GetDefText(buf, pdef, filename);
+
+ if (buf.length() > 0 && !buf.contains('.'))
+ buf.append(".pcx");
+
+ ctrl->SetStandardImage(buf);
+ }
+
+ else if (pdef->name()->value() == "activated_image") {
+ GetDefText(buf, pdef, filename);
+
+ if (buf.length() > 0 && !buf.contains('.'))
+ buf.append(".pcx");
+
+ ctrl->SetActivatedImage(buf);
+ }
+
+ else if (pdef->name()->value() == "transition_image") {
+ GetDefText(buf, pdef, filename);
+
+ if (buf.length() > 0 && !buf.contains('.'))
+ buf.append(".pcx");
+
+ ctrl->SetTransitionImage(buf);
+ }
+
+ else if (pdef->name()->value() == "picture") {
+ GetDefText(buf, pdef, filename);
+
+ if (buf.length() > 0 && !buf.contains('.'))
+ buf.append(".pcx");
+
+ ctrl->SetPicture(buf);
+ }
+
+ else if (pdef->name()->value() == "enabled") {
+ bool e;
+ GetDefBool(e, pdef, filename);
+ ctrl->SetEnabled(e);
+ }
+
+ else if (pdef->name()->value() == "item") {
+ GetDefText(buf, pdef, filename);
+ ctrl->AddItem(Game::GetText(buf));
+ }
+
+ else if (pdef->name()->value() == "tab") {
+ int tab = 0;
+ GetDefNumber(tab, pdef, filename);
+ ctrl->AddTab(tab);
+ }
+
+ else if (pdef->name()->value() == "column") {
+
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: column structure missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+ ParseColumnDef(ctrl, val);
+ }
+ }
+
+ else if (pdef->name()->value() == "orientation") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetOrientation(n);
+ }
+
+ else if (pdef->name()->value() == "leading") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetLeading(n);
+ }
+
+ else if (pdef->name()->value() == "line_height") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetLineHeight(n);
+ }
+
+ else if (pdef->name()->value() == "multiselect") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetMultiSelect(n);
+ }
+
+ else if (pdef->name()->value() == "dragdrop") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetDragDrop(n);
+ }
+
+ else if (pdef->name()->value() == "scroll_bar") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetScrollBarVisible(n);
+ }
+
+ else if (pdef->name()->value() == "smooth_scroll") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetSmoothScroll(b);
+ }
+
+ else if (pdef->name()->value() == "picture_loc") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetPictureLocation((short) n);
+ }
+
+ else if (pdef->name()->value() == "picture_type") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetPictureType((short) n);
+ }
+
+ else if (pdef->name()->value() == "style") {
+ DWORD s;
+ GetDefNumber(s, pdef, filename);
+ ctrl->SetStyle(s);
+ }
+
+ else if (pdef->name()->value() == "align" ||
+ pdef->name()->value() == "text_align") {
+ DWORD a = DT_LEFT;
+
+ if (GetDefText(buf, pdef, filename)) {
+ if (!stricmp(buf, "left"))
+ a = DT_LEFT;
+ else if (!stricmp(buf, "right"))
+ a = DT_RIGHT;
+ else if (!stricmp(buf, "center"))
+ a = DT_CENTER;
+ }
+
+ else {
+ GetDefNumber(a, pdef, filename);
+ }
+
+ ctrl->SetTextAlign(a);
+ }
+
+ else if (pdef->name()->value() == "single_line") {
+ bool single = false;
+ GetDefBool(single, pdef, filename);
+ ctrl->SetSingleLine(single);
+ }
+
+ else if (pdef->name()->value() == "bevel_width") {
+ DWORD s;
+ GetDefNumber(s, pdef, filename);
+ ctrl->SetBevelWidth((short) s);
+ }
+
+ else if (pdef->name()->value() == "active") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetActive(b);
+ }
+
+ else if (pdef->name()->value() == "animated") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetAnimated(b);
+ }
+
+ else if (pdef->name()->value() == "border") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetBorder(b);
+ }
+
+ else if (pdef->name()->value() == "drop_shadow") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetDropShadow(b);
+ }
+
+ else if (pdef->name()->value() == "show_headings") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetShowHeadings(b);
+ }
+
+ else if (pdef->name()->value() == "sticky") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetSticky(b);
+ }
+
+ else if (pdef->name()->value() == "transparent") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetTransparent(b);
+ }
+
+ else if (pdef->name()->value() == "hide_partial") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetHidePartial(b);
+ }
+
+ else if (pdef->name()->value() == "num_leds") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetNumLeds(n);
+ }
+
+ else if (pdef->name()->value() == "item_style") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetItemStyle((short) n);
+ }
+
+ else if (pdef->name()->value() == "selected_style") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetSelectedStyle((short) n);
+ }
+
+ else if (pdef->name()->value() == "password") {
+ Text password;
+ GetDefText(password, pdef, filename);
+ ctrl->SetPasswordChar((char) password[0]);
+ }
+
+ // layout constraints:
+
+ else if (pdef->name()->value() == "layout") {
+
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: layout structure missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+ ParseLayoutDef(&ctrl->layout, val);
+ }
+ }
+ }
+ }
+}
+
+void FormDef::ParseColumnDef(CtrlDef* ctrl, TermStruct* val)
+{
+ Text text;
+ char buf[256];
+ int width = 0;
+ int align = 0;
+ int sort = 0;
+ Color c;
+ bool use_color = false;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "text" ||
+ pdef->name()->value() == "title") {
+ GetDefText(buf, pdef, filename);
+ text = Game::GetText(buf);
+ }
+
+ else if (pdef->name()->value() == "width") {
+ GetDefNumber(width, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "align") {
+ align = DT_LEFT;
+
+ if (GetDefText(buf, pdef, filename)) {
+ if (!stricmp(buf, "left"))
+ align = DT_LEFT;
+ else if (!stricmp(buf, "right"))
+ align = DT_RIGHT;
+ else if (!stricmp(buf, "center"))
+ align = DT_CENTER;
+ }
+
+ else {
+ GetDefNumber(align, pdef, filename);
+ }
+ }
+
+ else if (pdef->name()->value() == "sort") {
+ GetDefNumber(sort, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "color") {
+ GetDefColor(c, pdef, filename);
+ use_color = true;
+ }
+ }
+ }
+
+ ctrl->AddColumn(text, width, align, sort);
+
+ if (use_color) {
+ int index = ctrl->NumColumns()-1;
+ ColumnDef* column = ctrl->GetColumn(index);
+
+ if (column) {
+ column->color = c;
+ column->use_color = true;
+ }
+ }
+}
+
+void FormDef::ParseLayoutDef(LayoutDef* def, TermStruct* val)
+{
+ if (!def || !val)
+ return;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "x_mins" ||
+ pdef->name()->value() == "cols") {
+ GetDefArray(def->x_mins, pdef, filename);
+ }
+
+ else
+ if (pdef->name()->value() == "y_mins" ||
+ pdef->name()->value() == "rows") {
+ GetDefArray(def->y_mins, pdef, filename);
+ }
+
+ else
+ if (pdef->name()->value() == "x_weights" ||
+ pdef->name()->value() == "col_wts") {
+ GetDefArray(def->x_weights, pdef, filename);
+ }
+
+ else
+ if (pdef->name()->value() == "y_weights" ||
+ pdef->name()->value() == "row_wts") {
+ GetDefArray(def->y_weights, pdef, filename);
+ }
+ }
+ }
+
+}
+
+
diff --git a/nGenEx/FormDef.h b/nGenEx/FormDef.h
new file mode 100644
index 0000000..131d3f4
--- /dev/null
+++ b/nGenEx/FormDef.h
@@ -0,0 +1,332 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FormDef.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Form and Control Definition Resources
+*/
+
+#ifndef FormDef_h
+#define FormDef_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Color.h"
+#include "Text.h"
+#include "ArrayList.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class FormDef; // values defining form style and control placement
+class CtrlDef; // values defining control style
+class WinDef; // base class for FormDef and CtrlDef
+class TermStruct; // used for parsing
+
+enum WinType {
+ WIN_DEF_FORM,
+ WIN_DEF_LABEL,
+ WIN_DEF_BUTTON,
+ WIN_DEF_COMBO,
+ WIN_DEF_EDIT,
+ WIN_DEF_IMAGE,
+ WIN_DEF_SLIDER,
+ WIN_DEF_LIST,
+ WIN_DEF_RICH
+};
+
+// +--------------------------------------------------------------------+
+
+class ColumnDef
+{
+public:
+ static const char* TYPENAME() { return "ColumnDef"; }
+
+ ColumnDef();
+ ColumnDef(const char* title, int width, int align, int sort);
+
+ Text title;
+ int width;
+ int align;
+ int sort;
+ Color color;
+ bool use_color;
+};
+
+// +--------------------------------------------------------------------+
+
+class LayoutDef
+{
+public:
+ static const char* TYPENAME() { return "LayoutDef"; }
+
+ ArrayList x_mins;
+ ArrayList y_mins;
+ FloatList x_weights;
+ FloatList y_weights;
+};
+
+// +--------------------------------------------------------------------+
+
+class WinDef
+{
+ friend class FormDef;
+
+public:
+ static const char* TYPENAME() { return "WinDef"; }
+
+ WinDef(DWORD id, DWORD type, const char* text=0, DWORD style=0);
+ virtual ~WinDef() { }
+
+ int operator == (const WinDef& w) const { return id == w.id; }
+
+ DWORD GetID() const { return id; }
+ void SetID(DWORD id);
+ DWORD GetParentID() const { return pid; }
+ void SetParentID(DWORD id);
+ DWORD GetType() const { return type; }
+ void SetType(DWORD type);
+
+ void SetRect(const Rect& r);
+ Rect GetRect() const { return rect; }
+ int GetX() const { return rect.x; }
+ int GetY() const { return rect.y; }
+ int GetW() const { return rect.w; }
+ int GetH() const { return rect.h; }
+
+ void SetEnabled(bool enable=true);
+ bool IsEnabled() const { return enabled; }
+
+ void SetStyle(DWORD s);
+ DWORD GetStyle() const { return style; }
+
+ void SetFont(const char* t);
+ const Text& GetFont() const { return font; }
+ void SetText(const char* t);
+ const Text& GetText() const { return text; }
+ void SetAltText(const char* t);
+ const Text& GetAltText() const { return alt_text; }
+ void SetTexture(const char* t);
+ const Text& GetTexture() const { return texture; }
+
+ void SetBackColor(Color c);
+ Color GetBackColor() const { return back_color; }
+ void SetBaseColor(Color c);
+ Color GetBaseColor() const { return base_color; }
+ void SetForeColor(Color c);
+ Color GetForeColor() const { return fore_color; }
+ void SetSingleLine(bool a);
+ bool GetSingleLine() const { return single_line; }
+ void SetTextAlign(DWORD a);
+ DWORD GetTextAlign() const { return text_align; }
+ void SetTransparent(bool t);
+ bool GetTransparent() const { return transparent; }
+ void SetHidePartial(bool a);
+ bool GetHidePartial() const { return hide_partial;}
+
+ void SetMargins(const Insets& m);
+ const Insets& GetMargins() const { return margins; }
+ void SetTextInsets(const Insets& t);
+ const Insets& GetTextInsets() const { return text_insets; }
+ void SetCellInsets(const Insets& t);
+ const Insets& GetCellInsets() const { return cell_insets; }
+ void SetCells(const Rect& r);
+ const Rect& GetCells() const { return cells; }
+
+ void SetFixedWidth(int w) { fixed_width = w; }
+ int GetFixedWidth() const { return fixed_width; }
+ void SetFixedHeight(int h) { fixed_height = h; }
+ int GetFixedHeight() const { return fixed_height;}
+
+ const LayoutDef& GetLayout() const { return layout; }
+
+protected:
+ DWORD id;
+ DWORD pid;
+ DWORD type;
+ Rect rect;
+ Text font;
+ Text text;
+ Text alt_text;
+ Text texture;
+ Text picture;
+ DWORD style;
+ DWORD text_align;
+ bool single_line;
+ bool enabled;
+ bool transparent;
+ bool hide_partial;
+ Color back_color;
+ Color base_color;
+ Color fore_color;
+
+ Insets margins;
+ Insets text_insets;
+ Insets cell_insets;
+ Rect cells;
+ int fixed_width;
+ int fixed_height;
+
+ LayoutDef layout;
+};
+
+// +--------------------------------------------------------------------+
+
+class CtrlDef : public WinDef
+{
+public:
+ static const char* TYPENAME() { return "CtrlDef"; }
+
+ CtrlDef(DWORD id=0, DWORD type=WIN_DEF_LABEL, const char* text=0, DWORD style=0);
+ virtual ~CtrlDef();
+
+ virtual CtrlDef& operator=(const CtrlDef& ctrl);
+
+ bool GetActive() const;
+ void SetActive(bool c);
+ Color GetActiveColor() const;
+ void SetActiveColor(Color c);
+ bool GetAnimated() const;
+ void SetAnimated(bool bNewValue);
+ short GetBevelWidth() const;
+ void SetBevelWidth(short nNewValue);
+ bool GetBorder() const;
+ void SetBorder(bool bNewValue);
+ Color GetBorderColor() const;
+ void SetBorderColor(Color c);
+ bool GetDropShadow() const;
+ void SetDropShadow(bool bNewValue);
+ bool GetIndent() const;
+ void SetIndent(bool bNewValue);
+ bool GetInvertLabel() const;
+ void SetInvertLabel(bool bNewValue);
+ int GetOrientation() const;
+ void SetOrientation(int o);
+ Text GetPicture() const;
+ void SetPicture(const Text& img_name);
+ short GetPictureLocation() const;
+ void SetPictureLocation(short nNewValue);
+ short GetPictureType() const;
+ void SetPictureType(short nNewValue);
+ bool GetSticky() const;
+ void SetSticky(bool bNewValue);
+ int GetNumLeds() const;
+ void SetNumLeds(int nNewValue);
+
+ int NumItems() const;
+ Text GetItem(int i) const;
+ void AddItem(const char* t);
+
+ int NumColumns() const;
+ ColumnDef* GetColumn(int i) const;
+ void AddColumn(const char* t, int w, int a, int s);
+
+ int NumTabs() const;
+ int GetTab(int i) const;
+ void SetTab(int i, int t);
+ void AddTab(int i);
+
+ bool GetShowHeadings() const;
+ void SetShowHeadings(bool bNewValue);
+ int GetLeading() const;
+ void SetLeading(int nNewValue);
+ int GetLineHeight() const;
+ void SetLineHeight(int nNewValue);
+ int GetMultiSelect() const;
+ void SetMultiSelect(int nNewValue);
+ int GetDragDrop() const;
+ void SetDragDrop(int nNewValue);
+ int GetScrollBarVisible() const;
+ void SetScrollBarVisible(int nNewValue);
+ bool GetSmoothScroll() const;
+ void SetSmoothScroll(bool bNewValue);
+
+ short GetItemStyle() const;
+ void SetItemStyle(short nNewValue);
+ short GetSelectedStyle() const;
+ void SetSelectedStyle(short nNewValue);
+
+ char GetPasswordChar() const;
+ void SetPasswordChar(char c);
+
+ Text GetStandardImage() const;
+ void SetStandardImage(const Text& img_name);
+ Text GetActivatedImage() const;
+ void SetActivatedImage(const Text& img_name);
+ Text GetTransitionImage() const;
+ void SetTransitionImage(const Text& img_name);
+
+protected:
+ WORD ctrl_flags;
+ short bevel_width;
+
+ Color active_color;
+ Color border_color;
+
+ Text picture;
+ short picture_loc;
+ short picture_type;
+
+ Text standard_image;
+ Text activated_image;
+ Text transition_image;
+
+ bool active;
+ bool show_headings;
+ int leading;
+ int line_height;
+ int multiselect;
+ int dragdrop;
+ int scroll_bar;
+ int orientation;
+ int num_leds;
+
+ short item_style;
+ short selected_style;
+
+ bool smooth_scroll;
+
+ List<Text> items;
+ List<ColumnDef> columns;
+
+ int ntabs;
+ int tabs[10];
+ char pass_char;
+};
+
+// +--------------------------------------------------------------------+
+
+class FormDef : public WinDef
+{
+public:
+ static const char* TYPENAME() { return "FormDef"; }
+
+ FormDef(const char* text=0, DWORD style=0);
+ virtual ~FormDef();
+
+ void Load(const char* filename);
+
+ void AddCtrl(CtrlDef* def);
+ CtrlDef* FindCtrl(BYTE ctrl_id);
+
+ ListIter<CtrlDef> GetControls() const;
+
+protected:
+ void ParseCtrlDef(CtrlDef* ctrl, TermStruct* val);
+ void ParseColumnDef(CtrlDef* ctrl, TermStruct* val);
+ void ParseLayoutDef(LayoutDef* def, TermStruct* val);
+
+ CtrlDef defctrl;
+ List<CtrlDef> controls;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif FormDef_h
+
diff --git a/nGenEx/FormWindow.cpp b/nGenEx/FormWindow.cpp
new file mode 100644
index 0000000..1613491
--- /dev/null
+++ b/nGenEx/FormWindow.cpp
@@ -0,0 +1,809 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Form.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Form Window class
+*/
+
+#include "MemDebug.h"
+#include "FormWindow.h"
+#include "Screen.h"
+#include "DataLoader.h"
+#include "Font.h"
+#include "FontMgr.h"
+
+#include "Button.h"
+#include "ComboBox.h"
+#include "EditBox.h"
+#include "ImageBox.h"
+#include "ListBox.h"
+#include "RichTextBox.h"
+#include "Slider.h"
+
+// +--------------------------------------------------------------------+
+
+FormWindow::FormWindow(Screen* screen, int ax, int ay, int aw, int ah,
+ DWORD aid, DWORD s, ActiveWindow* pParent)
+ : ActiveWindow(screen, ax, ay, aw, ah, aid, s, pParent)
+{
+ char buf[32];
+ sprintf(buf, "Form %d", id);
+ desc = buf;
+}
+
+// +--------------------------------------------------------------------+
+
+FormWindow::~FormWindow()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FormWindow::Init()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FormWindow::Destroy()
+{
+ Hide();
+ children.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FormWindow::AddControl(ActiveWindow* ctrl)
+{
+ if (ctrl) {
+ if (!children.contains(ctrl))
+ children.append(ctrl);
+
+ ctrl->SetForm(this);
+
+ if (!shown)
+ ctrl->Hide();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Button*
+FormWindow::CreateButton(const char* btn_text, int ax, int ay, int aw, int ah, DWORD aid, DWORD pid)
+{
+ Button* button = 0;
+ ActiveWindow* parent = this;
+
+ if (pid)
+ parent = FindControl(pid);
+
+ button = new(__FILE__,__LINE__) Button(parent, ax, ay, aw, ah, aid);
+
+ if (button) {
+ button->SetForm(this);
+ button->SetText(btn_text);
+
+ if (!shown)
+ button->Hide();
+ }
+
+ return button;
+}
+
+// +--------------------------------------------------------------------+
+
+ImageBox*
+FormWindow::CreateImageBox(const char* lbl_text, int ax, int ay, int aw, int ah, DWORD aid, DWORD pid)
+{
+ ImageBox* image = 0;
+ ActiveWindow* parent = this;
+
+ if (pid)
+ parent = FindControl(pid);
+
+ image = new(__FILE__,__LINE__) ImageBox(parent, ax, ay, aw, ah, aid);
+
+ if (image) {
+ image->SetForm(this);
+ image->SetText(lbl_text);
+
+ if (!shown)
+ image->Hide();
+ }
+
+ return image;
+}
+
+// +--------------------------------------------------------------------+
+
+ActiveWindow*
+FormWindow::CreateLabel(const char* label_text, int ax, int ay, int aw, int ah, DWORD aid, DWORD pid, DWORD astyle)
+{
+ ActiveWindow* label = 0;
+ ActiveWindow* parent = this;
+
+ if (pid)
+ parent = FindControl(pid);
+
+ label = new(__FILE__,__LINE__) ActiveWindow(screen, ax, ay, aw, ah, aid, astyle, parent);
+
+ if (label) {
+ label->SetForm(this);
+ label->SetText(label_text);
+
+ if (!shown)
+ label->Hide();
+ }
+
+ return label;
+}
+
+// +--------------------------------------------------------------------+
+
+ListBox*
+FormWindow::CreateListBox(const char* lbl_text, int ax, int ay, int aw, int ah, DWORD aid, DWORD pid)
+{
+ ListBox* list = 0;
+ ActiveWindow* parent = this;
+
+ if (pid)
+ parent = FindControl(pid);
+
+ list = new(__FILE__,__LINE__) ListBox(parent, ax, ay, aw, ah, aid);
+
+ if (list) {
+ list->SetForm(this);
+
+ if (!shown)
+ list->Hide();
+ }
+
+ return list;
+}
+
+// +--------------------------------------------------------------------+
+
+ComboBox*
+FormWindow::CreateComboBox(const char* lbl_text, int ax, int ay, int aw, int ah, DWORD aid, DWORD pid)
+{
+ ComboBox* combo = 0;
+ ActiveWindow* parent = this;
+
+ if (pid)
+ parent = FindControl(pid);
+
+ combo = new(__FILE__,__LINE__) ComboBox(parent, ax, ay, aw, ah, aid);
+
+ if (combo) {
+ combo->SetForm(this);
+ combo->SetLabel(lbl_text);
+
+ if (!shown)
+ combo->Hide();
+ }
+
+ return combo;
+}
+
+// +--------------------------------------------------------------------+
+
+EditBox*
+FormWindow::CreateEditBox(const char* lbl_text, int ax, int ay, int aw, int ah, DWORD aid, DWORD pid)
+{
+ EditBox* edit = 0;
+ ActiveWindow* parent = this;
+
+ if (pid)
+ parent = FindControl(pid);
+
+ edit = new(__FILE__,__LINE__) EditBox(parent, ax, ay, aw, ah, aid);
+
+ if (edit) {
+ edit->SetForm(this);
+ edit->SetText(lbl_text);
+
+ if (!shown)
+ edit->Hide();
+ }
+
+ return edit;
+}
+
+// +--------------------------------------------------------------------+
+
+RichTextBox*
+FormWindow::CreateRichTextBox(const char* label_text, int ax, int ay, int aw, int ah, DWORD aid, DWORD pid, DWORD astyle)
+{
+ RichTextBox* rtb = 0;
+ ActiveWindow* parent = this;
+
+ if (pid)
+ parent = FindControl(pid);
+
+ rtb = new(__FILE__,__LINE__) RichTextBox(parent, ax, ay, aw, ah, aid, astyle);
+
+ if (rtb) {
+ rtb->SetForm(this);
+ rtb->SetText(label_text);
+
+ if (!shown)
+ rtb->Hide();
+ }
+
+ return rtb;
+}
+
+// +--------------------------------------------------------------------+
+
+Slider*
+FormWindow::CreateSlider(const char* label_text, int ax, int ay, int aw, int ah, DWORD aid, DWORD pid, DWORD astyle)
+{
+ Slider* slider = 0;
+ ActiveWindow* parent = this;
+
+ if (pid)
+ parent = FindControl(pid);
+
+ slider = new(__FILE__,__LINE__) Slider(parent, ax, ay, aw, ah, aid);
+
+ if (slider) {
+ slider->SetForm(this);
+
+ if (!shown)
+ slider->Hide();
+ }
+
+ return slider;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FormWindow::Init(const FormDef& def)
+{
+ if (def.GetRect().w > 0 && def.GetRect().h > 0) {
+ // if form size is specified in def, and it is
+ // smaller than the current screen size,
+ // center the form on the display:
+
+ Rect r = def.GetRect();
+
+ if (r.w < screen->Width()) {
+ r.x = (screen->Width() - r.w) / 2;
+ }
+ else {
+ r.x = 0;
+ r.w = screen->Width();
+ }
+
+ if (r.h < screen->Height()) {
+ r.y = (screen->Height() - r.h) / 2;
+ }
+ else {
+ r.y = 0;
+ r.h = screen->Height();
+ }
+
+ MoveTo(r);
+ }
+
+ SetMargins(def.GetMargins());
+ SetTextInsets(def.GetTextInsets());
+ SetCellInsets(def.GetCellInsets());
+ SetCells(def.GetCells());
+ SetFixedWidth(def.GetFixedWidth());
+ SetFixedHeight(def.GetFixedHeight());
+
+ UseLayout(def.GetLayout().x_mins,
+ def.GetLayout().y_mins,
+ def.GetLayout().x_weights,
+ def.GetLayout().y_weights);
+
+ if (def.GetTexture().length() > 0) {
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Screens/");
+ loader->LoadTexture(def.GetTexture(), texture);
+ loader->SetDataPath("");
+ }
+
+ SetBackColor(def.GetBackColor());
+ SetForeColor(def.GetForeColor());
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) SetFont(f);
+
+ SetTransparent(def.GetTransparent());
+
+ ListIter<CtrlDef> ctrl = def.GetControls();
+ while (++ctrl) {
+ switch (ctrl->GetType()) {
+ case WIN_DEF_FORM:
+ case WIN_DEF_LABEL:
+ default:
+ CreateDefLabel(*ctrl);
+ break;
+
+ case WIN_DEF_BUTTON:
+ CreateDefButton(*ctrl);
+ break;
+
+ case WIN_DEF_COMBO:
+ CreateDefCombo(*ctrl);
+ break;
+
+ case WIN_DEF_IMAGE:
+ CreateDefImage(*ctrl);
+ break;
+
+ case WIN_DEF_EDIT:
+ CreateDefEdit(*ctrl);
+ break;
+
+ case WIN_DEF_LIST:
+ CreateDefList(*ctrl);
+ break;
+
+ case WIN_DEF_SLIDER:
+ CreateDefSlider(*ctrl);
+ break;
+
+ case WIN_DEF_RICH:
+ CreateDefRichText(*ctrl);
+ break;
+ }
+ }
+
+ RegisterControls();
+ DoLayout();
+ CalcGrid();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FormWindow::CreateDefLabel(CtrlDef& def)
+{
+ ActiveWindow* ctrl = CreateLabel(def.GetText(),
+ def.GetX(),
+ def.GetY(),
+ def.GetW(),
+ def.GetH(),
+ def.GetID(),
+ def.GetParentID(),
+ def.GetStyle());
+
+ ctrl->SetAltText(def.GetAltText());
+ ctrl->SetBackColor(def.GetBackColor());
+ ctrl->SetForeColor(def.GetForeColor());
+ ctrl->SetTextAlign(def.GetTextAlign());
+ ctrl->SetSingleLine(def.GetSingleLine());
+ ctrl->SetTransparent(def.GetTransparent());
+ ctrl->SetHidePartial(def.GetHidePartial());
+
+ ctrl->SetMargins(def.GetMargins());
+ ctrl->SetTextInsets(def.GetTextInsets());
+ ctrl->SetCellInsets(def.GetCellInsets());
+ ctrl->SetCells(def.GetCells());
+ ctrl->SetFixedWidth(def.GetFixedWidth());
+ ctrl->SetFixedHeight(def.GetFixedHeight());
+
+ ctrl->UseLayout(def.GetLayout().x_mins,
+ def.GetLayout().y_mins,
+ def.GetLayout().x_weights,
+ def.GetLayout().y_weights);
+
+ if (def.GetTexture().length() > 0) {
+ Bitmap* ctrl_tex = 0;
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Screens/");
+ loader->LoadTexture(def.GetTexture(), ctrl_tex);
+ loader->SetDataPath("");
+
+ ctrl->SetTexture(ctrl_tex);
+ }
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) ctrl->SetFont(f);
+}
+
+void
+FormWindow::CreateDefButton(CtrlDef& def)
+{
+ Button* ctrl = CreateButton(def.GetText(),
+ def.GetX(),
+ def.GetY(),
+ def.GetW(),
+ def.GetH(),
+ def.GetID(),
+ def.GetParentID());
+
+ if (def.GetStandardImage().length()) {
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Screens/");
+
+ Bitmap* bmp = 0;
+ loader->LoadTexture(def.GetStandardImage(), bmp);
+ ctrl->SetStandardImage(bmp);
+
+ if (def.GetActivatedImage().length()) {
+ loader->LoadTexture(def.GetActivatedImage(), bmp);
+ ctrl->SetActivatedImage(bmp);
+ }
+
+ if (def.GetTransitionImage().length()) {
+ loader->LoadTexture(def.GetTransitionImage(), bmp);
+ ctrl->SetTransitionImage(bmp);
+ }
+
+ loader->SetDataPath("");
+ }
+
+ ctrl->SetAltText(def.GetAltText());
+ ctrl->SetEnabled(def.IsEnabled());
+ ctrl->SetBackColor(def.GetBackColor());
+ ctrl->SetForeColor(def.GetForeColor());
+ ctrl->SetTextAlign(def.GetTextAlign());
+ ctrl->SetSingleLine(def.GetSingleLine());
+
+ ctrl->SetBevelWidth(def.GetBevelWidth());
+ ctrl->SetDropShadow(def.GetDropShadow());
+ ctrl->SetSticky(def.GetSticky());
+ ctrl->SetTransparent(def.GetTransparent());
+ ctrl->SetHidePartial(def.GetHidePartial());
+ ctrl->SetPictureLocation(def.GetPictureLocation());
+
+ ctrl->SetMargins(def.GetMargins());
+ ctrl->SetTextInsets(def.GetTextInsets());
+ ctrl->SetCellInsets(def.GetCellInsets());
+ ctrl->SetCells(def.GetCells());
+ ctrl->SetFixedWidth(def.GetFixedWidth());
+ ctrl->SetFixedHeight(def.GetFixedHeight());
+
+ if (def.GetPicture().length() > 0) {
+ Bitmap pict;
+ int type = def.GetPictureType();
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Screens/");
+ loader->LoadBitmap(def.GetPicture(), pict, type);
+ loader->SetDataPath("");
+
+ ctrl->SetPicture(pict);
+ }
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) ctrl->SetFont(f);
+}
+
+void
+FormWindow::CreateDefImage(CtrlDef& def)
+{
+ ImageBox* ctrl = CreateImageBox(def.GetText(),
+ def.GetX(),
+ def.GetY(),
+ def.GetW(),
+ def.GetH(),
+ def.GetID(),
+ def.GetParentID());
+
+ ctrl->SetAltText(def.GetAltText());
+ ctrl->SetBackColor(def.GetBackColor());
+ ctrl->SetForeColor(def.GetForeColor());
+ ctrl->SetStyle(def.GetStyle());
+ ctrl->SetTextAlign(def.GetTextAlign());
+ ctrl->SetSingleLine(def.GetSingleLine());
+ ctrl->SetTransparent(def.GetTransparent());
+ ctrl->SetHidePartial(def.GetHidePartial());
+
+ ctrl->SetMargins(def.GetMargins());
+ ctrl->SetTextInsets(def.GetTextInsets());
+ ctrl->SetCellInsets(def.GetCellInsets());
+ ctrl->SetCells(def.GetCells());
+ ctrl->SetFixedWidth(def.GetFixedWidth());
+ ctrl->SetFixedHeight(def.GetFixedHeight());
+
+ if (def.GetPicture().length() > 0) {
+ Bitmap picture;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Screens/");
+ loader->LoadBitmap(def.GetPicture(), picture);
+ loader->SetDataPath("");
+
+ ctrl->SetPicture(picture);
+ }
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) ctrl->SetFont(f);
+}
+
+void
+FormWindow::CreateDefList(CtrlDef& def)
+{
+ ListBox* ctrl = CreateListBox(def.GetText(),
+ def.GetX(),
+ def.GetY(),
+ def.GetW(),
+ def.GetH(),
+ def.GetID(),
+ def.GetParentID());
+
+ ctrl->SetAltText(def.GetAltText());
+ ctrl->SetEnabled(def.IsEnabled());
+ ctrl->SetBackColor(def.GetBackColor());
+ ctrl->SetForeColor(def.GetForeColor());
+ ctrl->SetStyle(def.GetStyle());
+ ctrl->SetTextAlign(def.GetTextAlign());
+ ctrl->SetTransparent(def.GetTransparent());
+ ctrl->SetHidePartial(def.GetHidePartial());
+
+ ctrl->SetLineHeight(def.GetLineHeight());
+ ctrl->SetShowHeadings(def.GetShowHeadings());
+ ctrl->SetLeading(def.GetLeading());
+ ctrl->SetMultiSelect(def.GetMultiSelect());
+ ctrl->SetDragDrop(def.GetDragDrop());
+ ctrl->SetScrollBarVisible(def.GetScrollBarVisible());
+ ctrl->SetSmoothScroll(def.GetSmoothScroll());
+ ctrl->SetItemStyle(def.GetItemStyle());
+ ctrl->SetSelectedStyle(def.GetSelectedStyle());
+
+ ctrl->SetMargins(def.GetMargins());
+ ctrl->SetTextInsets(def.GetTextInsets());
+ ctrl->SetCellInsets(def.GetCellInsets());
+ ctrl->SetCells(def.GetCells());
+ ctrl->SetFixedWidth(def.GetFixedWidth());
+ ctrl->SetFixedHeight(def.GetFixedHeight());
+
+ if (def.GetTexture().length() > 0) {
+ Bitmap* ctrl_tex = 0;
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Screens/");
+ loader->LoadTexture(def.GetTexture(), ctrl_tex);
+ loader->SetDataPath("");
+
+ ctrl->SetTexture(ctrl_tex);
+ }
+
+ int ncols = def.NumColumns();
+ for (int i = 0; i < ncols; i++) {
+ ColumnDef* col = def.GetColumn(i);
+ ctrl->AddColumn(col->title, col->width, col->align, col->sort);
+
+ if (col->use_color)
+ ctrl->SetColumnColor(i, col->color);
+ }
+
+ int nitems = def.NumItems();
+ for (i = 0; i < nitems; i++)
+ ctrl->AddItem(def.GetItem(i));
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) ctrl->SetFont(f);
+}
+
+void
+FormWindow::CreateDefCombo(CtrlDef& def)
+{
+ ComboBox* ctrl = CreateComboBox(def.GetText(),
+ def.GetX(),
+ def.GetY(),
+ def.GetW(),
+ def.GetH(),
+ def.GetID(),
+ def.GetParentID());
+
+ ctrl->SetAltText(def.GetAltText());
+ ctrl->SetEnabled(def.IsEnabled());
+ ctrl->SetBackColor(def.GetBackColor());
+ ctrl->SetForeColor(def.GetForeColor());
+ ctrl->SetTextAlign(def.GetTextAlign());
+
+ ctrl->SetActiveColor(def.GetActiveColor());
+ ctrl->SetBorderColor(def.GetBorderColor());
+ ctrl->SetBorder(def.GetBorder());
+ ctrl->SetBevelWidth(def.GetBevelWidth());
+ ctrl->SetTransparent(def.GetTransparent());
+ ctrl->SetHidePartial(def.GetHidePartial());
+
+ ctrl->SetMargins(def.GetMargins());
+ ctrl->SetTextInsets(def.GetTextInsets());
+ ctrl->SetCellInsets(def.GetCellInsets());
+ ctrl->SetCells(def.GetCells());
+ ctrl->SetFixedWidth(def.GetFixedWidth());
+ ctrl->SetFixedHeight(def.GetFixedHeight());
+
+ int nitems = def.NumItems();
+ for (int i = 0; i < nitems; i++)
+ ctrl->AddItem(def.GetItem(i));
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) ctrl->SetFont(f);
+}
+
+void
+FormWindow::CreateDefEdit(CtrlDef& def)
+{
+ EditBox* ctrl = CreateEditBox(def.GetText(),
+ def.GetX(),
+ def.GetY(),
+ def.GetW(),
+ def.GetH(),
+ def.GetID(),
+ def.GetParentID());
+
+ ctrl->SetAltText(def.GetAltText());
+ ctrl->SetEnabled(def.IsEnabled());
+ ctrl->SetBackColor(def.GetBackColor());
+ ctrl->SetForeColor(def.GetForeColor());
+ ctrl->SetStyle(def.GetStyle());
+ ctrl->SetSingleLine(def.GetSingleLine());
+ ctrl->SetTextAlign(def.GetTextAlign());
+ ctrl->SetTransparent(def.GetTransparent());
+ ctrl->SetHidePartial(def.GetHidePartial());
+ ctrl->SetPasswordChar(def.GetPasswordChar());
+
+ ctrl->SetMargins(def.GetMargins());
+ ctrl->SetTextInsets(def.GetTextInsets());
+ ctrl->SetCellInsets(def.GetCellInsets());
+ ctrl->SetCells(def.GetCells());
+ ctrl->SetFixedWidth(def.GetFixedWidth());
+ ctrl->SetFixedHeight(def.GetFixedHeight());
+
+ ctrl->SetLineHeight(def.GetLineHeight());
+ ctrl->SetScrollBarVisible(def.GetScrollBarVisible());
+ ctrl->SetSmoothScroll(def.GetSmoothScroll());
+
+ if (def.GetTexture().length() > 0) {
+ Bitmap* ctrl_tex = 0;
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Screens/");
+ loader->LoadTexture(def.GetTexture(), ctrl_tex);
+ loader->SetDataPath("");
+
+ ctrl->SetTexture(ctrl_tex);
+ }
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) ctrl->SetFont(f);
+}
+
+void
+FormWindow::CreateDefSlider(CtrlDef& def)
+{
+ Slider* ctrl = CreateSlider(def.GetText(),
+ def.GetX(),
+ def.GetY(),
+ def.GetW(),
+ def.GetH(),
+ def.GetID(),
+ def.GetParentID());
+
+
+ ctrl->SetAltText(def.GetAltText());
+ ctrl->SetEnabled(def.IsEnabled());
+ ctrl->SetBackColor(def.GetBackColor());
+ ctrl->SetForeColor(def.GetForeColor());
+
+ ctrl->SetActive(def.GetActive());
+ ctrl->SetOrientation(def.GetOrientation());
+ ctrl->SetFillColor(def.GetActiveColor());
+ ctrl->SetBorderColor(def.GetBorderColor());
+ ctrl->SetBorder(def.GetBorder());
+ ctrl->SetStyle(def.GetStyle());
+ ctrl->SetTransparent(def.GetTransparent());
+ ctrl->SetHidePartial(def.GetHidePartial());
+ ctrl->SetNumLeds(def.GetNumLeds());
+
+ ctrl->SetMargins(def.GetMargins());
+ ctrl->SetTextInsets(def.GetTextInsets());
+ ctrl->SetCellInsets(def.GetCellInsets());
+ ctrl->SetCells(def.GetCells());
+ ctrl->SetFixedWidth(def.GetFixedWidth());
+ ctrl->SetFixedHeight(def.GetFixedHeight());
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) ctrl->SetFont(f);
+}
+
+void
+FormWindow::CreateDefRichText(CtrlDef& def)
+{
+ RichTextBox* ctrl = CreateRichTextBox(def.GetText(),
+ def.GetX(),
+ def.GetY(),
+ def.GetW(),
+ def.GetH(),
+ def.GetID(),
+ def.GetParentID(),
+ def.GetStyle());
+
+ ctrl->SetAltText(def.GetAltText());
+ ctrl->SetBackColor(def.GetBackColor());
+ ctrl->SetForeColor(def.GetForeColor());
+ ctrl->SetLineHeight(def.GetLineHeight());
+ ctrl->SetLeading(def.GetLeading());
+ ctrl->SetScrollBarVisible(def.GetScrollBarVisible());
+ ctrl->SetSmoothScroll(def.GetSmoothScroll());
+ ctrl->SetTextAlign(def.GetTextAlign());
+ ctrl->SetTransparent(def.GetTransparent());
+ ctrl->SetHidePartial(def.GetHidePartial());
+
+ ctrl->SetMargins(def.GetMargins());
+ ctrl->SetTextInsets(def.GetTextInsets());
+ ctrl->SetCellInsets(def.GetCellInsets());
+ ctrl->SetCells(def.GetCells());
+ ctrl->SetFixedWidth(def.GetFixedWidth());
+ ctrl->SetFixedHeight(def.GetFixedHeight());
+
+ if (def.GetTexture().length() > 0) {
+ Bitmap* ctrl_tex = 0;
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Screens/");
+ loader->LoadTexture(def.GetTexture(), ctrl_tex);
+ loader->SetDataPath("");
+
+ ctrl->SetTexture(ctrl_tex);
+ }
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) ctrl->SetFont(f);
+
+ for (int i = 0; i < def.NumTabs(); i++)
+ ctrl->SetTabStop(i, def.GetTab(i));
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FormWindow::AdoptFormDef(const FormDef& def)
+{
+ Destroy();
+ Init(def);
+}
+
+// +--------------------------------------------------------------------+
+
+ActiveWindow*
+FormWindow::FindControl(DWORD id)
+{
+ return FindChild(id);
+}
+
+
+// +--------------------------------------------------------------------+
+
+ActiveWindow*
+FormWindow::FindControl(int x, int y)
+{
+ ActiveWindow* mouse_tgt = 0;
+
+ ListIter<ActiveWindow> iter = children;
+ while (++iter) {
+ ActiveWindow* test = iter.value();
+ if (test->TargetRect().Contains(x,y)) {
+ mouse_tgt = test;
+
+ while (test) {
+ test = test->FindChild(x,y);
+
+ if (test)
+ mouse_tgt = test;
+ }
+ }
+ }
+
+ return mouse_tgt;
+}
+
+
+
diff --git a/nGenEx/FormWindow.h b/nGenEx/FormWindow.h
new file mode 100644
index 0000000..b94dded
--- /dev/null
+++ b/nGenEx/FormWindow.h
@@ -0,0 +1,77 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FormWindow.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Form Window class (a window that manages controls)
+*/
+
+#ifndef FormWindow_h
+#define FormWindow_h
+
+#include "Types.h"
+#include "ActiveWindow.h"
+#include "FormDef.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Button;
+class ComboBox;
+class EditBox;
+class ImageBox;
+class ListBox;
+class RichTextBox;
+class Slider;
+
+// +--------------------------------------------------------------------+
+
+class FormWindow : public ActiveWindow
+{
+public:
+ FormWindow(Screen* s, int ax, int ay, int aw, int ah,
+ DWORD aid=0, DWORD style=0, ActiveWindow* parent=0);
+ virtual ~FormWindow();
+
+ // operations:
+ virtual void Init();
+ virtual void Init(const FormDef& def);
+ virtual void Destroy();
+ virtual ActiveWindow* FindControl(DWORD id);
+ virtual ActiveWindow* FindControl(int x, int y);
+ virtual void RegisterControls() { }
+
+ virtual void AdoptFormDef(const FormDef& def);
+ virtual void AddControl(ActiveWindow* ctrl);
+
+ virtual ActiveWindow* CreateLabel( const char* text, int x, int y, int w, int h, DWORD id=0, DWORD pid=0, DWORD style=0);
+ virtual Button* CreateButton( const char* text, int x, int y, int w, int h, DWORD id=0, DWORD pid=0);
+ virtual ImageBox* CreateImageBox( const char* text, int x, int y, int w, int h, DWORD id=0, DWORD pid=0);
+ virtual ListBox* CreateListBox( const char* text, int x, int y, int w, int h, DWORD id=0, DWORD pid=0);
+ virtual ComboBox* CreateComboBox( const char* text, int x, int y, int w, int h, DWORD id=0, DWORD pid=0);
+ virtual EditBox* CreateEditBox( const char* text, int x, int y, int w, int h, DWORD id=0, DWORD pid=0);
+ virtual RichTextBox* CreateRichTextBox(const char* text, int x, int y, int w, int h, DWORD id=0, DWORD pid=0, DWORD style=0);
+ virtual Slider* CreateSlider( const char* text, int x, int y, int w, int h, DWORD id=0, DWORD pid=0, DWORD style=0);
+
+ // property accessors:
+ ListIter<ActiveWindow> Controls() { return children; }
+
+protected:
+ virtual void CreateDefLabel(CtrlDef& def);
+ virtual void CreateDefButton(CtrlDef& def);
+ virtual void CreateDefImage(CtrlDef& def);
+ virtual void CreateDefList(CtrlDef& def);
+ virtual void CreateDefCombo(CtrlDef& def);
+ virtual void CreateDefEdit(CtrlDef& def);
+ virtual void CreateDefSlider(CtrlDef& def);
+ virtual void CreateDefRichText(CtrlDef& def);
+};
+
+#endif FormWindow_h
+
diff --git a/nGenEx/FormatUtil.cpp b/nGenEx/FormatUtil.cpp
new file mode 100644
index 0000000..3e86f07
--- /dev/null
+++ b/nGenEx/FormatUtil.cpp
@@ -0,0 +1,337 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FormatUtil.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+
+void FormatNumber(char* txt, double n)
+{
+ double a = fabs(n);
+
+ if (a < 1e3)
+ sprintf(txt, "%d", (int) (n));
+
+ else if (a < 1e6)
+ sprintf(txt, "%.1f K", (n/1e3));
+
+ else if (a < 1e9)
+ sprintf(txt, "%.1f M", (n/1e6));
+
+ else if (a < 1e12)
+ sprintf(txt, "%.1f G", (n/1e9));
+
+ else if (a < 1e15)
+ sprintf(txt, "%.1f T", (n/1e12));
+
+ else
+ sprintf(txt, "%.1e", n);
+}
+
+// +--------------------------------------------------------------------+
+
+void FormatNumberExp(char* txt, double n)
+{
+ double a = fabs(n);
+
+ if (a < 100e3)
+ sprintf(txt, "%d", (int) (n));
+
+ else
+ sprintf(txt, "%.1e", n);
+}
+
+// +--------------------------------------------------------------------+
+
+const int MINUTE = 60;
+const int HOUR = 60 * MINUTE;
+const int DAY = 24 * HOUR;
+
+void FormatTime(char* txt, double time)
+{
+ int t = (int) time;
+
+ int h = (t / HOUR);
+ int m = ((t - h*HOUR) / MINUTE);
+ int s = ((t - h*HOUR - m*MINUTE));
+
+ if (h > 0)
+ sprintf(txt, "%02d:%02d:%02d", h,m,s);
+ else
+ sprintf(txt, "%02d:%02d", m,s);
+}
+
+// +--------------------------------------------------------------------+
+
+void FormatTimeOfDay(char* txt, double time)
+{
+ int t = (int) time;
+
+ if (t >= DAY) {
+ int d = t / DAY;
+ t -= d * DAY;
+ }
+
+ int h = (t / HOUR);
+ int m = ((t - h*HOUR) / MINUTE);
+ int s = ((t - h*HOUR - m*MINUTE));
+
+ sprintf(txt, "%02d:%02d:%02d", h,m,s);
+}
+
+// +--------------------------------------------------------------------+
+
+void FormatDayTime(char* txt, double time, bool short_format)
+{
+ int t = (int) time;
+ int d = 1, h = 0, m = 0, s = 0;
+
+ if (t >= DAY) {
+ d = t / DAY;
+ t -= d * DAY;
+ d++;
+ }
+
+ if (t >= HOUR) {
+ h = t / HOUR;
+ t -= h * HOUR;
+ }
+
+ if (t >= MINUTE) {
+ m = t / MINUTE;
+ t -= m * MINUTE;
+ }
+
+ s = t;
+
+ if (short_format)
+ sprintf(txt, "%02d/%02d:%02d:%02d", d, h, m, s);
+ else
+ sprintf(txt, "Day %02d %02d:%02d:%02d", d, h, m, s);
+}
+
+// +--------------------------------------------------------------------+
+
+void FormatDay(char* txt, double time)
+{
+ int t = (int) time;
+ int d = 1, h = 0, m = 0, s = 0;
+
+ if (t >= DAY) {
+ d = t / DAY;
+ t -= d * DAY;
+ d++;
+ }
+
+ sprintf(txt, "Day %02d", d);
+}
+
+// +--------------------------------------------------------------------+
+
+void FormatPoint(char* txt, const Point& p)
+{
+ char x[16];
+ char y[16];
+ char z[16];
+
+ FormatNumber(x, p.x);
+ FormatNumber(y, p.y);
+ FormatNumber(z, p.z);
+
+ sprintf(txt, "(%s, %s, %s)", x, y, z);
+}
+
+// +--------------------------------------------------------------------+
+
+Text FormatTimeString(int utc)
+{
+ static const char* month[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ static const char* meridian[2] = { "AM", "PM" };
+
+ if (utc < 1)
+ utc = (int) time(0);
+
+ time_t aclock = utc; // Get time in seconds
+ struct tm *t = localtime(&aclock); // Convert time to struct tm form
+
+ char buffer[256];
+ sprintf(buffer, "%d %s %d, %2d:%02d:%02d %s",
+ t->tm_mday, month[t->tm_mon], 1900 + t->tm_year,
+ t->tm_hour > 12 ? t->tm_hour-12 : t->tm_hour,
+ t->tm_min, t->tm_sec, meridian[t->tm_hour > 12]);
+
+ return buffer;
+}
+
+// +--------------------------------------------------------------------+
+
+static char safe_str[2048];
+
+const char* SafeString(const char* s)
+{
+ ZeroMemory(safe_str, sizeof(safe_str));
+
+ if (s && *s) {
+ int len = strlen(s);
+ int n = 0;
+
+ for (int i = 0; i < len; i++) {
+ char c = s[i];
+
+ if (c == '\n') {
+ safe_str[n++] = '\\';
+ safe_str[n++] = 'n';
+ }
+
+ else if (c == '\t') {
+ safe_str[n++] = '\\';
+ safe_str[n++] = 't';
+ }
+
+ else if (c == '"') {
+ safe_str[n++] = '\'';
+ }
+
+ else if (c == '\\' && i < len-1) {
+ safe_str[n++] = s[i++];
+ safe_str[n++] = s[i++];
+ }
+
+ else if (c < 32 || c > 126) {
+ // non printing characters
+ }
+
+ else {
+ safe_str[n++] = c;
+ }
+
+ if (n > 2040)
+ break;
+ }
+ }
+
+ return safe_str;
+}
+
+// +--------------------------------------------------------------------+
+
+const char* SafeQuotes(const char* msg)
+{
+ int dst = 0;
+
+ if (msg) {
+ while (*msg && dst < 254) {
+ if (*msg == '"') {
+ safe_str[dst++] = '\'';
+ msg++;
+ }
+ else if (isspace(*msg)) {
+ safe_str[dst++] = ' ';
+ msg++;
+ }
+ else {
+ safe_str[dst++] = *msg++;
+ }
+ }
+ }
+
+ safe_str[dst] = 0;
+ return safe_str;
+}
+
+// +--------------------------------------------------------------------+
+
+Text FormatTextReplace(const char* msg, const char* tgt, const char* val)
+{
+ if (!msg || !tgt || !val)
+ return "";
+
+ if (!strchr(msg, *tgt))
+ return msg;
+
+ Text result;
+ char* buffer = new char[strlen(msg) + 1];
+ const char* p = msg;
+ char* q = buffer;
+ int tgtlen = strlen(tgt);
+
+ while (*p) {
+ if (!strncmp(p, tgt, tgtlen)) {
+ p += tgtlen;
+ *q = 0;
+ q = buffer;
+
+ result += buffer;
+ result += val;
+ }
+
+ else {
+ *q++ = *p++;
+ }
+ }
+
+ if (q != buffer) {
+ *q = 0;
+ result += buffer;
+ }
+
+ delete [] buffer;
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+Text FormatTextEscape(const char* msg)
+{
+ if (!msg)
+ return "";
+
+ if (!strchr(msg, '\\'))
+ return msg;
+
+ Text result;
+ char* buffer = new char[strlen(msg) + 1];
+ const char* p = msg;
+ char* q = buffer;
+
+ while (*p) {
+ if (*p == '\\') {
+ p++;
+
+ if (*p == 'n') {
+ *q++ = '\n';
+ p++;
+ }
+
+ else if (*p == 't') {
+ *q++ = '\t';
+ p++;
+ }
+
+ else {
+ *q++ = *p++;
+ }
+ }
+
+ else {
+ *q++ = *p++;
+ }
+ }
+
+ *q = 0;
+ result = buffer;
+ delete [] buffer;
+ return result;
+}
diff --git a/nGenEx/FormatUtil.h b/nGenEx/FormatUtil.h
new file mode 100644
index 0000000..5119543
--- /dev/null
+++ b/nGenEx/FormatUtil.h
@@ -0,0 +1,47 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FormatUtil.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Text formatting utilities
+*/
+
+#ifndef FormatUtil_h
+#define FormatUtil_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+void FormatNumber(char* txt, double n);
+void FormatNumberExp(char* txt, double n);
+void FormatTime(char* txt, double seconds);
+void FormatTimeOfDay(char* txt, double seconds);
+void FormatDayTime(char* txt, double seconds, bool short_format=false);
+void FormatDay(char* txt, double seconds);
+void FormatPoint(char* txt, const Point& p);
+Text FormatTimeString(int utc=0);
+
+const char* SafeString(const char* s);
+const char* SafeQuotes(const char* s);
+
+// scan msg and replace all occurrences of tgt with val
+// return new result, leave msg unmodified
+Text FormatTextReplace(const char* msg, const char* tgt, const char* val);
+
+// scan msg and replace all C-style \x escape sequences
+// with their single-character values, leave orig unmodified
+Text FormatTextEscape(const char* msg);
+
+// +--------------------------------------------------------------------+
+
+#endif FormatUtil_h
+
diff --git a/nGenEx/Game.cpp b/nGenEx/Game.cpp
new file mode 100644
index 0000000..f297955
--- /dev/null
+++ b/nGenEx/Game.cpp
@@ -0,0 +1,1574 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Game.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include "MemDebug.h"
+#include "Game.h"
+#include "Mouse.h"
+#include "Universe.h"
+#include "Screen.h"
+#include "Window.h"
+#include "EventDispatch.h"
+#include "Color.h"
+#include "DataLoader.h"
+#include "Keyboard.h"
+#include "Pcx.h"
+#include "Resource.h"
+#include "Bitmap.h"
+#include "MachineInfo.h"
+#include "Video.h"
+#include "VideoFactory.h"
+#include "VideoSettings.h"
+#include "AviFile.h"
+#include "ContentBundle.h"
+
+// +--------------------------------------------------------------------+
+
+FILE* ErrLog = 0;
+int ErrLine = 0;
+char ErrBuf[256];
+
+Game* game = 0;
+
+bool Game::active = false;
+bool Game::paused = false;
+bool Game::server = false;
+bool Game::show_mouse = false;
+DWORD Game::base_game_time = 0;
+DWORD Game::real_time = 0;
+DWORD Game::game_time = 0;
+DWORD Game::time_comp = 1;
+DWORD Game::frame_number = 0;
+
+const int VIDEO_FPS = 30;
+const double MAX_FRAME_TIME_VIDEO = 1.0 / (double) VIDEO_FPS;
+const double MAX_FRAME_TIME_NORMAL = 1.0 / 5.0;
+const double MIN_FRAME_TIME_NORMAL = 1.0 / 60.0;
+
+double Game::max_frame_length = MAX_FRAME_TIME_NORMAL;
+double Game::min_frame_length = MIN_FRAME_TIME_NORMAL;
+
+char Game::panicbuf[256];
+
+static LARGE_INTEGER perf_freq;
+static LARGE_INTEGER perf_cnt1;
+static LARGE_INTEGER perf_cnt2;
+
+// +--------------------------------------------------------------------+
+
+Game::Game()
+ : world(0), video_factory(0), video(0), video_settings(0), soundcard(0),
+ gamma(128), max_tex_size(2048), screen(0), totaltime(0),
+ hInst(0), hwnd(0), frame_rate(0), frame_count(0), frame_count0(0),
+ frame_time(0), frame_time0(0), gui_seconds(0), content(0),
+ status(Game::OK), exit_code(0), window_style(0), avi_file(0)
+{
+ if (!game) {
+ panicbuf[0] = 0;
+ game = this;
+ ZeroMemory(ErrBuf, 256);
+
+ video_settings = new(__FILE__,__LINE__) VideoSettings;
+
+ is_windowed = false;
+ is_active = false;
+ is_device_lost = false;
+ is_minimized = false;
+ is_maximized = false;
+ ignore_size_change = false;
+ is_device_initialized = false;
+ is_device_restored = false;
+ }
+ else
+ status = TOO_MANY;
+}
+
+Game::~Game()
+{
+ if (game == this)
+ game = 0;
+
+ delete content;
+ delete world;
+ delete screen;
+ delete video_factory;
+ delete video;
+ delete soundcard;
+ delete video_settings;
+ delete avi_file;
+
+ if (status == EXIT)
+ ShowStats();
+}
+
+// +--------------------------------------------------------------------+
+
+HINSTANCE Game::GetHINST()
+{
+ if (game)
+ return game->hInst;
+
+ return 0;
+}
+
+HWND Game::GetHWND()
+{
+ if (game)
+ return game->hwnd;
+
+ return 0;
+}
+
+bool Game::IsWindowed()
+{
+ if (game)
+ return game->is_windowed;
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+Game::GetText(const char* key)
+{
+ if (game && game->content && game->content->IsLoaded())
+ return game->content->GetText(key);
+
+ return key;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Game::GammaLevel()
+{
+ if (game)
+ return game->gamma;
+
+ return 0;
+}
+
+void
+Game::SetGammaLevel(int g)
+{
+ if (game) {
+ game->gamma = g;
+
+ if (game->video)
+ game->video->SetGammaLevel(g);
+ }
+}
+
+int
+Game::MaxTexSize()
+{
+ if (game && game->video) {
+ int max_vid_size = game->video->MaxTexSize();
+ return max_vid_size < game->max_tex_size ?
+ max_vid_size : game->max_tex_size;
+ }
+ else if (Video::GetInstance()) {
+ return Video::GetInstance()->MaxTexSize();
+ }
+
+ return 256;
+}
+
+int
+Game::MaxTexAspect()
+{
+ if (game && game->video) {
+ return game->video->MaxTexAspect();
+ }
+ else if (Video::GetInstance()) {
+ return Video::GetInstance()->MaxTexAspect();
+ }
+
+ return 1;
+}
+
+void
+Game::SetMaxTexSize(int n)
+{
+ if (game && n >= 64 && n <= 4096)
+ game->max_tex_size = n;
+}
+
+bool
+Game::DisplayModeSupported(int w, int h, int bpp)
+{
+ return game && game->video && game->video->IsModeSupported(w,h,bpp);
+}
+
+double
+Game::FrameRate()
+{
+ if (game)
+ return game->frame_rate;
+
+ return 0;
+}
+
+double
+Game::FrameTime()
+{
+ if (game)
+ return game->seconds;
+
+ return 0;
+}
+
+double
+Game::GUITime()
+{
+ if (game)
+ return game->gui_seconds;
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::Init(HINSTANCE hi, HINSTANCE hpi, LPSTR cmdline, int nCmdShow)
+{
+ status = OK;
+ hInst = hi;
+
+ Print(" Initializing Game\n");
+
+ stats.Clear();
+
+ if (!InitApplication(hInst)) { // Initialize shared things
+ Panic("Could not initialize the application.");
+ status = INIT_FAILED;
+ }
+
+ if (status == OK && !video_settings) {
+ Panic("No video settings specified");
+ status = INIT_FAILED;
+ }
+
+ if (status == OK) {
+ if (MachineInfo::GetPlatform() == MachineInfo::OS_WINNT) {
+ Panic(" D3D not available under WinNT 4");
+ status = INIT_FAILED;
+ }
+
+ else if (MachineInfo::GetDirectXVersion() < MachineInfo::DX_9) {
+ Panic(" Insufficient DirectX detected (Dx9 IS REQUIRED)");
+ status = INIT_FAILED;
+ }
+
+ Print(" Gamma Level = %d\n", gamma);
+ }
+
+ if (status == OK) {
+ Print("\n Initializing instance...\n");
+ // Perform initializations that apply to a specific instance
+ if (!InitInstance(hInst, nCmdShow)) {
+ Panic("Could not initialize the instance.");
+ status = INIT_FAILED;
+ }
+ }
+
+ if (status == OK) {
+ Print(" Initializing content...\n");
+ InitContent();
+ }
+
+ if (status == OK) {
+ Print(" Initializing game...\n");
+ if (!InitGame()) {
+ if (!panicbuf[0])
+ Panic("Could not initialize the game.");
+ status = INIT_FAILED;
+ }
+ }
+
+ return status == OK;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::InitApplication(HINSTANCE hInstance)
+{
+ WNDCLASS wc;
+ LOGBRUSH brush = { BS_SOLID, RGB(0,0,0), 0 };
+
+ if (server)
+ brush.lbColor = RGB(255,255,255);
+
+ // Fill in window class structure with parameters that
+ // describe the main window.
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.lpfnWndProc = (WNDPROC) WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(100));
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+
+ wc.hbrBackground = CreateBrushIndirect(&brush);
+ wc.lpszMenuName = app_name;
+ wc.lpszClassName = app_name;
+
+ // Register the window class and return success/failure code.
+ if (RegisterClass(&wc) == 0) {
+ DWORD err = GetLastError();
+
+ if (err == 1410) // class already exists, this is OK
+ return true;
+
+ else
+ Print("WARNING: Register Window Class: %08x\n", err);
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::InitInstance(HINSTANCE hInstance, int nCmdShow)
+{
+ hInst = hInstance;
+
+ // initialize the game timer:
+ base_game_time = 0;
+ QueryPerformanceFrequency(&perf_freq);
+ QueryPerformanceCounter(&perf_cnt1);
+
+ // center window on display:
+ int screenx = GetSystemMetrics(SM_CXSCREEN);
+ int screeny = GetSystemMetrics(SM_CYSCREEN);
+ int x_offset = 0;
+ int y_offset = 0;
+ int s_width = 800;
+ int s_height = 600;
+
+ if (server) {
+ s_width = 320;
+ s_height = 200;
+ }
+
+ else if (video_settings) {
+ s_width = video_settings->window_width;
+ s_height = video_settings->window_height;
+ }
+
+ if (s_width < screenx)
+ x_offset = (screenx - s_width) / 2;
+
+ if (s_height < screeny)
+ y_offset = (screeny - s_height) / 2;
+
+ // Create a main window for this application instance
+ RECT rctmp;
+ rctmp.left = x_offset;
+ rctmp.top = y_offset;
+ rctmp.right = x_offset + s_width;
+ rctmp.bottom = y_offset + s_height;
+
+ window_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME |
+ WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE;
+
+ AdjustWindowRect(&rctmp, window_style, 1);
+
+ hwnd = CreateWindow(
+ app_name, // Class name
+ app_name, // Caption
+
+ window_style,
+
+ x_offset, // Position
+ y_offset,
+
+ rctmp.right - rctmp.left, // Size
+ rctmp.bottom - rctmp.top,
+
+ 0, // Parent window (no parent)
+ 0, // use class menu
+ hInst, // handle to window instance
+ 0); // no params to pass on
+
+ // If window could not be created, return "failure"
+ if (!hwnd) {
+ Panic("Could not create window\n");
+ return false;
+ }
+
+ Print(" Window created.\n");
+
+ // Make the window visible and draw it
+ ShowWindow(hwnd, nCmdShow); // Show the window
+ UpdateWindow(hwnd); // Sends WM_PAINT message
+
+ // Save window properties
+ window_style = GetWindowLong(hwnd, GWL_STYLE);
+ GetWindowRect(hwnd, &bounds_rect);
+ GetClientRect(hwnd, &client_rect);
+
+ // Use client area to set video window size
+ video_settings->window_width = client_rect.right - client_rect.left;
+ video_settings->window_height = client_rect.bottom - client_rect.top;
+
+ Print(" Instance initialized.\n");
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::InitVideo()
+{
+ if (server) return true;
+
+ // create a video factory, and video object:
+ video_factory = new(__FILE__,__LINE__) VideoFactory(hwnd);
+
+ if (video_factory) {
+ Print(" Init Video...\n");
+ Print(" Request %s mode\n", video_settings->GetModeDescription());
+
+ video = video_factory->CreateVideo(video_settings);
+
+ if (video) {
+ if (!video->IsHardware()) {
+ video_factory->DestroyVideo(video);
+ video = 0;
+
+ Panic("3D Hardware Not Found");
+ }
+
+ // save a copy of the device-specific video settings:
+ else if (video->GetVideoSettings()) {
+ *video_settings = *video->GetVideoSettings();
+ is_windowed = video_settings->IsWindowed();
+ }
+ }
+
+ soundcard = video_factory->CreateSoundCard();
+ }
+
+ return (video && video->Status() == Video::VIDEO_OK);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::ResetVideo()
+{
+ if (server) return true;
+ if (!video_factory) return InitVideo();
+
+ Print(" Reset Video...\n");
+ Print(" Request %s mode\n", video_settings->GetModeDescription());
+
+ delete screen;
+
+ if (video && !video->Reset(video_settings)) {
+ video_factory->DestroyVideo(video);
+ video = video_factory->CreateVideo(video_settings);
+ }
+
+ if (!video || video->Status() != Video::VIDEO_OK) {
+ Panic("Could not re-create Video Interface.");
+ return false;
+ }
+
+ Print(" Re-created video object.\n");
+
+ // save a copy of the device-specific video settings:
+ if (video->GetVideoSettings()) {
+ *video_settings = *video->GetVideoSettings();
+ is_windowed = video_settings->IsWindowed();
+ }
+
+ Color::UseVideo(video);
+
+ screen = new(__FILE__,__LINE__) Screen(video);
+ if (!screen) {
+ Panic("Could not re-create Screen object.");
+ return false;
+ }
+
+ Print(" Re-created screen object.\n");
+
+ if (!screen->SetBackgroundColor(Color::Black))
+ Print(" WARNING: could not set video background color to Black\n");
+
+ screen->ClearAllFrames(true);
+ video->SetGammaLevel(gamma);
+
+ Print(" Re-established requested video parameters.\n");
+
+ Bitmap::CacheUpdate();
+ Print(" Refreshed texture bitmaps.\n\n");
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::ResizeVideo()
+{
+ if (!video || !video_settings) return false;
+ if (!is_windowed) return false;
+ if (ignore_size_change) return true;
+
+ HRESULT hr = S_OK;
+ RECT client_old;
+
+ client_old = client_rect;
+
+ // Update window properties
+ GetWindowRect(hwnd, &bounds_rect);
+ GetClientRect(hwnd, &client_rect);
+
+ if (client_old.right - client_old.left !=
+ client_rect.right - client_rect.left ||
+ client_old.bottom - client_old.top !=
+ client_rect.bottom - client_rect.top) {
+
+ // A new window size will require a new backbuffer
+ // size, so the 3D structures must be changed accordingly.
+ Pause(true);
+
+ video_settings->is_windowed = true;
+ video_settings->window_width = client_rect.right - client_rect.left;
+ video_settings->window_height = client_rect.bottom - client_rect.top;
+
+ ::Print("ResizeVideo() %d x %d\n", video_settings->window_width, video_settings->window_height);
+
+ if (video) {
+ video->Reset(video_settings);
+ }
+
+ Pause(false);
+ }
+
+ // save a copy of the device-specific video settings:
+ if (video->GetVideoSettings()) {
+ *video_settings = *video->GetVideoSettings();
+ is_windowed = video_settings->IsWindowed();
+ }
+
+ screen->Resize(video_settings->window_width, video_settings->window_height);
+
+ return hr == S_OK;
+}
+
+bool
+Game::ToggleFullscreen()
+{
+ bool result = false;
+
+ if (video && video_settings) {
+ Pause(true);
+ ignore_size_change = true;
+
+ // Toggle the windowed state
+ is_windowed = !is_windowed;
+ video_settings->is_windowed = is_windowed;
+
+ // Prepare window for windowed/fullscreen change
+ AdjustWindowForChange();
+
+ // Reset the 3D device
+ if (!video->Reset(video_settings)) {
+ // reset failed, try to restore...
+ ignore_size_change = false;
+
+ if (!is_windowed) {
+ // Restore window type to windowed mode
+ is_windowed = !is_windowed;
+ video_settings->is_windowed = is_windowed;
+
+ AdjustWindowForChange();
+
+ SetWindowPos(hwnd,
+ HWND_NOTOPMOST,
+ bounds_rect.left,
+ bounds_rect.top,
+ bounds_rect.right - bounds_rect.left,
+ bounds_rect.bottom - bounds_rect.top,
+ SWP_SHOWWINDOW);
+ }
+
+ ::Print("Unable to toggle %s fullscreen mode.\n", is_windowed ? "into" : "out of");
+ }
+
+ else {
+ ignore_size_change = false;
+
+ // When moving from fullscreen to windowed mode, it is important to
+ // adjust the window size after resetting the device rather than
+ // beforehand to ensure that you get the window size you want. For
+ // example, when switching from 640x480 fullscreen to windowed with
+ // a 1000x600 window on a 1024x768 desktop, it is impossible to set
+ // the window size to 1000x600 until after the display mode has
+ // changed to 1024x768, because windows cannot be larger than the
+ // desktop.
+
+ if (is_windowed) {
+ SetWindowPos(hwnd,
+ HWND_NOTOPMOST,
+ bounds_rect.left,
+ bounds_rect.top,
+ bounds_rect.right - bounds_rect.left,
+ bounds_rect.bottom - bounds_rect.top,
+ SWP_SHOWWINDOW);
+ }
+
+ GetClientRect(hwnd, &client_rect); // Update our copy
+ Pause(false);
+
+ if (is_windowed)
+ screen->Resize(video_settings->window_width,
+ video_settings->window_height);
+
+ else
+ screen->Resize(video_settings->fullscreen_mode.width,
+ video_settings->fullscreen_mode.height);
+
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+bool
+Game::AdjustWindowForChange()
+{
+ if (is_windowed) {
+ // Set windowed-mode style
+ SetWindowLong(hwnd, GWL_STYLE, window_style);
+ if (hmenu != NULL) {
+ SetMenu(hwnd, hmenu);
+ hmenu = NULL;
+ }
+ }
+ else {
+ // Set fullscreen-mode style
+ SetWindowLong(hwnd, GWL_STYLE, WS_POPUP|WS_SYSMENU|WS_VISIBLE);
+ if (hmenu == NULL) {
+ hmenu = GetMenu(hwnd);
+ SetMenu(hwnd, NULL);
+ }
+ }
+
+ return true;
+}
+
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::InitGame()
+{
+ if (server) {
+ Print(" InitGame() - server mode.\n");
+ }
+
+ else {
+ if (!SetupPalette()) {
+ Panic("Could not set up the palette.");
+ return false;
+ }
+
+ Print(" Palette loaded.\n");
+
+ if (!InitVideo() || !video || video->Status() != Video::VIDEO_OK) {
+ if (!panicbuf[0])
+ Panic("Could not create the Video Interface.");
+ return false;
+ }
+
+ Print(" Created video object.\n");
+
+ Color::UseVideo(video);
+
+ screen = new(__FILE__,__LINE__) Screen(video);
+ if (!screen) {
+ if (!panicbuf[0])
+ Panic("Could not create the Screen object.");
+ return false;
+ }
+
+ Print(" Created screen object.\n");
+
+ if (!screen->SetBackgroundColor(Color::Black))
+ Print(" WARNING: could not set video background color to Black\n");
+ screen->ClearAllFrames(true);
+
+ video->SetGammaLevel(gamma);
+
+ Print(" Established requested video parameters.\n\n");
+ }
+
+ return true;
+}
+
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::InitContent()
+{
+ DataLoader* loader = DataLoader::GetLoader();
+ List<Text> bundles;
+
+ loader->SetDataPath("Content/");
+ loader->ListFiles("content*", bundles);
+
+ ListIter<Text> iter = bundles;
+ while (++iter) {
+ Text* filename = iter.value();
+ int n = filename->indexOf('_');
+
+ if (n > 0) {
+ Locale::ParseLocale(filename->data() + n);
+ }
+ else {
+ delete content;
+ content = new(__FILE__,__LINE__) ContentBundle("content", 0);
+ }
+ }
+
+ loader->SetDataPath(0);
+ return true;
+}
+
+void
+Game::UseLocale(Locale* locale)
+{
+ if (game) {
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Content/");
+ delete game->content;
+
+ game->content = new(__FILE__,__LINE__) ContentBundle("content", locale);
+
+ loader->SetDataPath(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::SetupPalette()
+{
+ if (LoadPalette(standard_palette, inverse_palette)) {
+ Color::SetPalette(standard_palette, 256, inverse_palette);
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::LoadPalette(PALETTEENTRY* pal, BYTE* inv)
+{
+ char palheader[32];
+ struct {
+ WORD Version;
+ WORD NumberOfEntries;
+ PALETTEENTRY Entries[256];
+
+ } Palette = { 0x300, 256 };
+
+ DataLoader* loader = DataLoader::GetLoader();
+ BYTE* block;
+ char fname[256];
+ sprintf(fname, "%s.pal", palette_name);
+
+ if (!loader->LoadBuffer(fname, block)) {
+ Print(" Could not open file '%s'\n", fname);
+ return false;
+ }
+
+ memcpy(&palheader, block, 24);
+ memcpy((char*) Palette.Entries, (block+24), 256*4);
+
+ for (int i = 0; i < 256; i++) {
+ *pal++ = Palette.Entries[i];
+ }
+
+ loader->ReleaseBuffer(block);
+
+ sprintf(fname, "%s.ipl", palette_name);
+ int size = loader->LoadBuffer(fname, block);
+ if (size < 32768) {
+ Print(" Could not open file '%s'\n", fname);
+ return false;
+ }
+
+ memcpy(inv, block, 32768);
+ loader->ReleaseBuffer(block);
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Game::Run()
+{
+ MSG msg;
+
+ status = RUN;
+ Print("\n");
+ Print("+====================================================================+\n");
+ Print("| RUN |\n");
+ Print("+====================================================================+\n");
+
+ // Polling messages from event queue until quit
+ while (status < EXIT) {
+ if (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) {
+ if (msg.message == WM_QUIT)
+ break;
+
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ else {
+ if (ProfileGameLoop())
+ WaitMessage();
+ }
+ }
+
+ return exit_code ? exit_code : msg.wParam;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::Exit()
+{
+ Print("\n\n*** Game::Exit()\n");
+ status = EXIT;
+}
+
+void
+Game::Panic(const char* msg)
+{
+ if (msg) Print("*** PANIC: %s\n", msg);
+ else Print("*** PANIC! ***\n");
+
+ if (!msg) msg = "Unspecified fatal error.";
+ sprintf(panicbuf, "%s\nThis game will now terminate.", msg);
+
+ if (game) {
+ game->status = PANIC;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::Activate(bool f)
+{
+ active = f;
+
+ if (active && video)
+ video->InvalidateCache();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::Pause(bool f)
+{
+ if (f) {
+ if (soundcard)
+ soundcard->Pause();
+ paused = true;
+ }
+ else {
+ if (soundcard)
+ soundcard->Resume();
+ paused = false;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool ProfileGameLoop(void)
+{
+ return game->GameLoop();
+}
+
+bool
+Game::GameLoop()
+{
+ bool wait_for_windows_events = true;
+
+ if (active && !paused) {
+ if (!server) {
+ // Route Events to EventTargets
+ EventDispatch* ed = EventDispatch::GetInstance();
+ if (ed)
+ ed->Dispatch();
+ }
+
+ UpdateWorld();
+ GameState();
+
+ if (!server) {
+ UpdateScreen();
+ CollectStats();
+ }
+
+ wait_for_windows_events = false;
+ }
+ else if (active && paused) {
+ if (GetKey()=='P')
+ Pause(false);
+ }
+
+ QueryPerformanceCounter(&perf_cnt2);
+
+ double freq = (double) (perf_freq.QuadPart);
+ double msec = (double) (perf_cnt2.QuadPart - perf_cnt1.QuadPart);
+
+ msec /= freq;
+ msec *= 1000.0;
+
+ if (msec < 1)
+ msec = 1;
+
+ real_time += (DWORD) msec;
+
+ frame_number++;
+ Mouse::w = 0;
+
+ perf_cnt1 = perf_cnt2;
+
+ return wait_for_windows_events;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::UpdateWorld()
+{
+ long new_time = real_time;
+ double delta = new_time - frame_time;
+ gui_seconds = delta * 0.001;
+ seconds = max_frame_length;
+
+ if (time_comp == 1)
+ {
+ if (delta < max_frame_length * 1000)
+ seconds = delta * 0.001;
+ }
+ else
+ {
+ seconds = time_comp * delta * 0.001;
+ }
+
+ frame_time = new_time;
+ game_time += (DWORD) (seconds * 1000);
+
+ if (world)
+ world->ExecFrame(seconds);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::GameState()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::UpdateScreen()
+{
+ if (!screen || !video) return;
+
+ if (screen->Refresh()) {
+ if (Keyboard::KeyDown(VK_F12)) {
+ if (Keyboard::KeyDown(VK_SHIFT)) {
+ if (!avi_file) {
+ AVICapture(); // begin capturing
+ SetMaxFrameLength(MAX_FRAME_TIME_VIDEO);
+ }
+ else {
+ delete avi_file; // end capture;
+ avi_file = 0;
+ SetMaxFrameLength(MAX_FRAME_TIME_NORMAL);
+ }
+ }
+ else {
+ if (!avi_file) {
+ ScreenCapture();
+ }
+ else {
+ delete avi_file; // end capture;
+ avi_file = 0;
+ SetMaxFrameLength(MAX_FRAME_TIME_NORMAL);
+ }
+ }
+ }
+ else if (avi_file) {
+ AVICapture(); // continue capturing...
+ }
+
+ video->Present();
+ }
+ else {
+ Panic("Screen refresh failed.");
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Game*
+Game::GetInstance()
+{
+ return game;
+}
+
+Video*
+Game::GetVideo()
+{
+ if (game)
+ return game->video;
+
+ return 0;
+}
+
+Color
+Game::GetScreenColor()
+{
+ if (game)
+ return game->screen_color;
+
+ return Color::Black;
+}
+
+void
+Game::SetScreenColor(Color c)
+{
+ if (game) {
+ game->screen_color = c;
+
+ if (game->screen)
+ game->screen->SetBackgroundColor(c);
+ }
+}
+
+int
+Game::GetScreenWidth()
+{
+ if (game && game->video)
+ return game->video->Width();
+
+ return 0;
+}
+
+int
+Game::GetScreenHeight()
+{
+ if (game && game->video)
+ return game->video->Height();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::ScreenCapture(const char* name)
+{
+ if (server || !video || !screen) return;
+
+ static DWORD last_shot = 0;
+ static DWORD shot_num = 1;
+ DataLoader* loader = DataLoader::GetLoader();
+ char filename[256];
+
+ if (!name && (real_time - last_shot) < 1000)
+ return;
+
+ // try not to overwrite existing screen shots...
+ if (loader) {
+ bool use_file_sys = loader->IsFileSystemEnabled();
+ loader->UseFileSystem(true);
+ loader->SetDataPath(0);
+ List<Text> shot_list;
+ loader->ListFiles("*.PCX", shot_list);
+ loader->UseFileSystem(use_file_sys);
+
+ for (int i = 0; i < shot_list.size(); i++) {
+ Text* s = shot_list[i];
+ int n = 0;
+
+ sscanf(s->data()+1, "%d", &n);
+ if (shot_num <= (DWORD) n)
+ shot_num = n+1;
+ }
+
+ shot_list.destroy();
+ }
+
+ if (name)
+ strcpy(filename, name);
+ else
+ sprintf(filename, "A%d.PCX", shot_num++);
+
+ Bitmap bmp;
+
+ if (video && video->Capture(bmp)) {
+ PcxImage pcx(bmp.Width(), bmp.Height(), (LPDWORD) bmp.HiPixels());
+ pcx.Save((char*) filename);
+ }
+
+ last_shot = real_time;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::AVICapture(const char* name)
+{
+ if (server || !video || !screen) return;
+
+ if (!avi_file) {
+ char filename[256];
+ Bitmap bmp;
+ DataLoader* loader = DataLoader::GetLoader();
+ DWORD avi_num = 1;
+
+ // try not to overwrite existing screen shots...
+ if (loader) {
+ bool use_file_sys = loader->IsFileSystemEnabled();
+ loader->UseFileSystem(true);
+ loader->SetDataPath(0);
+ List<Text> avi_list;
+ loader->ListFiles("*.avi", avi_list);
+ loader->UseFileSystem(use_file_sys);
+
+ for (int i = 0; i < avi_list.size(); i++) {
+ Text* s = avi_list[i];
+ int n = 0;
+
+ sscanf(s->data()+1, "%d", &n);
+ if (avi_num <= (DWORD) n)
+ avi_num = n+1;
+ }
+
+ avi_list.destroy();
+ }
+
+ if (name)
+ strcpy(filename, name);
+ else
+ sprintf(filename, "A%d.avi", avi_num);
+
+ if (video && video->Capture(bmp)) {
+ //bmp.ScaleTo(bmp.Width()/2, bmp.Height()/2);
+ avi_file = new(__FILE__,__LINE__) AviFile(filename, Rect(0,0,bmp.Width(),bmp.Height()), VIDEO_FPS);
+ }
+
+ }
+
+ else {
+ Bitmap bmp;
+
+ if (video && video->Capture(bmp)) {
+ //bmp.ScaleTo(bmp.Width()/2, bmp.Height()/2);
+ avi_file->AddFrame(bmp);
+ }
+ }
+}
+
+
+
+// +--------------------------------------------------------------------+
+
+void
+Game::CollectStats()
+{
+ frame_count++;
+
+ if (!totaltime) totaltime = real_time;
+
+ if (frame_time - frame_time0 > 200) {
+ frame_rate = (frame_count - frame_count0) * 1000.0 / (frame_time - frame_time0);
+ frame_time0 = frame_time;
+ frame_count0 = frame_count;
+ }
+
+ if (video) {
+ stats.nframe = video->GetStats().nframe;
+ stats.nverts = video->GetStats().nverts;
+ stats.npolys = video->GetStats().npolys;
+ stats.nlines = video->GetStats().nlines;
+ stats.ncalls += video->GetStats().ncalls;
+
+ stats.total_verts += stats.nverts;
+ stats.total_polys += stats.npolys;
+ stats.total_lines += stats.nlines;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::ShowStats()
+{
+ if (server) return;
+
+ totaltime = real_time - totaltime;
+
+ Print("\n");
+ Print("Performance Data:\n");
+ Print("-----------------\n");
+
+ Print(" Time: %d msec\n", totaltime);
+ Print(" Frames: %d\n", stats.nframe);
+ Print(" Polys Rendered: %d\n", stats.total_polys);
+ Print(" Lines Rendered: %d\n", stats.total_lines);
+ Print(" Verts Rendered: %d\n", stats.total_verts);
+ Print(" Render Calls: %d\n", stats.ncalls);
+ Print("\n");
+
+ Print("Performance Statistics:\n");
+ Print("-----------------------\n");
+
+ Print(" Frames/Second: %.2f\n", (stats.nframe * 1000.0) / totaltime);
+ Print(" Polys/Frame: %.2f\n", (double) stats.total_polys / (double) stats.nframe);
+ Print(" Polys/Call: %.2f\n", (double) stats.total_polys / (double) stats.ncalls);
+ Print(" Polys/Second: %.2f\n", (stats.total_polys * 1000.0) / totaltime);
+ Print(" Lines/Second: %.2f\n", (stats.total_lines * 1000.0) / totaltime);
+ Print(" Verts/Second: %.2f\n", (stats.total_verts * 1000.0) / totaltime);
+
+ Print("\n");
+}
+
+// +====================================================================+
+// WndProc
+// +====================================================================+
+
+#ifndef WM_MOUSEWHEEL
+#define WM_MOUSEWHEEL 0x20A
+#endif
+
+LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM uParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_SYSKEYDOWN:
+ if (uParam == VK_TAB || uParam == VK_F4)
+ return DefWindowProc(hwnd, message, uParam, lParam);
+
+ return 0;
+
+ case WM_MENUCHAR:
+ return MNC_CLOSE << 16;
+
+ case WM_ACTIVATEAPP:
+ // Keep track of whether or not the app is in the foreground
+ if (game)
+ game->Activate(uParam?true:false);
+ break;
+
+ case WM_PAINT:
+ if (!game || !game->OnPaint())
+ return DefWindowProc(hwnd, message, uParam, lParam);
+ break;
+
+ case WM_SETCURSOR:
+ if (Game::ShowMouse()) {
+ return DefWindowProc(hwnd, message, uParam, lParam);
+ }
+ else {
+ // hide the windows mouse cursor
+ SetCursor(NULL);
+ return 1;
+ }
+ break;
+
+ case WM_ENTERSIZEMOVE:
+ // Halt frame movement while the app is sizing or moving
+ if (game)
+ game->Pause(true);
+ break;
+
+ case WM_SIZE:
+ // Pick up possible changes to window style due to maximize, etc.
+ if (game && game->hwnd != NULL ) {
+ game->window_style = GetWindowLong(game->hwnd, GWL_STYLE );
+
+ if (uParam == SIZE_MINIMIZED) {
+ game->Pause(true); // Pause while we're minimized
+ game->is_minimized = true;
+ game->is_maximized = false;
+ }
+
+ else if (uParam == SIZE_MAXIMIZED) {
+ if (game->is_minimized)
+ game->Pause(false); // Unpause since we're no longer minimized
+
+ game->is_minimized = false;
+ game->is_maximized = true;
+ game->ResizeVideo();
+ }
+
+ else if (uParam == SIZE_RESTORED) {
+ if (game->is_maximized) {
+ game->is_maximized = false;
+ game->ResizeVideo();
+ }
+
+ else if (game->is_minimized) {
+ game->Pause(false); // Unpause since we're no longer minimized
+ game->is_minimized = false;
+ game->ResizeVideo();
+ }
+ else {
+ // If we're neither maximized nor minimized, the window size
+ // is changing by the user dragging the window edges. In this
+ // case, we don't reset the device yet -- we wait until the
+ // user stops dragging, and a WM_EXITSIZEMOVE message comes.
+ }
+ }
+ }
+ break;
+
+ case WM_EXITSIZEMOVE:
+ if (game) {
+ game->Pause(false);
+ game->ResizeVideo();
+ }
+ break;
+
+
+ case WM_ENTERMENULOOP:
+ if (game)
+ game->Pause(true);
+ break;
+
+ case WM_EXITMENULOOP:
+ if (game)
+ game->Pause(false);
+ break;
+
+ /*
+ case WM_HELP:
+ if (game)
+ return game->OnHelp();
+ break;
+ */
+
+ case WM_KEYDOWN:
+ BufferKey(uParam);
+ return 0;
+
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+
+ case WM_MOUSEMOVE:
+ Mouse::x = LOWORD(lParam);
+ Mouse::y = HIWORD(lParam);
+ break;
+
+ case WM_LBUTTONDOWN:
+ Mouse::l = 1;
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ Mouse::l = 2;
+ break;
+
+ case WM_LBUTTONUP:
+ Mouse::l = 0;
+ break;
+
+ case WM_MBUTTONDOWN:
+ Mouse::m = 1;
+ break;
+
+ case WM_MBUTTONDBLCLK:
+ Mouse::m = 2;
+ break;
+
+ case WM_MBUTTONUP:
+ Mouse::m = 0;
+ break;
+
+ case WM_RBUTTONDOWN:
+ Mouse::r = 1;
+ break;
+
+ case WM_RBUTTONDBLCLK:
+ Mouse::r = 2;
+ break;
+
+ case WM_RBUTTONUP:
+ Mouse::r = 0;
+ break;
+
+ case WM_MOUSEWHEEL:
+ {
+ int w = (int) (uParam >> 16);
+ if (w > 32000) w -= 65536;
+ Mouse::w += w;
+ }
+ break;
+
+ case WM_CLOSE:
+ if (game) // && game->Server())
+ game->Exit();
+ break;
+
+ default:
+ return DefWindowProc(hwnd, message, uParam, lParam);
+ }
+
+ return 0;
+}
+
+// +====================================================================+
+
+const int MAX_KEY_BUF = 512;
+static int vkbuf[MAX_KEY_BUF];
+static int vkshiftbuf[MAX_KEY_BUF];
+static int vkins = 0;
+static int vkext = 0;
+
+void
+FlushKeys()
+{
+ Keyboard::FlushKeys();
+ vkins = vkext = 0;
+}
+
+void
+BufferKey(int vkey)
+{
+ if (vkey < 1) return;
+
+ int shift = 0;
+
+ if (GetAsyncKeyState(VK_SHIFT))
+ shift |= 1;
+
+ if (GetAsyncKeyState(VK_CONTROL))
+ shift |= 2;
+
+ if (GetAsyncKeyState(VK_MENU))
+ shift |= 4;
+
+ vkbuf[vkins] = vkey;
+ vkshiftbuf[vkins++] = shift;
+
+ if (vkins >= MAX_KEY_BUF)
+ vkins = 0;
+
+ if (vkins == vkext) {
+ vkext++;
+ if (vkext >= MAX_KEY_BUF)
+ vkext = 0;
+ }
+}
+
+int
+GetKey()
+{
+ if (vkins == vkext) return 0;
+
+ int result = vkbuf[vkext++];
+ if (vkext >= MAX_KEY_BUF)
+ vkext = 0;
+
+ return result;
+}
+
+int
+GetKeyPlus(int& key, int& shift)
+{
+ if (vkins == vkext) return 0;
+
+ key = vkbuf[vkext];
+ shift = vkshiftbuf[vkext++];
+
+ if (vkext >= MAX_KEY_BUF)
+ vkext = 0;
+
+ return key;
+}
+
+// +====================================================================+
+
+void Print(const char* fmt, ...)
+{
+ if (ErrLog) {
+ vsprintf(ErrBuf, fmt, (char *)(&fmt+1));
+
+ fprintf(ErrLog, ErrBuf);
+ fflush(ErrLog);
+ }
+}
+
+// +====================================================================+
+
+DWORD GetRealTime()
+{
+ if (game)
+ return Game::RealTime();
+
+ return timeGetTime();
+}
+
+DWORD Game::RealTime()
+{
+ return real_time;
+}
+
+DWORD Game::GameTime()
+{
+ return game_time;
+}
+
+DWORD Game::TimeCompression()
+{
+ return time_comp;
+}
+
+void Game::SetTimeCompression(DWORD comp)
+{
+ if (comp > 0 && comp <= 100)
+ time_comp = comp;
+}
+
+DWORD Game::Frame()
+{
+ return frame_number;
+}
+
+void Game::ResetGameTime()
+{
+ game_time = 0;
+}
+
+void Game::SkipGameTime(double seconds)
+{
+ if (seconds > 0)
+ game_time += (DWORD) (seconds * 1000);
+}
diff --git a/nGenEx/Game.h b/nGenEx/Game.h
new file mode 100644
index 0000000..ceec324
--- /dev/null
+++ b/nGenEx/Game.h
@@ -0,0 +1,220 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Game.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef Game_h
+#define Game_h
+
+#include "Types.h"
+#include "Screen.h"
+#include "Video.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+
+void FlushKeys();
+void BufferKey(int vkey);
+int GetKey();
+int GetKeyPlus(int& key, int& shift);
+
+extern "C" bool ProfileGameLoop(void);
+
+// +--------------------------------------------------------------------+
+
+class ContentBundle;
+class Locale;
+class Universe;
+class Sound;
+class SoundCard;
+class Video;
+class VideoFactory;
+class VideoSettings;
+class AviFile;
+class Text;
+
+// +--------------------------------------------------------------------+
+
+class Game
+{
+public:
+ static const char* TYPENAME() { return "Game"; }
+ enum STATUS { OK, RUN, EXIT, PANIC, INIT_FAILED, TOO_MANY };
+
+ Game();
+ virtual ~Game();
+
+ //
+ // MAIN GAME FUNCTIONALITY:
+ //
+
+ virtual bool Init(HINSTANCE hi, HINSTANCE hpi, LPSTR cmdline, int nCmdShow);
+ virtual int Run();
+ virtual void Exit();
+ virtual bool OnPaint() { return false; }
+ virtual bool OnHelp() { return false; }
+
+ virtual void Activate(bool f);
+ virtual void Pause(bool f);
+ int Status() const { return status; }
+
+ virtual void ScreenCapture(const char* name = 0);
+ virtual void AVICapture(const char* fname = 0);
+
+ const RenderStats& GetPolyStats() { return stats; }
+
+ //
+ // GENERAL GAME CLASS UTILITY METHODS:
+ //
+
+ static void Panic(const char* msg=0);
+ static bool DisplayModeSupported(int w, int h, int bpp);
+ static int MaxTexSize();
+ static int MaxTexAspect();
+ static int GammaLevel();
+ static void SetGammaLevel(int g);
+ static void SetMaxTexSize(int n);
+
+ static DWORD RealTime();
+ static DWORD GameTime();
+ static DWORD TimeCompression();
+ static void SetTimeCompression(DWORD comp);
+ static DWORD Frame();
+ static void ResetGameTime();
+ static void SkipGameTime(double seconds);
+
+ static double FrameRate();
+ static double FrameTime();
+ static double GUITime();
+ static void SetMaxFrameLength(double seconds) { max_frame_length = seconds; }
+ static void SetMinFrameLength(double seconds) { min_frame_length = seconds; }
+ static double GetMaxFrameLength() { return max_frame_length; }
+ static double GetMinFrameLength() { return min_frame_length; }
+
+ static Game* GetInstance();
+ static Video* GetVideo();
+ static Color GetScreenColor();
+ static void SetScreenColor(Color c);
+ static int GetScreenWidth();
+ static int GetScreenHeight();
+
+ static bool Active() { return active; }
+ static bool Paused() { return paused; }
+ static bool Server() { return server; }
+ static bool ShowMouse() { return show_mouse; }
+ static bool IsWindowed();
+
+ static HINSTANCE GetHINST();
+ static HWND GetHWND();
+
+ static void UseLocale(Locale* locale);
+ static Text GetText(const char* key);
+
+ static const char* GetPanicMessage() { return panicbuf; }
+
+ virtual bool GameLoop();
+ virtual void UpdateWorld();
+ virtual void GameState();
+ virtual void UpdateScreen();
+ virtual void CollectStats();
+
+ virtual bool InitApplication(HINSTANCE);
+ virtual bool InitInstance(HINSTANCE, int);
+ virtual bool InitContent();
+ virtual bool InitGame();
+ virtual bool InitVideo();
+ virtual bool ResizeVideo();
+ virtual bool ResetVideo();
+ virtual bool ToggleFullscreen();
+ virtual bool AdjustWindowForChange();
+
+ virtual bool SetupPalette();
+ virtual bool LoadPalette(PALETTEENTRY* pal, BYTE* inv);
+ virtual void ShowStats();
+
+protected:
+ friend bool ProfileGameLoop(void);
+ friend LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM uParam, LPARAM lParam);
+
+ ContentBundle* content;
+ Universe* world;
+ VideoFactory* video_factory;
+ Video* video;
+ VideoSettings* video_settings;
+ SoundCard* soundcard;
+ Screen* screen;
+ int gamma;
+ int max_tex_size;
+
+ RenderStats stats;
+ DWORD totaltime;
+
+ PALETTEENTRY standard_palette[256];
+ BYTE inverse_palette[32768];
+
+ HINSTANCE hInst;
+ HWND hwnd;
+ HMENU hmenu;
+ DWORD winstyle;
+
+ char* app_name;
+ char* title_text;
+ char* palette_name;
+
+ // Internal variables for the state of the app
+ bool is_windowed;
+ bool is_active;
+ bool is_device_lost;
+ bool is_minimized;
+ bool is_maximized;
+ bool ignore_size_change;
+ bool is_device_initialized;
+ bool is_device_restored;
+ DWORD window_style; // Saved window style for mode switches
+ RECT bounds_rect; // Saved window bounds for mode switches
+ RECT client_rect; // Saved client area size for mode switches
+
+
+ double gui_seconds;
+ double seconds;
+ double frame_rate;
+ int frame_count;
+ int frame_count0;
+ int frame_time;
+ int frame_time0;
+
+ int status;
+ int exit_code;
+ Color screen_color;
+
+ AviFile* avi_file;
+
+ static bool active;
+ static bool paused;
+ static bool server;
+ static bool show_mouse;
+ static DWORD base_game_time;
+ static DWORD real_time;
+ static DWORD game_time;
+ static DWORD time_comp;
+ static DWORD frame_number;
+
+ static double max_frame_length;
+ static double min_frame_length;
+
+ static char panicbuf[256];
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Game_h
+
+
diff --git a/nGenEx/Geometry.cpp b/nGenEx/Geometry.cpp
new file mode 100644
index 0000000..9f3e22b
--- /dev/null
+++ b/nGenEx/Geometry.cpp
@@ -0,0 +1,698 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Geometry.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Geometric Utilities
+*/
+
+#include "MemDebug.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+void Rect::Inflate(int dx, int dy)
+{
+ x -= dx;
+ w += dx*2;
+ y -= dy;
+ h += dy*2;
+}
+
+void Rect::Deflate(int dx, int dy)
+{
+ x += dx;
+ w -= dx*2;
+ y += dy;
+ h -= dy*2;
+}
+
+void Rect::Inset(int l, int r, int t, int b)
+{
+ x += l;
+ y += t;
+ w -= l + r;
+ h -= t + b;
+}
+
+int Rect::Contains(int ax, int ay) const
+{
+ if (ax < x) return 0;
+ if (ax > x+w) return 0;
+ if (ay < y) return 0;
+ if (ay > y+h) return 0;
+
+ return 1;
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Point::Normalize()
+{
+ double scale = 1.0;
+ double len = length();
+
+ if (len)
+ scale /= len;
+
+ x *= scale;
+ y *= scale;
+ z *= scale;
+
+ return len;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Point::SetElement(int i, double v)
+{
+ switch (i) {
+ case 0: x = v; break;
+ case 1: y = v; break;
+ case 2: z = v; break;
+ default: break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Point
+Point::operator*(const Matrix& m) const
+{
+ Point result;
+
+ result.x = (m.elem[0][0] * x) + (m.elem[1][0] * y) + (m.elem[2][0] * z);
+ result.y = (m.elem[0][1] * x) + (m.elem[1][1] * y) + (m.elem[2][1] * z);
+ result.z = (m.elem[0][2] * x) + (m.elem[1][2] * y) + (m.elem[2][2] * z);
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+double ClosestApproachTime(const Point& loc1, const Point& vel1,
+ const Point& loc2, const Point& vel2)
+{
+ double t = 0;
+
+ Point D = loc1-loc2;
+ Point Dv = vel1-vel2;
+
+ if (Dv.x || Dv.y || Dv.z)
+ t = -1 * (Dv*D) / (Dv*Dv);
+
+ return t;
+}
+
+// +--------------------------------------------------------------------+
+
+float
+Vec2::Normalize()
+{
+ float scale = 1.0f;
+ float len = length();
+
+ if (len)
+ scale /= len;
+
+ x *= scale;
+ y *= scale;
+
+ return len;
+}
+
+// +--------------------------------------------------------------------+
+
+float
+Vec3::Normalize()
+{
+ float scale = 1.0f;
+ float len = length();
+
+ if (len)
+ scale /= len;
+
+ x *= scale;
+ y *= scale;
+ z *= scale;
+
+ return len;
+}
+
+// +--------------------------------------------------------------------+
+
+Vec3
+Vec3::operator*(const Matrix& m) const
+{
+ Vec3 result;
+
+ result.x = (float) ((m.elem[0][0] * x) + (m.elem[1][0] * y) + (m.elem[2][0] * z));
+ result.y = (float) ((m.elem[0][1] * x) + (m.elem[1][1] * y) + (m.elem[2][1] * z));
+ result.z = (float) ((m.elem[0][2] * x) + (m.elem[1][2] * y) + (m.elem[2][2] * z));
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+double ClosestApproachTime(const Vec3& loc1, const Vec3& vel1,
+ const Vec3& loc2, const Vec3& vel2)
+{
+ double t = 0;
+
+ Point D = loc1-loc2;
+ Point Dv = vel1-vel2;
+
+ if (Dv.x || Dv.y || Dv.z)
+ t = -1 * (Dv*D) / (Dv*Dv);
+
+ return t;
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Quaternion::Normalize()
+{
+ double scale = 1.0;
+ double len = length();
+
+ if (len)
+ scale /= len;
+
+ x *= scale;
+ y *= scale;
+ z *= scale;
+ w *= scale;
+
+ return len;
+}
+
+// +--------------------------------------------------------------------+
+
+Matrix::Matrix()
+{
+ Identity();
+}
+
+Matrix::Matrix(const Matrix& m)
+{
+ CopyMemory(elem, m.elem, sizeof(elem));
+}
+
+Matrix::Matrix(const Point& vrt, const Point& vup, const Point& vpn)
+{
+ elem[0][0] = vrt.x;
+ elem[0][1] = vrt.y;
+ elem[0][2] = vrt.z;
+
+ elem[1][0] = vup.x;
+ elem[1][1] = vup.y;
+ elem[1][2] = vup.z;
+
+ elem[2][0] = vpn.x;
+ elem[2][1] = vpn.y;
+ elem[2][2] = vpn.z;
+}
+
+// +--------------------------------------------------------------------+
+
+Matrix&
+Matrix::operator =(const Matrix& m)
+{
+ CopyMemory(elem, m.elem, sizeof(elem));
+
+ return *this;
+}
+
+// +--------------------------------------------------------------------+
+
+Matrix&
+Matrix::operator*=(const Matrix& m)
+{
+ return *this = *this * m;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Matrix::Identity()
+{
+ elem[0][0] = 1;
+ elem[0][1] = 0;
+ elem[0][2] = 0;
+
+ elem[1][0] = 0;
+ elem[1][1] = 1;
+ elem[1][2] = 0;
+
+ elem[2][0] = 0;
+ elem[2][1] = 0;
+ elem[2][2] = 1;
+}
+
+// +--------------------------------------------------------------------+
+
+inline void swap_elem(double& a, double& b) { double t=a; a=b; b=t; }
+
+void
+Matrix::Transpose()
+{
+ swap_elem(elem[0][1], elem[1][0]);
+ swap_elem(elem[0][2], elem[2][0]);
+ swap_elem(elem[1][2], elem[2][1]);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Matrix::Rotate(double roll, double pitch, double yaw)
+{
+ double e[3][3];
+ CopyMemory(e, elem, sizeof(elem));
+
+ double sr = sin(roll);
+ double cr = cos(roll);
+ double sp = sin(pitch);
+ double cp = cos(pitch);
+ double sy = sin(yaw);
+ double cy = cos(yaw);
+
+ double a,b,c;
+
+ a = cy*cr;
+ b = cy*sr;
+ c = -sy;
+
+ elem[0][0] = a*e[0][0] + b*e[1][0] + c*e[2][0];
+ elem[0][1] = a*e[0][1] + b*e[1][1] + c*e[2][1];
+ elem[0][2] = a*e[0][2] + b*e[1][2] + c*e[2][2];
+
+ a = cp*-sr + sp*sy*cr;
+ b = cp* cr + sp*sy*sr;
+ c = sp*cy;
+
+ elem[1][0] = a*e[0][0] + b*e[1][0] + c*e[2][0];
+ elem[1][1] = a*e[0][1] + b*e[1][1] + c*e[2][1];
+ elem[1][2] = a*e[0][2] + b*e[1][2] + c*e[2][2];
+
+ a = -sp*-sr + cp*sy*cr;
+ b = -sp* cr + cp*sy*sr;
+ c = cp*cy;
+
+ elem[2][0] = a*e[0][0] + b*e[1][0] + c*e[2][0];
+ elem[2][1] = a*e[0][1] + b*e[1][1] + c*e[2][1];
+ elem[2][2] = a*e[0][2] + b*e[1][2] + c*e[2][2];
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Matrix::Roll(double roll)
+{
+ double s = sin(roll);
+ double c = cos(roll);
+
+ double e00 = elem[0][0];
+ double e01 = elem[0][1];
+ double e02 = elem[0][2];
+ double e10 = elem[1][0];
+ double e11 = elem[1][1];
+ double e12 = elem[1][2];
+
+ elem[0][0] = c*e00 + s*e10;
+ elem[0][1] = c*e01 + s*e11;
+ elem[0][2] = c*e02 + s*e12;
+
+ elem[1][0] = -s*e00 + c*e10;
+ elem[1][1] = -s*e01 + c*e11;
+ elem[1][2] = -s*e02 + c*e12;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Matrix::Pitch(double pitch)
+{
+ double s = sin(pitch);
+ double c = cos(pitch);
+
+ double e10 = elem[1][0];
+ double e11 = elem[1][1];
+ double e12 = elem[1][2];
+ double e20 = elem[2][0];
+ double e21 = elem[2][1];
+ double e22 = elem[2][2];
+
+ elem[1][0] = c*e10 + s*e20;
+ elem[1][1] = c*e11 + s*e21;
+ elem[1][2] = c*e12 + s*e22;
+
+ elem[2][0] = -s*e10 + c*e20;
+ elem[2][1] = -s*e11 + c*e21;
+ elem[2][2] = -s*e12 + c*e22;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Matrix::Yaw(double yaw)
+{
+ double s = sin(yaw);
+ double c = cos(yaw);
+
+ double e00 = elem[0][0];
+ double e01 = elem[0][1];
+ double e02 = elem[0][2];
+ double e20 = elem[2][0];
+ double e21 = elem[2][1];
+ double e22 = elem[2][2];
+
+ elem[0][0] = c*e00 - s*e20;
+ elem[0][1] = c*e01 - s*e21;
+ elem[0][2] = c*e02 - s*e22;
+
+ elem[2][0] = s*e00 + c*e20;
+ elem[2][1] = s*e01 + c*e21;
+ elem[2][2] = s*e02 + c*e22;
+}
+
+// +--------------------------------------------------------------------+
+
+inline int sign(double d) { return (d >= 0); }
+
+void
+Matrix::ComputeEulerAngles(double& roll, double& pitch, double& yaw) const
+{
+ double cy;
+
+ yaw = asin(-elem[0][2]);
+ cy = cos(yaw);
+ roll = asin(elem[0][1] / cy);
+ pitch = asin(elem[1][2] / cy);
+
+ if (sign(cos(roll)*cy) != sign(elem[0][0]))
+ roll = PI - roll;
+
+ if (sign(cos(pitch)*cy) != sign(elem[2][2]))
+ pitch = PI - pitch;
+}
+
+// +--------------------------------------------------------------------+
+
+Matrix
+Matrix::operator*(const Matrix& m) const
+{
+ Matrix r;
+
+ r.elem[0][0] = elem[0][0]*m.elem[0][0] + elem[0][1]*m.elem[1][0] + elem[0][2]*m.elem[2][0];
+ r.elem[0][1] = elem[0][0]*m.elem[0][1] + elem[0][1]*m.elem[1][1] + elem[0][2]*m.elem[2][1];
+ r.elem[0][2] = elem[0][0]*m.elem[0][2] + elem[0][1]*m.elem[1][2] + elem[0][2]*m.elem[2][2];
+
+ r.elem[1][0] = elem[1][0]*m.elem[0][0] + elem[1][1]*m.elem[1][0] + elem[1][2]*m.elem[2][0];
+ r.elem[1][1] = elem[1][0]*m.elem[0][1] + elem[1][1]*m.elem[1][1] + elem[1][2]*m.elem[2][1];
+ r.elem[1][2] = elem[1][0]*m.elem[0][2] + elem[1][1]*m.elem[1][2] + elem[1][2]*m.elem[2][2];
+
+ r.elem[2][0] = elem[2][0]*m.elem[0][0] + elem[2][1]*m.elem[1][0] + elem[2][2]*m.elem[2][0];
+ r.elem[2][1] = elem[2][0]*m.elem[0][1] + elem[2][1]*m.elem[1][1] + elem[2][2]*m.elem[2][1];
+ r.elem[2][2] = elem[2][0]*m.elem[0][2] + elem[2][1]*m.elem[1][2] + elem[2][2]*m.elem[2][2];
+
+ return r;
+}
+
+// +--------------------------------------------------------------------+
+
+Point
+Matrix::operator*(const Point& p) const
+{
+ Point result;
+
+ result.x = (elem[0][0] * p.x) + (elem[0][1] * p.y) + (elem[0][2] * p.z);
+ result.y = (elem[1][0] * p.x) + (elem[1][1] * p.y) + (elem[1][2] * p.z);
+ result.z = (elem[2][0] * p.x) + (elem[2][1] * p.y) + (elem[2][2] * p.z);
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+Vec3
+Matrix::operator*(const Vec3& v) const
+{
+ Vec3 result;
+
+ result.x = (float) ((elem[0][0] * v.x) + (elem[0][1] * v.y) + (elem[0][2] * v.z));
+ result.y = (float) ((elem[1][0] * v.x) + (elem[1][1] * v.y) + (elem[1][2] * v.z));
+ result.z = (float) ((elem[2][0] * v.x) + (elem[2][1] * v.y) + (elem[2][2] * v.z));
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Matrix::Cofactor(int i, int j) const
+{
+ int i1=0;
+ int i2=2;
+ int j1=0;
+ int j2=2;
+
+ if (i==0) i1=1; else if (i==2) i2=1;
+ if (j==0) j1=1; else if (j==2) j2=1;
+
+ double factor = elem[i1][j1]*elem[i2][j2] - elem[i1][j2]*elem[i2][j1];
+
+ if ((i+j) & 1)
+ factor *= -1;
+
+ return factor;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Matrix::Invert()
+{
+ double f[3][3];
+ int i, j;
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ f[i][j] = Cofactor(j,i);
+
+ double det = elem[0][0] * f[0][0] +
+ elem[0][1] * f[1][0] +
+ elem[0][2] * f[2][0];
+
+ if (det != 0) {
+ double inv = 1/det;
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ elem[i][j] = f[i][j] * inv;
+ }
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+Plane::Plane()
+ : distance(0.0f)
+{ }
+
+Plane::Plane(const Point& p0, const Point& p1, const Point& p2)
+{
+ Point d1 = p1 - p0;
+ Point d2 = p2 - p0;
+
+ normal = (Vec3) d1.cross(d2);
+ normal.Normalize();
+
+ distance = (float) (normal * p0);
+}
+
+Plane::Plane(const Vec3& v0, const Vec3& v1, const Vec3& v2)
+{
+ Vec3 d1 = v1 - v0;
+ Vec3 d2 = v2 - v0;
+
+ normal = d1.cross(d2);
+ normal.Normalize();
+
+ distance = normal * v0;
+}
+
+void Plane::Rotate(const Vec3& v0, const Matrix& m)
+{
+ normal = normal * m;
+ distance = normal * v0;
+}
+
+void Plane::Translate(const Vec3& v0)
+{
+ distance = normal * v0;
+}
+
+// +--------------------------------------------------------------------+
+// 3-D dot product.
+
+double DotProduct(const Point& a, const Point& b)
+{
+ return (a.x * b.x) + (a.y * b.y) + (a.z * b.z);
+}
+
+// +--------------------------------------------------------------------+
+// 3-D cross product.
+
+void CrossProduct(const Point& a, const Point& b, Point& out)
+{
+ out.x = (a.y * b.z) - (a.z * b.y);
+ out.y = (a.z * b.x) - (a.x * b.z);
+ out.z = (a.x * b.y) - (a.y * b.x);
+}
+
+// +--------------------------------------------------------------------+
+// Concatenate two 3x3 matrices.
+
+void MConcat(double in1[3][3], double in2[3][3], double out[3][3])
+{
+ int i, j;
+
+ for (i=0 ; i<3 ; i++) {
+ for (j=0 ; j<3 ; j++) {
+ out[i][j] = in1[i][0] * in2[0][j] +
+ in1[i][1] * in2[1][j] +
+ in1[i][2] * in2[2][j];
+ }
+ }
+}
+
+/* GRAPHICS GEMS II ----------------------------------------------------
+ *
+ * lines_intersect: AUTHOR: Mukesh Prasad
+ *
+ * This function computes whether two line segments,
+ * respectively joining the input points (x1,y1) -- (x2,y2)
+ * and the input points (x3,y3) -- (x4,y4) intersect.
+ * If the lines intersect, the output variables x, y are
+ * set to coordinates of the point of intersection.
+ *
+ * All values are in integers. The returned value is rounded
+ * to the nearest integer point.
+ *
+ * If non-integral grid points are relevant, the function
+ * can easily be transformed by substituting floating point
+ * calculations instead of integer calculations.
+ *
+ * Entry
+ * x1, y1, x2, y2 Coordinates of endpoints of one segment.
+ * x3, y3, x4, y4 Coordinates of endpoints of other segment.
+ *
+ * Exit
+ * x, y Coordinates of intersection point.
+ *
+ * The value returned by the function is one of:
+ *
+ * DONT_INTERSECT 0
+ * DO_INTERSECT 1
+ * COLLINEAR 2
+ *
+ * Error conditions:
+ *
+ * Depending upon the possible ranges, and particularly on 16-bit
+ * computers, care should be taken to protect from overflow.
+ *
+ * In the following code, 'long' values have been used for this
+ * purpose, instead of 'int'.
+ *
+ */
+
+#define DONT_INTERSECT 0
+#define DO_INTERSECT 1
+#define COLLINEAR 2
+
+inline int SAME_SIGNS(double a, double b)
+{
+ return ((a>=0 && b>=0) || (a<0 && b<0));
+}
+
+int
+lines_intersect(
+ /* 1st line segment */ double x1, double y1, double x2, double y2,
+ /* 2nd line segment */ double x3, double y3, double x4, double y4,
+ /* pt of intersect */ double& ix, double& iy)
+{
+ double a1, a2, b1, b2, c1, c2; /* Coefficients of line eqns. */
+ double r1, r2, r3, r4; /* 'Sign' values */
+ double denom, offset, num; /* Intermediate values */
+
+ /* Compute a1, b1, c1, where line joining points 1 and 2
+ * is "a1 x + b1 y + c1 = 0". */
+
+ a1 = y2 - y1;
+ b1 = x1 - x2;
+ c1 = x2 * y1 - x1 * y2;
+
+ /* Compute r3 and r4. */
+
+ r3 = a1 * x3 + b1 * y3 + c1;
+ r4 = a1 * x4 + b1 * y4 + c1;
+
+ /* Check signs of r3 and r4. If both point 3 and point 4 lie on
+ * same side of line 1, the line segments do not intersect. */
+
+ if ( r3 != 0 &&
+ r4 != 0 &&
+ SAME_SIGNS( r3, r4 ))
+ return ( DONT_INTERSECT );
+
+ /* Compute a2, b2, c2 */
+
+ a2 = y4 - y3;
+ b2 = x3 - x4;
+ c2 = x4 * y3 - x3 * y4;
+
+ /* Compute r1 and r2 */
+
+ r1 = a2 * x1 + b2 * y1 + c2;
+ r2 = a2 * x2 + b2 * y2 + c2;
+
+ /* Check signs of r1 and r2. If both point 1 and point 2 lie
+ * on same side of second line segment, the line segments do
+ * not intersect. */
+
+ if ( r1 != 0 &&
+ r2 != 0 &&
+ SAME_SIGNS( r1, r2 ))
+ return ( DONT_INTERSECT );
+
+ /* Line segments intersect: compute intersection point. */
+
+ denom = a1 * b2 - a2 * b1;
+ if ( denom == 0 )
+ return ( DONT_INTERSECT );
+ offset = denom < 0 ? - denom / 2 : denom / 2;
+
+ /* The denom/2 is to get rounding instead of truncating. It
+ * is added or subtracted to the numerator, depending upon the
+ * sign of the numerator. */
+
+ num = b1 * c2 - b2 * c1;
+ ix = ( num < 0 ? num - offset : num + offset ) / denom;
+
+ num = a2 * c1 - a1 * c2;
+ iy = ( num < 0 ? num - offset : num + offset ) / denom;
+
+ return ( DO_INTERSECT );
+}
+
diff --git a/nGenEx/Geometry.h b/nGenEx/Geometry.h
new file mode 100644
index 0000000..faa55fa
--- /dev/null
+++ b/nGenEx/Geometry.h
@@ -0,0 +1,302 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Geometry.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Geometric classes: Rect, Vec3, Point, Matrix, Plane
+*/
+
+#ifndef Geometry_h
+#define Geometry_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+struct Rect;
+struct Insets;
+struct Matrix;
+struct Vec3;
+struct Point;
+struct Quaternion;
+struct Plane;
+
+const double PI = 3.14159265358979323846;
+const double DEGREES = (PI/180);
+
+// +--------------------------------------------------------------------+
+
+struct Rect
+{
+ static const char* TYPENAME() { return "Rect"; }
+
+ Rect() : x(0), y(0), w(0), h(0) { }
+ Rect(int ix, int iy, int iw, int ih) : x(ix), y(iy), w(iw), h(ih) { }
+
+ int operator==(const Rect& r) const { return x==r.x && y==r.y && w==r.w && h==r.h; }
+ int operator!=(const Rect& r) const { return x!=r.x || y!=r.y || w!=r.w || h!=r.h; }
+
+ void Inflate(int dw, int dh);
+ void Deflate(int dw, int dh);
+ void Inset(int left, int right, int top, int bottom);
+ int Contains(int x, int y) const;
+
+ int x, y, w, h;
+};
+
+// +--------------------------------------------------------------------+
+
+struct Insets
+{
+ Insets() : left(0), right(0), top(0), bottom(0) { }
+ Insets(WORD l, WORD r, WORD t, WORD b) : left(l), right(r), top(t), bottom(b) { }
+
+ WORD left;
+ WORD right;
+ WORD top;
+ WORD bottom;
+};
+
+// +--------------------------------------------------------------------+
+
+struct Matrix
+{
+ static const char* TYPENAME() { return "Matrix"; }
+
+ Matrix();
+ Matrix(const Matrix& m);
+ Matrix(const Point& vrt, const Point& vup, const Point& vpn);
+
+ Matrix& operator = (const Matrix& m);
+ Matrix& operator *= (const Matrix& m);
+
+ double operator() (int i, int j) const { return elem[i][j]; }
+ double& operator() (int i, int j) { return elem[i][j]; }
+
+ void Identity();
+ void Transpose();
+ void Rotate(double roll, double pitch, double yaw);
+ void Roll(double roll);
+ void Pitch(double pitch);
+ void Yaw(double yaw);
+ void ComputeEulerAngles(double& roll, double& pitch, double& yaw) const;
+
+ double Cofactor(int i, int j) const;
+ void Invert();
+
+ Matrix Inverse() const {
+ Matrix result(*this);
+ result.Invert();
+ return result;
+ }
+
+ Matrix operator*(const Matrix& m) const;
+ Point operator*(const Point & p) const;
+ Vec3 operator*(const Vec3& v) const;
+
+ double elem[3][3];
+
+private:
+ Matrix(int no_init) { }
+};
+
+// +--------------------------------------------------------------------+
+
+struct Vec2
+{
+ static const char* TYPENAME() { return "Vec2"; }
+
+ Vec2() { }
+ Vec2(int ix, int iy) : x((float) ix), y((float) iy) { }
+ Vec2(float ix, float iy) : x(ix), y(iy) { }
+ Vec2(double ix, double iy) : x((float) ix), y((float) iy) { }
+
+ operator void*() const { return (void*) (x || y); }
+ int operator==(const Vec2& p) const { return x==p.x && y==p.y; }
+ int operator!=(const Vec2& p) const { return x!=p.x || y!=p.y; }
+ Vec2 operator+ (const Vec2& p) const { return Vec2(x+p.x, y+p.y); }
+ Vec2 operator- (const Vec2& p) const { return Vec2(x-p.x, y-p.y); }
+ Vec2 operator- () const { return Vec2(-x, -y); }
+ Vec2 operator* (float s) const { return Vec2(x*s, y*s); }
+ Vec2 operator/ (float s) const { return Vec2(x/s, y/s); }
+ float operator*(const Vec2& p) const { return (x*p.x + y*p.y); }
+
+ Vec2& operator= (const Vec2& p) { x =p.x; y =p.y; return *this; }
+ Vec2& operator+=(const Vec2& p) { x+=p.x; y+=p.y; return *this; }
+ Vec2& operator-=(const Vec2& p) { x-=p.x; y-=p.y; return *this; }
+ Vec2& operator*=(float s) { x*=s; y*=s; return *this; }
+ Vec2& operator/=(float s) { x/=s; y/=s; return *this; }
+
+ float length() const { return (float) sqrt(x*x+y*y); }
+ float Normalize();
+ float dot(const Vec2& p) const { return (x*p.x + y*p.y); }
+ Vec2 normal() const { return Vec2(-y, x); }
+
+ float x, y;
+};
+
+// +--------------------------------------------------------------------+
+
+struct Vec3
+{
+ static const char* TYPENAME() { return "Vec3"; }
+
+ Vec3() { }
+ Vec3(int ix, int iy, int iz) : x((float) ix), y((float) iy), z((float) iz) { }
+ Vec3(float ix, float iy, float iz) : x(ix), y(iy), z(iz) { }
+ Vec3(double ix, double iy, double iz) : x((float) ix), y((float) iy), z((float) iz) { }
+
+ operator void*() const { return (void*) (x || y || z); }
+ int operator==(const Vec3& p) const { return x==p.x && y==p.y && z==p.z; }
+ int operator!=(const Vec3& p) const { return x!=p.x || y!=p.y || z!=p.z; }
+ Vec3 operator+ (const Vec3& p) const { return Vec3(x+p.x, y+p.y, z+p.z); }
+ Vec3 operator- (const Vec3& p) const { return Vec3(x-p.x, y-p.y, z-p.z); }
+ Vec3 operator- () const { return Vec3(-x, -y, -z); }
+ Vec3 operator* (float s) const { return Vec3(x*s, y*s, z*s); }
+ Vec3 operator/ (float s) const { return Vec3(x/s, y/s, z/s); }
+ float operator* (const Vec3& p) const { return (x*p.x + y*p.y + z*p.z); }
+ Vec3 operator* (const Matrix&) const;
+
+ Vec3& operator= (const Vec3& p) { x =p.x; y =p.y; z =p.z; return *this; }
+ Vec3& operator+=(const Vec3& p) { x+=p.x; y+=p.y; z+=p.z; return *this; }
+ Vec3& operator-=(const Vec3& p) { x-=p.x; y-=p.y; z-=p.z; return *this; }
+ Vec3& operator*=(float s) { x*=s; y*=s; z*=s; return *this; }
+ Vec3& operator/=(float s) { x/=s; y/=s; z/=s; return *this; }
+
+ void SwapYZ() { float t = y; y = z; z = t; }
+ float length() const { return (float) sqrt(x*x+y*y+z*z); }
+ float Normalize();
+
+ float dot(const Vec3& p) const { return (x*p.x + y*p.y + z*p.z); }
+ Vec3 cross(const Vec3& v) const { return Vec3((y*v.z) - (z*v.y),
+ (z*v.x) - (x*v.z),
+ (x*v.y) - (y*v.x)); }
+
+ float x, y, z;
+};
+
+double ClosestApproachTime(const Vec3& loc1, const Vec3& vel1,
+ const Vec3& loc2, const Vec3& vel2);
+
+// +--------------------------------------------------------------------+
+
+struct Point
+{
+ static const char* TYPENAME() { return "Point"; }
+
+ Point() : x(0), y(0), z(0) { }
+ Point(double ix, double iy, double iz) : x(ix), y(iy), z(iz) { }
+ Point(const Point& p) : x(p.x), y(p.y), z(p.z) { }
+ Point(const Vec3& v) : x(v.x), y(v.y), z(v.z) { }
+
+ operator Vec3() const { return Vec3((float) x, (float) y, (float) z); }
+
+ operator void*() const { return (void*) (x || y || z); }
+ int operator==(const Point& p) const { return x==p.x && y==p.y && z==p.z; }
+ int operator!=(const Point& p) const { return x!=p.x || y!=p.y || z!=p.z; }
+ Point operator+ (const Point& p) const { return Point(x+p.x, y+p.y, z+p.z); }
+ Point operator- (const Point& p) const { return Point(x-p.x, y-p.y, z-p.z); }
+ Point operator- () const { return Point(-x, -y, -z); }
+ Point operator* (double s) const { return Point(x*s, y*s, z*s); }
+ Point operator/ (double s) const { return Point(x/s, y/s, z/s); }
+ double operator*(const Point& p) const { return (x*p.x + y*p.y + z*p.z); }
+ Point operator* (const Matrix& m) const;
+
+ Point& operator= (const Point& p) { x =p.x; y =p.y; z =p.z; return *this; }
+ Point& operator+=(const Point& p) { x+=p.x; y+=p.y; z+=p.z; return *this; }
+ Point& operator-=(const Point& p) { x-=p.x; y-=p.y; z-=p.z; return *this; }
+ Point& operator*=(double s) { x*=s; y*=s; z*=s; return *this; }
+ Point& operator/=(double s) { x/=s; y/=s; z/=s; return *this; }
+
+ double length() const { return sqrt(x*x+y*y+z*z); }
+ double Normalize();
+ void SwapYZ() { double t = y; y = z; z = t; }
+ Point OtherHand() const { return Point(-x, z, y); }
+
+ void SetElement(int i, double v);
+
+ double dot(const Point& p) const { return (x*p.x + y*p.y + z*p.z); }
+ Point cross(const Point& p) const { return Point((y*p.z) - (z*p.y),
+ (z*p.x) - (x*p.z),
+ (x*p.y) - (y*p.x)); }
+
+ double x, y, z;
+};
+
+double ClosestApproachTime(const Point& loc1, const Point& vel1,
+ const Point& loc2, const Point& vel2);
+
+// +--------------------------------------------------------------------+
+
+struct Quaternion
+{
+ static const char* TYPENAME() { return "Quaternion"; }
+
+ Quaternion() : x(0), y(0), z(0), w(0) { }
+ Quaternion(double ix,
+ double iy,
+ double iz,
+ double iw) : x(ix), y(iy), z(iz), w(iw) { }
+ Quaternion(const Quaternion& q) : x(q.x), y(q.y), z(q.z), w(q.w) { }
+
+ int operator==(const Quaternion& q) const { return x==q.x && y==q.y && z==q.z && w==q.w; }
+ int operator!=(const Quaternion& q) const { return x!=q.x || y!=q.y || z!=q.z || w!=q.w; }
+
+ Quaternion operator+ (const Quaternion& q) const { return Quaternion(x+q.x, y+q.y, z+q.z, w+q.w); }
+ Quaternion operator- (const Quaternion& q) const { return Quaternion(x-q.x, y-q.y, z-q.z, w-q.w); }
+ Quaternion operator- () const { return Quaternion(-x, -y, -z, -w); }
+ Quaternion operator* (double s) const { return Quaternion(x*s, y*s, z*s, w*s); }
+ Quaternion operator/ (double s) const { return Quaternion(x/s, y/s, z/s, w/s); }
+
+ Quaternion& operator= (const Quaternion& q) { x =q.x; y =q.y; z =q.z; w =q.w; return *this; }
+ Quaternion& operator+=(const Quaternion& q) { x+=q.x; y+=q.y; z+=q.z; w+=q.w; return *this; }
+ Quaternion& operator-=(const Quaternion& q) { x-=q.x; y-=q.y; z-=q.z; w-=q.w; return *this; }
+ Quaternion& operator*=(double s) { x*=s; y*=s; z*=s; w*=s; return *this; }
+ Quaternion& operator/=(double s) { x/=s; y/=s; z/=s; w/=s; return *this; }
+
+ double length() const { return sqrt(x*x + y*y + z*z + w*w); }
+ double Normalize();
+
+ double x, y, z, w;
+};
+
+// +--------------------------------------------------------------------+
+
+struct Plane
+{
+ static const char* TYPENAME() { return "Plane"; }
+
+ Plane();
+ Plane(const Point& p0, const Point& p1, const Point& p2);
+ Plane(const Vec3& v0, const Vec3& v1, const Vec3& v2);
+
+ void Rotate(const Vec3& v0, const Matrix& m);
+ void Translate(const Vec3& v0);
+
+ float distance;
+ Vec3 normal;
+};
+
+// +--------------------------------------------------------------------+
+
+double DotProduct(const Point& a, const Point& b);
+void CrossProduct(const Point& a, const Point& b, Point& out);
+void MConcat(double in1[3][3], double in2[3][3], double out[3][3]);
+
+// +--------------------------------------------------------------------+
+
+int lines_intersect(
+ /* 1st line segment */ double x1, double y1, double x2, double y2,
+ /* 2nd line segment */ double x3, double y3, double x4, double y4,
+ /* intersect point */ double& x, double& y);
+
+// +--------------------------------------------------------------------+
+
+#endif Geometry_h
+
diff --git a/nGenEx/Graphic.cpp b/nGenEx/Graphic.cpp
new file mode 100644
index 0000000..7d63301
--- /dev/null
+++ b/nGenEx/Graphic.cpp
@@ -0,0 +1,170 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Graphic.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract 3D Graphic Object
+*/
+
+#include "MemDebug.h"
+#include "Graphic.h"
+#include "Scene.h"
+#include "Projector.h"
+
+// +--------------------------------------------------------------------+
+
+int Graphic::id_key = 1;
+
+// +--------------------------------------------------------------------+
+
+Graphic::Graphic()
+ : id(id_key++), visible(true), loc(0.0f, 0.0f, 0.0f),
+ radius(0.0f), infinite(0), foreground(0), hidden(0), life(-1),
+ trans(false), shadow(false), luminous(false), depth(0.0f), scene(0)
+{
+ screen_rect.x = 0;
+ screen_rect.y = 0;
+ screen_rect.w = 0;
+ screen_rect.h = 0;
+
+ ZeroMemory(name, sizeof(name));
+ strcpy(name, "Graphic");
+}
+
+// +--------------------------------------------------------------------+
+
+Graphic::~Graphic()
+{ }
+
+int
+Graphic::operator < (const Graphic& g) const
+{
+ if (!infinite && g.infinite)
+ return 1;
+
+ else if (infinite && !g.infinite)
+ return 0;
+
+ double za = fabs(Depth());
+ double zb = fabs(g.Depth());
+
+ return (za < zb);
+}
+
+int
+Graphic::operator <= (const Graphic& g) const
+{
+ if (!infinite && g.infinite)
+ return 1;
+
+ else if (infinite && !g.infinite)
+ return 0;
+
+ double za = fabs(Depth());
+ double zb = fabs(g.Depth());
+
+ return (za <= zb);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Graphic::SetInfinite(bool b)
+{
+ infinite = (BYTE) b;
+
+ if (infinite)
+ depth = 1.0e9f;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Graphic::Nearer(Graphic* a, Graphic* b)
+{
+ if (a->depth < b->depth) return -1;
+ else if (a->depth == b->depth) return 0;
+ else return 1;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Graphic::Farther(Graphic* a, Graphic* b)
+{
+ if (a->depth > b->depth) return -1;
+ else if (a->depth == b->depth) return 0;
+ else return 1;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Graphic::Destroy()
+{
+ if (scene)
+ scene->DelGraphic(this);
+
+ delete this;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Graphic::CollidesWith(Graphic& o)
+{
+ Point delta_loc = loc - o.loc;
+
+ // bounding spheres test:
+ if (delta_loc.length() > radius + o.radius)
+ return 0;
+
+ return 1;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Graphic::CheckRayIntersection(Point Q, Point w, double len, Point& ipt,
+ bool treat_translucent_polys_as_solid)
+{
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Graphic::ProjectScreenRect(Projector* p)
+{
+ screen_rect.x = 2000;
+ screen_rect.y = 2000;
+ screen_rect.w = 0;
+ screen_rect.h = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Graphic::CheckVisibility(Projector& projector)
+{
+ if (projector.IsVisible( Location(), Radius()) &&
+ projector.ApparentRadius(Location(), Radius()) > 1) {
+
+ visible = true;
+ }
+ else {
+ visible = false;
+ screen_rect.x = 2000;
+ screen_rect.y = 2000;
+ screen_rect.w = 0;
+ screen_rect.h = 0;
+ }
+
+ return visible;
+}
diff --git a/nGenEx/Graphic.h b/nGenEx/Graphic.h
new file mode 100644
index 0000000..1416bf5
--- /dev/null
+++ b/nGenEx/Graphic.h
@@ -0,0 +1,139 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Graphic.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract 3D Graphic Object
+*/
+
+#ifndef Graphic_h
+#define Graphic_h
+
+#include "Geometry.h"
+#include "Color.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+#define GRAPHIC_DESTROY(x) if (x) { x->Destroy(); x = 0; }
+
+// +--------------------------------------------------------------------+
+
+class Projector;
+class Light;
+class Scene;
+class Video;
+
+// +--------------------------------------------------------------------+
+
+class Graphic
+{
+public:
+ static const char* TYPENAME() { return "Graphic"; }
+
+ enum TYPE { OTHER, SOLID, SPRITE, BOLT, QUAD };
+
+ enum RENDER_FLAGS {
+ RENDER_SOLID = 0x0001,
+ RENDER_ALPHA = 0x0002,
+ RENDER_ADDITIVE = 0x0004,
+ RENDER_FIRST_LIGHT = 0x1000,
+ RENDER_ADD_LIGHT = 0x2000
+ };
+
+ Graphic();
+ virtual ~Graphic();
+
+ int operator == (const Graphic& g) const { return id == g.id; }
+ int operator < (const Graphic& g) const;
+ int operator <= (const Graphic& g) const;
+
+ // operations
+ virtual void Render(Video* video, DWORD flags) { }
+ virtual void Update() { }
+ virtual void SetOrientation(const Matrix& o) { }
+
+ virtual int CollidesWith(Graphic& o);
+
+ // accessors / mutators
+ int Identity() const { return id; }
+ const char* Name() const { return name; }
+ bool IsVisible() const { return visible; }
+ void SetVisible(bool v) { visible = v; }
+ float Radius() const { return radius; }
+
+ Point Location() const { return loc; }
+ virtual void MoveTo(const Point& p) { loc = p; }
+ virtual void TranslateBy(const Point& ref) { loc = loc - ref; }
+
+ virtual float Depth() const { return depth; }
+ virtual void SetDepth(float d) { depth = d; }
+ static int Nearer(Graphic* a, Graphic* b);
+ static int Farther(Graphic* a, Graphic* b);
+
+ virtual int IsInfinite() const { return infinite; }
+ virtual void SetInfinite(bool b);
+ virtual int IsForeground() const { return foreground; }
+ virtual void SetForeground(bool f){ foreground = f; }
+ virtual int IsBackground() const { return background; }
+ virtual void SetBackground(bool b){ background = b; }
+
+ virtual int Hidden() const { return hidden; }
+ virtual int Life() const { return life; }
+ virtual void Destroy();
+ virtual void Hide() { hidden = true; }
+ virtual void Show() { hidden = false; }
+ virtual bool Luminous() const { return luminous;}
+ virtual void SetLuminous(bool l) { luminous = l; }
+ virtual bool Translucent() const { return trans; }
+ virtual bool CastsShadow() const { return shadow; }
+ virtual void SetShadow(bool s) { shadow = s; }
+
+ virtual bool IsSolid() const { return false; }
+ virtual bool IsSprite() const { return false; }
+ virtual bool IsBolt() const { return false; }
+ virtual bool IsQuad() const { return false; }
+
+ virtual void ProjectScreenRect(Projector* p);
+ const Rect& ScreenRect() const { return screen_rect; }
+ virtual Scene* GetScene() const { return scene; }
+ virtual void SetScene(Scene*s) { scene = s; }
+
+ virtual int CheckRayIntersection(Point pt, Point vpn, double len, Point& ipt,
+ bool treat_translucent_polys_as_solid=true);
+
+ virtual bool CheckVisibility(Projector& projector);
+
+protected:
+ static int id_key;
+
+ int id;
+ Point loc;
+ float depth;
+ float radius;
+ int life;
+
+ bool visible;
+ bool infinite;
+ bool foreground;
+ bool background;
+ bool hidden;
+ bool trans;
+ bool shadow;
+ bool luminous;
+
+ Rect screen_rect;
+ Scene* scene;
+ char name[32];
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Graphic_h
+
diff --git a/nGenEx/IA3D.H b/nGenEx/IA3D.H
new file mode 100644
index 0000000..9ecef73
--- /dev/null
+++ b/nGenEx/IA3D.H
@@ -0,0 +1,128 @@
+/*---------------------------------------------------------------------
+ *
+ * ia3d.h
+ *
+ *---------------------------------------------------------------------
+ *
+ * $Id: ia3d.h%v 1.1 1996/09/02 10:50:35 mike Exp mike $
+ *
+ *---------------------------------------------------------------------
+ *
+ * ia3d header file. It's the part the outside world needs to see.
+ *
+ *---------------------------------------------------------------------
+ *
+ * AUREAL SEMICONDUCTOR, INC. PROPRIETARY AND CONFIDENTIAL
+ * Copyright (c) 1996 Aureal Semiconductor, Inc. - All rights
+ * reserved.
+ *
+ *---------------------------------------------------------------------
+ */
+
+
+#ifndef _IA3D_H_
+#define _IA3D_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+// A3d Class ID! {D8F1EEE0-F634-11cf-8700-00A0245D918B}
+DEFINE_GUID(CLSID_A3d,
+0xd8f1eee0, 0xf634, 0x11cf, 0x87, 0x0, 0x0, 0xa0, 0x24, 0x5d, 0x91, 0x8b);
+
+// A3d Interface ID! {D8F1EEE1-F634-11cf-8700-00A0245D918B}
+DEFINE_GUID(IID_IA3d,
+0xd8f1eee1, 0xf634, 0x11cf, 0x87, 0x0, 0x0, 0xa0, 0x24, 0x5d, 0x91, 0x8b);
+
+
+// Bits for manipulating output modes
+
+// Values for bOutputMode
+#define OUTPUT_MODE_STEREO 0x00000001
+#define OUTPUT_MODE_QUAD 0x00000002
+
+// Values for FrontXtalkMode and bRearXtalkMode
+#define OUTPUT_HEADPHONES 0x00000001 // headphones
+#define OUTPUT_SPEAKERS_WIDE 0x00000002
+#define OUTPUT_SPEAKERS_NARROW 0x00000003
+
+// Values for Resource Management Mode
+#define A3D_RESOURCE_MODE_OFF 0x00000000
+#define A3D_RESOURCE_MODE_NOTIFY 0x00000001
+#define A3D_RESOURCE_MODE_DYNAMIC 0x00000002
+
+// Declare the IA3d Interface. It's not very complex at all.
+
+#undef INTERFACE
+#define INTERFACE IA3d
+
+typedef struct IA3d *LPIA3D;
+
+DECLARE_INTERFACE_(IA3d, IUnknown)
+{
+ // IUnknown
+ STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE;
+ STDMETHOD_(ULONG,AddRef) (THIS) PURE;
+ STDMETHOD_(ULONG,Release) (THIS) PURE;
+
+ // IA3d
+ STDMETHOD(SetOutputMode)(THIS_ DWORD dwFrontXtalkMode, DWORD dwBackXtalkMode, DWORD dwQuadMode) PURE;
+ STDMETHOD(GetOutputMode)(THIS_ DWORD *lpdwFrontXtalkMode, DWORD *lpdwBackXtalkMode, DWORD *lpdwQuadMode) PURE;
+
+ STDMETHOD(SetResourceManagerMode) (THIS_ DWORD ) PURE;
+ STDMETHOD(GetResourceManagerMode) (THIS_ DWORD *) PURE;
+
+ STDMETHOD(SetHFAbsorbFactor)(THIS_ FLOAT ) PURE;
+ STDMETHOD(GetHFAbsorbFactor)(THIS_ FLOAT *) PURE;
+
+};
+
+
+
+
+// The library function that gets things going. It returns an interface
+// pointer to DirectSound.
+
+#define A3D_OK 1 // A3dCreate returns this upon detection of A3D enabled hardware.
+
+_declspec (dllexport) HRESULT WINAPI
+A3dCreate(GUID * lpGUID, LPDIRECTSOUND * ppDS, IUnknown FAR *pUnkOuter );
+
+// Usefull Macros for C folks.
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+#define IA3d_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IA3d_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IA3d_Release(p) (p)->lpVtbl->Release(p)
+#define IA3d_SetOutputMode(p,a,b,c) (p)->lpVtbl->SetOutputMode(p,a,b,c)
+#define IA3d_GetOutputMode(p,a,b,c) (p)->lpVtbl->GetOutputMode(p,a,b,c)
+#define IA3d_SetResourceManagerMode(p,a) (p)->lpVtbl->SetResourceManagerMode(p,a)
+#define IA3d_GetResourceManagerMode(p,a) (p)->lpVtbl->GetResourceManagerMode(p,a)
+#define IA3d_SetHFAbsorbFactor(p,a) (p)->lpVtbl->SetHFAbsorbFactor(p,a)
+#define IA3d_GetHFAbsorbFactor(p,a) (p)->lpVtbl->GetHFAbsorbFactor(p,a)
+
+
+#else
+#define IA3d_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
+#define IA3d_AddRef(p) (p)->AddRef()
+#define IA3d_Release(p) (p)->Release()
+#define IA3d_SetOutputMode(p,a,b,c) (p)->SetOutputMode(a,b,c)
+#define IA3d_GetOutputMode(p,a,b,c) (p)->GetOutputMode(a,b,c)
+#define IA3d_SetResourceManagerMode(p,a) (p)->SetResourceManagerMode(a)
+#define IA3d_GetResourceManagerMode(p,a) (p)->GetResourceManagerMode(a)
+#define IA3d_SetHFAbsorbFactor(p,a) (p)->SetHFAbsorbFactor(a)
+#define IA3d_GetHFAbsorbFactor(p,a) (p)->GetHFAbsorbFactor(a)
+
+#endif
+
+
+
+#ifdef __cplusplus
+};
+#endif
+
+
+
+#endif // _IA3D_H_
diff --git a/nGenEx/ImageBox.cpp b/nGenEx/ImageBox.cpp
new file mode 100644
index 0000000..36b866d
--- /dev/null
+++ b/nGenEx/ImageBox.cpp
@@ -0,0 +1,298 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ImageBox.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ImageBox class
+*/
+
+#include "MemDebug.h"
+#include "ImageBox.h"
+#include "Video.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "DataLoader.h"
+
+// +--------------------------------------------------------------------+
+
+ImageBox::ImageBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid)
+ : ActiveWindow(p->GetScreen(), ax, ay, aw, ah, aid, 0, p), blend_mode(Video::BLEND_ALPHA)
+{
+ picture_loc = 1;
+ text_align = DT_CENTER;
+
+ char buf[32];
+ sprintf(buf, "ImageBox %d", id);
+ desc = buf;
+}
+
+ImageBox::ImageBox(Screen* s, int ax, int ay, int aw, int ah, DWORD aid)
+ : ActiveWindow(s, ax, ay, aw, ah, aid, 0, 0), blend_mode(Video::BLEND_ALPHA)
+{
+ picture_loc = 1;
+ text_align = DT_CENTER;
+
+ char buf[32];
+ sprintf(buf, "ImageBox %d", id);
+ desc = buf;
+}
+
+// +--------------------------------------------------------------------+
+
+ImageBox::~ImageBox()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+ImageBox::Draw()
+{
+ if (!shown)
+ return;
+
+ int x = 0;
+ int y = 0;
+ int w = rect.w;
+ int h = rect.h;
+ int img_w = picture.Width();
+ int img_h = picture.Height();
+
+ Rect box_rect(x,y,w,h);
+
+ // draw the picture (if any)
+ if (picture.Width()) {
+ Rect irect = CalcPictureRect();
+ DrawBitmap(irect.x,
+ irect.y,
+ irect.x + irect.w,
+ irect.y + irect.h,
+ &picture,
+ blend_mode);
+ }
+
+ // draw the border:
+ DrawStyleRect(0, 0, w, h, style);
+
+ // draw text here:
+ if (font && text.length()) {
+ int border_size = 4;
+
+ if (style & WIN_RAISED_FRAME && style & WIN_SUNK_FRAME)
+ border_size = 8;
+
+ Rect label_rect = CalcLabelRect(img_w,img_h);
+ int vert_space = label_rect.h;
+ int horz_space = label_rect.w;
+
+ DrawText(text.data(), 0, label_rect, DT_CALCRECT | DT_WORDBREAK | DT_CENTER);
+ vert_space = (vert_space - label_rect.h)/2;
+
+ label_rect.w = horz_space;
+
+ if (vert_space > 0)
+ label_rect.y += vert_space;
+
+ Color fore = ShadeColor(fore_color, 1);
+ font->SetColor(fore);
+ DrawText(text.data(), 0, label_rect, DT_WORDBREAK | DT_CENTER);
+ }
+}
+
+void
+ImageBox::DrawTabbedText()
+{
+ if (!shown)
+ return;
+
+ int x = 0;
+ int y = 0;
+ int w = rect.w;
+ int h = rect.h;
+ int img_w = picture.Width();
+ int img_h = picture.Height();
+
+ Rect box_rect(x,y,w,h);
+
+ // draw the picture (if any)
+ if (picture.Width()) {
+ Rect irect = CalcPictureRect();
+ DrawBitmap(irect.x,
+ irect.y,
+ irect.x + irect.w,
+ irect.y + irect.h,
+ &picture,
+ Video::BLEND_ALPHA);
+ }
+
+ ActiveWindow::DrawTabbedText();
+}
+
+Rect ImageBox::CalcLabelRect(int img_w, int img_h)
+{
+ // fit the text in the bevel:
+ Rect label_rect;
+ label_rect.x = 0;
+ label_rect.y = 0;
+ label_rect.w = rect.w;
+ label_rect.h = rect.h;
+
+ label_rect.Deflate(2,2);
+
+ // and around the picture, if any:
+ if (img_h != 0) {
+ switch (picture_loc) {
+ default:
+ case 0: // the four corner positions
+ case 2: // and the center position
+ case 4: // don't affect the text position
+ case 6:
+ case 8:
+ break;
+
+ case 1: // north
+ label_rect.y += img_h;
+ label_rect.h -= img_h;
+ break;
+
+ case 3: // west
+ label_rect.x += img_w;
+ label_rect.w -= img_w;
+ break;
+
+ case 5: // east
+ label_rect.w -= img_w;
+ break;
+
+ case 7: // south
+ label_rect.h -= img_h;
+ break;
+ }
+ }
+
+ return label_rect;
+}
+
+// +--------------------------------------------------------------------+
+
+Rect
+ImageBox::CalcPictureRect()
+{
+ if (target_rect.w > 0 && target_rect.h > 0)
+ return target_rect;
+
+ int w = rect.w;
+ int h = rect.h;
+ int img_w = picture.Width();
+ int img_h = picture.Height();
+
+ if (img_h > h) img_h = h-2;
+ if (img_w > w) img_w = w-2;
+
+ int img_x_offset = 0;
+ int img_y_offset = 0;
+
+ switch (picture_loc) {
+ default:
+ // TOP ROW:
+ case 0: break;
+
+ case 1: img_x_offset = (w/2-img_w/2);
+ break;
+
+ case 2: img_x_offset = w - img_w;
+ break;
+
+ // MIDDLE ROW:
+ case 3: img_y_offset = (h/2-img_h/2);
+ break;
+ case 4: img_x_offset = (w/2-img_w/2);
+ img_y_offset = (h/2-img_h/2);
+ break;
+ case 5: img_x_offset = w - img_w;
+ img_y_offset = (h/2-img_h/2);
+ break;
+
+ // BOTTOM ROW:
+ case 6:
+ img_y_offset = h - img_h;
+ break;
+ case 7: img_x_offset = (w/2-img_w/2);
+ img_y_offset = h - img_h;
+ break;
+ case 8: img_x_offset = w - img_w;
+ img_y_offset = h - img_h;
+ break;
+ }
+
+ Rect img_rect;
+ img_rect.x = img_x_offset;
+ img_rect.y = img_y_offset;
+ img_rect.w = img_w;
+ img_rect.h = img_h;
+
+ return img_rect;
+}
+
+// +--------------------------------------------------------------------+
+
+int ImageBox::OnMouseMove(int x, int y)
+{
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+int ImageBox::OnLButtonDown(int x, int y)
+{
+ return ActiveWindow::OnLButtonDown(x,y);
+}
+
+int ImageBox::OnLButtonUp(int x, int y)
+{
+ return ActiveWindow::OnLButtonUp(x,y);
+}
+
+int ImageBox::OnClick()
+{
+ return ActiveWindow::OnClick();
+}
+
+int ImageBox::OnMouseEnter(int mx, int my)
+{
+ return ActiveWindow::OnMouseEnter(mx, my);
+}
+
+int ImageBox::OnMouseExit(int mx, int my)
+{
+ return ActiveWindow::OnMouseExit(mx, my);
+}
+
+// +--------------------------------------------------------------------+
+
+void ImageBox::GetPicture(Bitmap& img) const
+{
+ img.CopyBitmap(picture);
+}
+
+void ImageBox::SetPicture(const Bitmap& img)
+{
+ picture.CopyBitmap(img);
+ picture.AutoMask();
+ picture.MakeTexture();
+}
+
+int ImageBox::GetPictureLocation() const
+{
+ return picture_loc;
+}
+
+void ImageBox::SetPictureLocation(int n)
+{
+ if (picture_loc != n && n >= 0 && n <= 8) {
+ picture_loc = n;
+ }
+}
diff --git a/nGenEx/ImageBox.h b/nGenEx/ImageBox.h
new file mode 100644
index 0000000..8a60cf7
--- /dev/null
+++ b/nGenEx/ImageBox.h
@@ -0,0 +1,71 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ImageBox.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ImageBox class
+*/
+
+#ifndef ImageBox_h
+#define ImageBox_h
+
+#include "Types.h"
+#include "ActiveWindow.h"
+#include "Bitmap.h"
+
+// +--------------------------------------------------------------------+
+
+class ImageBox : public ActiveWindow
+{
+public:
+ ImageBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD id=0);
+ ImageBox(Screen* s, int ax, int ay, int aw, int ah, DWORD id=0);
+ virtual ~ImageBox();
+
+ // Operations:
+ virtual void Draw();
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnClick();
+ virtual int OnMouseEnter(int x, int y);
+ virtual int OnMouseExit(int x, int y);
+
+ // Property accessors:
+ int GetBlendMode() const { return blend_mode; }
+ void SetBlendMode(int blend) { blend_mode = blend; }
+ bool GetBorder() const { return border; }
+ void SetBorder(bool bNewValue) { border = bNewValue; }
+ Color GetBorderColor() const { return border_color; }
+ void SetBorderColor(Color c) { border_color = c; }
+ void GetPicture(Bitmap& img) const;
+ void SetPicture(const Bitmap& img);
+ int GetPictureLocation() const;
+ void SetPictureLocation(int nNewValue);
+ Rect GetTargetRect() const { return target_rect; }
+ void SetTargetRect(const Rect& r) { target_rect = r; }
+
+protected:
+ virtual void DrawTabbedText();
+
+ Rect CalcLabelRect(int img_w, int img_h);
+ Rect CalcPictureRect();
+
+ bool border;
+ Color border_color;
+ Bitmap picture;
+ int picture_loc;
+ int blend_mode;
+ Rect target_rect;
+};
+
+#endif ImageBox_h
+
diff --git a/nGenEx/ImgView.cpp b/nGenEx/ImgView.cpp
new file mode 100644
index 0000000..7cfce10
--- /dev/null
+++ b/nGenEx/ImgView.cpp
@@ -0,0 +1,79 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ImgView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Bitmap "billboard" Image View class
+*/
+
+#include "MemDebug.h"
+#include "ImgView.h"
+#include "Color.h"
+#include "Window.h"
+#include "Video.h"
+#include "Bitmap.h"
+#include "Screen.h"
+
+// +--------------------------------------------------------------------+
+
+ImgView::ImgView(Window* c, Bitmap* bmp)
+ : View(c), img(bmp), width(0), height(0), x_offset(0), y_offset(0),
+ blend(Video::BLEND_SOLID)
+{
+ if (img) {
+ width = img->Width();
+ height = img->Height();
+ }
+
+ if (width < c->Width())
+ x_offset = (c->Width() - width) / 2;
+
+ if (height < c->Height())
+ y_offset = (c->Height() - height) / 2;
+}
+
+ImgView::~ImgView()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ImgView::Refresh()
+{
+ if (img && width > 0 && height > 0)
+ window->DrawBitmap(x_offset,
+ y_offset,
+ x_offset + width,
+ y_offset + height,
+ img,
+ blend);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ImgView::SetPicture(Bitmap* bmp)
+{
+ img = bmp;
+ width = 0;
+ height = 0;
+ x_offset = 0;
+ y_offset = 0;
+
+ if (img) {
+ width = img->Width();
+ height = img->Height();
+ }
+
+ if (window) {
+ x_offset = (window->Width() - width) / 2;
+ y_offset = (window->Height() - height) / 2;
+ }
+}
diff --git a/nGenEx/ImgView.h b/nGenEx/ImgView.h
new file mode 100644
index 0000000..8de4b8c
--- /dev/null
+++ b/nGenEx/ImgView.h
@@ -0,0 +1,51 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ImgView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Bitmap "Billboard" View class
+*/
+
+#ifndef ImgView_h
+#define ImgView_h
+
+#include "Types.h"
+#include "View.h"
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+
+// +--------------------------------------------------------------------+
+
+class ImgView : public View
+{
+public:
+ static const char* TYPENAME() { return "ImgView"; }
+
+ ImgView(Window* c, Bitmap* bmp);
+ virtual ~ImgView();
+
+ // Operations:
+ virtual void Refresh();
+
+ virtual Bitmap* GetPicture() const { return img; }
+ virtual void SetPicture(Bitmap* bmp);
+ virtual int GetBlend() const { return blend; }
+ virtual void SetBlend(int b) { blend = b; }
+
+protected:
+ Bitmap* img;
+ int x_offset, y_offset;
+ int width, height;
+ int blend;
+};
+
+#endif ImgView_h
+
diff --git a/nGenEx/Joystick.cpp b/nGenEx/Joystick.cpp
new file mode 100644
index 0000000..ba3b3ae
--- /dev/null
+++ b/nGenEx/Joystick.cpp
@@ -0,0 +1,914 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Joystick.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Joystick Input class
+*/
+
+#include "MemDebug.h"
+#include "Joystick.h"
+#include "MachineInfo.h"
+#include "Game.h"
+
+#define DIRECTINPUT_VERSION 0x0700
+
+#include <dinput.h>
+
+#define JOY_POVUPRIGHT 4500
+#define JOY_POVDNRIGHT 13500
+#define JOY_POVDNLEFT 22500
+#define JOY_POVUPLEFT 31500
+
+// +--------------------------------------------------------------------+
+// DIRECT INPUT SUPPORT
+
+const int MAX_DEVICES = 8;
+
+static LPDIRECTINPUT7 pdi = 0;
+static LPDIRECTINPUTDEVICE7 pdev = 0;
+static DIDEVICEINSTANCE devices[MAX_DEVICES];
+static int ndev = 0;
+static int idev = -1;
+static int strikes = 3;
+
+static Joystick* joystick = 0;
+
+void DirectInputError(const char* msg, HRESULT err);
+char* DIErrStr(HRESULT hr);
+void ReleaseDirectInput();
+
+// +--------------------------------------------------------------------+
+
+Joystick::Joystick()
+ : x(0), y(0), z(0), p(0), r(0), w(0), t(0)
+{
+ if (!joystick)
+ joystick = this;
+
+ select = 1;
+ rudder = 0;
+ throttle = 1;
+ sensitivity = 25;
+ dead_zone = 100;
+
+ for (int i = 0; i < MotionController::MaxActions; i++)
+ action[i] = false;
+
+ for (i = 0; i < KEY_MAP_SIZE; i++)
+ map[i] = 0;
+
+ for (i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ hat[i][j] = false;
+ }
+ }
+
+ map_axis[0] = KEY_JOY_AXIS_X;
+ map_axis[1] = KEY_JOY_AXIS_Y;
+ map_axis[2] = KEY_JOY_AXIS_RZ;
+ map_axis[3] = KEY_JOY_AXIS_S0;
+
+ inv_axis[0] = false;
+ inv_axis[1] = false;
+ inv_axis[2] = false;
+ inv_axis[3] = false;
+
+ if (MachineInfo::GetDirectXVersion() < MachineInfo::DX_7) {
+ Print("Joystick: DI7 not found, using multimedia library\n");
+ pdi = 0;
+ pdev = 0;
+ }
+
+ else if (!pdi) {
+ HRESULT hr = DirectInputCreateEx(Game::GetHINST(),
+ DIRECTINPUT_VERSION,
+ IID_IDirectInput7,
+ (void**)&pdi,
+ NULL);
+ if FAILED(hr) {
+ DirectInputError("Failed to initialize DI7", hr);
+ pdi = 0;
+ pdev = 0;
+ }
+ else {
+ Print("Joystick: initialized DI7 pdi = %08x\n", (DWORD) pdi);
+ }
+ }
+}
+
+Joystick::~Joystick()
+{
+ ReleaseDirectInput();
+ joystick = 0;
+}
+
+void ReleaseDirectInput()
+{
+ if (pdev) {
+ pdev->Unacquire();
+ pdev->Release();
+ pdev = 0;
+ }
+
+ if (pdi) {
+ pdi->Release();
+ pdi = 0;
+ }
+}
+
+Joystick* Joystick::GetInstance()
+{
+ return joystick;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Joystick::MapKeys(KeyMapEntry* mapping, int nkeys)
+{
+ ZeroMemory(map, sizeof(map));
+
+ for (int i = 0; i < nkeys; i++) {
+ KeyMapEntry k = mapping[i];
+
+ if (k.act >= KEY_MAP_FIRST && k.act < KEY_MAP_LAST) {
+ if (k.act == KEY_JOY_SENSE)
+ sensitivity = k.key;
+
+ else if (k.act == KEY_JOY_DEAD_ZONE)
+ dead_zone = k.key;
+
+ else if (k.act == KEY_JOY_SWAP)
+ swapped = k.key;
+
+ else if (k.act == KEY_JOY_RUDDER)
+ rudder = k.key;
+
+ else if (k.act == KEY_JOY_THROTTLE)
+ throttle = k.key;
+
+ else if (k.act == KEY_JOY_SELECT)
+ select = k.key;
+
+
+ else if (k.act == KEY_AXIS_YAW)
+ map_axis[0] = k.key;
+
+ else if (k.act == KEY_AXIS_PITCH)
+ map_axis[1] = k.key;
+
+ else if (k.act == KEY_AXIS_ROLL)
+ map_axis[2] = k.key;
+
+ else if (k.act == KEY_AXIS_THROTTLE)
+ map_axis[3] = k.key;
+
+
+ else if (k.act == KEY_AXIS_YAW_INVERT)
+ inv_axis[0] = k.key ? true : false;
+
+ else if (k.act == KEY_AXIS_PITCH_INVERT)
+ inv_axis[1] = k.key ? true : false;
+
+ else if (k.act == KEY_AXIS_ROLL_INVERT)
+ inv_axis[2] = k.key ? true : false;
+
+ else if (k.act == KEY_AXIS_THROTTLE_INVERT)
+ inv_axis[3] = k.key ? true : false;
+
+ else if (k.key >= KEY_JOY_1 && k.key <= KEY_JOY_32)
+ map[k.act] = k.key;
+
+ else if (k.alt >= KEY_JOY_1 && k.alt <= KEY_JOY_32)
+ map[k.act] = k.alt;
+
+ else if (k.joy >= KEY_JOY_1 && k.joy <= KEY_JOY_32)
+ map[k.act] = k.joy;
+
+ else if (k.key >= KEY_POV_0_UP && k.key <= KEY_POV_3_RIGHT)
+ map[k.act] = k.key;
+
+ else if (k.alt >= KEY_POV_0_UP && k.alt <= KEY_POV_3_RIGHT)
+ map[k.act] = k.alt;
+
+ else if (k.joy >= KEY_POV_0_UP && k.joy <= KEY_POV_3_RIGHT)
+ map[k.act] = k.joy;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static inline double sqr(double a) { return a*a; }
+
+BOOL FAR PASCAL EnumJoystick(LPCDIDEVICEINSTANCE pdinst, LPVOID pvSelect)
+{
+ CopyMemory(&devices[ndev++], pdinst, pdinst->dwSize);
+
+ ::Print("EnumJoystick %d: '%s'\n", ndev, pdinst->tszInstanceName);
+ ::Print(" guid: {%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x} \n",
+ (DWORD) pdinst->guidInstance.Data1,
+ (WORD) pdinst->guidInstance.Data2,
+ (WORD) pdinst->guidInstance.Data3,
+ (BYTE) pdinst->guidInstance.Data4[0],
+ (BYTE) pdinst->guidInstance.Data4[1],
+ (BYTE) pdinst->guidInstance.Data4[2],
+ (BYTE) pdinst->guidInstance.Data4[3],
+ (BYTE) pdinst->guidInstance.Data4[4],
+ (BYTE) pdinst->guidInstance.Data4[5]);
+
+ if (ndev < MAX_DEVICES)
+ return DIENUM_CONTINUE;
+
+ return DIENUM_STOP;
+}
+
+bool CreateDevice(int select)
+{
+ if (!pdi || ndev < select)
+ return false;
+
+ LPCDIDEVICEINSTANCE pdinst = &devices[select-1];
+
+ ::Print("Joystick CreateDevice(%d)\n", select);
+ ::Print(" name: %s\n\n", pdinst->tszInstanceName);
+
+ // release current device before trying to get another:
+ if (pdev) {
+ pdev->Unacquire();
+ pdev->Release();
+ pdev = 0;
+ }
+
+ HRESULT hr = DI_OK;
+ // Create the DirectInput joystick device:
+ hr = pdi->CreateDeviceEx(pdinst->guidInstance,
+ IID_IDirectInputDevice7,
+ (void**)&pdev,
+ NULL);
+
+ if (hr != DI_OK || pdev == 0) {
+ DirectInputError("Create Device Ex failed", hr);
+ return false;
+ }
+
+ // Set the data format:
+ hr = pdev->SetDataFormat(&c_dfDIJoystick);
+
+ if (hr != DI_OK) {
+ DirectInputError("Set Data Format failed", hr);
+ pdev->Release();
+ pdev = 0;
+ return false;
+ }
+
+ // Set the coop level:
+ hr = pdev->SetCooperativeLevel(Game::GetHWND(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
+
+ if (hr != DI_OK) {
+ DirectInputError("Set Cooperative Level failed", hr);
+ pdev->Release();
+ return false;
+ }
+
+ // Set data ranges
+ DIPROPRANGE diprg;
+ diprg.lMin = -32768;
+ diprg.lMax = +32768;
+
+ diprg.diph.dwSize = sizeof(diprg);
+ diprg.diph.dwHeaderSize = sizeof(diprg.diph);
+ diprg.diph.dwObj = DIJOFS_X;
+ diprg.diph.dwHow = DIPH_BYOFFSET;
+ pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
+
+ diprg.diph.dwObj = DIJOFS_Y;
+ pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
+
+ diprg.diph.dwObj = DIJOFS_Z;
+ pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
+
+ diprg.diph.dwObj = DIJOFS_RX;
+ pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
+
+ diprg.diph.dwObj = DIJOFS_RY;
+ pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
+
+ diprg.diph.dwObj = DIJOFS_RZ;
+ pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
+
+ diprg.diph.dwObj = DIJOFS_SLIDER(0);
+ pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
+
+ diprg.diph.dwObj = DIJOFS_SLIDER(1);
+ pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
+
+ ::Print("Created joystick %d (pdev = %08x)\n", select, (DWORD) pdev);
+ idev = select;
+ return true;
+}
+
+void
+Joystick::EnumerateDevices()
+{
+ if (!pdi) {
+ Print("Joystick: no DI7, unable to enumerate devices\n");
+ ndev = 0;
+ }
+
+ else if (ndev < 1) {
+ Print("Joystick: preparing to enumerate devices\n");
+
+ ndev = 0;
+ HRESULT hr =
+ pdi->EnumDevices(DIDEVTYPE_JOYSTICK,
+ EnumJoystick,
+ (LPVOID) 0,
+ DIEDFL_ATTACHEDONLY);
+
+ if (FAILED(hr)) {
+ DirectInputError("Failed to enumerate devices", hr);
+ ReleaseDirectInput();
+ }
+
+ else if (ndev < 1) {
+ Print("Joystick: no devices found\n");
+ ReleaseDirectInput();
+ }
+ }
+}
+
+int
+Joystick::NumDevices()
+{
+ return ndev;
+}
+
+const char*
+Joystick::GetDeviceName(int i)
+{
+ if (i >= 0 && i < ndev)
+ return devices[i].tszInstanceName;
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+static DIJOYSTATE joystate;
+static JOYINFOEX joyinfo;
+
+int
+Joystick::ReadRawAxis(int a)
+{
+ if (!joystick)
+ return 0;
+
+ int result = 0;
+
+ if (pdev) {
+ switch (a) {
+ case KEY_JOY_AXIS_X: result = joystate.lX; break;
+ case KEY_JOY_AXIS_Y: result = joystate.lY; break;
+ case KEY_JOY_AXIS_Z: result = joystate.lZ; break;
+ case KEY_JOY_AXIS_RX: result = joystate.lRx; break;
+ case KEY_JOY_AXIS_RY: result = joystate.lRy; break;
+ case KEY_JOY_AXIS_RZ: result = joystate.lRz; break;
+ case KEY_JOY_AXIS_S0: result = joystate.rglSlider[0]; break;
+ case KEY_JOY_AXIS_S1: result = joystate.rglSlider[1]; break;
+ }
+ }
+
+ else {
+ switch (a) {
+ case KEY_JOY_AXIS_X:
+ if (joyinfo.dwFlags & JOY_RETURNX)
+ result = joyinfo.dwXpos;
+ break;
+
+ case KEY_JOY_AXIS_Y:
+ if (joyinfo.dwFlags & JOY_RETURNY)
+ result = joyinfo.dwYpos;
+ break;
+
+ case KEY_JOY_AXIS_Z:
+ if (joyinfo.dwFlags & JOY_RETURNZ)
+ result = joyinfo.dwZpos;
+ break;
+
+ case KEY_JOY_AXIS_RZ:
+ if (joyinfo.dwFlags & JOY_RETURNR)
+ result = joyinfo.dwRpos;
+ break;
+ }
+ }
+
+ return result;
+}
+
+double
+Joystick::ReadAxisDI(int a)
+{
+ if (a < 0 || a > 3)
+ return 0;
+
+ double result = 0;
+
+ switch (map_axis[a]) {
+ case KEY_JOY_AXIS_X: result = joystate.lX; break;
+ case KEY_JOY_AXIS_Y: result = joystate.lY; break;
+ case KEY_JOY_AXIS_Z: result = joystate.lZ; break;
+ case KEY_JOY_AXIS_RX: result = joystate.lRx; break;
+ case KEY_JOY_AXIS_RY: result = joystate.lRy; break;
+ case KEY_JOY_AXIS_RZ: result = joystate.lRz; break;
+ case KEY_JOY_AXIS_S0: result = joystate.rglSlider[0]; break;
+ case KEY_JOY_AXIS_S1: result = joystate.rglSlider[1]; break;
+ }
+
+ if (a < 3) {
+ // ignore small movements:
+ if (result > dead_zone) result -= dead_zone;
+ else if (result < -dead_zone) result += dead_zone;
+ else result = 0;
+
+ double scale = 1.0 / (32768.0-dead_zone);
+
+ if (result >= 0)
+ result = sqr(result * scale);
+ else
+ result = sqr(result * scale) * -1.0;
+
+ if (inv_axis[a])
+ result = -result;
+ }
+ else {
+ result = (result+32768.0) / 65536.0;
+
+ if (inv_axis[a])
+ result = 1 - result;
+ }
+
+
+ return result;
+}
+
+double
+Joystick::ReadAxisMM(int a)
+{
+ if (a < 0 || a > 3)
+ return 0;
+
+ double result = 0;
+
+ switch (map_axis[a]) {
+ case KEY_JOY_AXIS_X:
+ if (joyinfo.dwFlags & JOY_RETURNX)
+ result = joyinfo.dwXpos - 32768;
+ break;
+
+ case KEY_JOY_AXIS_Y:
+ if (joyinfo.dwFlags & JOY_RETURNY)
+ result = joyinfo.dwYpos - 32768;
+ break;
+
+ case KEY_JOY_AXIS_Z:
+ if (joyinfo.dwFlags & JOY_RETURNZ)
+ result = joyinfo.dwZpos - 32768;
+ break;
+
+ case KEY_JOY_AXIS_RZ:
+ if (joyinfo.dwFlags & JOY_RETURNR)
+ result = joyinfo.dwRpos - 32768;
+ break;
+ }
+
+ if (a < 3) {
+ // ignore small movements:
+ if (result > dead_zone) result -= dead_zone;
+ else if (result < -dead_zone) result += dead_zone;
+ else result = 0;
+
+ double scale = 1.0 / (32768.0-dead_zone);
+
+ if (result >= 0)
+ result = sqr(result * scale);
+ else
+ result = sqr(result * scale) * -1.0;
+
+ if (inv_axis[a])
+ result = -result;
+ }
+ else {
+ result = (result+32768.0) / 65536.0;
+
+ if (inv_axis[a])
+ result = 1 - result;
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Joystick::Acquire()
+{
+ t = x = y = z = p = r = w = 0;
+ for (int i = 0; i < MotionController::MaxActions; i++)
+ action[i] = false;
+
+ for (i = 0; i < 4; i++)
+ for (int j = 0; j < 4; j++)
+ hat[i][j] = false;
+
+ if (select == 0)
+ return;
+
+ //============================================================
+ //
+ // FIRST TRY DIRECT INPUT
+
+ bool acquired = false;
+
+ if (pdi) {
+ if (idev != select) {
+ if (ndev < 1)
+ EnumerateDevices();
+
+ if (CreateDevice(select))
+ pdev->Acquire();
+ }
+
+ if (pdev) {
+ HRESULT hr = 0;
+
+ hr = pdev->Poll();
+ hr = pdev->GetDeviceState(sizeof(joystate), &joystate);
+
+ if (hr == DIERR_INPUTLOST) {
+ pdev->Acquire();
+
+ hr = pdev->Poll();
+ hr = pdev->GetDeviceState(sizeof(joystate), &joystate);
+
+ if (FAILED(hr)) {
+ strikes--;
+ ::Print("Joystick could not re-acquire joystick (%08x)\n", hr);
+
+ // give up before you hurt yourself:
+ if (strikes <= 0) {
+ ReleaseDirectInput();
+ ndev = 0;
+ select = 0;
+ }
+
+ return;
+ }
+ }
+
+ for (int i = 0; i < 32; i++)
+ action[i] = (joystate.rgbButtons[i] & 0x80) != 0;
+
+ double joy_x = ReadAxisDI(0);
+ double joy_y = ReadAxisDI(1);
+ double joy_r = rudder ? ReadAxisDI(2) : 0;
+ double joy_t = throttle ? ReadAxisDI(3) : 0;
+
+ int joy_p = joystate.rgdwPOV[0];
+
+ ProcessAxes(joy_x, joy_y, joy_r, joy_t);
+
+ for (i = 0; i < 4; i++)
+ ProcessHat(i, joystate.rgdwPOV[i]);
+
+ acquired = true;
+ }
+ }
+
+ //============================================================
+ //
+ // THEN TRY WINDOWS MULTIMEDIA LIBRARY
+
+ if (!acquired) {
+ memset(&joyinfo, 0, sizeof(JOYINFOEX));
+ joyinfo.dwSize = sizeof(JOYINFOEX);
+ joyinfo.dwFlags = JOY_RETURNALL;
+
+ HRESULT hr = 0;
+
+ if (select == 1)
+ hr = joyGetPosEx(JOYSTICKID1, &joyinfo);
+
+ else if (select == 2)
+ hr = joyGetPosEx(JOYSTICKID2, &joyinfo);
+
+ if (hr != 0) {
+ Print("\nJoystick::Acquire() joyGetPosEx %d failed (err=%08x)\n\n", select, hr);
+ select = 0;
+ }
+
+ action[0] = (joyinfo.dwButtons & JOY_BUTTON1) ? true : false;
+ action[1] = (joyinfo.dwButtons & JOY_BUTTON2) ? true : false;
+ action[2] = (joyinfo.dwButtons & JOY_BUTTON3) ? true : false;
+ action[3] = (joyinfo.dwButtons & JOY_BUTTON4) ? true : false;
+
+ double joy_x = ReadAxisMM(0);
+ double joy_y = ReadAxisMM(1);
+ double joy_r = rudder ? ReadAxisMM(2) : 0;
+ double joy_t = throttle ? ReadAxisMM(3) : 0;
+
+ ProcessAxes(joy_x, joy_y, joy_r, joy_t);
+ ProcessHat(0, joyinfo.dwPOV);
+ }
+
+ // lateral translations:
+ if (KeyDownMap(KEY_PLUS_Y)) y = 1;
+ else if (KeyDownMap(KEY_MINUS_Y)) y = -1;
+
+ if (KeyDownMap(KEY_PLUS_Z)) z = 1;
+ else if (KeyDownMap(KEY_MINUS_Z)) z = -1;
+
+ if (KeyDownMap(KEY_MINUS_X)) x = -1;
+ else if (KeyDownMap(KEY_PLUS_X)) x = 1;
+
+ // button-based turns:
+ const double steps=10;
+ static double p1=0, r1=0, w1=0;
+
+ // if roll and yaw are swapped --------------------------
+ if (swapped) {
+ // yaw:
+ if (KeyDownMap(KEY_ROLL_LEFT)) { if (w1<steps) w1+=1; w = -sqr(w1/steps); }
+ else if (KeyDownMap(KEY_ROLL_RIGHT)) { if (w1<steps) w1+=1; w = sqr(w1/steps); }
+
+ // roll:
+ if (KeyDownMap(KEY_YAW_LEFT)) { if (r1<steps) r1+=1; r = sqr(r1/steps); }
+ else if (KeyDownMap(KEY_YAW_RIGHT)) { if (r1<steps) r1+=1; r = -sqr(r1/steps); }
+ else w1 = 0;
+ }
+
+ // else roll and yaw are NOT swapped ---------------------
+ else {
+ // roll:
+ if (KeyDownMap(KEY_ROLL_LEFT)) { if (r1<steps) r1+=1; r = sqr(r1/steps); }
+ else if (KeyDownMap(KEY_ROLL_RIGHT)) { if (r1<steps) r1+=1; r = -sqr(r1/steps); }
+
+ // yaw left-right
+ if (KeyDownMap(KEY_YAW_LEFT)) { if (w1<steps) w1+=1; w = -sqr(w1/steps); }
+ else if (KeyDownMap(KEY_YAW_RIGHT)) { if (w1<steps) w1+=1; w = sqr(w1/steps); }
+ else w1 = 0;
+ }
+
+ // pitch --------------------------------------------------
+ if (KeyDownMap(KEY_PITCH_UP)) { if (p1<steps) p1+=1; p = -sqr(p1/steps); }
+ else if (KeyDownMap(KEY_PITCH_DOWN)) { if (p1<steps) p1+=1; p = sqr(p1/steps); }
+ else p1 = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Joystick::ProcessAxes(double joy_x, double joy_y, double joy_r, double joy_t)
+{
+ int roll_enable = 0;
+
+ joy_y *= -1;
+ joy_t = 1 - joy_t;
+
+ if (map[KEY_ROLL_ENABLE])
+ roll_enable = action[map[KEY_ROLL_ENABLE] - KEY_JOY_1];
+
+ // if roll and yaw are swapped --------------------------
+ if (swapped) {
+ if (roll_enable) {
+ w = joy_x;
+ r = joy_r;
+ }
+ else {
+ w = joy_r;
+ r = -joy_x;
+ }
+ }
+
+ // else roll and yaw are NOT swapped ---------------------
+ else {
+ if (roll_enable) {
+ w = joy_r;
+ r = joy_x;
+ }
+ else {
+ w = joy_x;
+ r = -joy_r;
+ }
+ }
+
+ p = joy_y;
+
+ // read throttle:
+ if (throttle) {
+ static double init_throttle = -1;
+ static bool latch_throttle = false;
+
+ if (init_throttle < 0)
+ init_throttle = joy_t;
+ else if (init_throttle != joy_t)
+ latch_throttle = true;
+
+ if (latch_throttle)
+ t = joy_t;
+ else
+ t = 0;
+ }
+ else {
+ t = 0;
+ }
+}
+
+void
+Joystick::ProcessHat(int i, DWORD joy_pov)
+{
+ if (i < 0 || i > 3) return;
+
+ if (LOWORD(joy_pov) == 0xFFFF)
+ return;
+
+ switch (joy_pov) {
+ case JOY_POVFORWARD: hat[i][0] = true; break;
+ case JOY_POVBACKWARD: hat[i][1] = true; break;
+ case JOY_POVLEFT: hat[i][2] = true; break;
+ case JOY_POVRIGHT: hat[i][3] = true; break;
+
+ case JOY_POVUPRIGHT: hat[i][0] = true;
+ hat[i][3] = true; break;
+
+ case JOY_POVDNRIGHT: hat[i][1] = true;
+ hat[i][3] = true; break;
+
+ case JOY_POVDNLEFT: hat[i][1] = true;
+ hat[i][2] = true; break;
+
+ case JOY_POVUPLEFT: hat[i][0] = true;
+ hat[i][2] = true; break;
+
+ default: break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool Joystick::KeyDown(int key)
+{
+ if (!joystick)
+ return false;
+
+ if (key >= KEY_JOY_1 && key <= KEY_JOY_32)
+ return joystick->action[key - KEY_JOY_1];
+
+ else if (key >= KEY_POV_0_UP && key <= KEY_POV_0_RIGHT)
+ return joystick->hat[0][key - KEY_POV_0_UP];
+
+ else if (key >= KEY_POV_1_UP && key <= KEY_POV_1_RIGHT)
+ return joystick->hat[1][key - KEY_POV_1_UP];
+
+ else if (key >= KEY_POV_2_UP && key <= KEY_POV_2_RIGHT)
+ return joystick->hat[2][key - KEY_POV_2_UP];
+
+ else if (key >= KEY_POV_3_UP && key <= KEY_POV_3_RIGHT)
+ return joystick->hat[3][key - KEY_POV_3_UP];
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool Joystick::KeyDownMap(int key)
+{
+ if (!joystick)
+ return false;
+
+ if (key >= KEY_MAP_FIRST && key <= KEY_MAP_LAST && joystick->map[key])
+ return KeyDown(joystick->map[key]);
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+int Joystick::GetAxisMap(int n)
+{
+ if (!joystick || n < 0 || n > 3)
+ return -1;
+
+ return joystick->map_axis[n];
+}
+
+int Joystick::GetAxisInv(int n)
+{
+ if (!joystick || n < 0 || n > 3)
+ return -1;
+
+ return joystick->inv_axis[n];
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DirectInputError(const char* msg, HRESULT err)
+{
+ static int report = 50;
+ if (report > 0)
+ report--;
+ else
+ return;
+
+ Print(" DirectInput7: %s. [%s]\n", msg, DIErrStr(err));
+}
+
+static char errstrbuf[128];
+
+char* DIErrStr(HRESULT hr)
+{
+ switch (hr) {
+ default:
+ sprintf(errstrbuf, "Unrecognized error value = %08x.", hr);
+ return errstrbuf;
+
+ case DI_OK:
+ return "No error.";
+
+ case DI_BUFFEROVERFLOW:
+ return "The device buffer overflowed and some input was lost. This value is equal to the S_FALSE standard COM return value.";
+ case DI_DOWNLOADSKIPPED:
+ return "The parameters of the effect were successfully updated, but the effect could not be downloaded because the associated device was not acquired in exclusive mode.";
+ case DI_EFFECTRESTARTED:
+ return "The effect was stopped, the parameters were updated, and the effect was restarted.";
+ case DI_POLLEDDEVICE:
+ return "The device is a polled device. As a result, device buffering does not collect any data and event notifications is not signaled until the IDirectInputDevice7::Poll method is called.";
+ case DI_TRUNCATED:
+ return "The parameters of the effect were successfully updated, but some of them were beyond the capabilities of the device and were truncated to the nearest supported value.";
+ case DI_TRUNCATEDANDRESTARTED:
+ return "Equal to DI_EFFECTRESTARTED | DI_TRUNCATED";
+ case DIERR_ACQUIRED:
+ return "The operation cannot be performed while the device is acquired.";
+ case DIERR_ALREADYINITIALIZED:
+ return "This object is already initialized";
+ case DIERR_BADDRIVERVER:
+ return "The object could not be created due to an incompatible driver version or mismatched or incomplete driver components.";
+ case DIERR_BETADIRECTINPUTVERSION:
+ return "The application was written for an unsupported prerelease version of DirectInput.";
+ case DIERR_DEVICEFULL:
+ return "The device is full.";
+ case DIERR_DEVICENOTREG:
+ return "The device or device instance is not registered with DirectInput. This value is equal to the REGDB_E_CLASSNOTREG standard COM return value.";
+ case DIERR_EFFECTPLAYING:
+ return "The parameters were updated in memory but were not downloaded to the device because the device does not support updating an effect while it is still playing.";
+ case DIERR_HASEFFECTS:
+ return "The device cannot be reinitialized because there are still effects attached to it.";
+ case DIERR_GENERIC:
+ return "An undetermined error occurred inside the DirectInput subsystem. This value is equal to the E_FAIL standard COM return value.";
+ case DIERR_HANDLEEXISTS:
+ return "The device already has an event notification associated with it. This value is equal to the E_ACCESSDENIED standard COM return value.";
+ case DIERR_INCOMPLETEEFFECT:
+ return "The effect could not be downloaded because essential information is missing. For example, no axes have been associated with the effect, or no type-specific information has been supplied.";
+ case DIERR_INPUTLOST:
+ return "Access to the input device has been lost. It must be reacquired.";
+ case DIERR_INVALIDPARAM:
+ return "An invalid parameter was passed to the returning function, or the object was not in a state that permitted the function to be called. This value is equal to the E_INVALIDARG standard COM return value.";
+ case DIERR_MOREDATA:
+ return "Not all the requested information fit into the buffer.";
+ case DIERR_NOAGGREGATION:
+ return "This object does not support aggregation.";
+ case DIERR_NOINTERFACE:
+ return "The specified interface is not supported by the object. This value is equal to the E_NOINTERFACE standard COM return value.";
+ case DIERR_NOTACQUIRED:
+ return "The operation cannot be performed unless the device is acquired.";
+ case DIERR_NOTBUFFERED:
+ return "The device is not buffered. Set the DIPROP_BUFFERSIZE property to enable buffering.";
+ case DIERR_NOTDOWNLOADED:
+ return "The effect is not downloaded.";
+ case DIERR_NOTEXCLUSIVEACQUIRED:
+ return "The operation cannot be performed unless the device is acquired in DISCL_EXCLUSIVE mode.";
+ case DIERR_NOTFOUND:
+ return "The requested object does not exist.";
+ case DIERR_NOTINITIALIZED:
+ return "This object has not been initialized.";
+ case DIERR_OLDDIRECTINPUTVERSION:
+ return "The application requires a newer version of DirectInput.";
+ case DIERR_OUTOFMEMORY:
+ return "The DirectInput subsystem could not allocate sufficient memory to complete the call. This value is equal to the E_OUTOFMEMORY standard COM return value.";
+ case DIERR_REPORTFULL:
+ return "More information was requested to be sent than can be sent to the device.";
+ case DIERR_UNPLUGGED:
+ return "The operation could not be completed because the device is not plugged in.";
+ case DIERR_UNSUPPORTED:
+ return "The function called is not supported at this time. This value is equal to the E_NOTIMPL standard COM return value.";
+ }
+}
+
diff --git a/nGenEx/Joystick.h b/nGenEx/Joystick.h
new file mode 100644
index 0000000..e80a06b
--- /dev/null
+++ b/nGenEx/Joystick.h
@@ -0,0 +1,82 @@
+/* Project nGen
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: Joystick.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Joystick Input class
+*/
+
+#ifndef Joystick_h
+#define Joystick_h
+
+#include "MotionController.h"
+
+// +--------------------------------------------------------------------+
+
+class Joystick : public MotionController
+{
+public:
+ static const char* TYPENAME() { return "Joystick"; }
+
+ Joystick();
+ virtual ~Joystick();
+
+ // setup
+ virtual void MapKeys(KeyMapEntry* mapping, int nkeys);
+
+ // sample the physical device
+ virtual void Acquire();
+
+ // translations
+ virtual double X() { return x; }
+ virtual double Y() { return y; }
+ virtual double Z() { return z; }
+
+ // rotations
+ virtual double Pitch() { return p; }
+ virtual double Roll() { return r; }
+ virtual double Yaw() { return w; }
+ virtual int Center() { return 0; }
+
+ // throttle
+ virtual double Throttle() { return t; }
+ virtual void SetThrottle(double throttle) { t = throttle; }
+
+ // actions
+ virtual int Action(int n) { return action[n]; }
+ virtual int ActionMap(int n) { return KeyDownMap(n); }
+
+ static bool KeyDown(int key);
+ static bool KeyDownMap(int key);
+
+ static Joystick* GetInstance();
+ static void EnumerateDevices();
+ static int NumDevices();
+ static const char* GetDeviceName(int i);
+
+ static int ReadRawAxis(int axis);
+ static int GetAxisMap(int n);
+ static int GetAxisInv(int n);
+
+protected:
+ double ReadAxisDI(int axis);
+ double ReadAxisMM(int axis);
+ void ProcessAxes(double joy_x, double joy_y, double joy_r, double joy_t);
+ void ProcessHat(int i, DWORD joy_pov);
+
+ double x,y,z,p,r,w,t;
+ bool action[MotionController::MaxActions];
+ bool hat[4][4];
+ int map[KEY_MAP_SIZE];
+ int map_axis[4];
+ bool inv_axis[4];
+};
+
+#endif Joystick_h
+
diff --git a/nGenEx/Keyboard.cpp b/nGenEx/Keyboard.cpp
new file mode 100644
index 0000000..52e201f
--- /dev/null
+++ b/nGenEx/Keyboard.cpp
@@ -0,0 +1,217 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Keyboard.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Keyboard Input class
+*/
+
+#include "MemDebug.h"
+#include "Keyboard.h"
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+
+static Keyboard* instance = 0;
+int Keyboard::map[KEY_MAP_SIZE];
+int Keyboard::alt[KEY_MAP_SIZE];
+
+Keyboard::Keyboard()
+ : x(0), y(0), z(0), p(0), r(0), w(0), c(0), p1(0), r1(0), w1(0), t(0)
+{
+ instance = this;
+ sensitivity = 25;
+ dead_zone = 100;
+
+ for (int i = 0; i < MotionController::MaxActions; i++)
+ action[i] = 0;
+
+ memset(map, 0, sizeof(map));
+ memset(alt, 0, sizeof(alt));
+
+ map[KEY_PLUS_X] = 'R';
+ map[KEY_MINUS_X] = 'E';
+ map[KEY_PLUS_Y] = VK_HOME;
+ map[KEY_MINUS_Y] = VK_END;
+ map[KEY_PLUS_Z] = VK_PRIOR; // page up
+ map[KEY_MINUS_Z] = VK_NEXT; // page down
+
+ map[KEY_PITCH_UP] = VK_DOWN;
+ map[KEY_PITCH_DOWN] = VK_UP;
+ map[KEY_YAW_LEFT] = VK_LEFT;
+ map[KEY_YAW_RIGHT] = VK_RIGHT;
+ map[KEY_ROLL_ENABLE] = 0; // used to be VK_CONTROL;
+}
+
+Keyboard::~Keyboard()
+{
+ instance = 0;
+}
+
+Keyboard*
+Keyboard::GetInstance()
+{
+ return instance;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Keyboard::MapKeys(KeyMapEntry* mapping, int nkeys)
+{
+ for (int i = 0; i < nkeys; i++) {
+ KeyMapEntry k = mapping[i];
+
+ if (k.act >= KEY_MAP_FIRST && k.act <= KEY_MAP_LAST) {
+ if (k.key == 0 || k.key > VK_MBUTTON && k.key < KEY_JOY_1) {
+ map[k.act] = k.key;
+ alt[k.act] = k.alt;
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool Keyboard::KeyDown(int key)
+{
+ if (key) {
+ short k = GetAsyncKeyState(key);
+ return (k<0)||(k&1);
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool Keyboard::KeyDownMap(int key)
+{
+ if (key >= KEY_MAP_FIRST && key <= KEY_MAP_LAST && map[key]) {
+ short k = GetAsyncKeyState(map[key]);
+ short a = -1;
+
+ if (alt[key] > 0 && alt[key] < KEY_JOY_1) {
+ a = GetAsyncKeyState(alt[key]);
+ }
+ else {
+ a = !GetAsyncKeyState(VK_SHIFT) &&
+ !GetAsyncKeyState(VK_MENU);
+ }
+
+ return ((k<0)||(k&1)) && ((a<0)||(a&1));
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Keyboard::FlushKeys()
+{
+ for (int i = 0; i < 255; i++)
+ GetAsyncKeyState(i);
+}
+
+// +--------------------------------------------------------------------+
+
+static inline double sqr(double a) { return a; } //*a; }
+
+void
+Keyboard::Acquire()
+{
+ t = x = y = z = p = r = w = c = 0;
+
+ for (int i = 0; i < MotionController::MaxActions; i++)
+ action[i] = 0;
+
+ int speed = 10;
+
+ // lateral translations:
+ if (KeyDownMap(KEY_PLUS_Y)) y = 1;
+ else if (KeyDownMap(KEY_MINUS_Y)) y = -1;
+
+ if (KeyDownMap(KEY_PLUS_Z)) z = 1;
+ else if (KeyDownMap(KEY_MINUS_Z)) z = -1;
+
+ if (KeyDownMap(KEY_MINUS_X)) x = -1;
+ else if (KeyDownMap(KEY_PLUS_X)) x = 1;
+
+ const double steps=10;
+
+ // if roll and yaw are swapped --------------------------
+ if (swapped) {
+ // yaw:
+ if (KeyDownMap(KEY_ROLL_LEFT)) { if (w1<steps) w1+=1; w = -sqr(w1/steps); }
+ else if (KeyDownMap(KEY_ROLL_RIGHT)) { if (w1<steps) w1+=1; w = sqr(w1/steps); }
+
+ // another way to yaw:
+ if (KeyDownMap(KEY_ROLL_ENABLE)) {
+ if (KeyDownMap(KEY_YAW_LEFT)) { if (w1<steps) w1+=1; w = -sqr(w1/steps); }
+ else if (KeyDownMap(KEY_YAW_RIGHT)) { if (w1<steps) w1+=1; w = sqr(w1/steps); }
+ else w1 = 0;
+ }
+
+ // roll:
+ else {
+ if (KeyDownMap(KEY_YAW_LEFT)) { if (r1<steps) r1+=1; r = sqr(r1/steps); }
+ else if (KeyDownMap(KEY_YAW_RIGHT)) { if (r1<steps) r1+=1; r = -sqr(r1/steps); }
+ else r1 = 0;
+ }
+ }
+
+ // else roll and yaw are NOT swapped ---------------------
+ else {
+ // roll:
+ if (KeyDownMap(KEY_ROLL_LEFT)) { if (r1<steps) r1+=1; r = sqr(r1/steps); }
+ else if (KeyDownMap(KEY_ROLL_RIGHT)) { if (r1<steps) r1+=1; r = -sqr(r1/steps); }
+
+ // another way to roll:
+ if (KeyDownMap(KEY_ROLL_ENABLE)) {
+ if (KeyDownMap(KEY_YAW_LEFT)) { if (r1<steps) r1+=1; r = sqr(r1/steps); }
+ else if (KeyDownMap(KEY_YAW_RIGHT)) { if (r1<steps) r1+=1; r = -sqr(r1/steps); }
+ else r1 = 0;
+ }
+
+ // yaw left-right
+ else {
+ if (KeyDownMap(KEY_YAW_LEFT)) { if (w1<steps) w1+=1; w = -sqr(w1/steps); }
+ else if (KeyDownMap(KEY_YAW_RIGHT)) { if (w1<steps) w1+=1; w = sqr(w1/steps); }
+ else w1 = 0;
+ }
+ }
+
+ // if pitch is inverted ----------------------------------
+ if (inverted) {
+ if (KeyDownMap(KEY_PITCH_DOWN)) { if (p1<steps) p1+=1; p = -sqr(p1/steps); }
+ else if (KeyDownMap(KEY_PITCH_UP)) { if (p1<steps) p1+=1; p = sqr(p1/steps); }
+ else p1 = 0;
+ }
+
+ // else pitch is NOT inverted ----------------------------
+ else {
+ if (KeyDownMap(KEY_PITCH_UP)) { if (p1<steps) p1+=1; p = -sqr(p1/steps); }
+ else if (KeyDownMap(KEY_PITCH_DOWN)) { if (p1<steps) p1+=1; p = sqr(p1/steps); }
+ else p1 = 0;
+ }
+
+ if (KeyDownMap(KEY_CENTER)) c = 1;
+
+ // actions
+ if (KeyDownMap(KEY_ACTION_0)) action[0] = 1;
+ if (KeyDownMap(KEY_ACTION_1)) action[1] = 1;
+ if (KeyDownMap(KEY_ACTION_2)) action[2] = 1;
+ if (KeyDownMap(KEY_ACTION_3)) action[3] = 1;
+}
+
+// +--------------------------------------------------------------------+
+
+
+
diff --git a/nGenEx/Keyboard.h b/nGenEx/Keyboard.h
new file mode 100644
index 0000000..db74950
--- /dev/null
+++ b/nGenEx/Keyboard.h
@@ -0,0 +1,74 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Keyboard.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Keyboard Input class
+*/
+
+#ifndef Keyboard_h
+#define Keyboard_h
+
+#include "MotionController.h"
+
+// +--------------------------------------------------------------------+
+
+class Keyboard : public MotionController
+{
+public:
+ static const char* TYPENAME() { return "Keyboard"; }
+
+ Keyboard();
+ virtual ~Keyboard();
+
+ // setup
+ virtual void MapKeys(KeyMapEntry* mapping, int nkeys);
+
+ // sample the physical device
+ virtual void Acquire();
+
+ // translations
+ virtual double X() { return x; }
+ virtual double Y() { return y; }
+ virtual double Z() { return z; }
+
+ // rotations
+ virtual double Pitch() { return p; }
+ virtual double Roll() { return r; }
+ virtual double Yaw() { return w; }
+ virtual int Center() { return c; }
+
+ // throttle
+ virtual double Throttle() { return t; }
+ virtual void SetThrottle(double throttle) { t = throttle; }
+
+ // actions
+ virtual int Action(int n) { return action[n]; }
+ virtual int ActionMap(int n) { return KeyDownMap(n); }
+
+ static bool KeyDown(int key);
+ static bool KeyDownMap(int key);
+ static void FlushKeys();
+
+ static Keyboard* GetInstance();
+
+protected:
+ double x,y,z,p,r,w,t;
+ double p1, r1, w1;
+ int c;
+ int action[MotionController::MaxActions];
+
+ static int map[KEY_MAP_SIZE];
+ static int alt[KEY_MAP_SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Keyboard_h
+
diff --git a/nGenEx/Layout.cpp b/nGenEx/Layout.cpp
new file mode 100644
index 0000000..dd2d792
--- /dev/null
+++ b/nGenEx/Layout.cpp
@@ -0,0 +1,246 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Layout.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Layout Resource class implementation
+*/
+
+#include "MemDebug.h"
+#include "Layout.h"
+
+// +--------------------------------------------------------------------+
+
+Layout::Layout()
+{ }
+
+Layout::~Layout()
+{ }
+
+// +--------------------------------------------------------------------+
+
+bool
+Layout::DoLayout(ActiveWindow* panel)
+{
+ if (!panel || panel->GetChildren().size() < 1)
+ return false;
+
+ if (cols.size() < 1 || rows.size() < 1)
+ return false;
+
+ ArrayList cell_x;
+ ArrayList cell_y;
+
+ ScaleWeights();
+ CalcCells(panel->Width(), panel->Height(), cell_x, cell_y);
+
+ ListIter<ActiveWindow> iter = panel->GetChildren();
+ while (++iter) {
+ ActiveWindow* w = iter.value();
+ Rect c = w->GetCells();
+ Rect r;
+ Rect rp = panel->GetRect();
+
+ if (c.x < 0) c.x = 0;
+ else if (c.x >= cell_x.size()) c.x = cell_x.size() - 1;
+ if (c.y < 0) c.y = 0;
+ else if (c.y >= cell_y.size()) c.y = cell_y.size() - 1;
+ if (c.x+c.w >= cell_x.size()) c.w = cell_x.size() - c.x - 1;
+ if (c.y+c.h >= cell_y.size()) c.h = cell_y.size() - c.y - 1;
+
+ r.x = cell_x[c.x] + w->GetCellInsets().left;
+ r.y = cell_y[c.y] + w->GetCellInsets().top;
+ r.w = cell_x[c.x+c.w] - w->GetCellInsets().right - r.x;
+ r.h = cell_y[c.y+c.h] - w->GetCellInsets().bottom - r.y;
+
+ r.x += panel->X();
+ r.y += panel->Y();
+
+ if (w->GetFixedWidth() && w->GetFixedWidth() < r.w)
+ r.w = w->GetFixedWidth();
+
+ if (w->GetFixedHeight() && w->GetFixedHeight() < r.h)
+ r.h = w->GetFixedHeight();
+
+ if (w->GetID() == 330 || w->GetID() == 125) {
+ int y1 = r.y + r.h;
+ int y2 = rp.y + rp.h;
+ }
+
+ if (w->GetHidePartial() && (r.x + r.w > rp.x + rp.w)) {
+ w->MoveTo(Rect(0,0,0,0));
+ }
+
+ else if (w->GetHidePartial() && (r.y + r.h > rp.y + rp.h)) {
+ w->MoveTo(Rect(0,0,0,0));
+ }
+
+ else {
+ w->MoveTo(r);
+ }
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Layout::ScaleWeights()
+{
+ int i;
+ float total = 0;
+
+ for (i = 0; i < col_weights.size(); i++)
+ total += col_weights[i];
+
+ if (total > 0) {
+ for (i = 0; i < col_weights.size(); i++)
+ col_weights[i] = col_weights[i] / total;
+ }
+
+ total = 0;
+ for (i = 0; i < row_weights.size(); i++)
+ total += row_weights[i];
+
+ if (total > 0) {
+ for (i = 0; i < row_weights.size(); i++)
+ row_weights[i] = row_weights[i] / total;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Layout::CalcCells(DWORD w, DWORD h, ArrayList& cell_x, ArrayList& cell_y)
+{
+ DWORD x = 0;
+ DWORD y = 0;
+ DWORD min_x = 0;
+ DWORD min_y = 0;
+ DWORD ext_x = 0;
+ DWORD ext_y = 0;
+ int i;
+
+ for (i = 0; i < cols.size(); i++)
+ min_x += cols[i];
+
+ for (i = 0; i < rows.size(); i++)
+ min_y += rows[i];
+
+ if (min_x < w)
+ ext_x = w - min_x;
+
+ if (min_y < h)
+ ext_y = h - min_y;
+
+ cell_x.append(x);
+ for (i = 0; i < cols.size(); i++) {
+ x += cols[i] + (DWORD) (ext_x * col_weights[i]);
+ cell_x.append(x);
+ }
+
+ cell_y.append(y);
+ for (i = 0; i < rows.size(); i++) {
+ y += rows[i] + (DWORD) (ext_y * row_weights[i]);
+ cell_y.append(y);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Layout::Clear()
+{
+ cols.clear();
+ rows.clear();
+
+ col_weights.clear();
+ row_weights.clear();
+}
+
+void
+Layout::AddCol(DWORD min_width, float col_factor)
+{
+ cols.append(min_width);
+ col_weights.append(col_factor);
+}
+
+void
+Layout::AddRow(DWORD min_height, float row_factor)
+{
+ rows.append(min_height);
+ row_weights.append(row_factor);
+}
+
+void
+Layout::SetConstraints(const ArrayList& min_x,
+ const ArrayList& min_y,
+ const FloatList& weight_x,
+ const FloatList& weight_y)
+{
+ Clear();
+
+ if (min_x.size() == weight_x.size() &&
+ min_y.size() == weight_y.size()) {
+
+ cols.append(min_x);
+ rows.append(min_y);
+
+ col_weights.append(weight_x);
+ row_weights.append(weight_y);
+ }
+}
+
+void
+Layout::SetConstraints(const FloatList& min_x,
+ const FloatList& min_y,
+ const FloatList& weight_x,
+ const FloatList& weight_y)
+{
+ Clear();
+
+ if (min_x.size() == weight_x.size() &&
+ min_y.size() == weight_y.size()) {
+
+ for (int i = 0; i < min_x.size(); i++)
+ cols.append((DWORD) min_x[i]);
+
+ for (i = 0; i < min_y.size(); i++)
+ rows.append((DWORD) min_y[i]);
+
+ col_weights.append(weight_x);
+ row_weights.append(weight_y);
+ }
+}
+
+void
+Layout::SetConstraints(int ncols,
+ int nrows,
+ const int* min_x,
+ const int* min_y,
+ const float* weight_x,
+ const float* weight_y)
+{
+ Clear();
+
+ if (nrows > 0 && ncols > 0) {
+ int i = 0;
+
+ for (i = 0; i < ncols; i++) {
+ cols.append(min_x[i]);
+ col_weights.append(weight_x[i]);
+ }
+
+ for (i = 0; i < nrows; i++) {
+ rows.append(min_y[i]);
+ row_weights.append(weight_y[i]);
+ }
+ }
+}
diff --git a/nGenEx/Layout.h b/nGenEx/Layout.h
new file mode 100644
index 0000000..9e901fd
--- /dev/null
+++ b/nGenEx/Layout.h
@@ -0,0 +1,66 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Layout.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Layout Manager class for ActiveWindow panels
+*/
+
+#ifndef Layout_h
+#define Layout_h
+
+#include "ActiveWindow.h"
+#include "ArrayList.h"
+
+// +--------------------------------------------------------------------+
+
+class Layout
+{
+public:
+ static const char* TYPENAME() { return "Layout"; }
+
+ Layout();
+ virtual ~Layout();
+
+ virtual bool DoLayout(ActiveWindow* panel);
+
+ virtual void Clear();
+ virtual void AddCol(DWORD min_width, float col_factor);
+ virtual void AddRow(DWORD min_height, float row_factor);
+
+ virtual void SetConstraints(const ArrayList& min_x,
+ const ArrayList& min_y,
+ const FloatList& weight_x,
+ const FloatList& weight_y);
+
+ virtual void SetConstraints(const FloatList& min_x,
+ const FloatList& min_y,
+ const FloatList& weight_x,
+ const FloatList& weight_y);
+
+ virtual void SetConstraints(int ncols,
+ int nrows,
+ const int* min_x,
+ const int* min_y,
+ const float* weight_x,
+ const float* weight_y);
+
+
+protected:
+ virtual void ScaleWeights();
+ virtual void CalcCells(DWORD w, DWORD h, ArrayList& cell_x, ArrayList& cell_y);
+
+ ArrayList cols;
+ ArrayList rows;
+ FloatList col_weights;
+ FloatList row_weights;
+};
+
+#endif Layout_h
+
diff --git a/nGenEx/Light.cpp b/nGenEx/Light.cpp
new file mode 100644
index 0000000..fa4d822
--- /dev/null
+++ b/nGenEx/Light.cpp
@@ -0,0 +1,72 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Light.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Dynamic Light Source
+*/
+
+#include "MemDebug.h"
+#include "Light.h"
+#include "Scene.h"
+
+// +--------------------------------------------------------------------+
+
+int Light::id_key = 1;
+
+// +--------------------------------------------------------------------+
+
+Light::Light(float l, float dl, int time)
+ : id(id_key++), type(LIGHT_POINT), life(time),
+ light(l), dldt(dl), color(255,255,255),
+ active(true), shadow(false), scene(0)
+{ }
+
+// +--------------------------------------------------------------------+
+
+Light::~Light()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+Light::Update()
+{
+ if (dldt < 1.0f)
+ light *= dldt;
+
+ if (life > 0) life--;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Light::Destroy()
+{
+ if (scene)
+ scene->DelLight(this);
+
+ delete this;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Light::MoveTo(const Point& dst)
+{
+ //if (type != LIGHT_DIRECTIONAL)
+ loc = dst;
+}
+
+void
+Light::TranslateBy(const Point& ref)
+{
+ if (type != LIGHT_DIRECTIONAL)
+ loc = loc - ref;
+}
diff --git a/nGenEx/Light.h b/nGenEx/Light.h
new file mode 100644
index 0000000..3c3cfe9
--- /dev/null
+++ b/nGenEx/Light.h
@@ -0,0 +1,96 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Light.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Dynamic Light Source
+*/
+
+#ifndef Light_h
+#define Light_h
+
+#include "Geometry.h"
+#include "Color.h"
+
+// +--------------------------------------------------------------------+
+
+#define LIGHT_DESTROY(x) if (x) { x->Destroy(); x = 0; }
+
+// +--------------------------------------------------------------------+
+
+class Scene;
+
+// +--------------------------------------------------------------------+
+
+class Light
+{
+public:
+ static const char* TYPENAME() { return "Light"; }
+
+ enum TYPES {
+ LIGHT_POINT = 1,
+ LIGHT_SPOT = 2,
+ LIGHT_DIRECTIONAL = 3,
+ LIGHT_FORCE_DWORD = 0x7fffffff
+ };
+
+ Light(float l=0.0f, float dl=1.0f, int time=-1);
+ virtual ~Light();
+
+ int operator == (const Light& l) const { return id == l.id; }
+
+ // operations
+ virtual void Update();
+
+ // accessors / mutators
+ int Identity() const { return id; }
+ Point Location() const { return loc; }
+
+ DWORD Type() const { return type; }
+ void SetType(DWORD t) { type = t; }
+ float Intensity() const { return light; }
+ void SetIntensity(float f) { light = f; }
+ Color GetColor() const { return color; }
+ void SetColor(Color c) { color = c; }
+ bool IsActive() const { return active; }
+ void SetActive(bool a) { active = a; }
+ bool CastsShadow() const { return shadow; }
+ void SetShadow(bool s) { shadow = s; }
+
+ bool IsPoint() const { return type == LIGHT_POINT; }
+ bool IsSpot() const { return type == LIGHT_SPOT; }
+ bool IsDirectional() const { return type == LIGHT_DIRECTIONAL; }
+
+ virtual void MoveTo(const Point& dst);
+ virtual void TranslateBy(const Point& ref);
+
+ virtual int Life() const { return life; }
+ virtual void Destroy();
+ virtual Scene* GetScene() const { return scene; }
+ virtual void SetScene(Scene*s) { scene = s; }
+
+protected:
+ static int id_key;
+
+ int id;
+ DWORD type;
+ Point loc;
+ int life;
+ float light;
+ float dldt;
+ Color color;
+ bool active;
+ bool shadow;
+ Scene* scene;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Light_h
+
diff --git a/nGenEx/ListBox.cpp b/nGenEx/ListBox.cpp
new file mode 100644
index 0000000..27671b1
--- /dev/null
+++ b/nGenEx/ListBox.cpp
@@ -0,0 +1,1333 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ListBox.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ListBox ActiveWindow class
+*/
+
+#include "MemDebug.h"
+#include "ListBox.h"
+#include "Button.h"
+#include "Bitmap.h"
+#include "FormWindow.h"
+#include "Video.h"
+#include "Font.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+
+// +--------------------------------------------------------------------+
+
+class ListBoxCell
+{
+public:
+ static const char* TYPENAME() { return "ListBoxCell"; }
+
+ ListBoxCell() : data(0), image(0) { }
+
+ Text text;
+ DWORD data;
+ Bitmap* image;
+};
+
+// +--------------------------------------------------------------------+
+
+class ListBoxItem
+{
+public:
+ static const char* TYPENAME() { return "ListBoxItem"; }
+
+ ListBoxItem() : data(0), image(0), selected(false), listbox(0), color(Color::White) { }
+ ~ListBoxItem() { subitems.destroy(); }
+
+ int operator < (const ListBoxItem& item) const;
+ int operator <=(const ListBoxItem& item) const;
+ int operator ==(const ListBoxItem& item) const;
+
+ Text text;
+ DWORD data;
+ Bitmap* image;
+ bool selected;
+ Color color;
+ List<ListBoxCell> subitems;
+
+ ListBox* listbox;
+};
+
+// +--------------------------------------------------------------------+
+
+class ListBoxColumn
+{
+public:
+ static const char* TYPENAME() { return "ListBoxColumn"; }
+
+ ListBoxColumn() : width(0), align(0), sort(0), color(Color::White), use_color(0), percent(0) { }
+
+ Text title;
+ int width;
+ int align;
+ int sort;
+ Color color;
+ int use_color;
+
+ double percent;
+};
+
+// +--------------------------------------------------------------------+
+
+int ListBoxItem::operator < (const ListBoxItem& item) const
+{
+ int sort_column = listbox->GetSortColumn() - 1;
+ int sort_criteria = listbox->GetSortCriteria();
+
+ if (listbox && listbox == item.listbox) {
+ if (sort_column == -1) {
+ switch (sort_criteria) {
+ case ListBox::LIST_SORT_NUMERIC_DESCENDING:
+ return data > item.data;
+
+ case ListBox::LIST_SORT_ALPHA_DESCENDING:
+ return text > item.text;
+
+ case ListBox::LIST_SORT_ALPHA_ASCENDING:
+ return text < item.text;
+
+ case ListBox::LIST_SORT_NUMERIC_ASCENDING:
+ return data < item.data;
+ }
+ }
+
+ else if (sort_column >= 0 &&
+ sort_column <= subitems.size() &&
+ sort_column <= item.subitems.size()) {
+
+ switch (sort_criteria) {
+ case ListBox::LIST_SORT_NUMERIC_DESCENDING:
+ return subitems[sort_column]->data > item.subitems[sort_column]->data;
+
+ case ListBox::LIST_SORT_ALPHA_DESCENDING:
+ return subitems[sort_column]->text > item.subitems[sort_column]->text;
+
+ case ListBox::LIST_SORT_ALPHA_ASCENDING:
+ return subitems[sort_column]->text < item.subitems[sort_column]->text;
+
+ case ListBox::LIST_SORT_NUMERIC_ASCENDING:
+ return subitems[sort_column]->data < item.subitems[sort_column]->data;
+ }
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBoxItem::operator <=(const ListBoxItem& item) const
+{
+ int sort_column = listbox->GetSortColumn() - 1;
+ int sort_criteria = listbox->GetSortCriteria();
+
+ if (listbox && listbox == item.listbox) {
+ if (sort_column == -1) {
+ switch (sort_criteria) {
+ case ListBox::LIST_SORT_NUMERIC_DESCENDING:
+ return data >= item.data;
+
+ case ListBox::LIST_SORT_ALPHA_DESCENDING:
+ return text >= item.text;
+
+ case ListBox::LIST_SORT_ALPHA_ASCENDING:
+ return text <= item.text;
+
+ case ListBox::LIST_SORT_NUMERIC_ASCENDING:
+ return data <= item.data;
+ }
+ }
+
+ else if (sort_column >= 0 &&
+ sort_column <= subitems.size() &&
+ sort_column <= item.subitems.size()) {
+
+ switch (sort_criteria) {
+ case ListBox::LIST_SORT_NUMERIC_DESCENDING:
+ return subitems[sort_column]->data >= item.subitems[sort_column]->data;
+
+ case ListBox::LIST_SORT_ALPHA_DESCENDING:
+ return subitems[sort_column]->text >= item.subitems[sort_column]->text;
+
+ case ListBox::LIST_SORT_ALPHA_ASCENDING:
+ return subitems[sort_column]->text <= item.subitems[sort_column]->text;
+
+ case ListBox::LIST_SORT_NUMERIC_ASCENDING:
+ return subitems[sort_column]->data <= item.subitems[sort_column]->data;
+ }
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBoxItem::operator == (const ListBoxItem& item) const
+{
+ int sort_column = listbox->GetSortColumn() - 1;
+ int sort_criteria = listbox->GetSortCriteria();
+
+ if (listbox && listbox == item.listbox) {
+ if (sort_column == -1) {
+ switch (sort_criteria) {
+ case ListBox::LIST_SORT_NUMERIC_DESCENDING:
+ return data == item.data;
+
+ case ListBox::LIST_SORT_ALPHA_DESCENDING:
+ return text == item.text;
+
+ case ListBox::LIST_SORT_ALPHA_ASCENDING:
+ return text == item.text;
+
+ case ListBox::LIST_SORT_NUMERIC_ASCENDING:
+ return data == item.data;
+ }
+ }
+
+ else if (sort_column >= 0 &&
+ sort_column <= subitems.size() &&
+ sort_column <= item.subitems.size()) {
+
+ switch (sort_criteria) {
+ case ListBox::LIST_SORT_NUMERIC_DESCENDING:
+ return subitems[sort_column]->data == item.subitems[sort_column]->data;
+
+ case ListBox::LIST_SORT_ALPHA_DESCENDING:
+ return subitems[sort_column]->text == item.subitems[sort_column]->text;
+
+ case ListBox::LIST_SORT_ALPHA_ASCENDING:
+ return subitems[sort_column]->text == item.subitems[sort_column]->text;
+
+ case ListBox::LIST_SORT_NUMERIC_ASCENDING:
+ return subitems[sort_column]->data == item.subitems[sort_column]->data;
+ }
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+static int old_cursor;
+
+// +--------------------------------------------------------------------+
+
+ListBox::ListBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid)
+ : ScrollWindow(p->GetScreen(), ax, ay, aw, ah, aid, 0, p)
+{
+ show_headings = false;
+ multiselect = false;
+ list_index = 0;
+ selcount = 0;
+
+ selected_color = Color(255, 255, 128);
+
+ sort_column = 0;
+ item_style = LIST_ITEM_STYLE_PLAIN;
+ seln_style = LIST_ITEM_STYLE_PLAIN;
+
+ char buf[32];
+ sprintf(buf, "ListBox %d", id);
+ desc = buf;
+}
+
+ListBox::ListBox(Screen* s, int ax, int ay, int aw, int ah, DWORD aid)
+ : ScrollWindow(s, ax, ay, aw, ah, aid)
+{
+ show_headings = false;
+ multiselect = false;
+ list_index = 0;
+ selcount = 0;
+
+ selected_color = Color(255, 255, 128);
+
+ sort_column = 0;
+ item_style = LIST_ITEM_STYLE_PLAIN;
+ seln_style = LIST_ITEM_STYLE_PLAIN;
+
+ char buf[32];
+ sprintf(buf, "ListBox %d", id);
+ desc = buf;
+}
+
+ListBox::~ListBox()
+{
+ items.destroy();
+ columns.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ListBox::DrawContent(const Rect& ctrl_rect)
+{
+ SizeColumns();
+
+ Rect item_rect = ctrl_rect;
+ item_rect.h = line_height;
+
+ int h = rect.h;
+
+ // draw headings at top, if needed:
+ if (show_headings) {
+ Color save_color = back_color;
+ back_color = ShadeColor(back_color, 1.3);
+ font->SetColor(fore_color);
+
+ int max_column = columns.size()-1;
+ item_rect.h += HEADING_EXTRA;
+
+ page_size = (h-item_rect.h) / (line_height + leading);
+
+ for (int column = 0; column <= max_column; column++) {
+ item_rect.w = GetColumnWidth(column);
+
+ // draw heading button
+ FillRect(item_rect, back_color);
+ DrawStyleRect(item_rect, WIN_RAISED_FRAME);
+
+ Rect title_rect = item_rect;
+ title_rect.Deflate(3,3);
+
+ DrawText(GetColumnTitle(column),
+ 0,
+ title_rect,
+ DT_CENTER|DT_SINGLELINE);
+
+ item_rect.x += item_rect.w;
+ }
+
+ item_rect.y += item_rect.h;
+ back_color = save_color;
+ item_rect.h = line_height;
+ }
+
+ int index = 0;
+ ListIter<ListBoxItem> iter = items;
+
+ while (++iter && item_rect.y < h) {
+ ListBoxItem* item = iter.value();
+
+ if (index++ >= top_index) {
+ // draw main item:
+ int column = 0;
+ item_rect.x = ctrl_rect.x;
+ item_rect.w = GetColumnWidth(column) - 2;
+
+ if (item_rect.y + item_rect.h > h) {
+ item_rect.h = h - item_rect.y - 1;
+ }
+
+ Color item_color = GetItemColor(index-1, 0);
+
+ if (item->selected) {
+ font->SetColor(selected_color);
+
+ if (seln_style == LIST_ITEM_STYLE_FILLED_BOX)
+ FillRect(item_rect, selected_color * 0.25);
+
+ if (seln_style >= LIST_ITEM_STYLE_BOX)
+ DrawRect(item_rect, selected_color);
+ }
+ else {
+ font->SetColor(item_color);
+
+ if (item_style == LIST_ITEM_STYLE_FILLED_BOX)
+ FillRect(item_rect, item_color * 0.25);
+
+ if (item_style >= LIST_ITEM_STYLE_BOX)
+ DrawRect(item_rect, item_color);
+ }
+
+ Rect text_rect = item_rect;
+
+ if (item->image && item->image->Width() > 0 && item->image->Height() > 0) {
+ DrawBitmap(text_rect.x, text_rect.y, text_rect.x + text_rect.w, text_rect.y + line_height, item->image);
+ }
+ else {
+ text_rect.Deflate(2,0);
+ DrawText(item->text.data(),
+ item->text.length(),
+ text_rect,
+ GetColumnAlign(column)|DT_SINGLELINE);
+ }
+
+ // draw subitems:
+ ListIter<ListBoxCell> sub_iter = item->subitems;
+ while (++sub_iter) {
+ ListBoxCell* sub = sub_iter.value();
+
+ column++;
+ item_rect.x += item_rect.w + 2;
+ item_rect.w = GetColumnWidth(column) - 2;
+
+ if (item->selected) {
+ if (seln_style == LIST_ITEM_STYLE_FILLED_BOX)
+ FillRect(item_rect, selected_color * 0.25);
+
+ if (seln_style >= LIST_ITEM_STYLE_BOX)
+ DrawRect(item_rect, selected_color);
+ }
+ else {
+ if (item_style == LIST_ITEM_STYLE_FILLED_BOX)
+ FillRect(item_rect, item_color * 0.25);
+
+ if (item_style >= LIST_ITEM_STYLE_BOX)
+ DrawRect(item_rect, item_color);
+ }
+
+ if (item->selected)
+ font->SetColor(selected_color);
+ else
+ font->SetColor(GetItemColor(index-1, column));
+
+ Rect text_rect = item_rect;
+ if (sub->image && sub->image->Width() > 0 && sub->image->Height() > 0) {
+ DrawBitmap(text_rect.x, text_rect.y, text_rect.x + text_rect.w, text_rect.y + line_height, sub->image);
+ }
+ else {
+ text_rect.Deflate(2,0);
+ DrawText(sub->text.data(),
+ sub->text.length(),
+ text_rect,
+ GetColumnAlign(column)|DT_SINGLELINE);
+ }
+ }
+
+ item_rect.y += line_height + leading;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ListBox::SizeColumns()
+{
+ ListBoxColumn* c = columns.first();
+
+ if (c->percent < 0.001) {
+ double total = 0;
+
+ ListIter<ListBoxColumn> iter = columns;
+ while (++iter) {
+ c = iter.value();
+ total += c->width;
+ }
+
+ iter.reset();
+ while (++iter) {
+ c = iter.value();
+ c->percent = c->width / total;
+ }
+ }
+
+ int usable_width = rect.w;
+ int used = 0;
+
+ if (IsScrollVisible()) {
+ usable_width -= SCROLL_WIDTH + 2;
+ }
+ else {
+ usable_width -= 3;
+ }
+
+ for (int i = 0; i < columns.size(); i++) {
+ c = columns[i];
+
+ if (i < columns.size() - 1)
+ c->width = (int) (c->percent * usable_width);
+ else
+ c->width = usable_width - used;
+
+ used += c->width;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::NumItems()
+{
+ return items.size();
+}
+
+int ListBox::NumColumns()
+{
+ return columns.size();
+}
+
+Text ListBox::GetItemText(int index)
+{
+ if (index >= 0 && index < items.size())
+ return items[index]->text;
+
+ return Text();
+}
+
+void ListBox::SetItemText(int index, const char* text)
+{
+ if (index >= 0 && index < items.size()) {
+ items[index]->text = text;
+ }
+}
+
+DWORD ListBox::GetItemData(int index)
+{
+ if (index >= 0 && index < items.size())
+ return items[index]->data;
+
+ return 0;
+}
+
+void ListBox::SetItemData(int index, DWORD data)
+{
+ if (index >= 0 && index < items.size()) {
+ items[index]->data = data;
+ }
+}
+
+Bitmap* ListBox::GetItemImage(int index)
+{
+ if (index >= 0 && index < items.size())
+ return items[index]->image;
+
+ return 0;
+}
+
+void ListBox::SetItemImage(int index, Bitmap* img)
+{
+ if (index >= 0 && index < items.size()) {
+ items[index]->image = img;
+ }
+}
+
+Color ListBox::GetItemColor(int index)
+{
+ if (index >= 0 && index < items.size())
+ return items[index]->color;
+
+ return Color::White;
+}
+
+void ListBox::SetItemColor(int index, Color c)
+{
+ if (index >= 0 && index < items.size()) {
+ items[index]->color = c;
+ }
+}
+
+Text ListBox::GetItemText(int index, int column)
+{
+ if (column == 0) {
+ return GetItemText(index);
+ }
+
+ if (index >= 0 && index < items.size()) {
+ ListBoxItem* item = items[index];
+
+ column--;
+ if (column >= 0 && column < item->subitems.size())
+ return item->subitems[column]->text;
+ }
+
+ return Text();
+}
+
+void ListBox::SetItemText(int index, int column, const char* text)
+{
+ if (column == 0) {
+ SetItemText(index, text);
+ return;
+ }
+
+ if (index >= 0 && index < items.size()) {
+ ListBoxItem* item = items[index];
+
+ column--;
+ if (column >= 0 && column < columns.size()-1) {
+ while (column >= item->subitems.size()) {
+ ListBoxCell* cell = new(__FILE__,__LINE__) ListBoxCell;
+ if (cell)
+ item->subitems.append(cell);
+ }
+
+ item->subitems[column]->text = text;
+ }
+ }
+}
+
+DWORD ListBox::GetItemData(int index, int column)
+{
+ if (column == 0) {
+ return GetItemData(index);
+ }
+
+ if (index >= 0 && index < items.size()) {
+ ListBoxItem* item = items[index];
+
+ column--;
+ if (column >= 0 && column < item->subitems.size())
+ return item->subitems[column]->data;
+ }
+
+ return 0;
+}
+
+void ListBox::SetItemData(int index, int column, DWORD data)
+{
+ if (column == 0) {
+ SetItemData(index, data);
+ return;
+ }
+
+ if (index >= 0 && index < items.size()) {
+ ListBoxItem* item = items[index];
+
+ column--;
+ if (column >= 0 && column < columns.size()-1) {
+ while (column >= item->subitems.size()) {
+ ListBoxCell* cell = new(__FILE__,__LINE__) ListBoxCell;
+ if (cell)
+ item->subitems.append(cell);
+ }
+
+ item->subitems[column]->data = data;
+ }
+ }
+}
+
+Bitmap* ListBox::GetItemImage(int index, int column)
+{
+ if (column == 0) {
+ return GetItemImage(index);
+ }
+
+ if (index >= 0 && index < items.size()) {
+ ListBoxItem* item = items[index];
+
+ column--;
+ if (column >= 0 && column < item->subitems.size())
+ return item->subitems[column]->image;
+ }
+
+ return 0;
+}
+
+void ListBox::SetItemImage(int index, int column, Bitmap* img)
+{
+ if (column == 0) {
+ SetItemImage(index, img);
+ return;
+ }
+
+ if (index >= 0 && index < items.size()) {
+ ListBoxItem* item = items[index];
+
+ column--;
+ if (column >= 0 && column < columns.size()-1) {
+ while (column >= item->subitems.size()) {
+ ListBoxCell* cell = new(__FILE__,__LINE__) ListBoxCell;
+ if (cell)
+ item->subitems.append(cell);
+ }
+
+ item->subitems[column]->image = img;
+ }
+ }
+}
+
+int ListBox::AddItem(const char* text)
+{
+ ListBoxItem* item = new(__FILE__,__LINE__) ListBoxItem;
+
+ if (item) {
+ item->text = text;
+ item->color = fore_color;
+ item->listbox = this;
+
+ items.append(item);
+
+ line_count = items.size();
+ list_index = items.size()-1;
+ }
+
+ return list_index+1;
+}
+
+int ListBox::AddItemWithData(const char* text, int data)
+{
+ ListBoxItem* item = new(__FILE__,__LINE__) ListBoxItem;
+
+ if (item) {
+ item->text = text;
+ item->data = data;
+ item->color = fore_color;
+ item->listbox = this;
+
+ items.append(item);
+
+ line_count = items.size();
+ list_index = items.size()-1;
+ }
+
+ return list_index+1;
+}
+
+int ListBox::AddImage(Bitmap* img)
+{
+ ListBoxItem* item = new(__FILE__,__LINE__) ListBoxItem;
+
+ if (item) {
+ item->image = img;
+ item->color = fore_color;
+ item->listbox = this;
+
+ items.append(item);
+
+ line_count = items.size();
+ list_index = items.size()-1;
+ }
+
+ return list_index+1;
+}
+
+void ListBox::InsertItem(int index, const char* text)
+{
+ if (index >=0 && index < items.size()) {
+ ListBoxItem* item = new(__FILE__,__LINE__) ListBoxItem;
+
+ if (item) {
+ item->text = text;
+ item->color = fore_color;
+ item->listbox = this;
+
+ list_index = index;
+ items.insert(item, list_index);
+ line_count = items.size();
+ }
+ }
+}
+
+void ListBox::InsertItemWithData(int index, const char* text, int data)
+{
+ if (index >=0 && index < items.size()) {
+ ListBoxItem* item = new(__FILE__,__LINE__) ListBoxItem;
+
+ if (item) {
+ item->text = text;
+ item->data = data;
+ item->color = fore_color;
+ item->listbox = this;
+
+ list_index = index;
+ items.insert(item, list_index);
+ line_count = items.size();
+ }
+ }
+}
+
+void ListBox::ClearItems()
+{
+ items.destroy();
+ selcount = 0;
+ top_index = 0;
+ list_index = 0;
+ line_count = 0;
+}
+
+void ListBox::RemoveItem(int index)
+{
+ if (index >= 0 && index < items.size()) {
+ if (items[index]->selected)
+ selcount--;
+ items.removeIndex(index);
+ line_count = items.size();
+ }
+}
+
+void ListBox::RemoveSelectedItems()
+{
+ if (selcount) {
+ ListIter<ListBoxItem> item = items;
+ while (++item) {
+ if (item->selected) {
+ delete item.removeItem();
+ }
+ }
+
+ line_count = items.size();
+ selcount = 0;
+ }
+}
+
+void ListBox::AddColumn(const char* title, int width, int align, int sort)
+{
+ ListBoxColumn* column = new(__FILE__,__LINE__) ListBoxColumn;
+
+ if (column) {
+ column->title = title;
+ column->width = width;
+ column->align = align;
+ column->sort = sort;
+
+ columns.append(column);
+ }
+}
+
+Text ListBox::GetColumnTitle(int index)
+{
+ if (index >= 0 && index < columns.size())
+ return columns[index]->title;
+
+ return Text();
+}
+
+void ListBox::SetColumnTitle(int index, const char* title)
+{
+ if (index >= 0 && index < columns.size()) {
+ columns[index]->title = title;
+ }
+}
+
+int ListBox::GetColumnWidth(int index)
+{
+ if (index >= 0 && index < columns.size())
+ return columns[index]->width;
+
+ return 0;
+}
+
+void ListBox::SetColumnWidth(int index, int width)
+{
+ if (index >= 0 && index < columns.size()) {
+ columns[index]->width = width;
+ }
+}
+
+int ListBox::GetColumnAlign(int index)
+{
+ if (index >= 0 && index < columns.size())
+ return columns[index]->align;
+
+ return 0;
+}
+
+void ListBox::SetColumnAlign(int index, int align)
+{
+ if (index >= 0 && index < columns.size()) {
+ columns[index]->align = align;
+ }
+}
+
+int ListBox::GetColumnSort(int index)
+{
+ if (index >= 0 && index < columns.size())
+ return columns[index]->sort;
+
+ return 0;
+}
+
+void ListBox::SetColumnSort(int index, int sort)
+{
+ if (index >= 0 && index < columns.size()) {
+ columns[index]->sort = sort;
+ }
+}
+
+Color ListBox::GetColumnColor(int index)
+{
+ if (index >= 0 && index < columns.size())
+ return columns[index]->color;
+
+ return Color::White;
+}
+
+void ListBox::SetColumnColor(int index, Color c)
+{
+ if (index >= 0 && index < columns.size()) {
+ columns[index]->color = c;
+ columns[index]->use_color = true;
+ }
+}
+
+Color ListBox::GetItemColor(int index, int column)
+{
+ Color c = Color::White;
+
+ if (index >= 0 && index < items.size())
+ c = items[index]->color;
+
+ if (column >= 0 && column < columns.size()) {
+ if (columns[column]->use_color)
+ c = columns[column]->color;
+ }
+
+ return c;
+}
+
+int ListBox::GetMultiSelect()
+{
+ return multiselect;
+}
+
+void ListBox::SetMultiSelect(int nNewValue)
+{
+ if (multiselect != nNewValue && (nNewValue == 0 || nNewValue == 1)) {
+ multiselect = nNewValue;
+ ClearSelection();
+ }
+}
+
+bool ListBox::GetShowHeadings()
+{
+ return show_headings;
+}
+
+void ListBox::SetShowHeadings(bool nNewValue)
+{
+ if (show_headings != nNewValue) {
+ show_headings = nNewValue;
+ }
+}
+
+Color ListBox::GetSelectedColor()
+{
+ return selected_color;
+}
+
+void ListBox::SetSelectedColor(Color c)
+{
+ if (selected_color != c) {
+ selected_color = c;
+ }
+}
+
+int ListBox::GetItemStyle() const
+{
+ return item_style;
+}
+
+void ListBox::SetItemStyle(int style)
+{
+ if (style >= LIST_ITEM_STYLE_PLAIN && style <= LIST_ITEM_STYLE_FILLED_BOX) {
+ item_style = style;
+ }
+}
+
+int ListBox::GetSelectedStyle() const
+{
+ return seln_style;
+}
+
+void ListBox::SetSelectedStyle(int style)
+{
+ if (style >= LIST_ITEM_STYLE_PLAIN && style <= LIST_ITEM_STYLE_FILLED_BOX) {
+ seln_style = style;
+ }
+}
+
+bool ListBox::IsSelected(int index)
+{
+ if (index >= 0 && index < items.size())
+ return items[index]->selected;
+
+ return false;
+}
+
+void ListBox::SetSelected(int index, bool bNewValue)
+{
+ if (index >= 0 && index < items.size()) {
+ if (!multiselect)
+ ClearSelection();
+
+ if (items[index]->selected != bNewValue) {
+ items[index]->selected = bNewValue;
+
+ if (bNewValue) {
+ list_index = index;
+ selcount++;
+ }
+ else {
+ selcount--;
+ }
+ }
+ }
+}
+
+void ListBox::ClearSelection()
+{
+ ListIter<ListBoxItem> item = items;
+ while (++item)
+ item->selected = false;
+
+ selcount = 0;
+}
+
+int ListBox::GetListIndex()
+{
+ return list_index;
+}
+
+int ListBox::GetLineCount()
+{
+ line_count = items.size();
+ return line_count;
+}
+
+int ListBox::GetSelCount()
+{
+ return selcount;
+}
+
+int ListBox::GetSelection()
+{
+ for (int i = 0; i < items.size(); i++)
+ if (items[i]->selected)
+ return i;
+
+ return -1;
+}
+
+Text ListBox::GetSelectedItem()
+{
+ int n = GetSelection();
+ return GetItemText(n);
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::GetSortColumn()
+{
+ return sort_column;
+}
+
+void ListBox::SetSortColumn(int col_index)
+{
+ if (col_index >= 0 && col_index <= columns.size())
+ sort_column = col_index;
+}
+
+int ListBox::GetSortCriteria()
+{
+ return GetColumnSort(sort_column);
+}
+
+void ListBox::SetSortCriteria(SORT sort)
+{
+ SetColumnSort(sort_column, sort);
+}
+
+// +--------------------------------------------------------------------+
+
+void ListBox::SortItems()
+{
+ if (sort_column >=0 && sort_column <= columns.size())
+ items.sort();
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::IndexFromPoint(int x, int y) const
+{
+ int sel_index = -1;
+
+ if (show_headings)
+ sel_index = top_index + (y - (line_height+HEADING_EXTRA)) / (line_height + leading);
+
+ else
+ sel_index = top_index + y / (line_height + leading);
+
+ return sel_index;
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::OnMouseMove(int x, int y)
+{
+ bool dirty = false;
+
+ if (captured) {
+ ActiveWindow* test = GetCapture();
+
+ if (test != this) {
+ captured = false;
+ dirty = true;
+ }
+
+ else {
+ if (selecting && !dragging) {
+ if (dragdrop && (x < rect.x ||
+ x > rect.x+rect.w ||
+ y < rect.y ||
+ y > rect.y+rect.h)) {
+
+ dragging = true;
+ OnDragStart(x,y);
+ }
+ }
+
+ if (scrolling == SCROLL_THUMB) {
+ mouse_y = y - rect.y - TRACK_START;
+
+ int dest = (int) ((double) mouse_y/track_length * (items.size()-1));
+ ScrollTo(dest);
+ dirty = true;
+ }
+ }
+ }
+
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+static bool preselected = false;
+
+int ListBox::OnLButtonDown(int x, int y)
+{
+ if (!captured)
+ captured = SetCapture();
+
+ mouse_x = x - rect.x;
+ mouse_y = y - rect.y;
+
+ int x_scroll_bar = rect.w;
+
+ if (IsScrollVisible())
+ x_scroll_bar -= SCROLL_WIDTH;
+
+ if (mouse_x < x_scroll_bar) {
+ scrolling = SCROLL_NONE;
+
+ if (show_headings && mouse_y < line_height+BORDER_WIDTH+EXTRA_WIDTH) {
+ int next_col_start = 0;
+ int max_column = columns.size()-1;
+
+ for (int column = 0; column < max_column; column++) {
+ next_col_start += GetColumnWidth(column);
+
+ if (mouse_x < next_col_start)
+ break;
+ }
+
+ sort_column = column;
+
+ int& sort_criteria = columns[sort_column]->sort;
+
+ if (sort_criteria != LIST_SORT_NEVER) {
+ if (!sort_criteria)
+ sort_criteria = LIST_SORT_ALPHA_DESCENDING;
+ else
+ sort_criteria = -sort_criteria;
+
+ SortItems();
+ }
+ }
+
+ else {
+ selecting = true;
+ }
+ }
+
+ else {
+ selecting = false;
+
+ if (mouse_y < TRACK_START) {
+ scrolling = SCROLL_UP;
+ Scroll(scrolling, 1);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+
+ else if (mouse_y > rect.h-TRACK_START) {
+ scrolling = SCROLL_DOWN;
+ if (top_index < items.size()-1)
+ top_index++;
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+
+ else if (mouse_y < thumb_pos) {
+ scrolling = SCROLL_PAGE_UP;
+ Scroll(scrolling, page_size);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+
+ else if (mouse_y > thumb_pos+THUMB_HEIGHT) {
+ scrolling = SCROLL_PAGE_DOWN;
+ Scroll(scrolling, page_size);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+
+ else {
+ scrolling = SCROLL_THUMB;
+ }
+ }
+
+ if (selecting) {
+ list_index = IndexFromPoint(mouse_x, mouse_y);
+ preselected = IsSelected(list_index);
+ if (!multiselect || !Keyboard::KeyDown(VK_SHIFT))
+ ClearSelection();
+ SetSelected(list_index);
+ EnsureVisible(list_index);
+ Button::PlaySound(Button::SND_LIST_SELECT);
+ }
+
+ return ActiveWindow::OnLButtonDown(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::OnLButtonUp(int x, int y)
+{
+ if (captured) {
+ mouse_x = x-rect.x;
+ mouse_y = y-rect.y;
+
+ if (dragging) {
+ if (mouse_x < 0 || mouse_x > rect.w || mouse_y < 0 || mouse_y > rect.h) {
+ FormWindow* parent_form = (FormWindow*) form;
+
+ if (parent_form) {
+ ActiveWindow* drop_target = parent_form->FindControl(x,y);
+
+ if (drop_target && drop_target->IsEnabled() && drop_target->IsShown())
+ drop_target->OnDragDrop(x,y,this);
+ }
+ }
+ }
+ else if (preselected) {
+ if (multiselect && Keyboard::KeyDown(VK_SHIFT)) {
+ SetSelected(list_index, false);
+ Button::PlaySound(Button::SND_LIST_SELECT);
+ }
+ }
+
+ ReleaseCapture();
+ captured = false;
+
+ Mouse::SetCursor((Mouse::CURSOR) old_cursor);
+ }
+
+ dragging = false;
+ selecting = false;
+
+ return ActiveWindow::OnLButtonUp(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::OnMouseWheel(int wheel)
+{
+ return ScrollWindow::OnMouseWheel(wheel);
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::OnClick()
+{
+ int fire_select = !scrolling;
+
+ if (scrolling == SCROLL_THUMB)
+ scrolling = SCROLL_NONE;
+
+ if (fire_select)
+ return ActiveWindow::OnSelect();
+ else
+ return ActiveWindow::OnClick();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::OnKeyDown(int vk, int flags)
+{
+ if (selcount == 1 && list_index >= 0 && list_index < items.size()) {
+ ListBoxItem* item = items[list_index];
+
+ if (vk == VK_DOWN) {
+ if (list_index < items.size() - 1) {
+ item->selected = false;
+ list_index++;
+ item = items[list_index];
+ item->selected = true;
+ OnClick();
+ return ActiveWindow::OnKeyDown(vk, flags);
+ }
+ }
+
+ else if (vk == VK_UP) {
+ if (list_index > 0) {
+ item->selected = false;
+ list_index--;
+ item = items[list_index];
+ item->selected = true;
+ OnClick();
+ return ActiveWindow::OnKeyDown(vk, flags);
+ }
+ }
+ }
+
+ return ScrollWindow::OnKeyDown(vk, flags);
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::OnDragStart(int x, int y)
+{
+ old_cursor = Mouse::SetCursor(Mouse::DRAG);
+ return ActiveWindow::OnDragStart(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::OnDragDrop(int x, int y, ActiveWindow* source)
+{
+ if (!dragdrop) return 0;
+
+ ListBox* drag_source = (ListBox*) source;
+
+ if (drag_source) {
+ int max_col = NumColumns();
+
+ if (max_col != drag_source->NumColumns())
+ max_col = 0;
+
+ for (int i = 0; i < drag_source->NumItems(); i++) {
+ if (drag_source->IsSelected(i)) {
+ AddItemWithData(drag_source->GetItemText(i),
+ drag_source->GetItemData(i));
+
+ for (int c = 1; c < max_col; c++) {
+ SetItemText(list_index, c, drag_source->GetItemText(i,c));
+ SetItemData(list_index, c, drag_source->GetItemData(i,c));
+ }
+
+ if (!multiselect)
+ ClearSelection();
+
+ items[list_index]->selected = true;
+ selcount++;
+ }
+ }
+
+ drag_source->RemoveSelectedItems();
+ Button::PlaySound(Button::SND_LIST_DROP);
+ }
+
+ return ActiveWindow::OnDragDrop(x,y,source);
+}
diff --git a/nGenEx/ListBox.h b/nGenEx/ListBox.h
new file mode 100644
index 0000000..289a2fa
--- /dev/null
+++ b/nGenEx/ListBox.h
@@ -0,0 +1,170 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ListBox.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ListBox ActiveWindow class
+*/
+
+#ifndef ListBox_h
+#define ListBox_h
+
+#include "Types.h"
+#include "ScrollWindow.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+class ListBox;
+class ListBoxCell;
+class ListBoxItem;
+class ListBoxColumn;
+
+// +--------------------------------------------------------------------+
+
+class ListBox : public ScrollWindow
+{
+public:
+ enum SORT { LIST_SORT_NUMERIC_DESCENDING = -2,
+ LIST_SORT_ALPHA_DESCENDING,
+ LIST_SORT_NONE,
+ LIST_SORT_ALPHA_ASCENDING,
+ LIST_SORT_NUMERIC_ASCENDING,
+ LIST_SORT_NEVER
+ };
+
+ enum ALIGN { LIST_ALIGN_LEFT = DT_LEFT,
+ LIST_ALIGN_CENTER = DT_CENTER,
+ LIST_ALIGN_RIGHT = DT_RIGHT
+ };
+
+ enum STYLE { LIST_ITEM_STYLE_PLAIN,
+ LIST_ITEM_STYLE_BOX,
+ LIST_ITEM_STYLE_FILLED_BOX
+ };
+
+ ListBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid);
+ ListBox(Screen* s, int ax, int ay, int aw, int ah, DWORD aid);
+ virtual ~ListBox();
+
+ // Operations:
+ virtual void DrawContent(const Rect& ctrl_rect);
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnMouseWheel(int wheel);
+ virtual int OnClick();
+
+ virtual int OnKeyDown(int vk, int flags);
+
+ // pseudo-events:
+ virtual int OnDragStart(int x, int y);
+ virtual int OnDragDrop(int x, int y, ActiveWindow* source);
+
+ // Property accessors:
+ int NumItems();
+ int NumColumns();
+
+ Text GetItemText(int index);
+ void SetItemText(int index, const char* text);
+ DWORD GetItemData(int index);
+ void SetItemData(int index, DWORD data);
+ Bitmap* GetItemImage(int index);
+ void SetItemImage(int index, Bitmap* img);
+ Color GetItemColor(int index);
+ void SetItemColor(int index, Color c);
+
+ Text GetItemText(int index, int column);
+ void SetItemText(int index, int column, const char* text);
+ DWORD GetItemData(int index, int column);
+ void SetItemData(int index, int column, DWORD data);
+ Bitmap* GetItemImage(int index, int column);
+ void SetItemImage(int index, int column, Bitmap* img);
+
+ int AddItem(const char* text);
+ int AddImage(Bitmap* img);
+ int AddItemWithData(const char* text, int data);
+ void InsertItem(int index, const char* text);
+ void InsertItemWithData(int index, const char* text, int data);
+ void ClearItems();
+ void RemoveItem(int index);
+ void RemoveSelectedItems();
+
+ void AddColumn(const char* title,
+ int width,
+ int align = ListBox::LIST_ALIGN_LEFT,
+ int sort = ListBox::LIST_SORT_NONE);
+
+ Text GetColumnTitle(int index);
+ void SetColumnTitle(int index, const char* title);
+ int GetColumnWidth(int index);
+ void SetColumnWidth(int index, int width);
+ int GetColumnAlign(int index);
+ void SetColumnAlign(int index, int align);
+ int GetColumnSort(int index);
+ void SetColumnSort(int index, int sort);
+ Color GetColumnColor(int index);
+ void SetColumnColor(int index, Color c);
+
+ Color GetItemColor(int index, int column);
+
+ int GetMultiSelect();
+ void SetMultiSelect(int nNewValue);
+ bool GetShowHeadings();
+ void SetShowHeadings(bool nNewValue);
+ Color GetSelectedColor();
+ void SetSelectedColor(Color c);
+
+ int GetItemStyle() const;
+ void SetItemStyle(int style);
+ int GetSelectedStyle() const;
+ void SetSelectedStyle(int style);
+
+ bool IsSelected(int index);
+ void SetSelected(int index, bool bNewValue=true);
+ void ClearSelection();
+
+ int GetSortColumn();
+ void SetSortColumn(int col_index);
+ int GetSortCriteria();
+ void SetSortCriteria(SORT sort);
+ void SortItems();
+ void SizeColumns();
+
+ // read-only:
+ virtual int GetListIndex();
+ virtual int GetLineCount();
+ virtual int GetSelCount();
+ virtual int GetSelection();
+ virtual Text GetSelectedItem();
+
+protected:
+ int IndexFromPoint(int x, int y) const;
+
+ // properties:
+ List<ListBoxItem> items;
+ List<ListBoxColumn> columns;
+
+ bool show_headings;
+ int multiselect;
+ int list_index;
+ int selcount;
+
+ Color selected_color;
+
+ int sort_column;
+ int item_style;
+ int seln_style;
+};
+
+#endif ListBox_h
+
diff --git a/nGenEx/Locale.cpp b/nGenEx/Locale.cpp
new file mode 100644
index 0000000..750e020
--- /dev/null
+++ b/nGenEx/Locale.cpp
@@ -0,0 +1,237 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Locale.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ 3D Locale (Polygon) Object
+*/
+
+#include "MemDebug.h"
+#include "Locale.h"
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+static List<Locale> locales;
+
+// +--------------------------------------------------------------------+
+
+Locale::Locale(const char* l, const char* c, const char* v)
+{
+ ZeroMemory(this, sizeof(Locale));
+ if (l && *l) {
+ strncpy(language, l, 6);
+ char* p = language;
+ while (*p) {
+ *p = tolower(*p);
+ p++;
+ }
+ }
+
+ if (c && *c) {
+ strncpy(country, c, 6);
+ char* p = country;
+ while (*p) {
+ *p = toupper(*p);
+ p++;
+ }
+ }
+
+ if (v && *v) {
+ strncpy(variant, v, 6);
+ char* p = variant;
+ while (*p) {
+ *p = tolower(*p);
+ p++;
+ }
+ }
+
+ locales.append(this);
+}
+
+// +--------------------------------------------------------------------+
+
+Locale::~Locale()
+{
+ locales.remove(this);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Locale::operator == (const Locale& that) const
+{
+ if (this == &that) return 1;
+
+ return !stricmp(language, that.language) &&
+ !stricmp(country, that.country) &&
+ !stricmp(variant, that.variant);
+}
+
+// +--------------------------------------------------------------------+
+
+Locale*
+Locale::ParseLocale(const char* str)
+{
+ if (str && *str) {
+ int i = 0;
+ char s1[4];
+ char s2[4];
+ char s3[4];
+
+ while (*str && *str != '_' && i < 3) {
+ s1[i] = *str++;
+ i++;
+ }
+ s1[i] = 0;
+ i = 0;
+
+ if (*str == '_')
+ str++;
+
+ while (*str && *str != '_' && i < 3) {
+ s2[i] = *str++;
+ i++;
+ }
+ s2[i] = 0;
+ i = 0;
+
+ if (*str == '_')
+ str++;
+
+ while (*str && *str != '_' && i < 3) {
+ s3[i] = *str++;
+ i++;
+ }
+ s3[i] = 0;
+ i = 0;
+
+ return CreateLocale(s1, s2, s3);
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+Locale*
+Locale::CreateLocale(const char* l, const char* c, const char* v)
+{
+ ListIter<Locale> iter = locales;
+ while (++iter) {
+ Locale* loc = iter.value();
+ if (!stricmp(l, loc->GetLanguage())) {
+ if (c && *c) {
+ if (!stricmp(c, loc->GetCountry())) {
+ if (v && *v) {
+ if (!stricmp(v, loc->GetVariant())) {
+ return loc;
+ }
+ }
+ else {
+ return loc;
+ }
+ }
+ }
+ else {
+ return loc;
+ }
+ }
+ }
+
+ if (l[0]) {
+ if (c[0]) {
+ if (v[0]) {
+ return new(__FILE__,__LINE__) Locale(l, c, v);
+ }
+ return new(__FILE__,__LINE__) Locale(l, c);
+ }
+ return new(__FILE__,__LINE__) Locale(l);
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+const List<Locale>&
+Locale::GetAllLocales()
+{
+ return locales;
+}
+
+// +--------------------------------------------------------------------+
+
+const Text
+Locale::GetFullCode() const
+{
+ Text result = language;
+ if (*country) {
+ result.append("_");
+ result.append(country);
+
+ if (*variant) {
+ result.append("_");
+ result.append(variant);
+ }
+ }
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+static const char* languages[] = {
+ "en", "English",
+ "fr", "French",
+ "de", "German",
+ "it", "Italian",
+ "pt", "Portuguese",
+ "ru", "Russian",
+ "es", "Spanish"
+};
+
+static const char* countries[] = {
+ "US", "USA",
+ "CA", "Canada",
+ "FR", "France",
+ "DE", "Germany",
+ "IT", "Italy",
+ "PT", "Portugal",
+ "RU", "Russia",
+ "ES", "Spain",
+ "UK", "United Kingdom"
+};
+
+const Text
+Locale::GetDisplayName() const
+{
+ Text result;
+ if (*language) {
+ for (int i = 0; i < 14; i += 2) {
+ if (!stricmp(language, languages[i])) {
+ result = languages[i+1];
+ break;
+ }
+ }
+
+ if (*country) {
+ for (int i = 0; i < 18; i += 2) {
+ if (!stricmp(country, countries[i])) {
+ result.append(" - ");
+ result.append(countries[i+1]);
+ break;
+ }
+ }
+ }
+
+ }
+ return result;
+}
+
diff --git a/nGenEx/Locale.h b/nGenEx/Locale.h
new file mode 100644
index 0000000..f649592
--- /dev/null
+++ b/nGenEx/Locale.h
@@ -0,0 +1,53 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Locale.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Description of locale by ISO language, country, and variant
+*/
+
+#ifndef Locale_h
+#define Locale_h
+
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Locale
+{
+public:
+ static const char* TYPENAME() { return "Locale"; }
+
+ Locale(const char* language, const char* country=0, const char* variant=0);
+ ~Locale();
+
+ int operator == (const Locale& that) const;
+
+ // Operations:
+ static const List<Locale>& GetAllLocales();
+ static Locale* ParseLocale(const char* str);
+
+ // Property accessors:
+ const char* GetLanguage() const { return language; }
+ const char* GetCountry() const { return country; }
+ const char* GetVariant() const { return variant; }
+ const Text GetFullCode() const;
+ const Text GetDisplayName() const;
+
+
+protected:
+ static Locale* CreateLocale(const char* language, const char* country=0, const char* variant=0);
+ char language[8];
+ char country[8];
+ char variant[8];
+};
+
+#endif Locale_h
+
diff --git a/nGenEx/MCIWave.cpp b/nGenEx/MCIWave.cpp
new file mode 100644
index 0000000..fdab8cd
--- /dev/null
+++ b/nGenEx/MCIWave.cpp
@@ -0,0 +1,155 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: MCIWave.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ MCI Wave Output stuff
+*/
+
+#include "MemDebug.h"
+#include "Types.h"
+
+// +----------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+// +----------------------------------------------------------------------+
+
+const int MCI_MAX_STR = 128;
+static char ret_str[MCI_MAX_STR];
+static char err_str[MCI_MAX_STR];
+static MCIERROR mci_err;
+static MMRESULT wav_err;
+extern HWND hwndApp;
+
+static int mci_send_string(const char* cmd_str)
+{
+ mci_err = mciSendString(cmd_str, ret_str, sizeof(ret_str), hwndApp);
+ if (mci_err) {
+ if (mciGetErrorString(mci_err, err_str, sizeof(err_str)))
+ Print("Error (%s): '%s'\n", cmd_str, err_str);
+ else
+ Print("Error (%s): %d - UNKNOWN\n", cmd_str, mci_err);
+ return 0;
+ }
+
+ return 1;
+}
+
+// +--------------------------------------------------------------------+
+
+static void print_wav_error()
+{
+ waveOutGetErrorText(wav_err, err_str, MCI_MAX_STR);
+ Print(err_str);
+}
+
+// +--------------------------------------------------------------------+
+
+int load_wave_file(const char* fname, LPWAVEHDR hdr, LPWAVEFORMATEX format)
+{
+ HMMIO hmmio; /* file handle for open file */
+ MMCKINFO mmckinfoParent; /* parent chunk information structure */
+ MMCKINFO mmckinfoSubchunk; /* subchunk information structure */
+ DWORD dwFmtSize; /* size of "fmt" chunk */
+ DWORD dwDataSize; /* size of "data" chunk */
+
+ /*
+ * Open the given file for reading with buffered I/O
+ * using the default internal buffer.
+ */
+ hmmio = mmioOpen((LPSTR) fname, NULL, MMIO_READ | MMIO_ALLOCBUF);
+
+ if (hmmio == NULL) {
+ Print("load_wave_file(): '%s' - Failed to open file.\n", fname);
+ return 0;
+ }
+
+ /*
+ * Locate a "RIFF" chunk with a "WAVE" form type
+ * to make sure the file is a WAVE file.
+ */
+ mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
+ if (mmioDescend(hmmio, (LPMMCKINFO) &mmckinfoParent, NULL, MMIO_FINDRIFF)) {
+ Print("load_wave_file(): '%s' - This is not a WAVE file.\n", fname);
+ mmioClose(hmmio, 0);
+ return 0;
+ }
+
+ /*
+ * Find the "fmt " chunk (form type "fmt "); it must be
+ * a subchunk of the "RIFF" parent chunk.
+ */
+ mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
+ if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK)) {
+ Print("load_wave_file(): '%s' - WAVE file has no \"fmt\" chunk\n", fname);
+ mmioClose(hmmio, 0);
+ return 0;
+ }
+
+ /*
+ * Get the size of the "fmt " chunk--allocate and lock memory for it.
+ */
+ dwFmtSize = mmckinfoSubchunk.cksize;
+
+ /* Read the "fmt " chunk. */
+ if (mmioRead(hmmio, (HPSTR) format, dwFmtSize) != (LRESULT)dwFmtSize) {
+ Print("load_wave_file(): '%s' - Failed to read format chunk.\n", fname);
+ mmioClose(hmmio, 0);
+ return 0;
+ }
+
+ /* Ascend out of the "fmt " subchunk. */
+ mmioAscend(hmmio, &mmckinfoSubchunk, 0);
+
+ /*
+ * Find the data subchunk. The current file position
+ * should be at the beginning of the data chunk.
+ */
+ mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
+ if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK)) {
+ Print("load_wave_file(): '%s' - WAVE file has no data chunk.\n", fname);
+ mmioClose(hmmio, 0);
+ return 0;
+ }
+
+ /* Get the size of the data subchunk. */
+ dwDataSize = mmckinfoSubchunk.cksize;
+ if (dwDataSize == 0L) {
+ Print("load_wave_file(): '%s' - The data chunk contains no data.\n", fname);
+ mmioClose(hmmio, 0);
+ return 0;
+ }
+
+ // allocate the data block:
+ hdr->lpData = (LPSTR) new(__FILE__,__LINE__) BYTE[dwDataSize];
+ hdr->dwBufferLength = dwDataSize;
+
+ /* Read the waveform data subchunk. */
+ if (mmioRead(hmmio, (HPSTR) hdr->lpData, dwDataSize) != (LRESULT)dwDataSize) {
+ Print("load_wave_file(): '%s' - Failed to read data chunk.\n", fname);
+ mmioClose(hmmio, 0);
+ return 0;
+ }
+
+ /* Close the file. */
+ mmioClose(hmmio, 0);
+
+ return 1;
+}
+
+// +--------------------------------------------------------------------+
+
+void delete_wave_file(LPWAVEHDR hdr, LPWAVEFORMATEX format)
+{
+ if (hdr) {
+ delete hdr->lpData;
+ hdr->lpData = 0;
+ }
+}
diff --git a/nGenEx/MCIWave.h b/nGenEx/MCIWave.h
new file mode 100644
index 0000000..d2ea654
--- /dev/null
+++ b/nGenEx/MCIWave.h
@@ -0,0 +1,25 @@
+/* Project nGen
+ John DiCamillo Software Consulting
+ Copyright © 1997-2000. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: MCIWave.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ MCI Wave Output stuff
+*/
+
+#ifndef MCI_WAVE_H
+#define MCI_WAVE_H
+
+// +--------------------------------------------------------------------+
+
+int load_wave_file(const char* fname, LPWAVEHDR hdr, LPWAVEFORMATEX format);
+void delete_wave_file(LPWAVEHDR hdr, LPWAVEFORMATEX format);
+
+// +--------------------------------------------------------------------+
+
+#endif
diff --git a/nGenEx/MachineInfo.cpp b/nGenEx/MachineInfo.cpp
new file mode 100644
index 0000000..89e6a47
--- /dev/null
+++ b/nGenEx/MachineInfo.cpp
@@ -0,0 +1,795 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: MachineInfo.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Collect and Display Machine, OS, and Driver Information
+*/
+
+#include "MemDebug.h"
+#include "MachineInfo.h"
+#include "Timesnap.h"
+
+#define DIRECTINPUT_VERSION 0x0700
+
+#include <stdio.h>
+#include <ddraw.h>
+#include <d3d9.h>
+#include <dinput.h>
+#include <winver.h>
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+static int cpu_class=-1;
+static int cpu_speed=-1;
+static int platform=-1;
+static int dx_version=-1;
+static int total_ram=-1;
+
+static OSVERSIONINFO os_ver = { sizeof(OSVERSIONINFO) };
+static SYSTEM_INFO cpu_info;
+static MEMORYSTATUS mem_info = { sizeof(MEMORYSTATUS) };
+
+// +--------------------------------------------------------------------+
+
+const char*
+MachineInfo::GetShortDescription()
+{
+ static char desc[256];
+
+ static const char* cpu_names[] = {
+ "8088",
+ "8086",
+ "80286",
+ "80386",
+ "80486",
+ "Pentium",
+ "Pentium II",
+ "Pentium 3",
+ "Pentium 4"
+ };
+
+ static const char* os_names[] = {
+ "DOS",
+ "Windows 95",
+ "Windows 98",
+ "Windows NT",
+ "Windows 2000",
+ "Windows XP"
+ };
+
+ int cpu_index = GetCpuClass();
+ if (cpu_index < 0) cpu_index = 0;
+ else if (cpu_index > 8) cpu_index = 8;
+
+ int os_index = GetPlatform();
+ if (os_index < 0) os_index = 0;
+ else if (os_index > 5) os_index = 5;
+
+ sprintf(desc, "%s %d MHz %d MB RAM %s",
+ cpu_names[cpu_index],
+ GetCpuSpeed(),
+ GetTotalRam(),
+ os_names[os_index]);
+
+ return desc;
+}
+
+// +--------------------------------------------------------------------+
+
+static void DescribeCpuMake();
+static void DescribeOwner95();
+static void DescribeOwnerNT();
+static void DescribeDrivers95(const char* sType);
+static void DescribeDriversNT(const char* sType);
+static void DescribeDriverVersion(const char* file);
+static void DescribeDXVersion(const char* component);
+
+// wait for at least target_time,
+// return the exact amount of time actually waited:
+
+static double SpinWait(double target_time)
+{
+ double actual_time = 0;
+
+ LARGE_INTEGER ifreq;
+ LARGE_INTEGER cnt1;
+ LARGE_INTEGER cnt2;
+
+ QueryPerformanceFrequency(&ifreq);
+ double freq = (double) ifreq.QuadPart;
+
+ QueryPerformanceCounter(&cnt1);
+
+ do {
+ QueryPerformanceCounter(&cnt2);
+
+ double delta = (double) (cnt2.QuadPart - cnt1.QuadPart);
+ actual_time = delta / freq;
+ }
+ while (actual_time < target_time);
+
+ return actual_time;
+}
+
+static double CalcCpuSpeed()
+{
+ DWORD clock1 = 0;
+ DWORD clock2 = 0;
+
+ TIMESNAP(clock1);
+
+ double seconds = SpinWait(0.1);
+
+ TIMESNAP(clock2);
+
+ double clocks = clock2 - clock1;
+
+ return (clocks/seconds);
+}
+
+/****************************************************************************
+ *
+ * GetDXVersion
+ *
+ * This function returns
+ * 0 Insufficient DirectX installed
+ * 9 At least DirectX 9 installed.
+ *
+ ****************************************************************************/
+
+DWORD GetDXVersion()
+{
+ HRESULT hr = 0;
+ HINSTANCE DDHinst = 0;
+ LPDIRECT3D9 d3d9 = 0;
+ OSVERSIONINFO osVer = { sizeof(OSVERSIONINFO) };
+
+ // First get the windows platform
+
+ if (!GetVersionEx(&osVer))
+ return 0;
+
+ // NT versions do not support DirectX 9
+ if (osVer.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+ if (osVer.dwMajorVersion <= 4)
+ return 0;
+ }
+
+ DDHinst = LoadLibrary("D3D9.DLL");
+ if (DDHinst == 0) {
+ return 0;
+ }
+
+ FreeLibrary(DDHinst);
+ return 9;
+}
+
+
+// +--------------------------------------------------------------------+
+
+int
+MachineInfo::GetCpuClass()
+{
+ if (cpu_class < 0) {
+ GetSystemInfo(&cpu_info);
+
+ if (cpu_info.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL ||
+ cpu_info.dwProcessorType < PROCESSOR_INTEL_PENTIUM)
+ Print("INCOMPATIBLE CPU TYPE!\n");
+
+ if (GetPlatform() < OS_WINNT)
+ cpu_class = CPU_P5;
+
+ else
+ cpu_class = cpu_info.wProcessorLevel;
+ }
+
+ return cpu_class;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MachineInfo::GetCpuSpeed()
+{
+ if (cpu_speed < 0) {
+ cpu_speed = (int) (CalcCpuSpeed() / 1e6);
+ }
+
+ return cpu_speed;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MachineInfo::GetTotalRam()
+{
+ if (total_ram < 0) {
+ GlobalMemoryStatus(&mem_info);
+ total_ram = (int) (mem_info.dwTotalPhys/(1024*1024)) + 1;
+ }
+
+ return total_ram;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MachineInfo::GetPlatform()
+{
+ if (platform < 0) {
+ GetVersionEx(&os_ver);
+
+ switch (os_ver.dwPlatformId) {
+ default:
+ case VER_PLATFORM_WIN32s: {
+ char msg[256];
+ sprintf(msg, "Invalid Operating System Platform: %d\n", os_ver.dwPlatformId);
+ Print(msg);
+ }
+ break;
+
+ case VER_PLATFORM_WIN32_WINDOWS:
+ if (os_ver.dwMajorVersion == 4 && os_ver.dwMinorVersion == 0)
+ platform = OS_WIN95;
+ else
+ platform = OS_WIN98;
+ break;
+
+ case VER_PLATFORM_WIN32_NT:
+ if (os_ver.dwMajorVersion == 4)
+ platform = OS_WINNT;
+ else if (os_ver.dwMajorVersion == 5 && os_ver.dwMinorVersion == 0)
+ platform = OS_WIN2K;
+ else if (os_ver.dwMajorVersion >= 5)
+ platform = OS_WINXP;
+
+ else {
+ platform = OS_INVALID;
+
+ Print("Invalid Operating System Platform (NT-series): %d.%d\n", os_ver.dwMajorVersion, os_ver.dwMinorVersion);
+ }
+
+ break;
+ }
+ }
+
+ return platform;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MachineInfo::GetDirectXVersion()
+{
+ if (dx_version < 0) {
+ dx_version = GetDXVersion();
+ }
+
+ return dx_version;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MachineInfo::DescribeMachine()
+{
+ GetPlatform();
+ GetCpuClass();
+
+ Print("+====================================================================+\n");
+ Print("| |\n");
+
+ char txt[256];
+
+ switch (platform) {
+ case OS_WIN95:
+ sprintf(txt, "Windows 95 version %d.%d.%d %s",
+ os_ver.dwMajorVersion,
+ os_ver.dwMinorVersion,
+ LOWORD(os_ver.dwBuildNumber),
+ os_ver.szCSDVersion);
+ break;
+
+ case OS_WIN98:
+ sprintf(txt, "Windows 98 version %d.%d.%d %s",
+ os_ver.dwMajorVersion,
+ os_ver.dwMinorVersion,
+ LOWORD(os_ver.dwBuildNumber),
+ os_ver.szCSDVersion);
+ break;
+
+ case OS_WINNT:
+ sprintf(txt, "Windows NT %d.%d (Build %d) %s",
+ os_ver.dwMajorVersion,
+ os_ver.dwMinorVersion,
+ os_ver.dwBuildNumber,
+ os_ver.szCSDVersion);
+ break;
+
+ case OS_WIN2K:
+ sprintf(txt, "Windows 2000 %d.%d (Build %d) %s",
+ os_ver.dwMajorVersion,
+ os_ver.dwMinorVersion,
+ os_ver.dwBuildNumber,
+ os_ver.szCSDVersion);
+
+ case OS_WINXP:
+ sprintf(txt, "Windows XP %d.%d (Build %d) %s",
+ os_ver.dwMajorVersion,
+ os_ver.dwMinorVersion,
+ os_ver.dwBuildNumber,
+ os_ver.szCSDVersion);
+ break;
+
+ default:
+ sprintf(txt, "Unknown Operating System Platform");
+ break;
+ }
+
+ Print("| %-66s |\n", txt);
+ Print("| |\n");
+
+ if (platform == OS_WIN95 || platform == OS_WIN98)
+ DescribeOwner95();
+ else
+ DescribeOwnerNT();
+
+ sprintf(txt, "CPUs Detected: %d CPU Level: %d.%d.%d CPU Speed: %d",
+ cpu_info.dwNumberOfProcessors,
+ cpu_info.wProcessorLevel,
+ cpu_info.wProcessorRevision >> 8,
+ cpu_info.wProcessorRevision & 0xff,
+ GetCpuSpeed() + 1);
+
+ Print("| %-66s |\n", txt);
+ DescribeCpuMake();
+
+ GlobalMemoryStatus(&mem_info);
+ total_ram = (int) (mem_info.dwTotalPhys/(1024*1024)) + 1;
+ int swap_max = (int) (mem_info.dwTotalPageFile/(1024*1024));
+ int swap_avail = (int) (mem_info.dwAvailPageFile/(1024*1024));
+
+ sprintf(txt, "%d MB RAM %d MB Max Swap %d MB Avail Swap",
+ total_ram, swap_max, swap_avail);
+
+
+ Print("| %-66s |\n", txt);
+
+ Print("| |\n");
+ Print("| DirectX %d installed. |\n",
+ GetDirectXVersion());
+ DescribeDXVersion("DDRAW");
+ DescribeDXVersion("D3DIM");
+ DescribeDXVersion("DINPUT");
+ DescribeDXVersion("DPLAY");
+ DescribeDXVersion("DSOUND");
+ DescribeDXVersion("DMUSIC");
+ DescribeDXVersion("DSHOW");
+ Print("| |\n");
+
+
+ if (platform == OS_WIN95 || platform == OS_WIN98) {
+ DescribeDrivers95("Display");
+ DescribeDrivers95("Media");
+ DescribeDrivers95("Monitor");
+ DescribeDrivers95("Multimedia");
+ }
+ else {
+ DescribeDriversNT("");
+ }
+
+ Print("+====================================================================+\n");
+ Print("\n");
+}
+
+// +--------------------------------------------------------------------+
+
+static void DescribeCpuMake()
+{
+ HKEY hkWin;
+ char sProcessor[256] = "";
+ char sMMXInfo[256] = "";
+ char sVendor[256] = "";
+ DWORD dwSize;
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ "Hardware\\Description\\System\\CentralProcessor\\0",
+ 0,
+ KEY_READ,
+ &hkWin) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "Identifier",
+ NULL,
+ NULL,
+ (LPBYTE) sProcessor,
+ &dwSize);
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "MMXIdentifier",
+ NULL,
+ NULL,
+ (LPBYTE) sMMXInfo,
+ &dwSize);
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "VendorIdentifier",
+ NULL,
+ NULL,
+ (LPBYTE) sVendor,
+ &dwSize);
+
+ RegCloseKey(hkWin);
+ }
+
+ if (sProcessor[0]) Print("| %-66s |\n", sProcessor);
+ if (sMMXInfo[0]) Print("| %-66s |\n", sMMXInfo);
+ if (sVendor[0]) Print("| %-66s |\n", sVendor);
+
+ if (sProcessor[0] || sMMXInfo[0] || sVendor[0])
+ Print("| |\n");
+}
+
+// +--------------------------------------------------------------------+
+
+static void DescribeOwner95()
+{
+ HKEY hkWin;
+ char sRegisteredOwner[256] = "";
+ char sRegisteredOrganization[256] = "";
+ DWORD dwSize;
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
+ 0,
+ KEY_READ,
+ &hkWin) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "RegisteredOwner",
+ NULL,
+ NULL,
+ (LPBYTE) sRegisteredOwner,
+ &dwSize);
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "RegisteredOrganization",
+ NULL,
+ NULL,
+ (LPBYTE) sRegisteredOrganization,
+ &dwSize);
+
+ RegCloseKey(hkWin);
+ }
+ else {
+ Print("Could not access registered owner\n");
+ }
+
+ if (sRegisteredOwner[0]) {
+ char txt[256];
+ sprintf(txt, "Registered Owner: %s, %s", sRegisteredOwner, sRegisteredOrganization);
+ Print("| %-66s |\n", txt);
+ Print("| |\n");
+ }
+}
+
+static void DescribeOwnerNT()
+{
+ HKEY hkWin;
+ char sRegisteredOwner[256] = "";
+ char sRegisteredOrganization[256] = "";
+ DWORD dwSize;
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ 0,
+ KEY_READ,
+ &hkWin) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "RegisteredOwner",
+ NULL,
+ NULL,
+ (LPBYTE) sRegisteredOwner,
+ &dwSize);
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "RegisteredOrganization",
+ NULL,
+ NULL,
+ (LPBYTE) sRegisteredOrganization,
+ &dwSize);
+
+ RegCloseKey(hkWin);
+ }
+ else {
+ Print("Could not access registered owner\n");
+ }
+
+ if (sRegisteredOwner[0]) {
+ char txt[256];
+ sprintf(txt, "Registered Owner: %s, %s", sRegisteredOwner, sRegisteredOrganization);
+ Print("| %-66s |\n", txt);
+ Print("| |\n");
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static void DescribeDrivers95(const char* sType)
+{
+ HKEY hkWin, hkSub;
+ int nKey = 0;
+ char sKey[256];
+ char sSub[256];
+ char sDriver[256];
+ char txt[256];
+ DWORD dwSize;
+ int worked;
+
+ // describe the video driver(s):
+ do {
+ worked = 0;
+
+ sprintf(sKey, "System\\CurrentControlSet\\Services\\Class\\%s\\%04X", sType, nKey);
+ sprintf(sSub, "System\\CurrentControlSet\\Services\\Class\\%s\\%04X\\DEFAULT", sType, nKey);
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ sKey,
+ 0,
+ KEY_READ,
+ &hkWin) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "DriverDesc",
+ NULL,
+ NULL,
+ (LPBYTE) sDriver,
+ &dwSize);
+
+ if (sDriver[0]) {
+ sprintf(txt, "* %s", sDriver);
+ Print("| %-66s |\n", txt);
+ worked = 1;
+ }
+
+ // try to find the driver file name:
+ if (worked) {
+ ZeroMemory(sDriver, sizeof(sDriver));
+
+ dwSize = 256;
+ DWORD err = RegQueryValueEx(hkWin, "Driver", NULL, NULL, (LPBYTE) sDriver, &dwSize);
+
+ if (err != ERROR_SUCCESS) {
+ dwSize = 256;
+ err = RegQueryValueEx(hkWin, "DeviceDriver", NULL, NULL, (LPBYTE) sDriver, &dwSize);
+ }
+
+ if (err != ERROR_SUCCESS) {
+ dwSize = 256;
+ err = RegQueryValueEx(hkWin, "drv", NULL, NULL, (LPBYTE) sDriver, &dwSize);
+ }
+
+ if (err != ERROR_SUCCESS) {
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ sSub,
+ 0,
+ KEY_READ,
+ &hkSub) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ err = RegQueryValueEx(hkSub, "drv", NULL, NULL, (LPBYTE) sDriver, &dwSize);
+
+ RegCloseKey(hkSub);
+ }
+ }
+
+ // if we found it, try to display version info:
+ if (err == ERROR_SUCCESS) {
+ DescribeDriverVersion(sDriver);
+ }
+
+ Print("| |\n");
+ }
+
+ RegCloseKey(hkWin);
+ }
+
+ nKey++;
+ }
+ while (worked);
+}
+
+static void DescribeDriversNT(const char* sType)
+{
+ Print("| |\n");
+
+ HKEY hkWin;
+ char sVideo[256] = "";
+ char sDriver[256] = "";
+ DWORD dwSize;
+
+ // find the pointer to the video driver:
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ "HARDWARE\\DEVICEMAP\\VIDEO",
+ 0,
+ KEY_READ,
+ &hkWin) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "\\Device\\Video0",
+ NULL,
+ NULL,
+ (LPBYTE) sVideo,
+ &dwSize);
+
+ RegCloseKey(hkWin);
+ }
+
+ // follow the pointer and get the driver description:
+ if (dwSize && sVideo[0]) {
+ const char* sLeader = "\\REGISTRY\\Machine\\";
+ int nLeader = strlen(sLeader);
+
+ if (strnicmp(sVideo, sLeader, nLeader) == 0) {
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ sVideo + nLeader,
+ 0,
+ KEY_READ,
+ &hkWin) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "Device Description",
+ NULL,
+ NULL,
+ (LPBYTE) sDriver,
+ &dwSize);
+
+ RegCloseKey(hkWin);
+ }
+ }
+ }
+
+ if (sDriver[0]) {
+ Print("| %-66s |\n", sDriver);
+ Print("| |\n");
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static char sTranslation[16];
+
+static void GetTranslation(const LPBYTE pBlock)
+{
+ LPBYTE sData = NULL;
+ UINT lenData = 0;
+
+ if (VerQueryValue(pBlock, "\\VarFileInfo\\Translation",
+ (LPVOID*) &sData, &lenData)) {
+
+ if (lenData && sData) {
+ sprintf(sTranslation, "%02X%02X%02X%02X", sData[1], sData[0], sData[3], sData[2]);
+ }
+ }
+}
+
+void DisplayVersionString(const LPBYTE pBlock, LPTSTR sSection)
+{
+ char txt[256];
+ char sFullSection[256];
+
+ sprintf(sFullSection, "\\StringFileInfo\\%s\\%s", sTranslation, sSection);
+
+ LPBYTE sData = NULL;
+ UINT lenData = 0;
+ DWORD dwErr = 0;
+
+ if (VerQueryValue(pBlock, sFullSection, (LPVOID*) &sData, &lenData)) {
+ if (lenData && sData) {
+ sprintf(txt, "%-16s %s", sSection, sData);
+ Print("| %-60s |\n", txt);
+ }
+ }
+}
+
+static void DescribeDriverVersion(const char* file)
+{
+ DWORD dwHandle = 0;
+ TCHAR szFile[512];
+
+ strcpy(szFile, file);
+
+ int nBytes = GetFileVersionInfoSize(szFile, &dwHandle);
+
+ if (nBytes <= 0) {
+ char szWinDir[256];
+ GetSystemDirectory(szWinDir, 256);
+ sprintf(szFile, "%s\\%s", szWinDir, file);
+
+ nBytes = GetFileVersionInfoSize(szFile, &dwHandle);
+
+ if (nBytes <= 0)
+ return;
+ }
+
+ LPBYTE pBlock = new(__FILE__,__LINE__) BYTE[nBytes];
+
+ if (pBlock && GetFileVersionInfo(szFile, dwHandle, nBytes, (LPVOID) pBlock)) {
+ GetTranslation(pBlock);
+ DisplayVersionString(pBlock, "CompanyName");
+// DisplayVersionString(pBlock, "FileDescription");
+ DisplayVersionString(pBlock, "FileVersion");
+// DisplayVersionString(pBlock, "InternalName");
+// DisplayVersionString(pBlock, "LegalCopyright");
+// DisplayVersionString(pBlock, "OriginalFilename");
+// DisplayVersionString(pBlock, "ProductName");
+// DisplayVersionString(pBlock, "ProductVersion");
+// DisplayVersionString(pBlock, "Comments");
+// DisplayVersionString(pBlock, "LegalTrademarks");
+// DisplayVersionString(pBlock, "PrivateBuild");
+// DisplayVersionString(pBlock, "SpecialBuild");
+ }
+
+ delete [] pBlock;
+}
+
+static void DescribeDXVersion(const char* component)
+{
+ DWORD dwHandle = 0;
+ char szFile[512];
+ char szWinDir[512];
+
+ GetSystemDirectory(szWinDir, 512);
+
+ sprintf(szFile, "%s\\%s.dll", szWinDir, component);
+
+ int nBytes = GetFileVersionInfoSize(szFile, &dwHandle);
+
+ if (nBytes <= 0) {
+ return;
+ }
+
+ LPBYTE pBlock = new(__FILE__,__LINE__) BYTE[nBytes];
+
+ if (pBlock && GetFileVersionInfo(szFile, dwHandle, nBytes, (LPVOID) pBlock)) {
+ GetTranslation(pBlock);
+
+ char txt[256];
+ char sFullSection[256];
+ LPBYTE sData = NULL;
+ UINT lenData = 0;
+ DWORD dwErr = 0;
+
+ sprintf(sFullSection, "\\StringFileInfo\\%s\\FileVersion", sTranslation);
+
+ if (VerQueryValue(pBlock, sFullSection, (LPVOID*) &sData, &lenData)) {
+ if (lenData && sData) {
+ sprintf(txt, "%-8s%s", component, sData);
+ Print("| %-64s |\n", txt);
+ }
+ }
+ }
+
+ delete [] pBlock;
+} \ No newline at end of file
diff --git a/nGenEx/MachineInfo.h b/nGenEx/MachineInfo.h
new file mode 100644
index 0000000..58f26f4
--- /dev/null
+++ b/nGenEx/MachineInfo.h
@@ -0,0 +1,42 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: MachineInfo.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Collect and Display Machine, OS, and Driver Information
+*/
+
+#ifndef MachineInfo_h
+#define MachineInfo_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class MachineInfo
+{
+public:
+ enum { CPU_INVALID, CPU_P5=5, CPU_P6=6, CPU_P7=7, CPU_PLUS };
+ enum { OS_INVALID, OS_WIN95, OS_WIN98, OS_WINNT, OS_WIN2K, OS_WINXP };
+ enum { DX_NONE, DX_3=3, DX_5=5, DX_6=6, DX_7=7, DX_8=8, DX_9=9, DX_PLUS };
+
+ static int GetCpuClass();
+ static int GetCpuSpeed();
+ static int GetTotalRam();
+ static int GetPlatform();
+ static int GetDirectXVersion();
+
+ static void DescribeMachine();
+
+ static const char* GetShortDescription();
+};
+
+// +--------------------------------------------------------------------+
+
+#endif MachineInfo_h \ No newline at end of file
diff --git a/nGenEx/Menu.cpp b/nGenEx/Menu.cpp
new file mode 100644
index 0000000..9daced4
--- /dev/null
+++ b/nGenEx/Menu.cpp
@@ -0,0 +1,143 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Menu.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Simple menu hierarchy class
+*/
+
+#include "MemDebug.h"
+#include "Menu.h"
+
+// +--------------------------------------------------------------------+
+
+void
+Menu::AddItem(Text label, DWORD value, bool enabled)
+{
+ MenuItem* item = new(__FILE__,__LINE__) MenuItem(label, value, enabled);
+
+ if (item) {
+ item->menu = this;
+ items.append(item);
+ }
+}
+
+void
+Menu::AddItem(MenuItem* item)
+{
+ if (item->submenu)
+ item->submenu->SetParent(this);
+ item->menu = this;
+ items.append(item);
+}
+
+void
+Menu::AddMenu(Text label, Menu* menu, DWORD value)
+{
+ MenuItem* item = new(__FILE__,__LINE__) MenuItem(label, value);
+
+ if (item) {
+ item->menu = this;
+ item->submenu = menu;
+ menu->parent = this;
+
+ items.append(item);
+ }
+}
+
+MenuItem*
+Menu::GetItem(int index)
+{
+ if (index >= 0 && index < items.size())
+ return items[index];
+
+ return 0;
+}
+
+void
+Menu::SetItem(int index, MenuItem* item)
+{
+ if (item && index >= 0 && index < items.size())
+ items[index] = item;
+}
+
+int
+Menu::NumItems() const
+{
+ return items.size();
+}
+
+void
+Menu::ClearItems()
+{
+ items.destroy();
+}
+
+
+// +--------------------------------------------------------------------+
+
+MenuItem::MenuItem(Text label, DWORD value, bool e)
+ : text(label), data(value), enabled(e), submenu(0), selected(0)
+{ }
+
+MenuItem::~MenuItem()
+{ }
+
+// +--------------------------------------------------------------------+
+
+Menu*
+MenuHistory::GetCurrent()
+{
+ int n = history.size();
+
+ if (n)
+ return history[n-1];
+
+ return 0;
+}
+
+Menu*
+MenuHistory::GetLevel(int n)
+{
+ if (n >= 0 && n < history.size())
+ return history[n];
+
+ return 0;
+}
+
+Menu*
+MenuHistory::Find(const char* title)
+{
+ for (int i = 0; i < history.size(); i++)
+ if (history[i]->GetTitle() == title)
+ return history[i];
+
+ return 0;
+}
+
+void
+MenuHistory::Pop()
+{
+ int n = history.size();
+
+ if (n)
+ history.removeIndex(n-1);
+}
+
+void
+MenuHistory::Push(Menu* menu)
+{
+ history.append(menu);
+}
+
+void
+MenuHistory::Clear()
+{
+ history.clear();
+}
diff --git a/nGenEx/Menu.h b/nGenEx/Menu.h
new file mode 100644
index 0000000..649784b
--- /dev/null
+++ b/nGenEx/Menu.h
@@ -0,0 +1,124 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Menu.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Simple menu hierarchy class
+*/
+
+#ifndef Menu_h
+#define Menu_h
+
+#include "Types.h"
+#include "List.h"
+#include "Text.h"
+
+// +-------------------------------------------------------------------+
+
+class Menu;
+class MenuItem;
+class MenuHistory;
+
+// +-------------------------------------------------------------------+
+
+class Menu
+{
+public:
+ static const char* TYPENAME() { return "Menu"; }
+
+ Menu() { }
+ Menu(Text t) : title(t) { }
+ virtual ~Menu() { items.destroy(); }
+
+ virtual Text GetTitle() const { return title; }
+ virtual void SetTitle(Text t) { title = t; }
+ virtual Menu* GetParent() const { return parent; }
+ virtual void SetParent(Menu* p) { parent = p; }
+
+ virtual void AddItem(Text label, DWORD value=0, bool enabled=true);
+ virtual void AddItem(MenuItem* item);
+ virtual void AddMenu(Text label, Menu* menu, DWORD value=0);
+ virtual MenuItem* GetItem(int index);
+ virtual void SetItem(int index, MenuItem* item);
+ virtual int NumItems() const;
+ virtual void ClearItems();
+
+ ListIter<MenuItem> GetItems() { return items; }
+
+protected:
+ Text title;
+ List<MenuItem> items;
+ Menu* parent;
+
+ friend class MenuItem;
+};
+
+// +-------------------------------------------------------------------+
+
+class MenuItem
+{
+public:
+ static const char* TYPENAME() { return "MenuItem"; }
+
+ MenuItem(Text label, DWORD value=0, bool enabled=true);
+ virtual ~MenuItem();
+
+ virtual Text GetText() const { return text; }
+ virtual void SetText(Text t) { text = t; }
+
+ virtual DWORD GetData() const { return data; }
+ virtual void SetData(DWORD d) { data = d; }
+
+ virtual int GetEnabled() const { return enabled; }
+ virtual void SetEnabled(int e) { enabled = e; }
+
+ virtual int GetSelected() const { return selected; }
+ virtual void SetSelected(int s) { selected = s; }
+
+ virtual Menu* GetMenu() const { return menu; }
+ virtual void SetMenu(Menu* m) { menu = m; }
+
+ virtual Menu* GetSubmenu() const { return submenu; }
+ virtual void SetSubmenu(Menu* s) { submenu = s; }
+
+protected:
+ Text text;
+ DWORD data;
+ int enabled;
+ int selected;
+
+ Menu* menu;
+ Menu* submenu;
+
+ friend class Menu;
+};
+
+// +-------------------------------------------------------------------+
+
+class MenuHistory
+{
+public:
+ static const char* TYPENAME() { return "MenuHistory"; }
+
+ MenuHistory() { }
+ virtual ~MenuHistory() { history.clear(); }
+
+ virtual Menu* GetCurrent();
+ virtual Menu* GetLevel(int n);
+ virtual Menu* Find(const char* title);
+ virtual void Pop();
+ virtual void Push(Menu* menu);
+ virtual void Clear();
+
+private:
+ List<Menu> history;
+};
+
+#endif Menu_h
+
diff --git a/nGenEx/MotionController.h b/nGenEx/MotionController.h
new file mode 100644
index 0000000..b6eab6f
--- /dev/null
+++ b/nGenEx/MotionController.h
@@ -0,0 +1,231 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: MotionController.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract MotionController class (hides details of Joystick, Keyboard, etc.)
+*/
+
+#ifndef MoCon_h
+#define MoCon_h
+
+// +--------------------------------------------------------------------+
+
+struct KeyMapEntry
+{
+ static const char* TYPENAME() { return "KeyMapEntry"; }
+
+ KeyMapEntry() : act(0), key(0), alt(0), joy(0) { }
+ KeyMapEntry(int a, int k, int s=0, int j=0) : act(a), key(k), alt(s), joy(j) { }
+
+ int operator==(const KeyMapEntry& k) const { return act==k.act && key==k.key && alt==k.alt && joy==k.joy; }
+ int operator!=(const KeyMapEntry& k) const { return !(*this==k); }
+
+ int act;
+ int key;
+ int alt;
+ int joy;
+};
+
+// +--------------------------------------------------------------------+
+
+const int KEY_MAP_SIZE = 256;
+const int KEY_BASE_SIZE = 64;
+const int KEY_USER_SIZE = KEY_MAP_SIZE - KEY_BASE_SIZE;
+
+const int KEY_MAP_BASE = 0;
+const int KEY_MAP_END = KEY_MAP_BASE + KEY_BASE_SIZE - 1;
+
+const int KEY_USER_BASE = KEY_MAP_END + 1;
+const int KEY_USER_END = KEY_USER_BASE + KEY_USER_SIZE - 1;
+
+const int KEY_MAP_FIRST = KEY_MAP_BASE;
+const int KEY_MAP_LAST = KEY_MAP_BASE + KEY_MAP_SIZE - 1;
+
+// MAP NAMES:
+
+const int KEY_PLUS_X = 1;
+const int KEY_MINUS_X = 2;
+const int KEY_PLUS_Y = 3;
+const int KEY_MINUS_Y = 4;
+const int KEY_PLUS_Z = 5;
+const int KEY_MINUS_Z = 6;
+
+const int KEY_PITCH_UP = 7;
+const int KEY_PITCH_DOWN = 8;
+const int KEY_YAW_LEFT = 9;
+const int KEY_YAW_RIGHT = 10;
+const int KEY_ROLL_LEFT = 11;
+const int KEY_ROLL_RIGHT = 12;
+const int KEY_CENTER = 13;
+const int KEY_ROLL_ENABLE = 14;
+
+const int KEY_ACTION_0 = 15;
+const int KEY_ACTION_1 = 16;
+const int KEY_ACTION_2 = 17;
+const int KEY_ACTION_3 = 18;
+
+const int KEY_CONTROL_MODEL = 19;
+const int KEY_MOUSE_SELECT = 20;
+const int KEY_MOUSE_SENSE = 21;
+const int KEY_MOUSE_SWAP = 22;
+const int KEY_MOUSE_INVERT = 23;
+const int KEY_MOUSE_ACTIVE = 24;
+
+const int KEY_JOY_SELECT = 25;
+const int KEY_JOY_THROTTLE = 26;
+const int KEY_JOY_RUDDER = 27;
+const int KEY_JOY_SENSE = 28;
+const int KEY_JOY_DEAD_ZONE = 29;
+const int KEY_JOY_SWAP = 30;
+
+const int KEY_AXIS_YAW = 32;
+const int KEY_AXIS_PITCH = 33;
+const int KEY_AXIS_ROLL = 34;
+const int KEY_AXIS_THROTTLE = 35;
+
+const int KEY_AXIS_YAW_INVERT = 38;
+const int KEY_AXIS_PITCH_INVERT = 39;
+const int KEY_AXIS_ROLL_INVERT = 40;
+const int KEY_AXIS_THROTTLE_INVERT = 41;
+
+
+// CONTROL VALUES:
+
+// joystick buttons and switches must use
+// ids greater than 255 so they don't interfere
+// with extended ascii numbers for keyboard keys
+
+const int KEY_JOY_AXIS_X = 0x1A0;
+const int KEY_JOY_AXIS_Y = 0x1A1;
+const int KEY_JOY_AXIS_Z = 0x1A2;
+const int KEY_JOY_AXIS_RX = 0x1A3;
+const int KEY_JOY_AXIS_RY = 0x1A4;
+const int KEY_JOY_AXIS_RZ = 0x1A5;
+const int KEY_JOY_AXIS_S0 = 0x1A6;
+const int KEY_JOY_AXIS_S1 = 0x1A7;
+
+const int KEY_JOY_1 = 0x1C1;
+const int KEY_JOY_2 = 0x1C2;
+const int KEY_JOY_3 = 0x1C3;
+const int KEY_JOY_4 = 0x1C4;
+const int KEY_JOY_5 = 0x1C5;
+const int KEY_JOY_6 = 0x1C6;
+const int KEY_JOY_7 = 0x1C7;
+const int KEY_JOY_8 = 0x1C8;
+const int KEY_JOY_9 = 0x1C9;
+const int KEY_JOY_10 = 0x1CA;
+const int KEY_JOY_11 = 0x1CB;
+const int KEY_JOY_12 = 0x1CC;
+const int KEY_JOY_13 = 0x1CD;
+const int KEY_JOY_14 = 0x1CE;
+const int KEY_JOY_15 = 0x1CF;
+const int KEY_JOY_16 = 0x1D0;
+
+const int KEY_JOY_32 = 0x1E0;
+
+const int KEY_POV_0_UP = 0x1F0;
+const int KEY_POV_0_DOWN = 0x1F1;
+const int KEY_POV_0_LEFT = 0x1F2;
+const int KEY_POV_0_RIGHT = 0x1F3;
+
+const int KEY_POV_1_UP = 0x1F4;
+const int KEY_POV_1_DOWN = 0x1F5;
+const int KEY_POV_1_LEFT = 0x1F6;
+const int KEY_POV_1_RIGHT = 0x1F7;
+
+const int KEY_POV_2_UP = 0x1F8;
+const int KEY_POV_2_DOWN = 0x1F9;
+const int KEY_POV_2_LEFT = 0x1FA;
+const int KEY_POV_2_RIGHT = 0x1FB;
+
+const int KEY_POV_3_UP = 0x1FC;
+const int KEY_POV_3_DOWN = 0x1FD;
+const int KEY_POV_3_LEFT = 0x1FE;
+const int KEY_POV_3_RIGHT = 0x1FF;
+
+// +--------------------------------------------------------------------+
+
+class MotionController
+{
+public:
+ static const char* TYPENAME() { return "MotionController"; }
+
+ MotionController()
+ : status(StatusOK), sensitivity(1), dead_zone(0),
+ swapped(0), inverted(0), rudder(0), throttle(0), select(0) { }
+
+ virtual ~MotionController() { }
+
+ enum StatusValue { StatusOK, StatusErr, StatusBadParm };
+ enum ActionValue { MaxActions = 32 };
+
+ StatusValue Status() const { return status; }
+ int Sensitivity() const { return sensitivity; }
+ int DeadZone() const { return dead_zone; }
+ int Swapped() const { return swapped; }
+ int Inverted() const { return inverted; }
+ int RudderEnabled() const { return rudder; }
+ int ThrottleEnabled() const { return throttle; }
+ int Selector() const { return select; }
+
+
+ // setup:
+ virtual void SetSensitivity(int sense, int dead)
+ {
+ if (sense > 0) sensitivity = sense;
+ if (dead > 0) dead_zone = dead;
+ }
+
+ virtual void SetSelector(int sel) { select = sel; }
+ virtual void SetRudderEnabled(int rud) { rudder = rud; }
+ virtual void SetThrottleEnabled(int t) { throttle = t; }
+
+ virtual void SwapYawRoll(int swap) { swapped = swap; }
+ virtual int GetSwapYawRoll() { return swapped; }
+ virtual void InvertPitch(int inv) { inverted = inv; }
+ virtual int GetInverted() { return inverted; }
+
+ virtual void MapKeys(KeyMapEntry* mapping, int nkeys) { }
+
+ // sample the physical device
+ virtual void Acquire() { }
+
+ // translations
+ virtual double X() { return 0; }
+ virtual double Y() { return 0; }
+ virtual double Z() { return 0; }
+
+ // rotations
+ virtual double Pitch() { return 0; }
+ virtual double Roll() { return 0; }
+ virtual double Yaw() { return 0; }
+ virtual int Center() { return 0; }
+
+ // throttle
+ virtual double Throttle() { return 0; }
+ virtual void SetThrottle(double t) { }
+
+ // actions
+ virtual int Action(int n) { return 0; }
+ virtual int ActionMap(int n) { return 0; }
+
+protected:
+ StatusValue status;
+ int sensitivity;
+ int dead_zone;
+ int swapped;
+ int inverted;
+ int rudder;
+ int throttle;
+ int select;
+};
+
+#endif MoCon_h
+
diff --git a/nGenEx/Mouse.cpp b/nGenEx/Mouse.cpp
new file mode 100644
index 0000000..30c0c05
--- /dev/null
+++ b/nGenEx/Mouse.cpp
@@ -0,0 +1,192 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Mouse.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mouse class
+*/
+
+#include "MemDebug.h"
+#include "Mouse.h"
+#include "DataLoader.h"
+#include "Window.h"
+#include "Screen.h"
+#include "Bitmap.h"
+#include "Video.h"
+
+// +--------------------------------------------------------------------+
+
+int Mouse::show = 1;
+int Mouse::cursor = Mouse::ARROW;
+
+int Mouse::x = 320;
+int Mouse::y = 240;
+int Mouse::l = 0;
+int Mouse::m = 0;
+int Mouse::r = 0;
+int Mouse::w = 0;
+
+Bitmap* Mouse::image[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+int Mouse::hotspot[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+Window* Mouse::window = 0;
+
+// +--------------------------------------------------------------------+
+
+void Mouse::Create(Screen* screen)
+{
+ if (screen) {
+ delete window;
+ window = new(__FILE__,__LINE__) Window(screen, 0, 0, screen->Width(), screen->Height());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void Mouse::Resize(Screen* screen)
+{
+ if (screen) {
+ delete window;
+ window = new(__FILE__,__LINE__) Window(screen, 0, 0, screen->Width(), screen->Height());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void Mouse::Close()
+{
+ for (int i = 0; i < 8; i++) {
+ delete image[i];
+ image[i] = 0;
+ }
+
+ delete window;
+ window = 0;
+
+ show = 0;
+ cursor = ARROW;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Mouse::SetCursorPos(int ax, int ay)
+{
+ POINT p;
+ int dx = 0;
+ int dy = 0;
+
+ ::GetCursorPos(&p);
+
+ dx = p.x - x;
+ dy = p.y - y;
+
+ x = ax;
+ y = ay;
+
+ ::SetCursorPos(x+dx,y+dy);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Mouse::SetCursor(CURSOR c)
+{
+ int old = cursor;
+ cursor = c;
+ return old;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Mouse::LoadCursor(CURSOR c, const char* name, HOTSPOT hs)
+{
+ int result = 0;
+
+ delete image[c];
+ image[c] = 0;
+
+ if (name && *name) {
+ image[c] = new(__FILE__,__LINE__) Bitmap;
+ result = DataLoader::GetLoader()->LoadBitmap(name, *image[c], Bitmap::BMP_TRANSPARENT);
+
+ if (result) {
+ Bitmap* bmp = image[c];
+
+ if (bmp && bmp->HiPixels())
+ image[c]->CopyAlphaRedChannel(image[c]->Width(), image[c]->Height(), (LPDWORD) image[c]->HiPixels());
+
+ hotspot[c] = hs;
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Mouse::Show(int s)
+{
+ show = s;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Mouse::Paint()
+{
+ if (!show || !window) return;
+
+ // draw using bitmap:
+ if (image[cursor]) {
+ int w2 = image[cursor]->Width()/2;
+ int h2 = image[cursor]->Height()/2;
+
+ if (hotspot[cursor] == HOTSPOT_NW)
+ window->DrawBitmap(x, y, x+w2*2, y+h2*2, image[cursor], Video::BLEND_ALPHA);
+ else
+ window->DrawBitmap(x-w2, y-h2, x+w2, y+h2, image[cursor], Video::BLEND_ALPHA);
+ }
+
+ // draw using primitives:
+ /***
+ else {
+ switch (cursor) {
+ case ARROW:
+ case CROSS:
+ case USER1:
+ case USER2:
+ case USER3:
+ default:
+ window->DrawLine(x-7, y, x+8, y, Color::White);
+ window->DrawLine(x, y-7, x, y+8, Color::White);
+ break;
+
+ case WAIT:
+ window->DrawLine(x-7, y-7, x+8, y-7, Color::White);
+ window->DrawLine(x-7, y-7, x+8, y+8, Color::White);
+ window->DrawLine(x-7, y+8, x+8, y-7, Color::White);
+ window->DrawLine(x-7, y+8, x+8, y+8, Color::White);
+ break;
+
+ case NOT:
+ window->DrawEllipse(x-7,y-7,x+8,y+8, Color::White);
+ window->DrawLine( x-7,y-7,x+8,y+8, Color::White);
+ break;
+
+ case DRAG:
+ window->DrawRect(x-7, y-6, x+8, y-3, Color::White);
+ window->DrawRect(x-7, y-1, x+8, y+2, Color::White);
+ window->DrawRect(x-7, y+4, x+8, y+7, Color::White);
+ break;
+ }
+ }
+ ***/
+}
diff --git a/nGenEx/Mouse.h b/nGenEx/Mouse.h
new file mode 100644
index 0000000..3f6e4ee
--- /dev/null
+++ b/nGenEx/Mouse.h
@@ -0,0 +1,75 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Mouse.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mouse class
+*/
+
+#ifndef Mouse_h
+#define Mouse_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+class Screen;
+class Window;
+
+// +--------------------------------------------------------------------+
+
+class Mouse
+{
+ friend LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+ friend class Game;
+
+public:
+ static const char* TYPENAME() { return "Mouse"; }
+
+ enum CURSOR { ARROW, CROSS, WAIT, NOT, DRAG, USER1, USER2, USER3 };
+ enum HOTSPOT { HOTSPOT_CTR, HOTSPOT_NW };
+
+ static int X() { return x; }
+ static int Y() { return y; }
+ static int LButton() { return l; }
+ static int MButton() { return m; }
+ static int RButton() { return r; }
+ static int Wheel() { return w; }
+
+ static void Paint();
+
+ static void SetCursorPos(int x, int y);
+ static void Show(int s=1);
+ static int SetCursor(CURSOR c);
+ static int LoadCursor(CURSOR c, const char* name, HOTSPOT hs = HOTSPOT_CTR);
+
+ static void Create(Screen* screen);
+ static void Resize(Screen* screen);
+ static void Close();
+
+private:
+ static int show;
+ static int cursor;
+
+ static int x;
+ static int y;
+ static int l;
+ static int m;
+ static int r;
+ static int w;
+
+ static Bitmap* image[8];
+ static int hotspot[8];
+
+ static Window* window;
+};
+
+#endif Mouse_h
+
diff --git a/nGenEx/MouseController.cpp b/nGenEx/MouseController.cpp
new file mode 100644
index 0000000..ecdf5ad
--- /dev/null
+++ b/nGenEx/MouseController.cpp
@@ -0,0 +1,247 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: MouseController.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ MouseController Input class
+*/
+
+#include "MemDebug.h"
+#include "MouseController.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "Game.h"
+#include "Video.h"
+
+// +--------------------------------------------------------------------+
+
+static MouseController* instance = 0;
+static DWORD rbutton_latch = 0;
+static DWORD mbutton_latch = 0;
+static DWORD active_latch = 0;
+
+// +--------------------------------------------------------------------+
+
+MouseController::MouseController()
+ : p(0), r(0), w(0), dx(0), dy(0), t(0)
+{
+ instance = this;
+ select = 0;
+ sensitivity = 10;
+ swapped = 0;
+ active = false;
+
+ active_key = 20; // caps lock
+
+ rbutton_latch = 0;
+
+ for (int i = 0; i < MotionController::MaxActions; i++)
+ action[i] = 0;
+}
+
+MouseController::~MouseController()
+{
+ instance = 0;
+}
+
+MouseController*
+MouseController::GetInstance()
+{
+ return instance;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MouseController::MapKeys(KeyMapEntry* mapping, int nkeys)
+{
+ for (int i = 0; i < nkeys; i++) {
+ KeyMapEntry k = mapping[i];
+
+ if (k.act >= KEY_MAP_FIRST && k.act <= KEY_MAP_LAST) {
+
+ if (k.act == KEY_MOUSE_SENSE)
+ sensitivity = k.key;
+
+ else if (k.act == KEY_MOUSE_SWAP)
+ swapped = k.key;
+
+ else if (k.act == KEY_MOUSE_INVERT)
+ inverted = k.key;
+
+ else if (k.act == KEY_MOUSE_SELECT)
+ select = k.key;
+
+ else if (k.act == KEY_MOUSE_ACTIVE)
+ active_key = k.key;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static inline double sqr(double a) { return a*a; }
+
+void
+MouseController::Acquire()
+{
+ p = r = w = 0;
+ action[0] = 0;
+ action[1] = 0;
+ action[2] = 0;
+ action[3] = 0;
+
+ if (active_key && Keyboard::KeyDown(active_key)) {
+ active_latch = 1;
+ }
+ else {
+ if (active_latch) {
+ active_latch = 0;
+ active = !active;
+ }
+ }
+
+ if (!select || !active)
+ return;
+
+ action[0] = Mouse::LButton();
+
+ int roll_enable = 0;
+
+ if (Mouse::RButton()) {
+ roll_enable = 1;
+
+ if (!rbutton_latch)
+ rbutton_latch = Game::RealTime();
+ }
+ else {
+ if (rbutton_latch) {
+ rbutton_latch = Game::RealTime() - rbutton_latch;
+ if (rbutton_latch < 250)
+ action[1] = 1;
+ }
+
+ rbutton_latch = 0;
+ }
+
+ if (Mouse::MButton()) {
+ if (!mbutton_latch)
+ mbutton_latch = Game::RealTime();
+ }
+ else {
+ if (mbutton_latch) {
+ action[3] = 1;
+ }
+
+ mbutton_latch = 0;
+ }
+
+ double step = 0;
+ int cx = Video::GetInstance()->Width()/2;
+ int cy = Video::GetInstance()->Height()/2;
+
+ dx += Mouse::X() - cx;
+ dy += Mouse::Y() - cy;
+
+ step = fabs(dx)/cx;
+
+ if (roll_enable || select == 1)
+ step *= 3 * sensitivity;
+ else
+ step *= step * sensitivity/4;
+
+ if (roll_enable) {
+ if (dx > 0)
+ r = -step;
+ else if (dx < 0)
+ r = step;
+ }
+ else {
+ if (dx > 0)
+ w = step;
+ else if (dx < 0)
+ w = -step;
+ }
+
+ step = fabs(dy)/cy;
+
+ if (select == 1)
+ step *= 2 * sensitivity;
+ else
+ step *= step * sensitivity/4;
+
+ if (inverted) {
+ step *= -1;
+ }
+
+ if (dy > 0)
+ p = step;
+ else if (dy < 0)
+ p = -step;
+
+ if (select == 1) {
+ ::SetCursorPos(cx, cy);
+
+ double drain = cx * 4 * Game::FrameTime();
+
+ if (dx > drain) {
+ dx -= drain;
+ }
+ else if (dx < -drain) {
+ dx += drain;
+ }
+ else {
+ dx = 0;
+ }
+
+ if (dy > drain) {
+ dy -= drain;
+ }
+ else if (dy < -drain) {
+ dy += drain;
+ }
+ else {
+ dy = 0;
+ }
+ }
+ else {
+ dx = 0;
+ dy = 0;
+ }
+
+ if (Mouse::Wheel() > 0) {
+ if (t < 0.25)
+ t += 0.025;
+ else
+ t += 0.1;
+
+ if (t > 1) t = 1;
+ }
+
+ else if (Mouse::Wheel() < 0) {
+ if (t < 0.25)
+ t -= 0.025;
+ else
+ t -= 0.1;
+
+ if (t < 0) t = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MouseController::ActionMap(int n)
+{
+ if (n >= KEY_ACTION_0 && n <= KEY_ACTION_3)
+ return action[n - KEY_ACTION_0];
+
+ return 0;
+}
+
diff --git a/nGenEx/MouseController.h b/nGenEx/MouseController.h
new file mode 100644
index 0000000..c9ed6bb
--- /dev/null
+++ b/nGenEx/MouseController.h
@@ -0,0 +1,70 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: MouseController.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Joystick Input class
+*/
+
+#ifndef MouseController_h
+#define MouseController_h
+
+#include "MotionController.h"
+
+// +--------------------------------------------------------------------+
+
+class MouseController : public MotionController
+{
+public:
+ static const char* TYPENAME() { return "MouseController"; }
+
+ MouseController();
+ virtual ~MouseController();
+
+ // setup
+ virtual void MapKeys(KeyMapEntry* mapping, int nkeys);
+
+ // sample the physical device
+ virtual void Acquire();
+
+ // translations
+ virtual double X() { return 0; }
+ virtual double Y() { return 0; }
+ virtual double Z() { return 0; }
+
+ // rotations
+ virtual double Pitch() { if (active) return p; return 0; }
+ virtual double Roll() { if (active) return r; return 0; }
+ virtual double Yaw() { if (active) return w; return 0; }
+ virtual int Center() { return 0; }
+
+ // throttle
+ virtual double Throttle() { if (active) return t; return 0; }
+ virtual void SetThrottle(double throttle) { t = throttle; }
+
+ // actions
+ virtual int Action(int n) { return action[n]; }
+ virtual int ActionMap(int n);
+
+ // actively sampling?
+ virtual bool Active() { return active; }
+ virtual void SetActive(bool a) { active = a; }
+
+ static MouseController* GetInstance();
+
+protected:
+ double p,r,w, dx, dy, t;
+ int action[MotionController::MaxActions];
+ int map[32];
+ bool active;
+ int active_key;
+};
+
+#endif MouseController_h
+
diff --git a/nGenEx/MultiController.cpp b/nGenEx/MultiController.cpp
new file mode 100644
index 0000000..7631ac5
--- /dev/null
+++ b/nGenEx/MultiController.cpp
@@ -0,0 +1,130 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: MultiController.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ MultiController Input class
+*/
+
+#include "MemDebug.h"
+#include "MultiController.h"
+
+// +--------------------------------------------------------------------+
+
+MultiController::MultiController()
+ : x(0), y(0), z(0), p(0), r(0), w(0), c(0), p1(0), r1(0), w1(0), t(0)
+{
+ for (int i = 0; i < MotionController::MaxActions; i++)
+ action[i] = 0;
+
+ nctrl = 0;
+ for (i = 0; i < 4; i++)
+ ctrl[i] = 0;
+}
+
+MultiController::~MultiController()
+{
+ for (int i = 0; i < 4; i++)
+ delete ctrl[i];
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MultiController::AddController(MotionController* c)
+{
+ if (nctrl < 4 && c)
+ ctrl[nctrl++] = c;
+}
+
+void
+MultiController::MapKeys(KeyMapEntry* mapping, int nkeys)
+{
+ for (int i = 0; i < nctrl; i++)
+ ctrl[i]->MapKeys(mapping, nkeys);
+}
+
+int
+MultiController::GetSwapYawRoll() const
+{
+ if (nctrl)
+ return ctrl[0]->GetSwapYawRoll();
+
+ return 0;
+}
+
+void
+MultiController::SwapYawRoll(int swap)
+{
+ for (int i = 0; i < nctrl; i++)
+ ctrl[i]->SwapYawRoll(swap);
+}
+
+// +--------------------------------------------------------------------+
+
+inline void clamp(double& x) { if (x<-1)x=-1; else if (x>1)x=1; }
+
+void
+MultiController::Acquire()
+{
+ t = x = y = z = p = r = w = c = 0;
+
+ for (int i = 0; i < MotionController::MaxActions; i++)
+ action[i] = 0;
+
+ for (i = 0; i < nctrl; i++) {
+ ctrl[i]->Acquire();
+
+ x += ctrl[i]->X();
+ y += ctrl[i]->Y();
+ z += ctrl[i]->Z();
+
+ r += ctrl[i]->Roll();
+ p += ctrl[i]->Pitch();
+ w += ctrl[i]->Yaw();
+ c += ctrl[i]->Center();
+ t += ctrl[i]->Throttle();
+
+ for (int a = 0; a < MotionController::MaxActions; a++)
+ action[a] += ctrl[i]->Action(a);
+ }
+
+ clamp(x);
+ clamp(y);
+ clamp(z);
+ clamp(r);
+ clamp(p);
+ clamp(w);
+ clamp(t);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MultiController::SetThrottle(double throttle)
+{
+ for (int i = 0; i < nctrl; i++)
+ ctrl[i]->SetThrottle(throttle);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MultiController::ActionMap(int key)
+{
+ for (int i = 0; i < nctrl; i++) {
+ int result = ctrl[i]->ActionMap(key);
+
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+
diff --git a/nGenEx/MultiController.h b/nGenEx/MultiController.h
new file mode 100644
index 0000000..a65ae85
--- /dev/null
+++ b/nGenEx/MultiController.h
@@ -0,0 +1,68 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: MultiController.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ComboController Motion Controller class
+*/
+
+#ifndef MultiController_h
+#define MultiController_h
+
+#include "MotionController.h"
+
+// +--------------------------------------------------------------------+
+
+class MultiController : public MotionController
+{
+public:
+ static const char* TYPENAME() { return "MultiController"; }
+
+ MultiController();
+ virtual ~MultiController();
+
+ virtual void AddController(MotionController* c);
+ virtual void MapKeys(KeyMapEntry* mapping, int nkeys);
+ virtual void SwapYawRoll(int swap);
+ virtual int GetSwapYawRoll() const;
+
+ // sample the physical device
+ virtual void Acquire();
+
+ // translations
+ virtual double X() { return x; }
+ virtual double Y() { return y; }
+ virtual double Z() { return z; }
+
+ // rotations
+ virtual double Pitch() { return p; }
+ virtual double Roll() { return r; }
+ virtual double Yaw() { return w; }
+ virtual int Center() { return c; }
+
+ // throttle
+ virtual double Throttle() { return t; }
+ virtual void SetThrottle(double throttle);
+
+ // actions
+ virtual int Action(int n) { return action[n]; }
+ virtual int ActionMap(int n);
+
+protected:
+ int nctrl;
+ MotionController* ctrl[4];
+
+ double x,y,z,p,r,w,t;
+ double p1, r1, w1;
+ int c;
+ int action[MotionController::MaxActions];
+};
+
+#endif // MultiController_h
+
diff --git a/nGenEx/PCX.CPP b/nGenEx/PCX.CPP
new file mode 100644
index 0000000..42b34b2
--- /dev/null
+++ b/nGenEx/PCX.CPP
@@ -0,0 +1,516 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: PCX.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ PCX image file loader
+*/
+
+
+#include "MemDebug.h"
+#include "PCX.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// +--------------------------------------------------------------------+
+
+#define MAX_SIZE (4*1024*1024)
+
+enum { BYTEMODE, RUNMODE };
+
+// +--------------------------------------------------------------------+
+
+PcxImage::PcxImage()
+ : width(0), height(0), bitmap(0), himap(0), imagebytes(0)
+{ }
+
+PcxImage::PcxImage(short w, short h, unsigned char* bits, unsigned char* colors)
+ : bitmap(0), himap(0)
+{
+ hdr.manufacturer = 10; // Always set to 10
+ hdr.version = 5; // Always 5 for 256-color files
+ hdr.encoding = 1; // Always set to 1
+ hdr.bits_per_pixel = 8; // Should be 8 for 256-color files
+ hdr.xmin = 0;
+ hdr.xmax = w-1;
+ hdr.ymin = 0;
+ hdr.ymax = h-1;
+ hdr.hres = 0x48;
+ hdr.vres = 0x48;
+
+ hdr.palette16[ 0] = (unsigned char) 0x00;
+ hdr.palette16[ 1] = (unsigned char) 0x00;
+ hdr.palette16[ 2] = (unsigned char) 0x00;
+ hdr.palette16[ 3] = (unsigned char) 0x80;
+ hdr.palette16[ 4] = (unsigned char) 0x00;
+ hdr.palette16[ 5] = (unsigned char) 0x00;
+ hdr.palette16[ 6] = (unsigned char) 0x00;
+ hdr.palette16[ 7] = (unsigned char) 0x80;
+ hdr.palette16[ 8] = (unsigned char) 0x00;
+ hdr.palette16[ 9] = (unsigned char) 0x80;
+ hdr.palette16[10] = (unsigned char) 0x80;
+ hdr.palette16[11] = (unsigned char) 0x00;
+ hdr.palette16[12] = (unsigned char) 0x00;
+ hdr.palette16[13] = (unsigned char) 0x00;
+ hdr.palette16[14] = (unsigned char) 0x80;
+ hdr.palette16[15] = (unsigned char) 0x80;
+
+ hdr.palette16[16] = (unsigned char) 0x00;
+ hdr.palette16[17] = (unsigned char) 0x80;
+ hdr.palette16[18] = (unsigned char) 0x00;
+ hdr.palette16[19] = (unsigned char) 0x80;
+ hdr.palette16[20] = (unsigned char) 0x80;
+ hdr.palette16[21] = (unsigned char) 0xC0;
+ hdr.palette16[22] = (unsigned char) 0xC0;
+ hdr.palette16[23] = (unsigned char) 0xC0;
+ hdr.palette16[24] = (unsigned char) 0xC0;
+ hdr.palette16[25] = (unsigned char) 0xDC;
+ hdr.palette16[26] = (unsigned char) 0xC0;
+ hdr.palette16[27] = (unsigned char) 0xA6;
+ hdr.palette16[28] = (unsigned char) 0xCA;
+ hdr.palette16[29] = (unsigned char) 0xF0;
+ hdr.palette16[30] = (unsigned char) 0x33;
+ hdr.palette16[31] = (unsigned char) 0x2B;
+
+ hdr.palette16[32] = (unsigned char) 0x1F;
+ hdr.palette16[33] = (unsigned char) 0x2B;
+ hdr.palette16[34] = (unsigned char) 0x23;
+ hdr.palette16[35] = (unsigned char) 0x1B;
+ hdr.palette16[36] = (unsigned char) 0x5F;
+ hdr.palette16[37] = (unsigned char) 0x5F;
+ hdr.palette16[38] = (unsigned char) 0x5F;
+ hdr.palette16[39] = (unsigned char) 0x2F;
+ hdr.palette16[40] = (unsigned char) 0x2F;
+ hdr.palette16[41] = (unsigned char) 0x2F;
+ hdr.palette16[42] = (unsigned char) 0x27;
+ hdr.palette16[43] = (unsigned char) 0x27;
+ hdr.palette16[44] = (unsigned char) 0x27;
+ hdr.palette16[45] = (unsigned char) 0x1F;
+ hdr.palette16[46] = (unsigned char) 0x1F;
+ hdr.palette16[47] = (unsigned char) 0x1F;
+
+ hdr.reserved = 0; // Reserved for future use
+ hdr.color_planes = 1; // Color planes
+ hdr.bytes_per_line = w; // Number of bytes in 1 line of pixels
+ hdr.palette_type = 1; // Should be 1 for color palette
+
+ for (unsigned int i = 0; i < 58; i++)
+ hdr.filler[i] = 0;
+
+ width = w;
+ height = h;
+ imagebytes = width * height;
+
+ bitmap = new(__FILE__,__LINE__) unsigned char [imagebytes];
+
+ if (bitmap) {
+ for (i = 0; i < imagebytes; i++)
+ bitmap[i] = bits[i];
+
+ unsigned char* p = pal;
+ for (i = 0; i < 256; i++) {
+ *p++ = *colors++;
+ *p++ = *colors++;
+ *p++ = *colors++;
+ colors++;
+ }
+ }
+}
+
+PcxImage::PcxImage(short w, short h, unsigned long* hibits)
+ : bitmap(0), himap(0)
+{
+ hdr.manufacturer = 10; // Always set to 10
+ hdr.version = 5; // Always 5 for true color files
+ hdr.encoding = 1; // Always set to 1
+ hdr.bits_per_pixel = 8; // Should be 8 for true color files
+ hdr.xmin = 0;
+ hdr.xmax = w-1;
+ hdr.ymin = 0;
+ hdr.ymax = h-1;
+ hdr.hres = 0x48;
+ hdr.vres = 0x48;
+
+ hdr.palette16[ 0] = (unsigned char) 0x00;
+ hdr.palette16[ 1] = (unsigned char) 0x00;
+ hdr.palette16[ 2] = (unsigned char) 0x00;
+ hdr.palette16[ 3] = (unsigned char) 0x80;
+ hdr.palette16[ 4] = (unsigned char) 0x00;
+ hdr.palette16[ 5] = (unsigned char) 0x00;
+ hdr.palette16[ 6] = (unsigned char) 0x00;
+ hdr.palette16[ 7] = (unsigned char) 0x80;
+ hdr.palette16[ 8] = (unsigned char) 0x00;
+ hdr.palette16[ 9] = (unsigned char) 0x80;
+ hdr.palette16[10] = (unsigned char) 0x80;
+ hdr.palette16[11] = (unsigned char) 0x00;
+ hdr.palette16[12] = (unsigned char) 0x00;
+ hdr.palette16[13] = (unsigned char) 0x00;
+ hdr.palette16[14] = (unsigned char) 0x80;
+ hdr.palette16[15] = (unsigned char) 0x80;
+
+ hdr.palette16[16] = (unsigned char) 0x00;
+ hdr.palette16[17] = (unsigned char) 0x80;
+ hdr.palette16[18] = (unsigned char) 0x00;
+ hdr.palette16[19] = (unsigned char) 0x80;
+ hdr.palette16[20] = (unsigned char) 0x80;
+ hdr.palette16[21] = (unsigned char) 0xC0;
+ hdr.palette16[22] = (unsigned char) 0xC0;
+ hdr.palette16[23] = (unsigned char) 0xC0;
+ hdr.palette16[24] = (unsigned char) 0xC0;
+ hdr.palette16[25] = (unsigned char) 0xDC;
+ hdr.palette16[26] = (unsigned char) 0xC0;
+ hdr.palette16[27] = (unsigned char) 0xA6;
+ hdr.palette16[28] = (unsigned char) 0xCA;
+ hdr.palette16[29] = (unsigned char) 0xF0;
+ hdr.palette16[30] = (unsigned char) 0x33;
+ hdr.palette16[31] = (unsigned char) 0x2B;
+
+ hdr.palette16[32] = (unsigned char) 0x1F;
+ hdr.palette16[33] = (unsigned char) 0x2B;
+ hdr.palette16[34] = (unsigned char) 0x23;
+ hdr.palette16[35] = (unsigned char) 0x1B;
+ hdr.palette16[36] = (unsigned char) 0x5F;
+ hdr.palette16[37] = (unsigned char) 0x5F;
+ hdr.palette16[38] = (unsigned char) 0x5F;
+ hdr.palette16[39] = (unsigned char) 0x2F;
+ hdr.palette16[40] = (unsigned char) 0x2F;
+ hdr.palette16[41] = (unsigned char) 0x2F;
+ hdr.palette16[42] = (unsigned char) 0x27;
+ hdr.palette16[43] = (unsigned char) 0x27;
+ hdr.palette16[44] = (unsigned char) 0x27;
+ hdr.palette16[45] = (unsigned char) 0x1F;
+ hdr.palette16[46] = (unsigned char) 0x1F;
+ hdr.palette16[47] = (unsigned char) 0x1F;
+
+ hdr.reserved = 0; // Reserved for future use
+ hdr.color_planes = 3; // Color planes
+ hdr.bytes_per_line = w; // Number of bytes in 1 line of pixels
+ hdr.palette_type = 1; // Should be 1 for color palette
+
+ for (unsigned int i = 0; i < 58; i++)
+ hdr.filler[i] = 0;
+
+ width = w;
+ height = h;
+ imagebytes = width * height;
+
+ himap = new(__FILE__,__LINE__) unsigned long [imagebytes];
+
+ if (himap) {
+ for (i = 0; i < imagebytes; i++)
+ himap[i] = hibits[i];
+ }
+}
+
+PcxImage::~PcxImage()
+{
+ delete [] bitmap;
+ delete [] himap;
+}
+
+// +--------------------------------------------------------------------+
+
+int PcxImage::Load(char *filename)
+{
+ unsigned long i;
+ short mode=BYTEMODE, bytecount;
+ unsigned char abyte, *p;
+ FILE *f;
+
+ f = fopen(filename,"rb");
+ if (f == NULL)
+ return PCX_NOFILE;
+
+ fread(&hdr, sizeof(PcxHeader), 1, f);
+
+ // read 256 color PCX file
+ if (hdr.color_planes == 1) {
+ width = 1 + hdr.xmax - hdr.xmin;
+ height = 1 + hdr.ymax - hdr.ymin;
+ imagebytes = width * height;
+
+ if (imagebytes > MAX_SIZE)
+ return PCX_TOOBIG;
+
+ // get palette from pcx file
+ fseek(f,-768L,SEEK_END);
+ fread(pal,768,1,f);
+
+ // now go back and read the pixel data:
+ fseek(f,sizeof(PcxHeader),SEEK_SET);
+
+ delete [] himap; himap = 0;
+ delete [] bitmap; bitmap = 0;
+
+ himap = new(__FILE__,__LINE__) unsigned long [imagebytes];
+ if (himap == NULL)
+ return PCX_NOMEM;
+
+ // force alpha channel to 255
+ memset(himap, 0xff, imagebytes*4);
+
+ unsigned long* pix = himap;
+ for (i=0; i<imagebytes; i++) {
+ if (mode == BYTEMODE) {
+ abyte = fgetc(f);
+
+ if (abyte > 0xbf) {
+ bytecount = abyte & 0x3f;
+ abyte = fgetc(f);
+ if (--bytecount > 0)
+ mode = RUNMODE;
+ }
+ }
+ else if (--bytecount == 0) {
+ mode = BYTEMODE;
+ }
+
+ *pix++ = 0xff000000 | (pal[3*abyte] << 16) | (pal[3*abyte+1] << 8) | (pal[3*abyte+2]);
+ }
+ }
+
+ // read 24-bit (true COLOR) PCX file
+ else {
+ width = 1 + hdr.xmax - hdr.xmin;
+ height = 1 + hdr.ymax - hdr.ymin;
+ imagebytes = width * height;
+
+ if (imagebytes > MAX_SIZE)
+ return PCX_TOOBIG;
+
+ delete [] himap; himap = 0;
+ delete [] bitmap; bitmap = 0;
+
+ himap = new(__FILE__,__LINE__) unsigned long [imagebytes];
+ if (himap == NULL)
+ return PCX_NOMEM;
+
+ // force alpha channel to 255
+ memset(himap, 0xff, imagebytes*4);
+
+ for (int row = 0; row < height; row++) {
+ // RED, GREEN, BLUE
+ for (int plane = 2; plane >= 0; plane--) {
+ p = ((unsigned char*) himap) + width*row*4 + plane;
+ for (int col = 0; col < width; col++) {
+ if (mode == BYTEMODE) {
+ abyte = fgetc(f);
+
+ if (abyte > 0xbf) {
+ bytecount = abyte & 0x3f;
+ abyte = fgetc(f);
+ if (--bytecount > 0)
+ mode = RUNMODE;
+ }
+ }
+ else if (--bytecount == 0) {
+ mode = BYTEMODE;
+ }
+
+ *p = abyte;
+ p += 4;
+ }
+ }
+ }
+ }
+
+ fclose(f);
+ return PCX_OK;
+}
+
+// +--------------------------------------------------------------------+
+
+int PcxImage::LoadBuffer(unsigned char* buf, int len)
+{
+ unsigned long i;
+ short mode=BYTEMODE, bytecount;
+ unsigned char abyte, *p;
+ unsigned char* fp;
+
+ if (buf == NULL)
+ return PCX_NOFILE;
+
+ fp = buf;
+ memcpy(&hdr, buf, sizeof(PcxHeader));
+ fp += sizeof(PcxHeader);
+
+ // read 256 color PCX file
+ if (hdr.color_planes == 1) {
+ width = 1 + hdr.xmax - hdr.xmin;
+ height = 1 + hdr.ymax - hdr.ymin;
+ imagebytes = width * height;
+
+ if (imagebytes > MAX_SIZE)
+ return PCX_TOOBIG;
+
+ // get palette from end of pcx file
+ memcpy(pal,buf+len-768,768);
+
+ delete [] himap; himap = 0;
+ delete [] bitmap; bitmap = 0;
+
+ himap = new(__FILE__,__LINE__) unsigned long [imagebytes];
+ if (himap == NULL)
+ return PCX_NOMEM;
+
+ memset(himap, 0, imagebytes*4);
+
+ unsigned long* pix = himap;
+ for (i=0; i<imagebytes; i++) {
+ if (mode == BYTEMODE) {
+ abyte = *fp++;
+
+ if (abyte > 0xbf) {
+ bytecount = abyte & 0x3f;
+ abyte = *fp++;
+ if (--bytecount > 0)
+ mode = RUNMODE;
+ }
+ }
+ else if (--bytecount == 0) {
+ mode = BYTEMODE;
+ }
+
+ *pix++ = 0xff000000 | (pal[3*abyte] << 16) | (pal[3*abyte+1] << 8) | (pal[3*abyte+2]);
+ }
+ }
+
+ // read 24-bit (true COLOR) PCX file
+ else {
+ width = 1 + hdr.xmax - hdr.xmin;
+ height = 1 + hdr.ymax - hdr.ymin;
+ imagebytes = width * height;
+
+ if (imagebytes > MAX_SIZE)
+ return PCX_TOOBIG;
+
+ delete [] himap; himap = 0;
+ delete [] bitmap; bitmap = 0;
+
+ himap = new(__FILE__,__LINE__) unsigned long [imagebytes];
+ if (himap == NULL)
+ return PCX_NOMEM;
+
+ memset(himap, 0, imagebytes*4);
+
+ for (int row = 0; row < height; row++) {
+ // RED, GREEN, BLUE
+ for (int plane = 2; plane >= 0; plane--) {
+ p = ((unsigned char*) himap) + width*row*4 + plane;
+ for (int col = 0; col < width; col++) {
+ if (mode == BYTEMODE) {
+ abyte = *fp++;
+
+ if (abyte > 0xbf) {
+ bytecount = abyte & 0x3f;
+ abyte = *fp++;
+ if (--bytecount > 0)
+ mode = RUNMODE;
+ }
+ }
+ else if (--bytecount == 0) {
+ mode = BYTEMODE;
+ }
+
+ *p = abyte;
+ p += 4;
+ }
+ }
+ }
+ }
+
+ return PCX_OK;
+}
+
+// +--------------------------------------------------------------------+
+
+int PcxImage::Save(char *filename)
+{
+ short mode=BYTEMODE;
+ FILE *f;
+
+ f = fopen(filename,"wb");
+ if (f == NULL)
+ return PCX_NOFILE;
+
+ fwrite(&hdr, sizeof(PcxHeader), 1, f);
+
+ if (hdr.color_planes == 1) {
+ unsigned char *p = bitmap;
+ unsigned long total = imagebytes;
+ unsigned long row = 0;
+ unsigned char palette_marker = 12;
+
+ while (total) {
+ unsigned char* start = p;
+ unsigned char count = 0;
+
+ while (*start == *p && count < 0x3f && row < width) {
+ p++;
+ count++;
+ row++;
+ }
+
+ if (count > 1 || *start > 0xbf) {
+ unsigned char b[2];
+ b[0] = 0xc0 | count;
+ b[1] = *start;
+ fwrite(b, 2, 1, f);
+ }
+ else {
+ fwrite(start, 1, 1, f);
+ }
+
+ total -= count;
+
+ if (row == width)
+ row = 0;
+ }
+
+ fwrite(&palette_marker,1,1,f);
+ fwrite(pal,768,1,f);
+ }
+
+ // write 24-bit (TRUE COLOR) PCX file
+ else {
+ for (int row = 0; row < height; row++) {
+ for (int plane = 2; plane >= 0; plane--) {
+ unsigned long col = 0;
+ unsigned char* p = ((unsigned char*) himap) + width*row*4 + plane;
+
+ while (col < width) {
+ unsigned char* start = p;
+ unsigned char count = 0;
+
+ while (*start == *p && count < 0x3f && col < width) {
+ p += 4;
+ count++;
+ col++;
+ }
+
+ if (count > 1 || *start > 0xbf) {
+ unsigned char b[2];
+ b[0] = 0xc0 | count;
+ b[1] = *start;
+ fwrite(b, 2, 1, f);
+ }
+ else {
+ fwrite(start, 1, 1, f);
+ }
+ }
+ }
+ }
+ }
+
+ fclose(f);
+ return PCX_OK; // return success
+}
+
diff --git a/nGenEx/ParseUtil.cpp b/nGenEx/ParseUtil.cpp
new file mode 100644
index 0000000..77a0427
--- /dev/null
+++ b/nGenEx/ParseUtil.cpp
@@ -0,0 +1,481 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ParseUtil.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Parser utility functions
+*/
+
+#include "MemDebug.h"
+#include "ParseUtil.h"
+#include "DataLoader.h"
+
+#include "stdio.h"
+
+// +--------------------------------------------------------------------+
+
+bool GetDefBool(bool& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing BOOL TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermBool* tn = def->term()->isBool();
+ if (tn) {
+ dst = tn->value();
+ return true;
+ }
+ else {
+ Print("WARNING: invalid bool %s in '%s'. value = ", def->name()->value().data(), file);
+ def->term()->print(10);
+ Print("\n");
+ }
+
+ return false;
+}
+
+bool GetDefText(Text& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing TEXT TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermText* tn = def->term()->isText();
+ if (tn) {
+ dst = tn->value();
+ return true;
+ }
+ else
+ Print("WARNING: invalid TEXT %s in '%s'\n", def->name()->value().data(), file);
+
+ return false;
+}
+
+bool GetDefText(char* dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing TEXT TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermText* tn = def->term()->isText();
+ if (tn) {
+ strcpy(dst, tn->value());
+ return true;
+ }
+ else
+ Print("WARNING: invalid TEXT %s in '%s'\n", def->name()->value().data(), file);
+
+ return false;
+}
+
+bool GetDefNumber(int& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing NUMBER TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermNumber* tr = def->term()->isNumber();
+ if (tr) {
+ dst = (int) tr->value();
+ return true;
+ }
+ else
+ Print("WARNING: invalid NUMBER %s in '%s'\n", def->name()->value().data(), file);
+
+ return false;
+}
+
+bool GetDefNumber(DWORD& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing NUMBER TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermNumber* tr = def->term()->isNumber();
+ if (tr) {
+ dst = (DWORD) tr->value();
+ return true;
+ }
+ else
+ Print("WARNING: invalid NUMBER %s in '%s'\n", def->name()->value().data(), file);
+
+ return false;
+}
+
+bool GetDefNumber(float& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing NUMBER TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermNumber* tr = def->term()->isNumber();
+ if (tr) {
+ dst = (float) tr->value();
+ return true;
+ }
+ else
+ Print("WARNING: invalid NUMBER %s in '%s'\n", def->name()->value().data(), file);
+
+ return false;
+}
+
+bool GetDefNumber(double& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing NUMBER TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermNumber* tr = def->term()->isNumber();
+ if (tr) {
+ dst = (double) tr->value();
+ return true;
+ }
+ else
+ Print("WARNING: invalid NUMBER %s in '%s'\n", def->name()->value().data(), file);
+
+ return false;
+}
+
+bool GetDefVec(Vec3& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing VEC3 TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ if (val->elements()->size() != 3) {
+ Print("WARNING: malformed vector in '%s'\n", file);
+ }
+ else {
+ dst.x = (float) (val->elements()->at(0)->isNumber()->value());
+ dst.y = (float) (val->elements()->at(1)->isNumber()->value());
+ dst.z = (float) (val->elements()->at(2)->isNumber()->value());
+
+ return true;
+ }
+ }
+ else {
+ Print("WARNING: vector expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+bool GetDefRect(Rect& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing RECT TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ if (val->elements()->size() != 4) {
+ Print("WARNING: malformed rect in '%s'\n", file);
+ }
+ else {
+ dst.x = (int) (val->elements()->at(0)->isNumber()->value());
+ dst.y = (int) (val->elements()->at(1)->isNumber()->value());
+ dst.w = (int) (val->elements()->at(2)->isNumber()->value());
+ dst.h = (int) (val->elements()->at(3)->isNumber()->value());
+
+ return true;
+ }
+ }
+ else {
+ Print("WARNING: rect expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+bool GetDefInsets(Insets& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing Insets TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ if (val->elements()->size() != 4) {
+ Print("WARNING: malformed Insets in '%s'\n", file);
+ }
+ else {
+ dst.left = (WORD) (val->elements()->at(0)->isNumber()->value());
+ dst.right = (WORD) (val->elements()->at(1)->isNumber()->value());
+ dst.top = (WORD) (val->elements()->at(2)->isNumber()->value());
+ dst.bottom = (WORD) (val->elements()->at(3)->isNumber()->value());
+
+ return true;
+ }
+ }
+ else {
+ Print("WARNING: Insets expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+bool GetDefColor(Color& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing COLOR TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ if (val->elements()->size() != 3) {
+ Print("WARNING: malformed color in '%s'\n", file);
+ }
+ else {
+ BYTE r, g, b;
+ double v0 = (val->elements()->at(0)->isNumber()->value());
+ double v1 = (val->elements()->at(1)->isNumber()->value());
+ double v2 = (val->elements()->at(2)->isNumber()->value());
+
+ if (v0 >= 0 && v0 <= 1 &&
+ v1 >= 0 && v1 <= 1 &&
+ v2 >= 0 && v2 <= 1) {
+
+ r = (BYTE) (v0 * 255);
+ g = (BYTE) (v1 * 255);
+ b = (BYTE) (v2 * 255);
+
+ }
+ else {
+ r = (BYTE) v0;
+ g = (BYTE) v1;
+ b = (BYTE) v2;
+ }
+
+ dst = Color(r,g,b);
+ return true;
+ }
+ }
+ else {
+ Print("WARNING: color expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+bool GetDefColor(ColorValue& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing COLOR TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ if (val->elements()->size() < 3 || val->elements()->size() > 4) {
+ Print("WARNING: malformed color in '%s'\n", file);
+ }
+ else {
+ double r = (val->elements()->at(0)->isNumber()->value());
+ double g = (val->elements()->at(1)->isNumber()->value());
+ double b = (val->elements()->at(2)->isNumber()->value());
+ double a = 1;
+
+ if (val->elements()->size() == 4)
+ a = (val->elements()->at(3)->isNumber()->value());
+
+ dst.Set((float) r, (float) g, (float) b, (float) a);
+ return true;
+ }
+ }
+ else {
+ Print("WARNING: color expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool GetDefArray(int* dst, int size, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing ARRAY TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ int nelem = val->elements()->size();
+
+ if (nelem > size)
+ nelem = size;
+
+ for (int i = 0; i < nelem; i++)
+ *dst++ = (int) (val->elements()->at(i)->isNumber()->value());
+
+ return true;
+ }
+ else {
+ Print("WARNING: array expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+bool GetDefArray(float* dst, int size, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing ARRAY TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ int nelem = val->elements()->size();
+
+ if (nelem > size)
+ nelem = size;
+
+ for (int i = 0; i < nelem; i++)
+ *dst++ = (float) (val->elements()->at(i)->isNumber()->value());
+
+ return true;
+ }
+ else {
+ Print("WARNING: array expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+bool GetDefArray(double* dst, int size, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing ARRAY TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ int nelem = val->elements()->size();
+
+ if (nelem > size)
+ nelem = size;
+
+ for (int i = 0; i < nelem; i++)
+ *dst++ = (double) (val->elements()->at(i)->isNumber()->value());
+
+ return true;
+ }
+ else {
+ Print("WARNING: array expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool GetDefArray(ArrayList& array, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing ARRAY TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ int nelem = val->elements()->size();
+
+ array.clear();
+
+ for (int i = 0; i < nelem; i++)
+ array.append((DWORD) (val->elements()->at(i)->isNumber()->value()));
+
+ return true;
+ }
+ else {
+ Print("WARNING: integer array expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+bool GetDefArray(FloatList& array, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing ARRAY TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ int nelem = val->elements()->size();
+
+ array.clear();
+
+ for (int i = 0; i < nelem; i++)
+ array.append((float) (val->elements()->at(i)->isNumber()->value()));
+
+ return true;
+ }
+ else {
+ Print("WARNING: float array expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool GetDefTime(int& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing TIME TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermText* tn = def->term()->isText();
+
+ if (tn) {
+ int d = 0;
+ int h = 0;
+ int m = 0;
+ int s = 0;
+
+ char buf[64];
+ strcpy(buf, tn->value());
+
+ if (strchr(buf, '/'))
+ sscanf(buf, "%d/%d:%d:%d", &d, &h, &m, &s);
+ else
+ sscanf(buf, "%d:%d:%d", &h, &m, &s);
+
+ dst = d * 24 * 60 * 60 +
+ h * 60 * 60 +
+ m * 60 +
+ s;
+
+ return true;
+ }
+ else
+ Print("WARNING: invalid TIME %s in '%s'\n", def->name()->value().data(), file);
+
+ return false;
+}
+
+
diff --git a/nGenEx/ParseUtil.h b/nGenEx/ParseUtil.h
new file mode 100644
index 0000000..19352f5
--- /dev/null
+++ b/nGenEx/ParseUtil.h
@@ -0,0 +1,52 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ParseUtil.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Parser utility functions
+*/
+
+#ifndef ParseUtil_h
+#define ParseUtil_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Color.h"
+#include "ArrayList.h"
+
+#include "Text.h"
+#include "Parser.h"
+#include "Reader.h"
+#include "Token.h"
+
+// +--------------------------------------------------------------------+
+
+bool GetDefBool(bool& dst, TermDef* def, const char* file);
+bool GetDefText(Text& dst, TermDef* def, const char* file);
+bool GetDefText(char* dst, TermDef* def, const char* file);
+bool GetDefNumber(int& dst, TermDef* def, const char* file);
+bool GetDefNumber(DWORD& dst, TermDef* def, const char* file);
+bool GetDefNumber(float& dst, TermDef* def, const char* file);
+bool GetDefNumber(double& dst, TermDef* def, const char* file);
+bool GetDefVec(Vec3& dst, TermDef* def, const char* file);
+bool GetDefColor(Color& dst, TermDef* def, const char* file);
+bool GetDefColor(ColorValue& dst, TermDef* def, const char* file);
+bool GetDefRect(Rect& dst, TermDef* def, const char* file);
+bool GetDefInsets(Insets& dst, TermDef* def, const char* file);
+bool GetDefTime(int& dst, TermDef* def, const char* file);
+
+bool GetDefArray(int* dst, int size, TermDef* def, const char* file);
+bool GetDefArray(float* dst, int size, TermDef* def, const char* file);
+bool GetDefArray(double* dst, int size, TermDef* def, const char* file);
+bool GetDefArray(ArrayList& array, TermDef* def, const char* file);
+bool GetDefArray(FloatList& array, TermDef* def, const char* file);
+
+// +--------------------------------------------------------------------+
+
+#endif ParseUtil_h
diff --git a/nGenEx/Particles.cpp b/nGenEx/Particles.cpp
new file mode 100644
index 0000000..d774bb0
--- /dev/null
+++ b/nGenEx/Particles.cpp
@@ -0,0 +1,264 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Particles.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Particle Burst class
+*/
+
+#include "MemDebug.h"
+#include "Particles.h"
+#include "Projector.h"
+#include "Light.h"
+#include "Bitmap.h"
+#include "Game.h"
+#include "Random.h"
+
+// +--------------------------------------------------------------------+
+
+inline float randf() { return (rand()-16384.0f)/32768.0f; }
+
+// +--------------------------------------------------------------------+
+
+Particles::Particles(Bitmap* bitmap, int np, const Vec3& base_loc, const Vec3& vel,
+ float bspeed, float dr, float s, float bloom, float dec, float rate,
+ bool cont, bool trail, bool rise, int a, int nframes)
+ : nparts(np), base_speed(bspeed), max_speed(bspeed*3.0f),
+ drag(dr), min_scale(s), max_scale(bloom), decay(dec),
+ release_rate(rate), continuous(cont), trailing(trail), rising(rise),
+ blend(a), extra(0.0f), point_sprite(0), emitting(true)
+{
+ MoveTo(base_loc);
+ ref_loc = base_loc;
+
+ trans = true;
+ luminous = true;
+ shadow = false;
+ nverts = nparts;
+
+ if (max_scale < min_scale)
+ max_scale = min_scale;
+
+ velocity = new(__FILE__,__LINE__) Point[nverts];
+ part_loc = new(__FILE__,__LINE__) Point[nverts];
+ release = new(__FILE__,__LINE__) Point[nverts];
+ intensity = new(__FILE__,__LINE__) float[nverts];
+ timestamp = new(__FILE__,__LINE__) float[nverts];
+ scale = new(__FILE__,__LINE__) float[nverts];
+ angle = new(__FILE__,__LINE__) float[nverts];
+ frame = new(__FILE__,__LINE__) BYTE[nverts];
+
+ float speed = base_speed;
+
+ for (int i = 0; i < nverts; i++) {
+ intensity[i] = 1.0f;
+ timestamp[i] = (float) (Game::GameTime() / 1000.0);
+ scale[i] = (float) (min_scale);
+ angle[i] = (float) (Random(0, 2*PI));
+ frame[i] = 0;
+
+ part_loc[i] = Point();
+ release[i] = ref_loc;
+ velocity[i] = RandomVector(speed);
+ velocity[i] += vel;
+
+ if (speed < max_speed)
+ speed += (float) Random(max_speed/15.0, max_speed/5.0);
+ else
+ speed = base_speed;
+ }
+
+ radius = 15000.0f;
+
+ if (decay > 2)
+ decay /= 256.0f;
+
+ if (nparts < 8) {
+ nverts = 1;
+ }
+
+ else if (nparts > 50 || continuous) {
+ nverts = (int) (nparts * 0.125 * release_rate);
+ }
+
+ point_sprite = new(__FILE__,__LINE__) Sprite(bitmap, nframes);
+ point_sprite->Scale(s);
+ point_sprite->SetBlendMode(blend);
+ point_sprite->SetFrameRate(nframes * decay);
+}
+
+Particles::~Particles()
+{
+ delete point_sprite;
+ delete [] velocity;
+ delete [] part_loc;
+ delete [] release;
+ delete [] timestamp;
+ delete [] intensity;
+ delete [] scale;
+ delete [] angle;
+ delete [] frame;
+}
+
+// +--------------------------------------------------------------------+
+
+void Particles::ExecFrame(double seconds)
+{
+ point_sprite->Update();
+
+ ref_loc = loc;
+ radius += max_speed * (float) seconds;
+
+ float scaled_drag = (float) exp(-drag * seconds);
+ float scale_inc = (float) ((max_scale-min_scale)*seconds*2);
+
+ for (int i = 0; i < nverts; i++) {
+ part_loc[i] += velocity[i] * (float) seconds;
+
+ if (rising) {
+ part_loc[i].y += (float) ((randf() + 1) * scale[i] * 80 * seconds);
+ }
+
+ // do the (chunky) blooming effect:
+ if (max_scale > 0 && scale[i] < max_scale) {
+ scale[i] += scale_inc * (float) ((i%3)/3.0);
+ }
+
+ double rho = angle[i];
+ int rot = i%4;
+ switch (rot) {
+ case 0: rho += seconds * 0.13; break;
+ case 1: rho -= seconds * 0.11; break;
+ case 2: rho += seconds * 0.09; break;
+ case 3: rho -= seconds * 0.07; break;
+ default: break;
+ }
+
+ angle[i] = (float) rho;
+ intensity[i] -= (float) (decay * seconds);
+
+ if (point_sprite->NumFrames() > 1) {
+ double age = Game::GameTime()/1000.0 - timestamp[i];
+ int n = (int) (age * point_sprite->FrameRate());
+
+ if (n >= point_sprite->NumFrames())
+ n = point_sprite->NumFrames() - 1;
+
+ frame[i] = n;
+ }
+
+ velocity[i] *= scaled_drag;
+ }
+
+ if (nverts < nparts && emitting) {
+ int nv = nverts;
+ double delta = nparts * release_rate * seconds;
+ int new_parts = (int) (delta + extra);
+ extra = (float) (delta + extra - new_parts);
+ nverts += new_parts;
+
+ if (nverts > nparts)
+ nverts = nparts;
+
+ for (int i = nv; i < nverts; i++) {
+ intensity[i] = 1;
+ timestamp[i] = (float) (Game::GameTime() / 1000.0);
+ scale[i] = (float) (min_scale);
+ angle[i] = (float) (Random(0, 2*PI));
+ frame[i] = 0;
+ part_loc[i] = Point();
+ release[i] = ref_loc;
+ }
+ }
+
+ if (nverts > nparts)
+ nverts = nparts;
+
+ // recycle dead particles:
+ if (continuous) {
+ float speed = base_speed;
+
+ for (int i = 0; i < nverts; i++) {
+ if (intensity[i] <= 0) {
+ part_loc[i] = Point();
+ release[i] = ref_loc;
+
+ intensity[i] = 1;
+ timestamp[i] = (float) (Game::GameTime() / 1000.0);
+ scale[i] = (float) (min_scale);
+ angle[i] = (float) (PI * rand() / 16384.0);
+ frame[i] = 0;
+ velocity[i] = RandomVector(speed);
+
+ if (speed < max_speed)
+ speed += (float) Random(max_speed/25.0, max_speed/18.0);
+ else
+ speed = base_speed;
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Particles::CheckVisibility(Projector& projector)
+{
+ float base = 256;
+
+ if (point_sprite && point_sprite->Frame())
+ base = (float) point_sprite->Frame()->Width();
+
+ float particle_radius = base * max_scale;
+
+ if (projector.IsVisible( Location(), Radius()) &&
+ projector.ApparentRadius(Location(), particle_radius) > 1) {
+
+ visible = true;
+ }
+ else {
+ visible = false;
+ }
+
+ return visible;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Particles::Render(Video* video, DWORD flags)
+{
+ if (hidden || !visible || !video || !point_sprite)
+ return;
+
+ if (blend == 2 && !(flags & Graphic::RENDER_ALPHA))
+ return;
+
+ if (blend == 4 && !(flags & Graphic::RENDER_ADDITIVE))
+ return;
+
+ for (int i = 0; i < nverts; i++) {
+ Point vloc;
+
+ if (trailing) {
+ vloc = part_loc[i] + release[i] - offset;
+ }
+ else {
+ vloc = part_loc[i] + loc;
+ }
+
+ point_sprite->MoveTo(vloc);
+ point_sprite->SetShade(intensity[i]);
+ point_sprite->Rescale(scale[i]);
+ point_sprite->SetAngle(angle[i]);
+ point_sprite->SetFrameIndex(frame[i]);
+
+ point_sprite->Render(video, flags);
+ }
+}
diff --git a/nGenEx/Particles.h b/nGenEx/Particles.h
new file mode 100644
index 0000000..ad473ee
--- /dev/null
+++ b/nGenEx/Particles.h
@@ -0,0 +1,86 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Particles.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Particle burst class
+*/
+
+#ifndef Particles_h
+#define Particles_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Graphic.h"
+#include "Sprite.h"
+
+// +--------------------------------------------------------------------+
+
+class Particles : public Graphic
+{
+public:
+ Particles(Bitmap* bitmap,
+ int np,
+ const Vec3& base_loc,
+ const Vec3& vel,
+ float base_speed = 500.0f,
+ float drag = 1.0f,
+ float scale = 1.0f,
+ float bloom = 0.0f,
+ float decay = 100.0f,
+ float release = 1.0f,
+ bool cont = false,
+ bool trail = true,
+ bool rise = false,
+ int blend = 3,
+ int nframes = 1);
+
+ virtual ~Particles();
+
+ virtual void Render(Video* video, DWORD flags);
+ virtual void ExecFrame(double seconds);
+ virtual void TranslateBy(const Point& ref) { offset = ref; loc = loc - ref; }
+ virtual bool CheckVisibility(Projector& projector);
+
+ virtual bool IsEmitting() const { return emitting; }
+ virtual void StopEmitting() { emitting = false; }
+
+protected:
+ int nparts;
+ int nverts;
+ int blend;
+ bool continuous;
+ bool trailing;
+ bool rising;
+ bool emitting;
+
+ float base_speed;
+ float max_speed;
+ float drag;
+ float release_rate;
+ float decay;
+ float min_scale;
+ float max_scale;
+ float extra;
+
+ Point ref_loc;
+ Point offset;
+ Point* velocity;
+ Point* part_loc;
+ Point* release;
+ float* timestamp;
+ float* intensity;
+ float* scale;
+ float* angle;
+ BYTE* frame;
+
+ Sprite* point_sprite;
+};
+
+#endif Particles_h
diff --git a/nGenEx/Pcx.h b/nGenEx/Pcx.h
new file mode 100644
index 0000000..4a209c5
--- /dev/null
+++ b/nGenEx/Pcx.h
@@ -0,0 +1,68 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: PCX.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ PCX image file loader
+*/
+
+#ifndef PCX_H
+#define PCX_H
+
+// +--------------------------------------------------------------------+
+
+enum { PCX_OK, PCX_NOMEM, PCX_TOOBIG, PCX_NOFILE };
+
+struct PcxHeader
+{
+ char manufacturer; // Always set to 10
+ char version; // Always 5 for 256-color files
+ char encoding; // Always set to 1
+ char bits_per_pixel; // Should be 8 for 256-color files
+ short xmin,ymin; // Coordinates for top left corner
+ short xmax,ymax; // Width and height of image
+ short hres; // Horizontal resolution of image
+ short vres; // Vertical resolution of image
+ char palette16[48]; // EGA palette; not used for 256-color files
+ char reserved; // Reserved for future use
+ char color_planes; // Color planes
+ short bytes_per_line; // Number of bytes in 1 line of pixels
+ short palette_type; // Should be 2 for color palette
+ char filler[58]; // Nothing but junk
+};
+
+// +--------------------------------------------------------------------+
+
+struct PcxImage
+{
+ static const char* TYPENAME() { return "PcxImage"; }
+
+ PcxImage(short w, short h, unsigned long* hibits);
+ PcxImage(short w, short h, unsigned char* bits, unsigned char* colors);
+
+ PcxImage();
+ ~PcxImage();
+
+ int Load(char *filename);
+ int Save(char *filename);
+
+ int LoadBuffer(unsigned char* buf, int len);
+
+ PcxHeader hdr;
+ unsigned char* bitmap;
+ unsigned long* himap;
+ unsigned char pal[768];
+ unsigned long imagebytes;
+ unsigned short width, height;
+};
+
+// +--------------------------------------------------------------------+
+
+
+#endif
diff --git a/nGenEx/Physical.cpp b/nGenEx/Physical.cpp
new file mode 100644
index 0000000..b6c132c
--- /dev/null
+++ b/nGenEx/Physical.cpp
@@ -0,0 +1,775 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Physical.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Physical Object
+*/
+
+#include "MemDebug.h"
+#include "Physical.h"
+#include "Graphic.h"
+#include "Light.h"
+#include "Director.h"
+
+// +--------------------------------------------------------------------+
+
+int Physical::id_key = 1;
+double Physical::sub_frame = 1.0 / 60.0;
+
+static const double GRAV = 6.673e-11;
+
+// +--------------------------------------------------------------------+
+
+Physical::Physical()
+ : id(id_key++), obj_type(0), rep(0), light(0),
+ thrust(0.0f), drag(0.0f), lat_thrust(false),
+ trans_x(0.0f), trans_y(0.0f), trans_z(0.0f), straight(false),
+ roll(0.0f), pitch(0.0f), yaw(0.0f), dr(0.0f), dp(0.0f), dy(0.0f),
+ dr_acc(0.0f), dp_acc(0.0f), dy_acc(0.0f),
+ dr_drg(0.0f), dp_drg(0.0f), dy_drg(0.0f),
+ flight_path_yaw(0.0f), flight_path_pitch(0.0f), primary_mass(0),
+ roll_rate(1.0f), pitch_rate(1.0f), yaw_rate(1.0f), shake(0.0f),
+ radius(0.0f), mass(1.0f), integrity(1.0f), life(-1), dir(0),
+ g_accel(0.0f), Do(0.0f), CL(0.0f), CD(0.0f), alpha(0.0f), stall(0.0f)
+{
+ strcpy(name, "unknown object");
+}
+
+// +--------------------------------------------------------------------+
+
+Physical::Physical(const char* n, int t)
+ : id(id_key++), obj_type(t), rep(0), light(0),
+ thrust(0.0f), drag(0.0f), lat_thrust(false),
+ trans_x(0.0f), trans_y(0.0f), trans_z(0.0f), straight(false),
+ roll(0.0f), pitch(0.0f), yaw(0.0f), dr(0.0f), dp(0.0f), dy(0.0f),
+ dr_acc(0.0f), dp_acc(0.0f), dy_acc(0.0f),
+ dr_drg(0.0f), dp_drg(0.0f), dy_drg(0.0f),
+ flight_path_yaw(0.0f), flight_path_pitch(0.0f), primary_mass(0),
+ roll_rate(1.0f), pitch_rate(1.0f), yaw_rate(1.0f), shake(0.0f),
+ radius(0.0f), mass(1.0f), integrity(1.0f), life(-1), dir(0),
+ g_accel(0.0f), Do(0.0f), CL(0.0f), CD(0.0f), alpha(0.0f), stall(0.0f)
+{
+ strncpy(name, n, NAMELEN-1);
+ name[NAMELEN-1] = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+Physical::~Physical()
+{
+ // inform graphic rep and light that we are leaving:
+ GRAPHIC_DESTROY(rep);
+ LIGHT_DESTROY(light);
+
+ // we own the director
+ delete dir;
+ dir = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+inline double random() { return rand()-16384; }
+
+void
+Physical::ExecFrame(double s)
+{
+ Point orig_velocity = Velocity();
+ arcade_velocity = Point();
+
+ // if this object is under direction,
+ // but doesn't need subframe accuracy,
+ // update the control parameters:
+ if (dir && !dir->Subframe())
+ dir->ExecFrame(s);
+
+ // decrement life before destroying the frame time:
+ if (life > 0)
+ life -= s;
+
+ // integrate equations
+ // using slices no larger
+ // than sub_frame:
+
+ double seconds = s;
+
+ while (s > 0.0) {
+ if (s > sub_frame)
+ seconds = sub_frame;
+ else
+ seconds = s;
+
+ // if the director needs subframe accuracy, run it now:
+ if (dir && dir->Subframe())
+ dir->ExecFrame(seconds);
+
+ if (!straight)
+ AngularFrame(seconds);
+
+ // LINEAR MOVEMENT ----------------------------
+ Point pos = cam.Pos();
+
+ // if the object is thrusting,
+ // accelerate along the camera normal:
+ if (thrust) {
+ Point thrustvec = cam.vpn();
+ thrustvec *= ((thrust/mass) * seconds);
+ velocity += thrustvec;
+ }
+
+ LinearFrame(seconds);
+
+ // move the position by the (time-frame scaled) velocity:
+ pos += velocity * seconds;
+ cam.MoveTo(pos);
+
+ s -= seconds;
+ }
+
+ alpha = 0.0f;
+
+ // now update the graphic rep and light sources:
+ if (rep) {
+ rep->MoveTo(cam.Pos());
+ rep->SetOrientation(cam.Orientation());
+ }
+
+ if (light) {
+ light->MoveTo(cam.Pos());
+ }
+
+ if (!straight)
+ CalcFlightPath();
+
+ accel = (Velocity() - orig_velocity) * (1/seconds);
+ if (!_finite(accel.x) || !_finite(accel.y) || !_finite(accel.z))
+ accel = Point();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::AeroFrame(double s)
+{
+ arcade_velocity = Point();
+
+ // if this object is under direction,
+ // but doesn't need subframe accuracy,
+ // update the control parameters:
+ if (dir && !dir->Subframe())
+ dir->ExecFrame(s);
+
+ // decrement life before destroying the frame time:
+ if (life > 0)
+ life -= s;
+
+ // integrate equations
+ // using slices no larger
+ // than sub_frame:
+
+ double seconds = s;
+
+ while (s > 0.0) {
+ if (s > sub_frame)
+ seconds = sub_frame;
+ else
+ seconds = s;
+
+ // if the director needs subframe accuracy, run it now:
+ if (dir && dir->Subframe())
+ dir->ExecFrame(seconds);
+
+ AngularFrame(seconds);
+
+ // LINEAR MOVEMENT ----------------------------
+ Point pos = cam.Pos();
+
+ // if the object is thrusting,
+ // accelerate along the camera normal:
+ if (thrust) {
+ Point thrustvec = cam.vpn();
+ thrustvec *= ((thrust/mass) * seconds);
+ velocity += thrustvec;
+ }
+
+ // AERODYNAMICS ------------------------------
+
+ if (lat_thrust)
+ LinearFrame(seconds);
+
+ // if no thrusters, do constant gravity:
+ else if (g_accel > 0)
+ velocity += Point(0, -g_accel, 0) * seconds;
+
+ // compute alpha, rho, drag, and lift:
+
+ Point vfp = velocity;
+ double v = vfp.Normalize();
+ double v_2 = 0;
+ double rho = GetDensity();
+ double lift = 0;
+
+ if (v > 150) {
+ v_2 = (v-150) * (v-150);
+
+ Point vfp1 = vfp - cam.vrt() * (vfp * cam.vrt());
+ vfp1.Normalize();
+
+ double cos_alpha = vfp1 * cam.vpn();
+
+ if (cos_alpha >= 1) {
+ alpha = 0.0f;
+ }
+ else {
+ alpha = (float) acos(cos_alpha);
+ }
+
+ // if flight path is above nose, alpha is negative:
+ if (vfp1 * cam.vup() > 0)
+ alpha = -alpha;
+
+ if (alpha <= stall) {
+ lift = CL * alpha * rho * v_2;
+ }
+ else {
+ lift = CL * (2*stall - alpha) * rho * v_2;
+ }
+
+ // add lift to velocity:
+ if (_finite(lift))
+ velocity += cam.vup() * lift * seconds;
+ else
+ lift = 0;
+
+ // if drag applies, decellerate:
+ double alpha_2 = alpha*alpha;
+ double drag_eff = (drag + (CD * alpha_2)) * rho * v_2;
+
+ Point vn = velocity;
+ vn.Normalize();
+
+ velocity += vn * -drag_eff * seconds;
+ }
+ else {
+ velocity *= exp(-drag * seconds);
+ }
+
+ // move the position by the (time-frame scaled) velocity:
+ pos += velocity * seconds;
+ cam.MoveTo(pos);
+
+ s -= seconds;
+ }
+
+ // now update the graphic rep and light sources:
+ if (rep) {
+ rep->MoveTo(cam.Pos());
+ rep->SetOrientation(cam.Orientation());
+ }
+
+ if (light) {
+ light->MoveTo(cam.Pos());
+ }
+}
+
+double
+Physical::GetDensity() const
+{
+ double alt = cam.Pos().y;
+ double rho = 0.75 * Do * (250e3-alt)/250e3;
+
+ return rho;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::ArcadeFrame(double s)
+{
+ // if this object is under direction,
+ // but doesn't need subframe accuracy,
+ // update the control parameters:
+ if (dir && !dir->Subframe())
+ dir->ExecFrame(s);
+
+ // decrement life before destroying the frame time:
+ if (life > 0)
+ life -= s;
+
+ // integrate equations
+ // using slices no larger
+ // than sub_frame:
+
+ double seconds = s;
+
+ while (s > 0.0) {
+ if (s > sub_frame)
+ seconds = sub_frame;
+ else
+ seconds = s;
+
+ // if the director needs subframe accuracy, run it now:
+ if (dir && dir->Subframe())
+ dir->ExecFrame(seconds);
+
+ if (!straight)
+ AngularFrame(seconds);
+
+ Point pos = cam.Pos();
+
+ // ARCADE FLIGHT MODEL:
+ // arcade_velocity vector is always in line with heading
+
+ double speed = arcade_velocity.Normalize();
+ double bleed = arcade_velocity * cam.vpn();
+
+ speed *= pow(bleed, 30);
+ arcade_velocity = cam.vpn() * speed;
+
+ if (thrust) {
+ Point thrustvec = cam.vpn();
+ thrustvec *= ((thrust/mass) * seconds);
+ arcade_velocity += thrustvec;
+ }
+
+ if (drag)
+ arcade_velocity *= exp(-drag * seconds);
+
+ LinearFrame(seconds);
+
+ // move the position by the (time-frame scaled) velocity:
+ pos += arcade_velocity * seconds +
+ velocity * seconds;
+
+ cam.MoveTo(pos);
+
+ s -= seconds;
+ }
+
+ alpha = 0.0f;
+
+ // now update the graphic rep and light sources:
+ if (rep) {
+ rep->MoveTo(cam.Pos());
+ rep->SetOrientation(cam.Orientation());
+ }
+
+ if (light) {
+ light->MoveTo(cam.Pos());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::AngularFrame(double seconds)
+{
+ if (!straight) {
+ dr += (float) (dr_acc * seconds);
+ dy += (float) (dy_acc * seconds);
+ dp += (float) (dp_acc * seconds);
+
+ dr *= (float) exp(-dr_drg * seconds);
+ dy *= (float) exp(-dy_drg * seconds);
+ dp *= (float) exp(-dp_drg * seconds);
+
+ roll = (float) (dr * seconds);
+ pitch = (float) (dp * seconds);
+ yaw = (float) (dy * seconds);
+
+ if (shake > 0.01) {
+ vibration = Point(random(), random(), random());
+ vibration.Normalize();
+ vibration *= (float) (shake * seconds);
+
+ shake *= (float) exp(-1.5 * seconds);
+ }
+ else {
+ vibration.x = vibration.y = vibration.z = 0.0f;
+ shake = 0.0f;
+ }
+
+ cam.Aim(roll, pitch, yaw);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::LinearFrame(double seconds)
+{
+ // deal with lateral thrusters:
+
+ if (trans_x) { // side-to-side
+ Point transvec = cam.vrt();
+ transvec *= ((trans_x/mass) * seconds);
+
+ velocity += transvec;
+ }
+
+ if (trans_y) { // fore-and-aft
+ Point transvec = cam.vpn();
+ transvec *= ((trans_y/mass) * seconds);
+
+ velocity += transvec;
+ }
+
+ if (trans_z) { // up-and-down
+ Point transvec = cam.vup();
+ transvec *= ((trans_z/mass) * seconds);
+
+ velocity += transvec;
+ }
+
+ // if gravity applies, attract:
+ if (primary_mass > 0) {
+ Point g = primary_loc - cam.Pos();
+ double r = g.Normalize();
+
+ g *= GRAV * primary_mass / (r*r);
+
+ velocity += g * seconds;
+ }
+
+ // constant gravity:
+ else if (g_accel > 0)
+ velocity += Point(0, -g_accel, 0) * seconds;
+
+ // if drag applies, decellerate:
+ if (drag)
+ velocity *= exp(-drag * seconds);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::CalcFlightPath()
+{
+ flight_path_yaw = 0.0f;
+ flight_path_pitch = 0.0f;
+
+ // transform flight path into camera frame:
+ Point flight_path = velocity;
+ if (flight_path.Normalize() < 1)
+ return;
+
+ Point tmp = flight_path;
+ flight_path.x = tmp * cam.vrt();
+ flight_path.y = tmp * cam.vup();
+ flight_path.z = tmp * cam.vpn();
+
+ if (flight_path.z < 0.1)
+ return;
+
+ // first, compute azimuth:
+ flight_path_yaw = (float) atan(flight_path.x / flight_path.z);
+ if (flight_path.z < 0) flight_path_yaw -= (float) PI;
+ if (flight_path_yaw < -PI) flight_path_yaw += (float) (2*PI);
+
+ // then, rotate path into azimuth frame to compute elevation:
+ Camera yaw_cam;
+ yaw_cam.Clone(cam);
+ yaw_cam.Yaw(flight_path_yaw);
+
+ flight_path.x = tmp * yaw_cam.vrt();
+ flight_path.y = tmp * yaw_cam.vup();
+ flight_path.z = tmp * yaw_cam.vpn();
+
+ flight_path_pitch = (float) atan(flight_path.y / flight_path.z);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::MoveTo(const Point& new_loc)
+{
+ cam.MoveTo(new_loc);
+}
+
+void
+Physical::TranslateBy(const Point& ref)
+{
+ Point new_loc = cam.Pos() - ref;
+ cam.MoveTo(new_loc);
+}
+
+void
+Physical::ApplyForce(const Point& force)
+{
+ velocity += force/mass;
+}
+
+void
+Physical::ApplyTorque(const Point& torque)
+{
+ dr += (float) (torque.x/mass);
+ dp += (float) (torque.y/mass);
+ dy += (float) (torque.z/mass);
+}
+
+void
+Physical::SetThrust(double t)
+{
+ thrust = (float) t;
+}
+
+void
+Physical::SetTransX(double t)
+{
+ trans_x = (float) t;
+}
+
+void
+Physical::SetTransY(double t)
+{
+ trans_y = (float) t;
+}
+
+void
+Physical::SetTransZ(double t)
+{
+ trans_z = (float) t;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::SetHeading(double r, double p, double y)
+{
+ roll = (float) r;
+ pitch = (float) p;
+ yaw = (float) y;
+
+ cam.Aim(roll, pitch, yaw);
+}
+
+void
+Physical::LookAt(const Point& dst)
+{
+ cam.LookAt(dst);
+}
+
+void
+Physical::CloneCam(const Camera& c)
+{
+ cam.Clone(c);
+}
+
+void
+Physical::SetAbsoluteOrientation(double r, double p, double y)
+{
+ roll = (float) r;
+ pitch = (float) p;
+ yaw = (float) y;
+
+ Camera work(Location().x, Location().y, Location().z);
+ work.Aim(r,p,y);
+ cam.Clone(work);
+}
+
+void
+Physical::ApplyRoll(double r)
+{
+ if (r > 1) r = 1;
+ else if (r < -1) r = -1;
+
+ dr_acc = (float) r * roll_rate;
+}
+
+void
+Physical::ApplyPitch(double p)
+{
+ if (p > 1) p = 1;
+ else if (p < -1) p = -1;
+
+ dp_acc = (float) p * pitch_rate;
+}
+
+void
+Physical::ApplyYaw(double y)
+{
+ if (y > 1) y = 1;
+ else if (y < -1) y = -1;
+
+ dy_acc = (float) y * yaw_rate;
+}
+
+void
+Physical::SetAngularRates(double r, double p, double y)
+{
+ roll_rate = (float) r;
+ pitch_rate = (float) p;
+ yaw_rate = (float) y;
+}
+
+void
+Physical::GetAngularRates(double& r, double& p, double& y)
+{
+ r = roll_rate;
+ p = pitch_rate;
+ y = yaw_rate;
+}
+
+void
+Physical::SetAngularDrag(double r, double p, double y)
+{
+ dr_drg = (float) r;
+ dp_drg = (float) p;
+ dy_drg = (float) y;
+}
+
+void
+Physical::GetAngularDrag(double& r, double& p, double& y)
+{
+ r = dr_drg;
+ p = dp_drg;
+ y = dy_drg;
+}
+
+void
+Physical::GetAngularThrust(double& r, double& p, double& y)
+{
+ r = 0;
+ p = 0;
+ y = 0;
+
+ if (dr_acc > 0.05 * roll_rate) r = 1;
+ else if (dr_acc < -0.05 * roll_rate) r = -1;
+ else if (dr > 0.01 * roll_rate) r = -1;
+ else if (dr < -0.01 * roll_rate) r = 1;
+
+ if (dy_acc > 0.05 * yaw_rate) y = 1;
+ else if (dy_acc < -0.05 * yaw_rate) y = -1;
+ else if (dy > 0.01 * yaw_rate) y = -1;
+ else if (dy < -0.01 * yaw_rate) y = 1;
+
+ if (dp_acc > 0.05 * pitch_rate) p = 1;
+ else if (dp_acc < -0.05 * pitch_rate) p = -1;
+ else if (dp > 0.01 * pitch_rate) p = -1;
+ else if (dp < -0.01 * pitch_rate) p = 1;
+}
+
+
+void
+Physical::SetPrimary(const Point& l, double m)
+{
+ primary_loc = l;
+ primary_mass = m;
+}
+
+void
+Physical::SetGravity(double g)
+{
+ if (g >= 0)
+ g_accel = (float) g;
+}
+
+void
+Physical::SetBaseDensity(double d)
+{
+ if (d >= 0)
+ Do = (float) d;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::InflictDamage(double damage, int /*type*/)
+{
+ integrity -= (float) damage;
+
+ if (integrity < 1.0f)
+ integrity = 0.0f;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Physical::CollidesWith(Physical& o)
+{
+ // representation collision test (will do bounding spheres first):
+ if (rep && o.rep)
+ return rep->CollidesWith(*o.rep);
+
+ Point delta_loc = Location() - o.Location();
+
+ // bounding spheres test:
+ if (delta_loc.length() > radius + o.radius)
+ return 0;
+
+ // assume collision:
+ return 1;
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::ElasticCollision(Physical& a, Physical& b)
+{
+ double mass_sum = a.mass + b.mass;
+ double mass_delta = a.mass - b.mass;
+
+ Point vel_a = (Point(b.velocity) * (2 * b.mass) + Point(a.velocity) * mass_delta) * (1/mass_sum);
+ Point vel_b = (Point(a.velocity) * (2 * a.mass) - Point(b.velocity) * mass_delta) * (1/mass_sum);
+
+ a.velocity = vel_a;
+ b.velocity = vel_b;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::InelasticCollision(Physical& a, Physical& b)
+{
+ double mass_sum = a.mass + b.mass;
+
+ Point vel_a = (Point(a.velocity) * a.mass + Point(b.velocity) * b.mass) * (1/mass_sum);
+
+ a.velocity = vel_a;
+ b.velocity = vel_a;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::SemiElasticCollision(Physical& a, Physical& b)
+{
+ double mass_sum = a.mass + b.mass;
+ double mass_delta = a.mass - b.mass;
+
+ Point avel = a.Velocity();
+ Point bvel = b.Velocity();
+ Point dv = avel - bvel;
+
+ // low delta-v: stick
+ if (dv.length() < 20) {
+ if (a.mass > b.mass) {
+ b.velocity = a.velocity;
+ }
+
+ else {
+ a.velocity = b.velocity;
+ }
+ }
+
+ // high delta-v: bounce
+ else {
+ Point Ve_a = (bvel * (2 * b.mass) + avel * mass_delta) * (1/mass_sum) * 0.65;
+ Point Ve_b = (avel * (2 * a.mass) - bvel * mass_delta) * (1/mass_sum) * 0.65;
+ Point Vi_ab = (avel * a.mass + bvel * b.mass) * (1/mass_sum) * 0.35;
+
+ a.arcade_velocity = Point();
+ b.arcade_velocity = Point();
+
+ a.velocity = Ve_a + Vi_ab;
+ b.velocity = Ve_b + Vi_ab;
+ }
+}
+
diff --git a/nGenEx/Physical.h b/nGenEx/Physical.h
new file mode 100644
index 0000000..fb202da
--- /dev/null
+++ b/nGenEx/Physical.h
@@ -0,0 +1,205 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Physical.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Physical Object
+*/
+
+#ifndef Physical_h
+#define Physical_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Camera.h"
+
+// +--------------------------------------------------------------------+
+
+class Director;
+class Graphic;
+class Light;
+
+// +--------------------------------------------------------------------+
+
+class Physical
+{
+public:
+ static const char* TYPENAME() { return "Physical"; }
+
+ Physical();
+ Physical(const char* n, int t=0);
+ virtual ~Physical();
+
+ int operator == (const Physical& p) const { return id == p.id; }
+
+ // Integration Loop Control:
+ static void SetSubFrameLength(double seconds) { sub_frame = seconds; }
+ static double GetSubFrameLength() { return sub_frame; }
+
+ // operations
+ virtual void ExecFrame(double seconds);
+ virtual void AeroFrame(double seconds);
+ virtual void ArcadeFrame(double seconds);
+
+ virtual void AngularFrame(double seconds);
+ virtual void LinearFrame(double seconds);
+
+ virtual void CalcFlightPath();
+
+ virtual void MoveTo(const Point& new_loc);
+ virtual void TranslateBy(const Point& ref);
+ virtual void ApplyForce(const Point& force);
+ virtual void ApplyTorque(const Point& torque);
+ virtual void SetThrust(double t);
+ virtual void SetTransX(double t);
+ virtual void SetTransY(double t);
+ virtual void SetTransZ(double t);
+ virtual void SetHeading(double r, double p, double y);
+ virtual void LookAt(const Point& dst);
+ virtual void ApplyRoll(double roll_acc);
+ virtual void ApplyPitch(double pitch_acc);
+ virtual void ApplyYaw(double yaw_acc);
+
+ virtual int CollidesWith(Physical& o);
+ static void ElasticCollision(Physical& a, Physical& b);
+ static void InelasticCollision(Physical& a, Physical& b);
+ static void SemiElasticCollision(Physical& a, Physical& b);
+ virtual void InflictDamage(double damage, int type = 0);
+
+ // accessors:
+ int Identity() const { return id; }
+ int Type() const { return obj_type; }
+ const char* Name() const { return name; }
+
+ Point Location() const { return cam.Pos(); }
+ Point Heading() const { return cam.vpn(); }
+ Point LiftLine() const { return cam.vup(); }
+ Point BeamLine() const { return cam.vrt(); }
+ Point Velocity() const { return velocity + arcade_velocity; }
+ Point Acceleration()
+ const { return accel; }
+ double Thrust() const { return thrust; }
+ double TransX() const { return trans_x; }
+ double TransY() const { return trans_y; }
+ double TransZ() const { return trans_z; }
+ double Drag() const { return drag; }
+
+ double Roll() const { return roll; }
+ double Pitch() const { return pitch; }
+ double Yaw() const { return yaw; }
+ Point Rotation() const { return Point(dp,dr,dy); }
+
+ double Alpha() const { return alpha; }
+
+ double FlightPathYawAngle() const { return flight_path_yaw; }
+ double FlightPathPitchAngle() const { return flight_path_pitch; }
+
+ double Radius() const { return radius; }
+ double Mass() const { return mass; }
+ double Integrity() const { return integrity; }
+ double Life() const { return life; }
+
+ double Shake() const { return shake; }
+ const Point& Vibration() const { return vibration; }
+
+ const Camera& Cam() const { return cam; }
+ Graphic* Rep() const { return rep; }
+ Light* LightSrc() const { return light; }
+
+ Director* GetDirector() const { return dir; }
+
+ // mutators:
+ virtual void SetAngularRates(double r, double p, double y);
+ virtual void GetAngularRates(double& r, double& p, double& y);
+ virtual void SetAngularDrag(double r, double p, double y);
+ virtual void GetAngularDrag(double& r, double& p, double& y);
+ virtual void GetAngularThrust(double& r, double& p, double& y);
+ virtual void SetVelocity(const Point& v) { velocity = v; }
+ virtual void SetAbsoluteOrientation(double roll, double pitch, double yaw);
+ virtual void CloneCam(const Camera& cam);
+ virtual void SetDrag(double d) { drag = (float) d; }
+
+ virtual void SetPrimary(const Point& loc, double mass);
+ virtual void SetGravity(double g);
+ virtual void SetBaseDensity(double d);
+
+ virtual double GetBaseDensity() const { return Do; }
+ virtual double GetDensity() const;
+
+ enum { NAMELEN = 48 };
+
+protected:
+ static int id_key;
+
+ // identification:
+ int id;
+ int obj_type;
+ char name[NAMELEN];
+
+ // position, velocity, and acceleration:
+ Camera cam;
+ Point velocity;
+ Point arcade_velocity;
+ Point accel;
+ float thrust;
+ float trans_x;
+ float trans_y;
+ float trans_z;
+ float drag;
+
+ // attitude and angular velocity:
+ float roll, pitch, yaw;
+ float dr, dp, dy;
+ float dr_acc, dp_acc, dy_acc;
+ float dr_drg, dp_drg, dy_drg;
+
+ float flight_path_yaw;
+ float flight_path_pitch;
+
+ // gravitation:
+ Point primary_loc;
+ double primary_mass;
+
+ // aerodynamics:
+ float g_accel; // acceleration due to gravity (constant)
+ float Do; // atmospheric density at sea level
+ float CL; // base coefficient of lift
+ float CD; // base coefficient of drag
+ float alpha; // current angle of attack (radians)
+ float stall; // stall angle of attack (radians)
+ bool lat_thrust; // lateral thrusters enabled in aero mode?
+ bool straight;
+
+ // vibration:
+ float shake;
+ Point vibration;
+
+ // scale factors for ApplyXxx():
+ float roll_rate, pitch_rate, yaw_rate;
+
+ // physical properties:
+ double life;
+ float radius;
+ float mass;
+ float integrity;
+
+ // graphic representation:
+ Graphic* rep;
+ Light* light;
+
+ // AI or human controller:
+ Director* dir; // null implies an autonomous object
+
+ static double sub_frame;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Physical_h
+
diff --git a/nGenEx/PngImage.cpp b/nGenEx/PngImage.cpp
new file mode 100644
index 0000000..0fedc6e
--- /dev/null
+++ b/nGenEx/PngImage.cpp
@@ -0,0 +1,246 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: PngImage.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ BMP image file loader
+*/
+
+
+#include "MemDebug.h"
+#include "PngImage.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "png.h"
+
+// +--------------------------------------------------------------------+
+
+PngImage::PngImage()
+ : width(0), height(0), bpp(0), alpha_loaded(false), image(0)
+{ }
+
+PngImage::~PngImage()
+{
+ delete [] image;
+}
+
+// +--------------------------------------------------------------------+
+
+int PngImage::Load(char *filename)
+{
+ int status = PNG_INVALID;
+ FILE* f;
+
+ f = fopen(filename,"rb");
+ if (f == NULL)
+ return PNG_NOFILE;
+
+ BYTE buf[12];
+ fread(buf, 8, 1, f);
+ fseek(f, 0, SEEK_SET);
+
+ if (png_sig_cmp(buf, (png_size_t)0, 8))
+ return PNG_INVALID;
+
+ png_structp png_ptr;
+ png_infop info_ptr;
+
+ /* Create and initialize the png_struct with the desired error handler
+ * functions. If you want to use the default stderr and longjump method,
+ * you can supply NULL for the last three parameters. We also supply the
+ * the compiler header file version, so that we know if the application
+ * was compiled with a compatible version of the library. REQUIRED
+ */
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+
+ if (!png_ptr) {
+ return PNG_NOMEM;
+ }
+
+ /* Allocate/initialize the memory for image information. REQUIRED. */
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
+ return PNG_NOMEM;
+ }
+
+ png_init_io(png_ptr, f);
+ png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, png_voidp_NULL);
+
+ status = CreateImage((void*) png_ptr, (void*) info_ptr);
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
+
+ fclose(f);
+ return status;
+}
+
+// +--------------------------------------------------------------------+
+
+struct PngIOStruct
+{
+ BYTE* fp;
+ BYTE* buffer;
+ DWORD length;
+};
+
+static void png_user_read_data(png_structp read_ptr, png_bytep data, png_size_t length)
+{
+ PngIOStruct* read_io_ptr = (PngIOStruct*) png_get_io_ptr(read_ptr);
+
+ if (!read_io_ptr)
+ return;
+
+ if (read_io_ptr->fp + length < read_io_ptr->buffer + read_io_ptr->length) {
+ memcpy(data, read_io_ptr->fp, length);
+ read_io_ptr->fp += length;
+ }
+}
+
+static void png_user_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+}
+
+static void png_user_flush_data(png_structp png_ptr)
+{
+}
+
+int PngImage::LoadBuffer(unsigned char* buf, int len)
+{
+ int status = PNG_INVALID;
+ PngIOStruct io;
+
+ if (buf == NULL)
+ return PNG_NOFILE;
+
+ if (png_sig_cmp(buf, (png_size_t)0, 8))
+ return PNG_INVALID;
+
+ io.buffer = buf;
+ io.fp = buf;
+ io.length = len;
+
+ png_structp png_ptr;
+ png_infop info_ptr;
+
+ /* Create and initialize the png_struct with the desired error handler
+ * functions. If you want to use the default stderr and longjump method,
+ * you can supply NULL for the last three parameters. We also supply the
+ * the compiler header file version, so that we know if the application
+ * was compiled with a compatible version of the library. REQUIRED
+ */
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+
+ if (!png_ptr) {
+ return PNG_NOMEM;
+ }
+
+ /* Allocate/initialize the memory for image information. REQUIRED. */
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
+ return PNG_NOMEM;
+ }
+
+ png_set_read_fn(png_ptr, (void *) (&io), png_user_read_data);
+ png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, png_voidp_NULL);
+
+ status = CreateImage((void*) png_ptr, (void*) info_ptr);
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
+
+ return status;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+PngImage::CreateImage(void* pptr, void* iptr)
+{
+ int status = PNG_INVALID;
+
+ png_structp png_ptr = (png_structp) pptr;
+ png_infop info_ptr = (png_infop) iptr;
+
+ width = info_ptr->width;
+ height = info_ptr->height;
+ bpp = info_ptr->pixel_depth;
+
+ if (width > 0 && width < 32768 && height > 0 && height < 32768) {
+ // true-color:
+ if (bpp >= 24) {
+ status = PNG_OK;
+
+ if (info_ptr->channels == 4)
+ alpha_loaded = true;
+
+ image = new DWORD[width*height];
+ BYTE** rows = png_get_rows(png_ptr, info_ptr);
+
+ for (DWORD row = 0; row < height; row++) {
+ BYTE* p = rows[row];
+
+ for (DWORD col = 0; col < width; col++) {
+ DWORD red = *p++;
+ DWORD green = *p++;
+ DWORD blue = *p++;
+ DWORD alpha = 0xff;
+
+ if (info_ptr->channels == 4)
+ alpha = *p++;
+
+ image[row*width+col] = (alpha << 24) | (red << 16) | (green << 8) | blue;
+ }
+ }
+ }
+
+ // paletted:
+ else if (bpp == 8 && info_ptr->num_palette > 0) {
+ DWORD pal[256];
+
+ if (info_ptr->num_trans > 0)
+ alpha_loaded = true;
+
+ for (int i = 0; i < 256; i++) {
+ if (i < info_ptr->num_palette) {
+ DWORD red = info_ptr->palette[i].red;
+ DWORD green = info_ptr->palette[i].green;
+ DWORD blue = info_ptr->palette[i].blue;
+ DWORD alpha = 0xff;
+
+ if (i < info_ptr->num_trans)
+ alpha = info_ptr->trans[i];
+
+ pal[i] = (alpha << 24) | (red << 16) | (green << 8) | blue;
+ }
+
+ else {
+ pal[i] = 0;
+ }
+ }
+
+ image = new DWORD[width*height];
+ BYTE** rows = png_get_rows(png_ptr, info_ptr);
+
+ for (DWORD row = 0; row < height; row++) {
+ BYTE* p = rows[row];
+
+ for (DWORD col = 0; col < width; col++) {
+ BYTE index = *p++;
+ image[row*width+col] = pal[index];
+ }
+ }
+ }
+ }
+
+ return status;
+} \ No newline at end of file
diff --git a/nGenEx/PngImage.h b/nGenEx/PngImage.h
new file mode 100644
index 0000000..dc03d27
--- /dev/null
+++ b/nGenEx/PngImage.h
@@ -0,0 +1,44 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: PngImage.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ PNG image file loader
+*/
+
+#ifndef PngImage_h
+#define PngImage_h
+
+// +--------------------------------------------------------------------+
+
+enum { PNG_OK, PNG_NOMEM, PNG_INVALID, PNG_NOFILE };
+
+// +--------------------------------------------------------------------+
+
+struct PngImage
+{
+ static const char* TYPENAME() { return "PngImage"; }
+
+ PngImage();
+ ~PngImage();
+
+ int Load(char *filename);
+ int LoadBuffer(unsigned char* buf, int len);
+ int CreateImage(void* png_ptr, void* info_ptr);
+
+ DWORD* image;
+ DWORD width;
+ DWORD height;
+ DWORD bpp;
+ bool alpha_loaded;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif PngImage_h
diff --git a/nGenEx/Polygon.cpp b/nGenEx/Polygon.cpp
new file mode 100644
index 0000000..419d3a5
--- /dev/null
+++ b/nGenEx/Polygon.cpp
@@ -0,0 +1,745 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Polygon.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Polygon and VertexSet structures for 3D rendering
+*/
+
+#include "MemDebug.h"
+#include "Polygon.h"
+#include "Bitmap.h"
+
+// +--------------------------------------------------------------------+
+
+VertexSet::VertexSet(int m)
+ : nverts(0), space(OBJECT_SPACE),
+ tu1(0), tv1(0), tangent(0), binormal(0)
+{
+ Resize(m);
+}
+
+// +--------------------------------------------------------------------+
+
+VertexSet::~VertexSet()
+{
+ Delete();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VertexSet::Resize(int m, bool preserve)
+{
+ // easy cases (no data will be preserved):
+ if (!m || !nverts || !preserve) {
+ bool additional_tex_coords = (tu1 != 0);
+
+ Delete();
+
+ nverts = m;
+
+ if (nverts <= 0) {
+ ZeroMemory(this, sizeof(VertexSet));
+ }
+
+ else {
+ loc = new(__FILE__,__LINE__) Vec3[nverts];
+ nrm = new(__FILE__,__LINE__) Vec3[nverts];
+ s_loc = new(__FILE__,__LINE__) Vec3[nverts];
+ tu = new(__FILE__,__LINE__) float[nverts];
+ tv = new(__FILE__,__LINE__) float[nverts];
+ rw = new(__FILE__,__LINE__) float[nverts];
+ diffuse = new(__FILE__,__LINE__) DWORD[nverts];
+ specular = new(__FILE__,__LINE__) DWORD[nverts];
+
+ if (additional_tex_coords)
+ CreateAdditionalTexCoords();
+
+ if (!loc || !nrm || !s_loc || !rw || !tu || !tv || !diffuse || !specular) {
+ nverts = 0;
+ nverts = 0;
+
+ delete [] loc;
+ delete [] nrm;
+ delete [] s_loc;
+ delete [] rw;
+ delete [] tu;
+ delete [] tv;
+ delete [] tu1;
+ delete [] tv1;
+ delete [] diffuse;
+ delete [] specular;
+
+ ZeroMemory(this, sizeof(VertexSet));
+ }
+ }
+ }
+
+ // actually need to copy data:
+ else {
+ int np = nverts;
+
+ nverts = m;
+
+ if (nverts < np)
+ np = nverts;
+
+ Vec3* new_loc = new(__FILE__,__LINE__) Vec3[nverts];
+ Vec3* new_nrm = new(__FILE__,__LINE__) Vec3[nverts];
+ Vec3* new_s_loc = new(__FILE__,__LINE__) Vec3[nverts];
+ float* new_rw = new(__FILE__,__LINE__) float[nverts];
+ float* new_tu = new(__FILE__,__LINE__) float[nverts];
+ float* new_tv = new(__FILE__,__LINE__) float[nverts];
+ float* new_tu1 = 0;
+ float* new_tv1 = 0;
+ DWORD* new_diffuse = new(__FILE__,__LINE__) DWORD[nverts];
+ DWORD* new_specular = new(__FILE__,__LINE__) DWORD[nverts];
+
+ if (tu1)
+ new_tu1 = new(__FILE__,__LINE__) float[nverts];
+
+ if (tv1)
+ new_tv1 = new(__FILE__,__LINE__) float[nverts];
+
+ if (new_loc) {
+ CopyMemory(new_loc, loc, np * sizeof(Vec3));
+ delete [] loc;
+ loc = new_loc;
+ }
+
+ if (new_nrm) {
+ CopyMemory(new_nrm, nrm, np * sizeof(Vec3));
+ delete [] nrm;
+ nrm = new_nrm;
+ }
+
+ if (new_s_loc) {
+ CopyMemory(new_s_loc, s_loc, np * sizeof(Vec3));
+ delete [] s_loc;
+ s_loc = new_s_loc;
+ }
+
+ if (new_tu) {
+ CopyMemory(new_tu, tu, np * sizeof(float));
+ delete [] tu;
+ tu = new_tu;
+ }
+
+ if (new_tv) {
+ CopyMemory(new_tv, tv, np * sizeof(float));
+ delete [] tv;
+ tv = new_tv;
+ }
+
+ if (new_tu1) {
+ CopyMemory(new_tu1, tu1, np * sizeof(float));
+ delete [] tu1;
+ tu = new_tu1;
+ }
+
+ if (new_tv1) {
+ CopyMemory(new_tv1, tv1, np * sizeof(float));
+ delete [] tv1;
+ tv = new_tv1;
+ }
+
+ if (new_diffuse) {
+ CopyMemory(new_diffuse, diffuse, np * sizeof(DWORD));
+ delete [] diffuse;
+ diffuse = new_diffuse;
+ }
+
+ if (new_specular) {
+ CopyMemory(new_specular, specular, np * sizeof(DWORD));
+ delete [] specular;
+ specular = new_specular;
+ }
+
+ if (!loc || !nrm || !s_loc || !rw || !tu || !tv || !diffuse || !specular) {
+ Delete();
+ ZeroMemory(this, sizeof(VertexSet));
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VertexSet::Delete()
+{
+ if (nverts) {
+ delete [] loc;
+ delete [] nrm;
+ delete [] s_loc;
+ delete [] rw;
+ delete [] tu;
+ delete [] tv;
+ delete [] tu1;
+ delete [] tv1;
+ delete [] diffuse;
+ delete [] specular;
+ delete [] tangent;
+ delete [] binormal;
+
+ tangent = 0;
+ binormal = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VertexSet::Clear()
+{
+ if (nverts) {
+ ZeroMemory(loc, sizeof(Vec3) * nverts);
+ ZeroMemory(nrm, sizeof(Vec3) * nverts);
+ ZeroMemory(s_loc, sizeof(Vec3) * nverts);
+ ZeroMemory(tu, sizeof(float) * nverts);
+ ZeroMemory(tv, sizeof(float) * nverts);
+ ZeroMemory(rw, sizeof(float) * nverts);
+ ZeroMemory(diffuse, sizeof(DWORD) * nverts);
+ ZeroMemory(specular, sizeof(DWORD) * nverts);
+
+ if (tu1)
+ ZeroMemory(tu1, sizeof(float) * nverts);
+
+ if (tv1)
+ ZeroMemory(tv1, sizeof(float) * nverts);
+
+ if (tangent)
+ ZeroMemory(tangent, sizeof(Vec3) * nverts);
+
+ if (binormal)
+ ZeroMemory(binormal, sizeof(Vec3) * nverts);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VertexSet::CreateTangents()
+{
+ if (tangent) delete [] tangent;
+ if (binormal) delete [] binormal;
+
+ tangent = 0;
+ binormal = 0;
+
+ if (nverts) {
+ tangent = new(__FILE__,__LINE__) Vec3[nverts];
+ binormal = new(__FILE__,__LINE__) Vec3[nverts];
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VertexSet::CreateAdditionalTexCoords()
+{
+ if (tu1) delete [] tu1;
+ if (tv1) delete [] tv1;
+
+ tu1 = 0;
+ tv1 = 0;
+
+ if (nverts) {
+ tu1 = new(__FILE__,__LINE__) float[nverts];
+ tv1 = new(__FILE__,__LINE__) float[nverts];
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VertexSet::CopyVertex(int dst, int src)
+{
+ if (src >= 0 && src < nverts && dst >= 0 && dst < nverts) {
+ loc[dst] = loc[src];
+ nrm[dst] = nrm[src];
+ s_loc[dst] = s_loc[src];
+ tu[dst] = tu[src];
+ tv[dst] = tv[src];
+ diffuse[dst] = diffuse[src];
+ specular[dst] = specular[src];
+
+ if (tu1)
+ tu1[dst] = tu1[src];
+
+ if (tv1)
+ tv1[dst] = tv1[src];
+
+ if (tangent)
+ tangent[dst] = tangent[src];
+
+ if (binormal)
+ binormal[dst] = binormal[src];
+
+ return true;
+ }
+
+ return false;
+}
+
+VertexSet*
+VertexSet::Clone() const
+{
+ VertexSet* result = new(__FILE__,__LINE__) VertexSet(nverts);
+
+ CopyMemory(result->loc, loc, nverts * sizeof(Vec3));
+ CopyMemory(result->nrm, nrm, nverts * sizeof(Vec3));
+ CopyMemory(result->s_loc, s_loc, nverts * sizeof(Vec3));
+ CopyMemory(result->rw, rw, nverts * sizeof(float));
+ CopyMemory(result->tu, tu, nverts * sizeof(float));
+ CopyMemory(result->tv, tv, nverts * sizeof(float));
+ CopyMemory(result->diffuse, diffuse, nverts * sizeof(DWORD));
+ CopyMemory(result->specular, specular, nverts * sizeof(DWORD));
+
+ if (tu1) {
+ if (!result->tu1)
+ result->tu1 = new(__FILE__,__LINE__) float[nverts];
+
+ CopyMemory(result->tu1, tu1, nverts * sizeof(float));
+ }
+
+ if (tv1) {
+ if (!result->tv1)
+ result->tv1 = new(__FILE__,__LINE__) float[nverts];
+
+ CopyMemory(result->tv1, tv1, nverts * sizeof(float));
+ }
+
+ if (tangent) {
+ if (!result->tangent)
+ result->tangent = new(__FILE__,__LINE__) Vec3[nverts];
+
+ CopyMemory(result->tangent, tangent, nverts * sizeof(Vec3));
+ }
+
+ if (binormal) {
+ if (!result->binormal)
+ result->binormal = new(__FILE__,__LINE__) Vec3[nverts];
+
+ CopyMemory(result->binormal, binormal, nverts * sizeof(Vec3));
+ }
+
+ return result;
+}
+
+void
+VertexSet::CalcExtents(Point& plus, Point& minus)
+{
+ plus = Point(-1e6, -1e6, -1e6);
+ minus = Point( 1e6, 1e6, 1e6);
+
+ for (int i = 0; i < nverts; i++) {
+ if (loc[i].x > plus.x) plus.x = loc[i].x;
+ if (loc[i].x < minus.x) minus.x = loc[i].x;
+ if (loc[i].y > plus.y) plus.y = loc[i].y;
+ if (loc[i].y < minus.y) minus.y = loc[i].y;
+ if (loc[i].z > plus.z) plus.z = loc[i].z;
+ if (loc[i].z < minus.z) minus.z = loc[i].z;
+ }
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+Poly::Poly(int init)
+ : nverts(0), visible(1), material(0), vertex_set(0), sortval(0), flatness(0)
+{ }
+
+// +--------------------------------------------------------------------+
+// Check to see if a test point is within the bounds of the poly.
+// The point is assumed to be coplanar with the poly. Return 1 if
+// the point is inside, 0 if the point is outside.
+
+Vec2 projverts[Poly::MAX_VERTS];
+
+static inline double extent3(double a, double b, double c)
+{
+ double d1 = fabs(a-b);
+ double d2 = fabs(a-c);
+ double d3 = fabs(b-c);
+
+ if (d1 > d2) {
+ if (d1 > d3)
+ return d1;
+ else
+ return d3;
+ }
+ else {
+ if (d2 > d3)
+ return d2;
+ else
+ return d3;
+ }
+}
+
+int Poly::Contains(const Vec3& pt) const
+{
+ // find largest 2d projection of this 3d Poly:
+ int projaxis;
+
+ double pnx = fabs(plane.normal.x);
+ double pny = fabs(plane.normal.y);
+ double pnz = fabs(plane.normal.z);
+
+ if (pnx > pny)
+ if (pnx > pnz)
+ if (plane.normal.x > 0)
+ projaxis = 1;
+ else
+ projaxis = -1;
+ else
+ if (plane.normal.z > 0)
+ projaxis = 3;
+ else
+ projaxis = -3;
+ else
+ if (pny > pnz)
+ if (plane.normal.y > 0)
+ projaxis = 2;
+ else
+ projaxis = -2;
+ else
+ if (plane.normal.z > 0)
+ projaxis = 3;
+ else
+ projaxis = -3;
+
+ int i;
+
+ for (i = 0; i < nverts; i++) {
+ Vec3 loc = vertex_set->loc[verts[i]];
+ switch (projaxis) {
+ case 1: projverts[i] = Vec2(loc.y, loc.z); break;
+ case -1: projverts[i] = Vec2(loc.z, loc.y); break;
+ case 2: projverts[i] = Vec2(loc.z, loc.x); break;
+ case -2: projverts[i] = Vec2(loc.x, loc.z); break;
+ case 3: projverts[i] = Vec2(loc.x, loc.y); break;
+ case -3: projverts[i] = Vec2(loc.y, loc.x); break;
+ }
+ }
+
+ // now project the test point into the same plane:
+ Vec2 test;
+ switch (projaxis) {
+ case 1: test.x = pt.y; test.y = pt.z; break;
+ case -1: test.x = pt.z; test.y = pt.y; break;
+ case 2: test.x = pt.z; test.y = pt.x; break;
+ case -2: test.x = pt.x; test.y = pt.z; break;
+ case 3: test.x = pt.x; test.y = pt.y; break;
+ case -3: test.x = pt.y; test.y = pt.x; break;
+ }
+
+ const float INSIDE_EPSILON = -0.01f;
+
+ // if the test point is outside of any segment,
+ // it is outside the entire convex Poly.
+ for (i = 0; i < nverts-1; i++) {
+ if (verts[i] != verts[i+1]) {
+ Vec2 segment = projverts[i+1] - projverts[i];
+ Vec2 segnorm = segment.normal();
+ Vec2 tdelta = projverts[i] - test;
+ float inside = segnorm * tdelta;
+ if (inside < INSIDE_EPSILON)
+ return 0;
+ }
+ }
+
+ // check last segment, too:
+ if (verts[0] != verts[nverts-1]) {
+ Vec2 segment = projverts[0] - projverts[nverts-1];
+ float inside = segment.normal() * (projverts[0] - test);
+ if (inside < INSIDE_EPSILON)
+ return 0;
+ }
+
+ // still here? must be inside:
+ return 1;
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+Material::Material()
+ : power(1.0f), brilliance(1.0f), bump(0.0f), blend(MTL_SOLID),
+ shadow(true), luminous(false),
+ tex_diffuse(0), tex_specular(0), tex_bumpmap(0), tex_emissive(0),
+ tex_alternate(0), tex_detail(0), thumbnail(0)
+{
+ ZeroMemory(name, sizeof(name));
+ ZeroMemory(shader, sizeof(shader));
+
+ ambient_value = 0.2f;
+ diffuse_value = 1.0f;
+ specular_value = 0.0f;
+ emissive_value = 0.0f;
+}
+
+// +--------------------------------------------------------------------+
+
+Material::~Material()
+{
+ // these objects are owned by the shared
+ // bitmap cache, so don't delete them now:
+ tex_diffuse = 0;
+ tex_specular = 0;
+ tex_bumpmap = 0;
+ tex_emissive = 0;
+ tex_alternate = 0;
+ tex_detail = 0;
+
+ // the thumbnail is unique to the material,
+ // so it is never cached:
+ if (thumbnail)
+ delete thumbnail;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Material::operator == (const Material& m) const
+{
+ if (this == &m) return 1;
+
+ if (Ka != m.Ka) return 0;
+ if (Kd != m.Kd) return 0;
+ if (Ks != m.Ks) return 0;
+ if (Ke != m.Ke) return 0;
+ if (power != m.power) return 0;
+ if (brilliance != m.brilliance) return 0;
+ if (bump != m.bump) return 0;
+ if (blend != m.blend) return 0;
+ if (shadow != m.shadow) return 0;
+ if (tex_diffuse != m.tex_diffuse) return 0;
+ if (tex_specular != m.tex_specular) return 0;
+ if (tex_bumpmap != m.tex_bumpmap) return 0;
+ if (tex_emissive != m.tex_emissive) return 0;
+ if (tex_alternate != m.tex_alternate) return 0;
+ if (tex_detail != m.tex_detail) return 0;
+
+ return !strcmp(name, m.name);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Material::Clear()
+{
+ Ka = ColorValue();
+ Kd = ColorValue();
+ Ks = ColorValue();
+ Ke = ColorValue();
+
+ power = 1.0f;
+ bump = 0.0f;
+ blend = MTL_SOLID;
+ shadow = true;
+
+ tex_diffuse = 0;
+ tex_specular = 0;
+ tex_bumpmap = 0;
+ tex_emissive = 0;
+ tex_alternate = 0;
+ tex_detail = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+static char shader_name[Material::NAMELEN];
+
+const char*
+Material::GetShader(int pass) const
+{
+ int level = 0;
+ if (pass > 1) pass--;
+
+ for (int i = 0; i < NAMELEN; i++) {
+ if (shader[i] == '/') {
+ level++;
+
+ if (level > pass)
+ return 0;
+ }
+
+ else if (shader[i] != 0) {
+ if (level == pass) {
+ ZeroMemory(shader_name, NAMELEN);
+
+ char* s = shader_name;
+ while (i < NAMELEN && shader[i] != 0 && shader[i] != '/') {
+ *s++ = shader[i++];
+ }
+
+ return shader_name;
+ }
+ }
+
+ else {
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Material::CreateThumbnail(int size)
+{
+ if (!thumbnail) {
+ thumbnail = new(__FILE__,__LINE__) Bitmap(size, size);
+ }
+
+ if (!thumbnail || thumbnail->Width() != thumbnail->Height())
+ return;
+
+ size = thumbnail->Width();
+
+ DWORD* image = new(__FILE__,__LINE__) DWORD[size*size];
+ DWORD* dst = image;
+
+ for (int j = 0; j < size; j++) {
+ for (int i = 0; i < size; i++) {
+ *dst++ = GetThumbColor(i, j, size);
+ }
+ }
+
+ thumbnail->CopyHighColorImage(size, size, image, Bitmap::BMP_SOLID);
+}
+
+DWORD
+Material::GetThumbColor(int i, int j, int size)
+{
+ Color result = Color::LightGray;
+
+ double x = i - size/2;
+ double y = j - size/2;
+ double r = 0.9 * size/2;
+ double d = sqrt(x*x + y*y);
+
+ if (d <= r) {
+ double z = sqrt(r*r - x*x - y*y);
+
+ Point loc(x,y,z);
+ Point nrm = loc; nrm.Normalize();
+ Point light(1,-1,1); light.Normalize();
+ Point eye(0,0,1);
+
+ ColorValue c = Ka * ColorValue(0.25f, 0.25f, 0.25f); // ambient light
+ ColorValue white(1,1,1);
+
+ double diffuse = nrm*light;
+ double v = 1 - (acos(nrm.y)/PI);
+ double u = asin(nrm.x / sin(acos(nrm.y))) / PI + 0.5;
+
+ ColorValue cd = Kd;
+ ColorValue cs = Ks;
+ ColorValue ce = Ke;
+
+ if (tex_diffuse) {
+ int tu = (int) (u * tex_diffuse->Width());
+ int tv = (int) (v * tex_diffuse->Height());
+ cd = Kd * tex_diffuse->GetColor(tu,tv);
+ }
+
+ if (tex_emissive) {
+ int tu = (int) (u * tex_emissive->Width());
+ int tv = (int) (v * tex_emissive->Height());
+ ce = Ke * tex_emissive->GetColor(tu,tv);
+ }
+
+ if (tex_bumpmap && bump != 0 && nrm.z > 0) {
+ // compute derivatives B(u,v)
+ int tu = (int) (u * tex_bumpmap->Width());
+ int tv = (int) (v * tex_bumpmap->Height());
+
+ double du1 = tex_bumpmap->GetColor(tu,tv).Red() -
+ tex_bumpmap->GetColor(tu-1,tv).Red();
+ double du2 = tex_bumpmap->GetColor(tu+1,tv).Red() -
+ tex_bumpmap->GetColor(tu,tv).Red();
+
+ double dv1 = tex_bumpmap->GetColor(tu,tv).Red() -
+ tex_bumpmap->GetColor(tu,tv-1).Red();
+ double dv2 = tex_bumpmap->GetColor(tu,tv+1).Red() -
+ tex_bumpmap->GetColor(tu,tv).Red();
+
+ double du = (du1 + du2) / 512 * 1e-8;
+ double dv = (dv1 + dv2) / 512 * 1e-8;
+
+ if (du || dv) {
+ Point Nu = nrm.cross(Point(0,-1,0)); Nu.Normalize();
+ Point Nv = nrm.cross(Point(1, 0,0)); Nv.Normalize();
+
+ nrm += (Nu*du*bump);
+ nrm += (Nv*dv*bump);
+ nrm.Normalize();
+
+ diffuse = nrm*light;
+ v = 1 - (acos(nrm.y)/PI);
+ u = asin(nrm.x / sin(acos(nrm.y))) / PI + 0.5;
+ }
+ }
+
+ if (tex_specular) {
+ int tu = (int) (u * tex_specular->Width());
+ int tv = (int) (v * tex_specular->Height());
+ cs = Ks * tex_specular->GetColor(tu,tv);
+ }
+
+ // anisotropic diffuse lighting
+ if (brilliance >= 0) {
+ diffuse = pow(diffuse, brilliance);
+ }
+
+ // forward lighting
+ if (diffuse > 0) {
+ // diffuse
+ c += cd * (white * diffuse);
+
+ // specular
+ if (power > 0) {
+ double spec = ((nrm * 2*(nrm*light) - light) * eye);
+ if (spec > 0.01) {
+ spec = pow(spec, power);
+ c += cs * (white * spec);
+ }
+ }
+ }
+
+ // back lighting
+ else {
+ diffuse *= -0.5;
+ c += cd * (white * diffuse);
+
+ // specular
+ if (power > 0) {
+ light *= -1;
+
+ double spec = ((nrm * 2*(nrm*light) - light) * eye);
+ if (spec > 0.01) {
+ spec = pow(spec, power);
+ c += cs * (white * spec) * 0.7;
+ }
+ }
+ }
+
+ c += ce;
+
+ result = c.ToColor();
+ }
+
+ return result.Value();
+} \ No newline at end of file
diff --git a/nGenEx/Polygon.h b/nGenEx/Polygon.h
new file mode 100644
index 0000000..ebb7bfb
--- /dev/null
+++ b/nGenEx/Polygon.h
@@ -0,0 +1,159 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Polygon.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Polygon structures: VertexSet, Poly, Material
+*/
+
+#ifndef Polygon_h
+#define Polygon_h
+
+#include "Geometry.h"
+#include "Color.h"
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+struct Poly;
+struct Material;
+struct VertexSet;
+
+// +--------------------------------------------------------------------+
+
+struct Poly
+{
+ static const char* TYPENAME() { return "Poly"; }
+
+ enum { MAX_VERTS = 4 };
+
+ Poly() { }
+ Poly(int init);
+ ~Poly() { }
+
+ int operator < (const Poly& p) const { return sortval < p.sortval; }
+ int operator == (const Poly& p) const { return this == &p; }
+
+ int Contains(const Vec3& pt) const;
+
+ BYTE nverts;
+ BYTE visible;
+ WORD verts[MAX_VERTS];
+ WORD vlocs[MAX_VERTS];
+ VertexSet* vertex_set;
+ Material* material;
+ int sortval;
+ float flatness;
+ Plane plane;
+};
+
+// +--------------------------------------------------------------------+
+
+struct Material
+{
+ static const char* TYPENAME() { return "Material"; }
+
+ enum BLEND_TYPE { MTL_SOLID=1, MTL_TRANSLUCENT=2, MTL_ADDITIVE=4 };
+ enum { NAMELEN=32 };
+
+ Material();
+ ~Material();
+
+ int operator == (const Material& m) const;
+
+ void Clear();
+
+ char name[NAMELEN];
+ char shader[NAMELEN];
+
+ ColorValue Ka; // ambient color
+ ColorValue Kd; // diffuse color
+ ColorValue Ks; // specular color
+ ColorValue Ke; // emissive color
+ float power; // highlight sharpness (big=shiny)
+ float brilliance; // diffuse power function
+ float bump; // bump level (0=none)
+ DWORD blend; // alpha blend type
+ bool shadow; // casts shadow
+ bool luminous; // verts have their own lighting
+
+ Bitmap* tex_diffuse;
+ Bitmap* tex_specular;
+ Bitmap* tex_bumpmap;
+ Bitmap* tex_emissive;
+ Bitmap* tex_alternate;
+ Bitmap* tex_detail;
+
+ bool IsSolid() const { return blend == MTL_SOLID; }
+ bool IsTranslucent() const { return blend == MTL_TRANSLUCENT; }
+ bool IsGlowing() const { return blend == MTL_ADDITIVE; }
+ const char* GetShader(int n) const;
+
+ //
+ // Support for Magic GUI
+ //
+
+ Color ambient_color;
+ Color diffuse_color;
+ Color specular_color;
+ Color emissive_color;
+
+ float ambient_value;
+ float diffuse_value;
+ float specular_value;
+ float emissive_value;
+
+ Bitmap* thumbnail; // preview image
+
+ void CreateThumbnail(int size=128);
+ DWORD GetThumbColor(int i, int j, int size);
+};
+
+// +--------------------------------------------------------------------+
+
+struct VertexSet
+{
+ static const char* TYPENAME() { return "VertexSet"; }
+
+ enum VertexSpaces { OBJECT_SPACE, WORLD_SPACE, VIEW_SPACE, SCREEN_SPACE };
+
+ VertexSet(int m);
+ ~VertexSet();
+
+ void Resize(int m, bool preserve=false);
+ void Delete();
+ void Clear();
+ void CreateTangents();
+ void CreateAdditionalTexCoords();
+ bool CopyVertex(int dst, int src);
+ void CalcExtents(Point& plus, Point& minus);
+
+ VertexSet* Clone() const;
+
+ int nverts;
+ int space;
+
+ Vec3* loc;
+ Vec3* nrm;
+ Vec3* s_loc;
+ float* rw;
+ float* tu;
+ float* tv;
+ float* tu1;
+ float* tv1;
+ DWORD* diffuse;
+ DWORD* specular;
+ Vec3* tangent;
+ Vec3* binormal;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Polygon_h
+
diff --git a/nGenEx/Projector.cpp b/nGenEx/Projector.cpp
new file mode 100644
index 0000000..af88d08
--- /dev/null
+++ b/nGenEx/Projector.cpp
@@ -0,0 +1,470 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Projector.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ 3D Projection Camera class
+*/
+
+#include "MemDebug.h"
+#include "Projector.h"
+
+// +--------------------------------------------------------------------+
+
+static const float CLIP_PLANE_EPSILON = 0.0001f;
+static const double Z_NEAR = 1.0;
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+static Camera emergency_cam;
+
+// +--------------------------------------------------------------------+
+
+Projector::Projector(Window* window, Camera* cam)
+ : camera(cam), infinite(0), depth_scale(1.0f), orthogonal(false), field_of_view(2)
+{
+ if (!camera)
+ camera = &emergency_cam;
+
+ UseWindow(window);
+}
+
+Projector::~Projector()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+Projector::UseCamera(Camera* cam)
+{
+ if (cam)
+ camera = cam;
+ else
+ camera = &emergency_cam;
+}
+
+void
+Projector::UseWindow(Window* win)
+{
+ Rect r = win->GetRect();
+ width = r.w;
+ height = r.h;
+
+ xcenter = (width / 2.0);
+ ycenter = (height / 2.0);
+
+ xclip0 = 0.0f;
+ xclip1 = (float) width-0.5f;
+ yclip0 = 0.0f;
+ yclip1 = (float) height-0.5f;
+
+ SetFieldOfView(field_of_view);
+}
+
+void
+Projector::SetFieldOfView(double fov)
+{
+ field_of_view = fov;
+
+ xscreenscale = width / fov;
+ yscreenscale = height / fov;
+
+ maxscale = max(xscreenscale, yscreenscale);
+
+ xangle = atan(2.0/fov * maxscale/xscreenscale);
+ yangle = atan(2.0/fov * maxscale/yscreenscale);
+}
+
+double
+Projector::GetFieldOfView() const
+{
+ return field_of_view;
+}
+
+void
+Projector::SetDepthScale(float scale)
+{
+ depth_scale = scale;
+}
+
+double
+Projector::GetDepthScale() const
+{
+ return depth_scale;
+}
+
+int
+Projector::SetInfinite(int i)
+{
+ int old = infinite;
+ infinite = i;
+ return old;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Projector::StartFrame()
+{
+ SetUpFrustum();
+ SetWorldSpace();
+}
+
+// +--------------------------------------------------------------------+
+// Transform a point from worldspace to viewspace.
+// +--------------------------------------------------------------------+
+
+void
+Projector::Transform(Vec3& vec) const
+{
+ Vec3 tvert = vec;
+
+ // Translate into a viewpoint-relative coordinate
+ if (!infinite)
+ tvert -= camera->Pos();
+
+ // old method:
+ vec.x = (tvert * camera->vrt());
+ vec.y = (tvert * camera->vup());
+ vec.z = (tvert * camera->vpn());
+
+ // Rotate into the view orientation
+ // vec = tvert * camera->Orientation();
+}
+
+// +--------------------------------------------------------------------+
+// Transform a point from worldspace to viewspace.
+// +--------------------------------------------------------------------+
+
+void
+Projector::Transform(Point& point) const
+{
+ Point tvert = point;
+
+ // Translate into a viewpoint-relative coordinate
+ if (!infinite)
+ tvert -= camera->Pos();
+
+ // old method:
+ point.x = (tvert * camera->vrt());
+ point.y = (tvert * camera->vup());
+ point.z = (tvert * camera->vpn());
+
+ // Rotate into the view orientation
+ // point = tvert * camera->Orientation();
+}
+
+// +--------------------------------------------------------------------+
+// APPARENT RADIUS OF PROJECTED OBJECT
+// Project a viewspace point into screen coordinates.
+// Use projected Z to determine apparent radius of object.
+// +--------------------------------------------------------------------+
+
+float
+Projector::ProjectRadius(const Vec3& v, float radius) const
+{
+ return (float) fabs((radius * maxscale) / v.z);
+}
+
+// +--------------------------------------------------------------------+
+// IN PLACE PROJECTION OF POINT
+// Project a viewspace point into screen coordinates.
+// Note that the y axis goes up in worldspace and viewspace, but
+// goes down in screenspace.
+// +--------------------------------------------------------------------+
+
+void
+Projector::Project(Vec3& v, bool clamp) const
+{
+ double zrecip;
+
+ if (orthogonal) {
+ double scale = field_of_view/2;
+ v.x = (float) (xcenter + scale * v.x);
+ v.y = (float) (height - (ycenter + scale * v.y));
+ v.z = (float) (0.0f);
+ }
+
+ else {
+ //zrecip = 2 * (1.0e5 / (1.0e5-1)) / v.z;
+ //zrecip = 2 * 0.97 / v.z; -- what the heck was this version used for?
+
+ zrecip = 2 / v.z;
+ v.x = (float) (xcenter + maxscale * v.x * zrecip);
+ v.y = (float) (height - (ycenter + maxscale * v.y * zrecip));
+ v.z = (float) (1 - zrecip);
+ }
+
+ // clamp the point to the viewport:
+ if (clamp) {
+ if (v.x < xclip0) v.x = xclip0;
+ if (v.x > xclip1) v.x = xclip1;
+ if (v.y < yclip0) v.y = yclip0;
+ if (v.y > yclip1) v.y = yclip1;
+ }
+}
+
+// +--------------------------------------------------------------------+
+// IN PLACE PROJECTION OF POINT
+// Project a viewspace point into screen coordinates.
+// Note that the y axis goes up in worldspace and viewspace, but
+// goes down in screenspace.
+// +--------------------------------------------------------------------+
+
+void
+Projector::Project(Point& v, bool clamp) const
+{
+ double zrecip;
+
+ if (orthogonal) {
+ double scale = field_of_view/2;
+ v.x = (xcenter + scale * v.x);
+ v.y = (height - (ycenter + scale * v.y));
+ v.z = 0;
+ }
+
+ else {
+ zrecip = 1 / v.z;
+ v.x = (xcenter + 2 * maxscale * v.x * zrecip);
+ v.y = (height - (ycenter + 2 * maxscale * v.y * zrecip));
+ v.z = (1 - zrecip);
+ }
+
+ // clamp the point to the viewport:
+ if (clamp) {
+ if (v.x < xclip0) v.x = xclip0;
+ if (v.x > xclip1) v.x = xclip1;
+ if (v.y < yclip0) v.y = yclip0;
+ if (v.y > yclip1) v.y = yclip1;
+ }
+}
+
+// +--------------------------------------------------------------------+
+// IN PLACE UN-PROJECTION OF POINT
+// Convert a point in screen coordinates back to viewspace.
+// Note that the y axis goes up in worldspace and viewspace, but
+// goes down in screenspace.
+// +--------------------------------------------------------------------+
+
+void
+Projector::Unproject(Point& v) const
+{
+ double zrecip = 1 / v.z;
+
+ /***
+ * forward projection:
+ v.x = (xcenter + maxscale * v.x * zrecip);
+ v.y = (height - (ycenter + maxscale * v.y * zrecip));
+ v.z = (1 - zrecip);
+ ***/
+
+ v.x = ( v.x - xcenter) / (maxscale * zrecip);
+ v.y = (height - v.y - ycenter) / (maxscale * zrecip);
+}
+
+// +--------------------------------------------------------------------+
+// IN PLACE PROJECTION OF RECTANGLE (FOR SPRITES)
+// Project a viewspace point into screen coordinates.
+// Note that the y axis goes up in worldspace and viewspace, but
+// goes down in screenspace.
+// +--------------------------------------------------------------------+
+
+void
+Projector::ProjectRect(Point& v, double& w, double& h) const
+{
+ double zrecip;
+
+ if (orthogonal) {
+ double scale = field_of_view/2;
+ v.x = (xcenter + scale * v.x);
+ v.y = (height - (ycenter + scale * v.y));
+ v.z = 0;
+ }
+
+ else {
+ zrecip = 1 / v.z;
+ v.x = (xcenter + 2 * maxscale * v.x * zrecip);
+ v.y = (height - (ycenter + 2 * maxscale * v.y * zrecip));
+ v.z = (1 - Z_NEAR*zrecip);
+
+ w *= maxscale * zrecip;
+ h *= maxscale * zrecip;
+ }
+}
+
+// +--------------------------------------------------------------------+
+// Set up a clip plane with the specified normal.
+// +--------------------------------------------------------------------+
+
+void
+Projector::SetWorldspaceClipPlane(Vec3& normal, Plane& plane)
+{
+ // Rotate the plane normal into worldspace
+ ViewToWorld(normal, plane.normal);
+ plane.distance = (float) (camera->Pos() * plane.normal + CLIP_PLANE_EPSILON);
+}
+
+// +--------------------------------------------------------------------+
+// Set up the planes of the frustum, in worldspace coordinates.
+// +--------------------------------------------------------------------+
+
+void
+Projector::SetUpFrustum()
+{
+ double angle, s, c;
+ Vec3 normal;
+
+ angle = XAngle();
+ s = sin(angle);
+ c = cos(angle);
+
+ // Left clip plane
+ normal.x = (float) s;
+ normal.y = (float) 0;
+ normal.z = (float) c;
+ view_planes[0].normal = normal;
+ view_planes[0].distance = CLIP_PLANE_EPSILON;
+ SetWorldspaceClipPlane(normal, world_planes[0]);
+
+ // Right clip plane
+ normal.x = (float) -s;
+ view_planes[1].normal = normal;
+ view_planes[1].distance = CLIP_PLANE_EPSILON;
+ SetWorldspaceClipPlane(normal, world_planes[1]);
+
+ angle = YAngle();
+ s = sin(angle);
+ c = cos(angle);
+
+ // Bottom clip plane
+ normal.x = (float) 0;
+ normal.y = (float) s;
+ normal.z = (float) c;
+ view_planes[2].normal = normal;
+ view_planes[2].distance = CLIP_PLANE_EPSILON;
+ SetWorldspaceClipPlane(normal, world_planes[2]);
+
+ // Top clip plane
+ normal.y = (float) -s;
+ view_planes[3].normal = normal;
+ view_planes[3].distance = CLIP_PLANE_EPSILON;
+ SetWorldspaceClipPlane(normal, world_planes[3]);
+}
+
+// +--------------------------------------------------------------------+
+// Clip the point against the frustum and return 1 if partially inside
+// Return 2 if completely inside
+// +--------------------------------------------------------------------+
+
+int
+Projector::IsVisible(const Vec3& v, float radius) const
+{
+ int visible = 1;
+ int complete = 1;
+
+ Plane* plane = (Plane*) frustum_planes;
+ if (infinite) {
+ complete = 0;
+
+ for (int i = 0; visible && (i < NUM_FRUSTUM_PLANES); i++) {
+ visible = ((v * plane->normal) >= CLIP_PLANE_EPSILON);
+ plane++;
+ }
+ }
+ else {
+ for (int i = 0; visible && (i < NUM_FRUSTUM_PLANES); i++) {
+ float dot = v * plane->normal;
+ visible = ((dot + radius) >= plane->distance);
+ complete = complete && ((dot - radius) >= plane->distance);
+ plane++;
+ }
+ }
+
+ return visible + complete;
+}
+
+// +--------------------------------------------------------------------+
+// Clip the bouding point against the frustum and return non zero
+// if at least partially inside. This version is not terribly
+// efficient as it checks all eight box corners rather than just
+// the minimum two.
+// +--------------------------------------------------------------------+
+
+int
+Projector::IsBoxVisible(const Point* p) const
+{
+ int i, j, outside = 0;
+
+ // if all eight corners are outside of the same
+ // frustrum plane, then the box is not visible
+ Plane* plane = (Plane*) frustum_planes;
+
+ if (infinite) {
+ for (i = 0; !outside && (i < NUM_FRUSTUM_PLANES); i++) {
+ for (j = 0; j < 8; j++)
+ outside += (p[j] * plane->normal) < CLIP_PLANE_EPSILON;
+
+ if (outside < 8)
+ outside = 0;
+
+ plane++;
+ }
+ }
+ else {
+ for (i = 0; !outside && (i < NUM_FRUSTUM_PLANES); i++) {
+ for (j = 0; j < 8; j++)
+ outside += (p[j] * plane->normal) < plane->distance;
+
+ if (outside < 8)
+ outside = 0;
+
+ plane++;
+ }
+ }
+
+ // if not outside, then the box is visible
+ return !outside;
+}
+
+// +--------------------------------------------------------------------+
+
+float
+Projector::ApparentRadius(const Vec3& v, float radius) const
+{
+ Vec3 vloc = v;
+
+ Transform(vloc); // transform in place
+ return ProjectRadius(vloc, radius);
+}
+
+
+// +--------------------------------------------------------------------+
+// Rotate a vector from viewspace to worldspace.
+// +--------------------------------------------------------------------+
+
+void
+Projector::ViewToWorld(Point& pin, Point& pout)
+{
+ // Rotate into the world orientation
+ pout.x = pin.x * camera->vrt().x + pin.y * camera->vup().x + pin.z * camera->vpn().x;
+ pout.y = pin.x * camera->vrt().y + pin.y * camera->vup().y + pin.z * camera->vpn().y;
+ pout.z = pin.x * camera->vrt().z + pin.y * camera->vup().z + pin.z * camera->vpn().z;
+}
+
+void
+Projector::ViewToWorld(Vec3& vin, Vec3& vout)
+{
+ // Rotate into the world orientation
+ vout.x = (float) (vin.x * camera->vrt().x + vin.y * camera->vup().x + vin.z * camera->vpn().x);
+ vout.y = (float) (vin.x * camera->vrt().y + vin.y * camera->vup().y + vin.z * camera->vpn().y);
+ vout.z = (float) (vin.x * camera->vrt().z + vin.y * camera->vup().z + vin.z * camera->vpn().z);
+}
+
diff --git a/nGenEx/Projector.h b/nGenEx/Projector.h
new file mode 100644
index 0000000..e70a383
--- /dev/null
+++ b/nGenEx/Projector.h
@@ -0,0 +1,106 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Projector.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ 3D Projection Camera class
+*/
+
+#ifndef Projector_h
+#define Projector_h
+
+#include "Geometry.h"
+#include "Window.h"
+#include "Camera.h"
+#include "Polygon.h"
+
+// +--------------------------------------------------------------------+
+
+class Projector
+{
+public:
+ Projector(Window* win, Camera* cam);
+ virtual ~Projector();
+
+ // Operations:
+ virtual void UseWindow(Window* win);
+ virtual void UseCamera(Camera* cam);
+ virtual void SetDepthScale(float scale);
+ virtual double GetDepthScale() const;
+ virtual void SetFieldOfView(double fov);
+ virtual double GetFieldOfView() const;
+ virtual int SetInfinite(int i);
+ virtual void StartFrame();
+
+ // accessor:
+ Point Pos() const { return camera->Pos(); }
+ Point vrt() { return camera->vrt(); }
+ Point vup() { return camera->vup(); }
+ Point vpn() { return camera->vpn(); }
+ const Matrix& Orientation() const { return camera->Orientation(); }
+
+ double XAngle() const { return xangle; }
+ double YAngle() const { return yangle; }
+
+ bool IsOrthogonal() const { return orthogonal; }
+ void SetOrthogonal(bool o) { orthogonal = o; }
+
+ // projection and clipping geometry:
+ virtual void Transform(Vec3& vec) const;
+ virtual void Transform(Point& point) const;
+
+ virtual void Project(Vec3& vec, bool clamp=true) const;
+ virtual void Project(Point& point, bool clamp=true) const;
+ virtual void ProjectRect(Point& origin, double& w, double& h) const;
+
+ virtual float ProjectRadius(const Vec3& vec, float radius) const;
+
+ virtual void Unproject(Point& point) const;
+ int IsVisible(const Vec3& v, float radius) const;
+ int IsBoxVisible(const Point* p) const;
+
+ float ApparentRadius(const Vec3& v, float radius) const;
+
+ virtual void SetWorldSpace() { frustum_planes = world_planes; }
+ virtual void SetViewSpace() { frustum_planes = view_planes; }
+
+ Plane* GetCurrentClipPlanes() { return frustum_planes; }
+
+ void SetUpFrustum();
+ void ViewToWorld(Point& pin, Point& pout);
+ void ViewToWorld(Vec3& vin, Vec3& vout);
+ void SetWorldspaceClipPlane(Vec3& normal, Plane& plane);
+
+protected:
+ Camera* camera;
+
+ int width, height;
+ double field_of_view;
+ double xscreenscale, yscreenscale, maxscale;
+ double xcenter, ycenter;
+ double xangle, yangle;
+
+ int infinite;
+ float depth_scale;
+ bool orthogonal;
+
+ enum DISPLAY_CONST {
+ NUM_FRUSTUM_PLANES= 4,
+ };
+
+ Plane* frustum_planes;
+ Plane world_planes[NUM_FRUSTUM_PLANES];
+ Plane view_planes[NUM_FRUSTUM_PLANES];
+
+ float xclip0, xclip1;
+ float yclip0, yclip1;
+};
+
+#endif Projector_h
+
diff --git a/nGenEx/Random.cpp b/nGenEx/Random.cpp
new file mode 100644
index 0000000..646b385
--- /dev/null
+++ b/nGenEx/Random.cpp
@@ -0,0 +1,148 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Random.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Utility functions for generating random numbers and locations.
+*/
+
+#include "MemDebug.h"
+#include "Random.h"
+
+// +----------------------------------------------------------------------+
+
+void RandomInit()
+{
+ srand(timeGetTime());
+}
+
+// +----------------------------------------------------------------------+
+
+Point RandomDirection()
+{
+ Point p = Point(rand() - 16384, rand() - 16384, 0);
+ p.Normalize();
+ return p;
+}
+
+// +----------------------------------------------------------------------+
+
+Point RandomPoint()
+{
+ Point p = Point(rand() - 16384, rand() - 16384, 0);
+ p.Normalize();
+ p *= 15e3 + rand()/3;
+ return p;
+}
+
+// +----------------------------------------------------------------------+
+
+Vec3 RandomVector(double radius)
+{
+ Vec3 v = Vec3(rand() - 16384, rand() - 16384, rand() - 16384);
+ v.Normalize();
+
+ if (radius > 0)
+ v *= (float) radius;
+ else
+ v *= (float) Random(radius/3, radius);
+
+ return v;
+}
+
+// +----------------------------------------------------------------------+
+
+double Random(double min, double max)
+{
+ double delta = max - min;
+ double r = delta * rand() / 32768.0;
+
+ return min + r;
+}
+
+// +----------------------------------------------------------------------+
+
+int RandomIndex()
+{
+ static int index = 0;
+ static int table[16] = { 0, 9, 4, 7, 14, 11, 2, 12, 1, 5, 13, 8, 6, 10, 3, 15 };
+
+ int r = 1 + ((rand() & 0x0700) >> 8);
+ index += r;
+ if (index > 1e7) index = 0;
+ return table[index % 16];
+}
+
+// +----------------------------------------------------------------------+
+
+bool RandomChance(int wins, int tries)
+{
+ double fraction = 256.0 * wins / tries;
+ double r = (rand() >> 4) & 0xFF;
+
+ return r < fraction;
+}
+
+// +----------------------------------------------------------------------+
+
+int RandomSequence(int current, int range)
+{
+ if (range > 1) {
+ int step = (int) Random(1, range-1);
+ return (current + step) % range;
+ }
+
+ return current;
+}
+
+// +----------------------------------------------------------------------+
+
+int RandomShuffle(int count)
+{
+ static int set_size = -1;
+ static BYTE set[256];
+ static int index = -1;
+
+ if (count < 0 || count > 250)
+ return 0;
+
+ if (set_size != count) {
+ set_size = count;
+ index = -1;
+ }
+
+ // need to reshuffle
+ if (index < 0 || index > set_size-1) {
+ // set up the deck
+ int tmp[256];
+ for (int i = 0; i < 256; i++)
+ tmp[i] = i;
+
+ // shuffle the cards
+ for (i = 0; i < set_size; i++) {
+ int n = (int) Random(0, set_size);
+ int tries = set_size;
+ while (tmp[n] < 0 && tries--) {
+ n = (n+1) % set_size;
+ }
+
+ if (tmp[n] >= 0) {
+ set[i] = tmp[n];
+ tmp[n] = -1;
+ }
+ else {
+ set[i] = 0;
+ }
+ }
+
+ index = 0;
+ }
+
+ return set[index++];
+}
diff --git a/nGenEx/Random.h b/nGenEx/Random.h
new file mode 100644
index 0000000..05411e1
--- /dev/null
+++ b/nGenEx/Random.h
@@ -0,0 +1,35 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Random.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Utility functions for generating random numbers and locations.
+*/
+
+#ifndef Random_h
+#define Random_h
+
+#include "Types.h"
+#include "Geometry.h"
+
+// +----------------------------------------------------------------------+
+
+void RandomInit();
+Point RandomDirection();
+Point RandomPoint();
+Vec3 RandomVector(double radius);
+double Random(double min=0, double max=1);
+int RandomIndex();
+bool RandomChance(int wins=1, int tries=2);
+int RandomSequence(int current, int range);
+int RandomShuffle(int count);
+
+// +----------------------------------------------------------------------+
+
+#endif Random_h
diff --git a/nGenEx/Res.cpp b/nGenEx/Res.cpp
new file mode 100644
index 0000000..60903be
--- /dev/null
+++ b/nGenEx/Res.cpp
@@ -0,0 +1,28 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Res.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Resource class
+*/
+
+#include "MemDebug.h"
+#include "Res.h"
+
+// +--------------------------------------------------------------------+
+
+static int RESOURCE_KEY = 1;
+
+Resource::Resource()
+ : id((HANDLE) RESOURCE_KEY++)
+{ }
+
+Resource::~Resource()
+{ }
+
diff --git a/nGenEx/Res.h b/nGenEx/Res.h
new file mode 100644
index 0000000..4732d23
--- /dev/null
+++ b/nGenEx/Res.h
@@ -0,0 +1,37 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Res.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Resource class
+*/
+
+#ifndef Res_h
+#define Res_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class Resource
+{
+public:
+ Resource();
+ virtual ~Resource();
+
+ int operator == (const Resource& r) const { return id == r.id; }
+
+ HANDLE Handle() const { return id; }
+
+protected:
+ HANDLE id;
+};
+
+#endif Res_h
+
diff --git a/nGenEx/Resource.h b/nGenEx/Resource.h
new file mode 100644
index 0000000..0b19029
--- /dev/null
+++ b/nGenEx/Resource.h
@@ -0,0 +1,21 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by nGen.rc
+//
+#define IDI_ICON1 101
+#define IDR_MENU1 102
+#define IDM_RAMP 40001
+#define IDM_RGB 40002
+#define IDM_HAL 40003
+#define IDM_EXIT 40004
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 105
+#define _APS_NEXT_COMMAND_VALUE 40005
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/nGenEx/RichTextBox.cpp b/nGenEx/RichTextBox.cpp
new file mode 100644
index 0000000..f7c75ff
--- /dev/null
+++ b/nGenEx/RichTextBox.cpp
@@ -0,0 +1,454 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: RichTextBox.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Window class
+*/
+
+#include "MemDebug.h"
+#include "RichTextBox.h"
+#include "EventDispatch.h"
+#include "Color.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "FontMgr.h"
+#include "Mouse.h"
+#include "Screen.h"
+#include "View.h"
+
+// +--------------------------------------------------------------------+
+
+RichTextBox::RichTextBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid, DWORD astyle)
+ : ScrollWindow(p->GetScreen(), ax, ay, aw, ah, aid, astyle, p)
+{
+ leading = 2;
+
+ char buf[32];
+ sprintf(buf, "RichTextBox %d", id);
+ desc = buf;
+}
+
+RichTextBox::RichTextBox(Screen* screen, int ax, int ay, int aw, int ah, DWORD aid, DWORD astyle)
+ : ScrollWindow(screen, ax, ay, aw, ah, aid, astyle)
+{
+ leading = 2;
+
+ char buf[32];
+ sprintf(buf, "RichTextBox %d", id);
+ desc = buf;
+}
+
+RichTextBox::~RichTextBox()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+RichTextBox::SetText(const char* t)
+{
+ ActiveWindow::SetText(t);
+ ScrollTo(0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+RichTextBox::DrawContent(const Rect& ctrl_rect)
+{
+ DrawTabbedText();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+RichTextBox::DrawTabbedText()
+{
+ if (font && text.length()) {
+ int border_size = 4;
+
+ if (style & WIN_RAISED_FRAME && style & WIN_SUNK_FRAME)
+ border_size = 8;
+
+ Rect label_rect = rect;
+
+ label_rect.x = border_size;
+ label_rect.y = border_size;
+ label_rect.w -= border_size * 2;
+ label_rect.h -= border_size * 2;
+
+ if (scroll_bar)
+ label_rect.w -= SCROLL_TRACK;
+
+ if (line_height < font->Height())
+ line_height = font->Height();
+
+ font->SetColor(fore_color);
+ DrawRichText(label_rect);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int RichTextBox::find_next_word_start(const char* text, int index)
+{
+ // step through intra-word space:
+ while (text[index] && isspace(text[index]) &&
+ (text[index] != '\t') &&
+ (text[index] != '\n') &&
+ (text[index] != '<'))
+ index++;
+
+ return index;
+}
+
+int RichTextBox::find_next_word_end(const char* text, int index)
+{
+ // check for leading newline or tag:
+ if (text[index] == '\n' || text[index] == '\t' || text[index] == '<')
+ return index;
+
+ // step through intra-word space:
+ while (text[index] && isspace(text[index]))
+ index++;
+
+ // step through word:
+ while (text[index] && !isspace(text[index]) &&
+ (text[index] != '-') &&
+ (text[index] != '<'))
+ index++;
+
+ if (index) {
+ if (text[index] != '-')
+ return index-1;
+ else
+ return index;
+ }
+
+ return 0;
+}
+
+int RichTextBox::parse_hex_digit(char c)
+{
+ if (isalpha(c))
+ return 10 + tolower(c) - 'a';
+
+ else if (isdigit(c))
+ return c - '0';
+
+ return 0;
+}
+
+int RichTextBox::process_tag(const char* text, int index, Font*& font)
+{
+ if (text[index] == '<') {
+ char tag[64];
+ int i = 0;
+
+ while (text[index] && (text[index] != '>'))
+ tag[i++] = text[index++];
+
+ if (text[index] == '>')
+ tag[i++] = text[index++];
+
+ tag[i] = 0;
+
+ switch (tag[1]) {
+ case 'c':
+ case 'C': if (strnicmp(tag+1, "color", 5) == 0) {
+ int r = 0;
+ int g = 0;
+ int b = 0;
+
+ if (i > 12) {
+ r = 16 * parse_hex_digit(tag[ 7]) + parse_hex_digit(tag[ 8]);
+ g = 16 * parse_hex_digit(tag[ 9]) + parse_hex_digit(tag[10]);
+ b = 16 * parse_hex_digit(tag[11]) + parse_hex_digit(tag[12]);
+ }
+
+ font->SetColor(Color(r,g,b));
+ }
+ break;
+
+ case 'f':
+ case 'F': if (strnicmp(tag+1, "font", 4) == 0) {
+ Color current_color = Color::White;
+
+ if (font)
+ current_color = font->GetColor();
+
+ tag[i-1] = 0;
+ font = FontMgr::Find(tag+6);
+ font->SetColor(current_color);
+ }
+ break;
+ }
+ }
+
+ return index;
+}
+
+int
+RichTextBox::GetNextTab(int xpos)
+{
+ for (int i = 0; i < 10; i++) {
+ if (tab[i] > xpos)
+ return tab[i];
+ }
+
+ return (xpos / 20) * 20 + 20;
+}
+
+void
+RichTextBox::DrawRichText(Rect& text_rect)
+{
+ // clip the rect:
+ Rect clip_rect = ClipRect(text_rect);
+ clip_rect.h -= 8;
+
+ if (clip_rect.w < 1 || clip_rect.h < 1)
+ return;
+
+ const char* t = text.data();
+ int count = text.length();
+ int nlines = 0;
+
+ int xpos = 0;
+ int block_start = 0;
+ int block_count = 0;
+ int curr_word_end = -1;
+ int next_word_end = 0;
+ int eol_index = 0;
+
+ int new_line = 0;
+ int x_offset = 0;
+ int y_offset = 0;
+ int length = 0;
+
+ Font* rich_font = font;
+ rich_font->SetColor(fore_color);
+
+ if (smooth_scroll) {
+ double fraction = smooth_offset - (int) smooth_offset;
+
+ y_offset = (int) ((1-fraction) * (rich_font->Height() + leading));
+ }
+
+ // while there is still text:
+ while (block_start < count) {
+ bool found_tag = false;
+
+ do {
+ found_tag = false;
+
+ if (t[block_start] == '<') {
+ block_start = process_tag(t, block_start, rich_font);
+ found_tag = true;
+ }
+
+ else if (t[block_start] == '\t') {
+ block_start++;
+ x_offset = GetNextTab(x_offset);
+
+ if (x_offset > text_rect.w) {
+ nlines++;
+ if (nlines > top_index)
+ y_offset += rich_font->Height() + leading;
+ x_offset = 0;
+ new_line = false;
+ }
+
+ found_tag = true;
+ }
+
+ else if (t[block_start] == '\r') {
+ block_start++;
+
+ if (t[block_start] == '\n')
+ block_start++;
+
+ nlines++;
+ if (nlines > top_index)
+ y_offset += rich_font->Height() + leading;
+ x_offset = 0;
+ new_line = false;
+
+ found_tag = true;
+ }
+
+ else if (t[block_start] == '\n') {
+ block_start++;
+
+ if (t[block_start] == '\r')
+ block_start++;
+
+ nlines++;
+ if (nlines > top_index)
+ y_offset += rich_font->Height() + leading;
+ x_offset = 0;
+ new_line = false;
+
+ found_tag = true;
+ }
+ }
+ while (found_tag);
+
+ next_word_end = find_next_word_end(t, block_start);
+
+ if (!next_word_end || next_word_end == curr_word_end) {
+ new_line = true;
+ }
+
+ else if (t[next_word_end] == '\n') {
+ eol_index = curr_word_end = next_word_end;
+ new_line = true;
+ }
+
+ else {
+ int word_len = next_word_end - block_start + 1;
+
+ length = rich_font->StringWidth(t+block_start, word_len);
+
+ // if this word was too long, wrap to next line:
+ if (x_offset + length > text_rect.w) {
+ nlines++;
+ if (nlines > top_index)
+ y_offset += rich_font->Height() + leading;
+ x_offset = 0;
+ new_line = false;
+ }
+
+ // is there a trailing newline?
+ curr_word_end = next_word_end;
+
+ // check for a newline in the next block of white space:
+ eol_index = 0;
+ const char* eol = &t[curr_word_end+1];
+ while (*eol && isspace(*eol) && *eol != '\n')
+ eol++;
+
+ if (*eol == '\n') {
+ eol_index = eol - t;
+ new_line = true;
+ }
+ }
+
+ block_count = curr_word_end - block_start + 1;
+
+ if (block_count > 0) {
+ length = rich_font->StringWidth(t+block_start, block_count);
+ }
+
+ // there was a single word longer than the entire line:
+ else {
+ block_count = next_word_end - block_start + 1;
+ length = rich_font->StringWidth(t+block_start, block_count);
+ curr_word_end = next_word_end;
+ }
+
+ if (length > 0 && nlines >= top_index && nlines < top_index+page_size) {
+ int x1 = text_rect.x + x_offset + rect.x;
+ int y1 = text_rect.y + y_offset + rect.y;
+
+ rich_font->DrawString(t+block_start, block_count, x1, y1, clip_rect);
+ }
+
+ if (new_line) {
+ nlines++;
+ if (nlines > top_index)
+ y_offset += rich_font->Height() + leading;
+ x_offset = 0;
+ new_line = false;
+ }
+
+ else if (length < 1 || text[next_word_end] == '-') {
+ x_offset += length;
+ }
+
+ else {
+ x_offset += length + rich_font->SpaceWidth();
+ }
+
+ if (eol_index > 0)
+ curr_word_end = eol_index;
+
+ block_start = find_next_word_start(t, curr_word_end+1);
+ }
+
+ line_count = nlines;
+}
+
+// +--------------------------------------------------------------------+
+
+int RichTextBox::OnMouseMove(int x, int y)
+{
+ if (captured) {
+ ActiveWindow* test = GetCapture();
+
+ if (test != this) {
+ captured = false;
+ }
+
+ else {
+ if (scrolling == SCROLL_THUMB) {
+ mouse_y = y - rect.y - TRACK_START;
+
+ int dest = (int) ((double) mouse_y/track_length * (line_count-1));
+ ScrollTo(dest);
+ }
+ }
+ }
+
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int RichTextBox::OnLButtonDown(int x, int y)
+{
+ return ScrollWindow::OnLButtonDown(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int RichTextBox::OnLButtonUp(int x, int y)
+{
+ return ScrollWindow::OnLButtonUp(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int RichTextBox::OnMouseWheel(int wheel)
+{
+ return ScrollWindow::OnMouseWheel(wheel);
+}
+
+// +--------------------------------------------------------------------+
+
+int RichTextBox::OnClick()
+{
+ int fire_click = !scrolling;
+
+ if (scrolling == SCROLL_THUMB)
+ scrolling = SCROLL_NONE;
+
+ if (fire_click)
+ return ActiveWindow::OnClick();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int RichTextBox::OnKeyDown(int vk, int flags)
+{
+ return ScrollWindow::OnKeyDown(vk, flags);
+}
+
diff --git a/nGenEx/RichTextBox.h b/nGenEx/RichTextBox.h
new file mode 100644
index 0000000..deb7caa
--- /dev/null
+++ b/nGenEx/RichTextBox.h
@@ -0,0 +1,65 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: RichTextBox.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Rich Text Window - an HTML-like control
+*/
+
+#ifndef RichTextBox_h
+#define RichTextBox_h
+
+#include "Types.h"
+#include "Color.h"
+#include "Bitmap.h"
+#include "ScrollWindow.h"
+#include "EventTarget.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class RichTextBox : public ScrollWindow
+{
+public:
+ static const char* TYPENAME() { return "RichTextBox"; }
+
+ RichTextBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid=0, DWORD astyle=0);
+ RichTextBox(Screen* s, int ax, int ay, int aw, int ah, DWORD aid=0, DWORD astyle=0);
+ virtual ~RichTextBox();
+
+ int operator == (const RichTextBox& w) const { return id == w.id; }
+
+ // Operations:
+ virtual void DrawContent(const Rect& ctrl_rect);
+ virtual void SetText(const char* t);
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnMouseWheel(int wheel);
+ virtual int OnClick();
+
+ virtual int OnKeyDown(int vk, int flags);
+
+protected:
+ virtual void DrawTabbedText();
+ virtual void DrawRichText(Rect& text_rect);
+ int GetNextTab(int xpos);
+
+ virtual int find_next_word_start(const char* text, int index);
+ virtual int find_next_word_end(const char* text, int index);
+ virtual int parse_hex_digit(char c);
+ virtual int process_tag(const char* text, int index, Font*& font);
+
+};
+
+#endif RichTextBox_h
+
diff --git a/nGenEx/Scene.cpp b/nGenEx/Scene.cpp
new file mode 100644
index 0000000..26ef58e
--- /dev/null
+++ b/nGenEx/Scene.cpp
@@ -0,0 +1,261 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Scene.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ A 3D Scene
+*/
+
+#include "MemDebug.h"
+#include "Scene.h"
+#include "Graphic.h"
+#include "Light.h"
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+Scene::Scene()
+{ }
+
+Scene::~Scene()
+{
+ background.destroy();
+ foreground.destroy();
+ graphics.destroy();
+ sprites.destroy();
+
+ lights.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Scene::AddBackground(Graphic* g)
+{
+ if (g) {
+ if (!background.contains(g))
+ background.append(g);
+
+ g->SetScene(this);
+ }
+}
+
+void
+Scene::DelBackground(Graphic* g)
+{
+ if (g) {
+ background.remove(g);
+ g->SetScene(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Scene::AddForeground(Graphic* g)
+{
+ if (g) {
+ if (!foreground.contains(g))
+ foreground.append(g);
+
+ g->SetScene(this);
+ }
+}
+
+void
+Scene::DelForeground(Graphic* g)
+{
+ if (g) {
+ foreground.remove(g);
+ g->SetScene(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Scene::AddGraphic(Graphic* g)
+{
+ if (g) {
+ if (!graphics.contains(g))
+ graphics.append(g);
+
+ g->SetScene(this);
+ }
+}
+
+void
+Scene::DelGraphic(Graphic* g)
+{
+ if (g) {
+ graphics.remove(g) || // it's gotta be in here somewhere!
+ foreground.remove(g) || // use the logical-or operator to early
+ sprites.remove(g) || // out when we find it...
+ background.remove(g);
+
+ g->SetScene(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Scene::AddSprite(Graphic* g)
+{
+ if (g) {
+ if (!sprites.contains(g))
+ sprites.append(g);
+
+ g->SetScene(this);
+ }
+}
+
+void
+Scene::DelSprite(Graphic* g)
+{
+ if (g) {
+ sprites.remove(g);
+ g->SetScene(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Scene::AddLight(Light* l)
+{
+ if (l) {
+ if (!lights.contains(l))
+ lights.append(l);
+ l->SetScene(this);
+ }
+}
+
+void
+Scene::DelLight(Light* l)
+{
+ if (l) {
+ lights.remove(l);
+ l->SetScene(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Scene::Collect()
+{
+ ListIter<Graphic> iter = graphics;
+
+ while (++iter) {
+ Graphic* g = iter.value();
+ if (g->Life() == 0) {
+ delete iter.removeItem();
+ }
+ }
+
+ iter.attach(sprites);
+
+ while (++iter) {
+ Graphic* g = iter.value();
+ if (g->Life() == 0) {
+ delete iter.removeItem();
+ }
+ }
+
+ ListIter<Light> iter1 = lights;
+
+ while (++iter1) {
+ Light* l = iter1.value();
+ if (l->Life() == 0) {
+ delete iter1.removeItem();
+ }
+ }
+}
+
+
+// +--------------------------------------------------------------------+
+
+bool
+Scene::IsLightObscured(const Point& obj_pos, const Point& light_pos, double obj_radius, Point* impact_point) const
+{
+ Point dir = light_pos - obj_pos;
+ double len = dir.Normalize();
+
+ Scene* pThis = (Scene*) this; // cast-away const
+ Graphic* g = 0;
+ bool obscured = false;
+
+ ListIter<Graphic> g_iter = pThis->graphics;
+ while (++g_iter && !obscured) {
+ g = g_iter.value();
+
+ if (g->CastsShadow() && !g->Hidden() && !g->IsInfinite()) {
+ double gdist = (g->Location() - obj_pos).length();
+ if (gdist > 0.1 && // different than object being obscured
+ g->Radius() > obj_radius && // larger than object being obscured
+ (g->Radius()*400)/gdist > 10) { // projects to a resonable size
+
+ Point delta = (g->Location() - light_pos);
+
+ if (delta.length() > g->Radius() / 100) { // not the object that is emitting the light
+ Point impact;
+ obscured = g->CheckRayIntersection(obj_pos, dir, len, impact, false) ? true : false;
+
+ if (impact_point)
+ *impact_point = impact;
+ }
+ }
+
+ else if (obj_radius < 0 && gdist < 0.1) { // special case for camera (needed for cockpits)
+ Point delta = (g->Location() - light_pos);
+
+ if (delta.length() > g->Radius() / 100) { // not the object that is emitting the light
+ Point impact;
+ obscured = g->CheckRayIntersection(obj_pos, dir, len, impact, false) ? true : false;
+ }
+ }
+ }
+ }
+
+ g_iter.attach(pThis->foreground);
+ while (++g_iter && !obscured) {
+ g = g_iter.value();
+
+ if (g->CastsShadow() && !g->Hidden()) {
+ double gdist = (g->Location() - obj_pos).length();
+ if (gdist > 0.1 && // different than object being obscured
+ g->Radius() > obj_radius && // larger than object being obscured
+ (g->Radius()*400)/gdist > 10) { // projects to a resonable size
+
+ Point delta = (g->Location() - light_pos);
+
+ if (delta.length() > g->Radius() / 100) { // not the object that is emitting the light
+ Point impact;
+ obscured = g->CheckRayIntersection(obj_pos, dir, len, impact, false) ? true : false;
+
+ if (impact_point)
+ *impact_point = impact;
+ }
+ }
+
+ else if (obj_radius < 0 && gdist < 0.1) { // special case for camera (needed for cockpits)
+ Point delta = (g->Location() - light_pos);
+
+ if (delta.length() > g->Radius() / 100) { // not the object that is emitting the light
+ Point impact;
+ obscured = g->CheckRayIntersection(obj_pos, dir, len, impact, false) ? true : false;
+ }
+ }
+ }
+ }
+
+ return obscured;
+}
diff --git a/nGenEx/Scene.h b/nGenEx/Scene.h
new file mode 100644
index 0000000..fdefed5
--- /dev/null
+++ b/nGenEx/Scene.h
@@ -0,0 +1,76 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Scene.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ A 3D Scene, basically a collection of 3D graphic objects
+*/
+
+#ifndef Scene_h
+#define Scene_h
+
+#include "Types.h"
+#include "Color.h"
+#include "Geometry.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Graphic;
+class Light;
+
+// +--------------------------------------------------------------------+
+
+class Scene
+{
+public:
+ static const char* TYPENAME() { return "Scene"; }
+
+ Scene();
+ virtual ~Scene();
+
+ void AddBackground(Graphic* g);
+ void DelBackground(Graphic* g);
+ void AddForeground(Graphic* g);
+ void DelForeground(Graphic* g);
+ void AddGraphic(Graphic* g);
+ void DelGraphic(Graphic* g);
+ void AddSprite(Graphic* g);
+ void DelSprite(Graphic* g);
+
+ void AddLight(Light* l);
+ void DelLight(Light* l);
+
+ List<Graphic>& Background() { return background; }
+ List<Graphic>& Foreground() { return foreground; }
+ List<Graphic>& Graphics() { return graphics; }
+ List<Graphic>& Sprites() { return sprites; }
+ List<Light>& Lights() { return lights; }
+ Color Ambient() { return ambient; }
+ void SetAmbient(Color a) { ambient = a; }
+
+ virtual void Collect();
+
+ virtual bool IsLightObscured(const Point& obj_pos,
+ const Point& light_pos,
+ double obj_radius,
+ Point* imp_point=0) const;
+
+protected:
+ List<Graphic> background;
+ List<Graphic> foreground;
+ List<Graphic> graphics;
+ List<Graphic> sprites;
+ List<Light> lights;
+ Color ambient;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Scene_h
diff --git a/nGenEx/Screen.cpp b/nGenEx/Screen.cpp
new file mode 100644
index 0000000..f50f8ec
--- /dev/null
+++ b/nGenEx/Screen.cpp
@@ -0,0 +1,161 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Screen.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ General Screen class - maintains and displays a list of windows
+*/
+
+#include "MemDebug.h"
+#include "Screen.h"
+#include "Bitmap.h"
+#include "Color.h"
+#include "Window.h"
+#include "Mouse.h"
+#include "Pcx.h"
+#include "Video.h"
+
+// +--------------------------------------------------------------------+
+
+Screen::Screen(Video* v)
+ : width(0), height(0), video(v), clear(0), closed(0)
+{
+ if (video) {
+ width = video->Width();
+ height = video->Height();
+ }
+
+ Mouse::Create(this);
+}
+
+Screen::~Screen()
+{
+ Mouse::Close();
+
+ closed = 1;
+ window_list.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Screen::AddWindow(Window* c)
+{
+ if (!c || closed) return false;
+
+ if (c->X() < 0) return false;
+ if (c->Y() < 0) return false;
+ if (c->X() + c->Width() > Width()) return false;
+ if (c->Y() + c->Height() > Height()) return false;
+
+ if (!window_list.contains(c))
+ window_list.append(c);
+
+ return true;
+}
+
+bool
+Screen::DelWindow(Window* c)
+{
+ if (!c || closed) return false;
+
+ return window_list.remove(c) == c;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Screen::ClearAllFrames(bool clear_all)
+{
+ if (clear_all)
+ clear = -1;
+ else
+ clear = 0;
+}
+
+void
+Screen::ClearNextFrames(int num_frames)
+{
+ if (clear >= 0 && clear < num_frames)
+ clear = num_frames;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Screen::SetBackgroundColor(Color c)
+{
+ if (video)
+ return video->SetBackgroundColor(c);
+ else
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Screen::Resize(int w, int h)
+{
+ // scale all root-level windows to new screen size:
+
+ ListIter<Window> iter = window_list;
+ while (++iter) {
+ Window* win = iter.value();
+
+ double w_x = win->GetRect().x / (double) width;
+ double w_y = win->GetRect().y / (double) height;
+ double w_w = win->GetRect().w / (double) width;
+ double w_h = win->GetRect().h / (double) height;
+
+ Rect r;
+
+ r.x = (int) (w_x * w);
+ r.y = (int) (w_y * h);
+ r.w = (int) (w_w * w);
+ r.h = (int) (w_h * h);
+
+ win->MoveTo(r);
+ }
+
+ width = w;
+ height = h;
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Screen::Refresh()
+{
+ if (clear && !video->ClearAll())
+ return false;
+
+ video->StartFrame();
+
+ ListIter<Window> iter = window_list;
+ while (++iter) {
+ Window* win = iter.value();
+
+ if (win->IsShown()) {
+ win->Paint();
+ }
+ }
+
+ Mouse::Paint();
+
+ video->EndFrame();
+
+ if (clear > 0) clear--;
+ return true;
+}
+
+
+
+
diff --git a/nGenEx/Screen.h b/nGenEx/Screen.h
new file mode 100644
index 0000000..39df897
--- /dev/null
+++ b/nGenEx/Screen.h
@@ -0,0 +1,65 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Screen.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ General Screen class - maintains and displays a list of windows
+*/
+
+#ifndef Screen_h
+#define Screen_h
+
+#include "Types.h"
+#include "Color.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+class Window;
+struct Rect;
+
+// +--------------------------------------------------------------------+
+
+class Screen
+{
+public:
+ static const char* TYPENAME() { return "Screen"; }
+
+ Screen(Video* v);
+ virtual ~Screen();
+
+ virtual bool SetBackgroundColor(Color c);
+
+ virtual bool Resize(int w, int h);
+ virtual bool Refresh();
+ virtual bool AddWindow(Window* c);
+ virtual bool DelWindow(Window* c);
+
+ int Width() const { return width; }
+ int Height() const { return height; }
+
+ virtual void ClearAllFrames(bool clear_all);
+ virtual void ClearNextFrames(int num_frames);
+
+ virtual Video* GetVideo() const { return video; }
+
+protected:
+ int width;
+ int height;
+ int clear;
+ int closed;
+
+ Video* video;
+
+ List<Window> window_list;
+};
+
+#endif Screen_h
+
diff --git a/nGenEx/ScrollWindow.cpp b/nGenEx/ScrollWindow.cpp
new file mode 100644
index 0000000..effd967
--- /dev/null
+++ b/nGenEx/ScrollWindow.cpp
@@ -0,0 +1,632 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ScrollWindow.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ScrollWindow ActiveWindow class
+*/
+
+#include "MemDebug.h"
+#include "ScrollWindow.h"
+#include "Button.h"
+#include "Bitmap.h"
+#include "FormWindow.h"
+#include "Video.h"
+#include "Font.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+
+
+DWORD GetRealTime();
+
+// +--------------------------------------------------------------------+
+
+static int old_cursor;
+
+// +--------------------------------------------------------------------+
+
+ScrollWindow::ScrollWindow(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid, DWORD s, ActiveWindow* paw)
+ : ActiveWindow(p->GetScreen(), ax, ay, aw, ah, aid, s, paw)
+{
+ captured = false;
+ dragging = false;
+ selecting = false;
+ scrolling = SCROLL_NONE;
+
+ leading = 0;
+ scroll_bar = SCROLL_AUTO;
+ dragdrop = false;
+ scroll_count = 0;
+ line_count = 0;
+ page_count = 0;
+ page_size = 1;
+ top_index = 0;
+ line_height = 0;
+ mouse_x = 0;
+ mouse_y = 0;
+
+ smooth_scroll = false;
+ smooth_offset = 0;
+
+ track_length = ah - 2*TRACK_START - THUMB_HEIGHT;
+ thumb_pos = TRACK_START;
+
+ char buf[32];
+ sprintf(buf, "ScrollWindow %d", id);
+ desc = buf;
+}
+
+ScrollWindow::ScrollWindow(Screen* s, int ax, int ay, int aw, int ah, DWORD aid, DWORD s1, ActiveWindow* paw)
+ : ActiveWindow(s, ax, ay, aw, ah, aid, s1, paw)
+{
+ captured = false;
+ dragging = false;
+ selecting = false;
+ scrolling = SCROLL_NONE;
+
+ leading = 0;
+ scroll_bar = SCROLL_AUTO;
+ dragdrop = false;
+ scroll_count = 0;
+ line_count = 0;
+ page_count = 0;
+ page_size = 1;
+ top_index = 0;
+ line_height = 0;
+ mouse_x = 0;
+ mouse_y = 0;
+
+ smooth_scroll = false;
+ smooth_offset = 0;
+
+ track_length = ah - 2*TRACK_START - THUMB_HEIGHT;
+ thumb_pos = TRACK_START;
+
+ char buf[32];
+ sprintf(buf, "ScrollWindow %d", id);
+ desc = buf;
+}
+
+ScrollWindow::~ScrollWindow()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+ScrollWindow::MoveTo(const Rect& r)
+{
+ ActiveWindow::MoveTo(r);
+
+ track_length = rect.h - 2*TRACK_START - THUMB_HEIGHT;
+ thumb_pos = TRACK_START;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ScrollWindow::Paint()
+{
+ if (transparent) {
+ DrawTransparent();
+ }
+
+ else {
+ ActiveWindow::Paint();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ScrollWindow::Draw()
+{
+ int x = 0;
+ int y = 0;
+ int w = rect.w;
+ int h = rect.h;
+
+ if (w < 1 || h < 1 || !shown)
+ return;
+
+ ActiveWindow::Draw();
+
+ if (line_height < 1)
+ line_height = GetFont()->Height();
+ page_size = h / (line_height + leading);
+
+ Rect ctrl_rect(x,y,w,h);
+ ctrl_rect.Deflate(BORDER_WIDTH, BORDER_WIDTH);
+
+ if (IsScrollVisible()) {
+ ctrl_rect.w -= SCROLL_TRACK;
+ }
+
+ DrawContent(ctrl_rect);
+ DrawScrollBar();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ScrollWindow::DrawTransparent()
+{
+ int x = 0;
+ int y = 0;
+ int w = rect.w;
+ int h = rect.h;
+
+ if (w < 1 || h < 1 || !shown)
+ return;
+
+ if (line_height < 1)
+ line_height = GetFont()->Height();
+ page_size = h / (line_height + leading);
+
+ Rect ctrl_rect(x,y,w,h);
+ ctrl_rect.Deflate(BORDER_WIDTH, BORDER_WIDTH);
+
+ if (IsScrollVisible()) {
+ ctrl_rect.w -= SCROLL_TRACK;
+ }
+
+ DrawTransparentContent(ctrl_rect);
+ DrawScrollBar();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ScrollWindow::DrawContent(const Rect& ctrl_rect)
+{
+ // override to do control-specific drawing
+}
+
+void
+ScrollWindow::DrawTransparentContent(const Rect& ctrl_rect)
+{
+ // override (if necessary) to do control-specific drawing
+ DrawContent(ctrl_rect);
+}
+
+void
+ScrollWindow::DrawScrollBar()
+{
+ // draw scroll bar if necessary:
+ if (IsScrollVisible()) {
+ Color save_color = back_color;
+ back_color = ShadeColor(back_color, 1.3);
+
+ // draw scroll track border:
+ DrawLine(rect.w-SCROLL_TRACK, BORDER_WIDTH, rect.w-SCROLL_TRACK, rect.h-BORDER_WIDTH, back_color);
+
+ // draw top button
+ Rect btn_rect(rect.w-SCROLL_WIDTH, BORDER_WIDTH, SCROLL_WIDTH-BORDER_WIDTH, SCROLL_HEIGHT);
+ FillRect(btn_rect, back_color);
+ DrawStyleRect(btn_rect, WIN_RAISED_FRAME);
+
+ // draw bottom button:
+ btn_rect.y = rect.h - (SCROLL_HEIGHT+BORDER_WIDTH);
+ FillRect(btn_rect, back_color);
+ DrawStyleRect(btn_rect, WIN_RAISED_FRAME);
+
+ // draw thumb:
+ btn_rect.y = thumb_pos;
+ btn_rect.h = btn_rect.w;
+ FillRect(btn_rect, back_color);
+ DrawStyleRect(btn_rect, WIN_RAISED_FRAME);
+
+ back_color = save_color;
+ }
+
+ if (scrolling && scroll_count)
+ Scroll(scrolling, scroll_count);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+ScrollWindow::IsScrollVisible()
+{
+ bool vis = false;
+
+ if (scroll_bar == SCROLL_ALWAYS ||
+ (scroll_bar == SCROLL_AUTO &&
+ line_count > page_size)) {
+ vis = true;
+ }
+
+ return vis;
+}
+
+int ScrollWindow::GetLineHeight()
+{
+ return line_height;
+}
+
+void ScrollWindow::SetLineHeight(int h)
+{
+ if (h >= 0)
+ line_height = h;
+}
+
+int ScrollWindow::GetLeading()
+{
+ return leading;
+}
+
+void ScrollWindow::SetLeading(int nNewValue)
+{
+ if (leading != nNewValue && nNewValue >= 0) {
+ leading = nNewValue;
+ }
+}
+
+int ScrollWindow::GetDragDrop()
+{
+ return dragdrop;
+}
+
+void ScrollWindow::SetDragDrop(int nNewValue)
+{
+ if (dragdrop != nNewValue && (nNewValue == 0 || nNewValue == 1)) {
+ dragdrop = nNewValue;
+ }
+}
+
+int ScrollWindow::GetScrollBarVisible()
+{
+ return scroll_bar;
+}
+
+void ScrollWindow::SetScrollBarVisible(int nNewValue)
+{
+ if (scroll_bar != nNewValue) {
+ scroll_bar = nNewValue;
+ }
+}
+
+bool ScrollWindow::GetSmoothScroll()
+{
+ return smooth_scroll;
+}
+
+void ScrollWindow::SetSmoothScroll(bool bNewValue)
+{
+ if (smooth_scroll != bNewValue) {
+ smooth_scroll = bNewValue;
+ smooth_offset = top_index;
+ }
+}
+
+bool ScrollWindow::CanScroll(int direction, int nlines)
+{
+ return false;
+}
+
+void ScrollWindow::EnsureVisible(int index)
+{
+ if (index < top_index)
+ ScrollTo(index);
+
+ else if (index > top_index+page_size)
+ ScrollTo(index-page_size);
+}
+
+void ScrollWindow::Scroll(int direction, int nlines)
+{
+ if (nlines) {
+ scrolling = direction;
+
+ if (direction == SCROLL_UP || direction == SCROLL_PAGE_UP) {
+ top_index--;
+
+ if (top_index < 0)
+ top_index = 0;
+
+ else
+ scroll_count = nlines-1;
+ }
+
+ else if (direction == SCROLL_DOWN || direction == SCROLL_PAGE_DOWN) {
+ top_index++;
+
+ if (top_index >= line_count)
+ top_index = line_count-1;
+
+ else
+ scroll_count = nlines-1;
+ }
+
+ smooth_offset = top_index;
+ thumb_pos = TRACK_START + (int) (track_length * (double) top_index/(line_count-1));
+
+ if (scroll_count < 1)
+ scrolling = SCROLL_NONE;
+ }
+}
+
+void ScrollWindow::SmoothScroll(int direction, double nlines)
+{
+ if (!smooth_scroll) {
+ Scroll(direction, (int) nlines);
+ return;
+ }
+
+ if (direction == SCROLL_UP || direction == SCROLL_PAGE_UP) {
+ smooth_offset -= nlines;
+
+ if (smooth_offset < 0)
+ smooth_offset = 0;
+ }
+
+ else if (direction == SCROLL_DOWN || direction == SCROLL_PAGE_DOWN) {
+ smooth_offset += nlines;
+
+ if (smooth_offset >= line_count)
+ smooth_offset = line_count-1;
+ }
+
+ top_index = (int) smooth_offset;
+ thumb_pos = TRACK_START + (int) (track_length * smooth_offset/(line_count-1));
+ scrolling = SCROLL_NONE;
+}
+
+void ScrollWindow::ScrollTo(int index)
+{
+ if (index >= 0 && index < line_count) {
+ top_index = index;
+ smooth_offset = index;
+
+ thumb_pos = TRACK_START + (int) (track_length * smooth_offset/(line_count-1));
+ }
+}
+
+int ScrollWindow::GetTopIndex()
+{
+ return top_index;
+}
+
+int ScrollWindow::GetPageCount()
+{
+ return line_count / GetPageSize();
+}
+
+int ScrollWindow::GetPageSize()
+{
+ return page_size;
+}
+
+int ScrollWindow::GetScrollTrack()
+{
+ return rect.w-SCROLL_TRACK;
+}
+
+int ScrollWindow::GetLineCount()
+{
+ return line_count;
+}
+
+// +--------------------------------------------------------------------+
+
+int ScrollWindow::OnMouseMove(int x, int y)
+{
+ bool dirty = false;
+
+ if (captured) {
+ ActiveWindow* test = GetCapture();
+
+ if (test != this) {
+ captured = false;
+ dirty = true;
+ }
+
+ else {
+ if (selecting && !dragging) {
+ if (dragdrop && (x < rect.x ||
+ x > rect.x+rect.w ||
+ y < rect.y ||
+ y > rect.y+rect.h)) {
+
+ dragging = true;
+ OnDragStart(x,y);
+ }
+ }
+
+ if (scrolling == SCROLL_THUMB) {
+ mouse_y = y - rect.y - TRACK_START;
+
+ int dest = (int) ((double) mouse_y/track_length * (line_count-1));
+ ScrollTo(dest);
+ dirty = true;
+ }
+ }
+ }
+
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int ScrollWindow::OnLButtonDown(int x, int y)
+{
+ if (!captured)
+ captured = SetCapture();
+
+ mouse_x = x - rect.x;
+ mouse_y = y - rect.y;
+
+ int x_scroll_bar = rect.w;
+
+ if (scroll_bar == SCROLL_ALWAYS ||
+ (scroll_bar == SCROLL_AUTO &&
+ line_count > page_size))
+ x_scroll_bar -= SCROLL_WIDTH;
+
+ if (mouse_x < x_scroll_bar) {
+ scrolling = SCROLL_NONE;
+ selecting = true;
+ }
+
+ else {
+ selecting = false;
+
+ if (mouse_y < TRACK_START) {
+ scrolling = SCROLL_UP;
+ Scroll(scrolling, 1);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+
+ else if (mouse_y > rect.h-TRACK_START) {
+ scrolling = SCROLL_DOWN;
+ if (top_index < line_count-1)
+ top_index++;
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+
+ else if (mouse_y < thumb_pos) {
+ scrolling = SCROLL_PAGE_UP;
+ Scroll(scrolling, page_size);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+
+ else if (mouse_y > thumb_pos+THUMB_HEIGHT) {
+ scrolling = SCROLL_PAGE_DOWN;
+ Scroll(scrolling, page_size);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+
+ else {
+ scrolling = SCROLL_THUMB;
+ }
+ }
+
+ return ActiveWindow::OnLButtonDown(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int ScrollWindow::OnLButtonUp(int x, int y)
+{
+ if (captured) {
+ mouse_x = x-rect.x;
+ mouse_y = y-rect.y;
+
+ if (dragging) {
+ if (mouse_x < 0 || mouse_x > rect.w || mouse_y < 0 || mouse_y > rect.h) {
+ FormWindow* parent_form = (FormWindow*) form;
+
+ if (parent_form) {
+ ActiveWindow* drop_target = parent_form->FindControl(x,y);
+
+ if (drop_target && drop_target->IsEnabled() && drop_target->IsShown())
+ drop_target->OnDragDrop(x,y,this);
+ }
+ }
+ }
+
+ ReleaseCapture();
+ captured = false;
+
+ Mouse::SetCursor((Mouse::CURSOR) old_cursor);
+ }
+
+ dragging = false;
+ selecting = false;
+
+ return ActiveWindow::OnLButtonUp(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int ScrollWindow::OnMouseWheel(int wheel)
+{
+ if (wheel > 0)
+ Scroll(SCROLL_UP, 1);
+
+ else if (wheel < 0)
+ Scroll(SCROLL_DOWN, 1);
+
+ if (GetLineCount() > 0) {
+ static double scroll_time = 0;
+
+ if (GetRealTime() - scroll_time > 0.5) {
+ scroll_time = GetRealTime();
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+ }
+
+ return ActiveWindow::OnMouseWheel(wheel);
+}
+
+// +--------------------------------------------------------------------+
+
+int ScrollWindow::OnClick()
+{
+ int fire_select = !scrolling;
+
+ if (scrolling == SCROLL_THUMB)
+ scrolling = SCROLL_NONE;
+
+ if (fire_select)
+ return ActiveWindow::OnSelect();
+ else
+ return ActiveWindow::OnClick();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int ScrollWindow::OnKeyDown(int vk, int flags)
+{
+ switch (vk) {
+ case VK_UP: Scroll(SCROLL_UP, 1);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ break;
+
+ case VK_DOWN: Scroll(SCROLL_DOWN, 1);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ break;
+
+ case VK_PRIOR: Scroll(SCROLL_UP, page_count);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ break;
+
+ case VK_NEXT: Scroll(SCROLL_DOWN, page_count);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ break;
+
+ case VK_HOME: EnsureVisible(0);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ break;
+
+ case VK_END: EnsureVisible(line_count-1);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ break;
+
+ default: break;
+ }
+
+ return ActiveWindow::OnKeyDown(vk, flags);
+}
+
+// +--------------------------------------------------------------------+
+
+int ScrollWindow::OnDragStart(int x, int y)
+{
+ old_cursor = Mouse::SetCursor(Mouse::DRAG);
+ return ActiveWindow::OnDragStart(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int ScrollWindow::OnDragDrop(int x, int y, ActiveWindow* source)
+{
+ return ActiveWindow::OnDragDrop(x,y,source);
+}
diff --git a/nGenEx/ScrollWindow.h b/nGenEx/ScrollWindow.h
new file mode 100644
index 0000000..21d44ea
--- /dev/null
+++ b/nGenEx/ScrollWindow.h
@@ -0,0 +1,132 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ScrollWindow.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ScrollWindow base class for List, Edit, and Rich Text controls
+*/
+
+#ifndef ScrollWindow_h
+#define ScrollWindow_h
+
+#include "Types.h"
+#include "ActiveWindow.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class ScrollWindow : public ActiveWindow
+{
+public:
+ enum POLICY { SCROLL_NEVER,
+ SCROLL_AUTO,
+ SCROLL_ALWAYS
+ };
+
+ enum SCROLL { SCROLL_NONE,
+ SCROLL_UP,
+ SCROLL_PAGE_UP,
+ SCROLL_DOWN,
+ SCROLL_PAGE_DOWN,
+ SCROLL_THUMB
+ };
+
+ enum MISC { BORDER_WIDTH = 2,
+ EXTRA_WIDTH = 4,
+ SCROLL_WIDTH = 16,
+ SCROLL_HEIGHT = 6,
+ SCROLL_TRACK = SCROLL_WIDTH + 1,
+ TRACK_START = BORDER_WIDTH + SCROLL_HEIGHT,
+ THUMB_HEIGHT = SCROLL_WIDTH,
+ HEADING_EXTRA = BORDER_WIDTH + EXTRA_WIDTH
+ };
+
+ ScrollWindow(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid, DWORD style=0, ActiveWindow* parent=0);
+ ScrollWindow(Screen* s, int ax, int ay, int aw, int ah, DWORD aid, DWORD style=0, ActiveWindow* parent=0);
+ virtual ~ScrollWindow();
+
+ // Operations:
+ virtual void Paint();
+ virtual void Draw();
+ virtual void DrawTransparent();
+ virtual void DrawContent(const Rect& ctrl_rect);
+ virtual void DrawTransparentContent(const Rect& ctrl_rect);
+ virtual void DrawScrollBar();
+ virtual void MoveTo(const Rect& r);
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnMouseWheel(int wheel);
+ virtual int OnClick();
+
+ virtual int OnKeyDown(int vk, int flags);
+
+ // pseudo-events:
+ virtual int OnDragStart(int x, int y);
+ virtual int OnDragDrop(int x, int y, ActiveWindow* source);
+
+ // Property accessors:
+ virtual int GetLineHeight();
+ virtual void SetLineHeight(int h);
+
+ virtual int GetLeading();
+ virtual void SetLeading(int nNewValue);
+ virtual int GetScrollBarVisible();
+ virtual void SetScrollBarVisible(int nNewValue);
+ virtual int GetDragDrop();
+ virtual void SetDragDrop(int nNewValue);
+ virtual bool GetSmoothScroll();
+ virtual void SetSmoothScroll(bool s);
+
+ virtual bool IsScrollVisible();
+ virtual bool CanScroll(int direction, int nlines=1);
+ virtual void EnsureVisible(int index);
+ virtual void Scroll(int direction, int nlines=1);
+ virtual void SmoothScroll(int direction, double nlines);
+ virtual void ScrollTo(int index);
+
+ // read-only:
+ virtual int GetTopIndex();
+ virtual int GetLineCount();
+ virtual int GetPageCount();
+ virtual int GetPageSize();
+ virtual int GetScrollTrack();
+
+ int IsDragging() const { return dragging; }
+ int IsSelecting() const { return selecting; }
+ int IsScrolling() const { return scrolling; }
+
+protected:
+ int captured;
+ int dragging;
+ int selecting;
+ int scrolling;
+ int scroll_count;
+ int mouse_x;
+ int mouse_y;
+ int track_length;
+ int thumb_pos;
+
+ int leading;
+ int scroll_bar;
+ int dragdrop;
+ int line_count;
+ int page_count;
+ int page_size;
+ int top_index;
+ int line_height;
+
+ bool smooth_scroll;
+ double smooth_offset;
+};
+
+#endif ScrollWindow_h
+
diff --git a/nGenEx/Sha1.cpp b/nGenEx/Sha1.cpp
new file mode 100644
index 0000000..bc27c56
--- /dev/null
+++ b/nGenEx/Sha1.cpp
@@ -0,0 +1,589 @@
+/*
+ * Sha1.cpp
+ *
+ * Copyright (C) 1998
+ * Paul E. Jones <paulej@acm.org>
+ * All Rights Reserved.
+ *
+ *****************************************************************************
+ * $Id: sha1.cpp,v 1.6 2001/03/20 06:54:54 paulej Exp $
+ *****************************************************************************
+ *
+ * Description:
+ * This class implements the Secure Hashing Standard as defined
+ * in FIPS PUB 180-1 published April 17, 1995.
+ *
+ * The Secure Hashing Standard, which uses the Secure Hashing
+ * Algorithm (SHA), produces a 160-bit message digest for a
+ * given data stream. In theory, it is highly improbable that
+ * two messages will produce the same message digest. Therefore,
+ * this algorithm can serve as a means of providing a "fingerprint"
+ * for a message.
+ *
+ * Portability Issues:
+ * SHA-1 is defined in terms of 32-bit "words". This code was
+ * written with the expectation that the processor has at least
+ * a 32-bit machine word size. If the machine word size is larger,
+ * the code should still function properly. One caveat to that
+ * is that the input functions taking characters and character arrays
+ * assume that only 8 bits of information are stored in each character.
+ *
+ * Caveats:
+ * SHA-1 is designed to work with messages less than 2^64 bits long.
+ * Although SHA-1 allows a message digest to be generated for
+ * messages of any number of bits less than 2^64, this implementation
+ * only works with messages with a length that is a multiple of 8
+ * bits.
+ *
+ */
+
+
+#include "Sha1.h"
+
+/*
+ * SHA1
+ *
+ * Description:
+ * This is the constructor for the sha1 class.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+SHA1::SHA1()
+{
+ Reset();
+}
+
+/*
+ * ~SHA1
+ *
+ * Description:
+ * This is the destructor for the sha1 class
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+SHA1::~SHA1()
+{
+ // The destructor does nothing
+}
+
+/*
+ * Reset
+ *
+ * Description:
+ * This function will initialize the sha1 class member variables
+ * in preparation for computing a new message digest.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+void SHA1::Reset()
+{
+ Length_Low = 0;
+ Length_High = 0;
+ Message_Block_Index = 0;
+
+ H[0] = 0x67452301;
+ H[1] = 0xEFCDAB89;
+ H[2] = 0x98BADCFE;
+ H[3] = 0x10325476;
+ H[4] = 0xC3D2E1F0;
+
+ Computed = false;
+ Corrupted = false;
+}
+
+/*
+ * Result
+ *
+ * Description:
+ * This function will return the 160-bit message digest into the
+ * array provided.
+ *
+ * Parameters:
+ * message_digest_array: [out]
+ * This is an array of five unsigned integers which will be filled
+ * with the message digest that has been computed.
+ *
+ * Returns:
+ * True if successful, false if it failed.
+ *
+ * Comments:
+ *
+ */
+bool SHA1::Result(unsigned *message_digest_array)
+{
+ int i; // Counter
+
+ if (Corrupted)
+ {
+ return false;
+ }
+
+ if (!Computed)
+ {
+ PadMessage();
+ Computed = true;
+ }
+
+ for(i = 0; i < 5; i++)
+ {
+ message_digest_array[i] = H[i];
+ }
+
+ return true;
+}
+
+/*
+ * Input
+ *
+ * Description:
+ * This function accepts an array of octets as the next portion of
+ * the message.
+ *
+ * Parameters:
+ * message_array: [in]
+ * An array of characters representing the next portion of the
+ * message.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+void SHA1::Input( const unsigned char *message_array,
+ unsigned length)
+{
+ if (!length)
+ {
+ return;
+ }
+
+ if (Computed || Corrupted)
+ {
+ Corrupted = true;
+ return;
+ }
+
+ while(length-- && !Corrupted)
+ {
+ Message_Block[Message_Block_Index++] = (*message_array & 0xFF);
+
+ Length_Low += 8;
+ Length_Low &= 0xFFFFFFFF; // Force it to 32 bits
+ if (Length_Low == 0)
+ {
+ Length_High++;
+ Length_High &= 0xFFFFFFFF; // Force it to 32 bits
+ if (Length_High == 0)
+ {
+ Corrupted = true; // Message is too long
+ }
+ }
+
+ if (Message_Block_Index == 64)
+ {
+ ProcessMessageBlock();
+ }
+
+ message_array++;
+ }
+}
+
+/*
+ * Input
+ *
+ * Description:
+ * This function accepts an array of octets as the next portion of
+ * the message.
+ *
+ * Parameters:
+ * message_array: [in]
+ * An array of characters representing the next portion of the
+ * message.
+ * length: [in]
+ * The length of the message_array
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+void SHA1::Input( const char *message_array,
+ unsigned length)
+{
+ Input((unsigned char *) message_array, length);
+}
+
+/*
+ * Input
+ *
+ * Description:
+ * This function accepts a single octets as the next message element.
+ *
+ * Parameters:
+ * message_element: [in]
+ * The next octet in the message.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+void SHA1::Input(unsigned char message_element)
+{
+ Input(&message_element, 1);
+}
+
+/*
+ * Input
+ *
+ * Description:
+ * This function accepts a single octet as the next message element.
+ *
+ * Parameters:
+ * message_element: [in]
+ * The next octet in the message.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+void SHA1::Input(char message_element)
+{
+ Input((unsigned char *) &message_element, 1);
+}
+
+/*
+ * operator<<
+ *
+ * Description:
+ * This operator makes it convenient to provide character strings to
+ * the SHA1 object for processing.
+ *
+ * Parameters:
+ * message_array: [in]
+ * The character array to take as input.
+ *
+ * Returns:
+ * A reference to the SHA1 object.
+ *
+ * Comments:
+ * Each character is assumed to hold 8 bits of information.
+ *
+ */
+SHA1& SHA1::operator<<(const char *message_array)
+{
+ const char *p = message_array;
+
+ while(*p)
+ {
+ Input(*p);
+ p++;
+ }
+
+ return *this;
+}
+
+/*
+ * operator<<
+ *
+ * Description:
+ * This operator makes it convenient to provide character strings to
+ * the SHA1 object for processing.
+ *
+ * Parameters:
+ * message_array: [in]
+ * The character array to take as input.
+ *
+ * Returns:
+ * A reference to the SHA1 object.
+ *
+ * Comments:
+ * Each character is assumed to hold 8 bits of information.
+ *
+ */
+SHA1& SHA1::operator<<(const unsigned char *message_array)
+{
+ const unsigned char *p = message_array;
+
+ while(*p)
+ {
+ Input(*p);
+ p++;
+ }
+
+ return *this;
+}
+
+/*
+ * operator<<
+ *
+ * Description:
+ * This function provides the next octet in the message.
+ *
+ * Parameters:
+ * message_element: [in]
+ * The next octet in the message
+ *
+ * Returns:
+ * A reference to the SHA1 object.
+ *
+ * Comments:
+ * The character is assumed to hold 8 bits of information.
+ *
+ */
+SHA1& SHA1::operator<<(const char message_element)
+{
+ Input((unsigned char *) &message_element, 1);
+
+ return *this;
+}
+
+/*
+ * operator<<
+ *
+ * Description:
+ * This function provides the next octet in the message.
+ *
+ * Parameters:
+ * message_element: [in]
+ * The next octet in the message
+ *
+ * Returns:
+ * A reference to the SHA1 object.
+ *
+ * Comments:
+ * The character is assumed to hold 8 bits of information.
+ *
+ */
+SHA1& SHA1::operator<<(const unsigned char message_element)
+{
+ Input(&message_element, 1);
+
+ return *this;
+}
+
+/*
+ * ProcessMessageBlock
+ *
+ * Description:
+ * This function will process the next 512 bits of the message
+ * stored in the Message_Block array.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ * Many of the variable names in this function, especially the single
+ * character names, were used because those were the names used
+ * in the publication.
+ *
+ */
+void SHA1::ProcessMessageBlock()
+{
+ const unsigned K[] = { // Constants defined for SHA-1
+ 0x5A827999,
+ 0x6ED9EBA1,
+ 0x8F1BBCDC,
+ 0xCA62C1D6
+ };
+ int t; // Loop counter
+ unsigned temp; // Temporary word value
+ unsigned W[80]; // Word sequence
+ unsigned A, B, C, D, E; // Word buffers
+
+ /*
+ * Initialize the first 16 words in the array W
+ */
+ for(t = 0; t < 16; t++)
+ {
+ W[t] = Message_Block[t * 4] << 24;
+ W[t] |= Message_Block[t * 4 + 1] << 16;
+ W[t] |= Message_Block[t * 4 + 2] << 8;
+ W[t] |= Message_Block[t * 4 + 3];
+ }
+
+ for(t = 16; t < 80; t++)
+ {
+ W[t] = CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+ }
+
+ A = H[0];
+ B = H[1];
+ C = H[2];
+ D = H[3];
+ E = H[4];
+
+ for(t = 0; t < 20; t++)
+ {
+ temp = CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ C = CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for(t = 20; t < 40; t++)
+ {
+ temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ C = CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for(t = 40; t < 60; t++)
+ {
+ temp = CircularShift(5,A) +
+ ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ C = CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for(t = 60; t < 80; t++)
+ {
+ temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ C = CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ H[0] = (H[0] + A) & 0xFFFFFFFF;
+ H[1] = (H[1] + B) & 0xFFFFFFFF;
+ H[2] = (H[2] + C) & 0xFFFFFFFF;
+ H[3] = (H[3] + D) & 0xFFFFFFFF;
+ H[4] = (H[4] + E) & 0xFFFFFFFF;
+
+ Message_Block_Index = 0;
+}
+
+/*
+ * PadMessage
+ *
+ * Description:
+ * According to the standard, the message must be padded to an even
+ * 512 bits. The first padding bit must be a '1'. The last 64 bits
+ * represent the length of the original message. All bits in between
+ * should be 0. This function will pad the message according to those
+ * rules by filling the message_block array accordingly. It will also
+ * call ProcessMessageBlock() appropriately. When it returns, it
+ * can be assumed that the message digest has been computed.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+void SHA1::PadMessage()
+{
+ /*
+ * Check to see if the current message block is too small to hold
+ * the initial padding bits and length. If so, we will pad the
+ * block, process it, and then continue padding into a second block.
+ */
+ if (Message_Block_Index > 55)
+ {
+ Message_Block[Message_Block_Index++] = 0x80;
+ while(Message_Block_Index < 64)
+ {
+ Message_Block[Message_Block_Index++] = 0;
+ }
+
+ ProcessMessageBlock();
+
+ while(Message_Block_Index < 56)
+ {
+ Message_Block[Message_Block_Index++] = 0;
+ }
+ }
+ else
+ {
+ Message_Block[Message_Block_Index++] = 0x80;
+ while(Message_Block_Index < 56)
+ {
+ Message_Block[Message_Block_Index++] = 0;
+ }
+
+ }
+
+ /*
+ * Store the message length as the last 8 octets
+ */
+ Message_Block[56] = (Length_High >> 24) & 0xFF;
+ Message_Block[57] = (Length_High >> 16) & 0xFF;
+ Message_Block[58] = (Length_High >> 8) & 0xFF;
+ Message_Block[59] = (Length_High) & 0xFF;
+ Message_Block[60] = (Length_Low >> 24) & 0xFF;
+ Message_Block[61] = (Length_Low >> 16) & 0xFF;
+ Message_Block[62] = (Length_Low >> 8) & 0xFF;
+ Message_Block[63] = (Length_Low) & 0xFF;
+
+ ProcessMessageBlock();
+}
+
+
+/*
+ * CircularShift
+ *
+ * Description:
+ * This member function will perform a circular shifting operation.
+ *
+ * Parameters:
+ * bits: [in]
+ * The number of bits to shift (1-31)
+ * word: [in]
+ * The value to shift (assumes a 32-bit integer)
+ *
+ * Returns:
+ * The shifted value.
+ *
+ * Comments:
+ *
+ */
+unsigned SHA1::CircularShift(int bits, unsigned word)
+{
+ return ((word << bits) & 0xFFFFFFFF) | ((word & 0xFFFFFFFF) >> (32-bits));
+}
diff --git a/nGenEx/Sha1.h b/nGenEx/Sha1.h
new file mode 100644
index 0000000..531a543
--- /dev/null
+++ b/nGenEx/Sha1.h
@@ -0,0 +1,89 @@
+/*
+ * Sha1.h
+ *
+ * Copyright (C) 1998
+ * Paul E. Jones <paulej@acm.org>
+ * All Rights Reserved.
+ *
+ *****************************************************************************
+ * $Id: sha1.h,v 1.4 2001/03/20 06:25:06 paulej Exp $
+ *****************************************************************************
+ *
+ * Description:
+ * This class implements the Secure Hashing Standard as defined
+ * in FIPS PUB 180-1 published April 17, 1995.
+ *
+ * Many of the variable names in this class, especially the single
+ * character names, were used because those were the names used
+ * in the publication.
+ *
+ * Please read the file sha1.cpp for more information.
+ *
+ */
+
+#ifndef _SHA1_H_
+#define _SHA1_H_
+
+class SHA1
+{
+
+ public:
+
+ SHA1();
+ virtual ~SHA1();
+
+ /*
+ * Re-initialize the class
+ */
+ void Reset();
+
+ /*
+ * Returns the message digest
+ */
+ bool Result(unsigned *message_digest_array);
+
+ /*
+ * Provide input to SHA1
+ */
+ void Input( const unsigned char *message_array,
+ unsigned length);
+ void Input( const char *message_array,
+ unsigned length);
+ void Input(unsigned char message_element);
+ void Input(char message_element);
+ SHA1& operator<<(const char *message_array);
+ SHA1& operator<<(const unsigned char *message_array);
+ SHA1& operator<<(const char message_element);
+ SHA1& operator<<(const unsigned char message_element);
+
+ private:
+
+ /*
+ * Process the next 512 bits of the message
+ */
+ void ProcessMessageBlock();
+
+ /*
+ * Pads the current message block to 512 bits
+ */
+ void PadMessage();
+
+ /*
+ * Performs a circular left shift operation
+ */
+ inline unsigned CircularShift(int bits, unsigned word);
+
+ unsigned H[5]; // Message digest buffers
+
+ unsigned Length_Low; // Message length in bits
+ unsigned Length_High; // Message length in bits
+
+ unsigned char Message_Block[64]; // 512-bit message blocks
+ int Message_Block_Index; // Index into message block array
+
+ bool Computed; // Is the digest computed?
+ bool Corrupted; // Is the message digest corruped?
+
+};
+
+#endif
diff --git a/nGenEx/Shadow.cpp b/nGenEx/Shadow.cpp
new file mode 100644
index 0000000..3038062
--- /dev/null
+++ b/nGenEx/Shadow.cpp
@@ -0,0 +1,176 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Shadow.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Dynamic Stencil Shadow Volumes
+*/
+
+#include "MemDebug.h"
+#include "Shadow.h"
+#include "Light.h"
+#include "Solid.h"
+#include "Scene.h"
+#include "Video.h"
+
+static bool visible_shadow_volumes = false;
+
+// +--------------------------------------------------------------------+
+
+Shadow::Shadow(Solid* s)
+ : verts(0), nverts(0), max_verts(0), edges(0), num_edges(0), enabled(true)
+{
+ solid = s;
+
+ if (solid && solid->GetModel()) {
+ Model* model = solid->GetModel();
+ int npolys = model->NumPolys();
+
+ max_verts = model->NumVerts() * 4;
+ verts = new(__FILE__,__LINE__) Vec3[max_verts];
+ edges = new(__FILE__,__LINE__) WORD[npolys * 6];
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Shadow::~Shadow()
+{
+ if (verts) delete [] verts;
+ if (edges) delete [] edges;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Shadow::Reset()
+{
+ num_edges = 0;
+ nverts = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Shadow::Render(Video* video)
+{
+ if (enabled)
+ video->DrawShadow(solid, nverts, verts, visible_shadow_volumes);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Shadow::Update(Light* light)
+{
+ Reset();
+
+ if (!light || !solid || !solid->GetModel() || !edges) return;
+
+ Vec3 lpos = light->Location();
+ bool directional = light->Type() == Light::LIGHT_DIRECTIONAL;
+ Model* model = solid->GetModel();
+
+ ListIter<Surface> iter = model->GetSurfaces();
+ while (++iter) {
+ Surface* s = iter.value();
+
+ // transform light location into surface object space
+ Matrix xform(solid->Orientation()); // XXX should be: (s->GetOrientation());
+
+ Vec3 tmp = light->Location();
+
+ if (!directional)
+ tmp -= (solid->Location() + s->GetOffset());
+
+ lpos.x = tmp * Vec3(xform(0,0), xform(0,1), xform(0,2));
+ lpos.y = tmp * Vec3(xform(1,0), xform(1,1), xform(1,2));
+ lpos.z = tmp * Vec3(xform(2,0), xform(2,1), xform(2,2));
+
+ // compute the silohuette for the mesh with respect to the light:
+
+ for (int i = 0; i < s->NumPolys(); i++) {
+ Poly* p = s->GetPolys() + i;
+
+ // skip polys with non-shadowing materials:
+ if (p->material && !p->material->shadow)
+ continue;
+
+ // if this poly faces the light:
+ if (p->plane.normal * lpos > 0) {
+ for (int n = 0; n < p->nverts; n++) {
+ if (n < p->nverts-1)
+ AddEdge(p->vlocs[n], p->vlocs[n+1]);
+ else
+ AddEdge(p->vlocs[n], p->vlocs[0]);
+ }
+ }
+ }
+
+ // extrude the silohuette away from the light source
+ // to create the shadow volume:
+
+ Vec3 extent = lpos * -1;
+ extent.Normalize();
+ extent *= 50.0e3f; //solid->Radius() * 2.1f;
+
+ for (i = 0; i < (int) num_edges; i++) {
+ if (nverts+6 <= max_verts) {
+ Vec3 v1 = s->GetVLoc()[edges[2*i+0]];
+ Vec3 v2 = s->GetVLoc()[edges[2*i+1]];
+ Vec3 v3 = v1 + extent;
+ Vec3 v4 = v2 + extent;
+
+ verts[nverts++] = v1;
+ verts[nverts++] = v2;
+ verts[nverts++] = v3;
+
+ verts[nverts++] = v2;
+ verts[nverts++] = v4;
+ verts[nverts++] = v3;
+ }
+ }
+ }
+}
+
+void
+Shadow::AddEdge(WORD v1, WORD v2)
+{
+ // Remove interior edges (which appear in the list twice)
+ for (DWORD i = 0; i < num_edges; i++) {
+ if ((edges[2*i+0] == v1 && edges[2*i+1] == v2) ||
+ (edges[2*i+0] == v2 && edges[2*i+1] == v1))
+ {
+ if (num_edges > 1) {
+ edges[2*i+0] = edges[2*(num_edges-1)+0];
+ edges[2*i+1] = edges[2*(num_edges-1)+1];
+ }
+
+ num_edges--;
+ return;
+ }
+ }
+
+ edges[2*num_edges+0] = v1;
+ edges[2*num_edges+1] = v2;
+
+ num_edges++;
+}
+
+bool
+Shadow::GetVisibleShadowVolumes()
+{
+ return visible_shadow_volumes;
+}
+
+void
+Shadow::SetVisibleShadowVolumes(bool vis)
+{
+ visible_shadow_volumes = vis;
+}
diff --git a/nGenEx/Shadow.h b/nGenEx/Shadow.h
new file mode 100644
index 0000000..a039337
--- /dev/null
+++ b/nGenEx/Shadow.h
@@ -0,0 +1,70 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Shadow.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Dynamic Stencil Shadow Volumes
+*/
+
+#ifndef Shadow_h
+#define Shadow_h
+
+#include "Geometry.h"
+#include "Color.h"
+
+// +--------------------------------------------------------------------+
+
+#define Shadow_DESTROY(x) if (x) { x->Destroy(); x = 0; }
+
+// +--------------------------------------------------------------------+
+
+class Light;
+class Scene;
+class Solid;
+class Video;
+
+// +--------------------------------------------------------------------+
+
+class Shadow
+{
+public:
+ static const char* TYPENAME() { return "Shadow"; }
+
+ Shadow(Solid* solid);
+ virtual ~Shadow();
+
+ int operator == (const Shadow& s) const { return this == &s; }
+
+ // operations
+ void Render(Video* video);
+ void Update(Light* light);
+ void AddEdge(WORD v1, WORD v2);
+ void Reset();
+
+ bool IsEnabled() const { return enabled; }
+ void SetEnabled(bool e) { enabled = e; }
+
+ static void SetVisibleShadowVolumes(bool vis);
+ static bool GetVisibleShadowVolumes();
+
+protected:
+ Solid* solid;
+ Vec3* verts;
+ int nverts;
+ int max_verts;
+ bool enabled;
+
+ WORD* edges;
+ DWORD num_edges;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Shadow_h
+
diff --git a/nGenEx/Skin.cpp b/nGenEx/Skin.cpp
new file mode 100644
index 0000000..f409501
--- /dev/null
+++ b/nGenEx/Skin.cpp
@@ -0,0 +1,175 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Skin.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Classes for rendering solid meshes of polygons
+*/
+
+#include "MemDebug.h"
+#include "Skin.h"
+#include "Solid.h"
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+Skin::Skin(const char* n)
+{
+ if (n && *n) {
+ strncpy(name, n, NAMELEN);
+ name[NAMELEN-1] = 0;
+ }
+
+ else {
+ ZeroMemory(name, NAMELEN);
+ }
+
+ ZeroMemory(path, 256);
+}
+
+// +--------------------------------------------------------------------+
+
+Skin::~Skin()
+{
+ cells.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Skin::SetName(const char* n)
+{
+ if (n && *n) {
+ strncpy(name, n, NAMELEN);
+ name[NAMELEN-1] = 0;
+ }
+}
+
+void
+Skin::SetPath(const char* n)
+{
+ if (n && *n) {
+ strncpy(path, n, 256);
+ path[255] = 0;
+ }
+
+ else {
+ ZeroMemory(path, 256);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Skin::AddMaterial(const Material* mtl)
+{
+ if (!mtl) return;
+
+ bool found = false;
+
+ ListIter<SkinCell> iter = cells;
+ while (++iter && !found) {
+ SkinCell* s = iter.value();
+
+ if (s->skin && !strcmp(s->skin->name, mtl->name)) {
+ s->skin = mtl;
+ found = true;
+ }
+ }
+
+ if (!found) {
+ SkinCell* s = new(__FILE__,__LINE__) SkinCell(mtl);
+ cells.append(s);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Skin::ApplyTo(Model* model) const
+{
+ if (model) {
+ for (int i = 0; i < cells.size(); i++) {
+ SkinCell* s = cells[i];
+
+ if (s->skin) {
+ s->orig = model->ReplaceMaterial(s->skin);
+ }
+ }
+ }
+}
+
+void
+Skin::Restore(Model* model) const
+{
+ if (model) {
+ for (int i = 0; i < cells.size(); i++) {
+ SkinCell* s = cells[i];
+
+ if (s->orig) {
+ model->ReplaceMaterial(s->orig);
+ s->orig = 0;
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+SkinCell::SkinCell(const Material* mtl)
+ : skin(mtl), orig(0)
+{
+}
+
+SkinCell::~SkinCell()
+{
+ delete skin;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+SkinCell::operator == (const SkinCell& other) const
+{
+ if (skin == other.skin)
+ return true;
+
+ if (skin && other.skin)
+ return !strcmp(skin->name, other.skin->name);
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+SkinCell::Name() const
+{
+ if (skin)
+ return skin->name;
+
+ return "Invalid Skin Cell";
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SkinCell::SetSkin(const Material* mtl)
+{
+ skin = mtl;
+}
+
+void
+SkinCell::SetOrig(const Material* mtl)
+{
+ orig = mtl;
+} \ No newline at end of file
diff --git a/nGenEx/Skin.h b/nGenEx/Skin.h
new file mode 100644
index 0000000..888c76b
--- /dev/null
+++ b/nGenEx/Skin.h
@@ -0,0 +1,92 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Skin.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Classes for managing run-time selectable skins on solid objects
+*/
+
+#ifndef Skin_h
+#define Skin_h
+
+#include "Polygon.h"
+#include "Graphic.h"
+#include "Video.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Solid;
+class Model;
+class Surface;
+class Segment;
+
+class Skin;
+class SkinCell;
+
+// +--------------------------------------------------------------------+
+
+class Skin
+{
+public:
+ static const char* TYPENAME() { return "Skin"; }
+ enum { NAMELEN=64 };
+
+ Skin(const char* name = 0);
+ virtual ~Skin();
+
+ // operations
+ void ApplyTo(Model* model) const;
+ void Restore(Model* model) const;
+
+ // accessors / mutators
+ const char* Name() const { return name; }
+ const char* Path() const { return path; }
+ int NumCells() const { return cells.size(); }
+
+ void SetName(const char* n);
+ void SetPath(const char* n);
+ void AddMaterial(const Material* mtl);
+
+protected:
+ char name[NAMELEN];
+ char path[256];
+ List<SkinCell> cells;
+};
+
+// +--------------------------------------------------------------------+
+
+class SkinCell
+{
+ friend class Skin;
+
+public:
+ static const char* TYPENAME() { return "SkinCell"; }
+
+ SkinCell(const Material* mtl=0);
+ ~SkinCell();
+
+ int operator == (const SkinCell& other) const;
+
+ const char* Name() const;
+ const Material* Skin() const { return skin; }
+ const Material* Orig() const { return orig; }
+
+ void SetSkin(const Material* mtl);
+ void SetOrig(const Material* mtl);
+
+private:
+ const Material* skin;
+ const Material* orig;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Skin_h
+
diff --git a/nGenEx/Slider.cpp b/nGenEx/Slider.cpp
new file mode 100644
index 0000000..f39254e
--- /dev/null
+++ b/nGenEx/Slider.cpp
@@ -0,0 +1,557 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Slider.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Slider/Gauge ActiveWindow class
+*/
+
+#include "MemDebug.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Font.h"
+
+// +--------------------------------------------------------------------+
+
+Slider::Slider(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid)
+ : ActiveWindow(p->GetScreen(), ax, ay, aw, ah, aid, 0, p)
+{
+ captured = false;
+ dragging = false;
+
+ border_color = Color::Black;
+ fill_color = Color::DarkBlue;
+
+ active = true;
+ border = true;
+ num_leds = 1;
+ orientation = 0;
+
+ range_min = 0;
+ range_max = 100;
+ step_size = 10;
+ show_thumb = 0;
+ thumb_size = -1;
+
+ nvalues = 1;
+ ZeroMemory(value, sizeof(value));
+
+ marker[0] = -1;
+ marker[1] = -1;
+
+ char buf[32];
+ sprintf(buf, "Slider %d", id);
+ desc = buf;
+}
+
+Slider::Slider(Screen* s, int ax, int ay, int aw, int ah, DWORD aid)
+ : ActiveWindow(s, ax, ay, aw, ah, aid)
+{
+ captured = false;
+ dragging = false;
+
+ border_color = Color::Black;
+ fill_color = Color::DarkBlue;
+
+ active = true;
+ border = true;
+ num_leds = 1;
+ orientation = 0;
+
+ range_min = 0;
+ range_max = 100;
+ step_size = 10;
+ show_thumb = 0;
+ thumb_size = -1;
+
+ nvalues = 1;
+ ZeroMemory(value, sizeof(value));
+
+ marker[0] = -1;
+ marker[1] = -1;
+
+ char buf[32];
+ sprintf(buf, "Slider %d", id);
+ desc = buf;
+}
+
+Slider::~Slider()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Slider::Draw()
+{
+ int x = 0;
+ int y = 0;
+ int w = rect.w;
+ int h = rect.h;
+
+ if (w < 1 || h < 1 || !shown)
+ return;
+
+ Rect ctrl_rect(x,y,w,h);
+
+ // draw the border:
+ if (border) {
+ Color oc = ShadeColor(border_color, 1);
+ DrawRect(0,0,w-1,h-1,oc);
+ DrawRect(1,1,w-2,h-2,oc);
+
+ ctrl_rect.Deflate(2,2);
+ }
+
+ // draw the bevel:
+ FillRect(ctrl_rect, back_color);
+ DrawStyleRect(ctrl_rect, WIN_SUNK_FRAME);
+
+ // HORIZONTAL
+ if (orientation == 0) {
+ // draw the leds:
+ int led_width = ((w - 6) / (num_leds)) - 1;
+ int led_height = ((h - 5) / (nvalues)) - 1;
+
+ if (nvalues < 2) {
+ int fill_width = (int) ((double)(w-4) * FractionalValue());
+ int num_lit = fill_width / (led_width+1);
+
+ Color fc = ShadeColor(fill_color, 1);
+
+ if (num_leds == 1) {
+ FillRect(2,2,2+fill_width,h-3,fc);
+ }
+ else {
+ int x0 = 2;
+
+ for (int i = 0; i < num_lit; i++) {
+ FillRect(x0,2,x0+led_width,h-3,fc);
+ x0 += led_width + 1;
+ }
+ }
+
+ // draw the thumb:
+ if (thumb_size) {
+ if (thumb_size < 0) thumb_size = h;
+
+ thumb_pos = 2+fill_width;
+
+ Rect thumb_rect(thumb_pos-thumb_size/2, 0, thumb_size, h);
+
+ if (thumb_rect.x < 0)
+ thumb_rect.x = 0;
+
+ else if (thumb_rect.x > w-thumb_size)
+ thumb_rect.x = w-thumb_size;
+
+ if (show_thumb) {
+ FillRect(thumb_rect, back_color);
+ DrawStyleRect(thumb_rect, WIN_RAISED_FRAME);
+ }
+ }
+ }
+
+ else {
+ Color fc = ShadeColor(fill_color, 1);
+
+ int y0 = 3;
+
+ for (int i = 0; i < nvalues; i++) {
+ int fill_width = (int) ((double)(w-6) * FractionalValue(i));
+ FillRect(3,y0,3+fill_width,y0+led_height,fc);
+
+ y0 += led_height+1;
+ }
+ }
+
+ // draw the markers:
+ if (marker[0] >= 0) {
+ int m = marker[0];
+ Color c = ShadeColor(base_color, 1.6); // full highlight
+ DrawLine(m-3, 1, m+4, 1, c);
+ c = ShadeColor(base_color, 1.3); // soft highlight
+ DrawLine(m-3, 2, m-3, 4, c);
+ DrawLine(m-2, 4, m-2, 6, c);
+ DrawLine(m-1, 6, m-1, 8, c);
+ DrawLine(m , 8, m , 10, c);
+ c = base_color; // body
+ DrawLine(m-2, 2, m-2, 4, c);
+ DrawLine(m-1, 2, m-1, 6, c);
+ DrawLine(m , 2, m , 8, c);
+ DrawLine(m+1, 2, m+1, 6, c);
+ DrawLine(m+2, 2, m+2, 4, c);
+ c = ShadeColor(base_color, 0.5); // shadow
+ DrawLine(m+1, 6, m+1, 8, c);
+ DrawLine(m+2, 4, m+2, 6, c);
+ DrawLine(m+3, 2, m+3, 4, c);
+ }
+
+ if (marker[1] >= 0) {
+ int m = marker[0];
+ Color c = ShadeColor(base_color, 0.5); // shadow
+ DrawLine(m-3, h-2, m+4, h-2, c);
+ DrawLine(m+1, h-6, m+1, h-8, c);
+ DrawLine(m+2, h-4, m+2, h-6, c);
+ DrawLine(m+3, h-2, m+3, h-4, c);
+ c = ShadeColor(base_color, 1.3); // soft highlight
+ DrawLine(m-3, h-2, m-3, h-4, c);
+ DrawLine(m-2, h-4, m-2, h-6, c);
+ DrawLine(m-1, h-6, m-1, h-8, c);
+ DrawLine(m , h-8, m , h-10, c);
+ c = base_color; // body
+ DrawLine(m-2, h-2, m-2, h-4, c);
+ DrawLine(m-1, h-2, m-1, h-6, c);
+ DrawLine(m , h-2, m , h-8, c);
+ DrawLine(m+1, h-2, m+1, h-6, c);
+ DrawLine(m+2, h-2, m+2, h-4, c);
+ }
+ }
+
+ // VERTICAL
+ else {
+ // draw the leds:
+ int led_width = ((w - 5) / (nvalues)) - 1;
+
+ if (num_leds > 1) {
+ }
+ else {
+ if (nvalues < 2) {
+ int led_width = w - 4;
+ int led_height = (int) ((double)(h-4) * FractionalValue());
+
+ Color fc = ShadeColor(fill_color, 1);
+ FillRect(2, h-2-led_height, 2+led_width, h-2, fc);
+
+ // draw the thumb:
+ if (thumb_size) {
+ if (thumb_size < 0) thumb_size = w;
+
+ thumb_pos = h-2-led_height;
+
+ Rect thumb_rect(0, thumb_pos-(thumb_size/2), w, thumb_size);
+
+ if (thumb_rect.y < 0)
+ thumb_rect.y = 0;
+
+ else if (thumb_rect.y > h-thumb_size)
+ thumb_rect.y = h-thumb_size;
+
+ if (show_thumb) {
+ FillRect(thumb_rect, back_color);
+ DrawStyleRect(thumb_rect, WIN_RAISED_FRAME);
+ }
+ }
+ }
+
+ else {
+ Color fc = ShadeColor(fill_color, 1);
+
+ int x0 = 3;
+
+ for (int i = 0; i < nvalues; i++) {
+ int led_height = (int) ((double)(h-6) * FractionalValue(i));
+ FillRect(x0,h-3-led_height,x0+led_width,h-3,fc);
+
+ x0 += led_width+1;
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool Slider::GetActive()
+{
+ return active;
+}
+
+void Slider::SetActive(bool bNewValue)
+{
+ active = bNewValue;
+}
+
+bool Slider::GetBorder()
+{
+ return border;
+}
+
+void Slider::SetBorder(bool bNewValue)
+{
+ border = bNewValue;
+}
+
+Color Slider::GetBorderColor()
+{
+ return border_color;
+}
+
+void Slider::SetBorderColor(Color newValue)
+{
+ border_color = newValue;
+}
+
+Color Slider::GetFillColor()
+{
+ return fill_color;
+}
+
+void Slider::SetFillColor(Color cNewValue)
+{
+ fill_color = cNewValue;
+}
+
+int Slider::GetNumLeds()
+{
+ return num_leds;
+}
+
+void Slider::SetNumLeds(int nNewValue)
+{
+ if (nNewValue >= 0) {
+ num_leds = nNewValue;
+ }
+}
+
+int Slider::GetOrientation()
+{
+ return orientation;
+}
+
+void Slider::SetOrientation(int nNewValue)
+{
+ if (nNewValue == 0 || nNewValue == 1) {
+ orientation = nNewValue;
+ }
+}
+
+int Slider::GetRangeMin()
+{
+ return range_min;
+}
+
+void Slider::SetRangeMin(int nNewValue)
+{
+ range_min = nNewValue;
+}
+
+int Slider::GetRangeMax()
+{
+ return range_max;
+}
+
+void Slider::SetRangeMax(int nNewValue)
+{
+ range_max = nNewValue;
+}
+
+int Slider::GetStepSize()
+{
+ return step_size;
+}
+
+void Slider::SetStepSize(int nNewValue)
+{
+ if (nNewValue < range_max - range_min)
+ step_size = nNewValue;
+}
+
+bool Slider::GetShowThumb()
+{
+ return show_thumb?true:false;
+}
+
+void Slider::SetShowThumb(bool bNewValue)
+{
+ show_thumb = bNewValue;
+}
+
+int Slider::GetThumbSize()
+{
+ return thumb_size;
+}
+
+void Slider::SetThumbSize(int nNewValue)
+{
+ if (nNewValue < range_max - range_min)
+ thumb_size = nNewValue;
+}
+
+int Slider::NumValues()
+{
+ return nvalues;
+}
+
+int Slider::GetValue(int index)
+{
+ if (index >= 0 && index < nvalues)
+ return value[index];
+
+ return 0;
+}
+
+void Slider::SetValue(int nNewValue, int index)
+{
+ if (index >= 0 && index < MAX_VAL) {
+ value[index] = nNewValue;
+
+ if (index >= nvalues)
+ nvalues = index+1;
+ }
+}
+
+void Slider::SetMarker(int nNewValue, int index)
+{
+ if (index >= 0 && index < 2) {
+ marker[index] = nNewValue;
+ }
+}
+
+double Slider::FractionalValue(int index)
+{
+ if (index >= 0 && index < nvalues)
+ return ((double) (value[index]-range_min)) / ((double) (range_max-range_min));
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void Slider::StepUp(int index)
+{
+ if (index >= 0 && index < nvalues) {
+ value[index] += step_size;
+
+ if (value[index] > range_max)
+ value[index] = range_max;
+ }
+}
+
+void Slider::StepDown(int index)
+{
+ if (index >= 0 && index < nvalues) {
+ value[index] -= step_size;
+
+ if (value[index] < range_min)
+ value[index] = range_min;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int Slider::OnMouseMove(int x, int y)
+{
+ bool dirty = false;
+
+ if (captured)
+ {
+ ActiveWindow* test = GetCapture();
+
+ if (test != this)
+ {
+ captured = false;
+ dirty = true;
+ }
+
+ else if (dragging)
+ {
+ mouse_x = x - rect.x;
+ if (mouse_x < 0) mouse_x = 0;
+ else if (mouse_x > rect.w) mouse_x = rect.w;
+
+ mouse_y = rect.h - (y - rect.y);
+ if (mouse_y < 0) mouse_y = 0;
+ else if (mouse_y > rect.h) mouse_y = rect.h;
+
+ // HORIZONTAL
+ if (orientation == 0) {
+ SetValue((int) ((double) mouse_x/rect.w * (range_max-range_min) + range_min));
+ }
+
+ // VERTICAL
+ else {
+ SetValue((int) ((double) mouse_y/rect.h * (range_max-range_min) + range_min));
+ }
+
+ dirty = true;
+ }
+ }
+
+ if (dirty)
+ OnClick();
+
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+int Slider::OnLButtonDown(int x, int y)
+{
+ if (!active)
+ return 0;
+
+ if (!captured)
+ captured = SetCapture();
+
+ mouse_x = x - rect.x;
+ mouse_y = y - rect.y;
+
+ // HORIZONTAL
+ if (orientation == 0) {
+ if (mouse_x < thumb_pos-thumb_size/2) {
+ StepDown();
+ }
+
+ else if (mouse_x > thumb_pos+thumb_size/2) {
+ StepUp();
+ }
+
+ else {
+ dragging = true;
+ }
+ }
+
+ // VERTICAL
+ else {
+ if (mouse_y < thumb_pos-thumb_size/2) {
+ StepUp();
+ }
+
+ else if (mouse_y > thumb_pos+thumb_size/2) {
+ StepDown();
+ }
+
+ else {
+ dragging = true;
+ }
+ }
+
+ if (!dragging)
+ OnClick();
+
+ return ActiveWindow::OnLButtonDown(x,y);
+}
+
+int Slider::OnLButtonUp(int x, int y)
+{
+ if (!active)
+ return 0;
+
+ if (captured) {
+ ReleaseCapture();
+ captured = 0;
+ dragging = false;
+ }
+
+ return ActiveWindow::OnLButtonUp(x,y);
+}
+
+int Slider::OnClick()
+{
+ return ActiveWindow::OnClick();
+}
diff --git a/nGenEx/Slider.h b/nGenEx/Slider.h
new file mode 100644
index 0000000..0adbec3
--- /dev/null
+++ b/nGenEx/Slider.h
@@ -0,0 +1,107 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Slider.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Slider/Gauge ActiveWindow class
+*/
+
+#ifndef Slider_h
+#define Slider_h
+
+#include "Types.h"
+#include "ActiveWindow.h"
+
+// +--------------------------------------------------------------------+
+
+class Slider : public ActiveWindow
+{
+public:
+ static const char* TYPENAME() { return "Slider"; }
+
+ enum { MAX_VAL=8, ORIENT_HORIZONTAL=0, ORIENT_VERTICAL=1 };
+
+ Slider(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid);
+ Slider(Screen* s, int ax, int ay, int aw, int ah, DWORD aid);
+ virtual ~Slider();
+
+ // Operations:
+ virtual void Draw();
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnClick();
+
+ // Property accessors:
+ bool GetActive();
+ void SetActive(bool bNewValue);
+ Color GetFillColor();
+ void SetFillColor(Color c);
+ bool GetBorder();
+ void SetBorder(bool bNewValue);
+ Color GetBorderColor();
+ void SetBorderColor(Color c);
+
+ int GetNumLeds();
+ void SetNumLeds(int nNewValue);
+ int GetOrientation();
+ void SetOrientation(int nNewValue);
+
+ int GetRangeMin();
+ void SetRangeMin(int nNewValue);
+ int GetRangeMax();
+ void SetRangeMax(int nNewValue);
+ int GetStepSize();
+ void SetStepSize(int nNewValue);
+ int GetThumbSize();
+ void SetThumbSize(int nNewValue);
+ bool GetShowThumb();
+ void SetShowThumb(bool bNewValue);
+
+ int NumValues();
+ int GetValue(int index=0);
+ void SetValue(int nNewValue, int index=0);
+ double FractionalValue(int index=0);
+
+ void SetMarker(int nNewValue, int index=0);
+
+ // Methods:
+ void StepUp(int index=0);
+ void StepDown(int index=0);
+
+protected:
+ int captured;
+ int dragging;
+ int mouse_x;
+ int mouse_y;
+
+ bool active; // true => slider; false => gauge
+ bool border;
+ Color border_color;
+ Color fill_color; // default: dark blue
+
+ int num_leds; // default: 1
+ int orientation; // 0 => horizontal; !0 => vertical
+
+ int range_min;
+ int range_max;
+ int step_size;
+ int show_thumb;
+ int thumb_size;
+ int thumb_pos;
+
+ int nvalues;
+ int value[MAX_VAL];
+ int marker[2];
+};
+
+#endif Slider_h
+
diff --git a/nGenEx/Solid.cpp b/nGenEx/Solid.cpp
new file mode 100644
index 0000000..e60ba16
--- /dev/null
+++ b/nGenEx/Solid.cpp
@@ -0,0 +1,2481 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Solid.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Classes for rendering solid meshes of polygons
+*/
+
+#include "MemDebug.h"
+#include "Solid.h"
+#include "Scene.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Light.h"
+#include "Shadow.h"
+#include "Projector.h"
+#include "OPCODE.h"
+
+#ifdef for
+#undef for
+#endif
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+static bool use_collision_detection = true;
+
+bool Solid::IsCollisionEnabled() { return use_collision_detection; }
+void Solid::EnableCollision(bool e) { use_collision_detection = e; }
+
+// +--------------------------------------------------------------------+
+
+Opcode::AABBTreeCollider opcode_collider;
+
+class OPCODE_data
+{
+public:
+ OPCODE_data(Surface* s) {
+ bool status = false;
+
+ if (s) {
+ using namespace Opcode;
+ opcode_collider.SetFirstContact(true);
+
+ npolys = s->NumPolys();
+ nverts = s->NumVerts();
+ ntris = s->NumIndices() / 3;
+
+ locs = new(__FILE__,__LINE__) IcePoint[nverts];
+ tris = new(__FILE__,__LINE__) IndexedTriangle[ntris];
+
+ if (locs && tris) {
+ int i, n = 0;
+
+ for (i = 0; i < nverts; i++) {
+ IcePoint* p = locs + i;
+ Vec3* v = s->GetVertexSet()->loc + i;
+
+ p->Set(v->x, v->y, v->z);
+ }
+
+ for (i = 0; i < npolys; i++) {
+ Poly* p = s->GetPolys() + i;
+
+ if (p->nverts == 3) {
+ IndexedTriangle& t = tris[n++];
+
+ t.mVRef[0] = p->verts[0];
+ t.mVRef[1] = p->verts[2];
+ t.mVRef[2] = p->verts[1];
+ }
+ else {
+ IndexedTriangle& t1 = tris[n++];
+ IndexedTriangle& t2 = tris[n++];
+
+ t1.mVRef[0] = p->verts[0];
+ t1.mVRef[1] = p->verts[2];
+ t1.mVRef[2] = p->verts[1];
+
+ t2.mVRef[0] = p->verts[0];
+ t2.mVRef[1] = p->verts[3];
+ t2.mVRef[2] = p->verts[2];
+ }
+ }
+
+ mesh.SetNbVertices(nverts);
+ mesh.SetNbTriangles(ntris);
+ mesh.SetPointers(tris, locs);
+
+ OPCODECREATE creator;
+ creator.mIMesh = &mesh;
+ status = model.Build(creator);
+ }
+ }
+ else {
+ tris = 0;
+ locs = 0;
+ npolys = 0;
+ nverts = 0;
+ ntris = 0;
+ }
+ }
+
+ ~OPCODE_data() {
+ delete [] tris;
+ delete [] locs;
+ }
+
+ Opcode::Model model;
+ Opcode::MeshInterface mesh;
+ IndexedTriangle* tris;
+ IcePoint* locs;
+ int npolys;
+ int nverts;
+ int ntris;
+};
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+Solid::Solid()
+ : model(0), own_model(1),
+ roll(0.0f), pitch(0.0f), yaw(0.0f), intersection_poly(0)
+{
+ shadow = true;
+ sprintf(name, "Solid %d", id);
+}
+
+// +--------------------------------------------------------------------+
+
+Solid::~Solid()
+{
+ if (own_model)
+ delete model;
+
+ shadows.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::Update()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::SetOrientation(const Matrix& o)
+{
+ orientation = o;
+}
+
+void
+Solid::SetLuminous(bool l)
+{
+ luminous = l;
+
+ if (model && luminous) {
+ model->luminous = luminous;
+
+ ListIter<Material> iter = model->GetMaterials();
+
+ while (++iter) {
+ Material* mtl = iter.value();
+
+ mtl->Ka = Color::Black;
+ mtl->Kd = Color::Black;
+ mtl->Ks = Color::Black;
+ mtl->Ke = Color::White;
+
+ if (mtl->tex_diffuse && !mtl->tex_emissive)
+ mtl->tex_emissive = mtl->tex_diffuse;
+ }
+
+ ListIter<Surface> s_iter = model->GetSurfaces();
+ while (++s_iter) {
+ Surface* surface = s_iter.value();
+ VertexSet* vset = surface->GetVertexSet();
+
+ for (int i = 0; i < vset->nverts; i++) {
+ vset->diffuse[i] = Color::White.Value();
+ vset->specular[i] = Color::Black.Value();
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::SetOrientation(const Solid& match)
+{
+ if (!model || infinite)
+ return;
+
+ // copy the orientation matrix from the solid we are matching:
+ orientation = match.Orientation();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::Render(Video* video, DWORD flags)
+{
+ if (flags & RENDER_ADDITIVE)
+ return;
+
+ if (video && model && model->NumPolys()) {
+ DWORD blend_modes = Video::BLEND_SOLID;
+
+ if (flags == RENDER_ALPHA)
+ blend_modes = Video::BLEND_ALPHA | Video::BLEND_ADDITIVE;
+
+ video->DrawSolid(this, blend_modes);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::SelectDetail(Projector* p)
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::ProjectScreenRect(Projector* p)
+{
+ if (model && p) {
+ Point tmp = loc;
+ p->Transform(tmp);
+
+ if (tmp.z > 1) {
+ int l = 2000;
+ int r = -2000;
+ int t = 2000;
+ int b = -2000;
+
+ for (int i = 0; i < 6; i++) {
+ Point extent;
+
+ if (i < 2)
+ extent.x = model->extents[i];
+
+ else if (i < 4)
+ extent.y = model->extents[i];
+
+ else
+ extent.z = model->extents[i];
+
+ extent = extent * orientation + loc;
+
+ p->Transform(extent);
+ p->Project(extent);
+
+ if (extent.x < l) l = (int) extent.x;
+ if (extent.x > r) r = (int) extent.x;
+ if (extent.y < t) t = (int) extent.y;
+ if (extent.y > b) b = (int) extent.y;
+ }
+
+ screen_rect.x = l;
+ screen_rect.y = t;
+ screen_rect.w = r-l;
+ screen_rect.h = b-t;
+ return;
+ }
+ }
+
+ screen_rect.x = 2000;
+ screen_rect.y = 2000;
+ screen_rect.w = 0;
+ screen_rect.h = 0;
+}
+
+// +--------------------------------------------------------------------+
+// Polygon Interference Detection:
+
+int
+Solid::CollidesWith(Graphic& o)
+{
+ Vec3 delta_loc = Location() - o.Location();
+
+ // bounding spheres test:
+ if (delta_loc.length() > Radius() + o.Radius())
+ return 0;
+
+ // possible collision, but no further refinement can be done:
+ if (!o.IsSolid())
+ return 1;
+
+ Solid& s = (Solid&) o;
+
+ // use the OPCODE library to check for polygon interference:
+ if (model && s.model) {
+ using namespace Opcode;
+
+ bool contact = false;
+
+ // first, reverse the orientation matrices for OPCODE:
+ Matrix m1 = orientation;
+ Matrix m2 = s.orientation;
+
+ Matrix4x4 world0;
+ Matrix4x4 world1;
+
+ world0.m[0][0] = (float) m1.elem[0][0];
+ world0.m[0][1] = (float) m1.elem[0][1];
+ world0.m[0][2] = (float) m1.elem[0][2];
+ world0.m[0][3] = 0.0f;
+
+ world0.m[1][0] = (float) m1.elem[1][0];
+ world0.m[1][1] = (float) m1.elem[1][1];
+ world0.m[1][2] = (float) m1.elem[1][2];
+ world0.m[1][3] = 0.0f;
+
+ world0.m[2][0] = (float) m1.elem[2][0];
+ world0.m[2][1] = (float) m1.elem[2][1];
+ world0.m[2][2] = (float) m1.elem[2][2];
+ world0.m[2][3] = 0.0f;
+
+ world0.m[3][0] = (float) Location().x;
+ world0.m[3][1] = (float) Location().y;
+ world0.m[3][2] = (float) Location().z;
+ world0.m[3][3] = 1.0f;
+
+ world1.m[0][0] = (float) m2.elem[0][0];
+ world1.m[0][1] = (float) m2.elem[1][0];
+ world1.m[0][2] = (float) m2.elem[2][0];
+ world1.m[0][3] = 0.0f;
+
+ world1.m[1][0] = (float) m2.elem[0][1];
+ world1.m[1][1] = (float) m2.elem[1][1];
+ world1.m[1][2] = (float) m2.elem[2][1];
+ world1.m[1][3] = 0.0f;
+
+ world1.m[2][0] = (float) m2.elem[0][2];
+ world1.m[2][1] = (float) m2.elem[1][2];
+ world1.m[2][2] = (float) m2.elem[2][2];
+ world1.m[2][3] = 0.0f;
+
+ world1.m[3][0] = (float) s.Location().x;
+ world1.m[3][1] = (float) s.Location().y;
+ world1.m[3][2] = (float) s.Location().z;
+ world1.m[3][3] = 1.0f;
+
+ ListIter<Surface> s1_iter = model->surfaces;
+ while (++s1_iter && !contact) {
+ Surface* s1 = s1_iter.value();
+
+ ListIter<Surface> s2_iter = s.model->surfaces;
+ while (++s2_iter && !contact) {
+ Surface* s2 = s2_iter.value();
+
+ if (s1->opcode && s2->opcode) {
+ BVTCache bvt;
+ bvt.Model0 = &s1->opcode->model;
+ bvt.Model1 = &s2->opcode->model;
+
+ if (opcode_collider.Collide(bvt, &world0, &world1))
+ if (opcode_collider.GetContactStatus() != 0)
+ contact = true;
+ }
+ }
+ }
+
+ return contact;
+ }
+
+
+ return 1;
+}
+
+// +--------------------------------------------------------------------+
+// Find the intersection of the ray (Q + w*len) with the solid.
+// If the ray intersects a polygon of the solid, place the intersection
+// point in ipt, and return 1. Otherwise, return 0.
+
+int
+Solid::CheckRayIntersection(Point Q, Point w, double len, Point& ipt,
+ bool treat_translucent_polys_as_solid)
+{
+ int impact = 0;
+
+ if (!model || model->npolys < 1)
+ return impact;
+
+ // check right angle spherical distance:
+ Point d0 = loc - Q;
+ Point d1 = d0.cross(w);
+ double dlen = d1.length(); // distance of point from line
+
+ if (dlen > radius) // clean miss
+ return 0; // (no impact)
+
+ // possible collision course...
+
+ /**********************************
+
+
+ /--- + leading_edge = Q + w * len
+ / / \
+ delta2 / delta 0
+ / / \
+ / *........x <- solid location
+ / /
+ / / delta1
+ /--- Q * = closest point
+
+
+ ************************************/
+
+ // find the point on the ray that is closest
+ // to the solid's location:
+ Point closest = Q + w * (d0 * w);
+
+ // find the leading edge, and it's distance from the location:
+ Point leading_edge = Q + w*len;
+ Point leading_delta = leading_edge - loc;
+ double leading_dist = leading_delta.length();
+
+ // if the leading edge is not within the bounding sphere,
+ if (leading_dist > radius) {
+ // check to see if the closest point is between the
+ // ray's endpoints:
+ Point delta1 = closest - Q;
+ Point delta2 = leading_edge - Q; // this is w*len
+
+ // if the closest point is not between the leading edge
+ // and the origin, this ray does not intersect:
+ if (delta1 * delta2 < 0 || delta1.length() > len) {
+ return 0;
+ }
+ }
+
+ // probable hit at this point...
+
+ // if not active, that's good enough:
+ if (GetScene() == 0) {
+ ipt = closest;
+ return 1;
+ }
+
+ // transform ray into object space:
+ Matrix xform(Orientation());
+
+ Vec3 tmp = w;
+
+ w.x = tmp * Vec3(xform(0,0), xform(0,1), xform(0,2));
+ w.y = tmp * Vec3(xform(1,0), xform(1,1), xform(1,2));
+ w.z = tmp * Vec3(xform(2,0), xform(2,1), xform(2,2));
+
+ tmp = Q-loc;
+
+ Q.x = tmp * Vec3(xform(0,0), xform(0,1), xform(0,2));
+ Q.y = tmp * Vec3(xform(1,0), xform(1,1), xform(1,2));
+ Q.z = tmp * Vec3(xform(2,0), xform(2,1), xform(2,2));
+
+ double min = len;
+ intersection_poly = 0;
+
+ // check each polygon:
+ ListIter<Surface> iter = model->surfaces;
+ while (++iter) {
+ Surface* s = iter.value();
+ Poly* p = s->GetPolys();
+
+ for (int i = 0; i < s->NumPolys(); i++) {
+ if (!treat_translucent_polys_as_solid && p->material && !p->material->IsSolid()) {
+ p++;
+ continue;
+ }
+
+ Point v = p->plane.normal;
+ double d = p->plane.distance;
+
+ double denom = w*v;
+
+ if (denom < -1.0e-5) {
+ Point P = v * d;
+ double ilen = ((P-Q)*v)/denom;
+
+ if (ilen > 0 && ilen < min) {
+ Point intersect = Q + w * ilen;
+
+ if (p->Contains(intersect)) {
+ intersection_poly = p;
+ ipt = intersect;
+ min = ilen;
+ impact = 1;
+ }
+ }
+ }
+
+ p++;
+ }
+ }
+
+ // xform impact point back into world coordinates:
+
+ if (impact) {
+ ipt = (ipt * Orientation()) + loc;
+ }
+
+ return impact;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::ClearModel()
+{
+ if (own_model && model) {
+ delete model;
+ model = 0;
+ }
+
+ radius = 0.0f;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::UseModel(Model* m)
+{
+ // get rid of the existing model:
+ ClearModel();
+
+ // point to the new model:
+ own_model = 0;
+ model = m;
+ radius = m->radius;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Solid::Load(const char* mag_file, double scale)
+{
+ // get ready to load, delete existing model:
+ ClearModel();
+
+ // loading our own copy, so we own the model:
+ model = new(__FILE__,__LINE__) Model;
+ own_model = 1;
+
+ // now load the model:
+ if (model->Load(mag_file, scale)) {
+ radius = model->radius;
+ strncpy(name, model->name, sizeof(name));
+ return true;
+ }
+
+ // load failed:
+ ClearModel();
+ return false;
+}
+
+bool
+Solid::Load(ModelFile* mod_file, double scale)
+{
+ // get ready to load, delete existing model:
+ ClearModel();
+
+ // loading our own copy, so we own the model:
+ model = new(__FILE__,__LINE__) Model;
+ own_model = 1;
+
+ // now load the model:
+ if (model->Load(mod_file, scale)) {
+ radius = model->radius;
+ return true;
+ }
+
+ // load failed:
+ ClearModel();
+ return false;
+}
+
+bool
+Solid::Rescale(double scale)
+{
+ if (!own_model || !model)
+ return false;
+
+ radius = 0;
+
+ ListIter<Surface> iter = model->GetSurfaces();
+ while (++iter) {
+ Surface* s = iter.value();
+
+ for (int v = 0; v < s->NumVerts(); v++) {
+ s->vertex_set->loc[v] *= (float) scale;
+ s->vloc[v] *= (float) scale;
+
+ float lvi = s->vloc[v].length();
+ if (lvi > radius)
+ radius = lvi;
+ }
+ }
+
+ model->radius = radius;
+
+ InvalidateSurfaceData();
+
+ return true;
+}
+
+void
+Solid::CreateShadows(int nlights)
+{
+ while (shadows.size() < nlights) {
+ shadows.append(new(__FILE__,__LINE__) Shadow(this));
+ }
+}
+
+void
+Solid::UpdateShadows(List<Light>& lights)
+{
+ List<Light> active_lights;
+ ListIter<Light> iter = lights;
+
+ while (++iter) {
+ Light* light = iter.value();
+
+ if (light->IsActive() && light->CastsShadow()) {
+ double distance = Point(Location() - light->Location()).length();
+ double intensity = light->Intensity();
+
+ if (light->Type() == Light::LIGHT_POINT) {
+ if (intensity / distance > 1)
+ active_lights.append(light);
+ }
+
+ else if (light->Type() == Light::LIGHT_DIRECTIONAL) {
+ if (intensity > 0.65)
+ active_lights.insert(light);
+ }
+ }
+ }
+
+ iter.attach(active_lights);
+
+ while (++iter) {
+ Light* light = iter.value();
+ int index = iter.index();
+
+ if (index < shadows.size()) {
+ shadows[index]->Update(light);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::DeletePrivateData()
+{
+ if (model)
+ model->DeletePrivateData();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::InvalidateSurfaceData()
+{
+ if (!model)
+ return;
+
+ bool invalidate = model->IsDynamic();
+
+ ListIter<Surface> iter = model->GetSurfaces();
+ while (++iter) {
+ Surface* s = iter.value();
+ VideoPrivateData* vpd = s->GetVideoPrivateData();
+
+ if (vpd) {
+ if (invalidate) {
+ vpd->Invalidate();
+ }
+ else {
+ delete vpd;
+ s->SetVideoPrivateData(0);
+ }
+ }
+ }
+}
+
+void
+Solid::InvalidateSegmentData()
+{
+ if (!model)
+ return;
+
+ bool invalidate = model->IsDynamic();
+
+ ListIter<Surface> iter = model->GetSurfaces();
+ while (++iter) {
+ Surface* s = iter.value();
+
+ ListIter<Segment> seg_iter = s->GetSegments();
+ while (++seg_iter) {
+ Segment* segment = seg_iter.value();
+ VideoPrivateData* vpd = segment->GetVideoPrivateData();
+
+ if (vpd) {
+ if (invalidate) {
+ vpd->Invalidate();
+ }
+ else {
+ delete vpd;
+ segment->SetVideoPrivateData(0);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Solid::IsDynamic() const
+{
+ if (model)
+ return model->IsDynamic();
+
+ return false;
+}
+
+void
+Solid::SetDynamic(bool d)
+{
+ if (model && own_model)
+ model->SetDynamic(d);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::GetAllTextures(List<Bitmap>& textures)
+{
+ if (model)
+ model->GetAllTextures(textures);
+}
+
+void
+Model::GetAllTextures(List<Bitmap>& textures)
+{
+ ListIter<Material> m_iter = materials;
+ while (++m_iter) {
+ Material* m = m_iter.value();
+
+ if (m->tex_diffuse && !textures.contains(m->tex_diffuse))
+ textures.append(m->tex_diffuse);
+
+ if (m->tex_specular && !textures.contains(m->tex_specular))
+ textures.append(m->tex_specular);
+
+ if (m->tex_emissive && !textures.contains(m->tex_emissive))
+ textures.append(m->tex_emissive);
+
+ if (m->tex_bumpmap && !textures.contains(m->tex_bumpmap))
+ textures.append(m->tex_bumpmap);
+
+ if (m->tex_detail && !textures.contains(m->tex_detail))
+ textures.append(m->tex_detail);
+ }
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+Model::Model()
+ : nverts(0), npolys(0), radius(0), luminous(false), dynamic(false)
+{
+ ZeroMemory(name, sizeof(name));
+}
+
+Model::Model(const Model& m)
+ : nverts(0), npolys(0), radius(0), luminous(false), dynamic(false)
+{
+ operator=(m);
+}
+
+// +--------------------------------------------------------------------+
+
+Model::~Model()
+{
+ surfaces.destroy();
+ materials.destroy();
+}
+
+Model&
+Model::operator = (const Model& m)
+{
+ if (this != &m) {
+ surfaces.destroy();
+ materials.destroy();
+
+ CopyMemory(name, m.name, Solid::NAMELEN);
+
+ nverts = m.nverts;
+ npolys = m.npolys;
+ radius = m.radius;
+ luminous = m.luminous;
+ dynamic = m.dynamic;
+
+ Model* pmod = (Model*) &m;
+
+ ListIter<Material> m_iter = pmod->materials;
+ while (++m_iter) {
+ Material* matl1 = m_iter.value();
+ Material* matl2 = new(__FILE__,__LINE__) Material;
+
+ CopyMemory(matl2, matl1, sizeof(Material));
+ matl2->thumbnail = 0;
+
+ materials.append(matl2);
+ }
+
+ ListIter<Surface> s_iter = pmod->surfaces;
+ while (++s_iter) {
+ Surface* surf1 = s_iter.value();
+ Surface* surf2 = new(__FILE__,__LINE__) Surface;
+
+ surf2->Copy(*surf1, this);
+ surfaces.append(surf2);
+ }
+ }
+
+ return *this;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Model::NumSegments() const
+{
+ int nsegments = 0;
+
+ for (int i = 0; i < surfaces.size(); i++) {
+ const Surface* s = surfaces[i];
+ nsegments += s->NumSegments();
+ }
+
+ return nsegments;
+}
+
+// +--------------------------------------------------------------------+
+
+inline bool Collinear(const double* a, const double* b, const double* c)
+{
+ Point ab(b[0]-a[0], b[1]-a[1], b[2]-a[2]);
+ Point ac(c[0]-a[0], c[1]-a[1], c[2]-a[2]);
+ Point cross = ab.cross(ac);
+ return (cross.length() == 0);
+}
+
+struct HomogenousPlane
+{
+ double distance;
+ double normal_x;
+ double normal_y;
+ double normal_z;
+ double normal_w;
+};
+
+static void LoadPlane(Plane& p, DataLoader* l, BYTE*& fp)
+{
+ HomogenousPlane tmp;
+ l->fread(&tmp, sizeof(HomogenousPlane), 1, fp);
+}
+
+static void LoadFlags(LPDWORD flags, DataLoader* l, BYTE*& fp)
+{
+ DWORD magic_flags;
+ l->fread(&magic_flags, sizeof(DWORD), 1, fp);
+
+ /** OLD MAGIC FLAGS
+ enum { FLAT_SHADED = 1,
+ LUMINOUS = 2,
+ TRANSLUCENT = 4, \\ must swap
+ CHROMAKEY = 8, // these two
+ FOREGROUND = 16, -- not used
+ WIREFRAME = 32, -- not used
+ SPECULAR1 = 64,
+ SPECULAR2 = 128 };
+ ***/
+
+ const DWORD magic_mask = 0x0fc3;
+
+ *flags = magic_flags & magic_mask;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Model::Load(const char* mag_file, double scale)
+{
+ BYTE* block;
+ DataLoader* loader = DataLoader::GetLoader();
+ bool result = false;
+
+ radius = 0.0f;
+ extents[0] = 0.0f;
+ extents[1] = 0.0f;
+ extents[2] = 0.0f;
+ extents[3] = 0.0f;
+ extents[4] = 0.0f;
+ extents[5] = 0.0f;
+
+ if (!loader) {
+ Print("MAG Open Failed: no data loader for file '%s'\n", mag_file);
+ return result;
+ }
+
+ int size = loader->LoadBuffer(mag_file, block);
+ BYTE* fp = block;
+
+ // check MAG file:
+ if (!size) {
+ Print("MAG Open Failed: could not open file '%s'\n", mag_file);
+ return result;
+ }
+
+ strncpy(name, mag_file, 31);
+ name[31] = 0;
+
+ char file_id[5];
+ CopyMemory(file_id, block, 4);
+ file_id[4] = '\0';
+ int version = 1;
+
+ if (!strcmp(file_id, "MAG6")) {
+ version = 6;
+ }
+ else if (!strcmp(file_id, "MAG5")) {
+ version = 5;
+ }
+ else if (!strcmp(file_id, "MAG4")) {
+ version = 4;
+ }
+ else {
+ Print("MAG Open Failed: File '%s' Invalid file type '%s'\n", mag_file, file_id);
+ loader->ReleaseBuffer(block);
+ return result;
+ }
+
+ // get ready to load, delete existing model:
+ surfaces.destroy();
+ materials.destroy();
+ nverts = 0;
+ npolys = 0;
+
+ // now load the model:
+ switch (version) {
+ case 4:
+ case 5:
+ result = LoadMag5(block, size, scale);
+ break;
+
+ case 6:
+ result = LoadMag6(block, size, scale);
+ break;
+
+ default:
+ break;
+ }
+
+ loader->ReleaseBuffer(block);
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Model::Load(ModelFile* mod_file, double scale)
+{
+ if (mod_file) {
+ return mod_file->Load(this, scale);
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+static int mcomp(const void* a, const void* b)
+{
+ Poly* pa = (Poly*) a;
+ Poly* pb = (Poly*) b;
+
+ if (pa->sortval == pb->sortval)
+ return 0;
+
+ if (pa->sortval < pb->sortval)
+ return 1;
+
+ return -1;
+}
+
+bool
+Model::LoadMag5(BYTE* block, int len, double scale)
+{
+ bool result = false;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ BYTE* fp = block + 4;
+ int ntex = 0;
+ int nsurfs = 0;
+
+ loader->fread(&ntex, sizeof(ntex), 1, fp);
+ loader->fread(&nsurfs, sizeof(nsurfs), 1, fp);
+
+ // create a default gray material:
+ Material* mtl = new Material;
+
+ if (mtl) {
+ mtl->Ka = Color::LightGray;
+ mtl->Kd = Color::LightGray;
+ mtl->Ks = ColorValue(0.2f,0.2f,0.2f);
+ mtl->power = 20.0f;
+
+ mtl->ambient_value = 1.0f;
+ mtl->ambient_color = Color::LightGray;
+ mtl->diffuse_value = 1.0f;
+ mtl->diffuse_color = Color::LightGray;
+ mtl->specular_value = 0.2f;
+ mtl->specular_color = Color::White;
+ strcpy(mtl->name, "(default)");
+
+ materials.append(mtl);
+ }
+
+ // read texture list:
+ for (int i = 0; i < ntex; i++) {
+ Material* mtl = new(__FILE__,__LINE__) Material;
+ char tname[32];
+
+ if (mtl) {
+ mtl->Ka = ColorValue(0.5f,0.5f,0.5f);
+ mtl->Kd = ColorValue(1.0f,1.0f,1.0f);
+ mtl->Ks = ColorValue(0.2f,0.2f,0.2f);
+ mtl->power = 20.0f;
+
+ mtl->ambient_value = 1.0f;
+ mtl->ambient_color = Color::Gray;
+ mtl->diffuse_value = 1.0f;
+ mtl->diffuse_color = Color::White;
+ mtl->specular_value = 0.2f;
+ mtl->specular_color = Color::White;
+
+ loader->fread(tname, 32, 1, fp);
+ loader->LoadTexture(tname, mtl->tex_diffuse, Bitmap::BMP_SOLID, true);
+ strcpy(mtl->name, tname);
+
+ char* dot = strrchr(mtl->name, '.');
+ if (dot)
+ *dot = 0;
+
+ char* plus = strrchr(mtl->name, '+');
+ if (plus)
+ *plus = 0;
+
+ materials.append(mtl);
+ }
+ }
+
+
+ loader->fread(&nverts, 4, 1, fp);
+ loader->fread(&npolys, 4, 1, fp);
+
+ // plan on creating four verts per poly:
+ int mag_nverts = nverts;
+ int next_vert = nverts;
+
+ nverts = npolys * 4;
+
+ Surface* s = new(__FILE__,__LINE__) Surface;
+ VertexSet* vset = 0;
+
+ if (s) {
+ strcpy(s->name, "default");
+
+ s->model = this;
+ s->vertex_set = new(__FILE__,__LINE__) VertexSet(nverts);
+ s->vloc = new(__FILE__,__LINE__) Vec3[nverts];
+
+ ZeroMemory(s->vertex_set->loc, nverts * sizeof(Vec3));
+ ZeroMemory(s->vertex_set->diffuse, nverts * sizeof(DWORD));
+ ZeroMemory(s->vertex_set->specular, nverts * sizeof(DWORD));
+ ZeroMemory(s->vertex_set->tu, nverts * sizeof(float));
+ ZeroMemory(s->vertex_set->tv, nverts * sizeof(float));
+ ZeroMemory(s->vertex_set->rw, nverts * sizeof(float));
+ ZeroMemory(s->vloc, nverts * sizeof(Vec3));
+
+ s->npolys = npolys;
+ s->polys = new(__FILE__,__LINE__) Poly[npolys];
+
+ ZeroMemory(s->polys, sizeof(Poly) * npolys);
+ surfaces.append(s);
+
+ vset = s->vertex_set;
+
+ // read vertex set:
+ for (int v = 0; v < mag_nverts; v++) {
+ Vec3 vert, norm;
+ DWORD vstate;
+
+ loader->fread(&vert, sizeof(Vec3), 1, fp);
+ loader->fread(&norm, sizeof(Vec3), 1, fp);
+ loader->fread(&vstate, sizeof(DWORD), 1, fp);
+
+ vert.SwapYZ();
+ vert *= (float) scale;
+
+ vset->loc[v] = vert;
+ vset->nrm[v] = norm;
+
+ double d = vert.length();
+ if (d > radius)
+ radius = (float) d;
+
+ if (vert.x > extents[0]) extents[0] = vert.x;
+ if (vert.x < extents[1]) extents[1] = vert.x;
+ if (vert.y > extents[2]) extents[2] = vert.y;
+ if (vert.y < extents[3]) extents[3] = vert.y;
+ if (vert.z > extents[4]) extents[4] = vert.z;
+ if (vert.z < extents[5]) extents[5] = vert.z;
+ }
+
+ while (v < nverts)
+ vset->nrm[v++] = Vec3(1,0,0);
+
+ // read polys:
+ Vec3 dummy_center;
+ DWORD dummy_flags;
+ DWORD dummy_color;
+ Plane dummy_plane;
+ int texture_num;
+ int poly_nverts;
+ int vert_index_buffer[32];
+ float texture_index_buffer[32];
+
+ for (int n = 0; n < npolys; n++) {
+ Poly& poly = s->polys[n];
+ poly.vertex_set = vset;
+
+ loader->fread(&dummy_flags, sizeof(DWORD), 1, fp);
+ loader->fread(&dummy_center, sizeof(Vec3), 1, fp);
+
+ LoadPlane(dummy_plane, loader, fp);
+
+ loader->fread(&dummy_color, sizeof(DWORD), 1, fp);
+ loader->fread(&texture_num, sizeof(int), 1, fp);
+
+ if (texture_num >= 0 && texture_num < ntex) {
+ int mtl_num = texture_num + 1;
+ poly.material = materials[mtl_num];
+ poly.sortval = texture_num;
+
+ bool flag_translucent = (dummy_flags & 0x04) ? true : false;
+ bool flag_transparent = (dummy_flags & 0x08) ? true : false;
+
+ // luminous
+ if (dummy_flags & 2) {
+ Material* mtl = materials[mtl_num];
+
+ mtl->Ka = ColorValue(0,0,0,0);
+ mtl->Kd = ColorValue(0,0,0,0);
+ mtl->Ks = ColorValue(0,0,0,0);
+ mtl->Ke = ColorValue(1,1,1,1);
+
+ mtl->tex_emissive = mtl->tex_diffuse;
+ }
+
+ // glowing (additive)
+ if (flag_translucent && flag_transparent)
+ materials[mtl_num]->blend = Material::MTL_ADDITIVE;
+
+ // translucent (alpha)
+ else if (flag_translucent)
+ materials[mtl_num]->blend = Material::MTL_TRANSLUCENT;
+
+ // transparent (just use alpha for this)
+ else if (flag_transparent)
+ materials[mtl_num]->blend = Material::MTL_TRANSLUCENT;
+ }
+ else {
+ poly.material = materials.first();
+ poly.sortval = 1000;
+ }
+
+ // hack: store flat shaded flag in unused visible byte
+ poly.visible = (BYTE) (dummy_flags & 1);
+
+ loader->fread(&poly_nverts, sizeof(int), 1, fp);
+ loader->fread(vert_index_buffer, sizeof(int), poly_nverts, fp);
+
+ if (poly_nverts == 3)
+ s->nindices += 3;
+
+ else if (poly_nverts == 4)
+ s->nindices += 6;
+
+ poly.nverts = poly_nverts;
+ for (int vi = 0; vi < poly_nverts; vi++) {
+ int v = vert_index_buffer[vi];
+
+ if (vset->rw[v] > 0) {
+ vset->CopyVertex(next_vert, v);
+ v = next_vert++;
+ }
+
+ vset->rw[v] = 1;
+ poly.verts[vi] = v;
+ }
+
+ loader->fread(texture_index_buffer, sizeof(float), poly_nverts, fp); // tu's
+ for (vi = 0; vi < poly_nverts; vi++) {
+ int v = poly.verts[vi];
+ vset->tu[v] = texture_index_buffer[vi];
+ }
+
+ loader->fread(texture_index_buffer, sizeof(float), poly_nverts, fp); // tv's
+ for (vi = 0; vi < poly_nverts; vi++) {
+ int v = poly.verts[vi];
+ vset->tv[v] = texture_index_buffer[vi];
+ }
+
+ fp += 16;
+ }
+
+ // pass 2 (adjust vertex normals for flat polys):
+ for (n = 0; n < npolys; n++) {
+ Poly& poly = s->polys[n];
+ poly.plane = Plane(vset->loc[poly.verts[0]],
+ vset->loc[poly.verts[2]],
+ vset->loc[poly.verts[1]]);
+
+ // hack: retrieve flat shaded flag from unused visible byte
+ if (poly.visible) {
+ int poly_nverts = poly.nverts;
+
+ for (int vi = 0; vi < poly_nverts; vi++) {
+ int v = poly.verts[vi];
+ vset->nrm[v] = poly.plane.normal;
+ }
+ }
+ }
+
+ // sort the polys by material index:
+ qsort((void*) s->polys, s->npolys, sizeof(Poly), mcomp);
+
+ // then assign them to cohesive segments:
+ Segment* segment = 0;
+
+ for (n = 0; n < npolys; n++) {
+ if (segment && segment->material == s->polys[n].material) {
+ segment->npolys++;
+ }
+ else {
+ segment = 0;
+ }
+
+ if (!segment) {
+ segment = new(__FILE__,__LINE__) Segment;
+
+ segment->npolys = 1;
+ segment->polys = &s->polys[n];
+ segment->material = segment->polys->material;
+ segment->model = this;
+
+ s->segments.append(segment);
+ }
+ }
+
+ s->BuildHull();
+
+ result = nverts && npolys;
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+struct MaterialMag6 {
+ char name[Material::NAMELEN];
+ char shader[Material::NAMELEN];
+ float power; // highlight sharpness (big=shiny)
+ float brilliance; // diffuse power function
+ float bump; // bump level (0=none)
+ DWORD blend; // alpha blend type
+ bool shadow; // material casts shadow
+ bool luminous; // verts have their own lighting
+
+ Color ambient_color;
+ Color diffuse_color;
+ Color specular_color;
+ Color emissive_color;
+
+ float ambient_value;
+ float diffuse_value;
+ float specular_value;
+ float emissive_value;
+
+ BYTE tex_diffuse;
+ BYTE tex_specular;
+ BYTE tex_bumpmap;
+ BYTE tex_emissive;
+};
+
+// +--------------------------------------------------------------------+
+
+bool
+Model::LoadMag6(BYTE* block, int len, double scale)
+{
+ bool result = false;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ BYTE* fp = block + 4;
+ int i = 0;
+ int ntex = 0;
+ int nmtls = 0;
+ int nsurfs = 0;
+ List<Bitmap> textures;
+
+ loader->fread(&ntex, sizeof(ntex), 1, fp); // size of texture block
+ loader->fread(&nmtls, sizeof(nmtls), 1, fp); // number of materials
+ loader->fread(&nsurfs, sizeof(nsurfs), 1, fp); // number of surfaces
+
+ // read texture list:
+ if (ntex) {
+ char* buffer = new(__FILE__,__LINE__) char[ntex];
+ char* p = buffer;
+ Bitmap* bmp = 0;
+
+ loader->fread(buffer, ntex, 1, fp);
+
+ while (p < buffer + ntex) {
+ loader->LoadTexture(p, bmp, Bitmap::BMP_SOLID, true);
+ textures.append(bmp);
+
+ p += strlen(p) + 1;
+ }
+
+ delete [] buffer;
+ }
+
+ for (i = 0; i < nmtls; i++) {
+ MaterialMag6 m6;
+ Material* mtl = new(__FILE__,__LINE__) Material;
+
+ loader->fread(&m6, sizeof(m6), 1, fp);
+
+ if (mtl) {
+ CopyMemory(mtl->name, m6.name, Material::NAMELEN);
+ CopyMemory(mtl->shader, m6.shader, Material::NAMELEN);
+
+ mtl->ambient_value = m6.ambient_value;
+ mtl->ambient_color = m6.ambient_color;
+ mtl->diffuse_value = m6.diffuse_value;
+ mtl->diffuse_color = m6.diffuse_color;
+ mtl->specular_value = m6.specular_value;
+ mtl->specular_color = m6.specular_color;
+ mtl->emissive_value = m6.emissive_value;
+ mtl->emissive_color = m6.emissive_color;
+
+ mtl->Ka = ColorValue(mtl->ambient_color) * mtl->ambient_value;
+ mtl->Kd = ColorValue(mtl->diffuse_color) * mtl->diffuse_value;
+ mtl->Ks = ColorValue(mtl->specular_color) * mtl->specular_value;
+ mtl->Ke = ColorValue(mtl->emissive_color) * mtl->emissive_value;
+
+ mtl->power = m6.power;
+ mtl->brilliance = m6.brilliance;
+ mtl->bump = m6.bump;
+ mtl->blend = m6.blend;
+ mtl->shadow = m6.shadow;
+ mtl->luminous = m6.luminous;
+
+ if (m6.tex_diffuse && m6.tex_diffuse <= textures.size())
+ mtl->tex_diffuse = textures[m6.tex_diffuse - 1];
+
+ if (m6.tex_specular && m6.tex_specular <= textures.size())
+ mtl->tex_specular = textures[m6.tex_specular - 1];
+
+ if (m6.tex_emissive && m6.tex_emissive <= textures.size())
+ mtl->tex_emissive = textures[m6.tex_emissive - 1];
+
+ if (m6.tex_bumpmap && m6.tex_bumpmap <= textures.size())
+ mtl->tex_bumpmap = textures[m6.tex_bumpmap - 1];
+
+ materials.append(mtl);
+ }
+ }
+
+ for (i = 0; i < nsurfs; i++) {
+ int nverts = 0;
+ int npolys = 0;
+ BYTE namelen = 0;
+ char name[128];
+
+ loader->fread(&nverts, 4, 1, fp);
+ loader->fread(&npolys, 4, 1, fp);
+ loader->fread(&namelen, 1, 1, fp);
+ loader->fread(name, 1, namelen, fp);
+
+ Surface* surface = new(__FILE__,__LINE__) Surface;
+ surface->model = this;
+ surface->SetName(name);
+ surface->CreateVerts(nverts);
+ surface->CreatePolys(npolys);
+
+ VertexSet* vset = surface->GetVertexSet();
+ Poly* polys = surface->GetPolys();
+
+ ZeroMemory(polys, sizeof(Poly) * npolys);
+
+ // read vertex set:
+ for (int v = 0; v < nverts; v++) {
+ loader->fread(&vset->loc[v], sizeof(float), 3, fp);
+ loader->fread(&vset->nrm[v], sizeof(float), 3, fp);
+ loader->fread(&vset->tu[v], sizeof(float), 1, fp);
+ loader->fread(&vset->tv[v], sizeof(float), 1, fp);
+
+ vset->loc[v] *= (float) scale;
+
+ Vec3 vert = vset->loc[v];
+
+ double d = vert.length();
+ if (d > radius)
+ radius = (float) d;
+
+ if (vert.x > extents[0]) extents[0] = vert.x;
+ if (vert.x < extents[1]) extents[1] = vert.x;
+ if (vert.y > extents[2]) extents[2] = vert.y;
+ if (vert.y < extents[3]) extents[3] = vert.y;
+ if (vert.z > extents[4]) extents[4] = vert.z;
+ if (vert.z < extents[5]) extents[5] = vert.z;
+ }
+
+ // read polys:
+ for (int n = 0; n < npolys; n++) {
+ Poly& poly = polys[n];
+ BYTE poly_nverts = 0;
+ BYTE material_index = 0;
+ WORD poly_verts[8];
+
+ loader->fread(&poly_nverts, sizeof(BYTE), 1, fp);
+ loader->fread(&material_index, sizeof(BYTE), 1, fp);
+ loader->fread(&poly_verts[0], sizeof(WORD), poly_nverts, fp);
+
+ if (poly_nverts >= 3) {
+ poly.nverts = poly_nverts;
+
+ for (int i = 0; i < poly_nverts; i++) {
+ poly.verts[i] = poly_verts[i];
+ }
+ }
+ else {
+ poly.sortval = 666;
+ }
+
+ if (material_index > 0) {
+ poly.material = materials[material_index-1];
+ poly.sortval = material_index;
+ }
+ else if (materials.size()) {
+ poly.material = materials.first();
+ poly.sortval = 1;
+ }
+ else {
+ poly.sortval = 1000;
+ }
+
+ if (poly.nverts == 3)
+ surface->AddIndices(3);
+
+ else if (poly.nverts == 4)
+ surface->AddIndices(6);
+
+ poly.vertex_set = vset;
+ poly.plane = Plane(vset->loc[poly.verts[0]],
+ vset->loc[poly.verts[2]],
+ vset->loc[poly.verts[1]]);
+ }
+
+ // sort the polys by material index:
+ qsort((void*) polys, npolys, sizeof(Poly), mcomp);
+
+ // then assign them to cohesive segments:
+ Segment* segment = 0;
+
+ for (n = 0; n < npolys; n++) {
+ if (segment && segment->material == polys[n].material) {
+ segment->npolys++;
+ }
+ else {
+ segment = 0;
+ }
+
+ if (!segment) {
+ segment = new(__FILE__,__LINE__) Segment;
+
+ segment->npolys = 1;
+ segment->polys = &polys[n];
+ segment->material = segment->polys->material;
+ segment->model = this;
+
+ surface->GetSegments().append(segment);
+ }
+ }
+
+ surface->BuildHull();
+ surfaces.append(surface);
+
+ this->nverts += nverts;
+ this->npolys += npolys;
+ }
+
+
+ result = nverts && npolys;
+ return result;
+}
+
+void
+Model::AddSurface(Surface* surface)
+{
+ if (surface) {
+ surface->model = this;
+
+ ListIter<Segment> iter = surface->segments;
+ while (++iter) {
+ Segment* segment = iter.value();
+ segment->model = this;
+ }
+
+ surface->BuildHull();
+ surfaces.append(surface);
+
+ nverts += surface->NumVerts();
+ npolys += surface->NumPolys();
+ }
+}
+
+
+// +--------------------------------------------------------------------+
+
+const Material*
+Model::FindMaterial(const char* mtl_name) const
+{
+ if (mtl_name && *mtl_name) {
+ Model* pThis = (Model*) this;
+
+ ListIter<Material> iter = pThis->materials;
+ while (++iter) {
+ Material* mtl = iter.value();
+
+ if (!strcmp(mtl->name, mtl_name))
+ return mtl;
+ }
+ }
+
+ return 0;
+}
+
+const Material*
+Model::ReplaceMaterial(const Material* mtl)
+{
+ const Material* mtl_orig = 0;
+
+ if (mtl) {
+ mtl_orig = FindMaterial(mtl->name);
+
+ if (mtl_orig) {
+ int n = materials.index(mtl_orig);
+ materials[n] = (Material*) mtl;
+
+ ListIter<Surface> surf_iter = surfaces;
+ while (++surf_iter) {
+ Surface* surf = surf_iter.value();
+
+ ListIter<Segment> seg_iter = surf->GetSegments();
+ while (++seg_iter) {
+ Segment* segment = seg_iter.value();
+
+ if (segment->material == mtl_orig)
+ segment->material = (Material*) mtl;
+ }
+ }
+ }
+ }
+
+ return mtl_orig;
+}
+
+// +--------------------------------------------------------------------+
+
+Poly*
+Model::AddPolys(int nsurf, int np, int nv)
+{
+ if (nsurf >= 0 && nsurf < surfaces.size())
+ return surfaces[nsurf]->AddPolys(np, nv);
+
+ ::Print("WARNING: AddPolys(%d,%d,%d) invalid surface\n", nsurf, np, nv);
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Model::ExplodeMesh()
+{
+ ListIter<Surface> iter = surfaces;
+
+ int nv = 0;
+ int np = 0;
+
+ while (++iter) {
+ Surface* s = iter.value();
+ s->ExplodeMesh();
+
+ nv += s->NumVerts();
+ np += s->NumPolys();
+ }
+
+ nverts = nv;
+ npolys = np;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Model::OptimizeMesh()
+{
+ ListIter<Surface> iter = surfaces;
+
+ int nv = 0;
+ int np = 0;
+
+ while (++iter) {
+ Surface* s = iter.value();
+ s->OptimizeMesh();
+
+ nv += s->NumVerts();
+ np += s->NumPolys();
+ }
+
+ nverts = nv;
+ npolys = np;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Model::OptimizeMaterials()
+{
+ for (int i = 0; i < materials.size(); i++) {
+ Material* m1 = materials[i];
+
+ for (int n = i; n < materials.size(); n++) {
+ Material* m2 = materials[n];
+
+ // if they match, merge them:
+ if (*m1 == *m2) {
+ List<Poly> polys;
+ SelectPolys(polys, m2);
+
+ ListIter<Poly> iter = polys;
+ while (++iter) {
+ Poly* p = iter.value();
+ p->material = m1;
+ }
+
+ // and discard the duplicate:
+ materials.remove(m2);
+ delete m2;
+ }
+ }
+ }
+}
+
+void
+Model::ScaleBy(double factor)
+{
+ ListIter<Surface> iter = surfaces;
+
+ while (++iter) {
+ Surface* s = iter.value();
+ s->ScaleBy(factor);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Model::Normalize()
+{
+ ListIter<Surface> iter = surfaces;
+
+ while (++iter) {
+ Surface* s = iter.value();
+ s->Normalize();
+ }
+}
+
+void
+Model::SelectPolys(List<Poly>& polys, Vec3 loc)
+{
+ ListIter<Surface> iter = surfaces;
+
+ while (++iter) {
+ Surface* s = iter.value();
+ s->SelectPolys(polys, loc);
+ }
+}
+
+void
+Model::SelectPolys(List<Poly>& polys, Material* m)
+{
+ ListIter<Surface> iter = surfaces;
+
+ while (++iter) {
+ Surface* s = iter.value();
+ s->SelectPolys(polys, m);
+ }
+}
+
+void
+Model::ComputeTangents()
+{
+ ListIter<Surface> iter = surfaces;
+
+ while (++iter) {
+ Surface* s = iter.value();
+ s->ComputeTangents();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Model::DeletePrivateData()
+{
+ ListIter<Surface> iter = surfaces;
+ while (++iter) {
+ Surface* s = iter.value();
+ VideoPrivateData* vpd = s->GetVideoPrivateData();
+
+ if (vpd) {
+ delete vpd;
+ s->SetVideoPrivateData(0);
+ }
+
+ ListIter<Segment> seg_iter = s->GetSegments();
+ while (++seg_iter) {
+ Segment* segment = seg_iter.value();
+ VideoPrivateData* vpd = segment->video_data;
+
+ if (vpd) {
+ delete vpd;
+ segment->video_data = 0;
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+Surface::Surface()
+ : model(0), vertex_set(0), vloc(0), nhull(0), npolys(0), nindices(0),
+ polys(0), state(0), video_data(0), opcode(0)
+{
+ ZeroMemory(name, sizeof(name));
+}
+
+Surface::~Surface()
+{
+ segments.destroy();
+
+ delete opcode;
+ delete vertex_set;
+ delete [] vloc;
+ delete [] polys;
+ delete video_data;
+
+ model = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Surface::Copy(Surface& s, Model* m)
+{
+ segments.destroy();
+
+ delete opcode;
+ delete vertex_set;
+ delete [] vloc;
+ delete [] polys;
+ delete video_data;
+
+ CopyMemory(name, s.name, Solid::NAMELEN);
+
+ model = m;
+ radius = s.radius;
+ nhull = s.nhull;
+ npolys = s.npolys;
+ nindices = s.nindices;
+ state = s.state;
+ offset = s.offset;
+ orientation = s.orientation;
+ opcode = 0;
+ video_data = 0;
+
+ vertex_set = s.vertex_set->Clone();
+
+ if (nhull > 0) {
+ vloc = new(__FILE__,__LINE__) Vec3[nhull];
+ CopyMemory(vloc, s.vloc, nhull * sizeof(Vec3));
+ }
+ else {
+ vloc = 0;
+ }
+
+ polys = new(__FILE__,__LINE__) Poly[npolys];
+ CopyMemory(polys, s.polys, npolys * sizeof(Poly));
+
+ for (int i = 0; i < npolys; i++) {
+ polys[i].vertex_set = vertex_set;
+
+ if (s.polys[i].material)
+ polys[i].material = (Material*) model->FindMaterial(s.polys[i].material->name);
+ }
+
+ ListIter<Segment> iter = s.segments;
+ while (++iter) {
+ Segment* seg1 = iter.value();
+ Segment* seg2 = new(__FILE__,__LINE__) Segment;
+
+ seg2->npolys = seg1->npolys;
+ seg2->polys = polys + (seg1->polys - s.polys);
+
+ if (seg2->polys[0].material)
+ seg2->material = seg2->polys[0].material;
+
+ seg2->model = model;
+ seg2->video_data = 0;
+
+ segments.append(seg2);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Surface::SetName(const char* n)
+{
+ int len = sizeof(name);
+
+ ZeroMemory(name, len);
+ strncpy(name, n, len-1);
+}
+
+void
+Surface::SetHidden(bool b)
+{
+ if (b)
+ state = state | HIDDEN;
+
+ else
+ state = state & ~HIDDEN;
+}
+
+void
+Surface::SetLocked(bool b)
+{
+ if (b)
+ state = state | LOCKED;
+
+ else
+ state = state & ~LOCKED;
+}
+
+void
+Surface::SetSimplified(bool b)
+{
+ if (b)
+ state = state | SIMPLE;
+
+ else
+ state = state & ~SIMPLE;
+}
+
+void
+Surface::CreateVerts(int nverts)
+{
+ if (!vertex_set && !vloc) {
+ vertex_set = new(__FILE__,__LINE__) VertexSet(nverts);
+ vloc = new(__FILE__,__LINE__) Vec3[nverts];
+ }
+}
+
+void
+Surface::CreatePolys(int np)
+{
+ if (!polys && !npolys) {
+ npolys = np;
+ polys = new(__FILE__,__LINE__) Poly[npolys];
+
+ ZeroMemory(polys, npolys * sizeof(Poly));
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Poly*
+Surface::AddPolys(int np, int nv)
+{
+ if ( polys && vertex_set &&
+ np > 0 && np + npolys < MAX_POLYS &&
+ nv > 0 && nv + vertex_set->nverts < MAX_VERTS)
+ {
+ int newverts = nv + vertex_set->nverts;
+ int newpolys = np + npolys;
+
+ vertex_set->Resize(newverts, true);
+
+ Poly* pset = new(__FILE__,__LINE__) Poly[newpolys];
+ Poly* pnew = pset + npolys;
+
+ CopyMemory(pset, polys, npolys * sizeof(Poly));
+ ZeroMemory(pnew, np * sizeof(Poly));
+
+ if (segments.size() > 0) {
+ Segment* seg = segments.last();
+ Material* mtl = seg->material;
+
+ for (int i = 0; i < np; i++) {
+ Poly* p = pnew + i;
+ p->material = mtl;
+ }
+
+ seg->npolys += np;
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Surface::ExplodeMesh()
+{
+ if (!vertex_set || vertex_set->nverts < 3)
+ return;
+
+ int i, j, v;
+ int nverts = 0;
+
+ // count max verts:
+ for (i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+ nverts += p->nverts;
+ }
+
+ // create target vertex set:
+ VertexSet* vset = new(__FILE__,__LINE__) VertexSet(nverts);
+ v = 0;
+
+ // explode verts:
+ for (i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+ p->vertex_set = vset;
+
+ for (j = 0; j < p->nverts; j++) {
+ int vsrc = p->verts[j];
+
+ vset->loc[v] = vertex_set->loc[vsrc];
+ vset->nrm[v] = vertex_set->nrm[vsrc];
+ vset->tu[v] = vertex_set->tu[vsrc];
+ vset->tv[v] = vertex_set->tv[vsrc];
+ vset->rw[v] = vertex_set->rw[vsrc];
+ vset->diffuse[v] = vertex_set->diffuse[vsrc];
+ vset->specular[v] = vertex_set->specular[vsrc];
+
+ p->verts[j] = v++;
+ }
+ }
+
+ // finalize:
+ if (vset) {
+ delete vertex_set;
+ vertex_set = vset;
+ }
+
+ if (vloc)
+ delete [] vloc;
+
+ vloc = new(__FILE__,__LINE__) Vec3[nverts];
+
+ ComputeTangents();
+ BuildHull();
+}
+
+// +--------------------------------------------------------------------+
+
+const double SELECT_EPSILON = 0.05;
+const double SELECT_TEXTURE = 0.0001;
+
+static bool MatchVerts(VertexSet* vset, int i, int j)
+{
+ double d = 0;
+ const Vec3& vl1 = vset->loc[i];
+ const Vec3& vn1 = vset->nrm[i];
+ float tu1 = vset->tu[i];
+ float tv1 = vset->tv[i];
+ const Vec3& vl2 = vset->loc[j];
+ const Vec3& vn2 = vset->nrm[j];
+ float tu2 = vset->tu[j];
+ float tv2 = vset->tv[j];
+
+ d = fabs(vl1.x - vl2.x);
+ if (d > SELECT_EPSILON)
+ return false;
+
+ d = fabs(vl1.y - vl2.y);
+ if (d > SELECT_EPSILON)
+ return false;
+
+ d = fabs(vl1.z - vl2.z);
+ if (d > SELECT_EPSILON)
+ return false;
+
+ d = fabs(vn1.x - vn2.x);
+ if (d > SELECT_EPSILON)
+ return false;
+
+ d = fabs(vn1.y - vn2.y);
+ if (d > SELECT_EPSILON)
+ return false;
+
+ d = fabs(vn1.z - vn2.z);
+ if (d > SELECT_EPSILON)
+ return false;
+
+ d = fabs(tu1 - tu2);
+ if (d > SELECT_TEXTURE)
+ return false;
+
+ d = fabs(tv1 - tv2);
+ if (d > SELECT_TEXTURE)
+ return false;
+
+ return true;
+}
+
+void
+Surface::OptimizeMesh()
+{
+ if (!vertex_set || vertex_set->nverts < 3)
+ return;
+
+ int i, j, n, v;
+ int nverts = vertex_set->nverts;
+ int used = 0;
+ int final = 0;
+ int nmatch = 0;
+
+ // create vertex maps:
+ BYTE* vert_map = new BYTE[nverts];
+ WORD* vert_dst = new WORD[nverts];
+ ZeroMemory(vert_map, nverts * sizeof(BYTE));
+ ZeroMemory(vert_dst, nverts * sizeof(WORD));
+
+ // count used verts:
+ for (i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+
+ for (j = 0; j < p->nverts; j++) {
+ WORD vert = p->verts[j];
+
+ if (vert < nverts) {
+ vert_map[vert]++;
+ used++;
+ }
+ }
+ }
+
+ // create target vertex set:
+ VertexSet* vset = new(__FILE__,__LINE__) VertexSet(used);
+ v = 0;
+
+ // compress verts:
+ for (i = 0; i < nverts; i++) {
+ if (vert_map[i] == 0) continue;
+
+ vert_dst[i] = v;
+ vset->loc[v] = vertex_set->loc[i];
+ vset->nrm[v] = vertex_set->nrm[i];
+ vset->tu[v] = vertex_set->tu[i];
+ vset->tv[v] = vertex_set->tv[i];
+ vset->rw[v] = vertex_set->rw[i];
+ vset->diffuse[v] = vertex_set->diffuse[i];
+ vset->specular[v] = vertex_set->specular[i];
+
+ for (int j = i+1; j < nverts; j++) {
+ if (vert_map[j] == 0) continue;
+
+ if (MatchVerts(vertex_set, i, j)) {
+ vert_map[j] = 0;
+ vert_dst[j] = v;
+ nmatch++;
+ }
+ }
+
+ v++;
+ }
+
+ final = v;
+
+ // remap polys:
+ for (n = 0; n < npolys; n++) {
+ Poly* p = polys + n;
+ p->vertex_set = vset;
+ for (v = 0; v < p->nverts; v++) {
+ p->verts[v] = vert_dst[ p->verts[v] ];
+ }
+ }
+
+ // finalize:
+ if (vset && final < nverts) {
+ delete vertex_set;
+ vertex_set = vset;
+ vset->Resize(final, true);
+ nverts = final;
+ }
+
+ // clean up and rebuild hull:
+ delete [] vert_map;
+
+ if (vloc)
+ delete [] vloc;
+
+ vloc = new(__FILE__,__LINE__) Vec3[nverts];
+
+ ComputeTangents();
+ BuildHull();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Surface::ScaleBy(double factor)
+{
+ offset *= factor;
+
+ if (vertex_set && vertex_set->nverts) {
+ for (int i = 0; i < vertex_set->nverts; i++) {
+ vertex_set->loc[i] *= (float) factor;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Surface::BuildHull()
+{
+ if (npolys < 1 || !vertex_set || vertex_set->nverts < 1)
+ return;
+
+ nhull = 0;
+
+ for (int i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+
+ for (int n = 0; n < p->nverts; n++) {
+ WORD v = p->verts[n];
+ WORD h;
+
+ for (h = 0; h < nhull; h++) {
+ Vec3& vl = vertex_set->loc[v];
+ Vec3& loc = vloc[h];
+
+ double d = vl.x - loc.x;
+
+ if (d < -SELECT_EPSILON || d > SELECT_EPSILON)
+ continue;
+
+ d = vl.y - loc.y;
+
+ if (d < -SELECT_EPSILON || d > SELECT_EPSILON)
+ continue;
+
+ d = vl.z - loc.z;
+
+ if (d < -SELECT_EPSILON || d > SELECT_EPSILON)
+ continue;
+
+ // found a match:
+ break;
+ }
+
+ // didn't find a match:
+ if (h >= nhull) {
+ vloc[h] = vertex_set->loc[v];
+ nhull = h+1;
+ }
+
+ p->vlocs[n] = h;
+ }
+ }
+
+ if (use_collision_detection)
+ InitializeCollisionHull();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Surface::Normalize()
+{
+ if (npolys < 1 || !vertex_set || vertex_set->nverts < 1)
+ return;
+
+ // STEP ONE: initialize poly planes
+
+ for (int i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+
+ p->plane = Plane( vertex_set->loc[ p->verts[0] ],
+ vertex_set->loc[ p->verts[2] ],
+ vertex_set->loc[ p->verts[1] ] );
+ }
+
+ // STEP TWO: compute vertex normals by averaging adjecent poly planes
+
+ List<Poly> faces;
+ for (int v = 0; v < vertex_set->nverts; v++) {
+ faces.clear();
+ SelectPolys(faces, vertex_set->loc[v]);
+
+ if (faces.size()) {
+ vertex_set->nrm[v] = Vec3(0.0f, 0.0f, 0.0f);
+
+ for (i = 0; i < faces.size(); i++) {
+ vertex_set->nrm[v] += faces[i]->plane.normal;
+ }
+
+ vertex_set->nrm[v].Normalize();
+ }
+
+ else if (vertex_set->loc[v].length() > 0) {
+ vertex_set->nrm[v] = vertex_set->loc[v];
+ vertex_set->nrm[v].Normalize();
+ }
+
+ else {
+ vertex_set->nrm[v] = Vec3(0.0f, 1.0f, 0.0f);
+ }
+ }
+
+ // STEP THREE: adjust vertex normals for poly flatness
+
+ for (i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+
+ for (int n = 0; n < p->nverts; n++) {
+ int v = p->verts[n];
+
+ vertex_set->nrm[v] = vertex_set->nrm[v] * (1.0f - p->flatness) +
+ p->plane.normal * ( p->flatness);
+ }
+ }
+}
+
+void
+Surface::SelectPolys(List<Poly>& selection, Vec3 loc)
+{
+ const double SELECT_EPSILON = 0.05;
+
+ for (int i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+
+ for (int n = 0; n < p->nverts; n++) {
+ int v = p->verts[n];
+ Vec3& vl = vertex_set->loc[v];
+ double d = vl.x - loc.x;
+
+ if (d < -SELECT_EPSILON || d > SELECT_EPSILON)
+ continue;
+
+ d = vl.y - loc.y;
+
+ if (d < -SELECT_EPSILON || d > SELECT_EPSILON)
+ continue;
+
+ d = vl.z - loc.z;
+
+ if (d < -SELECT_EPSILON || d > SELECT_EPSILON)
+ continue;
+
+ selection.append(p);
+ break;
+ }
+ }
+}
+
+void
+Surface::SelectPolys(List<Poly>& selection, Material* m)
+{
+ for (int i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+
+ if (p->material == m)
+ selection.append(p);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Surface::ComputeTangents()
+{
+ Vec3 tangent;
+ Vec3 binormal;
+
+ if (!vertex_set || !vertex_set->nverts)
+ return;
+
+ if (vertex_set->tangent)
+ return;
+
+ vertex_set->CreateTangents();
+
+ for (int i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+
+ CalcGradients(*p, tangent, binormal);
+
+ for (int n = 0; n < p->nverts; n++) {
+ vertex_set->tangent[p->verts[n]] = tangent;
+ vertex_set->binormal[p->verts[n]] = binormal;
+ }
+ }
+}
+
+void
+Surface::CalcGradients(Poly& p, Vec3& tangent, Vec3& binormal)
+{
+ // using Eric Lengyel's approach with a few modifications
+ // from Mathematics for 3D Game Programmming and Computer Graphics
+ // want to be able to trasform a vector in Object Space to Tangent Space
+ // such that the x-axis cooresponds to the 's' direction and the
+ // y-axis corresponds to the 't' direction, and the z-axis corresponds
+ // to <0,0,1>, straight up out of the texture map
+
+ VertexSet* vset = p.vertex_set;
+
+ Vec3 P = vset->loc[p.verts[1]] - vset->loc[p.verts[0]];
+ Vec3 Q = vset->loc[p.verts[2]] - vset->loc[p.verts[0]];
+
+ float s1 = vset->tu[p.verts[1]] - vset->tu[p.verts[0]];
+ float t1 = vset->tv[p.verts[1]] - vset->tv[p.verts[0]];
+ float s2 = vset->tu[p.verts[2]] - vset->tu[p.verts[0]];
+ float t2 = vset->tv[p.verts[2]] - vset->tv[p.verts[0]];
+
+ float tmp = 1.0f;
+ float denom = s1*t2 - s2*t1;
+
+ if (fabsf(denom) > 0.0001f)
+ tmp = 1.0f/(denom);
+
+ tangent.x = (t2*P.x - t1*Q.x) * tmp;
+ tangent.y = (t2*P.y - t1*Q.y) * tmp;
+ tangent.z = (t2*P.z - t1*Q.z) * tmp;
+
+ tangent.Normalize();
+
+ binormal.x = (s1*Q.x - s2*P.x) * tmp;
+ binormal.y = (s1*Q.y - s2*P.y) * tmp;
+ binormal.z = (s1*Q.z - s2*P.z) * tmp;
+
+ binormal.Normalize();
+}
+
+void
+Surface::InitializeCollisionHull()
+{
+ opcode = new(__FILE__,__LINE__) OPCODE_data(this);
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+Segment::Segment()
+{
+ ZeroMemory(this, sizeof(Segment));
+}
+
+Segment::Segment(int n, Poly* p, Material* mtl, Model* mod)
+ : npolys(n), polys(p), material(mtl), model(mod), video_data(0)
+{
+}
+
+Segment::~Segment()
+{
+ delete video_data;
+
+ ZeroMemory(this, sizeof(Segment));
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+ModelFile::ModelFile(const char* fname)
+ : model(0), pname(0), pnverts(0), pnpolys(0), pradius(0)
+{
+ int len = sizeof(filename);
+ ZeroMemory(filename, len);
+ strncpy(filename, fname, len);
+ filename[len-1] = 0;
+}
+
+ModelFile::~ModelFile()
+{
+}
+
+bool
+ModelFile::Load(Model* m, double scale)
+{
+ model = m;
+
+ // expose model innards for child classes:
+
+ if (model) {
+ pname = model->name;
+ pnverts = &model->nverts;
+ pnpolys = &model->npolys;
+ pradius = &model->radius;
+ }
+
+ return false;
+}
+
+bool
+ModelFile::Save(Model* m)
+{
+ model = m;
+
+ // expose model innards for child classes:
+
+ if (model) {
+ pname = model->name;
+ pnverts = &model->nverts;
+ pnpolys = &model->npolys;
+ pradius = &model->radius;
+ }
+
+ return false;
+}
+
diff --git a/nGenEx/Solid.h b/nGenEx/Solid.h
new file mode 100644
index 0000000..99a49f7
--- /dev/null
+++ b/nGenEx/Solid.h
@@ -0,0 +1,313 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Solid.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Classes for rendering solid meshes of polygons
+*/
+
+#ifndef Solid_h
+#define Solid_h
+
+#include "Polygon.h"
+#include "Graphic.h"
+#include "Video.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Solid;
+class Model;
+class ModelFile;
+class Surface;
+class Segment;
+class Shadow;
+class Light;
+
+class OPCODE_data; // for collision detection
+
+// +--------------------------------------------------------------------+
+
+class Solid : public Graphic
+{
+public:
+ static const char* TYPENAME() { return "Solid"; }
+
+ enum { NAMELEN = 64 };
+
+ static bool IsCollisionEnabled();
+ static void EnableCollision(bool enable);
+
+ Solid();
+ virtual ~Solid();
+
+ // operations
+ virtual void Render(Video* video, DWORD flags);
+ virtual void SelectDetail(Projector* p);
+ virtual void ProjectScreenRect(Projector* p);
+ virtual void Update();
+
+ // accessors / mutators
+ Model* GetModel() const { return model; }
+ void GetAllTextures(List<Bitmap>& textures);
+
+ virtual bool IsDynamic() const;
+ virtual void SetDynamic(bool d);
+ virtual void SetLuminous(bool l);
+ virtual void SetOrientation(const Matrix& o);
+ virtual void SetOrientation(const Solid& match);
+ const Matrix& Orientation() const { return orientation; }
+ float Roll() const { return roll; }
+ float Pitch() const { return pitch; }
+ float Yaw() const { return yaw; }
+ virtual bool IsSolid() const { return true; }
+
+ // stencil shadows
+ virtual void CreateShadows(int nlights=1);
+ virtual void UpdateShadows(List<Light>& lights);
+ List<Shadow>& GetShadows() { return shadows; }
+
+ bool Load(const char* mag_file, double scale=1.0);
+ bool Load(ModelFile* loader, double scale=1.0);
+ void UseModel(Model* model);
+ void ClearModel();
+ bool Rescale(double scale);
+
+ // collision detection
+ virtual int CollidesWith(Graphic& o);
+ virtual int CheckRayIntersection(Point pt, Point vpn, double len, Point& ipt,
+ bool treat_translucent_polys_as_solid=true);
+ virtual Poly* GetIntersectionPoly() const { return intersection_poly; }
+
+ // buffer management
+ virtual void DeletePrivateData();
+ virtual void InvalidateSurfaceData();
+ virtual void InvalidateSegmentData();
+
+protected:
+ Model* model;
+ bool own_model;
+
+ float roll, pitch, yaw;
+ Matrix orientation;
+ Poly* intersection_poly;
+
+ List<Shadow> shadows;
+};
+
+// +--------------------------------------------------------------------+
+
+class Model
+{
+ friend class Solid;
+ friend class ModelFile;
+
+public:
+ static const char* TYPENAME() { return "Model"; }
+
+ enum { MAX_VERTS = 64000, MAX_POLYS = 16000 };
+
+ Model();
+ Model(const Model& m);
+ ~Model();
+
+ Model& operator = (const Model& m);
+ int operator == (const Model& that) const { return this == &that; }
+
+ bool Load(const char* mag_file, double scale=1.0);
+ bool Load(ModelFile* loader, double scale=1.0);
+
+ const char* Name() const { return name; }
+ int NumVerts() const { return nverts; }
+ int NumSurfaces() const { return surfaces.size(); }
+ int NumMaterials() const { return materials.size(); }
+ int NumPolys() const { return npolys; }
+ int NumSegments() const;
+ double Radius() const { return radius; }
+ bool IsDynamic() const { return dynamic; }
+ void SetDynamic(bool d) { dynamic = d; }
+ bool IsLuminous() const { return luminous; }
+ void SetLuminous(bool l) { luminous = l; }
+
+ List<Surface>& GetSurfaces() { return surfaces; }
+ List<Material>& GetMaterials() { return materials; }
+ const Material* FindMaterial(const char* mtl_name) const;
+ const Material* ReplaceMaterial(const Material* mtl);
+ void GetAllTextures(List<Bitmap>& textures);
+
+ Poly* AddPolys(int nsurf, int npolys, int nverts);
+ void ExplodeMesh();
+ void OptimizeMesh();
+ void OptimizeMaterials();
+ void ScaleBy(double factor);
+
+ void Normalize();
+ void SelectPolys(List<Poly>&, Material* mtl);
+ void SelectPolys(List<Poly>&, Vec3 loc);
+
+ void AddSurface(Surface* s);
+ void ComputeTangents();
+
+ // buffer management
+ void DeletePrivateData();
+
+private:
+ bool LoadMag5(BYTE* block, int len, double scale);
+ bool LoadMag6(BYTE* block, int len, double scale);
+
+ char name[Solid::NAMELEN];
+ List<Surface> surfaces;
+ List<Material> materials;
+ int nverts;
+ int npolys;
+ float radius;
+ float extents[6];
+ bool luminous;
+ bool dynamic;
+};
+
+// +--------------------------------------------------------------------+
+
+class Surface
+{
+ friend class Solid;
+ friend class Model;
+
+public:
+ static const char* TYPENAME() { return "Surface"; }
+
+ enum { HIDDEN=1, LOCKED=2, SIMPLE=4, MAX_VERTS=64000, MAX_POLYS=16000 };
+
+ Surface();
+ ~Surface();
+
+ int operator == (const Surface& s) const { return this == &s; }
+
+ const char* Name() const { return name; }
+ int NumVerts() const { return vertex_set ? vertex_set->nverts : 0; }
+ int NumSegments() const { return segments.size(); }
+ int NumPolys() const { return npolys; }
+ int NumIndices() const { return nindices; }
+ bool IsHidden() const { return state & HIDDEN ? true : false; }
+ bool IsLocked() const { return state & LOCKED ? true : false; }
+ bool IsSimplified() const { return state & SIMPLE ? true : false; }
+
+ Model* GetModel() const { return model; }
+ List<Segment>& GetSegments() { return segments; }
+ const Point& GetOffset() const { return offset; }
+ const Matrix& GetOrientation() const { return orientation; }
+ double Radius() const { return radius; }
+ VertexSet* GetVertexSet() const { return vertex_set; }
+ Vec3* GetVLoc() const { return vloc; }
+ Poly* GetPolys() const { return polys; }
+
+ void SetName(const char* n);
+ void SetHidden(bool b);
+ void SetLocked(bool b);
+ void SetSimplified(bool b);
+
+ void CreateVerts(int nverts);
+ void CreatePolys(int npolys);
+ void AddIndices(int n) { nindices += n; }
+ Poly* AddPolys(int npolys, int nverts);
+
+ VideoPrivateData* GetVideoPrivateData() const { return video_data; }
+ void SetVideoPrivateData(VideoPrivateData* vpd)
+ { video_data = vpd; }
+
+ void ScaleBy(double factor);
+
+ void BuildHull();
+ void Normalize();
+ void SelectPolys(List<Poly>&, Material* mtl);
+ void SelectPolys(List<Poly>&, Vec3 loc);
+
+ void InitializeCollisionHull();
+ void ComputeTangents();
+ void CalcGradients(Poly& p, Vec3& tangent, Vec3& binormal);
+
+ void Copy(Surface& s, Model* m);
+ void OptimizeMesh();
+ void ExplodeMesh();
+
+private:
+ char name[Solid::NAMELEN];
+ Model* model;
+ VertexSet* vertex_set; // for rendering
+ Vec3* vloc; // for shadow hull
+ float radius;
+ int nhull;
+ int npolys;
+ int nindices;
+ int state;
+ Poly* polys;
+ List<Segment> segments;
+
+ Point offset;
+ Matrix orientation;
+
+public:
+ OPCODE_data* opcode;
+
+private:
+ VideoPrivateData* video_data;
+};
+
+// +--------------------------------------------------------------------+
+
+class Segment
+{
+public:
+ static const char* TYPENAME() { return "Segment"; }
+
+ Segment();
+ Segment(int n, Poly* p, Material* mtl, Model* mod=0);
+ ~Segment();
+
+ bool IsSolid() const { return material ? material->IsSolid() : true; }
+ bool IsTranslucent() const { return material ? material->IsTranslucent(): false; }
+ bool IsGlowing() const { return material ? material->IsGlowing() : false; }
+
+ VideoPrivateData* GetVideoPrivateData() const { return video_data; }
+ void SetVideoPrivateData(VideoPrivateData* vpd)
+ { video_data = vpd; }
+
+ int npolys;
+ Poly* polys;
+ Material* material;
+ Model* model;
+ VideoPrivateData* video_data;
+};
+
+// +--------------------------------------------------------------------+
+
+class ModelFile
+{
+public:
+ ModelFile(const char* fname);
+ virtual ~ModelFile();
+
+ virtual bool Load(Model* m, double scale=1.0);
+ virtual bool Save(Model* m);
+
+protected:
+ char filename[256];
+ Model* model;
+
+ // internal accessors:
+ char* pname;
+ int* pnverts;
+ int* pnpolys;
+ float* pradius;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Solid_h
+
diff --git a/nGenEx/Sound.cpp b/nGenEx/Sound.cpp
new file mode 100644
index 0000000..433ebd9
--- /dev/null
+++ b/nGenEx/Sound.cpp
@@ -0,0 +1,284 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Sound.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract sound class
+*/
+
+#include "MemDebug.h"
+#include "Sound.h"
+#include "SoundCard.h"
+#include "Wave.h"
+
+#include <vorbis/codec.h>
+#include <vorbis/vorbisfile.h>
+
+// +--------------------------------------------------------------------+
+
+SoundCard* Sound::creator = 0;
+
+Sound*
+Sound::CreateStream(const char* filename)
+{
+ Sound* sound = 0;
+
+ if (!filename || !filename[0] || !creator)
+ return sound;
+
+ int namelen = strlen(filename);
+
+ if (namelen < 5)
+ return sound;
+
+ if ((filename[namelen-3] == 'o' || filename[namelen-3] == 'O') &&
+ (filename[namelen-2] == 'g' || filename[namelen-2] == 'G') &&
+ (filename[namelen-1] == 'g' || filename[namelen-1] == 'G')) {
+
+ return CreateOggStream(filename);
+ }
+
+ WAVE_HEADER head;
+ WAVE_FMT fmt;
+ WAVE_FACT fact;
+ WAVE_DATA data;
+ WAVEFORMATEX wfex;
+
+ ZeroMemory(&head, sizeof(head));
+ ZeroMemory(&fmt, sizeof(fmt));
+ ZeroMemory(&fact, sizeof(fact));
+ ZeroMemory(&data, sizeof(data));
+
+ LPBYTE buf = 0;
+ LPBYTE p = 0;
+ int len = 0;
+
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ if (len > 4096) {
+ len = 4096;
+ }
+
+ buf = new(__FILE__,__LINE__) BYTE[len];
+
+ if (buf && len)
+ fread(buf, len, 1, f);
+
+ fclose(f);
+ }
+
+
+ if (len > sizeof(head)) {
+ CopyMemory(&head, buf, sizeof(head));
+
+ if (head.RIFF == MAKEFOURCC('R', 'I', 'F', 'F') &&
+ head.WAVE == MAKEFOURCC('W', 'A', 'V', 'E')) {
+
+ p = buf + sizeof(WAVE_HEADER);
+
+ do {
+ DWORD chunk_id = *((LPDWORD) p);
+
+ switch (chunk_id) {
+ case MAKEFOURCC('f', 'm', 't', ' '):
+ CopyMemory(&fmt, p, sizeof(fmt));
+ p += fmt.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('f', 'a', 'c', 't'):
+ CopyMemory(&fact, p, sizeof(fact));
+ p += fact.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('s', 'm', 'p', 'l'):
+ CopyMemory(&fact, p, sizeof(fact));
+ p += fact.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('d', 'a', 't', 'a'):
+ CopyMemory(&data, p, sizeof(data));
+ p += 8;
+ break;
+
+ default:
+ delete buf;
+ return sound;
+ }
+ }
+ while (data.chunk_size == 0);
+
+ wfex.wFormatTag = fmt.wFormatTag;
+ wfex.nChannels = fmt.nChannels;
+ wfex.nSamplesPerSec = fmt.nSamplesPerSec;
+ wfex.nAvgBytesPerSec = fmt.nAvgBytesPerSec;
+ wfex.nBlockAlign = fmt.nBlockAlign;
+ wfex.wBitsPerSample = fmt.wBitsPerSample;
+ wfex.cbSize = 0;
+
+ sound = Create(Sound::STREAMED, &wfex);
+
+ if (sound) {
+ sound->SetFilename(filename);
+ sound->StreamFile(filename, p - buf);
+ }
+ }
+ }
+
+ delete buf;
+ return sound;
+}
+
+// +--------------------------------------------------------------------+
+
+Sound*
+Sound::CreateOggStream(const char* filename)
+{
+ Sound* sound = 0;
+
+ if (!filename || !filename[0] || !creator)
+ return sound;
+
+ int namelen = strlen(filename);
+
+ if (namelen < 5)
+ return sound;
+
+ WAVEFORMATEX wfex;
+ ZeroMemory(&wfex, sizeof(wfex));
+
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ OggVorbis_File* povf = new(__FILE__,__LINE__) OggVorbis_File;
+
+ if (!povf) {
+ Print("Sound::CreateOggStream(%s) - out of memory!\n", filename);
+ return sound;
+ }
+
+ ZeroMemory(povf, sizeof(OggVorbis_File));
+
+ if (ov_open(f, povf, NULL, 0) < 0) {
+ Print("Sound::CreateOggStream(%s) - not an Ogg bitstream\n", filename);
+ delete povf;
+ return sound;
+ }
+
+ Print("\nOpened Ogg Bitstream '%s'\n", filename);
+ char **ptr=ov_comment(povf,-1)->user_comments;
+ vorbis_info *vi=ov_info(povf,-1);
+ while(*ptr){
+ Print("%s\n", *ptr);
+ ++ptr;
+ }
+
+ Print("Bitstream is %d channel, %ldHz\n", vi->channels, vi->rate);
+ Print("Decoded length: %ld samples\n",
+ (long)ov_pcm_total(povf,-1));
+ Print("Encoded by: %s\n\n", ov_comment(povf,-1)->vendor);
+
+ wfex.wFormatTag = WAVE_FORMAT_PCM;
+ wfex.nChannels = vi->channels;
+ wfex.nSamplesPerSec = vi->rate;
+ wfex.nAvgBytesPerSec = vi->channels * vi->rate * 2;
+ wfex.nBlockAlign = vi->channels * 2;
+ wfex.wBitsPerSample = 16;
+ wfex.cbSize = 0;
+
+ sound = Create(Sound::STREAMED | Sound::OGGVORBIS,
+ &wfex,
+ sizeof(OggVorbis_File),
+ (LPBYTE) povf);
+
+ sound->SetFilename(filename);
+ }
+
+ return sound;
+}
+
+// +--------------------------------------------------------------------+
+
+Sound*
+Sound::Create(DWORD flags, LPWAVEFORMATEX format)
+{
+ if (creator) return creator->CreateSound(flags, format);
+ else return 0;
+}
+
+Sound*
+Sound::Create(DWORD flags, LPWAVEFORMATEX format, DWORD len, LPBYTE data)
+{
+ if (creator) return creator->CreateSound(flags, format, len, data);
+ else return 0;
+}
+
+void
+Sound::SetListener(const Camera& cam, const Vec3& vel)
+{
+ if (creator)
+ creator->SetListener(cam, vel);
+}
+
+// +--------------------------------------------------------------------+
+
+Sound::Sound()
+ : status(UNINITIALIZED), volume(0), flags(0), looped(0),
+ velocity(0,0,0), location(0,0,0), sound_check(0)
+{
+ strcpy(filename, "Sound()");
+}
+
+// +--------------------------------------------------------------------+
+
+Sound::~Sound()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+Sound::Release()
+{
+ flags &= ~LOCKED;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sound::AddToSoundCard()
+{
+ if (creator)
+ creator->AddSound(this);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sound::SetFilename(const char* s)
+{
+ if (s) {
+ int n = strlen(s);
+
+ if (n >= 60) {
+ ZeroMemory(filename, sizeof(filename));
+ strcpy(filename, "...");
+ strcat(filename, s + n - 59);
+ filename[63] = 0;
+ }
+
+ else {
+ strcpy(filename, s);
+ }
+ }
+}
+
diff --git a/nGenEx/Sound.h b/nGenEx/Sound.h
new file mode 100644
index 0000000..acd03ad
--- /dev/null
+++ b/nGenEx/Sound.h
@@ -0,0 +1,150 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Sound.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Sound Object
+*/
+
+#ifndef Sound_h
+#define Sound_h
+
+#include "Types.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class SoundCard;
+class SoundCheck;
+class Camera;
+
+// +--------------------------------------------------------------------+
+
+class Sound
+{
+public:
+ static const char* TYPENAME() { return "Sound"; }
+
+ static Sound* CreateStream(const char* filename);
+ static Sound* CreateOggStream(const char* filename);
+ static Sound* Create(DWORD flags, LPWAVEFORMATEX format);
+ static Sound* Create(DWORD flags, LPWAVEFORMATEX format, DWORD len, LPBYTE data);
+ static void SetListener(const Camera& cam, const Vec3& vel);
+ static void UseSoundCard(SoundCard* s) { creator = s; }
+
+public:
+ Sound();
+ virtual ~Sound();
+
+ int operator==(const Sound& rhs) const { return this == &rhs; }
+
+ enum FlagEnum { AMBIENT = 0x0000,
+ LOCALIZED = 0x0001,
+ LOC_3D = 0x0002,
+ MEMORY = 0x0000,
+ STREAMED = 0x0004,
+ ONCE = 0x0000,
+ LOOP = 0x0008,
+ FREE = 0x0000,
+ LOCKED = 0x0010,
+ DOPPLER = 0x0020,
+ INTERFACE = 0x0040,
+ OGGVORBIS = 0x4000,
+ RESOURCE = 0x8000 // not playable, only used to store data
+ };
+
+ enum StatusEnum { UNINITIALIZED,
+ INITIALIZING,
+ READY,
+ PLAYING,
+ DONE };
+
+ // once per frame:
+ virtual void Update() { }
+
+ // mark for collection:
+ virtual void Release();
+
+ // data loading:
+ // this method is for streamed sounds:
+ virtual HRESULT StreamFile(const char* name, DWORD offset) { return E_NOINTERFACE; }
+
+ // this method is for memory sounds:
+ virtual HRESULT Load(DWORD bytes, BYTE* data) { return E_NOINTERFACE; } // => Ready
+
+ // this method is for sound resources:
+ virtual Sound* Duplicate() { return 0; } // => Ready
+
+ // transport operations:
+ virtual HRESULT Play() { return E_NOINTERFACE; } // => Playing
+ virtual HRESULT Rewind() { return E_NOINTERFACE; } // => Ready
+ virtual HRESULT Pause() { return E_NOINTERFACE; } // => Ready
+ virtual HRESULT Stop() { return E_NOINTERFACE; } // => Done
+
+ // accessors / mutators
+ int IsReady() const { return status == READY; }
+ int IsPlaying() const { return status == PLAYING; }
+ int IsDone() const { return status == DONE; }
+ int LoopCount() const { return looped; }
+
+ virtual DWORD GetFlags() const { return flags; }
+ virtual void SetFlags(DWORD f) { flags = f; }
+ virtual DWORD GetStatus() const { return status; }
+
+ virtual long GetVolume() const { return volume; }
+ virtual void SetVolume(long v) { volume = v; }
+ virtual long GetPan() const { return 0; }
+ virtual void SetPan(long p) { }
+
+ // (only for streamed sounds)
+ virtual double GetTotalTime() const { return 0; }
+ virtual double GetTimeRemaining() const { return 0; }
+ virtual double GetTimeElapsed() const { return 0; }
+
+ // These should be relative to the listener:
+ // (only used for localized sounds)
+ virtual const Vec3& GetLocation() const { return location; }
+ virtual void SetLocation(const Vec3& l) { location = l; }
+ virtual const Vec3& GetVelocity() const { return velocity; }
+ virtual void SetVelocity(const Vec3& v) { velocity = v; }
+
+ virtual float GetMinDistance() const { return 0; }
+ virtual void SetMinDistance(float f) { }
+ virtual float GetMaxDistance() const { return 0; }
+ virtual void SetMaxDistance(float f) { }
+
+ virtual void SetSoundCheck(SoundCheck* s) { sound_check = s; }
+ virtual void AddToSoundCard();
+
+ const char* GetFilename() const { return filename; }
+ void SetFilename(const char* s);
+
+protected:
+ DWORD flags;
+ DWORD status;
+ long volume; // centibels, (0 .. -10000)
+ int looped;
+ Vec3 location;
+ Vec3 velocity;
+ SoundCheck* sound_check;
+ char filename[64];
+
+ static SoundCard* creator;
+};
+
+// +--------------------------------------------------------------------+
+
+class SoundCheck
+{
+public:
+ virtual void Update(Sound* s) { }
+};
+
+#endif Sound_h
+
diff --git a/nGenEx/SoundCard.cpp b/nGenEx/SoundCard.cpp
new file mode 100644
index 0000000..98e9f20
--- /dev/null
+++ b/nGenEx/SoundCard.cpp
@@ -0,0 +1,103 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: SoundCard.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract sound card class
+*/
+
+#include "MemDebug.h"
+#include "SoundCard.h"
+#include "Sound.h"
+
+// +--------------------------------------------------------------------+
+
+DWORD WINAPI SoundCardUpdateProc(LPVOID link);
+
+// +--------------------------------------------------------------------+
+
+SoundCard::SoundCard()
+ : status(SC_UNINITIALIZED), hthread(0), shutdown(false)
+{
+ DWORD thread_id = 0;
+ hthread = CreateThread(0, 4096, SoundCardUpdateProc,
+ (LPVOID) this, 0, &thread_id);
+}
+
+// +--------------------------------------------------------------------+
+
+SoundCard::~SoundCard()
+{
+ shutdown = true;
+
+ WaitForSingleObject(hthread, 500);
+ CloseHandle(hthread);
+ hthread = 0;
+
+ sounds.destroy();
+ status = SC_UNINITIALIZED;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD WINAPI SoundCardUpdateProc(LPVOID link)
+{
+ SoundCard* card = (SoundCard*) link;
+
+ if (card)
+ return card->UpdateThread();
+
+ return (DWORD) E_POINTER;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+SoundCard::UpdateThread()
+{
+ while (!shutdown) {
+ Update();
+ Sleep(50);
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundCard::Update()
+{
+ AutoThreadSync a(sync);
+
+ ListIter<Sound> iter = sounds;
+ while (++iter) {
+ Sound* s = iter.value();
+
+ s->Update();
+
+ if (s->GetStatus() == Sound::DONE &&
+ !(s->GetFlags() & Sound::LOCKED)) {
+
+ delete iter.removeItem();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundCard::AddSound(Sound* s)
+{
+ AutoThreadSync a(sync);
+
+ if (!sounds.contains(s))
+ sounds.append(s);
+}
+
diff --git a/nGenEx/SoundCard.h b/nGenEx/SoundCard.h
new file mode 100644
index 0000000..460a731
--- /dev/null
+++ b/nGenEx/SoundCard.h
@@ -0,0 +1,76 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: SoundCard.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Audio Output class (hides details of DirectSound)
+*/
+
+#ifndef SoundCard_h
+#define SoundCard_h
+
+#include "Types.h"
+#include "List.h"
+#include "ThreadSync.h"
+
+// +--------------------------------------------------------------------+
+
+class Sound;
+class Camera;
+struct Vec3;
+
+// +--------------------------------------------------------------------+
+
+class SoundCard
+{
+public:
+ static const char* TYPENAME() { return "SoundCard"; }
+
+ SoundCard();
+ virtual ~SoundCard();
+
+ enum SoundStatus { SC_UNINITIALIZED,
+ SC_OK,
+ SC_ERROR,
+ SC_BAD_PARAM };
+ SoundStatus Status() const { return status; }
+
+ // Format of the sound card's primary buffer:
+ virtual bool GetFormat(LPWAVEFORMATEX format) { return false; }
+ virtual bool SetFormat(LPWAVEFORMATEX format) { return false; }
+ virtual bool SetFormat(int bits, int channels, int hertz) { return false; }
+ virtual bool Pause() { return false; }
+ virtual bool Resume() { return false; }
+ virtual bool StopSoundEffects() { return false; }
+
+ // Get a blank, writable sound buffer:
+ virtual Sound* CreateSound(DWORD flags, LPWAVEFORMATEX format) { return 0; }
+
+ // Create a sound resource:
+ virtual Sound* CreateSound(DWORD flags, LPWAVEFORMATEX format,
+ DWORD len, LPBYTE data) { return 0; }
+
+ // once per frame:
+ virtual void Update();
+
+ virtual void SetListener(const Camera& cam, const Vec3& vel) { }
+ virtual DWORD UpdateThread();
+ virtual void AddSound(Sound* s);
+
+protected:
+
+ bool shutdown;
+ HANDLE hthread;
+ SoundStatus status;
+ List<Sound> sounds;
+ ThreadSync sync;
+};
+
+#endif SoundCard_h
+
diff --git a/nGenEx/SoundD3D.cpp b/nGenEx/SoundD3D.cpp
new file mode 100644
index 0000000..212e856
--- /dev/null
+++ b/nGenEx/SoundD3D.cpp
@@ -0,0 +1,1295 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: SoundD3D.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ DirectSound and DirectSound3D (sound card) class
+*/
+
+//#define INITGUID
+#include <objbase.h>
+#include <cguid.h>
+#include <mmsystem.h>
+#include <dsound.h>
+
+
+#include "MemDebug.h"
+#include "SoundD3D.h"
+#include "Game.h"
+
+#ifdef DIRECT_SOUND_3D
+#include "ia3d.h"
+#endif
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* msg, ...);
+char* DSErrStr(HRESULT dserr);
+void SoundD3DError(const char* msg, HRESULT dserr);
+
+static int DS3D_report_errors = 1;
+
+#ifndef RELEASE
+#define RELEASE(x) if (x) { x->Release(); x=NULL; }
+#endif
+
+// +====================================================================+
+// | SOUND CARD D3D
+// +====================================================================+
+
+SoundCardD3D::SoundCardD3D(HWND hwnd)
+ : soundcard(0), primary(0)
+{
+ HRESULT err = 0;
+ status = SC_ERROR;
+
+ // 1) Get interface to DirectSound object:
+
+#ifdef DIRECT_SOUND_3D
+ CoInitialize(NULL);
+ err = CoCreateInstance(CLSID_A3d, NULL, CLSCTX_INPROC_SERVER,
+ IID_IDirectSound, (VOID **)&soundcard);
+
+ if (SUCCEEDED(err)) {
+ soundcard->Initialize(NULL);
+ SoundD3DError("Initialized Aureal3D Sound", 0);
+ }
+ else {
+ SoundD3DError("Could not initialize Aureal3D Sound", err);
+ SoundD3DError("Proceding with standard DirectSoundCreate", 0);
+#endif
+
+ err = DirectSoundCreate(0, &soundcard, 0);
+ if (FAILED(err)) {
+ SoundD3DError("Could not create DirectSound object.", err);
+ soundcard = 0;
+ primary = 0;
+ return;
+ }
+
+#ifdef DIRECT_SOUND_3D
+ }
+#endif
+
+ // 2) Set the cooperative level:
+ err = soundcard->SetCooperativeLevel(hwnd, DSSCL_PRIORITY);
+ if (FAILED(err)) {
+ SoundD3DError("Could not set cooperative level.", err);
+ RELEASE(soundcard);
+ return;
+ }
+
+ // Prepare to initialize the primary buffer:
+ DSCAPS caps;
+ memset(&caps, 0, sizeof(caps));
+ caps.dwSize = sizeof(caps);
+
+ err = soundcard->GetCaps(&caps);
+ if (FAILED(err)) {
+ SoundD3DError("Could not get soundcard caps.", err);
+ RELEASE(soundcard);
+ return;
+ }
+
+ if (caps.dwFlags & DSCAPS_EMULDRIVER)
+ Print(" WARNING: using DirectSound emulated drivers\n");
+
+ memset(&dsbd, 0, sizeof(dsbd));
+ dsbd.dwSize = sizeof(dsbd);
+
+#ifdef DIRECT_SOUND_3D
+ int use_ds3d = true;
+
+ // 3) Set up the primary buffer (try to use DS3D):
+ dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRL3D;
+
+ err = soundcard->CreateSoundBuffer(&dsbd, &primary, 0);
+ if (err == DSERR_CONTROLUNAVAIL) {
+ use_ds3d = false;
+
+ // try again, without using DS3D
+ dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLDEFAULT;
+
+ err = soundcard->CreateSoundBuffer(&dsbd, &primary, 0);
+ if (FAILED(err)) {
+ SoundD3DError("Could not initialize primary buffer", err);
+ RELEASE(soundcard);
+ return;
+ }
+ else {
+ Print(" WARNING: DirectSound3D is not available, simulating instead\n");
+ }
+ }
+
+ // 4) Set up the listener:
+ if (primary && use_ds3d) {
+ err = primary->QueryInterface(IID_IDirectSound3DListener, (void**)&listener);
+ if (FAILED(err)) {
+ SoundD3DError("Could not get listener interface", err);
+ }
+ else {
+ listener->SetPosition(0.0f, 0.0f, 0.0f, DS3D_IMMEDIATE);
+ listener->SetOrientation(0.0f, 0.0f, 1.0f,
+ 0.0f, 1.0f, 0.0f, DS3D_IMMEDIATE);
+ }
+ }
+
+#else
+ // 3) Set up the primary buffer:
+ dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
+
+ err = soundcard->CreateSoundBuffer(&dsbd, &primary, 0);
+ if (FAILED(err)) {
+ SoundD3DError("Could not initialize primary buffer", err);
+ RELEASE(soundcard);
+ return;
+ }
+#endif
+
+ // 5) Set primary buffer parameters to 16 bit STEREO at 44kHz
+ SetFormat(16, 2, 44100);
+
+ // read back the result format and display to the log file:
+ GetFormat(0);
+ ShowFormat();
+
+ status = SC_OK;
+}
+
+// +--------------------------------------------------------------------+
+
+SoundCardD3D::~SoundCardD3D()
+{
+ ListIter<Sound> iter = sounds;
+ while (++iter) {
+ Sound* s = iter.value();
+ s->Stop();
+ }
+
+ sounds.destroy();
+
+#ifdef DIRECT_SOUND_3D
+ RELEASE(listener);
+#endif
+
+ RELEASE(primary);
+ RELEASE(soundcard);
+
+ Print(" SoundCardD3D: shutdown\n");
+
+ status = SC_UNINITIALIZED;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::SetFormat(int bits, int channels, int hertz)
+{
+ if (!soundcard) return false;
+
+ DSCAPS caps;
+ memset(&caps, 0, sizeof(caps));
+ caps.dwSize = sizeof(caps);
+ soundcard->GetCaps(&caps);
+
+ if (!(caps.dwFlags & DSCAPS_PRIMARY16BIT)) bits = 8;
+ if (!(caps.dwFlags & DSCAPS_PRIMARYSTEREO)) channels = 1;
+
+ memset(&wfex, 0, sizeof(wfex));
+
+ wfex.wFormatTag = WAVE_FORMAT_PCM;
+ wfex.nChannels = channels;
+ wfex.nSamplesPerSec = hertz;
+ wfex.nBlockAlign = (channels * bits) / 8;
+ wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;
+ wfex.wBitsPerSample = bits;
+
+ return SetFormat(&wfex);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::SetFormat(LPWAVEFORMATEX format)
+{
+ HRESULT err = E_FAIL;
+
+ if (primary)
+ err = primary->SetFormat(format);
+
+ return SUCCEEDED(err);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::GetFormat(LPWAVEFORMATEX format)
+{
+ if (!format) format = &wfex;
+
+ HRESULT err = E_FAIL;
+
+ if (primary)
+ err = primary->GetFormat(format, sizeof(WAVEFORMATEX), 0);
+
+ return SUCCEEDED(err);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundCardD3D::ShowFormat()
+{
+ Print(" SoundCardD3D Primary Buffer Format:\n");
+ Print(" bits: %d\n", wfex.wBitsPerSample);
+ Print(" chls: %d\n", wfex.nChannels);
+ Print(" rate: %d\n\n", wfex.nSamplesPerSec);
+}
+
+// +--------------------------------------------------------------------+
+
+Sound*
+SoundCardD3D::CreateSound(DWORD flags, LPWAVEFORMATEX format)
+{
+ if (!soundcard) return 0;
+
+ Sound* result = new(__FILE__,__LINE__) SoundD3D(soundcard, flags, format);
+ if (result) AddSound(result);
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+Sound*
+SoundCardD3D::CreateSound(DWORD flags, LPWAVEFORMATEX format, DWORD len, LPBYTE data)
+{
+ if (!soundcard) return 0;
+ Sound* result = new(__FILE__,__LINE__) SoundD3D(soundcard, flags, format, len, data);
+
+ if (flags & (Sound::STREAMED | Sound::OGGVORBIS)) {
+ if (result)
+ AddSound(result);
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundCardD3D::SetListener(const Camera& cam, const Vec3& vel)
+{
+ Point pos = cam.Pos();
+
+#ifdef DIRECT_SOUND_3D
+ listener->SetPosition((float) pos.x, (float) pos.z, (float) pos.y, DS3D_IMMEDIATE);
+ listener->SetOrientation((float) cam.vpn().x, (float) cam.vpn().y, (float) cam.vpn().z,
+ (float) cam.vup().x, (float) cam.vup().y, (float) cam.vup().z,
+ DS3D_IMMEDIATE);
+ listener->SetVelocity(vel.x, vel.y, vel.z, DS3D_IMMEDIATE);
+#else
+ listener.Clone(cam);
+ listener.MoveTo(pos.x, pos.z, pos.y);
+ velocity = vel;
+#endif
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::Pause()
+{
+ AutoThreadSync a(sync);
+
+ ListIter<Sound> iter = sounds;
+ while (++iter) {
+ Sound* s = iter.value();
+
+ if ((s->GetFlags() & Sound::INTERFACE) == 0)
+ s->Pause();
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::Resume()
+{
+ AutoThreadSync a(sync);
+
+ ListIter<Sound> iter = sounds;
+ while (++iter) {
+ Sound* s = iter.value();
+
+ if (s->IsReady())
+ s->Play();
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::StopSoundEffects()
+{
+ AutoThreadSync a(sync);
+
+ DWORD ok_sounds = (Sound::INTERFACE | Sound::OGGVORBIS | Sound::RESOURCE);
+
+ ListIter<Sound> iter = sounds;
+ while (++iter) {
+ Sound* s = iter.value();
+
+ if ((s->GetFlags() & ok_sounds) == 0)
+ s->Stop();
+ }
+
+ return true;
+}
+
+// +====================================================================+
+// | SOUND D3D
+// +====================================================================+
+
+SoundD3D::SoundD3D(LPDIRECTSOUND card, DWORD flag_req, LPWAVEFORMATEX format)
+ : soundcard(card), buffer(0), min_dist(1.0f), max_dist(100000.0f),
+ stream(0), stream_left(0), min_safety(0), read_size(0), transfer(0), w(0), r(0),
+ stream_offset(0), data_len(0), data(0), moved(false), eos_written(false), eos_latch(0),
+ ov_file(0), total_time(0)
+{
+ flags = flag_req;
+
+ CopyMemory(&wfex, format, sizeof(wfex));
+ ZeroMemory(&dsbd, sizeof(dsbd));
+
+ dsbd.dwSize = sizeof(dsbd);
+ dsbd.dwFlags = DSBCAPS_CTRLVOLUME /* | DSBCAPS_GETCURRENTPOSITION2 */;
+ dsbd.lpwfxFormat = &wfex;
+
+#ifdef DIRECT_SOUND_3D
+ sound3d = 0;
+ if (flags & LOCALIZED)
+ if (flags & LOC_3D)
+ dsbd.dwFlags |= DSBCAPS_CTRL3D;
+ else
+ dsbd.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
+#else
+ dsbd.dwFlags |= DSBCAPS_LOCSOFTWARE;
+
+ if (flags & LOCALIZED)
+ dsbd.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
+#endif
+
+}
+
+// +--------------------------------------------------------------------+
+// SOUND RESOURCE CONSTRUCTOR:
+// (Now also used to create Ogg Vorbis streaming sound objects)
+
+SoundD3D::SoundD3D(LPDIRECTSOUND card, DWORD flag_req, LPWAVEFORMATEX format, DWORD len, LPBYTE pData)
+ : soundcard(card), buffer(0), min_dist(1.0f), max_dist(100000.0f),
+ stream(0), stream_left(0), min_safety(0), read_size(0), transfer(0), w(0), r(0),
+ stream_offset(0), data_len(0), data(0), moved(false), eos_written(false), eos_latch(0),
+ ov_file(0)
+{
+ flags = flag_req;
+
+ if (!(flags & (STREAMED | OGGVORBIS)))
+ flags = flag_req | RESOURCE;
+
+ CopyMemory(&wfex, format, sizeof(wfex));
+ ZeroMemory(&dsbd, sizeof(dsbd));
+
+ dsbd.dwSize = sizeof(dsbd);
+ dsbd.dwFlags = DSBCAPS_CTRLVOLUME /* | DSBCAPS_GETCURRENTPOSITION2 */;
+ dsbd.lpwfxFormat = &wfex;
+
+#ifdef DIRECT_SOUND_3D
+ sound3d = 0;
+ if (flags & LOCALIZED)
+ if (flags & LOC_3D)
+ dsbd.dwFlags |= DSBCAPS_CTRL3D;
+ else
+ dsbd.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
+#else
+ if (flags & LOCALIZED)
+ dsbd.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
+#endif
+
+ if (len) {
+ // If this is an OGG VORBIS streaming sound,
+ // the parameter that normally points to the actual data
+ // is used to pass in an Ogg Vorbis file structure instead:
+
+ if (flags & OGGVORBIS) {
+ ov_file = (OggVorbis_File*) pData;
+ StreamOggFile();
+ }
+
+ else {
+ data_len = len;
+ data = new(__FILE__,__LINE__) BYTE[len];
+
+ if (!data) {
+ data_len = 0;
+ }
+
+ else {
+ CopyMemory(data, pData, data_len);
+ Load(data_len, data);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+SoundD3D::~SoundD3D()
+{
+ delete [] data;
+ delete [] transfer;
+
+ if (ov_file) {
+ ov_clear(ov_file);
+ delete ov_file;
+ }
+
+ else if (stream) {
+ fclose(stream);
+ }
+
+ RELEASE(buffer);
+
+#ifdef DIRECT_SOUND_3D
+ RELEASE(sound3d);
+#endif
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundD3D::Update()
+{
+ if (!buffer || status != PLAYING) return; // nothing to do
+
+ DWORD dstat;
+ HRESULT hr = buffer->GetStatus(&dstat);
+ if (FAILED(hr)) {
+ SoundD3DError("Update: GetStatus failed", hr);
+ return;
+ }
+
+ AutoThreadSync a(sync);
+
+ if (sound_check) sound_check->Update(this);
+
+ if (!Game::Paused() || flags & STREAMED) {
+ // see if we are done:
+ if (!(dstat & DSBSTATUS_PLAYING)) {
+ status = DONE;
+ buffer->Stop();
+ return;
+ }
+
+ // if not done, see if we need to change location params:
+ if (moved) {
+ Localize();
+ }
+ }
+
+ // if not done, see if we need to load more data
+ // into the streaming buffer:
+ if (flags & STREAMED) {
+ buffer->GetCurrentPosition(&r, 0);
+
+ DWORD data_left;
+ if (w > r)
+ data_left = w - r;
+ else
+ data_left = w + (read_size + min_safety) - r;
+
+ // getting low, fill 'er up:
+ if (eos_written || data_left <= min_safety) {
+ StreamBlock();
+
+ if (stream_left == 0) {
+ // if this is the end of a looping stream,
+ if (flags & LOOP) {
+ RewindStream();
+ looped++;
+ }
+ else {
+ if (!eos_written) {
+ eos_written = true;
+ eos_latch = 3;
+ }
+
+ else if (--eos_latch == 0) {
+ status = DONE;
+ buffer->Stop();
+ }
+ }
+ }
+
+ status = PLAYING;
+ }
+ }
+}
+
+void
+SoundD3D::StreamBlock()
+{
+ if (flags & OGGVORBIS) {
+ StreamOggBlock();
+ return;
+ }
+
+ if (!stream || !stream_left)
+ return;
+
+ if (stream_left < read_size) {
+ if (stream_left > 0) {
+ fread(transfer, stream_left, 1, stream);
+ Load(stream_left, transfer);
+ stream_left = 0;
+ }
+ }
+
+ else if (read_size > 0) {
+ fread(transfer, read_size, 1, stream);
+ Load(read_size, transfer);
+ stream_left -= read_size;
+ }
+}
+
+int ogg_read_error_count = 0;
+
+void
+SoundD3D::StreamOggBlock()
+{
+ int current_bitstream;
+ DWORD bytes_read = 0;
+ long retval = 0;
+ char* p = (char*) transfer;
+
+ while (stream_left && bytes_read < read_size) {
+ retval = ov_read(ov_file, p, read_size-bytes_read, 0,2,1, &current_bitstream);
+
+ if (retval == 0) {
+ /* EOF */
+ stream_left = 0;
+ }
+ else if (retval < 0) {
+ /* error in the stream. Not a problem, just reporting it in
+ case the app cares. In this case, we don't. */
+ ogg_read_error_count++;
+ }
+ else {
+ /* we don't bother dealing with sample rate changes, etc, but you'll have to ??? */
+ bytes_read += retval;
+ stream_left -= retval;
+ p += retval;
+ }
+ }
+
+ if (bytes_read)
+ Load(bytes_read, transfer);
+}
+
+void
+SoundD3D::RewindStream()
+{
+ if (flags & OGGVORBIS) {
+ RewindOggStream();
+ return;
+ }
+
+ if (!stream || !buffer)
+ return;
+
+ // rewind the stream and keep going...
+ eos_written = false;
+ eos_latch = 0;
+ read_size = wfex.nAvgBytesPerSec / 2;
+
+ // find the size of the file:
+ fseek(stream, 0, SEEK_END);
+ stream_left = ftell(stream) - stream_offset;
+ fseek(stream, stream_offset, SEEK_SET);
+
+ total_time = (double) stream_left /
+ (double) wfex.nAvgBytesPerSec;
+
+ if (stream_left < read_size) {
+ status = DONE;
+ buffer->Stop();
+ }
+}
+
+void
+SoundD3D::RewindOggStream()
+{
+ if (!ov_file || !buffer)
+ return;
+
+ // rewind the stream and keep going...
+ eos_written = false;
+ eos_latch = 0;
+ read_size = wfex.nAvgBytesPerSec / 2;
+
+ // set the stream pointer back to the beginning:
+ ov_pcm_seek(ov_file, 0);
+
+ // find the size of the file:
+ stream_left = (DWORD) ov_pcm_total(ov_file,-1);
+ stream_offset = 0;
+
+ total_time = (double) stream_left /
+ (double) wfex.nAvgBytesPerSec;
+
+ if (stream_left < read_size) {
+ status = DONE;
+ buffer->Stop();
+ }
+}
+
+void
+SoundD3D::Localize()
+{
+#ifdef DIRECT_SOUND_3D
+ if (sound3d) {
+ sound3d->SetMinDistance(min_dist, DS3D_IMMEDIATE);
+ sound3d->SetMaxDistance(max_dist, DS3D_IMMEDIATE);
+ sound3d->SetPosition(location.x, location.y, location.z, DS3D_IMMEDIATE);
+ sound3d->SetVelocity(velocity.x, velocity.y, velocity.z, DS3D_IMMEDIATE);
+ }
+
+#else
+
+ // if no buffer, nothing to do:
+ if (!buffer) {
+ moved = false;
+ return;
+ }
+
+ // Compute pan and volume from scratch:
+
+ if ((flags & LOC_3D) && creator) {
+ Vec3 loc = location;
+
+ SoundCardD3D* ears = (SoundCardD3D*) creator;
+ Camera& listener = ears->listener;
+ Vec3 ear_loc = listener.Pos(); ear_loc.SwapYZ();
+ Vec3 direction = loc - ear_loc;
+
+ loc.x = direction * listener.vrt();
+ loc.y = direction * listener.vup();
+ loc.z = direction * listener.vpn();
+
+ double pan = 10000;
+ if (loc.z != 0.0f) pan = fabs(1000.0f * loc.x / loc.z);
+ if (pan > 10000) pan = 10000;
+ if (loc.x < 0) pan = -pan;
+
+ if (volume > 0)
+ volume = 0;
+
+ double vol = volume;
+ double mind2 = min_dist * min_dist;
+ double maxd2 = max_dist * max_dist;
+ double d2 = (loc.x*loc.x) + (loc.y*loc.y) + (loc.z*loc.z);
+
+ if (d2 > maxd2)
+ vol = -10000;
+ else if (d2 > mind2)
+ vol -= (d2-mind2)/(maxd2-mind2) * (vol+10000);
+
+ // clamp volume to legal range:
+ if (vol < -10000) vol = -10000;
+ else if (vol > volume) vol = volume;
+
+ /***
+ Print("Localize: ears = (%f, %f, %f)\n", ear_loc.x, ear_loc.y, ear_loc.z);
+ Print(" world = (%f, %f, %f)\n", location.x, location.y, location.z);
+ Print(" view = (%f, %f, %f)\n", loc.x, loc.y, loc.z);
+ Print(" Pan=%f Volume=%f\n", pan, vol);
+ /***/
+
+ HRESULT hr = buffer->SetPan((LONG) pan);
+ if (!SUCCEEDED(hr)) {
+ char warn[512];
+ sprintf(warn, "Warning could not set pan on buffer to %d", pan);
+ SoundD3DError(warn, hr);
+ }
+
+ hr = buffer->SetVolume((LONG) vol);
+ if (!SUCCEEDED(hr)) {
+ char warn[512];
+ sprintf(warn, "Warning: could not set volume on buffer to %d", vol);
+ SoundD3DError(warn, hr);
+ }
+
+ // if not too far to hear...
+ if ((flags & DOPPLER) && (d2 < maxd2)) {
+ // COMPUTE DOPPLER SHIFT:
+ const float c = 10000.0f;
+
+ direction.Normalize();
+ float v_L = ears->velocity * direction;
+ float v_S = velocity * direction;
+
+ DWORD f_shift = wfex.nSamplesPerSec;
+
+ if (v_L != v_S) {
+ // towards listener:
+ if (v_S < 0)
+ f_shift = wfex.nSamplesPerSec + 20;
+ else
+ f_shift = wfex.nSamplesPerSec - 20;
+ }
+
+ // distance rolloff of high frequencies:
+ double dist = sqrt(d2);
+ DWORD roll_off = (DWORD) (80 * dist/max_dist);
+
+ f_shift -= roll_off;
+
+ if (f_shift < 100) f_shift = 100;
+ if (f_shift > 100000) f_shift = 100000;
+
+ hr = buffer->SetFrequency(f_shift);
+ if (!SUCCEEDED(hr)) {
+ char warn[512];
+ sprintf(warn, "Warning: could not set Doppler frequency on buffer to %d", f_shift);
+ SoundD3DError(warn, hr);
+ }
+ }
+ }
+ else {
+ buffer->SetPan((LONG) location.x);
+ buffer->SetVolume((LONG) volume);
+ }
+#endif
+
+ moved = false;
+}
+
+// +--------------------------------------------------------------------+
+
+Sound*
+SoundD3D::Duplicate()
+{
+ Sound* sound = 0;
+
+ if (flags & RESOURCE) {
+ sound = Sound::Create(flags & ~RESOURCE, &wfex);
+
+ if (sound && !(flags & STREAMED)) {
+ sound->SetMinDistance(min_dist);
+ sound->SetMaxDistance(max_dist);
+
+ if (!buffer) {
+ sound->Load(data_len, data);
+ }
+
+ else {
+ SoundD3D* s3d = (SoundD3D*) sound;
+ soundcard->DuplicateSoundBuffer(buffer, &s3d->buffer);
+ sound->Rewind();
+ }
+ }
+ }
+
+ return sound;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::StreamFile(const char* name, DWORD offset)
+{
+ DWORD buf_size = wfex.nAvgBytesPerSec / 2;
+ DWORD safety_zone = buf_size * 2;
+
+ if (stream) {
+ delete[] transfer;
+ transfer = 0;
+ fclose(stream);
+ }
+
+ status = UNINITIALIZED;
+ stream_left = 0;
+ stream_offset = offset;
+
+ eos_written = false;
+ eos_latch = 0;
+ min_safety = safety_zone;
+ read_size = buf_size;
+
+ // open the stream:
+ if ((stream = fopen(name, "rb")) == 0) {
+ SoundD3DError("StreamFile: could not open stream", E_FAIL);
+ return E_FAIL;
+ }
+
+ // find the size of the file:
+ fseek(stream, 0, SEEK_END);
+ stream_left = ftell(stream) - offset;
+ fseek(stream, stream_offset, SEEK_SET);
+
+ total_time = (double) stream_left /
+ (double) wfex.nAvgBytesPerSec;
+
+ if (stream_left < read_size) {
+ read_size = stream_left;
+ }
+
+ HRESULT hr = AllocateBuffer(read_size + min_safety);
+
+ if (FAILED(hr))
+ return hr;
+
+ flags |= STREAMED;
+
+ // preload the buffer:
+ w = r = 0;
+ transfer = new(__FILE__,__LINE__) BYTE[read_size + 1024];
+
+ if (!transfer) {
+ hr = E_FAIL;
+ }
+
+ else {
+ ZeroMemory(transfer, read_size+1024);
+ StreamBlock();
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::StreamOggFile()
+{
+ DWORD buf_size = wfex.nAvgBytesPerSec / 2;
+ DWORD safety_zone = buf_size * 2;
+
+ if (stream) {
+ delete[] transfer;
+ fclose(stream);
+
+ transfer = 0;
+ stream = 0;
+ }
+
+ status = UNINITIALIZED;
+ stream_left = (DWORD) ov_pcm_total(ov_file,-1);
+ stream_offset = 0;
+
+ eos_written = false;
+ eos_latch = 0;
+ min_safety = safety_zone;
+ read_size = buf_size;
+
+ total_time = (double) stream_left /
+ (double) wfex.nAvgBytesPerSec;
+
+ if (stream_left < read_size) {
+ read_size = stream_left;
+ }
+
+ HRESULT hr = AllocateBuffer(read_size + min_safety);
+
+ if (FAILED(hr))
+ return hr;
+
+ flags |= STREAMED | OGGVORBIS;
+
+ // preload the buffer:
+ w = r = 0;
+ transfer = new(__FILE__,__LINE__) BYTE[read_size + 1024];
+
+ if (!transfer) {
+ hr = E_FAIL;
+ }
+
+ else {
+ ZeroMemory(transfer, read_size+1024);
+ StreamOggBlock();
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::Load(DWORD bytes, BYTE* data)
+{
+ status = UNINITIALIZED;
+
+ HRESULT hr;
+
+ if (!buffer) {
+ hr = AllocateBuffer(bytes);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+
+ LPVOID dest1, dest2;
+ DWORD size1, size2;
+
+ hr = buffer->Lock(w, bytes, &dest1, &size1, &dest2, &size2, 0);
+
+ if (hr == DSERR_BUFFERLOST) {
+ buffer->Restore();
+ hr = buffer->Lock(w, bytes, &dest1, &size1, &dest2, &size2, 0);
+ }
+
+ if (SUCCEEDED(hr)) {
+ CopyMemory(dest1, data, size1);
+ if (dest2) {
+ CopyMemory(dest2, data + size1, size2);
+ }
+
+ if (flags & STREAMED)
+ w = (w + size1 + size2) % (read_size + min_safety);
+ else
+ w += size1 + size2;
+
+ hr = buffer->Unlock(dest1, size1, dest2, size2);
+ if (FAILED(hr)) {
+ SoundD3DError("Load: could not unlock buffer", hr);
+ }
+ }
+ else {
+ SoundD3DError("Load: could not lock buffer", hr);
+ }
+
+ if (SUCCEEDED(hr)) {
+ status = READY;
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::AllocateBuffer(DWORD bytes)
+{
+ HRESULT hr = S_OK;
+
+ if (!buffer) {
+ dsbd.dwBufferBytes = bytes;
+
+ if (soundcard) {
+ hr = soundcard->CreateSoundBuffer(&dsbd, &buffer, NULL);
+
+ if (FAILED(hr)) {
+ SoundD3DError("AllocateBuffer: could not create buffer", hr);
+
+ Print(" dsbd.dwSize = %8d\n", dsbd.dwSize);
+ Print(" dsbd.dwFlags = %08x\n", dsbd.dwFlags);
+ Print(" dsbd.dwBufferBytes = %8d\n", dsbd.dwBufferBytes);
+ Print(" dsbd.lpwfxFormat = %08x\n", dsbd.lpwfxFormat);
+
+ if (dsbd.lpwfxFormat) {
+ Print(" wfex.wBitsPerSample = %8d\n", dsbd.lpwfxFormat->wBitsPerSample);
+ Print(" wfex.nChannels = %8d\n", dsbd.lpwfxFormat->nChannels);
+ Print(" wfex.nSamplesPerSec = %8d\n", dsbd.lpwfxFormat->nSamplesPerSec);
+ }
+ }
+ }
+ else {
+ SoundD3DError("AllocateBuffer: soundcard is null", E_FAIL);
+ }
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::Play()
+{
+ if (IsPlaying()) return S_OK;
+ if (!buffer) return E_FAIL;
+
+ HRESULT hr = E_FAIL;
+
+ if (IsDone())
+ hr = Rewind();
+
+ if (IsReady()) {
+ if (moved)
+ Localize();
+
+ if (flags & LOOP || flags & STREAMED)
+ hr = buffer->Play(0, 0, DSBPLAY_LOOPING);
+ else
+ hr = buffer->Play(0, 0, 0);
+
+ if (SUCCEEDED(hr))
+ status = PLAYING;
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::Rewind()
+{
+ if (!buffer) return E_FAIL;
+
+ HRESULT hr = S_OK;
+
+ if (IsPlaying())
+ hr = Stop();
+
+ if (flags & STREAMED) {
+ RewindStream();
+ StreamBlock();
+ }
+
+ else {
+ hr = buffer->SetCurrentPosition(0);
+ }
+
+ if (SUCCEEDED(hr)) {
+ status = READY;
+ looped = 0;
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::Pause()
+{
+ if (status == DONE)
+ return S_OK;
+
+ HRESULT hr = Stop();
+
+ if (SUCCEEDED(hr))
+ status = READY;
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::Stop()
+{
+ if (!buffer)
+ return E_FAIL;
+
+ if (!IsPlaying()) {
+ status = DONE;
+ return S_OK;
+ }
+
+ status = DONE;
+ return buffer->Stop();
+}
+
+// +--------------------------------------------------------------------+
+
+double
+SoundD3D::GetTimeRemaining() const
+{
+ double time_left = -1;
+
+ if (IsPlaying() || IsReady()) {
+ time_left = (double) stream_left /
+ (double) wfex.nAvgBytesPerSec;
+ }
+
+ return time_left;
+}
+
+double
+SoundD3D::GetTimeElapsed() const
+{
+ double time_elapsed = 0;
+
+ if (IsPlaying()) {
+ time_elapsed = total_time - GetTimeRemaining();
+ }
+
+ return time_elapsed;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundD3D::SetVolume(long v)
+{
+ if (v > 0) v = 0;
+ else if (v < -10000) v = -10000;
+
+ volume = v;
+ moved = true;
+}
+
+// +--------------------------------------------------------------------+
+
+long
+SoundD3D::GetPan() const
+{
+ long p = 10000;
+
+ if (location.z) p = (long) fabs(location.x/location.z);
+ if (p > 10000) p = 10000;
+ if (location.x < 0) p = -p;
+
+ return p;
+}
+
+void
+SoundD3D::SetPan(long p)
+{
+ if (p > 10000) p = 10000;
+ if (p < -10000) p = -10000;
+
+ location.x = (float) p;
+ location.y = 0.0f;
+ location.z = 1.0f;
+ moved = true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundD3D::SetLocation(const Vec3& l)
+{
+ location = l;
+ moved = true;
+}
+
+void
+SoundD3D::SetVelocity(const Vec3& v)
+{
+ velocity = v;
+ moved = true;
+}
+
+// +--------------------------------------------------------------------+
+
+float
+SoundD3D::GetMinDistance() const
+{
+ return min_dist;
+}
+
+void
+SoundD3D::SetMinDistance(float f)
+{
+ min_dist = f;
+ moved = true;
+}
+
+// +--------------------------------------------------------------------+
+
+float
+SoundD3D::GetMaxDistance() const
+{
+ return max_dist;
+}
+void
+SoundD3D::SetMaxDistance(float f)
+{
+ max_dist = f;
+ moved = true;
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+SoundD3DError(const char* msg, HRESULT err)
+{
+ Print(" SoundD3D: %s. [%s]\n", msg, DSErrStr(err));
+}
+
+char*
+DSErrStr(HRESULT err)
+{
+ switch (err) {
+ case DS_OK: return "DS_OK";
+
+ case DSERR_ALLOCATED: return
+ "The call failed because resources (such as a priority level) "
+ "were already being used by another caller.";
+
+ case DSERR_CONTROLUNAVAIL: return
+ "The control (vol,pan,etc.) requested by the caller is not available.";
+
+ case DSERR_INVALIDPARAM: return
+ "An invalid parameter was passed to the returning function.";
+
+ case DSERR_INVALIDCALL: return
+ "This call is not valid for the current state of this object";
+
+ case DSERR_GENERIC: return
+ "An undetermined error occured inside the DirectSound subsystem";
+
+ case DSERR_PRIOLEVELNEEDED: return
+ "The caller does not have the priority level required for the function to succeed.";
+
+ case DSERR_OUTOFMEMORY: return
+ "Not enough free memory is available to complete the operation";
+
+ case DSERR_BADFORMAT: return
+ "The specified WAVE format is not supported";
+
+ case DSERR_UNSUPPORTED: return
+ "The function called is not supported at this time";
+
+ case DSERR_NODRIVER: return
+ "No sound driver is available for use";
+
+ case DSERR_ALREADYINITIALIZED: return
+ "This object is already initialized";
+
+ case DSERR_NOAGGREGATION: return
+ "This object does not support aggregation";
+
+ case DSERR_BUFFERLOST: return
+ "The buffer memory has been lost, and must be restored.";
+
+ case DSERR_OTHERAPPHASPRIO: return
+ "Another app has a higher priority level, preventing this call from succeeding.";
+
+ case DSERR_UNINITIALIZED: return
+ "This object has not been initialized.";
+
+#ifdef DIRECT_SOUND_3D
+ case DSERR_NOINTERFACE: return
+ "The requested COM interface is not available.";
+#endif
+
+ default: return "Unknown Error Code";
+ }
+
+ return "Internal Error";
+}
+
diff --git a/nGenEx/SoundD3D.h b/nGenEx/SoundD3D.h
new file mode 100644
index 0000000..8c09987
--- /dev/null
+++ b/nGenEx/SoundD3D.h
@@ -0,0 +1,162 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: SoundD3D.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ DirectSound3D Audio Output and Buffer classes
+*/
+
+#ifndef SoundD3D_h
+#define SoundD3D_h
+
+//#define DIRECT_SOUND_3D
+#include "SoundCard.h"
+#include "Sound.h"
+#include "Camera.h"
+#include "ThreadSync.h"
+#include <stdio.h>
+#include <dsound.h>
+#include <vorbis/vorbisfile.h>
+
+// +--------------------------------------------------------------------+
+
+class SoundD3D;
+class SoundCardD3D;
+
+// +--------------------------------------------------------------------+
+// Sound Implementation for DirectSound and DirectSound3D
+
+class SoundD3D : public Sound
+{
+public:
+ static const char* TYPENAME() { return "SoundD3D"; }
+
+ SoundD3D(LPDIRECTSOUND card, DWORD flags, LPWAVEFORMATEX format);
+ SoundD3D(LPDIRECTSOUND card, DWORD flags, LPWAVEFORMATEX format, DWORD len, LPBYTE data);
+ virtual ~SoundD3D();
+
+ virtual void Update();
+
+ virtual HRESULT StreamFile(const char* name, DWORD offset);
+ virtual HRESULT Load(DWORD bytes, BYTE* data);
+ virtual HRESULT Play();
+ virtual HRESULT Rewind();
+ virtual HRESULT Pause();
+ virtual HRESULT Stop();
+
+ virtual Sound* Duplicate();
+
+ // (only for streamed sounds)
+ virtual double GetTotalTime() const { return total_time; }
+ virtual double GetTimeRemaining() const;
+ virtual double GetTimeElapsed() const;
+
+ // (only used for localized sounds)
+ virtual void SetVolume(long v);
+ virtual long GetPan() const;
+ virtual void SetPan(long p);
+ virtual void SetLocation(const Vec3& l);
+ virtual void SetVelocity(const Vec3& v);
+
+ virtual float GetMinDistance() const;
+ virtual void SetMinDistance(float f);
+ virtual float GetMaxDistance() const;
+ virtual void SetMaxDistance(float f);
+
+
+protected:
+ void Localize();
+ HRESULT AllocateBuffer(DWORD bytes);
+ HRESULT StreamOggFile();
+
+ void StreamBlock();
+ void StreamOggBlock();
+ void RewindStream();
+ void RewindOggStream();
+
+ LPDIRECTSOUND soundcard;
+ WAVEFORMATEX wfex;
+ DSBUFFERDESC dsbd;
+ LPDIRECTSOUNDBUFFER buffer;
+
+ DWORD data_len;
+ LPBYTE data;
+
+#ifdef DIRECT_SOUND_3D
+ LPDIRECTSOUND3DBUFFER sound3d;
+#endif
+
+ float min_dist;
+ float max_dist;
+
+// STREAMED SOUND SUPPORT:
+ FILE* stream;
+ DWORD stream_left;
+ double total_time;
+ DWORD min_safety;
+ DWORD read_size;
+ BYTE* transfer;
+ DWORD w, r;
+ DWORD stream_offset;
+ bool eos_written;
+ BYTE eos_latch;
+ bool moved;
+
+ ThreadSync sync;
+ OggVorbis_File* ov_file;
+};
+
+// +--------------------------------------------------------------------+
+// Sound Card Implementation for DS and DS3D
+
+class SoundCardD3D : public SoundCard
+{
+ friend class SoundD3D;
+
+public:
+ static const char* TYPENAME() { return "SoundCardD3D"; }
+
+ SoundCardD3D(HWND hwnd);
+ virtual ~SoundCardD3D();
+
+ // Format of the sound card's primary buffer:
+ virtual bool GetFormat(LPWAVEFORMATEX format);
+ virtual bool SetFormat(LPWAVEFORMATEX format);
+ virtual bool SetFormat(int bits, int channels, int hertz);
+
+ virtual void ShowFormat();
+
+ // Get a blank, writable sound buffer:
+ virtual Sound* CreateSound(DWORD flags, LPWAVEFORMATEX format);
+
+ // Create a sound resource:
+ virtual Sound* CreateSound(DWORD flags, LPWAVEFORMATEX format, DWORD len, LPBYTE data);
+
+ virtual void SetListener(const Camera& cam, const Vec3& vel);
+ virtual bool Pause();
+ virtual bool Resume();
+ virtual bool StopSoundEffects();
+
+protected:
+ LPDIRECTSOUND soundcard;
+ LPDIRECTSOUNDBUFFER primary;
+
+#ifdef DIRECT_SOUND_3D
+ LPDIRECTSOUND3DLISTENER listener;
+#else
+ Camera listener;
+ Vec3 velocity;
+#endif
+
+ WAVEFORMATEX wfex;
+ DSBUFFERDESC dsbd;
+};
+
+#endif SoundD3D_h
+
diff --git a/nGenEx/Sprite.cpp b/nGenEx/Sprite.cpp
new file mode 100644
index 0000000..4086314
--- /dev/null
+++ b/nGenEx/Sprite.cpp
@@ -0,0 +1,380 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Sprite.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Sprite (Polygon) Object
+*/
+
+#include "MemDebug.h"
+#include "Sprite.h"
+#include "Bitmap.h"
+#include "Camera.h"
+#include "Polygon.h"
+#include "Video.h"
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+
+Sprite::Sprite()
+ : w(0), h(0), nframes(0), own_frames(0),
+ frames(0), frame_index(0), frame_time(100), loop(0), shade(1.0),
+ angle(0.0), blend_mode(4), filter(1), vset(4), poly(0)
+{
+ trans = true;
+
+ vset.space = VertexSet::WORLD_SPACE;
+ for (int i = 0; i < 4; i++) {
+ vset.diffuse[i] = Color::White.Value();
+ }
+
+ vset.tu[0] = 0.0f;
+ vset.tv[0] = 0.0f;
+ vset.tu[1] = 1.0f;
+ vset.tv[1] = 0.0f;
+ vset.tu[2] = 1.0f;
+ vset.tv[2] = 1.0f;
+ vset.tu[3] = 0.0f;
+ vset.tv[3] = 1.0f;
+
+ poly.nverts = 4;
+ poly.vertex_set = &vset;
+ poly.material = &mtl;
+ poly.verts[0] = 0;
+ poly.verts[1] = 1;
+ poly.verts[2] = 2;
+ poly.verts[3] = 3;
+}
+
+// +--------------------------------------------------------------------+
+
+Sprite::Sprite(Bitmap* animation, int length, int repeat, int share)
+ : w(0), h(0), nframes(0), own_frames(0),
+ frames(0), frame_index(0), frame_time(67), loop(0), shade(1.0),
+ angle(0.0), blend_mode(4), filter(1), vset(4), poly(0)
+{
+ trans = true;
+ SetAnimation(animation, length, repeat, share);
+
+ vset.space = VertexSet::WORLD_SPACE;
+ for (int i = 0; i < 4; i++) {
+ vset.diffuse[i] = Color::White.Value();
+ }
+
+ vset.tu[0] = 0.0f;
+ vset.tv[0] = 0.0f;
+ vset.tu[1] = 1.0f;
+ vset.tv[1] = 0.0f;
+ vset.tu[2] = 1.0f;
+ vset.tv[2] = 1.0f;
+ vset.tu[3] = 0.0f;
+ vset.tv[3] = 1.0f;
+
+ poly.nverts = 4;
+ poly.vertex_set = &vset;
+ poly.material = &mtl;
+ poly.verts[0] = 0;
+ poly.verts[1] = 1;
+ poly.verts[2] = 2;
+ poly.verts[3] = 3;}
+
+// +--------------------------------------------------------------------+
+
+Sprite::~Sprite()
+{
+ if (own_frames) {
+ if (nframes == 1)
+ delete frames;
+ else
+ delete [] frames;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::Scale(double scale)
+{
+ if (scale >= 0) {
+ w = (int) (scale * w);
+ h = (int) (scale * h);
+
+ radius = (float) ((w>h) ? w : h) / 2.0f;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::Rescale(double scale)
+{
+ if (scale >= 0 && Frame()) {
+ w = (int) (scale * Frame()->Width());
+ h = (int) (scale * Frame()->Height());
+
+ radius = (float) ((w>h) ? w : h) / 2.0f;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::Reshape(int w1, int h1)
+{
+ if (w1 >= 0 && h1 >= 0 && Frame()) {
+ w = w1;
+ h = h1;
+
+ radius = (float) ((w>h) ? w : h) / 2.0f;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::SetAnimation(Bitmap* animation, int length, int repeat, int share)
+{
+ if (animation) {
+ strncpy(name, animation->GetFilename(), 31);
+ name[31] = 0;
+
+ if (own_frames) {
+ if (nframes == 1)
+ delete frames;
+ else
+ delete [] frames;
+ }
+
+ w = animation->Width();
+ h = animation->Height();
+
+ radius = (float) ((w>h) ? w : h) / 2.0f;
+
+ own_frames = !share;
+ nframes = length;
+ frames = animation;
+ frame_index = 0;
+
+ if (repeat) {
+ loop = 1;
+ life = -1;
+ }
+ else {
+ loop = 0;
+ life = nframes;
+ }
+
+ last_time = Game::RealTime() - frame_time;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::SetTexCoords(const double* uv_interleaved)
+{
+ if (uv_interleaved) {
+ vset.tu[0] = (float) uv_interleaved[0];
+ vset.tv[0] = (float) uv_interleaved[1];
+ vset.tu[1] = (float) uv_interleaved[2];
+ vset.tv[1] = (float) uv_interleaved[3];
+ vset.tu[2] = (float) uv_interleaved[4];
+ vset.tv[2] = (float) uv_interleaved[5];
+ vset.tu[3] = (float) uv_interleaved[6];
+ vset.tv[3] = (float) uv_interleaved[7];
+ }
+ else {
+ vset.tu[0] = 0.0f;
+ vset.tv[0] = 0.0f;
+ vset.tu[1] = 1.0f;
+ vset.tv[1] = 0.0f;
+ vset.tu[2] = 1.0f;
+ vset.tv[2] = 1.0f;
+ vset.tu[3] = 0.0f;
+ vset.tv[3] = 1.0f;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Sprite::FrameRate() const
+{
+ return 1000.0 / (double) frame_time;
+}
+
+void
+Sprite::SetFrameRate(double rate)
+{
+ if (rate > 0.001 && rate < 100) {
+ frame_time = (int) (1000.0 / rate);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::SetFrameIndex(int n)
+{
+ if (n >= 0 && n < nframes)
+ frame_index = n;
+}
+
+// +--------------------------------------------------------------------+
+
+Bitmap*
+Sprite::Frame() const
+{
+ return frames + frame_index;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::Render(Video* video, DWORD flags)
+{
+ if (shade < 0.001 || hidden || !visible || !video)
+ return;
+
+ if (blend_mode == 2 && !(flags & Graphic::RENDER_ALPHA))
+ return;
+
+ if (blend_mode == 4 && !(flags & Graphic::RENDER_ADDITIVE))
+ return;
+
+ if (life > 0 || loop) {
+ const Camera* camera = video->GetCamera();
+ Matrix orient(camera->Orientation());
+ Vec3 nrm(camera->vpn() * -1);
+ ColorValue white((float) shade, (float) shade, (float) shade, (float) shade);
+ DWORD diff = white.ToColor().Value();
+
+ orient.Roll(angle);
+
+ Vec3 vx = Vec3((float) orient(0,0),
+ (float) orient(0,1),
+ (float) orient(0,2)) * (float) (w/2.0f);
+
+ Vec3 vy = Vec3((float) orient(1,0),
+ (float) orient(1,1),
+ (float) orient(1,2)) * (float) (h/2.0f);
+
+ vset.loc[0] = loc - vx + vy;
+ vset.nrm[0] = nrm;
+ vset.diffuse[0] = diff;
+
+ vset.loc[1] = loc + vx + vy;
+ vset.nrm[1] = nrm;
+ vset.diffuse[1] = diff;
+
+ vset.loc[2] = loc + vx - vy;
+ vset.nrm[2] = nrm;
+ vset.diffuse[2] = diff;
+
+ vset.loc[3] = loc - vx - vy;
+ vset.nrm[3] = nrm;
+ vset.diffuse[3] = diff;
+
+ if (luminous) {
+ mtl.Ka = Color::Black;
+ mtl.Kd = Color::Black;
+ mtl.Ks = Color::Black;
+ mtl.Ke = white;
+ mtl.tex_diffuse = Frame();
+ mtl.tex_emissive = Frame();
+ mtl.blend = blend_mode;
+ mtl.luminous = luminous;
+ }
+
+ else {
+ mtl.Ka = white;
+ mtl.Kd = white;
+ mtl.Ks = Color::Black;
+ mtl.Ke = Color::Black;
+ mtl.tex_diffuse = Frame();
+ mtl.tex_emissive = 0;
+ mtl.blend = blend_mode;
+ mtl.luminous = luminous;
+ }
+
+ video->DrawPolys(1, &poly);
+ }
+
+ memset(&screen_rect, 0, sizeof(Rect));
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::Render2D(Video* video)
+{
+ if (shade < 0.001 || hidden || !visible || !video)
+ return;
+
+ ColorValue white((float) shade, (float) shade, (float) shade, (float) shade);
+ DWORD diff = white.ToColor().Value();
+
+ double ca = cos(Angle());
+ double sa = sin(Angle());
+
+ double w2 = Width() / 2.0;
+ double h2 = Height() / 2.0;
+
+ vset.s_loc[0].x = (float) (loc.x + (-w2*ca - -h2*sa) - 0.5);
+ vset.s_loc[0].y = (float) (loc.y + (-w2*sa + -h2*ca) - 0.5);
+ vset.s_loc[0].z = 0.0f;
+ vset.rw[0] = 1.0f;
+ vset.diffuse[0] = diff;
+
+ vset.s_loc[1].x = (float) (loc.x + ( w2*ca - -h2*sa) - 0.5);
+ vset.s_loc[1].y = (float) (loc.y + ( w2*sa + -h2*ca) - 0.5);
+ vset.s_loc[1].z = 0.0f;
+ vset.rw[1] = 1.0f;
+ vset.diffuse[1] = diff;
+
+ vset.s_loc[2].x = (float) (loc.x + ( w2*ca - h2*sa) - 0.5);
+ vset.s_loc[2].y = (float) (loc.y + ( w2*sa + h2*ca) - 0.5);
+ vset.s_loc[2].z = 0.0f;
+ vset.rw[2] = 1.0f;
+ vset.diffuse[2] = diff;
+
+ vset.s_loc[3].x = (float) (loc.x + (-w2*ca - h2*sa) - 0.5);
+ vset.s_loc[3].y = (float) (loc.y + (-w2*sa + h2*ca) - 0.5);
+ vset.s_loc[3].z = 0.0f;
+ vset.rw[3] = 1.0f;
+ vset.diffuse[3] = diff;
+
+ mtl.Kd = white;
+ mtl.tex_diffuse = Frame();
+ mtl.blend = blend_mode;
+
+ video->DrawScreenPolys(1, &poly, blend_mode);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::Update()
+{
+ if (life > 0 || loop) {
+ DWORD time = Game::RealTime();
+ while (time - last_time > frame_time) {
+ life--;
+ frame_index++;
+ if (frame_index >= nframes)
+ frame_index = 0;
+
+ last_time += frame_time;
+ }
+
+ if (life < 0 && !loop)
+ life = 0;
+ }
+}
+
diff --git a/nGenEx/Sprite.h b/nGenEx/Sprite.h
new file mode 100644
index 0000000..a40212a
--- /dev/null
+++ b/nGenEx/Sprite.h
@@ -0,0 +1,90 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Sprite.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Sprite Object
+*/
+
+#ifndef Sprite_h
+#define Sprite_h
+
+#include "Types.h"
+#include "Graphic.h"
+#include "Polygon.h"
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+
+class Sprite : public Graphic
+{
+public:
+ static const char* TYPENAME() { return "Sprite"; }
+
+ Sprite();
+ Sprite(Bitmap* animation, int length=1, int repeat=1, int share=1);
+ virtual ~Sprite();
+
+ // operations
+ virtual void Render(Video* video, DWORD flags);
+ virtual void Render2D(Video* video);
+ virtual void Update();
+ virtual void Scale(double scale);
+ virtual void Rescale(double scale);
+ virtual void Reshape(int w1, int h1);
+
+ // accessors / mutators
+ int Width() const { return w; }
+ int Height() const { return h; }
+ int Looping() const { return loop; }
+ int NumFrames() const { return nframes; }
+ double FrameRate() const;
+ void SetFrameRate(double rate);
+
+ double Shade() const { return shade; }
+ void SetShade(double s) { shade = s; }
+ double Angle() const { return angle; }
+ void SetAngle(double a) { angle = a; }
+ int BlendMode() const { return blend_mode; }
+ void SetBlendMode(int a) { blend_mode = a; }
+ int Filter() const { return filter; }
+ void SetFilter(int f) { filter = f; }
+ virtual void SetAnimation(Bitmap* animation, int length=1, int repeat=1, int share=1);
+ virtual void SetTexCoords(const double* uv_interleaved);
+
+ Bitmap* Frame() const;
+ void SetFrameIndex(int n);
+
+ virtual bool IsSprite() const { return true; }
+
+protected:
+ int w, h;
+ int loop;
+
+ int nframes;
+ int own_frames;
+ Bitmap* frames;
+ int frame_index;
+ DWORD frame_time;
+ DWORD last_time;
+ double shade;
+ double angle;
+ int blend_mode;
+ int filter;
+
+ Poly poly;
+ Material mtl;
+ VertexSet vset;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Sprite_h
+
diff --git a/nGenEx/TexCubeDX9.cpp b/nGenEx/TexCubeDX9.cpp
new file mode 100644
index 0000000..47720a4
--- /dev/null
+++ b/nGenEx/TexCubeDX9.cpp
@@ -0,0 +1,120 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: TexDX9.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct3D Texture Cache
+*/
+
+#include "MemDebug.h"
+#include "TexCubeDX9.h"
+#include "VideoDX9.h"
+#include "Bitmap.h"
+#include "Color.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+void VideoDX9Error(const char* msg, HRESULT err);
+
+#ifndef RELEASE
+#define RELEASE(x) if (x) { x->Release(); x=NULL; }
+#endif
+
+// +--------------------------------------------------------------------+
+
+TexCubeDX9::TexCubeDX9(VideoDX9* v)
+ : video(v), texture(0)
+{
+ d3d = video->Direct3D();
+ d3ddevice = video->D3DDevice();
+
+ for (int i = 0; i < 6; i++) {
+ faces[i] = 0;
+ last_modified[i] = 0;
+ }
+}
+
+TexCubeDX9::~TexCubeDX9()
+{
+ RELEASE(texture);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+TexCubeDX9::LoadTexture(Bitmap* bmp, int face_index)
+{
+ if (!d3ddevice) return false;
+
+ if (faces[face_index] == bmp && last_modified[face_index] >= bmp->LastModified())
+ return true; // already loaded and hasn't been modified
+
+ HRESULT hr = D3D_OK;
+
+ // create the texture, if necessary
+ if (!texture) {
+ hr = d3ddevice->CreateCubeTexture(bmp->Width(),
+ 1, // one mip level
+ 0, // no specific usage
+ D3DFMT_A8R8G8B8, // format matching Color::rgba
+ D3DPOOL_MANAGED,
+ &texture,
+ 0);
+
+ if (FAILED(hr) || !texture) {
+ VideoDX9Error("LoadTexture - could not create cube texture", hr);
+ return false;
+ }
+ }
+
+ // lock the surface for writing
+ D3DLOCKED_RECT locked_rect;
+ D3DCUBEMAP_FACES face = (D3DCUBEMAP_FACES) face_index;
+ hr = texture->LockRect(face, 0, &locked_rect, 0, 0);
+
+ if (FAILED(hr)) {
+ VideoDX9Error("LoadTexture - could not lock texture surface", hr);
+ RELEASE(texture);
+ return false;
+ }
+
+ // load the bitmap into the texture surface
+ for (int i = 0; i < bmp->Height(); i++) {
+ BYTE* src = (BYTE*) (bmp->HiPixels() + i * bmp->Width());
+ BYTE* dst = (BYTE*) locked_rect.pBits + i * locked_rect.Pitch;
+
+ CopyMemory(dst, src, bmp->Width() * sizeof(Color));
+ }
+
+ // unlock the surface
+ texture->UnlockRect(face, 0);
+
+ faces[face_index] = bmp;
+ last_modified[face_index] = bmp->LastModified();
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+IDirect3DCubeTexture9*
+TexCubeDX9::GetTexture()
+{
+ if (texture) {
+ // need to refresh anything?
+ for (int i = 0; i < 6; i++) {
+ if (faces[i] && last_modified[i] < faces[i]->LastModified()) {
+ LoadTexture(faces[i], i);
+ }
+ }
+ }
+
+ return texture;
+}
diff --git a/nGenEx/TexCubeDX9.h b/nGenEx/TexCubeDX9.h
new file mode 100644
index 0000000..1dd54cc
--- /dev/null
+++ b/nGenEx/TexCubeDX9.h
@@ -0,0 +1,49 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: TexCubeDX9.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct 3D Texture Cube for Env Mapping
+*/
+
+#ifndef TexCubeDX9_h
+#define TexCubeDX9_h
+
+#include "Bitmap.h"
+
+// +--------------------------------------------------------------------+
+
+class Video;
+class VideoDX9;
+class Bitmap;
+struct VD3D_texture_format;
+
+// +--------------------------------------------------------------------+
+
+class TexCubeDX9
+{
+public:
+ TexCubeDX9(VideoDX9* video);
+ virtual ~TexCubeDX9();
+
+ IDirect3DCubeTexture9* GetTexture();
+ bool LoadTexture(Bitmap* bmp, int face);
+
+private:
+ VideoDX9* video;
+ IDirect3D9* d3d;
+ IDirect3DDevice9* d3ddevice;
+
+ IDirect3DCubeTexture9* texture;
+ Bitmap* faces[6];
+ DWORD last_modified[6];
+};
+
+#endif // TexCubeDX9_h
+
diff --git a/nGenEx/TexDX9.cpp b/nGenEx/TexDX9.cpp
new file mode 100644
index 0000000..de182e1
--- /dev/null
+++ b/nGenEx/TexDX9.cpp
@@ -0,0 +1,418 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: TexDX9.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct3D Texture Cache
+*/
+
+#include "MemDebug.h"
+#include "TexDX9.h"
+#include "VideoDX9.h"
+#include "Bitmap.h"
+#include "Color.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+void VideoDX9Error(const char* msg, HRESULT err);
+
+#define TEXDX9_VERBOSE 0
+#define DX9_TEXTURE_CACHE_SIZE 256
+
+#ifndef RELEASE
+#define RELEASE(x) if (x) { x->Release(); x=NULL; }
+#endif
+
+// +--------------------------------------------------------------------+
+
+void
+TexCacheDX9Entry::Release()
+{
+ RELEASE(texture);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TexCacheDX9Entry::Unload()
+{
+ RELEASE(texture);
+ bitmap_id = 0;
+ used_last = 0;
+ last_modified = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+TexCacheDX9::TexCacheDX9(VideoDX9* v)
+ : video(v), count(0), mru(0)
+{
+ d3d = video->Direct3D();
+ d3ddevice = video->D3DDevice();
+
+ // clear the texture cache
+ cache = new(__FILE__,__LINE__) TexCacheDX9Entry[DX9_TEXTURE_CACHE_SIZE];
+ vidmem = video->VidMemFree();
+}
+
+TexCacheDX9::~TexCacheDX9()
+{
+ int used = 0;
+
+ if (cache) {
+ for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++) {
+ if (cache[i].bitmap_id)
+ used++;
+ cache[i].Unload();
+ }
+
+ delete [] cache;
+ cache = 0;
+ }
+
+ Print("TexCacheDX9: final used count = %d\n", used);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+TexCacheDX9::LoadTexture(Bitmap* bmp, TexCacheDX9Entry* entry)
+{
+ if (!d3ddevice) return false;
+
+ HRESULT hr = D3D_OK;
+
+ // create the texture, if necessary
+ if (!entry->texture || entry->bitmap_id != bmp->Handle()) {
+ hr = d3ddevice->CreateTexture(bmp->Width(),
+ bmp->Height(),
+ 1, // one mip level
+ 0, // no specific usage
+ D3DFMT_A8R8G8B8, // format matching Color::rgba
+ D3DPOOL_MANAGED,
+ &entry->texture,
+ 0);
+
+ if (FAILED(hr) || !entry->texture) {
+ VideoDX9Error("LoadTexture - could not create texture", hr);
+ return false;
+ }
+ }
+
+ // lock the surface for writing
+ D3DLOCKED_RECT locked_rect;
+ hr = entry->texture->LockRect(0, &locked_rect, 0, 0);
+
+ if (FAILED(hr)) {
+ VideoDX9Error("LoadTexture - could not lock texture surface", hr);
+ entry->Unload();
+ return false;
+ }
+
+ // load the bitmap into the texture surface
+ for (int i = 0; i < bmp->Height(); i++) {
+ BYTE* src = (BYTE*) (bmp->HiPixels() + i * bmp->Width());
+ BYTE* dst = (BYTE*) locked_rect.pBits + i * locked_rect.Pitch;
+
+ CopyMemory(dst, src, bmp->Width() * sizeof(Color));
+ }
+
+ // unlock the surface
+ entry->texture->UnlockRect(0);
+
+ entry->last_modified = bmp->LastModified();
+ vidmem = video->VidMemFree();
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TexCacheDX9::CreateNormalMap(int index, float amp)
+{
+ if (d3ddevice && cache[index].texture && !cache[index].normal) {
+ HRESULT hr = D3D_OK;
+ TexCacheDX9Entry* entry = &cache[index];
+ Bitmap* bmp = Bitmap::GetBitmapByID(entry->bitmap_id);
+
+ IDirect3DTexture9* normal_map = 0;
+
+ // create the normal map texture
+ hr = d3ddevice->CreateTexture(bmp->Width(),
+ bmp->Height(),
+ 1, // one mip levels
+ 0, // no specific usage
+ D3DFMT_A8R8G8B8, // format matching Color::rgba
+ D3DPOOL_MANAGED,
+ &normal_map,
+ 0);
+
+ if (FAILED(hr) || !normal_map) {
+ VideoDX9Error("CreateNormalMap - could not create texture", hr);
+ return;
+ }
+
+ D3DXComputeNormalMap(normal_map, entry->texture ,NULL, 0, D3DX_CHANNEL_RED, amp);
+
+ entry->texture->Release();
+ entry->texture = normal_map;
+ entry->normal = true;
+
+ // The D3DX function destroys the alpha channel data
+ // when it builds the normal map. We want the original
+ // height data stored in the alpha channel, so we need
+ // to add it back in now.
+
+ // lock the surface for writing
+ D3DLOCKED_RECT locked_rect;
+ hr = normal_map->LockRect(0, &locked_rect, 0, 0);
+
+ if (FAILED(hr)) {
+ VideoDX9Error("CreateNormalMap - could not insert height channel", hr);
+ return;
+ }
+
+ // load the bitmap into the texture surface
+ for (int i = 0; i < bmp->Height(); i++) {
+ BYTE* src = (BYTE*) (bmp->HiPixels() + i * bmp->Width());
+ BYTE* dst = (BYTE*) locked_rect.pBits + i * locked_rect.Pitch;
+
+ src += 2; // red channel
+ dst += 3; // alpha channel
+
+ for (int n = 0; n < bmp->Width(); n++) {
+ *dst = *src;
+
+ dst += 4;
+ src += 4;
+ }
+ }
+
+ // unlock the surface
+ normal_map->UnlockRect(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+IDirect3DTexture9*
+TexCacheDX9::FindTexture(Bitmap* bmp)
+{
+ int avail = -1;
+
+ if (!bmp)
+ return 0;
+
+ // check most recently used:
+ if (cache[mru].bitmap_id == bmp->Handle()) {
+ cache[mru].used_last = frame_number;
+
+ // need to refresh?
+ if (cache[mru].last_modified < bmp->LastModified()) {
+ LoadTexture(bmp, &cache[mru]);
+ cache[mru].normal = false;
+ }
+
+ return cache[mru].texture;
+ }
+
+ // find cache entry, or first free:
+ for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++)
+ if (cache[i].bitmap_id == bmp->Handle()) {
+ cache[i].used_last = frame_number;
+ mru = i;
+
+ // need to refresh?
+ if (cache[mru].last_modified < bmp->LastModified()) {
+ LoadTexture(bmp, &cache[mru]);
+ cache[mru].normal = false;
+ }
+
+ return cache[i].texture;
+ }
+ else if (avail < 0 && cache[i].bitmap_id == 0)
+ avail = i;
+
+ // no free space
+ if (avail < 0)
+ if (FreeUpCache())
+ return FindTexture(bmp);
+ else
+ return 0;
+
+ TexCacheDX9Entry* entry = &cache[avail];
+ entry->bitmap_id = bmp->Handle();
+ entry->used_last = frame_number;
+
+ if (LoadTexture(bmp, entry)) {
+#if TEXDX9_VERBOSE
+ Print(" Tex %3d: id=%2d, size=%3d, type=%d, hTex=%3d, frame=%6d vmf=%8d\n",
+ avail, bmp->Handle(), bmp->Width(), bmp->Type(),
+ cache[avail].texture, cache[avail].used_last, vidmem);
+#endif
+ mru = avail;
+ cache[mru].normal = false;
+ return entry->texture;
+ }
+ else {
+ // failed to load texture,
+ // erase cache entry:
+ entry->Unload();
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+IDirect3DTexture9*
+TexCacheDX9::FindNormalMap(Bitmap* bmp, float amp)
+{
+ int avail = -1;
+
+ if (!bmp)
+ return 0;
+
+ // check most recently used:
+ if (cache[mru].bitmap_id == bmp->Handle()) {
+ cache[mru].used_last = frame_number;
+
+ // need to refresh?
+ if (cache[mru].last_modified < bmp->LastModified()) {
+ LoadTexture(bmp, &cache[mru]);
+ cache[mru].normal = false;
+ }
+
+ if (!cache[mru].normal)
+ CreateNormalMap(mru, amp);
+
+ return cache[mru].texture;
+ }
+
+ // find cache entry, or first free:
+ for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++)
+ if (cache[i].bitmap_id == bmp->Handle()) {
+ cache[i].used_last = frame_number;
+ mru = i;
+
+ // need to refresh?
+ if (cache[i].last_modified < bmp->LastModified()) {
+ LoadTexture(bmp, &cache[mru]);
+ cache[i].normal = false;
+ }
+
+ if (!cache[i].normal)
+ CreateNormalMap(i, amp);
+
+ return cache[i].texture;
+ }
+ else if (avail < 0 && cache[i].bitmap_id == 0)
+ avail = i;
+
+ // no free space
+ if (avail < 0)
+ if (FreeUpCache())
+ return FindTexture(bmp);
+ else
+ return 0;
+
+ TexCacheDX9Entry* entry = &cache[avail];
+ entry->bitmap_id = bmp->Handle();
+ entry->used_last = frame_number;
+
+ if (LoadTexture(bmp, entry)) {
+#if TEXDX9_VERBOSE
+ Print(" Tex %3d: id=%2d, size=%3d, type=%d, hTex=%3d, frame=%6d vmf=%8d\n",
+ avail, bmp->Handle(), bmp->Width(), bmp->Type(),
+ cache[avail].texture, cache[avail].used_last, vidmem);
+#endif
+ mru = avail;
+ CreateNormalMap(mru, amp);
+ return entry->texture;
+ }
+ else {
+ // failed to load texture,
+ // erase cache entry:
+ entry->Unload();
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+TexCacheDX9::FreeLRU(int tries)
+{
+ int unloaded = 0;
+
+ while (tries--) {
+ int oldest = -1;
+ DWORD old = frame_number;
+
+ for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++) {
+ DWORD ul = cache[i].used_last;
+
+ if (ul && ul < old && ul != frame_number) {
+ old = ul;
+ oldest = i;
+ }
+ }
+
+ if (oldest >= 0) {
+ cache[oldest].Unload();
+ unloaded++;
+ }
+ else
+ break;
+ }
+
+ vidmem = video->VidMemFree();
+
+#if TEXDX9_VERBOSE
+ Print(" FreeLRU() frame=%6d unloaded=%2d vmf=%8d\n", frame_number, unloaded, vidmem);
+#endif
+
+ return unloaded;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+TexCacheDX9::FreeUpCache()
+{
+ int unloaded = 0;
+
+ for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++) {
+ if (cache[i].used_last && cache[i].used_last < frame_number) {
+ cache[i].Unload();
+ unloaded++;
+ }
+ }
+
+ vidmem = video->VidMemFree();
+
+ Print(" FreeUpCache() frame=%6d unloaded=%2d vmf=%8d\n", frame_number, unloaded, vidmem);
+
+ return unloaded;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TexCacheDX9::InvalidateCache()
+{
+ for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++) {
+ cache[i].Unload();
+ cache[i].normal = false;
+ }
+
+ vidmem = video->VidMemFree();
+}
diff --git a/nGenEx/TexDX9.h b/nGenEx/TexDX9.h
new file mode 100644
index 0000000..88c6357
--- /dev/null
+++ b/nGenEx/TexDX9.h
@@ -0,0 +1,79 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: TexDX9.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct 3D Texture Cache
+*/
+
+#ifndef TexDX9_h
+#define TexDX9_h
+
+#include "Bitmap.h"
+
+// +--------------------------------------------------------------------+
+
+class Video;
+class VideoDX9;
+class Bitmap;
+struct VD3D_texture_format;
+
+// +--------------------------------------------------------------------+
+
+struct TexCacheDX9Entry
+{
+ TexCacheDX9Entry() : bitmap_id(0), texture(0), used_last(0),
+ last_modified(0), normal(false) { }
+
+ void Release();
+ void Unload();
+
+ HANDLE bitmap_id;
+ IDirect3DTexture9* texture;
+ DWORD used_last;
+ DWORD last_modified;
+ bool normal;
+};
+
+// +--------------------------------------------------------------------+
+
+class TexCacheDX9
+{
+public:
+ TexCacheDX9(VideoDX9* video);
+ virtual ~TexCacheDX9();
+
+ void FrameNumber(int n) { frame_number = n; }
+ IDirect3DTexture9* FindTexture(Bitmap* bmp);
+ IDirect3DTexture9* FindNormalMap(Bitmap* bmp, float amp=1);
+ bool LoadTexture(Bitmap* bmp, TexCacheDX9Entry* entry);
+ void InvalidateCache();
+
+ int count;
+
+private:
+ int FreeLRU(int tries=4);
+ int FreeUpCache();
+ void CreateNormalMap(int index, float amp=1);
+
+ VideoDX9* video;
+ IDirect3D9* d3d;
+ IDirect3DDevice9* d3ddevice;
+
+ DWORD vidmem;
+
+ int bad_frame;
+ DWORD frame_number;
+
+ int mru;
+ TexCacheDX9Entry* cache;
+};
+
+#endif // TexDX9_h
+
diff --git a/nGenEx/TimeSnap.h b/nGenEx/TimeSnap.h
new file mode 100644
index 0000000..0f5d2ae
--- /dev/null
+++ b/nGenEx/TimeSnap.h
@@ -0,0 +1,26 @@
+/* Project nGen
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: TimeSnap.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef TimeSnap_h
+#define TimeSnap_h
+
+// +--------------------------------------------------------------------+
+
+#define TIMESNAP(clock) _asm push eax\
+ _asm push edx\
+ _asm _emit 0x0F\
+ _asm _emit 0x31\
+ _asm mov clock, eax\
+ _asm pop edx\
+ _asm pop eax
+
+// +--------------------------------------------------------------------+
+
+#endif TimeSnap_h \ No newline at end of file
diff --git a/nGenEx/Types.h b/nGenEx/Types.h
new file mode 100644
index 0000000..cd695e1
--- /dev/null
+++ b/nGenEx/Types.h
@@ -0,0 +1,66 @@
+/* Project nGen
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: Types.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Basic Type Definitions
+*/
+
+#ifndef Types_h
+#define Types_h
+
+// +--------------------------------------------------------------------+
+
+#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
+#define STRICT 1
+
+// Works with Windows 2000 and later and Windows 98 or later
+#undef _WIN32_IE
+#undef WINVER
+#undef _WIN32_WINDOWS
+#undef _WIN32_WINNT
+#define WINVER 0x0500
+#define _WIN32_WINDOWS 0x0410
+#define _WIN32_WINNT 0x0500
+
+#if !defined(HMONITOR_DECLARED)
+ #define HMONITOR_DECLARED
+ DECLARE_HANDLE(HMONITOR);
+#endif
+
+#include <windows.h>
+#include <windowsx.h>
+#include <assert.h>
+#include <math.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+
+// Enable extra D3D debugging in debug builds if using the debug DirectX runtime.
+// This makes D3D objects work well in the debugger watch window, but slows down
+// performance slightly.
+#if defined(DEBUG) | defined(_DEBUG)
+#define D3D_DEBUG_INFO
+#endif
+
+// Direct3D includes
+#include <d3d9.h>
+#include <d3dx9.h>
+
+// DirectSound includes
+#include <mmsystem.h>
+#include <mmreg.h>
+#include <dsound.h>
+
+// +--------------------------------------------------------------------+
+
+#endif Types_h
+
diff --git a/nGenEx/Universe.h b/nGenEx/Universe.h
new file mode 100644
index 0000000..d599586
--- /dev/null
+++ b/nGenEx/Universe.h
@@ -0,0 +1,32 @@
+/* Project nGen
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: Universe.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Universe class
+*/
+
+#ifndef Universe_h
+#define Universe_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class Universe
+{
+public:
+ Universe() { }
+ virtual ~Universe() { }
+
+ virtual void ExecFrame(double seconds) { }
+};
+
+#endif Universe_h
+
diff --git a/nGenEx/Video.cpp b/nGenEx/Video.cpp
new file mode 100644
index 0000000..ee4b31f
--- /dev/null
+++ b/nGenEx/Video.cpp
@@ -0,0 +1,65 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Video.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Video Interface (singleton definition)
+*/
+
+#include "MemDebug.h"
+#include "Video.h"
+#include "VideoSettings.h"
+
+// +--------------------------------------------------------------------+
+
+Video* Video::video_instance = 0;
+
+// +--------------------------------------------------------------------+
+
+Video::Video()
+{
+ status = VIDEO_OK;
+ video_instance = this;
+
+ shadow_enabled = true;
+ bump_enabled = true;
+ spec_enabled = true;
+
+ camera = 0;
+}
+
+Video::~Video()
+{
+ if (video_instance == this)
+ video_instance = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Video::IsWindowed() const
+{
+ const VideoSettings* vs = GetVideoSettings();
+
+ if (vs)
+ return vs->IsWindowed();
+
+ return false;
+}
+
+bool
+Video::IsFullScreen() const
+{
+ const VideoSettings* vs = GetVideoSettings();
+
+ if (vs)
+ return !vs->IsWindowed();
+
+ return true;
+}
diff --git a/nGenEx/Video.h b/nGenEx/Video.h
new file mode 100644
index 0000000..393a704
--- /dev/null
+++ b/nGenEx/Video.h
@@ -0,0 +1,242 @@
+/* Project nGen
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: Video.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Video Interface
+*/
+
+#ifndef Video_h
+#define Video_h
+
+#include "Geometry.h"
+#include "Color.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Video;
+struct VideoMode;
+class VideoSettings;
+class VideoPrivateData;
+
+class Bitmap;
+class Camera;
+struct Rect;
+struct Poly;
+struct Material;
+struct VertexSet;
+class Light;
+class Solid;
+class Surface;
+
+// +--------------------------------------------------------------------+
+
+struct RenderStats
+{
+ int nframe;
+ int nverts;
+ int npolys;
+ int nlines;
+
+ int ncalls;
+
+ int total_verts;
+ int total_polys;
+ int total_lines;
+
+ void Clear() { nverts = npolys = nlines = ncalls =
+ total_verts = total_polys = total_lines = 0; }
+};
+
+// +--------------------------------------------------------------------+
+
+class Video
+{
+public:
+ enum STATUS { VIDEO_OK, VIDEO_ERR, VIDEO_BAD_PARM };
+
+ enum RENDER_STATE {
+ FILL_MODE,
+ SHADE_MODE,
+ LIGHTING_ENABLE,
+ Z_ENABLE,
+ Z_WRITE_ENABLE,
+ Z_BIAS,
+ TEXTURE_FILTER,
+ DITHER_ENABLE,
+ SPECULAR_ENABLE,
+ FOG_ENABLE,
+ FOG_COLOR,
+ FOG_DENSITY,
+ STENCIL_ENABLE,
+ TEXTURE_WRAP,
+ LIGHTING_PASS,
+
+ RENDER_STATE_MAX
+ };
+
+ enum BLEND_TYPE {
+ BLEND_SOLID = 1,
+ BLEND_ALPHA = 2,
+ BLEND_ADDITIVE = 4,
+ BLEND_FORCE_DWORD = 0x7fffffff,
+ };
+
+ enum SHADE_TYPE {
+ SHADE_FLAT = 1,
+ SHADE_GOURAUD = 2,
+ SHADE_PHONG = 3,
+ SHADE_FORCE_DWORD = 0x7fffffff,
+ };
+
+ enum FILL_TYPE {
+ FILL_POINT = 1,
+ FILL_WIREFRAME = 2,
+ FILL_SOLID = 3,
+ FILL_FORCE_DWORD = 0x7fffffff,
+ };
+
+ enum FILTER_TYPE {
+ FILTER_NONE = 1,
+ FILTER_LINEAR = 2,
+ FILTER_MIPMAP = 3,
+ FILTER_MIPLINEAR = 4,
+ FILTER_TRILINEAR = 6,
+ FILTER_FORCE_DWORD = 0x7fffffff,
+ };
+
+ enum PROJECTION_TYPE {
+ PROJECTION_PERSPECTIVE = 1,
+ PROJECTION_ORTHOGONAL = 2,
+ PROJECTION_FORCE_DWORD = 0x7fffffff,
+ };
+
+ Video();
+ virtual ~Video();
+
+ STATUS Status() const { return status; }
+ virtual const VideoSettings*
+ GetVideoSettings() const { return 0; }
+ virtual bool SetVideoSettings(const VideoSettings* vs) { return false; }
+ virtual bool Reset(const VideoSettings* vs) { return false; }
+
+ virtual bool SetBackgroundColor(Color c) { return false; }
+ virtual bool SetGammaLevel(int g) { return true; }
+ virtual bool SetObjTransform(const Matrix& o, const Point& l){ return false; }
+
+ virtual int Width() const { return 0; }
+ virtual int Height() const { return 0; }
+ virtual int Depth() const { return 0; }
+
+ virtual void RecoverSurfaces() { }
+
+ virtual bool ClearAll() { return false; }
+ virtual bool ClearDepthBuffer() { return false; }
+ virtual bool Present() { return false; }
+ virtual bool Pause() { return false; }
+ virtual bool Resume() { return false; }
+
+ virtual bool IsWindowed() const;
+ virtual bool IsFullScreen() const;
+ virtual bool IsModeSupported(int width, int height, int bpp)
+ const { return true; }
+ virtual bool IsHardware() const { return false; }
+ virtual bool IsHardwareTL() const { return false; }
+ virtual int ZDepth() const { return 0; }
+ virtual DWORD VidMemFree() const { return 0; }
+ virtual int D3DLevel() const { return 0; }
+ virtual int MaxTexSize() const { return 256; }
+ virtual int MaxTexAspect() const { return 0; }
+ virtual int GammaLevel() const { return 190; }
+
+ virtual bool IsShadowEnabled() const { return shadow_enabled; }
+ virtual bool IsBumpMapEnabled() const { return bump_enabled; }
+ virtual bool IsSpecMapEnabled() const { return spec_enabled; }
+
+ virtual void SetShadowEnabled(bool e) { shadow_enabled = e; }
+ virtual void SetBumpMapEnabled(bool e) { bump_enabled = e; }
+ virtual void SetSpecMapEnabled(bool e) { spec_enabled = e; }
+
+ virtual bool Capture(Bitmap& bmp) { return false; }
+ virtual bool GetWindowRect(Rect& r) { return false; }
+ virtual bool SetWindowRect(const Rect& r) { return false; }
+ virtual bool SetViewport(int x, int y, int w, int h) { return false; }
+ virtual bool SetCamera(const Camera* cam) { camera = cam;
+ return false; }
+ virtual bool SetProjection(float fov,
+ float znear=1.0f,
+ float zfar=1.0e6f,
+ DWORD type=PROJECTION_PERSPECTIVE) { return false; }
+ virtual bool SetEnvironment(Bitmap** faces) { return false; }
+ virtual bool SetAmbient(Color c) { return false; }
+ virtual bool SetLights(const List<Light>& lights) { return false; }
+ virtual bool SetRenderState(RENDER_STATE state, DWORD value) { return false; }
+ virtual bool SetBlendType(int blend_type) { return false; }
+ virtual bool StartFrame() { return false; }
+ virtual bool EndFrame() { return false; }
+
+ virtual bool DrawPolys(int npolys, Poly* p) { return false; }
+ virtual bool DrawScreenPolys(int npolys, Poly* p, int blend=0) { return false; }
+ virtual bool DrawSolid(Solid* s, DWORD blend_modes=0xf) { return false; }
+ virtual bool DrawShadow(Solid* s, int nverts, Vec3* verts, bool vis=false)
+ { return false; }
+ virtual bool DrawLines(int nlines, Vec3* v, Color c, int blend=0) { return false; }
+ virtual bool DrawScreenLines(int nlines, float* v, Color c, int blend=0)
+ { return false; }
+ virtual bool DrawPoints(VertexSet* v) { return false; }
+ virtual bool DrawPolyOutline(Poly* p) { return false; }
+ virtual bool UseMaterial(Material* m) { return false; }
+
+ virtual bool UseXFont(const char* name, int size, bool b, bool i) { return false; }
+ virtual bool DrawText(const char* text, int count, const Rect& rect,
+ DWORD format, Color c) { return false; }
+
+ virtual void PreloadTexture(Bitmap* bmp) { }
+ virtual void PreloadSurface(Surface* s) { }
+ virtual void InvalidateCache() { }
+
+ const Camera* GetCamera() const { return camera; }
+ const RenderStats& GetStats() const { return stats; }
+ static Video* GetInstance() { return video_instance; }
+
+protected:
+ STATUS status;
+ RenderStats stats;
+ const Camera* camera;
+
+ bool shadow_enabled;
+ bool bump_enabled;
+ bool spec_enabled;
+
+ static Video* video_instance;
+};
+
+// +--------------------------------------------------------------------+
+
+class VideoPrivateData
+{
+public:
+ VideoPrivateData() : valid(false) { }
+ virtual ~VideoPrivateData() { }
+
+ virtual int GetType() const { return 0; }
+
+ virtual bool IsValid() const { return valid; }
+ virtual void Invalidate() { valid = false; }
+ virtual void Validate() { valid = true; }
+
+protected:
+ bool valid;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Video_h
+
diff --git a/nGenEx/VideoDX9.cpp b/nGenEx/VideoDX9.cpp
new file mode 100644
index 0000000..a818a2d
--- /dev/null
+++ b/nGenEx/VideoDX9.cpp
@@ -0,0 +1,3675 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoDX9.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct3D Video class for DirectX 9
+*/
+
+#include "MemDebug.h"
+#include "VideoDX9.h"
+#include "VideoDX9Enum.h"
+#include "VideoDX9VertexBuffer.h"
+#include "TexDX9.h"
+#include "TexCubeDX9.h"
+#include "Camera.h"
+#include "Color.h"
+#include "DataLoader.h"
+#include "Polygon.h"
+#include "Light.h"
+#include "Solid.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* msg, ...);
+char* D3DErrStr(HRESULT dderr);
+void VideoDX9Error(const char* msg, HRESULT dderr);
+static TexCacheDX9* texcache = 0;
+static TexCubeDX9* environment_cube = 0;
+static bool surface_has_tangent_data = false;
+static Light* main_light;
+static Light* back_light;
+static D3DXMATRIX matrixWorld;
+static D3DXMATRIX matrixView;
+static D3DXMATRIX matrixProj;
+static D3DXMATRIX matrixWorldInverse;
+
+extern int VD3D_describe_things;
+
+#ifndef RELEASE
+#define RELEASE(x) if (x) { x->Release(); x=NULL; }
+#endif
+
+#ifndef F2DW
+#define F2DW(x) (*(DWORD*)(&x))
+#endif
+
+#ifndef DW2I
+#define DW2I(x) (*(int*)(&x))
+#endif
+
+// +--------------------------------------------------------------------+
+
+typedef HRESULT (WINAPI * LPDDCE)(GUID FAR *, LPVOID *, REFIID , IUnknown FAR *);
+
+static D3DMATRIX identity_matrix = {
+ FLOAT(1.0), FLOAT(0.0), FLOAT(0.0), FLOAT(0.0),
+ FLOAT(0.0), FLOAT(1.0), FLOAT(0.0), FLOAT(0.0),
+ FLOAT(0.0), FLOAT(0.0), FLOAT(1.0), FLOAT(0.0),
+ FLOAT(0.0), FLOAT(0.0), FLOAT(0.0), FLOAT(1.0)
+};
+
+// +--------------------------------------------------------------------+
+
+List<Model> model_clients;
+
+class VideoDX9SurfaceData : public VideoPrivateData
+{
+public:
+ VideoDX9SurfaceData(Model* m) : model(m), vertex_buffer(0), index_buffer(0) {
+ if (!model_clients.contains(model))
+ model_clients.append(model);
+ }
+
+ virtual ~VideoDX9SurfaceData() {
+ model_clients.remove(model);
+
+ delete vertex_buffer;
+ delete index_buffer;
+ }
+
+ enum { TYPE = 9001 };
+ virtual int GetType() const { return TYPE; }
+
+ Model* model;
+ VideoDX9VertexBuffer* vertex_buffer;
+ VideoDX9IndexBuffer* index_buffer;
+};
+
+class VideoDX9SegmentData : public VideoPrivateData
+{
+public:
+ VideoDX9SegmentData() : first_vert(0), num_verts(0), first_index(0), num_tris(0) { }
+ virtual ~VideoDX9SegmentData() { }
+
+ enum { TYPE = 9002 };
+ virtual int GetType() const { return TYPE; }
+
+ int first_vert;
+ int num_verts;
+ int first_index;
+ int num_tris;
+};
+
+// +--------------------------------------------------------------------+
+
+static int d3dstate_table[] = {
+ D3DRS_FILLMODE, // FILL_MODE
+ D3DRS_SHADEMODE, // SHADE_MODE
+ D3DRS_LIGHTING, // LIGHTING_ENABLE
+ D3DRS_ZENABLE, // Z_ENABLE
+ D3DRS_ZWRITEENABLE, // Z_WRITE_ENABLE
+ D3DRS_DEPTHBIAS, // Z_BIAS
+ 0, // TEXTURE_FILTER
+ D3DRS_DITHERENABLE, // DITHER_ENABLE
+ D3DRS_SPECULARENABLE, // SPECULAR_ENABLE
+ D3DRS_FOGENABLE, // FOG_ENABLE
+ D3DRS_FOGCOLOR, // FOG_COLOR
+ D3DRS_FOGDENSITY, // FOG_DENSITY
+ D3DRS_STENCILENABLE, // STENCIL_ENABLE
+ 0x11111111, // TEXTURE_WRAP (special case)
+ 0 // LIGHTING_PASS
+};
+
+static const int NUM_SCREEN_VERTS = 1024;
+static const int NUM_SCREEN_INDICES = NUM_SCREEN_VERTS * 2;
+
+// +--------------------------------------------------------------------+
+
+struct VideoDX9ScreenVertex
+{
+ FLOAT sx, sy, sz, rhw;
+ DWORD diffuse;
+ FLOAT tu, tv;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9ScreenVertex::FVF = D3DFVF_XYZRHW |
+ D3DFVF_DIFFUSE |
+ D3DFVF_TEX1;
+
+struct VideoDX9NormalVertex
+{
+ FLOAT x, y, z;
+ FLOAT nx, ny, nz;
+ FLOAT t0u, t0v;
+ FLOAT t1u, t1v;
+ FLOAT tx, ty, tz;
+ FLOAT bx, by, bz;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9NormalVertex::FVF = 0;
+
+// Global Vertex Declaration shared by shaders
+D3DVERTEXELEMENT9 videoDX9NormalVertexElements[] =
+{
+ { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
+ { 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 },
+ { 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
+ { 0, 32, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
+ { 0, 40, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 2 },
+ { 0, 52, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 3 },
+ D3DDECL_END()
+};
+
+struct VideoDX9SolidVertex
+{
+ FLOAT x, y, z;
+ FLOAT nx, ny, nz;
+ FLOAT tu, tv;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9SolidVertex::FVF = D3DFVF_XYZ |
+ D3DFVF_NORMAL |
+ D3DFVF_TEX1 |
+ D3DFVF_TEXCOORDSIZE2(0);
+
+struct VideoDX9LuminousVertex
+{
+ FLOAT x, y, z;
+ DWORD diffuse;
+ FLOAT tu, tv;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9LuminousVertex::FVF = D3DFVF_XYZ |
+ D3DFVF_DIFFUSE |
+ D3DFVF_TEX1 |
+ D3DFVF_TEXCOORDSIZE2(0);
+
+struct VideoDX9DetailVertex
+{
+ FLOAT x, y, z;
+ DWORD diffuse;
+ DWORD specular;
+ FLOAT tu, tv;
+ FLOAT tu1, tv1;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9DetailVertex::FVF = D3DFVF_XYZ |
+ D3DFVF_DIFFUSE |
+ D3DFVF_SPECULAR |
+ D3DFVF_TEX2;
+
+struct VideoDX9LineVertex
+{
+ FLOAT x, y, z;
+ DWORD diffuse;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9LineVertex::FVF = D3DFVF_XYZ |
+ D3DFVF_DIFFUSE;
+
+enum {
+ DX9_STRATEGY_NONE,
+ DX9_STRATEGY_SIMPLE,
+ DX9_STRATEGY_GLOW,
+ DX9_STRATEGY_SPECMAP,
+ DX9_STRATEGY_EMISSIVE,
+ DX9_STRATEGY_SPEC_EMISSIVE,
+ DX9_STRATEGY_BLEND,
+ DX9_STRATEGY_BLEND_DETAIL
+};
+
+// +--------------------------------------------------------------------+
+
+static VideoDX9* video_dx9_instance = 0;
+
+VideoDX9::VideoDX9(const HWND& window, VideoSettings* vs)
+ : width(0), height(0), bpp(0), hwnd(window), surface(0),
+ d3d(0), d3ddevice(0), device_lost(false), fade(0),
+ zdepth(0), gamma(128), num_verts(0), first_vert(0),
+ current_texture(0), screen_vbuf(0), screen_ibuf(0),
+ font_verts(0), font_indices(0), font_nverts(0),
+ nlights(0), use_material(0), d3dx_font(0),
+ segment_material(0), strategy(0), passes(0),
+ screen_line_verts(0), line_verts(0),
+ vertex_declaration(0),
+ magic_fx(0), magic_fx_code(0), magic_fx_code_len(0)
+{
+ video_dx9_instance = this;
+
+ Print("\n********************************\n");
+ Print("* Direct 3D version 9 *\n");
+ Print("********************************\n\n");
+
+ status = VIDEO_ERR;
+ HRESULT err = E_OUTOFMEMORY;
+
+ d3d = Direct3DCreate9(D3D_SDK_VERSION);
+ dx9enum = new(__FILE__,__LINE__) VideoDX9Enum(d3d);
+
+ if (d3d && dx9enum) {
+ if (vs) {
+ dx9enum->req_fullscreen = vs->is_windowed ? false : true;
+ dx9enum->req_windowed = vs->is_windowed ? true : false;
+ dx9enum->min_stencil_bits = vs->shadows ? 8 : 0;
+ dx9enum->uses_depth_buffer = true;
+ }
+ else {
+ dx9enum->req_fullscreen = video_settings.is_windowed ? false : true;
+ dx9enum->req_windowed = video_settings.is_windowed ? true : false;
+ dx9enum->min_stencil_bits = video_settings.shadows ? 8 : 0;
+ dx9enum->uses_depth_buffer = true;
+ }
+
+ err = dx9enum->Enumerate();
+
+ if (FAILED(err)) {
+ VideoDX9Error("(ctor) could not enumerate dx9 properties", err);
+ delete dx9enum;
+ return;
+ }
+ }
+ else {
+ VideoDX9Error("(ctor) could not create enumerator", err);
+ return;
+ }
+
+ SetVideoSettings(vs);
+
+ if (video_settings.is_windowed)
+ dx9enum->SuggestWindowSettings(&video_settings);
+ else
+ dx9enum->SuggestFullscreenSettings(&video_settings);
+
+ SetupParams();
+
+ if (VD3D_describe_things > 2) {
+ Print("\nD3DPRESENT_PARAMETERS:\n");
+ Print(" BackBufferWidth: %d\n", d3dparams.BackBufferWidth);
+ Print(" BackBufferHeight: %d\n", d3dparams.BackBufferHeight);
+ Print(" BackBufferCount: %d\n", d3dparams.BackBufferCount);
+ Print(" BackBufferFormat: %s\n", VideoDX9DisplayMode::D3DFormatToString(d3dparams.BackBufferFormat));
+ Print(" Multisample Type: %d\n", d3dparams.MultiSampleType);
+ Print(" Multisample Qual: %d\n", d3dparams.MultiSampleQuality);
+ Print(" Swap Effect: %d\n", d3dparams.SwapEffect);
+ Print(" Device Window: %08X\n", d3dparams.hDeviceWindow);
+ Print(" Windowed: %s\n", d3dparams.Windowed ? "true" : "false");
+ Print(" Enable Depth/Stencil: %s\n", d3dparams.EnableAutoDepthStencil ? "true" : "false");
+ Print(" Depth/Stencil Format: %s\n", VideoDX9DisplayMode::D3DFormatToString(d3dparams.AutoDepthStencilFormat));
+ Print(" Flags: %08X\n", d3dparams.Flags);
+ Print(" Fullscreen Refresh: %d Hz\n", d3dparams.FullScreen_RefreshRateInHz);
+
+ switch (d3dparams.PresentationInterval) {
+ case D3DPRESENT_INTERVAL_IMMEDIATE:
+ Print(" Present Interval: IMMEDIATE\n");
+ break;
+
+ case D3DPRESENT_INTERVAL_DEFAULT:
+ Print(" Present Interval: DEFAULT\n");
+ break;
+
+ case D3DPRESENT_INTERVAL_ONE:
+ Print(" Present Interval: ONE\n");
+ break;
+
+ case D3DPRESENT_INTERVAL_TWO:
+ Print(" Present Interval: TWO\n");
+ break;
+
+ case D3DPRESENT_INTERVAL_THREE:
+ Print(" Present Interval: THREE\n");
+ break;
+
+ case D3DPRESENT_INTERVAL_FOUR:
+ Print(" Present Interval: FOUR\n");
+ break;
+
+ default:
+ Print(" Present Interval: Unknown (%d)\n", d3dparams.PresentationInterval);
+ break;
+ }
+
+ Print("\n");
+ }
+
+ Print(" Creating Video Device for HWND = %08x\n", window);
+
+ err = d3d->CreateDevice(D3DADAPTER_DEFAULT,
+ D3DDEVTYPE_HAL,
+ window,
+ D3DCREATE_HARDWARE_VERTEXPROCESSING,
+ &d3dparams,
+ &d3ddevice);
+
+ if (FAILED(err)) {
+ VideoDX9Error("(ctor) could not create device", err);
+ return;
+ }
+
+ width = video_settings.GetWidth();
+ height = video_settings.GetHeight();
+ bpp = video_settings.GetDepth();
+
+ shadow_enabled = vs->shadows;
+ bump_enabled = vs->bumpmaps;
+ spec_enabled = vs->specmaps;
+
+ render_state[FILL_MODE] = FILL_SOLID;
+ render_state[SHADE_MODE] = SHADE_GOURAUD;
+ render_state[Z_ENABLE] = false;
+ render_state[Z_WRITE_ENABLE] = false;
+ render_state[Z_BIAS] = 0;
+ render_state[TEXTURE_FILTER] = FILTER_LINEAR;
+ render_state[DITHER_ENABLE] = false;
+ render_state[SPECULAR_ENABLE] = true;
+ render_state[FOG_ENABLE] = false;
+ render_state[FOG_COLOR] = 0;
+ render_state[FOG_DENSITY] = 0;
+ render_state[STENCIL_ENABLE] = false;
+ render_state[TEXTURE_WRAP] = true;
+ render_state[LIGHTING_PASS] = 0;
+
+ ZeroMemory(&rect, sizeof(rect));
+
+ if (!texcache)
+ texcache = new(__FILE__,__LINE__) TexCacheDX9(this);
+
+ if (texcache)
+ texcache->count++;
+
+ if (VD3D_describe_things > 0) {
+ DWORD vmf = VidMemFree() / (1024 * 1024);
+ Print(" Available Texture Memory: %d MB\n\n", vmf);
+ }
+
+ if (CreateBuffers()) {
+ d3ddevice->SetRenderState(D3DRS_ALPHATESTENABLE, false);
+ d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
+
+ status = VIDEO_OK;
+ }
+
+ ZeroMemory(font_name, 64);
+ font_size = 0;
+ font_bold = false;
+ font_ital = false;
+}
+
+// +--------------------------------------------------------------------+
+
+VideoDX9::~VideoDX9()
+{
+ DestroyBuffers();
+
+ texcache->count--;
+ if (!texcache->count) {
+ delete texcache;
+ texcache = 0;
+ }
+
+ delete environment_cube;
+ delete dx9enum;
+
+ RELEASE(d3dx_font);
+ RELEASE(d3ddevice);
+ RELEASE(d3d);
+
+ if (magic_fx_code)
+ delete [] magic_fx_code;
+
+ Print(" VideoDX9: shutdown\n");
+ video_dx9_instance = 0;
+}
+
+IDirect3DDevice9*
+VideoDX9::GetD3DDevice9()
+{
+ if (video_dx9_instance)
+ return video_dx9_instance->d3ddevice;
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetupParams()
+{
+ if (!dx9enum || dx9enum->NumAdapters() < 1) {
+ status = VIDEO_ERR;
+ return false;
+ }
+
+ int adapter_index = video_settings.GetAdapterIndex();
+
+ if (adapter_index < 0 || adapter_index >= dx9enum->NumAdapters()) {
+ ::Print("WARNING: VideoDX9 could not select adapter %d (max=%d)\n",
+ adapter_index, dx9enum->NumAdapters());
+
+ adapter_index = 0;
+ }
+
+ dx9enum->SelectAdapter(adapter_index);
+
+ d3dparams.Windowed = video_settings.IsWindowed();
+ d3dparams.BackBufferCount = 2;
+ d3dparams.MultiSampleType = D3DMULTISAMPLE_NONE;
+ d3dparams.MultiSampleQuality = 0;
+ d3dparams.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ d3dparams.EnableAutoDepthStencil = dx9enum->uses_depth_buffer;
+ d3dparams.hDeviceWindow = hwnd;
+
+ if (dx9enum->uses_depth_buffer) {
+ d3dparams.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
+ d3dparams.AutoDepthStencilFormat = (D3DFORMAT) video_settings.GetDepthStencilFormat();
+ }
+ else {
+ d3dparams.Flags = 0;
+ }
+
+ d3dparams.Flags |= D3DPRESENTFLAG_DEVICECLIP;
+
+ if (video_settings.IsWindowed()) {
+ d3dparams.BackBufferWidth = video_settings.window_width;
+ d3dparams.BackBufferHeight = video_settings.window_height;
+ d3dparams.BackBufferFormat = (D3DFORMAT) video_settings.GetBackBufferFormat();
+ d3dparams.FullScreen_RefreshRateInHz = 0;
+ d3dparams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
+ }
+ else {
+ d3dparams.BackBufferWidth = video_settings.GetWidth();
+ d3dparams.BackBufferHeight = video_settings.GetHeight();
+ d3dparams.BackBufferFormat = (D3DFORMAT) video_settings.GetBackBufferFormat();
+ d3dparams.FullScreen_RefreshRateInHz = video_settings.GetRefreshRate();
+ d3dparams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
+ }
+
+ return true;
+}
+
+
+bool
+VideoDX9::IsModeSupported(int w, int h, int b) const
+{
+ if (dx9enum)
+ return dx9enum->IsModeSupported(w, h, b);
+
+ return false;
+}
+
+bool
+VideoDX9::SetVideoSettings(const VideoSettings* vs)
+{
+ // custom video settings:
+ if (vs) {
+ if (vs != &video_settings)
+ CopyMemory(&video_settings, vs, sizeof(VideoSettings));
+ }
+
+ // default video settings:
+ else {
+ ZeroMemory(&video_settings, sizeof(VideoSettings));
+
+ video_settings.fullscreen_mode.width = 800;
+ video_settings.fullscreen_mode.height = 600;
+ video_settings.fullscreen_mode.format = VideoMode::FMT_X8R8G8B8;
+ }
+
+ return true;
+}
+
+bool
+VideoDX9::Reset(const VideoSettings* vs)
+{
+ if (!d3ddevice || !SetVideoSettings(vs)) {
+ status = VIDEO_ERR;
+ return false;
+ }
+
+ bool using_x_font = (d3dx_font != 0);
+
+ RELEASE(d3dx_font);
+ InvalidateCache();
+ DestroyBuffers();
+ SetupParams();
+
+ HRESULT hr = d3ddevice->Reset(&d3dparams);
+
+ if (FAILED(hr)) {
+ VideoDX9Error("could not reset d3d device", hr);
+ status = VIDEO_ERR;
+ return false;
+ }
+
+ // Store render target surface desc
+ IDirect3DSurface9* back_buffer;
+ d3ddevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &back_buffer);
+ back_buffer->GetDesc(&back_buffer_desc);
+ RELEASE(back_buffer);
+
+ width = video_settings.GetWidth();
+ height = video_settings.GetHeight();
+ bpp = video_settings.GetDepth();
+
+ shadow_enabled = vs->shadows;
+ bump_enabled = vs->bumpmaps;
+ spec_enabled = vs->specmaps;
+
+
+ if (CreateBuffers()) {
+ d3ddevice->SetRenderState(D3DRS_ALPHATESTENABLE, false);
+ d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
+
+ D3DVIEWPORT9 view;
+
+ hr = d3ddevice->GetViewport(&view);
+ if (SUCCEEDED(hr)) {
+ rect.x = view.X;
+ rect.y = view.Y;
+ rect.w = view.Width;
+ rect.h = view.Height;
+ }
+
+ if (using_x_font)
+ UseXFont(font_name, font_size, font_bold, font_ital);
+
+ status = VIDEO_OK;
+ }
+
+ return status == VIDEO_OK;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::CreateBuffers()
+{
+ if (d3ddevice) {
+ UINT vertex_size = sizeof(VideoDX9ScreenVertex);
+ UINT index_size = sizeof(WORD);
+
+ if (!screen_vbuf) {
+ screen_vbuf = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
+ this,
+ NUM_SCREEN_VERTS,
+ vertex_size,
+ VideoDX9ScreenVertex::FVF,
+ D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
+ }
+
+ if (!screen_ibuf) {
+ screen_ibuf = new(__FILE__,__LINE__) VideoDX9IndexBuffer(
+ this,
+ NUM_SCREEN_INDICES,
+ D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
+ }
+
+ screen_line_verts = new(__FILE__,__LINE__) VideoDX9ScreenVertex[256];
+ line_verts = new(__FILE__,__LINE__) VideoDX9LineVertex[512];
+
+ // create effects:
+ LPD3DXBUFFER code_buffer = 0;
+ DataLoader* loader = DataLoader::GetLoader();
+ HRESULT hr = E_FAIL;
+
+ hr = d3ddevice->CreateVertexDeclaration(videoDX9NormalVertexElements,
+ &vertex_declaration);
+
+ if (video_settings.use_effects && !magic_fx_code) {
+ if (loader) {
+ magic_fx_code_len = loader->LoadBuffer("magic.fx", magic_fx_code, true, true);
+ }
+ else {
+ FILE* f = ::fopen("magic.fx", "rb");
+
+ if (f) {
+ ::fseek(f, 0, SEEK_END);
+ magic_fx_code_len = ftell(f);
+ ::fseek(f, 0, SEEK_SET);
+
+ magic_fx_code = new(__FILE__,__LINE__) BYTE[magic_fx_code_len+1];
+ if (magic_fx_code) {
+ ::fread(magic_fx_code, magic_fx_code_len, 1, f);
+ magic_fx_code[magic_fx_code_len] = 0;
+ }
+
+ ::fclose(f);
+ }
+ }
+ }
+
+ if (video_settings.use_effects && magic_fx_code && magic_fx_code_len) {
+ hr = D3DXCreateEffect(d3ddevice,
+ magic_fx_code,
+ magic_fx_code_len,
+ 0, 0, 0, 0,
+ &magic_fx,
+ &code_buffer);
+
+ if (code_buffer) {
+ ::Print("ERROR - Failed to compile 'magic.fx'\n");
+ ::Print((const char*) code_buffer->GetBufferPointer());
+ ::Print("\n\n");
+ RELEASE(code_buffer);
+ }
+ }
+ }
+
+ return screen_vbuf && screen_ibuf;
+}
+
+bool
+VideoDX9::DestroyBuffers()
+{
+ if (line_verts) {
+ delete line_verts;
+ line_verts = 0;
+ }
+
+ if (screen_line_verts) {
+ delete screen_line_verts;
+ screen_line_verts = 0;
+ }
+
+ if (screen_vbuf) {
+ delete screen_vbuf;
+ screen_vbuf = 0;
+ }
+
+ if (screen_ibuf) {
+ delete screen_ibuf;
+ screen_ibuf = 0;
+ }
+
+ if (font_verts) {
+ delete [] font_verts;
+ font_verts = 0;
+ }
+
+ if (font_indices) {
+ delete [] font_indices;
+ font_indices = 0;
+ }
+
+ font_nverts = 0;
+
+ RELEASE(vertex_declaration);
+ RELEASE(magic_fx);
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+VideoDX9::VidMemFree() const
+{
+ UINT result = 0;
+
+ if (d3ddevice)
+ result = d3ddevice->GetAvailableTextureMem();
+
+ return result;
+}
+
+int
+VideoDX9::MaxTexSize() const
+{
+ if (d3d && dx9enum && dx9enum->GetAdapterInfo()) {
+ VideoDX9DeviceInfo* dev_info = dx9enum->GetDeviceInfo(video_settings.GetDeviceType());
+
+ if (dev_info) {
+ return (int) dev_info->caps.MaxTextureWidth;
+ }
+ }
+
+ return 0;
+}
+
+int
+VideoDX9::MaxTexAspect() const
+{
+ if (d3d && dx9enum && dx9enum->GetAdapterInfo()) {
+ VideoDX9DeviceInfo* dev_info = dx9enum->GetDeviceInfo(video_settings.GetDeviceType());
+
+ if (dev_info) {
+ return (int) dev_info->caps.MaxTextureAspectRatio;
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VideoDX9::RecoverSurfaces()
+{
+ Print("VideoDX9::RecoverSurfaces()\n");
+
+ HRESULT hr = D3D_OK;
+
+ surface = 0;
+
+ hr = d3ddevice->TestCooperativeLevel();
+
+ if (hr == D3DERR_DEVICELOST) {
+ // This means that some app took exclusive mode access
+ // we need to sit in a loop till we get back to the right mode.
+ Print("D3DERR_DEVICELOST\n");
+
+ do {
+ Sleep(500);
+ hr = d3ddevice->TestCooperativeLevel();
+ } while (hr == D3DERR_DEVICELOST);
+ }
+
+ if (hr == D3DERR_DEVICENOTRESET) {
+ if (Reset(&video_settings))
+ hr = S_OK;
+ }
+
+ if (SUCCEEDED(hr)) {
+ Print("* Invalidating Texture Cache\n");
+ // Re-fill the contents of textures which just got restored:
+ InvalidateCache();
+
+ device_lost = false;
+ }
+
+ Print("* Vid Mem Free: %8d\n", VidMemFree());
+ Print("* Recover Surfaces Complete.\n\n");
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetBackgroundColor(Color c)
+{
+ background = c;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// RampValue
+//
+// The gamma function with inputs in [0,255], scaled to a range with the
+// default range appropriate for D3DGAMMARAMP.
+//
+inline WORD
+RampValue(UINT i, double recip_gamma, double fade)
+{
+ return (WORD) (65535.0 * fade * pow(i/255.f, recip_gamma));
+}
+
+//-----------------------------------------------------------------------------
+// ReciprocalGamma
+//
+// Given a gamma corrected i in [0,255], return 1/gamma
+//
+inline float
+ReciprocalGamma(UINT i)
+{
+ return logf(i/255.f)/logf(0.5f);
+}
+
+//-----------------------------------------------------------------------------
+// GammaValue
+//
+// Given a gamma corrected color channel value in [0,255], return the gamma.
+//
+inline float
+GammaValue(UINT i)
+{
+ return logf(0.5f)/logf(i/255.f);
+}
+
+bool
+VideoDX9::SetGammaLevel(int g)
+{
+ HRESULT hr = E_FAIL;
+ double f = Color::GetFade();
+
+ if (gamma != g || fade != f) {
+ if (d3ddevice) {
+ //::Print("VideoDX9 - SetGammaLevel(%d) fade = %f\n", g, f);
+
+ // compute 1/gamma
+ float recip_gray = ReciprocalGamma(g);
+
+ // compute i**(1/gamma) for all i and scale to range
+ for (UINT i = 0; i < 256; i++) {
+ int val = RampValue(i, recip_gray, f);
+
+ gamma_ramp.red[i] = val;
+ gamma_ramp.green[i] = val;
+ gamma_ramp.blue[i] = val;
+ }
+
+ d3ddevice->SetGammaRamp(0, D3DSGR_NO_CALIBRATION, &gamma_ramp);
+ hr = D3D_OK;
+ }
+
+ gamma = g;
+ fade = f;
+ }
+
+ return SUCCEEDED(hr);
+}
+
+bool
+VideoDX9::SetObjTransform(const Matrix& orient, const Point& loc)
+{
+ HRESULT hr = E_FAIL;
+
+ if (d3ddevice) {
+ D3DMATRIX world_matrix;
+ CreateD3DMatrix(world_matrix, orient, loc);
+ hr = d3ddevice->SetTransform(D3DTS_WORLD, &world_matrix);
+
+ matrixWorld = world_matrix;
+ D3DXMatrixInverse(&matrixWorldInverse, 0, &matrixWorld);
+ }
+
+ return SUCCEEDED(hr);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::ClearAll()
+{
+ HRESULT err;
+
+ err = d3ddevice->Clear(0,
+ NULL,
+ D3DCLEAR_TARGET | D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER,
+ background.Value(),
+ 1.0f,
+ 0);
+
+ if (FAILED(err)) {
+ static int report = 10;
+ if (report > 0) {
+ VideoDX9Error("Failed to clear device", err);
+ report--;
+ }
+ }
+
+ return true;
+}
+
+bool
+VideoDX9::ClearDepthBuffer()
+{
+ HRESULT err;
+
+ err = d3ddevice->Clear(0,
+ NULL,
+ D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER,
+ 0,
+ 1.0f,
+ 0);
+
+ if (FAILED(err)) {
+ static int report = 10;
+ if (report > 0) {
+ VideoDX9Error("Failed to clear depth buffer", err);
+ report--;
+ }
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::Present()
+{
+ // Show the frame on the primary surface.
+ HRESULT err = d3ddevice->Present( NULL, NULL, NULL, NULL );
+
+ if (FAILED(err)) {
+ if (err == D3DERR_DEVICELOST) {
+ device_lost = true;
+ }
+
+ else {
+ static int report = 10;
+ if (report > 0) {
+ VideoDX9Error("Could not present frame", err);
+ report--;
+ }
+ }
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::Pause()
+{
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::Resume()
+{
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VideoDX9::PreloadSurface(Surface* s)
+{
+ if (s)
+ PrepareSurface(s);
+}
+
+void
+VideoDX9::PreloadTexture(Bitmap* tex)
+{
+ if (texcache && tex)
+ texcache->FindTexture(tex);
+}
+
+void
+VideoDX9::InvalidateCache()
+{
+ ListIter<Model> iter = model_clients;
+ while (++iter) {
+ // remove each model from the list...
+ Model* model = iter.removeItem();
+
+ // ...so that the buffer destructor doesn't
+ // do it and mess up the iterator.
+ model->DeletePrivateData();
+ }
+
+ if (texcache)
+ texcache->InvalidateCache();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VideoDX9::CreateD3DMatrix(D3DMATRIX& result, const Matrix& m, const Point& p)
+{
+ result._11 = (float) m.elem[0][0];
+ result._12 = (float) m.elem[1][0];
+ result._13 = (float) m.elem[2][0];
+ result._14 = 0.0f;
+
+ result._21 = (float) m.elem[0][1];
+ result._22 = (float) m.elem[1][1];
+ result._23 = (float) m.elem[2][1];
+ result._24 = 0.0f;
+
+ result._31 = (float) m.elem[0][2];
+ result._32 = (float) m.elem[1][2];
+ result._33 = (float) m.elem[2][2];
+ result._34 = 0.0f;
+
+ result._41 = (float) p.x;
+ result._42 = (float) p.y;
+ result._43 = (float) p.z;
+ result._44 = 1.0f;
+}
+
+void
+VideoDX9::CreateD3DMatrix(D3DMATRIX& result, const Matrix& m, const Vec3& v)
+{
+ result._11 = (float) m.elem[0][0];
+ result._12 = (float) m.elem[1][0];
+ result._13 = (float) m.elem[2][0];
+ result._14 = 0.0f;
+
+ result._21 = (float) m.elem[0][1];
+ result._22 = (float) m.elem[1][1];
+ result._23 = (float) m.elem[2][1];
+ result._24 = 0.0f;
+
+ result._31 = (float) m.elem[0][2];
+ result._32 = (float) m.elem[1][2];
+ result._33 = (float) m.elem[2][2];
+ result._34 = 0.0f;
+
+ result._41 = v.x;
+ result._42 = v.y;
+ result._43 = v.z;
+ result._44 = 1.0f;
+}
+
+void
+VideoDX9::CreateD3DMaterial(D3DMATERIAL9& result, const Material& mtl)
+{
+ CopyMemory(&result.Diffuse, &mtl.Kd, sizeof(D3DCOLORVALUE));
+ CopyMemory(&result.Ambient, &mtl.Ka, sizeof(D3DCOLORVALUE));
+ CopyMemory(&result.Specular, &mtl.Ks, sizeof(D3DCOLORVALUE));
+ CopyMemory(&result.Emissive, &mtl.Ke, sizeof(D3DCOLORVALUE));
+
+ result.Power = mtl.power;
+}
+
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::Capture(Bitmap& bmp)
+{
+ if (d3ddevice) {
+ HRESULT hr = E_FAIL;
+ LPDIRECT3DSURFACE9 pSurf=NULL, pTempSurf=NULL;
+ D3DSURFACE_DESC desc;
+ D3DDISPLAYMODE dm;
+
+ // get display dimensions
+ // this will be the dimensions of the front buffer
+ hr = d3ddevice->GetDisplayMode(0, &dm);
+
+ if (FAILED(hr))
+ VideoDX9Error("VideoDX9::Capture - Can't get display mode!", hr);
+
+ desc.Width = dm.Width;
+ desc.Height = dm.Height;
+ desc.Format = D3DFMT_A8R8G8B8;
+
+ hr = d3ddevice->CreateOffscreenPlainSurface(
+ desc.Width,
+ desc.Height,
+ desc.Format,
+ D3DPOOL_SYSTEMMEM,
+ &pTempSurf,
+ NULL);
+
+ if (FAILED(hr)) {
+ VideoDX9Error("VideoDX9::Capture - Cannot create offscreen buffer 1", hr);
+ return false;
+ }
+
+ hr = d3ddevice->GetFrontBufferData(0, pTempSurf);
+
+ if (FAILED(hr)) {
+ RELEASE(pTempSurf);
+ VideoDX9Error("VideoDX9::Capture - Can't get front buffer", hr);
+ return false;
+ }
+
+
+ if (video_settings.IsWindowed()) {
+ POINT pt={0, 0};
+ RECT srcRect;
+
+ // capture only the client area of the screen:
+ ::GetClientRect(hwnd, &srcRect);
+ ::ClientToScreen(hwnd, (LPPOINT) &srcRect);
+ srcRect.right += srcRect.left;
+ srcRect.bottom += srcRect.top;
+
+ desc.Width = srcRect.right - srcRect.left;
+ desc.Height = srcRect.bottom - srcRect.top;
+ desc.Format = D3DFMT_A8R8G8B8; // this is what we get from the screen, so stick with it
+
+ // NB we can't lock the back buffer direct because it's no created that way
+ // and to do so hits performance, so copy to another surface
+ // Must be the same format as the source surface
+ hr = d3ddevice->CreateOffscreenPlainSurface(
+ desc.Width,
+ desc.Height,
+ desc.Format,
+ D3DPOOL_DEFAULT,
+ &pSurf,
+ NULL);
+
+ if (FAILED(hr)) {
+ RELEASE(pSurf);
+ VideoDX9Error("VideoDX9::Capture - Cannot create offscreen buffer 2", hr);
+ return false;
+ }
+
+ // Copy
+ hr = d3ddevice->UpdateSurface(pTempSurf, &srcRect, pSurf, &pt);
+
+ if (FAILED(hr)) {
+ RELEASE(pTempSurf);
+ RELEASE(pSurf);
+ VideoDX9Error("VideoDX9::Capture - Cannot update surface", hr);
+ return false;
+ }
+
+ RELEASE(pTempSurf);
+ pTempSurf = pSurf;
+ pSurf = NULL;
+ }
+
+ D3DLOCKED_RECT lockedRect;
+ hr = pTempSurf->LockRect(&lockedRect, NULL,
+ D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK);
+
+ if (FAILED(hr)) {
+ VideoDX9Error("VideoDX9::Capture - can't lock rect", hr);
+ RELEASE(pTempSurf);
+ return false;
+ }
+
+ // Allocate color buffer
+ DWORD* buffer = new DWORD[desc.Width * desc.Height];
+ BYTE* src = (BYTE*) lockedRect.pBits;
+ BYTE* dst = (BYTE*) buffer;
+ Color clr;
+
+ for (DWORD y = 0; y < desc.Height; y++) {
+ BYTE *pRow = src;
+
+ for (DWORD x = 0; x < desc.Width; x++) {
+ switch(desc.Format) {
+ case D3DFMT_R5G6B5:
+ clr = Color::Unformat(*((WORD*) (pRow)));
+
+ *dst++ = (BYTE) clr.Red();
+ *dst++ = (BYTE) clr.Green();
+ *dst++ = (BYTE) clr.Blue();
+ *dst++ = 255;
+ break;
+
+ case D3DFMT_A8R8G8B8:
+ case D3DFMT_X8R8G8B8:
+ *dst++ = pRow[0]; // R
+ *dst++ = pRow[1]; // G
+ *dst++ = pRow[2]; // B
+ *dst++ = 255;
+
+ pRow += 4;
+ break;
+
+ case D3DFMT_R8G8B8:
+ *dst++ = pRow[0]; // R
+ *dst++ = pRow[1]; // G
+ *dst++ = pRow[2]; // B
+ *dst++ = 255;
+
+ pRow += 3;
+ break;
+ }
+
+ }
+
+ src += lockedRect.Pitch;
+ }
+
+ bmp.CopyHighColorImage(desc.Width, desc.Height, buffer);
+
+ delete [] buffer;
+
+ RELEASE(pTempSurf);
+ RELEASE(pSurf);
+
+ return SUCCEEDED(hr);
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::GetWindowRect(Rect& r)
+{
+ if (d3ddevice && (rect.w < 1 || rect.h < 1)) {
+ D3DVIEWPORT9 view;
+ HRESULT hr = d3ddevice->GetViewport(&view);
+ if (SUCCEEDED(hr)) {
+ rect.x = view.X;
+ rect.y = view.Y;
+ rect.w = view.Width;
+ rect.h = view.Height;
+ }
+ }
+
+ r = rect;
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetWindowRect(const Rect& r)
+{
+ return SetViewport(r.x, r.y, r.w, r.h);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetViewport(int x, int y, int w, int h)
+{
+ if (!d3d || !d3ddevice)
+ return false;
+
+ HRESULT hr;
+
+ // set up the viewport according to args:
+ D3DVIEWPORT9 view;
+
+ view.X = x;
+ view.Y = y;
+ view.Width = w;
+ view.Height = h;
+ view.MinZ = 0.0f;
+ view.MaxZ = 1.0f;
+
+ hr = d3ddevice->SetViewport(&view);
+ if (FAILED(hr)) {
+ VideoDX9Error("could not initialize viewport", hr);
+ return false;
+ }
+
+ // set up the render state:
+ for (int i = FILL_MODE; i < TEXTURE_WRAP; i++) {
+ if (d3dstate_table[i]) {
+ d3ddevice->SetRenderState((D3DRENDERSTATETYPE) d3dstate_table[i], render_state[i]);
+ }
+ }
+
+ d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
+
+ rect.x = x;
+ rect.y = y;
+ rect.w = w;
+ rect.h = h;
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetAmbient(Color c)
+{
+ ambient = c;
+ return true;
+}
+
+bool
+VideoDX9::SetLights(const List<Light>& lights)
+{
+ if (d3ddevice) {
+ main_light = 0;
+ back_light = 0;
+
+ ListIter<Light> iter = (List<Light>&) lights;
+ int index = -1;
+
+ while (++iter) {
+ Light* light = iter.value();
+
+ if (light->IsActive()) {
+ D3DLIGHT9 d3d_light;
+ ZeroMemory(&d3d_light, sizeof(d3d_light));
+ d3d_light.Type = (D3DLIGHTTYPE) light->Type();
+
+ if (light->Type() == Light::LIGHT_DIRECTIONAL) {
+ d3d_light.Direction.x = (float) (-light->Location().x);
+ d3d_light.Direction.y = (float) (-light->Location().y);
+ d3d_light.Direction.z = (float) (-light->Location().z);
+
+ if (d3d_light.Direction.x == 0 &&
+ d3d_light.Direction.y == 0 &&
+ d3d_light.Direction.z == 0) {
+
+ d3d_light.Direction.y = -1;
+ }
+
+ if (light->CastsShadow()) {
+ if (!main_light || light->Intensity() > main_light->Intensity())
+ main_light = light;
+ }
+ else if (!back_light) {
+ back_light = light;
+ }
+ }
+ else {
+ d3d_light.Position.x = (float) ( light->Location().x);
+ d3d_light.Position.y = (float) ( light->Location().y);
+ d3d_light.Position.z = (float) ( light->Location().z);
+ }
+
+ float r = (light->GetColor().Red() / 255.0f) * light->Intensity();
+ float g = (light->GetColor().Green() / 255.0f) * light->Intensity();
+ float b = (light->GetColor().Blue() / 255.0f) * light->Intensity();
+
+ d3d_light.Diffuse.r = r;
+ d3d_light.Diffuse.g = g;
+ d3d_light.Diffuse.b = b;
+
+ d3d_light.Specular.r = r;
+ d3d_light.Specular.g = g;
+ d3d_light.Specular.b = b;
+
+ d3d_light.Range = light->Intensity() * 10.0f;
+ d3d_light.Attenuation0 = 0.1f;
+ d3d_light.Attenuation1 = 0.7f;
+ d3d_light.Attenuation2 = 0.0f;
+
+ index++;
+ d3ddevice->SetLight(index, &d3d_light);
+ d3ddevice->LightEnable(index, TRUE);
+ }
+ }
+
+ // turn off any unused lights from before:
+ while (nlights > index+1) {
+ d3ddevice->LightEnable(--nlights, FALSE);
+ }
+
+ nlights = index + 1;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool
+VideoDX9::SetCamera(const Camera* cam)
+{
+ if (d3ddevice) {
+ camera = cam;
+
+ D3DMATRIX m;
+ CreateD3DMatrix(m, cam->Orientation(), cam->Pos());
+ d3ddevice->SetTransform(D3DTS_VIEW, &m);
+ matrixView = m;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool
+VideoDX9::SetProjection(float fov, float znear, float zfar, DWORD type)
+{
+ if (d3ddevice && zfar > znear) {
+ D3DMATRIX m;
+ float h, w, Q;
+
+ double width = (float) (rect.w);
+ double height = (float) (rect.h);
+ ZeroMemory(&m, sizeof(m));
+
+ /***
+ *** PERSPECTIVE PROJECTION:
+ ***/
+
+ if (type == PROJECTION_PERSPECTIVE) {
+ double xscale = width / fov;
+ double yscale = height / fov;
+
+ double maxscale = xscale;
+ if (yscale > xscale) maxscale = yscale;
+
+ double xangle = atan(fov/2 * maxscale/xscale);
+
+ w = (float) (2/tan(xangle)); // 1/tan(x) == cot(x)
+ h = (float) (w * width/height);
+ Q = zfar/(zfar - znear);
+
+ m._11 = w;
+ m._22 = h;
+ m._33 = Q;
+ m._43 = -Q*znear;
+ m._34 = 1;
+ }
+
+ /***
+ *** ORTHOGONAL PROJECTION:
+ ***/
+
+ else if (type == PROJECTION_ORTHOGONAL) {
+ m._11 = (float) (fov/width);
+ m._22 = (float) (fov/height);
+ m._33 = (float) (1/(zfar-znear));
+ m._43 = (float) (znear/(znear-zfar));
+ m._44 = (float) (1);
+ }
+
+ else {
+ return false;
+ }
+
+ d3ddevice->SetTransform(D3DTS_PROJECTION, &m);
+ matrixProj = m;
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetEnvironment(Bitmap** faces)
+{
+ if (environment_cube && !faces) {
+ delete environment_cube;
+ environment_cube = 0;
+ return true;
+ }
+
+ if (!environment_cube) {
+ environment_cube = new(__FILE__,__LINE__) TexCubeDX9(this);
+ }
+
+ if (environment_cube) {
+ bool ok = true;
+ for (int i = 0; i < 6; i++)
+ ok = ok && environment_cube->LoadTexture(faces[i], i);
+ return ok;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetRenderState(RENDER_STATE state, DWORD value)
+{
+ if (!d3ddevice)
+ return false;
+
+ if (render_state[state] == value || d3dstate_table[state] == 0) {
+ render_state[state] = value;
+ return true;
+ }
+
+ HRESULT hr = E_FAIL;
+
+ // special case for texture wrapping:
+ if (state == TEXTURE_WRAP) {
+ DWORD wrap = D3DTADDRESS_CLAMP;
+
+ if (value)
+ wrap = D3DTADDRESS_WRAP;
+
+ hr = d3ddevice->SetSamplerState(0, D3DSAMP_ADDRESSU, wrap);
+ hr = d3ddevice->SetSamplerState(0, D3DSAMP_ADDRESSV, wrap);
+ }
+
+ // special case for fog enable:
+ else if (state == FOG_ENABLE) {
+ if (value) {
+ hr = d3ddevice->SetRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_EXP);
+ hr = d3ddevice->SetRenderState(D3DRS_FOGTABLEMODE, D3DFOG_NONE);
+ }
+
+ hr = d3ddevice->SetRenderState(D3DRS_FOGENABLE, value);
+ }
+
+ // special case for z bias
+ else if (state == Z_BIAS) {
+ if (value) {
+ FLOAT bias_scale = 1.0f;
+ FLOAT depth_bias = (FLOAT) (DW2I(value) / -10000.0);
+
+ hr = d3ddevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, F2DW(bias_scale));
+ hr = d3ddevice->SetRenderState(D3DRS_DEPTHBIAS, F2DW(depth_bias));
+ }
+ else {
+ hr = d3ddevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, 0);
+ hr = d3ddevice->SetRenderState(D3DRS_DEPTHBIAS, 0);
+ }
+ }
+
+ // set default z func along with z enable
+ else if (state == Z_ENABLE) {
+ hr = d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
+ hr = d3ddevice->SetRenderState(D3DRS_ZENABLE, value);
+ }
+
+ // all other render states:
+ else {
+ hr = d3ddevice->SetRenderState((D3DRENDERSTATETYPE) d3dstate_table[state], value);
+ }
+
+ if (FAILED(hr)) {
+ VideoDX9Error("could not SetRenderState", hr);
+ return false;
+ }
+ else {
+ render_state[state] = value;
+ }
+
+ return true;
+}
+
+bool
+VideoDX9::SetBlendType(int blend_type)
+{
+ if (blend_type == current_blend_state)
+ return true;
+
+ switch (blend_type) {
+ default:
+ // map misc blend types to SOLID
+ // and fall through to that case
+
+ blend_type = BLEND_SOLID;
+
+ case BLEND_SOLID:
+ d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
+ break;
+
+ case BLEND_ALPHA:
+ d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
+ d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
+ d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
+ break;
+
+ case BLEND_ADDITIVE:
+ d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
+ d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
+ d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
+ break;
+ }
+
+ current_blend_state = blend_type;
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::StartFrame()
+{
+ if (device_lost) {
+ RecoverSurfaces();
+
+ if (status != VIDEO_OK)
+ return false;
+ }
+
+ stats.Clear();
+
+ HRESULT err = 0;
+ static int frame_number = 1;
+ static int report_errs = 100;
+
+ stats.nframe = frame_number;
+ texcache->FrameNumber(frame_number++);
+
+ // update gamma ramp for global fade value:
+ SetGammaLevel(gamma);
+
+ ClearAll();
+
+ err = d3ddevice->BeginScene();
+
+ if (FAILED(err)) {
+ if (report_errs > 0) {
+ report_errs--;
+ VideoDX9Error("could not begin scene", err);
+ }
+
+ return false;
+ }
+
+ scene_active = 1;
+ current_blend_state = -1;
+
+ SetRenderState(LIGHTING_PASS, 0);
+ SetRenderState(STENCIL_ENABLE, false);
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::EndFrame()
+{
+ HRESULT err = 0;
+
+ if (scene_active) {
+ err = d3ddevice->EndScene();
+
+ if (FAILED(err)) {
+ VideoDX9Error("could not end scene", err);
+ return false;
+ }
+ }
+
+ scene_active = 0;
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+static DWORD ColorModulate(DWORD a, DWORD b)
+{
+ float a0 = (float) ((a ) & 0xff)/255.0f;
+ float a1 = (float) ((a>> 8) & 0xff)/255.0f;
+ float a2 = (float) ((a>>16) & 0xff)/255.0f;
+ float a3 = (float) ((a>>24) & 0xff)/255.0f;
+
+ float b0 = (float) ((b ) & 0xff)/255.0f;
+ float b1 = (float) ((b>> 8) & 0xff)/255.0f;
+ float b2 = (float) ((b>>16) & 0xff)/255.0f;
+ float b3 = (float) ((b>>24) & 0xff)/255.0f;
+
+ return (DWORD) ((BYTE)(a3*b3*255.0f) << 24) |
+ ((BYTE)(a2*b2*255.0f) << 16) |
+ ((BYTE)(a1*b1*255.0f) << 8) |
+ ((BYTE)(a0*b0*255.0f));
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::PopulateScreenVerts(VertexSet* vset)
+{
+ if (!vset || !screen_vbuf)
+ return false;
+
+ num_verts = vset->nverts;
+
+ VideoDX9ScreenVertex* v = (VideoDX9ScreenVertex*) screen_vbuf->Lock(num_verts);
+
+ if (v) {
+ first_vert = screen_vbuf->GetNextVert();
+
+ for (int i = 0; i < num_verts; i++) {
+ v->sx = vset->s_loc[i].x;
+ v->sy = vset->s_loc[i].y;
+ v->sz = vset->s_loc[i].z;
+ v->rhw = vset->rw[i];
+
+ v->diffuse = vset->diffuse[i];
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+
+ v++;
+ }
+
+ screen_vbuf->Unlock();
+ return true;
+ }
+
+ Print(" VideoDX9: could not lock screen vbuf for %d verts.\n", num_verts);
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawPolys(int npolys, Poly* polys)
+{
+ bool result = false;
+
+ if (d3ddevice && polys && npolys > 0) {
+ // screen space polys:
+ if (polys->vertex_set->space == VertexSet::SCREEN_SPACE)
+ return DrawScreenPolys(npolys, polys);
+
+ // world space polys:
+ stats.ncalls++;
+
+ VertexSet* vset = polys->vertex_set;
+ int nverts = vset->nverts;
+ bool luminous = false;
+ bool detail = false;
+ DWORD fvf = 0;
+ void* verts = 0;
+ int vsize = 0;
+ WORD* indices = new(__FILE__,__LINE__) WORD[npolys*6];
+
+ if (polys->material) {
+ luminous = polys->material->luminous;
+ }
+
+ if (vset->tu1 != 0) {
+ VideoDX9DetailVertex* v = new(__FILE__,__LINE__) VideoDX9DetailVertex[nverts];
+ verts = v;
+ vsize = sizeof(VideoDX9DetailVertex);
+ fvf = VideoDX9DetailVertex::FVF;
+
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->diffuse = vset->diffuse[i];
+ v->specular = vset->specular[i];
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+ v->tu1 = vset->tu1[i];
+ v->tv1 = vset->tv1[i];
+
+ v++;
+ }
+ }
+
+ else if (luminous) {
+ VideoDX9LuminousVertex* v = new(__FILE__,__LINE__) VideoDX9LuminousVertex[nverts];
+ verts = v;
+ vsize = sizeof(VideoDX9LuminousVertex);
+ fvf = VideoDX9LuminousVertex::FVF;
+
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->diffuse = vset->diffuse[i];
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+
+ v++;
+ }
+ }
+
+ else {
+ VideoDX9SolidVertex* v = new(__FILE__,__LINE__) VideoDX9SolidVertex[nverts];
+ verts = v;
+ vsize = sizeof(VideoDX9SolidVertex);
+ fvf = VideoDX9SolidVertex::FVF;
+
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->nx = vset->nrm[i].x;
+ v->ny = vset->nrm[i].y;
+ v->nz = vset->nrm[i].z;
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+
+ v++;
+ }
+ }
+
+ if (verts && indices) {
+ HRESULT hr = E_FAIL;
+
+ // fill index array
+ int num_indices = 0;
+ int num_tris = 0;
+
+ WORD* s = indices;
+ Poly* p = polys;
+
+ for (int i = 0; i < npolys; i++) {
+ if (p->nverts == 3) {
+ num_indices += 3;
+ num_tris += 1;
+
+ *s++ = p->verts[0];
+ *s++ = p->verts[1];
+ *s++ = p->verts[2];
+ }
+
+ else if (p->nverts == 4) {
+ num_indices += 6;
+ num_tris += 2;
+
+ *s++ = p->verts[0];
+ *s++ = p->verts[1];
+ *s++ = p->verts[2];
+
+ *s++ = p->verts[0];
+ *s++ = p->verts[2];
+ *s++ = p->verts[3];
+ }
+
+ p++;
+ }
+
+ hr = d3ddevice->SetTransform(D3DTS_WORLD, &identity_matrix);
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(fvf);
+
+ // send primitives to the device
+ Material* mtl = polys->material;
+ IDirect3DTexture9* texture = 0;
+
+ if (mtl && texcache && mtl->tex_diffuse) {
+ texture = texcache->FindTexture(mtl->tex_diffuse);
+ }
+
+ if (current_texture != texture) {
+ hr = d3ddevice->SetTexture(0, texture);
+ current_texture = texture;
+ }
+
+ if (mtl && texcache && mtl->tex_detail) {
+ texture = texcache->FindTexture(mtl->tex_detail);
+ hr = d3ddevice->SetTexture(1, texture);
+ }
+
+ if (mtl && !luminous) {
+ D3DMATERIAL9 d3dmtl;
+ CreateD3DMaterial(d3dmtl, *mtl);
+
+ hr = d3ddevice->SetMaterial(&d3dmtl);
+ hr = d3ddevice->SetRenderState(D3DRS_AMBIENT, ambient.Value());
+ }
+
+ // set render states and select buffers
+ SetRenderState(FILL_MODE, D3DFILL_SOLID);
+ SetRenderState(LIGHTING_ENABLE, luminous ? FALSE : TRUE);
+ SetBlendType(mtl->blend);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+
+
+ hr = d3ddevice->DrawIndexedPrimitiveUP(
+ D3DPT_TRIANGLELIST,
+ 0,
+ nverts,
+ num_tris,
+ indices,
+ D3DFMT_INDEX16,
+ verts,
+ vsize);
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not draw 3D polys.", hr);
+ }
+
+ delete [] verts;
+ delete [] indices;
+
+ if (SUCCEEDED(hr)) {
+ stats.nverts += nverts;
+ stats.npolys += num_tris;
+ result = true;
+ }
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawScreenPolys(int npolys, Poly* polys, int blend)
+{
+ bool result = false;
+ HRESULT hr = E_FAIL;
+
+ if (d3ddevice && polys && npolys > 0 && screen_vbuf && screen_ibuf) {
+ stats.ncalls++;
+
+ // fill screen vertex buffer
+ if (!PopulateScreenVerts(polys->vertex_set))
+ return false;
+
+ // fill screen index buffer
+ int num_indices = 0;
+ int num_tris = 0;
+
+ // count the number of indices needed for these polys
+ for (int i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+
+ if (p->nverts == 3) {
+ num_indices += 3;
+ num_tris += 1;
+ }
+
+ else if (p->nverts == 4) {
+ num_indices += 6;
+ num_tris += 2;
+ }
+ }
+
+ WORD* screen_indices = screen_ibuf->Lock(num_indices);
+ int first_index = screen_ibuf->GetNextIndex();
+
+ if (!screen_indices) {
+ Print(" VideoDX9: could not lock screen ibuf for %d indices.\n", num_indices);
+ return false;
+ }
+
+ // copy the indices into the locked index buffer
+ WORD* s = screen_indices;
+ Poly* p = polys;
+
+ for (i = 0; i < npolys; i++) {
+ if (p->nverts == 3) {
+ *s++ = p->verts[0] + first_vert;
+ *s++ = p->verts[1] + first_vert;
+ *s++ = p->verts[2] + first_vert;
+ }
+ else if (p->nverts == 4) {
+ *s++ = p->verts[0] + first_vert;
+ *s++ = p->verts[1] + first_vert;
+ *s++ = p->verts[2] + first_vert;
+
+ *s++ = p->verts[0] + first_vert;
+ *s++ = p->verts[2] + first_vert;
+ *s++ = p->verts[3] + first_vert;
+ }
+
+ p++;
+ }
+
+ screen_ibuf->Unlock();
+
+ // set render states and select buffers
+ SetRenderState(FILL_MODE, D3DFILL_SOLID);
+ SetRenderState(Z_ENABLE, D3DZB_FALSE);
+ SetRenderState(Z_WRITE_ENABLE, D3DZB_FALSE);
+ SetRenderState(LIGHTING_ENABLE, FALSE);
+ SetBlendType(blend);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+
+ // send primitives to the device
+ Material* mtl = polys->material;
+ IDirect3DTexture9* texture = 0;
+
+ if (mtl && texcache && mtl->tex_diffuse) {
+ texture = texcache->FindTexture(mtl->tex_diffuse);
+ }
+
+ if (current_texture != texture) {
+ hr = d3ddevice->SetTexture(0, texture);
+ current_texture = texture;
+ }
+
+ screen_vbuf->Select(0);
+ screen_ibuf->Select();
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9ScreenVertex::FVF);
+
+ hr = d3ddevice->DrawIndexedPrimitive(
+ D3DPT_TRIANGLELIST,
+ 0,
+ first_vert,
+ num_verts,
+ first_index,
+ num_tris);
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not draw screen polys.", hr);
+ }
+ else {
+ stats.nverts += num_verts;
+ stats.npolys += num_tris;
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawSolid(Solid* s, DWORD blend_modes)
+{
+ bool result = false;
+ HRESULT hr = E_FAIL;
+
+ if (d3ddevice && s && s->GetModel()) {
+ Model* model = s->GetModel();
+ Matrix orient = s->Orientation();
+ orient.Transpose();
+
+ D3DMATRIX world_matrix;
+ CreateD3DMatrix(world_matrix, orient, s->Location());
+ d3ddevice->SetTransform(D3DTS_WORLD, &world_matrix);
+ matrixWorld = world_matrix;
+ D3DXMatrixInverse(&matrixWorldInverse, 0, &matrixWorld);
+
+ ListIter<Surface> surf_iter = model->GetSurfaces();
+ while (++surf_iter) {
+ Surface* surf = surf_iter.value();
+
+ if (surf->IsHidden() || surf->IsSimplified())
+ continue;
+
+ if (PrepareSurface(surf)) {
+ result = true;
+
+ VideoDX9SurfaceData* surf_data = (VideoDX9SurfaceData*) surf->GetVideoPrivateData();
+ surf_data->vertex_buffer->Select(0);
+ surf_data->index_buffer->Select();
+
+ ListIter<Segment> seg_iter = surf->GetSegments();
+ while (++seg_iter) {
+ Segment* segment = seg_iter.value();
+ Material* mtl = segment->material;
+
+ if (mtl && (blend_modes & mtl->blend)) {
+ result = result && DrawSegment(segment);
+ }
+ }
+ }
+ }
+ }
+
+ surface_has_tangent_data = false;
+
+ return result;
+}
+
+bool
+VideoDX9::DrawSegment(Segment* segment)
+{
+ bool result = false;
+ bool detail = false;
+ bool luminous = false;
+ HRESULT hr = E_FAIL;
+
+ if (segment && segment->video_data) {
+ stats.ncalls++;
+
+ VideoDX9SegmentData* seg_data = (VideoDX9SegmentData*) segment->video_data;
+ int first_vert = seg_data->first_vert;
+ int num_verts = seg_data->num_verts;
+ int first_index = seg_data->first_index;
+ int num_tris = seg_data->num_tris;
+
+ if (segment->model)
+ luminous = segment->model->IsLuminous();
+
+ // set render states and select buffers
+ d3ddevice->SetRenderState(D3DRS_AMBIENT, ambient.Value());
+
+ Material* mtl = segment->material;
+
+ if (use_material)
+ mtl = use_material;
+
+ if (segment->polys && segment->polys->vertex_set && segment->polys->vertex_set->tu1)
+ detail = true;
+
+ // send primitives to the device
+ if (detail) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9DetailVertex::FVF);
+ }
+
+ else if (luminous) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9LuminousVertex::FVF);
+ }
+
+ else if (surface_has_tangent_data && vertex_declaration) {
+ hr = d3ddevice->SetVertexDeclaration(vertex_declaration);
+ hr = d3ddevice->SetVertexShader(NULL);
+ }
+
+ else {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9SolidVertex::FVF);
+ }
+
+ if (render_state[FILL_MODE] == FILL_WIREFRAME) {
+ PrepareMaterial(mtl);
+ SetupPass(0);
+
+ for (int i = 0; i < segment->npolys; i++) {
+ DrawPolyOutline(segment->polys + i);
+ }
+ }
+
+ else if (luminous) {
+ PrepareMaterial(mtl);
+
+ SetRenderState(FILL_MODE, D3DFILL_SOLID);
+ SetRenderState(LIGHTING_ENABLE, FALSE);
+ SetBlendType(mtl->blend);
+
+ for (int pass = 0; pass < passes; pass++) {
+ SetupPass(pass);
+
+ hr = d3ddevice->DrawIndexedPrimitive(
+ D3DPT_TRIANGLELIST,
+ 0,
+ first_vert,
+ num_verts,
+ first_index,
+ num_tris);
+ }
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+ }
+
+ else {
+ PrepareMaterial(mtl);
+
+ if (strategy == DX9_STRATEGY_GLOW && render_state[LIGHTING_PASS] > 0) {
+ hr = 0;
+ }
+
+ else if (magic_fx && strategy < DX9_STRATEGY_BLEND && !detail) {
+ DWORD vs_version = 0;
+ DWORD ps_version = 0;
+ bool shaders_ok = false;
+
+ VideoDX9DeviceInfo* dev_info = dx9enum->GetDeviceInfo(video_settings.GetDeviceType());
+
+ if (dev_info) {
+ vs_version = video_settings.enable_vs ? dev_info->caps.VertexShaderVersion : 0;
+ ps_version = video_settings.enable_ps ? dev_info->caps.PixelShaderVersion : 0;
+
+ if (vs_version >= D3DVS_VERSION(1,1))
+ shaders_ok = true;
+ }
+
+ if (surface_has_tangent_data && vertex_declaration) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetVertexDeclaration(vertex_declaration);
+ }
+
+ else {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9SolidVertex::FVF);
+ }
+
+ bool would_bump = !IsBumpMapEnabled() &&
+ mtl->bump > 0.5;
+
+ bool will_bump = IsBumpMapEnabled() &&
+ surface_has_tangent_data &&
+ shaders_ok &&
+ camera;
+
+ bool do_pix = will_bump && render_state[LIGHTING_PASS] == 0;
+ bool do_bump = will_bump && render_state[LIGHTING_PASS] > 0;
+
+ D3DXMATRIX matrixWVP = matrixWorld * matrixView * matrixProj;
+
+ D3DXVECTOR4 eyePos( (float) camera->Pos().x,
+ (float) camera->Pos().y,
+ (float) camera->Pos().z,
+ 1.0f);
+
+ D3DXVECTOR4 eyeObj;
+ D3DXVec4Transform(&eyeObj, &eyePos, &matrixWorldInverse);
+
+ D3DXVECTOR4 lightPos(100.0f, 100.0f, 100.0f, 1.0f);
+ D3DXVECTOR4 lightColor(1,1,1,1);
+ D3DXVECTOR4 ambientColor(0,0,0,1);
+
+ ambientColor.x = ambient.fRed();
+ ambientColor.y = ambient.fGreen();
+ ambientColor.z = ambient.fBlue();
+
+ if (main_light && (render_state[LIGHTING_PASS] > 0 || mtl->blend > Material::MTL_SOLID)) {
+ lightPos.x = (float) main_light->Location().x;
+ lightPos.y = (float) main_light->Location().y;
+ lightPos.z = (float) main_light->Location().z;
+
+ if (mtl->tex_bumpmap && do_bump)
+ D3DXVec4Transform(&lightPos, &lightPos, &matrixWorldInverse);
+
+ lightColor.x = main_light->GetColor().fRed() * main_light->Intensity();
+ lightColor.y = main_light->GetColor().fGreen() * main_light->Intensity();
+ lightColor.z = main_light->GetColor().fBlue() * main_light->Intensity();
+ lightColor.w = 1.0f;
+ }
+
+ else if (back_light && render_state[LIGHTING_PASS] == 0) {
+ lightPos.x = (float) back_light->Location().x;
+ lightPos.y = (float) back_light->Location().y;
+ lightPos.z = (float) back_light->Location().z;
+
+ lightColor.x = back_light->GetColor().fRed() * back_light->Intensity();
+ lightColor.y = back_light->GetColor().fGreen() * back_light->Intensity();
+ lightColor.z = back_light->GetColor().fBlue() * back_light->Intensity();
+ lightColor.w = 1.0f;
+ }
+
+ D3DXVECTOR4 lightDir = D3DXVECTOR4(0.0f, 0.0f, 0.0f, 0.0f) - lightPos;
+ D3DXVec4Normalize(&lightDir, &lightDir);
+
+ magic_fx->SetMatrix("wvp", &matrixWVP);
+ magic_fx->SetMatrix("world", &matrixWorld);
+ magic_fx->SetMatrix("view", &matrixView);
+ magic_fx->SetMatrix("proj", &matrixProj);
+ magic_fx->SetMatrix("worldInv", &matrixWorldInverse);
+
+ magic_fx->SetVector("light1Pos", &lightPos);
+ magic_fx->SetVector("light1Dir", &lightDir);
+ magic_fx->SetVector("light1Color", &lightColor);
+ magic_fx->SetVector("ambientColor", &ambientColor);
+ magic_fx->SetVector("eyeObj", &eyeObj);
+
+ FLOAT base_bias = (FLOAT) (DW2I(render_state[Z_BIAS]) / -10000.0);
+ magic_fx->SetFloat("bias", base_bias + video_settings.depth_bias);
+
+ ColorValue orig_ks = mtl->Ks;
+
+ if (would_bump && mtl->specular_value >= 0.5)
+ mtl->Ks = mtl->Ks * 0.3;
+
+ magic_fx->SetValue("Ka", &mtl->Ka, sizeof(ColorValue));
+ magic_fx->SetValue("Kd", &mtl->Kd, sizeof(ColorValue));
+ magic_fx->SetValue("Ke", &mtl->Ke, sizeof(ColorValue));
+ magic_fx->SetValue("Ks", &mtl->Ks, sizeof(ColorValue));
+ magic_fx->SetFloat("Ns", mtl->power);
+
+ if (would_bump && mtl->specular_value >= 0.5)
+ mtl->Ks = orig_ks;
+
+ if (mtl->tex_diffuse) {
+ IDirect3DTexture9* texture = texcache->FindTexture(mtl->tex_diffuse);
+ magic_fx->SetTexture("tex_d", texture);
+ }
+ else {
+ magic_fx->SetTexture("tex_d", 0);
+ }
+
+ if (mtl->tex_emissive) {
+ IDirect3DTexture9* texture = texcache->FindTexture(mtl->tex_emissive);
+ magic_fx->SetTexture("tex_e", texture);
+ }
+ else {
+ magic_fx->SetTexture("tex_e", 0);
+ }
+
+ if (mtl->tex_specular && IsSpecMapEnabled()) {
+ IDirect3DTexture9* texture = texcache->FindTexture(mtl->tex_specular);
+ magic_fx->SetTexture("tex_s", texture);
+ }
+ else {
+ magic_fx->SetTexture("tex_s", 0);
+ }
+
+ if (mtl->tex_bumpmap && do_bump) {
+ IDirect3DTexture9* texture = texcache->FindNormalMap(mtl->tex_bumpmap, mtl->bump);
+ magic_fx->SetTexture("tex_n", texture);
+ magic_fx->SetFloat("offsetAmp", mtl->bump / mtl->tex_bumpmap->Height());
+ }
+
+ else if (mtl->tex_bumpmap && !do_bump) {
+ IDirect3DTexture9* texture = texcache->FindTexture(mtl->tex_bumpmap);
+ magic_fx->SetTexture("tex_x", texture);
+ }
+ else {
+ magic_fx->SetTexture("tex_x", 0);
+ }
+
+ const char* mtl_shader = mtl->GetShader(render_state[LIGHTING_PASS]);
+ D3DXHANDLE hnd_shader = 0;
+
+ if (mtl_shader) {
+ if (!strcmp(mtl_shader, "null"))
+ return true;
+
+ hnd_shader = magic_fx->GetTechniqueByName(mtl_shader);
+ }
+
+ if (hnd_shader) {
+ hr = magic_fx->SetTechnique(hnd_shader);
+ }
+ else {
+ if (will_bump) {
+ if (mtl->tex_specular && IsSpecMapEnabled()) {
+ if (mtl->tex_bumpmap && do_bump) {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("BumpSpecMapPix");
+ else
+ hr = magic_fx->SetTechnique("BumpSpecMap");
+ }
+ else if (mtl->tex_emissive && render_state[LIGHTING_PASS] == 0) {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("EmissiveSpecMapPix");
+ else
+ hr = magic_fx->SetTechnique("EmissiveSpecularTexture");
+ }
+ else {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("SpecMapPix");
+ else
+ hr = magic_fx->SetTechnique("SpecularTexture");
+ }
+ }
+
+ else {
+ if (mtl->tex_bumpmap && do_bump) {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("BumpMapPix");
+ else
+ hr = magic_fx->SetTechnique("BumpMap");
+ }
+ else if (mtl->tex_emissive && render_state[LIGHTING_PASS] == 0) {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("EmissivePix");
+ else
+ hr = magic_fx->SetTechnique("EmissiveTexture");
+ }
+ else {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("SimplePix");
+ else
+ hr = magic_fx->SetTechnique("SimpleTexture");
+ }
+ }
+ }
+
+ else if (texcache && mtl->tex_diffuse) {
+ if (mtl->tex_specular && IsSpecMapEnabled()) {
+ if (mtl->tex_emissive && render_state[LIGHTING_PASS] == 0) {
+ hr = magic_fx->SetTechnique("EmissiveSpecularTexture");
+ }
+ else {
+ hr = magic_fx->SetTechnique("SpecularTexture");
+ }
+ }
+
+ else {
+ if (mtl->tex_emissive && render_state[LIGHTING_PASS] == 0) {
+ hr = magic_fx->SetTechnique("EmissiveTexture");
+ }
+ else {
+ hr = magic_fx->SetTechnique("SimpleTexture");
+ }
+ }
+ }
+
+ else {
+ hr = magic_fx->SetTechnique("SimpleMaterial");
+ }
+ }
+
+ if (environment_cube != 0 && magic_fx->IsParameterUsed("env_cube", hnd_shader)) {
+ /*
+ D3DXMATRIX matP;
+ D3DXMatrixInverse(&matP, NULL, &matrixView);
+ matP._41 = matP._42 = matP._43 = 0.0f;
+ */
+
+ D3DXMATRIX env_matrix;
+ D3DXMatrixIdentity(&env_matrix);
+ //D3DXMatrixScaling(&env_matrix, 1.0f, 1.0f, -1.0f);
+
+ //D3DXMatrixMultiply(&env_matrix, &matP, &env_matrix);
+ magic_fx->SetMatrix("env_matrix", &env_matrix);
+ magic_fx->SetTexture("env_cube", environment_cube->GetTexture());
+ }
+
+ if (render_state[STENCIL_ENABLE]) {
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);
+ d3ddevice->SetRenderState(D3DRS_STENCILREF, 0x01);
+ d3ddevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_GREATER);
+ }
+ else {
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
+ }
+
+ if (render_state[LIGHTING_PASS] > 0) {
+ current_blend_state = 100;
+ SetBlendType(BLEND_ADDITIVE);
+ SetRenderState(Z_WRITE_ENABLE, FALSE);
+ }
+ else {
+ current_blend_state = 100;
+ SetBlendType(mtl->blend);
+ }
+
+ UINT nPasses = 0;
+
+ hr = magic_fx->Begin(&nPasses, 0);
+
+ for (UINT i = 0; i < nPasses; i++) {
+ hr = magic_fx->BeginPass(i);
+
+ hr = d3ddevice->DrawIndexedPrimitive(
+ D3DPT_TRIANGLELIST,
+ 0,
+ first_vert,
+ num_verts,
+ first_index,
+ num_tris);
+
+ hr = magic_fx->EndPass();
+ }
+
+ hr = magic_fx->End();
+ }
+
+ else {
+ for (int pass = 0; pass < passes; pass++) {
+ SetupPass(pass);
+
+ hr = d3ddevice->DrawIndexedPrimitive(
+ D3DPT_TRIANGLELIST,
+ 0,
+ first_vert,
+ num_verts,
+ first_index,
+ num_tris);
+
+ if (detail) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9DetailVertex::FVF);
+ }
+
+ else if (luminous) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9LuminousVertex::FVF);
+ }
+
+ else if (surface_has_tangent_data && vertex_declaration) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetVertexDeclaration(vertex_declaration);
+ }
+
+ else {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9SolidVertex::FVF);
+ }
+ }
+ }
+ }
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not draw solid polys.", hr);
+ }
+ else {
+ stats.nverts += num_verts;
+ stats.npolys += num_tris;
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+bool
+VideoDX9::DrawPolyOutline(Poly* p)
+{
+ if (d3ddevice && p && p->nverts >= 3) {
+ static VideoDX9LineVertex verts[8];
+
+ int nlines = p->nverts;
+ VertexSet* vset = p->vertex_set;
+ WORD index = 0;
+ Color color = Color::Black;
+ HRESULT hr = E_FAIL;
+
+ ZeroMemory(verts, sizeof(verts));
+
+ if (p->material)
+ color = p->material->Kd.ToColor();
+
+ for (int i = 0; i < p->nverts; i++) {
+ index = p->verts[i];
+
+ verts[i].x = vset->loc[index].x;
+ verts[i].y = vset->loc[index].y;
+ verts[i].z = vset->loc[index].z;
+ verts[i].diffuse = color.Value();
+ }
+
+ // last vertex, to close the loop
+ index = p->verts[0];
+ i = p->nverts;
+
+ verts[i].x = vset->loc[index].x;
+ verts[i].y = vset->loc[index].y;
+ verts[i].z = vset->loc[index].z;
+ verts[i].diffuse = color.Value();
+
+ current_texture = 0;
+
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9LineVertex::FVF);
+ hr = d3ddevice->SetTexture(0, 0);
+ hr = d3ddevice->DrawPrimitiveUP(
+ D3DPT_LINESTRIP,
+ nlines,
+ verts,
+ sizeof(VideoDX9LineVertex));
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawShadow(Solid* s, int nverts, Vec3* shadow_verts, bool visible)
+{
+ bool result = false;
+ HRESULT hr = E_FAIL;
+
+ if (d3ddevice && s && nverts && shadow_verts && IsShadowEnabled()) {
+ Matrix orient = s->Orientation();
+ orient.Transpose();
+
+ D3DMATRIX world_matrix;
+ CreateD3DMatrix(world_matrix, orient, s->Location());
+ d3ddevice->SetTransform(D3DTS_WORLD, &world_matrix);
+ matrixWorld = world_matrix;
+
+ // show shadow outlines:
+ if (visible) {
+ static VideoDX9LineVertex verts[4];
+
+ d3ddevice->SetRenderState(D3DRS_ZENABLE, TRUE);
+ d3ddevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
+ d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
+ d3ddevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
+ d3ddevice->SetRenderState(D3DRS_LIGHTING, FALSE);
+ d3ddevice->SetVertexShader(NULL);
+ d3ddevice->SetFVF(VideoDX9LineVertex::FVF);
+ d3ddevice->SetTexture(0, 0);
+
+ SetBlendType(BLEND_ALPHA);
+
+ for (int i = 0; i < nverts; i+=3) {
+ DWORD c = 0xa0ffff80;
+
+ verts[0].x = shadow_verts[i+0].x;
+ verts[0].y = shadow_verts[i+0].y;
+ verts[0].z = shadow_verts[i+0].z;
+ verts[0].diffuse = c;
+
+ verts[1].x = shadow_verts[i+1].x;
+ verts[1].y = shadow_verts[i+1].y;
+ verts[1].z = shadow_verts[i+1].z;
+ verts[1].diffuse = c;
+
+ verts[2].x = shadow_verts[i+2].x;
+ verts[2].y = shadow_verts[i+2].y;
+ verts[2].z = shadow_verts[i+2].z;
+ verts[2].diffuse = c;
+
+ verts[3].x = shadow_verts[i+0].x;
+ verts[3].y = shadow_verts[i+0].y;
+ verts[3].z = shadow_verts[i+0].z;
+ verts[3].diffuse = c;
+
+ hr = d3ddevice->DrawPrimitiveUP(
+ D3DPT_LINESTRIP,
+ 3,
+ verts,
+ sizeof(VideoDX9LineVertex));
+ }
+
+ // restore lighting state
+ d3ddevice->SetRenderState(D3DRS_LIGHTING, render_state[LIGHTING_ENABLE]);
+ }
+
+ // render shadows into stencil buffer:
+
+ // Disable z-buffer writes (note: z-testing still occurs), and enable the
+ // stencil-buffer
+ d3ddevice->SetRenderState(D3DRS_ZENABLE, TRUE);
+ d3ddevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);
+
+ // Dont bother with interpolating color
+ d3ddevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
+
+ // Set up stencil compare fuction, reference value, and masks.
+ // Stencil test passes if ((ref & mask) cmpfn (stencil & mask)) is true.
+ // Note: since we set up the stencil-test to always pass, the STENCILFAIL
+ // renderstate is really not needed.
+ d3ddevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS );
+ d3ddevice->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP );
+ d3ddevice->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP );
+
+ // If z-test passes, inc/decrement stencil buffer value
+ d3ddevice->SetRenderState(D3DRS_STENCILREF, 0x1 );
+ d3ddevice->SetRenderState(D3DRS_STENCILMASK, 0xff );
+ d3ddevice->SetRenderState(D3DRS_STENCILWRITEMASK, 0xff );
+ d3ddevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_INCR );
+
+ // Make sure that no pixels get drawn to the frame buffer
+ d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE );
+ d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO );
+ d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE );
+
+ d3ddevice->SetVertexShader(NULL);
+ d3ddevice->SetFVF(D3DFVF_XYZ);
+
+ // Draw front-side of shadow volume in stencil/z only
+ hr = d3ddevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, nverts/3, shadow_verts, sizeof(Vec3));
+
+ // Now reverse cull order so back sides of shadow volume are written.
+ d3ddevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW );
+
+ // Decrement stencil buffer value
+ d3ddevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_DECR );
+
+ // Draw back-side of shadow volume in stencil/z only
+ hr = d3ddevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, nverts/3, shadow_verts, sizeof(Vec3));
+
+ // restore render states
+ d3ddevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
+ d3ddevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
+ d3ddevice->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
+
+ // force restore of current blend type
+ int type = current_blend_state;
+ current_blend_state = 100;
+ SetBlendType(type);
+
+ result = SUCCEEDED(hr);
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawLines(int nlines, Vec3* points, Color c, int blend)
+{
+ bool result = false;
+
+ if (d3ddevice && points && nlines > 0 && nlines <= 256) {
+ stats.ncalls++;
+
+ VideoDX9LineVertex* verts = line_verts;
+
+ if (verts) {
+ HRESULT hr = E_FAIL;
+
+ for (int i = 0; i < 2*nlines; i++) {
+ VideoDX9LineVertex* v = verts + i;
+ Vec3* p = points + i;
+
+ v->x = p->x;
+ v->y = p->y;
+ v->z = p->z;
+ v->diffuse = c.Value();
+ }
+
+ hr = d3ddevice->SetTransform(D3DTS_WORLD, &identity_matrix);
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9LineVertex::FVF);
+
+ DWORD old_lighting = render_state[LIGHTING_ENABLE];
+
+ // untextured lines:
+ if (current_texture) {
+ d3ddevice->SetTexture(0, 0);
+ current_texture = 0;
+ }
+
+ SetRenderState(LIGHTING_ENABLE, FALSE);
+ SetBlendType(blend);
+
+ hr = d3ddevice->DrawPrimitiveUP(
+ D3DPT_LINELIST,
+ nlines,
+ verts,
+ sizeof(VideoDX9LineVertex));
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not draw 3D lines.", hr);
+ }
+
+ SetRenderState(LIGHTING_ENABLE, old_lighting);
+
+ if (SUCCEEDED(hr)) {
+ stats.nverts += 2*nlines;
+ stats.nlines += nlines;
+ result = true;
+ }
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawScreenLines(int nlines, float* points, Color c, int blend)
+{
+ bool result = false;
+
+ if (d3ddevice && points && nlines > 0 && nlines <= 64) {
+ stats.ncalls++;
+
+ VideoDX9ScreenVertex* verts = screen_line_verts;
+
+ if (verts) {
+ HRESULT hr = E_FAIL;
+
+ for (int i = 0; i < 2*nlines; i++) {
+ VideoDX9ScreenVertex* v = verts + i;
+
+ v->sx = points[2*i + 0];
+ v->sy = points[2*i + 1];
+ v->sz = 0.0f;
+ v->rhw = 1.0f;
+ v->diffuse = c.Value();
+ v->tu = 0.0f;
+ v->tv = 0.0f;
+ }
+
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9ScreenVertex::FVF);
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not set FVF for screen lines.", hr);
+ }
+
+ else {
+ if (current_texture != 0) {
+ hr = d3ddevice->SetTexture(0, 0);
+ current_texture = 0;
+ }
+
+ SetRenderState(FILL_MODE, D3DFILL_SOLID);
+ SetRenderState(Z_ENABLE, D3DZB_FALSE);
+ SetRenderState(LIGHTING_ENABLE, FALSE);
+ SetBlendType(blend);
+
+ hr = d3ddevice->DrawPrimitiveUP(
+ D3DPT_LINELIST,
+ nlines,
+ verts,
+ sizeof(VideoDX9ScreenVertex));
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not draw screen lines.", hr);
+ }
+ }
+
+ if (SUCCEEDED(hr)) {
+ stats.nverts += 2*nlines;
+ stats.nlines += nlines;
+ result = true;
+ }
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawPoints(VertexSet* vset)
+{
+ if (vset && vset->nverts) {
+ HRESULT hr = E_FAIL;
+
+ int nverts = vset->nverts;
+ VideoDX9LineVertex* verts = new(__FILE__,__LINE__) VideoDX9LineVertex[nverts];
+
+ if (verts) {
+ for (int i = 0; i < nverts; i++) {
+ VideoDX9LineVertex* v = verts + i;
+ Vec3* p = vset->loc + i;
+
+ v->x = p->x;
+ v->y = p->y;
+ v->z = p->z;
+ v->diffuse = vset->diffuse[i];
+ }
+
+ SetRenderState(LIGHTING_ENABLE, FALSE);
+
+ hr = d3ddevice->SetTransform(D3DTS_WORLD, &identity_matrix);
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9LineVertex::FVF);
+ hr = d3ddevice->SetTexture(0, 0);
+ hr = d3ddevice->DrawPrimitiveUP(
+ D3DPT_POINTLIST,
+ nverts,
+ verts,
+ sizeof(VideoDX9LineVertex));
+
+ delete [] verts;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::UseMaterial(Material* m)
+{
+ use_material = m;
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::UseXFont(const char* name, int size, bool bold, bool ital)
+{
+ if (d3ddevice && name && *name && size > 4) {
+ RELEASE(d3dx_font);
+
+ strcpy(font_name, name);
+ font_size = size;
+ font_bold = bold;
+ font_ital = ital;
+
+ HRESULT hr = E_FAIL;
+ HDC hdc = GetDC(NULL);
+ int nLogPixelsY = GetDeviceCaps(hdc, LOGPIXELSY);
+
+ ReleaseDC(NULL, hdc);
+
+ int nHeight = -size * nLogPixelsY / 72;
+
+ hr = D3DXCreateFont(d3ddevice, // D3D device
+ nHeight, // Height
+ 0, // Width
+ bold ? FW_BOLD : FW_NORMAL, // Weight
+ 1, // MipLevels, 0 = autogen mipmaps
+ ital, // Italic
+ DEFAULT_CHARSET, // CharSet
+ OUT_DEFAULT_PRECIS, // OutputPrecision
+ DEFAULT_QUALITY, // Quality
+ DEFAULT_PITCH | FF_DONTCARE, // PitchAndFamily
+ name, // pFaceName
+ &d3dx_font); // ppFont
+
+ if (SUCCEEDED(hr)) {
+ return true;
+ }
+ }
+
+ RELEASE(d3dx_font);
+ return false;
+}
+
+bool
+VideoDX9::DrawText(const char* text, int count, const Rect& rect, DWORD format, Color c)
+{
+ if (d3ddevice && text && *text && d3dx_font) {
+ RECT r;
+ r.left = rect.x;
+ r.top = rect.y;
+ r.right = rect.x + rect.w;
+ r.bottom = rect.y + rect.h;
+
+ d3dx_font->DrawText(0, text, count, &r, format, c.Value());
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::PrepareSurface(Surface* surf)
+{
+ if (surf) {
+ int nverts = surf->NumVerts();
+ int nindices = surf->NumIndices();
+ bool detail = surf->GetVertexSet()->tu1 != 0;
+ bool luminous = false;
+ DWORD dynamic = 0;
+
+ if (surf->GetModel()) {
+ luminous = surf->GetModel()->IsLuminous();
+ dynamic = surf->GetModel()->IsDynamic() ? D3DUSAGE_DYNAMIC : 0;
+ }
+
+ surface_has_tangent_data = !luminous && (surf->GetVertexSet()->tangent && surf->GetVertexSet()->binormal);
+
+ VideoDX9SurfaceData* surf_data = (VideoDX9SurfaceData*) surf->GetVideoPrivateData();
+
+ if (!surf_data) {
+ surf_data = new(__FILE__,__LINE__) VideoDX9SurfaceData(surf->GetModel());
+
+ surface_has_tangent_data = false;
+
+ if (surf->GetVertexSet()->tangent && surf->GetVertexSet()->binormal) {
+ surface_has_tangent_data = true;
+ surf_data->vertex_buffer = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
+ this,
+ nverts,
+ sizeof(VideoDX9NormalVertex),
+ 0, // not an FVF vertex buffer
+ dynamic | D3DUSAGE_WRITEONLY);
+ }
+
+ else if (detail) {
+ surf_data->vertex_buffer = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
+ this,
+ nverts,
+ sizeof(VideoDX9DetailVertex),
+ VideoDX9DetailVertex::FVF,
+ dynamic | D3DUSAGE_WRITEONLY);
+ }
+
+ else if (luminous) {
+ surf_data->vertex_buffer = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
+ this,
+ nverts,
+ sizeof(VideoDX9LuminousVertex),
+ VideoDX9LuminousVertex::FVF,
+ dynamic | D3DUSAGE_WRITEONLY);
+ }
+
+ else {
+ surf_data->vertex_buffer = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
+ this,
+ nverts,
+ sizeof(VideoDX9SolidVertex),
+ VideoDX9SolidVertex::FVF,
+ dynamic | D3DUSAGE_WRITEONLY);
+ }
+
+ surf_data->index_buffer = new(__FILE__,__LINE__) VideoDX9IndexBuffer(
+ this,
+ nindices,
+ dynamic | D3DUSAGE_WRITEONLY);
+
+ if (!surf_data->vertex_buffer || !surf_data->index_buffer) {
+ Print("VideoDX9: Unable to prepare surface '%s'\n", surf->Name());
+ delete surf_data;
+ return false;
+ }
+
+ surf->SetVideoPrivateData(surf_data);
+ }
+
+ if (surf_data && !surf_data->IsValid()) {
+ if (detail) {
+ VideoDX9DetailVertex* v = (VideoDX9DetailVertex*) surf_data->vertex_buffer->Lock(nverts);
+
+ if (v) {
+ const VertexSet* vset = surf->GetVertexSet();
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->diffuse = vset->diffuse[i];
+ v->specular = vset->specular[i];
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+ v->tu1 = vset->tu1[i];
+ v->tv1 = vset->tv1[i];
+
+ v++;
+ }
+
+ surf_data->vertex_buffer->Unlock();
+ }
+ }
+
+ else if (luminous) {
+ VideoDX9LuminousVertex* v = (VideoDX9LuminousVertex*) surf_data->vertex_buffer->Lock(nverts);
+
+ if (v) {
+ const VertexSet* vset = surf->GetVertexSet();
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->diffuse = vset->diffuse[i];
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+
+ v++;
+ }
+
+ surf_data->vertex_buffer->Unlock();
+ }
+ }
+
+ else if (surface_has_tangent_data) {
+ VideoDX9NormalVertex* v = (VideoDX9NormalVertex*) surf_data->vertex_buffer->Lock(nverts);
+
+ if (v) {
+ const VertexSet* vset = surf->GetVertexSet();
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->nx = vset->nrm[i].x;
+ v->ny = vset->nrm[i].y;
+ v->nz = vset->nrm[i].z;
+
+ v->t0u = vset->tu[i];
+ v->t0v = vset->tv[i];
+ v->t1u = vset->tu[i];
+ v->t1v = vset->tv[i];
+
+ v->tx = vset->tangent[i].x;
+ v->ty = vset->tangent[i].y;
+ v->tz = vset->tangent[i].z;
+
+ v->bx = vset->binormal[i].x;
+ v->by = vset->binormal[i].y;
+ v->bz = vset->binormal[i].z;
+
+ v++;
+ }
+
+ surf_data->vertex_buffer->Unlock();
+ }
+ }
+
+ else {
+ VideoDX9SolidVertex* v = (VideoDX9SolidVertex*) surf_data->vertex_buffer->Lock(nverts);
+
+ if (v) {
+ const VertexSet* vset = surf->GetVertexSet();
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->nx = vset->nrm[i].x;
+ v->ny = vset->nrm[i].y;
+ v->nz = vset->nrm[i].z;
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+
+ v++;
+ }
+
+ surf_data->vertex_buffer->Unlock();
+ }
+ }
+
+ WORD* indices = surf_data->index_buffer->Lock(nindices);
+
+ if (indices) {
+ // copy the indices into the locked index buffer
+ WORD* s = indices;
+ Poly* p = surf->GetPolys();
+
+ for (int i = 0; i < surf->NumPolys(); i++) {
+ if (p->nverts == 3) {
+ *s++ = p->verts[0];
+ *s++ = p->verts[2];
+ *s++ = p->verts[1];
+ }
+ else if (p->nverts == 4) {
+ *s++ = p->verts[0];
+ *s++ = p->verts[2];
+ *s++ = p->verts[1];
+
+ *s++ = p->verts[0];
+ *s++ = p->verts[3];
+ *s++ = p->verts[2];
+ }
+
+ p++;
+ }
+
+ surf_data->index_buffer->Unlock();
+ }
+
+ surf_data->Validate();
+ }
+
+ int first_index = 0;
+
+ ListIter<Segment> seg_iter = surf->GetSegments();
+ while (++seg_iter) {
+ Segment* segment = seg_iter.value();
+
+ if (!segment->video_data) {
+ VideoDX9SegmentData* seg_data = new(__FILE__,__LINE__) VideoDX9SegmentData;
+
+ int num_tris = 0;
+ for (int i = 0; i < segment->npolys; i++)
+ num_tris += segment->polys[i].nverts-2;
+
+ seg_data->first_vert = 0;
+ seg_data->num_verts = surf->NumVerts();
+ seg_data->first_index = first_index;
+ seg_data->num_tris = num_tris;
+
+ segment->video_data = seg_data;
+
+ first_index += num_tris * 3;
+ }
+ }
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+VideoDX9::PrepareMaterial(Material* m)
+{
+ segment_material = m;
+ strategy = 0;
+ passes = 0;
+
+ if (m) {
+ int max_stages = 1;
+ int max_textures = 1;
+ bool multiply_add = false;
+ bool dotproduct3 = false;
+ bool vertexshader = false;
+ bool pixelshader = false;
+ DWORD vs_version = 0;
+ DWORD ps_version = 0;
+
+ VideoDX9DeviceInfo* dev_info = dx9enum->GetDeviceInfo(video_settings.GetDeviceType());
+
+ if (dev_info) {
+ max_stages = (int) dev_info->caps.MaxTextureBlendStages;
+ max_textures = (int) dev_info->caps.MaxSimultaneousTextures;
+ multiply_add = (dev_info->caps.TextureOpCaps & D3DTEXOPCAPS_MULTIPLYADD) ? true : false;
+ dotproduct3 = (dev_info->caps.TextureOpCaps & D3DTEXOPCAPS_DOTPRODUCT3) ? true : false;
+
+ vs_version = video_settings.enable_vs ? dev_info->caps.VertexShaderVersion : 0;
+ ps_version = video_settings.enable_ps ? dev_info->caps.PixelShaderVersion : 0;
+
+ vertexshader = vs_version >= D3DVS_VERSION(1,1);
+ pixelshader = ps_version >= D3DPS_VERSION(2,0);
+ }
+
+ strategy = DX9_STRATEGY_SIMPLE;
+ passes = 1;
+
+ if (m->tex_alternate) {
+ if (m->tex_detail && max_textures > 2 && max_stages > 4)
+ strategy = DX9_STRATEGY_BLEND_DETAIL;
+
+ else if (max_textures > 1 && max_stages > 3)
+ strategy = DX9_STRATEGY_BLEND;
+ }
+
+ else if (m->tex_emissive && (!m->tex_diffuse || m->tex_diffuse == m->tex_emissive)) {
+ strategy = DX9_STRATEGY_GLOW;
+ }
+
+ else if (IsSpecMapEnabled() && m->tex_specular && !m->tex_emissive) {
+ strategy = DX9_STRATEGY_SPECMAP;
+
+ if (max_textures < 2 || max_stages < 2 || !multiply_add)
+ passes = 2;
+ }
+
+ else if ((!IsSpecMapEnabled() || !m->tex_specular) && m->tex_emissive) {
+ strategy = DX9_STRATEGY_EMISSIVE;
+
+ if (max_textures < 2 || max_stages < 2)
+ passes = 2;
+ }
+
+ else if (IsSpecMapEnabled() && m->tex_specular && m->tex_emissive) {
+ strategy = DX9_STRATEGY_SPEC_EMISSIVE;
+
+ if (max_textures < 2 || max_stages < 2)
+ passes = 3;
+
+ else if (max_textures < 3 || max_stages < 3 || !multiply_add)
+ passes = 2;
+ }
+ }
+
+ return passes;
+}
+
+bool
+VideoDX9::SetupPass(int pass)
+{
+ if (pass < 0 || pass >= passes)
+ return false;
+
+ if (pass == 0) {
+ D3DMATERIAL9 d3dmtl;
+ IDirect3DTexture9* texture_0 = 0;
+ IDirect3DTexture9* texture_1 = 0;
+ IDirect3DTexture9* texture_2 = 0;
+ Bitmap* tex_bmp_0 = 0;
+ Bitmap* tex_bmp_1 = 0;
+ Bitmap* tex_bmp_2 = 0;
+ ColorValue orig_spec = segment_material->Ks;
+ HRESULT hr = E_FAIL;
+
+ if (segment_material->tex_specular && passes > 1)
+ segment_material->Ks = Color::Black;
+
+ CreateD3DMaterial(d3dmtl, *segment_material);
+ segment_material->Ks = orig_spec;
+
+ hr = d3ddevice->SetMaterial(&d3dmtl);
+
+ if (strategy == DX9_STRATEGY_SIMPLE) {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ }
+
+ else if (strategy == DX9_STRATEGY_BLEND) {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ tex_bmp_1 = segment_material->tex_alternate;
+ }
+
+ else if (strategy == DX9_STRATEGY_BLEND_DETAIL) {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ tex_bmp_1 = segment_material->tex_alternate;
+ tex_bmp_2 = segment_material->tex_detail;
+ }
+
+ else if (strategy == DX9_STRATEGY_SPECMAP) {
+ if (passes == 1) {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ tex_bmp_1 = segment_material->tex_specular;
+ }
+ else {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ }
+ }
+
+ else if (strategy == DX9_STRATEGY_EMISSIVE && passes == 1 ||
+ strategy == DX9_STRATEGY_SPEC_EMISSIVE && passes == 2) {
+ if (segment_material->tex_diffuse) {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ tex_bmp_1 = segment_material->tex_emissive;
+ }
+ else {
+ tex_bmp_0 = segment_material->tex_emissive;
+ }
+ }
+
+ else {
+ tex_bmp_0 = segment_material->tex_emissive;
+ }
+
+ if (texcache && tex_bmp_0) {
+ texture_0 = texcache->FindTexture(tex_bmp_0);
+
+ hr = d3ddevice->SetTexture(0, texture_0);
+ current_texture = texture_0;
+
+ if (tex_bmp_1) {
+ if (strategy >= DX9_STRATEGY_BLEND) {
+ texture_1 = texcache->FindTexture(tex_bmp_1);
+ hr = d3ddevice->SetTexture(1, texture_1);
+
+ if (tex_bmp_2) {
+ texture_2 = texcache->FindTexture(tex_bmp_2);
+ hr = d3ddevice->SetTexture(2, texture_2);
+ }
+ }
+
+ else {
+ texture_1 = texcache->FindTexture(tex_bmp_1);
+ hr = d3ddevice->SetTexture(1, texture_1);
+
+ if (tex_bmp_2) {
+ texture_2 = texcache->FindTexture(tex_bmp_2);
+ hr = d3ddevice->SetTexture(2, texture_2);
+ }
+ }
+ }
+ }
+ else {
+ hr = d3ddevice->SetTexture(0, 0);
+ current_texture = 0;
+ }
+
+ SetBlendType(segment_material->blend);
+
+ if (texture_0 && texture_1 && strategy == DX9_STRATEGY_BLEND) {
+ d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BLENDCURRENTALPHA);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
+
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetTextureStageState(2, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(2, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+
+ d3ddevice->SetTextureStageState(3, D3DTSS_COLOROP, D3DTOP_DISABLE);
+ }
+
+ else if (texture_0 && texture_1 && texture_2 && strategy == DX9_STRATEGY_BLEND_DETAIL) {
+ d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(2, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(2, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(2, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BLENDCURRENTALPHA);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
+
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA);
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLORARG2, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(2, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(2, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(2, D3DTSS_TEXCOORDINDEX, 1);
+
+ d3ddevice->SetTextureStageState(3, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(3, D3DTSS_COLORARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(3, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetTextureStageState(3, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(3, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+ }
+
+ else if (texture_0 && strategy == DX9_STRATEGY_GLOW) {
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ }
+
+ else if (texture_0) {
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ }
+
+ if (texture_1 && strategy == DX9_STRATEGY_SPECMAP) {
+ d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MULTIPLYADD);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_SPECULAR);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ }
+
+ else if (texture_1 && strategy == DX9_STRATEGY_EMISSIVE) {
+ d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ }
+
+ else if (texture_1 && strategy == DX9_STRATEGY_SPEC_EMISSIVE) {
+ d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ }
+
+ else if (strategy < DX9_STRATEGY_BLEND) {
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+ }
+ }
+
+ else if (pass == 1) {
+ D3DMATERIAL9 d3dmtl;
+ IDirect3DTexture9* texture = 0;
+ Bitmap* tex_bmp = 0;
+ ColorValue orig_Ka = segment_material->Ka;
+ ColorValue orig_Kd = segment_material->Kd;
+ HRESULT hr = E_FAIL;
+
+ if (segment_material->tex_specular) {
+ segment_material->Ka = Color::Black;
+ segment_material->Kd = Color::Black;
+ }
+
+ CreateD3DMaterial(d3dmtl, *segment_material);
+
+ segment_material->Ka = orig_Ka;
+ segment_material->Kd = orig_Kd;
+
+ hr = d3ddevice->SetMaterial(&d3dmtl);
+
+ if (strategy == DX9_STRATEGY_SPECMAP ||
+ strategy == DX9_STRATEGY_SPEC_EMISSIVE) {
+ tex_bmp = segment_material->tex_specular;
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_SPECULAR);
+ }
+
+ else if (strategy == DX9_STRATEGY_EMISSIVE) {
+ tex_bmp = segment_material->tex_emissive;
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ }
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+
+ if (texcache && tex_bmp) {
+ texture = texcache->FindTexture(tex_bmp);
+ hr = d3ddevice->SetTexture(0, texture);
+ current_texture = texture;
+
+ SetBlendType(BLEND_ADDITIVE);
+ }
+ }
+
+ if (render_state[STENCIL_ENABLE]) {
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);
+ d3ddevice->SetRenderState(D3DRS_STENCILREF, 0x01);
+ d3ddevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_GREATER);
+ }
+ else {
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
+ }
+
+ if (render_state[LIGHTING_PASS] > 0) {
+ SetBlendType(BLEND_ADDITIVE);
+ SetRenderState(Z_WRITE_ENABLE, FALSE);
+ }
+
+ return true;
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+VideoDX9Error(const char* msg, HRESULT err)
+{
+ Print(" VideoDX9: %s. [%s]\n", msg, D3DErrStr(err));
+}
+
+char* D3DErrStr(HRESULT hr)
+{
+ static char errstrbuf[128];
+
+ switch (hr) {
+ default:
+ sprintf(errstrbuf, "Unrecognized error value = %08x.", hr);
+ return errstrbuf;
+
+ case D3D_OK:
+ return "No error.";
+
+ case D3DERR_WRONGTEXTUREFORMAT:
+ return "Wrong texture format.";
+
+ case D3DERR_UNSUPPORTEDCOLOROPERATION:
+ return "Unsupported color operation.";
+
+ case D3DERR_UNSUPPORTEDCOLORARG:
+ return "Unsupported color argument.";
+
+ case D3DERR_UNSUPPORTEDALPHAOPERATION:
+ return "Unsupported alpha operation.";
+
+ case D3DERR_UNSUPPORTEDALPHAARG:
+ return "Unsupported alpha argument.";
+
+ case D3DERR_TOOMANYOPERATIONS:
+ return "Too many operations.";
+
+ case D3DERR_CONFLICTINGTEXTUREFILTER:
+ return "Conflicting texture filter.";
+
+ case D3DERR_UNSUPPORTEDFACTORVALUE:
+ return "Unsupported factor value.";
+
+ case D3DERR_CONFLICTINGRENDERSTATE:
+ return "Conflicting render state.";
+
+ case D3DERR_UNSUPPORTEDTEXTUREFILTER:
+ return "Unsupported texture filter.";
+
+ case D3DERR_CONFLICTINGTEXTUREPALETTE:
+ return "Conflicting texture palette.";
+
+ case D3DERR_DRIVERINTERNALERROR:
+ return "Driver internal error.";
+
+
+ case D3DERR_NOTFOUND:
+ return "Resource was not found.";
+
+ case D3DERR_MOREDATA:
+ return "More data?";
+
+ case D3DERR_DEVICELOST:
+ return "Device lost.";
+
+ case D3DERR_DEVICENOTRESET:
+ return "Device is not reset.";
+
+ case D3DERR_NOTAVAILABLE:
+ return "Not available.";
+
+ case D3DERR_OUTOFVIDEOMEMORY:
+ return "Out of video memory.";
+
+ case E_OUTOFMEMORY:
+ return "Out of system memory.";
+
+ case D3DERR_INVALIDDEVICE:
+ return "Invalid device selection.";
+
+ case D3DERR_INVALIDCALL:
+ return "Invalid call or parameter.";
+
+ case D3DERR_DRIVERINVALIDCALL:
+ return "Driver invalid call.";
+
+ case D3DERR_WASSTILLDRAWING:
+ return "The device was still drawing.";
+
+ case D3DOK_NOAUTOGEN:
+ return "Autogeneration is not supported by this device.";
+
+ }
+}
+
+ \ No newline at end of file
diff --git a/nGenEx/VideoDX9.h b/nGenEx/VideoDX9.h
new file mode 100644
index 0000000..d89bb77
--- /dev/null
+++ b/nGenEx/VideoDX9.h
@@ -0,0 +1,195 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoDX9.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct3D and Direct3D Video classes for DirectX 7
+*/
+
+#ifndef VideoDX9_h
+#define VideoDX9_h
+
+#include "Video.h"
+#include "VideoSettings.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class VideoDX9;
+class VideoDX9Enum;
+class VideoDX9VertexBuffer;
+class VideoDX9IndexBuffer;
+struct VideoDX9ScreenVertex;
+class Surface;
+class Segment;
+
+struct VideoDX9ScreenVertex;
+struct VideoDX9SolidVertex;
+struct VideoDX9LuminousVertex;
+struct VideoDX9LineVertex;
+
+
+// +--------------------------------------------------------------------+
+
+class VideoDX9 : public Video
+{
+public:
+ VideoDX9(const HWND& window, VideoSettings* vs);
+ virtual ~VideoDX9();
+
+ virtual const VideoSettings*
+ GetVideoSettings() const { return &video_settings; }
+ virtual bool SetVideoSettings(const VideoSettings* vs);
+
+ virtual bool SetBackgroundColor(Color c);
+ virtual bool SetGammaLevel(int g);
+ virtual bool SetObjTransform(const Matrix& o, const Point& l);
+
+ virtual bool SetupParams();
+ virtual bool Reset(const VideoSettings* vs);
+
+ virtual bool StartFrame();
+ virtual bool EndFrame();
+
+ virtual int Width() const { return width; }
+ virtual int Height() const { return height; }
+ virtual int Depth() const { return bpp; }
+
+ virtual void RecoverSurfaces();
+
+ virtual bool ClearAll();
+ virtual bool ClearDepthBuffer();
+ virtual bool Present();
+ virtual bool Pause();
+ virtual bool Resume();
+
+ virtual IDirect3D9* Direct3D() const { return d3d; }
+ virtual IDirect3DDevice9* D3DDevice() const { return d3ddevice; }
+ static IDirect3DDevice9* GetD3DDevice9();
+
+ virtual bool IsModeSupported(int width, int height, int bpp) const;
+ virtual bool IsHardware() const { return true; }
+ virtual int ZDepth() const { return zdepth; }
+ virtual DWORD VidMemFree() const;
+ virtual int D3DLevel() const { return 9; }
+ virtual int MaxTexSize() const;
+ virtual int MaxTexAspect() const;
+ virtual int GammaLevel() const { return gamma; }
+
+ virtual bool Capture(Bitmap& bmp);
+ virtual bool GetWindowRect(Rect& r);
+ virtual bool SetWindowRect(const Rect& r);
+ virtual bool SetViewport(int x, int y, int w, int h);
+ virtual bool SetCamera(const Camera* cam);
+ virtual bool SetEnvironment(Bitmap** faces);
+ virtual bool SetAmbient(Color c);
+ virtual bool SetLights(const List<Light>& lights);
+ virtual bool SetProjection(float fov,
+ float znear=1.0f,
+ float zfar=1.0e6f,
+ DWORD type=PROJECTION_PERSPECTIVE);
+ virtual bool SetRenderState(RENDER_STATE state, DWORD value);
+ virtual bool SetBlendType(int blend_type);
+
+ virtual bool DrawPolys(int npolys, Poly* p);
+ virtual bool DrawScreenPolys(int npolys, Poly* p, int blend=0);
+ virtual bool DrawSolid(Solid* s, DWORD blend_modes=0xf);
+ virtual bool DrawShadow(Solid* s, int nverts, Vec3* verts, bool vis=false);
+ virtual bool DrawLines(int nlines, Vec3* v, Color c, int blend=0);
+ virtual bool DrawScreenLines(int nlines, float* v, Color c, int blend=0);
+ virtual bool DrawPoints(VertexSet* v);
+ virtual bool DrawPolyOutline(Poly* p);
+ virtual bool UseMaterial(Material* m);
+
+ virtual bool UseXFont(const char* name, int size, bool b, bool i);
+ virtual bool DrawText(const char* text, int count, const Rect& rect,
+ DWORD format, Color c);
+
+ virtual void PreloadTexture(Bitmap* bmp);
+ virtual void PreloadSurface(Surface* s);
+ virtual void InvalidateCache();
+
+ static void CreateD3DMatrix(D3DMATRIX& result, const Matrix& m, const Point& p);
+ static void CreateD3DMatrix(D3DMATRIX& result, const Matrix& m, const Vec3& v);
+ static void CreateD3DMaterial(D3DMATERIAL9& result, const Material& mtl);
+
+private:
+ bool CreateBuffers();
+ bool DestroyBuffers();
+ bool PopulateScreenVerts(VertexSet* vset);
+ bool PrepareSurface(Surface* s);
+ bool DrawSegment(Segment* s);
+
+ int PrepareMaterial(Material* m);
+ bool SetupPass(int n);
+
+ HWND hwnd;
+ int width;
+ int height;
+ int bpp;
+ int gamma;
+ int zdepth;
+ Color background;
+
+ VideoDX9Enum* dx9enum;
+ VideoSettings video_settings;
+
+ IDirect3D9* d3d;
+ IDirect3DDevice9* d3ddevice;
+ D3DPRESENT_PARAMETERS d3dparams;
+ D3DSURFACE_DESC back_buffer_desc;
+ bool device_lost;
+
+ BYTE* surface;
+
+ DWORD texture_format[3];
+ D3DGAMMARAMP gamma_ramp;
+ double fade;
+
+ Rect rect;
+
+ IDirect3DVertexDeclaration9* vertex_declaration;
+ ID3DXEffect* magic_fx;
+ BYTE* magic_fx_code;
+ int magic_fx_code_len;
+
+ IDirect3DTexture9* current_texture;
+ int current_blend_state;
+ int scene_active;
+ DWORD render_state[RENDER_STATE_MAX];
+ Material* use_material;
+
+ Material* segment_material;
+ int strategy;
+ int passes;
+
+ ID3DXFont* d3dx_font;
+ char font_name[64];
+ int font_size;
+ bool font_bold;
+ bool font_ital;
+
+ Color ambient;
+ int nlights;
+
+ int first_vert;
+ int num_verts;
+
+ VideoDX9VertexBuffer* screen_vbuf;
+ VideoDX9IndexBuffer* screen_ibuf;
+ VideoDX9ScreenVertex* font_verts;
+ WORD* font_indices;
+ int font_nverts;
+
+ VideoDX9ScreenVertex* screen_line_verts;
+ VideoDX9LineVertex* line_verts;
+};
+
+#endif VideoDX9_h
+
diff --git a/nGenEx/VideoDX9Enum.cpp b/nGenEx/VideoDX9Enum.cpp
new file mode 100644
index 0000000..ca2ca7a
--- /dev/null
+++ b/nGenEx/VideoDX9Enum.cpp
@@ -0,0 +1,1057 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoDX9Enum.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct3D Video class for DirectX 9
+*/
+
+#include "MemDebug.h"
+#include "VideoDX9Enum.h"
+#include "VideoSettings.h"
+#include "Color.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* msg, ...);
+char* DDErrStr(HRESULT dderr);
+void VideoDX9Error(const char* msg, HRESULT dderr);
+int VD3D_describe_things;
+
+#ifndef RELEASE
+#define RELEASE(x) if (x) { x->Release(); x=NULL; }
+#endif
+
+
+// +--------------------------------------------------------------------+
+
+static UINT GetBitsPerPixel(D3DFORMAT fmt)
+{
+ switch (fmt) {
+ case D3DFMT_A8R8G8B8: return 32;
+ case D3DFMT_X8R8G8B8: return 32;
+ case D3DFMT_A2B10G10R10: return 32;
+ case D3DFMT_A2R10G10B10: return 32;
+ case D3DFMT_R8G8B8: return 24;
+ case D3DFMT_R5G6B5: return 16;
+ case D3DFMT_X1R5G5B5: return 16;
+ case D3DFMT_A1R5G5B5: return 16;
+ case D3DFMT_A4R4G4B4: return 16;
+ case D3DFMT_A8R3G3B2: return 16;
+ case D3DFMT_X4R4G4B4: return 16;
+ case D3DFMT_R3G3B2: return 8;
+ default: return 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static UINT GetColorChannelBits(D3DFORMAT fmt)
+{
+ switch (fmt) {
+ case D3DFMT_A2B10G10R10: return 10;
+ case D3DFMT_A2R10G10B10: return 10;
+ case D3DFMT_R8G8B8: return 8;
+ case D3DFMT_A8R8G8B8: return 8;
+ case D3DFMT_X8R8G8B8: return 8;
+ case D3DFMT_R5G6B5: return 6;
+ case D3DFMT_X1R5G5B5: return 5;
+ case D3DFMT_A1R5G5B5: return 5;
+ case D3DFMT_A4R4G4B4: return 4;
+ case D3DFMT_R3G3B2: return 2;
+ case D3DFMT_A8R3G3B2: return 2;
+ case D3DFMT_X4R4G4B4: return 4;
+ default: return 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static UINT GetAlphaChannelBits(D3DFORMAT fmt)
+{
+ switch (fmt) {
+ case D3DFMT_R8G8B8: return 0;
+ case D3DFMT_A8R8G8B8: return 8;
+ case D3DFMT_X8R8G8B8: return 0;
+ case D3DFMT_R5G6B5: return 0;
+ case D3DFMT_X1R5G5B5: return 0;
+ case D3DFMT_A1R5G5B5: return 1;
+ case D3DFMT_A4R4G4B4: return 4;
+ case D3DFMT_R3G3B2: return 0;
+ case D3DFMT_A8R3G3B2: return 8;
+ case D3DFMT_X4R4G4B4: return 0;
+ case D3DFMT_A2B10G10R10: return 2;
+ case D3DFMT_A2R10G10B10: return 2;
+ default: return 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static UINT GetDepthBits(D3DFORMAT fmt)
+{
+ switch (fmt) {
+ case D3DFMT_D16: return 16;
+ case D3DFMT_D15S1: return 15;
+ case D3DFMT_D24X8: return 24;
+ case D3DFMT_D24S8: return 24;
+ case D3DFMT_D24X4S4: return 24;
+ case D3DFMT_D32: return 32;
+ default: return 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static UINT GetStencilBits(D3DFORMAT fmt)
+{
+ switch (fmt) {
+ case D3DFMT_D16: return 0;
+ case D3DFMT_D15S1: return 1;
+ case D3DFMT_D24X8: return 0;
+ case D3DFMT_D24S8: return 8;
+ case D3DFMT_D24X4S4: return 4;
+ case D3DFMT_D32: return 0;
+ default: return 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+//
+// This routine prints a text description of the indicated driver
+// into the error log file.
+
+static void DescribeGUID(GUID* lpGUID)
+{
+ if (lpGUID)
+ Print(" GUID: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+ lpGUID->Data1, lpGUID->Data2, lpGUID->Data3,
+ lpGUID->Data4[0], lpGUID->Data4[1],
+ lpGUID->Data4[2], lpGUID->Data4[3],
+ lpGUID->Data4[4], lpGUID->Data4[5],
+ lpGUID->Data4[6], lpGUID->Data4[7]);
+ else
+ Print(" GUID IS NULL!\n");
+}
+
+
+// +--------------------------------------------------------------------+
+
+VideoDX9Enum::VideoDX9Enum(IDirect3D9* d3d9)
+{
+ if (d3d9) {
+ d3d = d3d9;
+ d3d->AddRef();
+ }
+
+ min_width = 640;
+ min_height = 480;
+ min_color_bits = 5;
+ min_alpha_bits = 0;
+ min_depth_bits = 16;
+ min_stencil_bits = 0;
+
+ uses_depth_buffer = false;
+ uses_mixed_vp = false;
+ req_windowed = false;
+ req_fullscreen = false;
+
+ adapter_index = 0;
+}
+
+VideoDX9Enum::~VideoDX9Enum()
+{
+ adapter_info_list.destroy();
+ RELEASE(d3d);
+}
+
+void
+VideoDX9Enum::SetDirect3D9(IDirect3D9* d3d9)
+{
+ RELEASE(d3d);
+
+ if (d3d9) {
+ d3d = d3d9;
+ d3d->AddRef();
+ }
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+VideoDX9Enum::SelectAdapter(int index)
+{
+ if (index >= 0 && index < adapter_info_list.size())
+ adapter_index = index;
+}
+
+VideoDX9AdapterInfo*
+VideoDX9Enum::GetAdapterInfo()
+{
+ if (adapter_index >= 0 && adapter_index < adapter_info_list.size())
+ return adapter_info_list[adapter_index];
+
+ return 0;
+}
+
+VideoDX9DeviceInfo*
+VideoDX9Enum::GetDeviceInfo(DWORD devtype)
+{
+ if (adapter_index >= 0 && adapter_index < adapter_info_list.size()) {
+ VideoDX9AdapterInfo* adapter = adapter_info_list[adapter_index];
+
+ if (adapter) {
+ ListIter<VideoDX9DeviceInfo> iter = adapter->device_info_list;
+ while (++iter) {
+ VideoDX9DeviceInfo* dev_info = iter.value();
+
+ if (dev_info->device_type == (D3DDEVTYPE) devtype)
+ return dev_info;
+ }
+ }
+ }
+
+ return 0;
+}
+
+bool
+VideoDX9Enum::IsModeSupported(int w, int h, int b) const
+{
+ if (adapter_index >= 0 && adapter_index < adapter_info_list.size()) {
+ VideoDX9AdapterInfo* adapter = adapter_info_list[adapter_index];
+
+ ListIter<VideoDX9DisplayMode> mode_iter = adapter->display_mode_list;
+ while (++mode_iter) {
+ VideoDX9DisplayMode* mode = mode_iter.value();
+
+ if (mode->width == (UINT) w &&
+ mode->height == (UINT) h &&
+ GetBitsPerPixel(mode->format) == (UINT) b) {
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+VideoDX9Enum::Enumerate()
+{
+ HRESULT hr = E_FAIL;
+
+ if (!d3d)
+ return hr;
+
+ if (VD3D_describe_things > 0) {
+ Print("Video DX9 Enumerate Adapters and Devices\n");
+ Print("----------------------------------------\n\n");
+ }
+
+ allowed_adapter_format_list.append(D3DFMT_X8R8G8B8);
+ allowed_adapter_format_list.append(D3DFMT_X1R5G5B5);
+ allowed_adapter_format_list.append(D3DFMT_R5G6B5);
+ allowed_adapter_format_list.append(D3DFMT_A2R10G10B10);
+
+ VideoDX9AdapterInfo* adapter_info = 0;
+ ArrayList adapter_format_list;
+ UINT num_adapters = d3d->GetAdapterCount();
+
+ for (UINT ordinal = 0; ordinal < num_adapters; ordinal++) {
+ adapter_info = new(__FILE__,__LINE__) VideoDX9AdapterInfo;
+ if (!adapter_info)
+ return E_OUTOFMEMORY;
+
+ adapter_info->adapter_ordinal = ordinal;
+ d3d->GetAdapterIdentifier(ordinal, 0, &adapter_info->adapter_identifier);
+
+ // Get list of all display modes on this adapter.
+ // Also build a temporary list of all display adapter formats.
+ adapter_format_list.clear();
+
+ for (int iaaf = 0; iaaf < allowed_adapter_format_list.size(); iaaf++) {
+ D3DFORMAT allowed_adapter_format = (D3DFORMAT) allowed_adapter_format_list[iaaf];
+ UINT num_adapter_modes = d3d->GetAdapterModeCount(ordinal, allowed_adapter_format);
+
+ for (UINT mode = 0; mode < num_adapter_modes; mode++) {
+ D3DDISPLAYMODE display_mode;
+ d3d->EnumAdapterModes(ordinal, allowed_adapter_format, mode, &display_mode);
+
+ if (display_mode.Width < min_width ||
+ display_mode.Height < min_height ||
+ GetColorChannelBits(display_mode.Format) < min_color_bits) {
+ continue;
+ }
+
+ VideoDX9DisplayMode* dx9_display_mode = new(__FILE__,__LINE__) VideoDX9DisplayMode(display_mode);
+
+ if (!dx9_display_mode) {
+ delete adapter_info;
+ return E_OUTOFMEMORY;
+ }
+
+ adapter_info->display_mode_list.append(dx9_display_mode);
+
+ if (!adapter_format_list.contains(display_mode.Format))
+ adapter_format_list.append(display_mode.Format);
+ }
+ }
+
+ // Sort displaymode list
+ adapter_info->display_mode_list.sort();
+
+ if (VD3D_describe_things > 0) {
+ Print("Adapter %d. %s\n", ordinal, adapter_info->adapter_identifier.Description);
+ DescribeGUID(&adapter_info->adapter_identifier.DeviceIdentifier);
+
+ if (VD3D_describe_things > 4) {
+ ListIter<VideoDX9DisplayMode> m_iter = adapter_info->display_mode_list;
+ while (++m_iter) {
+ VideoDX9DisplayMode* m = m_iter.value();
+
+ Print(" Mode %3d %s\n", m_iter.index(), m->GetDescription());
+ }
+
+ Print("\n");
+ }
+ }
+
+ // Get info for each device on this adapter
+ if (FAILED(hr = EnumerateDevices(adapter_info, adapter_format_list))) {
+ delete adapter_info;
+ return hr;
+ }
+
+ // If at least one device on this adapter is available and compatible
+ // with the app, add the adapterInfo to the list
+ if (adapter_info->device_info_list.size() == 0)
+ delete adapter_info;
+ else
+ adapter_info_list.append(adapter_info);
+
+ if (VD3D_describe_things > 0) {
+ Print("\n");
+ }
+ }
+
+ return S_OK;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+VideoDX9Enum::EnumerateDevices(VideoDX9AdapterInfo* adapter_info, ArrayList& adapter_format_list)
+{
+ HRESULT hr = E_FAIL;
+ const D3DDEVTYPE dtypes[3] = { D3DDEVTYPE_HAL, D3DDEVTYPE_SW, D3DDEVTYPE_REF };
+ const char* dtypestr[3] = { "D3DDEVTYPE_HAL", "D3DDEVTYPE_SW", "D3DDEVTYPE_REF" };
+ VideoDX9DeviceInfo* device_info = 0;
+
+ for (int i = 0; i < 3; i++ ) {
+ device_info = new(__FILE__,__LINE__) VideoDX9DeviceInfo;
+ if (!device_info)
+ return E_OUTOFMEMORY;
+
+ device_info->adapter_ordinal = adapter_info->adapter_ordinal;
+ device_info->device_type = dtypes[i];
+
+ if (FAILED(d3d->GetDeviceCaps(adapter_info->adapter_ordinal,
+ device_info->device_type,
+ &device_info->caps))) {
+ delete device_info;
+ continue;
+ }
+
+ if (VD3D_describe_things > 1) {
+ Print(" Device %d - %s\n", i, dtypestr[i]);
+ Print(" Max Texture Width: %d\n", device_info->caps.MaxTextureWidth);
+ Print(" Max Texture Height: %d\n", device_info->caps.MaxTextureHeight);
+ }
+
+ // Get info for each device combo on this device
+ if (FAILED(hr = EnumerateDeviceCombos(device_info, adapter_format_list))) {
+ delete device_info;
+ return hr;
+ }
+
+ // make sure at least one devicecombo for this device was found
+ if (device_info->device_combo_list.size() < 1) {
+ delete device_info;
+ continue;
+ }
+
+ adapter_info->device_info_list.append(device_info);
+ }
+
+ return S_OK;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+VideoDX9Enum::EnumerateDeviceCombos(VideoDX9DeviceInfo* device_info, ArrayList& adapter_format_list)
+{
+ const D3DFORMAT back_buffer_formats[] = {
+ D3DFMT_A8R8G8B8,
+ D3DFMT_X8R8G8B8,
+ D3DFMT_R8G8B8,
+ D3DFMT_R5G6B5,
+ D3DFMT_A1R5G5B5,
+ D3DFMT_X1R5G5B5
+ };
+
+ bool is_windowed[] = { false, true };
+
+ // See which adapter formats are supported by this device
+ D3DFORMAT a_fmt;
+ for (int i = 0; i < adapter_format_list.size(); i++) {
+ a_fmt = (D3DFORMAT) adapter_format_list[i];
+
+ D3DFORMAT b_fmt;
+ for (int n = 0; n < 6; n++) {
+ b_fmt = back_buffer_formats[n];
+
+ if (GetAlphaChannelBits(b_fmt) < min_alpha_bits)
+ continue;
+
+ bool win;
+ for (int w = 0; w < 2; w++) {
+ win = is_windowed[w];
+
+ if (!win && req_windowed)
+ continue;
+
+ if (win && req_fullscreen)
+ continue;
+
+ if (FAILED(d3d->CheckDeviceType(device_info->adapter_ordinal,
+ device_info->device_type,
+ a_fmt,
+ b_fmt,
+ win))) {
+ continue;
+ }
+
+ // At this point, we have an adapter/device/adapterformat/backbufferformat/iswindowed
+ // DeviceCombo that is supported by the system. We still need to confirm that it's
+ // compatible with the app, and find one or more suitable depth/stencil buffer format,
+ // multisample type, vertex processing type, and present interval.
+
+ VideoDX9DeviceCombo* device_combo = 0;
+ device_combo = new(__FILE__,__LINE__) VideoDX9DeviceCombo;
+ if (!device_combo)
+ return E_OUTOFMEMORY;
+
+ device_combo->adapter_ordinal = device_info->adapter_ordinal;
+ device_combo->device_type = device_info->device_type;
+ device_combo->adapter_format = a_fmt;
+ device_combo->back_buffer_format = b_fmt;
+ device_combo->is_windowed = win;
+
+ if (uses_depth_buffer) {
+ BuildDepthStencilFormatList(device_combo);
+ if (device_combo->depth_stencil_fmt_list.size() < 1) {
+ delete device_combo;
+ continue;
+ }
+ }
+
+ BuildMultiSampleTypeList(device_combo);
+ if (device_combo->multisample_type_list.size() < 1) {
+ delete device_combo;
+ continue;
+ }
+
+ BuildDSMSConflictList(device_combo);
+
+ BuildVertexProcessingTypeList(device_info, device_combo);
+ if (device_combo->vertex_processing_list.size() < 1) {
+ delete device_combo;
+ continue;
+ }
+
+ BuildPresentIntervalList(device_info, device_combo);
+
+ device_info->device_combo_list.append(device_combo);
+ }
+ }
+ }
+
+ return S_OK;
+}
+
+void
+VideoDX9Enum::BuildDepthStencilFormatList(VideoDX9DeviceCombo* device_combo)
+{
+ const D3DFORMAT depth_stencil_formats[] = {
+ D3DFMT_D32,
+ D3DFMT_D24S8,
+ D3DFMT_D24X4S4,
+ D3DFMT_D24X8,
+ D3DFMT_D16,
+ D3DFMT_D15S1,
+ };
+
+ for (int i = 0; i < 6; i++) {
+ D3DFORMAT fmt = depth_stencil_formats[i];
+
+ if (GetDepthBits(fmt) < min_depth_bits)
+ continue;
+
+ if (GetStencilBits(fmt) < min_stencil_bits)
+ continue;
+
+ if (SUCCEEDED(d3d->CheckDeviceFormat(device_combo->adapter_ordinal,
+ device_combo->device_type,
+ device_combo->adapter_format,
+ D3DUSAGE_DEPTHSTENCIL,
+ D3DRTYPE_SURFACE,
+ fmt))) {
+
+ if (SUCCEEDED(d3d->CheckDepthStencilMatch(device_combo->adapter_ordinal,
+ device_combo->device_type,
+ device_combo->adapter_format,
+ device_combo->back_buffer_format,
+ fmt))) {
+
+ device_combo->depth_stencil_fmt_list.append(fmt);
+ }
+ }
+ }
+}
+
+void
+VideoDX9Enum::BuildMultiSampleTypeList(VideoDX9DeviceCombo* device_combo)
+{
+ const D3DMULTISAMPLE_TYPE multisample_type_array[] = {
+ D3DMULTISAMPLE_NONE,
+ D3DMULTISAMPLE_NONMASKABLE,
+ D3DMULTISAMPLE_2_SAMPLES,
+ D3DMULTISAMPLE_3_SAMPLES,
+ D3DMULTISAMPLE_4_SAMPLES,
+ D3DMULTISAMPLE_5_SAMPLES,
+ D3DMULTISAMPLE_6_SAMPLES,
+ D3DMULTISAMPLE_7_SAMPLES,
+ D3DMULTISAMPLE_8_SAMPLES,
+ D3DMULTISAMPLE_9_SAMPLES,
+ D3DMULTISAMPLE_10_SAMPLES,
+ D3DMULTISAMPLE_11_SAMPLES,
+ D3DMULTISAMPLE_12_SAMPLES,
+ D3DMULTISAMPLE_13_SAMPLES,
+ D3DMULTISAMPLE_14_SAMPLES,
+ D3DMULTISAMPLE_15_SAMPLES,
+ D3DMULTISAMPLE_16_SAMPLES,
+ };
+
+ for (int i = 0; i < 17; i++) {
+ D3DMULTISAMPLE_TYPE multisample_type = multisample_type_array[i];
+ DWORD multisample_qual = 0;
+
+ if (SUCCEEDED(d3d->CheckDeviceMultiSampleType(device_combo->adapter_ordinal,
+ device_combo->device_type,
+ device_combo->back_buffer_format,
+ device_combo->is_windowed,
+ multisample_type,
+ &multisample_qual))) {
+
+ device_combo->multisample_type_list.append(multisample_type);
+ device_combo->multisample_qual_list.append(multisample_qual);
+ }
+ }
+}
+
+void
+VideoDX9Enum::BuildDSMSConflictList(VideoDX9DeviceCombo* device_combo)
+{
+ for (int i = 0; i < device_combo->depth_stencil_fmt_list.size(); i++) {
+ D3DFORMAT depth_format = (D3DFORMAT) device_combo->depth_stencil_fmt_list[i];
+
+ for (int n = 0; n < device_combo->multisample_type_list.size(); n++) {
+ D3DMULTISAMPLE_TYPE multisample_type = (D3DMULTISAMPLE_TYPE) device_combo->multisample_type_list[n];
+
+ if (FAILED(d3d->CheckDeviceMultiSampleType(device_combo->adapter_ordinal,
+ device_combo->device_type,
+ depth_format,
+ device_combo->is_windowed,
+ multisample_type,
+ NULL))) {
+
+ VideoDX9FormatConflict* conflict = new(__FILE__,__LINE__) VideoDX9FormatConflict;
+
+ conflict->ds_format = depth_format;
+ conflict->multisample_type = multisample_type;
+
+ device_combo->conflict_list.append(conflict);
+ }
+ }
+ }
+}
+
+void
+VideoDX9Enum::BuildVertexProcessingTypeList(VideoDX9DeviceInfo* device_info, VideoDX9DeviceCombo* device_combo)
+{
+ if ((device_info->caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) != 0) {
+ if ((device_info->caps.DevCaps & D3DDEVCAPS_PUREDEVICE) != 0) {
+ device_combo->vertex_processing_list.append(PURE_HARDWARE_VP);
+ }
+
+ device_combo->vertex_processing_list.append(HARDWARE_VP);
+
+ if (uses_mixed_vp) {
+ device_combo->vertex_processing_list.append(MIXED_VP);
+ }
+ }
+
+ device_combo->vertex_processing_list.append(SOFTWARE_VP);
+}
+
+void
+VideoDX9Enum::BuildPresentIntervalList(VideoDX9DeviceInfo* device_info, VideoDX9DeviceCombo* device_combo)
+{
+ const DWORD present_interval_array[] = {
+ D3DPRESENT_INTERVAL_IMMEDIATE,
+ D3DPRESENT_INTERVAL_DEFAULT,
+ D3DPRESENT_INTERVAL_ONE,
+ D3DPRESENT_INTERVAL_TWO,
+ D3DPRESENT_INTERVAL_THREE,
+ D3DPRESENT_INTERVAL_FOUR,
+ };
+
+ for (int i = 0; i < 6; i++) {
+ DWORD interval = present_interval_array[i];
+
+ if (device_combo->is_windowed && i > 2) {
+ // none of the remaining intervals are supported in windowed mode.
+ break;
+ }
+
+ // Note that D3DPRESENT_INTERVAL_DEFAULT is zero, so you
+ // can't do a caps check for it -- it is always available.
+
+ if (interval == D3DPRESENT_INTERVAL_DEFAULT ||
+ (device_info->caps.PresentationIntervals & interval)) {
+
+ device_combo->present_interval_list.append(interval);
+ }
+ }
+}
+
+bool
+VideoDX9Enum::SuggestWindowSettings(VideoSettings* vs)
+{
+ if (!vs)
+ return false;
+
+ // Get display mode of primary adapter (which is assumed to be where the window
+ // will appear)
+ D3DDISPLAYMODE desktop_display_mode;
+ d3d->GetAdapterDisplayMode(0, &desktop_display_mode);
+
+ VideoDX9AdapterInfo* best_adapter_info = 0;
+ VideoDX9DeviceInfo* best_device_info = 0;
+ VideoDX9DeviceCombo* best_device_combo = 0;
+ int best_adapter_index = 0;
+ int best_device_index = 0;
+
+ ListIter<VideoDX9AdapterInfo> a_iter = adapter_info_list;
+ while (++a_iter) {
+ VideoDX9AdapterInfo* adapter_info = a_iter.value();
+
+ ListIter<VideoDX9DeviceInfo> d_iter = adapter_info->device_info_list;
+ while (++d_iter) {
+ VideoDX9DeviceInfo* device_info = d_iter.value();
+
+ ListIter<VideoDX9DeviceCombo> c_iter = device_info->device_combo_list;
+ while (++c_iter) {
+ VideoDX9DeviceCombo* device_combo = c_iter.value();
+
+ bool formats_match = (device_combo->back_buffer_format == device_combo->adapter_format);
+
+ if (!device_combo->is_windowed)
+ continue;
+
+ if (device_combo->adapter_format != desktop_display_mode.Format)
+ continue;
+
+ // If we haven't found a compatible DeviceCombo yet, or if this set
+ // is better (because it's a HAL, and/or because formats match better),
+ // save it
+ if (best_device_combo == NULL ||
+ best_device_combo->device_type != D3DDEVTYPE_HAL && device_combo->device_type == D3DDEVTYPE_HAL ||
+ device_combo->device_type == D3DDEVTYPE_HAL && formats_match) {
+
+ best_adapter_info = adapter_info;
+ best_adapter_index = a_iter.index();
+ best_device_info = device_info;
+ best_device_index = d_iter.index();
+ best_device_combo = device_combo;
+
+ if (device_combo->device_type == D3DDEVTYPE_HAL && formats_match) {
+ // This windowed device combo looks great -- take it
+ goto EndWindowedDeviceComboSearch;
+ }
+
+ // Otherwise keep looking for a better windowed device combo
+ }
+ }
+ }
+ }
+
+EndWindowedDeviceComboSearch:
+ if (best_device_combo == NULL)
+ return false;
+
+ VideoDeviceInfo* win_device = &vs->windowed_device;
+
+ vs->is_windowed = true;
+ vs->windowed_mode.width = desktop_display_mode.Width;
+ vs->windowed_mode.height = desktop_display_mode.Height;
+ vs->windowed_mode.refresh = desktop_display_mode.RefreshRate;
+ vs->windowed_mode.format = desktop_display_mode.Format;
+
+ win_device->adapter_index = best_adapter_index;
+ win_device->device_index = best_device_index;
+ win_device->device_type = best_device_info->device_type;
+ win_device->back_buffer_format = best_device_combo->back_buffer_format;
+ win_device->depth_buffer_bits = GetDepthBits((D3DFORMAT) best_device_combo->depth_stencil_fmt_list[0]);
+ win_device->depth_stencil_format = best_device_combo->depth_stencil_fmt_list[0];
+ win_device->multisample_type = best_device_combo->multisample_type_list[0];
+ win_device->multisample_qual = best_device_combo->multisample_qual_list[0];
+ win_device->vertex_processing = best_device_combo->vertex_processing_list[0];
+
+ return true;
+}
+
+bool
+VideoDX9Enum::SuggestFullscreenSettings(VideoSettings* vs)
+{
+ if (!vs)
+ return false;
+
+ WORD desired_width = vs->fullscreen_mode.width;
+ WORD desired_height = vs->fullscreen_mode.height;
+
+ // For fullscreen, default to first HAL DeviceCombo that supports the current desktop
+ // display mode, or any display mode if HAL is not compatible with the desktop mode, or
+ // non-HAL if no HAL is available
+ D3DDISPLAYMODE desktop_display_mode;
+ D3DDISPLAYMODE best_desktop_display_mode;
+
+ best_desktop_display_mode.Width = 0;
+ best_desktop_display_mode.Height = 0;
+ best_desktop_display_mode.Format = D3DFMT_UNKNOWN;
+ best_desktop_display_mode.RefreshRate = 0;
+
+ VideoDX9AdapterInfo* best_adapter_info = 0;
+ VideoDX9DeviceInfo* best_device_info = 0;
+ VideoDX9DeviceCombo* best_device_combo = 0;
+ int best_adapter_index = 0;
+ int best_device_index = 0;
+
+ ListIter<VideoDX9AdapterInfo> a_iter = adapter_info_list;
+ while (++a_iter) {
+ VideoDX9AdapterInfo* adapter_info = a_iter.value();
+ d3d->GetAdapterDisplayMode(adapter_info->adapter_ordinal, &desktop_display_mode);
+
+ ListIter<VideoDX9DeviceInfo> d_iter = adapter_info->device_info_list;
+ while (++d_iter) {
+ VideoDX9DeviceInfo* device_info = d_iter.value();
+
+ ListIter<VideoDX9DeviceCombo> c_iter = device_info->device_combo_list;
+ while (++c_iter) {
+ VideoDX9DeviceCombo* device_combo = c_iter.value();
+
+ bool bAdapterMatchesBB = (device_combo->back_buffer_format == device_combo->adapter_format);
+ bool bAdapterMatchesDesktop = (device_combo->adapter_format == desktop_display_mode.Format);
+
+ if (device_combo->is_windowed)
+ continue;
+
+ // If we haven't found a compatible set yet, or if this set
+ // is better (because it's a HAL, and/or because formats match better),
+ // save it
+ if (best_device_combo == NULL ||
+ best_device_combo->device_type != D3DDEVTYPE_HAL && device_info->device_type == D3DDEVTYPE_HAL ||
+ device_combo->device_type == D3DDEVTYPE_HAL && best_device_combo->adapter_format != desktop_display_mode.Format && bAdapterMatchesDesktop ||
+ device_combo->device_type == D3DDEVTYPE_HAL && bAdapterMatchesDesktop && bAdapterMatchesBB ) {
+
+ best_desktop_display_mode = desktop_display_mode;
+ best_adapter_info = adapter_info;
+ best_device_info = device_info;
+ best_device_combo = device_combo;
+
+ if (device_info->device_type == D3DDEVTYPE_HAL && bAdapterMatchesDesktop && bAdapterMatchesBB) {
+ // This fullscreen device combo looks great -- take it
+ goto EndFullscreenDeviceComboSearch;
+ }
+
+ // Otherwise keep looking for a better fullscreen device combo
+ }
+ }
+ }
+ }
+
+EndFullscreenDeviceComboSearch:
+ if (best_device_combo == NULL)
+ return false;
+
+ // Need to find a display mode on the best adapter that uses best_device_combo->adapter_format
+ // and is as close to best_desktop_display_mode's res as possible
+ VideoDX9DisplayMode best_display_mode;
+
+ ListIter<VideoDX9DisplayMode> m_iter = best_adapter_info->display_mode_list;
+ while (++m_iter) {
+ VideoDX9DisplayMode* display_mode = m_iter.value();
+
+ if (display_mode->format != best_device_combo->adapter_format)
+ continue;
+
+ if (display_mode->width == desired_width && //best_desktop_display_mode.Width &&
+ display_mode->height == desired_height && //best_desktop_display_mode.Height &&
+ display_mode->refresh == best_desktop_display_mode.RefreshRate) {
+
+ // found a perfect match, so stop
+ best_display_mode = *display_mode;
+ break;
+ }
+ else if (display_mode->width == desired_width && //best_desktop_display_mode.Width &&
+ display_mode->height == desired_height && //best_desktop_display_mode.Height &&
+ display_mode->refresh > best_desktop_display_mode.RefreshRate) {
+ // refresh rate doesn't match, but width/height match, so keep this
+ // and keep looking
+ best_display_mode = *display_mode;
+ }
+ else if (display_mode->width == desired_width) { //best_desktop_display_mode.Width) {
+ // width matches, so keep this and keep looking
+ best_display_mode = *display_mode;
+ }
+ else if (best_display_mode.width == 0)
+ {
+ // we don't have anything better yet, so keep this and keep looking
+ best_display_mode = *display_mode;
+ }
+ }
+
+ VideoDeviceInfo* fs_device = &vs->fullscreen_device;
+
+ vs->is_windowed = false;
+ vs->fullscreen_mode.width = best_display_mode.width;
+ vs->fullscreen_mode.height = best_display_mode.height;
+ vs->fullscreen_mode.refresh = best_display_mode.refresh;
+ vs->fullscreen_mode.format = best_display_mode.format;
+
+ fs_device->adapter_index = best_adapter_index;
+ fs_device->device_index = best_device_index;
+ fs_device->device_type = best_device_info->device_type;
+ fs_device->back_buffer_format = best_device_combo->back_buffer_format;
+ fs_device->depth_buffer_bits = GetDepthBits((D3DFORMAT) best_device_combo->depth_stencil_fmt_list[0]);
+ fs_device->depth_stencil_format = best_device_combo->depth_stencil_fmt_list[0];
+ fs_device->multisample_type = best_device_combo->multisample_type_list[0];
+ fs_device->multisample_qual = best_device_combo->multisample_qual_list[0];
+ fs_device->vertex_processing = best_device_combo->vertex_processing_list[0];
+
+ return true;
+}
+
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+VideoDX9DisplayMode::VideoDX9DisplayMode()
+ : width(0), height(0), refresh(0), format(D3DFMT_UNKNOWN)
+{ }
+
+VideoDX9DisplayMode::VideoDX9DisplayMode(const VideoDX9DisplayMode& m)
+ : width(m.width), height(m.height), refresh(m.refresh), format(m.format)
+{ }
+
+VideoDX9DisplayMode::VideoDX9DisplayMode(const D3DDISPLAYMODE& m)
+ : width(m.Width), height(m.Height), refresh(m.RefreshRate), format(m.Format)
+{ }
+
+int
+VideoDX9DisplayMode::operator<(const VideoDX9DisplayMode& m) const
+{
+ if (width < m.width)
+ return 1;
+
+ if (width > m.width)
+ return 0;
+
+ if (height < m.height)
+ return 1;
+
+ if (height > m.height)
+ return 0;
+
+ if (format < m.format)
+ return 1;
+
+ if (format > m.format)
+ return 0;
+
+ if (refresh < m.refresh)
+ return 1;
+
+ return 0;
+}
+
+int
+VideoDX9DisplayMode::operator<=(const VideoDX9DisplayMode& m) const
+{
+ // if less than ...
+ if (*this < m)
+ return 1;
+
+ // ... or equal to ...
+ if (width == m.width &&
+ height == m.height &&
+ format == m.format &&
+ refresh == m.refresh)
+ return 1;
+
+ // must be greater than
+ return 0;
+}
+
+const char*
+VideoDX9DisplayMode::GetDescription() const
+{
+ static char desc[32];
+
+ sprintf(desc, "%4d x %4d %-12s %d Hz",
+ width,
+ height,
+ D3DFormatToString(format),
+ refresh);
+
+ return desc;
+}
+
+const char*
+VideoDX9DisplayMode::D3DFormatToString(D3DFORMAT format)
+{
+ const char* str = "Unknown Format";
+
+ switch (format) {
+ case D3DFMT_UNKNOWN: str = "UNKNOWN"; break;
+ case D3DFMT_R8G8B8: str = "R8G8B8"; break;
+ case D3DFMT_A8R8G8B8: str = "A8R8G8B8"; break;
+ case D3DFMT_X8R8G8B8: str = "X8R8G8B8"; break;
+ case D3DFMT_R5G6B5: str = "R5G6B5"; break;
+ case D3DFMT_X1R5G5B5: str = "X1R5G5B5"; break;
+ case D3DFMT_A1R5G5B5: str = "A1R5G5B5"; break;
+ case D3DFMT_A4R4G4B4: str = "A4R4G4B4"; break;
+ case D3DFMT_R3G3B2: str = "R3G3B2"; break;
+ case D3DFMT_A8: str = "A8"; break;
+ case D3DFMT_A8R3G3B2: str = "A8R3G3B2"; break;
+ case D3DFMT_X4R4G4B4: str = "X4R4G4B4"; break;
+ case D3DFMT_A2B10G10R10: str = "A2B10G10R10"; break;
+ case D3DFMT_A8B8G8R8: str = "A8B8G8R8"; break;
+ case D3DFMT_X8B8G8R8: str = "X8B8G8R8"; break;
+ case D3DFMT_G16R16: str = "G16R16"; break;
+ case D3DFMT_A2R10G10B10: str = "A2R10G10B10"; break;
+ case D3DFMT_A16B16G16R16: str = "A16B16G16R16"; break;
+ case D3DFMT_A8P8: str = "A8P8"; break;
+ case D3DFMT_P8: str = "P8"; break;
+ case D3DFMT_L8: str = "L8"; break;
+ case D3DFMT_A8L8: str = "A8L8"; break;
+ case D3DFMT_A4L4: str = "A4L4"; break;
+ case D3DFMT_V8U8: str = "V8U8"; break;
+ case D3DFMT_L6V5U5: str = "L6V5U5"; break;
+ case D3DFMT_X8L8V8U8: str = "X8L8V8U8"; break;
+ case D3DFMT_Q8W8V8U8: str = "Q8W8V8U8"; break;
+ case D3DFMT_V16U16: str = "V16U16"; break;
+ case D3DFMT_A2W10V10U10: str = "A2W10V10U10"; break;
+ case D3DFMT_UYVY: str = "UYVY"; break;
+ case D3DFMT_YUY2: str = "YUY2"; break;
+ case D3DFMT_DXT1: str = "DXT1"; break;
+ case D3DFMT_DXT2: str = "DXT2"; break;
+ case D3DFMT_DXT3: str = "DXT3"; break;
+ case D3DFMT_DXT4: str = "DXT4"; break;
+ case D3DFMT_DXT5: str = "DXT5"; break;
+ case D3DFMT_D16_LOCKABLE: str = "D16_LOCKABLE"; break;
+ case D3DFMT_D32: str = "D32"; break;
+ case D3DFMT_D15S1: str = "D15S1"; break;
+ case D3DFMT_D24S8: str = "D24S8"; break;
+ case D3DFMT_D24X8: str = "D24X8"; break;
+ case D3DFMT_D24X4S4: str = "D24X4S4"; break;
+ case D3DFMT_D16: str = "D16"; break;
+ case D3DFMT_L16: str = "L16"; break;
+ case D3DFMT_VERTEXDATA: str = "VERTEXDATA"; break;
+ case D3DFMT_INDEX16: str = "INDEX16"; break;
+ case D3DFMT_INDEX32: str = "INDEX32"; break;
+ case D3DFMT_Q16W16V16U16: str = "Q16W16V16U16"; break;
+ case D3DFMT_MULTI2_ARGB8: str = "MULTI2_ARGB8"; break;
+ case D3DFMT_R16F: str = "R16F"; break;
+ case D3DFMT_G16R16F: str = "G16R16F"; break;
+ case D3DFMT_A16B16G16R16F: str = "A16B16G16R16F"; break;
+ case D3DFMT_R32F: str = "R32F"; break;
+ case D3DFMT_G32R32F: str = "G32R32F"; break;
+ case D3DFMT_A32B32G32R32F: str = "A32B32G32R32F"; break;
+ case D3DFMT_CxV8U8: str = "CxV8U8"; break;
+ default: str = "Unknown format"; break;
+ }
+
+ return str;
+}
+
+
+// +--------------------------------------------------------------------+
+
+VideoDX9AdapterInfo::VideoDX9AdapterInfo()
+ : adapter_ordinal(0)
+{
+ ZeroMemory(&adapter_identifier, sizeof(adapter_identifier));
+}
+
+VideoDX9AdapterInfo::~VideoDX9AdapterInfo()
+{
+ display_mode_list.destroy();
+ device_info_list.destroy();
+}
+
+const char*
+VideoDX9AdapterInfo::GetDescription() const
+{
+ return adapter_identifier.Description;
+}
+
+// +--------------------------------------------------------------------+
+
+VideoDX9DeviceInfo::VideoDX9DeviceInfo()
+ : adapter_ordinal(0), device_type(D3DDEVTYPE_HAL)
+{
+ ZeroMemory(&caps, sizeof(caps));
+}
+
+VideoDX9DeviceInfo::~VideoDX9DeviceInfo()
+{
+ device_combo_list.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+VideoDX9DeviceCombo::VideoDX9DeviceCombo()
+ : adapter_ordinal(0), device_type(D3DDEVTYPE_HAL),
+ adapter_format((D3DFORMAT) 0),
+ back_buffer_format((D3DFORMAT) 0),
+ is_windowed(false)
+{
+}
+
+VideoDX9DeviceCombo::~VideoDX9DeviceCombo()
+{
+ conflict_list.destroy();
+}
diff --git a/nGenEx/VideoDX9Enum.h b/nGenEx/VideoDX9Enum.h
new file mode 100644
index 0000000..f00664f
--- /dev/null
+++ b/nGenEx/VideoDX9Enum.h
@@ -0,0 +1,185 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoDX9Enum.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct3D and Direct3D Video classes for DirectX 7
+*/
+
+#ifndef VideoDX9Enum_h
+#define VideoDX9Enum_h
+
+#include <d3d9.h>
+
+#include "Video.h"
+#include "ArrayList.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class VideoDX9Enum;
+struct VideoDX9DisplayMode;
+struct VideoDX9AdapterInfo;
+struct VideoDX9DeviceInfo;
+struct VideoDX9DSMSConflict;
+struct VideoDX9DeviceCombo;
+class VideoSettings;
+
+// +--------------------------------------------------------------------+
+
+class VideoDX9Enum
+{
+public:
+ enum VP_TYPE {
+ SOFTWARE_VP,
+ MIXED_VP,
+ HARDWARE_VP,
+ PURE_HARDWARE_VP
+ };
+
+ VideoDX9Enum(IDirect3D9* d3d9);
+ ~VideoDX9Enum();
+
+ void SetDirect3D9(IDirect3D9* d3d9);
+
+ HRESULT Enumerate();
+ bool SuggestWindowSettings(VideoSettings* vs);
+ bool SuggestFullscreenSettings(VideoSettings* vs);
+
+ int NumAdapters() const { return adapter_info_list.size(); }
+ void SelectAdapter(int index);
+ VideoDX9AdapterInfo* GetAdapterInfo();
+ VideoDX9DeviceInfo* GetDeviceInfo(DWORD devtype);
+
+ bool IsModeSupported(int width, int height, int bpp) const;
+
+ UINT min_width;
+ UINT min_height;
+ UINT min_color_bits;
+ UINT min_alpha_bits;
+ UINT min_depth_bits;
+ UINT min_stencil_bits;
+
+ bool uses_depth_buffer;
+ bool uses_mixed_vp;
+ bool req_windowed;
+ bool req_fullscreen;
+
+private:
+ HRESULT EnumerateDevices(VideoDX9AdapterInfo* adapter_info, ArrayList& adapter_format_list);
+ HRESULT EnumerateDeviceCombos(VideoDX9DeviceInfo* device_info, ArrayList& adapter_format_list);
+
+ void BuildDepthStencilFormatList(VideoDX9DeviceCombo* device_combo);
+ void BuildMultiSampleTypeList(VideoDX9DeviceCombo* device_combo);
+ void BuildDSMSConflictList(VideoDX9DeviceCombo* device_combo);
+ void BuildVertexProcessingTypeList(VideoDX9DeviceInfo* device_info, VideoDX9DeviceCombo* device_combo);
+ void BuildPresentIntervalList(VideoDX9DeviceInfo* device_info, VideoDX9DeviceCombo* device_combo);
+
+ IDirect3D9* d3d;
+ ArrayList allowed_adapter_format_list;
+
+ List<VideoDX9AdapterInfo> adapter_info_list;
+ int adapter_index;
+};
+
+
+// +--------------------------------------------------------------------+
+
+struct VideoDX9AdapterInfo
+{
+ static const char* TYPENAME() { return "VideoDX9AdapterInfo"; }
+
+ VideoDX9AdapterInfo();
+ ~VideoDX9AdapterInfo();
+
+ const char* GetDescription() const;
+
+ int adapter_ordinal;
+ D3DADAPTER_IDENTIFIER9 adapter_identifier;
+ List<VideoDX9DisplayMode> display_mode_list;
+ List<VideoDX9DeviceInfo> device_info_list;
+};
+
+
+// +--------------------------------------------------------------------+
+
+struct VideoDX9DisplayMode
+{
+ static const char* TYPENAME() { return "VideoDX9DisplayMode"; }
+
+ VideoDX9DisplayMode();
+ VideoDX9DisplayMode(const VideoDX9DisplayMode& m);
+ VideoDX9DisplayMode(const D3DDISPLAYMODE& m);
+
+ int operator<(const VideoDX9DisplayMode& m) const;
+ int operator<=(const VideoDX9DisplayMode& m) const;
+
+ const char* GetDescription() const;
+ static const char* D3DFormatToString(D3DFORMAT format);
+
+ UINT width;
+ UINT height;
+ UINT refresh;
+ D3DFORMAT format;
+};
+
+
+// +--------------------------------------------------------------------+
+
+struct VideoDX9DeviceInfo
+{
+ static const char* TYPENAME() { return "VideoDX9DeviceInfo"; }
+
+ VideoDX9DeviceInfo();
+ ~VideoDX9DeviceInfo();
+
+ int adapter_ordinal;
+ D3DDEVTYPE device_type;
+ D3DCAPS9 caps;
+ List<VideoDX9DeviceCombo> device_combo_list;
+};
+
+
+// +--------------------------------------------------------------------+
+
+struct VideoDX9FormatConflict
+{
+ static const char* TYPENAME() { return "VideoDX9FormatConflict"; }
+
+ D3DFORMAT ds_format;
+ D3DMULTISAMPLE_TYPE multisample_type;
+};
+
+
+// +--------------------------------------------------------------------+
+
+struct VideoDX9DeviceCombo
+{
+ VideoDX9DeviceCombo();
+ ~VideoDX9DeviceCombo();
+
+ int adapter_ordinal;
+ D3DDEVTYPE device_type;
+ D3DFORMAT adapter_format;
+ D3DFORMAT back_buffer_format;
+ bool is_windowed;
+
+ ArrayList vertex_processing_list;
+ ArrayList depth_stencil_fmt_list;
+ ArrayList multisample_type_list;
+ ArrayList multisample_qual_list;
+ ArrayList present_interval_list;
+
+ List<VideoDX9FormatConflict> conflict_list;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif VideoDX9Enum_h
+
diff --git a/nGenEx/VideoDX9VertexBuffer.cpp b/nGenEx/VideoDX9VertexBuffer.cpp
new file mode 100644
index 0000000..08d3bb2
--- /dev/null
+++ b/nGenEx/VideoDX9VertexBuffer.cpp
@@ -0,0 +1,281 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoDX9VertexBuffer.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct3D Video class for DirectX 9
+*/
+
+#include "MemDebug.h"
+#include "VideoDX9VertexBuffer.h"
+#include "Color.h"
+
+// +--------------------------------------------------------------------+
+
+void VideoDX9Error(const char* msg, HRESULT dderr);
+extern int VD3D_describe_things;
+
+#ifndef RELEASE
+#define RELEASE(x) if (x) { x->Release(); x=NULL; }
+#endif
+
+// +--------------------------------------------------------------------+
+
+VideoDX9VertexBuffer::VideoDX9VertexBuffer(VideoDX9* dx9,
+ UINT nverts,
+ UINT vsize,
+ DWORD format,
+ DWORD usage)
+ : video(dx9), vertex_buffer(0),
+ num_verts(nverts), num_locked(0), vert_size(vsize), next_vert(0),
+ is_dynamic(false)
+{
+ UINT len = num_verts * vert_size;
+
+ if (video && len) {
+ is_dynamic = (usage & D3DUSAGE_DYNAMIC) ? true : false;
+ D3DPOOL pool = is_dynamic ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
+
+ HRESULT hr = video->D3DDevice()->CreateVertexBuffer(len,
+ usage,
+ format,
+ pool,
+ &vertex_buffer,
+ 0);
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report) {
+ VideoDX9Error("Could not create vertex buffer.", hr);
+ report--;
+ }
+
+ num_verts = 0;
+ vert_size = 0;
+ next_vert = 0;
+ }
+ }
+}
+
+VideoDX9VertexBuffer::~VideoDX9VertexBuffer()
+{
+ RELEASE(vertex_buffer);
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+VideoDX9VertexBuffer::Lock(UINT count)
+{
+ if (vertex_buffer && count <= num_verts) {
+ DWORD flags = 0;
+
+ if (count == 0)
+ count = num_verts;
+
+ if (is_dynamic) {
+ flags = D3DLOCK_NOOVERWRITE;
+
+ if (next_vert + count > num_verts) {
+ next_vert = 0;
+ flags = D3DLOCK_DISCARD;
+ }
+ }
+
+ void* result = 0;
+ HRESULT hr = 0;
+
+ hr = vertex_buffer->Lock(next_vert * vert_size,
+ count * vert_size,
+ &result,
+ flags);
+
+ if (SUCCEEDED(hr)) {
+ num_locked = count;
+ return (BYTE*) result;
+ }
+ }
+
+ return 0;
+}
+
+void
+VideoDX9VertexBuffer::Unlock()
+{
+ if (vertex_buffer && num_locked > 0) {
+ vertex_buffer->Unlock();
+
+ next_vert += num_locked;
+ num_locked = 0;
+ }
+}
+
+bool
+VideoDX9VertexBuffer::Select(int stream)
+{
+ if (video && vertex_buffer) {
+ HRESULT hr = E_FAIL;
+
+ if (num_locked > 0)
+ Unlock();
+
+ hr = video->D3DDevice()->SetStreamSource(stream,
+ vertex_buffer,
+ 0,
+ vert_size);
+
+ if (SUCCEEDED(hr))
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+UINT
+VideoDX9VertexBuffer::GetNumVerts() const
+{
+ return num_verts;
+}
+
+UINT
+VideoDX9VertexBuffer::GetVertSize() const
+{
+ return vert_size;
+}
+
+UINT
+VideoDX9VertexBuffer::GetNextVert() const
+{
+ return next_vert;
+}
+
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+
+VideoDX9IndexBuffer::VideoDX9IndexBuffer(VideoDX9* dx9,
+ UINT nind,
+ DWORD usage)
+ : video(dx9), index_buffer(0),
+ num_indices(nind), num_locked(0), next_index(0),
+ is_dynamic(false)
+{
+ UINT len = num_indices * sizeof(WORD);
+
+ if (video && len) {
+ is_dynamic = (usage & D3DUSAGE_DYNAMIC) ? true : false;
+ D3DPOOL pool = is_dynamic ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
+
+ HRESULT hr = video->D3DDevice()->CreateIndexBuffer(len,
+ usage,
+ D3DFMT_INDEX16,
+ pool,
+ &index_buffer,
+ 0);
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report) {
+ VideoDX9Error("Could not create index buffer.", hr);
+ report--;
+ }
+
+ num_indices = 0;
+ next_index = 0;
+ }
+ }
+}
+
+VideoDX9IndexBuffer::~VideoDX9IndexBuffer()
+{
+ RELEASE(index_buffer);
+}
+
+// +--------------------------------------------------------------------+
+
+WORD*
+VideoDX9IndexBuffer::Lock(UINT count)
+{
+ if (index_buffer && count <= num_indices) {
+ DWORD flags = 0;
+
+ if (count == 0)
+ count = num_indices;
+
+ if (is_dynamic) {
+ flags = D3DLOCK_NOOVERWRITE;
+
+ if (next_index + count > num_indices) {
+ next_index = 0;
+ flags = D3DLOCK_DISCARD;
+ }
+ }
+
+ void* result = 0;
+ HRESULT hr = 0;
+
+ hr = index_buffer->Lock(next_index * 2,
+ count * 2,
+ &result,
+ flags);
+
+ if (SUCCEEDED(hr)) {
+ num_locked = count;
+ return (WORD*) result;
+ }
+ }
+
+ return 0;
+}
+
+void
+VideoDX9IndexBuffer::Unlock()
+{
+ if (index_buffer && num_locked > 0) {
+ index_buffer->Unlock();
+
+ next_index += num_locked;
+ num_locked = 0;
+ }
+}
+
+bool
+VideoDX9IndexBuffer::Select()
+{
+ if (video && index_buffer) {
+ if (num_locked > 0)
+ Unlock();
+
+ HRESULT hr = video->D3DDevice()->SetIndices(index_buffer);
+
+ if (SUCCEEDED(hr))
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+UINT
+VideoDX9IndexBuffer::GetNumIndices() const
+{
+ return num_indices;
+}
+
+UINT
+VideoDX9IndexBuffer::GetNextIndex() const
+{
+ return next_index;
+}
+
diff --git a/nGenEx/VideoDX9VertexBuffer.h b/nGenEx/VideoDX9VertexBuffer.h
new file mode 100644
index 0000000..815cb86
--- /dev/null
+++ b/nGenEx/VideoDX9VertexBuffer.h
@@ -0,0 +1,79 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoDX9VertexBuffer.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Vertex and Index Buffer classes for DirectX 9
+*/
+
+#ifndef VideoDX9VertexBuffer_h
+#define VideoDX9VertexBuffer_h
+
+#include "VideoDX9.h"
+
+// +--------------------------------------------------------------------+
+
+class VideoDX9VertexBuffer
+{
+public:
+ VideoDX9VertexBuffer(VideoDX9* dx9,
+ UINT num_verts,
+ UINT vert_size,
+ DWORD format,
+ DWORD usage);
+ ~VideoDX9VertexBuffer();
+
+ BYTE* Lock(UINT count);
+ void Unlock();
+ bool Select(int stream=0);
+
+ UINT GetNumVerts() const;
+ UINT GetVertSize() const;
+ UINT GetNextVert() const;
+
+private:
+ VideoDX9* video;
+ IDirect3DVertexBuffer9* vertex_buffer;
+
+ UINT num_verts;
+ UINT num_locked;
+ UINT vert_size;
+ UINT next_vert;
+ bool is_dynamic;
+};
+
+// +--------------------------------------------------------------------+
+
+class VideoDX9IndexBuffer
+{
+public:
+ VideoDX9IndexBuffer(VideoDX9* dx9,
+ UINT num_indices,
+ DWORD usage);
+ ~VideoDX9IndexBuffer();
+
+ WORD* Lock(UINT count);
+ void Unlock();
+ bool Select();
+
+ UINT GetNumIndices() const;
+ UINT GetNextIndex() const;
+
+private:
+ VideoDX9* video;
+ IDirect3DIndexBuffer9* index_buffer;
+
+ UINT num_indices;
+ UINT num_locked;
+ UINT next_index;
+ bool is_dynamic;
+};
+
+#endif VideoDX9VertexBuffer_h
+
diff --git a/nGenEx/VideoFactory.cpp b/nGenEx/VideoFactory.cpp
new file mode 100644
index 0000000..ded9e84
--- /dev/null
+++ b/nGenEx/VideoFactory.cpp
@@ -0,0 +1,71 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoFac.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Video and Polygon Renderer Factory class
+*/
+
+#include "MemDebug.h"
+#include "VideoFactory.h"
+
+#include "VideoDX9.h"
+#include "SoundD3D.h"
+
+// +--------------------------------------------------------------------+
+
+VideoFactory::VideoFactory(HWND h)
+ : hwnd(h), video(0), audio(0)
+{ }
+
+VideoFactory::~VideoFactory()
+{ }
+
+// +--------------------------------------------------------------------+
+
+Video*
+VideoFactory::CreateVideo(VideoSettings* vs)
+{
+ if (!video) {
+ video = (Video*) new(__FILE__,__LINE__) VideoDX9(hwnd, vs);
+
+ if (!video) {
+ delete video;
+ video = 0;
+ }
+ }
+
+ return video;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VideoFactory::DestroyVideo(Video* v)
+{
+ if (v == video) {
+ delete video;
+ video = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+SoundCard*
+VideoFactory::CreateSoundCard()
+{
+ if (!audio) {
+ audio = new(__FILE__,__LINE__) SoundCardD3D(hwnd);
+ Sound::UseSoundCard(audio);
+ }
+
+ return audio;
+}
+
+
diff --git a/nGenEx/VideoFactory.h b/nGenEx/VideoFactory.h
new file mode 100644
index 0000000..f30db1b
--- /dev/null
+++ b/nGenEx/VideoFactory.h
@@ -0,0 +1,42 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoFactory.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Video Factory class
+*/
+
+#ifndef VideoFactory_h
+#define VideoFactory_h
+
+#include "Types.h"
+#include "Video.h"
+#include "SoundCard.h"
+
+// +--------------------------------------------------------------------+
+
+class VideoFactory
+{
+public:
+ VideoFactory(HWND h);
+ virtual ~VideoFactory();
+
+ virtual Video* CreateVideo(VideoSettings* vs);
+ virtual void DestroyVideo(Video* video);
+ virtual SoundCard* CreateSoundCard();
+
+private:
+ HWND hwnd;
+
+ Video* video;
+ SoundCard* audio;
+};
+
+#endif VideoFactory_h
+
diff --git a/nGenEx/VideoSettings.cpp b/nGenEx/VideoSettings.cpp
new file mode 100644
index 0000000..cd4c8df
--- /dev/null
+++ b/nGenEx/VideoSettings.cpp
@@ -0,0 +1,268 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoSettings.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Video Settings class implementation
+*/
+
+#include "MemDebug.h"
+#include "VideoSettings.h"
+
+// +--------------------------------------------------------------------+
+
+VideoSettings::VideoSettings()
+{
+ fullscreen_mode.width = 1024;
+ fullscreen_mode.height = 768;
+ fullscreen_mode.refresh = 75;
+ fullscreen_mode.format = VideoMode::FMT_X8R8G8B8;
+
+ windowed_mode.width = 800;
+ windowed_mode.height = 600;
+ windowed_mode.refresh = 0;
+ windowed_mode.format = VideoMode::FMT_NONE;
+
+ window_width = 800;
+ window_height = 600;
+
+ is_windowed = false;
+ use_effects = true;
+ shadows = true;
+ bumpmaps = true;
+ specmaps = true;
+ max_detail = 4;
+ enable_vs = true;
+ enable_ps = true;
+ depth_bias = 0;
+}
+
+VideoSettings::~VideoSettings()
+{ }
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoSettings::IsWindowed() const
+{
+ return is_windowed;
+}
+
+bool
+VideoSettings::UseEffects() const
+{
+ return use_effects;
+}
+
+int
+VideoSettings::GetWidth() const
+{
+ if (is_windowed)
+ return window_width;
+
+ return fullscreen_mode.width;
+}
+
+int
+VideoSettings::GetHeight() const
+{
+ if (is_windowed)
+ return window_height;
+
+ return fullscreen_mode.height;
+}
+
+int
+VideoSettings::GetDepth() const
+{
+ int fmt = 0;
+ int bpp = 0;
+
+ if (is_windowed)
+ fmt = windowed_mode.format;
+ else
+ fmt = fullscreen_mode.format;
+
+ switch (fmt) {
+ default:
+ case VideoMode::FMT_NONE: bpp = 0; break;
+ case VideoMode::FMT_R5G5B5: bpp = 15; break;
+ case VideoMode::FMT_R5G6B5: bpp = 16; break;
+ case VideoMode::FMT_R8G8B8: bpp = 24; break;
+ case VideoMode::FMT_X8R8G8B8: bpp = 32; break;
+ }
+
+ return bpp;
+}
+
+int
+VideoSettings::GetPixSize() const
+{
+ int fmt = 0;
+ int pix = 0;
+
+ if (is_windowed)
+ fmt = windowed_mode.format;
+ else
+ fmt = fullscreen_mode.format;
+
+ switch (fmt) {
+ default:
+ case VideoMode::FMT_NONE: pix = 0; break;
+ case VideoMode::FMT_R5G5B5: pix = 2; break;
+ case VideoMode::FMT_R5G6B5: pix = 2; break;
+ case VideoMode::FMT_R8G8B8: pix = 3; break;
+ case VideoMode::FMT_X8R8G8B8: pix = 4; break;
+ }
+
+ return pix;
+}
+
+int
+VideoSettings::GetRefreshRate() const
+{
+ if (is_windowed)
+ return windowed_mode.refresh;
+
+ return fullscreen_mode.refresh;
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+VideoSettings::GetModeDescription() const
+{
+ if (is_windowed)
+ return windowed_mode.GetDescription();
+
+ return fullscreen_mode.GetDescription();
+}
+
+// +--------------------------------------------------------------------+
+
+int
+VideoSettings::GetVertexProcessing() const
+{
+ if (is_windowed)
+ return windowed_device.vertex_processing;
+
+ return fullscreen_device.vertex_processing;
+}
+
+int
+VideoSettings::GetDepthBufferBits() const
+{
+ if (is_windowed)
+ return windowed_device.depth_buffer_bits;
+
+ return fullscreen_device.depth_buffer_bits;
+}
+
+int
+VideoSettings::GetAdapterIndex() const
+{
+ if (is_windowed)
+ return windowed_device.adapter_index;
+
+ return fullscreen_device.adapter_index;
+}
+
+int
+VideoSettings::GetDeviceIndex() const
+{
+ if (is_windowed)
+ return windowed_device.device_index;
+
+ return fullscreen_device.device_index;
+}
+
+DWORD
+VideoSettings::GetDeviceType() const
+{
+ if (is_windowed)
+ return windowed_device.device_type;
+
+ return fullscreen_device.device_type;
+}
+
+DWORD
+VideoSettings::GetDepthStencilFormat() const
+{
+ if (is_windowed)
+ return windowed_device.depth_stencil_format;
+
+ return fullscreen_device.depth_stencil_format;
+}
+
+DWORD
+VideoSettings::GetBackBufferFormat() const
+{
+ if (is_windowed)
+ return windowed_device.back_buffer_format;
+
+ return fullscreen_device.back_buffer_format;
+}
+
+const char*
+VideoSettings::GetAdapterDesc() const
+{
+ if (is_windowed)
+ return windowed_device.adapter_desc;
+
+ return fullscreen_device.adapter_desc;
+}
+
+const char*
+VideoSettings::GetDeviceDesc() const
+{
+ if (is_windowed)
+ return windowed_device.device_desc;
+
+ return fullscreen_device.device_desc;
+}
+
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+const char*
+VideoMode::GetDescription() const
+{
+ static char desc[32];
+
+ int bpp = 0;
+ switch (format) {
+ default:
+ case VideoMode::FMT_NONE: bpp = 0; break;
+ case VideoMode::FMT_R5G5B5: bpp = 15; break;
+ case VideoMode::FMT_R5G6B5: bpp = 16; break;
+ case VideoMode::FMT_R8G8B8: bpp = 24; break;
+ case VideoMode::FMT_X8R8G8B8: bpp = 32; break;
+ }
+
+ sprintf(desc, "%d x %d x %d", width, height, bpp);
+ return desc;
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+VideoDeviceInfo::VideoDeviceInfo()
+{
+ ZeroMemory(this, sizeof(VideoDeviceInfo));
+
+ vertex_processing = VideoSettings::VTX_HARDWARE;
+ depth_buffer_bits = 32;
+}
+
+VideoDeviceInfo::~VideoDeviceInfo()
+{
+}
diff --git a/nGenEx/VideoSettings.h b/nGenEx/VideoSettings.h
new file mode 100644
index 0000000..e88e645
--- /dev/null
+++ b/nGenEx/VideoSettings.h
@@ -0,0 +1,131 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoSettings.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Video Settings class
+*/
+
+#ifndef VideoSettings_h
+#define VideoSettings_h
+
+#include "Types.h"
+#include "Video.h"
+
+// +--------------------------------------------------------------------+
+
+struct VideoMode
+{
+ enum Format {
+ FMT_NONE = 0,
+ FMT_R5G5B5 = 24,
+ FMT_R5G6B5 = 23,
+ FMT_R8G8B8 = 20,
+ FMT_X8R8G8B8 = 22
+ };
+
+ VideoMode() : width(0), height(0), refresh(0), format(0) { }
+ VideoMode(int w, int h, Format f, int r=0) : width(w), height(h), refresh(r), format(f) { }
+
+ int operator == (const VideoMode& m) const { return m.width == width &&
+ m.height == height &&
+ m.format == format; }
+ int operator != (const VideoMode& m) const { return m.width != width ||
+ m.height != height ||
+ m.format != format; }
+
+ const char* GetDescription() const;
+
+ int width;
+ int height;
+ int refresh;
+ int format;
+};
+
+// +--------------------------------------------------------------------+
+
+struct VideoDeviceInfo
+{
+ VideoDeviceInfo();
+ ~VideoDeviceInfo();
+
+ int vertex_processing;
+ int depth_buffer_bits;
+ int adapter_index;
+ int device_index;
+ DWORD device_type;
+ DWORD depth_stencil_format;
+ DWORD back_buffer_format;
+ DWORD multisample_type;
+ DWORD multisample_qual;
+ char adapter_desc[128];
+ char device_desc[128];
+};
+
+// +--------------------------------------------------------------------+
+
+class VideoSettings
+{
+public:
+ enum VertexProcessing {
+ VTX_SOFTWARE,
+ VTX_MIXED,
+ VTX_HARDWARE,
+ VTX_PURE
+ };
+
+ VideoSettings();
+ ~VideoSettings();
+
+ // accessor methods
+
+ bool IsWindowed() const;
+ bool UseEffects() const;
+ int GetWidth() const;
+ int GetHeight() const;
+ int GetDepth() const;
+ int GetPixSize() const;
+ int GetRefreshRate() const;
+
+ const char* GetModeDescription() const;
+
+ int GetVertexProcessing() const;
+ int GetDepthBufferBits() const;
+ int GetAdapterIndex() const;
+ int GetDeviceIndex() const;
+ DWORD GetDeviceType() const;
+ DWORD GetDepthStencilFormat() const;
+ DWORD GetBackBufferFormat() const;
+ const char* GetAdapterDesc() const;
+ const char* GetDeviceDesc() const;
+
+ // properties
+
+ bool is_windowed;
+ bool use_effects;
+ VideoMode fullscreen_mode;
+ VideoMode windowed_mode;
+ int window_width;
+ int window_height;
+ VideoDeviceInfo fullscreen_device;
+ VideoDeviceInfo windowed_device;
+
+ // feature set
+
+ bool shadows;
+ bool bumpmaps;
+ bool specmaps;
+ int max_detail;
+ DWORD enable_vs;
+ DWORD enable_ps;
+ float depth_bias;
+};
+
+#endif VideoSettings_h
+
diff --git a/nGenEx/View.h b/nGenEx/View.h
new file mode 100644
index 0000000..fd1108c
--- /dev/null
+++ b/nGenEx/View.h
@@ -0,0 +1,52 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: View.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract View class
+*/
+
+#ifndef View_h
+#define View_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class Window;
+
+// +--------------------------------------------------------------------+
+
+class View
+{
+ friend class Window;
+
+public:
+ static const char* TYPENAME() { return "View"; }
+
+ View(Window* c) : window(c) { }
+ virtual ~View() { }
+
+ int operator == (const View& that) const { return this == &that; }
+
+ // Operations:
+ virtual void Refresh() { }
+ virtual void OnWindowMove() { }
+ virtual void OnShow() { }
+ virtual void OnHide() { }
+
+ virtual void SetWindow(Window* w) { window = w; OnWindowMove(); }
+ virtual Window* GetWindow() { return window; }
+
+protected:
+ Window* window;
+};
+
+#endif View_h
+
diff --git a/nGenEx/Water.cpp b/nGenEx/Water.cpp
new file mode 100644
index 0000000..41fa2f3
--- /dev/null
+++ b/nGenEx/Water.cpp
@@ -0,0 +1,295 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: Water.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Water surface effect w/ reflection and caustics
+*/
+
+#include "MemDebug.h"
+#include "Water.h"
+#include "Random.h"
+
+// +--------------------------------------------------------------------+
+
+struct WATER_REFRACT
+{
+ // Vrefract = (V + refract * N) * norm
+ float refract;
+ float refractNorm;
+ DWORD diffuse;
+};
+
+struct WATER_SURFACE
+{
+ float height;
+ Vec3 normal;
+};
+
+// +--------------------------------------------------------------------+
+
+#if defined(_X86) && !defined(_WIN64)
+inline int f2i(float flt)
+{
+ volatile int n;
+
+ __asm
+ {
+ fld flt
+ fistp n
+ }
+
+ return n;
+}
+#else
+inline int f2i(float flt)
+{
+ return (int) flt;
+}
+#endif
+
+
+// +--------------------------------------------------------------------+
+
+static WATER_REFRACT RefractionTable[512];
+static bool refractInit = false;
+
+static const int WAVE_SIZE = 256;
+static const DWORD WAVE_MASK = 0xff;
+
+// +--------------------------------------------------------------------+
+
+Water::Water()
+ : size(0), depth(0), scaleTex(1), avgHeight(0),
+ nVertices(0), surface(0), waves(0)
+{
+}
+
+Water::~Water()
+{
+ delete [] surface;
+ delete [] waves;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Water::Init(int n, float s, float d)
+{
+ size = s;
+ depth = d;
+ scaleTex = 1/size;
+
+ // Calculate number of vertices
+ nVertices = n;
+
+ // Create refraction table
+ if (!refractInit) {
+ WATER_REFRACT* refract = &RefractionTable[256];
+
+ for (UINT u = 0; u < 256; u++) {
+ float fCos0 = (float) u / (float) 256.0f;
+ float f0 = acosf(fCos0);
+ float fSin0 = sinf(f0);
+
+ float fSin1 = fSin0 / 1.333f; // water
+ float f1 = asinf(fSin1);
+ float fCos1 = cosf(f1);
+
+ refract[u].refract = fSin0 / fSin1 * fCos1 - fCos0;
+ refract[u].refractNorm = - fSin1 / fSin0;
+ refract[u].diffuse = ((((0xff - u)*(0xff - u)*(0xff - u)) << 8) & 0xff000000);
+
+ RefractionTable[u] = RefractionTable[256];
+ }
+
+ refractInit = true;
+ }
+
+ // Create maps
+ if (surface)
+ delete [] surface;
+
+ surface = new(__FILE__,__LINE__) WATER_SURFACE[n*n];
+ ZeroMemory(surface, n*n * sizeof(WATER_SURFACE));
+
+ if (waves)
+ delete [] waves;
+
+ waves = new(__FILE__,__LINE__) float[WAVE_SIZE*4];
+
+ double f = 1.0 / (double) WAVE_SIZE;
+ for (int i = 0; i < WAVE_SIZE; i++) {
+ double s0 = sin(2*PI*i*f);
+ double s1 = sin(4*PI*i*f);
+ double s2 = sin(6*PI*i*f);
+ double s3 = sin(8*PI*i*f);
+
+ waves[0*WAVE_SIZE + i] = (float) (1.8 * s0*s0 - 0.9);
+ waves[1*WAVE_SIZE + i] = (float) (1.6 * s1*s1 - 0.8);
+ waves[2*WAVE_SIZE + i] = (float) (0.4 * s2);
+ waves[3*WAVE_SIZE + i] = (float) (0.8 * s3*s3 - 0.4);
+ }
+
+ for (i = 0; i < 4; i++) {
+ offsets[i] = (float) Random(0, WAVE_SIZE);
+ }
+
+ offsets[4] = 12.45f;
+ offsets[5] = 14.23f;
+ offsets[6] = 16.72f;
+ offsets[7] = 20.31f;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Water::CalcWaves(double seconds)
+{
+ int i, n[4];
+ UINT SIZE = nVertices;
+ UINT STEP = WAVE_SIZE / (SIZE-1);
+ UINT STEP2 = STEP/2;
+ UINT AREA = SIZE * SIZE;
+ UINT x, y;
+
+ for (i = 0; i < 4; i++) {
+ n[i] = (int) offsets[i];
+ }
+
+ WATER_SURFACE* pSurf = surface;
+
+ // compute heights
+ for (y = 0; y < SIZE; y++) {
+ for (x = 0; x < SIZE; x++) {
+ float h = 0;
+ h += waves[ ((n[0] + x*STEP
+ - y*STEP2) & WAVE_MASK) + 0*WAVE_SIZE ];
+ h += waves[ ((n[1] + x*STEP2
+ + y*STEP) & WAVE_MASK) + 1*WAVE_SIZE ];
+ h += waves[ ((n[2] + x*STEP) & WAVE_MASK) + 2*WAVE_SIZE ];
+ h += waves[ ((n[3] + y*STEP) & WAVE_MASK) + 3*WAVE_SIZE ];
+
+ pSurf->height = h * depth;
+ pSurf++;
+ }
+ }
+
+ // compute normals
+ UINT uXN, uX0, uXP;
+ UINT uYN, uY0, uYP;
+
+ uYP = AREA - SIZE;
+ uY0 = 0;
+ uYN = SIZE;
+
+ for (y = 0; y < SIZE; y++) {
+ uXP = SIZE - 1;
+ uX0 = 0;
+ uXN = 1;
+
+ for (x = 0; x < SIZE; x++) {
+ Vec3 vecN;
+ float f;
+
+ f = surface[uXN + uYN].height - surface[uXP + uYP].height; vecN.x = vecN.z = f;
+ f = surface[uX0 + uYN].height - surface[uX0 + uYP].height; vecN.z += f;
+ f = surface[uXP + uYN].height - surface[uXN + uYP].height; vecN.x -= f; vecN.z += f;
+ f = surface[uXN + uY0].height - surface[uXP + uY0].height; vecN.x += f;
+
+ vecN.y = -15.0f * depth;
+ vecN.Normalize();
+
+ surface[uX0 + uY0].normal = vecN * -1.0f;
+
+ uXP = uX0;
+ uX0 = uXN;
+ uXN = (uXN + 1) % SIZE;
+ }
+
+ uYP = uY0;
+ uY0 = uYN;
+ uYN = (uYN + SIZE) % AREA;
+ }
+
+ // update offsets
+ for (i = 0; i < 4; i++) {
+ offsets[i] += (float) (offsets[i+4] * seconds);
+
+ if (offsets[i] > WAVE_SIZE)
+ offsets[i] -= WAVE_SIZE;
+ }
+
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Water::UpdateSurface(Vec3& eyePos, VertexSet* vset)
+{
+ UINT SIZE = nVertices;
+ UINT AREA = SIZE * SIZE;
+ UINT x, y;
+
+ WATER_SURFACE* pSurf = surface;
+ Vec3* pLoc = vset->loc;
+ Vec3* pNorm = vset->nrm;
+ DWORD* pDiff = vset->diffuse;
+ float* pTu = vset->tu;
+ float* pTv = vset->tv;
+
+ float fInc = 1.0f / (float) (SIZE-1);
+ float fx = 0.0f;
+ float fz = 0.0f;
+
+ for (y = 0; y < SIZE; y++) {
+ for (x = 0; x < SIZE; x++) {
+ // update vertex height and normal
+ pLoc->y += pSurf->height;
+ *pNorm = pSurf->normal;
+
+ /*
+ // Update texture coords and diffuse based upon refraction
+ Vec3 vec = eyePos - *pLoc;
+ vec.Normalize();
+
+ WATER_REFRACT *pRefract;
+ pRefract = RefractionTable + 256 + f2i(vec.dot(*pNorm) * 255.0f);
+
+ *pDiff = pRefract->diffuse;
+
+ // compute apparent displacement
+ Vec3 vecD = (pSurf->normal * pRefract->refract + vec) * pRefract->refractNorm;
+ Vec3 vecP = *pLoc;
+ vecP.y -= depth;
+
+ // perturb texture coords
+ float fB = vecD * vecP * 2.0f;
+ float fD = fB * fB - depth;
+ float fScale = (-fB + sqrtf(fD)) * 0.5f;
+
+ *pTu = vecD.x * fScale + fx;
+ *pTv = vecD.z * fScale + fz;
+ */
+
+ fx += fInc;
+ pSurf++;
+ pLoc++;
+ pNorm++;
+ pDiff++;
+ pTu++;
+ pTv++;
+ }
+
+ fx = 0.0f;
+ fz += fInc;
+ }
+}
+
+
diff --git a/nGenEx/Water.h b/nGenEx/Water.h
new file mode 100644
index 0000000..2f88db0
--- /dev/null
+++ b/nGenEx/Water.h
@@ -0,0 +1,54 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: Water.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Water surface effect w/ reflection and refraction
+*/
+
+#ifndef Water_h
+#define Water_h
+
+#include "Geometry.h"
+#include "Polygon.h"
+#include "Color.h"
+
+// +--------------------------------------------------------------------+
+
+struct WATER_SURFACE;
+
+// +--------------------------------------------------------------------+
+
+class Water
+{
+public:
+ Water();
+ virtual ~Water();
+
+ virtual void Init(int nVerts, float size, float depth);
+ virtual void CalcWaves(double seconds);
+ virtual void UpdateSurface(Vec3& eyePos, VertexSet* vset);
+
+protected:
+ float size;
+ float depth;
+ float scaleTex;
+ float avgHeight;
+
+ DWORD nVertices;
+
+ WATER_SURFACE* surface;
+ float* waves;
+ float offsets[16];
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Water_h
+
diff --git a/nGenEx/Wave.h b/nGenEx/Wave.h
new file mode 100644
index 0000000..9b630ca
--- /dev/null
+++ b/nGenEx/Wave.h
@@ -0,0 +1,52 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Wave.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef Wave_h
+#define Wave_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+struct WAVE_HEADER
+{
+ DWORD RIFF;
+ DWORD file_len;
+ DWORD WAVE;
+};
+
+struct WAVE_FMT
+{
+ DWORD FMT;
+ DWORD chunk_size;
+ WORD wFormatTag;
+ WORD nChannels;
+ DWORD nSamplesPerSec;
+ DWORD nAvgBytesPerSec;
+ WORD nBlockAlign;
+ WORD wBitsPerSample;
+};
+
+struct WAVE_FACT
+{
+ DWORD FACT;
+ DWORD chunk_size;
+};
+
+struct WAVE_DATA
+{
+ DWORD DATA;
+ DWORD chunk_size;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Wave_h
+
diff --git a/nGenEx/WebBrowser.cpp b/nGenEx/WebBrowser.cpp
new file mode 100644
index 0000000..a5f5b94
--- /dev/null
+++ b/nGenEx/WebBrowser.cpp
@@ -0,0 +1,133 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: WebBrowser.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Helper class to find and launch user's default web browser
+*/
+
+#include "MemDebug.h"
+#include "WebBrowser.h"
+
+// +--------------------------------------------------------------------+
+
+WebBrowser::WebBrowser()
+{
+ FindDefaultBrowser();
+ FindOpenCommand();
+}
+
+WebBrowser::~WebBrowser()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+
+void
+WebBrowser::OpenURL(const char* url)
+{
+ if (url) {
+ char cmdline[256];
+
+ if (command.contains("%1")) {
+ strcpy(cmdline, command.replace("%1", url).data());
+ }
+ else {
+ strcpy(cmdline, Text(command + " " + url).data());
+ }
+
+ STARTUPINFO s;
+ ZeroMemory(&s, sizeof(s));
+ s.cb = sizeof(s);
+
+ PROCESS_INFORMATION pi;
+ ZeroMemory(&pi, sizeof(pi));
+
+ if (CreateProcess(NULL, cmdline, 0, 0, 0, 0, 0, 0, &s, &pi)) {
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+ else {
+ ::Print("Unable to launch web browser for url '%s'\n", url);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WebBrowser::FindDefaultBrowser()
+{
+ HKEY hkey;
+ char value[256] = "";
+ DWORD dwSize;
+
+ ZeroMemory(value, 256);
+
+ if (RegOpenKeyEx(HKEY_CLASSES_ROOT,
+ ".html",
+ 0,
+ KEY_READ,
+ &hkey) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ RegQueryValueEx(hkey,
+ "",
+ NULL,
+ NULL,
+ (LPBYTE) value,
+ &dwSize);
+
+ RegCloseKey(hkey);
+
+ if (dwSize > 0) {
+ ::Print("Default Web Browser: %s\n", value);
+ browser = value;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WebBrowser::FindOpenCommand()
+{
+ HKEY hkey;
+ char value[256] = "";
+ DWORD dwSize;
+
+ ZeroMemory(value, 256);
+
+ if (RegOpenKeyEx(HKEY_CLASSES_ROOT,
+ browser + "\\shell\\open\\command",
+ 0,
+ KEY_READ,
+ &hkey) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ RegQueryValueEx(hkey,
+ "",
+ NULL,
+ NULL,
+ (LPBYTE) value,
+ &dwSize);
+
+ RegCloseKey(hkey);
+
+ if (dwSize > 0) {
+ ::Print("Browser Shell Open Command: %s\n", value);
+ command = value;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+
diff --git a/nGenEx/WebBrowser.h b/nGenEx/WebBrowser.h
new file mode 100644
index 0000000..b8a27e1
--- /dev/null
+++ b/nGenEx/WebBrowser.h
@@ -0,0 +1,46 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: WebBrowser.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Helper class to find and launch user's default web browser
+*/
+
+#ifndef WebBrowser_h
+#define WebBrowser_h
+
+#include "Types.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class WebBrowser
+{
+public:
+ static const char* TYPENAME() { return "WebBrowser"; }
+
+ WebBrowser();
+ virtual ~WebBrowser();
+
+ virtual void OpenURL(const char* url);
+
+protected:
+ void FindDefaultBrowser();
+ void FindOpenCommand();
+
+ Text browser;
+ Text command;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif WebBrowser_h
+
+
diff --git a/nGenEx/Window.cpp b/nGenEx/Window.cpp
new file mode 100644
index 0000000..a69e00d
--- /dev/null
+++ b/nGenEx/Window.cpp
@@ -0,0 +1,936 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Window.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Window class
+*/
+
+#include "MemDebug.h"
+#include "Window.h"
+#include "Bitmap.h"
+#include "Color.h"
+#include "Fix.h"
+#include "Font.h"
+#include "Polygon.h"
+#include "Screen.h"
+#include "Video.h"
+#include "View.h"
+
+static VertexSet vset4(4);
+
+// +--------------------------------------------------------------------+
+
+Window::Window(Screen* s, int ax, int ay, int aw, int ah)
+ : screen(s), rect(ax, ay, aw, ah), shown(true), font(0)
+{ }
+
+// +--------------------------------------------------------------------+
+
+Window::~Window()
+{
+ view_list.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Window::AddView(View* v)
+{
+ if (!v) return false;
+
+ if (!view_list.contains(v))
+ view_list.append(v);
+
+ return true;
+}
+
+bool
+Window::DelView(View* v)
+{
+ if (!v) return false;
+
+ return view_list.remove(v) == v;
+}
+
+void
+Window::MoveTo(const Rect& r)
+{
+ if (rect.x == r.x &&
+ rect.y == r.y &&
+ rect.w == r.w &&
+ rect.h == r.h)
+ return;
+
+ rect = r;
+
+ ListIter<View> v = view_list;
+ while (++v)
+ v->OnWindowMove();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Window::Paint()
+{
+ ListIter<View> v = view_list;
+ while (++v)
+ v->Refresh();
+}
+
+// +--------------------------------------------------------------------+
+
+static inline void swap(int& a, int& b) { int tmp=a; a=b; b=tmp; }
+static inline void sort(int& a, int& b) { if (a>b) swap(a,b); }
+static inline void swap(double& a, double& b) { double tmp=a; a=b; b=tmp; }
+static inline void sort(double& a, double& b) { if (a>b) swap(a,b); }
+
+Rect
+Window::ClipRect(const Rect& r)
+{
+ Rect clip_rect = r;
+
+ clip_rect.x += rect.x;
+ clip_rect.y += rect.y;
+
+ if (clip_rect.x < rect.x) {
+ clip_rect.w -= rect.x - clip_rect.x;
+ clip_rect.x = rect.x;
+ }
+
+ if (clip_rect.y < rect.y) {
+ clip_rect.h -= rect.y - clip_rect.y;
+ clip_rect.y = rect.y;
+ }
+
+ if (clip_rect.x + clip_rect.w > rect.x + rect.w)
+ clip_rect.w = rect.x + rect.w - clip_rect.x;
+
+ if (clip_rect.y + clip_rect.h > rect.y + rect.h)
+ clip_rect.h = rect.y + rect.h - clip_rect.y;
+
+ return clip_rect;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Window::ClipLine(int& x1, int& y1, int& x2, int& y2)
+{
+ // vertical lines:
+ if (x1==x2) {
+clip_vertical:
+ sort(y1,y2);
+ if (x1 < 0 || x1 >= rect.w) return false;
+ if (y1 < 0) y1 = 0;
+ if (y2 >= rect.h) y2 = rect.h;
+ return true;
+ }
+
+ // horizontal lines:
+ if (y1==y2) {
+clip_horizontal:
+ sort(x1,x2);
+ if (y1 < 0 || y1 >= rect.h) return false;
+ if (x1 < 0) x1 = 0;
+ if (x2 > rect.w) x2 = rect.w;
+ return true;
+ }
+
+ // general lines:
+
+ // sort left to right:
+ if (x1 > x2) {
+ swap(x1,x2);
+ swap(y1,y2);
+ }
+
+ double m = (double)(y2-y1) / (double)(x2-x1);
+ double b = (double) y1 - (m * x1);
+
+ // clip:
+ if (x1 < 0) { x1 = 0; y1 = (int) b; }
+ if (x1 >= rect.w) return false;
+ if (x2 < 0) return false;
+ if (x2 > rect.w-1) { x2 = rect.w-1; y2 = (int) (m * x2 + b); }
+
+ if (y1 < 0 && y2 < 0) return false;
+ if (y1 >= rect.h && y2 >= rect.h) return false;
+
+ if (y1 < 0) { y1 = 0; x1 = (int) (-b/m); }
+ if (y1 >= rect.h) { y1 = rect.h-1; x1 = (int) ((y1-b)/m); }
+ if (y2 < 0) { y2 = 0; x2 = (int) (-b/m); }
+ if (y2 >= rect.h) { y2 = rect.h-1; x2 = (int) ((y2-b)/m); }
+
+ if (x1 == x2)
+ goto clip_vertical;
+
+ if (y1 == y2)
+ goto clip_horizontal;
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Window::ClipLine(double& x1, double& y1, double& x2, double& y2)
+{
+ // vertical lines:
+ if (x1==x2) {
+clip_vertical:
+ sort(y1,y2);
+ if (x1 < 0 || x1 >= rect.w) return false;
+ if (y1 < 0) y1 = 0;
+ if (y2 >= rect.h) y2 = rect.h;
+ return true;
+ }
+
+ // horizontal lines:
+ if (y1==y2) {
+clip_horizontal:
+ sort(x1,x2);
+ if (y1 < 0 || y1 >= rect.h) return false;
+ if (x1 < 0) x1 = 0;
+ if (x2 > rect.w) x2 = rect.w;
+ return true;
+ }
+
+ // general lines:
+
+ // sort left to right:
+ if (x1 > x2) {
+ swap(x1,x2);
+ swap(y1,y2);
+ }
+
+ double m = (double)(y2-y1) / (double)(x2-x1);
+ double b = (double) y1 - (m * x1);
+
+ // clip:
+ if (x1 < 0) { x1 = 0; y1 = b; }
+ if (x1 >= rect.w) return false;
+ if (x2 < 0) return false;
+ if (x2 > rect.w-1) { x2 = rect.w-1; y2 = (m * x2 + b); }
+
+ if (y1 < 0 && y2 < 0) return false;
+ if (y1 >= rect.h && y2 >= rect.h) return false;
+
+ if (y1 < 0) { y1 = 0; x1 = (-b/m); }
+ if (y1 >= rect.h) { y1 = rect.h-1; x1 = ((y1-b)/m); }
+ if (y2 < 0) { y2 = 0; x2 = (-b/m); }
+ if (y2 >= rect.h) { y2 = rect.h-1; x2 = ((y2-b)/m); }
+
+ if (x1 == x2)
+ goto clip_vertical;
+
+ if (y1 == y2)
+ goto clip_horizontal;
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Window::DrawLine(int x1, int y1, int x2, int y2, Color color, int blend)
+{
+ if (!screen || !screen->GetVideo()) return;
+
+ if (ClipLine(x1,y1,x2,y2)) {
+ float points[4];
+
+ points[0] = (float) (rect.x + x1);
+ points[1] = (float) (rect.y + y1);
+ points[2] = (float) (rect.x + x2);
+ points[3] = (float) (rect.y + y2);
+
+ Video* video = screen->GetVideo();
+ video->DrawScreenLines(1, points, color, blend);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Window::DrawRect(int x1, int y1, int x2, int y2, Color color, int blend)
+{
+ if (!screen || !screen->GetVideo()) return;
+
+ sort(x1,x2);
+ sort(y1,y2);
+
+ if (x1 > rect.w || x2 < 0 || y1 > rect.h || y2 < 0)
+ return;
+
+ float points[16];
+
+ points[ 0] = (float) (rect.x + x1);
+ points[ 1] = (float) (rect.y + y1);
+ points[ 2] = (float) (rect.x + x2);
+ points[ 3] = (float) (rect.y + y1);
+
+ points[ 4] = (float) (rect.x + x2);
+ points[ 5] = (float) (rect.y + y1);
+ points[ 6] = (float) (rect.x + x2);
+ points[ 7] = (float) (rect.y + y2);
+
+ points[ 8] = (float) (rect.x + x2);
+ points[ 9] = (float) (rect.y + y2);
+ points[10] = (float) (rect.x + x1);
+ points[11] = (float) (rect.y + y2);
+
+ points[12] = (float) (rect.x + x1);
+ points[13] = (float) (rect.y + y2);
+ points[14] = (float) (rect.x + x1);
+ points[15] = (float) (rect.y + y1);
+
+ Video* video = screen->GetVideo();
+ video->DrawScreenLines(4, points, color, blend);
+}
+
+void
+Window::DrawRect(const Rect& r, Color color, int blend)
+{
+ DrawRect(r.x, r.y, r.x+r.w, r.y+r.h, color, blend);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Window::FillRect(int x1, int y1, int x2, int y2, Color color, int blend)
+{
+ if (!screen || !screen->GetVideo()) return;
+
+ sort(x1,x2);
+ sort(y1,y2);
+
+ if (x1 > rect.w || x2 < 0 || y1 > rect.h || y2 < 0)
+ return;
+
+ vset4.space = VertexSet::SCREEN_SPACE;
+ for (int i = 0; i < 4; i++) {
+ vset4.diffuse[i] = color.Value();
+ }
+
+ vset4.s_loc[0].x = (float) (rect.x + x1) - 0.5f;
+ vset4.s_loc[0].y = (float) (rect.y + y1) - 0.5f;
+ vset4.s_loc[0].z = 0.0f;
+ vset4.rw[0] = 1.0f;
+ vset4.tu[0] = 0.0f;
+ vset4.tv[0] = 0.0f;
+
+ vset4.s_loc[1].x = (float) (rect.x + x2) - 0.5f;
+ vset4.s_loc[1].y = (float) (rect.y + y1) - 0.5f;
+ vset4.s_loc[1].z = 0.0f;
+ vset4.rw[1] = 1.0f;
+ vset4.tu[1] = 1.0f;
+ vset4.tv[1] = 0.0f;
+
+ vset4.s_loc[2].x = (float) (rect.x + x2) - 0.5f;
+ vset4.s_loc[2].y = (float) (rect.y + y2) - 0.5f;
+ vset4.s_loc[2].z = 0.0f;
+ vset4.rw[2] = 1.0f;
+ vset4.tu[2] = 1.0f;
+ vset4.tv[2] = 1.0f;
+
+ vset4.s_loc[3].x = (float) (rect.x + x1) - 0.5f;
+ vset4.s_loc[3].y = (float) (rect.y + y2) - 0.5f;
+ vset4.s_loc[3].z = 0.0f;
+ vset4.rw[3] = 1.0f;
+ vset4.tu[3] = 0.0f;
+ vset4.tv[3] = 1.0f;
+
+ Poly poly(0);
+ poly.nverts = 4;
+ poly.vertex_set = &vset4;
+ poly.verts[0] = 0;
+ poly.verts[1] = 1;
+ poly.verts[2] = 2;
+ poly.verts[3] = 3;
+
+ Video* video = screen->GetVideo();
+ video->DrawScreenPolys(1, &poly, blend);
+}
+
+void
+Window::FillRect(const Rect& r, Color color, int blend)
+{
+ FillRect(r.x, r.y, r.x+r.w, r.y+r.h, color, blend);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Window::DrawLines(int nPts, POINT* pts, Color color, int blend)
+{
+ if (nPts < 2 || nPts > 16)
+ return;
+
+ if (!screen || !screen->GetVideo())
+ return;
+
+ float f[64];
+ int n = 0;
+
+ for (int i = 0; i < nPts-1; i++) {
+ f[n++] = (float) rect.x + pts[i].x;
+ f[n++] = (float) rect.y + pts[i].y;
+ f[n++] = (float) rect.x + pts[i+1].x;
+ f[n++] = (float) rect.y + pts[i+1].y;
+ }
+
+ Video* video = screen->GetVideo();
+ video->DrawScreenLines(nPts-1, f, color, blend);
+}
+
+void
+Window::DrawPoly(int nPts, POINT* pts, Color color, int blend)
+{
+ if (nPts < 3 || nPts > 8)
+ return;
+
+ if (!screen || !screen->GetVideo())
+ return;
+
+ float f[32];
+ int n = 0;
+
+ for (int i = 0; i < nPts-1; i++) {
+ f[n++] = (float) rect.x + pts[i].x;
+ f[n++] = (float) rect.y + pts[i].y;
+ f[n++] = (float) rect.x + pts[i+1].x;
+ f[n++] = (float) rect.y + pts[i+1].y;
+ }
+
+ f[n++] = (float) rect.x + pts[nPts-1].x;
+ f[n++] = (float) rect.y + pts[nPts-1].y;
+ f[n++] = (float) rect.x + pts[0].x;
+ f[n++] = (float) rect.y + pts[0].y;
+
+ Video* video = screen->GetVideo();
+ video->DrawScreenLines(nPts, f, color, blend);
+}
+
+void
+Window::FillPoly(int nPts, POINT* pts, Color color, int blend)
+{
+ if (nPts < 3 || nPts > 4)
+ return;
+
+ if (!screen || !screen->GetVideo())
+ return;
+
+ vset4.space = VertexSet::SCREEN_SPACE;
+ for (int i = 0; i < nPts; i++) {
+ vset4.diffuse[i] = color.Value();
+ vset4.s_loc[i].x = (float) (rect.x + pts[i].x) - 0.5f;
+ vset4.s_loc[i].y = (float) (rect.y + pts[i].y) - 0.5f;
+ vset4.s_loc[i].z = 0.0f;
+ vset4.rw[i] = 1.0f;
+ vset4.tu[i] = 0.0f;
+ vset4.tv[i] = 0.0f;
+ }
+
+ Poly poly(0);
+ poly.nverts = nPts;
+ poly.vertex_set = &vset4;
+ poly.verts[0] = 0;
+ poly.verts[1] = 1;
+ poly.verts[2] = 2;
+ poly.verts[3] = 3;
+
+ Video* video = screen->GetVideo();
+ video->DrawScreenPolys(1, &poly, blend);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Window::DrawBitmap(int x1, int y1, int x2, int y2, Bitmap* img, int blend)
+{
+ Rect clip_rect;
+ clip_rect.w = rect.w;
+ clip_rect.h = rect.h;
+
+ ClipBitmap(x1,y1,x2,y2,img,Color::White,blend,clip_rect);
+}
+
+void
+Window::FadeBitmap(int x1, int y1, int x2, int y2, Bitmap* img, Color c, int blend)
+{
+ Rect clip_rect;
+ clip_rect.w = rect.w;
+ clip_rect.h = rect.h;
+
+ ClipBitmap(x1,y1,x2,y2,img,c,blend,clip_rect);
+}
+
+void
+Window::ClipBitmap(int x1, int y1, int x2, int y2, Bitmap* img, Color c, int blend, const Rect& clip_rect)
+{
+ if (!screen || !screen->GetVideo() || !img) return;
+
+ Rect clip = clip_rect;
+
+ // clip the clip rect to the window rect:
+ if (clip.x < 0) {
+ clip.w -= clip.x;
+ clip.x = 0;
+ }
+
+ if (clip.x + clip.w > rect.w) {
+ clip.w -= (clip.x + clip.w - rect.w);
+ }
+
+ if (clip.y < 0) {
+ clip.h -= clip.y;
+ clip.y = 0;
+ }
+
+ if (clip.y + clip.h > rect.h) {
+ clip.h -= (clip.y + clip.h - rect.h);
+ }
+
+ // now clip the bitmap to the validated clip rect:
+ sort(x1,x2);
+ sort(y1,y2);
+
+ if (x1 > clip.x + clip.w || x2 < clip.x || y1 > clip.y + clip.h || y2 < clip.y)
+ return;
+
+ vset4.space = VertexSet::SCREEN_SPACE;
+ for (int i = 0; i < 4; i++) {
+ vset4.diffuse[i] = c.Value();
+ }
+
+ float u1 = 0.0f;
+ float u2 = 1.0f;
+ float v1 = 0.0f;
+ float v2 = 1.0f;
+ float iw = (float) (x2-x1);
+ float ih = (float) (y2-y1);
+ int x3 = clip.x + clip.w;
+ int y3 = clip.y + clip.h;
+
+ if (x1 < clip.x) {
+ u1 = (clip.x - x1) / iw;
+ x1 = clip.x;
+ }
+
+ if (x2 > x3) {
+ u2 = 1.0f - (x2 - x3) / iw;
+ x2 = x3;
+ }
+
+ if (y1 < clip.y) {
+ v1 = (clip.y - y1) / ih;
+ y1 = clip.y;
+ }
+
+ if (y2 > y3) {
+ v2 = 1.0f - (y2 - y3) / ih;
+ y2 = y3;
+ }
+
+ vset4.s_loc[0].x = (float) (rect.x + x1) - 0.5f;
+ vset4.s_loc[0].y = (float) (rect.y + y1) - 0.5f;
+ vset4.s_loc[0].z = 0.0f;
+ vset4.rw[0] = 1.0f;
+ vset4.tu[0] = u1;
+ vset4.tv[0] = v1;
+
+ vset4.s_loc[1].x = (float) (rect.x + x2) - 0.5f;
+ vset4.s_loc[1].y = (float) (rect.y + y1) - 0.5f;
+ vset4.s_loc[1].z = 0.0f;
+ vset4.rw[1] = 1.0f;
+ vset4.tu[1] = u2;
+ vset4.tv[1] = v1;
+
+ vset4.s_loc[2].x = (float) (rect.x + x2) - 0.5f;
+ vset4.s_loc[2].y = (float) (rect.y + y2) - 0.5f;
+ vset4.s_loc[2].z = 0.0f;
+ vset4.rw[2] = 1.0f;
+ vset4.tu[2] = u2;
+ vset4.tv[2] = v2;
+
+ vset4.s_loc[3].x = (float) (rect.x + x1) - 0.5f;
+ vset4.s_loc[3].y = (float) (rect.y + y2) - 0.5f;
+ vset4.s_loc[3].z = 0.0f;
+ vset4.rw[3] = 1.0f;
+ vset4.tu[3] = u1;
+ vset4.tv[3] = v2;
+
+ Material mtl;
+ mtl.tex_diffuse = img;
+
+ Poly poly(0);
+ poly.nverts = 4;
+ poly.vertex_set = &vset4;
+ poly.material = &mtl;
+ poly.verts[0] = 0;
+ poly.verts[1] = 1;
+ poly.verts[2] = 2;
+ poly.verts[3] = 3;
+
+ Video* video = screen->GetVideo();
+
+ video->SetRenderState(Video::TEXTURE_WRAP, 0);
+ video->DrawScreenPolys(1, &poly, blend);
+ video->SetRenderState(Video::TEXTURE_WRAP, 1);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Window::TileBitmap(int x1, int y1, int x2, int y2, Bitmap* img, int blend)
+{
+ if (!screen || !screen->GetVideo()) return;
+ if (!img || !img->Width() || !img->Height()) return;
+
+ vset4.space = VertexSet::SCREEN_SPACE;
+ for (int i = 0; i < 4; i++) {
+ vset4.diffuse[i] = Color::White.Value();
+ }
+
+ float xscale = (float) rect.w / (float) img->Width();
+ float yscale = (float) rect.h / (float) img->Height();
+
+ vset4.s_loc[0].x = (float) (rect.x + x1) - 0.5f;
+ vset4.s_loc[0].y = (float) (rect.y + y1) - 0.5f;
+ vset4.s_loc[0].z = 0.0f;
+ vset4.rw[0] = 1.0f;
+ vset4.tu[0] = 0.0f;
+ vset4.tv[0] = 0.0f;
+
+ vset4.s_loc[1].x = (float) (rect.x + x2) - 0.5f;
+ vset4.s_loc[1].y = (float) (rect.y + y1) - 0.5f;
+ vset4.s_loc[1].z = 0.0f;
+ vset4.rw[1] = 1.0f;
+ vset4.tu[1] = xscale;
+ vset4.tv[1] = 0.0f;
+
+ vset4.s_loc[2].x = (float) (rect.x + x2) - 0.5f;
+ vset4.s_loc[2].y = (float) (rect.y + y2) - 0.5f;
+ vset4.s_loc[2].z = 0.0f;
+ vset4.rw[2] = 1.0f;
+ vset4.tu[2] = xscale;
+ vset4.tv[2] = yscale;
+
+ vset4.s_loc[3].x = (float) (rect.x + x1) - 0.5f;
+ vset4.s_loc[3].y = (float) (rect.y + y2) - 0.5f;
+ vset4.s_loc[3].z = 0.0f;
+ vset4.rw[3] = 1.0f;
+ vset4.tu[3] = 0.0f;
+ vset4.tv[3] = yscale;
+
+ Material mtl;
+ mtl.tex_diffuse = img;
+
+ Poly poly(0);
+ poly.nverts = 4;
+ poly.vertex_set = &vset4;
+ poly.material = &mtl;
+ poly.verts[0] = 0;
+ poly.verts[1] = 1;
+ poly.verts[2] = 2;
+ poly.verts[3] = 3;
+
+ Video* video = screen->GetVideo();
+ video->DrawScreenPolys(1, &poly, blend);
+}
+
+// +--------------------------------------------------------------------+
+
+static float ellipse_pts[256];
+
+void
+Window::DrawEllipse(int x1, int y1, int x2, int y2, Color color, int blend)
+{
+ Video* video = screen->GetVideo();
+
+ if (!video)
+ return;
+
+ sort(x1,x2);
+ sort(y1,y2);
+
+ if (x1 > rect.w || x2 < 0 || y1 > rect.h || y2 < 0)
+ return;
+
+ double w2 = (x2-x1)/2.0;
+ double h2 = (y2-y1)/2.0;
+ double cx = rect.x + x1 + w2;
+ double cy = rect.y + y1 + h2;
+ double r = w2;
+ int ns = 4;
+ int np = 0;
+
+ if (h2 > r)
+ r = h2;
+
+ if (r > 2*ns)
+ ns = (int) (r/2);
+
+ if (ns > 64)
+ ns = 64;
+
+ double theta = 0;
+ double dt = (PI/2) / ns;
+
+ // quadrant 1 (lower right):
+ if (cx < (rect.x+rect.w) && cy < (rect.y + rect.h)) {
+ theta = 0;
+ np = 0;
+
+ for (int i = 0; i < ns; i++) {
+ double ex1 = x1 + w2 + cos(theta) * w2;
+ double ey1 = y1 + h2 + sin(theta) * h2;
+
+ theta += dt;
+
+ double ex2 = x1 + w2 + cos(theta) * w2;
+ double ey2 = y1 + h2 + sin(theta) * h2;
+
+ if (ClipLine(ex1, ey1, ex2, ey2)) {
+ ellipse_pts[np++] = (float) (rect.x + ex1);
+ ellipse_pts[np++] = (float) (rect.y + ey1);
+ ellipse_pts[np++] = (float) (rect.x + ex2);
+ ellipse_pts[np++] = (float) (rect.y + ey2);
+ }
+ }
+
+ video->DrawScreenLines(np/4, ellipse_pts, color, blend);
+ }
+
+ // quadrant 2 (lower left):
+ if (cx > rect.x && cy < (rect.y + rect.h)) {
+ theta = 90*DEGREES;
+ np = 0;
+
+ for (int i = 0; i < ns; i++) {
+ double ex1 = x1 + w2 + cos(theta) * w2;
+ double ey1 = y1 + h2 + sin(theta) * h2;
+
+ theta += dt;
+
+ double ex2 = x1 + w2 + cos(theta) * w2;
+ double ey2 = y1 + h2 + sin(theta) * h2;
+
+ if (ClipLine(ex1, ey1, ex2, ey2)) {
+ ellipse_pts[np++] = (float) (rect.x + ex1);
+ ellipse_pts[np++] = (float) (rect.y + ey1);
+ ellipse_pts[np++] = (float) (rect.x + ex2);
+ ellipse_pts[np++] = (float) (rect.y + ey2);
+ }
+ }
+
+ video->DrawScreenLines(np/4, ellipse_pts, color, blend);
+ }
+
+ // quadrant 3 (upper left):
+ if (cx > rect.x && cy > rect.y) {
+ theta = 180*DEGREES;
+ np = 0;
+
+ for (int i = 0; i < ns; i++) {
+ double ex1 = x1 + w2 + cos(theta) * w2;
+ double ey1 = y1 + h2 + sin(theta) * h2;
+
+ theta += dt;
+
+ double ex2 = x1 + w2 + cos(theta) * w2;
+ double ey2 = y1 + h2 + sin(theta) * h2;
+
+ if (ClipLine(ex1, ey1, ex2, ey2)) {
+ ellipse_pts[np++] = (float) (rect.x + ex1);
+ ellipse_pts[np++] = (float) (rect.y + ey1);
+ ellipse_pts[np++] = (float) (rect.x + ex2);
+ ellipse_pts[np++] = (float) (rect.y + ey2);
+ }
+ }
+
+ video->DrawScreenLines(np/4, ellipse_pts, color, blend);
+ }
+
+ // quadrant 4 (upper right):
+ if (cx < (rect.x+rect.w) && cy > rect.y) {
+ theta = 270*DEGREES;
+ np = 0;
+
+ for (int i = 0; i < ns; i++) {
+ double ex1 = x1 + w2 + cos(theta) * w2;
+ double ey1 = y1 + h2 + sin(theta) * h2;
+
+ theta += dt;
+
+ double ex2 = x1 + w2 + cos(theta) * w2;
+ double ey2 = y1 + h2 + sin(theta) * h2;
+
+ if (ClipLine(ex1, ey1, ex2, ey2)) {
+ ellipse_pts[np++] = (float) (rect.x + ex1);
+ ellipse_pts[np++] = (float) (rect.y + ey1);
+ ellipse_pts[np++] = (float) (rect.x + ex2);
+ ellipse_pts[np++] = (float) (rect.y + ey2);
+ }
+ }
+
+ video->DrawScreenLines(np/4, ellipse_pts, color, blend);
+ }
+
+}
+
+void
+Window::FillEllipse(int x1, int y1, int x2, int y2, Color color, int blend)
+{
+ Video* video = screen->GetVideo();
+
+ if (!video)
+ return;
+
+ sort(x1,x2);
+ sort(y1,y2);
+
+ if (x1 > rect.w || x2 < 0 || y1 > rect.h || y2 < 0)
+ return;
+
+ double w2 = (x2-x1)/2.0;
+ double h2 = (y2-y1)/2.0;
+ double cx = x1 + w2;
+ double cy = y1 + h2;
+ double r = w2;
+ int ns = 4;
+ int np = 0;
+
+ if (h2 > r)
+ r = h2;
+
+ if (r > 2*ns)
+ ns = (int) (r/2);
+
+ if (ns > 64)
+ ns = 64;
+
+ double theta = -PI / 2;
+ double dt = PI / ns;
+
+ for (int i = 0; i < ns; i++) {
+ double ex1 = cos(theta) * w2;
+ double ey1 = sin(theta) * h2;
+
+ theta += dt;
+
+ double ex2 = cos(theta) * w2;
+ double ey2 = sin(theta) * h2;
+
+ POINT pts[4];
+
+ pts[0].x = (int) (cx - ex1);
+ pts[0].y = (int) (cy + ey1);
+
+ pts[1].x = (int) (cx + ex1);
+ pts[1].y = (int) (cy + ey1);
+
+ pts[2].x = (int) (cx + ex2);
+ pts[2].y = (int) (cy + ey2);
+
+ pts[3].x = (int) (cx - ex2);
+ pts[3].y = (int) (cy + ey2);
+
+ if (pts[0].x > rect.w && pts[3].x > rect.w)
+ continue;
+
+ if (pts[1].x < 0 && pts[2].x < 0)
+ continue;
+
+ if (pts[0].y > rect.h)
+ return;
+
+ if (pts[2].y < 0)
+ continue;
+
+ if (pts[0].x < 0) pts[0].x = 0;
+ if (pts[3].x < 0) pts[3].x = 0;
+ if (pts[1].x > rect.w) pts[1].x = rect.w;
+ if (pts[2].x > rect.w) pts[2].x = rect.w;
+
+ if (pts[0].y < 0) pts[0].y = 0;
+ if (pts[1].y < 0) pts[1].y = 0;
+ if (pts[2].y > rect.h) pts[2].y = rect.h;
+ if (pts[3].y > rect.h) pts[3].y = rect.h;
+
+ FillPoly(4, pts, color, blend);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Window::Print(int x1, int y1, const char* fmt, ...)
+{
+ if (!font || x1<0 || y1<0 || x1>=rect.w || y1>=rect.h || !fmt)
+ return;
+
+ x1 += rect.x;
+ y1 += rect.y;
+
+ char msgbuf[512];
+ vsprintf(msgbuf, fmt, (char *)(&fmt+1));
+ font->DrawString(msgbuf, strlen(msgbuf), x1, y1, rect);
+}
+
+void
+Window::DrawText(const char* txt, int count, Rect& txt_rect, DWORD flags)
+{
+ if (!font)
+ return;
+
+ if (txt && !count)
+ count = strlen(txt);
+
+ // clip the rect:
+ Rect clip_rect = txt_rect;
+
+ if (clip_rect.x < 0) {
+ int dx = -clip_rect.x;
+ clip_rect.x += dx;
+ clip_rect.w -= dx;
+ }
+
+ if (clip_rect.y < 0) {
+ int dy = -clip_rect.y;
+ clip_rect.y += dy;
+ clip_rect.h -= dy;
+ }
+
+ if (clip_rect.w < 1 || clip_rect.h < 1)
+ return;
+
+ if (clip_rect.x + clip_rect.w > rect.w)
+ clip_rect.w = rect.w - clip_rect.x;
+
+ if (clip_rect.y + clip_rect.h > rect.h)
+ clip_rect.h = rect.h - clip_rect.y;
+
+ clip_rect.x += rect.x;
+ clip_rect.y += rect.y;
+
+ if (font && txt && count) {
+ font->DrawText(txt, count, clip_rect, flags);
+ font->SetAlpha(1);
+ }
+
+ // if calc only, update the rectangle:
+ if (flags & DT_CALCRECT) {
+ txt_rect.h = clip_rect.h;
+ txt_rect.w = clip_rect.w;
+ }
+}
+
diff --git a/nGenEx/Window.h b/nGenEx/Window.h
new file mode 100644
index 0000000..ccafd96
--- /dev/null
+++ b/nGenEx/Window.h
@@ -0,0 +1,104 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Window.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Window class (a region of a screen or buffer)
+*/
+
+#ifndef Window_h
+#define Window_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Color;
+class Bitmap;
+class Font;
+class Screen;
+class View;
+
+// +--------------------------------------------------------------------+
+
+class Window
+{
+ friend class Screen;
+
+public:
+ static const char* TYPENAME() { return "Window"; }
+
+ Window(Screen* s, int ax, int ay, int aw, int ah);
+ virtual ~Window();
+
+ int operator == (const Window& that) const { return this == &that; }
+
+ // Screen dimensions:
+ Screen* GetScreen() const { return screen; }
+ const Rect& GetRect() const { return rect; }
+ int X() const { return rect.x; }
+ int Y() const { return rect.y; }
+ int Width() const { return rect.w; }
+ int Height() const { return rect.h; }
+
+ // Operations:
+ virtual void Paint();
+ virtual void Show() { shown = true; }
+ virtual void Hide() { shown = false; }
+ virtual bool IsShown() const { return shown; }
+
+ virtual void MoveTo(const Rect& r);
+
+ virtual bool AddView(View* v);
+ virtual bool DelView(View* v);
+
+ Rect ClipRect(const Rect& r);
+ bool ClipLine(int& x1, int& y1, int& x2, int& y2);
+ bool ClipLine(double& x1, double& y1, double& x2, double& y2);
+
+ void DrawLine(int x1, int y1, int x2, int y2, Color color, int blend=0);
+ void DrawRect(int x1, int y1, int x2, int y2, Color color, int blend=0);
+ void DrawRect(const Rect& r, Color color, int blend=0);
+ void FillRect(int x1, int y1, int x2, int y2, Color color, int blend=0);
+ void FillRect(const Rect& r, Color color, int alpha=0);
+ void DrawBitmap(int x1, int y1, int x2, int y2, Bitmap* img, int blend=0);
+ void FadeBitmap(int x1, int y1, int x2, int y2, Bitmap* img, Color c, int blend);
+ void ClipBitmap(int x1, int y1, int x2, int y2, Bitmap* img, Color c, int blend, const Rect& clip);
+ void TileBitmap(int x1, int y1, int x2, int y2, Bitmap* img, int blend=0);
+ void DrawLines(int nPts, POINT* pts, Color color, int blend=0);
+ void DrawPoly(int nPts, POINT* pts, Color color, int blend=0);
+ void FillPoly(int nPts, POINT* pts, Color color, int blend=0);
+
+ void DrawEllipse(int x1, int y1, int x2, int y2, Color color, int blend=0);
+ void FillEllipse(int x1, int y1, int x2, int y2, Color color, int blend=0);
+
+ // text methods:
+ void SetFont(Font* f) { font = f; }
+ Font* GetFont() const { return font; }
+
+ void Print(int x1, int y1, const char* fmt, ...);
+ void DrawText(const char* txt, int count, Rect& txt_rect, DWORD flags);
+
+protected:
+ // translate screen coords into window relative coords
+ virtual void ScreenToWindow(int& x, int& y) { }
+ virtual void ScreenToWindow(Rect& r) { }
+
+ Rect rect;
+ Screen* screen;
+ bool shown;
+ Font* font;
+
+ List<View> view_list;
+};
+
+#endif Window_h
+
diff --git a/nGenEx/nGenEx.dsp b/nGenEx/nGenEx.dsp
new file mode 100644
index 0000000..5a179f9
--- /dev/null
+++ b/nGenEx/nGenEx.dsp
@@ -0,0 +1,800 @@
+# Microsoft Developer Studio Project File - Name="nGenEx" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=nGenEx - 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 "nGenEx.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 "nGenEx.mak" CFG="nGenEx - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "nGenEx - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "nGenEx - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "nGenEx - 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 "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\parser" /I "..\FoundationEx" /I "..\zlib" /I "..\LibPng" /I "..\Opcode\OpcodeLib" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FR /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
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF "$(CFG)" == "nGenEx - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "nGenEx___Win32_Debug"
+# PROP BASE Intermediate_Dir "nGenEx___Win32_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 "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\parser" /I "..\FoundationEx" /I "..\zlib" /I "..\LibPng" /I "..\Opcode\OpcodeLib" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /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
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF
+
+# Begin Target
+
+# Name "nGenEx - Win32 Release"
+# Name "nGenEx - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\ActiveWindow.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Archive.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\ArrayList.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\AviFile.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Bitmap.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Bmp.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Bolt.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Button.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Camera.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CameraView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Color.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComboBox.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComboList.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ContentBundle.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\D3DXImage.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\DataLoader.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\EditBox.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Encrypt.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\EventDispatch.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\FadeView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Fix.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Font.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\FontMgr.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\FormatUtil.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\FormDef.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\FormWindow.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Game.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Geometry.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Graphic.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ImageBox.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ImgView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Joystick.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Keyboard.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Layout.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Light.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ListBox.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Locale.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MachineInfo.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MCIWave.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\MemDebug.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Menu.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Mouse.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MouseController.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MultiController.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Parser.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ParseUtil.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Particles.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\PCX.CPP
+# End Source File
+# Begin Source File
+
+SOURCE=.\Physical.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\PngImage.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Polygon.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Projector.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Random.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Reader.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Res.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\RichTextBox.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Scene.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Screen.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ScrollWindow.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sha1.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Shadow.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Skin.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Slider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Solid.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sound.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\SoundCard.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\SoundD3D.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sprite.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Term.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\TexCubeDX9.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\TexDX9.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\Text.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Token.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Video.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoDX9.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoDX9Enum.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoDX9VertexBuffer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoFactory.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoSettings.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Water.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\WebBrowser.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Window.cpp
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\ActiveWindow.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Archive.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\ArrayList.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\AviFile.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Bitmap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Bmp.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Bolt.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Button.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Camera.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CameraView.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Color.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComboBox.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComboList.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ContentBundle.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\D3DXImage.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DataLoader.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\Dictionary.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\Dictionary.inl
+# End Source File
+# Begin Source File
+
+SOURCE=.\Director.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\EditBox.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Encrypt.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\EventDispatch.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\EventTarget.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\EventTgt.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Fix.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Font.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FontMgr.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FormatUtil.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FormDef.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FormWindow.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Game.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Geometry.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Graphic.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\IA3D.H
+# End Source File
+# Begin Source File
+
+SOURCE=.\ImageBox.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ImgView.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Joystick.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Keyboard.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Layout.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Light.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\List.inl
+# End Source File
+# Begin Source File
+
+SOURCE=.\ListBox.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Locale.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MachineInfo.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MCIWave.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\MemDebug.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Menu.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MotionController.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Mouse.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MouseController.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MultiController.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Parser.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Particles.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Pcx.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Physical.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Polygon.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Projector.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Random.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Reader.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Res.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resource.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RichTextBox.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Scene.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Screen.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ScrollWindow.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sha1.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Shadow.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Skin.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Slider.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Solid.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sound.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SoundCard.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SoundD3D.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sprite.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Term.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TexCubeDX9.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TexDX9.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\Text.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\ThreadSync.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TimeSnap.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Token.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Types.h
+# End Source File
+# Begin Source File
+
+SOURCE="..\..\Program Files\Microsoft Visual Studio\VC98\Include\SYS\TYPES.H"
+# End Source File
+# Begin Source File
+
+SOURCE=.\Universe.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Video.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoDX9.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoDX9Enum.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoDX9VertexBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoFactory.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoSettings.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\View.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Water.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Wave.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WebBrowser.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Window.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/nGenEx/nGenEx.dsw b/nGenEx/nGenEx.dsw
new file mode 100644
index 0000000..e0f1773
--- /dev/null
+++ b/nGenEx/nGenEx.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "nGenEx"=.\nGenEx.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/nGenEx/nGenEx.vcxproj b/nGenEx/nGenEx.vcxproj
new file mode 100644
index 0000000..7f5f315
--- /dev/null
+++ b/nGenEx/nGenEx.vcxproj
@@ -0,0 +1,310 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Template|Win32">
+ <Configuration>Template</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <SccProjectName />
+ <SccLocalPath />
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Template|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Template|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>.\Debug\</OutDir>
+ <IntDir>.\Debug\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>.\Release\</OutDir>
+ <IntDir>.\Release\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+ <FunctionLevelLinking>false</FunctionLevelLinking>
+ <Optimization>Disabled</Optimization>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <WarningLevel>Level3</WarningLevel>
+ <MinimalRebuild>true</MinimalRebuild>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <AdditionalIncludeDirectories>..\parser;..\FoundationEx;..\zlib;..\LibPng;..\Opcode\OpcodeLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AssemblerListingLocation>.\Debug\</AssemblerListingLocation>
+ <BrowseInformation>true</BrowseInformation>
+ <PrecompiledHeaderOutputFile>.\Debug\nGenEx.pch</PrecompiledHeaderOutputFile>
+ <ObjectFileName>.\Debug\</ObjectFileName>
+ <ProgramDataBaseFileName>.\Debug\</ProgramDataBaseFileName>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ </ClCompile>
+ <ResourceCompile>
+ <Culture>0x0409</Culture>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug\nGenEx.bsc</OutputFile>
+ </Bscmake>
+ <Lib>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug\nGenEx.lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <StringPooling>true</StringPooling>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <Optimization>MaxSpeed</Optimization>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <WarningLevel>Level3</WarningLevel>
+ <AdditionalIncludeDirectories>..\parser;..\FoundationEx;..\zlib;..\LibPng;..\Opcode\OpcodeLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AssemblerListingLocation>.\Release\</AssemblerListingLocation>
+ <BrowseInformation>true</BrowseInformation>
+ <PrecompiledHeaderOutputFile>.\Release\nGenEx.pch</PrecompiledHeaderOutputFile>
+ <ObjectFileName>.\Release\</ObjectFileName>
+ <ProgramDataBaseFileName>.\Release\</ProgramDataBaseFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <Culture>0x0409</Culture>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release\nGenEx.bsc</OutputFile>
+ </Bscmake>
+ <Lib>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release\nGenEx.lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="ActiveWindow.cpp" />
+ <ClCompile Include="Archive.cpp" />
+ <ClCompile Include="..\FoundationEx\ArrayList.cpp" />
+ <ClCompile Include="AviFile.cpp" />
+ <ClCompile Include="Bitmap.cpp" />
+ <ClCompile Include="Bmp.cpp" />
+ <ClCompile Include="Bolt.cpp" />
+ <ClCompile Include="Button.cpp" />
+ <ClCompile Include="Camera.cpp" />
+ <ClCompile Include="CameraView.cpp" />
+ <ClCompile Include="Color.cpp" />
+ <ClCompile Include="ComboBox.cpp" />
+ <ClCompile Include="ComboList.cpp" />
+ <ClCompile Include="ContentBundle.cpp" />
+ <ClCompile Include="D3DXImage.cpp" />
+ <ClCompile Include="DataLoader.cpp" />
+ <ClCompile Include="EditBox.cpp" />
+ <ClCompile Include="Encrypt.cpp" />
+ <ClCompile Include="EventDispatch.cpp" />
+ <ClCompile Include="FadeView.cpp" />
+ <ClCompile Include="Fix.cpp" />
+ <ClCompile Include="Font.cpp" />
+ <ClCompile Include="FontMgr.cpp" />
+ <ClCompile Include="FormatUtil.cpp" />
+ <ClCompile Include="FormDef.cpp" />
+ <ClCompile Include="FormWindow.cpp" />
+ <ClCompile Include="Game.cpp" />
+ <ClCompile Include="Geometry.cpp" />
+ <ClCompile Include="Graphic.cpp" />
+ <ClCompile Include="ImageBox.cpp" />
+ <ClCompile Include="ImgView.cpp" />
+ <ClCompile Include="Joystick.cpp" />
+ <ClCompile Include="Keyboard.cpp" />
+ <ClCompile Include="Layout.cpp" />
+ <ClCompile Include="Light.cpp" />
+ <ClCompile Include="ListBox.cpp" />
+ <ClCompile Include="Locale.cpp" />
+ <ClCompile Include="MachineInfo.cpp" />
+ <ClCompile Include="MCIWave.cpp" />
+ <ClCompile Include="..\FoundationEx\MemDebug.cpp" />
+ <ClCompile Include="Menu.cpp" />
+ <ClCompile Include="Mouse.cpp" />
+ <ClCompile Include="MouseController.cpp" />
+ <ClCompile Include="MultiController.cpp" />
+ <ClCompile Include="..\Parser\Parser.cpp" />
+ <ClCompile Include="ParseUtil.cpp" />
+ <ClCompile Include="Particles.cpp" />
+ <ClCompile Include="PCX.CPP" />
+ <ClCompile Include="Physical.cpp" />
+ <ClCompile Include="PngImage.cpp" />
+ <ClCompile Include="Polygon.cpp" />
+ <ClCompile Include="Projector.cpp" />
+ <ClCompile Include="Random.cpp" />
+ <ClCompile Include="..\Parser\Reader.cpp" />
+ <ClCompile Include="Res.cpp" />
+ <ClCompile Include="RichTextBox.cpp" />
+ <ClCompile Include="Scene.cpp" />
+ <ClCompile Include="Screen.cpp" />
+ <ClCompile Include="ScrollWindow.cpp" />
+ <ClCompile Include="Sha1.cpp" />
+ <ClCompile Include="Shadow.cpp" />
+ <ClCompile Include="Skin.cpp" />
+ <ClCompile Include="Slider.cpp" />
+ <ClCompile Include="Solid.cpp" />
+ <ClCompile Include="Sound.cpp" />
+ <ClCompile Include="SoundCard.cpp" />
+ <ClCompile Include="SoundD3D.cpp" />
+ <ClCompile Include="Sprite.cpp" />
+ <ClCompile Include="..\Parser\Term.cpp" />
+ <ClCompile Include="TexCubeDX9.cpp" />
+ <ClCompile Include="TexDX9.cpp" />
+ <ClCompile Include="..\FoundationEx\Text.cpp" />
+ <ClCompile Include="..\Parser\Token.cpp" />
+ <ClCompile Include="Video.cpp" />
+ <ClCompile Include="VideoDX9.cpp" />
+ <ClCompile Include="VideoDX9Enum.cpp" />
+ <ClCompile Include="VideoDX9VertexBuffer.cpp" />
+ <ClCompile Include="VideoFactory.cpp" />
+ <ClCompile Include="VideoSettings.cpp" />
+ <ClCompile Include="Water.cpp" />
+ <ClCompile Include="WebBrowser.cpp" />
+ <ClCompile Include="Window.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="ActiveWindow.h" />
+ <ClInclude Include="Archive.h" />
+ <ClInclude Include="..\FoundationEx\ArrayList.h" />
+ <ClInclude Include="AviFile.h" />
+ <ClInclude Include="Bitmap.h" />
+ <ClInclude Include="Bmp.h" />
+ <ClInclude Include="Bolt.h" />
+ <ClInclude Include="Button.h" />
+ <ClInclude Include="Camera.h" />
+ <ClInclude Include="CameraView.h" />
+ <ClInclude Include="Color.h" />
+ <ClInclude Include="ComboBox.h" />
+ <ClInclude Include="ComboList.h" />
+ <ClInclude Include="ContentBundle.h" />
+ <ClInclude Include="D3DXImage.h" />
+ <ClInclude Include="DataLoader.h" />
+ <ClInclude Include="..\FoundationEx\Dictionary.h" />
+ <ClInclude Include="Director.h" />
+ <ClInclude Include="EditBox.h" />
+ <ClInclude Include="Encrypt.h" />
+ <ClInclude Include="EventDispatch.h" />
+ <ClInclude Include="EventTarget.h" />
+ <ClInclude Include="EventTgt.h" />
+ <ClInclude Include="Fix.h" />
+ <ClInclude Include="Font.h" />
+ <ClInclude Include="FontMgr.h" />
+ <ClInclude Include="FormatUtil.h" />
+ <ClInclude Include="FormDef.h" />
+ <ClInclude Include="FormWindow.h" />
+ <ClInclude Include="Game.h" />
+ <ClInclude Include="Geometry.h" />
+ <ClInclude Include="Graphic.h" />
+ <ClInclude Include="IA3D.H" />
+ <ClInclude Include="ImageBox.h" />
+ <ClInclude Include="ImgView.h" />
+ <ClInclude Include="Joystick.h" />
+ <ClInclude Include="Keyboard.h" />
+ <ClInclude Include="Layout.h" />
+ <ClInclude Include="Light.h" />
+ <ClInclude Include="ListBox.h" />
+ <ClInclude Include="Locale.h" />
+ <ClInclude Include="MachineInfo.h" />
+ <ClInclude Include="MCIWave.h" />
+ <ClInclude Include="..\FoundationEx\MemDebug.h" />
+ <ClInclude Include="Menu.h" />
+ <ClInclude Include="MotionController.h" />
+ <ClInclude Include="Mouse.h" />
+ <ClInclude Include="MouseController.h" />
+ <ClInclude Include="MultiController.h" />
+ <ClInclude Include="..\Parser\Parser.h" />
+ <ClInclude Include="Particles.h" />
+ <ClInclude Include="Pcx.h" />
+ <ClInclude Include="Physical.h" />
+ <ClInclude Include="Polygon.h" />
+ <ClInclude Include="Projector.h" />
+ <ClInclude Include="Random.h" />
+ <ClInclude Include="..\Parser\Reader.h" />
+ <ClInclude Include="Res.h" />
+ <ClInclude Include="Resource.h" />
+ <ClInclude Include="RichTextBox.h" />
+ <ClInclude Include="Scene.h" />
+ <ClInclude Include="Screen.h" />
+ <ClInclude Include="ScrollWindow.h" />
+ <ClInclude Include="Sha1.h" />
+ <ClInclude Include="Shadow.h" />
+ <ClInclude Include="Skin.h" />
+ <ClInclude Include="Slider.h" />
+ <ClInclude Include="Solid.h" />
+ <ClInclude Include="Sound.h" />
+ <ClInclude Include="SoundCard.h" />
+ <ClInclude Include="SoundD3D.h" />
+ <ClInclude Include="Sprite.h" />
+ <ClInclude Include="..\Parser\Term.h" />
+ <ClInclude Include="TexCubeDX9.h" />
+ <ClInclude Include="TexDX9.h" />
+ <ClInclude Include="..\FoundationEx\Text.h" />
+ <ClInclude Include="..\FoundationEx\ThreadSync.h" />
+ <ClInclude Include="TimeSnap.h" />
+ <ClInclude Include="..\Parser\Token.h" />
+ <ClInclude Include="Types.h" />
+ <ClInclude Include="..\..\Program Files\Microsoft Visual Studio\VC98\Include\SYS\TYPES.H" />
+ <ClInclude Include="Universe.h" />
+ <ClInclude Include="Video.h" />
+ <ClInclude Include="VideoDX9.h" />
+ <ClInclude Include="VideoDX9Enum.h" />
+ <ClInclude Include="VideoDX9VertexBuffer.h" />
+ <ClInclude Include="VideoFactory.h" />
+ <ClInclude Include="VideoSettings.h" />
+ <ClInclude Include="View.h" />
+ <ClInclude Include="Water.h" />
+ <ClInclude Include="Wave.h" />
+ <ClInclude Include="WebBrowser.h" />
+ <ClInclude Include="Window.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\FoundationEx\Dictionary.inl">
+ <FileType>Document</FileType>
+ </CustomBuild>
+ <CustomBuild Include="..\FoundationEx\List.inl">
+ <FileType>Document</FileType>
+ </CustomBuild>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/nGenEx/nGenEx.vcxproj.filters b/nGenEx/nGenEx.vcxproj.filters
new file mode 100644
index 0000000..b31e5e7
--- /dev/null
+++ b/nGenEx/nGenEx.vcxproj.filters
@@ -0,0 +1,550 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{7036ad9d-3475-4be6-8676-a944961e0ea0}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{b22ff676-bf70-4e5a-bab4-348f2a5ed43b}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="ActiveWindow.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Archive.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\FoundationEx\ArrayList.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="AviFile.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Bitmap.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Bmp.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Bolt.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Button.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Camera.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CameraView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Color.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ComboBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ComboList.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ContentBundle.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="D3DXImage.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="DataLoader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="EditBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Encrypt.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="EventDispatch.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FadeView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Fix.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Font.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FontMgr.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FormatUtil.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FormDef.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FormWindow.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Game.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Geometry.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Graphic.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ImageBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ImgView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Joystick.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Keyboard.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Layout.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Light.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ListBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Locale.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MachineInfo.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MCIWave.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\FoundationEx\MemDebug.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Menu.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Mouse.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MouseController.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MultiController.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Parser\Parser.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ParseUtil.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Particles.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="PCX.CPP">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Physical.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="PngImage.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Polygon.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Projector.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Random.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Parser\Reader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Res.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="RichTextBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Scene.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Screen.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ScrollWindow.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Sha1.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Shadow.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Skin.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Slider.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Solid.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Sound.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SoundCard.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SoundD3D.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Sprite.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Parser\Term.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TexCubeDX9.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TexDX9.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\FoundationEx\Text.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Parser\Token.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Video.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VideoDX9.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VideoDX9Enum.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VideoDX9VertexBuffer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VideoFactory.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VideoSettings.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Water.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WebBrowser.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Window.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="ActiveWindow.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Archive.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\FoundationEx\ArrayList.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="AviFile.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Bitmap.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Bmp.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Bolt.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Button.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Camera.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CameraView.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Color.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ComboBox.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ComboList.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ContentBundle.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="D3DXImage.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="DataLoader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\FoundationEx\Dictionary.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Director.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="EditBox.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Encrypt.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="EventDispatch.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="EventTarget.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="EventTgt.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Fix.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Font.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="FontMgr.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="FormatUtil.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="FormDef.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="FormWindow.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Game.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Geometry.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Graphic.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="IA3D.H">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ImageBox.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ImgView.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Joystick.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Keyboard.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Layout.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Light.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ListBox.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Locale.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MachineInfo.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MCIWave.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\FoundationEx\MemDebug.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Menu.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MotionController.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Mouse.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MouseController.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MultiController.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\Parser\Parser.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Particles.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Pcx.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Physical.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Polygon.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Projector.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Random.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\Parser\Reader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Res.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="RichTextBox.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Scene.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Screen.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ScrollWindow.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Sha1.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Shadow.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Skin.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Slider.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Solid.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Sound.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SoundCard.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SoundD3D.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Sprite.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\Parser\Term.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="TexCubeDX9.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="TexDX9.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\FoundationEx\Text.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\FoundationEx\ThreadSync.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="TimeSnap.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\Parser\Token.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Types.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Program Files\Microsoft Visual Studio\VC98\Include\SYS\TYPES.H">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Universe.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Video.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="VideoDX9.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="VideoDX9Enum.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="VideoDX9VertexBuffer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="VideoFactory.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="VideoSettings.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="View.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Water.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Wave.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="WebBrowser.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Window.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\FoundationEx\Dictionary.inl">
+ <Filter>Header Files</Filter>
+ </CustomBuild>
+ <CustomBuild Include="..\FoundationEx\List.inl">
+ <Filter>Header Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/nGenEx/nGenEx.vcxproj.user b/nGenEx/nGenEx.vcxproj.user
new file mode 100644
index 0000000..695b5c7
--- /dev/null
+++ b/nGenEx/nGenEx.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project> \ No newline at end of file
diff --git a/oggvorbis/include/ogg/config_types.h b/oggvorbis/include/ogg/config_types.h
new file mode 100644
index 0000000..496e900
--- /dev/null
+++ b/oggvorbis/include/ogg/config_types.h
@@ -0,0 +1,11 @@
+#ifndef __CONFIG_TYPES_H__
+#define __CONFIG_TYPES_H__
+
+/* these are filled in by configure */
+typedef int16_t ogg_int16_t;
+typedef u_int16_t ogg_uint16_t;
+typedef int32_t ogg_int32_t;
+typedef u_int32_t ogg_uint32_t;
+typedef int64_t ogg_int64_t;
+
+#endif
diff --git a/oggvorbis/include/ogg/ogg.h b/oggvorbis/include/ogg/ogg.h
new file mode 100644
index 0000000..ef4a262
--- /dev/null
+++ b/oggvorbis/include/ogg/ogg.h
@@ -0,0 +1,202 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 *
+ * by the Xiph.Org Foundation http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: toplevel libogg include
+ last mod: $Id: ogg.h,v 1.1 2005-08-22 03:11:32 Goober5000 Exp $
+
+ ********************************************************************/
+#ifndef _OGG_H
+#define _OGG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ogg/os_types.h>
+
+typedef struct {
+ long endbyte;
+ int endbit;
+
+ unsigned char *buffer;
+ unsigned char *ptr;
+ long storage;
+} oggpack_buffer;
+
+/* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/
+
+typedef struct {
+ unsigned char *header;
+ long header_len;
+ unsigned char *body;
+ long body_len;
+} ogg_page;
+
+/* ogg_stream_state contains the current encode/decode state of a logical
+ Ogg bitstream **********************************************************/
+
+typedef struct {
+ unsigned char *body_data; /* bytes from packet bodies */
+ long body_storage; /* storage elements allocated */
+ long body_fill; /* elements stored; fill mark */
+ long body_returned; /* elements of fill returned */
+
+
+ int *lacing_vals; /* The values that will go to the segment table */
+ ogg_int64_t *granule_vals; /* granulepos values for headers. Not compact
+ this way, but it is simple coupled to the
+ lacing fifo */
+ long lacing_storage;
+ long lacing_fill;
+ long lacing_packet;
+ long lacing_returned;
+
+ unsigned char header[282]; /* working space for header encode */
+ int header_fill;
+
+ int e_o_s; /* set when we have buffered the last packet in the
+ logical bitstream */
+ int b_o_s; /* set after we've written the initial page
+ of a logical bitstream */
+ long serialno;
+ long pageno;
+ ogg_int64_t packetno; /* sequence number for decode; the framing
+ knows where there's a hole in the data,
+ but we need coupling so that the codec
+ (which is in a seperate abstraction
+ layer) also knows about the gap */
+ ogg_int64_t granulepos;
+
+} ogg_stream_state;
+
+/* ogg_packet is used to encapsulate the data and metadata belonging
+ to a single raw Ogg/Vorbis packet *************************************/
+
+typedef struct {
+ unsigned char *packet;
+ long bytes;
+ long b_o_s;
+ long e_o_s;
+
+ ogg_int64_t granulepos;
+
+ ogg_int64_t packetno; /* sequence number for decode; the framing
+ knows where there's a hole in the data,
+ but we need coupling so that the codec
+ (which is in a seperate abstraction
+ layer) also knows about the gap */
+} ogg_packet;
+
+typedef struct {
+ unsigned char *data;
+ int storage;
+ int fill;
+ int returned;
+
+ int unsynced;
+ int headerbytes;
+ int bodybytes;
+} ogg_sync_state;
+
+/* Ogg BITSTREAM PRIMITIVES: bitstream ************************/
+
+extern void oggpack_writeinit(oggpack_buffer *b);
+extern void oggpack_writetrunc(oggpack_buffer *b,long bits);
+extern void oggpack_writealign(oggpack_buffer *b);
+extern void oggpack_writecopy(oggpack_buffer *b,void *source,long bits);
+extern void oggpack_reset(oggpack_buffer *b);
+extern void oggpack_writeclear(oggpack_buffer *b);
+extern void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes);
+extern void oggpack_write(oggpack_buffer *b,unsigned long value,int bits);
+extern long oggpack_look(oggpack_buffer *b,int bits);
+extern long oggpack_look1(oggpack_buffer *b);
+extern void oggpack_adv(oggpack_buffer *b,int bits);
+extern void oggpack_adv1(oggpack_buffer *b);
+extern long oggpack_read(oggpack_buffer *b,int bits);
+extern long oggpack_read1(oggpack_buffer *b);
+extern long oggpack_bytes(oggpack_buffer *b);
+extern long oggpack_bits(oggpack_buffer *b);
+extern unsigned char *oggpack_get_buffer(oggpack_buffer *b);
+
+extern void oggpackB_writeinit(oggpack_buffer *b);
+extern void oggpackB_writetrunc(oggpack_buffer *b,long bits);
+extern void oggpackB_writealign(oggpack_buffer *b);
+extern void oggpackB_writecopy(oggpack_buffer *b,void *source,long bits);
+extern void oggpackB_reset(oggpack_buffer *b);
+extern void oggpackB_writeclear(oggpack_buffer *b);
+extern void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes);
+extern void oggpackB_write(oggpack_buffer *b,unsigned long value,int bits);
+extern long oggpackB_look(oggpack_buffer *b,int bits);
+extern long oggpackB_look1(oggpack_buffer *b);
+extern void oggpackB_adv(oggpack_buffer *b,int bits);
+extern void oggpackB_adv1(oggpack_buffer *b);
+extern long oggpackB_read(oggpack_buffer *b,int bits);
+extern long oggpackB_read1(oggpack_buffer *b);
+extern long oggpackB_bytes(oggpack_buffer *b);
+extern long oggpackB_bits(oggpack_buffer *b);
+extern unsigned char *oggpackB_get_buffer(oggpack_buffer *b);
+
+/* Ogg BITSTREAM PRIMITIVES: encoding **************************/
+
+extern int ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op);
+extern int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og);
+extern int ogg_stream_flush(ogg_stream_state *os, ogg_page *og);
+
+/* Ogg BITSTREAM PRIMITIVES: decoding **************************/
+
+extern int ogg_sync_init(ogg_sync_state *oy);
+extern int ogg_sync_clear(ogg_sync_state *oy);
+extern int ogg_sync_reset(ogg_sync_state *oy);
+extern int ogg_sync_destroy(ogg_sync_state *oy);
+
+extern char *ogg_sync_buffer(ogg_sync_state *oy, long size);
+extern int ogg_sync_wrote(ogg_sync_state *oy, long bytes);
+extern long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og);
+extern int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og);
+extern int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og);
+extern int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op);
+extern int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op);
+
+/* Ogg BITSTREAM PRIMITIVES: general ***************************/
+
+extern int ogg_stream_init(ogg_stream_state *os,int serialno);
+extern int ogg_stream_clear(ogg_stream_state *os);
+extern int ogg_stream_reset(ogg_stream_state *os);
+extern int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno);
+extern int ogg_stream_destroy(ogg_stream_state *os);
+extern int ogg_stream_eos(ogg_stream_state *os);
+
+extern void ogg_page_checksum_set(ogg_page *og);
+
+extern int ogg_page_version(ogg_page *og);
+extern int ogg_page_continued(ogg_page *og);
+extern int ogg_page_bos(ogg_page *og);
+extern int ogg_page_eos(ogg_page *og);
+extern ogg_int64_t ogg_page_granulepos(ogg_page *og);
+extern int ogg_page_serialno(ogg_page *og);
+extern long ogg_page_pageno(ogg_page *og);
+extern int ogg_page_packets(ogg_page *og);
+
+extern void ogg_packet_clear(ogg_packet *op);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _OGG_H */
+
+
+
+
+
+
diff --git a/oggvorbis/include/ogg/os_types.h b/oggvorbis/include/ogg/os_types.h
new file mode 100644
index 0000000..5320561
--- /dev/null
+++ b/oggvorbis/include/ogg/os_types.h
@@ -0,0 +1,106 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 *
+ * by the Xiph.Org Foundation http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: #ifdef jail to whip a few platforms into the UNIX ideal.
+ last mod: $Id: os_types.h,v 1.1 2005-08-22 03:11:32 Goober5000 Exp $
+
+ ********************************************************************/
+#ifndef _OS_TYPES_H
+#define _OS_TYPES_H
+
+/* make it easy on the folks that want to compile the libs with a
+ different malloc than stdlib */
+#define _ogg_malloc malloc
+#define _ogg_calloc calloc
+#define _ogg_realloc realloc
+#define _ogg_free free
+
+#ifdef _WIN32
+
+# ifndef __GNUC__
+ /* MSVC/Borland */
+ typedef __int64 ogg_int64_t;
+ typedef __int32 ogg_int32_t;
+ typedef unsigned __int32 ogg_uint32_t;
+ typedef __int16 ogg_int16_t;
+ typedef unsigned __int16 ogg_uint16_t;
+# else
+ /* Cygwin */
+ #include <_G_config.h>
+ typedef _G_int64_t ogg_int64_t;
+ typedef _G_int32_t ogg_int32_t;
+ typedef _G_uint32_t ogg_uint32_t;
+ typedef _G_int16_t ogg_int16_t;
+ typedef _G_uint16_t ogg_uint16_t;
+# endif
+
+#elif defined(__MACOS__)
+
+# include <sys/types.h>
+ typedef SInt16 ogg_int16_t;
+ typedef UInt16 ogg_uint16_t;
+ typedef SInt32 ogg_int32_t;
+ typedef UInt32 ogg_uint32_t;
+ typedef SInt64 ogg_int64_t;
+
+#elif defined(__MACOSX__) /* MacOS X Framework build */
+
+# include <sys/types.h>
+ typedef int16_t ogg_int16_t;
+ typedef u_int16_t ogg_uint16_t;
+ typedef int32_t ogg_int32_t;
+ typedef u_int32_t ogg_uint32_t;
+ typedef int64_t ogg_int64_t;
+
+#elif defined(__BEOS__)
+
+ /* Be */
+# include <inttypes.h>
+ typedef int16_t ogg_int16_t;
+ typedef u_int16_t ogg_uint16_t;
+ typedef int32_t ogg_int32_t;
+ typedef u_int32_t ogg_uint32_t;
+ typedef int64_t ogg_int64_t;
+
+#elif defined (__EMX__)
+
+ /* OS/2 GCC */
+ typedef short ogg_int16_t;
+ typedef unsigned short ogg_uint16_t;
+ typedef int ogg_int32_t;
+ typedef unsigned int ogg_uint32_t;
+ typedef long long ogg_int64_t;
+
+#elif defined (DJGPP)
+
+ /* DJGPP */
+ typedef short ogg_int16_t;
+ typedef int ogg_int32_t;
+ typedef unsigned int ogg_uint32_t;
+ typedef long long ogg_int64_t;
+
+#elif defined(R5900)
+
+ /* PS2 EE */
+ typedef long ogg_int64_t;
+ typedef int ogg_int32_t;
+ typedef unsigned ogg_uint32_t;
+ typedef short ogg_int16_t;
+
+#else
+
+# include <sys/types.h>
+# include <ogg/config_types.h>
+
+#endif
+
+#endif /* _OS_TYPES_H */
diff --git a/oggvorbis/include/theora/theora.h b/oggvorbis/include/theora/theora.h
new file mode 100644
index 0000000..8c4f883
--- /dev/null
+++ b/oggvorbis/include/theora/theora.h
@@ -0,0 +1,539 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2003 *
+ * by the Xiph.Org Foundation http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function:
+ last mod: $Id: theora.h,v 1.1.2.1 2006-12-25 21:44:11 taylor Exp $
+
+ ********************************************************************/
+
+#ifndef _O_THEORA_H_
+#define _O_THEORA_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#ifndef LIBOGG2
+#include <ogg/ogg.h>
+#else
+#include <ogg2/ogg.h>
+/* This is temporary until libogg2 is more complete */
+ogg_buffer_state *ogg_buffer_create(void);
+#endif
+
+/** \mainpage
+ *
+ * \section intro Introduction
+ *
+ * This is the documentation for the libtheora C API.
+ * libtheora is the reference implementation for
+ * <a href="http://www.theora.org/">Theora</a>, a free video codec.
+ * Theora is derived from On2's VP3 codec with improved integration for
+ * Ogg multimedia formats by <a href="http://www.xiph.org/">Xiph.Org</a>.
+ */
+
+/** \file
+ * The libtheora C API.
+ */
+
+/**
+ * A YUV buffer for passing uncompressed frames to and from the codec.
+ * This holds a Y'CbCr frame in planar format. The CbCr planes can be
+ * subsampled and have their own separate dimensions and row stride
+ * offsets. Note that the strides may be negative in some
+ * configurations. For theora the width and height of the largest plane
+ * must be a multiple of 16. The actual meaningful picture size and
+ * offset are stored in the theora_info structure; frames returned by
+ * the decoder may need to be cropped for display.
+ *
+ * All samples are 8 bits. Within each plane samples are ordered by
+ * row from the top of the frame to the bottom. Within each row samples
+ * are ordered from left to right.
+ */
+typedef struct {
+ int y_width; /**< Width of the Y' luminance plane */
+ int y_height; /**< Height of the luminance plane */
+ int y_stride; /**< Offset in bytes between successive rows */
+
+ int uv_width; /**< Height of the Cb and Cr chroma planes */
+ int uv_height; /**< Width of the chroma planes */
+ int uv_stride; /**< Offset between successive chroma rows */
+ unsigned char *y; /**< Pointer to start of luminance data */
+ unsigned char *u; /**< Pointer to start of Cb data */
+ unsigned char *v; /**< Pointer to start of Cr data */
+
+} yuv_buffer;
+
+/**
+ * A Colorspace.
+ */
+typedef enum {
+ OC_CS_UNSPECIFIED, /**< The colorspace is unknown or unspecified */
+ OC_CS_ITU_REC_470M, /**< This is the best option for 'NTSC' content */
+ OC_CS_ITU_REC_470BG, /**< This is the best option for 'PAL' content */
+ OC_CS_NSPACES /**< This marks the end of the defined colorspaces */
+} theora_colorspace;
+
+/**
+ * A Chroma subsampling
+ *
+ * These enumerate the available chroma subsampling options supported
+ * by the theora format. See Section 4.4 of the specification for
+ * exact definitions.
+ */
+typedef enum {
+ OC_PF_420, /**< Chroma subsampling by 2 in each direction (4:2:0) */
+ OC_PF_RSVD, /**< Reserved value */
+ OC_PF_422, /**< Horizonatal chroma subsampling by 2 (4:2:2) */
+ OC_PF_444, /**< No chroma subsampling at all (4:4:4) */
+} theora_pixelformat;
+
+/**
+ * Theora bitstream info.
+ * Contains the basic playback parameters for a stream,
+ * corresponds to the initial 'info' header packet.
+ *
+ * Encoded theora frames must be a multiple of 16 is size;
+ * this is what the width and height members represent. To
+ * handle other sizes, a crop rectangle is specified in
+ * frame_height and frame_width, offset_x and offset_y. The
+ * offset and size should still be a multiple of 2 to avoid
+ * chroma sampling shifts. Offset values in this structure
+ * are measured from the upper left of the image.
+ *
+ * Frame rate, in frames per second, is stored as a rational
+ * fraction. So is the aspect ratio. Note that this refers
+ * to the aspect ratio of the frame pixels, not of the
+ * overall frame itself.
+ *
+ * see the example code for use of the other parameters and
+ * good default settings for the encoder parameters.
+ */
+typedef struct {
+ ogg_uint32_t width; /**< encoded frame width */
+ ogg_uint32_t height; /**< encoded frame height */
+ ogg_uint32_t frame_width; /**< display frame width */
+ ogg_uint32_t frame_height; /**< display frame height */
+ ogg_uint32_t offset_x; /**< horizontal offset of the displayed frame */
+ ogg_uint32_t offset_y; /**< vertical offset of the displayed frame */
+ ogg_uint32_t fps_numerator; /**< frame rate numerator **/
+ ogg_uint32_t fps_denominator; /**< frame rate denominator **/
+ ogg_uint32_t aspect_numerator; /**< pixel aspect ratio numerator */
+ ogg_uint32_t aspect_denominator; /**< pixel aspect ratio denominator */
+ theora_colorspace colorspace; /**< colorspace */
+ int target_bitrate; /**< nominal bitrate in bits per second */
+ int quality; /**< Nominal quality setting, 0-63 */
+ int quick_p; /**< Quick encode/decode */
+
+ /* decode only */
+ unsigned char version_major;
+ unsigned char version_minor;
+ unsigned char version_subminor;
+
+ void *codec_setup;
+
+ /* encode only */
+ int dropframes_p;
+ int keyframe_auto_p;
+ ogg_uint32_t keyframe_frequency;
+ ogg_uint32_t keyframe_frequency_force; /* also used for decode init to
+ get granpos shift correct */
+ ogg_uint32_t keyframe_data_target_bitrate;
+ ogg_int32_t keyframe_auto_threshold;
+ ogg_uint32_t keyframe_mindistance;
+ ogg_int32_t noise_sensitivity;
+ ogg_int32_t sharpness;
+
+ theora_pixelformat pixelformat; /**< chroma subsampling mode to expect */
+
+} theora_info;
+
+/** Codec internal state and context.
+ */
+typedef struct{
+ theora_info *i;
+ ogg_int64_t granulepos;
+
+ void *internal_encode;
+ void *internal_decode;
+
+} theora_state;
+
+/**
+ * Comment header metadata.
+ *
+ * This structure holds the in-stream metadata corresponding to
+ * the 'comment' header packet.
+ *
+ * Meta data is stored as a series of (tag, value) pairs, in
+ * length-encoded string vectors. The first occurence of the
+ * '=' character delimits the tag and value. A particular tag
+ * may occur more than once. The character set encoding for
+ * the strings is always utf-8, but the tag names are limited
+ * to case-insensitive ascii. See the spec for details.
+ *
+ * In filling in this structure, theora_decode_header() will
+ * null-terminate the user_comment strings for safety. However,
+ * the bitstream format itself treats them as 8-bit clean,
+ * and so the length array should be treated as authoritative
+ * for their length.
+ */
+typedef struct theora_comment{
+ char **user_comments; /**< An array of comment string vectors */
+ int *comment_lengths; /**< An array of corresponding string vector lengths in bytes */
+ int comments; /**< The total number of comment string vectors */
+ char *vendor; /**< The vendor string identifying the encoder, null terminated */
+
+} theora_comment;
+
+#define OC_FAULT -1 /**< General failure */
+#define OC_EINVAL -10 /**< Library encountered invalid internal data */
+#define OC_DISABLED -11 /**< Requested action is disabled */
+#define OC_BADHEADER -20 /**< Header packet was corrupt/invalid */
+#define OC_NOTFORMAT -21 /**< Packet is not a theora packet */
+#define OC_VERSION -22 /**< Bitstream version is not handled */
+#define OC_IMPL -23 /**< Feature or action not implemented */
+#define OC_BADPACKET -24 /**< Packet is corrupt */
+#define OC_NEWPACKET -25 /**< Packet is an (ignorable) unhandled extension */
+#define OC_DUPFRAME 1 /**< Packet is a dropped frame */
+
+/**
+ * Retrieve a human-readable string to identify the encoder vendor and version.
+ * \returns A version string.
+ */
+extern const char *theora_version_string(void);
+
+/**
+ * Retrieve a 32-bit version number.
+ * This number is composed of a 16-bit major version, 8-bit minor version
+ * and 8 bit sub-version, composed as follows:
+<pre>
+ (VERSION_MAJOR<<16) + (VERSION_MINOR<<8) + (VERSION_SUB)
+</pre>
+* \returns The version number.
+*/
+extern ogg_uint32_t theora_version_number(void);
+
+/**
+ * Initialize the theora encoder.
+ * \param th The theora_state handle to initialize for encoding.
+ * \param ti A theora_info struct filled with the desired encoding parameters.
+ * \retval 0 Success
+ */
+extern int theora_encode_init(theora_state *th, theora_info *ti);
+
+/**
+ * Submit a YUV buffer to the theora encoder.
+ * \param t A theora_state handle previously initialized for encoding.
+ * \param yuv A buffer of YUV data to encode.
+ * \retval OC_EINVAL Encoder is not ready, or is finished.
+ * \retval -1 The size of the given frame differs from those previously input
+ * \retval 0 Success
+ */
+extern int theora_encode_YUVin(theora_state *t, yuv_buffer *yuv);
+
+/**
+ * Request the next packet of encoded video.
+ * The encoded data is placed in a user-provided ogg_packet structure.
+ * \param t A theora_state handle previously initialized for encoding.
+ * \param last_p whether this is the last packet the encoder should produce.
+ * \param op An ogg_packet structure to fill. libtheora will set all
+ * elements of this structure, including a pointer to encoded
+ * data. The memory for the encoded data is owned by libtheora.
+ * \retval 0 No internal storage exists OR no packet is ready
+ * \retval -1 The encoding process has completed
+ * \retval 1 Success
+ */
+extern int theora_encode_packetout( theora_state *t, int last_p,
+ ogg_packet *op);
+
+/**
+ * Request a packet containing the initial header.
+ * A pointer to the header data is placed in a user-provided ogg_packet
+ * structure.
+ * \param t A theora_state handle previously initialized for encoding.
+ * \param op An ogg_packet structure to fill. libtheora will set all
+ * elements of this structure, including a pointer to the header
+ * data. The memory for the header data is owned by libtheora.
+ * \retval 0 Success
+ */
+extern int theora_encode_header(theora_state *t, ogg_packet *op);
+
+/**
+ * Request a comment header packet from provided metadata.
+ * A pointer to the comment data is placed in a user-provided ogg_packet
+ * structure.
+ * \param tc A theora_comment structure filled with the desired metadata
+ * \param op An ogg_packet structure to fill. libtheora will set all
+ * elements of this structure, including a pointer to the encoded
+ * comment data. The memory for the comment data is owned by
+ * libtheora.
+ * \retval 0 Success
+ */
+extern int theora_encode_comment(theora_comment *tc, ogg_packet *op);
+
+/**
+ * Request a packet containing the codebook tables for the stream.
+ * A pointer to the codebook data is placed in a user-provided ogg_packet
+ * structure.
+ * \param t A theora_state handle previously initialized for encoding.
+ * \param op An ogg_packet structure to fill. libtheora will set all
+ * elements of this structure, including a pointer to the codebook
+ * data. The memory for the header data is owned by libtheora.
+ * \retval 0 Success
+ */
+extern int theora_encode_tables(theora_state *t, ogg_packet *op);
+
+/**
+ * Decode an Ogg packet, with the expectation that the packet contains
+ * an initial header, comment data or codebook tables.
+ *
+ * \param ci A theora_info structure to fill. This must have been previously
+ * initialized with theora_info_init(). If \a op contains an initial
+ * header, theora_decode_header() will fill \a ci with the
+ * parsed header values. If \a op contains codebook tables,
+ * theora_decode_header() will parse these and attach an internal
+ * representation to \a ci->codec_setup.
+ * \param cc A theora_comment structure to fill. If \a op contains comment
+ * data, theora_decode_header() will fill \a cc with the parsed
+ * comments.
+ * \param op An ogg_packet structure which you expect contains an initial
+ * header, comment data or codebook tables.
+ *
+ * \retval OC_BADHEADER \a op is NULL; OR the first byte of \a op->packet
+ * has the signature of an initial packet, but op is
+ * not a b_o_s packet; OR this packet has the signature
+ * of an initial header packet, but an initial header
+ * packet has already been seen; OR this packet has the
+ * signature of a comment packet, but the initial header
+ * has not yet been seen; OR this packet has the signature
+ * of a comment packet, but contains invalid data; OR
+ * this packet has the signature of codebook tables,
+ * but the initial header or comments have not yet
+ * been seen; OR this packet has the signature of codebook
+ * tables, but contains invalid data;
+ * OR the stream being decoded has a compatible version
+ * but this packet does not have the signature of a
+ * theora initial header, comments, or codebook packet
+ * \retval OC_VERSION The packet data of \a op is an initial header with
+ * a version which is incompatible with this version of
+ * libtheora.
+ * \retval OC_NEWPACKET the stream being decoded has an incompatible (future)
+ * version and contains an unknown signature.
+ * \retval 0 Success
+ *
+ * \note The normal usage is that theora_decode_header() be called on the
+ * first three packets of a theora logical bitstream in succession.
+ */
+extern int theora_decode_header(theora_info *ci, theora_comment *cc,
+ ogg_packet *op);
+
+/**
+ * Initialize a theora_state handle for decoding.
+ * \param th The theora_state handle to initialize.
+ * \param c A theora_info struct filled with the desired decoding parameters.
+ * This is of course usually obtained from a previous call to
+ * theora_decode_header().
+ * \retval 0 Success
+ */
+extern int theora_decode_init(theora_state *th, theora_info *c);
+
+/**
+ * Input a packet containing encoded data into the theora decoder.
+ * \param th A theora_state handle previously initialized for decoding.
+ * \param op An ogg_packet containing encoded theora data.
+ * \retval 0 Success
+ * \retval OC_BADPACKET \a op does not contain encoded video data
+ */
+extern int theora_decode_packetin(theora_state *th,ogg_packet *op);
+
+/**
+ * Output the next available frame of decoded YUV data.
+ * \param th A theora_state handle previously initialized for decoding.
+ * \param yuv A yuv_buffer in which libtheora should place the decoded data.
+ * \retval 0 Success
+ */
+extern int theora_decode_YUVout(theora_state *th,yuv_buffer *yuv);
+
+/**
+ * Report whether a theora packet is a header or not
+ * This function does no verification beyond checking the header
+ * flag bit so it should not be used for bitstream identification;
+ * use theora_decode_header() for that.
+ *
+ * \param op An ogg_packet containing encoded theora data.
+ * \retval 1 The packet is a header packet
+ * \retval 0 The packet is not a header packet (and so contains frame data)
+ *
+ * Thus function was added in the 1.0alpha4 release.
+ */
+extern int theora_packet_isheader(ogg_packet *op);
+
+/**
+ * Report whether a theora packet is a keyframe or not
+ *
+ * \param op An ogg_packet containing encoded theora data.
+ * \retval 1 The packet contains a keyframe image
+ * \retval 0 The packet is contains an interframe delta
+ * \retval -1 The packet is not an image data packet at all
+ *
+ * Thus function was added in the 1.0alpha4 release.
+ */
+extern int theora_packet_iskeyframe(ogg_packet *op);
+
+/**
+ * Report the granulepos shift radix
+ *
+ * When embedded in Ogg, Theora uses a two-part granulepos,
+ * splitting the 64-bit field into two pieces. The more-significant
+ * section represents the frame count at the last keyframe,
+ * and the less-significant section represents the count of
+ * frames since the last keyframe. In this way the overall
+ * field is still non-decreasing with time, but usefully encodes
+ * a pointer to the last keyframe, which is necessary for
+ * correctly restarting decode after a seek.
+ *
+ * This function reports the number of bits used to represent
+ * the distance to the last keyframe, and thus how the granulepos
+ * field must be shifted or masked to obtain the two parts.
+ *
+ * Since libtheora returns compressed data in an ogg_packet
+ * structure, this may be generally useful even if the Theora
+ * packets are not being used in an Ogg container.
+ *
+ * \param ti A previously initialized theora_info struct
+ * \returns The bit shift dividing the two granulepos fields
+ *
+ * This function was added in the 1.0alpha5 release.
+ */
+int theora_granule_shift(theora_info *ti);
+
+/**
+ * Convert a granulepos to an absolute frame number. The granulepos is
+ * interpreted in the context of a given theora_state handle.
+ *
+ * \param th A previously initialized theora_state handle (encode or decode)
+ * \param granulepos The granulepos to convert.
+ * \returns The frame number corresponding to \a granulepos.
+ * \retval -1 The given granulepos is undefined (i.e. negative)
+ *
+ * Thus function was added in the 1.0alpha4 release.
+ */
+extern ogg_int64_t theora_granule_frame(theora_state *th,ogg_int64_t granulepos);
+
+/**
+ * Convert a granulepos to absolute time in seconds. The granulepos is
+ * interpreted in the context of a given theora_state handle.
+ * \param th A previously initialized theora_state handle (encode or decode)
+ * \param granulepos The granulepos to convert.
+ * \returns The absolute time in seconds corresponding to \a granulepos.
+ * \retval -1. The given granulepos is undefined (i.e. negative), or
+ * \retval -1. The function has been disabled because floating
+ * point support is not available.
+ */
+extern double theora_granule_time(theora_state *th,ogg_int64_t granulepos);
+
+/**
+ * Initialize a theora_info structure. All values within the given theora_info
+ * structure are initialized, and space is allocated within libtheora for
+ * internal codec setup data.
+ * \param c A theora_info struct to initialize.
+ */
+extern void theora_info_init(theora_info *c);
+
+/**
+ * Clear a theora_info structure. All values within the given theora_info
+ * structure are cleared, and associated internal codec setup data is freed.
+ * \param c A theora_info struct to initialize.
+ */
+extern void theora_info_clear(theora_info *c);
+
+/**
+ * Free all internal data associated with a theora_state handle.
+ * \param t A theora_state handle.
+ */
+extern void theora_clear(theora_state *t);
+
+/**
+ * Initialize an allocated theora_comment structure
+ * \param tc An allocated theora_comment structure
+ **/
+extern void theora_comment_init(theora_comment *tc);
+
+/**
+ * Add a comment to an initialized theora_comment structure
+ * \param tc A previously initialized theora comment structure
+ * \param comment A null-terminated string encoding the comment in the form
+ * "TAG=the value"
+ *
+ * Neither theora_comment_add() nor theora_comment_add_tag() support
+ * comments containing null values, although the bitstream format
+ * supports this. To add such comments you will need to manipulate
+ * the theora_comment structure directly.
+ **/
+
+extern void theora_comment_add(theora_comment *tc, char *comment);
+
+/**
+ * Add a comment to an initialized theora_comment structure.
+ * \param tc A previously initialized theora comment structure
+ * \param tag A null-terminated string containing the tag
+ * associated with the comment.
+ * \param value The corresponding value as a null-terminated string
+ *
+ * Neither theora_comment_add() nor theora_comment_add_tag() support
+ * comments containing null values, although the bitstream format
+ * supports this. To add such comments you will need to manipulate
+ * the theora_comment structure directly.
+ **/
+extern void theora_comment_add_tag(theora_comment *tc,
+ char *tag, char *value);
+
+/**
+ * Look up a comment value by tag.
+ * \param tc Tn initialized theora_comment structure
+ * \param tag The tag to look up
+ * \param count The instance of the tag. The same tag can appear multiple
+ * times, each with a distinct and ordered value, so an index
+ * is required to retrieve them all.
+ * \returns A pointer to the queried tag's value
+ * \retval NULL No matching tag is found
+ *
+ * \note Use theora_comment_query_count() to get the legal range for the
+ * count parameter.
+ **/
+
+extern char *theora_comment_query(theora_comment *tc, char *tag, int count);
+
+/** Look up the number of instances of a tag.
+ * \param tc An initialized theora_comment structure
+ * \param tag The tag to look up
+ * \returns The number on instances of a particular tag.
+ *
+ * Call this first when querying for a specific tag and then interate
+ * over the number of instances with separate calls to
+ * theora_comment_query() to retrieve all instances in order.
+ **/
+extern int theora_comment_query_count(theora_comment *tc, char *tag);
+
+/**
+ * Clear an allocated theora_comment struct so that it can be freed.
+ * \param tc An allocated theora_comment structure.
+ **/
+extern void theora_comment_clear(theora_comment *tc);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _O_THEORA_H_ */
diff --git a/oggvorbis/include/vorbis/codec.h b/oggvorbis/include/vorbis/codec.h
new file mode 100644
index 0000000..49dffa1
--- /dev/null
+++ b/oggvorbis/include/vorbis/codec.h
@@ -0,0 +1,240 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 *
+ * by the XIPHOPHORUS Company http://www.xiph.org/ *
+
+ ********************************************************************
+
+ function: libvorbis codec headers
+ last mod: $Id: codec.h,v 1.1 2005-08-22 03:11:32 Goober5000 Exp $
+
+ ********************************************************************/
+
+#ifndef _vorbis_codec_h_
+#define _vorbis_codec_h_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#include <ogg/ogg.h>
+
+typedef struct vorbis_info{
+ int version;
+ int channels;
+ long rate;
+
+ /* The below bitrate declarations are *hints*.
+ Combinations of the three values carry the following implications:
+
+ all three set to the same value:
+ implies a fixed rate bitstream
+ only nominal set:
+ implies a VBR stream that averages the nominal bitrate. No hard
+ upper/lower limit
+ upper and or lower set:
+ implies a VBR bitstream that obeys the bitrate limits. nominal
+ may also be set to give a nominal rate.
+ none set:
+ the coder does not care to speculate.
+ */
+
+ long bitrate_upper;
+ long bitrate_nominal;
+ long bitrate_lower;
+ long bitrate_window;
+
+ void *codec_setup;
+} vorbis_info;
+
+/* vorbis_dsp_state buffers the current vorbis audio
+ analysis/synthesis state. The DSP state belongs to a specific
+ logical bitstream ****************************************************/
+typedef struct vorbis_dsp_state{
+ int analysisp;
+ vorbis_info *vi;
+
+ float **pcm;
+ float **pcmret;
+ int pcm_storage;
+ int pcm_current;
+ int pcm_returned;
+
+ int preextrapolate;
+ int eofflag;
+
+ long lW;
+ long W;
+ long nW;
+ long centerW;
+
+ ogg_int64_t granulepos;
+ ogg_int64_t sequence;
+
+ ogg_int64_t glue_bits;
+ ogg_int64_t time_bits;
+ ogg_int64_t floor_bits;
+ ogg_int64_t res_bits;
+
+ void *backend_state;
+} vorbis_dsp_state;
+
+typedef struct vorbis_block{
+ /* necessary stream state for linking to the framing abstraction */
+ float **pcm; /* this is a pointer into local storage */
+ oggpack_buffer opb;
+
+ long lW;
+ long W;
+ long nW;
+ int pcmend;
+ int mode;
+
+ int eofflag;
+ ogg_int64_t granulepos;
+ ogg_int64_t sequence;
+ vorbis_dsp_state *vd; /* For read-only access of configuration */
+
+ /* local storage to avoid remallocing; it's up to the mapping to
+ structure it */
+ void *localstore;
+ long localtop;
+ long localalloc;
+ long totaluse;
+ struct alloc_chain *reap;
+
+ /* bitmetrics for the frame */
+ long glue_bits;
+ long time_bits;
+ long floor_bits;
+ long res_bits;
+
+ void *internal;
+
+} vorbis_block;
+
+/* vorbis_block is a single block of data to be processed as part of
+the analysis/synthesis stream; it belongs to a specific logical
+bitstream, but is independant from other vorbis_blocks belonging to
+that logical bitstream. *************************************************/
+
+struct alloc_chain{
+ void *ptr;
+ struct alloc_chain *next;
+};
+
+/* vorbis_info contains all the setup information specific to the
+ specific compression/decompression mode in progress (eg,
+ psychoacoustic settings, channel setup, options, codebook
+ etc). vorbis_info and substructures are in backends.h.
+*********************************************************************/
+
+/* the comments are not part of vorbis_info so that vorbis_info can be
+ static storage */
+typedef struct vorbis_comment{
+ /* unlimited user comment fields. libvorbis writes 'libvorbis'
+ whatever vendor is set to in encode */
+ char **user_comments;
+ int *comment_lengths;
+ int comments;
+ char *vendor;
+
+} vorbis_comment;
+
+
+/* libvorbis encodes in two abstraction layers; first we perform DSP
+ and produce a packet (see docs/analysis.txt). The packet is then
+ coded into a framed OggSquish bitstream by the second layer (see
+ docs/framing.txt). Decode is the reverse process; we sync/frame
+ the bitstream and extract individual packets, then decode the
+ packet back into PCM audio.
+
+ The extra framing/packetizing is used in streaming formats, such as
+ files. Over the net (such as with UDP), the framing and
+ packetization aren't necessary as they're provided by the transport
+ and the streaming layer is not used */
+
+/* Vorbis PRIMITIVES: general ***************************************/
+
+extern void vorbis_info_init(vorbis_info *vi);
+extern void vorbis_info_clear(vorbis_info *vi);
+extern int vorbis_info_blocksize(vorbis_info *vi,int zo);
+extern void vorbis_comment_init(vorbis_comment *vc);
+extern void vorbis_comment_add(vorbis_comment *vc, char *comment);
+extern void vorbis_comment_add_tag(vorbis_comment *vc,
+ char *tag, char *contents);
+extern char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count);
+extern int vorbis_comment_query_count(vorbis_comment *vc, char *tag);
+extern void vorbis_comment_clear(vorbis_comment *vc);
+
+extern int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb);
+extern int vorbis_block_clear(vorbis_block *vb);
+extern void vorbis_dsp_clear(vorbis_dsp_state *v);
+extern double vorbis_granule_time(vorbis_dsp_state *v,
+ ogg_int64_t granulepos);
+
+/* Vorbis PRIMITIVES: analysis/DSP layer ****************************/
+
+extern int vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi);
+extern int vorbis_commentheader_out(vorbis_comment *vc, ogg_packet *op);
+extern int vorbis_analysis_headerout(vorbis_dsp_state *v,
+ vorbis_comment *vc,
+ ogg_packet *op,
+ ogg_packet *op_comm,
+ ogg_packet *op_code);
+extern float **vorbis_analysis_buffer(vorbis_dsp_state *v,int vals);
+extern int vorbis_analysis_wrote(vorbis_dsp_state *v,int vals);
+extern int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb);
+extern int vorbis_analysis(vorbis_block *vb,ogg_packet *op);
+
+extern int vorbis_bitrate_addblock(vorbis_block *vb);
+extern int vorbis_bitrate_flushpacket(vorbis_dsp_state *vd,
+ ogg_packet *op);
+
+/* Vorbis PRIMITIVES: synthesis layer *******************************/
+extern int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,
+ ogg_packet *op);
+
+extern int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi);
+extern int vorbis_synthesis_restart(vorbis_dsp_state *v);
+extern int vorbis_synthesis(vorbis_block *vb,ogg_packet *op);
+extern int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op);
+extern int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb);
+extern int vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm);
+extern int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm);
+extern int vorbis_synthesis_read(vorbis_dsp_state *v,int samples);
+extern long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op);
+
+extern int vorbis_synthesis_halfrate(vorbis_info *v,int flag);
+extern int vorbis_synthesis_halfrate_p(vorbis_info *v);
+
+/* Vorbis ERRORS and return codes ***********************************/
+
+#define OV_FALSE -1
+#define OV_EOF -2
+#define OV_HOLE -3
+
+#define OV_EREAD -128
+#define OV_EFAULT -129
+#define OV_EIMPL -130
+#define OV_EINVAL -131
+#define OV_ENOTVORBIS -132
+#define OV_EBADHEADER -133
+#define OV_EVERSION -134
+#define OV_ENOTAUDIO -135
+#define OV_EBADPACKET -136
+#define OV_EBADLINK -137
+#define OV_ENOSEEK -138
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
diff --git a/oggvorbis/include/vorbis/vorbisenc.h b/oggvorbis/include/vorbis/vorbisenc.h
new file mode 100644
index 0000000..e1a05e5
--- /dev/null
+++ b/oggvorbis/include/vorbis/vorbisenc.h
@@ -0,0 +1,93 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 *
+ * by the XIPHOPHORUS Company http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: vorbis encode-engine setup
+ last mod: $Id: vorbisenc.h,v 1.1 2005-08-22 03:11:32 Goober5000 Exp $
+
+ ********************************************************************/
+
+#ifndef _OV_ENC_H_
+#define _OV_ENC_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#include "codec.h"
+
+extern int vorbis_encode_init(vorbis_info *vi,
+ long channels,
+ long rate,
+
+ long max_bitrate,
+ long nominal_bitrate,
+ long min_bitrate);
+
+extern int vorbis_encode_setup_managed(vorbis_info *vi,
+ long channels,
+ long rate,
+
+ long max_bitrate,
+ long nominal_bitrate,
+ long min_bitrate);
+
+extern int vorbis_encode_setup_vbr(vorbis_info *vi,
+ long channels,
+ long rate,
+
+ float /* quality level from 0. (lo) to 1. (hi) */
+ );
+
+extern int vorbis_encode_init_vbr(vorbis_info *vi,
+ long channels,
+ long rate,
+
+ float base_quality /* quality level from 0. (lo) to 1. (hi) */
+ );
+
+extern int vorbis_encode_setup_init(vorbis_info *vi);
+
+extern int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg);
+
+#define OV_ECTL_RATEMANAGE_GET 0x10
+
+#define OV_ECTL_RATEMANAGE_SET 0x11
+#define OV_ECTL_RATEMANAGE_AVG 0x12
+#define OV_ECTL_RATEMANAGE_HARD 0x13
+
+#define OV_ECTL_LOWPASS_GET 0x20
+#define OV_ECTL_LOWPASS_SET 0x21
+
+#define OV_ECTL_IBLOCK_GET 0x30
+#define OV_ECTL_IBLOCK_SET 0x31
+
+struct ovectl_ratemanage_arg {
+ int management_active;
+
+ long bitrate_hard_min;
+ long bitrate_hard_max;
+ double bitrate_hard_window;
+
+ long bitrate_av_lo;
+ long bitrate_av_hi;
+ double bitrate_av_window;
+ double bitrate_av_window_center;
+};
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
+
diff --git a/oggvorbis/include/vorbis/vorbisfile.h b/oggvorbis/include/vorbis/vorbisfile.h
new file mode 100644
index 0000000..0e8d3d1
--- /dev/null
+++ b/oggvorbis/include/vorbis/vorbisfile.h
@@ -0,0 +1,143 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 *
+ * by the XIPHOPHORUS Company http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: stdio-based convenience library for opening/seeking/decoding
+ last mod: $Id: vorbisfile.h,v 1.1 2005-08-22 03:11:32 Goober5000 Exp $
+
+ ********************************************************************/
+
+#ifndef _OV_FILE_H_
+#define _OV_FILE_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#include <stdio.h>
+#include "codec.h"
+
+/* The function prototypes for the callbacks are basically the same as for
+ * the stdio functions fread, fseek, fclose, ftell.
+ * The one difference is that the FILE * arguments have been replaced with
+ * a void * - this is to be used as a pointer to whatever internal data these
+ * functions might need. In the stdio case, it's just a FILE * cast to a void *
+ *
+ * If you use other functions, check the docs for these functions and return
+ * the right values. For seek_func(), you *MUST* return -1 if the stream is
+ * unseekable
+ */
+typedef struct {
+ size_t (*read_func) (void *ptr, size_t size, size_t nmemb, void *datasource);
+ int (*seek_func) (void *datasource, ogg_int64_t offset, int whence);
+ int (*close_func) (void *datasource);
+ long (*tell_func) (void *datasource);
+} ov_callbacks;
+
+#define NOTOPEN 0
+#define PARTOPEN 1
+#define OPENED 2
+#define STREAMSET 3
+#define INITSET 4
+
+typedef struct OggVorbis_File {
+ void *datasource; /* Pointer to a FILE *, etc. */
+ int seekable;
+ ogg_int64_t offset;
+ ogg_int64_t end;
+ ogg_sync_state oy;
+
+ /* If the FILE handle isn't seekable (eg, a pipe), only the current
+ stream appears */
+ int links;
+ ogg_int64_t *offsets;
+ ogg_int64_t *dataoffsets;
+ long *serialnos;
+ ogg_int64_t *pcmlengths; /* overloaded to maintain binary
+ compatability; x2 size, stores both
+ beginning and end values */
+ vorbis_info *vi;
+ vorbis_comment *vc;
+
+ /* Decoding working state local storage */
+ ogg_int64_t pcm_offset;
+ int ready_state;
+ long current_serialno;
+ int current_link;
+
+ double bittrack;
+ double samptrack;
+
+ ogg_stream_state os; /* take physical pages, weld into a logical
+ stream of packets */
+ vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
+ vorbis_block vb; /* local working space for packet->PCM decode */
+
+ ov_callbacks callbacks;
+
+} OggVorbis_File;
+
+extern int ov_clear(OggVorbis_File *vf);
+extern int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes);
+extern int ov_open_callbacks(void *datasource, OggVorbis_File *vf,
+ char *initial, long ibytes, ov_callbacks callbacks);
+
+extern int ov_test(FILE *f,OggVorbis_File *vf,char *initial,long ibytes);
+extern int ov_test_callbacks(void *datasource, OggVorbis_File *vf,
+ char *initial, long ibytes, ov_callbacks callbacks);
+extern int ov_test_open(OggVorbis_File *vf);
+
+extern long ov_bitrate(OggVorbis_File *vf,int i);
+extern long ov_bitrate_instant(OggVorbis_File *vf);
+extern long ov_streams(OggVorbis_File *vf);
+extern long ov_seekable(OggVorbis_File *vf);
+extern long ov_serialnumber(OggVorbis_File *vf,int i);
+
+extern ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i);
+extern ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i);
+extern double ov_time_total(OggVorbis_File *vf,int i);
+
+extern int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos);
+extern int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos);
+extern int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos);
+extern int ov_time_seek(OggVorbis_File *vf,double pos);
+extern int ov_time_seek_page(OggVorbis_File *vf,double pos);
+
+extern int ov_raw_seek_lap(OggVorbis_File *vf,ogg_int64_t pos);
+extern int ov_pcm_seek_lap(OggVorbis_File *vf,ogg_int64_t pos);
+extern int ov_pcm_seek_page_lap(OggVorbis_File *vf,ogg_int64_t pos);
+extern int ov_time_seek_lap(OggVorbis_File *vf,double pos);
+extern int ov_time_seek_page_lap(OggVorbis_File *vf,double pos);
+
+extern ogg_int64_t ov_raw_tell(OggVorbis_File *vf);
+extern ogg_int64_t ov_pcm_tell(OggVorbis_File *vf);
+extern double ov_time_tell(OggVorbis_File *vf);
+
+extern vorbis_info *ov_info(OggVorbis_File *vf,int link);
+extern vorbis_comment *ov_comment(OggVorbis_File *vf,int link);
+
+extern long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int samples,
+ int *bitstream);
+extern long ov_read(OggVorbis_File *vf,char *buffer,int length,
+ int bigendianp,int word,int sgned,int *bitstream);
+extern int ov_crosslap(OggVorbis_File *vf1,OggVorbis_File *vf2);
+
+extern int ov_halfrate(OggVorbis_File *vf,int flag);
+extern int ov_halfrate_p(OggVorbis_File *vf);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
+
diff --git a/oggvorbis/lib/ogg_static.lib b/oggvorbis/lib/ogg_static.lib
new file mode 100644
index 0000000..bd66ac9
--- /dev/null
+++ b/oggvorbis/lib/ogg_static.lib
Binary files differ
diff --git a/oggvorbis/lib/ogg_static_d.lib b/oggvorbis/lib/ogg_static_d.lib
new file mode 100644
index 0000000..e7afe87
--- /dev/null
+++ b/oggvorbis/lib/ogg_static_d.lib
Binary files differ
diff --git a/oggvorbis/lib/theora_static.lib b/oggvorbis/lib/theora_static.lib
new file mode 100644
index 0000000..d62cddd
--- /dev/null
+++ b/oggvorbis/lib/theora_static.lib
Binary files differ
diff --git a/oggvorbis/lib/theora_static_d.lib b/oggvorbis/lib/theora_static_d.lib
new file mode 100644
index 0000000..4e2c9e8
--- /dev/null
+++ b/oggvorbis/lib/theora_static_d.lib
Binary files differ
diff --git a/oggvorbis/lib/vorbis_static.lib b/oggvorbis/lib/vorbis_static.lib
new file mode 100644
index 0000000..e2d2c9c
--- /dev/null
+++ b/oggvorbis/lib/vorbis_static.lib
Binary files differ
diff --git a/oggvorbis/lib/vorbis_static_d.lib b/oggvorbis/lib/vorbis_static_d.lib
new file mode 100644
index 0000000..982fe60
--- /dev/null
+++ b/oggvorbis/lib/vorbis_static_d.lib
Binary files differ
diff --git a/oggvorbis/lib/vorbisfile_static.lib b/oggvorbis/lib/vorbisfile_static.lib
new file mode 100644
index 0000000..fadf41f
--- /dev/null
+++ b/oggvorbis/lib/vorbisfile_static.lib
Binary files differ
diff --git a/oggvorbis/lib/vorbisfile_static_d.lib b/oggvorbis/lib/vorbisfile_static_d.lib
new file mode 100644
index 0000000..edc0eac
--- /dev/null
+++ b/oggvorbis/lib/vorbisfile_static_d.lib
Binary files differ
diff --git a/zlib/ChangeLog b/zlib/ChangeLog
new file mode 100644
index 0000000..09bca1b
--- /dev/null
+++ b/zlib/ChangeLog
@@ -0,0 +1,764 @@
+
+ ChangeLog file for zlib
+
+Changes in 1.2.2 (3 October 2004)
+- Update zlib.h comments on gzip in-memory processing
+- Set adler to 1 in inflateReset() to support Java test suite [Walles]
+- Add contrib/dotzlib [Ravn]
+- Update win32/DLL_FAQ.txt [Truta]
+- Update contrib/minizip [Vollant]
+- Move contrib/visual-basic.txt to old/ [Truta]
+- Fix assembler builds in projects/visualc6/ [Truta]
+
+Changes in 1.2.1.2 (9 September 2004)
+- Update INDEX file
+- Fix trees.c to update strm->data_type (no one ever noticed!)
+- Fix bug in error case in inflate.c, infback.c, and infback9.c [Brown]
+- Add "volatile" to crc table flag declaration (for DYNAMIC_CRC_TABLE)
+- Add limited multitasking protection to DYNAMIC_CRC_TABLE
+- Add NO_vsnprintf for VMS in zutil.h [Mozilla]
+- Don't declare strerror() under VMS [Mozilla]
+- Add comment to DYNAMIC_CRC_TABLE to use get_crc_table() to initialize
+- Update contrib/ada [Anisimkov]
+- Update contrib/minizip [Vollant]
+- Fix configure to not hardcode directories for Darwin [Peterson]
+- Fix gzio.c to not return error on empty files [Brown]
+- Fix indentation; update version in contrib/delphi/ZLib.pas and
+ contrib/pascal/zlibpas.pas [Truta]
+- Update mkasm.bat in contrib/masmx86 [Truta]
+- Update contrib/untgz [Truta]
+- Add projects/README.projects [Truta]
+- Add project for MS Visual C++ 6.0 in projects/visualc6 [Cadieux, Truta]
+- Update win32/DLL_FAQ.txt [Truta]
+- Update list of Z_PREFIX symbols in zconf.h [Randers-Pehrson, Truta]
+- Remove an unnecessary assignment to curr in inftrees.c [Truta]
+- Add OS/2 to exe builds in configure [Poltorak]
+- Remove err dummy parameter in zlib.h [Kientzle]
+
+Changes in 1.2.1.1 (9 January 2004)
+- Update email address in README
+- Several FAQ updates
+- Fix a big fat bug in inftrees.c that prevented decoding valid
+ dynamic blocks with only literals and no distance codes --
+ Thanks to "Hot Emu" for the bug report and sample file
+- Add a note to puff.c on no distance codes case.
+
+Changes in 1.2.1 (17 November 2003)
+- Remove a tab in contrib/gzappend/gzappend.c
+- Update some interfaces in contrib for new zlib functions
+- Update zlib version number in some contrib entries
+- Add Windows CE definition for ptrdiff_t in zutil.h [Mai, Truta]
+- Support shared libraries on Hurd and KFreeBSD [Brown]
+- Fix error in NO_DIVIDE option of adler32.c
+
+Changes in 1.2.0.8 (4 November 2003)
+- Update version in contrib/delphi/ZLib.pas and contrib/pascal/zlibpas.pas
+- Add experimental NO_DIVIDE #define in adler32.c
+ - Possibly faster on some processors (let me know if it is)
+- Correct Z_BLOCK to not return on first inflate call if no wrap
+- Fix strm->data_type on inflate() return to correctly indicate EOB
+- Add deflatePrime() function for appending in the middle of a byte
+- Add contrib/gzappend for an example of appending to a stream
+- Update win32/DLL_FAQ.txt [Truta]
+- Delete Turbo C comment in README [Truta]
+- Improve some indentation in zconf.h [Truta]
+- Fix infinite loop on bad input in configure script [Church]
+- Fix gzeof() for concatenated gzip files [Johnson]
+- Add example to contrib/visual-basic.txt [Michael B.]
+- Add -p to mkdir's in Makefile.in [vda]
+- Fix configure to properly detect presence or lack of printf functions
+- Add AS400 support [Monnerat]
+- Add a little Cygwin support [Wilson]
+
+Changes in 1.2.0.7 (21 September 2003)
+- Correct some debug formats in contrib/infback9
+- Cast a type in a debug statement in trees.c
+- Change search and replace delimiter in configure from % to # [Beebe]
+- Update contrib/untgz to 0.2 with various fixes [Truta]
+- Add build support for Amiga [Nikl]
+- Remove some directories in old that have been updated to 1.2
+- Add dylib building for Mac OS X in configure and Makefile.in
+- Remove old distribution stuff from Makefile
+- Update README to point to DLL_FAQ.txt, and add comment on Mac OS X
+- Update links in README
+
+Changes in 1.2.0.6 (13 September 2003)
+- Minor FAQ updates
+- Update contrib/minizip to 1.00 [Vollant]
+- Remove test of gz functions in example.c when GZ_COMPRESS defined [Truta]
+- Update POSTINC comment for 68060 [Nikl]
+- Add contrib/infback9 with deflate64 decoding (unsupported)
+- For MVS define NO_vsnprintf and undefine FAR [van Burik]
+- Add pragma for fdopen on MVS [van Burik]
+
+Changes in 1.2.0.5 (8 September 2003)
+- Add OF to inflateBackEnd() declaration in zlib.h
+- Remember start when using gzdopen in the middle of a file
+- Use internal off_t counters in gz* functions to properly handle seeks
+- Perform more rigorous check for distance-too-far in inffast.c
+- Add Z_BLOCK flush option to return from inflate at block boundary
+- Set strm->data_type on return from inflate
+ - Indicate bits unused, if at block boundary, and if in last block
+- Replace size_t with ptrdiff_t in crc32.c, and check for correct size
+- Add condition so old NO_DEFLATE define still works for compatibility
+- FAQ update regarding the Windows DLL [Truta]
+- INDEX update: add qnx entry, remove aix entry [Truta]
+- Install zlib.3 into mandir [Wilson]
+- Move contrib/zlib_dll_FAQ.txt to win32/DLL_FAQ.txt; update [Truta]
+- Adapt the zlib interface to the new DLL convention guidelines [Truta]
+- Introduce ZLIB_WINAPI macro to allow the export of functions using
+ the WINAPI calling convention, for Visual Basic [Vollant, Truta]
+- Update msdos and win32 scripts and makefiles [Truta]
+- Export symbols by name, not by ordinal, in win32/zlib.def [Truta]
+- Add contrib/ada [Anisimkov]
+- Move asm files from contrib/vstudio/vc70_32 to contrib/asm386 [Truta]
+- Rename contrib/asm386 to contrib/masmx86 [Truta, Vollant]
+- Add contrib/masm686 [Truta]
+- Fix offsets in contrib/inflate86 and contrib/masmx86/inffas32.asm
+ [Truta, Vollant]
+- Update contrib/delphi; rename to contrib/pascal; add example [Truta]
+- Remove contrib/delphi2; add a new contrib/delphi [Truta]
+- Avoid inclusion of the nonstandard <memory.h> in contrib/iostream,
+ and fix some method prototypes [Truta]
+- Fix the ZCR_SEED2 constant to avoid warnings in contrib/minizip
+ [Truta]
+- Avoid the use of backslash (\) in contrib/minizip [Vollant]
+- Fix file time handling in contrib/untgz; update makefiles [Truta]
+- Update contrib/vstudio/vc70_32 to comply with the new DLL guidelines
+ [Vollant]
+- Remove contrib/vstudio/vc15_16 [Vollant]
+- Rename contrib/vstudio/vc70_32 to contrib/vstudio/vc7 [Truta]
+- Update README.contrib [Truta]
+- Invert the assignment order of match_head and s->prev[...] in
+ INSERT_STRING [Truta]
+- Compare TOO_FAR with 32767 instead of 32768, to avoid 16-bit warnings
+ [Truta]
+- Compare function pointers with 0, not with NULL or Z_NULL [Truta]
+- Fix prototype of syncsearch in inflate.c [Truta]
+- Introduce ASMINF macro to be enabled when using an ASM implementation
+ of inflate_fast [Truta]
+- Change NO_DEFLATE to NO_GZCOMPRESS [Truta]
+- Modify test_gzio in example.c to take a single file name as a
+ parameter [Truta]
+- Exit the example.c program if gzopen fails [Truta]
+- Add type casts around strlen in example.c [Truta]
+- Remove casting to sizeof in minigzip.c; give a proper type
+ to the variable compared with SUFFIX_LEN [Truta]
+- Update definitions of STDC and STDC99 in zconf.h [Truta]
+- Synchronize zconf.h with the new Windows DLL interface [Truta]
+- Use SYS16BIT instead of __32BIT__ to distinguish between
+ 16- and 32-bit platforms [Truta]
+- Use far memory allocators in small 16-bit memory models for
+ Turbo C [Truta]
+- Add info about the use of ASMV, ASMINF and ZLIB_WINAPI in
+ zlibCompileFlags [Truta]
+- Cygwin has vsnprintf [Wilson]
+- In Windows16, OS_CODE is 0, as in MSDOS [Truta]
+- In Cygwin, OS_CODE is 3 (Unix), not 11 (Windows32) [Wilson]
+
+Changes in 1.2.0.4 (10 August 2003)
+- Minor FAQ updates
+- Be more strict when checking inflateInit2's windowBits parameter
+- Change NO_GUNZIP compile option to NO_GZIP to cover deflate as well
+- Add gzip wrapper option to deflateInit2 using windowBits
+- Add updated QNX rule in configure and qnx directory [Bonnefoy]
+- Make inflate distance-too-far checks more rigorous
+- Clean up FAR usage in inflate
+- Add casting to sizeof() in gzio.c and minigzip.c
+
+Changes in 1.2.0.3 (19 July 2003)
+- Fix silly error in gzungetc() implementation [Vollant]
+- Update contrib/minizip and contrib/vstudio [Vollant]
+- Fix printf format in example.c
+- Correct cdecl support in zconf.in.h [Anisimkov]
+- Minor FAQ updates
+
+Changes in 1.2.0.2 (13 July 2003)
+- Add ZLIB_VERNUM in zlib.h for numerical preprocessor comparisons
+- Attempt to avoid warnings in crc32.c for pointer-int conversion
+- Add AIX to configure, remove aix directory [Bakker]
+- Add some casts to minigzip.c
+- Improve checking after insecure sprintf() or vsprintf() calls
+- Remove #elif's from crc32.c
+- Change leave label to inf_leave in inflate.c and infback.c to avoid
+ library conflicts
+- Remove inflate gzip decoding by default--only enable gzip decoding by
+ special request for stricter backward compatibility
+- Add zlibCompileFlags() function to return compilation information
+- More typecasting in deflate.c to avoid warnings
+- Remove leading underscore from _Capital #defines [Truta]
+- Fix configure to link shared library when testing
+- Add some Windows CE target adjustments [Mai]
+- Remove #define ZLIB_DLL in zconf.h [Vollant]
+- Add zlib.3 [Rodgers]
+- Update RFC URL in deflate.c and algorithm.txt [Mai]
+- Add zlib_dll_FAQ.txt to contrib [Truta]
+- Add UL to some constants [Truta]
+- Update minizip and vstudio [Vollant]
+- Remove vestigial NEED_DUMMY_RETURN from zconf.in.h
+- Expand use of NO_DUMMY_DECL to avoid all dummy structures
+- Added iostream3 to contrib [Schwardt]
+- Replace rewind() with fseek() for WinCE [Truta]
+- Improve setting of zlib format compression level flags
+ - Report 0 for huffman and rle strategies and for level == 0 or 1
+ - Report 2 only for level == 6
+- Only deal with 64K limit when necessary at compile time [Truta]
+- Allow TOO_FAR check to be turned off at compile time [Truta]
+- Add gzclearerr() function [Souza]
+- Add gzungetc() function
+
+Changes in 1.2.0.1 (17 March 2003)
+- Add Z_RLE strategy for run-length encoding [Truta]
+ - When Z_RLE requested, restrict matches to distance one
+ - Update zlib.h, minigzip.c, gzopen(), gzdopen() for Z_RLE
+- Correct FASTEST compilation to allow level == 0
+- Clean up what gets compiled for FASTEST
+- Incorporate changes to zconf.in.h [Vollant]
+ - Refine detection of Turbo C need for dummy returns
+ - Refine ZLIB_DLL compilation
+ - Include additional header file on VMS for off_t typedef
+- Try to use _vsnprintf where it supplants vsprintf [Vollant]
+- Add some casts in inffast.c
+- Enchance comments in zlib.h on what happens if gzprintf() tries to
+ write more than 4095 bytes before compression
+- Remove unused state from inflateBackEnd()
+- Remove exit(0) from minigzip.c, example.c
+- Get rid of all those darn tabs
+- Add "check" target to Makefile.in that does the same thing as "test"
+- Add "mostlyclean" and "maintainer-clean" targets to Makefile.in
+- Update contrib/inflate86 [Anderson]
+- Update contrib/testzlib, contrib/vstudio, contrib/minizip [Vollant]
+- Add msdos and win32 directories with makefiles [Truta]
+- More additions and improvements to the FAQ
+
+Changes in 1.2.0 (9 March 2003)
+- New and improved inflate code
+ - About 20% faster
+ - Does not allocate 32K window unless and until needed
+ - Automatically detects and decompresses gzip streams
+ - Raw inflate no longer needs an extra dummy byte at end
+ - Added inflateBack functions using a callback interface--even faster
+ than inflate, useful for file utilities (gzip, zip)
+ - Added inflateCopy() function to record state for random access on
+ externally generated deflate streams (e.g. in gzip files)
+ - More readable code (I hope)
+- New and improved crc32()
+ - About 50% faster, thanks to suggestions from Rodney Brown
+- Add deflateBound() and compressBound() functions
+- Fix memory leak in deflateInit2()
+- Permit setting dictionary for raw deflate (for parallel deflate)
+- Fix const declaration for gzwrite()
+- Check for some malloc() failures in gzio.c
+- Fix bug in gzopen() on single-byte file 0x1f
+- Fix bug in gzread() on concatenated file with 0x1f at end of buffer
+ and next buffer doesn't start with 0x8b
+- Fix uncompress() to return Z_DATA_ERROR on truncated input
+- Free memory at end of example.c
+- Remove MAX #define in trees.c (conflicted with some libraries)
+- Fix static const's in deflate.c, gzio.c, and zutil.[ch]
+- Declare malloc() and free() in gzio.c if STDC not defined
+- Use malloc() instead of calloc() in zutil.c if int big enough
+- Define STDC for AIX
+- Add aix/ with approach for compiling shared library on AIX
+- Add HP-UX support for shared libraries in configure
+- Add OpenUNIX support for shared libraries in configure
+- Use $cc instead of gcc to build shared library
+- Make prefix directory if needed when installing
+- Correct Macintosh avoidance of typedef Byte in zconf.h
+- Correct Turbo C memory allocation when under Linux
+- Use libz.a instead of -lz in Makefile (assure use of compiled library)
+- Update configure to check for snprintf or vsnprintf functions and their
+ return value, warn during make if using an insecure function
+- Fix configure problem with compile-time knowledge of HAVE_UNISTD_H that
+ is lost when library is used--resolution is to build new zconf.h
+- Documentation improvements (in zlib.h):
+ - Document raw deflate and inflate
+ - Update RFCs URL
+ - Point out that zlib and gzip formats are different
+ - Note that Z_BUF_ERROR is not fatal
+ - Document string limit for gzprintf() and possible buffer overflow
+ - Note requirement on avail_out when flushing
+ - Note permitted values of flush parameter of inflate()
+- Add some FAQs (and even answers) to the FAQ
+- Add contrib/inflate86/ for x86 faster inflate
+- Add contrib/blast/ for PKWare Data Compression Library decompression
+- Add contrib/puff/ simple inflate for deflate format description
+
+Changes in 1.1.4 (11 March 2002)
+- ZFREE was repeated on same allocation on some error conditions.
+ This creates a security problem described in
+ http://www.zlib.org/advisory-2002-03-11.txt
+- Returned incorrect error (Z_MEM_ERROR) on some invalid data
+- Avoid accesses before window for invalid distances with inflate window
+ less than 32K.
+- force windowBits > 8 to avoid a bug in the encoder for a window size
+ of 256 bytes. (A complete fix will be available in 1.1.5).
+
+Changes in 1.1.3 (9 July 1998)
+- fix "an inflate input buffer bug that shows up on rare but persistent
+ occasions" (Mark)
+- fix gzread and gztell for concatenated .gz files (Didier Le Botlan)
+- fix gzseek(..., SEEK_SET) in write mode
+- fix crc check after a gzeek (Frank Faubert)
+- fix miniunzip when the last entry in a zip file is itself a zip file
+ (J Lillge)
+- add contrib/asm586 and contrib/asm686 (Brian Raiter)
+ See http://www.muppetlabs.com/~breadbox/software/assembly.html
+- add support for Delphi 3 in contrib/delphi (Bob Dellaca)
+- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti)
+- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren)
+- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks)
+- added a FAQ file
+
+- Support gzdopen on Mac with Metrowerks (Jason Linhart)
+- Do not redefine Byte on Mac (Brad Pettit & Jason Linhart)
+- define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young)
+- avoid some warnings with Borland C (Tom Tanner)
+- fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant)
+- emulate utime() for WIN32 in contrib/untgz (Gilles Vollant)
+- allow several arguments to configure (Tim Mooney, Frodo Looijaard)
+- use libdir and includedir in Makefile.in (Tim Mooney)
+- support shared libraries on OSF1 V4 (Tim Mooney)
+- remove so_locations in "make clean" (Tim Mooney)
+- fix maketree.c compilation error (Glenn, Mark)
+- Python interface to zlib now in Python 1.5 (Jeremy Hylton)
+- new Makefile.riscos (Rich Walker)
+- initialize static descriptors in trees.c for embedded targets (Nick Smith)
+- use "foo-gz" in example.c for RISCOS and VMS (Nick Smith)
+- add the OS/2 files in Makefile.in too (Andrew Zabolotny)
+- fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane)
+- fix maketree.c to allow clean compilation of inffixed.h (Mark)
+- fix parameter check in deflateCopy (Gunther Nikl)
+- cleanup trees.c, use compressed_len only in debug mode (Christian Spieler)
+- Many portability patches by Christian Spieler:
+ . zutil.c, zutil.h: added "const" for zmem*
+ . Make_vms.com: fixed some typos
+ . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists
+ . msdos/Makefile.msc: remove "default rtl link library" info from obj files
+ . msdos/Makefile.*: use model-dependent name for the built zlib library
+ . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc:
+ new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT)
+- use define instead of typedef for Bytef also for MSC small/medium (Tom Lane)
+- replace __far with _far for better portability (Christian Spieler, Tom Lane)
+- fix test for errno.h in configure (Tim Newsham)
+
+Changes in 1.1.2 (19 March 98)
+- added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant)
+ See http://www.winimage.com/zLibDll/unzip.html
+- preinitialize the inflate tables for fixed codes, to make the code
+ completely thread safe (Mark)
+- some simplifications and slight speed-up to the inflate code (Mark)
+- fix gzeof on non-compressed files (Allan Schrum)
+- add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs)
+- use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn)
+- added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny)
+- add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori)
+- do not wrap extern "C" around system includes (Tom Lane)
+- mention zlib binding for TCL in README (Andreas Kupries)
+- added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert)
+- allow "make install prefix=..." even after configure (Glenn Randers-Pehrson)
+- allow "configure --prefix $HOME" (Tim Mooney)
+- remove warnings in example.c and gzio.c (Glenn Randers-Pehrson)
+- move Makefile.sas to amiga/Makefile.sas
+
+Changes in 1.1.1 (27 Feb 98)
+- fix macros _tr_tally_* in deflate.h for debug mode (Glenn Randers-Pehrson)
+- remove block truncation heuristic which had very marginal effect for zlib
+ (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the
+ compression ratio on some files. This also allows inlining _tr_tally for
+ matches in deflate_slow.
+- added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier)
+
+Changes in 1.1.0 (24 Feb 98)
+- do not return STREAM_END prematurely in inflate (John Bowler)
+- revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler)
+- compile with -DFASTEST to get compression code optimized for speed only
+- in minigzip, try mmap'ing the input file first (Miguel Albrecht)
+- increase size of I/O buffers in minigzip.c and gzio.c (not a big gain
+ on Sun but significant on HP)
+
+- add a pointer to experimental unzip library in README (Gilles Vollant)
+- initialize variable gcc in configure (Chris Herborth)
+
+Changes in 1.0.9 (17 Feb 1998)
+- added gzputs and gzgets functions
+- do not clear eof flag in gzseek (Mark Diekhans)
+- fix gzseek for files in transparent mode (Mark Diekhans)
+- do not assume that vsprintf returns the number of bytes written (Jens Krinke)
+- replace EXPORT with ZEXPORT to avoid conflict with other programs
+- added compress2 in zconf.h, zlib.def, zlib.dnt
+- new asm code from Gilles Vollant in contrib/asm386
+- simplify the inflate code (Mark):
+ . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new()
+ . ZALLOC the length list in inflate_trees_fixed() instead of using stack
+ . ZALLOC the value area for huft_build() instead of using stack
+ . Simplify Z_FINISH check in inflate()
+
+- Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8
+- in inftrees.c, avoid cc -O bug on HP (Farshid Elahi)
+- in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with
+ the declaration of FAR (Gilles VOllant)
+- install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann)
+- read_buf buf parameter of type Bytef* instead of charf*
+- zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout)
+- do not redeclare unlink in minigzip.c for WIN32 (John Bowler)
+- fix check for presence of directories in "make install" (Ian Willis)
+
+Changes in 1.0.8 (27 Jan 1998)
+- fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant)
+- fix gzgetc and gzputc for big endian systems (Markus Oberhumer)
+- added compress2() to allow setting the compression level
+- include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong)
+- use constant arrays for the static trees in trees.c instead of computing
+ them at run time (thanks to Ken Raeburn for this suggestion). To create
+ trees.h, compile with GEN_TREES_H and run "make test".
+- check return code of example in "make test" and display result
+- pass minigzip command line options to file_compress
+- simplifying code of inflateSync to avoid gcc 2.8 bug
+
+- support CC="gcc -Wall" in configure -s (QingLong)
+- avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn)
+- fix test for shared library support to avoid compiler warnings
+- zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant)
+- check for TARGET_OS_MAC in addition to MACOS (Brad Pettit)
+- do not use fdopen for Metrowerks on Mac (Brad Pettit))
+- add checks for gzputc and gzputc in example.c
+- avoid warnings in gzio.c and deflate.c (Andreas Kleinert)
+- use const for the CRC table (Ken Raeburn)
+- fixed "make uninstall" for shared libraries
+- use Tracev instead of Trace in infblock.c
+- in example.c use correct compressed length for test_sync
+- suppress +vnocompatwarnings in configure for HPUX (not always supported)
+
+Changes in 1.0.7 (20 Jan 1998)
+- fix gzseek which was broken in write mode
+- return error for gzseek to negative absolute position
+- fix configure for Linux (Chun-Chung Chen)
+- increase stack space for MSC (Tim Wegner)
+- get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant)
+- define EXPORTVA for gzprintf (Gilles Vollant)
+- added man page zlib.3 (Rick Rodgers)
+- for contrib/untgz, fix makedir() and improve Makefile
+
+- check gzseek in write mode in example.c
+- allocate extra buffer for seeks only if gzseek is actually called
+- avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant)
+- add inflateSyncPoint in zconf.h
+- fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def
+
+Changes in 1.0.6 (19 Jan 1998)
+- add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and
+ gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code)
+- Fix a deflate bug occuring only with compression level 0 (thanks to
+ Andy Buckler for finding this one).
+- In minigzip, pass transparently also the first byte for .Z files.
+- return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress()
+- check Z_FINISH in inflate (thanks to Marc Schluper)
+- Implement deflateCopy (thanks to Adam Costello)
+- make static libraries by default in configure, add --shared option.
+- move MSDOS or Windows specific files to directory msdos
+- suppress the notion of partial flush to simplify the interface
+ (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4)
+- suppress history buffer provided by application to simplify the interface
+ (this feature was not implemented anyway in 1.0.4)
+- next_in and avail_in must be initialized before calling inflateInit or
+ inflateInit2
+- add EXPORT in all exported functions (for Windows DLL)
+- added Makefile.nt (thanks to Stephen Williams)
+- added the unsupported "contrib" directory:
+ contrib/asm386/ by Gilles Vollant <info@winimage.com>
+ 386 asm code replacing longest_match().
+ contrib/iostream/ by Kevin Ruland <kevin@rodin.wustl.edu>
+ A C++ I/O streams interface to the zlib gz* functions
+ contrib/iostream2/ by Tyge Løvset <Tyge.Lovset@cmr.no>
+ Another C++ I/O streams interface
+ contrib/untgz/ by "Pedro A. Aranda Guti\irrez" <paag@tid.es>
+ A very simple tar.gz file extractor using zlib
+ contrib/visual-basic.txt by Carlos Rios <c_rios@sonda.cl>
+ How to use compress(), uncompress() and the gz* functions from VB.
+- pass params -f (filtered data), -h (huffman only), -1 to -9 (compression
+ level) in minigzip (thanks to Tom Lane)
+
+- use const for rommable constants in deflate
+- added test for gzseek and gztell in example.c
+- add undocumented function inflateSyncPoint() (hack for Paul Mackerras)
+- add undocumented function zError to convert error code to string
+ (for Tim Smithers)
+- Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code.
+- Use default memcpy for Symantec MSDOS compiler.
+- Add EXPORT keyword for check_func (needed for Windows DLL)
+- add current directory to LD_LIBRARY_PATH for "make test"
+- create also a link for libz.so.1
+- added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura)
+- use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX)
+- added -soname for Linux in configure (Chun-Chung Chen,
+- assign numbers to the exported functions in zlib.def (for Windows DLL)
+- add advice in zlib.h for best usage of deflateSetDictionary
+- work around compiler bug on Atari (cast Z_NULL in call of s->checkfn)
+- allow compilation with ANSI keywords only enabled for TurboC in large model
+- avoid "versionString"[0] (Borland bug)
+- add NEED_DUMMY_RETURN for Borland
+- use variable z_verbose for tracing in debug mode (L. Peter Deutsch).
+- allow compilation with CC
+- defined STDC for OS/2 (David Charlap)
+- limit external names to 8 chars for MVS (Thomas Lund)
+- in minigzip.c, use static buffers only for 16-bit systems
+- fix suffix check for "minigzip -d foo.gz"
+- do not return an error for the 2nd of two consecutive gzflush() (Felix Lee)
+- use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau)
+- added makelcc.bat for lcc-win32 (Tom St Denis)
+- in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe)
+- Avoid expanded $Id$. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion.
+- check for unistd.h in configure (for off_t)
+- remove useless check parameter in inflate_blocks_free
+- avoid useless assignment of s->check to itself in inflate_blocks_new
+- do not flush twice in gzclose (thanks to Ken Raeburn)
+- rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h
+- use NO_ERRNO_H instead of enumeration of operating systems with errno.h
+- work around buggy fclose on pipes for HP/UX
+- support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson)
+- fix configure if CC is already equal to gcc
+
+Changes in 1.0.5 (3 Jan 98)
+- Fix inflate to terminate gracefully when fed corrupted or invalid data
+- Use const for rommable constants in inflate
+- Eliminate memory leaks on error conditions in inflate
+- Removed some vestigial code in inflate
+- Update web address in README
+
+Changes in 1.0.4 (24 Jul 96)
+- In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF
+ bit, so the decompressor could decompress all the correct data but went
+ on to attempt decompressing extra garbage data. This affected minigzip too.
+- zlibVersion and gzerror return const char* (needed for DLL)
+- port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno)
+- use z_error only for DEBUG (avoid problem with DLLs)
+
+Changes in 1.0.3 (2 Jul 96)
+- use z_streamp instead of z_stream *, which is now a far pointer in MSDOS
+ small and medium models; this makes the library incompatible with previous
+ versions for these models. (No effect in large model or on other systems.)
+- return OK instead of BUF_ERROR if previous deflate call returned with
+ avail_out as zero but there is nothing to do
+- added memcmp for non STDC compilers
+- define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly)
+- define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO)
+- better check for 16-bit mode MSC (avoids problem with Symantec)
+
+Changes in 1.0.2 (23 May 96)
+- added Windows DLL support
+- added a function zlibVersion (for the DLL support)
+- fixed declarations using Bytef in infutil.c (pb with MSDOS medium model)
+- Bytef is define's instead of typedef'd only for Borland C
+- avoid reading uninitialized memory in example.c
+- mention in README that the zlib format is now RFC1950
+- updated Makefile.dj2
+- added algorithm.doc
+
+Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion]
+- fix array overlay in deflate.c which sometimes caused bad compressed data
+- fix inflate bug with empty stored block
+- fix MSDOS medium model which was broken in 0.99
+- fix deflateParams() which could generated bad compressed data.
+- Bytef is define'd instead of typedef'ed (work around Borland bug)
+- added an INDEX file
+- new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32),
+ Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas)
+- speed up adler32 for modern machines without auto-increment
+- added -ansi for IRIX in configure
+- static_init_done in trees.c is an int
+- define unlink as delete for VMS
+- fix configure for QNX
+- add configure branch for SCO and HPUX
+- avoid many warnings (unused variables, dead assignments, etc...)
+- no fdopen for BeOS
+- fix the Watcom fix for 32 bit mode (define FAR as empty)
+- removed redefinition of Byte for MKWERKS
+- work around an MWKERKS bug (incorrect merge of all .h files)
+
+Changes in 0.99 (27 Jan 96)
+- allow preset dictionary shared between compressor and decompressor
+- allow compression level 0 (no compression)
+- add deflateParams in zlib.h: allow dynamic change of compression level
+ and compression strategy.
+- test large buffers and deflateParams in example.c
+- add optional "configure" to build zlib as a shared library
+- suppress Makefile.qnx, use configure instead
+- fixed deflate for 64-bit systems (detected on Cray)
+- fixed inflate_blocks for 64-bit systems (detected on Alpha)
+- declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2)
+- always return Z_BUF_ERROR when deflate() has nothing to do
+- deflateInit and inflateInit are now macros to allow version checking
+- prefix all global functions and types with z_ with -DZ_PREFIX
+- make falloc completely reentrant (inftrees.c)
+- fixed very unlikely race condition in ct_static_init
+- free in reverse order of allocation to help memory manager
+- use zlib-1.0/* instead of zlib/* inside the tar.gz
+- make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith
+ -Wconversion -Wstrict-prototypes -Wmissing-prototypes"
+- allow gzread on concatenated .gz files
+- deflateEnd now returns Z_DATA_ERROR if it was premature
+- deflate is finally (?) fully deterministic (no matches beyond end of input)
+- Document Z_SYNC_FLUSH
+- add uninstall in Makefile
+- Check for __cpluplus in zlib.h
+- Better test in ct_align for partial flush
+- avoid harmless warnings for Borland C++
+- initialize hash_head in deflate.c
+- avoid warning on fdopen (gzio.c) for HP cc -Aa
+- include stdlib.h for STDC compilers
+- include errno.h for Cray
+- ignore error if ranlib doesn't exist
+- call ranlib twice for NeXTSTEP
+- use exec_prefix instead of prefix for libz.a
+- renamed ct_* as _tr_* to avoid conflict with applications
+- clear z->msg in inflateInit2 before any error return
+- initialize opaque in example.c, gzio.c, deflate.c and inflate.c
+- fixed typo in zconf.h (_GNUC__ => __GNUC__)
+- check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode)
+- fix typo in Make_vms.com (f$trnlnm -> f$getsyi)
+- in fcalloc, normalize pointer if size > 65520 bytes
+- don't use special fcalloc for 32 bit Borland C++
+- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc...
+- use Z_BINARY instead of BINARY
+- document that gzclose after gzdopen will close the file
+- allow "a" as mode in gzopen.
+- fix error checking in gzread
+- allow skipping .gz extra-field on pipes
+- added reference to Perl interface in README
+- put the crc table in FAR data (I dislike more and more the medium model :)
+- added get_crc_table
+- added a dimension to all arrays (Borland C can't count).
+- workaround Borland C bug in declaration of inflate_codes_new & inflate_fast
+- guard against multiple inclusion of *.h (for precompiled header on Mac)
+- Watcom C pretends to be Microsoft C small model even in 32 bit mode.
+- don't use unsized arrays to avoid silly warnings by Visual C++:
+ warning C4746: 'inflate_mask' : unsized array treated as '__far'
+ (what's wrong with far data in far model?).
+- define enum out of inflate_blocks_state to allow compilation with C++
+
+Changes in 0.95 (16 Aug 95)
+- fix MSDOS small and medium model (now easier to adapt to any compiler)
+- inlined send_bits
+- fix the final (:-) bug for deflate with flush (output was correct but
+ not completely flushed in rare occasions).
+- default window size is same for compression and decompression
+ (it's now sufficient to set MAX_WBITS in zconf.h).
+- voidp -> voidpf and voidnp -> voidp (for consistency with other
+ typedefs and because voidnp was not near in large model).
+
+Changes in 0.94 (13 Aug 95)
+- support MSDOS medium model
+- fix deflate with flush (could sometimes generate bad output)
+- fix deflateReset (zlib header was incorrectly suppressed)
+- added support for VMS
+- allow a compression level in gzopen()
+- gzflush now calls fflush
+- For deflate with flush, flush even if no more input is provided.
+- rename libgz.a as libz.a
+- avoid complex expression in infcodes.c triggering Turbo C bug
+- work around a problem with gcc on Alpha (in INSERT_STRING)
+- don't use inline functions (problem with some gcc versions)
+- allow renaming of Byte, uInt, etc... with #define.
+- avoid warning about (unused) pointer before start of array in deflate.c
+- avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c
+- avoid reserved word 'new' in trees.c
+
+Changes in 0.93 (25 June 95)
+- temporarily disable inline functions
+- make deflate deterministic
+- give enough lookahead for PARTIAL_FLUSH
+- Set binary mode for stdin/stdout in minigzip.c for OS/2
+- don't even use signed char in inflate (not portable enough)
+- fix inflate memory leak for segmented architectures
+
+Changes in 0.92 (3 May 95)
+- don't assume that char is signed (problem on SGI)
+- Clear bit buffer when starting a stored block
+- no memcpy on Pyramid
+- suppressed inftest.c
+- optimized fill_window, put longest_match inline for gcc
+- optimized inflate on stored blocks.
+- untabify all sources to simplify patches
+
+Changes in 0.91 (2 May 95)
+- Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h
+- Document the memory requirements in zconf.h
+- added "make install"
+- fix sync search logic in inflateSync
+- deflate(Z_FULL_FLUSH) now works even if output buffer too short
+- after inflateSync, don't scare people with just "lo world"
+- added support for DJGPP
+
+Changes in 0.9 (1 May 95)
+- don't assume that zalloc clears the allocated memory (the TurboC bug
+ was Mark's bug after all :)
+- let again gzread copy uncompressed data unchanged (was working in 0.71)
+- deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented
+- added a test of inflateSync in example.c
+- moved MAX_WBITS to zconf.h because users might want to change that.
+- document explicitly that zalloc(64K) on MSDOS must return a normalized
+ pointer (zero offset)
+- added Makefiles for Microsoft C, Turbo C, Borland C++
+- faster crc32()
+
+Changes in 0.8 (29 April 95)
+- added fast inflate (inffast.c)
+- deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this
+ is incompatible with previous versions of zlib which returned Z_OK.
+- work around a TurboC compiler bug (bad code for b << 0, see infutil.h)
+ (actually that was not a compiler bug, see 0.81 above)
+- gzread no longer reads one extra byte in certain cases
+- In gzio destroy(), don't reference a freed structure
+- avoid many warnings for MSDOS
+- avoid the ERROR symbol which is used by MS Windows
+
+Changes in 0.71 (14 April 95)
+- Fixed more MSDOS compilation problems :( There is still a bug with
+ TurboC large model.
+
+Changes in 0.7 (14 April 95)
+- Added full inflate support.
+- Simplified the crc32() interface. The pre- and post-conditioning
+ (one's complement) is now done inside crc32(). WARNING: this is
+ incompatible with previous versions; see zlib.h for the new usage.
+
+Changes in 0.61 (12 April 95)
+- workaround for a bug in TurboC. example and minigzip now work on MSDOS.
+
+Changes in 0.6 (11 April 95)
+- added minigzip.c
+- added gzdopen to reopen a file descriptor as gzFile
+- added transparent reading of non-gziped files in gzread.
+- fixed bug in gzread (don't read crc as data)
+- fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose).
+- don't allocate big arrays in the stack (for MSDOS)
+- fix some MSDOS compilation problems
+
+Changes in 0.5:
+- do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but
+ not yet Z_FULL_FLUSH.
+- support decompression but only in a single step (forced Z_FINISH)
+- added opaque object for zalloc and zfree.
+- added deflateReset and inflateReset
+- added a variable zlib_version for consistency checking.
+- renamed the 'filter' parameter of deflateInit2 as 'strategy'.
+ Added Z_FILTERED and Z_HUFFMAN_ONLY constants.
+
+Changes in 0.4:
+- avoid "zip" everywhere, use zlib instead of ziplib.
+- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush
+ if compression method == 8.
+- added adler32 and crc32
+- renamed deflateOptions as deflateInit2, call one or the other but not both
+- added the method parameter for deflateInit2.
+- added inflateInit2
+- simplied considerably deflateInit and inflateInit by not supporting
+ user-provided history buffer. This is supported only in deflateInit2
+ and inflateInit2.
+
+Changes in 0.3:
+- prefix all macro names with Z_
+- use Z_FINISH instead of deflateEnd to finish compression.
+- added Z_HUFFMAN_ONLY
+- added gzerror()
diff --git a/zlib/FAQ b/zlib/FAQ
new file mode 100644
index 0000000..bb5355f
--- /dev/null
+++ b/zlib/FAQ
@@ -0,0 +1,337 @@
+
+ Frequently Asked Questions about zlib
+
+
+If your question is not there, please check the zlib home page
+http://www.zlib.org which may have more recent information.
+The lastest zlib FAQ is at http://www.gzip.org/zlib/zlib_faq.html
+
+
+ 1. Is zlib Y2K-compliant?
+
+ Yes. zlib doesn't handle dates.
+
+ 2. Where can I get a Windows DLL version?
+
+ The zlib sources can be compiled without change to produce a DLL.
+ See the file win32/DLL_FAQ.txt in the zlib distribution.
+ Pointers to the precompiled DLL are found in the zlib web site at
+ http://www.zlib.org.
+
+ 3. Where can I get a Visual Basic interface to zlib?
+
+ See
+ * http://www.dogma.net/markn/articles/zlibtool/zlibtool.htm
+ * contrib/visual-basic.txt in the zlib distribution
+ * win32/DLL_FAQ.txt in the zlib distribution
+
+ 4. compress() returns Z_BUF_ERROR.
+
+ Make sure that before the call of compress, the length of the compressed
+ buffer is equal to the total size of the compressed buffer and not
+ zero. For Visual Basic, check that this parameter is passed by reference
+ ("as any"), not by value ("as long").
+
+ 5. deflate() or inflate() returns Z_BUF_ERROR.
+
+ Before making the call, make sure that avail_in and avail_out are not
+ zero. When setting the parameter flush equal to Z_FINISH, also make sure
+ that avail_out is big enough to allow processing all pending input.
+ Note that a Z_BUF_ERROR is not fatal--another call to deflate() or
+ inflate() can be made with more input or output space. A Z_BUF_ERROR
+ may in fact be unavoidable depending on how the functions are used, since
+ it is not possible to tell whether or not there is more output pending
+ when strm.avail_out returns with zero.
+
+ 6. Where's the zlib documentation (man pages, etc.)?
+
+ It's in zlib.h for the moment, and Francis S. Lin has converted it to a
+ web page zlib.html. Volunteers to transform this to Unix-style man pages,
+ please contact us (zlib@gzip.org). Examples of zlib usage are in the files
+ example.c and minigzip.c.
+
+ 7. Why don't you use GNU autoconf or libtool or ...?
+
+ Because we would like to keep zlib as a very small and simple
+ package. zlib is rather portable and doesn't need much configuration.
+
+ 8. I found a bug in zlib.
+
+ Most of the time, such problems are due to an incorrect usage of
+ zlib. Please try to reproduce the problem with a small program and send
+ the corresponding source to us at zlib@gzip.org . Do not send
+ multi-megabyte data files without prior agreement.
+
+ 9. Why do I get "undefined reference to gzputc"?
+
+ If "make test" produces something like
+
+ example.o(.text+0x154): undefined reference to `gzputc'
+
+ check that you don't have old files libz.* in /usr/lib, /usr/local/lib or
+ /usr/X11R6/lib. Remove any old versions, then do "make install".
+
+10. I need a Delphi interface to zlib.
+
+ See the contrib/delphi directory in the zlib distribution.
+
+11. Can zlib handle .zip archives?
+
+ Not by itself, no. See the directory contrib/minizip in the zlib
+ distribution.
+
+12. Can zlib handle .Z files?
+
+ No, sorry. You have to spawn an uncompress or gunzip subprocess, or adapt
+ the code of uncompress on your own.
+
+13. How can I make a Unix shared library?
+
+ make clean
+ ./configure -s
+ make
+
+14. How do I install a shared zlib library on Unix?
+
+ After the above, then:
+
+ make install
+
+ However, many flavors of Unix come with a shared zlib already installed.
+ Before going to the trouble of compiling a shared version of zlib and
+ trying to install it, you may want to check if it's already there! If you
+ can #include <zlib.h>, it's there. The -lz option will probably link to it.
+
+15. I have a question about OttoPDF.
+
+ We are not the authors of OttoPDF. The real author is on the OttoPDF web
+ site: Joel Hainley, jhainley@myndkryme.com.
+
+16. Can zlib decode Flate data in an Adobe PDF file?
+
+ Yes. See http://www.fastio.com/ (ClibPDF), or http://www.pdflib.com/ .
+ To modify PDF forms, see http://sourceforge.net/projects/acroformtool/ .
+
+17. Why am I getting this "register_frame_info not found" error on Solaris?
+
+ After installing zlib 1.1.4 on Solaris 2.6, running applications using zlib
+ generates an error such as:
+
+ ld.so.1: rpm: fatal: relocation error: file /usr/local/lib/libz.so:
+ symbol __register_frame_info: referenced symbol not found
+
+ The symbol __register_frame_info is not part of zlib, it is generated by
+ the C compiler (cc or gcc). You must recompile applications using zlib
+ which have this problem. This problem is specific to Solaris. See
+ http://www.sunfreeware.com for Solaris versions of zlib and applications
+ using zlib.
+
+18. Why does gzip give an error on a file I make with compress/deflate?
+
+ The compress and deflate functions produce data in the zlib format, which
+ is different and incompatible with the gzip format. The gz* functions in
+ zlib on the other hand use the gzip format. Both the zlib and gzip
+ formats use the same compressed data format internally, but have different
+ headers and trailers around the compressed data.
+
+19. Ok, so why are there two different formats?
+
+ The gzip format was designed to retain the directory information about
+ a single file, such as the name and last modification date. The zlib
+ format on the other hand was designed for in-memory and communication
+ channel applications, and has a much more compact header and trailer and
+ uses a faster integrity check than gzip.
+
+20. Well that's nice, but how do I make a gzip file in memory?
+
+ You can request that deflate write the gzip format instead of the zlib
+ format using deflateInit2(). You can also request that inflate decode
+ the gzip format using inflateInit2(). Read zlib.h for more details.
+
+ Note that you cannot specify special gzip header contents (e.g. a file
+ name or modification date), nor will inflate tell you what was in the
+ gzip header. If you need to customize the header or see what's in it,
+ you can use the raw deflate and inflate operations and the crc32()
+ function and roll your own gzip encoding and decoding. Read the gzip
+ RFC 1952 for details of the header and trailer format.
+
+21. Is zlib thread-safe?
+
+ Yes. However any library routines that zlib uses and any application-
+ provided memory allocation routines must also be thread-safe. zlib's gz*
+ functions use stdio library routines, and most of zlib's functions use the
+ library memory allocation routines by default. zlib's Init functions allow
+ for the application to provide custom memory allocation routines.
+
+ Of course, you should only operate on any given zlib or gzip stream from a
+ single thread at a time.
+
+22. Can I use zlib in my commercial application?
+
+ Yes. Please read the license in zlib.h.
+
+23. Is zlib under the GNU license?
+
+ No. Please read the license in zlib.h.
+
+24. The license says that altered source versions must be "plainly marked". So
+ what exactly do I need to do to meet that requirement?
+
+ You need to change the ZLIB_VERSION and ZLIB_VERNUM #defines in zlib.h. In
+ particular, the final version number needs to be changed to "f", and an
+ identification string should be appended to ZLIB_VERSION. Version numbers
+ x.x.x.f are reserved for modifications to zlib by others than the zlib
+ maintainers. For example, if the version of the base zlib you are altering
+ is "1.2.3.4", then in zlib.h you should change ZLIB_VERNUM to 0x123f, and
+ ZLIB_VERSION to something like "1.2.3.f-zachary-mods-v3". You can also
+ update the version strings in deflate.c and inftrees.c.
+
+ For altered source distributions, you should also note the origin and
+ nature of the changes in zlib.h, as well as in ChangeLog and README, along
+ with the dates of the alterations. The origin should include at least your
+ name (or your company's name), and an email address to contact for help or
+ issues with the library.
+
+ Note that distributing a compiled zlib library along with zlib.h and
+ zconf.h is also a source distribution, and so you should change
+ ZLIB_VERSION and ZLIB_VERNUM and note the origin and nature of the changes
+ in zlib.h as you would for a full source distribution.
+
+25. Will zlib work on a big-endian or little-endian architecture, and can I
+ exchange compressed data between them?
+
+ Yes and yes.
+
+26. Will zlib work on a 64-bit machine?
+
+ It should. It has been tested on 64-bit machines, and has no dependence
+ on any data types being limited to 32-bits in length. If you have any
+ difficulties, please provide a complete problem report to zlib@gzip.org
+
+27. Will zlib decompress data from the PKWare Data Compression Library?
+
+ No. The PKWare DCL uses a completely different compressed data format
+ than does PKZIP and zlib. However, you can look in zlib's contrib/blast
+ directory for a possible solution to your problem.
+
+28. Can I access data randomly in a compressed stream?
+
+ No, not without some preparation. If when compressing you periodically
+ use Z_FULL_FLUSH, carefully write all the pending data at those points,
+ and keep an index of those locations, then you can start decompression
+ at those points. You have to be careful to not use Z_FULL_FLUSH too
+ often, since it can significantly degrade compression.
+
+29. Does zlib work on MVS, OS/390, CICS, etc.?
+
+ We don't know for sure. We have heard occasional reports of success on
+ these systems. If you do use it on one of these, please provide us with
+ a report, instructions, and patches that we can reference when we get
+ these questions. Thanks.
+
+30. Is there some simpler, easier to read version of inflate I can look at
+ to understand the deflate format?
+
+ First off, you should read RFC 1951. Second, yes. Look in zlib's
+ contrib/puff directory.
+
+31. Does zlib infringe on any patents?
+
+ As far as we know, no. In fact, that was originally the whole point behind
+ zlib. Look here for some more information:
+
+ http://www.gzip.org/#faq11
+
+32. Can zlib work with greater than 4 GB of data?
+
+ Yes. inflate() and deflate() will process any amount of data correctly.
+ Each call of inflate() or deflate() is limited to input and output chunks
+ of the maximum value that can be stored in the compiler's "unsigned int"
+ type, but there is no limit to the number of chunks. Note however that the
+ strm.total_in and strm_total_out counters may be limited to 4 GB. These
+ counters are provided as a convenience and are not used internally by
+ inflate() or deflate(). The application can easily set up its own counters
+ updated after each call of inflate() or deflate() to count beyond 4 GB.
+ compress() and uncompress() may be limited to 4 GB, since they operate in a
+ single call. gzseek() and gztell() may be limited to 4 GB depending on how
+ zlib is compiled. See the zlibCompileFlags() function in zlib.h.
+
+ The word "may" appears several times above since there is a 4 GB limit
+ only if the compiler's "long" type is 32 bits. If the compiler's "long"
+ type is 64 bits, then the limit is 16 exabytes.
+
+33. Does zlib have any security vulnerabilities?
+
+ The only one that we are aware of is potentially in gzprintf(). If zlib
+ is compiled to use sprintf() or vsprintf(), then there is no protection
+ against a buffer overflow of a 4K string space, other than the caller of
+ gzprintf() assuring that the output will not exceed 4K. On the other
+ hand, if zlib is compiled to use snprintf() or vsnprintf(), which should
+ normally be the case, then there is no vulnerability. The ./configure
+ script will display warnings if an insecure variation of sprintf() will
+ be used by gzprintf(). Also the zlibCompileFlags() function will return
+ information on what variant of sprintf() is used by gzprintf().
+
+ If you don't have snprintf() or vsnprintf() and would like one, you can
+ find a portable implementation here:
+
+ http://www.ijs.si/software/snprintf/
+
+ Note that you should be using the most recent version of zlib. Versions
+ 1.1.3 and before were subject to a double-free vulnerability.
+
+34. Is there a Java version of zlib?
+
+ Probably what you want is to use zlib in Java. zlib is already included
+ as part of the Java SDK in the java.util.zip package. If you really want
+ a version of zlib written in the Java language, look on the zlib home
+ page for links: http://www.zlib.org/
+
+35. I get this or that compiler or source-code scanner warning when I crank it
+ up to maximally-pedantic. Can't you guys write proper code?
+
+ Many years ago, we gave up attempting to avoid warnings on every compiler
+ in the universe. It just got to be a waste of time, and some compilers
+ were downright silly. So now, we simply make sure that the code always
+ works.
+
+36. Will zlib read the (insert any ancient or arcane format here) compressed
+ data format?
+
+ Probably not. Look in the comp.compression FAQ for pointers to various
+ formats and associated software.
+
+37. How can I encrypt/decrypt zip files with zlib?
+
+ zlib doesn't support encryption. The original PKZIP encryption is very weak
+ and can be broken with freely available programs. To get strong encryption,
+ use GnuPG, http://www.gnupg.org/ , which already includes zlib compression.
+ For PKZIP compatible "encryption", look at http://www.info-zip.org/
+
+38. What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings?
+
+ "gzip" is the gzip format, and "deflate" is the zlib format. They should
+ probably have called the second one "zlib" instead to avoid confusion
+ with the raw deflate compressed data format. While the HTTP 1.1 RFC 2616
+ correctly points to the zlib specification in RFC 1950 for the "deflate"
+ transfer encoding, there have been reports of servers and browsers that
+ incorrectly produce or expect raw deflate data per the deflate
+ specficiation in RFC 1951, most notably Microsoft. So even though the
+ "deflate" transfer encoding using the zlib format would be the more
+ efficient approach (and in fact exactly what the zlib format was designed
+ for), using the "gzip" transfer encoding is probably more reliable due to
+ an unfortunate choice of name on the part of the HTTP 1.1 authors.
+
+ Bottom line: use the gzip format for HTTP 1.1 encoding.
+
+39. Does zlib support the new "Deflate64" format introduced by PKWare?
+
+ No. PKWare has apparently decided to keep that format proprietary, since
+ they have not documented it as they have previous compression formats.
+ In any case, the compression improvements are so modest compared to other
+ more modern approaches, that it's not worth the effort to implement.
+
+40. Can you please sign these lengthy legal documents and fax them back to us
+ so that we can use your software in our product?
+
+ No. Go away. Shoo.
diff --git a/zlib/INDEX b/zlib/INDEX
new file mode 100644
index 0000000..4d7eac4
--- /dev/null
+++ b/zlib/INDEX
@@ -0,0 +1,51 @@
+ChangeLog history of changes
+FAQ Frequently Asked Questions about zlib
+INDEX this file
+Makefile makefile for Unix (generated by configure)
+Makefile.in makefile for Unix (template for configure)
+README guess what
+algorithm.txt description of the (de)compression algorithm
+configure configure script for Unix
+zconf.in.h template for zconf.h (used by configure)
+
+amiga/ makefiles for Amiga SAS C
+as400/ makefiles for IBM AS/400
+msdos/ makefiles for MSDOS
+old/ makefiles for various architectures and zlib documentation
+ files that have not yet been updated for zlib 1.2.x
+projects/ projects for various Integrated Development Environments
+qnx/ makefiles for QNX
+win32/ makefiles for Windows
+
+ zlib public header files (must be kept):
+zconf.h
+zlib.h
+
+ private source files used to build the zlib library:
+adler32.c
+compress.c
+crc32.c
+crc32.h
+deflate.c
+deflate.h
+gzio.c
+infback.c
+inffast.c
+inffast.h
+inffixed.h
+inflate.c
+inflate.h
+inftrees.c
+inftrees.h
+trees.c
+trees.h
+uncompr.c
+zutil.c
+zutil.h
+
+ source files for sample programs:
+example.c
+minigzip.c
+
+ unsupported contribution by third parties
+See contrib/README.contrib
diff --git a/zlib/Makefile b/zlib/Makefile
new file mode 100644
index 0000000..bbcca3a
--- /dev/null
+++ b/zlib/Makefile
@@ -0,0 +1,154 @@
+# Makefile for zlib
+# Copyright (C) 1995-2003 Jean-loup Gailly.
+# For conditions of distribution and use, see copyright notice in zlib.h
+
+# To compile and test, type:
+# ./configure; make test
+# The call of configure is optional if you don't have special requirements
+# If you wish to build zlib as a shared library, use: ./configure -s
+
+# To use the asm code, type:
+# cp contrib/asm?86/match.S ./match.S
+# make LOC=-DASMV OBJA=match.o
+
+# To install /usr/local/lib/libz.* and /usr/local/include/zlib.h, type:
+# make install
+# To install in $HOME instead of /usr/local, use:
+# make install prefix=$HOME
+
+CC=cc
+
+CFLAGS=-O
+#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7
+#CFLAGS=-g -DDEBUG
+#CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \
+# -Wstrict-prototypes -Wmissing-prototypes
+
+LDFLAGS=libz.a
+LDSHARED=$(CC)
+CPP=$(CC) -E
+
+LIBS=libz.a
+SHAREDLIB=libz.so
+SHAREDLIBV=libz.so.1.2.2
+SHAREDLIBM=libz.so.1
+
+AR=ar rc
+RANLIB=ranlib
+TAR=tar
+SHELL=/bin/sh
+EXE=
+
+prefix = /usr/local
+exec_prefix = ${prefix}
+libdir = ${exec_prefix}/lib
+includedir = ${prefix}/include
+mandir = ${prefix}/share/man
+man3dir = ${mandir}/man3
+
+OBJS = adler32.o compress.o crc32.o gzio.o uncompr.o deflate.o trees.o \
+ zutil.o inflate.o infback.o inftrees.o inffast.o
+
+OBJA =
+# to use the asm code: make OBJA=match.o
+
+TEST_OBJS = example.o minigzip.o
+
+all: example$(EXE) minigzip$(EXE)
+
+check: test
+test: all
+ @LD_LIBRARY_PATH=.:$(LD_LIBRARY_PATH) ; export LD_LIBRARY_PATH; \
+ echo hello world | ./minigzip | ./minigzip -d || \
+ echo ' *** minigzip test FAILED ***' ; \
+ if ./example; then \
+ echo ' *** zlib test OK ***'; \
+ else \
+ echo ' *** zlib test FAILED ***'; \
+ fi
+
+libz.a: $(OBJS) $(OBJA)
+ $(AR) $@ $(OBJS) $(OBJA)
+ -@ ($(RANLIB) $@ || true) >/dev/null 2>&1
+
+match.o: match.S
+ $(CPP) match.S > _match.s
+ $(CC) -c _match.s
+ mv _match.o match.o
+ rm -f _match.s
+
+$(SHAREDLIBV): $(OBJS)
+ $(LDSHARED) -o $@ $(OBJS)
+ rm -f $(SHAREDLIB) $(SHAREDLIBM)
+ ln -s $@ $(SHAREDLIB)
+ ln -s $@ $(SHAREDLIBM)
+
+example$(EXE): example.o $(LIBS)
+ $(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS)
+
+minigzip$(EXE): minigzip.o $(LIBS)
+ $(CC) $(CFLAGS) -o $@ minigzip.o $(LDFLAGS)
+
+install: $(LIBS)
+ -@if [ ! -d $(exec_prefix) ]; then mkdir -p $(exec_prefix); fi
+ -@if [ ! -d $(includedir) ]; then mkdir -p $(includedir); fi
+ -@if [ ! -d $(libdir) ]; then mkdir -p $(libdir); fi
+ -@if [ ! -d $(man3dir) ]; then mkdir -p $(man3dir); fi
+ cp zlib.h zconf.h $(includedir)
+ chmod 644 $(includedir)/zlib.h $(includedir)/zconf.h
+ cp $(LIBS) $(libdir)
+ cd $(libdir); chmod 755 $(LIBS)
+ -@(cd $(libdir); $(RANLIB) libz.a || true) >/dev/null 2>&1
+ cd $(libdir); if test -f $(SHAREDLIBV); then \
+ rm -f $(SHAREDLIB) $(SHAREDLIBM); \
+ ln -s $(SHAREDLIBV) $(SHAREDLIB); \
+ ln -s $(SHAREDLIBV) $(SHAREDLIBM); \
+ (ldconfig || true) >/dev/null 2>&1; \
+ fi
+ cp zlib.3 $(man3dir)
+ chmod 644 $(man3dir)/zlib.3
+# The ranlib in install is needed on NeXTSTEP which checks file times
+# ldconfig is for Linux
+
+uninstall:
+ cd $(includedir); \
+ cd $(libdir); rm -f libz.a; \
+ if test -f $(SHAREDLIBV); then \
+ rm -f $(SHAREDLIBV) $(SHAREDLIB) $(SHAREDLIBM); \
+ fi
+ cd $(man3dir); rm -f zlib.3
+
+mostlyclean: clean
+clean:
+ rm -f *.o *~ example$(EXE) minigzip$(EXE) \
+ libz.* foo.gz so_locations \
+ _match.s maketree contrib/infback9/*.o
+
+maintainer-clean: distclean
+distclean: clean
+ cp -p Makefile.in Makefile
+ cp -p zconf.in.h zconf.h
+ rm -f .DS_Store
+
+tags:
+ etags *.[ch]
+
+depend:
+ makedepend -- $(CFLAGS) -- *.[ch]
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
+adler32.o: zlib.h zconf.h
+compress.o: zlib.h zconf.h
+crc32.o: crc32.h zlib.h zconf.h
+deflate.o: deflate.h zutil.h zlib.h zconf.h
+example.o: zlib.h zconf.h
+gzio.o: zutil.h zlib.h zconf.h
+inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
+inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
+infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
+inftrees.o: zutil.h zlib.h zconf.h inftrees.h
+minigzip.o: zlib.h zconf.h
+trees.o: deflate.h zutil.h zlib.h zconf.h trees.h
+uncompr.o: zlib.h zconf.h
+zutil.o: zutil.h zlib.h zconf.h
diff --git a/zlib/Makefile.in b/zlib/Makefile.in
new file mode 100644
index 0000000..bbcca3a
--- /dev/null
+++ b/zlib/Makefile.in
@@ -0,0 +1,154 @@
+# Makefile for zlib
+# Copyright (C) 1995-2003 Jean-loup Gailly.
+# For conditions of distribution and use, see copyright notice in zlib.h
+
+# To compile and test, type:
+# ./configure; make test
+# The call of configure is optional if you don't have special requirements
+# If you wish to build zlib as a shared library, use: ./configure -s
+
+# To use the asm code, type:
+# cp contrib/asm?86/match.S ./match.S
+# make LOC=-DASMV OBJA=match.o
+
+# To install /usr/local/lib/libz.* and /usr/local/include/zlib.h, type:
+# make install
+# To install in $HOME instead of /usr/local, use:
+# make install prefix=$HOME
+
+CC=cc
+
+CFLAGS=-O
+#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7
+#CFLAGS=-g -DDEBUG
+#CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \
+# -Wstrict-prototypes -Wmissing-prototypes
+
+LDFLAGS=libz.a
+LDSHARED=$(CC)
+CPP=$(CC) -E
+
+LIBS=libz.a
+SHAREDLIB=libz.so
+SHAREDLIBV=libz.so.1.2.2
+SHAREDLIBM=libz.so.1
+
+AR=ar rc
+RANLIB=ranlib
+TAR=tar
+SHELL=/bin/sh
+EXE=
+
+prefix = /usr/local
+exec_prefix = ${prefix}
+libdir = ${exec_prefix}/lib
+includedir = ${prefix}/include
+mandir = ${prefix}/share/man
+man3dir = ${mandir}/man3
+
+OBJS = adler32.o compress.o crc32.o gzio.o uncompr.o deflate.o trees.o \
+ zutil.o inflate.o infback.o inftrees.o inffast.o
+
+OBJA =
+# to use the asm code: make OBJA=match.o
+
+TEST_OBJS = example.o minigzip.o
+
+all: example$(EXE) minigzip$(EXE)
+
+check: test
+test: all
+ @LD_LIBRARY_PATH=.:$(LD_LIBRARY_PATH) ; export LD_LIBRARY_PATH; \
+ echo hello world | ./minigzip | ./minigzip -d || \
+ echo ' *** minigzip test FAILED ***' ; \
+ if ./example; then \
+ echo ' *** zlib test OK ***'; \
+ else \
+ echo ' *** zlib test FAILED ***'; \
+ fi
+
+libz.a: $(OBJS) $(OBJA)
+ $(AR) $@ $(OBJS) $(OBJA)
+ -@ ($(RANLIB) $@ || true) >/dev/null 2>&1
+
+match.o: match.S
+ $(CPP) match.S > _match.s
+ $(CC) -c _match.s
+ mv _match.o match.o
+ rm -f _match.s
+
+$(SHAREDLIBV): $(OBJS)
+ $(LDSHARED) -o $@ $(OBJS)
+ rm -f $(SHAREDLIB) $(SHAREDLIBM)
+ ln -s $@ $(SHAREDLIB)
+ ln -s $@ $(SHAREDLIBM)
+
+example$(EXE): example.o $(LIBS)
+ $(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS)
+
+minigzip$(EXE): minigzip.o $(LIBS)
+ $(CC) $(CFLAGS) -o $@ minigzip.o $(LDFLAGS)
+
+install: $(LIBS)
+ -@if [ ! -d $(exec_prefix) ]; then mkdir -p $(exec_prefix); fi
+ -@if [ ! -d $(includedir) ]; then mkdir -p $(includedir); fi
+ -@if [ ! -d $(libdir) ]; then mkdir -p $(libdir); fi
+ -@if [ ! -d $(man3dir) ]; then mkdir -p $(man3dir); fi
+ cp zlib.h zconf.h $(includedir)
+ chmod 644 $(includedir)/zlib.h $(includedir)/zconf.h
+ cp $(LIBS) $(libdir)
+ cd $(libdir); chmod 755 $(LIBS)
+ -@(cd $(libdir); $(RANLIB) libz.a || true) >/dev/null 2>&1
+ cd $(libdir); if test -f $(SHAREDLIBV); then \
+ rm -f $(SHAREDLIB) $(SHAREDLIBM); \
+ ln -s $(SHAREDLIBV) $(SHAREDLIB); \
+ ln -s $(SHAREDLIBV) $(SHAREDLIBM); \
+ (ldconfig || true) >/dev/null 2>&1; \
+ fi
+ cp zlib.3 $(man3dir)
+ chmod 644 $(man3dir)/zlib.3
+# The ranlib in install is needed on NeXTSTEP which checks file times
+# ldconfig is for Linux
+
+uninstall:
+ cd $(includedir); \
+ cd $(libdir); rm -f libz.a; \
+ if test -f $(SHAREDLIBV); then \
+ rm -f $(SHAREDLIBV) $(SHAREDLIB) $(SHAREDLIBM); \
+ fi
+ cd $(man3dir); rm -f zlib.3
+
+mostlyclean: clean
+clean:
+ rm -f *.o *~ example$(EXE) minigzip$(EXE) \
+ libz.* foo.gz so_locations \
+ _match.s maketree contrib/infback9/*.o
+
+maintainer-clean: distclean
+distclean: clean
+ cp -p Makefile.in Makefile
+ cp -p zconf.in.h zconf.h
+ rm -f .DS_Store
+
+tags:
+ etags *.[ch]
+
+depend:
+ makedepend -- $(CFLAGS) -- *.[ch]
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
+adler32.o: zlib.h zconf.h
+compress.o: zlib.h zconf.h
+crc32.o: crc32.h zlib.h zconf.h
+deflate.o: deflate.h zutil.h zlib.h zconf.h
+example.o: zlib.h zconf.h
+gzio.o: zutil.h zlib.h zconf.h
+inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
+inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
+infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
+inftrees.o: zutil.h zlib.h zconf.h inftrees.h
+minigzip.o: zlib.h zconf.h
+trees.o: deflate.h zutil.h zlib.h zconf.h trees.h
+uncompr.o: zlib.h zconf.h
+zutil.o: zutil.h zlib.h zconf.h
diff --git a/zlib/README b/zlib/README
new file mode 100644
index 0000000..c3453b8
--- /dev/null
+++ b/zlib/README
@@ -0,0 +1,126 @@
+ZLIB DATA COMPRESSION LIBRARY
+
+zlib 1.2.2 is a general purpose data compression library. All the code is
+thread safe. The data format used by the zlib library is described by RFCs
+(Request for Comments) 1950 to 1952 in the files
+http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format)
+and rfc1952.txt (gzip format). These documents are also available in other
+formats from ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html
+
+All functions of the compression library are documented in the file zlib.h
+(volunteer to write man pages welcome, contact zlib@gzip.org). A usage example
+of the library is given in the file example.c which also tests that the library
+is working correctly. Another example is given in the file minigzip.c. The
+compression library itself is composed of all source files except example.c and
+minigzip.c.
+
+To compile all files and run the test program, follow the instructions given at
+the top of Makefile. In short "make test; make install" should work for most
+machines. For Unix: "./configure; make test; make install" For MSDOS, use one
+of the special makefiles such as Makefile.msc. For VMS, use Make_vms.com or
+descrip.mms.
+
+Questions about zlib should be sent to <zlib@gzip.org>, or to Gilles Vollant
+<info@winimage.com> for the Windows DLL version. The zlib home page is
+http://www.zlib.org or http://www.gzip.org/zlib/ Before reporting a problem,
+please check this site to verify that you have the latest version of zlib;
+otherwise get the latest version and check whether the problem still exists or
+not.
+
+PLEASE read the zlib FAQ http://www.gzip.org/zlib/zlib_faq.html before asking
+for help.
+
+Mark Nelson <markn@ieee.org> wrote an article about zlib for the Jan. 1997
+issue of Dr. Dobb's Journal; a copy of the article is available in
+http://dogma.net/markn/articles/zlibtool/zlibtool.htm
+
+The changes made in version 1.2.2 are documented in the file ChangeLog.
+
+Unsupported third party contributions are provided in directory "contrib".
+
+A Java implementation of zlib is available in the Java Development Kit
+http://java.sun.com/j2se/1.4.2/docs/api/java/util/zip/package-summary.html
+See the zlib home page http://www.zlib.org for details.
+
+A Perl interface to zlib written by Paul Marquess <pmqs@cpan.org> is in the
+CPAN (Comprehensive Perl Archive Network) sites
+http://www.cpan.org/modules/by-module/Compress/
+
+A Python interface to zlib written by A.M. Kuchling <amk@amk.ca> is
+available in Python 1.5 and later versions, see
+http://www.python.org/doc/lib/module-zlib.html
+
+A zlib binding for TCL written by Andreas Kupries <a.kupries@westend.com> is
+availlable at http://www.oche.de/~akupries/soft/trf/trf_zip.html
+
+An experimental package to read and write files in .zip format, written on top
+of zlib by Gilles Vollant <info@winimage.com>, is available in the
+contrib/minizip directory of zlib.
+
+
+Notes for some targets:
+
+- For Windows DLL versions, please see win32/DLL_FAQ.txt
+
+- For 64-bit Irix, deflate.c must be compiled without any optimization. With
+ -O, one libpng test fails. The test works in 32 bit mode (with the -n32
+ compiler flag). The compiler bug has been reported to SGI.
+
+- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works
+ when compiled with cc.
+
+- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is
+ necessary to get gzprintf working correctly. This is done by configure.
+
+- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with
+ other compilers. Use "make test" to check your compiler.
+
+- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers.
+
+- For PalmOs, see http://palmzlib.sourceforge.net/
+
+- When building a shared, i.e. dynamic library on Mac OS X, the library must be
+ installed before testing (do "make install" before "make test"), since the
+ library location is specified in the library.
+
+
+Acknowledgments:
+
+ The deflate format used by zlib was defined by Phil Katz. The deflate
+ and zlib specifications were written by L. Peter Deutsch. Thanks to all the
+ people who reported problems and suggested various improvements in zlib;
+ they are too numerous to cite here.
+
+Copyright notice:
+
+ (C) 1995-2004 Jean-loup Gailly and Mark Adler
+
+ 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.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+If you use the zlib library in a product, we would appreciate *not*
+receiving lengthy legal documents to sign. The sources are provided
+for free but without warranty of any kind. The library has been
+entirely written by Jean-loup Gailly and Mark Adler; it does not
+include third-party code.
+
+If you redistribute modified sources, we would appreciate that you include
+in the file ChangeLog history information documenting your changes. Please
+read the FAQ for more information on the distribution of modified source
+versions.
diff --git a/zlib/Zlib.dsp b/zlib/Zlib.dsp
new file mode 100644
index 0000000..21818dd
--- /dev/null
+++ b/zlib/Zlib.dsp
@@ -0,0 +1,188 @@
+# Microsoft Developer Studio Project File - Name="Zlib" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=Zlib - 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 "Zlib.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 "Zlib.mak" CFG="Zlib - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "Zlib - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "Zlib - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "Zlib - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Zlib___Win32_Release"
+# PROP BASE Intermediate_Dir "Zlib___Win32_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 "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /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
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF "$(CFG)" == "Zlib - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Zlib___Win32_Debug"
+# PROP BASE Intermediate_Dir "Zlib___Win32_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 "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /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
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF
+
+# Begin Target
+
+# Name "Zlib - Win32 Release"
+# Name "Zlib - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\adler32.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\compress.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\crc32.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\deflate.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\gzio.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\infback.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\inffast.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\inflate.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\inftrees.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\minigzip.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\trees.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\uncompr.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\zutil.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\crc32.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\deflate.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\inffast.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\inffixed.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\inflate.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\inftrees.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\trees.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zconf.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zconf.in.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zutil.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/zlib/Zlib.dsw b/zlib/Zlib.dsw
new file mode 100644
index 0000000..d0435f6
--- /dev/null
+++ b/zlib/Zlib.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Zlib"=.\Zlib.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/zlib/Zlib.ncb b/zlib/Zlib.ncb
new file mode 100644
index 0000000..fcb2c2f
--- /dev/null
+++ b/zlib/Zlib.ncb
Binary files differ
diff --git a/zlib/Zlib.opt b/zlib/Zlib.opt
new file mode 100644
index 0000000..0441266
--- /dev/null
+++ b/zlib/Zlib.opt
Binary files differ
diff --git a/zlib/Zlib.plg b/zlib/Zlib.plg
new file mode 100644
index 0000000..1129185
--- /dev/null
+++ b/zlib/Zlib.plg
@@ -0,0 +1,68 @@
+<html>
+<body>
+<pre>
+<h1>Build Log</h1>
+<h3>
+--------------------Configuration: Zlib - Win32 Release--------------------
+</h3>
+<h3>Command Lines</h3>
+Creating temporary file "H:\TMP\RSP85.tmp" with contents
+[
+/nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fp"Release/Zlib.pch" /YX /Fo"Release/" /Fd"Release/" /FD /c
+"C:\GameDev\Zlib\adler32.c"
+"C:\GameDev\Zlib\compress.c"
+"C:\GameDev\Zlib\crc32.c"
+"C:\GameDev\Zlib\deflate.c"
+"C:\GameDev\Zlib\gzio.c"
+"C:\GameDev\Zlib\infback.c"
+"C:\GameDev\Zlib\inffast.c"
+"C:\GameDev\Zlib\inflate.c"
+"C:\GameDev\Zlib\inftrees.c"
+"C:\GameDev\Zlib\minigzip.c"
+"C:\GameDev\Zlib\trees.c"
+"C:\GameDev\Zlib\uncompr.c"
+"C:\GameDev\Zlib\zutil.c"
+]
+Creating command line "cl.exe @H:\TMP\RSP85.tmp"
+Creating temporary file "H:\TMP\RSP86.tmp" with contents
+[
+/nologo /out:"Release\Zlib.lib"
+.\Release\adler32.obj
+.\Release\compress.obj
+.\Release\crc32.obj
+.\Release\deflate.obj
+.\Release\gzio.obj
+.\Release\infback.obj
+.\Release\inffast.obj
+.\Release\inflate.obj
+.\Release\inftrees.obj
+.\Release\minigzip.obj
+.\Release\trees.obj
+.\Release\uncompr.obj
+.\Release\zutil.obj
+]
+Creating command line "link.exe -lib @H:\TMP\RSP86.tmp"
+<h3>Output Window</h3>
+Compiling...
+adler32.c
+compress.c
+crc32.c
+deflate.c
+gzio.c
+infback.c
+inffast.c
+inflate.c
+inftrees.c
+minigzip.c
+trees.c
+uncompr.c
+zutil.c
+Creating library...
+
+
+
+<h3>Results</h3>
+Zlib.lib - 0 error(s), 0 warning(s)
+</pre>
+</body>
+</html>
diff --git a/zlib/Zlib.vcxproj b/zlib/Zlib.vcxproj
new file mode 100644
index 0000000..a163fa9
--- /dev/null
+++ b/zlib/Zlib.vcxproj
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Template|Win32">
+ <Configuration>Template</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <SccProjectName />
+ <SccLocalPath />
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Template|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Template|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>.\Debug\</OutDir>
+ <IntDir>.\Debug\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>.\Release\</OutDir>
+ <IntDir>.\Release\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+ <FunctionLevelLinking>false</FunctionLevelLinking>
+ <Optimization>Disabled</Optimization>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <WarningLevel>Level3</WarningLevel>
+ <MinimalRebuild>true</MinimalRebuild>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AssemblerListingLocation>.\Debug\</AssemblerListingLocation>
+ <PrecompiledHeaderOutputFile>.\Debug\Zlib.pch</PrecompiledHeaderOutputFile>
+ <ObjectFileName>.\Debug\</ObjectFileName>
+ <ProgramDataBaseFileName>.\Debug\</ProgramDataBaseFileName>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ </ClCompile>
+ <ResourceCompile>
+ <Culture>0x0409</Culture>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug\Zlib.bsc</OutputFile>
+ </Bscmake>
+ <Lib>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug\Zlib.lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <StringPooling>true</StringPooling>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <Optimization>MaxSpeed</Optimization>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <WarningLevel>Level3</WarningLevel>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AssemblerListingLocation>.\Release\</AssemblerListingLocation>
+ <PrecompiledHeaderOutputFile>.\Release\Zlib.pch</PrecompiledHeaderOutputFile>
+ <ObjectFileName>.\Release\</ObjectFileName>
+ <ProgramDataBaseFileName>.\Release\</ProgramDataBaseFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <Culture>0x0409</Culture>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release\Zlib.bsc</OutputFile>
+ </Bscmake>
+ <Lib>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release\Zlib.lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="adler32.c" />
+ <ClCompile Include="compress.c" />
+ <ClCompile Include="crc32.c" />
+ <ClCompile Include="deflate.c" />
+ <ClCompile Include="gzio.c" />
+ <ClCompile Include="infback.c" />
+ <ClCompile Include="inffast.c" />
+ <ClCompile Include="inflate.c" />
+ <ClCompile Include="inftrees.c" />
+ <ClCompile Include="minigzip.c" />
+ <ClCompile Include="trees.c" />
+ <ClCompile Include="uncompr.c" />
+ <ClCompile Include="zutil.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="crc32.h" />
+ <ClInclude Include="deflate.h" />
+ <ClInclude Include="inffast.h" />
+ <ClInclude Include="inffixed.h" />
+ <ClInclude Include="inflate.h" />
+ <ClInclude Include="inftrees.h" />
+ <ClInclude Include="trees.h" />
+ <ClInclude Include="zconf.h" />
+ <ClInclude Include="zconf.in.h" />
+ <ClInclude Include="zlib.h" />
+ <ClInclude Include="zutil.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/zlib/Zlib.vcxproj.filters b/zlib/Zlib.vcxproj.filters
new file mode 100644
index 0000000..49b7870
--- /dev/null
+++ b/zlib/Zlib.vcxproj.filters
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{fdbfe4fc-5863-4305-9494-63fb153c8952}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{6d63b947-3073-4f9d-b68f-d0232f40a6ca}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="adler32.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="compress.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="crc32.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="deflate.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="gzio.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="infback.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="inffast.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="inflate.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="inftrees.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="minigzip.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="trees.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="uncompr.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="zutil.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="crc32.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="deflate.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="inffast.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="inffixed.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="inflate.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="inftrees.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="trees.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="zconf.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="zconf.in.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="zlib.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="zutil.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/zlib/adler32.c b/zlib/adler32.c
new file mode 100644
index 0000000..dd6d60f
--- /dev/null
+++ b/zlib/adler32.c
@@ -0,0 +1,74 @@
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+#define BASE 65521UL /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf,i) {s1 += buf[i]; s2 += s1;}
+#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf) DO8(buf,0); DO8(buf,8);
+
+#ifdef NO_DIVIDE
+# define MOD(a) \
+ do { \
+ if (a >= (BASE << 16)) a -= (BASE << 16); \
+ if (a >= (BASE << 15)) a -= (BASE << 15); \
+ if (a >= (BASE << 14)) a -= (BASE << 14); \
+ if (a >= (BASE << 13)) a -= (BASE << 13); \
+ if (a >= (BASE << 12)) a -= (BASE << 12); \
+ if (a >= (BASE << 11)) a -= (BASE << 11); \
+ if (a >= (BASE << 10)) a -= (BASE << 10); \
+ if (a >= (BASE << 9)) a -= (BASE << 9); \
+ if (a >= (BASE << 8)) a -= (BASE << 8); \
+ if (a >= (BASE << 7)) a -= (BASE << 7); \
+ if (a >= (BASE << 6)) a -= (BASE << 6); \
+ if (a >= (BASE << 5)) a -= (BASE << 5); \
+ if (a >= (BASE << 4)) a -= (BASE << 4); \
+ if (a >= (BASE << 3)) a -= (BASE << 3); \
+ if (a >= (BASE << 2)) a -= (BASE << 2); \
+ if (a >= (BASE << 1)) a -= (BASE << 1); \
+ if (a >= BASE) a -= BASE; \
+ } while (0)
+#else
+# define MOD(a) a %= BASE
+#endif
+
+/* ========================================================================= */
+uLong ZEXPORT adler32(adler, buf, len)
+ uLong adler;
+ const Bytef *buf;
+ uInt len;
+{
+ unsigned long s1 = adler & 0xffff;
+ unsigned long s2 = (adler >> 16) & 0xffff;
+ int k;
+
+ if (buf == Z_NULL) return 1L;
+
+ while (len > 0) {
+ k = len < NMAX ? (int)len : NMAX;
+ len -= k;
+ while (k >= 16) {
+ DO16(buf);
+ buf += 16;
+ k -= 16;
+ }
+ if (k != 0) do {
+ s1 += *buf++;
+ s2 += s1;
+ } while (--k);
+ MOD(s1);
+ MOD(s2);
+ }
+ return (s2 << 16) | s1;
+}
diff --git a/zlib/algorithm.txt b/zlib/algorithm.txt
new file mode 100644
index 0000000..9f6b068
--- /dev/null
+++ b/zlib/algorithm.txt
@@ -0,0 +1,209 @@
+1. Compression algorithm (deflate)
+
+The deflation algorithm used by gzip (also zip and zlib) is a variation of
+LZ77 (Lempel-Ziv 1977, see reference below). It finds duplicated strings in
+the input data. The second occurrence of a string is replaced by a
+pointer to the previous string, in the form of a pair (distance,
+length). Distances are limited to 32K bytes, and lengths are limited
+to 258 bytes. When a string does not occur anywhere in the previous
+32K bytes, it is emitted as a sequence of literal bytes. (In this
+description, `string' must be taken as an arbitrary sequence of bytes,
+and is not restricted to printable characters.)
+
+Literals or match lengths are compressed with one Huffman tree, and
+match distances are compressed with another tree. The trees are stored
+in a compact form at the start of each block. The blocks can have any
+size (except that the compressed data for one block must fit in
+available memory). A block is terminated when deflate() determines that
+it would be useful to start another block with fresh trees. (This is
+somewhat similar to the behavior of LZW-based _compress_.)
+
+Duplicated strings are found using a hash table. All input strings of
+length 3 are inserted in the hash table. A hash index is computed for
+the next 3 bytes. If the hash chain for this index is not empty, all
+strings in the chain are compared with the current input string, and
+the longest match is selected.
+
+The hash chains are searched starting with the most recent strings, to
+favor small distances and thus take advantage of the Huffman encoding.
+The hash chains are singly linked. There are no deletions from the
+hash chains, the algorithm simply discards matches that are too old.
+
+To avoid a worst-case situation, very long hash chains are arbitrarily
+truncated at a certain length, determined by a runtime option (level
+parameter of deflateInit). So deflate() does not always find the longest
+possible match but generally finds a match which is long enough.
+
+deflate() also defers the selection of matches with a lazy evaluation
+mechanism. After a match of length N has been found, deflate() searches for
+a longer match at the next input byte. If a longer match is found, the
+previous match is truncated to a length of one (thus producing a single
+literal byte) and the process of lazy evaluation begins again. Otherwise,
+the original match is kept, and the next match search is attempted only N
+steps later.
+
+The lazy match evaluation is also subject to a runtime parameter. If
+the current match is long enough, deflate() reduces the search for a longer
+match, thus speeding up the whole process. If compression ratio is more
+important than speed, deflate() attempts a complete second search even if
+the first match is already long enough.
+
+The lazy match evaluation is not performed for the fastest compression
+modes (level parameter 1 to 3). For these fast modes, new strings
+are inserted in the hash table only when no match was found, or
+when the match is not too long. This degrades the compression ratio
+but saves time since there are both fewer insertions and fewer searches.
+
+
+2. Decompression algorithm (inflate)
+
+2.1 Introduction
+
+The key question is how to represent a Huffman code (or any prefix code) so
+that you can decode fast. The most important characteristic is that shorter
+codes are much more common than longer codes, so pay attention to decoding the
+short codes fast, and let the long codes take longer to decode.
+
+inflate() sets up a first level table that covers some number of bits of
+input less than the length of longest code. It gets that many bits from the
+stream, and looks it up in the table. The table will tell if the next
+code is that many bits or less and how many, and if it is, it will tell
+the value, else it will point to the next level table for which inflate()
+grabs more bits and tries to decode a longer code.
+
+How many bits to make the first lookup is a tradeoff between the time it
+takes to decode and the time it takes to build the table. If building the
+table took no time (and if you had infinite memory), then there would only
+be a first level table to cover all the way to the longest code. However,
+building the table ends up taking a lot longer for more bits since short
+codes are replicated many times in such a table. What inflate() does is
+simply to make the number of bits in the first table a variable, and then
+to set that variable for the maximum speed.
+
+For inflate, which has 286 possible codes for the literal/length tree, the size
+of the first table is nine bits. Also the distance trees have 30 possible
+values, and the size of the first table is six bits. Note that for each of
+those cases, the table ended up one bit longer than the ``average'' code
+length, i.e. the code length of an approximately flat code which would be a
+little more than eight bits for 286 symbols and a little less than five bits
+for 30 symbols.
+
+
+2.2 More details on the inflate table lookup
+
+Ok, you want to know what this cleverly obfuscated inflate tree actually
+looks like. You are correct that it's not a Huffman tree. It is simply a
+lookup table for the first, let's say, nine bits of a Huffman symbol. The
+symbol could be as short as one bit or as long as 15 bits. If a particular
+symbol is shorter than nine bits, then that symbol's translation is duplicated
+in all those entries that start with that symbol's bits. For example, if the
+symbol is four bits, then it's duplicated 32 times in a nine-bit table. If a
+symbol is nine bits long, it appears in the table once.
+
+If the symbol is longer than nine bits, then that entry in the table points
+to another similar table for the remaining bits. Again, there are duplicated
+entries as needed. The idea is that most of the time the symbol will be short
+and there will only be one table look up. (That's whole idea behind data
+compression in the first place.) For the less frequent long symbols, there
+will be two lookups. If you had a compression method with really long
+symbols, you could have as many levels of lookups as is efficient. For
+inflate, two is enough.
+
+So a table entry either points to another table (in which case nine bits in
+the above example are gobbled), or it contains the translation for the symbol
+and the number of bits to gobble. Then you start again with the next
+ungobbled bit.
+
+You may wonder: why not just have one lookup table for how ever many bits the
+longest symbol is? The reason is that if you do that, you end up spending
+more time filling in duplicate symbol entries than you do actually decoding.
+At least for deflate's output that generates new trees every several 10's of
+kbytes. You can imagine that filling in a 2^15 entry table for a 15-bit code
+would take too long if you're only decoding several thousand symbols. At the
+other extreme, you could make a new table for every bit in the code. In fact,
+that's essentially a Huffman tree. But then you spend two much time
+traversing the tree while decoding, even for short symbols.
+
+So the number of bits for the first lookup table is a trade of the time to
+fill out the table vs. the time spent looking at the second level and above of
+the table.
+
+Here is an example, scaled down:
+
+The code being decoded, with 10 symbols, from 1 to 6 bits long:
+
+A: 0
+B: 10
+C: 1100
+D: 11010
+E: 11011
+F: 11100
+G: 11101
+H: 11110
+I: 111110
+J: 111111
+
+Let's make the first table three bits long (eight entries):
+
+000: A,1
+001: A,1
+010: A,1
+011: A,1
+100: B,2
+101: B,2
+110: -> table X (gobble 3 bits)
+111: -> table Y (gobble 3 bits)
+
+Each entry is what the bits decode as and how many bits that is, i.e. how
+many bits to gobble. Or the entry points to another table, with the number of
+bits to gobble implicit in the size of the table.
+
+Table X is two bits long since the longest code starting with 110 is five bits
+long:
+
+00: C,1
+01: C,1
+10: D,2
+11: E,2
+
+Table Y is three bits long since the longest code starting with 111 is six
+bits long:
+
+000: F,2
+001: F,2
+010: G,2
+011: G,2
+100: H,2
+101: H,2
+110: I,3
+111: J,3
+
+So what we have here are three tables with a total of 20 entries that had to
+be constructed. That's compared to 64 entries for a single table. Or
+compared to 16 entries for a Huffman tree (six two entry tables and one four
+entry table). Assuming that the code ideally represents the probability of
+the symbols, it takes on the average 1.25 lookups per symbol. That's compared
+to one lookup for the single table, or 1.66 lookups per symbol for the
+Huffman tree.
+
+There, I think that gives you a picture of what's going on. For inflate, the
+meaning of a particular symbol is often more than just a letter. It can be a
+byte (a "literal"), or it can be either a length or a distance which
+indicates a base value and a number of bits to fetch after the code that is
+added to the base value. Or it might be the special end-of-block code. The
+data structures created in inftrees.c try to encode all that information
+compactly in the tables.
+
+
+Jean-loup Gailly Mark Adler
+jloup@gzip.org madler@alumni.caltech.edu
+
+
+References:
+
+[LZ77] Ziv J., Lempel A., ``A Universal Algorithm for Sequential Data
+Compression,'' IEEE Transactions on Information Theory, Vol. 23, No. 3,
+pp. 337-343.
+
+``DEFLATE Compressed Data Format Specification'' available in
+http://www.ietf.org/rfc/rfc1951.txt
diff --git a/zlib/compress.c b/zlib/compress.c
new file mode 100644
index 0000000..5a7eeee
--- /dev/null
+++ b/zlib/compress.c
@@ -0,0 +1,79 @@
+/* compress.c -- compress a memory buffer
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+/* ===========================================================================
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in deflateInit. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least 0.1% larger than sourceLen plus
+ 12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+
+ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+ Z_STREAM_ERROR if the level parameter is invalid.
+*/
+int ZEXPORT compress2 (dest, destLen, source, sourceLen, level)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+ int level;
+{
+ z_stream stream;
+ int err;
+
+ stream.next_in = (Bytef*)source;
+ stream.avail_in = (uInt)sourceLen;
+#ifdef MAXSEG_64K
+ /* Check for source > 64K on 16-bit machine: */
+ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+#endif
+ stream.next_out = dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+ stream.opaque = (voidpf)0;
+
+ err = deflateInit(&stream, level);
+ if (err != Z_OK) return err;
+
+ err = deflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ deflateEnd(&stream);
+ return err == Z_OK ? Z_BUF_ERROR : err;
+ }
+ *destLen = stream.total_out;
+
+ err = deflateEnd(&stream);
+ return err;
+}
+
+/* ===========================================================================
+ */
+int ZEXPORT compress (dest, destLen, source, sourceLen)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+{
+ return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION);
+}
+
+/* ===========================================================================
+ If the default memLevel or windowBits for deflateInit() is changed, then
+ this function needs to be updated.
+ */
+uLong ZEXPORT compressBound (sourceLen)
+ uLong sourceLen;
+{
+ return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11;
+}
diff --git a/zlib/configure b/zlib/configure
new file mode 100644
index 0000000..6784c57
--- /dev/null
+++ b/zlib/configure
@@ -0,0 +1,443 @@
+#!/bin/sh
+# configure script for zlib. This script is needed only if
+# you wish to build a shared library and your system supports them,
+# of if you need special compiler, flags or install directory.
+# Otherwise, you can just use directly "make test; make install"
+#
+# To create a shared library, use "configure --shared"; by default a static
+# library is created. If the primitive shared library support provided here
+# does not work, use ftp://prep.ai.mit.edu/pub/gnu/libtool-*.tar.gz
+#
+# To impose specific compiler or flags or install directory, use for example:
+# prefix=$HOME CC=cc CFLAGS="-O4" ./configure
+# or for csh/tcsh users:
+# (setenv prefix $HOME; setenv CC cc; setenv CFLAGS "-O4"; ./configure)
+# LDSHARED is the command to be used to create a shared library
+
+# Incorrect settings of CC or CFLAGS may prevent creating a shared library.
+# If you have problems, try without defining CC and CFLAGS before reporting
+# an error.
+
+LIBS=libz.a
+LDFLAGS="-L. ${LIBS}"
+VER=`sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`
+VER2=`sed -n -e '/VERSION "/s/.*"\([0-9]*\\.[0-9]*\)\\..*/\1/p' < zlib.h`
+VER1=`sed -n -e '/VERSION "/s/.*"\([0-9]*\)\\..*/\1/p' < zlib.h`
+AR=${AR-"ar rc"}
+RANLIB=${RANLIB-"ranlib"}
+prefix=${prefix-/usr/local}
+exec_prefix=${exec_prefix-'${prefix}'}
+libdir=${libdir-'${exec_prefix}/lib'}
+includedir=${includedir-'${prefix}/include'}
+mandir=${mandir-'${prefix}/share/man'}
+shared_ext='.so'
+shared=0
+gcc=0
+old_cc="$CC"
+old_cflags="$CFLAGS"
+
+while test $# -ge 1
+do
+case "$1" in
+ -h* | --h*)
+ echo 'usage:'
+ echo ' configure [--shared] [--prefix=PREFIX] [--exec_prefix=EXPREFIX]'
+ echo ' [--libdir=LIBDIR] [--includedir=INCLUDEDIR]'
+ exit 0;;
+ -p*=* | --p*=*) prefix=`echo $1 | sed 's/[-a-z_]*=//'`; shift;;
+ -e*=* | --e*=*) exec_prefix=`echo $1 | sed 's/[-a-z_]*=//'`; shift;;
+ -l*=* | --libdir=*) libdir=`echo $1 | sed 's/[-a-z_]*=//'`; shift;;
+ -i*=* | --includedir=*) includedir=`echo $1 | sed 's/[-a-z_]*=//'`;shift;;
+ -p* | --p*) prefix="$2"; shift; shift;;
+ -e* | --e*) exec_prefix="$2"; shift; shift;;
+ -l* | --l*) libdir="$2"; shift; shift;;
+ -i* | --i*) includedir="$2"; shift; shift;;
+ -s* | --s*) shared=1; shift;;
+ *) echo "unknown option: $1"; echo "$0 --help for help"; exit 1;;
+ esac
+done
+
+test=ztest$$
+cat > $test.c <<EOF
+extern int getchar();
+int hello() {return getchar();}
+EOF
+
+test -z "$CC" && echo Checking for gcc...
+cc=${CC-gcc}
+cflags=${CFLAGS-"-O3"}
+# to force the asm version use: CFLAGS="-O3 -DASMV" ./configure
+case "$cc" in
+ *gcc*) gcc=1;;
+esac
+
+if test "$gcc" -eq 1 && ($cc -c $cflags $test.c) 2>/dev/null; then
+ CC="$cc"
+ SFLAGS=${CFLAGS-"-fPIC -O3"}
+ CFLAGS="$cflags"
+ case `(uname -s || echo unknown) 2>/dev/null` in
+ Linux | linux | GNU | GNU/*) LDSHARED=${LDSHARED-"$cc -shared -Wl,-soname,libz.so.1"};;
+ CYGWIN* | Cygwin* | cygwin* | OS/2* )
+ EXE='.exe';;
+ QNX*) # This is for QNX6. I suppose that the QNX rule below is for QNX2,QNX4
+ # (alain.bonnefoy@icbt.com)
+ LDSHARED=${LDSHARED-"$cc -shared -Wl,-hlibz.so.1"};;
+ HP-UX*) LDSHARED=${LDSHARED-"$cc -shared $SFLAGS"}
+ shared_ext='.sl'
+ SHAREDLIB='libz.sl';;
+ Darwin*) shared_ext='.dylib'
+ SHAREDLIB=libz$shared_ext
+ SHAREDLIBV=libz.$VER$shared_ext
+ SHAREDLIBM=libz.$VER1$shared_ext
+ LDSHARED=${LDSHARED-"$cc -dynamiclib -install_name $libdir/$SHAREDLIBV -compatibility_version $VER2 -current_version $VER"};;
+ *) LDSHARED=${LDSHARED-"$cc -shared"};;
+ esac
+else
+ # find system name and corresponding cc options
+ CC=${CC-cc}
+ case `(uname -sr || echo unknown) 2>/dev/null` in
+ HP-UX*) SFLAGS=${CFLAGS-"-O +z"}
+ CFLAGS=${CFLAGS-"-O"}
+# LDSHARED=${LDSHARED-"ld -b +vnocompatwarnings"}
+ LDSHARED=${LDSHARED-"ld -b"}
+ shared_ext='.sl'
+ SHAREDLIB='libz.sl';;
+ IRIX*) SFLAGS=${CFLAGS-"-ansi -O2 -rpath ."}
+ CFLAGS=${CFLAGS-"-ansi -O2"}
+ LDSHARED=${LDSHARED-"cc -shared"};;
+ OSF1\ V4*) SFLAGS=${CFLAGS-"-O -std1"}
+ CFLAGS=${CFLAGS-"-O -std1"}
+ LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so -Wl,-msym -Wl,-rpath,$(libdir) -Wl,-set_version,${VER}:1.0"};;
+ OSF1*) SFLAGS=${CFLAGS-"-O -std1"}
+ CFLAGS=${CFLAGS-"-O -std1"}
+ LDSHARED=${LDSHARED-"cc -shared"};;
+ QNX*) SFLAGS=${CFLAGS-"-4 -O"}
+ CFLAGS=${CFLAGS-"-4 -O"}
+ LDSHARED=${LDSHARED-"cc"}
+ RANLIB=${RANLIB-"true"}
+ AR="cc -A";;
+ SCO_SV\ 3.2*) SFLAGS=${CFLAGS-"-O3 -dy -KPIC "}
+ CFLAGS=${CFLAGS-"-O3"}
+ LDSHARED=${LDSHARED-"cc -dy -KPIC -G"};;
+ SunOS\ 5*) SFLAGS=${CFLAGS-"-fast -xcg89 -KPIC -R."}
+ CFLAGS=${CFLAGS-"-fast -xcg89"}
+ LDSHARED=${LDSHARED-"cc -G"};;
+ SunOS\ 4*) SFLAGS=${CFLAGS-"-O2 -PIC"}
+ CFLAGS=${CFLAGS-"-O2"}
+ LDSHARED=${LDSHARED-"ld"};;
+ UNIX_System_V\ 4.2.0)
+ SFLAGS=${CFLAGS-"-KPIC -O"}
+ CFLAGS=${CFLAGS-"-O"}
+ LDSHARED=${LDSHARED-"cc -G"};;
+ UNIX_SV\ 4.2MP)
+ SFLAGS=${CFLAGS-"-Kconform_pic -O"}
+ CFLAGS=${CFLAGS-"-O"}
+ LDSHARED=${LDSHARED-"cc -G"};;
+ OpenUNIX\ 5)
+ SFLAGS=${CFLAGS-"-KPIC -O"}
+ CFLAGS=${CFLAGS-"-O"}
+ LDSHARED=${LDSHARED-"cc -G"};;
+ AIX*) # Courtesy of dbakker@arrayasolutions.com
+ SFLAGS=${CFLAGS-"-O -qmaxmem=8192"}
+ CFLAGS=${CFLAGS-"-O -qmaxmem=8192"}
+ LDSHARED=${LDSHARED-"xlc -G"};;
+ # send working options for other systems to support@gzip.org
+ *) SFLAGS=${CFLAGS-"-O"}
+ CFLAGS=${CFLAGS-"-O"}
+ LDSHARED=${LDSHARED-"cc -shared"};;
+ esac
+fi
+
+SHAREDLIB=${SHAREDLIB-"libz$shared_ext"}
+SHAREDLIBV=${SHAREDLIBV-"libz$shared_ext.$VER"}
+SHAREDLIBM=${SHAREDLIBM-"libz$shared_ext.$VER1"}
+
+if test $shared -eq 1; then
+ echo Checking for shared library support...
+ # we must test in two steps (cc then ld), required at least on SunOS 4.x
+ if test "`($CC -c $SFLAGS $test.c) 2>&1`" = "" &&
+ test "`($LDSHARED -o $test$shared_ext $test.o) 2>&1`" = ""; then
+ CFLAGS="$SFLAGS"
+ LIBS="$SHAREDLIBV"
+ echo Building shared library $SHAREDLIBV with $CC.
+ elif test -z "$old_cc" -a -z "$old_cflags"; then
+ echo No shared library support.
+ shared=0;
+ else
+ echo 'No shared library support; try without defining CC and CFLAGS'
+ shared=0;
+ fi
+fi
+if test $shared -eq 0; then
+ LDSHARED="$CC"
+ echo Building static library $LIBS version $VER with $CC.
+else
+ LDFLAGS="-L. ${SHAREDLIBV}"
+fi
+
+cat > $test.c <<EOF
+#include <unistd.h>
+int main() { return 0; }
+EOF
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+ sed < zconf.in.h "/HAVE_UNISTD_H/s%0%1%" > zconf.h
+ echo "Checking for unistd.h... Yes."
+else
+ cp -p zconf.in.h zconf.h
+ echo "Checking for unistd.h... No."
+fi
+
+cat > $test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+#include "zconf.h"
+
+int main()
+{
+#ifndef STDC
+ choke me
+#endif
+
+ return 0;
+}
+EOF
+
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+ echo "Checking whether to use vs[n]printf() or s[n]printf()... using vs[n]printf()"
+
+ cat > $test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+
+int mytest(char *fmt, ...)
+{
+ char buf[20];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ return 0;
+}
+
+int main()
+{
+ return (mytest("Hello%d\n", 1));
+}
+EOF
+
+ if test "`($CC $CFLAGS -o $test $test.c) 2>&1`" = ""; then
+ echo "Checking for vsnprintf() in stdio.h... Yes."
+
+ cat >$test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+
+int mytest(char *fmt, ...)
+{
+ int n;
+ char buf[20];
+ va_list ap;
+
+ va_start(ap, fmt);
+ n = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ return n;
+}
+
+int main()
+{
+ return (mytest("Hello%d\n", 1));
+}
+EOF
+
+ if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+ echo "Checking for return value of vsnprintf()... Yes."
+ else
+ CFLAGS="$CFLAGS -DHAS_vsnprintf_void"
+ echo "Checking for return value of vsnprintf()... No."
+ echo " WARNING: apparently vsnprintf() does not return a value. zlib"
+ echo " can build but will be open to possible string-format security"
+ echo " vulnerabilities."
+ fi
+ else
+ CFLAGS="$CFLAGS -DNO_vsnprintf"
+ echo "Checking for vsnprintf() in stdio.h... No."
+ echo " WARNING: vsnprintf() not found, falling back to vsprintf(). zlib"
+ echo " can build but will be open to possible buffer-overflow security"
+ echo " vulnerabilities."
+
+ cat >$test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+
+int mytest(char *fmt, ...)
+{
+ int n;
+ char buf[20];
+ va_list ap;
+
+ va_start(ap, fmt);
+ n = vsprintf(buf, fmt, ap);
+ va_end(ap);
+ return n;
+}
+
+int main()
+{
+ return (mytest("Hello%d\n", 1));
+}
+EOF
+
+ if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+ echo "Checking for return value of vsprintf()... Yes."
+ else
+ CFLAGS="$CFLAGS -DHAS_vsprintf_void"
+ echo "Checking for return value of vsprintf()... No."
+ echo " WARNING: apparently vsprintf() does not return a value. zlib"
+ echo " can build but will be open to possible string-format security"
+ echo " vulnerabilities."
+ fi
+ fi
+else
+ echo "Checking whether to use vs[n]printf() or s[n]printf()... using s[n]printf()"
+
+ cat >$test.c <<EOF
+#include <stdio.h>
+
+int mytest()
+{
+ char buf[20];
+
+ snprintf(buf, sizeof(buf), "%s", "foo");
+ return 0;
+}
+
+int main()
+{
+ return (mytest());
+}
+EOF
+
+ if test "`($CC $CFLAGS -o $test $test.c) 2>&1`" = ""; then
+ echo "Checking for snprintf() in stdio.h... Yes."
+
+ cat >$test.c <<EOF
+#include <stdio.h>
+
+int mytest()
+{
+ char buf[20];
+
+ return snprintf(buf, sizeof(buf), "%s", "foo");
+}
+
+int main()
+{
+ return (mytest());
+}
+EOF
+
+ if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+ echo "Checking for return value of snprintf()... Yes."
+ else
+ CFLAGS="$CFLAGS -DHAS_snprintf_void"
+ echo "Checking for return value of snprintf()... No."
+ echo " WARNING: apparently snprintf() does not return a value. zlib"
+ echo " can build but will be open to possible string-format security"
+ echo " vulnerabilities."
+ fi
+ else
+ CFLAGS="$CFLAGS -DNO_snprintf"
+ echo "Checking for snprintf() in stdio.h... No."
+ echo " WARNING: snprintf() not found, falling back to sprintf(). zlib"
+ echo " can build but will be open to possible buffer-overflow security"
+ echo " vulnerabilities."
+
+ cat >$test.c <<EOF
+#include <stdio.h>
+
+int mytest()
+{
+ char buf[20];
+
+ return sprintf(buf, "%s", "foo");
+}
+
+int main()
+{
+ return (mytest());
+}
+EOF
+
+ if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+ echo "Checking for return value of sprintf()... Yes."
+ else
+ CFLAGS="$CFLAGS -DHAS_sprintf_void"
+ echo "Checking for return value of sprintf()... No."
+ echo " WARNING: apparently sprintf() does not return a value. zlib"
+ echo " can build but will be open to possible string-format security"
+ echo " vulnerabilities."
+ fi
+ fi
+fi
+
+cat >$test.c <<EOF
+#include <errno.h>
+int main() { return 0; }
+EOF
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+ echo "Checking for errno.h... Yes."
+else
+ echo "Checking for errno.h... No."
+ CFLAGS="$CFLAGS -DNO_ERRNO_H"
+fi
+
+cat > $test.c <<EOF
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+caddr_t hello() {
+ return mmap((caddr_t)0, (off_t)0, PROT_READ, MAP_SHARED, 0, (off_t)0);
+}
+EOF
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+ CFLAGS="$CFLAGS -DUSE_MMAP"
+ echo Checking for mmap support... Yes.
+else
+ echo Checking for mmap support... No.
+fi
+
+CPP=${CPP-"$CC -E"}
+case $CFLAGS in
+ *ASMV*)
+ if test "`nm $test.o | grep _hello`" = ""; then
+ CPP="$CPP -DNO_UNDERLINE"
+ echo Checking for underline in external names... No.
+ else
+ echo Checking for underline in external names... Yes.
+ fi;;
+esac
+
+rm -f $test.[co] $test $test$shared_ext
+
+# udpate Makefile
+sed < Makefile.in "
+/^CC *=/s#=.*#=$CC#
+/^CFLAGS *=/s#=.*#=$CFLAGS#
+/^CPP *=/s#=.*#=$CPP#
+/^LDSHARED *=/s#=.*#=$LDSHARED#
+/^LIBS *=/s#=.*#=$LIBS#
+/^SHAREDLIB *=/s#=.*#=$SHAREDLIB#
+/^SHAREDLIBV *=/s#=.*#=$SHAREDLIBV#
+/^SHAREDLIBM *=/s#=.*#=$SHAREDLIBM#
+/^AR *=/s#=.*#=$AR#
+/^RANLIB *=/s#=.*#=$RANLIB#
+/^EXE *=/s#=.*#=$EXE#
+/^prefix *=/s#=.*#=$prefix#
+/^exec_prefix *=/s#=.*#=$exec_prefix#
+/^libdir *=/s#=.*#=$libdir#
+/^includedir *=/s#=.*#=$includedir#
+/^mandir *=/s#=.*#=$mandir#
+/^LDFLAGS *=/s#=.*#=$LDFLAGS#
+" > Makefile
diff --git a/zlib/crc32.c b/zlib/crc32.c
new file mode 100644
index 0000000..7373092
--- /dev/null
+++ b/zlib/crc32.c
@@ -0,0 +1,333 @@
+/* crc32.c -- compute the CRC-32 of a data stream
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster
+ * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing
+ * tables for updating the shift register in one step with three exclusive-ors
+ * instead of four steps with four exclusive-ors. This results about a factor
+ * of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3.
+ */
+
+/* @(#) $Id$ */
+
+/*
+ Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore
+ protection on the static variables used to control the first-use generation
+ of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should
+ first call get_crc_table() to initialize the tables before allowing more than
+ one thread to use crc32().
+ */
+
+#ifdef MAKECRCH
+# include <stdio.h>
+# ifndef DYNAMIC_CRC_TABLE
+# define DYNAMIC_CRC_TABLE
+# endif /* !DYNAMIC_CRC_TABLE */
+#endif /* MAKECRCH */
+
+#include "zutil.h" /* for STDC and FAR definitions */
+
+#define local static
+
+/* Find a four-byte integer type for crc32_little() and crc32_big(). */
+#ifndef NOBYFOUR
+# ifdef STDC /* need ANSI C limits.h to determine sizes */
+# include <limits.h>
+# define BYFOUR
+# if (UINT_MAX == 0xffffffffUL)
+ typedef unsigned int u4;
+# else
+# if (ULONG_MAX == 0xffffffffUL)
+ typedef unsigned long u4;
+# else
+# if (USHRT_MAX == 0xffffffffUL)
+ typedef unsigned short u4;
+# else
+# undef BYFOUR /* can't find a four-byte integer type! */
+# endif
+# endif
+# endif
+# endif /* STDC */
+#endif /* !NOBYFOUR */
+
+/* Definitions for doing the crc four data bytes at a time. */
+#ifdef BYFOUR
+# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \
+ (((w)&0xff00)<<8)+(((w)&0xff)<<24))
+ local unsigned long crc32_little OF((unsigned long,
+ const unsigned char FAR *, unsigned));
+ local unsigned long crc32_big OF((unsigned long,
+ const unsigned char FAR *, unsigned));
+# define TBLS 8
+#else
+# define TBLS 1
+#endif /* BYFOUR */
+
+#ifdef DYNAMIC_CRC_TABLE
+
+local volatile int crc_table_empty = 1;
+local unsigned long FAR crc_table[TBLS][256];
+local void make_crc_table OF((void));
+#ifdef MAKECRCH
+ local void write_table OF((FILE *, const unsigned long FAR *));
+#endif /* MAKECRCH */
+
+/*
+ Generate tables for a byte-wise 32-bit CRC calculation on the polynomial:
+ x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
+
+ Polynomials over GF(2) are represented in binary, one bit per coefficient,
+ with the lowest powers in the most significant bit. Then adding polynomials
+ is just exclusive-or, and multiplying a polynomial by x is a right shift by
+ one. If we call the above polynomial p, and represent a byte as the
+ polynomial q, also with the lowest power in the most significant bit (so the
+ byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
+ where a mod b means the remainder after dividing a by b.
+
+ This calculation is done using the shift-register method of multiplying and
+ taking the remainder. The register is initialized to zero, and for each
+ incoming bit, x^32 is added mod p to the register if the bit is a one (where
+ x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
+ x (which is shifting right by one and adding x^32 mod p if the bit shifted
+ out is a one). We start with the highest power (least significant bit) of
+ q and repeat for all eight bits of q.
+
+ The first table is simply the CRC of all possible eight bit values. This is
+ all the information needed to generate CRCs on data a byte at a time for all
+ combinations of CRC register values and incoming bytes. The remaining tables
+ allow for word-at-a-time CRC calculation for both big-endian and little-
+ endian machines, where a word is four bytes.
+*/
+local void make_crc_table()
+{
+ unsigned long c;
+ int n, k;
+ unsigned long poly; /* polynomial exclusive-or pattern */
+ /* terms of polynomial defining this crc (except x^32): */
+ static volatile int first = 1; /* flag to limit concurrent making */
+ static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
+
+ /* See if another task is already doing this (not thread-safe, but better
+ than nothing -- significantly reduces duration of vulnerability in
+ case the advice about DYNAMIC_CRC_TABLE is ignored) */
+ if (first) {
+ first = 0;
+
+ /* make exclusive-or pattern from polynomial (0xedb88320UL) */
+ poly = 0UL;
+ for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++)
+ poly |= 1UL << (31 - p[n]);
+
+ /* generate a crc for every 8-bit value */
+ for (n = 0; n < 256; n++) {
+ c = (unsigned long)n;
+ for (k = 0; k < 8; k++)
+ c = c & 1 ? poly ^ (c >> 1) : c >> 1;
+ crc_table[0][n] = c;
+ }
+
+#ifdef BYFOUR
+ /* generate crc for each value followed by one, two, and three zeros,
+ and then the byte reversal of those as well as the first table */
+ for (n = 0; n < 256; n++) {
+ c = crc_table[0][n];
+ crc_table[4][n] = REV(c);
+ for (k = 1; k < 4; k++) {
+ c = crc_table[0][c & 0xff] ^ (c >> 8);
+ crc_table[k][n] = c;
+ crc_table[k + 4][n] = REV(c);
+ }
+ }
+#endif /* BYFOUR */
+
+ crc_table_empty = 0;
+ }
+ else { /* not first */
+ /* wait for the other guy to finish (not efficient, but rare) */
+ while (crc_table_empty)
+ ;
+ }
+
+#ifdef MAKECRCH
+ /* write out CRC tables to crc32.h */
+ {
+ FILE *out;
+
+ out = fopen("crc32.h", "w");
+ if (out == NULL) return;
+ fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n");
+ fprintf(out, " * Generated automatically by crc32.c\n */\n\n");
+ fprintf(out, "local const unsigned long FAR ");
+ fprintf(out, "crc_table[TBLS][256] =\n{\n {\n");
+ write_table(out, crc_table[0]);
+# ifdef BYFOUR
+ fprintf(out, "#ifdef BYFOUR\n");
+ for (k = 1; k < 8; k++) {
+ fprintf(out, " },\n {\n");
+ write_table(out, crc_table[k]);
+ }
+ fprintf(out, "#endif\n");
+# endif /* BYFOUR */
+ fprintf(out, " }\n};\n");
+ fclose(out);
+ }
+#endif /* MAKECRCH */
+}
+
+#ifdef MAKECRCH
+local void write_table(out, table)
+ FILE *out;
+ const unsigned long FAR *table;
+{
+ int n;
+
+ for (n = 0; n < 256; n++)
+ fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n],
+ n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", "));
+}
+#endif /* MAKECRCH */
+
+#else /* !DYNAMIC_CRC_TABLE */
+/* ========================================================================
+ * Tables of CRC-32s of all single-byte values, made by make_crc_table().
+ */
+#include "crc32.h"
+#endif /* DYNAMIC_CRC_TABLE */
+
+/* =========================================================================
+ * This function can be used by asm versions of crc32()
+ */
+const unsigned long FAR * ZEXPORT get_crc_table()
+{
+#ifdef DYNAMIC_CRC_TABLE
+ if (crc_table_empty)
+ make_crc_table();
+#endif /* DYNAMIC_CRC_TABLE */
+ return (const unsigned long FAR *)crc_table;
+}
+
+/* ========================================================================= */
+#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8)
+#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1
+
+/* ========================================================================= */
+unsigned long ZEXPORT crc32(crc, buf, len)
+ unsigned long crc;
+ const unsigned char FAR *buf;
+ unsigned len;
+{
+ if (buf == Z_NULL) return 0UL;
+
+#ifdef DYNAMIC_CRC_TABLE
+ if (crc_table_empty)
+ make_crc_table();
+#endif /* DYNAMIC_CRC_TABLE */
+
+#ifdef BYFOUR
+ if (sizeof(void *) == sizeof(ptrdiff_t)) {
+ u4 endian;
+
+ endian = 1;
+ if (*((unsigned char *)(&endian)))
+ return crc32_little(crc, buf, len);
+ else
+ return crc32_big(crc, buf, len);
+ }
+#endif /* BYFOUR */
+ crc = crc ^ 0xffffffffUL;
+ while (len >= 8) {
+ DO8;
+ len -= 8;
+ }
+ if (len) do {
+ DO1;
+ } while (--len);
+ return crc ^ 0xffffffffUL;
+}
+
+#ifdef BYFOUR
+
+/* ========================================================================= */
+#define DOLIT4 c ^= *buf4++; \
+ c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \
+ crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24]
+#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4
+
+/* ========================================================================= */
+local unsigned long crc32_little(crc, buf, len)
+ unsigned long crc;
+ const unsigned char FAR *buf;
+ unsigned len;
+{
+ register u4 c;
+ register const u4 FAR *buf4;
+
+ c = (u4)crc;
+ c = ~c;
+ while (len && ((ptrdiff_t)buf & 3)) {
+ c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+ len--;
+ }
+
+ buf4 = (const u4 FAR *)buf;
+ while (len >= 32) {
+ DOLIT32;
+ len -= 32;
+ }
+ while (len >= 4) {
+ DOLIT4;
+ len -= 4;
+ }
+ buf = (const unsigned char FAR *)buf4;
+
+ if (len) do {
+ c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+ } while (--len);
+ c = ~c;
+ return (unsigned long)c;
+}
+
+/* ========================================================================= */
+#define DOBIG4 c ^= *++buf4; \
+ c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \
+ crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24]
+#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4
+
+/* ========================================================================= */
+local unsigned long crc32_big(crc, buf, len)
+ unsigned long crc;
+ const unsigned char FAR *buf;
+ unsigned len;
+{
+ register u4 c;
+ register const u4 FAR *buf4;
+
+ c = REV((u4)crc);
+ c = ~c;
+ while (len && ((ptrdiff_t)buf & 3)) {
+ c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+ len--;
+ }
+
+ buf4 = (const u4 FAR *)buf;
+ buf4--;
+ while (len >= 32) {
+ DOBIG32;
+ len -= 32;
+ }
+ while (len >= 4) {
+ DOBIG4;
+ len -= 4;
+ }
+ buf4++;
+ buf = (const unsigned char FAR *)buf4;
+
+ if (len) do {
+ c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+ } while (--len);
+ c = ~c;
+ return (unsigned long)(REV(c));
+}
+
+#endif /* BYFOUR */
diff --git a/zlib/crc32.h b/zlib/crc32.h
new file mode 100644
index 0000000..5de49bc
--- /dev/null
+++ b/zlib/crc32.h
@@ -0,0 +1,441 @@
+/* crc32.h -- tables for rapid CRC calculation
+ * Generated automatically by crc32.c
+ */
+
+local const unsigned long FAR crc_table[TBLS][256] =
+{
+ {
+ 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
+ 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,
+ 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,
+ 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
+ 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,
+ 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,
+ 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,
+ 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
+ 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,
+ 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,
+ 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,
+ 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
+ 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,
+ 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,
+ 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,
+ 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
+ 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,
+ 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,
+ 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,
+ 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
+ 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,
+ 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,
+ 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,
+ 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
+ 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,
+ 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,
+ 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,
+ 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
+ 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,
+ 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,
+ 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,
+ 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
+ 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,
+ 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,
+ 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,
+ 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
+ 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,
+ 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,
+ 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,
+ 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
+ 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,
+ 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,
+ 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,
+ 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
+ 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,
+ 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,
+ 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,
+ 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
+ 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,
+ 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,
+ 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,
+ 0x2d02ef8dUL
+#ifdef BYFOUR
+ },
+ {
+ 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL,
+ 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL,
+ 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL,
+ 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL,
+ 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL,
+ 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL,
+ 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL,
+ 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL,
+ 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL,
+ 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL,
+ 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL,
+ 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL,
+ 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL,
+ 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL,
+ 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL,
+ 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL,
+ 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL,
+ 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL,
+ 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL,
+ 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL,
+ 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL,
+ 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL,
+ 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL,
+ 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL,
+ 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL,
+ 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL,
+ 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL,
+ 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL,
+ 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL,
+ 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL,
+ 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL,
+ 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL,
+ 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL,
+ 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL,
+ 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL,
+ 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL,
+ 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL,
+ 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL,
+ 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL,
+ 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL,
+ 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL,
+ 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL,
+ 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL,
+ 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL,
+ 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL,
+ 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL,
+ 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL,
+ 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL,
+ 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL,
+ 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL,
+ 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL,
+ 0x9324fd72UL
+ },
+ {
+ 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL,
+ 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL,
+ 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL,
+ 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL,
+ 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL,
+ 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL,
+ 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL,
+ 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL,
+ 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL,
+ 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL,
+ 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL,
+ 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL,
+ 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL,
+ 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL,
+ 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL,
+ 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL,
+ 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL,
+ 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL,
+ 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL,
+ 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL,
+ 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL,
+ 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL,
+ 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL,
+ 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL,
+ 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL,
+ 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL,
+ 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL,
+ 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL,
+ 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL,
+ 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL,
+ 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL,
+ 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL,
+ 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL,
+ 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL,
+ 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL,
+ 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL,
+ 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL,
+ 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL,
+ 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL,
+ 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL,
+ 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL,
+ 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL,
+ 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL,
+ 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL,
+ 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL,
+ 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL,
+ 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL,
+ 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL,
+ 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL,
+ 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL,
+ 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL,
+ 0xbe9834edUL
+ },
+ {
+ 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL,
+ 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL,
+ 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL,
+ 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL,
+ 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL,
+ 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL,
+ 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL,
+ 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL,
+ 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL,
+ 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL,
+ 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL,
+ 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL,
+ 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL,
+ 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL,
+ 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL,
+ 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL,
+ 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL,
+ 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL,
+ 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL,
+ 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL,
+ 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL,
+ 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL,
+ 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL,
+ 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL,
+ 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL,
+ 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL,
+ 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL,
+ 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL,
+ 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL,
+ 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL,
+ 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL,
+ 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL,
+ 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL,
+ 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL,
+ 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL,
+ 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL,
+ 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL,
+ 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL,
+ 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL,
+ 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL,
+ 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL,
+ 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL,
+ 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL,
+ 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL,
+ 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL,
+ 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL,
+ 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL,
+ 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL,
+ 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL,
+ 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL,
+ 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL,
+ 0xde0506f1UL
+ },
+ {
+ 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL,
+ 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL,
+ 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL,
+ 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL,
+ 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL,
+ 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL,
+ 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL,
+ 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL,
+ 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL,
+ 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL,
+ 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL,
+ 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL,
+ 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL,
+ 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL,
+ 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL,
+ 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL,
+ 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL,
+ 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL,
+ 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL,
+ 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL,
+ 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL,
+ 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL,
+ 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL,
+ 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL,
+ 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL,
+ 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL,
+ 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL,
+ 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL,
+ 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL,
+ 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL,
+ 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL,
+ 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL,
+ 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL,
+ 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL,
+ 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL,
+ 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL,
+ 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL,
+ 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL,
+ 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL,
+ 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL,
+ 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL,
+ 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL,
+ 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL,
+ 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL,
+ 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL,
+ 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL,
+ 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL,
+ 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL,
+ 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL,
+ 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL,
+ 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL,
+ 0x8def022dUL
+ },
+ {
+ 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL,
+ 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL,
+ 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL,
+ 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL,
+ 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL,
+ 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL,
+ 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL,
+ 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL,
+ 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL,
+ 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL,
+ 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL,
+ 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL,
+ 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL,
+ 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL,
+ 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL,
+ 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL,
+ 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL,
+ 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL,
+ 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL,
+ 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL,
+ 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL,
+ 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL,
+ 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL,
+ 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL,
+ 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL,
+ 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL,
+ 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL,
+ 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL,
+ 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL,
+ 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL,
+ 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL,
+ 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL,
+ 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL,
+ 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL,
+ 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL,
+ 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL,
+ 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL,
+ 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL,
+ 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL,
+ 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL,
+ 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL,
+ 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL,
+ 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL,
+ 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL,
+ 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL,
+ 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL,
+ 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL,
+ 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL,
+ 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL,
+ 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL,
+ 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL,
+ 0x72fd2493UL
+ },
+ {
+ 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL,
+ 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL,
+ 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL,
+ 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL,
+ 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL,
+ 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL,
+ 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL,
+ 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL,
+ 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL,
+ 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL,
+ 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL,
+ 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL,
+ 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL,
+ 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL,
+ 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL,
+ 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL,
+ 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL,
+ 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL,
+ 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL,
+ 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL,
+ 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL,
+ 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL,
+ 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL,
+ 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL,
+ 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL,
+ 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL,
+ 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL,
+ 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL,
+ 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL,
+ 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL,
+ 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL,
+ 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL,
+ 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL,
+ 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL,
+ 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL,
+ 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL,
+ 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL,
+ 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL,
+ 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL,
+ 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL,
+ 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL,
+ 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL,
+ 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL,
+ 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL,
+ 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL,
+ 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL,
+ 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL,
+ 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL,
+ 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL,
+ 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL,
+ 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL,
+ 0xed3498beUL
+ },
+ {
+ 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL,
+ 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL,
+ 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL,
+ 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL,
+ 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL,
+ 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL,
+ 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL,
+ 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL,
+ 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL,
+ 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL,
+ 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL,
+ 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL,
+ 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL,
+ 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL,
+ 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL,
+ 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL,
+ 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL,
+ 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL,
+ 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL,
+ 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL,
+ 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL,
+ 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL,
+ 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL,
+ 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL,
+ 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL,
+ 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL,
+ 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL,
+ 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL,
+ 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL,
+ 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL,
+ 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL,
+ 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL,
+ 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL,
+ 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL,
+ 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL,
+ 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL,
+ 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL,
+ 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL,
+ 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL,
+ 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL,
+ 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL,
+ 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL,
+ 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL,
+ 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL,
+ 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL,
+ 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL,
+ 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL,
+ 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL,
+ 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL,
+ 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL,
+ 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL,
+ 0xf10605deUL
+#endif
+ }
+};
diff --git a/zlib/deflate.c b/zlib/deflate.c
new file mode 100644
index 0000000..33a41e9
--- /dev/null
+++ b/zlib/deflate.c
@@ -0,0 +1,1502 @@
+/* deflate.c -- compress data using the deflation algorithm
+ * Copyright (C) 1995-2004 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process depends on being able to identify portions
+ * of the input text which are identical to earlier input (within a
+ * sliding window trailing behind the input currently being processed).
+ *
+ * The most straightforward technique turns out to be the fastest for
+ * most input files: try all possible matches and select the longest.
+ * The key feature of this algorithm is that insertions into the string
+ * dictionary are very simple and thus fast, and deletions are avoided
+ * completely. Insertions are performed at each input character, whereas
+ * string matches are performed only when the previous match ends. So it
+ * is preferable to spend more time in matches to allow very fast string
+ * insertions and avoid deletions. The matching algorithm for small
+ * strings is inspired from that of Rabin & Karp. A brute force approach
+ * is used to find longer strings when a small match has been found.
+ * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
+ * (by Leonid Broukhis).
+ * A previous version of this file used a more sophisticated algorithm
+ * (by Fiala and Greene) which is guaranteed to run in linear amortized
+ * time, but has a larger average cost, uses more memory and is patented.
+ * However the F&G algorithm may be faster for some highly redundant
+ * files if the parameter max_chain_length (described below) is too large.
+ *
+ * ACKNOWLEDGEMENTS
+ *
+ * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
+ * I found it in 'freeze' written by Leonid Broukhis.
+ * Thanks to many people for bug reports and testing.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
+ * Available in http://www.ietf.org/rfc/rfc1951.txt
+ *
+ * A description of the Rabin and Karp algorithm is given in the book
+ * "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
+ *
+ * Fiala,E.R., and Greene,D.H.
+ * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
+ *
+ */
+
+/* @(#) $Id$ */
+
+#include "deflate.h"
+
+const char deflate_copyright[] =
+ " deflate 1.2.2 Copyright 1995-2004 Jean-loup Gailly ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+/* ===========================================================================
+ * Function prototypes.
+ */
+typedef enum {
+ need_more, /* block not completed, need more input or more output */
+ block_done, /* block flush performed */
+ finish_started, /* finish started, need only more output at next deflate */
+ finish_done /* finish done, accept no more input or output */
+} block_state;
+
+typedef block_state (*compress_func) OF((deflate_state *s, int flush));
+/* Compression function. Returns the block state after the call. */
+
+local void fill_window OF((deflate_state *s));
+local block_state deflate_stored OF((deflate_state *s, int flush));
+local block_state deflate_fast OF((deflate_state *s, int flush));
+#ifndef FASTEST
+local block_state deflate_slow OF((deflate_state *s, int flush));
+#endif
+local void lm_init OF((deflate_state *s));
+local void putShortMSB OF((deflate_state *s, uInt b));
+local void flush_pending OF((z_streamp strm));
+local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size));
+#ifndef FASTEST
+#ifdef ASMV
+ void match_init OF((void)); /* asm code initialization */
+ uInt longest_match OF((deflate_state *s, IPos cur_match));
+#else
+local uInt longest_match OF((deflate_state *s, IPos cur_match));
+#endif
+#endif
+local uInt longest_match_fast OF((deflate_state *s, IPos cur_match));
+
+#ifdef DEBUG
+local void check_match OF((deflate_state *s, IPos start, IPos match,
+ int length));
+#endif
+
+/* ===========================================================================
+ * Local data
+ */
+
+#define NIL 0
+/* Tail of hash chains */
+
+#ifndef TOO_FAR
+# define TOO_FAR 4096
+#endif
+/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+typedef struct config_s {
+ ush good_length; /* reduce lazy search above this match length */
+ ush max_lazy; /* do not perform lazy search above this match length */
+ ush nice_length; /* quit search above this match length */
+ ush max_chain;
+ compress_func func;
+} config;
+
+#ifdef FASTEST
+local const config configuration_table[2] = {
+/* good lazy nice chain */
+/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */
+/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */
+#else
+local const config configuration_table[10] = {
+/* good lazy nice chain */
+/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */
+/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */
+/* 2 */ {4, 5, 16, 8, deflate_fast},
+/* 3 */ {4, 6, 32, 32, deflate_fast},
+
+/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */
+/* 5 */ {8, 16, 32, 32, deflate_slow},
+/* 6 */ {8, 16, 128, 128, deflate_slow},
+/* 7 */ {8, 32, 128, 256, deflate_slow},
+/* 8 */ {32, 128, 258, 1024, deflate_slow},
+/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */
+#endif
+
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
+ */
+
+#define EQUAL 0
+/* result of memcmp for equal strings */
+
+#ifndef NO_DUMMY_DECL
+struct static_tree_desc_s {int dummy;}; /* for buggy compilers */
+#endif
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * IN assertion: all calls to to UPDATE_HASH are made with consecutive
+ * input characters, so that a running hash key can be computed from the
+ * previous key instead of complete recalculation each time.
+ */
+#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
+
+
+/* ===========================================================================
+ * Insert string str in the dictionary and set match_head to the previous head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * If this file is compiled with -DFASTEST, the compression level is forced
+ * to 1, and no hash chains are maintained.
+ * IN assertion: all calls to to INSERT_STRING are made with consecutive
+ * input characters and the first MIN_MATCH bytes of str are valid
+ * (except for the last MIN_MATCH-1 bytes of the input file).
+ */
+#ifdef FASTEST
+#define INSERT_STRING(s, str, match_head) \
+ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+ match_head = s->head[s->ins_h], \
+ s->head[s->ins_h] = (Pos)(str))
+#else
+#define INSERT_STRING(s, str, match_head) \
+ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+ match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \
+ s->head[s->ins_h] = (Pos)(str))
+#endif
+
+/* ===========================================================================
+ * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
+ * prev[] will be initialized on the fly.
+ */
+#define CLEAR_HASH(s) \
+ s->head[s->hash_size-1] = NIL; \
+ zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
+
+/* ========================================================================= */
+int ZEXPORT deflateInit_(strm, level, version, stream_size)
+ z_streamp strm;
+ int level;
+ const char *version;
+ int stream_size;
+{
+ return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY, version, stream_size);
+ /* To do: ignore strm->next_in if we use it as window */
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
+ version, stream_size)
+ z_streamp strm;
+ int level;
+ int method;
+ int windowBits;
+ int memLevel;
+ int strategy;
+ const char *version;
+ int stream_size;
+{
+ deflate_state *s;
+ int wrap = 1;
+ static const char my_version[] = ZLIB_VERSION;
+
+ ushf *overlay;
+ /* We overlay pending_buf and d_buf+l_buf. This works since the average
+ * output size for (length,distance) codes is <= 24 bits.
+ */
+
+ if (version == Z_NULL || version[0] != my_version[0] ||
+ stream_size != sizeof(z_stream)) {
+ return Z_VERSION_ERROR;
+ }
+ if (strm == Z_NULL) return Z_STREAM_ERROR;
+
+ strm->msg = Z_NULL;
+ if (strm->zalloc == (alloc_func)0) {
+ strm->zalloc = zcalloc;
+ strm->opaque = (voidpf)0;
+ }
+ if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+
+#ifdef FASTEST
+ if (level != 0) level = 1;
+#else
+ if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#endif
+
+ if (windowBits < 0) { /* suppress zlib wrapper */
+ wrap = 0;
+ windowBits = -windowBits;
+ }
+#ifdef GZIP
+ else if (windowBits > 15) {
+ wrap = 2; /* write gzip wrapper instead */
+ windowBits -= 16;
+ }
+#endif
+ if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED ||
+ windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
+ strategy < 0 || strategy > Z_RLE) {
+ return Z_STREAM_ERROR;
+ }
+ if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */
+ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
+ if (s == Z_NULL) return Z_MEM_ERROR;
+ strm->state = (struct internal_state FAR *)s;
+ s->strm = strm;
+
+ s->wrap = wrap;
+ s->w_bits = windowBits;
+ s->w_size = 1 << s->w_bits;
+ s->w_mask = s->w_size - 1;
+
+ s->hash_bits = memLevel + 7;
+ s->hash_size = 1 << s->hash_bits;
+ s->hash_mask = s->hash_size - 1;
+ s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
+
+ s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
+ s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos));
+ s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos));
+
+ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
+
+ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
+ s->pending_buf = (uchf *) overlay;
+ s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);
+
+ if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
+ s->pending_buf == Z_NULL) {
+ s->status = FINISH_STATE;
+ strm->msg = (char*)ERR_MSG(Z_MEM_ERROR);
+ deflateEnd (strm);
+ return Z_MEM_ERROR;
+ }
+ s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
+ s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
+
+ s->level = level;
+ s->strategy = strategy;
+ s->method = (Byte)method;
+
+ return deflateReset(strm);
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
+ z_streamp strm;
+ const Bytef *dictionary;
+ uInt dictLength;
+{
+ deflate_state *s;
+ uInt length = dictLength;
+ uInt n;
+ IPos hash_head = 0;
+
+ if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL ||
+ strm->state->wrap == 2 ||
+ (strm->state->wrap == 1 && strm->state->status != INIT_STATE))
+ return Z_STREAM_ERROR;
+
+ s = strm->state;
+ if (s->wrap)
+ strm->adler = adler32(strm->adler, dictionary, dictLength);
+
+ if (length < MIN_MATCH) return Z_OK;
+ if (length > MAX_DIST(s)) {
+ length = MAX_DIST(s);
+#ifndef USE_DICT_HEAD
+ dictionary += dictLength - length; /* use the tail of the dictionary */
+#endif
+ }
+ zmemcpy(s->window, dictionary, length);
+ s->strstart = length;
+ s->block_start = (long)length;
+
+ /* Insert all strings in the hash table (except for the last two bytes).
+ * s->lookahead stays null, so s->ins_h will be recomputed at the next
+ * call of fill_window.
+ */
+ s->ins_h = s->window[0];
+ UPDATE_HASH(s, s->ins_h, s->window[1]);
+ for (n = 0; n <= length - MIN_MATCH; n++) {
+ INSERT_STRING(s, n, hash_head);
+ }
+ if (hash_head) hash_head = 0; /* to make compiler happy */
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateReset (strm)
+ z_streamp strm;
+{
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) {
+ return Z_STREAM_ERROR;
+ }
+
+ strm->total_in = strm->total_out = 0;
+ strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
+ strm->data_type = Z_UNKNOWN;
+
+ s = (deflate_state *)strm->state;
+ s->pending = 0;
+ s->pending_out = s->pending_buf;
+
+ if (s->wrap < 0) {
+ s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */
+ }
+ s->status = s->wrap ? INIT_STATE : BUSY_STATE;
+ strm->adler =
+#ifdef GZIP
+ s->wrap == 2 ? crc32(0L, Z_NULL, 0) :
+#endif
+ adler32(0L, Z_NULL, 0);
+ s->last_flush = Z_NO_FLUSH;
+
+ _tr_init(s);
+ lm_init(s);
+
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflatePrime (strm, bits, value)
+ z_streamp strm;
+ int bits;
+ int value;
+{
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ strm->state->bi_valid = bits;
+ strm->state->bi_buf = (ush)(value & ((1 << bits) - 1));
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateParams(strm, level, strategy)
+ z_streamp strm;
+ int level;
+ int strategy;
+{
+ deflate_state *s;
+ compress_func func;
+ int err = Z_OK;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ s = strm->state;
+
+#ifdef FASTEST
+ if (level != 0) level = 1;
+#else
+ if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#endif
+ if (level < 0 || level > 9 || strategy < 0 || strategy > Z_RLE) {
+ return Z_STREAM_ERROR;
+ }
+ func = configuration_table[s->level].func;
+
+ if (func != configuration_table[level].func && strm->total_in != 0) {
+ /* Flush the last buffer: */
+ err = deflate(strm, Z_PARTIAL_FLUSH);
+ }
+ if (s->level != level) {
+ s->level = level;
+ s->max_lazy_match = configuration_table[level].max_lazy;
+ s->good_match = configuration_table[level].good_length;
+ s->nice_match = configuration_table[level].nice_length;
+ s->max_chain_length = configuration_table[level].max_chain;
+ }
+ s->strategy = strategy;
+ return err;
+}
+
+/* =========================================================================
+ * For the default windowBits of 15 and memLevel of 8, this function returns
+ * a close to exact, as well as small, upper bound on the compressed size.
+ * They are coded as constants here for a reason--if the #define's are
+ * changed, then this function needs to be changed as well. The return
+ * value for 15 and 8 only works for those exact settings.
+ *
+ * For any setting other than those defaults for windowBits and memLevel,
+ * the value returned is a conservative worst case for the maximum expansion
+ * resulting from using fixed blocks instead of stored blocks, which deflate
+ * can emit on compressed data for some combinations of the parameters.
+ *
+ * This function could be more sophisticated to provide closer upper bounds
+ * for every combination of windowBits and memLevel, as well as wrap.
+ * But even the conservative upper bound of about 14% expansion does not
+ * seem onerous for output buffer allocation.
+ */
+uLong ZEXPORT deflateBound(strm, sourceLen)
+ z_streamp strm;
+ uLong sourceLen;
+{
+ deflate_state *s;
+ uLong destLen;
+
+ /* conservative upper bound */
+ destLen = sourceLen +
+ ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11;
+
+ /* if can't get parameters, return conservative bound */
+ if (strm == Z_NULL || strm->state == Z_NULL)
+ return destLen;
+
+ /* if not default parameters, return conservative bound */
+ s = strm->state;
+ if (s->w_bits != 15 || s->hash_bits != 8 + 7)
+ return destLen;
+
+ /* default settings: return tight bound for that case */
+ return compressBound(sourceLen);
+}
+
+/* =========================================================================
+ * Put a short in the pending buffer. The 16-bit value is put in MSB order.
+ * IN assertion: the stream state is correct and there is enough room in
+ * pending_buf.
+ */
+local void putShortMSB (s, b)
+ deflate_state *s;
+ uInt b;
+{
+ put_byte(s, (Byte)(b >> 8));
+ put_byte(s, (Byte)(b & 0xff));
+}
+
+/* =========================================================================
+ * Flush as much pending output as possible. All deflate() output goes
+ * through this function so some applications may wish to modify it
+ * to avoid allocating a large strm->next_out buffer and copying into it.
+ * (See also read_buf()).
+ */
+local void flush_pending(strm)
+ z_streamp strm;
+{
+ unsigned len = strm->state->pending;
+
+ if (len > strm->avail_out) len = strm->avail_out;
+ if (len == 0) return;
+
+ zmemcpy(strm->next_out, strm->state->pending_out, len);
+ strm->next_out += len;
+ strm->state->pending_out += len;
+ strm->total_out += len;
+ strm->avail_out -= len;
+ strm->state->pending -= len;
+ if (strm->state->pending == 0) {
+ strm->state->pending_out = strm->state->pending_buf;
+ }
+}
+
+/* ========================================================================= */
+int ZEXPORT deflate (strm, flush)
+ z_streamp strm;
+ int flush;
+{
+ int old_flush; /* value of flush param for previous deflate call */
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ flush > Z_FINISH || flush < 0) {
+ return Z_STREAM_ERROR;
+ }
+ s = strm->state;
+
+ if (strm->next_out == Z_NULL ||
+ (strm->next_in == Z_NULL && strm->avail_in != 0) ||
+ (s->status == FINISH_STATE && flush != Z_FINISH)) {
+ ERR_RETURN(strm, Z_STREAM_ERROR);
+ }
+ if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
+
+ s->strm = strm; /* just in case */
+ old_flush = s->last_flush;
+ s->last_flush = flush;
+
+ /* Write the header */
+ if (s->status == INIT_STATE) {
+#ifdef GZIP
+ if (s->wrap == 2) {
+ put_byte(s, 31);
+ put_byte(s, 139);
+ put_byte(s, 8);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, s->level == 9 ? 2 :
+ (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
+ 4 : 0));
+ put_byte(s, 255);
+ s->status = BUSY_STATE;
+ strm->adler = crc32(0L, Z_NULL, 0);
+ }
+ else
+#endif
+ {
+ uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
+ uInt level_flags;
+
+ if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2)
+ level_flags = 0;
+ else if (s->level < 6)
+ level_flags = 1;
+ else if (s->level == 6)
+ level_flags = 2;
+ else
+ level_flags = 3;
+ header |= (level_flags << 6);
+ if (s->strstart != 0) header |= PRESET_DICT;
+ header += 31 - (header % 31);
+
+ s->status = BUSY_STATE;
+ putShortMSB(s, header);
+
+ /* Save the adler32 of the preset dictionary: */
+ if (s->strstart != 0) {
+ putShortMSB(s, (uInt)(strm->adler >> 16));
+ putShortMSB(s, (uInt)(strm->adler & 0xffff));
+ }
+ strm->adler = adler32(0L, Z_NULL, 0);
+ }
+ }
+
+ /* Flush as much pending output as possible */
+ if (s->pending != 0) {
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ /* Since avail_out is 0, deflate will be called again with
+ * more output space, but possibly with both pending and
+ * avail_in equal to zero. There won't be anything to do,
+ * but this is not an error situation so make sure we
+ * return OK instead of BUF_ERROR at next call of deflate:
+ */
+ s->last_flush = -1;
+ return Z_OK;
+ }
+
+ /* Make sure there is something to do and avoid duplicate consecutive
+ * flushes. For repeated and useless calls with Z_FINISH, we keep
+ * returning Z_STREAM_END instead of Z_BUF_ERROR.
+ */
+ } else if (strm->avail_in == 0 && flush <= old_flush &&
+ flush != Z_FINISH) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* User must not provide more input after the first FINISH: */
+ if (s->status == FINISH_STATE && strm->avail_in != 0) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* Start a new block or continue the current one.
+ */
+ if (strm->avail_in != 0 || s->lookahead != 0 ||
+ (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) {
+ block_state bstate;
+
+ bstate = (*(configuration_table[s->level].func))(s, flush);
+
+ if (bstate == finish_started || bstate == finish_done) {
+ s->status = FINISH_STATE;
+ }
+ if (bstate == need_more || bstate == finish_started) {
+ if (strm->avail_out == 0) {
+ s->last_flush = -1; /* avoid BUF_ERROR next call, see above */
+ }
+ return Z_OK;
+ /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
+ * of deflate should use the same flush parameter to make sure
+ * that the flush is complete. So we don't have to output an
+ * empty block here, this will be done at next call. This also
+ * ensures that for a very small output buffer, we emit at most
+ * one empty block.
+ */
+ }
+ if (bstate == block_done) {
+ if (flush == Z_PARTIAL_FLUSH) {
+ _tr_align(s);
+ } else { /* FULL_FLUSH or SYNC_FLUSH */
+ _tr_stored_block(s, (char*)0, 0L, 0);
+ /* For a full flush, this empty block will be recognized
+ * as a special marker by inflate_sync().
+ */
+ if (flush == Z_FULL_FLUSH) {
+ CLEAR_HASH(s); /* forget history */
+ }
+ }
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */
+ return Z_OK;
+ }
+ }
+ }
+ Assert(strm->avail_out > 0, "bug2");
+
+ if (flush != Z_FINISH) return Z_OK;
+ if (s->wrap <= 0) return Z_STREAM_END;
+
+ /* Write the trailer */
+#ifdef GZIP
+ if (s->wrap == 2) {
+ put_byte(s, (Byte)(strm->adler & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 16) & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 24) & 0xff));
+ put_byte(s, (Byte)(strm->total_in & 0xff));
+ put_byte(s, (Byte)((strm->total_in >> 8) & 0xff));
+ put_byte(s, (Byte)((strm->total_in >> 16) & 0xff));
+ put_byte(s, (Byte)((strm->total_in >> 24) & 0xff));
+ }
+ else
+#endif
+ {
+ putShortMSB(s, (uInt)(strm->adler >> 16));
+ putShortMSB(s, (uInt)(strm->adler & 0xffff));
+ }
+ flush_pending(strm);
+ /* If avail_out is zero, the application will call deflate again
+ * to flush the rest.
+ */
+ if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */
+ return s->pending != 0 ? Z_OK : Z_STREAM_END;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateEnd (strm)
+ z_streamp strm;
+{
+ int status;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+
+ status = strm->state->status;
+ if (status != INIT_STATE && status != BUSY_STATE &&
+ status != FINISH_STATE) {
+ return Z_STREAM_ERROR;
+ }
+
+ /* Deallocate in reverse order of allocations: */
+ TRY_FREE(strm, strm->state->pending_buf);
+ TRY_FREE(strm, strm->state->head);
+ TRY_FREE(strm, strm->state->prev);
+ TRY_FREE(strm, strm->state->window);
+
+ ZFREE(strm, strm->state);
+ strm->state = Z_NULL;
+
+ return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
+}
+
+/* =========================================================================
+ * Copy the source state to the destination state.
+ * To simplify the source, this is not supported for 16-bit MSDOS (which
+ * doesn't have enough memory anyway to duplicate compression states).
+ */
+int ZEXPORT deflateCopy (dest, source)
+ z_streamp dest;
+ z_streamp source;
+{
+#ifdef MAXSEG_64K
+ return Z_STREAM_ERROR;
+#else
+ deflate_state *ds;
+ deflate_state *ss;
+ ushf *overlay;
+
+
+ if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) {
+ return Z_STREAM_ERROR;
+ }
+
+ ss = source->state;
+
+ *dest = *source;
+
+ ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
+ if (ds == Z_NULL) return Z_MEM_ERROR;
+ dest->state = (struct internal_state FAR *) ds;
+ *ds = *ss;
+ ds->strm = dest;
+
+ ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
+ ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos));
+ ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos));
+ overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
+ ds->pending_buf = (uchf *) overlay;
+
+ if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
+ ds->pending_buf == Z_NULL) {
+ deflateEnd (dest);
+ return Z_MEM_ERROR;
+ }
+ /* following zmemcpy do not work for 16-bit MSDOS */
+ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
+ zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos));
+ zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos));
+ zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
+
+ ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
+ ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);
+ ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
+
+ ds->l_desc.dyn_tree = ds->dyn_ltree;
+ ds->d_desc.dyn_tree = ds->dyn_dtree;
+ ds->bl_desc.dyn_tree = ds->bl_tree;
+
+ return Z_OK;
+#endif /* MAXSEG_64K */
+}
+
+/* ===========================================================================
+ * Read a new buffer from the current input stream, update the adler32
+ * and total number of bytes read. All deflate() input goes through
+ * this function so some applications may wish to modify it to avoid
+ * allocating a large strm->next_in buffer and copying from it.
+ * (See also flush_pending()).
+ */
+local int read_buf(strm, buf, size)
+ z_streamp strm;
+ Bytef *buf;
+ unsigned size;
+{
+ unsigned len = strm->avail_in;
+
+ if (len > size) len = size;
+ if (len == 0) return 0;
+
+ strm->avail_in -= len;
+
+ if (strm->state->wrap == 1) {
+ strm->adler = adler32(strm->adler, strm->next_in, len);
+ }
+#ifdef GZIP
+ else if (strm->state->wrap == 2) {
+ strm->adler = crc32(strm->adler, strm->next_in, len);
+ }
+#endif
+ zmemcpy(buf, strm->next_in, len);
+ strm->next_in += len;
+ strm->total_in += len;
+
+ return (int)len;
+}
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new zlib stream
+ */
+local void lm_init (s)
+ deflate_state *s;
+{
+ s->window_size = (ulg)2L*s->w_size;
+
+ CLEAR_HASH(s);
+
+ /* Set the default configuration parameters:
+ */
+ s->max_lazy_match = configuration_table[s->level].max_lazy;
+ s->good_match = configuration_table[s->level].good_length;
+ s->nice_match = configuration_table[s->level].nice_length;
+ s->max_chain_length = configuration_table[s->level].max_chain;
+
+ s->strstart = 0;
+ s->block_start = 0L;
+ s->lookahead = 0;
+ s->match_length = s->prev_length = MIN_MATCH-1;
+ s->match_available = 0;
+ s->ins_h = 0;
+#ifdef ASMV
+ match_init(); /* initialize the asm code */
+#endif
+}
+
+#ifndef FASTEST
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ * OUT assertion: the match length is not greater than s->lookahead.
+ */
+#ifndef ASMV
+/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
+ * match.S. The code will be functionally equivalent.
+ */
+local uInt longest_match(s, cur_match)
+ deflate_state *s;
+ IPos cur_match; /* current match */
+{
+ unsigned chain_length = s->max_chain_length;/* max hash chain length */
+ register Bytef *scan = s->window + s->strstart; /* current string */
+ register Bytef *match; /* matched string */
+ register int len; /* length of current match */
+ int best_len = s->prev_length; /* best match length so far */
+ int nice_match = s->nice_match; /* stop if match long enough */
+ IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
+ s->strstart - (IPos)MAX_DIST(s) : NIL;
+ /* Stop when cur_match becomes <= limit. To simplify the code,
+ * we prevent matches with the string of window index 0.
+ */
+ Posf *prev = s->prev;
+ uInt wmask = s->w_mask;
+
+#ifdef UNALIGNED_OK
+ /* Compare two bytes at a time. Note: this is not always beneficial.
+ * Try with and without -DUNALIGNED_OK to check.
+ */
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
+ register ush scan_start = *(ushf*)scan;
+ register ush scan_end = *(ushf*)(scan+best_len-1);
+#else
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+ register Byte scan_end1 = scan[best_len-1];
+ register Byte scan_end = scan[best_len];
+#endif
+
+ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+ /* Do not waste too much time if we already have a good match: */
+ if (s->prev_length >= s->good_match) {
+ chain_length >>= 2;
+ }
+ /* Do not look for matches beyond the end of the input. This is necessary
+ * to make deflate deterministic.
+ */
+ if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
+
+ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+ do {
+ Assert(cur_match < s->strstart, "no future");
+ match = s->window + cur_match;
+
+ /* Skip to next match if the match length cannot increase
+ * or if the match length is less than 2:
+ */
+#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
+ /* This code assumes sizeof(unsigned short) == 2. Do not use
+ * UNALIGNED_OK if your compiler uses a different size.
+ */
+ if (*(ushf*)(match+best_len-1) != scan_end ||
+ *(ushf*)match != scan_start) continue;
+
+ /* It is not necessary to compare scan[2] and match[2] since they are
+ * always equal when the other bytes match, given that the hash keys
+ * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
+ * strstart+3, +5, ... up to strstart+257. We check for insufficient
+ * lookahead only every 4th comparison; the 128th check will be made
+ * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
+ * necessary to put more guard bytes at the end of the window, or
+ * to check more often for insufficient lookahead.
+ */
+ Assert(scan[2] == match[2], "scan[2]?");
+ scan++, match++;
+ do {
+ } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ scan < strend);
+ /* The funny "do {}" generates better code on most compilers */
+
+ /* Here, scan <= window+strstart+257 */
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+ if (*scan == *match) scan++;
+
+ len = (MAX_MATCH - 1) - (int)(strend-scan);
+ scan = strend - (MAX_MATCH-1);
+
+#else /* UNALIGNED_OK */
+
+ if (match[best_len] != scan_end ||
+ match[best_len-1] != scan_end1 ||
+ *match != *scan ||
+ *++match != scan[1]) continue;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match++;
+ Assert(*scan == *match, "match[2]?");
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+ len = MAX_MATCH - (int)(strend - scan);
+ scan = strend - MAX_MATCH;
+
+#endif /* UNALIGNED_OK */
+
+ if (len > best_len) {
+ s->match_start = cur_match;
+ best_len = len;
+ if (len >= nice_match) break;
+#ifdef UNALIGNED_OK
+ scan_end = *(ushf*)(scan+best_len-1);
+#else
+ scan_end1 = scan[best_len-1];
+ scan_end = scan[best_len];
+#endif
+ }
+ } while ((cur_match = prev[cur_match & wmask]) > limit
+ && --chain_length != 0);
+
+ if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
+ return s->lookahead;
+}
+#endif /* ASMV */
+#endif /* FASTEST */
+
+/* ---------------------------------------------------------------------------
+ * Optimized version for level == 1 or strategy == Z_RLE only
+ */
+local uInt longest_match_fast(s, cur_match)
+ deflate_state *s;
+ IPos cur_match; /* current match */
+{
+ register Bytef *scan = s->window + s->strstart; /* current string */
+ register Bytef *match; /* matched string */
+ register int len; /* length of current match */
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+
+ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+ Assert(cur_match < s->strstart, "no future");
+
+ match = s->window + cur_match;
+
+ /* Return failure if the match length is less than 2:
+ */
+ if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match += 2;
+ Assert(*scan == *match, "match[2]?");
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+ len = MAX_MATCH - (int)(strend - scan);
+
+ if (len < MIN_MATCH) return MIN_MATCH - 1;
+
+ s->match_start = cur_match;
+ return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead;
+}
+
+#ifdef DEBUG
+/* ===========================================================================
+ * Check that the match at match_start is indeed a match.
+ */
+local void check_match(s, start, match, length)
+ deflate_state *s;
+ IPos start, match;
+ int length;
+{
+ /* check that the match is indeed a match */
+ if (zmemcmp(s->window + match,
+ s->window + start, length) != EQUAL) {
+ fprintf(stderr, " start %u, match %u, length %d\n",
+ start, match, length);
+ do {
+ fprintf(stderr, "%c%c", s->window[match++], s->window[start++]);
+ } while (--length != 0);
+ z_error("invalid match");
+ }
+ if (z_verbose > 1) {
+ fprintf(stderr,"\\[%d,%d]", start-match, length);
+ do { putc(s->window[start++], stderr); } while (--length != 0);
+ }
+}
+#else
+# define check_match(s, start, match, length)
+#endif /* DEBUG */
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * IN assertion: lookahead < MIN_LOOKAHEAD
+ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+ * At least one byte has been read, or avail_in == 0; reads are
+ * performed for at least two bytes (required for the zip translate_eol
+ * option -- not supported here).
+ */
+local void fill_window(s)
+ deflate_state *s;
+{
+ register unsigned n, m;
+ register Posf *p;
+ unsigned more; /* Amount of free space at the end of the window. */
+ uInt wsize = s->w_size;
+
+ do {
+ more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
+
+ /* Deal with !@#$% 64K limit: */
+ if (sizeof(int) <= 2) {
+ if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
+ more = wsize;
+
+ } else if (more == (unsigned)(-1)) {
+ /* Very unlikely, but possible on 16 bit machine if
+ * strstart == 0 && lookahead == 1 (input done a byte at time)
+ */
+ more--;
+ }
+ }
+
+ /* If the window is almost full and there is insufficient lookahead,
+ * move the upper half to the lower one to make room in the upper half.
+ */
+ if (s->strstart >= wsize+MAX_DIST(s)) {
+
+ zmemcpy(s->window, s->window+wsize, (unsigned)wsize);
+ s->match_start -= wsize;
+ s->strstart -= wsize; /* we now have strstart >= MAX_DIST */
+ s->block_start -= (long) wsize;
+
+ /* Slide the hash table (could be avoided with 32 bit values
+ at the expense of memory usage). We slide even when level == 0
+ to keep the hash table consistent if we switch back to level > 0
+ later. (Using level 0 permanently is not an optimal usage of
+ zlib, so we don't care about this pathological case.)
+ */
+ n = s->hash_size;
+ p = &s->head[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ } while (--n);
+
+ n = wsize;
+#ifndef FASTEST
+ p = &s->prev[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ /* If n is not on any hash chain, prev[n] is garbage but
+ * its value will never be used.
+ */
+ } while (--n);
+#endif
+ more += wsize;
+ }
+ if (s->strm->avail_in == 0) return;
+
+ /* If there was no sliding:
+ * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+ * more == window_size - lookahead - strstart
+ * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+ * => more >= window_size - 2*WSIZE + 2
+ * In the BIG_MEM or MMAP case (not yet supported),
+ * window_size == input_size + MIN_LOOKAHEAD &&
+ * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
+ * Otherwise, window_size == 2*WSIZE so more >= 2.
+ * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+ */
+ Assert(more >= 2, "more < 2");
+
+ n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
+ s->lookahead += n;
+
+ /* Initialize the hash value now that we have some input: */
+ if (s->lookahead >= MIN_MATCH) {
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ }
+ /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
+ * but this is not important since only literal bytes will be emitted.
+ */
+
+ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
+}
+
+/* ===========================================================================
+ * Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match.
+ */
+#define FLUSH_BLOCK_ONLY(s, eof) { \
+ _tr_flush_block(s, (s->block_start >= 0L ? \
+ (charf *)&s->window[(unsigned)s->block_start] : \
+ (charf *)Z_NULL), \
+ (ulg)((long)s->strstart - s->block_start), \
+ (eof)); \
+ s->block_start = s->strstart; \
+ flush_pending(s->strm); \
+ Tracev((stderr,"[FLUSH]")); \
+}
+
+/* Same but force premature exit if necessary. */
+#define FLUSH_BLOCK(s, eof) { \
+ FLUSH_BLOCK_ONLY(s, eof); \
+ if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \
+}
+
+/* ===========================================================================
+ * Copy without compression as much as possible from the input stream, return
+ * the current block state.
+ * This function does not insert new strings in the dictionary since
+ * uncompressible data is probably not useful. This function is used
+ * only for the level=0 compression option.
+ * NOTE: this function should be optimized to avoid extra copying from
+ * window to pending_buf.
+ */
+local block_state deflate_stored(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
+ * to pending_buf_size, and each stored block has a 5 byte header:
+ */
+ ulg max_block_size = 0xffff;
+ ulg max_start;
+
+ if (max_block_size > s->pending_buf_size - 5) {
+ max_block_size = s->pending_buf_size - 5;
+ }
+
+ /* Copy as much as possible from input to output: */
+ for (;;) {
+ /* Fill the window as much as possible: */
+ if (s->lookahead <= 1) {
+
+ Assert(s->strstart < s->w_size+MAX_DIST(s) ||
+ s->block_start >= (long)s->w_size, "slide too late");
+
+ fill_window(s);
+ if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more;
+
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+ Assert(s->block_start >= 0L, "block gone");
+
+ s->strstart += s->lookahead;
+ s->lookahead = 0;
+
+ /* Emit a stored block if pending_buf will be full: */
+ max_start = s->block_start + max_block_size;
+ if (s->strstart == 0 || (ulg)s->strstart >= max_start) {
+ /* strstart == 0 is possible when wraparound on 16-bit machine */
+ s->lookahead = (uInt)(s->strstart - max_start);
+ s->strstart = (uInt)max_start;
+ FLUSH_BLOCK(s, 0);
+ }
+ /* Flush if we may have to slide, otherwise block_start may become
+ * negative and the data will be gone:
+ */
+ if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) {
+ FLUSH_BLOCK(s, 0);
+ }
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+
+/* ===========================================================================
+ * Compress as much as possible from the input stream, return the current
+ * block state.
+ * This function does not perform lazy evaluation of matches and inserts
+ * new strings in the dictionary only for unmatched strings or for short
+ * matches. It is used only for the fast compression options.
+ */
+local block_state deflate_fast(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head = NIL; /* head of the hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ * At this point we have always match_length < MIN_MATCH
+ */
+ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+#ifdef FASTEST
+ if ((s->strategy < Z_HUFFMAN_ONLY) ||
+ (s->strategy == Z_RLE && s->strstart - hash_head == 1)) {
+ s->match_length = longest_match_fast (s, hash_head);
+ }
+#else
+ if (s->strategy < Z_HUFFMAN_ONLY) {
+ s->match_length = longest_match (s, hash_head);
+ } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) {
+ s->match_length = longest_match_fast (s, hash_head);
+ }
+#endif
+ /* longest_match() or longest_match_fast() sets match_start */
+ }
+ if (s->match_length >= MIN_MATCH) {
+ check_match(s, s->strstart, s->match_start, s->match_length);
+
+ _tr_tally_dist(s, s->strstart - s->match_start,
+ s->match_length - MIN_MATCH, bflush);
+
+ s->lookahead -= s->match_length;
+
+ /* Insert new strings in the hash table only if the match length
+ * is not too large. This saves time but degrades compression.
+ */
+#ifndef FASTEST
+ if (s->match_length <= s->max_insert_length &&
+ s->lookahead >= MIN_MATCH) {
+ s->match_length--; /* string at strstart already in table */
+ do {
+ s->strstart++;
+ INSERT_STRING(s, s->strstart, hash_head);
+ /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+ * always MIN_MATCH bytes ahead.
+ */
+ } while (--s->match_length != 0);
+ s->strstart++;
+ } else
+#endif
+ {
+ s->strstart += s->match_length;
+ s->match_length = 0;
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
+ * matter since it will be recomputed at next deflate call.
+ */
+ }
+ } else {
+ /* No match, output a literal byte */
+ Tracevv((stderr,"%c", s->window[s->strstart]));
+ _tr_tally_lit (s, s->window[s->strstart], bflush);
+ s->lookahead--;
+ s->strstart++;
+ }
+ if (bflush) FLUSH_BLOCK(s, 0);
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+
+#ifndef FASTEST
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+local block_state deflate_slow(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head = NIL; /* head of hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ /* Process the input block. */
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ */
+ s->prev_length = s->match_length, s->prev_match = s->match_start;
+ s->match_length = MIN_MATCH-1;
+
+ if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
+ s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ if (s->strategy < Z_HUFFMAN_ONLY) {
+ s->match_length = longest_match (s, hash_head);
+ } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) {
+ s->match_length = longest_match_fast (s, hash_head);
+ }
+ /* longest_match() or longest_match_fast() sets match_start */
+
+ if (s->match_length <= 5 && (s->strategy == Z_FILTERED
+#if TOO_FAR <= 32767
+ || (s->match_length == MIN_MATCH &&
+ s->strstart - s->match_start > TOO_FAR)
+#endif
+ )) {
+
+ /* If prev_match is also MIN_MATCH, match_start is garbage
+ * but we will ignore the current match anyway.
+ */
+ s->match_length = MIN_MATCH-1;
+ }
+ }
+ /* If there was a match at the previous step and the current
+ * match is not better, output the previous match:
+ */
+ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
+ uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
+ /* Do not insert strings in hash table beyond this. */
+
+ check_match(s, s->strstart-1, s->prev_match, s->prev_length);
+
+ _tr_tally_dist(s, s->strstart -1 - s->prev_match,
+ s->prev_length - MIN_MATCH, bflush);
+
+ /* Insert in hash table all strings up to the end of the match.
+ * strstart-1 and strstart are already inserted. If there is not
+ * enough lookahead, the last two strings are not inserted in
+ * the hash table.
+ */
+ s->lookahead -= s->prev_length-1;
+ s->prev_length -= 2;
+ do {
+ if (++s->strstart <= max_insert) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+ } while (--s->prev_length != 0);
+ s->match_available = 0;
+ s->match_length = MIN_MATCH-1;
+ s->strstart++;
+
+ if (bflush) FLUSH_BLOCK(s, 0);
+
+ } else if (s->match_available) {
+ /* If there was no match at the previous position, output a
+ * single literal. If there was a match but the current match
+ * is longer, truncate the previous match to a single literal.
+ */
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+ if (bflush) {
+ FLUSH_BLOCK_ONLY(s, 0);
+ }
+ s->strstart++;
+ s->lookahead--;
+ if (s->strm->avail_out == 0) return need_more;
+ } else {
+ /* There is no previous match to compare with, wait for
+ * the next step to decide.
+ */
+ s->match_available = 1;
+ s->strstart++;
+ s->lookahead--;
+ }
+ }
+ Assert (flush != Z_NO_FLUSH, "no flush?");
+ if (s->match_available) {
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+ s->match_available = 0;
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+#endif /* FASTEST */
diff --git a/zlib/deflate.h b/zlib/deflate.h
new file mode 100644
index 0000000..0433283
--- /dev/null
+++ b/zlib/deflate.h
@@ -0,0 +1,325 @@
+/* deflate.h -- internal compression state
+ * Copyright (C) 1995-2002 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id$ */
+
+#ifndef DEFLATE_H
+#define DEFLATE_H
+
+#include "zutil.h"
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+ trailer creation by deflate(). NO_GZIP would be used to avoid linking in
+ the crc code when it is not needed. For shared libraries, gzip encoding
+ should be left enabled. */
+#ifndef NO_GZIP
+# define GZIP
+#endif
+
+/* ===========================================================================
+ * Internal compression state.
+ */
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS 256
+/* number of literal bytes 0..255 */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES 30
+/* number of distance codes */
+
+#define BL_CODES 19
+/* number of codes used to transfer the bit lengths */
+
+#define HEAP_SIZE (2*L_CODES+1)
+/* maximum heap size */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define INIT_STATE 42
+#define BUSY_STATE 113
+#define FINISH_STATE 666
+/* Stream status */
+
+
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data_s {
+ union {
+ ush freq; /* frequency count */
+ ush code; /* bit string */
+ } fc;
+ union {
+ ush dad; /* father node in Huffman tree */
+ ush len; /* length of bit string */
+ } dl;
+} FAR ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad dl.dad
+#define Len dl.len
+
+typedef struct static_tree_desc_s static_tree_desc;
+
+typedef struct tree_desc_s {
+ ct_data *dyn_tree; /* the dynamic tree */
+ int max_code; /* largest code with non zero frequency */
+ static_tree_desc *stat_desc; /* the corresponding static tree */
+} FAR tree_desc;
+
+typedef ush Pos;
+typedef Pos FAR Posf;
+typedef unsigned IPos;
+
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+typedef struct internal_state {
+ z_streamp strm; /* pointer back to this zlib stream */
+ int status; /* as the name implies */
+ Bytef *pending_buf; /* output still pending */
+ ulg pending_buf_size; /* size of pending_buf */
+ Bytef *pending_out; /* next pending byte to output to the stream */
+ int pending; /* nb of bytes in the pending buffer */
+ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */
+ Byte method; /* STORED (for zip only) or DEFLATED */
+ int last_flush; /* value of flush param for previous deflate call */
+
+ /* used by deflate.c: */
+
+ uInt w_size; /* LZ77 window size (32K by default) */
+ uInt w_bits; /* log2(w_size) (8..16) */
+ uInt w_mask; /* w_size - 1 */
+
+ Bytef *window;
+ /* Sliding window. Input bytes are read into the second half of the window,
+ * and move to the first half later to keep a dictionary of at least wSize
+ * bytes. With this organization, matches are limited to a distance of
+ * wSize-MAX_MATCH bytes, but this ensures that IO is always
+ * performed with a length multiple of the block size. Also, it limits
+ * the window size to 64K, which is quite useful on MSDOS.
+ * To do: use the user input buffer as sliding window.
+ */
+
+ ulg window_size;
+ /* Actual size of window: 2*wSize, except when the user input buffer
+ * is directly used as sliding window.
+ */
+
+ Posf *prev;
+ /* Link to older string with same hash index. To limit the size of this
+ * array to 64K, this link is maintained only for the last 32K strings.
+ * An index in this array is thus a window index modulo 32K.
+ */
+
+ Posf *head; /* Heads of the hash chains or NIL. */
+
+ uInt ins_h; /* hash index of string to be inserted */
+ uInt hash_size; /* number of elements in hash table */
+ uInt hash_bits; /* log2(hash_size) */
+ uInt hash_mask; /* hash_size-1 */
+
+ uInt hash_shift;
+ /* Number of bits by which ins_h must be shifted at each input
+ * step. It must be such that after MIN_MATCH steps, the oldest
+ * byte no longer takes part in the hash key, that is:
+ * hash_shift * MIN_MATCH >= hash_bits
+ */
+
+ long block_start;
+ /* Window position at the beginning of the current output block. Gets
+ * negative when the window is moved backwards.
+ */
+
+ uInt match_length; /* length of best match */
+ IPos prev_match; /* previous match */
+ int match_available; /* set if previous match exists */
+ uInt strstart; /* start of string to insert */
+ uInt match_start; /* start of matching string */
+ uInt lookahead; /* number of valid bytes ahead in window */
+
+ uInt prev_length;
+ /* Length of the best match at previous step. Matches not greater than this
+ * are discarded. This is used in the lazy match evaluation.
+ */
+
+ uInt max_chain_length;
+ /* To speed up deflation, hash chains are never searched beyond this
+ * length. A higher limit improves compression ratio but degrades the
+ * speed.
+ */
+
+ uInt max_lazy_match;
+ /* Attempt to find a better match only when the current match is strictly
+ * smaller than this value. This mechanism is used only for compression
+ * levels >= 4.
+ */
+# define max_insert_length max_lazy_match
+ /* Insert new strings in the hash table only if the match length is not
+ * greater than this length. This saves time but degrades compression.
+ * max_insert_length is used only for compression levels <= 3.
+ */
+
+ int level; /* compression level (1..9) */
+ int strategy; /* favor or force Huffman coding*/
+
+ uInt good_match;
+ /* Use a faster search when the previous match is longer than this */
+
+ int nice_match; /* Stop searching when current match exceeds this */
+
+ /* used by trees.c: */
+ /* Didn't use ct_data typedef below to supress compiler warning */
+ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
+ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
+ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
+
+ struct tree_desc_s l_desc; /* desc. for literal tree */
+ struct tree_desc_s d_desc; /* desc. for distance tree */
+ struct tree_desc_s bl_desc; /* desc. for bit length tree */
+
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
+ int heap_len; /* number of elements in the heap */
+ int heap_max; /* element of largest frequency */
+ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+ * The same heap array is used to build all trees.
+ */
+
+ uch depth[2*L_CODES+1];
+ /* Depth of each subtree used as tie breaker for trees of equal frequency
+ */
+
+ uchf *l_buf; /* buffer for literals or lengths */
+
+ uInt lit_bufsize;
+ /* Size of match buffer for literals/lengths. There are 4 reasons for
+ * limiting lit_bufsize to 64K:
+ * - frequencies can be kept in 16 bit counters
+ * - if compression is not successful for the first block, all input
+ * data is still in the window so we can still emit a stored block even
+ * when input comes from standard input. (This can also be done for
+ * all blocks if lit_bufsize is not greater than 32K.)
+ * - if compression is not successful for a file smaller than 64K, we can
+ * even emit a stored file instead of a stored block (saving 5 bytes).
+ * This is applicable only for zip (not gzip or zlib).
+ * - creating new Huffman trees less frequently may not provide fast
+ * adaptation to changes in the input data statistics. (Take for
+ * example a binary file with poorly compressible code followed by
+ * a highly compressible string table.) Smaller buffer sizes give
+ * fast adaptation but have of course the overhead of transmitting
+ * trees more frequently.
+ * - I can't count above 4
+ */
+
+ uInt last_lit; /* running index in l_buf */
+
+ ushf *d_buf;
+ /* Buffer for distances. To simplify the code, d_buf and l_buf have
+ * the same number of elements. To use different lengths, an extra flag
+ * array would be necessary.
+ */
+
+ ulg opt_len; /* bit length of current block with optimal trees */
+ ulg static_len; /* bit length of current block with static trees */
+ uInt matches; /* number of string matches in current block */
+ int last_eob_len; /* bit length of EOB code for last block */
+
+#ifdef DEBUG
+ ulg compressed_len; /* total bit length of compressed file mod 2^32 */
+ ulg bits_sent; /* bit length of compressed data sent mod 2^32 */
+#endif
+
+ ush bi_buf;
+ /* Output buffer. bits are inserted starting at the bottom (least
+ * significant bits).
+ */
+ int bi_valid;
+ /* Number of valid bits in bi_buf. All bits above the last valid bit
+ * are always zero.
+ */
+
+} FAR deflate_state;
+
+/* Output a byte on the stream.
+ * IN assertion: there is enough room in pending_buf.
+ */
+#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
+
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+ /* in trees.c */
+void _tr_init OF((deflate_state *s));
+int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
+void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len,
+ int eof));
+void _tr_align OF((deflate_state *s));
+void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len,
+ int eof));
+
+#define d_code(dist) \
+ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. _dist_code[256] and _dist_code[257] are never
+ * used.
+ */
+
+#ifndef DEBUG
+/* Inline versions of _tr_tally for speed: */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+ extern uch _length_code[];
+ extern uch _dist_code[];
+#else
+ extern const uch _length_code[];
+ extern const uch _dist_code[];
+#endif
+
+# define _tr_tally_lit(s, c, flush) \
+ { uch cc = (c); \
+ s->d_buf[s->last_lit] = 0; \
+ s->l_buf[s->last_lit++] = cc; \
+ s->dyn_ltree[cc].Freq++; \
+ flush = (s->last_lit == s->lit_bufsize-1); \
+ }
+# define _tr_tally_dist(s, distance, length, flush) \
+ { uch len = (length); \
+ ush dist = (distance); \
+ s->d_buf[s->last_lit] = dist; \
+ s->l_buf[s->last_lit++] = len; \
+ dist--; \
+ s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
+ s->dyn_dtree[d_code(dist)].Freq++; \
+ flush = (s->last_lit == s->lit_bufsize-1); \
+ }
+#else
+# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
+# define _tr_tally_dist(s, distance, length, flush) \
+ flush = _tr_tally(s, distance, length)
+#endif
+
+#endif /* DEFLATE_H */
diff --git a/zlib/example.c b/zlib/example.c
new file mode 100644
index 0000000..24a5d42
--- /dev/null
+++ b/zlib/example.c
@@ -0,0 +1,567 @@
+/* example.c -- usage example of the zlib compression library
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#include <stdio.h>
+#include "zlib.h"
+
+#ifdef STDC
+# include <string.h>
+# include <stdlib.h>
+#else
+ extern void exit OF((int));
+#endif
+
+#if defined(VMS) || defined(RISCOS)
+# define TESTFILE "foo-gz"
+#else
+# define TESTFILE "foo.gz"
+#endif
+
+#define CHECK_ERR(err, msg) { \
+ if (err != Z_OK) { \
+ fprintf(stderr, "%s error: %d\n", msg, err); \
+ exit(1); \
+ } \
+}
+
+const char hello[] = "hello, hello!";
+/* "hello world" would be more standard, but the repeated "hello"
+ * stresses the compression code better, sorry...
+ */
+
+const char dictionary[] = "hello";
+uLong dictId; /* Adler32 value of the dictionary */
+
+void test_compress OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+void test_gzio OF((const char *fname,
+ Byte *uncompr, uLong uncomprLen));
+void test_deflate OF((Byte *compr, uLong comprLen));
+void test_inflate OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+void test_large_deflate OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+void test_large_inflate OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+void test_flush OF((Byte *compr, uLong *comprLen));
+void test_sync OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+void test_dict_deflate OF((Byte *compr, uLong comprLen));
+void test_dict_inflate OF((Byte *compr, uLong comprLen,
+ Byte *uncompr, uLong uncomprLen));
+int main OF((int argc, char *argv[]));
+
+/* ===========================================================================
+ * Test compress() and uncompress()
+ */
+void test_compress(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ int err;
+ uLong len = (uLong)strlen(hello)+1;
+
+ err = compress(compr, &comprLen, (const Bytef*)hello, len);
+ CHECK_ERR(err, "compress");
+
+ strcpy((char*)uncompr, "garbage");
+
+ err = uncompress(uncompr, &uncomprLen, compr, comprLen);
+ CHECK_ERR(err, "uncompress");
+
+ if (strcmp((char*)uncompr, hello)) {
+ fprintf(stderr, "bad uncompress\n");
+ exit(1);
+ } else {
+ printf("uncompress(): %s\n", (char *)uncompr);
+ }
+}
+
+/* ===========================================================================
+ * Test read/write of .gz files
+ */
+void test_gzio(fname, uncompr, uncomprLen)
+ const char *fname; /* compressed file name */
+ Byte *uncompr;
+ uLong uncomprLen;
+{
+#ifdef NO_GZCOMPRESS
+ fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n");
+#else
+ int err;
+ int len = (int)strlen(hello)+1;
+ gzFile file;
+ z_off_t pos;
+
+ file = gzopen(fname, "wb");
+ if (file == NULL) {
+ fprintf(stderr, "gzopen error\n");
+ exit(1);
+ }
+ gzputc(file, 'h');
+ if (gzputs(file, "ello") != 4) {
+ fprintf(stderr, "gzputs err: %s\n", gzerror(file, &err));
+ exit(1);
+ }
+ if (gzprintf(file, ", %s!", "hello") != 8) {
+ fprintf(stderr, "gzprintf err: %s\n", gzerror(file, &err));
+ exit(1);
+ }
+ gzseek(file, 1L, SEEK_CUR); /* add one zero byte */
+ gzclose(file);
+
+ file = gzopen(fname, "rb");
+ if (file == NULL) {
+ fprintf(stderr, "gzopen error\n");
+ exit(1);
+ }
+ strcpy((char*)uncompr, "garbage");
+
+ if (gzread(file, uncompr, (unsigned)uncomprLen) != len) {
+ fprintf(stderr, "gzread err: %s\n", gzerror(file, &err));
+ exit(1);
+ }
+ if (strcmp((char*)uncompr, hello)) {
+ fprintf(stderr, "bad gzread: %s\n", (char*)uncompr);
+ exit(1);
+ } else {
+ printf("gzread(): %s\n", (char*)uncompr);
+ }
+
+ pos = gzseek(file, -8L, SEEK_CUR);
+ if (pos != 6 || gztell(file) != pos) {
+ fprintf(stderr, "gzseek error, pos=%ld, gztell=%ld\n",
+ (long)pos, (long)gztell(file));
+ exit(1);
+ }
+
+ if (gzgetc(file) != ' ') {
+ fprintf(stderr, "gzgetc error\n");
+ exit(1);
+ }
+
+ if (gzungetc(' ', file) != ' ') {
+ fprintf(stderr, "gzungetc error\n");
+ exit(1);
+ }
+
+ gzgets(file, (char*)uncompr, (int)uncomprLen);
+ if (strlen((char*)uncompr) != 7) { /* " hello!" */
+ fprintf(stderr, "gzgets err after gzseek: %s\n", gzerror(file, &err));
+ exit(1);
+ }
+ if (strcmp((char*)uncompr, hello + 6)) {
+ fprintf(stderr, "bad gzgets after gzseek\n");
+ exit(1);
+ } else {
+ printf("gzgets() after gzseek: %s\n", (char*)uncompr);
+ }
+
+ gzclose(file);
+#endif
+}
+
+/* ===========================================================================
+ * Test deflate() with small buffers
+ */
+void test_deflate(compr, comprLen)
+ Byte *compr;
+ uLong comprLen;
+{
+ z_stream c_stream; /* compression stream */
+ int err;
+ uLong len = (uLong)strlen(hello)+1;
+
+ c_stream.zalloc = (alloc_func)0;
+ c_stream.zfree = (free_func)0;
+ c_stream.opaque = (voidpf)0;
+
+ err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION);
+ CHECK_ERR(err, "deflateInit");
+
+ c_stream.next_in = (Bytef*)hello;
+ c_stream.next_out = compr;
+
+ while (c_stream.total_in != len && c_stream.total_out < comprLen) {
+ c_stream.avail_in = c_stream.avail_out = 1; /* force small buffers */
+ err = deflate(&c_stream, Z_NO_FLUSH);
+ CHECK_ERR(err, "deflate");
+ }
+ /* Finish the stream, still forcing small buffers: */
+ for (;;) {
+ c_stream.avail_out = 1;
+ err = deflate(&c_stream, Z_FINISH);
+ if (err == Z_STREAM_END) break;
+ CHECK_ERR(err, "deflate");
+ }
+
+ err = deflateEnd(&c_stream);
+ CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with small buffers
+ */
+void test_inflate(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ int err;
+ z_stream d_stream; /* decompression stream */
+
+ strcpy((char*)uncompr, "garbage");
+
+ d_stream.zalloc = (alloc_func)0;
+ d_stream.zfree = (free_func)0;
+ d_stream.opaque = (voidpf)0;
+
+ d_stream.next_in = compr;
+ d_stream.avail_in = 0;
+ d_stream.next_out = uncompr;
+
+ err = inflateInit(&d_stream);
+ CHECK_ERR(err, "inflateInit");
+
+ while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) {
+ d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */
+ err = inflate(&d_stream, Z_NO_FLUSH);
+ if (err == Z_STREAM_END) break;
+ CHECK_ERR(err, "inflate");
+ }
+
+ err = inflateEnd(&d_stream);
+ CHECK_ERR(err, "inflateEnd");
+
+ if (strcmp((char*)uncompr, hello)) {
+ fprintf(stderr, "bad inflate\n");
+ exit(1);
+ } else {
+ printf("inflate(): %s\n", (char *)uncompr);
+ }
+}
+
+/* ===========================================================================
+ * Test deflate() with large buffers and dynamic change of compression level
+ */
+void test_large_deflate(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ z_stream c_stream; /* compression stream */
+ int err;
+
+ c_stream.zalloc = (alloc_func)0;
+ c_stream.zfree = (free_func)0;
+ c_stream.opaque = (voidpf)0;
+
+ err = deflateInit(&c_stream, Z_BEST_SPEED);
+ CHECK_ERR(err, "deflateInit");
+
+ c_stream.next_out = compr;
+ c_stream.avail_out = (uInt)comprLen;
+
+ /* At this point, uncompr is still mostly zeroes, so it should compress
+ * very well:
+ */
+ c_stream.next_in = uncompr;
+ c_stream.avail_in = (uInt)uncomprLen;
+ err = deflate(&c_stream, Z_NO_FLUSH);
+ CHECK_ERR(err, "deflate");
+ if (c_stream.avail_in != 0) {
+ fprintf(stderr, "deflate not greedy\n");
+ exit(1);
+ }
+
+ /* Feed in already compressed data and switch to no compression: */
+ deflateParams(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY);
+ c_stream.next_in = compr;
+ c_stream.avail_in = (uInt)comprLen/2;
+ err = deflate(&c_stream, Z_NO_FLUSH);
+ CHECK_ERR(err, "deflate");
+
+ /* Switch back to compressing mode: */
+ deflateParams(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED);
+ c_stream.next_in = uncompr;
+ c_stream.avail_in = (uInt)uncomprLen;
+ err = deflate(&c_stream, Z_NO_FLUSH);
+ CHECK_ERR(err, "deflate");
+
+ err = deflate(&c_stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ fprintf(stderr, "deflate should report Z_STREAM_END\n");
+ exit(1);
+ }
+ err = deflateEnd(&c_stream);
+ CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with large buffers
+ */
+void test_large_inflate(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ int err;
+ z_stream d_stream; /* decompression stream */
+
+ strcpy((char*)uncompr, "garbage");
+
+ d_stream.zalloc = (alloc_func)0;
+ d_stream.zfree = (free_func)0;
+ d_stream.opaque = (voidpf)0;
+
+ d_stream.next_in = compr;
+ d_stream.avail_in = (uInt)comprLen;
+
+ err = inflateInit(&d_stream);
+ CHECK_ERR(err, "inflateInit");
+
+ for (;;) {
+ d_stream.next_out = uncompr; /* discard the output */
+ d_stream.avail_out = (uInt)uncomprLen;
+ err = inflate(&d_stream, Z_NO_FLUSH);
+ if (err == Z_STREAM_END) break;
+ CHECK_ERR(err, "large inflate");
+ }
+
+ err = inflateEnd(&d_stream);
+ CHECK_ERR(err, "inflateEnd");
+
+ if (d_stream.total_out != 2*uncomprLen + comprLen/2) {
+ fprintf(stderr, "bad large inflate: %ld\n", d_stream.total_out);
+ exit(1);
+ } else {
+ printf("large_inflate(): OK\n");
+ }
+}
+
+/* ===========================================================================
+ * Test deflate() with full flush
+ */
+void test_flush(compr, comprLen)
+ Byte *compr;
+ uLong *comprLen;
+{
+ z_stream c_stream; /* compression stream */
+ int err;
+ uInt len = (uInt)strlen(hello)+1;
+
+ c_stream.zalloc = (alloc_func)0;
+ c_stream.zfree = (free_func)0;
+ c_stream.opaque = (voidpf)0;
+
+ err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION);
+ CHECK_ERR(err, "deflateInit");
+
+ c_stream.next_in = (Bytef*)hello;
+ c_stream.next_out = compr;
+ c_stream.avail_in = 3;
+ c_stream.avail_out = (uInt)*comprLen;
+ err = deflate(&c_stream, Z_FULL_FLUSH);
+ CHECK_ERR(err, "deflate");
+
+ compr[3]++; /* force an error in first compressed block */
+ c_stream.avail_in = len - 3;
+
+ err = deflate(&c_stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ CHECK_ERR(err, "deflate");
+ }
+ err = deflateEnd(&c_stream);
+ CHECK_ERR(err, "deflateEnd");
+
+ *comprLen = c_stream.total_out;
+}
+
+/* ===========================================================================
+ * Test inflateSync()
+ */
+void test_sync(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ int err;
+ z_stream d_stream; /* decompression stream */
+
+ strcpy((char*)uncompr, "garbage");
+
+ d_stream.zalloc = (alloc_func)0;
+ d_stream.zfree = (free_func)0;
+ d_stream.opaque = (voidpf)0;
+
+ d_stream.next_in = compr;
+ d_stream.avail_in = 2; /* just read the zlib header */
+
+ err = inflateInit(&d_stream);
+ CHECK_ERR(err, "inflateInit");
+
+ d_stream.next_out = uncompr;
+ d_stream.avail_out = (uInt)uncomprLen;
+
+ inflate(&d_stream, Z_NO_FLUSH);
+ CHECK_ERR(err, "inflate");
+
+ d_stream.avail_in = (uInt)comprLen-2; /* read all compressed data */
+ err = inflateSync(&d_stream); /* but skip the damaged part */
+ CHECK_ERR(err, "inflateSync");
+
+ err = inflate(&d_stream, Z_FINISH);
+ if (err != Z_DATA_ERROR) {
+ fprintf(stderr, "inflate should report DATA_ERROR\n");
+ /* Because of incorrect adler32 */
+ exit(1);
+ }
+ err = inflateEnd(&d_stream);
+ CHECK_ERR(err, "inflateEnd");
+
+ printf("after inflateSync(): hel%s\n", (char *)uncompr);
+}
+
+/* ===========================================================================
+ * Test deflate() with preset dictionary
+ */
+void test_dict_deflate(compr, comprLen)
+ Byte *compr;
+ uLong comprLen;
+{
+ z_stream c_stream; /* compression stream */
+ int err;
+
+ c_stream.zalloc = (alloc_func)0;
+ c_stream.zfree = (free_func)0;
+ c_stream.opaque = (voidpf)0;
+
+ err = deflateInit(&c_stream, Z_BEST_COMPRESSION);
+ CHECK_ERR(err, "deflateInit");
+
+ err = deflateSetDictionary(&c_stream,
+ (const Bytef*)dictionary, sizeof(dictionary));
+ CHECK_ERR(err, "deflateSetDictionary");
+
+ dictId = c_stream.adler;
+ c_stream.next_out = compr;
+ c_stream.avail_out = (uInt)comprLen;
+
+ c_stream.next_in = (Bytef*)hello;
+ c_stream.avail_in = (uInt)strlen(hello)+1;
+
+ err = deflate(&c_stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ fprintf(stderr, "deflate should report Z_STREAM_END\n");
+ exit(1);
+ }
+ err = deflateEnd(&c_stream);
+ CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with a preset dictionary
+ */
+void test_dict_inflate(compr, comprLen, uncompr, uncomprLen)
+ Byte *compr, *uncompr;
+ uLong comprLen, uncomprLen;
+{
+ int err;
+ z_stream d_stream; /* decompression stream */
+
+ strcpy((char*)uncompr, "garbage");
+
+ d_stream.zalloc = (alloc_func)0;
+ d_stream.zfree = (free_func)0;
+ d_stream.opaque = (voidpf)0;
+
+ d_stream.next_in = compr;
+ d_stream.avail_in = (uInt)comprLen;
+
+ err = inflateInit(&d_stream);
+ CHECK_ERR(err, "inflateInit");
+
+ d_stream.next_out = uncompr;
+ d_stream.avail_out = (uInt)uncomprLen;
+
+ for (;;) {
+ err = inflate(&d_stream, Z_NO_FLUSH);
+ if (err == Z_STREAM_END) break;
+ if (err == Z_NEED_DICT) {
+ if (d_stream.adler != dictId) {
+ fprintf(stderr, "unexpected dictionary");
+ exit(1);
+ }
+ err = inflateSetDictionary(&d_stream, (const Bytef*)dictionary,
+ sizeof(dictionary));
+ }
+ CHECK_ERR(err, "inflate with dict");
+ }
+
+ err = inflateEnd(&d_stream);
+ CHECK_ERR(err, "inflateEnd");
+
+ if (strcmp((char*)uncompr, hello)) {
+ fprintf(stderr, "bad inflate with dict\n");
+ exit(1);
+ } else {
+ printf("inflate with dictionary: %s\n", (char *)uncompr);
+ }
+}
+
+/* ===========================================================================
+ * Usage: example [output.gz [input.gz]]
+ */
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ Byte *compr, *uncompr;
+ uLong comprLen = 10000*sizeof(int); /* don't overflow on MSDOS */
+ uLong uncomprLen = comprLen;
+ static const char* myVersion = ZLIB_VERSION;
+
+ if (zlibVersion()[0] != myVersion[0]) {
+ fprintf(stderr, "incompatible zlib version\n");
+ exit(1);
+
+ } else if (strcmp(zlibVersion(), ZLIB_VERSION) != 0) {
+ fprintf(stderr, "warning: different zlib version\n");
+ }
+
+ printf("zlib version %s = 0x%04x, compile flags = 0x%lx\n",
+ ZLIB_VERSION, ZLIB_VERNUM, zlibCompileFlags());
+
+ compr = (Byte*)calloc((uInt)comprLen, 1);
+ uncompr = (Byte*)calloc((uInt)uncomprLen, 1);
+ /* compr and uncompr are cleared to avoid reading uninitialized
+ * data and to ensure that uncompr compresses well.
+ */
+ if (compr == Z_NULL || uncompr == Z_NULL) {
+ printf("out of memory\n");
+ exit(1);
+ }
+ test_compress(compr, comprLen, uncompr, uncomprLen);
+
+ test_gzio((argc > 1 ? argv[1] : TESTFILE),
+ uncompr, uncomprLen);
+
+ test_deflate(compr, comprLen);
+ test_inflate(compr, comprLen, uncompr, uncomprLen);
+
+ test_large_deflate(compr, comprLen, uncompr, uncomprLen);
+ test_large_inflate(compr, comprLen, uncompr, uncomprLen);
+
+ test_flush(compr, &comprLen);
+ test_sync(compr, comprLen, uncompr, uncomprLen);
+ comprLen = uncomprLen;
+
+ test_dict_deflate(compr, comprLen);
+ test_dict_inflate(compr, comprLen, uncompr, uncomprLen);
+
+ free(compr);
+ free(uncompr);
+
+ return 0;
+}
diff --git a/zlib/gzio.c b/zlib/gzio.c
new file mode 100644
index 0000000..de93379
--- /dev/null
+++ b/zlib/gzio.c
@@ -0,0 +1,1009 @@
+/* gzio.c -- IO on .gz files
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Compile this file with -DNO_GZCOMPRESS to avoid the compression code.
+ */
+
+/* @(#) $Id$ */
+
+#include <stdio.h>
+
+#include "zutil.h"
+
+#ifdef NO_DEFLATE /* for compatiblity with old definition */
+# define NO_GZCOMPRESS
+#endif
+
+#ifndef NO_DUMMY_DECL
+struct internal_state {int dummy;}; /* for buggy compilers */
+#endif
+
+#ifndef Z_BUFSIZE
+# ifdef MAXSEG_64K
+# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */
+# else
+# define Z_BUFSIZE 16384
+# endif
+#endif
+#ifndef Z_PRINTF_BUFSIZE
+# define Z_PRINTF_BUFSIZE 4096
+#endif
+
+#ifdef __MVS__
+# pragma map (fdopen , "\174\174FDOPEN")
+ FILE *fdopen(int, const char *);
+#endif
+
+#ifndef STDC
+extern voidp malloc OF((uInt size));
+extern void free OF((voidpf ptr));
+#endif
+
+#define ALLOC(size) malloc(size)
+#define TRYFREE(p) {if (p) free(p);}
+
+static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
+
+/* gzip flag byte */
+#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
+#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
+#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
+#define COMMENT 0x10 /* bit 4 set: file comment present */
+#define RESERVED 0xE0 /* bits 5..7: reserved */
+
+typedef struct gz_stream {
+ z_stream stream;
+ int z_err; /* error code for last stream operation */
+ int z_eof; /* set if end of input file */
+ FILE *file; /* .gz file */
+ Byte *inbuf; /* input buffer */
+ Byte *outbuf; /* output buffer */
+ uLong crc; /* crc32 of uncompressed data */
+ char *msg; /* error message */
+ char *path; /* path name for debugging only */
+ int transparent; /* 1 if input file is not a .gz file */
+ char mode; /* 'w' or 'r' */
+ z_off_t start; /* start of compressed data in file (header skipped) */
+ z_off_t in; /* bytes into deflate or inflate */
+ z_off_t out; /* bytes out of deflate or inflate */
+ int back; /* one character push-back */
+ int last; /* true if push-back is last character */
+} gz_stream;
+
+
+local gzFile gz_open OF((const char *path, const char *mode, int fd));
+local int do_flush OF((gzFile file, int flush));
+local int get_byte OF((gz_stream *s));
+local void check_header OF((gz_stream *s));
+local int destroy OF((gz_stream *s));
+local void putLong OF((FILE *file, uLong x));
+local uLong getLong OF((gz_stream *s));
+
+/* ===========================================================================
+ Opens a gzip (.gz) file for reading or writing. The mode parameter
+ is as in fopen ("rb" or "wb"). The file is given either by file descriptor
+ or path name (if fd == -1).
+ gz_open returns NULL if the file could not be opened or if there was
+ insufficient memory to allocate the (de)compression state; errno
+ can be checked to distinguish the two cases (if errno is zero, the
+ zlib error is Z_MEM_ERROR).
+*/
+local gzFile gz_open (path, mode, fd)
+ const char *path;
+ const char *mode;
+ int fd;
+{
+ int err;
+ int level = Z_DEFAULT_COMPRESSION; /* compression level */
+ int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
+ char *p = (char*)mode;
+ gz_stream *s;
+ char fmode[80]; /* copy of mode, without the compression level */
+ char *m = fmode;
+
+ if (!path || !mode) return Z_NULL;
+
+ s = (gz_stream *)ALLOC(sizeof(gz_stream));
+ if (!s) return Z_NULL;
+
+ s->stream.zalloc = (alloc_func)0;
+ s->stream.zfree = (free_func)0;
+ s->stream.opaque = (voidpf)0;
+ s->stream.next_in = s->inbuf = Z_NULL;
+ s->stream.next_out = s->outbuf = Z_NULL;
+ s->stream.avail_in = s->stream.avail_out = 0;
+ s->file = NULL;
+ s->z_err = Z_OK;
+ s->z_eof = 0;
+ s->in = 0;
+ s->out = 0;
+ s->back = EOF;
+ s->crc = crc32(0L, Z_NULL, 0);
+ s->msg = NULL;
+ s->transparent = 0;
+
+ s->path = (char*)ALLOC(strlen(path)+1);
+ if (s->path == NULL) {
+ return destroy(s), (gzFile)Z_NULL;
+ }
+ strcpy(s->path, path); /* do this early for debugging */
+
+ s->mode = '\0';
+ do {
+ if (*p == 'r') s->mode = 'r';
+ if (*p == 'w' || *p == 'a') s->mode = 'w';
+ if (*p >= '0' && *p <= '9') {
+ level = *p - '0';
+ } else if (*p == 'f') {
+ strategy = Z_FILTERED;
+ } else if (*p == 'h') {
+ strategy = Z_HUFFMAN_ONLY;
+ } else if (*p == 'R') {
+ strategy = Z_RLE;
+ } else {
+ *m++ = *p; /* copy the mode */
+ }
+ } while (*p++ && m != fmode + sizeof(fmode));
+ if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL;
+
+ if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+ err = Z_STREAM_ERROR;
+#else
+ err = deflateInit2(&(s->stream), level,
+ Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy);
+ /* windowBits is passed < 0 to suppress zlib header */
+
+ s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
+#endif
+ if (err != Z_OK || s->outbuf == Z_NULL) {
+ return destroy(s), (gzFile)Z_NULL;
+ }
+ } else {
+ s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE);
+
+ err = inflateInit2(&(s->stream), -MAX_WBITS);
+ /* windowBits is passed < 0 to tell that there is no zlib header.
+ * Note that in this case inflate *requires* an extra "dummy" byte
+ * after the compressed stream in order to complete decompression and
+ * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
+ * present after the compressed stream.
+ */
+ if (err != Z_OK || s->inbuf == Z_NULL) {
+ return destroy(s), (gzFile)Z_NULL;
+ }
+ }
+ s->stream.avail_out = Z_BUFSIZE;
+
+ errno = 0;
+ s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode);
+
+ if (s->file == NULL) {
+ return destroy(s), (gzFile)Z_NULL;
+ }
+ if (s->mode == 'w') {
+ /* Write a very simple .gz header:
+ */
+ fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
+ Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE);
+ s->start = 10L;
+ /* We use 10L instead of ftell(s->file) to because ftell causes an
+ * fflush on some systems. This version of the library doesn't use
+ * start anyway in write mode, so this initialization is not
+ * necessary.
+ */
+ } else {
+ check_header(s); /* skip the .gz header */
+ s->start = ftell(s->file) - s->stream.avail_in;
+ }
+
+ return (gzFile)s;
+}
+
+/* ===========================================================================
+ Opens a gzip (.gz) file for reading or writing.
+*/
+gzFile ZEXPORT gzopen (path, mode)
+ const char *path;
+ const char *mode;
+{
+ return gz_open (path, mode, -1);
+}
+
+/* ===========================================================================
+ Associate a gzFile with the file descriptor fd. fd is not dup'ed here
+ to mimic the behavio(u)r of fdopen.
+*/
+gzFile ZEXPORT gzdopen (fd, mode)
+ int fd;
+ const char *mode;
+{
+ char name[20];
+
+ if (fd < 0) return (gzFile)Z_NULL;
+ sprintf(name, "<fd:%d>", fd); /* for debugging */
+
+ return gz_open (name, mode, fd);
+}
+
+/* ===========================================================================
+ * Update the compression level and strategy
+ */
+int ZEXPORT gzsetparams (file, level, strategy)
+ gzFile file;
+ int level;
+ int strategy;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+ /* Make room to allow flushing */
+ if (s->stream.avail_out == 0) {
+
+ s->stream.next_out = s->outbuf;
+ if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
+ s->z_err = Z_ERRNO;
+ }
+ s->stream.avail_out = Z_BUFSIZE;
+ }
+
+ return deflateParams (&(s->stream), level, strategy);
+}
+
+/* ===========================================================================
+ Read a byte from a gz_stream; update next_in and avail_in. Return EOF
+ for end of file.
+ IN assertion: the stream s has been sucessfully opened for reading.
+*/
+local int get_byte(s)
+ gz_stream *s;
+{
+ if (s->z_eof) return EOF;
+ if (s->stream.avail_in == 0) {
+ errno = 0;
+ s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file);
+ if (s->stream.avail_in == 0) {
+ s->z_eof = 1;
+ if (ferror(s->file)) s->z_err = Z_ERRNO;
+ return EOF;
+ }
+ s->stream.next_in = s->inbuf;
+ }
+ s->stream.avail_in--;
+ return *(s->stream.next_in)++;
+}
+
+/* ===========================================================================
+ Check the gzip header of a gz_stream opened for reading. Set the stream
+ mode to transparent if the gzip magic header is not present; set s->err
+ to Z_DATA_ERROR if the magic header is present but the rest of the header
+ is incorrect.
+ IN assertion: the stream s has already been created sucessfully;
+ s->stream.avail_in is zero for the first time, but may be non-zero
+ for concatenated .gz files.
+*/
+local void check_header(s)
+ gz_stream *s;
+{
+ int method; /* method byte */
+ int flags; /* flags byte */
+ uInt len;
+ int c;
+
+ /* Assure two bytes in the buffer so we can peek ahead -- handle case
+ where first byte of header is at the end of the buffer after the last
+ gzip segment */
+ len = s->stream.avail_in;
+ if (len < 2) {
+ if (len) s->inbuf[0] = s->stream.next_in[0];
+ errno = 0;
+ len = fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file);
+ if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO;
+ s->stream.avail_in += len;
+ s->stream.next_in = s->inbuf;
+ if (s->stream.avail_in < 2) {
+ s->transparent = s->stream.avail_in;
+ return;
+ }
+ }
+
+ /* Peek ahead to check the gzip magic header */
+ if (s->stream.next_in[0] != gz_magic[0] ||
+ s->stream.next_in[1] != gz_magic[1]) {
+ s->transparent = 1;
+ return;
+ }
+ s->stream.avail_in -= 2;
+ s->stream.next_in += 2;
+
+ /* Check the rest of the gzip header */
+ method = get_byte(s);
+ flags = get_byte(s);
+ if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
+ s->z_err = Z_DATA_ERROR;
+ return;
+ }
+
+ /* Discard time, xflags and OS code: */
+ for (len = 0; len < 6; len++) (void)get_byte(s);
+
+ if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
+ len = (uInt)get_byte(s);
+ len += ((uInt)get_byte(s))<<8;
+ /* len is garbage if EOF but the loop below will quit anyway */
+ while (len-- != 0 && get_byte(s) != EOF) ;
+ }
+ if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
+ while ((c = get_byte(s)) != 0 && c != EOF) ;
+ }
+ if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
+ while ((c = get_byte(s)) != 0 && c != EOF) ;
+ }
+ if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
+ for (len = 0; len < 2; len++) (void)get_byte(s);
+ }
+ s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
+}
+
+ /* ===========================================================================
+ * Cleanup then free the given gz_stream. Return a zlib error code.
+ Try freeing in the reverse order of allocations.
+ */
+local int destroy (s)
+ gz_stream *s;
+{
+ int err = Z_OK;
+
+ if (!s) return Z_STREAM_ERROR;
+
+ TRYFREE(s->msg);
+
+ if (s->stream.state != NULL) {
+ if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+ err = Z_STREAM_ERROR;
+#else
+ err = deflateEnd(&(s->stream));
+#endif
+ } else if (s->mode == 'r') {
+ err = inflateEnd(&(s->stream));
+ }
+ }
+ if (s->file != NULL && fclose(s->file)) {
+#ifdef ESPIPE
+ if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */
+#endif
+ err = Z_ERRNO;
+ }
+ if (s->z_err < 0) err = s->z_err;
+
+ TRYFREE(s->inbuf);
+ TRYFREE(s->outbuf);
+ TRYFREE(s->path);
+ TRYFREE(s);
+ return err;
+}
+
+/* ===========================================================================
+ Reads the given number of uncompressed bytes from the compressed file.
+ gzread returns the number of bytes actually read (0 for end of file).
+*/
+int ZEXPORT gzread (file, buf, len)
+ gzFile file;
+ voidp buf;
+ unsigned len;
+{
+ gz_stream *s = (gz_stream*)file;
+ Bytef *start = (Bytef*)buf; /* starting point for crc computation */
+ Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */
+
+ if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR;
+
+ if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
+ if (s->z_err == Z_STREAM_END) return 0; /* EOF */
+
+ next_out = (Byte*)buf;
+ s->stream.next_out = (Bytef*)buf;
+ s->stream.avail_out = len;
+
+ if (s->stream.avail_out && s->back != EOF) {
+ *next_out++ = s->back;
+ s->stream.next_out++;
+ s->stream.avail_out--;
+ s->back = EOF;
+ s->out++;
+ if (s->last) {
+ s->z_err = Z_STREAM_END;
+ return 1;
+ }
+ }
+
+ while (s->stream.avail_out != 0) {
+
+ if (s->transparent) {
+ /* Copy first the lookahead bytes: */
+ uInt n = s->stream.avail_in;
+ if (n > s->stream.avail_out) n = s->stream.avail_out;
+ if (n > 0) {
+ zmemcpy(s->stream.next_out, s->stream.next_in, n);
+ next_out += n;
+ s->stream.next_out = next_out;
+ s->stream.next_in += n;
+ s->stream.avail_out -= n;
+ s->stream.avail_in -= n;
+ }
+ if (s->stream.avail_out > 0) {
+ s->stream.avail_out -= fread(next_out, 1, s->stream.avail_out,
+ s->file);
+ }
+ len -= s->stream.avail_out;
+ s->in += len;
+ s->out += len;
+ if (len == 0) s->z_eof = 1;
+ return (int)len;
+ }
+ if (s->stream.avail_in == 0 && !s->z_eof) {
+
+ errno = 0;
+ s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file);
+ if (s->stream.avail_in == 0) {
+ s->z_eof = 1;
+ if (ferror(s->file)) {
+ s->z_err = Z_ERRNO;
+ break;
+ }
+ if (feof(s->file)) { /* avoid error for empty file */
+ s->z_err = Z_STREAM_END;
+ break;
+ }
+ }
+ s->stream.next_in = s->inbuf;
+ }
+ s->in += s->stream.avail_in;
+ s->out += s->stream.avail_out;
+ s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
+ s->in -= s->stream.avail_in;
+ s->out -= s->stream.avail_out;
+
+ if (s->z_err == Z_STREAM_END) {
+ /* Check CRC and original size */
+ s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
+ start = s->stream.next_out;
+
+ if (getLong(s) != s->crc) {
+ s->z_err = Z_DATA_ERROR;
+ } else {
+ (void)getLong(s);
+ /* The uncompressed length returned by above getlong() may be
+ * different from s->out in case of concatenated .gz files.
+ * Check for such files:
+ */
+ check_header(s);
+ if (s->z_err == Z_OK) {
+ inflateReset(&(s->stream));
+ s->crc = crc32(0L, Z_NULL, 0);
+ }
+ }
+ }
+ if (s->z_err != Z_OK || s->z_eof) break;
+ }
+ s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
+
+ return (int)(len - s->stream.avail_out);
+}
+
+
+/* ===========================================================================
+ Reads one byte from the compressed file. gzgetc returns this byte
+ or -1 in case of end of file or error.
+*/
+int ZEXPORT gzgetc(file)
+ gzFile file;
+{
+ unsigned char c;
+
+ return gzread(file, &c, 1) == 1 ? c : -1;
+}
+
+
+/* ===========================================================================
+ Push one byte back onto the stream.
+*/
+int ZEXPORT gzungetc(c, file)
+ int c;
+ gzFile file;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF;
+ s->back = c;
+ s->out--;
+ s->last = (s->z_err == Z_STREAM_END);
+ if (s->last) s->z_err = Z_OK;
+ s->z_eof = 0;
+ return c;
+}
+
+
+/* ===========================================================================
+ Reads bytes from the compressed file until len-1 characters are
+ read, or a newline character is read and transferred to buf, or an
+ end-of-file condition is encountered. The string is then terminated
+ with a null character.
+ gzgets returns buf, or Z_NULL in case of error.
+
+ The current implementation is not optimized at all.
+*/
+char * ZEXPORT gzgets(file, buf, len)
+ gzFile file;
+ char *buf;
+ int len;
+{
+ char *b = buf;
+ if (buf == Z_NULL || len <= 0) return Z_NULL;
+
+ while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ;
+ *buf = '\0';
+ return b == buf && len > 0 ? Z_NULL : b;
+}
+
+
+#ifndef NO_GZCOMPRESS
+/* ===========================================================================
+ Writes the given number of uncompressed bytes into the compressed file.
+ gzwrite returns the number of bytes actually written (0 in case of error).
+*/
+int ZEXPORT gzwrite (file, buf, len)
+ gzFile file;
+ voidpc buf;
+ unsigned len;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+ s->stream.next_in = (Bytef*)buf;
+ s->stream.avail_in = len;
+
+ while (s->stream.avail_in != 0) {
+
+ if (s->stream.avail_out == 0) {
+
+ s->stream.next_out = s->outbuf;
+ if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
+ s->z_err = Z_ERRNO;
+ break;
+ }
+ s->stream.avail_out = Z_BUFSIZE;
+ }
+ s->in += s->stream.avail_in;
+ s->out += s->stream.avail_out;
+ s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
+ s->in -= s->stream.avail_in;
+ s->out -= s->stream.avail_out;
+ if (s->z_err != Z_OK) break;
+ }
+ s->crc = crc32(s->crc, (const Bytef *)buf, len);
+
+ return (int)(len - s->stream.avail_in);
+}
+
+
+/* ===========================================================================
+ Converts, formats, and writes the args to the compressed file under
+ control of the format string, as in fprintf. gzprintf returns the number of
+ uncompressed bytes actually written (0 in case of error).
+*/
+#ifdef STDC
+#include <stdarg.h>
+
+int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...)
+{
+ char buf[Z_PRINTF_BUFSIZE];
+ va_list va;
+ int len;
+
+ buf[sizeof(buf) - 1] = 0;
+ va_start(va, format);
+#ifdef NO_vsnprintf
+# ifdef HAS_vsprintf_void
+ (void)vsprintf(buf, format, va);
+ va_end(va);
+ for (len = 0; len < sizeof(buf); len++)
+ if (buf[len] == 0) break;
+# else
+ len = vsprintf(buf, format, va);
+ va_end(va);
+# endif
+#else
+# ifdef HAS_vsnprintf_void
+ (void)vsnprintf(buf, sizeof(buf), format, va);
+ va_end(va);
+ len = strlen(buf);
+# else
+ len = vsnprintf(buf, sizeof(buf), format, va);
+ va_end(va);
+# endif
+#endif
+ if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0)
+ return 0;
+ return gzwrite(file, buf, (unsigned)len);
+}
+#else /* not ANSI C */
+
+int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+ a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ gzFile file;
+ const char *format;
+ int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+ a11, a12, a13, a14, a15, a16, a17, a18, a19, a20;
+{
+ char buf[Z_PRINTF_BUFSIZE];
+ int len;
+
+ buf[sizeof(buf) - 1] = 0;
+#ifdef NO_snprintf
+# ifdef HAS_sprintf_void
+ sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8,
+ a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+ for (len = 0; len < sizeof(buf); len++)
+ if (buf[len] == 0) break;
+# else
+ len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8,
+ a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+# endif
+#else
+# ifdef HAS_snprintf_void
+ snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8,
+ a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+ len = strlen(buf);
+# else
+ len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8,
+ a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+# endif
+#endif
+ if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0)
+ return 0;
+ return gzwrite(file, buf, len);
+}
+#endif
+
+/* ===========================================================================
+ Writes c, converted to an unsigned char, into the compressed file.
+ gzputc returns the value that was written, or -1 in case of error.
+*/
+int ZEXPORT gzputc(file, c)
+ gzFile file;
+ int c;
+{
+ unsigned char cc = (unsigned char) c; /* required for big endian systems */
+
+ return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1;
+}
+
+
+/* ===========================================================================
+ Writes the given null-terminated string to the compressed file, excluding
+ the terminating null character.
+ gzputs returns the number of characters written, or -1 in case of error.
+*/
+int ZEXPORT gzputs(file, s)
+ gzFile file;
+ const char *s;
+{
+ return gzwrite(file, (char*)s, (unsigned)strlen(s));
+}
+
+
+/* ===========================================================================
+ Flushes all pending output into the compressed file. The parameter
+ flush is as in the deflate() function.
+*/
+local int do_flush (file, flush)
+ gzFile file;
+ int flush;
+{
+ uInt len;
+ int done = 0;
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+ s->stream.avail_in = 0; /* should be zero already anyway */
+
+ for (;;) {
+ len = Z_BUFSIZE - s->stream.avail_out;
+
+ if (len != 0) {
+ if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) {
+ s->z_err = Z_ERRNO;
+ return Z_ERRNO;
+ }
+ s->stream.next_out = s->outbuf;
+ s->stream.avail_out = Z_BUFSIZE;
+ }
+ if (done) break;
+ s->out += s->stream.avail_out;
+ s->z_err = deflate(&(s->stream), flush);
+ s->out -= s->stream.avail_out;
+
+ /* Ignore the second of two consecutive flushes: */
+ if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
+
+ /* deflate has finished flushing only when it hasn't used up
+ * all the available space in the output buffer:
+ */
+ done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
+
+ if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
+ }
+ return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+}
+
+int ZEXPORT gzflush (file, flush)
+ gzFile file;
+ int flush;
+{
+ gz_stream *s = (gz_stream*)file;
+ int err = do_flush (file, flush);
+
+ if (err) return err;
+ fflush(s->file);
+ return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+}
+#endif /* NO_GZCOMPRESS */
+
+/* ===========================================================================
+ Sets the starting position for the next gzread or gzwrite on the given
+ compressed file. The offset represents a number of bytes in the
+ gzseek returns the resulting offset location as measured in bytes from
+ the beginning of the uncompressed stream, or -1 in case of error.
+ SEEK_END is not implemented, returns error.
+ In this version of the library, gzseek can be extremely slow.
+*/
+z_off_t ZEXPORT gzseek (file, offset, whence)
+ gzFile file;
+ z_off_t offset;
+ int whence;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || whence == SEEK_END ||
+ s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) {
+ return -1L;
+ }
+
+ if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+ return -1L;
+#else
+ if (whence == SEEK_SET) {
+ offset -= s->in;
+ }
+ if (offset < 0) return -1L;
+
+ /* At this point, offset is the number of zero bytes to write. */
+ if (s->inbuf == Z_NULL) {
+ s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */
+ if (s->inbuf == Z_NULL) return -1L;
+ zmemzero(s->inbuf, Z_BUFSIZE);
+ }
+ while (offset > 0) {
+ uInt size = Z_BUFSIZE;
+ if (offset < Z_BUFSIZE) size = (uInt)offset;
+
+ size = gzwrite(file, s->inbuf, size);
+ if (size == 0) return -1L;
+
+ offset -= size;
+ }
+ return s->in;
+#endif
+ }
+ /* Rest of function is for reading only */
+
+ /* compute absolute position */
+ if (whence == SEEK_CUR) {
+ offset += s->out;
+ }
+ if (offset < 0) return -1L;
+
+ if (s->transparent) {
+ /* map to fseek */
+ s->back = EOF;
+ s->stream.avail_in = 0;
+ s->stream.next_in = s->inbuf;
+ if (fseek(s->file, offset, SEEK_SET) < 0) return -1L;
+
+ s->in = s->out = offset;
+ return offset;
+ }
+
+ /* For a negative seek, rewind and use positive seek */
+ if (offset >= s->out) {
+ offset -= s->out;
+ } else if (gzrewind(file) < 0) {
+ return -1L;
+ }
+ /* offset is now the number of bytes to skip. */
+
+ if (offset != 0 && s->outbuf == Z_NULL) {
+ s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
+ if (s->outbuf == Z_NULL) return -1L;
+ }
+ if (offset && s->back != EOF) {
+ s->back = EOF;
+ s->out++;
+ offset--;
+ if (s->last) s->z_err = Z_STREAM_END;
+ }
+ while (offset > 0) {
+ int size = Z_BUFSIZE;
+ if (offset < Z_BUFSIZE) size = (int)offset;
+
+ size = gzread(file, s->outbuf, (uInt)size);
+ if (size <= 0) return -1L;
+ offset -= size;
+ }
+ return s->out;
+}
+
+/* ===========================================================================
+ Rewinds input file.
+*/
+int ZEXPORT gzrewind (file)
+ gzFile file;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'r') return -1;
+
+ s->z_err = Z_OK;
+ s->z_eof = 0;
+ s->back = EOF;
+ s->stream.avail_in = 0;
+ s->stream.next_in = s->inbuf;
+ s->crc = crc32(0L, Z_NULL, 0);
+ if (!s->transparent) (void)inflateReset(&s->stream);
+ s->in = 0;
+ s->out = 0;
+ return fseek(s->file, s->start, SEEK_SET);
+}
+
+/* ===========================================================================
+ Returns the starting position for the next gzread or gzwrite on the
+ given compressed file. This position represents a number of bytes in the
+ uncompressed data stream.
+*/
+z_off_t ZEXPORT gztell (file)
+ gzFile file;
+{
+ return gzseek(file, 0L, SEEK_CUR);
+}
+
+/* ===========================================================================
+ Returns 1 when EOF has previously been detected reading the given
+ input stream, otherwise zero.
+*/
+int ZEXPORT gzeof (file)
+ gzFile file;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ /* With concatenated compressed files that can have embedded
+ * crc trailers, z_eof is no longer the only/best indicator of EOF
+ * on a gz_stream. Handle end-of-stream error explicitly here.
+ */
+ if (s == NULL || s->mode != 'r') return 0;
+ if (s->z_eof) return 1;
+ return s->z_err == Z_STREAM_END;
+}
+
+/* ===========================================================================
+ Outputs a long in LSB order to the given file
+*/
+local void putLong (file, x)
+ FILE *file;
+ uLong x;
+{
+ int n;
+ for (n = 0; n < 4; n++) {
+ fputc((int)(x & 0xff), file);
+ x >>= 8;
+ }
+}
+
+/* ===========================================================================
+ Reads a long in LSB order from the given gz_stream. Sets z_err in case
+ of error.
+*/
+local uLong getLong (s)
+ gz_stream *s;
+{
+ uLong x = (uLong)get_byte(s);
+ int c;
+
+ x += ((uLong)get_byte(s))<<8;
+ x += ((uLong)get_byte(s))<<16;
+ c = get_byte(s);
+ if (c == EOF) s->z_err = Z_DATA_ERROR;
+ x += ((uLong)c)<<24;
+ return x;
+}
+
+/* ===========================================================================
+ Flushes all pending output if necessary, closes the compressed file
+ and deallocates all the (de)compression state.
+*/
+int ZEXPORT gzclose (file)
+ gzFile file;
+{
+ int err;
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL) return Z_STREAM_ERROR;
+
+ if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+ return Z_STREAM_ERROR;
+#else
+ err = do_flush (file, Z_FINISH);
+ if (err != Z_OK) return destroy((gz_stream*)file);
+
+ putLong (s->file, s->crc);
+ putLong (s->file, (uLong)(s->in & 0xffffffff));
+#endif
+ }
+ return destroy((gz_stream*)file);
+}
+
+/* ===========================================================================
+ Returns the error message for the last error which occured on the
+ given compressed file. errnum is set to zlib error number. If an
+ error occured in the file system and not in the compression library,
+ errnum is set to Z_ERRNO and the application may consult errno
+ to get the exact error code.
+*/
+const char * ZEXPORT gzerror (file, errnum)
+ gzFile file;
+ int *errnum;
+{
+ char *m;
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL) {
+ *errnum = Z_STREAM_ERROR;
+ return (const char*)ERR_MSG(Z_STREAM_ERROR);
+ }
+ *errnum = s->z_err;
+ if (*errnum == Z_OK) return (const char*)"";
+
+ m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg);
+
+ if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err);
+
+ TRYFREE(s->msg);
+ s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3);
+ if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR);
+ strcpy(s->msg, s->path);
+ strcat(s->msg, ": ");
+ strcat(s->msg, m);
+ return (const char*)s->msg;
+}
+
+/* ===========================================================================
+ Clear the error and end-of-file flags, and do the same for the real file.
+*/
+void ZEXPORT gzclearerr (file)
+ gzFile file;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL) return;
+ if (s->z_err != Z_STREAM_END) s->z_err = Z_OK;
+ s->z_eof = 0;
+ clearerr(s->file);
+}
diff --git a/zlib/infback.c b/zlib/infback.c
new file mode 100644
index 0000000..d6f110f
--- /dev/null
+++ b/zlib/infback.c
@@ -0,0 +1,622 @@
+/* infback.c -- inflate using a call-back interface
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ This code is largely copied from inflate.c. Normally either infback.o or
+ inflate.o would be linked into an application--not both. The interface
+ with inffast.c is retained so that optimized assembler-coded versions of
+ inflate_fast() can be used with either inflate.c or infback.c.
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+/* function prototypes */
+local void fixedtables OF((struct inflate_state FAR *state));
+
+/*
+ strm provides memory allocation functions in zalloc and zfree, or
+ Z_NULL to use the library memory allocation functions.
+
+ windowBits is in the range 8..15, and window is a user-supplied
+ window and output buffer that is 2**windowBits bytes.
+ */
+int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size)
+z_stream FAR *strm;
+int windowBits;
+unsigned char FAR *window;
+const char *version;
+int stream_size;
+{
+ struct inflate_state FAR *state;
+
+ if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+ stream_size != (int)(sizeof(z_stream)))
+ return Z_VERSION_ERROR;
+ if (strm == Z_NULL || window == Z_NULL ||
+ windowBits < 8 || windowBits > 15)
+ return Z_STREAM_ERROR;
+ strm->msg = Z_NULL; /* in case we return an error */
+ if (strm->zalloc == (alloc_func)0) {
+ strm->zalloc = zcalloc;
+ strm->opaque = (voidpf)0;
+ }
+ if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+ state = (struct inflate_state FAR *)ZALLOC(strm, 1,
+ sizeof(struct inflate_state));
+ if (state == Z_NULL) return Z_MEM_ERROR;
+ Tracev((stderr, "inflate: allocated\n"));
+ strm->state = (voidpf)state;
+ state->wbits = windowBits;
+ state->wsize = 1U << windowBits;
+ state->window = window;
+ state->write = 0;
+ state->whave = 0;
+ return Z_OK;
+}
+
+/*
+ Return state with length and distance decoding tables and index sizes set to
+ fixed code decoding. Normally this returns fixed tables from inffixed.h.
+ If BUILDFIXED is defined, then instead this routine builds the tables the
+ first time it's called, and returns those tables the first time and
+ thereafter. This reduces the size of the code by about 2K bytes, in
+ exchange for a little execution time. However, BUILDFIXED should not be
+ used for threaded applications, since the rewriting of the tables and virgin
+ may not be thread-safe.
+ */
+local void fixedtables(state)
+struct inflate_state FAR *state;
+{
+#ifdef BUILDFIXED
+ static int virgin = 1;
+ static code *lenfix, *distfix;
+ static code fixed[544];
+
+ /* build fixed huffman tables if first call (may not be thread safe) */
+ if (virgin) {
+ unsigned sym, bits;
+ static code *next;
+
+ /* literal/length table */
+ sym = 0;
+ while (sym < 144) state->lens[sym++] = 8;
+ while (sym < 256) state->lens[sym++] = 9;
+ while (sym < 280) state->lens[sym++] = 7;
+ while (sym < 288) state->lens[sym++] = 8;
+ next = fixed;
+ lenfix = next;
+ bits = 9;
+ inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
+
+ /* distance table */
+ sym = 0;
+ while (sym < 32) state->lens[sym++] = 5;
+ distfix = next;
+ bits = 5;
+ inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
+
+ /* do this just once */
+ virgin = 0;
+ }
+#else /* !BUILDFIXED */
+# include "inffixed.h"
+#endif /* BUILDFIXED */
+ state->lencode = lenfix;
+ state->lenbits = 9;
+ state->distcode = distfix;
+ state->distbits = 5;
+}
+
+/* Macros for inflateBack(): */
+
+/* Load returned state from inflate_fast() */
+#define LOAD() \
+ do { \
+ put = strm->next_out; \
+ left = strm->avail_out; \
+ next = strm->next_in; \
+ have = strm->avail_in; \
+ hold = state->hold; \
+ bits = state->bits; \
+ } while (0)
+
+/* Set state from registers for inflate_fast() */
+#define RESTORE() \
+ do { \
+ strm->next_out = put; \
+ strm->avail_out = left; \
+ strm->next_in = next; \
+ strm->avail_in = have; \
+ state->hold = hold; \
+ state->bits = bits; \
+ } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+ do { \
+ hold = 0; \
+ bits = 0; \
+ } while (0)
+
+/* Assure that some input is available. If input is requested, but denied,
+ then return a Z_BUF_ERROR from inflateBack(). */
+#define PULL() \
+ do { \
+ if (have == 0) { \
+ have = in(in_desc, &next); \
+ if (have == 0) { \
+ next = Z_NULL; \
+ ret = Z_BUF_ERROR; \
+ goto inf_leave; \
+ } \
+ } \
+ } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflateBack()
+ with an error if there is no input available. */
+#define PULLBYTE() \
+ do { \
+ PULL(); \
+ have--; \
+ hold += (unsigned long)(*next++) << bits; \
+ bits += 8; \
+ } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator. If there is
+ not enough available input to do that, then return from inflateBack() with
+ an error. */
+#define NEEDBITS(n) \
+ do { \
+ while (bits < (unsigned)(n)) \
+ PULLBYTE(); \
+ } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+ ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+ do { \
+ hold >>= (n); \
+ bits -= (unsigned)(n); \
+ } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+ do { \
+ hold >>= bits & 7; \
+ bits -= bits & 7; \
+ } while (0)
+
+/* Assure that some output space is available, by writing out the window
+ if it's full. If the write fails, return from inflateBack() with a
+ Z_BUF_ERROR. */
+#define ROOM() \
+ do { \
+ if (left == 0) { \
+ put = state->window; \
+ left = state->wsize; \
+ state->whave = left; \
+ if (out(out_desc, put, left)) { \
+ ret = Z_BUF_ERROR; \
+ goto inf_leave; \
+ } \
+ } \
+ } while (0)
+
+/*
+ strm provides the memory allocation functions and window buffer on input,
+ and provides information on the unused input on return. For Z_DATA_ERROR
+ returns, strm will also provide an error message.
+
+ in() and out() are the call-back input and output functions. When
+ inflateBack() needs more input, it calls in(). When inflateBack() has
+ filled the window with output, or when it completes with data in the
+ window, it calls out() to write out the data. The application must not
+ change the provided input until in() is called again or inflateBack()
+ returns. The application must not change the window/output buffer until
+ inflateBack() returns.
+
+ in() and out() are called with a descriptor parameter provided in the
+ inflateBack() call. This parameter can be a structure that provides the
+ information required to do the read or write, as well as accumulated
+ information on the input and output such as totals and check values.
+
+ in() should return zero on failure. out() should return non-zero on
+ failure. If either in() or out() fails, than inflateBack() returns a
+ Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it
+ was in() or out() that caused in the error. Otherwise, inflateBack()
+ returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format
+ error, or Z_MEM_ERROR if it could not allocate memory for the state.
+ inflateBack() can also return Z_STREAM_ERROR if the input parameters
+ are not correct, i.e. strm is Z_NULL or the state was not initialized.
+ */
+int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc)
+z_stream FAR *strm;
+in_func in;
+void FAR *in_desc;
+out_func out;
+void FAR *out_desc;
+{
+ struct inflate_state FAR *state;
+ unsigned char FAR *next; /* next input */
+ unsigned char FAR *put; /* next output */
+ unsigned have, left; /* available input and output */
+ unsigned long hold; /* bit buffer */
+ unsigned bits; /* bits in bit buffer */
+ unsigned copy; /* number of stored or match bytes to copy */
+ unsigned char FAR *from; /* where to copy match bytes from */
+ code this; /* current decoding table entry */
+ code last; /* parent table entry */
+ unsigned len; /* length to copy for repeats, bits to drop */
+ int ret; /* return code */
+ static const unsigned short order[19] = /* permutation of code lengths */
+ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+ /* Check that the strm exists and that the state was initialized */
+ if (strm == Z_NULL || strm->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+
+ /* Reset the state */
+ strm->msg = Z_NULL;
+ state->mode = TYPE;
+ state->last = 0;
+ state->whave = 0;
+ next = strm->next_in;
+ have = next != Z_NULL ? strm->avail_in : 0;
+ hold = 0;
+ bits = 0;
+ put = state->window;
+ left = state->wsize;
+
+ /* Inflate until end of block marked as last */
+ for (;;)
+ switch (state->mode) {
+ case TYPE:
+ /* determine and dispatch block type */
+ if (state->last) {
+ BYTEBITS();
+ state->mode = DONE;
+ break;
+ }
+ NEEDBITS(3);
+ state->last = BITS(1);
+ DROPBITS(1);
+ switch (BITS(2)) {
+ case 0: /* stored block */
+ Tracev((stderr, "inflate: stored block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = STORED;
+ break;
+ case 1: /* fixed block */
+ fixedtables(state);
+ Tracev((stderr, "inflate: fixed codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = LEN; /* decode codes */
+ break;
+ case 2: /* dynamic block */
+ Tracev((stderr, "inflate: dynamic codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = TABLE;
+ break;
+ case 3:
+ strm->msg = (char *)"invalid block type";
+ state->mode = BAD;
+ }
+ DROPBITS(2);
+ break;
+
+ case STORED:
+ /* get and verify stored block length */
+ BYTEBITS(); /* go to byte boundary */
+ NEEDBITS(32);
+ if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+ strm->msg = (char *)"invalid stored block lengths";
+ state->mode = BAD;
+ break;
+ }
+ state->length = (unsigned)hold & 0xffff;
+ Tracev((stderr, "inflate: stored length %u\n",
+ state->length));
+ INITBITS();
+
+ /* copy stored block from input to output */
+ while (state->length != 0) {
+ copy = state->length;
+ PULL();
+ ROOM();
+ if (copy > have) copy = have;
+ if (copy > left) copy = left;
+ zmemcpy(put, next, copy);
+ have -= copy;
+ next += copy;
+ left -= copy;
+ put += copy;
+ state->length -= copy;
+ }
+ Tracev((stderr, "inflate: stored end\n"));
+ state->mode = TYPE;
+ break;
+
+ case TABLE:
+ /* get dynamic table entries descriptor */
+ NEEDBITS(14);
+ state->nlen = BITS(5) + 257;
+ DROPBITS(5);
+ state->ndist = BITS(5) + 1;
+ DROPBITS(5);
+ state->ncode = BITS(4) + 4;
+ DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+ if (state->nlen > 286 || state->ndist > 30) {
+ strm->msg = (char *)"too many length or distance symbols";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ Tracev((stderr, "inflate: table sizes ok\n"));
+
+ /* get code length code lengths (not a typo) */
+ state->have = 0;
+ while (state->have < state->ncode) {
+ NEEDBITS(3);
+ state->lens[order[state->have++]] = (unsigned short)BITS(3);
+ DROPBITS(3);
+ }
+ while (state->have < 19)
+ state->lens[order[state->have++]] = 0;
+ state->next = state->codes;
+ state->lencode = (code const FAR *)(state->next);
+ state->lenbits = 7;
+ ret = inflate_table(CODES, state->lens, 19, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid code lengths set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: code lengths ok\n"));
+
+ /* get length and distance code code lengths */
+ state->have = 0;
+ while (state->have < state->nlen + state->ndist) {
+ for (;;) {
+ this = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (this.val < 16) {
+ NEEDBITS(this.bits);
+ DROPBITS(this.bits);
+ state->lens[state->have++] = this.val;
+ }
+ else {
+ if (this.val == 16) {
+ NEEDBITS(this.bits + 2);
+ DROPBITS(this.bits);
+ if (state->have == 0) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ len = (unsigned)(state->lens[state->have - 1]);
+ copy = 3 + BITS(2);
+ DROPBITS(2);
+ }
+ else if (this.val == 17) {
+ NEEDBITS(this.bits + 3);
+ DROPBITS(this.bits);
+ len = 0;
+ copy = 3 + BITS(3);
+ DROPBITS(3);
+ }
+ else {
+ NEEDBITS(this.bits + 7);
+ DROPBITS(this.bits);
+ len = 0;
+ copy = 11 + BITS(7);
+ DROPBITS(7);
+ }
+ if (state->have + copy > state->nlen + state->ndist) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ while (copy--)
+ state->lens[state->have++] = (unsigned short)len;
+ }
+ }
+
+ /* handle error breaks in while */
+ if (state->mode == BAD) break;
+
+ /* build code tables */
+ state->next = state->codes;
+ state->lencode = (code const FAR *)(state->next);
+ state->lenbits = 9;
+ ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid literal/lengths set";
+ state->mode = BAD;
+ break;
+ }
+ state->distcode = (code const FAR *)(state->next);
+ state->distbits = 6;
+ ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+ &(state->next), &(state->distbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid distances set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: codes ok\n"));
+ state->mode = LEN;
+
+ case LEN:
+ /* use inflate_fast() if we have enough input and output */
+ if (have >= 6 && left >= 258) {
+ RESTORE();
+ if (state->whave < state->wsize)
+ state->whave = state->wsize - left;
+ inflate_fast(strm, state->wsize);
+ LOAD();
+ break;
+ }
+
+ /* get a literal, length, or end-of-block code */
+ for (;;) {
+ this = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (this.op && (this.op & 0xf0) == 0) {
+ last = this;
+ for (;;) {
+ this = state->lencode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ }
+ DROPBITS(this.bits);
+ state->length = (unsigned)this.val;
+
+ /* process literal */
+ if (this.op == 0) {
+ Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", this.val));
+ ROOM();
+ *put++ = (unsigned char)(state->length);
+ left--;
+ state->mode = LEN;
+ break;
+ }
+
+ /* process end of block */
+ if (this.op & 32) {
+ Tracevv((stderr, "inflate: end of block\n"));
+ state->mode = TYPE;
+ break;
+ }
+
+ /* invalid code */
+ if (this.op & 64) {
+ strm->msg = (char *)"invalid literal/length code";
+ state->mode = BAD;
+ break;
+ }
+
+ /* length code -- get extra bits, if any */
+ state->extra = (unsigned)(this.op) & 15;
+ if (state->extra != 0) {
+ NEEDBITS(state->extra);
+ state->length += BITS(state->extra);
+ DROPBITS(state->extra);
+ }
+ Tracevv((stderr, "inflate: length %u\n", state->length));
+
+ /* get distance code */
+ for (;;) {
+ this = state->distcode[BITS(state->distbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if ((this.op & 0xf0) == 0) {
+ last = this;
+ for (;;) {
+ this = state->distcode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ }
+ DROPBITS(this.bits);
+ if (this.op & 64) {
+ strm->msg = (char *)"invalid distance code";
+ state->mode = BAD;
+ break;
+ }
+ state->offset = (unsigned)this.val;
+
+ /* get distance extra bits, if any */
+ state->extra = (unsigned)(this.op) & 15;
+ if (state->extra != 0) {
+ NEEDBITS(state->extra);
+ state->offset += BITS(state->extra);
+ DROPBITS(state->extra);
+ }
+ if (state->offset > state->wsize - (state->whave < state->wsize ?
+ left : 0)) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+ Tracevv((stderr, "inflate: distance %u\n", state->offset));
+
+ /* copy match from window to output */
+ do {
+ ROOM();
+ copy = state->wsize - state->offset;
+ if (copy < left) {
+ from = put + copy;
+ copy = left - copy;
+ }
+ else {
+ from = put - state->offset;
+ copy = left;
+ }
+ if (copy > state->length) copy = state->length;
+ state->length -= copy;
+ left -= copy;
+ do {
+ *put++ = *from++;
+ } while (--copy);
+ } while (state->length != 0);
+ break;
+
+ case DONE:
+ /* inflate stream terminated properly -- write leftover output */
+ ret = Z_STREAM_END;
+ if (left < state->wsize) {
+ if (out(out_desc, state->window, state->wsize - left))
+ ret = Z_BUF_ERROR;
+ }
+ goto inf_leave;
+
+ case BAD:
+ ret = Z_DATA_ERROR;
+ goto inf_leave;
+
+ default: /* can't happen, but makes compilers happy */
+ ret = Z_STREAM_ERROR;
+ goto inf_leave;
+ }
+
+ /* Return unused input */
+ inf_leave:
+ strm->next_in = next;
+ strm->avail_in = have;
+ return ret;
+}
+
+int ZEXPORT inflateBackEnd(strm)
+z_stream FAR *strm;
+{
+ if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
+ return Z_STREAM_ERROR;
+ ZFREE(strm, strm->state);
+ strm->state = Z_NULL;
+ Tracev((stderr, "inflate: end\n"));
+ return Z_OK;
+}
diff --git a/zlib/inffast.c b/zlib/inffast.c
new file mode 100644
index 0000000..af9f38f
--- /dev/null
+++ b/zlib/inffast.c
@@ -0,0 +1,305 @@
+/* inffast.c -- fast decoding
+ * Copyright (C) 1995-2004 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+#ifndef ASMINF
+
+/* Allow machine dependent optimization for post-increment or pre-increment.
+ Based on testing to date,
+ Pre-increment preferred for:
+ - PowerPC G3 (Adler)
+ - MIPS R5000 (Randers-Pehrson)
+ Post-increment preferred for:
+ - none
+ No measurable difference:
+ - Pentium III (Anderson)
+ - M68060 (Nikl)
+ */
+#ifdef POSTINC
+# define OFF 0
+# define PUP(a) *(a)++
+#else
+# define OFF 1
+# define PUP(a) *++(a)
+#endif
+
+/*
+ Decode literal, length, and distance codes and write out the resulting
+ literal and match bytes until either not enough input or output is
+ available, an end-of-block is encountered, or a data error is encountered.
+ When large enough input and output buffers are supplied to inflate(), for
+ example, a 16K input buffer and a 64K output buffer, more than 95% of the
+ inflate execution time is spent in this routine.
+
+ Entry assumptions:
+
+ state->mode == LEN
+ strm->avail_in >= 6
+ strm->avail_out >= 258
+ start >= strm->avail_out
+ state->bits < 8
+
+ On return, state->mode is one of:
+
+ LEN -- ran out of enough output space or enough available input
+ TYPE -- reached end of block code, inflate() to interpret next block
+ BAD -- error in block data
+
+ Notes:
+
+ - The maximum input bits used by a length/distance pair is 15 bits for the
+ length code, 5 bits for the length extra, 15 bits for the distance code,
+ and 13 bits for the distance extra. This totals 48 bits, or six bytes.
+ Therefore if strm->avail_in >= 6, then there is enough input to avoid
+ checking for available input while decoding.
+
+ - The maximum bytes that a single length/distance pair can output is 258
+ bytes, which is the maximum length that can be coded. inflate_fast()
+ requires strm->avail_out >= 258 for each loop to avoid checking for
+ output space.
+ */
+void inflate_fast(strm, start)
+z_streamp strm;
+unsigned start; /* inflate()'s starting value for strm->avail_out */
+{
+ struct inflate_state FAR *state;
+ unsigned char FAR *in; /* local strm->next_in */
+ unsigned char FAR *last; /* while in < last, enough input available */
+ unsigned char FAR *out; /* local strm->next_out */
+ unsigned char FAR *beg; /* inflate()'s initial strm->next_out */
+ unsigned char FAR *end; /* while out < end, enough space available */
+ unsigned wsize; /* window size or zero if not using window */
+ unsigned whave; /* valid bytes in the window */
+ unsigned write; /* window write index */
+ unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */
+ unsigned long hold; /* local strm->hold */
+ unsigned bits; /* local strm->bits */
+ code const FAR *lcode; /* local strm->lencode */
+ code const FAR *dcode; /* local strm->distcode */
+ unsigned lmask; /* mask for first level of length codes */
+ unsigned dmask; /* mask for first level of distance codes */
+ code this; /* retrieved table entry */
+ unsigned op; /* code bits, operation, extra bits, or */
+ /* window position, window bytes to copy */
+ unsigned len; /* match length, unused bytes */
+ unsigned dist; /* match distance */
+ unsigned char FAR *from; /* where to copy match from */
+
+ /* copy state to local variables */
+ state = (struct inflate_state FAR *)strm->state;
+ in = strm->next_in - OFF;
+ last = in + (strm->avail_in - 5);
+ out = strm->next_out - OFF;
+ beg = out - (start - strm->avail_out);
+ end = out + (strm->avail_out - 257);
+ wsize = state->wsize;
+ whave = state->whave;
+ write = state->write;
+ window = state->window;
+ hold = state->hold;
+ bits = state->bits;
+ lcode = state->lencode;
+ dcode = state->distcode;
+ lmask = (1U << state->lenbits) - 1;
+ dmask = (1U << state->distbits) - 1;
+
+ /* decode literals and length/distances until end-of-block or not enough
+ input data or output space */
+ do {
+ if (bits < 15) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ this = lcode[hold & lmask];
+ dolen:
+ op = (unsigned)(this.bits);
+ hold >>= op;
+ bits -= op;
+ op = (unsigned)(this.op);
+ if (op == 0) { /* literal */
+ Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", this.val));
+ PUP(out) = (unsigned char)(this.val);
+ }
+ else if (op & 16) { /* length base */
+ len = (unsigned)(this.val);
+ op &= 15; /* number of extra bits */
+ if (op) {
+ if (bits < op) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ len += (unsigned)hold & ((1U << op) - 1);
+ hold >>= op;
+ bits -= op;
+ }
+ Tracevv((stderr, "inflate: length %u\n", len));
+ if (bits < 15) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ this = dcode[hold & dmask];
+ dodist:
+ op = (unsigned)(this.bits);
+ hold >>= op;
+ bits -= op;
+ op = (unsigned)(this.op);
+ if (op & 16) { /* distance base */
+ dist = (unsigned)(this.val);
+ op &= 15; /* number of extra bits */
+ if (bits < op) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ if (bits < op) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ }
+ dist += (unsigned)hold & ((1U << op) - 1);
+ hold >>= op;
+ bits -= op;
+ Tracevv((stderr, "inflate: distance %u\n", dist));
+ op = (unsigned)(out - beg); /* max distance in output */
+ if (dist > op) { /* see if copy from window */
+ op = dist - op; /* distance back in window */
+ if (op > whave) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+ from = window - OFF;
+ if (write == 0) { /* very common case */
+ from += wsize - op;
+ if (op < len) { /* some from window */
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ else if (write < op) { /* wrap around window */
+ from += wsize + write - op;
+ op -= write;
+ if (op < len) { /* some from end of window */
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = window - OFF;
+ if (write < len) { /* some from start of window */
+ op = write;
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ }
+ else { /* contiguous in window */
+ from += write - op;
+ if (op < len) { /* some from window */
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ while (len > 2) {
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ len -= 3;
+ }
+ if (len) {
+ PUP(out) = PUP(from);
+ if (len > 1)
+ PUP(out) = PUP(from);
+ }
+ }
+ else {
+ from = out - dist; /* copy direct from output */
+ do { /* minimum length is three */
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ len -= 3;
+ } while (len > 2);
+ if (len) {
+ PUP(out) = PUP(from);
+ if (len > 1)
+ PUP(out) = PUP(from);
+ }
+ }
+ }
+ else if ((op & 64) == 0) { /* 2nd level distance code */
+ this = dcode[this.val + (hold & ((1U << op) - 1))];
+ goto dodist;
+ }
+ else {
+ strm->msg = (char *)"invalid distance code";
+ state->mode = BAD;
+ break;
+ }
+ }
+ else if ((op & 64) == 0) { /* 2nd level length code */
+ this = lcode[this.val + (hold & ((1U << op) - 1))];
+ goto dolen;
+ }
+ else if (op & 32) { /* end-of-block */
+ Tracevv((stderr, "inflate: end of block\n"));
+ state->mode = TYPE;
+ break;
+ }
+ else {
+ strm->msg = (char *)"invalid literal/length code";
+ state->mode = BAD;
+ break;
+ }
+ } while (in < last && out < end);
+
+ /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
+ len = bits >> 3;
+ in -= len;
+ bits -= len << 3;
+ hold &= (1U << bits) - 1;
+
+ /* update state and return */
+ strm->next_in = in + OFF;
+ strm->next_out = out + OFF;
+ strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last));
+ strm->avail_out = (unsigned)(out < end ?
+ 257 + (end - out) : 257 - (out - end));
+ state->hold = hold;
+ state->bits = bits;
+ return;
+}
+
+/*
+ inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):
+ - Using bit fields for code structure
+ - Different op definition to avoid & for extra bits (do & for table bits)
+ - Three separate decoding do-loops for direct, window, and write == 0
+ - Special case for distance > 1 copies to do overlapped load and store copy
+ - Explicit branch predictions (based on measured branch probabilities)
+ - Deferring match copy and interspersed it with decoding subsequent codes
+ - Swapping literal/length else
+ - Swapping window/direct else
+ - Larger unrolled copy loops (three is about right)
+ - Moving len -= 3 statement into middle of loop
+ */
+
+#endif /* !ASMINF */
diff --git a/zlib/inffast.h b/zlib/inffast.h
new file mode 100644
index 0000000..614fa78
--- /dev/null
+++ b/zlib/inffast.h
@@ -0,0 +1,11 @@
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+void inflate_fast OF((z_streamp strm, unsigned start));
diff --git a/zlib/inffixed.h b/zlib/inffixed.h
new file mode 100644
index 0000000..423d5c5
--- /dev/null
+++ b/zlib/inffixed.h
@@ -0,0 +1,94 @@
+ /* inffixed.h -- table for decoding fixed codes
+ * Generated automatically by makefixed().
+ */
+
+ /* WARNING: this file should *not* be used by applications. It
+ is part of the implementation of the compression library and
+ is subject to change. Applications should only use zlib.h.
+ */
+
+ static const code lenfix[512] = {
+ {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48},
+ {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128},
+ {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59},
+ {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176},
+ {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20},
+ {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100},
+ {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8},
+ {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216},
+ {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76},
+ {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114},
+ {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},
+ {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148},
+ {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42},
+ {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86},
+ {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15},
+ {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236},
+ {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62},
+ {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},
+ {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31},
+ {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162},
+ {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25},
+ {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105},
+ {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4},
+ {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202},
+ {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69},
+ {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125},
+ {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13},
+ {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195},
+ {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35},
+ {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91},
+ {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19},
+ {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246},
+ {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55},
+ {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135},
+ {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99},
+ {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190},
+ {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16},
+ {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96},
+ {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6},
+ {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209},
+ {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},
+ {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116},
+ {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4},
+ {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153},
+ {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44},
+ {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82},
+ {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11},
+ {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},
+ {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58},
+ {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138},
+ {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51},
+ {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173},
+ {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30},
+ {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110},
+ {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0},
+ {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195},
+ {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65},
+ {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121},
+ {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},
+ {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258},
+ {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37},
+ {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93},
+ {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23},
+ {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251},
+ {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51},
+ {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},
+ {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67},
+ {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183},
+ {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23},
+ {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103},
+ {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9},
+ {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223},
+ {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79},
+ {0,9,255}
+ };
+
+ static const code distfix[32] = {
+ {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025},
+ {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193},
+ {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385},
+ {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577},
+ {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073},
+ {22,5,193},{64,5,0}
+ };
diff --git a/zlib/inflate.c b/zlib/inflate.c
new file mode 100644
index 0000000..81d93b0
--- /dev/null
+++ b/zlib/inflate.c
@@ -0,0 +1,1274 @@
+/* inflate.c -- zlib decompression
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * Change history:
+ *
+ * 1.2.beta0 24 Nov 2002
+ * - First version -- complete rewrite of inflate to simplify code, avoid
+ * creation of window when not needed, minimize use of window when it is
+ * needed, make inffast.c even faster, implement gzip decoding, and to
+ * improve code readability and style over the previous zlib inflate code
+ *
+ * 1.2.beta1 25 Nov 2002
+ * - Use pointers for available input and output checking in inffast.c
+ * - Remove input and output counters in inffast.c
+ * - Change inffast.c entry and loop from avail_in >= 7 to >= 6
+ * - Remove unnecessary second byte pull from length extra in inffast.c
+ * - Unroll direct copy to three copies per loop in inffast.c
+ *
+ * 1.2.beta2 4 Dec 2002
+ * - Change external routine names to reduce potential conflicts
+ * - Correct filename to inffixed.h for fixed tables in inflate.c
+ * - Make hbuf[] unsigned char to match parameter type in inflate.c
+ * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset)
+ * to avoid negation problem on Alphas (64 bit) in inflate.c
+ *
+ * 1.2.beta3 22 Dec 2002
+ * - Add comments on state->bits assertion in inffast.c
+ * - Add comments on op field in inftrees.h
+ * - Fix bug in reuse of allocated window after inflateReset()
+ * - Remove bit fields--back to byte structure for speed
+ * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths
+ * - Change post-increments to pre-increments in inflate_fast(), PPC biased?
+ * - Add compile time option, POSTINC, to use post-increments instead (Intel?)
+ * - Make MATCH copy in inflate() much faster for when inflate_fast() not used
+ * - Use local copies of stream next and avail values, as well as local bit
+ * buffer and bit count in inflate()--for speed when inflate_fast() not used
+ *
+ * 1.2.beta4 1 Jan 2003
+ * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings
+ * - Move a comment on output buffer sizes from inffast.c to inflate.c
+ * - Add comments in inffast.c to introduce the inflate_fast() routine
+ * - Rearrange window copies in inflate_fast() for speed and simplification
+ * - Unroll last copy for window match in inflate_fast()
+ * - Use local copies of window variables in inflate_fast() for speed
+ * - Pull out common write == 0 case for speed in inflate_fast()
+ * - Make op and len in inflate_fast() unsigned for consistency
+ * - Add FAR to lcode and dcode declarations in inflate_fast()
+ * - Simplified bad distance check in inflate_fast()
+ * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new
+ * source file infback.c to provide a call-back interface to inflate for
+ * programs like gzip and unzip -- uses window as output buffer to avoid
+ * window copying
+ *
+ * 1.2.beta5 1 Jan 2003
+ * - Improved inflateBack() interface to allow the caller to provide initial
+ * input in strm.
+ * - Fixed stored blocks bug in inflateBack()
+ *
+ * 1.2.beta6 4 Jan 2003
+ * - Added comments in inffast.c on effectiveness of POSTINC
+ * - Typecasting all around to reduce compiler warnings
+ * - Changed loops from while (1) or do {} while (1) to for (;;), again to
+ * make compilers happy
+ * - Changed type of window in inflateBackInit() to unsigned char *
+ *
+ * 1.2.beta7 27 Jan 2003
+ * - Changed many types to unsigned or unsigned short to avoid warnings
+ * - Added inflateCopy() function
+ *
+ * 1.2.0 9 Mar 2003
+ * - Changed inflateBack() interface to provide separate opaque descriptors
+ * for the in() and out() functions
+ * - Changed inflateBack() argument and in_func typedef to swap the length
+ * and buffer address return values for the input function
+ * - Check next_in and next_out for Z_NULL on entry to inflate()
+ *
+ * The history for versions after 1.2.0 are in ChangeLog in zlib distribution.
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+#ifdef MAKEFIXED
+# ifndef BUILDFIXED
+# define BUILDFIXED
+# endif
+#endif
+
+/* function prototypes */
+local void fixedtables OF((struct inflate_state FAR *state));
+local int updatewindow OF((z_streamp strm, unsigned out));
+#ifdef BUILDFIXED
+ void makefixed OF((void));
+#endif
+local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf,
+ unsigned len));
+
+int ZEXPORT inflateReset(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ strm->total_in = strm->total_out = state->total = 0;
+ strm->msg = Z_NULL;
+ strm->adler = 1; /* to support ill-conceived Java test suite */
+ state->mode = HEAD;
+ state->last = 0;
+ state->havedict = 0;
+ state->wsize = 0;
+ state->whave = 0;
+ state->hold = 0;
+ state->bits = 0;
+ state->lencode = state->distcode = state->next = state->codes;
+ Tracev((stderr, "inflate: reset\n"));
+ return Z_OK;
+}
+
+int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)
+z_streamp strm;
+int windowBits;
+const char *version;
+int stream_size;
+{
+ struct inflate_state FAR *state;
+
+ if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+ stream_size != (int)(sizeof(z_stream)))
+ return Z_VERSION_ERROR;
+ if (strm == Z_NULL) return Z_STREAM_ERROR;
+ strm->msg = Z_NULL; /* in case we return an error */
+ if (strm->zalloc == (alloc_func)0) {
+ strm->zalloc = zcalloc;
+ strm->opaque = (voidpf)0;
+ }
+ if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+ state = (struct inflate_state FAR *)
+ ZALLOC(strm, 1, sizeof(struct inflate_state));
+ if (state == Z_NULL) return Z_MEM_ERROR;
+ Tracev((stderr, "inflate: allocated\n"));
+ strm->state = (voidpf)state;
+ if (windowBits < 0) {
+ state->wrap = 0;
+ windowBits = -windowBits;
+ }
+ else {
+ state->wrap = (windowBits >> 4) + 1;
+#ifdef GUNZIP
+ if (windowBits < 48) windowBits &= 15;
+#endif
+ }
+ if (windowBits < 8 || windowBits > 15) {
+ ZFREE(strm, state);
+ strm->state = Z_NULL;
+ return Z_STREAM_ERROR;
+ }
+ state->wbits = (unsigned)windowBits;
+ state->window = Z_NULL;
+ return inflateReset(strm);
+}
+
+int ZEXPORT inflateInit_(strm, version, stream_size)
+z_streamp strm;
+const char *version;
+int stream_size;
+{
+ return inflateInit2_(strm, DEF_WBITS, version, stream_size);
+}
+
+/*
+ Return state with length and distance decoding tables and index sizes set to
+ fixed code decoding. Normally this returns fixed tables from inffixed.h.
+ If BUILDFIXED is defined, then instead this routine builds the tables the
+ first time it's called, and returns those tables the first time and
+ thereafter. This reduces the size of the code by about 2K bytes, in
+ exchange for a little execution time. However, BUILDFIXED should not be
+ used for threaded applications, since the rewriting of the tables and virgin
+ may not be thread-safe.
+ */
+local void fixedtables(state)
+struct inflate_state FAR *state;
+{
+#ifdef BUILDFIXED
+ static int virgin = 1;
+ static code *lenfix, *distfix;
+ static code fixed[544];
+
+ /* build fixed huffman tables if first call (may not be thread safe) */
+ if (virgin) {
+ unsigned sym, bits;
+ static code *next;
+
+ /* literal/length table */
+ sym = 0;
+ while (sym < 144) state->lens[sym++] = 8;
+ while (sym < 256) state->lens[sym++] = 9;
+ while (sym < 280) state->lens[sym++] = 7;
+ while (sym < 288) state->lens[sym++] = 8;
+ next = fixed;
+ lenfix = next;
+ bits = 9;
+ inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
+
+ /* distance table */
+ sym = 0;
+ while (sym < 32) state->lens[sym++] = 5;
+ distfix = next;
+ bits = 5;
+ inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
+
+ /* do this just once */
+ virgin = 0;
+ }
+#else /* !BUILDFIXED */
+# include "inffixed.h"
+#endif /* BUILDFIXED */
+ state->lencode = lenfix;
+ state->lenbits = 9;
+ state->distcode = distfix;
+ state->distbits = 5;
+}
+
+#ifdef MAKEFIXED
+#include <stdio.h>
+
+/*
+ Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also
+ defines BUILDFIXED, so the tables are built on the fly. makefixed() writes
+ those tables to stdout, which would be piped to inffixed.h. A small program
+ can simply call makefixed to do this:
+
+ void makefixed(void);
+
+ int main(void)
+ {
+ makefixed();
+ return 0;
+ }
+
+ Then that can be linked with zlib built with MAKEFIXED defined and run:
+
+ a.out > inffixed.h
+ */
+void makefixed()
+{
+ unsigned low, size;
+ struct inflate_state state;
+
+ fixedtables(&state);
+ puts(" /* inffixed.h -- table for decoding fixed codes");
+ puts(" * Generated automatically by makefixed().");
+ puts(" */");
+ puts("");
+ puts(" /* WARNING: this file should *not* be used by applications.");
+ puts(" It is part of the implementation of this library and is");
+ puts(" subject to change. Applications should only use zlib.h.");
+ puts(" */");
+ puts("");
+ size = 1U << 9;
+ printf(" static const code lenfix[%u] = {", size);
+ low = 0;
+ for (;;) {
+ if ((low % 7) == 0) printf("\n ");
+ printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits,
+ state.lencode[low].val);
+ if (++low == size) break;
+ putchar(',');
+ }
+ puts("\n };");
+ size = 1U << 5;
+ printf("\n static const code distfix[%u] = {", size);
+ low = 0;
+ for (;;) {
+ if ((low % 6) == 0) printf("\n ");
+ printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits,
+ state.distcode[low].val);
+ if (++low == size) break;
+ putchar(',');
+ }
+ puts("\n };");
+}
+#endif /* MAKEFIXED */
+
+/*
+ Update the window with the last wsize (normally 32K) bytes written before
+ returning. If window does not exist yet, create it. This is only called
+ when a window is already in use, or when output has been written during this
+ inflate call, but the end of the deflate stream has not been reached yet.
+ It is also called to create a window for dictionary data when a dictionary
+ is loaded.
+
+ Providing output buffers larger than 32K to inflate() should provide a speed
+ advantage, since only the last 32K of output is copied to the sliding window
+ upon return from inflate(), and since all distances after the first 32K of
+ output will fall in the output data, making match copies simpler and faster.
+ The advantage may be dependent on the size of the processor's data caches.
+ */
+local int updatewindow(strm, out)
+z_streamp strm;
+unsigned out;
+{
+ struct inflate_state FAR *state;
+ unsigned copy, dist;
+
+ state = (struct inflate_state FAR *)strm->state;
+
+ /* if it hasn't been done already, allocate space for the window */
+ if (state->window == Z_NULL) {
+ state->window = (unsigned char FAR *)
+ ZALLOC(strm, 1U << state->wbits,
+ sizeof(unsigned char));
+ if (state->window == Z_NULL) return 1;
+ }
+
+ /* if window not in use yet, initialize */
+ if (state->wsize == 0) {
+ state->wsize = 1U << state->wbits;
+ state->write = 0;
+ state->whave = 0;
+ }
+
+ /* copy state->wsize or less output bytes into the circular window */
+ copy = out - strm->avail_out;
+ if (copy >= state->wsize) {
+ zmemcpy(state->window, strm->next_out - state->wsize, state->wsize);
+ state->write = 0;
+ state->whave = state->wsize;
+ }
+ else {
+ dist = state->wsize - state->write;
+ if (dist > copy) dist = copy;
+ zmemcpy(state->window + state->write, strm->next_out - copy, dist);
+ copy -= dist;
+ if (copy) {
+ zmemcpy(state->window, strm->next_out - copy, copy);
+ state->write = copy;
+ state->whave = state->wsize;
+ }
+ else {
+ state->write += dist;
+ if (state->write == state->wsize) state->write = 0;
+ if (state->whave < state->wsize) state->whave += dist;
+ }
+ }
+ return 0;
+}
+
+/* Macros for inflate(): */
+
+/* check function to use adler32() for zlib or crc32() for gzip */
+#ifdef GUNZIP
+# define UPDATE(check, buf, len) \
+ (state->flags ? crc32(check, buf, len) : adler32(check, buf, len))
+#else
+# define UPDATE(check, buf, len) adler32(check, buf, len)
+#endif
+
+/* check macros for header crc */
+#ifdef GUNZIP
+# define CRC2(check, word) \
+ do { \
+ hbuf[0] = (unsigned char)(word); \
+ hbuf[1] = (unsigned char)((word) >> 8); \
+ check = crc32(check, hbuf, 2); \
+ } while (0)
+
+# define CRC4(check, word) \
+ do { \
+ hbuf[0] = (unsigned char)(word); \
+ hbuf[1] = (unsigned char)((word) >> 8); \
+ hbuf[2] = (unsigned char)((word) >> 16); \
+ hbuf[3] = (unsigned char)((word) >> 24); \
+ check = crc32(check, hbuf, 4); \
+ } while (0)
+#endif
+
+/* Load registers with state in inflate() for speed */
+#define LOAD() \
+ do { \
+ put = strm->next_out; \
+ left = strm->avail_out; \
+ next = strm->next_in; \
+ have = strm->avail_in; \
+ hold = state->hold; \
+ bits = state->bits; \
+ } while (0)
+
+/* Restore state from registers in inflate() */
+#define RESTORE() \
+ do { \
+ strm->next_out = put; \
+ strm->avail_out = left; \
+ strm->next_in = next; \
+ strm->avail_in = have; \
+ state->hold = hold; \
+ state->bits = bits; \
+ } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+ do { \
+ hold = 0; \
+ bits = 0; \
+ } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflate()
+ if there is no input available. */
+#define PULLBYTE() \
+ do { \
+ if (have == 0) goto inf_leave; \
+ have--; \
+ hold += (unsigned long)(*next++) << bits; \
+ bits += 8; \
+ } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator. If there is
+ not enough available input to do that, then return from inflate(). */
+#define NEEDBITS(n) \
+ do { \
+ while (bits < (unsigned)(n)) \
+ PULLBYTE(); \
+ } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+ ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+ do { \
+ hold >>= (n); \
+ bits -= (unsigned)(n); \
+ } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+ do { \
+ hold >>= bits & 7; \
+ bits -= bits & 7; \
+ } while (0)
+
+/* Reverse the bytes in a 32-bit value */
+#define REVERSE(q) \
+ ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \
+ (((q) & 0xff00) << 8) + (((q) & 0xff) << 24))
+
+/*
+ inflate() uses a state machine to process as much input data and generate as
+ much output data as possible before returning. The state machine is
+ structured roughly as follows:
+
+ for (;;) switch (state) {
+ ...
+ case STATEn:
+ if (not enough input data or output space to make progress)
+ return;
+ ... make progress ...
+ state = STATEm;
+ break;
+ ...
+ }
+
+ so when inflate() is called again, the same case is attempted again, and
+ if the appropriate resources are provided, the machine proceeds to the
+ next state. The NEEDBITS() macro is usually the way the state evaluates
+ whether it can proceed or should return. NEEDBITS() does the return if
+ the requested bits are not available. The typical use of the BITS macros
+ is:
+
+ NEEDBITS(n);
+ ... do something with BITS(n) ...
+ DROPBITS(n);
+
+ where NEEDBITS(n) either returns from inflate() if there isn't enough
+ input left to load n bits into the accumulator, or it continues. BITS(n)
+ gives the low n bits in the accumulator. When done, DROPBITS(n) drops
+ the low n bits off the accumulator. INITBITS() clears the accumulator
+ and sets the number of available bits to zero. BYTEBITS() discards just
+ enough bits to put the accumulator on a byte boundary. After BYTEBITS()
+ and a NEEDBITS(8), then BITS(8) would return the next byte in the stream.
+
+ NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return
+ if there is no input available. The decoding of variable length codes uses
+ PULLBYTE() directly in order to pull just enough bytes to decode the next
+ code, and no more.
+
+ Some states loop until they get enough input, making sure that enough
+ state information is maintained to continue the loop where it left off
+ if NEEDBITS() returns in the loop. For example, want, need, and keep
+ would all have to actually be part of the saved state in case NEEDBITS()
+ returns:
+
+ case STATEw:
+ while (want < need) {
+ NEEDBITS(n);
+ keep[want++] = BITS(n);
+ DROPBITS(n);
+ }
+ state = STATEx;
+ case STATEx:
+
+ As shown above, if the next state is also the next case, then the break
+ is omitted.
+
+ A state may also return if there is not enough output space available to
+ complete that state. Those states are copying stored data, writing a
+ literal byte, and copying a matching string.
+
+ When returning, a "goto inf_leave" is used to update the total counters,
+ update the check value, and determine whether any progress has been made
+ during that inflate() call in order to return the proper return code.
+ Progress is defined as a change in either strm->avail_in or strm->avail_out.
+ When there is a window, goto inf_leave will update the window with the last
+ output written. If a goto inf_leave occurs in the middle of decompression
+ and there is no window currently, goto inf_leave will create one and copy
+ output to the window for the next call of inflate().
+
+ In this implementation, the flush parameter of inflate() only affects the
+ return code (per zlib.h). inflate() always writes as much as possible to
+ strm->next_out, given the space available and the provided input--the effect
+ documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers
+ the allocation of and copying into a sliding window until necessary, which
+ provides the effect documented in zlib.h for Z_FINISH when the entire input
+ stream available. So the only thing the flush parameter actually does is:
+ when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it
+ will return Z_BUF_ERROR if it has not reached the end of the stream.
+ */
+
+int ZEXPORT inflate(strm, flush)
+z_streamp strm;
+int flush;
+{
+ struct inflate_state FAR *state;
+ unsigned char FAR *next; /* next input */
+ unsigned char FAR *put; /* next output */
+ unsigned have, left; /* available input and output */
+ unsigned long hold; /* bit buffer */
+ unsigned bits; /* bits in bit buffer */
+ unsigned in, out; /* save starting available input and output */
+ unsigned copy; /* number of stored or match bytes to copy */
+ unsigned char FAR *from; /* where to copy match bytes from */
+ code this; /* current decoding table entry */
+ code last; /* parent table entry */
+ unsigned len; /* length to copy for repeats, bits to drop */
+ int ret; /* return code */
+#ifdef GUNZIP
+ unsigned char hbuf[4]; /* buffer for gzip header crc calculation */
+#endif
+ static const unsigned short order[19] = /* permutation of code lengths */
+ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+ if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL ||
+ (strm->next_in == Z_NULL && strm->avail_in != 0))
+ return Z_STREAM_ERROR;
+
+ state = (struct inflate_state FAR *)strm->state;
+ if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */
+ LOAD();
+ in = have;
+ out = left;
+ ret = Z_OK;
+ for (;;)
+ switch (state->mode) {
+ case HEAD:
+ if (state->wrap == 0) {
+ state->mode = TYPEDO;
+ break;
+ }
+ NEEDBITS(16);
+#ifdef GUNZIP
+ if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */
+ state->check = crc32(0L, Z_NULL, 0);
+ CRC2(state->check, hold);
+ INITBITS();
+ state->mode = FLAGS;
+ break;
+ }
+ state->flags = 0; /* expect zlib header */
+ if (!(state->wrap & 1) || /* check if zlib header allowed */
+#else
+ if (
+#endif
+ ((BITS(8) << 8) + (hold >> 8)) % 31) {
+ strm->msg = (char *)"incorrect header check";
+ state->mode = BAD;
+ break;
+ }
+ if (BITS(4) != Z_DEFLATED) {
+ strm->msg = (char *)"unknown compression method";
+ state->mode = BAD;
+ break;
+ }
+ DROPBITS(4);
+ if (BITS(4) + 8 > state->wbits) {
+ strm->msg = (char *)"invalid window size";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: zlib header ok\n"));
+ strm->adler = state->check = adler32(0L, Z_NULL, 0);
+ state->mode = hold & 0x200 ? DICTID : TYPE;
+ INITBITS();
+ break;
+#ifdef GUNZIP
+ case FLAGS:
+ NEEDBITS(16);
+ state->flags = (int)(hold);
+ if ((state->flags & 0xff) != Z_DEFLATED) {
+ strm->msg = (char *)"unknown compression method";
+ state->mode = BAD;
+ break;
+ }
+ if (state->flags & 0xe000) {
+ strm->msg = (char *)"unknown header flags set";
+ state->mode = BAD;
+ break;
+ }
+ if (state->flags & 0x0200) CRC2(state->check, hold);
+ INITBITS();
+ state->mode = TIME;
+ case TIME:
+ NEEDBITS(32);
+ if (state->flags & 0x0200) CRC4(state->check, hold);
+ INITBITS();
+ state->mode = OS;
+ case OS:
+ NEEDBITS(16);
+ if (state->flags & 0x0200) CRC2(state->check, hold);
+ INITBITS();
+ state->mode = EXLEN;
+ case EXLEN:
+ if (state->flags & 0x0400) {
+ NEEDBITS(16);
+ state->length = (unsigned)(hold);
+ if (state->flags & 0x0200) CRC2(state->check, hold);
+ INITBITS();
+ }
+ state->mode = EXTRA;
+ case EXTRA:
+ if (state->flags & 0x0400) {
+ copy = state->length;
+ if (copy > have) copy = have;
+ if (copy) {
+ if (state->flags & 0x0200)
+ state->check = crc32(state->check, next, copy);
+ have -= copy;
+ next += copy;
+ state->length -= copy;
+ }
+ if (state->length) goto inf_leave;
+ }
+ state->mode = NAME;
+ case NAME:
+ if (state->flags & 0x0800) {
+ if (have == 0) goto inf_leave;
+ copy = 0;
+ do {
+ len = (unsigned)(next[copy++]);
+ } while (len && copy < have);
+ if (state->flags & 0x02000)
+ state->check = crc32(state->check, next, copy);
+ have -= copy;
+ next += copy;
+ if (len) goto inf_leave;
+ }
+ state->mode = COMMENT;
+ case COMMENT:
+ if (state->flags & 0x1000) {
+ if (have == 0) goto inf_leave;
+ copy = 0;
+ do {
+ len = (unsigned)(next[copy++]);
+ } while (len && copy < have);
+ if (state->flags & 0x02000)
+ state->check = crc32(state->check, next, copy);
+ have -= copy;
+ next += copy;
+ if (len) goto inf_leave;
+ }
+ state->mode = HCRC;
+ case HCRC:
+ if (state->flags & 0x0200) {
+ NEEDBITS(16);
+ if (hold != (state->check & 0xffff)) {
+ strm->msg = (char *)"header crc mismatch";
+ state->mode = BAD;
+ break;
+ }
+ INITBITS();
+ }
+ strm->adler = state->check = crc32(0L, Z_NULL, 0);
+ state->mode = TYPE;
+ break;
+#endif
+ case DICTID:
+ NEEDBITS(32);
+ strm->adler = state->check = REVERSE(hold);
+ INITBITS();
+ state->mode = DICT;
+ case DICT:
+ if (state->havedict == 0) {
+ RESTORE();
+ return Z_NEED_DICT;
+ }
+ strm->adler = state->check = adler32(0L, Z_NULL, 0);
+ state->mode = TYPE;
+ case TYPE:
+ if (flush == Z_BLOCK) goto inf_leave;
+ case TYPEDO:
+ if (state->last) {
+ BYTEBITS();
+ state->mode = CHECK;
+ break;
+ }
+ NEEDBITS(3);
+ state->last = BITS(1);
+ DROPBITS(1);
+ switch (BITS(2)) {
+ case 0: /* stored block */
+ Tracev((stderr, "inflate: stored block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = STORED;
+ break;
+ case 1: /* fixed block */
+ fixedtables(state);
+ Tracev((stderr, "inflate: fixed codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = LEN; /* decode codes */
+ break;
+ case 2: /* dynamic block */
+ Tracev((stderr, "inflate: dynamic codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = TABLE;
+ break;
+ case 3:
+ strm->msg = (char *)"invalid block type";
+ state->mode = BAD;
+ }
+ DROPBITS(2);
+ break;
+ case STORED:
+ BYTEBITS(); /* go to byte boundary */
+ NEEDBITS(32);
+ if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+ strm->msg = (char *)"invalid stored block lengths";
+ state->mode = BAD;
+ break;
+ }
+ state->length = (unsigned)hold & 0xffff;
+ Tracev((stderr, "inflate: stored length %u\n",
+ state->length));
+ INITBITS();
+ state->mode = COPY;
+ case COPY:
+ copy = state->length;
+ if (copy) {
+ if (copy > have) copy = have;
+ if (copy > left) copy = left;
+ if (copy == 0) goto inf_leave;
+ zmemcpy(put, next, copy);
+ have -= copy;
+ next += copy;
+ left -= copy;
+ put += copy;
+ state->length -= copy;
+ break;
+ }
+ Tracev((stderr, "inflate: stored end\n"));
+ state->mode = TYPE;
+ break;
+ case TABLE:
+ NEEDBITS(14);
+ state->nlen = BITS(5) + 257;
+ DROPBITS(5);
+ state->ndist = BITS(5) + 1;
+ DROPBITS(5);
+ state->ncode = BITS(4) + 4;
+ DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+ if (state->nlen > 286 || state->ndist > 30) {
+ strm->msg = (char *)"too many length or distance symbols";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ Tracev((stderr, "inflate: table sizes ok\n"));
+ state->have = 0;
+ state->mode = LENLENS;
+ case LENLENS:
+ while (state->have < state->ncode) {
+ NEEDBITS(3);
+ state->lens[order[state->have++]] = (unsigned short)BITS(3);
+ DROPBITS(3);
+ }
+ while (state->have < 19)
+ state->lens[order[state->have++]] = 0;
+ state->next = state->codes;
+ state->lencode = (code const FAR *)(state->next);
+ state->lenbits = 7;
+ ret = inflate_table(CODES, state->lens, 19, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid code lengths set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: code lengths ok\n"));
+ state->have = 0;
+ state->mode = CODELENS;
+ case CODELENS:
+ while (state->have < state->nlen + state->ndist) {
+ for (;;) {
+ this = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (this.val < 16) {
+ NEEDBITS(this.bits);
+ DROPBITS(this.bits);
+ state->lens[state->have++] = this.val;
+ }
+ else {
+ if (this.val == 16) {
+ NEEDBITS(this.bits + 2);
+ DROPBITS(this.bits);
+ if (state->have == 0) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ len = state->lens[state->have - 1];
+ copy = 3 + BITS(2);
+ DROPBITS(2);
+ }
+ else if (this.val == 17) {
+ NEEDBITS(this.bits + 3);
+ DROPBITS(this.bits);
+ len = 0;
+ copy = 3 + BITS(3);
+ DROPBITS(3);
+ }
+ else {
+ NEEDBITS(this.bits + 7);
+ DROPBITS(this.bits);
+ len = 0;
+ copy = 11 + BITS(7);
+ DROPBITS(7);
+ }
+ if (state->have + copy > state->nlen + state->ndist) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ while (copy--)
+ state->lens[state->have++] = (unsigned short)len;
+ }
+ }
+
+ /* handle error breaks in while */
+ if (state->mode == BAD) break;
+
+ /* build code tables */
+ state->next = state->codes;
+ state->lencode = (code const FAR *)(state->next);
+ state->lenbits = 9;
+ ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid literal/lengths set";
+ state->mode = BAD;
+ break;
+ }
+ state->distcode = (code const FAR *)(state->next);
+ state->distbits = 6;
+ ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+ &(state->next), &(state->distbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid distances set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: codes ok\n"));
+ state->mode = LEN;
+ case LEN:
+ if (have >= 6 && left >= 258) {
+ RESTORE();
+ inflate_fast(strm, out);
+ LOAD();
+ break;
+ }
+ for (;;) {
+ this = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (this.op && (this.op & 0xf0) == 0) {
+ last = this;
+ for (;;) {
+ this = state->lencode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ }
+ DROPBITS(this.bits);
+ state->length = (unsigned)this.val;
+ if ((int)(this.op) == 0) {
+ Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", this.val));
+ state->mode = LIT;
+ break;
+ }
+ if (this.op & 32) {
+ Tracevv((stderr, "inflate: end of block\n"));
+ state->mode = TYPE;
+ break;
+ }
+ if (this.op & 64) {
+ strm->msg = (char *)"invalid literal/length code";
+ state->mode = BAD;
+ break;
+ }
+ state->extra = (unsigned)(this.op) & 15;
+ state->mode = LENEXT;
+ case LENEXT:
+ if (state->extra) {
+ NEEDBITS(state->extra);
+ state->length += BITS(state->extra);
+ DROPBITS(state->extra);
+ }
+ Tracevv((stderr, "inflate: length %u\n", state->length));
+ state->mode = DIST;
+ case DIST:
+ for (;;) {
+ this = state->distcode[BITS(state->distbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if ((this.op & 0xf0) == 0) {
+ last = this;
+ for (;;) {
+ this = state->distcode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ }
+ DROPBITS(this.bits);
+ if (this.op & 64) {
+ strm->msg = (char *)"invalid distance code";
+ state->mode = BAD;
+ break;
+ }
+ state->offset = (unsigned)this.val;
+ state->extra = (unsigned)(this.op) & 15;
+ state->mode = DISTEXT;
+ case DISTEXT:
+ if (state->extra) {
+ NEEDBITS(state->extra);
+ state->offset += BITS(state->extra);
+ DROPBITS(state->extra);
+ }
+ if (state->offset > state->whave + out - left) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+ Tracevv((stderr, "inflate: distance %u\n", state->offset));
+ state->mode = MATCH;
+ case MATCH:
+ if (left == 0) goto inf_leave;
+ copy = out - left;
+ if (state->offset > copy) { /* copy from window */
+ copy = state->offset - copy;
+ if (copy > state->write) {
+ copy -= state->write;
+ from = state->window + (state->wsize - copy);
+ }
+ else
+ from = state->window + (state->write - copy);
+ if (copy > state->length) copy = state->length;
+ }
+ else { /* copy from output */
+ from = put - state->offset;
+ copy = state->length;
+ }
+ if (copy > left) copy = left;
+ left -= copy;
+ state->length -= copy;
+ do {
+ *put++ = *from++;
+ } while (--copy);
+ if (state->length == 0) state->mode = LEN;
+ break;
+ case LIT:
+ if (left == 0) goto inf_leave;
+ *put++ = (unsigned char)(state->length);
+ left--;
+ state->mode = LEN;
+ break;
+ case CHECK:
+ if (state->wrap) {
+ NEEDBITS(32);
+ out -= left;
+ strm->total_out += out;
+ state->total += out;
+ if (out)
+ strm->adler = state->check =
+ UPDATE(state->check, put - out, out);
+ out = left;
+ if ((
+#ifdef GUNZIP
+ state->flags ? hold :
+#endif
+ REVERSE(hold)) != state->check) {
+ strm->msg = (char *)"incorrect data check";
+ state->mode = BAD;
+ break;
+ }
+ INITBITS();
+ Tracev((stderr, "inflate: check matches trailer\n"));
+ }
+#ifdef GUNZIP
+ state->mode = LENGTH;
+ case LENGTH:
+ if (state->wrap && state->flags) {
+ NEEDBITS(32);
+ if (hold != (state->total & 0xffffffffUL)) {
+ strm->msg = (char *)"incorrect length check";
+ state->mode = BAD;
+ break;
+ }
+ INITBITS();
+ Tracev((stderr, "inflate: length matches trailer\n"));
+ }
+#endif
+ state->mode = DONE;
+ case DONE:
+ ret = Z_STREAM_END;
+ goto inf_leave;
+ case BAD:
+ ret = Z_DATA_ERROR;
+ goto inf_leave;
+ case MEM:
+ return Z_MEM_ERROR;
+ case SYNC:
+ default:
+ return Z_STREAM_ERROR;
+ }
+
+ /*
+ Return from inflate(), updating the total counts and the check value.
+ If there was no progress during the inflate() call, return a buffer
+ error. Call updatewindow() to create and/or update the window state.
+ Note: a memory error from inflate() is non-recoverable.
+ */
+ inf_leave:
+ RESTORE();
+ if (state->wsize || (state->mode < CHECK && out != strm->avail_out))
+ if (updatewindow(strm, out)) {
+ state->mode = MEM;
+ return Z_MEM_ERROR;
+ }
+ in -= strm->avail_in;
+ out -= strm->avail_out;
+ strm->total_in += in;
+ strm->total_out += out;
+ state->total += out;
+ if (state->wrap && out)
+ strm->adler = state->check =
+ UPDATE(state->check, strm->next_out - out, out);
+ strm->data_type = state->bits + (state->last ? 64 : 0) +
+ (state->mode == TYPE ? 128 : 0);
+ if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK)
+ ret = Z_BUF_ERROR;
+ return ret;
+}
+
+int ZEXPORT inflateEnd(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+ if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
+ return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (state->window != Z_NULL) ZFREE(strm, state->window);
+ ZFREE(strm, strm->state);
+ strm->state = Z_NULL;
+ Tracev((stderr, "inflate: end\n"));
+ return Z_OK;
+}
+
+int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength)
+z_streamp strm;
+const Bytef *dictionary;
+uInt dictLength;
+{
+ struct inflate_state FAR *state;
+ unsigned long id;
+
+ /* check state */
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (state->mode != DICT) return Z_STREAM_ERROR;
+
+ /* check for correct dictionary id */
+ id = adler32(0L, Z_NULL, 0);
+ id = adler32(id, dictionary, dictLength);
+ if (id != state->check) return Z_DATA_ERROR;
+
+ /* copy dictionary to window */
+ if (updatewindow(strm, strm->avail_out)) {
+ state->mode = MEM;
+ return Z_MEM_ERROR;
+ }
+ if (dictLength > state->wsize) {
+ zmemcpy(state->window, dictionary + dictLength - state->wsize,
+ state->wsize);
+ state->whave = state->wsize;
+ }
+ else {
+ zmemcpy(state->window + state->wsize - dictLength, dictionary,
+ dictLength);
+ state->whave = dictLength;
+ }
+ state->havedict = 1;
+ Tracev((stderr, "inflate: dictionary set\n"));
+ return Z_OK;
+}
+
+/*
+ Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found
+ or when out of input. When called, *have is the number of pattern bytes
+ found in order so far, in 0..3. On return *have is updated to the new
+ state. If on return *have equals four, then the pattern was found and the
+ return value is how many bytes were read including the last byte of the
+ pattern. If *have is less than four, then the pattern has not been found
+ yet and the return value is len. In the latter case, syncsearch() can be
+ called again with more data and the *have state. *have is initialized to
+ zero for the first call.
+ */
+local unsigned syncsearch(have, buf, len)
+unsigned FAR *have;
+unsigned char FAR *buf;
+unsigned len;
+{
+ unsigned got;
+ unsigned next;
+
+ got = *have;
+ next = 0;
+ while (next < len && got < 4) {
+ if ((int)(buf[next]) == (got < 2 ? 0 : 0xff))
+ got++;
+ else if (buf[next])
+ got = 0;
+ else
+ got = 4 - got;
+ next++;
+ }
+ *have = got;
+ return next;
+}
+
+int ZEXPORT inflateSync(strm)
+z_streamp strm;
+{
+ unsigned len; /* number of bytes to look at or looked at */
+ unsigned long in, out; /* temporary to save total_in and total_out */
+ unsigned char buf[4]; /* to restore bit buffer to byte string */
+ struct inflate_state FAR *state;
+
+ /* check parameters */
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR;
+
+ /* if first time, start search in bit buffer */
+ if (state->mode != SYNC) {
+ state->mode = SYNC;
+ state->hold <<= state->bits & 7;
+ state->bits -= state->bits & 7;
+ len = 0;
+ while (state->bits >= 8) {
+ buf[len++] = (unsigned char)(state->hold);
+ state->hold >>= 8;
+ state->bits -= 8;
+ }
+ state->have = 0;
+ syncsearch(&(state->have), buf, len);
+ }
+
+ /* search available input */
+ len = syncsearch(&(state->have), strm->next_in, strm->avail_in);
+ strm->avail_in -= len;
+ strm->next_in += len;
+ strm->total_in += len;
+
+ /* return no joy or set up to restart inflate() on a new block */
+ if (state->have != 4) return Z_DATA_ERROR;
+ in = strm->total_in; out = strm->total_out;
+ inflateReset(strm);
+ strm->total_in = in; strm->total_out = out;
+ state->mode = TYPE;
+ return Z_OK;
+}
+
+/*
+ Returns true if inflate is currently at the end of a block generated by
+ Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
+ implementation to provide an additional safety check. PPP uses
+ Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored
+ block. When decompressing, PPP checks that at the end of input packet,
+ inflate is waiting for these length bytes.
+ */
+int ZEXPORT inflateSyncPoint(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ return state->mode == STORED && state->bits == 0;
+}
+
+int ZEXPORT inflateCopy(dest, source)
+z_streamp dest;
+z_streamp source;
+{
+ struct inflate_state FAR *state;
+ struct inflate_state FAR *copy;
+ unsigned char FAR *window;
+
+ /* check input */
+ if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL ||
+ source->zalloc == (alloc_func)0 || source->zfree == (free_func)0)
+ return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)source->state;
+
+ /* allocate space */
+ copy = (struct inflate_state FAR *)
+ ZALLOC(source, 1, sizeof(struct inflate_state));
+ if (copy == Z_NULL) return Z_MEM_ERROR;
+ window = Z_NULL;
+ if (state->window != Z_NULL) {
+ window = (unsigned char FAR *)
+ ZALLOC(source, 1U << state->wbits, sizeof(unsigned char));
+ if (window == Z_NULL) {
+ ZFREE(source, copy);
+ return Z_MEM_ERROR;
+ }
+ }
+
+ /* copy state */
+ *dest = *source;
+ *copy = *state;
+ copy->lencode = copy->codes + (state->lencode - state->codes);
+ copy->distcode = copy->codes + (state->distcode - state->codes);
+ copy->next = copy->codes + (state->next - state->codes);
+ if (window != Z_NULL)
+ zmemcpy(window, state->window, 1U << state->wbits);
+ copy->window = window;
+ dest->state = (voidpf)copy;
+ return Z_OK;
+}
diff --git a/zlib/inflate.h b/zlib/inflate.h
new file mode 100644
index 0000000..b696512
--- /dev/null
+++ b/zlib/inflate.h
@@ -0,0 +1,117 @@
+/* inflate.h -- internal inflate state definition
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+ trailer decoding by inflate(). NO_GZIP would be used to avoid linking in
+ the crc code when it is not needed. For shared libraries, gzip decoding
+ should be left enabled. */
+#ifndef NO_GZIP
+# define GUNZIP
+#endif
+
+/* Possible inflate modes between inflate() calls */
+typedef enum {
+ HEAD, /* i: waiting for magic header */
+#ifdef GUNZIP
+ FLAGS, /* i: waiting for method and flags (gzip) */
+ TIME, /* i: waiting for modification time (gzip) */
+ OS, /* i: waiting for extra flags and operating system (gzip) */
+ EXLEN, /* i: waiting for extra length (gzip) */
+ EXTRA, /* i: waiting for extra bytes (gzip) */
+ NAME, /* i: waiting for end of file name (gzip) */
+ COMMENT, /* i: waiting for end of comment (gzip) */
+ HCRC, /* i: waiting for header crc (gzip) */
+#endif
+ DICTID, /* i: waiting for dictionary check value */
+ DICT, /* waiting for inflateSetDictionary() call */
+ TYPE, /* i: waiting for type bits, including last-flag bit */
+ TYPEDO, /* i: same, but skip check to exit inflate on new block */
+ STORED, /* i: waiting for stored size (length and complement) */
+ COPY, /* i/o: waiting for input or output to copy stored block */
+ TABLE, /* i: waiting for dynamic block table lengths */
+ LENLENS, /* i: waiting for code length code lengths */
+ CODELENS, /* i: waiting for length/lit and distance code lengths */
+ LEN, /* i: waiting for length/lit code */
+ LENEXT, /* i: waiting for length extra bits */
+ DIST, /* i: waiting for distance code */
+ DISTEXT, /* i: waiting for distance extra bits */
+ MATCH, /* o: waiting for output space to copy string */
+ LIT, /* o: waiting for output space to write literal */
+ CHECK, /* i: waiting for 32-bit check value */
+#ifdef GUNZIP
+ LENGTH, /* i: waiting for 32-bit length (gzip) */
+#endif
+ DONE, /* finished check, done -- remain here until reset */
+ BAD, /* got a data error -- remain here until reset */
+ MEM, /* got an inflate() memory error -- remain here until reset */
+ SYNC /* looking for synchronization bytes to restart inflate() */
+} inflate_mode;
+
+/*
+ State transitions between above modes -
+
+ (most modes can go to the BAD or MEM mode -- not shown for clarity)
+
+ Process header:
+ HEAD -> (gzip) or (zlib)
+ (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME
+ NAME -> COMMENT -> HCRC -> TYPE
+ (zlib) -> DICTID or TYPE
+ DICTID -> DICT -> TYPE
+ Read deflate blocks:
+ TYPE -> STORED or TABLE or LEN or CHECK
+ STORED -> COPY -> TYPE
+ TABLE -> LENLENS -> CODELENS -> LEN
+ Read deflate codes:
+ LEN -> LENEXT or LIT or TYPE
+ LENEXT -> DIST -> DISTEXT -> MATCH -> LEN
+ LIT -> LEN
+ Process trailer:
+ CHECK -> LENGTH -> DONE
+ */
+
+/* state maintained between inflate() calls. Approximately 7K bytes. */
+struct inflate_state {
+ inflate_mode mode; /* current inflate mode */
+ int last; /* true if processing last block */
+ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */
+ int havedict; /* true if dictionary provided */
+ int flags; /* gzip header method and flags (0 if zlib) */
+ unsigned long check; /* protected copy of check value */
+ unsigned long total; /* protected copy of output count */
+ /* sliding window */
+ unsigned wbits; /* log base 2 of requested window size */
+ unsigned wsize; /* window size or zero if not using window */
+ unsigned whave; /* valid bytes in the window */
+ unsigned write; /* window write index */
+ unsigned char FAR *window; /* allocated sliding window, if needed */
+ /* bit accumulator */
+ unsigned long hold; /* input bit accumulator */
+ unsigned bits; /* number of bits in "in" */
+ /* for string and stored block copying */
+ unsigned length; /* literal or length of data to copy */
+ unsigned offset; /* distance back to copy string from */
+ /* for table and code decoding */
+ unsigned extra; /* extra bits needed */
+ /* fixed and dynamic code tables */
+ code const FAR *lencode; /* starting table for length/literal codes */
+ code const FAR *distcode; /* starting table for distance codes */
+ unsigned lenbits; /* index bits for lencode */
+ unsigned distbits; /* index bits for distcode */
+ /* dynamic table building */
+ unsigned ncode; /* number of code length code lengths */
+ unsigned nlen; /* number of length code lengths */
+ unsigned ndist; /* number of distance code lengths */
+ unsigned have; /* number of code lengths in lens[] */
+ code FAR *next; /* next available space in codes[] */
+ unsigned short lens[320]; /* temporary storage for code lengths */
+ unsigned short work[288]; /* work area for code table building */
+ code codes[ENOUGH]; /* space for code tables */
+};
diff --git a/zlib/inftrees.c b/zlib/inftrees.c
new file mode 100644
index 0000000..214cb23
--- /dev/null
+++ b/zlib/inftrees.c
@@ -0,0 +1,328 @@
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995-2004 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+
+#define MAXBITS 15
+
+const char inflate_copyright[] =
+ " inflate 1.2.2 Copyright 1995-2004 Mark Adler ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+/*
+ Build a set of tables to decode the provided canonical Huffman code.
+ The code lengths are lens[0..codes-1]. The result starts at *table,
+ whose indices are 0..2^bits-1. work is a writable array of at least
+ lens shorts, which is used as a work area. type is the type of code
+ to be generated, CODES, LENS, or DISTS. On return, zero is success,
+ -1 is an invalid code, and +1 means that ENOUGH isn't enough. table
+ on return points to the next available entry's address. bits is the
+ requested root table index bits, and on return it is the actual root
+ table index bits. It will differ if the request is greater than the
+ longest code or if it is less than the shortest code.
+ */
+int inflate_table(type, lens, codes, table, bits, work)
+codetype type;
+unsigned short FAR *lens;
+unsigned codes;
+code FAR * FAR *table;
+unsigned FAR *bits;
+unsigned short FAR *work;
+{
+ unsigned len; /* a code's length in bits */
+ unsigned sym; /* index of code symbols */
+ unsigned min, max; /* minimum and maximum code lengths */
+ unsigned root; /* number of index bits for root table */
+ unsigned curr; /* number of index bits for current table */
+ unsigned drop; /* code bits to drop for sub-table */
+ int left; /* number of prefix codes available */
+ unsigned used; /* code entries in table used */
+ unsigned huff; /* Huffman code */
+ unsigned incr; /* for incrementing code, index */
+ unsigned fill; /* index for replicating entries */
+ unsigned low; /* low bits for current root entry */
+ unsigned mask; /* mask for low root bits */
+ code this; /* table entry for duplication */
+ code FAR *next; /* next available space in table */
+ const unsigned short FAR *base; /* base value table to use */
+ const unsigned short FAR *extra; /* extra bits table to use */
+ int end; /* use base and extra for symbol > end */
+ unsigned short count[MAXBITS+1]; /* number of codes of each length */
+ unsigned short offs[MAXBITS+1]; /* offsets in table for each length */
+ static const unsigned short lbase[31] = { /* Length codes 257..285 base */
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+ static const unsigned short lext[31] = { /* Length codes 257..285 extra */
+ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
+ 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 199, 198};
+ static const unsigned short dbase[32] = { /* Distance codes 0..29 base */
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577, 0, 0};
+ static const unsigned short dext[32] = { /* Distance codes 0..29 extra */
+ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
+ 23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
+ 28, 28, 29, 29, 64, 64};
+
+ /*
+ Process a set of code lengths to create a canonical Huffman code. The
+ code lengths are lens[0..codes-1]. Each length corresponds to the
+ symbols 0..codes-1. The Huffman code is generated by first sorting the
+ symbols by length from short to long, and retaining the symbol order
+ for codes with equal lengths. Then the code starts with all zero bits
+ for the first code of the shortest length, and the codes are integer
+ increments for the same length, and zeros are appended as the length
+ increases. For the deflate format, these bits are stored backwards
+ from their more natural integer increment ordering, and so when the
+ decoding tables are built in the large loop below, the integer codes
+ are incremented backwards.
+
+ This routine assumes, but does not check, that all of the entries in
+ lens[] are in the range 0..MAXBITS. The caller must assure this.
+ 1..MAXBITS is interpreted as that code length. zero means that that
+ symbol does not occur in this code.
+
+ The codes are sorted by computing a count of codes for each length,
+ creating from that a table of starting indices for each length in the
+ sorted table, and then entering the symbols in order in the sorted
+ table. The sorted table is work[], with that space being provided by
+ the caller.
+
+ The length counts are used for other purposes as well, i.e. finding
+ the minimum and maximum length codes, determining if there are any
+ codes at all, checking for a valid set of lengths, and looking ahead
+ at length counts to determine sub-table sizes when building the
+ decoding tables.
+ */
+
+ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
+ for (len = 0; len <= MAXBITS; len++)
+ count[len] = 0;
+ for (sym = 0; sym < codes; sym++)
+ count[lens[sym]]++;
+
+ /* bound code lengths, force root to be within code lengths */
+ root = *bits;
+ for (max = MAXBITS; max >= 1; max--)
+ if (count[max] != 0) break;
+ if (root > max) root = max;
+ if (max == 0) { /* no symbols to code at all */
+ this.op = (unsigned char)64; /* invalid code marker */
+ this.bits = (unsigned char)1;
+ this.val = (unsigned short)0;
+ *(*table)++ = this; /* make a table to force an error */
+ *(*table)++ = this;
+ *bits = 1;
+ return 0; /* no symbols, but wait for decoding to report error */
+ }
+ for (min = 1; min <= MAXBITS; min++)
+ if (count[min] != 0) break;
+ if (root < min) root = min;
+
+ /* check for an over-subscribed or incomplete set of lengths */
+ left = 1;
+ for (len = 1; len <= MAXBITS; len++) {
+ left <<= 1;
+ left -= count[len];
+ if (left < 0) return -1; /* over-subscribed */
+ }
+ if (left > 0 && (type == CODES || (codes - count[0] != 1)))
+ return -1; /* incomplete set */
+
+ /* generate offsets into symbol table for each length for sorting */
+ offs[1] = 0;
+ for (len = 1; len < MAXBITS; len++)
+ offs[len + 1] = offs[len] + count[len];
+
+ /* sort symbols by length, by symbol order within each length */
+ for (sym = 0; sym < codes; sym++)
+ if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym;
+
+ /*
+ Create and fill in decoding tables. In this loop, the table being
+ filled is at next and has curr index bits. The code being used is huff
+ with length len. That code is converted to an index by dropping drop
+ bits off of the bottom. For codes where len is less than drop + curr,
+ those top drop + curr - len bits are incremented through all values to
+ fill the table with replicated entries.
+
+ root is the number of index bits for the root table. When len exceeds
+ root, sub-tables are created pointed to by the root entry with an index
+ of the low root bits of huff. This is saved in low to check for when a
+ new sub-table should be started. drop is zero when the root table is
+ being filled, and drop is root when sub-tables are being filled.
+
+ When a new sub-table is needed, it is necessary to look ahead in the
+ code lengths to determine what size sub-table is needed. The length
+ counts are used for this, and so count[] is decremented as codes are
+ entered in the tables.
+
+ used keeps track of how many table entries have been allocated from the
+ provided *table space. It is checked when a LENS table is being made
+ against the space in *table, ENOUGH, minus the maximum space needed by
+ the worst case distance code, MAXD. This should never happen, but the
+ sufficiency of ENOUGH has not been proven exhaustively, hence the check.
+ This assumes that when type == LENS, bits == 9.
+
+ sym increments through all symbols, and the loop terminates when
+ all codes of length max, i.e. all codes, have been processed. This
+ routine permits incomplete codes, so another loop after this one fills
+ in the rest of the decoding tables with invalid code markers.
+ */
+
+ /* set up for code type */
+ switch (type) {
+ case CODES:
+ base = extra = work; /* dummy value--not used */
+ end = 19;
+ break;
+ case LENS:
+ base = lbase;
+ base -= 257;
+ extra = lext;
+ extra -= 257;
+ end = 256;
+ break;
+ default: /* DISTS */
+ base = dbase;
+ extra = dext;
+ end = -1;
+ }
+
+ /* initialize state for loop */
+ huff = 0; /* starting code */
+ sym = 0; /* starting code symbol */
+ len = min; /* starting code length */
+ next = *table; /* current table to fill in */
+ curr = root; /* current table index bits */
+ drop = 0; /* current bits to drop from code for index */
+ low = (unsigned)(-1); /* trigger new sub-table when len > root */
+ used = 1U << root; /* use root table entries */
+ mask = used - 1; /* mask for comparing low */
+
+ /* check available table space */
+ if (type == LENS && used >= ENOUGH - MAXD)
+ return 1;
+
+ /* process all codes and make table entries */
+ for (;;) {
+ /* create table entry */
+ this.bits = (unsigned char)(len - drop);
+ if ((int)(work[sym]) < end) {
+ this.op = (unsigned char)0;
+ this.val = work[sym];
+ }
+ else if ((int)(work[sym]) > end) {
+ this.op = (unsigned char)(extra[work[sym]]);
+ this.val = base[work[sym]];
+ }
+ else {
+ this.op = (unsigned char)(32 + 64); /* end of block */
+ this.val = 0;
+ }
+
+ /* replicate for those indices with low len bits equal to huff */
+ incr = 1U << (len - drop);
+ fill = 1U << curr;
+ do {
+ fill -= incr;
+ next[(huff >> drop) + fill] = this;
+ } while (fill != 0);
+
+ /* backwards increment the len-bit code huff */
+ incr = 1U << (len - 1);
+ while (huff & incr)
+ incr >>= 1;
+ if (incr != 0) {
+ huff &= incr - 1;
+ huff += incr;
+ }
+ else
+ huff = 0;
+
+ /* go to next symbol, update count, len */
+ sym++;
+ if (--(count[len]) == 0) {
+ if (len == max) break;
+ len = lens[work[sym]];
+ }
+
+ /* create new sub-table if needed */
+ if (len > root && (huff & mask) != low) {
+ /* if first time, transition to sub-tables */
+ if (drop == 0)
+ drop = root;
+
+ /* increment past last table */
+ next += 1U << curr;
+
+ /* determine length of next table */
+ curr = len - drop;
+ left = (int)(1 << curr);
+ while (curr + drop < max) {
+ left -= count[curr + drop];
+ if (left <= 0) break;
+ curr++;
+ left <<= 1;
+ }
+
+ /* check for enough space */
+ used += 1U << curr;
+ if (type == LENS && used >= ENOUGH - MAXD)
+ return 1;
+
+ /* point entry in root table to sub-table */
+ low = huff & mask;
+ (*table)[low].op = (unsigned char)curr;
+ (*table)[low].bits = (unsigned char)root;
+ (*table)[low].val = (unsigned short)(next - *table);
+ }
+ }
+
+ /*
+ Fill in rest of table for incomplete codes. This loop is similar to the
+ loop above in incrementing huff for table indices. It is assumed that
+ len is equal to curr + drop, so there is no loop needed to increment
+ through high index bits. When the current sub-table is filled, the loop
+ drops back to the root table to fill in any remaining entries there.
+ */
+ this.op = (unsigned char)64; /* invalid code marker */
+ this.bits = (unsigned char)(len - drop);
+ this.val = (unsigned short)0;
+ while (huff != 0) {
+ /* when done with sub-table, drop back to root table */
+ if (drop != 0 && (huff & mask) != low) {
+ drop = 0;
+ len = root;
+ next = *table;
+ this.bits = (unsigned char)len;
+ }
+
+ /* put invalid code marker in table */
+ next[huff >> drop] = this;
+
+ /* backwards increment the len-bit code huff */
+ incr = 1U << (len - 1);
+ while (huff & incr)
+ incr >>= 1;
+ if (incr != 0) {
+ huff &= incr - 1;
+ huff += incr;
+ }
+ else
+ huff = 0;
+ }
+
+ /* set return parameters */
+ *table += used;
+ *bits = root;
+ return 0;
+}
diff --git a/zlib/inftrees.h b/zlib/inftrees.h
new file mode 100644
index 0000000..1dbfe53
--- /dev/null
+++ b/zlib/inftrees.h
@@ -0,0 +1,55 @@
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* Structure for decoding tables. Each entry provides either the
+ information needed to do the operation requested by the code that
+ indexed that table entry, or it provides a pointer to another
+ table that indexes more bits of the code. op indicates whether
+ the entry is a pointer to another table, a literal, a length or
+ distance, an end-of-block, or an invalid code. For a table
+ pointer, the low four bits of op is the number of index bits of
+ that table. For a length or distance, the low four bits of op
+ is the number of extra bits to get after the code. bits is
+ the number of bits in this code or part of the code to drop off
+ of the bit buffer. val is the actual byte to output in the case
+ of a literal, the base length or distance, or the offset from
+ the current table to the next table. Each entry is four bytes. */
+typedef struct {
+ unsigned char op; /* operation, extra bits, table bits */
+ unsigned char bits; /* bits in this part of the code */
+ unsigned short val; /* offset in table or code value */
+} code;
+
+/* op values as set by inflate_table():
+ 00000000 - literal
+ 0000tttt - table link, tttt != 0 is the number of table index bits
+ 0001eeee - length or distance, eeee is the number of extra bits
+ 01100000 - end of block
+ 01000000 - invalid code
+ */
+
+/* Maximum size of dynamic tree. The maximum found in a long but non-
+ exhaustive search was 1004 code structures (850 for length/literals
+ and 154 for distances, the latter actually the result of an
+ exhaustive search). The true maximum is not known, but the value
+ below is more than safe. */
+#define ENOUGH 1440
+#define MAXD 154
+
+/* Type of code to build for inftable() */
+typedef enum {
+ CODES,
+ LENS,
+ DISTS
+} codetype;
+
+extern int inflate_table OF((codetype type, unsigned short FAR *lens,
+ unsigned codes, code FAR * FAR *table,
+ unsigned FAR *bits, unsigned short FAR *work));
diff --git a/zlib/minigzip.c b/zlib/minigzip.c
new file mode 100644
index 0000000..0a58a0a
--- /dev/null
+++ b/zlib/minigzip.c
@@ -0,0 +1,322 @@
+/* minigzip.c -- simulate gzip using the zlib compression library
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * minigzip is a minimal implementation of the gzip utility. This is
+ * only an example of using zlib and isn't meant to replace the
+ * full-featured gzip. No attempt is made to deal with file systems
+ * limiting names to 14 or 8+3 characters, etc... Error checking is
+ * very limited. So use minigzip only for testing; use gzip for the
+ * real thing. On MSDOS, use only on file names without extension
+ * or in pipe mode.
+ */
+
+/* @(#) $Id$ */
+
+#include <stdio.h>
+#include "zlib.h"
+
+#ifdef STDC
+# include <string.h>
+# include <stdlib.h>
+#else
+ extern void exit OF((int));
+#endif
+
+#ifdef USE_MMAP
+# include <sys/types.h>
+# include <sys/mman.h>
+# include <sys/stat.h>
+#endif
+
+#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
+# include <fcntl.h>
+# include <io.h>
+# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
+#else
+# define SET_BINARY_MODE(file)
+#endif
+
+#ifdef VMS
+# define unlink delete
+# define GZ_SUFFIX "-gz"
+#endif
+#ifdef RISCOS
+# define unlink remove
+# define GZ_SUFFIX "-gz"
+# define fileno(file) file->__file
+#endif
+#if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+# include <unix.h> /* for fileno */
+#endif
+
+#ifndef WIN32 /* unlink already in stdio.h for WIN32 */
+ extern int unlink OF((const char *));
+#endif
+
+#ifndef GZ_SUFFIX
+# define GZ_SUFFIX ".gz"
+#endif
+#define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
+
+#define BUFLEN 16384
+#define MAX_NAME_LEN 1024
+
+#ifdef MAXSEG_64K
+# define local static
+ /* Needed for systems with limitation on stack size. */
+#else
+# define local
+#endif
+
+char *prog;
+
+void error OF((const char *msg));
+void gz_compress OF((FILE *in, gzFile out));
+#ifdef USE_MMAP
+int gz_compress_mmap OF((FILE *in, gzFile out));
+#endif
+void gz_uncompress OF((gzFile in, FILE *out));
+void file_compress OF((char *file, char *mode));
+void file_uncompress OF((char *file));
+int main OF((int argc, char *argv[]));
+
+/* ===========================================================================
+ * Display error message and exit
+ */
+void error(msg)
+ const char *msg;
+{
+ fprintf(stderr, "%s: %s\n", prog, msg);
+ exit(1);
+}
+
+/* ===========================================================================
+ * Compress input to output then close both files.
+ */
+
+void gz_compress(in, out)
+ FILE *in;
+ gzFile out;
+{
+ local char buf[BUFLEN];
+ int len;
+ int err;
+
+#ifdef USE_MMAP
+ /* Try first compressing with mmap. If mmap fails (minigzip used in a
+ * pipe), use the normal fread loop.
+ */
+ if (gz_compress_mmap(in, out) == Z_OK) return;
+#endif
+ for (;;) {
+ len = (int)fread(buf, 1, sizeof(buf), in);
+ if (ferror(in)) {
+ perror("fread");
+ exit(1);
+ }
+ if (len == 0) break;
+
+ if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err));
+ }
+ fclose(in);
+ if (gzclose(out) != Z_OK) error("failed gzclose");
+}
+
+#ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */
+
+/* Try compressing the input file at once using mmap. Return Z_OK if
+ * if success, Z_ERRNO otherwise.
+ */
+int gz_compress_mmap(in, out)
+ FILE *in;
+ gzFile out;
+{
+ int len;
+ int err;
+ int ifd = fileno(in);
+ caddr_t buf; /* mmap'ed buffer for the entire input file */
+ off_t buf_len; /* length of the input file */
+ struct stat sb;
+
+ /* Determine the size of the file, needed for mmap: */
+ if (fstat(ifd, &sb) < 0) return Z_ERRNO;
+ buf_len = sb.st_size;
+ if (buf_len <= 0) return Z_ERRNO;
+
+ /* Now do the actual mmap: */
+ buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
+ if (buf == (caddr_t)(-1)) return Z_ERRNO;
+
+ /* Compress the whole file at once: */
+ len = gzwrite(out, (char *)buf, (unsigned)buf_len);
+
+ if (len != (int)buf_len) error(gzerror(out, &err));
+
+ munmap(buf, buf_len);
+ fclose(in);
+ if (gzclose(out) != Z_OK) error("failed gzclose");
+ return Z_OK;
+}
+#endif /* USE_MMAP */
+
+/* ===========================================================================
+ * Uncompress input to output then close both files.
+ */
+void gz_uncompress(in, out)
+ gzFile in;
+ FILE *out;
+{
+ local char buf[BUFLEN];
+ int len;
+ int err;
+
+ for (;;) {
+ len = gzread(in, buf, sizeof(buf));
+ if (len < 0) error (gzerror(in, &err));
+ if (len == 0) break;
+
+ if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
+ error("failed fwrite");
+ }
+ }
+ if (fclose(out)) error("failed fclose");
+
+ if (gzclose(in) != Z_OK) error("failed gzclose");
+}
+
+
+/* ===========================================================================
+ * Compress the given file: create a corresponding .gz file and remove the
+ * original.
+ */
+void file_compress(file, mode)
+ char *file;
+ char *mode;
+{
+ local char outfile[MAX_NAME_LEN];
+ FILE *in;
+ gzFile out;
+
+ strcpy(outfile, file);
+ strcat(outfile, GZ_SUFFIX);
+
+ in = fopen(file, "rb");
+ if (in == NULL) {
+ perror(file);
+ exit(1);
+ }
+ out = gzopen(outfile, mode);
+ if (out == NULL) {
+ fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
+ exit(1);
+ }
+ gz_compress(in, out);
+
+ unlink(file);
+}
+
+
+/* ===========================================================================
+ * Uncompress the given file and remove the original.
+ */
+void file_uncompress(file)
+ char *file;
+{
+ local char buf[MAX_NAME_LEN];
+ char *infile, *outfile;
+ FILE *out;
+ gzFile in;
+ uInt len = (uInt)strlen(file);
+
+ strcpy(buf, file);
+
+ if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
+ infile = file;
+ outfile = buf;
+ outfile[len-3] = '\0';
+ } else {
+ outfile = file;
+ infile = buf;
+ strcat(infile, GZ_SUFFIX);
+ }
+ in = gzopen(infile, "rb");
+ if (in == NULL) {
+ fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
+ exit(1);
+ }
+ out = fopen(outfile, "wb");
+ if (out == NULL) {
+ perror(file);
+ exit(1);
+ }
+
+ gz_uncompress(in, out);
+
+ unlink(infile);
+}
+
+
+/* ===========================================================================
+ * Usage: minigzip [-d] [-f] [-h] [-r] [-1 to -9] [files...]
+ * -d : decompress
+ * -f : compress with Z_FILTERED
+ * -h : compress with Z_HUFFMAN_ONLY
+ * -r : compress with Z_RLE
+ * -1 to -9 : compression level
+ */
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int uncompr = 0;
+ gzFile file;
+ char outmode[20];
+
+ strcpy(outmode, "wb6 ");
+
+ prog = argv[0];
+ argc--, argv++;
+
+ while (argc > 0) {
+ if (strcmp(*argv, "-d") == 0)
+ uncompr = 1;
+ else if (strcmp(*argv, "-f") == 0)
+ outmode[3] = 'f';
+ else if (strcmp(*argv, "-h") == 0)
+ outmode[3] = 'h';
+ else if (strcmp(*argv, "-r") == 0)
+ outmode[3] = 'R';
+ else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' &&
+ (*argv)[2] == 0)
+ outmode[2] = (*argv)[1];
+ else
+ break;
+ argc--, argv++;
+ }
+ if (argc == 0) {
+ SET_BINARY_MODE(stdin);
+ SET_BINARY_MODE(stdout);
+ if (uncompr) {
+ file = gzdopen(fileno(stdin), "rb");
+ if (file == NULL) error("can't gzdopen stdin");
+ gz_uncompress(file, stdout);
+ } else {
+ file = gzdopen(fileno(stdout), outmode);
+ if (file == NULL) error("can't gzdopen stdout");
+ gz_compress(stdin, file);
+ }
+ } else {
+ do {
+ if (uncompr) {
+ file_uncompress(*argv);
+ } else {
+ file_compress(*argv, outmode);
+ }
+ } while (argv++, --argc);
+ }
+ return 0;
+}
diff --git a/zlib/trees.c b/zlib/trees.c
new file mode 100644
index 0000000..26f3cf1
--- /dev/null
+++ b/zlib/trees.c
@@ -0,0 +1,1215 @@
+/* trees.c -- output deflated data using Huffman coding
+ * Copyright (C) 1995-2003 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process uses several Huffman trees. The more
+ * common source values are represented by shorter bit sequences.
+ *
+ * Each code tree is stored in a compressed form which is itself
+ * a Huffman encoding of the lengths of all the code strings (in
+ * ascending order by source values). The actual code strings are
+ * reconstructed from the lengths in the inflate process, as described
+ * in the deflate specification.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
+ * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
+ *
+ * Storer, James A.
+ * Data Compression: Methods and Theory, pp. 49-50.
+ * Computer Science Press, 1988. ISBN 0-7167-8156-5.
+ *
+ * Sedgewick, R.
+ * Algorithms, p290.
+ * Addison-Wesley, 1983. ISBN 0-201-06672-6.
+ */
+
+/* @(#) $Id$ */
+
+/* #define GEN_TREES_H */
+
+#include "deflate.h"
+
+#ifdef DEBUG
+# include <ctype.h>
+#endif
+
+/* ===========================================================================
+ * Constants
+ */
+
+#define MAX_BL_BITS 7
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+#define END_BLOCK 256
+/* end of block literal code */
+
+#define REP_3_6 16
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+
+#define REPZ_3_10 17
+/* repeat a zero length 3-10 times (3 bits of repeat count) */
+
+#define REPZ_11_138 18
+/* repeat a zero length 11-138 times (7 bits of repeat count) */
+
+local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
+ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
+
+local const int extra_dbits[D_CODES] /* extra bits for each distance code */
+ = {0,0,0,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};
+
+local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */
+ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
+
+local const uch bl_order[BL_CODES]
+ = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+#define Buf_size (8 * 2*sizeof(char))
+/* Number of bits used within bi_buf. (bi_buf might be implemented on
+ * more than 16 bits on some systems.)
+ */
+
+/* ===========================================================================
+ * Local data. These are initialized only once.
+ */
+
+#define DIST_CODE_LEN 512 /* see definition of array dist_code below */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+/* non ANSI compilers may not accept trees.h */
+
+local ct_data static_ltree[L_CODES+2];
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
+ * below).
+ */
+
+local ct_data static_dtree[D_CODES];
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+uch _dist_code[DIST_CODE_LEN];
+/* Distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+uch _length_code[MAX_MATCH-MIN_MATCH+1];
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+local int base_length[LENGTH_CODES];
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+local int base_dist[D_CODES];
+/* First normalized distance for each code (0 = distance of 1) */
+
+#else
+# include "trees.h"
+#endif /* GEN_TREES_H */
+
+struct static_tree_desc_s {
+ const ct_data *static_tree; /* static tree or NULL */
+ const intf *extra_bits; /* extra bits for each code or NULL */
+ int extra_base; /* base index for extra_bits */
+ int elems; /* max number of elements in the tree */
+ int max_length; /* max bit length for the codes */
+};
+
+local static_tree_desc static_l_desc =
+{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
+
+local static_tree_desc static_d_desc =
+{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS};
+
+local static_tree_desc static_bl_desc =
+{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS};
+
+/* ===========================================================================
+ * Local (static) routines in this file.
+ */
+
+local void tr_static_init OF((void));
+local void init_block OF((deflate_state *s));
+local void pqdownheap OF((deflate_state *s, ct_data *tree, int k));
+local void gen_bitlen OF((deflate_state *s, tree_desc *desc));
+local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count));
+local void build_tree OF((deflate_state *s, tree_desc *desc));
+local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local void send_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local int build_bl_tree OF((deflate_state *s));
+local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
+ int blcodes));
+local void compress_block OF((deflate_state *s, ct_data *ltree,
+ ct_data *dtree));
+local void set_data_type OF((deflate_state *s));
+local unsigned bi_reverse OF((unsigned value, int length));
+local void bi_windup OF((deflate_state *s));
+local void bi_flush OF((deflate_state *s));
+local void copy_block OF((deflate_state *s, charf *buf, unsigned len,
+ int header));
+
+#ifdef GEN_TREES_H
+local void gen_trees_header OF((void));
+#endif
+
+#ifndef DEBUG
+# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
+ /* Send a code of the given tree. c and tree must not have side effects */
+
+#else /* DEBUG */
+# define send_code(s, c, tree) \
+ { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \
+ send_bits(s, tree[c].Code, tree[c].Len); }
+#endif
+
+/* ===========================================================================
+ * Output a short LSB first on the stream.
+ * IN assertion: there is enough room in pendingBuf.
+ */
+#define put_short(s, w) { \
+ put_byte(s, (uch)((w) & 0xff)); \
+ put_byte(s, (uch)((ush)(w) >> 8)); \
+}
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+#ifdef DEBUG
+local void send_bits OF((deflate_state *s, int value, int length));
+
+local void send_bits(s, value, length)
+ deflate_state *s;
+ int value; /* value to send */
+ int length; /* number of bits */
+{
+ Tracevv((stderr," l %2d v %4x ", length, value));
+ Assert(length > 0 && length <= 15, "invalid length");
+ s->bits_sent += (ulg)length;
+
+ /* If not enough room in bi_buf, use (valid) bits from bi_buf and
+ * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+ * unused bits in value.
+ */
+ if (s->bi_valid > (int)Buf_size - length) {
+ s->bi_buf |= (value << s->bi_valid);
+ put_short(s, s->bi_buf);
+ s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
+ s->bi_valid += length - Buf_size;
+ } else {
+ s->bi_buf |= value << s->bi_valid;
+ s->bi_valid += length;
+ }
+}
+#else /* !DEBUG */
+
+#define send_bits(s, value, length) \
+{ int len = length;\
+ if (s->bi_valid > (int)Buf_size - len) {\
+ int val = value;\
+ s->bi_buf |= (val << s->bi_valid);\
+ put_short(s, s->bi_buf);\
+ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
+ s->bi_valid += len - Buf_size;\
+ } else {\
+ s->bi_buf |= (value) << s->bi_valid;\
+ s->bi_valid += len;\
+ }\
+}
+#endif /* DEBUG */
+
+
+/* the arguments must not have side effects */
+
+/* ===========================================================================
+ * Initialize the various 'constant' tables.
+ */
+local void tr_static_init()
+{
+#if defined(GEN_TREES_H) || !defined(STDC)
+ static int static_init_done = 0;
+ int n; /* iterates over tree elements */
+ int bits; /* bit counter */
+ int length; /* length value */
+ int code; /* code value */
+ int dist; /* distance index */
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ if (static_init_done) return;
+
+ /* For some embedded targets, global variables are not initialized: */
+ static_l_desc.static_tree = static_ltree;
+ static_l_desc.extra_bits = extra_lbits;
+ static_d_desc.static_tree = static_dtree;
+ static_d_desc.extra_bits = extra_dbits;
+ static_bl_desc.extra_bits = extra_blbits;
+
+ /* Initialize the mapping length (0..255) -> length code (0..28) */
+ length = 0;
+ for (code = 0; code < LENGTH_CODES-1; code++) {
+ base_length[code] = length;
+ for (n = 0; n < (1<<extra_lbits[code]); n++) {
+ _length_code[length++] = (uch)code;
+ }
+ }
+ Assert (length == 256, "tr_static_init: length != 256");
+ /* Note that the length 255 (match length 258) can be represented
+ * in two different ways: code 284 + 5 bits or code 285, so we
+ * overwrite length_code[255] to use the best encoding:
+ */
+ _length_code[length-1] = (uch)code;
+
+ /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+ dist = 0;
+ for (code = 0 ; code < 16; code++) {
+ base_dist[code] = dist;
+ for (n = 0; n < (1<<extra_dbits[code]); n++) {
+ _dist_code[dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "tr_static_init: dist != 256");
+ dist >>= 7; /* from now on, all distances are divided by 128 */
+ for ( ; code < D_CODES; code++) {
+ base_dist[code] = dist << 7;
+ for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
+ _dist_code[256 + dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "tr_static_init: 256+dist != 512");
+
+ /* Construct the codes of the static literal tree */
+ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
+ n = 0;
+ while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
+ while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
+ while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
+ while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
+ /* Codes 286 and 287 do not exist, but we must include them in the
+ * tree construction to get a canonical Huffman tree (longest code
+ * all ones)
+ */
+ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
+
+ /* The static distance tree is trivial: */
+ for (n = 0; n < D_CODES; n++) {
+ static_dtree[n].Len = 5;
+ static_dtree[n].Code = bi_reverse((unsigned)n, 5);
+ }
+ static_init_done = 1;
+
+# ifdef GEN_TREES_H
+ gen_trees_header();
+# endif
+#endif /* defined(GEN_TREES_H) || !defined(STDC) */
+}
+
+/* ===========================================================================
+ * Genererate the file trees.h describing the static trees.
+ */
+#ifdef GEN_TREES_H
+# ifndef DEBUG
+# include <stdio.h>
+# endif
+
+# define SEPARATOR(i, last, width) \
+ ((i) == (last)? "\n};\n\n" : \
+ ((i) % (width) == (width)-1 ? ",\n" : ", "))
+
+void gen_trees_header()
+{
+ FILE *header = fopen("trees.h", "w");
+ int i;
+
+ Assert (header != NULL, "Can't open trees.h");
+ fprintf(header,
+ "/* header created automatically with -DGEN_TREES_H */\n\n");
+
+ fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n");
+ for (i = 0; i < L_CODES+2; i++) {
+ fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code,
+ static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5));
+ }
+
+ fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n");
+ for (i = 0; i < D_CODES; i++) {
+ fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code,
+ static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5));
+ }
+
+ fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n");
+ for (i = 0; i < DIST_CODE_LEN; i++) {
+ fprintf(header, "%2u%s", _dist_code[i],
+ SEPARATOR(i, DIST_CODE_LEN-1, 20));
+ }
+
+ fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n");
+ for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) {
+ fprintf(header, "%2u%s", _length_code[i],
+ SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20));
+ }
+
+ fprintf(header, "local const int base_length[LENGTH_CODES] = {\n");
+ for (i = 0; i < LENGTH_CODES; i++) {
+ fprintf(header, "%1u%s", base_length[i],
+ SEPARATOR(i, LENGTH_CODES-1, 20));
+ }
+
+ fprintf(header, "local const int base_dist[D_CODES] = {\n");
+ for (i = 0; i < D_CODES; i++) {
+ fprintf(header, "%5u%s", base_dist[i],
+ SEPARATOR(i, D_CODES-1, 10));
+ }
+
+ fclose(header);
+}
+#endif /* GEN_TREES_H */
+
+/* ===========================================================================
+ * Initialize the tree data structures for a new zlib stream.
+ */
+void _tr_init(s)
+ deflate_state *s;
+{
+ tr_static_init();
+
+ s->l_desc.dyn_tree = s->dyn_ltree;
+ s->l_desc.stat_desc = &static_l_desc;
+
+ s->d_desc.dyn_tree = s->dyn_dtree;
+ s->d_desc.stat_desc = &static_d_desc;
+
+ s->bl_desc.dyn_tree = s->bl_tree;
+ s->bl_desc.stat_desc = &static_bl_desc;
+
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+#ifdef DEBUG
+ s->compressed_len = 0L;
+ s->bits_sent = 0L;
+#endif
+
+ /* Initialize the first block of the first file: */
+ init_block(s);
+}
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+local void init_block(s)
+ deflate_state *s;
+{
+ int n; /* iterates over tree elements */
+
+ /* Initialize the trees. */
+ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0;
+ for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0;
+ for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
+
+ s->dyn_ltree[END_BLOCK].Freq = 1;
+ s->opt_len = s->static_len = 0L;
+ s->last_lit = s->matches = 0;
+}
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+
+/* ===========================================================================
+ * Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len.
+ */
+#define pqremove(s, tree, top) \
+{\
+ top = s->heap[SMALLEST]; \
+ s->heap[SMALLEST] = s->heap[s->heap_len--]; \
+ pqdownheap(s, tree, SMALLEST); \
+}
+
+/* ===========================================================================
+ * Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length.
+ */
+#define smaller(tree, n, m, depth) \
+ (tree[n].Freq < tree[m].Freq || \
+ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+local void pqdownheap(s, tree, k)
+ deflate_state *s;
+ ct_data *tree; /* the tree to restore */
+ int k; /* node to move down */
+{
+ int v = s->heap[k];
+ int j = k << 1; /* left son of k */
+ while (j <= s->heap_len) {
+ /* Set j to the smallest of the two sons: */
+ if (j < s->heap_len &&
+ smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
+ j++;
+ }
+ /* Exit if v is smaller than both sons */
+ if (smaller(tree, v, s->heap[j], s->depth)) break;
+
+ /* Exchange v with the smallest son */
+ s->heap[k] = s->heap[j]; k = j;
+
+ /* And continue down the tree, setting j to the left son of k */
+ j <<= 1;
+ }
+ s->heap[k] = v;
+}
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ * above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ * array bl_count contains the frequencies for each bit length.
+ * The length opt_len is updated; static_len is also updated if stree is
+ * not null.
+ */
+local void gen_bitlen(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ int max_code = desc->max_code;
+ const ct_data *stree = desc->stat_desc->static_tree;
+ const intf *extra = desc->stat_desc->extra_bits;
+ int base = desc->stat_desc->extra_base;
+ int max_length = desc->stat_desc->max_length;
+ int h; /* heap index */
+ int n, m; /* iterate over the tree elements */
+ int bits; /* bit length */
+ int xbits; /* extra bits */
+ ush f; /* frequency */
+ int overflow = 0; /* number of elements with bit length too large */
+
+ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
+
+ /* In a first pass, compute the optimal bit lengths (which may
+ * overflow in the case of the bit length tree).
+ */
+ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
+
+ for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
+ n = s->heap[h];
+ bits = tree[tree[n].Dad].Len + 1;
+ if (bits > max_length) bits = max_length, overflow++;
+ tree[n].Len = (ush)bits;
+ /* We overwrite tree[n].Dad which is no longer needed */
+
+ if (n > max_code) continue; /* not a leaf node */
+
+ s->bl_count[bits]++;
+ xbits = 0;
+ if (n >= base) xbits = extra[n-base];
+ f = tree[n].Freq;
+ s->opt_len += (ulg)f * (bits + xbits);
+ if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits);
+ }
+ if (overflow == 0) return;
+
+ Trace((stderr,"\nbit length overflow\n"));
+ /* This happens for example on obj2 and pic of the Calgary corpus */
+
+ /* Find the first bit length which could increase: */
+ do {
+ bits = max_length-1;
+ while (s->bl_count[bits] == 0) bits--;
+ s->bl_count[bits]--; /* move one leaf down the tree */
+ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
+ s->bl_count[max_length]--;
+ /* The brother of the overflow item also moves one step up,
+ * but this does not affect bl_count[max_length]
+ */
+ overflow -= 2;
+ } while (overflow > 0);
+
+ /* Now recompute all bit lengths, scanning in increasing frequency.
+ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+ * lengths instead of fixing only the wrong ones. This idea is taken
+ * from 'ar' written by Haruhiko Okumura.)
+ */
+ for (bits = max_length; bits != 0; bits--) {
+ n = s->bl_count[bits];
+ while (n != 0) {
+ m = s->heap[--h];
+ if (m > max_code) continue;
+ if (tree[m].Len != (unsigned) bits) {
+ Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
+ s->opt_len += ((long)bits - (long)tree[m].Len)
+ *(long)tree[m].Freq;
+ tree[m].Len = (ush)bits;
+ }
+ n--;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ * zero code length.
+ */
+local void gen_codes (tree, max_code, bl_count)
+ ct_data *tree; /* the tree to decorate */
+ int max_code; /* largest code with non zero frequency */
+ ushf *bl_count; /* number of codes at each bit length */
+{
+ ush next_code[MAX_BITS+1]; /* next code value for each bit length */
+ ush code = 0; /* running code value */
+ int bits; /* bit index */
+ int n; /* code index */
+
+ /* The distribution counts are first used to generate the code values
+ * without bit reversal.
+ */
+ for (bits = 1; bits <= MAX_BITS; bits++) {
+ next_code[bits] = code = (code + bl_count[bits-1]) << 1;
+ }
+ /* Check that the bit counts in bl_count are consistent. The last code
+ * must be all ones.
+ */
+ Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
+ "inconsistent bit counts");
+ Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
+
+ for (n = 0; n <= max_code; n++) {
+ int len = tree[n].Len;
+ if (len == 0) continue;
+ /* Now reverse the bits */
+ tree[n].Code = bi_reverse(next_code[len]++, len);
+
+ Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
+ n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
+ }
+}
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ * and corresponding code. The length opt_len is updated; static_len is
+ * also updated if stree is not null. The field max_code is set.
+ */
+local void build_tree(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ const ct_data *stree = desc->stat_desc->static_tree;
+ int elems = desc->stat_desc->elems;
+ int n, m; /* iterate over heap elements */
+ int max_code = -1; /* largest code with non zero frequency */
+ int node; /* new node being created */
+
+ /* Construct the initial heap, with least frequent element in
+ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+ * heap[0] is not used.
+ */
+ s->heap_len = 0, s->heap_max = HEAP_SIZE;
+
+ for (n = 0; n < elems; n++) {
+ if (tree[n].Freq != 0) {
+ s->heap[++(s->heap_len)] = max_code = n;
+ s->depth[n] = 0;
+ } else {
+ tree[n].Len = 0;
+ }
+ }
+
+ /* The pkzip format requires that at least one distance code exists,
+ * and that at least one bit should be sent even if there is only one
+ * possible code. So to avoid special checks later on we force at least
+ * two codes of non zero frequency.
+ */
+ while (s->heap_len < 2) {
+ node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
+ tree[node].Freq = 1;
+ s->depth[node] = 0;
+ s->opt_len--; if (stree) s->static_len -= stree[node].Len;
+ /* node is 0 or 1 so it does not have extra bits */
+ }
+ desc->max_code = max_code;
+
+ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+ * establish sub-heaps of increasing lengths:
+ */
+ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
+
+ /* Construct the Huffman tree by repeatedly combining the least two
+ * frequent nodes.
+ */
+ node = elems; /* next internal node of the tree */
+ do {
+ pqremove(s, tree, n); /* n = node of least frequency */
+ m = s->heap[SMALLEST]; /* m = node of next least frequency */
+
+ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
+ s->heap[--(s->heap_max)] = m;
+
+ /* Create a new node father of n and m */
+ tree[node].Freq = tree[n].Freq + tree[m].Freq;
+ s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ?
+ s->depth[n] : s->depth[m]) + 1);
+ tree[n].Dad = tree[m].Dad = (ush)node;
+#ifdef DUMP_BL_TREE
+ if (tree == s->bl_tree) {
+ fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
+ node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
+ }
+#endif
+ /* and insert the new node in the heap */
+ s->heap[SMALLEST] = node++;
+ pqdownheap(s, tree, SMALLEST);
+
+ } while (s->heap_len >= 2);
+
+ s->heap[--(s->heap_max)] = s->heap[SMALLEST];
+
+ /* At this point, the fields freq and dad are set. We can now
+ * generate the bit lengths.
+ */
+ gen_bitlen(s, (tree_desc *)desc);
+
+ /* The field len is now set, we can generate the bit codes */
+ gen_codes ((ct_data *)tree, max_code, s->bl_count);
+}
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree.
+ */
+local void scan_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ if (nextlen == 0) max_count = 138, min_count = 3;
+ tree[max_code+1].Len = (ush)0xffff; /* guard */
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ s->bl_tree[curlen].Freq += count;
+ } else if (curlen != 0) {
+ if (curlen != prevlen) s->bl_tree[curlen].Freq++;
+ s->bl_tree[REP_3_6].Freq++;
+ } else if (count <= 10) {
+ s->bl_tree[REPZ_3_10].Freq++;
+ } else {
+ s->bl_tree[REPZ_11_138].Freq++;
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+local void send_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ /* tree[max_code+1].Len = -1; */ /* guard already set */
+ if (nextlen == 0) max_count = 138, min_count = 3;
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
+
+ } else if (curlen != 0) {
+ if (curlen != prevlen) {
+ send_code(s, curlen, s->bl_tree); count--;
+ }
+ Assert(count >= 3 && count <= 6, " 3_6?");
+ send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
+
+ } else if (count <= 10) {
+ send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
+
+ } else {
+ send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+local int build_bl_tree(s)
+ deflate_state *s;
+{
+ int max_blindex; /* index of last bit length code of non zero freq */
+
+ /* Determine the bit length frequencies for literal and distance trees */
+ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
+ scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
+
+ /* Build the bit length tree: */
+ build_tree(s, (tree_desc *)(&(s->bl_desc)));
+ /* opt_len now includes the length of the tree representations, except
+ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+ */
+
+ /* Determine the number of bit length codes to send. The pkzip format
+ * requires that at least 4 bit length codes be sent. (appnote.txt says
+ * 3 but the actual value used is 4.)
+ */
+ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
+ if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
+ }
+ /* Update opt_len to include the bit length tree and counts */
+ s->opt_len += 3*(max_blindex+1) + 5+5+4;
+ Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
+ s->opt_len, s->static_len));
+
+ return max_blindex;
+}
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+local void send_all_trees(s, lcodes, dcodes, blcodes)
+ deflate_state *s;
+ int lcodes, dcodes, blcodes; /* number of codes for each tree */
+{
+ int rank; /* index in bl_order */
+
+ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+ Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
+ "too many codes");
+ Tracev((stderr, "\nbl counts: "));
+ send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
+ send_bits(s, dcodes-1, 5);
+ send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */
+ for (rank = 0; rank < blcodes; rank++) {
+ Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+ send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
+ }
+ Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
+ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
+ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
+}
+
+/* ===========================================================================
+ * Send a stored block
+ */
+void _tr_stored_block(s, buf, stored_len, eof)
+ deflate_state *s;
+ charf *buf; /* input block */
+ ulg stored_len; /* length of input block */
+ int eof; /* true if this is the last block for a file */
+{
+ send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */
+#ifdef DEBUG
+ s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
+ s->compressed_len += (stored_len + 4) << 3;
+#endif
+ copy_block(s, buf, (unsigned)stored_len, 1); /* with header */
+}
+
+/* ===========================================================================
+ * Send one empty static block to give enough lookahead for inflate.
+ * This takes 10 bits, of which 7 may remain in the bit buffer.
+ * The current inflate code requires 9 bits of lookahead. If the
+ * last two codes for the previous block (real code plus EOB) were coded
+ * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode
+ * the last real code. In this case we send two empty static blocks instead
+ * of one. (There are no problems if the previous block is stored or fixed.)
+ * To simplify the code, we assume the worst case of last real code encoded
+ * on one bit only.
+ */
+void _tr_align(s)
+ deflate_state *s;
+{
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+#ifdef DEBUG
+ s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
+#endif
+ bi_flush(s);
+ /* Of the 10 bits for the empty block, we have already sent
+ * (10 - bi_valid) bits. The lookahead for the last real code (before
+ * the EOB of the previous block) was thus at least one plus the length
+ * of the EOB plus what we have just sent of the empty static block.
+ */
+ if (1 + s->last_eob_len + 10 - s->bi_valid < 9) {
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+#ifdef DEBUG
+ s->compressed_len += 10L;
+#endif
+ bi_flush(s);
+ }
+ s->last_eob_len = 7;
+}
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file.
+ */
+void _tr_flush_block(s, buf, stored_len, eof)
+ deflate_state *s;
+ charf *buf; /* input block, or NULL if too old */
+ ulg stored_len; /* length of input block */
+ int eof; /* true if this is the last block for a file */
+{
+ ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
+ int max_blindex = 0; /* index of last bit length code of non zero freq */
+
+ /* Build the Huffman trees unless a stored block is forced */
+ if (s->level > 0) {
+
+ /* Check if the file is ascii or binary */
+ if (s->strm->data_type == Z_UNKNOWN) set_data_type(s);
+
+ /* Construct the literal and distance trees */
+ build_tree(s, (tree_desc *)(&(s->l_desc)));
+ Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+
+ build_tree(s, (tree_desc *)(&(s->d_desc)));
+ Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+ /* At this point, opt_len and static_len are the total bit lengths of
+ * the compressed block data, excluding the tree representations.
+ */
+
+ /* Build the bit length tree for the above two trees, and get the index
+ * in bl_order of the last bit length code to send.
+ */
+ max_blindex = build_bl_tree(s);
+
+ /* Determine the best encoding. Compute the block lengths in bytes. */
+ opt_lenb = (s->opt_len+3+7)>>3;
+ static_lenb = (s->static_len+3+7)>>3;
+
+ Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
+ opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
+ s->last_lit));
+
+ if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
+
+ } else {
+ Assert(buf != (char*)0, "lost buf");
+ opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
+ }
+
+#ifdef FORCE_STORED
+ if (buf != (char*)0) { /* force stored block */
+#else
+ if (stored_len+4 <= opt_lenb && buf != (char*)0) {
+ /* 4: two words for the lengths */
+#endif
+ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+ * Otherwise we can't have processed more than WSIZE input bytes since
+ * the last block flush, because compression would have been
+ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+ * transform a block into a stored block.
+ */
+ _tr_stored_block(s, buf, stored_len, eof);
+
+#ifdef FORCE_STATIC
+ } else if (static_lenb >= 0) { /* force static trees */
+#else
+ } else if (static_lenb == opt_lenb) {
+#endif
+ send_bits(s, (STATIC_TREES<<1)+eof, 3);
+ compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree);
+#ifdef DEBUG
+ s->compressed_len += 3 + s->static_len;
+#endif
+ } else {
+ send_bits(s, (DYN_TREES<<1)+eof, 3);
+ send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
+ max_blindex+1);
+ compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree);
+#ifdef DEBUG
+ s->compressed_len += 3 + s->opt_len;
+#endif
+ }
+ Assert (s->compressed_len == s->bits_sent, "bad compressed size");
+ /* The above check is made mod 2^32, for files larger than 512 MB
+ * and uLong implemented on 32 bits.
+ */
+ init_block(s);
+
+ if (eof) {
+ bi_windup(s);
+#ifdef DEBUG
+ s->compressed_len += 7; /* align on byte boundary */
+#endif
+ }
+ Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
+ s->compressed_len-7*eof));
+}
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+int _tr_tally (s, dist, lc)
+ deflate_state *s;
+ unsigned dist; /* distance of matched string */
+ unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
+{
+ s->d_buf[s->last_lit] = (ush)dist;
+ s->l_buf[s->last_lit++] = (uch)lc;
+ if (dist == 0) {
+ /* lc is the unmatched char */
+ s->dyn_ltree[lc].Freq++;
+ } else {
+ s->matches++;
+ /* Here, lc is the match length - MIN_MATCH */
+ dist--; /* dist = match distance - 1 */
+ Assert((ush)dist < (ush)MAX_DIST(s) &&
+ (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
+ (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match");
+
+ s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;
+ s->dyn_dtree[d_code(dist)].Freq++;
+ }
+
+#ifdef TRUNCATE_BLOCK
+ /* Try to guess if it is profitable to stop the current block here */
+ if ((s->last_lit & 0x1fff) == 0 && s->level > 2) {
+ /* Compute an upper bound for the compressed length */
+ ulg out_length = (ulg)s->last_lit*8L;
+ ulg in_length = (ulg)((long)s->strstart - s->block_start);
+ int dcode;
+ for (dcode = 0; dcode < D_CODES; dcode++) {
+ out_length += (ulg)s->dyn_dtree[dcode].Freq *
+ (5L+extra_dbits[dcode]);
+ }
+ out_length >>= 3;
+ Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
+ s->last_lit, in_length, out_length,
+ 100L - out_length*100L/in_length));
+ if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
+ }
+#endif
+ return (s->last_lit == s->lit_bufsize-1);
+ /* We avoid equality with lit_bufsize because of wraparound at 64K
+ * on 16 bit machines and because stored blocks are restricted to
+ * 64K-1 bytes.
+ */
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+local void compress_block(s, ltree, dtree)
+ deflate_state *s;
+ ct_data *ltree; /* literal tree */
+ ct_data *dtree; /* distance tree */
+{
+ unsigned dist; /* distance of matched string */
+ int lc; /* match length or unmatched char (if dist == 0) */
+ unsigned lx = 0; /* running index in l_buf */
+ unsigned code; /* the code to send */
+ int extra; /* number of extra bits to send */
+
+ if (s->last_lit != 0) do {
+ dist = s->d_buf[lx];
+ lc = s->l_buf[lx++];
+ if (dist == 0) {
+ send_code(s, lc, ltree); /* send a literal byte */
+ Tracecv(isgraph(lc), (stderr," '%c' ", lc));
+ } else {
+ /* Here, lc is the match length - MIN_MATCH */
+ code = _length_code[lc];
+ send_code(s, code+LITERALS+1, ltree); /* send the length code */
+ extra = extra_lbits[code];
+ if (extra != 0) {
+ lc -= base_length[code];
+ send_bits(s, lc, extra); /* send the extra length bits */
+ }
+ dist--; /* dist is now the match distance - 1 */
+ code = d_code(dist);
+ Assert (code < D_CODES, "bad d_code");
+
+ send_code(s, code, dtree); /* send the distance code */
+ extra = extra_dbits[code];
+ if (extra != 0) {
+ dist -= base_dist[code];
+ send_bits(s, dist, extra); /* send the extra distance bits */
+ }
+ } /* literal or match pair ? */
+
+ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
+ Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
+ "pendingBuf overflow");
+
+ } while (lx < s->last_lit);
+
+ send_code(s, END_BLOCK, ltree);
+ s->last_eob_len = ltree[END_BLOCK].Len;
+}
+
+/* ===========================================================================
+ * Set the data type to ASCII or BINARY, using a crude approximation:
+ * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise.
+ * IN assertion: the fields freq of dyn_ltree are set and the total of all
+ * frequencies does not exceed 64K (to fit in an int on 16 bit machines).
+ */
+local void set_data_type(s)
+ deflate_state *s;
+{
+ int n = 0;
+ unsigned ascii_freq = 0;
+ unsigned bin_freq = 0;
+ while (n < 7) bin_freq += s->dyn_ltree[n++].Freq;
+ while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq;
+ while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq;
+ s->strm->data_type = bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII;
+}
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+local unsigned bi_reverse(code, len)
+ unsigned code; /* the value to invert */
+ int len; /* its bit length */
+{
+ register unsigned res = 0;
+ do {
+ res |= code & 1;
+ code >>= 1, res <<= 1;
+ } while (--len > 0);
+ return res >> 1;
+}
+
+/* ===========================================================================
+ * Flush the bit buffer, keeping at most 7 bits in it.
+ */
+local void bi_flush(s)
+ deflate_state *s;
+{
+ if (s->bi_valid == 16) {
+ put_short(s, s->bi_buf);
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ } else if (s->bi_valid >= 8) {
+ put_byte(s, (Byte)s->bi_buf);
+ s->bi_buf >>= 8;
+ s->bi_valid -= 8;
+ }
+}
+
+/* ===========================================================================
+ * Flush the bit buffer and align the output on a byte boundary
+ */
+local void bi_windup(s)
+ deflate_state *s;
+{
+ if (s->bi_valid > 8) {
+ put_short(s, s->bi_buf);
+ } else if (s->bi_valid > 0) {
+ put_byte(s, (Byte)s->bi_buf);
+ }
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+#ifdef DEBUG
+ s->bits_sent = (s->bits_sent+7) & ~7;
+#endif
+}
+
+/* ===========================================================================
+ * Copy a stored block, storing first the length and its
+ * one's complement if requested.
+ */
+local void copy_block(s, buf, len, header)
+ deflate_state *s;
+ charf *buf; /* the input data */
+ unsigned len; /* its length */
+ int header; /* true if block header must be written */
+{
+ bi_windup(s); /* align on byte boundary */
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+
+ if (header) {
+ put_short(s, (ush)len);
+ put_short(s, (ush)~len);
+#ifdef DEBUG
+ s->bits_sent += 2*16;
+#endif
+ }
+#ifdef DEBUG
+ s->bits_sent += (ulg)len<<3;
+#endif
+ while (len--) {
+ put_byte(s, *buf++);
+ }
+}
diff --git a/zlib/trees.h b/zlib/trees.h
new file mode 100644
index 0000000..1ca868b
--- /dev/null
+++ b/zlib/trees.h
@@ -0,0 +1,128 @@
+/* header created automatically with -DGEN_TREES_H */
+
+local const ct_data static_ltree[L_CODES+2] = {
+{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}},
+{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}},
+{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}},
+{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}},
+{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}},
+{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}},
+{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}},
+{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}},
+{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}},
+{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}},
+{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}},
+{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}},
+{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}},
+{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}},
+{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}},
+{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}},
+{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}},
+{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}},
+{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}},
+{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}},
+{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}},
+{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}},
+{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}},
+{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}},
+{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}},
+{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}},
+{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}},
+{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}},
+{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}},
+{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}},
+{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}},
+{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}},
+{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}},
+{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}},
+{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}},
+{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}},
+{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}},
+{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}},
+{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}},
+{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}},
+{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}},
+{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}},
+{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}},
+{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}},
+{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}},
+{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}},
+{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}},
+{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}},
+{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}},
+{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}},
+{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}},
+{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}},
+{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}},
+{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}},
+{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}},
+{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}},
+{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}},
+{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}}
+};
+
+local const ct_data static_dtree[D_CODES] = {
+{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},
+{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},
+{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},
+{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},
+{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},
+{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}
+};
+
+const uch _dist_code[DIST_CODE_LEN] = {
+ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8,
+ 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10,
+10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17,
+18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
+};
+
+const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12,
+13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
+17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
+19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
+22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
+};
+
+local const int base_length[LENGTH_CODES] = {
+0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
+64, 80, 96, 112, 128, 160, 192, 224, 0
+};
+
+local const int base_dist[D_CODES] = {
+ 0, 1, 2, 3, 4, 6, 8, 12, 16, 24,
+ 32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
+ 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576
+};
+
diff --git a/zlib/uncompr.c b/zlib/uncompr.c
new file mode 100644
index 0000000..ad6db0a
--- /dev/null
+++ b/zlib/uncompr.c
@@ -0,0 +1,61 @@
+/* uncompr.c -- decompress a memory buffer
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+/* ===========================================================================
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be large enough to hold the
+ entire uncompressed data. (The size of the uncompressed data must have
+ been saved previously by the compressor and transmitted to the decompressor
+ by some mechanism outside the scope of this compression library.)
+ Upon exit, destLen is the actual size of the compressed buffer.
+ This function can be used to decompress a whole file at once if the
+ input file is mmap'ed.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted.
+*/
+int ZEXPORT uncompress (dest, destLen, source, sourceLen)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+{
+ z_stream stream;
+ int err;
+
+ stream.next_in = (Bytef*)source;
+ stream.avail_in = (uInt)sourceLen;
+ /* Check for source > 64K on 16-bit machine: */
+ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+
+ stream.next_out = dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+
+ err = inflateInit(&stream);
+ if (err != Z_OK) return err;
+
+ err = inflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ inflateEnd(&stream);
+ if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
+ return Z_DATA_ERROR;
+ return err;
+ }
+ *destLen = stream.total_out;
+
+ err = inflateEnd(&stream);
+ return err;
+}
diff --git a/zlib/zconf.h b/zlib/zconf.h
new file mode 100644
index 0000000..b849dbb
--- /dev/null
+++ b/zlib/zconf.h
@@ -0,0 +1,326 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2004 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZCONF_H
+#define ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ */
+#ifdef Z_PREFIX
+# define deflateInit_ z_deflateInit_
+# define deflate z_deflate
+# define deflateEnd z_deflateEnd
+# define inflateInit_ z_inflateInit_
+# define inflate z_inflate
+# define inflateEnd z_inflateEnd
+# define deflateInit2_ z_deflateInit2_
+# define deflateSetDictionary z_deflateSetDictionary
+# define deflateCopy z_deflateCopy
+# define deflateReset z_deflateReset
+# define deflateParams z_deflateParams
+# define deflateBound z_deflateBound
+# define deflatePrime z_deflatePrime
+# define inflateInit2_ z_inflateInit2_
+# define inflateSetDictionary z_inflateSetDictionary
+# define inflateSync z_inflateSync
+# define inflateSyncPoint z_inflateSyncPoint
+# define inflateCopy z_inflateCopy
+# define inflateReset z_inflateReset
+# define inflateBack z_inflateBack
+# define inflateBackEnd z_inflateBackEnd
+# define compress z_compress
+# define compress2 z_compress2
+# define compressBound z_compressBound
+# define uncompress z_uncompress
+# define adler32 z_adler32
+# define crc32 z_crc32
+# define get_crc_table z_get_crc_table
+# define zError z_zError
+
+# define Byte z_Byte
+# define uInt z_uInt
+# define uLong z_uLong
+# define Bytef z_Bytef
+# define charf z_charf
+# define intf z_intf
+# define uIntf z_uIntf
+# define uLongf z_uLongf
+# define voidpf z_voidpf
+# define voidp z_voidp
+#endif
+
+#if defined(__MSDOS__) && !defined(MSDOS)
+# define MSDOS
+#endif
+#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
+# define OS2
+#endif
+#if defined(_WINDOWS) && !defined(WINDOWS)
+# define WINDOWS
+#endif
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+# define WIN32
+#endif
+#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
+# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
+# ifndef SYS16BIT
+# define SYS16BIT
+# endif
+# endif
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#ifdef SYS16BIT
+# define MAXSEG_64K
+#endif
+#ifdef MSDOS
+# define UNALIGNED_OK
+#endif
+
+#ifdef __STDC_VERSION__
+# ifndef STDC
+# define STDC
+# endif
+# if __STDC_VERSION__ >= 199901L
+# ifndef STDC99
+# define STDC99
+# endif
+# endif
+#endif
+#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
+# define STDC
+#endif
+
+#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */
+# define STDC
+#endif
+
+#ifndef STDC
+# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+# define const /* note: need a more gentle solution here */
+# endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
+# define NO_DUMMY_DECL
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# ifdef MAXSEG_64K
+# define MAX_MEM_LEVEL 8
+# else
+# define MAX_MEM_LEVEL 9
+# endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ (1 << (windowBits+2)) + (1 << (memLevel+9))
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#ifdef SYS16BIT
+# if defined(M_I86SM) || defined(M_I86MM)
+ /* MSC small or medium model */
+# define SMALL_MEDIUM
+# ifdef _MSC_VER
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+# if (defined(__SMALL__) || defined(__MEDIUM__))
+ /* Turbo C small or medium model */
+# define SMALL_MEDIUM
+# ifdef __BORLANDC__
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+#endif
+
+#if defined(WINDOWS) || defined(WIN32)
+ /* If building or using zlib as a DLL, define ZLIB_DLL.
+ * This is not mandatory, but it offers a little performance increase.
+ */
+# ifdef ZLIB_DLL
+# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
+# ifdef ZLIB_INTERNAL
+# define ZEXTERN extern __declspec(dllexport)
+# else
+# define ZEXTERN extern __declspec(dllimport)
+# endif
+# endif
+# endif /* ZLIB_DLL */
+ /* If building or using zlib with the WINAPI/WINAPIV calling convention,
+ * define ZLIB_WINAPI.
+ * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
+ */
+# ifdef ZLIB_WINAPI
+# ifdef FAR
+# undef FAR
+# endif
+# include <windows.h>
+ /* No need for _export, use ZLIB.DEF instead. */
+ /* For complete Windows compatibility, use WINAPI, not __stdcall. */
+# define ZEXPORT WINAPI
+# ifdef WIN32
+# define ZEXPORTVA WINAPIV
+# else
+# define ZEXPORTVA FAR CDECL
+# endif
+# endif
+#endif
+
+#if defined (__BEOS__)
+# ifdef ZLIB_DLL
+# ifdef ZLIB_INTERNAL
+# define ZEXPORT __declspec(dllexport)
+# define ZEXPORTVA __declspec(dllexport)
+# else
+# define ZEXPORT __declspec(dllimport)
+# define ZEXPORTVA __declspec(dllimport)
+# endif
+# endif
+#endif
+
+#ifndef ZEXTERN
+# define ZEXTERN extern
+#endif
+#ifndef ZEXPORT
+# define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+# define ZEXPORTVA
+#endif
+
+#ifndef FAR
+# define FAR
+#endif
+
+#if !defined(__MACTYPES__)
+typedef unsigned char Byte; /* 8 bits */
+#endif
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+ /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+# define Bytef Byte FAR
+#else
+ typedef Byte FAR Bytef;
+#endif
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+ typedef void const *voidpc;
+ typedef void FAR *voidpf;
+ typedef void *voidp;
+#else
+ typedef Byte const *voidpc;
+ typedef Byte FAR *voidpf;
+ typedef Byte *voidp;
+#endif
+
+#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */
+# include <sys/types.h> /* for off_t */
+# include <unistd.h> /* for SEEK_* and off_t */
+# ifdef VMS
+# include <unixio.h> /* for off_t */
+# endif
+# define z_off_t off_t
+#endif
+#ifndef SEEK_SET
+# define SEEK_SET 0 /* Seek from beginning of file. */
+# define SEEK_CUR 1 /* Seek from current position. */
+# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
+#endif
+#ifndef z_off_t
+# define z_off_t long
+#endif
+
+#if defined(__OS400__)
+# define NO_vsnprintf
+#endif
+
+#if defined(__MVS__)
+# define NO_vsnprintf
+# ifdef FAR
+# undef FAR
+# endif
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+# pragma map(deflateInit_,"DEIN")
+# pragma map(deflateInit2_,"DEIN2")
+# pragma map(deflateEnd,"DEEND")
+# pragma map(deflateBound,"DEBND")
+# pragma map(inflateInit_,"ININ")
+# pragma map(inflateInit2_,"ININ2")
+# pragma map(inflateEnd,"INEND")
+# pragma map(inflateSync,"INSY")
+# pragma map(inflateSetDictionary,"INSEDI")
+# pragma map(compressBound,"CMBND")
+# pragma map(inflate_table,"INTABL")
+# pragma map(inflate_fast,"INFA")
+# pragma map(inflate_copyright,"INCOPY")
+#endif
+
+#endif /* ZCONF_H */
diff --git a/zlib/zconf.in.h b/zlib/zconf.in.h
new file mode 100644
index 0000000..b849dbb
--- /dev/null
+++ b/zlib/zconf.in.h
@@ -0,0 +1,326 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2004 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZCONF_H
+#define ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ */
+#ifdef Z_PREFIX
+# define deflateInit_ z_deflateInit_
+# define deflate z_deflate
+# define deflateEnd z_deflateEnd
+# define inflateInit_ z_inflateInit_
+# define inflate z_inflate
+# define inflateEnd z_inflateEnd
+# define deflateInit2_ z_deflateInit2_
+# define deflateSetDictionary z_deflateSetDictionary
+# define deflateCopy z_deflateCopy
+# define deflateReset z_deflateReset
+# define deflateParams z_deflateParams
+# define deflateBound z_deflateBound
+# define deflatePrime z_deflatePrime
+# define inflateInit2_ z_inflateInit2_
+# define inflateSetDictionary z_inflateSetDictionary
+# define inflateSync z_inflateSync
+# define inflateSyncPoint z_inflateSyncPoint
+# define inflateCopy z_inflateCopy
+# define inflateReset z_inflateReset
+# define inflateBack z_inflateBack
+# define inflateBackEnd z_inflateBackEnd
+# define compress z_compress
+# define compress2 z_compress2
+# define compressBound z_compressBound
+# define uncompress z_uncompress
+# define adler32 z_adler32
+# define crc32 z_crc32
+# define get_crc_table z_get_crc_table
+# define zError z_zError
+
+# define Byte z_Byte
+# define uInt z_uInt
+# define uLong z_uLong
+# define Bytef z_Bytef
+# define charf z_charf
+# define intf z_intf
+# define uIntf z_uIntf
+# define uLongf z_uLongf
+# define voidpf z_voidpf
+# define voidp z_voidp
+#endif
+
+#if defined(__MSDOS__) && !defined(MSDOS)
+# define MSDOS
+#endif
+#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
+# define OS2
+#endif
+#if defined(_WINDOWS) && !defined(WINDOWS)
+# define WINDOWS
+#endif
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+# define WIN32
+#endif
+#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
+# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
+# ifndef SYS16BIT
+# define SYS16BIT
+# endif
+# endif
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#ifdef SYS16BIT
+# define MAXSEG_64K
+#endif
+#ifdef MSDOS
+# define UNALIGNED_OK
+#endif
+
+#ifdef __STDC_VERSION__
+# ifndef STDC
+# define STDC
+# endif
+# if __STDC_VERSION__ >= 199901L
+# ifndef STDC99
+# define STDC99
+# endif
+# endif
+#endif
+#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
+# define STDC
+#endif
+
+#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */
+# define STDC
+#endif
+
+#ifndef STDC
+# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+# define const /* note: need a more gentle solution here */
+# endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
+# define NO_DUMMY_DECL
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# ifdef MAXSEG_64K
+# define MAX_MEM_LEVEL 8
+# else
+# define MAX_MEM_LEVEL 9
+# endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ (1 << (windowBits+2)) + (1 << (memLevel+9))
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#ifdef SYS16BIT
+# if defined(M_I86SM) || defined(M_I86MM)
+ /* MSC small or medium model */
+# define SMALL_MEDIUM
+# ifdef _MSC_VER
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+# if (defined(__SMALL__) || defined(__MEDIUM__))
+ /* Turbo C small or medium model */
+# define SMALL_MEDIUM
+# ifdef __BORLANDC__
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+#endif
+
+#if defined(WINDOWS) || defined(WIN32)
+ /* If building or using zlib as a DLL, define ZLIB_DLL.
+ * This is not mandatory, but it offers a little performance increase.
+ */
+# ifdef ZLIB_DLL
+# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
+# ifdef ZLIB_INTERNAL
+# define ZEXTERN extern __declspec(dllexport)
+# else
+# define ZEXTERN extern __declspec(dllimport)
+# endif
+# endif
+# endif /* ZLIB_DLL */
+ /* If building or using zlib with the WINAPI/WINAPIV calling convention,
+ * define ZLIB_WINAPI.
+ * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
+ */
+# ifdef ZLIB_WINAPI
+# ifdef FAR
+# undef FAR
+# endif
+# include <windows.h>
+ /* No need for _export, use ZLIB.DEF instead. */
+ /* For complete Windows compatibility, use WINAPI, not __stdcall. */
+# define ZEXPORT WINAPI
+# ifdef WIN32
+# define ZEXPORTVA WINAPIV
+# else
+# define ZEXPORTVA FAR CDECL
+# endif
+# endif
+#endif
+
+#if defined (__BEOS__)
+# ifdef ZLIB_DLL
+# ifdef ZLIB_INTERNAL
+# define ZEXPORT __declspec(dllexport)
+# define ZEXPORTVA __declspec(dllexport)
+# else
+# define ZEXPORT __declspec(dllimport)
+# define ZEXPORTVA __declspec(dllimport)
+# endif
+# endif
+#endif
+
+#ifndef ZEXTERN
+# define ZEXTERN extern
+#endif
+#ifndef ZEXPORT
+# define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+# define ZEXPORTVA
+#endif
+
+#ifndef FAR
+# define FAR
+#endif
+
+#if !defined(__MACTYPES__)
+typedef unsigned char Byte; /* 8 bits */
+#endif
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+ /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+# define Bytef Byte FAR
+#else
+ typedef Byte FAR Bytef;
+#endif
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+ typedef void const *voidpc;
+ typedef void FAR *voidpf;
+ typedef void *voidp;
+#else
+ typedef Byte const *voidpc;
+ typedef Byte FAR *voidpf;
+ typedef Byte *voidp;
+#endif
+
+#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */
+# include <sys/types.h> /* for off_t */
+# include <unistd.h> /* for SEEK_* and off_t */
+# ifdef VMS
+# include <unixio.h> /* for off_t */
+# endif
+# define z_off_t off_t
+#endif
+#ifndef SEEK_SET
+# define SEEK_SET 0 /* Seek from beginning of file. */
+# define SEEK_CUR 1 /* Seek from current position. */
+# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
+#endif
+#ifndef z_off_t
+# define z_off_t long
+#endif
+
+#if defined(__OS400__)
+# define NO_vsnprintf
+#endif
+
+#if defined(__MVS__)
+# define NO_vsnprintf
+# ifdef FAR
+# undef FAR
+# endif
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+# pragma map(deflateInit_,"DEIN")
+# pragma map(deflateInit2_,"DEIN2")
+# pragma map(deflateEnd,"DEEND")
+# pragma map(deflateBound,"DEBND")
+# pragma map(inflateInit_,"ININ")
+# pragma map(inflateInit2_,"ININ2")
+# pragma map(inflateEnd,"INEND")
+# pragma map(inflateSync,"INSY")
+# pragma map(inflateSetDictionary,"INSEDI")
+# pragma map(compressBound,"CMBND")
+# pragma map(inflate_table,"INTABL")
+# pragma map(inflate_fast,"INFA")
+# pragma map(inflate_copyright,"INCOPY")
+#endif
+
+#endif /* ZCONF_H */
diff --git a/zlib/zlib.3 b/zlib/zlib.3
new file mode 100644
index 0000000..21a03fc
--- /dev/null
+++ b/zlib/zlib.3
@@ -0,0 +1,159 @@
+.TH ZLIB 3 "3 October 2004"
+.SH NAME
+zlib \- compression/decompression library
+.SH SYNOPSIS
+[see
+.I zlib.h
+for full description]
+.SH DESCRIPTION
+The
+.I zlib
+library is a general purpose data compression library.
+The code is thread safe.
+It provides in-memory compression and decompression functions,
+including integrity checks of the uncompressed data.
+This version of the library supports only one compression method (deflation)
+but other algorithms will be added later
+and will have the same stream interface.
+.LP
+Compression can be done in a single step if the buffers are large enough
+(for example if an input file is mmap'ed),
+or can be done by repeated calls of the compression function.
+In the latter case,
+the application must provide more input and/or consume the output
+(providing more output space) before each call.
+.LP
+The library also supports reading and writing files in
+.IR gzip (1)
+(.gz) format
+with an interface similar to that of stdio.
+.LP
+The library does not install any signal handler.
+The decoder checks the consistency of the compressed data,
+so the library should never crash even in case of corrupted input.
+.LP
+All functions of the compression library are documented in the file
+.IR zlib.h .
+The distribution source includes examples of use of the library
+in the files
+.I example.c
+and
+.IR minigzip.c .
+.LP
+Changes to this version are documented in the file
+.I ChangeLog
+that accompanies the source,
+and are concerned primarily with bug fixes and portability enhancements.
+.LP
+A Java implementation of
+.I zlib
+is available in the Java Development Kit 1.1:
+.IP
+http://www.javasoft.com/products/JDK/1.1/docs/api/Package-java.util.zip.html
+.LP
+A Perl interface to
+.IR zlib ,
+written by Paul Marquess (pmqs@cpan.org),
+is available at CPAN (Comprehensive Perl Archive Network) sites,
+including:
+.IP
+http://www.cpan.org/modules/by-module/Compress/
+.LP
+A Python interface to
+.IR zlib ,
+written by A.M. Kuchling (amk@magnet.com),
+is available in Python 1.5 and later versions:
+.IP
+http://www.python.org/doc/lib/module-zlib.html
+.LP
+A
+.I zlib
+binding for
+.IR tcl (1),
+written by Andreas Kupries (a.kupries@westend.com),
+is availlable at:
+.IP
+http://www.westend.com/~kupries/doc/trf/man/man.html
+.LP
+An experimental package to read and write files in .zip format,
+written on top of
+.I zlib
+by Gilles Vollant (info@winimage.com),
+is available at:
+.IP
+http://www.winimage.com/zLibDll/unzip.html
+and also in the
+.I contrib/minizip
+directory of the main
+.I zlib
+web site.
+.SH "SEE ALSO"
+The
+.I zlib
+web site can be found at either of these locations:
+.IP
+http://www.zlib.org
+.br
+http://www.gzip.org/zlib/
+.LP
+The data format used by the zlib library is described by RFC
+(Request for Comments) 1950 to 1952 in the files:
+.IP
+http://www.ietf.org/rfc/rfc1950.txt (concerning zlib format)
+.br
+http://www.ietf.org/rfc/rfc1951.txt (concerning deflate format)
+.br
+http://www.ietf.org/rfc/rfc1952.txt (concerning gzip format)
+.LP
+These documents are also available in other formats from:
+.IP
+ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html
+.LP
+Mark Nelson (markn@ieee.org) wrote an article about
+.I zlib
+for the Jan. 1997 issue of Dr. Dobb's Journal;
+a copy of the article is available at:
+.IP
+http://dogma.net/markn/articles/zlibtool/zlibtool.htm
+.SH "REPORTING PROBLEMS"
+Before reporting a problem,
+please check the
+.I zlib
+web site to verify that you have the latest version of
+.IR zlib ;
+otherwise,
+obtain the latest version and see if the problem still exists.
+Please read the
+.I zlib
+FAQ at:
+.IP
+http://www.gzip.org/zlib/zlib_faq.html
+.LP
+before asking for help.
+Send questions and/or comments to zlib@gzip.org,
+or (for the Windows DLL version) to Gilles Vollant (info@winimage.com).
+.SH AUTHORS
+Version 1.2.2
+Copyright (C) 1995-2004 Jean-loup Gailly (jloup@gzip.org)
+and Mark Adler (madler@alumni.caltech.edu).
+.LP
+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.
+See the distribution directory with respect to requirements
+governing redistribution.
+The deflate format used by
+.I zlib
+was defined by Phil Katz.
+The deflate and
+.I zlib
+specifications were written by L. Peter Deutsch.
+Thanks to all the people who reported problems and suggested various
+improvements in
+.IR zlib ;
+who are too numerous to cite here.
+.LP
+UNIX manual page by R. P. C. Rodgers,
+U.S. National Library of Medicine (rodgers@nlm.nih.gov).
+.\" end of man page
diff --git a/zlib/zlib.h b/zlib/zlib.h
new file mode 100644
index 0000000..e067b12
--- /dev/null
+++ b/zlib/zlib.h
@@ -0,0 +1,1200 @@
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+ version 1.2.2, October 3rd, 2004
+
+ Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler
+
+ 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.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt
+ (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+*/
+
+#ifndef ZLIB_H
+#define ZLIB_H
+
+#include "zconf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_VERSION "1.2.2"
+#define ZLIB_VERNUM 0x1220
+
+/*
+ The 'zlib' compression library provides in-memory compression and
+ decompression functions, including integrity checks of the uncompressed
+ data. This version of the library supports only one compression method
+ (deflation) but other algorithms will be added later and will have the same
+ stream interface.
+
+ Compression can be done in a single step if the buffers are large
+ enough (for example if an input file is mmap'ed), or can be done by
+ repeated calls of the compression function. In the latter case, the
+ application must provide more input and/or consume the output
+ (providing more output space) before each call.
+
+ The compressed data format used by default by the in-memory functions is
+ the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped
+ around a deflate stream, which is itself documented in RFC 1951.
+
+ The library also supports reading and writing files in gzip (.gz) format
+ with an interface similar to that of stdio using the functions that start
+ with "gz". The gzip format is different from the zlib format. gzip is a
+ gzip wrapper, documented in RFC 1952, wrapped around a deflate stream.
+
+ This library can optionally read and write gzip streams in memory as well.
+
+ The zlib format was designed to be compact and fast for use in memory
+ and on communications channels. The gzip format was designed for single-
+ file compression on file systems, has a larger header than zlib to maintain
+ directory information, and uses a different, slower check method than zlib.
+
+ The library does not install any signal handler. The decoder checks
+ the consistency of the compressed data, so the library should never
+ crash even in case of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void (*free_func) OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+ Bytef *next_in; /* next input byte */
+ uInt avail_in; /* number of bytes available at next_in */
+ uLong total_in; /* total nb of input bytes read so far */
+
+ Bytef *next_out; /* next output byte should be put there */
+ uInt avail_out; /* remaining free space at next_out */
+ uLong total_out; /* total nb of bytes output so far */
+
+ char *msg; /* last error message, NULL if no error */
+ struct internal_state FAR *state; /* not visible by applications */
+
+ alloc_func zalloc; /* used to allocate the internal state */
+ free_func zfree; /* used to free the internal state */
+ voidpf opaque; /* private data object passed to zalloc and zfree */
+
+ int data_type; /* best guess about the data type: ascii or binary */
+ uLong adler; /* adler32 value of the uncompressed data */
+ uLong reserved; /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+ The application must update next_in and avail_in when avail_in has
+ dropped to zero. It must update next_out and avail_out when avail_out
+ has dropped to zero. The application must initialize zalloc, zfree and
+ opaque before calling the init function. All other fields are set by the
+ compression library and must not be updated by the application.
+
+ The opaque value provided by the application will be passed as the first
+ parameter for calls of zalloc and zfree. This can be useful for custom
+ memory management. The compression library attaches no meaning to the
+ opaque value.
+
+ zalloc must return Z_NULL if there is not enough memory for the object.
+ If zlib is used in a multi-threaded application, zalloc and zfree must be
+ thread safe.
+
+ On 16-bit systems, the functions zalloc and zfree must be able to allocate
+ exactly 65536 bytes, but will not be required to allocate more than this
+ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+ pointers returned by zalloc for objects of exactly 65536 bytes *must*
+ have their offset normalized to zero. The default allocation function
+ provided by this library ensures this (see zutil.c). To reduce memory
+ requirements and avoid any allocation of 64K objects, at the expense of
+ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+ The fields total_in and total_out can be used for statistics or
+ progress reports. After compression, total_in holds the total size of
+ the uncompressed data and may be saved for use in the decompressor
+ (particularly if the decompressor wants to decompress everything in
+ a single step).
+*/
+
+ /* constants */
+
+#define Z_NO_FLUSH 0
+#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */
+#define Z_SYNC_FLUSH 2
+#define Z_FULL_FLUSH 3
+#define Z_FINISH 4
+#define Z_BLOCK 5
+/* Allowed flush values; see deflate() and inflate() below for details */
+
+#define Z_OK 0
+#define Z_STREAM_END 1
+#define Z_NEED_DICT 2
+#define Z_ERRNO (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR (-3)
+#define Z_MEM_ERROR (-4)
+#define Z_BUF_ERROR (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative
+ * values are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION 0
+#define Z_BEST_SPEED 1
+#define Z_BEST_COMPRESSION 9
+#define Z_DEFAULT_COMPRESSION (-1)
+/* compression levels */
+
+#define Z_FILTERED 1
+#define Z_HUFFMAN_ONLY 2
+#define Z_RLE 3
+#define Z_DEFAULT_STRATEGY 0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY 0
+#define Z_ASCII 1
+#define Z_UNKNOWN 2
+/* Possible values of the data_type field (though see inflate()) */
+
+#define Z_DEFLATED 8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+ /* basic functions */
+
+ZEXTERN const char * ZEXPORT zlibVersion OF((void));
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+ If the first character differs, the library code actually used is
+ not compatible with the zlib.h header file used by the application.
+ This check is automatically made by deflateInit and inflateInit.
+ */
+
+/*
+ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
+
+ Initializes the internal stream state for compression. The fields
+ zalloc, zfree and opaque must be initialized before by the caller.
+ If zalloc and zfree are set to Z_NULL, deflateInit updates them to
+ use default allocation functions.
+
+ The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+ 1 gives best speed, 9 gives best compression, 0 gives no compression at
+ all (the input data is simply copied a block at a time).
+ Z_DEFAULT_COMPRESSION requests a default compromise between speed and
+ compression (currently equivalent to level 6).
+
+ deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if level is not a valid compression level,
+ Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+ with the version assumed by the caller (ZLIB_VERSION).
+ msg is set to null if there is no error message. deflateInit does not
+ perform any compression: this will be done by deflate().
+*/
+
+
+ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
+/*
+ deflate compresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce some
+ output latency (reading input without producing any output) except when
+ forced to flush.
+
+ The detailed semantics are as follows. deflate performs one or both of the
+ following actions:
+
+ - Compress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in and avail_in are updated and
+ processing will resume at this point for the next call of deflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. This action is forced if the parameter flush is non zero.
+ Forcing flush frequently degrades the compression ratio, so this parameter
+ should be set only when necessary (in interactive applications).
+ Some output may be provided even if flush is not set.
+
+ Before the call of deflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating avail_in or avail_out accordingly; avail_out
+ should never be zero before the call. The application can consume the
+ compressed output when it wants, for example when the output buffer is full
+ (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
+ and with zero avail_out, it must be called again after making room in the
+ output buffer because there might be more output pending.
+
+ If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
+ flushed to the output buffer and the output is aligned on a byte boundary, so
+ that the decompressor can get all input data available so far. (In particular
+ avail_in is zero after the call if enough output space has been provided
+ before the call.) Flushing may degrade compression for some compression
+ algorithms and so it should be used only when necessary.
+
+ If flush is set to Z_FULL_FLUSH, all output is flushed as with
+ Z_SYNC_FLUSH, and the compression state is reset so that decompression can
+ restart from this point if previous compressed data has been damaged or if
+ random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
+ the compression.
+
+ If deflate returns with avail_out == 0, this function must be called again
+ with the same value of the flush parameter and more output space (updated
+ avail_out), until the flush is complete (deflate returns with non-zero
+ avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
+ avail_out is greater than six to avoid repeated flush markers due to
+ avail_out == 0 on return.
+
+ If the parameter flush is set to Z_FINISH, pending input is processed,
+ pending output is flushed and deflate returns with Z_STREAM_END if there
+ was enough output space; if deflate returns with Z_OK, this function must be
+ called again with Z_FINISH and more output space (updated avail_out) but no
+ more input data, until it returns with Z_STREAM_END or an error. After
+ deflate has returned Z_STREAM_END, the only possible operations on the
+ stream are deflateReset or deflateEnd.
+
+ Z_FINISH can be used immediately after deflateInit if all the compression
+ is to be done in a single step. In this case, avail_out must be at least
+ the value returned by deflateBound (see below). If deflate does not return
+ Z_STREAM_END, then it must be called again as described above.
+
+ deflate() sets strm->adler to the adler32 checksum of all input read
+ so far (that is, total_in bytes).
+
+ deflate() may update data_type if it can make a good guess about
+ the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered
+ binary. This field is only for information purposes and does not affect
+ the compression algorithm in any manner.
+
+ deflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if all input has been
+ consumed and all output has been produced (only when flush is set to
+ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+ if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible
+ (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not
+ fatal, and deflate() can be called again with more input and more output
+ space to continue compressing.
+*/
+
+
+ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+ stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+ prematurely (some input or output was discarded). In the error case,
+ msg may be set but then points to a static string (which must not be
+ deallocated).
+*/
+
+
+/*
+ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
+
+ Initializes the internal stream state for decompression. The fields
+ next_in, avail_in, zalloc, zfree and opaque must be initialized before by
+ the caller. If next_in is not Z_NULL and avail_in is large enough (the exact
+ value depends on the compression method), inflateInit determines the
+ compression method from the zlib header and allocates all data structures
+ accordingly; otherwise the allocation will be deferred to the first call of
+ inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to
+ use default allocation functions.
+
+ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+ version assumed by the caller. msg is set to null if there is no error
+ message. inflateInit does not perform any decompression apart from reading
+ the zlib header if present: this will be done by inflate(). (So next_in and
+ avail_in may be modified, but next_out and avail_out are unchanged.)
+*/
+
+
+ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
+/*
+ inflate decompresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce
+ some output latency (reading input without producing any output) except when
+ forced to flush.
+
+ The detailed semantics are as follows. inflate performs one or both of the
+ following actions:
+
+ - Decompress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in is updated and processing
+ will resume at this point for the next call of inflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. inflate() provides as much output as possible, until there
+ is no more input data or no more space in the output buffer (see below
+ about the flush parameter).
+
+ Before the call of inflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating the next_* and avail_* values accordingly.
+ The application can consume the uncompressed output when it wants, for
+ example when the output buffer is full (avail_out == 0), or after each
+ call of inflate(). If inflate returns Z_OK and with zero avail_out, it
+ must be called again after making room in the output buffer because there
+ might be more output pending.
+
+ The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH,
+ Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much
+ output as possible to the output buffer. Z_BLOCK requests that inflate() stop
+ if and when it get to the next deflate block boundary. When decoding the zlib
+ or gzip format, this will cause inflate() to return immediately after the
+ header and before the first block. When doing a raw inflate, inflate() will
+ go ahead and process the first block, and will return when it gets to the end
+ of that block, or when it runs out of data.
+
+ The Z_BLOCK option assists in appending to or combining deflate streams.
+ Also to assist in this, on return inflate() will set strm->data_type to the
+ number of unused bits in the last byte taken from strm->next_in, plus 64
+ if inflate() is currently decoding the last block in the deflate stream,
+ plus 128 if inflate() returned immediately after decoding an end-of-block
+ code or decoding the complete header up to just before the first byte of the
+ deflate stream. The end-of-block will not be indicated until all of the
+ uncompressed data from that block has been written to strm->next_out. The
+ number of unused bits may in general be greater than seven, except when
+ bit 7 of data_type is set, in which case the number of unused bits will be
+ less than eight.
+
+ inflate() should normally be called until it returns Z_STREAM_END or an
+ error. However if all decompression is to be performed in a single step
+ (a single call of inflate), the parameter flush should be set to
+ Z_FINISH. In this case all pending input is processed and all pending
+ output is flushed; avail_out must be large enough to hold all the
+ uncompressed data. (The size of the uncompressed data may have been saved
+ by the compressor for this purpose.) The next operation on this stream must
+ be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+ is never required, but can be used to inform inflate that a faster approach
+ may be used for the single inflate() call.
+
+ In this implementation, inflate() always flushes as much output as
+ possible to the output buffer, and always uses the faster approach on the
+ first call. So the only effect of the flush parameter in this implementation
+ is on the return value of inflate(), as noted below, or when it returns early
+ because Z_BLOCK is used.
+
+ If a preset dictionary is needed after this call (see inflateSetDictionary
+ below), inflate sets strm->adler to the adler32 checksum of the dictionary
+ chosen by the compressor and returns Z_NEED_DICT; otherwise it sets
+ strm->adler to the adler32 checksum of all output produced so far (that is,
+ total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described
+ below. At the end of the stream, inflate() checks that its computed adler32
+ checksum is equal to that saved by the compressor and returns Z_STREAM_END
+ only if the checksum is correct.
+
+ inflate() will decompress and check either zlib-wrapped or gzip-wrapped
+ deflate data. The header type is detected automatically. Any information
+ contained in the gzip header is not retained, so applications that need that
+ information should instead use raw inflate, see inflateInit2() below, or
+ inflateBack() and perform their own processing of the gzip header and
+ trailer.
+
+ inflate() returns Z_OK if some progress has been made (more input processed
+ or more output produced), Z_STREAM_END if the end of the compressed data has
+ been reached and all uncompressed output has been produced, Z_NEED_DICT if a
+ preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
+ corrupted (input stream not conforming to the zlib format or incorrect check
+ value), Z_STREAM_ERROR if the stream structure was inconsistent (for example
+ if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory,
+ Z_BUF_ERROR if no progress is possible or if there was not enough room in the
+ output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and
+ inflate() can be called again with more input and more output space to
+ continue decompressing. If Z_DATA_ERROR is returned, the application may then
+ call inflateSync() to look for a good compression block if a partial recovery
+ of the data is desired.
+*/
+
+
+ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+ was inconsistent. In the error case, msg may be set but then points to a
+ static string (which must not be deallocated).
+*/
+
+ /* Advanced functions */
+
+/*
+ The following functions are needed only in some special applications.
+*/
+
+/*
+ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
+ int level,
+ int method,
+ int windowBits,
+ int memLevel,
+ int strategy));
+
+ This is another version of deflateInit with more compression options. The
+ fields next_in, zalloc, zfree and opaque must be initialized before by
+ the caller.
+
+ The method parameter is the compression method. It must be Z_DEFLATED in
+ this version of the library.
+
+ The windowBits parameter is the base two logarithm of the window size
+ (the size of the history buffer). It should be in the range 8..15 for this
+ version of the library. Larger values of this parameter result in better
+ compression at the expense of memory usage. The default value is 15 if
+ deflateInit is used instead.
+
+ windowBits can also be -8..-15 for raw deflate. In this case, -windowBits
+ determines the window size. deflate() will then generate raw deflate data
+ with no zlib header or trailer, and will not compute an adler32 check value.
+
+ windowBits can also be greater than 15 for optional gzip encoding. Add
+ 16 to windowBits to write a simple gzip header and trailer around the
+ compressed data instead of a zlib wrapper. The gzip header will have no
+ file name, no extra data, no comment, no modification time (set to zero),
+ no header crc, and the operating system will be set to 255 (unknown). If a
+ gzip stream is being written, strm->adler is a crc32 instead of an adler32.
+
+ The memLevel parameter specifies how much memory should be allocated
+ for the internal compression state. memLevel=1 uses minimum memory but
+ is slow and reduces compression ratio; memLevel=9 uses maximum memory
+ for optimal speed. The default value is 8. See zconf.h for total memory
+ usage as a function of windowBits and memLevel.
+
+ The strategy parameter is used to tune the compression algorithm. Use the
+ value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+ filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no
+ string match), or Z_RLE to limit match distances to one (run-length
+ encoding). Filtered data consists mostly of small values with a somewhat
+ random distribution. In this case, the compression algorithm is tuned to
+ compress them better. The effect of Z_FILTERED is to force more Huffman
+ coding and less string matching; it is somewhat intermediate between
+ Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as
+ Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy
+ parameter only affects the compression ratio but not the correctness of the
+ compressed output even if it is not set appropriately.
+
+ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid
+ method). msg is set to null if there is no error message. deflateInit2 does
+ not perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the compression dictionary from the given byte sequence
+ without producing any compressed output. This function must be called
+ immediately after deflateInit, deflateInit2 or deflateReset, before any
+ call of deflate. The compressor and decompressor must use exactly the same
+ dictionary (see inflateSetDictionary).
+
+ The dictionary should consist of strings (byte sequences) that are likely
+ to be encountered later in the data to be compressed, with the most commonly
+ used strings preferably put towards the end of the dictionary. Using a
+ dictionary is most useful when the data to be compressed is short and can be
+ predicted with good accuracy; the data can then be compressed better than
+ with the default empty dictionary.
+
+ Depending on the size of the compression data structures selected by
+ deflateInit or deflateInit2, a part of the dictionary may in effect be
+ discarded, for example if the dictionary is larger than the window size in
+ deflate or deflate2. Thus the strings most likely to be useful should be
+ put at the end of the dictionary, not at the front.
+
+ Upon return of this function, strm->adler is set to the adler32 value
+ of the dictionary; the decompressor may later use this value to determine
+ which dictionary has been used by the compressor. (The adler32 value
+ applies to the whole dictionary even if only a subset of the dictionary is
+ actually used by the compressor.) If a raw deflate was requested, then the
+ adler32 value is not computed and strm->adler is not set.
+
+ deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent (for example if deflate has already been called for this stream
+ or if the compression method is bsort). deflateSetDictionary does not
+ perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream.
+
+ This function can be useful when several compression strategies will be
+ tried, for example when there are several ways of pre-processing the input
+ data with a filter. The streams that will be discarded should then be freed
+ by calling deflateEnd. Note that deflateCopy duplicates the internal
+ compression state which can be quite large, so this strategy is slow and
+ can consume lots of memory.
+
+ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to deflateEnd followed by deflateInit,
+ but does not free and reallocate all the internal compression state.
+ The stream will keep the same compression level and any other attributes
+ that may have been set by deflateInit2.
+
+ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
+ int level,
+ int strategy));
+/*
+ Dynamically update the compression level and compression strategy. The
+ interpretation of level and strategy is as in deflateInit2. This can be
+ used to switch between compression and straight copy of the input data, or
+ to switch to a different kind of input data requiring a different
+ strategy. If the compression level is changed, the input available so far
+ is compressed with the old level (and may be flushed); the new level will
+ take effect only at the next call of deflate().
+
+ Before the call of deflateParams, the stream state must be set as for
+ a call of deflate(), since the currently available input may have to
+ be compressed and flushed. In particular, strm->avail_out must be non-zero.
+
+ deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
+ stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
+ if strm->avail_out was zero.
+*/
+
+ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
+ uLong sourceLen));
+/*
+ deflateBound() returns an upper bound on the compressed size after
+ deflation of sourceLen bytes. It must be called after deflateInit()
+ or deflateInit2(). This would be used to allocate an output buffer
+ for deflation in a single pass, and so would be called before deflate().
+*/
+
+ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
+ int bits,
+ int value));
+/*
+ deflatePrime() inserts bits in the deflate output stream. The intent
+ is that this function is used to start off the deflate output with the
+ bits leftover from a previous deflate stream when appending to it. As such,
+ this function can only be used for raw deflate, and must be used before the
+ first deflate() call after a deflateInit2() or deflateReset(). bits must be
+ less than or equal to 16, and that many of the least significant bits of
+ value will be inserted in the output.
+
+ deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
+ int windowBits));
+
+ This is another version of inflateInit with an extra parameter. The
+ fields next_in, avail_in, zalloc, zfree and opaque must be initialized
+ before by the caller.
+
+ The windowBits parameter is the base two logarithm of the maximum window
+ size (the size of the history buffer). It should be in the range 8..15 for
+ this version of the library. The default value is 15 if inflateInit is used
+ instead. windowBits must be greater than or equal to the windowBits value
+ provided to deflateInit2() while compressing, or it must be equal to 15 if
+ deflateInit2() was not used. If a compressed stream with a larger window
+ size is given as input, inflate() will return with the error code
+ Z_DATA_ERROR instead of trying to allocate a larger window.
+
+ windowBits can also be -8..-15 for raw inflate. In this case, -windowBits
+ determines the window size. inflate() will then process raw deflate data,
+ not looking for a zlib or gzip header, not generating a check value, and not
+ looking for any check values for comparison at the end of the stream. This
+ is for use with other formats that use the deflate compressed data format
+ such as zip. Those formats provide their own check values. If a custom
+ format is developed using the raw deflate format for compressed data, it is
+ recommended that a check value such as an adler32 or a crc32 be applied to
+ the uncompressed data as is done in the zlib, gzip, and zip formats. For
+ most applications, the zlib format should be used as is. Note that comments
+ above on the use in deflateInit2() applies to the magnitude of windowBits.
+
+ windowBits can also be greater than 15 for optional gzip decoding. Add
+ 32 to windowBits to enable zlib and gzip decoding with automatic header
+ detection, or add 16 to decode only the gzip format (the zlib format will
+ return a Z_DATA_ERROR. If a gzip stream is being decoded, strm->adler is
+ a crc32 instead of an adler32.
+
+ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative
+ memLevel). msg is set to null if there is no error message. inflateInit2
+ does not perform any decompression apart from reading the zlib header if
+ present: this will be done by inflate(). (So next_in and avail_in may be
+ modified, but next_out and avail_out are unchanged.)
+*/
+
+ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the decompression dictionary from the given uncompressed byte
+ sequence. This function must be called immediately after a call of inflate
+ if this call returned Z_NEED_DICT. The dictionary chosen by the compressor
+ can be determined from the adler32 value returned by this call of
+ inflate. The compressor and decompressor must use exactly the same
+ dictionary (see deflateSetDictionary).
+
+ inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+ expected one (incorrect adler32 value). inflateSetDictionary does not
+ perform any decompression: this will be done by subsequent calls of
+ inflate().
+*/
+
+ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
+/*
+ Skips invalid compressed data until a full flush point (see above the
+ description of deflate with Z_FULL_FLUSH) can be found, or until all
+ available input is skipped. No output is provided.
+
+ inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
+ if no more input was provided, Z_DATA_ERROR if no flush point has been found,
+ or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+ case, the application may save the current current value of total_in which
+ indicates where valid compressed data was found. In the error case, the
+ application may repeatedly call inflateSync, providing more input each time,
+ until success or end of the input data.
+*/
+
+ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream.
+
+ This function can be useful when randomly accessing a large stream. The
+ first pass through the stream can periodically record the inflate state,
+ allowing restarting inflate at those points when randomly accessing the
+ stream.
+
+ inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to inflateEnd followed by inflateInit,
+ but does not free and reallocate all the internal decompression state.
+ The stream will keep attributes that may have been set by inflateInit2.
+
+ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateBackInit OF((z_stream FAR *strm, int windowBits,
+ unsigned char FAR *window));
+
+ Initialize the internal stream state for decompression using inflateBack()
+ calls. The fields zalloc, zfree and opaque in strm must be initialized
+ before the call. If zalloc and zfree are Z_NULL, then the default library-
+ derived memory allocation routines are used. windowBits is the base two
+ logarithm of the window size, in the range 8..15. window is a caller
+ supplied buffer of that size. Except for special applications where it is
+ assured that deflate was used with small window sizes, windowBits must be 15
+ and a 32K byte window must be supplied to be able to decompress general
+ deflate streams.
+
+ See inflateBack() for the usage of these routines.
+
+ inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of
+ the paramaters are invalid, Z_MEM_ERROR if the internal state could not
+ be allocated, or Z_VERSION_ERROR if the version of the library does not
+ match the version of the header file.
+*/
+
+typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *));
+typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
+
+ZEXTERN int ZEXPORT inflateBack OF((z_stream FAR *strm,
+ in_func in, void FAR *in_desc,
+ out_func out, void FAR *out_desc));
+/*
+ inflateBack() does a raw inflate with a single call using a call-back
+ interface for input and output. This is more efficient than inflate() for
+ file i/o applications in that it avoids copying between the output and the
+ sliding window by simply making the window itself the output buffer. This
+ function trusts the application to not change the output buffer passed by
+ the output function, at least until inflateBack() returns.
+
+ inflateBackInit() must be called first to allocate the internal state
+ and to initialize the state with the user-provided window buffer.
+ inflateBack() may then be used multiple times to inflate a complete, raw
+ deflate stream with each call. inflateBackEnd() is then called to free
+ the allocated state.
+
+ A raw deflate stream is one with no zlib or gzip header or trailer.
+ This routine would normally be used in a utility that reads zip or gzip
+ files and writes out uncompressed files. The utility would decode the
+ header and process the trailer on its own, hence this routine expects
+ only the raw deflate stream to decompress. This is different from the
+ normal behavior of inflate(), which expects either a zlib or gzip header and
+ trailer around the deflate stream.
+
+ inflateBack() uses two subroutines supplied by the caller that are then
+ called by inflateBack() for input and output. inflateBack() calls those
+ routines until it reads a complete deflate stream and writes out all of the
+ uncompressed data, or until it encounters an error. The function's
+ parameters and return types are defined above in the in_func and out_func
+ typedefs. inflateBack() will call in(in_desc, &buf) which should return the
+ number of bytes of provided input, and a pointer to that input in buf. If
+ there is no input available, in() must return zero--buf is ignored in that
+ case--and inflateBack() will return a buffer error. inflateBack() will call
+ out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out()
+ should return zero on success, or non-zero on failure. If out() returns
+ non-zero, inflateBack() will return with an error. Neither in() nor out()
+ are permitted to change the contents of the window provided to
+ inflateBackInit(), which is also the buffer that out() uses to write from.
+ The length written by out() will be at most the window size. Any non-zero
+ amount of input may be provided by in().
+
+ For convenience, inflateBack() can be provided input on the first call by
+ setting strm->next_in and strm->avail_in. If that input is exhausted, then
+ in() will be called. Therefore strm->next_in must be initialized before
+ calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called
+ immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in
+ must also be initialized, and then if strm->avail_in is not zero, input will
+ initially be taken from strm->next_in[0 .. strm->avail_in - 1].
+
+ The in_desc and out_desc parameters of inflateBack() is passed as the
+ first parameter of in() and out() respectively when they are called. These
+ descriptors can be optionally used to pass any information that the caller-
+ supplied in() and out() functions need to do their job.
+
+ On return, inflateBack() will set strm->next_in and strm->avail_in to
+ pass back any unused input that was provided by the last in() call. The
+ return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR
+ if in() or out() returned an error, Z_DATA_ERROR if there was a format
+ error in the deflate stream (in which case strm->msg is set to indicate the
+ nature of the error), or Z_STREAM_ERROR if the stream was not properly
+ initialized. In the case of Z_BUF_ERROR, an input or output error can be
+ distinguished using strm->next_in which will be Z_NULL only if in() returned
+ an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to
+ out() returning non-zero. (in() will always be called before out(), so
+ strm->next_in is assured to be defined if out() returns non-zero.) Note
+ that inflateBack() cannot return Z_OK.
+*/
+
+ZEXTERN int ZEXPORT inflateBackEnd OF((z_stream FAR *strm));
+/*
+ All memory allocated by inflateBackInit() is freed.
+
+ inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream
+ state was inconsistent.
+*/
+
+ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
+/* Return flags indicating compile-time options.
+
+ Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
+ 1.0: size of uInt
+ 3.2: size of uLong
+ 5.4: size of voidpf (pointer)
+ 7.6: size of z_off_t
+
+ Compiler, assembler, and debug options:
+ 8: DEBUG
+ 9: ASMV or ASMINF -- use ASM code
+ 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention
+ 11: 0 (reserved)
+
+ One-time table building (smaller code, but not thread-safe if true):
+ 12: BUILDFIXED -- build static block decoding tables when needed
+ 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed
+ 14,15: 0 (reserved)
+
+ Library content (indicates missing functionality):
+ 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
+ deflate code when not needed)
+ 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect
+ and decode gzip streams (to avoid linking crc code)
+ 18-19: 0 (reserved)
+
+ Operation variations (changes in library functionality):
+ 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate
+ 21: FASTEST -- deflate algorithm with only one, lowest compression level
+ 22,23: 0 (reserved)
+
+ The sprintf variant used by gzprintf (zero is best):
+ 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format
+ 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure!
+ 26: 0 = returns value, 1 = void -- 1 means inferred string length returned
+
+ Remainder:
+ 27-31: 0 (reserved)
+ */
+
+
+ /* utility functions */
+
+/*
+ The following utility functions are implemented on top of the
+ basic stream-oriented functions. To simplify the interface, some
+ default options are assumed (compression level and memory usage,
+ standard memory allocation functions). The source code of these
+ utility functions can easily be modified if you need special options.
+*/
+
+ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Compresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be at least the value returned
+ by compressBound(sourceLen). Upon exit, destLen is the actual size of the
+ compressed buffer.
+ This function can be used to compress a whole file at once if the
+ input file is mmap'ed.
+ compress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer.
+*/
+
+ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen,
+ int level));
+/*
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in deflateInit. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least the value returned by
+ compressBound(sourceLen). Upon exit, destLen is the actual size of the
+ compressed buffer.
+
+ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+ Z_STREAM_ERROR if the level parameter is invalid.
+*/
+
+ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));
+/*
+ compressBound() returns an upper bound on the compressed size after
+ compress() or compress2() on sourceLen bytes. It would be used before
+ a compress() or compress2() call to allocate the destination buffer.
+*/
+
+ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be large enough to hold the
+ entire uncompressed data. (The size of the uncompressed data must have
+ been saved previously by the compressor and transmitted to the decompressor
+ by some mechanism outside the scope of this compression library.)
+ Upon exit, destLen is the actual size of the compressed buffer.
+ This function can be used to decompress a whole file at once if the
+ input file is mmap'ed.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.
+*/
+
+
+typedef voidp gzFile;
+
+ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
+/*
+ Opens a gzip (.gz) file for reading or writing. The mode parameter
+ is as in fopen ("rb" or "wb") but can also include a compression level
+ ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
+ Huffman only compression as in "wb1h", or 'R' for run-length encoding
+ as in "wb1R". (See the description of deflateInit2 for more information
+ about the strategy parameter.)
+
+ gzopen can be used to read a file which is not in gzip format; in this
+ case gzread will directly read from the file without decompression.
+
+ gzopen returns NULL if the file could not be opened or if there was
+ insufficient memory to allocate the (de)compression state; errno
+ can be checked to distinguish the two cases (if errno is zero, the
+ zlib error is Z_MEM_ERROR). */
+
+ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
+/*
+ gzdopen() associates a gzFile with the file descriptor fd. File
+ descriptors are obtained from calls like open, dup, creat, pipe or
+ fileno (in the file has been previously opened with fopen).
+ The mode parameter is as in gzopen.
+ The next call of gzclose on the returned gzFile will also close the
+ file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
+ descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
+ gzdopen returns NULL if there was insufficient memory to allocate
+ the (de)compression state.
+*/
+
+ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
+/*
+ Dynamically update the compression level or strategy. See the description
+ of deflateInit2 for the meaning of these parameters.
+ gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
+ opened for writing.
+*/
+
+ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
+/*
+ Reads the given number of uncompressed bytes from the compressed file.
+ If the input file was not in gzip format, gzread copies the given number
+ of bytes into the buffer.
+ gzread returns the number of uncompressed bytes actually read (0 for
+ end of file, -1 for error). */
+
+ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
+ voidpc buf, unsigned len));
+/*
+ Writes the given number of uncompressed bytes into the compressed file.
+ gzwrite returns the number of uncompressed bytes actually written
+ (0 in case of error).
+*/
+
+ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...));
+/*
+ Converts, formats, and writes the args to the compressed file under
+ control of the format string, as in fprintf. gzprintf returns the number of
+ uncompressed bytes actually written (0 in case of error). The number of
+ uncompressed bytes written is limited to 4095. The caller should assure that
+ this limit is not exceeded. If it is exceeded, then gzprintf() will return
+ return an error (0) with nothing written. In this case, there may also be a
+ buffer overflow with unpredictable consequences, which is possible only if
+ zlib was compiled with the insecure functions sprintf() or vsprintf()
+ because the secure snprintf() or vsnprintf() functions were not available.
+*/
+
+ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
+/*
+ Writes the given null-terminated string to the compressed file, excluding
+ the terminating null character.
+ gzputs returns the number of characters written, or -1 in case of error.
+*/
+
+ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
+/*
+ Reads bytes from the compressed file until len-1 characters are read, or
+ a newline character is read and transferred to buf, or an end-of-file
+ condition is encountered. The string is then terminated with a null
+ character.
+ gzgets returns buf, or Z_NULL in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
+/*
+ Writes c, converted to an unsigned char, into the compressed file.
+ gzputc returns the value that was written, or -1 in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
+/*
+ Reads one byte from the compressed file. gzgetc returns this byte
+ or -1 in case of end of file or error.
+*/
+
+ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));
+/*
+ Push one character back onto the stream to be read again later.
+ Only one character of push-back is allowed. gzungetc() returns the
+ character pushed, or -1 on failure. gzungetc() will fail if a
+ character has been pushed but not read yet, or if c is -1. The pushed
+ character will be discarded if the stream is repositioned with gzseek()
+ or gzrewind().
+*/
+
+ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
+/*
+ Flushes all pending output into the compressed file. The parameter
+ flush is as in the deflate() function. The return value is the zlib
+ error number (see function gzerror below). gzflush returns Z_OK if
+ the flush parameter is Z_FINISH and all output could be flushed.
+ gzflush should be called only when strictly necessary because it can
+ degrade compression.
+*/
+
+ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
+ z_off_t offset, int whence));
+/*
+ Sets the starting position for the next gzread or gzwrite on the
+ given compressed file. The offset represents a number of bytes in the
+ uncompressed data stream. The whence parameter is defined as in lseek(2);
+ the value SEEK_END is not supported.
+ If the file is opened for reading, this function is emulated but can be
+ extremely slow. If the file is opened for writing, only forward seeks are
+ supported; gzseek then compresses a sequence of zeroes up to the new
+ starting position.
+
+ gzseek returns the resulting offset location as measured in bytes from
+ the beginning of the uncompressed stream, or -1 in case of error, in
+ particular if the file is opened for writing and the new starting position
+ would be before the current position.
+*/
+
+ZEXTERN int ZEXPORT gzrewind OF((gzFile file));
+/*
+ Rewinds the given file. This function is supported only for reading.
+
+ gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+*/
+
+ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file));
+/*
+ Returns the starting position for the next gzread or gzwrite on the
+ given compressed file. This position represents a number of bytes in the
+ uncompressed data stream.
+
+ gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
+*/
+
+ZEXTERN int ZEXPORT gzeof OF((gzFile file));
+/*
+ Returns 1 when EOF has previously been detected reading the given
+ input stream, otherwise zero.
+*/
+
+ZEXTERN int ZEXPORT gzclose OF((gzFile file));
+/*
+ Flushes all pending output if necessary, closes the compressed file
+ and deallocates all the (de)compression state. The return value is the zlib
+ error number (see function gzerror below).
+*/
+
+ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
+/*
+ Returns the error message for the last error which occurred on the
+ given compressed file. errnum is set to zlib error number. If an
+ error occurred in the file system and not in the compression library,
+ errnum is set to Z_ERRNO and the application may consult errno
+ to get the exact error code.
+*/
+
+ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
+/*
+ Clears the error and end-of-file flags for file. This is analogous to the
+ clearerr() function in stdio. This is useful for continuing to read a gzip
+ file that is being written concurrently.
+*/
+
+ /* checksum functions */
+
+/*
+ These functions are not related to compression but are exported
+ anyway because they might be useful in applications using the
+ compression library.
+*/
+
+ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+
+/*
+ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+ return the updated checksum. If buf is NULL, this function returns
+ the required initial value for the checksum.
+ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+ much faster. Usage example:
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ adler = adler32(adler, buffer, length);
+ }
+ if (adler != original_adler) error();
+*/
+
+ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
+/*
+ Update a running crc with the bytes buf[0..len-1] and return the updated
+ crc. If buf is NULL, this function returns the required initial value
+ for the crc. Pre- and post-conditioning (one's complement) is performed
+ within this function so it shouldn't be done by the application.
+ Usage example:
+
+ uLong crc = crc32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ crc = crc32(crc, buffer, length);
+ }
+ if (crc != original_crc) error();
+*/
+
+
+ /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method,
+ int windowBits, int memLevel,
+ int strategy, const char *version,
+ int stream_size));
+ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateBackInit_ OF((z_stream FAR *strm, int windowBits,
+ unsigned char FAR *window,
+ const char *version,
+ int stream_size));
+#define deflateInit(strm, level) \
+ deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit(strm) \
+ inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream))
+#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+ (strategy), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit2(strm, windowBits) \
+ inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+#define inflateBackInit(strm, windowBits, window) \
+ inflateBackInit_((strm), (windowBits), (window), \
+ ZLIB_VERSION, sizeof(z_stream))
+
+
+#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL)
+ struct internal_state {int dummy;}; /* hack for buggy compilers */
+#endif
+
+ZEXTERN const char * ZEXPORT zError OF((int));
+ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z));
+ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZLIB_H */
diff --git a/zlib/zutil.c b/zlib/zutil.c
new file mode 100644
index 0000000..a94cdb8
--- /dev/null
+++ b/zlib/zutil.c
@@ -0,0 +1,319 @@
+/* zutil.c -- target dependent utility functions for the compression library
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#include "zutil.h"
+
+#ifndef NO_DUMMY_DECL
+struct internal_state {int dummy;}; /* for buggy compilers */
+#endif
+
+#ifndef STDC
+extern void exit OF((int));
+#endif
+
+const char * const z_errmsg[10] = {
+"need dictionary", /* Z_NEED_DICT 2 */
+"stream end", /* Z_STREAM_END 1 */
+"", /* Z_OK 0 */
+"file error", /* Z_ERRNO (-1) */
+"stream error", /* Z_STREAM_ERROR (-2) */
+"data error", /* Z_DATA_ERROR (-3) */
+"insufficient memory", /* Z_MEM_ERROR (-4) */
+"buffer error", /* Z_BUF_ERROR (-5) */
+"incompatible version",/* Z_VERSION_ERROR (-6) */
+""};
+
+
+const char * ZEXPORT zlibVersion()
+{
+ return ZLIB_VERSION;
+}
+
+uLong ZEXPORT zlibCompileFlags()
+{
+ uLong flags;
+
+ flags = 0;
+ switch (sizeof(uInt)) {
+ case 2: break;
+ case 4: flags += 1; break;
+ case 8: flags += 2; break;
+ default: flags += 3;
+ }
+ switch (sizeof(uLong)) {
+ case 2: break;
+ case 4: flags += 1 << 2; break;
+ case 8: flags += 2 << 2; break;
+ default: flags += 3 << 2;
+ }
+ switch (sizeof(voidpf)) {
+ case 2: break;
+ case 4: flags += 1 << 4; break;
+ case 8: flags += 2 << 4; break;
+ default: flags += 3 << 4;
+ }
+ switch (sizeof(z_off_t)) {
+ case 2: break;
+ case 4: flags += 1 << 6; break;
+ case 8: flags += 2 << 6; break;
+ default: flags += 3 << 6;
+ }
+#ifdef DEBUG
+ flags += 1 << 8;
+#endif
+#if defined(ASMV) || defined(ASMINF)
+ flags += 1 << 9;
+#endif
+#ifdef ZLIB_WINAPI
+ flags += 1 << 10;
+#endif
+#ifdef BUILDFIXED
+ flags += 1 << 12;
+#endif
+#ifdef DYNAMIC_CRC_TABLE
+ flags += 1 << 13;
+#endif
+#ifdef NO_GZCOMPRESS
+ flags += 1 << 16;
+#endif
+#ifdef NO_GZIP
+ flags += 1 << 17;
+#endif
+#ifdef PKZIP_BUG_WORKAROUND
+ flags += 1 << 20;
+#endif
+#ifdef FASTEST
+ flags += 1 << 21;
+#endif
+#ifdef STDC
+# ifdef NO_vsnprintf
+ flags += 1 << 25;
+# ifdef HAS_vsprintf_void
+ flags += 1 << 26;
+# endif
+# else
+# ifdef HAS_vsnprintf_void
+ flags += 1 << 26;
+# endif
+# endif
+#else
+ flags += 1 << 24;
+# ifdef NO_snprintf
+ flags += 1 << 25;
+# ifdef HAS_sprintf_void
+ flags += 1 << 26;
+# endif
+# else
+# ifdef HAS_snprintf_void
+ flags += 1 << 26;
+# endif
+# endif
+#endif
+ return flags;
+}
+
+#ifdef DEBUG
+
+# ifndef verbose
+# define verbose 0
+# endif
+int z_verbose = verbose;
+
+void z_error (m)
+ char *m;
+{
+ fprintf(stderr, "%s\n", m);
+ exit(1);
+}
+#endif
+
+/* exported to allow conversion of error code to string for compress() and
+ * uncompress()
+ */
+const char * ZEXPORT zError(err)
+ int err;
+{
+ return ERR_MSG(err);
+}
+
+#if defined(_WIN32_WCE)
+ /* does not exist on WCE */
+ int errno = 0;
+#endif
+
+#ifndef HAVE_MEMCPY
+
+void zmemcpy(dest, source, len)
+ Bytef* dest;
+ const Bytef* source;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = *source++; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+
+int zmemcmp(s1, s2, len)
+ const Bytef* s1;
+ const Bytef* s2;
+ uInt len;
+{
+ uInt j;
+
+ for (j = 0; j < len; j++) {
+ if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
+ }
+ return 0;
+}
+
+void zmemzero(dest, len)
+ Bytef* dest;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = 0; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+#endif
+
+
+#ifdef SYS16BIT
+
+#ifdef __TURBOC__
+/* Turbo C in 16-bit mode */
+
+# define MY_ZCALLOC
+
+/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
+ * and farmalloc(64K) returns a pointer with an offset of 8, so we
+ * must fix the pointer. Warning: the pointer must be put back to its
+ * original form in order to free it, use zcfree().
+ */
+
+#define MAX_PTR 10
+/* 10*64K = 640K */
+
+local int next_ptr = 0;
+
+typedef struct ptr_table_s {
+ voidpf org_ptr;
+ voidpf new_ptr;
+} ptr_table;
+
+local ptr_table table[MAX_PTR];
+/* This table is used to remember the original form of pointers
+ * to large buffers (64K). Such pointers are normalized with a zero offset.
+ * Since MSDOS is not a preemptive multitasking OS, this table is not
+ * protected from concurrent access. This hack doesn't work anyway on
+ * a protected system like OS/2. Use Microsoft C instead.
+ */
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+ voidpf buf = opaque; /* just to make some compilers happy */
+ ulg bsize = (ulg)items*size;
+
+ /* If we allocate less than 65520 bytes, we assume that farmalloc
+ * will return a usable pointer which doesn't have to be normalized.
+ */
+ if (bsize < 65520L) {
+ buf = farmalloc(bsize);
+ if (*(ush*)&buf != 0) return buf;
+ } else {
+ buf = farmalloc(bsize + 16L);
+ }
+ if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
+ table[next_ptr].org_ptr = buf;
+
+ /* Normalize the pointer to seg:0 */
+ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;
+ *(ush*)&buf = 0;
+ table[next_ptr++].new_ptr = buf;
+ return buf;
+}
+
+void zcfree (voidpf opaque, voidpf ptr)
+{
+ int n;
+ if (*(ush*)&ptr != 0) { /* object < 64K */
+ farfree(ptr);
+ return;
+ }
+ /* Find the original pointer */
+ for (n = 0; n < next_ptr; n++) {
+ if (ptr != table[n].new_ptr) continue;
+
+ farfree(table[n].org_ptr);
+ while (++n < next_ptr) {
+ table[n-1] = table[n];
+ }
+ next_ptr--;
+ return;
+ }
+ ptr = opaque; /* just to make some compilers happy */
+ Assert(0, "zcfree: ptr not found");
+}
+
+#endif /* __TURBOC__ */
+
+
+#ifdef M_I86
+/* Microsoft C in 16-bit mode */
+
+# define MY_ZCALLOC
+
+#if (!defined(_MSC_VER) || (_MSC_VER <= 600))
+# define _halloc halloc
+# define _hfree hfree
+#endif
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ return _halloc((long)items, size);
+}
+
+void zcfree (voidpf opaque, voidpf ptr)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ _hfree(ptr);
+}
+
+#endif /* M_I86 */
+
+#endif /* SYS16BIT */
+
+
+#ifndef MY_ZCALLOC /* Any system without a special alloc function */
+
+#ifndef STDC
+extern voidp malloc OF((uInt size));
+extern voidp calloc OF((uInt items, uInt size));
+extern void free OF((voidpf ptr));
+#endif
+
+voidpf zcalloc (opaque, items, size)
+ voidpf opaque;
+ unsigned items;
+ unsigned size;
+{
+ if (opaque) items += size - size; /* make compiler happy */
+ return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) :
+ (voidpf)calloc(items, size);
+}
+
+void zcfree (opaque, ptr)
+ voidpf opaque;
+ voidpf ptr;
+{
+ free(ptr);
+ if (opaque) return; /* make compiler happy */
+}
+
+#endif /* MY_ZCALLOC */
diff --git a/zlib/zutil.h b/zlib/zutil.h
new file mode 100644
index 0000000..08f9440
--- /dev/null
+++ b/zlib/zutil.h
@@ -0,0 +1,263 @@
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZUTIL_H
+#define ZUTIL_H
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+#ifdef STDC
+# include <stddef.h>
+# include <string.h>
+# include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+ extern int errno;
+#else
+# include <errno.h>
+#endif
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+typedef unsigned char uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long ulg;
+
+extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
+/* (size given to avoid silly warnings with Visual C++) */
+
+#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
+
+#define ERR_RETURN(strm,err) \
+ return (strm->msg = (char*)ERR_MSG(err), (err))
+/* To be used only when the state is known to be valid */
+
+ /* common constants */
+
+#ifndef DEF_WBITS
+# define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+#else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES 2
+/* The three kinds of block type */
+
+#define MIN_MATCH 3
+#define MAX_MATCH 258
+/* The minimum and maximum match lengths */
+
+#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
+
+ /* target dependencies */
+
+#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32))
+# define OS_CODE 0x00
+# if defined(__TURBOC__) || defined(__BORLANDC__)
+# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
+ /* Allow compilation with ANSI keywords only enabled */
+ void _Cdecl farfree( void *block );
+ void *_Cdecl farmalloc( unsigned long nbytes );
+# else
+# include <alloc.h>
+# endif
+# else /* MSC or DJGPP */
+# include <malloc.h>
+# endif
+#endif
+
+#ifdef AMIGA
+# define OS_CODE 0x01
+#endif
+
+#if defined(VAXC) || defined(VMS)
+# define OS_CODE 0x02
+# define F_OPEN(name, mode) \
+ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
+#endif
+
+#if defined(ATARI) || defined(atarist)
+# define OS_CODE 0x05
+#endif
+
+#ifdef OS2
+# define OS_CODE 0x06
+#endif
+
+#if defined(MACOS) || defined(TARGET_OS_MAC)
+# define OS_CODE 0x07
+# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+# include <unix.h> /* for fdopen */
+# else
+# ifndef fdopen
+# define fdopen(fd,mode) NULL /* No fdopen() */
+# endif
+# endif
+#endif
+
+#ifdef TOPS20
+# define OS_CODE 0x0a
+#endif
+
+#ifdef WIN32
+# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */
+# define OS_CODE 0x0b
+# endif
+#endif
+
+#ifdef __50SERIES /* Prime/PRIMOS */
+# define OS_CODE 0x0f
+#endif
+
+#if defined(_BEOS_) || defined(RISCOS)
+# define fdopen(fd,mode) NULL /* No fdopen() */
+#endif
+
+#if (defined(_MSC_VER) && (_MSC_VER > 600))
+# if defined(_WIN32_WCE)
+# define fdopen(fd,mode) NULL /* No fdopen() */
+# ifndef _PTRDIFF_T_DEFINED
+ typedef int ptrdiff_t;
+# define _PTRDIFF_T_DEFINED
+# endif
+# else
+# define fdopen(fd,type) _fdopen(fd,type)
+# endif
+#endif
+
+ /* common defaults */
+
+#ifndef OS_CODE
+# define OS_CODE 0x03 /* assume Unix */
+#endif
+
+#ifndef F_OPEN
+# define F_OPEN(name, mode) fopen((name), (mode))
+#endif
+
+ /* functions */
+
+#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550)
+# ifndef HAVE_VSNPRINTF
+# define HAVE_VSNPRINTF
+# endif
+#endif
+#if defined(__CYGWIN__)
+# ifndef HAVE_VSNPRINTF
+# define HAVE_VSNPRINTF
+# endif
+#endif
+#ifndef HAVE_VSNPRINTF
+# ifdef MSDOS
+ /* vsnprintf may exist on some MS-DOS compilers (DJGPP?),
+ but for now we just assume it doesn't. */
+# define NO_vsnprintf
+# endif
+# ifdef __TURBOC__
+# define NO_vsnprintf
+# endif
+# ifdef WIN32
+ /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */
+# if !defined(vsnprintf) && !defined(NO_vsnprintf)
+# define vsnprintf _vsnprintf
+# endif
+# endif
+# ifdef __SASC
+# define NO_vsnprintf
+# endif
+#endif
+#ifdef VMS
+# define NO_vsnprintf
+#endif
+
+#ifdef HAVE_STRERROR
+# ifndef VMS
+ extern char *strerror OF((int));
+# endif
+# define zstrerror(errnum) strerror(errnum)
+#else
+# define zstrerror(errnum) ""
+#endif
+
+#if defined(pyr)
+# define NO_MEMCPY
+#endif
+#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
+ /* Use our own functions for small and medium model with MSC <= 5.0.
+ * You may have to use the same strategy for Borland C (untested).
+ * The __SC__ check is for Symantec.
+ */
+# define NO_MEMCPY
+#endif
+#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
+# define HAVE_MEMCPY
+#endif
+#ifdef HAVE_MEMCPY
+# ifdef SMALL_MEDIUM /* MSDOS small or medium model */
+# define zmemcpy _fmemcpy
+# define zmemcmp _fmemcmp
+# define zmemzero(dest, len) _fmemset(dest, 0, len)
+# else
+# define zmemcpy memcpy
+# define zmemcmp memcmp
+# define zmemzero(dest, len) memset(dest, 0, len)
+# endif
+#else
+ extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));
+ extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));
+ extern void zmemzero OF((Bytef* dest, uInt len));
+#endif
+
+/* Diagnostic functions */
+#ifdef DEBUG
+# include <stdio.h>
+ extern int z_verbose;
+ extern void z_error OF((char *m));
+# define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+# define Trace(x) {if (z_verbose>=0) fprintf x ;}
+# define Tracev(x) {if (z_verbose>0) fprintf x ;}
+# define Tracevv(x) {if (z_verbose>1) fprintf x ;}
+# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
+# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}
+#else
+# define Assert(cond,msg)
+# define Trace(x)
+# define Tracev(x)
+# define Tracevv(x)
+# define Tracec(c,x)
+# define Tracecv(c,x)
+#endif
+
+
+voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size));
+void zcfree OF((voidpf opaque, voidpf ptr));
+
+#define ZALLOC(strm, items, size) \
+ (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
+#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
+
+#endif /* ZUTIL_H */